From 8b362dcbde9ad62205526344332f60648dbb81c3 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Fri, 3 Apr 2026 13:34:54 +0530 Subject: [PATCH 1/3] feat: pass cli as run source --- src/lib/api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/api.ts b/src/lib/api.ts index 5cdf898..436dd91 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -18,6 +18,7 @@ export function getClient(requireAuth = true): AxiosInstance { baseURL: apiUrl, headers: { 'Content-Type': 'application/json', + 'x-run-source': 'cli', ...(apiKey ? { 'x-api-key': apiKey } : {}), }, timeout: 60000, From 82621001c4b2386e7e0633c3633141d0beb2f5d2 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Mon, 6 Apr 2026 14:35:56 +0530 Subject: [PATCH 2/3] feat: add Claude Code plugin with maxun skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add .claude-plugin/plugin.json manifest for Claude Code plugin system - Add skills/maxun/SKILL.md with full instructions for listing robots, running scrapes, fetching results, and aborting runs - Add skills/maxun/scripts/maxun.sh — curl-based Maxun API helper (requires MAXUN_API_KEY env var, supports MAXUN_BASE_URL for self-hosted) - Plugin invocable as /maxun:maxun in Claude Code - Submit to official marketplace at claude.ai/settings/plugins/submit --- .claude-plugin/plugin.json | 12 +++ skills/maxun/SKILL.md | 90 ++++++++++++++++++ skills/maxun/scripts/maxun.sh | 172 ++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 skills/maxun/SKILL.md create mode 100755 skills/maxun/scripts/maxun.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..c23e4cb --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "maxun", + "description": "List and run Maxun web scraping robots from Claude Code. Scrape websites, manage robots, and retrieve results using the Maxun API.", + "version": "1.0.0", + "author": { + "name": "Maxun", + "url": "https://www.maxun.dev" + }, + "homepage": "https://www.maxun.dev", + "repository": "https://github.com/getmaxun/maxun-cli", + "license": "AGPL-3.0" +} diff --git a/skills/maxun/SKILL.md b/skills/maxun/SKILL.md new file mode 100644 index 0000000..df60be6 --- /dev/null +++ b/skills/maxun/SKILL.md @@ -0,0 +1,90 @@ +--- +name: maxun +description: List and run Maxun web scraping robots. Use when asked to list robots, run a robot, scrape a website, or get robot results using Maxun. +argument-hint: "list | run | runs | result | get | abort " +allowed-tools: Bash +--- + +# Maxun Skill + +Maxun is a web scraping platform. The user has robots that scrape websites automatically. + +## INSTRUCTIONS FOR THE AI MODEL + +You have ONE job here: call the `Bash` tool with the exact command strings below. Do not paraphrase. Do not guess. Copy the command string exactly. + +The helper script is at: `${CLAUDE_SKILL_DIR}/scripts/maxun.sh` + +Before running any command, check that `MAXUN_API_KEY` is set. If it isn't, tell the user to export it: +```bash +export MAXUN_API_KEY="your-api-key-here" +``` + +### Action: List all robots + +When the user says anything like "list my robots", "show robots", "what robots do I have": + +Call the `Bash` tool with this exact command: +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" list +``` + +Display the output directly — it IS the robot list. Do not interpret it further. + +### Action: Run a robot + +When the user wants to run or scrape with a specific robot: + +Step 1 — List robots first: +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" list +``` + +Step 2 — Find the robot ID, then run it: +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" run +``` + +### Action: List past runs + +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" runs +``` + +### Action: Get a run result + +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" result +``` + +### Action: Get robot details + +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" get +``` + +### Action: Abort a run + +``` +bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" abort +``` + +## Error Handling + +- No robots found → tell user to create one at https://app.maxun.dev +- Robot still running → poll with `result ` + +## Setup + +Set your Maxun API key before using this skill: + +```bash +export MAXUN_API_KEY="your-api-key-here" +``` + +For **self-hosted Maxun**, also set: +```bash +export MAXUN_BASE_URL="http://localhost:8080" +``` + +Cloud users (`app.maxun.dev`) do not need `MAXUN_BASE_URL` — it defaults to the cloud URL. diff --git a/skills/maxun/scripts/maxun.sh b/skills/maxun/scripts/maxun.sh new file mode 100755 index 0000000..6cf8071 --- /dev/null +++ b/skills/maxun/scripts/maxun.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +# maxun.sh — Maxun SDK API helper for OpenClaw +# All routes are under /api/sdk/ and require x-api-key header only. +# +# Commands: +# list List all robots +# get Get robot details +# run Execute a robot (synchronous — waits for result) +# result Get a specific run result +# runs List all runs for a robot +# abort Abort an in-progress run + +set -euo pipefail + +BASE_URL="${MAXUN_BASE_URL:-https://app.maxun.dev}" +API_KEY="${MAXUN_API_KEY:-}" + +if [[ -z "$API_KEY" ]]; then + echo '{"error": "MAXUN_API_KEY environment variable is not set"}' >&2 + exit 1 +fi + +AUTH_HEADER="x-api-key: $API_KEY" + +_get() { + curl -sf -H "$AUTH_HEADER" -H "Content-Type: application/json" "$BASE_URL$1" +} + +_post() { + local path="$1" + local body="${2:-{}}" + curl -sf -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \ + -d "$body" "$BASE_URL$path" +} + +cmd="${1:-}" + +case "$cmd" in + + list) + LIMIT="${2:-}" + _get "/api/sdk/robots" | python3 -c " +import json, sys +raw = '${LIMIT}' +limit = int(raw) if raw.isdigit() else None +data = json.load(sys.stdin) +robots = data.get('data', []) +total = len(robots) +if limit: + robots = robots[:limit] +if not robots: + print('No robots found.') +else: + shown = len(robots) + print(f'Showing {shown} of {total} robot(s):\n' if limit else f'Found {total} robot(s):\n') + for r in robots: + meta = r.get('recording_meta', {}) + print(f' ID: {meta.get(\"id\", r.get(\"id\", \"\"))}') + print(f' Name: {meta.get(\"name\", \"\")}') + print(f' Type: {meta.get(\"type\", \"\")}') + print(f' URL: {meta.get(\"url\", \"\")}') + print() +" + ;; + + get) + ROBOT_ID="${2:?'Usage: maxun.sh get '}" + _get "/api/sdk/robots/$ROBOT_ID" | python3 -c " +import json, sys +data = json.load(sys.stdin) +robot = data.get('data', data) +print(json.dumps(robot, indent=2)) +" + ;; + + run) + ROBOT_ID="${2:?'Usage: maxun.sh run '}" + echo "Executing robot $ROBOT_ID (waiting for completion)..." >&2 + _post "/api/sdk/robots/$ROBOT_ID/execute" | python3 -c " +import json, sys +data = json.load(sys.stdin) +result = data.get('data', data) +print(f'Run ID: {result.get(\"runId\", \"\")}') +print(f'Status: {result.get(\"status\", \"\")}') +print() +extracted = result.get('data', {}) +text_data = extracted.get('textData', {}) +list_data = extracted.get('listData', []) +crawl_data = extracted.get('crawlData', []) +search_data = extracted.get('searchData', {}) +if text_data: + print('Text Data:') + print(json.dumps(text_data, indent=2)) +if list_data: + print(f'List Data ({len(list_data)} items):') + print(json.dumps(list_data[:5], indent=2)) + if len(list_data) > 5: + print(f' ... and {len(list_data) - 5} more items') +if crawl_data: + print(f'Crawl Data ({len(crawl_data)} pages):') + print(json.dumps(crawl_data[:3], indent=2)) +if search_data: + print('Search Data:') + print(json.dumps(search_data, indent=2)) +if not any([text_data, list_data, crawl_data, search_data]): + print('No data extracted.') + print(json.dumps(result, indent=2)) +" + ;; + + result) + ROBOT_ID="${2:?'Usage: maxun.sh result '}" + RUN_ID="${3:?'Usage: maxun.sh result '}" + _get "/api/sdk/robots/$ROBOT_ID/runs/$RUN_ID" | python3 -c " +import json, sys +data = json.load(sys.stdin) +run = data.get('data', data) +print(f'Run ID: {run.get(\"runId\", run.get(\"id\", \"\"))}') +print(f'Status: {run.get(\"status\", \"\")}') +print(f'Started: {run.get(\"startedAt\", \"\")}') +print(f'Ended: {run.get(\"finishedAt\", \"\")}') +print() +output = run.get('serializableOutput', {}) +if output: + print('Output:') + print(json.dumps(output, indent=2)) +else: + print('No output yet.') +" + ;; + + runs) + ROBOT_ID="${2:?'Usage: maxun.sh runs '}" + _get "/api/sdk/robots/$ROBOT_ID/runs" | python3 -c " +import json, sys +data = json.load(sys.stdin) +runs = data.get('data', []) +print(f'Total runs: {len(runs)}\n') +for r in runs: + print(f' Run ID: {r.get(\"runId\",\"\")} | Status: {r.get(\"status\",\"?\")} | Started: {r.get(\"startedAt\",\"\")}') +" + ;; + + abort) + ROBOT_ID="${2:?'Usage: maxun.sh abort '}" + RUN_ID="${3:?'Usage: maxun.sh abort '}" + _post "/api/sdk/robots/$ROBOT_ID/runs/$RUN_ID/abort" | python3 -c " +import json, sys +data = json.load(sys.stdin) +print(json.dumps(data, indent=2)) +" + ;; + + *) + echo "Maxun SDK API helper" + echo "" + echo "Usage: maxun.sh [args]" + echo "" + echo "Commands:" + echo " list List all robots" + echo " get Get robot details" + echo " run Execute a robot (synchronous)" + echo " result Get a specific run result" + echo " runs List all runs for a robot" + echo " abort Abort a running execution" + echo "" + echo "Environment:" + echo " MAXUN_API_KEY Required. Your Maxun API key." + echo " MAXUN_BASE_URL Optional. Default: https://app.maxun.dev" + exit 1 + ;; +esac From fdae2c2fa7fe70c73327f81e6a32d1a5e5d28c7e Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Mon, 6 Apr 2026 14:45:57 +0530 Subject: [PATCH 3/3] feat: restrict skill to cloud-only (app.maxun.dev) - Hardcode BASE_URL to app.maxun.dev in maxun.sh - Reject MAXUN_BASE_URL with clear error if set - Remove self-hosted setup docs from SKILL.md - Update error message to point to app.maxun.dev/settings --- skills/maxun/SKILL.md | 11 +++-------- skills/maxun/scripts/maxun.sh | 12 +++++++++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/skills/maxun/SKILL.md b/skills/maxun/SKILL.md index df60be6..847cf61 100644 --- a/skills/maxun/SKILL.md +++ b/skills/maxun/SKILL.md @@ -1,6 +1,6 @@ --- name: maxun -description: List and run Maxun web scraping robots. Use when asked to list robots, run a robot, scrape a website, or get robot results using Maxun. +description: List and run Maxun cloud robots. Use when asked to list robots, run a robot, scrape a website, or get robot results using Maxun. argument-hint: "list | run | runs | result | get | abort " allowed-tools: Bash --- @@ -76,15 +76,10 @@ bash "${CLAUDE_SKILL_DIR}/scripts/maxun.sh" abort ## Setup -Set your Maxun API key before using this skill: +This skill works with **Maxun cloud only** (`app.maxun.dev`). Set your API key: ```bash export MAXUN_API_KEY="your-api-key-here" ``` -For **self-hosted Maxun**, also set: -```bash -export MAXUN_BASE_URL="http://localhost:8080" -``` - -Cloud users (`app.maxun.dev`) do not need `MAXUN_BASE_URL` — it defaults to the cloud URL. +Get your key at: https://app.maxun.dev/settings diff --git a/skills/maxun/scripts/maxun.sh b/skills/maxun/scripts/maxun.sh index 6cf8071..f1cfd36 100755 --- a/skills/maxun/scripts/maxun.sh +++ b/skills/maxun/scripts/maxun.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -# maxun.sh — Maxun SDK API helper for OpenClaw +# maxun.sh — Maxun SDK API helper for Claude Code (cloud only) # All routes are under /api/sdk/ and require x-api-key header only. +# NOTE: This skill only works with app.maxun.dev (cloud). Self-hosted is not supported. # # Commands: # list List all robots @@ -12,11 +13,16 @@ set -euo pipefail -BASE_URL="${MAXUN_BASE_URL:-https://app.maxun.dev}" +BASE_URL="https://app.maxun.dev" API_KEY="${MAXUN_API_KEY:-}" +if [[ -n "${MAXUN_BASE_URL:-}" ]]; then + echo 'Error: This skill only supports the Maxun cloud (app.maxun.dev). Self-hosted instances are not supported.' >&2 + exit 1 +fi + if [[ -z "$API_KEY" ]]; then - echo '{"error": "MAXUN_API_KEY environment variable is not set"}' >&2 + echo '{"error": "MAXUN_API_KEY environment variable is not set. Get your key at https://app.maxun.dev/settings"}' >&2 exit 1 fi