@clawnch/clawtomaton 0.2.0 → 0.2.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 +7 -5
- package/dist/cli.js +58 -1
- package/dist/cli.js.map +1 -1
- package/dist/identity/index.d.ts +11 -1
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/identity/index.js +33 -1
- package/dist/identity/index.js.map +1 -1
- package/dist/skills/clawnx.js +1 -1
- package/dist/skills/clawnx.js.map +1 -1
- package/dist/skills/conway.d.ts +16 -8
- package/dist/skills/conway.d.ts.map +1 -1
- package/dist/skills/conway.js +698 -151
- package/dist/skills/conway.js.map +1 -1
- package/dist/skills/deploy.d.ts.map +1 -1
- package/dist/skills/deploy.js +5 -4
- package/dist/skills/deploy.js.map +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -1
- package/package.json +1 -1
package/dist/skills/conway.js
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Skill: conway — Conway Terminal integration
|
|
2
|
+
* Skill: conway — Full Conway Terminal integration.
|
|
3
3
|
*
|
|
4
|
-
* Conway Terminal
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Covers all Conway Terminal v2.0.9 MCP tools:
|
|
5
|
+
* - Sandbox lifecycle (create, list, get, delete)
|
|
6
|
+
* - Sandbox execution (exec, write_file, read_file)
|
|
7
|
+
* - Sandbox networking (expose_port, list_ports, remove_port, get_url)
|
|
8
|
+
* - Sandbox custom domains (add_domain, list_domains, remove_domain)
|
|
9
|
+
* - Sandbox monitoring (terminal_session, metrics, activity, commands)
|
|
10
|
+
* - PTY sessions (create, write, read, close, resize, list)
|
|
11
|
+
* - Wallet & x402 payments (wallet_info, wallet_networks, x402_discover, x402_check, x402_fetch)
|
|
12
|
+
* - Domain registration & DNS (search, list, info, register, renew, dns_*, pricing, check, privacy, nameservers)
|
|
13
|
+
* - Credits & billing (balance, history, pricing, topup)
|
|
14
|
+
* - Inference (chat_completions)
|
|
8
15
|
*
|
|
9
16
|
* Authentication:
|
|
10
|
-
* - Sandbox/Cloud API: CONWAY_API_KEY (from ~/.conway/config.json or env)
|
|
11
|
-
* - Domain API: SIWE via conway-terminal wallet (~/.
|
|
17
|
+
* - Sandbox/Cloud/Credits/Inference API: CONWAY_API_KEY (from ~/.conway/config.json or env)
|
|
18
|
+
* - Domain API: SIWE via conway-terminal wallet (~/.conway/wallet.json)
|
|
19
|
+
* - x402 payments: via conway-terminal wallet + x402 protocol
|
|
12
20
|
*
|
|
13
21
|
* This is optional infrastructure — agents don't need it to function,
|
|
14
|
-
* but it lets them build real web presence and
|
|
15
|
-
*/
|
|
16
|
-
const CONWAY_API_URL = 'https://api.conway.tech';
|
|
17
|
-
const CONWAY_DOMAIN_API_URL = 'https://api.conway.domains';
|
|
18
|
-
/**
|
|
19
|
-
* Make an authenticated request to the Conway Cloud/Sandbox API.
|
|
22
|
+
* but it lets them build real web presence, run compute, and pay for services.
|
|
20
23
|
*/
|
|
24
|
+
const CONWAY_API_URL = process.env.CONWAY_API_URL || 'https://api.conway.tech';
|
|
25
|
+
const CONWAY_DOMAIN_API_URL = process.env.CONWAY_DOMAIN_API_URL || 'https://api.conway.domains';
|
|
26
|
+
const CONWAY_PREVIEW_DOMAIN = process.env.CONWAY_PREVIEW_DOMAIN || 'life.conway.tech';
|
|
27
|
+
// ─── Conway Cloud/Sandbox API ────────────────────────────────────────────────
|
|
21
28
|
async function conwayApiRequest(apiKey, method, path, body) {
|
|
22
29
|
const response = await fetch(`${CONWAY_API_URL}${path}`, {
|
|
23
30
|
method,
|
|
@@ -33,12 +40,17 @@ async function conwayApiRequest(apiKey, method, path, body) {
|
|
|
33
40
|
}
|
|
34
41
|
return response.json();
|
|
35
42
|
}
|
|
43
|
+
// ─── Conway Domain API (SIWE auth) ──────────────────────────────────────────
|
|
44
|
+
let domainJwt = null;
|
|
45
|
+
let domainJwtExpiry = 0;
|
|
36
46
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
47
|
+
* Get a JWT for the Conway Domain API via SIWE auth.
|
|
48
|
+
* Caches the token for 50 minutes (tokens expire at 60).
|
|
39
49
|
*/
|
|
40
|
-
async function
|
|
41
|
-
|
|
50
|
+
async function getDomainJwt() {
|
|
51
|
+
if (domainJwt && Date.now() < domainJwtExpiry) {
|
|
52
|
+
return domainJwt;
|
|
53
|
+
}
|
|
42
54
|
let conway;
|
|
43
55
|
try {
|
|
44
56
|
// @ts-ignore — conway-terminal is an optional dependency
|
|
@@ -48,14 +60,13 @@ async function conwayDomainRequest(method, path, body) {
|
|
|
48
60
|
throw new Error('conway-terminal package not installed. Run: npm install conway-terminal\n' +
|
|
49
61
|
'Then run: npx conway-terminal --init to create wallet + API key');
|
|
50
62
|
}
|
|
51
|
-
// Get or create Conway wallet
|
|
52
63
|
const { account } = await conway.getWallet();
|
|
53
|
-
//
|
|
64
|
+
// 1. Get nonce
|
|
54
65
|
const nonceResp = await fetch(`${CONWAY_DOMAIN_API_URL}/auth/nonce`, { method: 'POST' });
|
|
55
66
|
if (!nonceResp.ok)
|
|
56
67
|
throw new Error(`Domain auth: failed to get nonce (${nonceResp.status})`);
|
|
57
68
|
const { nonce } = (await nonceResp.json());
|
|
58
|
-
//
|
|
69
|
+
// 2. Sign SIWE message
|
|
59
70
|
// @ts-expect-error — siwe is a transitive dependency of conway-terminal
|
|
60
71
|
const { SiweMessage } = await import('siwe');
|
|
61
72
|
const url = new URL(CONWAY_DOMAIN_API_URL);
|
|
@@ -71,6 +82,7 @@ async function conwayDomainRequest(method, path, body) {
|
|
|
71
82
|
});
|
|
72
83
|
const messageString = siweMessage.prepareMessage();
|
|
73
84
|
const signature = await account.signMessage({ message: messageString });
|
|
85
|
+
// 3. Verify → get JWT
|
|
74
86
|
const verifyResp = await fetch(`${CONWAY_DOMAIN_API_URL}/auth/verify`, {
|
|
75
87
|
method: 'POST',
|
|
76
88
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -81,11 +93,16 @@ async function conwayDomainRequest(method, path, body) {
|
|
|
81
93
|
const { access_token } = (await verifyResp.json());
|
|
82
94
|
if (!access_token)
|
|
83
95
|
throw new Error('Domain auth: no access token returned');
|
|
84
|
-
|
|
96
|
+
domainJwt = access_token;
|
|
97
|
+
domainJwtExpiry = Date.now() + 50 * 60 * 1000;
|
|
98
|
+
return access_token;
|
|
99
|
+
}
|
|
100
|
+
async function conwayDomainRequest(method, path, body) {
|
|
101
|
+
const jwt = await getDomainJwt();
|
|
85
102
|
const response = await fetch(`${CONWAY_DOMAIN_API_URL}${path}`, {
|
|
86
103
|
method,
|
|
87
104
|
headers: {
|
|
88
|
-
Authorization: `Bearer ${
|
|
105
|
+
Authorization: `Bearer ${jwt}`,
|
|
89
106
|
'Content-Type': 'application/json',
|
|
90
107
|
},
|
|
91
108
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -96,35 +113,154 @@ async function conwayDomainRequest(method, path, body) {
|
|
|
96
113
|
}
|
|
97
114
|
return response.json();
|
|
98
115
|
}
|
|
116
|
+
// ─── x402 helpers ────────────────────────────────────────────────────────────
|
|
117
|
+
async function getConwayModule() {
|
|
118
|
+
try {
|
|
119
|
+
// @ts-ignore — conway-terminal is an optional dependency
|
|
120
|
+
return await import('conway-terminal');
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new Error('conway-terminal package not installed. Run: npm install conway-terminal\n' +
|
|
124
|
+
'Then run: npx conway-terminal --init to create wallet + API key');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function x402DomainFetch(path, method, body) {
|
|
128
|
+
const conway = await getConwayModule();
|
|
129
|
+
const { account } = await conway.getWallet();
|
|
130
|
+
const jwt = await getDomainJwt();
|
|
131
|
+
const url = `${CONWAY_DOMAIN_API_URL}${path}`;
|
|
132
|
+
const response = await conway.x402Fetch(account, url, {
|
|
133
|
+
method,
|
|
134
|
+
headers: {
|
|
135
|
+
Authorization: `Bearer ${jwt}`,
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
},
|
|
138
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
139
|
+
});
|
|
140
|
+
const data = await response.json();
|
|
141
|
+
return data;
|
|
142
|
+
}
|
|
143
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
144
|
+
function json(data) {
|
|
145
|
+
return JSON.stringify(data, null, 2);
|
|
146
|
+
}
|
|
147
|
+
function ok(data) {
|
|
148
|
+
return { callId: '', success: true, result: json(data) };
|
|
149
|
+
}
|
|
150
|
+
function fail(error) {
|
|
151
|
+
return { callId: '', success: false, result: null, error };
|
|
152
|
+
}
|
|
153
|
+
function requireApiKey() {
|
|
154
|
+
const apiKey = process.env.CONWAY_API_KEY;
|
|
155
|
+
if (!apiKey) {
|
|
156
|
+
throw new Error('CONWAY_API_KEY not set. Get one at https://app.conway.tech or run: npx conway-terminal --provision');
|
|
157
|
+
}
|
|
158
|
+
return apiKey;
|
|
159
|
+
}
|
|
160
|
+
function requireParam(params, name) {
|
|
161
|
+
const val = params[name];
|
|
162
|
+
if (!val && val !== 0)
|
|
163
|
+
throw new Error(`Required parameter "${name}" is missing`);
|
|
164
|
+
return String(val);
|
|
165
|
+
}
|
|
166
|
+
// ─── All actions by category ─────────────────────────────────────────────────
|
|
167
|
+
const ALL_ACTIONS = [
|
|
168
|
+
// Sandbox lifecycle
|
|
169
|
+
'sandbox_create', 'sandbox_list', 'sandbox_get', 'sandbox_delete',
|
|
170
|
+
// Sandbox execution
|
|
171
|
+
'sandbox_exec', 'sandbox_write_file', 'sandbox_read_file',
|
|
172
|
+
// Sandbox networking
|
|
173
|
+
'sandbox_expose_port', 'sandbox_list_ports', 'sandbox_remove_port', 'sandbox_get_url',
|
|
174
|
+
// Sandbox custom domains
|
|
175
|
+
'sandbox_add_domain', 'sandbox_list_domains', 'sandbox_remove_domain',
|
|
176
|
+
// Sandbox monitoring
|
|
177
|
+
'sandbox_terminal_session', 'sandbox_metrics', 'sandbox_activity', 'sandbox_commands',
|
|
178
|
+
// PTY sessions
|
|
179
|
+
'sandbox_pty_create', 'sandbox_pty_write', 'sandbox_pty_read',
|
|
180
|
+
'sandbox_pty_close', 'sandbox_pty_resize', 'sandbox_pty_list',
|
|
181
|
+
// Wallet & x402
|
|
182
|
+
'wallet_info', 'wallet_networks', 'x402_discover', 'x402_check', 'x402_fetch',
|
|
183
|
+
// Domains
|
|
184
|
+
'domain_search', 'domain_list', 'domain_info', 'domain_register', 'domain_renew',
|
|
185
|
+
'domain_dns_list', 'domain_dns_add', 'domain_dns_update', 'domain_dns_delete',
|
|
186
|
+
'domain_pricing', 'domain_check', 'domain_privacy', 'domain_nameservers',
|
|
187
|
+
// Credits
|
|
188
|
+
'credits_balance', 'credits_history', 'credits_pricing', 'credits_topup',
|
|
189
|
+
// Inference
|
|
190
|
+
'chat_completions',
|
|
191
|
+
];
|
|
192
|
+
// ─── Skill definition ────────────────────────────────────────────────────────
|
|
99
193
|
export const conwaySkill = {
|
|
100
194
|
name: 'conway',
|
|
101
|
-
description: 'Conway Terminal:
|
|
102
|
-
'Sandbox actions need CONWAY_API_KEY. Domain actions need conway-terminal
|
|
195
|
+
description: 'Conway Terminal: full cloud infrastructure, compute, wallet, payments, domains, and deployment. ' +
|
|
196
|
+
'Sandbox actions (sandbox_*) need CONWAY_API_KEY. Domain actions (domain_*) need conway-terminal installed. ' +
|
|
197
|
+
'Wallet/x402 actions need conway-terminal installed. Credits actions need CONWAY_API_KEY. ' +
|
|
198
|
+
`Actions: ${ALL_ACTIONS.join(', ')}.`,
|
|
103
199
|
parameters: [
|
|
104
200
|
{
|
|
105
201
|
name: 'action',
|
|
106
202
|
type: 'string',
|
|
107
|
-
description:
|
|
108
|
-
'"domain_search", "domain_register", "domain_list", "domain_info", ' +
|
|
109
|
-
'"domain_dns_list", "domain_dns_add", "domain_check", "credits_balance".',
|
|
203
|
+
description: `Action to perform. One of: ${ALL_ACTIONS.join(', ')}.`,
|
|
110
204
|
required: true,
|
|
111
205
|
},
|
|
112
206
|
{
|
|
113
207
|
name: 'sandbox_id',
|
|
114
208
|
type: 'string',
|
|
115
|
-
description: 'Sandbox ID (32-char hex). Required for
|
|
209
|
+
description: 'Sandbox ID (32-char hex). Required for most sandbox_* and sandbox_pty_* actions.',
|
|
210
|
+
required: false,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'session_id',
|
|
214
|
+
type: 'string',
|
|
215
|
+
description: 'PTY session ID. Required for sandbox_pty_write, sandbox_pty_read, sandbox_pty_close, sandbox_pty_resize.',
|
|
116
216
|
required: false,
|
|
117
217
|
},
|
|
118
218
|
{
|
|
119
219
|
name: 'command',
|
|
120
220
|
type: 'string',
|
|
121
|
-
description: 'Shell command (for sandbox_exec).',
|
|
221
|
+
description: 'Shell command (for sandbox_exec) or PTY command (for sandbox_pty_create, e.g. "bash", "python3").',
|
|
222
|
+
required: false,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'path',
|
|
226
|
+
type: 'string',
|
|
227
|
+
description: 'Absolute file path in sandbox (for sandbox_write_file, sandbox_read_file).',
|
|
228
|
+
required: false,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'content',
|
|
232
|
+
type: 'string',
|
|
233
|
+
description: 'File content (for sandbox_write_file). Text or base64-encoded binary.',
|
|
234
|
+
required: false,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'encoding',
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'Content encoding for sandbox_write_file: omit for text, "base64" for binary.',
|
|
240
|
+
required: false,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'port',
|
|
244
|
+
type: 'string',
|
|
245
|
+
description: 'Port number (for sandbox_expose_port, sandbox_remove_port, sandbox_get_url).',
|
|
246
|
+
required: false,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'visibility',
|
|
250
|
+
type: 'string',
|
|
251
|
+
description: 'Port visibility for sandbox_expose_port: "public" or "private" (default: "public").',
|
|
252
|
+
required: false,
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'subdomain',
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'Custom subdomain for sandbox_expose_port (e.g. "my-app" -> my-app.life.conway.tech).',
|
|
122
258
|
required: false,
|
|
123
259
|
},
|
|
124
260
|
{
|
|
125
261
|
name: 'domain',
|
|
126
262
|
type: 'string',
|
|
127
|
-
description: 'Domain name (for
|
|
263
|
+
description: 'Domain name (for domain_* actions and sandbox_add_domain/sandbox_remove_domain).',
|
|
128
264
|
required: false,
|
|
129
265
|
},
|
|
130
266
|
{
|
|
@@ -133,140 +269,551 @@ export const conwaySkill = {
|
|
|
133
269
|
description: 'Search keyword (for domain_search).',
|
|
134
270
|
required: false,
|
|
135
271
|
},
|
|
272
|
+
{
|
|
273
|
+
name: 'tlds',
|
|
274
|
+
type: 'string',
|
|
275
|
+
description: 'Comma-separated TLDs for domain_search or domain_pricing (e.g. "com,io,ai").',
|
|
276
|
+
required: false,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'domains',
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Comma-separated domain names for domain_check (e.g. "example.com,test.io").',
|
|
282
|
+
required: false,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'years',
|
|
286
|
+
type: 'string',
|
|
287
|
+
description: 'Registration/renewal period in years (1-10) for domain_register, domain_renew.',
|
|
288
|
+
required: false,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'privacy',
|
|
292
|
+
type: 'string',
|
|
293
|
+
description: 'WHOIS privacy: "true" or "false" (for domain_register, domain_privacy).',
|
|
294
|
+
required: false,
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: 'nameservers',
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'Comma-separated nameservers for domain_nameservers (e.g. "ns1.example.com,ns2.example.com").',
|
|
300
|
+
required: false,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'record_id',
|
|
304
|
+
type: 'string',
|
|
305
|
+
description: 'DNS record ID (for domain_dns_update, domain_dns_delete).',
|
|
306
|
+
required: false,
|
|
307
|
+
},
|
|
136
308
|
{
|
|
137
309
|
name: 'spec',
|
|
138
310
|
type: 'string',
|
|
139
|
-
description: 'JSON config
|
|
311
|
+
description: 'JSON config. For sandbox_create: {"name":"my-vm","vcpu":1,"memory_mb":512,"disk_gb":5,"region":"us-east"}. ' +
|
|
312
|
+
'For domain_dns_add: {"type":"A","host":"@","value":"1.2.3.4","ttl":3600}. ' +
|
|
313
|
+
'For domain_dns_update: {"host":"www","value":"1.2.3.4","ttl":3600}. ' +
|
|
314
|
+
'For chat_completions: {"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}],"temperature":1,"max_tokens":1000}.',
|
|
315
|
+
required: false,
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'url',
|
|
319
|
+
type: 'string',
|
|
320
|
+
description: 'URL (for x402_discover, x402_check, x402_fetch).',
|
|
321
|
+
required: false,
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'method',
|
|
325
|
+
type: 'string',
|
|
326
|
+
description: 'HTTP method for x402_fetch (default: "GET").',
|
|
327
|
+
required: false,
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'headers',
|
|
331
|
+
type: 'string',
|
|
332
|
+
description: 'JSON headers for x402_fetch (e.g. {"X-Custom":"value"}).',
|
|
333
|
+
required: false,
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'body',
|
|
337
|
+
type: 'string',
|
|
338
|
+
description: 'Request body for x402_fetch (POST/PUT/PATCH).',
|
|
339
|
+
required: false,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'network',
|
|
343
|
+
type: 'string',
|
|
344
|
+
description: 'Network ID for wallet_info: "eip155:8453" (Base) or "eip155:84532" (Base Sepolia). Default: "eip155:8453".',
|
|
345
|
+
required: false,
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: 'amount_usd',
|
|
349
|
+
type: 'string',
|
|
350
|
+
description: 'Credit top-up amount in USD tier units for credits_topup. Valid tiers: 5, 25, 100, 500, 1000, 2500.',
|
|
351
|
+
required: false,
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: 'address',
|
|
355
|
+
type: 'string',
|
|
356
|
+
description: 'Wallet address to credit for credits_topup. Defaults to local wallet.',
|
|
357
|
+
required: false,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'limit',
|
|
361
|
+
type: 'string',
|
|
362
|
+
description: 'Max entries to return (for credits_history, sandbox_activity, sandbox_commands).',
|
|
363
|
+
required: false,
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: 'offset',
|
|
367
|
+
type: 'string',
|
|
368
|
+
description: 'Pagination offset (for credits_history).',
|
|
369
|
+
required: false,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'timeout',
|
|
373
|
+
type: 'string',
|
|
374
|
+
description: 'Timeout in seconds for sandbox_exec (default: 30).',
|
|
375
|
+
required: false,
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: 'cols',
|
|
379
|
+
type: 'string',
|
|
380
|
+
description: 'Terminal columns for sandbox_pty_create or sandbox_pty_resize (default: 80).',
|
|
381
|
+
required: false,
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: 'rows',
|
|
385
|
+
type: 'string',
|
|
386
|
+
description: 'Terminal rows for sandbox_pty_create or sandbox_pty_resize (default: 24).',
|
|
387
|
+
required: false,
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: 'input',
|
|
391
|
+
type: 'string',
|
|
392
|
+
description: 'Input to send to PTY session (for sandbox_pty_write). Use "\\n" for Enter.',
|
|
393
|
+
required: false,
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: 'full',
|
|
397
|
+
type: 'string',
|
|
398
|
+
description: 'If "true", return full scrollback buffer for sandbox_pty_read.',
|
|
140
399
|
required: false,
|
|
141
400
|
},
|
|
142
401
|
],
|
|
143
|
-
execute: async (params
|
|
402
|
+
execute: async (params) => {
|
|
144
403
|
try {
|
|
145
404
|
const action = params.action;
|
|
146
|
-
//
|
|
147
|
-
if (action
|
|
148
|
-
const apiKey =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
405
|
+
// ─── Sandbox lifecycle ──────────────────────────────────────────
|
|
406
|
+
if (action === 'sandbox_create') {
|
|
407
|
+
const apiKey = requireApiKey();
|
|
408
|
+
const spec = params.spec ? JSON.parse(params.spec) : {};
|
|
409
|
+
const result = await conwayApiRequest(apiKey, 'POST', '/v1/sandboxes', {
|
|
410
|
+
name: spec.name || `sandbox-${Date.now()}`,
|
|
411
|
+
vcpu: spec.vcpu ?? 1,
|
|
412
|
+
memory_mb: spec.memory_mb ?? 512,
|
|
413
|
+
disk_gb: spec.disk_gb ?? 5,
|
|
414
|
+
region: spec.region ?? 'eu-north',
|
|
415
|
+
});
|
|
416
|
+
return ok(result);
|
|
417
|
+
}
|
|
418
|
+
if (action === 'sandbox_list') {
|
|
419
|
+
const apiKey = requireApiKey();
|
|
420
|
+
const result = await conwayApiRequest(apiKey, 'GET', '/v1/sandboxes');
|
|
421
|
+
return ok(result);
|
|
422
|
+
}
|
|
423
|
+
if (action === 'sandbox_get') {
|
|
424
|
+
const apiKey = requireApiKey();
|
|
425
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
426
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}`);
|
|
427
|
+
return ok(result);
|
|
428
|
+
}
|
|
429
|
+
if (action === 'sandbox_delete') {
|
|
430
|
+
const apiKey = requireApiKey();
|
|
431
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
432
|
+
await conwayApiRequest(apiKey, 'DELETE', `/v1/sandboxes/${sandboxId}`);
|
|
433
|
+
return ok({ success: true, message: `Sandbox ${sandboxId} deleted` });
|
|
434
|
+
}
|
|
435
|
+
// ─── Sandbox execution ──────────────────────────────────────────
|
|
436
|
+
if (action === 'sandbox_exec') {
|
|
437
|
+
const apiKey = requireApiKey();
|
|
438
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
439
|
+
const command = requireParam(params, 'command');
|
|
440
|
+
const timeout = params.timeout ? parseInt(params.timeout) : 30;
|
|
441
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/exec`, {
|
|
442
|
+
command,
|
|
443
|
+
timeout,
|
|
444
|
+
});
|
|
445
|
+
return ok(result);
|
|
446
|
+
}
|
|
447
|
+
if (action === 'sandbox_write_file') {
|
|
448
|
+
const apiKey = requireApiKey();
|
|
449
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
450
|
+
const filePath = requireParam(params, 'path');
|
|
451
|
+
const content = requireParam(params, 'content');
|
|
452
|
+
const encoding = params.encoding;
|
|
453
|
+
const MAX_UPLOAD = 10 * 1024 * 1024; // 10MB
|
|
454
|
+
const contentBytes = encoding === 'base64'
|
|
455
|
+
? Math.ceil(content.length * 3 / 4)
|
|
456
|
+
: new TextEncoder().encode(content).length;
|
|
457
|
+
if (contentBytes > MAX_UPLOAD) {
|
|
458
|
+
return fail(`Content too large: ${contentBytes} bytes exceeds ${MAX_UPLOAD} byte limit`);
|
|
459
|
+
}
|
|
460
|
+
const body = { path: filePath, content };
|
|
461
|
+
if (encoding)
|
|
462
|
+
body.encoding = encoding;
|
|
463
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/files/upload/json`, body);
|
|
464
|
+
return ok(result);
|
|
465
|
+
}
|
|
466
|
+
if (action === 'sandbox_read_file') {
|
|
467
|
+
const apiKey = requireApiKey();
|
|
468
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
469
|
+
const filePath = requireParam(params, 'path');
|
|
470
|
+
const result = (await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/exec`, {
|
|
471
|
+
command: `cat ${filePath}`,
|
|
472
|
+
timeout: 10,
|
|
473
|
+
}));
|
|
474
|
+
if (result.exit_code !== 0) {
|
|
475
|
+
return fail(`Failed to read file: ${result.stderr}`);
|
|
476
|
+
}
|
|
477
|
+
return ok({ content: result.stdout, path: filePath });
|
|
478
|
+
}
|
|
479
|
+
// ─── Sandbox networking ─────────────────────────────────────────
|
|
480
|
+
if (action === 'sandbox_expose_port') {
|
|
481
|
+
const apiKey = requireApiKey();
|
|
482
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
483
|
+
const port = parseInt(requireParam(params, 'port'));
|
|
484
|
+
const body = {
|
|
485
|
+
port,
|
|
486
|
+
visibility: params.visibility || 'public',
|
|
487
|
+
};
|
|
488
|
+
if (params.subdomain)
|
|
489
|
+
body.subdomain = params.subdomain;
|
|
490
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/ports/expose`, body);
|
|
491
|
+
return ok(result);
|
|
492
|
+
}
|
|
493
|
+
if (action === 'sandbox_list_ports') {
|
|
494
|
+
const apiKey = requireApiKey();
|
|
495
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
496
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/ports`);
|
|
497
|
+
return ok(result);
|
|
498
|
+
}
|
|
499
|
+
if (action === 'sandbox_remove_port') {
|
|
500
|
+
const apiKey = requireApiKey();
|
|
501
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
502
|
+
const port = requireParam(params, 'port');
|
|
503
|
+
const result = await conwayApiRequest(apiKey, 'DELETE', `/v1/sandboxes/${sandboxId}/ports/${port}`);
|
|
504
|
+
return ok(result);
|
|
505
|
+
}
|
|
506
|
+
if (action === 'sandbox_get_url') {
|
|
507
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
508
|
+
const port = requireParam(params, 'port');
|
|
509
|
+
const url = `https://${port}-${sandboxId}.${CONWAY_PREVIEW_DOMAIN}`;
|
|
510
|
+
return ok({ url });
|
|
511
|
+
}
|
|
512
|
+
// ─── Sandbox custom domains ─────────────────────────────────────
|
|
513
|
+
if (action === 'sandbox_add_domain') {
|
|
514
|
+
const apiKey = requireApiKey();
|
|
515
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
516
|
+
const domain = requireParam(params, 'domain');
|
|
517
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/domains`, { domain });
|
|
518
|
+
return ok(result);
|
|
519
|
+
}
|
|
520
|
+
if (action === 'sandbox_list_domains') {
|
|
521
|
+
const apiKey = requireApiKey();
|
|
522
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
523
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/domains`);
|
|
524
|
+
return ok(result);
|
|
525
|
+
}
|
|
526
|
+
if (action === 'sandbox_remove_domain') {
|
|
527
|
+
const apiKey = requireApiKey();
|
|
528
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
529
|
+
const domain = requireParam(params, 'domain');
|
|
530
|
+
const result = await conwayApiRequest(apiKey, 'DELETE', `/v1/sandboxes/${sandboxId}/domains/${encodeURIComponent(domain)}`);
|
|
531
|
+
return ok(result);
|
|
532
|
+
}
|
|
533
|
+
// ─── Sandbox monitoring ─────────────────────────────────────────
|
|
534
|
+
if (action === 'sandbox_terminal_session') {
|
|
535
|
+
const apiKey = requireApiKey();
|
|
536
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
537
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/terminal-session`);
|
|
538
|
+
return ok(result);
|
|
539
|
+
}
|
|
540
|
+
if (action === 'sandbox_metrics') {
|
|
541
|
+
const apiKey = requireApiKey();
|
|
542
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
543
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/metrics`);
|
|
544
|
+
return ok(result);
|
|
545
|
+
}
|
|
546
|
+
if (action === 'sandbox_activity') {
|
|
547
|
+
const apiKey = requireApiKey();
|
|
548
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
549
|
+
const limit = params.limit ? parseInt(params.limit) : 50;
|
|
550
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/activity?limit=${limit}`);
|
|
551
|
+
return ok(result);
|
|
552
|
+
}
|
|
553
|
+
if (action === 'sandbox_commands') {
|
|
554
|
+
const apiKey = requireApiKey();
|
|
555
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
556
|
+
const limit = params.limit ? parseInt(params.limit) : 50;
|
|
557
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/commands?limit=${limit}`);
|
|
558
|
+
return ok(result);
|
|
559
|
+
}
|
|
560
|
+
// ─── PTY sessions ──────────────────────────────────────────────
|
|
561
|
+
if (action === 'sandbox_pty_create') {
|
|
562
|
+
const apiKey = requireApiKey();
|
|
563
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
564
|
+
const command = requireParam(params, 'command');
|
|
565
|
+
const cols = params.cols ? parseInt(params.cols) : 80;
|
|
566
|
+
const rows = params.rows ? parseInt(params.rows) : 24;
|
|
567
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/pty`, {
|
|
568
|
+
command,
|
|
569
|
+
cols,
|
|
570
|
+
rows,
|
|
571
|
+
});
|
|
572
|
+
return ok(result);
|
|
573
|
+
}
|
|
574
|
+
if (action === 'sandbox_pty_write') {
|
|
575
|
+
const apiKey = requireApiKey();
|
|
576
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
577
|
+
const sessionId = requireParam(params, 'session_id');
|
|
578
|
+
const input = requireParam(params, 'input');
|
|
579
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/pty/${sessionId}/write`, { input });
|
|
580
|
+
return ok(result);
|
|
581
|
+
}
|
|
582
|
+
if (action === 'sandbox_pty_read') {
|
|
583
|
+
const apiKey = requireApiKey();
|
|
584
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
585
|
+
const sessionId = requireParam(params, 'session_id');
|
|
586
|
+
const full = params.full === 'true' ? '?full=true' : '';
|
|
587
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/pty/${sessionId}/read${full}`);
|
|
588
|
+
return ok(result);
|
|
589
|
+
}
|
|
590
|
+
if (action === 'sandbox_pty_close') {
|
|
591
|
+
const apiKey = requireApiKey();
|
|
592
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
593
|
+
const sessionId = requireParam(params, 'session_id');
|
|
594
|
+
const result = await conwayApiRequest(apiKey, 'DELETE', `/v1/sandboxes/${sandboxId}/pty/${sessionId}`);
|
|
595
|
+
return ok(result);
|
|
596
|
+
}
|
|
597
|
+
if (action === 'sandbox_pty_resize') {
|
|
598
|
+
const apiKey = requireApiKey();
|
|
599
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
600
|
+
const sessionId = requireParam(params, 'session_id');
|
|
601
|
+
const cols = parseInt(requireParam(params, 'cols'));
|
|
602
|
+
const rows = parseInt(requireParam(params, 'rows'));
|
|
603
|
+
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/pty/${sessionId}/resize`, { cols, rows });
|
|
604
|
+
return ok(result);
|
|
605
|
+
}
|
|
606
|
+
if (action === 'sandbox_pty_list') {
|
|
607
|
+
const apiKey = requireApiKey();
|
|
608
|
+
const sandboxId = requireParam(params, 'sandbox_id');
|
|
609
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/sandboxes/${sandboxId}/pty`);
|
|
610
|
+
return ok(result);
|
|
611
|
+
}
|
|
612
|
+
// ─── Wallet & x402 ─────────────────────────────────────────────
|
|
613
|
+
if (action === 'wallet_info') {
|
|
614
|
+
const conway = await getConwayModule();
|
|
615
|
+
const { account, isNew } = await conway.getWallet();
|
|
616
|
+
const network = params.network || 'eip155:8453';
|
|
617
|
+
try {
|
|
618
|
+
const balance = await conway.getUsdcBalance(account.address, network);
|
|
619
|
+
return ok({
|
|
620
|
+
address: account.address,
|
|
621
|
+
isNew,
|
|
622
|
+
network,
|
|
623
|
+
balance: { usdc: balance, formatted: `$${balance.toFixed(2)} USDC` },
|
|
624
|
+
configDir: conway.getConfigDir(),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
return ok({
|
|
629
|
+
address: account.address,
|
|
630
|
+
isNew,
|
|
631
|
+
network,
|
|
632
|
+
error: error.message || String(error),
|
|
633
|
+
configDir: conway.getConfigDir(),
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (action === 'wallet_networks') {
|
|
638
|
+
const conway = await getConwayModule();
|
|
639
|
+
const networks = Object.entries(conway.SUPPORTED_NETWORKS).map(([id, config]) => ({
|
|
640
|
+
id,
|
|
641
|
+
name: config.name,
|
|
642
|
+
chainId: config.chain.id,
|
|
643
|
+
usdcAddress: config.usdcAddress,
|
|
644
|
+
}));
|
|
645
|
+
return ok({ networks });
|
|
646
|
+
}
|
|
647
|
+
if (action === 'x402_discover') {
|
|
648
|
+
const conway = await getConwayModule();
|
|
649
|
+
const url = requireParam(params, 'url');
|
|
650
|
+
const result = await conway.discoverX402Endpoints(url);
|
|
651
|
+
return ok(result);
|
|
652
|
+
}
|
|
653
|
+
if (action === 'x402_check') {
|
|
654
|
+
const conway = await getConwayModule();
|
|
655
|
+
const url = requireParam(params, 'url');
|
|
656
|
+
const method = params.method || undefined;
|
|
657
|
+
const result = await conway.checkX402Endpoint(url, method);
|
|
658
|
+
return ok(result);
|
|
659
|
+
}
|
|
660
|
+
if (action === 'x402_fetch') {
|
|
661
|
+
const conway = await getConwayModule();
|
|
662
|
+
const { account } = await conway.getWallet();
|
|
663
|
+
const url = requireParam(params, 'url');
|
|
664
|
+
const fetchOptions = {};
|
|
665
|
+
if (params.method)
|
|
666
|
+
fetchOptions.method = params.method;
|
|
667
|
+
if (params.headers)
|
|
668
|
+
fetchOptions.headers = JSON.parse(params.headers);
|
|
669
|
+
if (params.body)
|
|
670
|
+
fetchOptions.body = params.body;
|
|
671
|
+
const response = await conway.x402Fetch(account, url, fetchOptions);
|
|
672
|
+
const responseBody = await response.text();
|
|
673
|
+
return ok({
|
|
674
|
+
status: response.status,
|
|
675
|
+
statusText: response.statusText,
|
|
676
|
+
body: responseBody,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
// ─── Domains ────────────────────────────────────────────────────
|
|
680
|
+
if (action === 'domain_search') {
|
|
681
|
+
const query = requireParam(params, 'query');
|
|
682
|
+
const tlds = params.tlds || 'com,io,ai,xyz,net,org,dev';
|
|
683
|
+
const result = await conwayDomainRequest('GET', `/domains/search?q=${encodeURIComponent(query)}&tlds=${encodeURIComponent(tlds)}`);
|
|
684
|
+
return ok(result);
|
|
685
|
+
}
|
|
686
|
+
if (action === 'domain_list') {
|
|
687
|
+
const result = await conwayDomainRequest('GET', '/domains');
|
|
688
|
+
return ok(result);
|
|
689
|
+
}
|
|
690
|
+
if (action === 'domain_info') {
|
|
691
|
+
const domain = requireParam(params, 'domain');
|
|
692
|
+
const result = await conwayDomainRequest('GET', `/domains/${encodeURIComponent(domain)}`);
|
|
693
|
+
return ok(result);
|
|
694
|
+
}
|
|
695
|
+
if (action === 'domain_register') {
|
|
696
|
+
const domain = requireParam(params, 'domain');
|
|
697
|
+
const years = params.years ? parseInt(params.years) : 1;
|
|
698
|
+
const privacy = params.privacy !== 'false';
|
|
699
|
+
const result = await x402DomainFetch('/domains/register', 'POST', { domain, years, privacy });
|
|
700
|
+
return ok(result);
|
|
701
|
+
}
|
|
702
|
+
if (action === 'domain_renew') {
|
|
703
|
+
const domain = requireParam(params, 'domain');
|
|
704
|
+
const years = params.years ? parseInt(params.years) : 1;
|
|
705
|
+
const result = await x402DomainFetch(`/domains/${encodeURIComponent(domain)}/renew`, 'POST', { years });
|
|
706
|
+
return ok(result);
|
|
707
|
+
}
|
|
708
|
+
if (action === 'domain_dns_list') {
|
|
709
|
+
const domain = requireParam(params, 'domain');
|
|
710
|
+
const result = await conwayDomainRequest('GET', `/domains/${encodeURIComponent(domain)}/dns`);
|
|
711
|
+
return ok(result);
|
|
712
|
+
}
|
|
713
|
+
if (action === 'domain_dns_add') {
|
|
714
|
+
const domain = requireParam(params, 'domain');
|
|
715
|
+
const spec = params.spec ? JSON.parse(params.spec) : {};
|
|
716
|
+
const result = await conwayDomainRequest('POST', `/domains/${encodeURIComponent(domain)}/dns`, spec);
|
|
717
|
+
return ok(result);
|
|
718
|
+
}
|
|
719
|
+
if (action === 'domain_dns_update') {
|
|
720
|
+
const domain = requireParam(params, 'domain');
|
|
721
|
+
const recordId = requireParam(params, 'record_id');
|
|
722
|
+
const spec = params.spec ? JSON.parse(params.spec) : {};
|
|
723
|
+
const result = await conwayDomainRequest('PUT', `/domains/${encodeURIComponent(domain)}/dns/${encodeURIComponent(recordId)}`, spec);
|
|
724
|
+
return ok(result);
|
|
725
|
+
}
|
|
726
|
+
if (action === 'domain_dns_delete') {
|
|
727
|
+
const domain = requireParam(params, 'domain');
|
|
728
|
+
const recordId = requireParam(params, 'record_id');
|
|
729
|
+
const result = await conwayDomainRequest('DELETE', `/domains/${encodeURIComponent(domain)}/dns/${encodeURIComponent(recordId)}`);
|
|
730
|
+
return ok(result);
|
|
731
|
+
}
|
|
732
|
+
if (action === 'domain_pricing') {
|
|
733
|
+
const tlds = params.tlds ? `?tlds=${encodeURIComponent(params.tlds)}` : '';
|
|
734
|
+
const result = await conwayDomainRequest('GET', `/domains/pricing${tlds}`);
|
|
735
|
+
return ok(result);
|
|
736
|
+
}
|
|
737
|
+
if (action === 'domain_check') {
|
|
738
|
+
const domains = requireParam(params, 'domains');
|
|
739
|
+
const result = await conwayDomainRequest('GET', `/domains/check?domains=${encodeURIComponent(domains)}`);
|
|
740
|
+
return ok(result);
|
|
741
|
+
}
|
|
742
|
+
if (action === 'domain_privacy') {
|
|
743
|
+
const domain = requireParam(params, 'domain');
|
|
744
|
+
const enabled = params.privacy === 'true';
|
|
745
|
+
const result = await conwayDomainRequest('PUT', `/domains/${encodeURIComponent(domain)}/privacy`, { enabled });
|
|
746
|
+
return ok(result);
|
|
747
|
+
}
|
|
748
|
+
if (action === 'domain_nameservers') {
|
|
749
|
+
const domain = requireParam(params, 'domain');
|
|
750
|
+
const nsString = requireParam(params, 'nameservers');
|
|
751
|
+
const nameservers = nsString.split(',').map((s) => s.trim());
|
|
752
|
+
const result = await conwayDomainRequest('PUT', `/domains/${encodeURIComponent(domain)}/nameservers`, { nameservers });
|
|
753
|
+
return ok(result);
|
|
754
|
+
}
|
|
755
|
+
// ─── Credits ────────────────────────────────────────────────────
|
|
756
|
+
if (action === 'credits_balance') {
|
|
757
|
+
const apiKey = requireApiKey();
|
|
758
|
+
const result = await conwayApiRequest(apiKey, 'GET', '/v1/credits/balance');
|
|
759
|
+
return ok(result);
|
|
760
|
+
}
|
|
761
|
+
if (action === 'credits_history') {
|
|
762
|
+
const apiKey = requireApiKey();
|
|
763
|
+
const limit = params.limit ? parseInt(params.limit) : 20;
|
|
764
|
+
const offset = params.offset ? parseInt(params.offset) : 0;
|
|
765
|
+
const result = await conwayApiRequest(apiKey, 'GET', `/v1/credits/history?limit=${limit}&offset=${offset}`);
|
|
766
|
+
return ok(result);
|
|
767
|
+
}
|
|
768
|
+
if (action === 'credits_pricing') {
|
|
769
|
+
const apiKey = requireApiKey();
|
|
770
|
+
const result = await conwayApiRequest(apiKey, 'GET', '/v1/credits/pricing');
|
|
771
|
+
return ok(result);
|
|
772
|
+
}
|
|
773
|
+
if (action === 'credits_topup') {
|
|
774
|
+
const conway = await getConwayModule();
|
|
775
|
+
const { account } = await conway.getWallet();
|
|
776
|
+
const amountUsd = parseInt(requireParam(params, 'amount_usd'));
|
|
777
|
+
const validTiers = [5, 25, 100, 500, 1000, 2500];
|
|
778
|
+
if (!validTiers.includes(amountUsd)) {
|
|
779
|
+
return fail(`amount_usd must be one of: ${validTiers.join(', ')}`);
|
|
780
|
+
}
|
|
781
|
+
const recipientAddress = params.address || account.address;
|
|
782
|
+
const url = `${CONWAY_API_URL}/pay/${amountUsd}/${recipientAddress}`;
|
|
783
|
+
const response = await conway.x402Fetch(account, url, { method: 'GET' });
|
|
784
|
+
const raw = await response.text();
|
|
785
|
+
if (!response.ok) {
|
|
786
|
+
return fail(`Credits top-up failed (${response.status}): ${raw}`);
|
|
787
|
+
}
|
|
788
|
+
let parsed;
|
|
789
|
+
try {
|
|
790
|
+
parsed = JSON.parse(raw);
|
|
156
791
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const spec = params.spec ? JSON.parse(params.spec) : {};
|
|
160
|
-
const result = await conwayApiRequest(apiKey, 'POST', '/v1/sandboxes', {
|
|
161
|
-
name: spec.name,
|
|
162
|
-
vcpu: spec.vcpu ?? 1,
|
|
163
|
-
memory_mb: spec.memory_mb ?? 512,
|
|
164
|
-
disk_gb: spec.disk_gb ?? 5,
|
|
165
|
-
region: spec.region ?? 'us-east',
|
|
166
|
-
});
|
|
167
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
168
|
-
}
|
|
169
|
-
case 'sandbox_list': {
|
|
170
|
-
const result = await conwayApiRequest(apiKey, 'GET', '/v1/sandboxes');
|
|
171
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
172
|
-
}
|
|
173
|
-
case 'sandbox_exec': {
|
|
174
|
-
const sandboxId = params.sandbox_id;
|
|
175
|
-
const command = params.command;
|
|
176
|
-
if (!sandboxId || !command) {
|
|
177
|
-
return { callId: '', success: false, result: null, error: 'sandbox_id and command are required for sandbox_exec' };
|
|
178
|
-
}
|
|
179
|
-
const result = await conwayApiRequest(apiKey, 'POST', `/v1/sandboxes/${sandboxId}/exec`, {
|
|
180
|
-
command,
|
|
181
|
-
timeout: 30,
|
|
182
|
-
});
|
|
183
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
184
|
-
}
|
|
185
|
-
case 'sandbox_delete': {
|
|
186
|
-
const sandboxId = params.sandbox_id;
|
|
187
|
-
if (!sandboxId) {
|
|
188
|
-
return { callId: '', success: false, result: null, error: 'sandbox_id is required for sandbox_delete' };
|
|
189
|
-
}
|
|
190
|
-
const result = await conwayApiRequest(apiKey, 'DELETE', `/v1/sandboxes/${sandboxId}`);
|
|
191
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
192
|
-
}
|
|
193
|
-
case 'credits_balance': {
|
|
194
|
-
const result = await conwayApiRequest(apiKey, 'GET', '/v1/credits/balance');
|
|
195
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
196
|
-
}
|
|
197
|
-
default:
|
|
198
|
-
return { callId: '', success: false, result: null, error: `Unknown sandbox action: "${action}"` };
|
|
792
|
+
catch {
|
|
793
|
+
parsed = { raw_response: raw };
|
|
199
794
|
}
|
|
795
|
+
return ok({ ...parsed, amount_usd: amountUsd, recipient_address: recipientAddress });
|
|
200
796
|
}
|
|
201
|
-
//
|
|
202
|
-
if (action
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return { callId: '', success: false, result: null, error: 'query is required for domain_search' };
|
|
208
|
-
}
|
|
209
|
-
const result = await conwayDomainRequest('GET', `/domains/search?q=${encodeURIComponent(query)}`);
|
|
210
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
211
|
-
}
|
|
212
|
-
case 'domain_list': {
|
|
213
|
-
const result = await conwayDomainRequest('GET', '/domains');
|
|
214
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
215
|
-
}
|
|
216
|
-
case 'domain_info': {
|
|
217
|
-
const domain = params.domain;
|
|
218
|
-
if (!domain) {
|
|
219
|
-
return { callId: '', success: false, result: null, error: 'domain is required for domain_info' };
|
|
220
|
-
}
|
|
221
|
-
const result = await conwayDomainRequest('GET', `/domains/${encodeURIComponent(domain)}`);
|
|
222
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
223
|
-
}
|
|
224
|
-
case 'domain_check': {
|
|
225
|
-
const domain = params.domain;
|
|
226
|
-
if (!domain) {
|
|
227
|
-
return { callId: '', success: false, result: null, error: 'domain is required for domain_check (comma-separated for multiple)' };
|
|
228
|
-
}
|
|
229
|
-
const result = await conwayDomainRequest('GET', `/domains/check?domains=${encodeURIComponent(domain)}`);
|
|
230
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
231
|
-
}
|
|
232
|
-
case 'domain_register': {
|
|
233
|
-
const domain = params.domain;
|
|
234
|
-
if (!domain) {
|
|
235
|
-
return { callId: '', success: false, result: null, error: 'domain is required for domain_register' };
|
|
236
|
-
}
|
|
237
|
-
// Domain registration uses x402 payment — requires USDC in Conway wallet
|
|
238
|
-
// For now use the JWT-authenticated endpoint; the x402 payment is handled server-side
|
|
239
|
-
const result = await conwayDomainRequest('POST', '/domains/register', { domain });
|
|
240
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
241
|
-
}
|
|
242
|
-
case 'domain_dns_list': {
|
|
243
|
-
const domain = params.domain;
|
|
244
|
-
if (!domain) {
|
|
245
|
-
return { callId: '', success: false, result: null, error: 'domain is required for domain_dns_list' };
|
|
246
|
-
}
|
|
247
|
-
const result = await conwayDomainRequest('GET', `/domains/${encodeURIComponent(domain)}/dns`);
|
|
248
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
249
|
-
}
|
|
250
|
-
case 'domain_dns_add': {
|
|
251
|
-
const domain = params.domain;
|
|
252
|
-
const spec = params.spec;
|
|
253
|
-
if (!domain || !spec) {
|
|
254
|
-
return { callId: '', success: false, result: null, error: 'domain and spec (DNS record JSON) are required for domain_dns_add' };
|
|
255
|
-
}
|
|
256
|
-
const record = JSON.parse(spec);
|
|
257
|
-
const result = await conwayDomainRequest('POST', `/domains/${encodeURIComponent(domain)}/dns`, record);
|
|
258
|
-
return { callId: '', success: true, result: JSON.stringify(result, null, 2) };
|
|
259
|
-
}
|
|
260
|
-
default:
|
|
261
|
-
return { callId: '', success: false, result: null, error: `Unknown domain action: "${action}". Valid domain actions: domain_search, domain_list, domain_info, domain_check, domain_register, domain_dns_list, domain_dns_add.` };
|
|
797
|
+
// ─── Inference ──────────────────────────────────────────────────
|
|
798
|
+
if (action === 'chat_completions') {
|
|
799
|
+
const apiKey = requireApiKey();
|
|
800
|
+
const spec = params.spec ? JSON.parse(params.spec) : {};
|
|
801
|
+
if (!spec.model || !spec.messages) {
|
|
802
|
+
return fail('chat_completions requires spec with "model" and "messages" fields');
|
|
262
803
|
}
|
|
804
|
+
const body = {
|
|
805
|
+
model: spec.model,
|
|
806
|
+
messages: spec.messages,
|
|
807
|
+
};
|
|
808
|
+
if (spec.temperature !== undefined)
|
|
809
|
+
body.temperature = spec.temperature;
|
|
810
|
+
if (spec.max_tokens !== undefined)
|
|
811
|
+
body.max_tokens = spec.max_tokens;
|
|
812
|
+
const result = await conwayApiRequest(apiKey, 'POST', '/v1/chat/completions', body);
|
|
813
|
+
return ok(result);
|
|
263
814
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
success: false,
|
|
267
|
-
result: null,
|
|
268
|
-
error: `Unknown Conway action: "${action}". Use sandbox_* or domain_* actions.`,
|
|
269
|
-
};
|
|
815
|
+
// ─── Unknown action ─────────────────────────────────────────────
|
|
816
|
+
return fail(`Unknown Conway action: "${action}". Valid actions: ${ALL_ACTIONS.join(', ')}.`);
|
|
270
817
|
}
|
|
271
818
|
catch (err) {
|
|
272
819
|
return { callId: '', success: false, result: null, error: err instanceof Error ? err.message : String(err) };
|