@xpr-agents/openclaw 0.3.0 → 0.3.2

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.
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 — 55 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 for AI assistants to autonomously manage agents, jobs, feedback, validations, and escrow on-chain.
4
4
 
5
5
  ## XPR Agents Ecosystem
6
6
 
@@ -8,7 +8,7 @@ OpenClaw plugin for the XPR Network Trustless Agent Registry — 55 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) | 55 MCP tools for AI assistants |
11
+ | [`@xpr-agents/openclaw`](https://www.npmjs.com/package/@xpr-agents/openclaw) | 72 MCP tools for AI assistants |
12
12
 
13
13
  ## Quick Start
14
14
 
@@ -42,16 +42,31 @@ cd my-agent
42
42
 
43
43
  ## Configuration
44
44
 
45
- Set environment variables for server-side signing:
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:
46
+
47
+ ```bash
48
+ # One-time keychain setup
49
+ npm i -g @proton/cli
50
+ proton chain:set proton # or proton-test
51
+ proton key:add # paste your PVT_K1_ key; stored encrypted
52
+ # Or for hosted consoles without a real TTY:
53
+ # echo "no" | proton key:add PVT_K1_yourkey
54
+ ```
46
55
 
47
56
  ```env
48
57
  XPR_ACCOUNT=myagent
49
- XPR_PRIVATE_KEY=PVT_K1_...
50
- XPR_RPC_ENDPOINT=https://tn1.protonnz.com
51
- XPR_NETWORK=testnet
52
- MAX_TRANSFER_AMOUNT=10000000
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.
65
+ # A2A_SIGNING_KEY=PVT_K1_a2a_only_key
53
66
  ```
54
67
 
68
+ > **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.
69
+
55
70
  ## License
56
71
 
57
72
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpr-agents/openclaw",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
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": {
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * Code Sandbox Skill — execute JavaScript in a sandboxed V8 context
4
+ *
5
+ * Zero external dependencies — uses Node.js built-in `vm` module.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.default = codeSandboxSkill;
12
+ const vm_1 = __importDefault(require("vm"));
13
+ // ── Constants ───────────────────────────────────
14
+ const DEFAULT_TIMEOUT = 5000;
15
+ const MAX_TIMEOUT = 30000;
16
+ const MAX_OUTPUT_SIZE = 10 * 1024 * 1024; // 10MB
17
+ // ── Sandbox helpers ─────────────────────────────
18
+ function createSandboxGlobals(input, logs) {
19
+ // Capture console methods
20
+ const consoleMock = {
21
+ log: (...args) => {
22
+ logs.push(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
23
+ },
24
+ warn: (...args) => {
25
+ logs.push('[warn] ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
26
+ },
27
+ error: (...args) => {
28
+ logs.push('[error] ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '));
29
+ },
30
+ };
31
+ return {
32
+ INPUT: input,
33
+ console: consoleMock,
34
+ JSON,
35
+ Math,
36
+ Date,
37
+ Array,
38
+ Object,
39
+ String,
40
+ Number,
41
+ RegExp,
42
+ Map,
43
+ Set,
44
+ parseInt,
45
+ parseFloat,
46
+ isNaN,
47
+ isFinite,
48
+ encodeURIComponent,
49
+ decodeURIComponent,
50
+ atob: (s) => Buffer.from(s, 'base64').toString('binary'),
51
+ btoa: (s) => Buffer.from(s, 'binary').toString('base64'),
52
+ // Explicitly undefined — blocked
53
+ require: undefined,
54
+ process: undefined,
55
+ globalThis: undefined,
56
+ global: undefined,
57
+ };
58
+ }
59
+ function serializeResult(value) {
60
+ if (value === undefined)
61
+ return 'undefined';
62
+ try {
63
+ const str = JSON.stringify(value, null, 2);
64
+ if (str.length > MAX_OUTPUT_SIZE) {
65
+ return str.slice(0, MAX_OUTPUT_SIZE) + '\n... [truncated at 10MB]';
66
+ }
67
+ return str;
68
+ }
69
+ catch {
70
+ return String(value);
71
+ }
72
+ }
73
+ // ── Skill entry point ───────────────────────────
74
+ function codeSandboxSkill(api) {
75
+ // ── execute_js ──
76
+ api.registerTool({
77
+ name: 'execute_js',
78
+ description: [
79
+ 'Run JavaScript code in a sandboxed V8 context.',
80
+ 'Pass data via "input" (JSON), access it as INPUT in code.',
81
+ 'console.log/warn/error are captured in the "logs" array.',
82
+ 'Available: JSON, Math, Date, Array, Object, String, Number, RegExp, Map, Set,',
83
+ 'parseInt, parseFloat, isNaN, isFinite, encodeURIComponent, decodeURIComponent, atob, btoa.',
84
+ 'No network, filesystem, require, or import access. Max 30s timeout, 10MB output.',
85
+ ].join(' '),
86
+ parameters: {
87
+ type: 'object',
88
+ required: ['code'],
89
+ properties: {
90
+ code: { type: 'string', description: 'JavaScript code to execute. The last expression value is returned as the result.' },
91
+ input: { description: 'Optional JSON data available as INPUT in the code.' },
92
+ timeout: { type: 'number', description: 'Execution timeout in milliseconds (default 5000, max 30000).' },
93
+ },
94
+ },
95
+ handler: async ({ code, input, timeout }) => {
96
+ if (!code || typeof code !== 'string') {
97
+ return { error: 'code parameter is required and must be a string' };
98
+ }
99
+ const timeoutMs = Math.min(Math.max(timeout || DEFAULT_TIMEOUT, 100), MAX_TIMEOUT);
100
+ const logs = [];
101
+ const startTime = Date.now();
102
+ try {
103
+ const globals = createSandboxGlobals(input, logs);
104
+ const context = vm_1.default.createContext(globals, {
105
+ codeGeneration: { strings: false, wasm: false },
106
+ });
107
+ // Wrap code so the last expression is returned
108
+ const wrapped = `(function() {\n${code}\n})()`;
109
+ const script = new vm_1.default.Script(wrapped, { filename: 'sandbox.js' });
110
+ const result = script.runInContext(context, { timeout: timeoutMs });
111
+ const durationMs = Date.now() - startTime;
112
+ const serialized = serializeResult(result);
113
+ if (serialized.length > MAX_OUTPUT_SIZE) {
114
+ return {
115
+ result: serialized.slice(0, 1000) + '... [truncated]',
116
+ logs,
117
+ duration_ms: durationMs,
118
+ warning: 'Output exceeded 10MB limit and was truncated',
119
+ };
120
+ }
121
+ // Parse back to preserve types (arrays, objects)
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(serialized);
125
+ }
126
+ catch {
127
+ parsed = serialized === 'undefined' ? undefined : serialized;
128
+ }
129
+ return { result: parsed, logs, duration_ms: durationMs };
130
+ }
131
+ catch (err) {
132
+ const durationMs = Date.now() - startTime;
133
+ const message = err.message || String(err);
134
+ // Provide helpful error context
135
+ if (message.includes('Script execution timed out')) {
136
+ return { error: `Execution timed out after ${timeoutMs}ms. Keep code efficient or increase timeout (max ${MAX_TIMEOUT}ms).`, logs, duration_ms: durationMs };
137
+ }
138
+ if (message.includes('Code generation from strings disallowed')) {
139
+ return { error: 'eval() and Function() constructor are blocked in the sandbox. Use direct code instead.', logs, duration_ms: durationMs };
140
+ }
141
+ return { error: message, logs, duration_ms: durationMs };
142
+ }
143
+ },
144
+ });
145
+ // ── eval_expression ──
146
+ api.registerTool({
147
+ name: 'eval_expression',
148
+ description: [
149
+ 'Evaluate a single JavaScript expression and return the result.',
150
+ 'Lightweight alternative to execute_js for quick calculations.',
151
+ 'Examples: "15 * 4500 * 0.01", "new Date().toISOString()", "[1,2,3].map(x => x*x)".',
152
+ ].join(' '),
153
+ parameters: {
154
+ type: 'object',
155
+ required: ['expression'],
156
+ properties: {
157
+ expression: { type: 'string', description: 'A JavaScript expression to evaluate.' },
158
+ },
159
+ },
160
+ handler: async ({ expression }) => {
161
+ if (!expression || typeof expression !== 'string') {
162
+ return { error: 'expression parameter is required and must be a string' };
163
+ }
164
+ try {
165
+ const globals = createSandboxGlobals(undefined, []);
166
+ const context = vm_1.default.createContext(globals, {
167
+ codeGeneration: { strings: false, wasm: false },
168
+ });
169
+ const script = new vm_1.default.Script(`(${expression})`, { filename: 'expr.js' });
170
+ const result = script.runInContext(context, { timeout: DEFAULT_TIMEOUT });
171
+ let serialized;
172
+ try {
173
+ serialized = JSON.parse(JSON.stringify(result));
174
+ }
175
+ catch {
176
+ serialized = String(result);
177
+ }
178
+ return {
179
+ result: serialized,
180
+ type: result === null ? 'null' : Array.isArray(result) ? 'array' : typeof result,
181
+ };
182
+ }
183
+ catch (err) {
184
+ return { error: err.message || String(err) };
185
+ }
186
+ },
187
+ });
188
+ }