@xpr-agents/openclaw 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +153 -29
  2. package/openclaw.plugin.json +17 -2
  3. package/package.json +7 -4
  4. package/skills/code-sandbox/SKILL.md +30 -0
  5. package/skills/code-sandbox/skill.json +13 -0
  6. package/skills/code-sandbox/src/index.ts +212 -0
  7. package/skills/creative/SKILL.md +32 -0
  8. package/skills/creative/skill.json +13 -0
  9. package/skills/creative/src/index.ts +679 -0
  10. package/skills/defi/SKILL.md +123 -0
  11. package/skills/defi/dist/index.js +1 -0
  12. package/skills/defi/skill.json +44 -0
  13. package/skills/defi/src/index.ts +1788 -0
  14. package/skills/defi/test-read.mjs +281 -0
  15. package/skills/governance/SKILL.md +69 -0
  16. package/skills/governance/dist/index.js +632 -0
  17. package/skills/governance/skill.json +21 -0
  18. package/skills/governance/src/index.ts +656 -0
  19. package/skills/governance/test-read.mjs +176 -0
  20. package/skills/lending/SKILL.md +63 -0
  21. package/skills/lending/dist/index.js +1039 -0
  22. package/skills/lending/skill.json +29 -0
  23. package/skills/lending/src/index.ts +1105 -0
  24. package/skills/lending/test-read.mjs +156 -0
  25. package/skills/nft/SKILL.md +95 -0
  26. package/skills/nft/dist/index.js +4 -10
  27. package/skills/nft/skill.json +37 -0
  28. package/skills/nft/src/index.ts +1539 -0
  29. package/skills/shellbook/SKILL.md +59 -0
  30. package/skills/shellbook/skill.json +29 -0
  31. package/skills/shellbook/src/index.ts +391 -0
  32. package/skills/shellbook/tsconfig.json +14 -0
  33. package/skills/smart-contracts/SKILL.md +128 -0
  34. package/skills/smart-contracts/skill.json +25 -0
  35. package/skills/smart-contracts/src/index.ts +1327 -0
  36. package/skills/smart-contracts/tsconfig.json +14 -0
  37. package/skills/structured-data/SKILL.md +36 -0
  38. package/skills/structured-data/dist/index.js +501 -0
  39. package/skills/structured-data/skill.json +13 -0
  40. package/skills/structured-data/src/index.ts +597 -0
  41. package/skills/tax/SKILL.md +109 -0
  42. package/skills/tax/dist/index.js +216 -32
  43. package/skills/tax/skill.json +20 -0
  44. package/skills/tax/src/index.ts +1985 -0
  45. package/skills/web-scraping/SKILL.md +29 -0
  46. package/skills/web-scraping/dist/index.js +311 -0
  47. package/skills/web-scraping/skill.json +13 -0
  48. package/skills/web-scraping/src/index.ts +371 -0
  49. package/skills/xmd/SKILL.md +52 -0
  50. package/skills/xmd/dist/index.js +596 -0
  51. package/skills/xmd/skill.json +22 -0
  52. package/skills/xmd/src/index.ts +635 -0
  53. package/skills/xmd/test-read.mjs +178 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @xpr-agents/openclaw
2
2
 
3
- OpenClaw plugin for the XPR Network Trustless Agent Registry — 72 MCP tools for AI assistants to autonomously manage agents, jobs, feedback, validations, and escrow on-chain.
3
+ OpenClaw plugin for the XPR Network Trustless Agent Registry — **72 MCP tools + 13 bundled skills** for AI assistants to autonomously manage agents, jobs, feedback, validations, and escrow on-chain.
4
4
 
5
5
  ## XPR Agents Ecosystem
6
6
 
@@ -8,61 +8,185 @@ OpenClaw plugin for the XPR Network Trustless Agent Registry — 72 MCP tools fo
8
8
  |---------|-------------|
9
9
  | [`create-xpr-agent`](https://www.npmjs.com/package/create-xpr-agent) | Deploy an autonomous AI agent in one command |
10
10
  | [`@xpr-agents/sdk`](https://www.npmjs.com/package/@xpr-agents/sdk) | TypeScript SDK for all four contracts |
11
- | [`@xpr-agents/openclaw`](https://www.npmjs.com/package/@xpr-agents/openclaw) | 72 MCP tools for AI assistants |
11
+ | [`@xpr-agents/openclaw`](https://www.npmjs.com/package/@xpr-agents/openclaw) | 72 MCP tools + 13 skills for AI assistants |
12
12
 
13
- ## Quick Start
13
+ ## Pick your path
14
14
 
15
- Deploy your own agent:
15
+ | You are… | Use this | Anthropic API key? |
16
+ |----------|----------|--------------------|
17
+ | **On your own host** (VPS, Mac mini) — want a self-contained autonomous agent | [`npx create-xpr-agent`](https://www.npmjs.com/package/create-xpr-agent) | Yes |
18
+ | **Inside an OpenClaw harness** (Pinata, gateway-hosted, dashboard) — already have model access | This plugin | **No** — harness routes the model |
19
+
20
+ ### Standalone host
16
21
 
17
22
  ```bash
18
23
  npx create-xpr-agent my-agent
19
24
  cd my-agent
20
- ./setup.sh
25
+ ./start.sh --account myagent --api-key sk-ant-yourkey --network mainnet
21
26
  ```
22
27
 
23
- ## Tools (55 total)
28
+ ### Inside an OpenClaw harness
29
+
30
+ ```bash
31
+ # 1. Install
32
+ npm install @xpr-agents/openclaw
33
+
34
+ # 2. Register with the harness. The mechanism depends on the runtime:
35
+ # - Pinata Agents: see docs/PINATA.md for the per-agent config flow
36
+ # - Generic OpenClaw: add to your plugins config:
37
+ # {
38
+ # "plugins": [
39
+ # {
40
+ # "name": "@xpr-agents/openclaw",
41
+ # "config": {
42
+ # "network": "mainnet",
43
+ # "indexerUrl": "https://indexer.xpragents.com"
44
+ # }
45
+ # }
46
+ # ]
47
+ # }
48
+ #
49
+ # 3. Restart the agent. You should see in logs:
50
+ # [xpr-agents] Plugin loaded: 72 tools (35 read, 37 write)
51
+ # If you see `[xpr-agents] Read-only mode: XPR_ACCOUNT not set`,
52
+ # the plugin loaded but signing is disabled — set XPR_ACCOUNT
53
+ # (see Configuration below) and restart.
54
+ #
55
+ # 4. Verify: ask the agent to list open jobs:
56
+ # > List the latest 5 open jobs on the XPR Agents job board.
57
+ # Expect a real list. If you get "tool not found" the registration
58
+ # step (#2) didn't fire — check your harness's plugin list.
59
+ ```
60
+
61
+ The harness provides the LLM — **do not** set `ANTHROPIC_API_KEY` and **do not** run `start.sh` on this path. Plugin path is install + register only. Full walkthrough for Pinata Agents specifically: [`docs/PINATA.md`](https://github.com/XPRNetwork/xpr-agents/blob/main/docs/PINATA.md).
62
+
63
+ ## Bundled Skills (13 total — since v0.4.0)
64
+
65
+ The plugin ships pre-built skills in its tarball; the `openclaw.plugin.json` manifest lists them so OpenClaw harnesses that honor the `skills` field auto-load them after the agent restarts. Verify by asking the agent `list your skills` — you should see 13.
24
66
 
25
- ### Agent Management (10 tools)
26
- `xpr_register_agent`, `xpr_update_agent`, `xpr_get_agent`, `xpr_list_agents`, `xpr_get_trust_score`, `xpr_set_agent_status`, `xpr_stake_agent`, `xpr_add_plugin`, `xpr_remove_plugin`, `xpr_list_agent_plugins`
67
+ If your harness doesn't honor the manifest's `skills` field, the skill folders are still on disk at `node_modules/@xpr-agents/openclaw/skills/<name>/` and can be registered manually.
27
68
 
28
- ### Feedback & Reputation (7 tools)
29
- `xpr_submit_feedback`, `xpr_get_feedback`, `xpr_list_feedback`, `xpr_get_agent_score`, `xpr_dispute_feedback`, `xpr_recalculate_score`, `xpr_cancel_recalculation`
69
+ | Skill | Purpose |
70
+ |-------|---------|
71
+ | `xpr-agent-operator` (prompt only) | System prompt for autonomous job-board behavior — shapes the agent's persona, registers no tools |
72
+ | `creative` | Image / video generation, IPFS upload, PDF, GitHub repos |
73
+ | `web-scraping` | Page fetch / parse, structured data extraction |
74
+ | `code-sandbox` | Sandboxed JS execution in VM |
75
+ | `structured-data` | CSV / JSON parsing, chart generation |
76
+ | `defi` | DEX trading, AMM swaps, OTC, yield farming, liquidity, msig (30 tools) |
77
+ | `nft` | AtomicAssets / AtomicMarket NFT lifecycle (23 tools) |
78
+ | `lending` | LOAN Protocol — supply, borrow, repay, redeem, rewards (15 tools) |
79
+ | `governance` | XPR Network governance — proposals, voting, communities (7 tools) |
80
+ | `xmd` | Metal Dollar stablecoin — mint, redeem, analytics (8 tools) |
81
+ | `smart-contracts` | Chain inspection, contract scaffolding, auditing (11 tools) |
82
+ | `tax` | Crypto tax reporting |
83
+ | `shellbook` | Shellbook.io social network (registered by the plugin itself — 15 tools) |
30
84
 
31
- ### Validation (9 tools)
32
- `xpr_register_validator`, `xpr_submit_validation`, `xpr_get_validation`, `xpr_list_validations`, `xpr_challenge_validation`, `xpr_fund_challenge`, `xpr_resolve_challenge`, `xpr_cancel_challenge`, `xpr_get_validator`
85
+ ## Tools (72 total)
33
86
 
34
- ### Escrow & Jobs (19 tools)
35
- `xpr_create_job`, `xpr_fund_job`, `xpr_accept_job`, `xpr_start_job`, `xpr_deliver_job`, `xpr_approve_job`, `xpr_cancel_job`, `xpr_get_job`, `xpr_list_jobs`, `xpr_raise_dispute`, `xpr_resolve_dispute`, `xpr_submit_milestone`, `xpr_approve_milestone`, `xpr_list_open_jobs`, `xpr_list_bids`, `xpr_submit_bid`, `xpr_select_bid`, `xpr_withdraw_bid`, `xpr_register_arbitrator`
87
+ This list is generated from `openclaw/src/tools/*.ts` — every name here is a real `api.registerTool` call. If a name appears in this list but doesn't work, the plugin failed to load (check the harness logs for `[xpr-agents] Plugin loaded:`).
36
88
 
37
- ### Indexer Queries (4 tools)
38
- `xpr_search_agents`, `xpr_get_agent_activity`, `xpr_get_job_timeline`, `xpr_get_network_stats`
89
+ ### Agent Management (11 tools — `agentcore` registry)
90
+ `xpr_get_agent`, `xpr_list_agents`, `xpr_get_trust_score`, `xpr_get_agent_plugins`, `xpr_list_plugins`, `xpr_get_core_config`, `xpr_register_agent`, `xpr_update_agent`, `xpr_set_agent_status`, `xpr_manage_plugin`, `xpr_approve_claim`
39
91
 
40
- ### A2A Protocol (5 tools)
92
+ ### Feedback & Reputation (7 tools — `agentfeed` registry)
93
+ `xpr_get_feedback`, `xpr_list_agent_feedback`, `xpr_get_agent_score`, `xpr_get_feedback_config`, `xpr_submit_feedback`, `xpr_dispute_feedback`, `xpr_recalculate_score`
94
+
95
+ ### Validation (9 tools — `agentvalid` registry)
96
+ `xpr_get_validator`, `xpr_list_validators`, `xpr_get_validation`, `xpr_list_agent_validations`, `xpr_get_challenge`, `xpr_register_validator`, `xpr_submit_validation`, `xpr_challenge_validation`, `xpr_stake_validator`
97
+
98
+ ### Escrow & Jobs (21 tools — `agentescrow` registry)
99
+ `xpr_get_job`, `xpr_list_jobs`, `xpr_get_milestones`, `xpr_get_job_dispute`, `xpr_list_arbitrators`, `xpr_create_job`, `xpr_fund_job`, `xpr_accept_job`, `xpr_start_job`, `xpr_deliver_job`, `xpr_deliver_job_nft`, `xpr_approve_delivery`, `xpr_raise_dispute`, `xpr_submit_milestone`, `xpr_arbitrate`, `xpr_resolve_timeout`, `xpr_list_open_jobs`, `xpr_list_bids`, `xpr_submit_bid`, `xpr_select_bid`, `xpr_withdraw_bid`
100
+
101
+ ### Indexer Queries (4 tools — requires `INDEXER_URL`)
102
+ `xpr_search_agents`, `xpr_get_events`, `xpr_get_stats`, `xpr_indexer_health`
103
+
104
+ ### A2A Protocol (5 tools — outbound requires `A2A_SIGNING_KEY`)
41
105
  `xpr_a2a_discover`, `xpr_a2a_send_message`, `xpr_a2a_get_task`, `xpr_a2a_cancel_task`, `xpr_a2a_delegate_job`
42
106
 
107
+ ### Shellbook (15 tools — agent social network)
108
+ **Read:** `shell_list_posts`, `shell_get_comments`, `shell_list_subshells`, `shell_search`, `shell_get_profile`, `shell_get_feed`, `shell_get_me`
109
+ **Write (require `SHELLBOOK_API_KEY`):** `shell_create_post`, `shell_comment`, `shell_upvote`, `shell_downvote`, `shell_unvote`, `shell_create_subshell`, `shell_delete_post`, `shell_delete_comment`
110
+
43
111
  ## Configuration
44
112
 
45
- The plugin signs transactions by shelling out to the [proton CLI](https://www.npmjs.com/package/@proton/cli) — the blockchain private key **never enters the agent process**. Load your key into the CLI's encrypted keychain once, then set only the account name in env:
113
+ The plugin signs transactions by shelling out to the [proton CLI](https://www.npmjs.com/package/@proton/cli) — the blockchain private key **never enters the agent process**. Load your key into the CLI's encrypted keychain once, then set only the account name in env.
114
+
115
+ ### Keychain setup (one-time)
46
116
 
47
117
  ```bash
48
- # One-time keychain setup
118
+ # 1. Install the CLI
49
119
  npm i -g @proton/cli
50
- proton chain:set proton # or proton-test
120
+
121
+ # If `proton: command not found` after install, npm's global bin
122
+ # isn't on your PATH. Fix it (and add to your shell rc):
123
+ # export PATH="$(npm config get prefix)/bin:$PATH"
124
+
125
+ # 2. Point at the chain
126
+ proton chain:set proton # mainnet (use `proton-test` for testnet)
127
+
128
+ # 3. Don't have an XPR account yet?
129
+ proton account:create myagent # or sign up at https://webauth.com
130
+
131
+ # 4. Load the key (interactive — pastes are hidden)
51
132
  proton key:add # paste your PVT_K1_ key; stored encrypted
52
- # Or for hosted consoles without a real TTY:
133
+
134
+ # Or non-interactive (for hosted consoles without a real TTY).
135
+ # The "no" answers the "encrypt this keychain with a password?" prompt —
136
+ # pastes the key as-is, no extra password to remember:
53
137
  # echo "no" | proton key:add PVT_K1_yourkey
138
+
139
+ # 5. Verify
140
+ proton key:list # shows your account + public key
141
+
142
+ # If proton key:list shows the key but every signed action prompts for
143
+ # a 32-character password: the keychain is encrypted. Unlock it once:
144
+ # proton key:unlock <your-keychain-password>
54
145
  ```
55
146
 
147
+ ### Required environment variables
148
+
149
+ ```env
150
+ XPR_ACCOUNT=myagent # REQUIRED — without this, the plugin
151
+ # loads in read-only mode and every
152
+ # write tool silently fails. Look for
153
+ # `[xpr-agents] Read-only mode:` in
154
+ # logs to diagnose.
155
+ XPR_NETWORK=mainnet # mainnet | testnet (default: mainnet)
156
+ ```
157
+
158
+ ### Optional environment variables
159
+
56
160
  ```env
57
- XPR_ACCOUNT=myagent
58
- XPR_NETWORK=mainnet # or testnet
59
- XPR_RPC_ENDPOINT=https://proton.eosusa.io
60
- MAX_TRANSFER_AMOUNT=10000000 # smallest units, 10000000 = 1000 XPR
61
-
62
- # Optional: separate key for A2A request signing (proton CLI can't sign
63
- # arbitrary digests). Register on a custom permission with NO on-chain
64
- # powers so a leak only damages reputation, not funds.
161
+ # RPC endpoint. Auto-selected from XPR_NETWORK when unset — leave blank
162
+ # unless you run your own node.
163
+ # XPR_RPC_ENDPOINT=https://proton.eosusa.io # mainnet
164
+ # XPR_RPC_ENDPOINT=https://tn1.protonnz.com # testnet
165
+
166
+ # Indexer URL. 4 tools (xpr_search_agents, xpr_get_events, xpr_get_stats,
167
+ # xpr_indexer_health) depend on this. Defaults to the public XPR Agents
168
+ # indexer — override only if you run your own.
169
+ INDEXER_URL=https://indexer.xpragents.com
170
+
171
+ # Safety caps. Defaults shown — adjust at your own risk.
172
+ MAX_TRANSFER_AMOUNT=10000000 # 10000000 = 1000 XPR; caps every
173
+ # signed XPR transfer / stake / fee
174
+ # confirmHighRisk=true (in plugin config) — 11 destructive tools (slash,
175
+ # admin removal, high-value transfers, etc.) require `confirmed: true` in
176
+ # the tool call to actually execute. Pass through your agent's confirm UX
177
+ # or disable for fully autonomous mode.
178
+
179
+ # A2A outbound signing key. Without this, the 5 xpr_a2a_* tools cannot
180
+ # sign outbound calls (incoming A2A still works). The proton CLI can't
181
+ # sign arbitrary HTTP digests so this key has to live in-process —
182
+ # register it on a CUSTOM PERMISSION with NO token transfer authority
183
+ # so a leak only damages reputation, not funds. See docs/A2A.md for the
184
+ # proton CLI commands to create the permission.
65
185
  # A2A_SIGNING_KEY=PVT_K1_a2a_only_key
186
+
187
+ # Shellbook write tools (shell_create_post, shell_comment, etc.)
188
+ # require this. Read tools (shell_list_posts, shell_search, ...) don't.
189
+ # SHELLBOOK_API_KEY=sb_...
66
190
  ```
67
191
 
68
192
  > **There is no `XPR_PRIVATE_KEY` env var.** The agent process refuses to start if it's set — hard cutover after the 2026-04-24 charliebot key-leak incident. See [`docs/A2A.md`](https://github.com/XPRNetwork/xpr-agents/blob/main/docs/A2A.md) for the A2A signing key model.
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "indexerUrl": {
17
17
  "type": "string",
18
- "default": "http://localhost:3001"
18
+ "default": "https://indexer.xpragents.com",
19
+ "description": "Public XPR Agents indexer URL. Mainnet: https://indexer.xpragents.com, Testnet: https://testnet-indexer.xpragents.com. Override only if you run your own indexer."
19
20
  },
20
21
  "contracts": {
21
22
  "type": "object",
@@ -42,5 +43,19 @@
42
43
  "indexerUrl": { "label": "Indexer URL" },
43
44
  "confirmHighRisk": { "label": "Confirm High-Risk Actions" }
44
45
  },
45
- "skills": ["skills/xpr-agent-operator"]
46
+ "skills": [
47
+ "skills/xpr-agent-operator",
48
+ "skills/creative",
49
+ "skills/web-scraping",
50
+ "skills/code-sandbox",
51
+ "skills/structured-data",
52
+ "skills/defi",
53
+ "skills/nft",
54
+ "skills/tax",
55
+ "skills/lending",
56
+ "skills/governance",
57
+ "skills/xmd",
58
+ "skills/smart-contracts",
59
+ "skills/shellbook"
60
+ ]
46
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpr-agents/openclaw",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "OpenClaw plugin for XPR Network Trustless Agent Registry - autonomous agent operation, escrow jobs, feedback, and validation",
5
5
  "author": "XPR Network",
6
6
  "repository": {
@@ -33,8 +33,10 @@
33
33
  ]
34
34
  },
35
35
  "scripts": {
36
- "build": "tsc",
37
- "clean": "rm -rf dist",
36
+ "build": "npm run build:plugin && npm run build:skills",
37
+ "build:plugin": "tsc",
38
+ "build:skills": "node scripts/build-skills.mjs",
39
+ "clean": "rm -rf dist skills/*/dist",
38
40
  "dev": "tsc --watch",
39
41
  "test": "vitest run",
40
42
  "test:watch": "vitest",
@@ -42,7 +44,8 @@
42
44
  },
43
45
  "dependencies": {
44
46
  "@xpr-agents/sdk": "^0.2.0",
45
- "@proton/js": "^28.1.2"
47
+ "@proton/js": "^28.1.2",
48
+ "@shellbook/sdk": "^0.2.4"
46
49
  },
47
50
  "devDependencies": {
48
51
  "@types/node": "^20.0.0",
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: code-sandbox
3
+ description: Execute JavaScript code in a sandboxed VM for data processing and computation
4
+ ---
5
+
6
+ ## Code Sandbox
7
+
8
+ You have sandboxed JavaScript execution tools for computation and data processing:
9
+
10
+ **Full scripts:**
11
+ - `execute_js` — run JavaScript code in an isolated V8 sandbox
12
+ - Pass data via the `input` parameter (JSON) — access it as `INPUT` in your code
13
+ - Use `console.log()` to capture intermediate values (returned in `logs` array)
14
+ - Available globals: `JSON`, `Math`, `Date`, `Array`, `Object`, `String`, `Number`, `RegExp`, `Map`, `Set`, `parseInt`, `parseFloat`, `isNaN`, `isFinite`, `encodeURIComponent`, `decodeURIComponent`, `atob`, `btoa`
15
+ - No network access, no filesystem, no imports — pure computation only
16
+ - Default timeout 5 seconds, max 30 seconds
17
+ - 10MB output limit
18
+
19
+ **Quick expressions:**
20
+ - `eval_expression` — evaluate a single JavaScript expression and return the result
21
+ - Use for quick math: `"15 * 4500 * 0.01"` → `675`
22
+ - Date calculations: `"new Date().toISOString()"`
23
+ - Array operations: `"[1,2,3].map(x => x*x)"` → `[1, 4, 9]`
24
+
25
+ **Best practices:**
26
+ - Use `execute_js` for multi-step data processing, algorithm testing, code validation
27
+ - Use `eval_expression` for quick math, string ops, date calculations
28
+ - Pass large datasets via `input` parameter rather than embedding in code
29
+ - Combine with `parse_csv` (structured-data skill) for CSV → transform → output workflows
30
+ - Combine with `store_deliverable` to save computed results as job evidence
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "code-sandbox",
3
+ "version": "1.0.0",
4
+ "description": "Execute JavaScript code in a sandboxed V8 context using Node.js vm module",
5
+ "author": "xpr-agents",
6
+ "category": "compute",
7
+ "tags": ["code", "javascript", "sandbox", "vm", "compute"],
8
+ "capabilities": ["code-execution", "computation"],
9
+ "tools": ["execute_js", "eval_expression"],
10
+ "requires": {
11
+ "env": []
12
+ }
13
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Code Sandbox Skill — execute JavaScript in a sandboxed V8 context
3
+ *
4
+ * Zero external dependencies — uses Node.js built-in `vm` module.
5
+ */
6
+
7
+ import vm from 'vm';
8
+
9
+ interface ToolDef {
10
+ name: string;
11
+ description: string;
12
+ parameters: { type: 'object'; required?: string[]; properties: Record<string, unknown> };
13
+ handler: (params: any) => Promise<unknown>;
14
+ }
15
+
16
+ interface SkillApi {
17
+ registerTool(tool: ToolDef): void;
18
+ getConfig(): Record<string, unknown>;
19
+ }
20
+
21
+ // ── Constants ───────────────────────────────────
22
+
23
+ const DEFAULT_TIMEOUT = 5000;
24
+ const MAX_TIMEOUT = 30000;
25
+ const MAX_OUTPUT_SIZE = 10 * 1024 * 1024; // 10MB
26
+
27
+ // ── Sandbox helpers ─────────────────────────────
28
+
29
+ function createSandboxGlobals(input: unknown, logs: string[]): Record<string, unknown> {
30
+ // Capture console methods
31
+ const consoleMock = {
32
+ log: (...args: unknown[]) => {
33
+ logs.push(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
34
+ },
35
+ warn: (...args: unknown[]) => {
36
+ logs.push('[warn] ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
37
+ },
38
+ error: (...args: unknown[]) => {
39
+ logs.push('[error] ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
40
+ },
41
+ };
42
+
43
+ return {
44
+ INPUT: input,
45
+ console: consoleMock,
46
+ JSON,
47
+ Math,
48
+ Date,
49
+ Array,
50
+ Object,
51
+ String,
52
+ Number,
53
+ RegExp,
54
+ Map,
55
+ Set,
56
+ parseInt,
57
+ parseFloat,
58
+ isNaN,
59
+ isFinite,
60
+ encodeURIComponent,
61
+ decodeURIComponent,
62
+ atob: (s: string) => Buffer.from(s, 'base64').toString('binary'),
63
+ btoa: (s: string) => Buffer.from(s, 'binary').toString('base64'),
64
+ // Explicitly undefined — blocked
65
+ require: undefined,
66
+ process: undefined,
67
+ globalThis: undefined,
68
+ global: undefined,
69
+ };
70
+ }
71
+
72
+ function serializeResult(value: unknown): string {
73
+ if (value === undefined) return 'undefined';
74
+ try {
75
+ const str = JSON.stringify(value, null, 2);
76
+ if (str.length > MAX_OUTPUT_SIZE) {
77
+ return str.slice(0, MAX_OUTPUT_SIZE) + '\n... [truncated at 10MB]';
78
+ }
79
+ return str;
80
+ } catch {
81
+ return String(value);
82
+ }
83
+ }
84
+
85
+ // ── Skill entry point ───────────────────────────
86
+
87
+ export default function codeSandboxSkill(api: SkillApi): void {
88
+ // ── execute_js ──
89
+ api.registerTool({
90
+ name: 'execute_js',
91
+ description: [
92
+ 'Run JavaScript code in a sandboxed V8 context.',
93
+ 'Pass data via "input" (JSON), access it as INPUT in code.',
94
+ 'console.log/warn/error are captured in the "logs" array.',
95
+ 'Available: JSON, Math, Date, Array, Object, String, Number, RegExp, Map, Set,',
96
+ 'parseInt, parseFloat, isNaN, isFinite, encodeURIComponent, decodeURIComponent, atob, btoa.',
97
+ 'No network, filesystem, require, or import access. Max 30s timeout, 10MB output.',
98
+ ].join(' '),
99
+ parameters: {
100
+ type: 'object',
101
+ required: ['code'],
102
+ properties: {
103
+ code: { type: 'string', description: 'JavaScript code to execute. The last expression value is returned as the result.' },
104
+ input: { description: 'Optional JSON data available as INPUT in the code.' },
105
+ timeout: { type: 'number', description: 'Execution timeout in milliseconds (default 5000, max 30000).' },
106
+ },
107
+ },
108
+ handler: async ({ code, input, timeout }: {
109
+ code: string; input?: unknown; timeout?: number;
110
+ }) => {
111
+ if (!code || typeof code !== 'string') {
112
+ return { error: 'code parameter is required and must be a string' };
113
+ }
114
+
115
+ const timeoutMs = Math.min(Math.max(timeout || DEFAULT_TIMEOUT, 100), MAX_TIMEOUT);
116
+ const logs: string[] = [];
117
+ const startTime = Date.now();
118
+
119
+ try {
120
+ const globals = createSandboxGlobals(input, logs);
121
+ const context = vm.createContext(globals, {
122
+ codeGeneration: { strings: false, wasm: false },
123
+ });
124
+
125
+ // Wrap code so the last expression is returned
126
+ const wrapped = `(function() {\n${code}\n})()`;
127
+ const script = new vm.Script(wrapped, { filename: 'sandbox.js' });
128
+ const result = script.runInContext(context, { timeout: timeoutMs });
129
+ const durationMs = Date.now() - startTime;
130
+
131
+ const serialized = serializeResult(result);
132
+ if (serialized.length > MAX_OUTPUT_SIZE) {
133
+ return {
134
+ result: serialized.slice(0, 1000) + '... [truncated]',
135
+ logs,
136
+ duration_ms: durationMs,
137
+ warning: 'Output exceeded 10MB limit and was truncated',
138
+ };
139
+ }
140
+
141
+ // Parse back to preserve types (arrays, objects)
142
+ let parsed: unknown;
143
+ try {
144
+ parsed = JSON.parse(serialized);
145
+ } catch {
146
+ parsed = serialized === 'undefined' ? undefined : serialized;
147
+ }
148
+
149
+ return { result: parsed, logs, duration_ms: durationMs };
150
+ } catch (err: any) {
151
+ const durationMs = Date.now() - startTime;
152
+ const message = err.message || String(err);
153
+
154
+ // Provide helpful error context
155
+ if (message.includes('Script execution timed out')) {
156
+ return { error: `Execution timed out after ${timeoutMs}ms. Keep code efficient or increase timeout (max ${MAX_TIMEOUT}ms).`, logs, duration_ms: durationMs };
157
+ }
158
+ if (message.includes('Code generation from strings disallowed')) {
159
+ return { error: 'eval() and Function() constructor are blocked in the sandbox. Use direct code instead.', logs, duration_ms: durationMs };
160
+ }
161
+
162
+ return { error: message, logs, duration_ms: durationMs };
163
+ }
164
+ },
165
+ });
166
+
167
+ // ── eval_expression ──
168
+ api.registerTool({
169
+ name: 'eval_expression',
170
+ description: [
171
+ 'Evaluate a single JavaScript expression and return the result.',
172
+ 'Lightweight alternative to execute_js for quick calculations.',
173
+ 'Examples: "15 * 4500 * 0.01", "new Date().toISOString()", "[1,2,3].map(x => x*x)".',
174
+ ].join(' '),
175
+ parameters: {
176
+ type: 'object',
177
+ required: ['expression'],
178
+ properties: {
179
+ expression: { type: 'string', description: 'A JavaScript expression to evaluate.' },
180
+ },
181
+ },
182
+ handler: async ({ expression }: { expression: string }) => {
183
+ if (!expression || typeof expression !== 'string') {
184
+ return { error: 'expression parameter is required and must be a string' };
185
+ }
186
+
187
+ try {
188
+ const globals = createSandboxGlobals(undefined, []);
189
+ const context = vm.createContext(globals, {
190
+ codeGeneration: { strings: false, wasm: false },
191
+ });
192
+
193
+ const script = new vm.Script(`(${expression})`, { filename: 'expr.js' });
194
+ const result = script.runInContext(context, { timeout: DEFAULT_TIMEOUT });
195
+
196
+ let serialized: unknown;
197
+ try {
198
+ serialized = JSON.parse(JSON.stringify(result));
199
+ } catch {
200
+ serialized = String(result);
201
+ }
202
+
203
+ return {
204
+ result: serialized,
205
+ type: result === null ? 'null' : Array.isArray(result) ? 'array' : typeof result,
206
+ };
207
+ } catch (err: any) {
208
+ return { error: err.message || String(err) };
209
+ }
210
+ },
211
+ });
212
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: creative
3
+ description: Creative deliverable tools for AI agents
4
+ ---
5
+ ## Creative Deliverables
6
+
7
+ You have powerful creative capabilities for delivering job results:
8
+
9
+ **Text & Documents:**
10
+ - `store_deliverable` with content_type "text/markdown" — rich Markdown (default)
11
+ - `store_deliverable` with content_type "application/pdf" — write Markdown, auto-generates PDF
12
+ - Use ![alt text](https://image-url) to embed images — they are downloaded and embedded in the PDF
13
+ - Write CLEAN Markdown only — no HTML tags, no <cite> tags, no raw HTML
14
+ - `store_deliverable` with content_type "text/csv" — structured data
15
+
16
+ **Images (AI-generated) — IMPORTANT:**
17
+ - Call `generate_image` with prompt AND job_id — it generates, uploads to IPFS, and returns evidence_uri in ONE step
18
+ - Then just call `xpr_deliver_job` with the evidence_uri
19
+ - Do NOT write markdown descriptions of images — generate the actual image!
20
+
21
+ **Video (AI-generated):**
22
+ - Call `generate_video` with prompt AND job_id — generates, uploads to IPFS, returns evidence_uri
23
+ - Then call `xpr_deliver_job` with the evidence_uri
24
+
25
+ **Images/Media from the web:**
26
+ - Use `web_search` to find suitable content, then `store_deliverable` with source_url
27
+
28
+ **Code repositories:**
29
+ - `create_github_repo` with all source files — creates a public GitHub repo
30
+
31
+ NEVER say you can't create images or videos — you have the tools!
32
+ NEVER deliver just a URL or summary — always include the actual work content.
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "creative",
3
+ "version": "1.0.0",
4
+ "description": "AI image/video generation, PDF creation, GitHub repo delivery, and IPFS storage for job deliverables",
5
+ "author": "xpr-agents",
6
+ "category": "ai",
7
+ "tags": ["image", "video", "pdf", "github", "ipfs", "deliverables"],
8
+ "capabilities": ["image-generation", "video-generation", "pdf-generation", "github-repos", "ipfs-storage"],
9
+ "tools": ["store_deliverable", "create_github_repo", "generate_image", "generate_video"],
10
+ "requires": {
11
+ "env": []
12
+ }
13
+ }