@darwinso/cli 0.1.0 → 0.4.0
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 +40 -17
- package/dist/index.js +1086 -52
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
5
|
import { dirname, join } from 'node:path';
|
|
5
|
-
const defaultBaseUrl = 'https://api.darwin.so/api/
|
|
6
|
+
const defaultBaseUrl = 'https://api.darwin.so/api/v2';
|
|
6
7
|
const configRoot = process.env.XDG_CONFIG_HOME?.trim() || join(homedir(), '.config');
|
|
7
8
|
const configPath = join(configRoot, 'darwin', 'config.json');
|
|
8
9
|
class DarwinApiError extends Error {
|
|
@@ -25,9 +26,11 @@ async function readConfig() {
|
|
|
25
26
|
}
|
|
26
27
|
async function credentials() {
|
|
27
28
|
const config = await readConfig();
|
|
29
|
+
const baseUrl = process.env.DARWIN_API_URL?.trim() || config.baseUrl?.trim() || defaultBaseUrl;
|
|
28
30
|
return {
|
|
29
31
|
apiKey: process.env.DARWIN_API_KEY?.trim() || config.apiKey?.trim(),
|
|
30
|
-
baseUrl
|
|
32
|
+
baseUrl,
|
|
33
|
+
v2BaseUrl: /\/v2\/?$/.test(baseUrl) ? baseUrl : baseUrl.replace(/\/v1\/?$/, '/v2'),
|
|
31
34
|
};
|
|
32
35
|
}
|
|
33
36
|
async function request(apiKey, baseUrl, path, options = {}) {
|
|
@@ -42,7 +45,11 @@ async function request(apiKey, baseUrl, path, options = {}) {
|
|
|
42
45
|
headers: {
|
|
43
46
|
Accept: 'application/json',
|
|
44
47
|
Authorization: `Bearer ${apiKey}`,
|
|
48
|
+
'X-Darwin-Access-Point': 'cli',
|
|
49
|
+
'X-Darwin-Client': '@darwinso/cli',
|
|
50
|
+
'X-Darwin-Client-Version': '0.4.0',
|
|
45
51
|
...(options.body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
52
|
+
...options.headers,
|
|
46
53
|
},
|
|
47
54
|
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
48
55
|
});
|
|
@@ -82,6 +89,30 @@ function integerOption(args, name) {
|
|
|
82
89
|
}
|
|
83
90
|
return parsed;
|
|
84
91
|
}
|
|
92
|
+
function repeatedOption(args, name) {
|
|
93
|
+
const values = [];
|
|
94
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
95
|
+
if (args[index] !== name)
|
|
96
|
+
continue;
|
|
97
|
+
const value = args[index + 1];
|
|
98
|
+
if (!value || value.startsWith('--')) {
|
|
99
|
+
throw new Error(`${name} requires a value.`);
|
|
100
|
+
}
|
|
101
|
+
values.push(value);
|
|
102
|
+
index += 1;
|
|
103
|
+
}
|
|
104
|
+
return values;
|
|
105
|
+
}
|
|
106
|
+
function booleanOption(args, name) {
|
|
107
|
+
const value = option(args, name);
|
|
108
|
+
if (value === undefined)
|
|
109
|
+
return undefined;
|
|
110
|
+
if (value === 'true')
|
|
111
|
+
return true;
|
|
112
|
+
if (value === 'false')
|
|
113
|
+
return false;
|
|
114
|
+
throw new Error(`${name} must be true or false.`);
|
|
115
|
+
}
|
|
85
116
|
function jsonObjectOption(args, name) {
|
|
86
117
|
const value = option(args, name);
|
|
87
118
|
if (!value)
|
|
@@ -98,6 +129,126 @@ function jsonObjectOption(args, name) {
|
|
|
98
129
|
}
|
|
99
130
|
return parsed;
|
|
100
131
|
}
|
|
132
|
+
async function jsonObjectFileOption(args, name) {
|
|
133
|
+
const path = option(args, name);
|
|
134
|
+
if (!path)
|
|
135
|
+
return undefined;
|
|
136
|
+
let parsed;
|
|
137
|
+
try {
|
|
138
|
+
parsed = JSON.parse(await readFile(path, 'utf8'));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
throw new Error(`${name} must reference a readable JSON file.`);
|
|
142
|
+
}
|
|
143
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
144
|
+
throw new Error(`${name} must contain a JSON object.`);
|
|
145
|
+
}
|
|
146
|
+
return parsed;
|
|
147
|
+
}
|
|
148
|
+
function jsonArrayOption(args, name) {
|
|
149
|
+
const value = option(args, name);
|
|
150
|
+
if (!value)
|
|
151
|
+
return [];
|
|
152
|
+
let parsed;
|
|
153
|
+
try {
|
|
154
|
+
parsed = JSON.parse(value);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
throw new Error(`${name} must be valid JSON.`);
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(parsed)) {
|
|
160
|
+
throw new Error(`${name} must be a JSON array.`);
|
|
161
|
+
}
|
|
162
|
+
return parsed;
|
|
163
|
+
}
|
|
164
|
+
function dataOption(args) {
|
|
165
|
+
return args.includes('--data') ? jsonObjectOption(args, '--data') : {};
|
|
166
|
+
}
|
|
167
|
+
function requiredOption(args, name) {
|
|
168
|
+
const value = option(args, name)?.trim();
|
|
169
|
+
if (!value) {
|
|
170
|
+
throw new Error(`${name} is required.`);
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
function enumOption(args, name, values) {
|
|
175
|
+
const value = option(args, name);
|
|
176
|
+
if (value === undefined)
|
|
177
|
+
return undefined;
|
|
178
|
+
const normalized = value.toUpperCase();
|
|
179
|
+
if (!values.includes(normalized)) {
|
|
180
|
+
throw new Error(`${name} must be one of: ${values.join(', ')}.`);
|
|
181
|
+
}
|
|
182
|
+
return normalized;
|
|
183
|
+
}
|
|
184
|
+
function lowerEnumOption(args, name, values) {
|
|
185
|
+
const value = option(args, name);
|
|
186
|
+
if (value === undefined)
|
|
187
|
+
return undefined;
|
|
188
|
+
const normalized = value.toLowerCase();
|
|
189
|
+
if (!values.includes(normalized)) {
|
|
190
|
+
throw new Error(`${name} must be one of: ${values.join(', ')}.`);
|
|
191
|
+
}
|
|
192
|
+
return normalized;
|
|
193
|
+
}
|
|
194
|
+
function withOptionalString(body, key, value) {
|
|
195
|
+
if (value !== undefined)
|
|
196
|
+
body[key] = value;
|
|
197
|
+
}
|
|
198
|
+
function idempotencyHeaders(args) {
|
|
199
|
+
const supplied = option(args, '--idempotency-key')?.trim();
|
|
200
|
+
if (supplied && supplied.length > 200) {
|
|
201
|
+
throw new Error('--idempotency-key must be at most 200 characters.');
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
'Idempotency-Key': supplied || randomUUID(),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function id(value, usage) {
|
|
208
|
+
const normalized = value?.trim();
|
|
209
|
+
if (!normalized) {
|
|
210
|
+
throw new Error(usage);
|
|
211
|
+
}
|
|
212
|
+
return encodeURIComponent(normalized);
|
|
213
|
+
}
|
|
214
|
+
function textArgument(args, start, valueOptions) {
|
|
215
|
+
const options = new Set(valueOptions);
|
|
216
|
+
const words = [];
|
|
217
|
+
for (let index = start; index < args.length; index += 1) {
|
|
218
|
+
const value = args[index];
|
|
219
|
+
if (!value)
|
|
220
|
+
continue;
|
|
221
|
+
if (options.has(value)) {
|
|
222
|
+
index += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (value.startsWith('--')) {
|
|
226
|
+
throw new Error(`Unknown option: ${value}`);
|
|
227
|
+
}
|
|
228
|
+
words.push(value);
|
|
229
|
+
}
|
|
230
|
+
return words.join(' ').trim();
|
|
231
|
+
}
|
|
232
|
+
function aiQuery(args) {
|
|
233
|
+
return { aiId: option(args, '--ai') };
|
|
234
|
+
}
|
|
235
|
+
function mergeFields(body, fields) {
|
|
236
|
+
for (const [key, value] of fields)
|
|
237
|
+
withOptionalString(body, key, value);
|
|
238
|
+
return body;
|
|
239
|
+
}
|
|
240
|
+
function normalizeCapabilitySelections(body) {
|
|
241
|
+
if (body.capabilitySelections === undefined && Array.isArray(body.listingSelections)) {
|
|
242
|
+
body.capabilitySelections = body.listingSelections.map((selection) => {
|
|
243
|
+
if (!selection || typeof selection !== 'object' || Array.isArray(selection))
|
|
244
|
+
return selection;
|
|
245
|
+
const { listingId, ...rest } = selection;
|
|
246
|
+
return { ...rest, capabilityId: listingId };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
delete body.listingSelections;
|
|
250
|
+
return body;
|
|
251
|
+
}
|
|
101
252
|
function redact(value) {
|
|
102
253
|
if (!value)
|
|
103
254
|
return null;
|
|
@@ -145,27 +296,60 @@ Usage:
|
|
|
145
296
|
darwin configure --api-key <key> [--base-url <url>]
|
|
146
297
|
darwin config show
|
|
147
298
|
darwin logout
|
|
148
|
-
darwin
|
|
149
|
-
darwin
|
|
150
|
-
darwin
|
|
151
|
-
darwin
|
|
152
|
-
darwin
|
|
153
|
-
darwin
|
|
154
|
-
darwin
|
|
155
|
-
darwin
|
|
156
|
-
darwin
|
|
157
|
-
darwin
|
|
299
|
+
darwin account show
|
|
300
|
+
darwin ais <list|get|create|update|activity> [...]
|
|
301
|
+
darwin ais skills
|
|
302
|
+
darwin ais integrations
|
|
303
|
+
darwin requests <list|action> [...]
|
|
304
|
+
darwin conversations <send|list|create|get|message> [...]
|
|
305
|
+
darwin goals <list|get|create|update|action|publish> [...]
|
|
306
|
+
darwin deals <list|get|create|update|action|payments> [...]
|
|
307
|
+
darwin transactions <list|get|action> [...]
|
|
308
|
+
darwin outcomes <list|get|evidence> [...]
|
|
309
|
+
darwin supply businesses <list|get|create|update> [...]
|
|
310
|
+
darwin host capabilities <list|get|create|update|archive> [...]
|
|
311
|
+
darwin supply orders <list|get> [...]
|
|
312
|
+
darwin supply earnings get <business-id>
|
|
313
|
+
darwin applications <list|get|create|update|archive|usage> [...]
|
|
314
|
+
darwin applications webhooks <list|create|revoke|deliveries|retry> [...]
|
|
158
315
|
|
|
159
316
|
Options:
|
|
317
|
+
--ai <id> Explicitly target an accessible AI
|
|
318
|
+
--data <json> Supply or extend a JSON request body
|
|
160
319
|
-h, --help Show help
|
|
161
320
|
-v, --version Show the installed version
|
|
162
321
|
|
|
163
322
|
Set DARWIN_API_KEY and optionally DARWIN_API_URL instead of storing local
|
|
164
323
|
configuration when running in CI.
|
|
324
|
+
|
|
325
|
+
Run "darwin <resource> --help" for examples in the documentation:
|
|
326
|
+
https://darwin.so/docs/cli
|
|
165
327
|
`);
|
|
166
328
|
}
|
|
167
329
|
async function main() {
|
|
168
|
-
|
|
330
|
+
let args = process.argv.slice(2);
|
|
331
|
+
// Keep the old command spelling as an input alias, but route and describe
|
|
332
|
+
// capabilities through Host from here on.
|
|
333
|
+
if (args[0] === 'supply' && args[1] === 'listings') {
|
|
334
|
+
args = ['host', 'capabilities', ...args.slice(2)];
|
|
335
|
+
}
|
|
336
|
+
if (args[0] === 'agents')
|
|
337
|
+
args[0] = 'ais';
|
|
338
|
+
if (args[0] === 'agent')
|
|
339
|
+
args[0] = 'ai';
|
|
340
|
+
args = args.map((value) => (value === '--agent' ? '--ai' : value === '--exclude-agent' ? '--exclude-ai' : value));
|
|
341
|
+
if (args[0] === 'applications' && args[1] === 'webhooks') {
|
|
342
|
+
const operation = args[2] === 'create'
|
|
343
|
+
? 'create-webhook'
|
|
344
|
+
: args[2] === 'revoke'
|
|
345
|
+
? 'revoke-webhook'
|
|
346
|
+
: args[2] === 'deliveries'
|
|
347
|
+
? 'webhook-deliveries'
|
|
348
|
+
: args[2] === 'retry'
|
|
349
|
+
? 'retry-webhook'
|
|
350
|
+
: 'webhooks';
|
|
351
|
+
args = ['applications', operation, ...args.slice(3)];
|
|
352
|
+
}
|
|
169
353
|
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
170
354
|
help();
|
|
171
355
|
return;
|
|
@@ -186,6 +370,9 @@ async function main() {
|
|
|
186
370
|
await logout();
|
|
187
371
|
return;
|
|
188
372
|
}
|
|
373
|
+
if (args[0] === 'account' && args[1] === 'show') {
|
|
374
|
+
args = ['account'];
|
|
375
|
+
}
|
|
189
376
|
const auth = await credentials();
|
|
190
377
|
if (!auth.apiKey) {
|
|
191
378
|
throw new Error('No API key found. Run darwin configure --api-key <key> or set DARWIN_API_KEY.');
|
|
@@ -193,94 +380,941 @@ async function main() {
|
|
|
193
380
|
const resource = args[0];
|
|
194
381
|
const operation = args[1];
|
|
195
382
|
let result;
|
|
196
|
-
if (resource === '
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
383
|
+
if (resource === 'account') {
|
|
384
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/account');
|
|
385
|
+
}
|
|
386
|
+
else if (resource === 'ais' && operation === 'skills') {
|
|
387
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/skills');
|
|
388
|
+
}
|
|
389
|
+
else if (resource === 'ais' && operation === 'integrations') {
|
|
390
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/integrations');
|
|
391
|
+
}
|
|
392
|
+
else if (resource === 'requests' && operation === 'list') {
|
|
393
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/requests', {
|
|
394
|
+
query: {
|
|
395
|
+
...aiQuery(args),
|
|
396
|
+
limit: integerOption(args, '--limit'),
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
else if (resource === 'requests' && operation === 'action') {
|
|
401
|
+
const action = args[3]?.toUpperCase();
|
|
402
|
+
if (action !== 'ACCEPT' && action !== 'DECLINE') {
|
|
403
|
+
throw new Error('Request action must be accept or decline.');
|
|
404
|
+
}
|
|
405
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/requests/${id(args[2], 'Pass a request ID after "darwin requests action".')}/${action.toLowerCase()}`, {
|
|
406
|
+
method: 'POST',
|
|
407
|
+
body: {
|
|
408
|
+
...(option(args, '--ai') ? { aiId: option(args, '--ai') } : {}),
|
|
409
|
+
},
|
|
410
|
+
headers: idempotencyHeaders(args),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
else if ((resource === 'ai' && operation === 'message') || (resource === 'conversations' && operation === 'send')) {
|
|
414
|
+
const content = textArgument(args, 2, ['--ai', '--request-id']);
|
|
200
415
|
if (!content) {
|
|
201
|
-
throw new Error('Pass a message after "darwin
|
|
416
|
+
throw new Error('Pass a message after "darwin conversations send".');
|
|
202
417
|
}
|
|
203
|
-
result = await request(auth.apiKey, auth.
|
|
418
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/ai/messages', {
|
|
204
419
|
method: 'POST',
|
|
205
420
|
body: {
|
|
206
421
|
content,
|
|
422
|
+
aiId: option(args, '--ai'),
|
|
207
423
|
requestId: option(args, '--request-id'),
|
|
208
424
|
},
|
|
209
425
|
});
|
|
210
426
|
}
|
|
211
427
|
else if (resource === 'conversation') {
|
|
212
|
-
result = await request(auth.apiKey, auth.
|
|
428
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/ai/conversation', {
|
|
213
429
|
query: {
|
|
430
|
+
...aiQuery(args),
|
|
214
431
|
limit: integerOption(args, '--limit'),
|
|
215
432
|
cursor: option(args, '--cursor'),
|
|
216
433
|
},
|
|
217
434
|
});
|
|
218
435
|
}
|
|
219
|
-
else if (resource === '
|
|
220
|
-
result = await request(auth.apiKey, auth.
|
|
436
|
+
else if (resource === 'ais' && operation === 'list') {
|
|
437
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/ais');
|
|
221
438
|
}
|
|
222
|
-
else if (resource === '
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
439
|
+
else if (resource === 'ais' && operation === 'get') {
|
|
440
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[2], 'Pass an AI ID after "darwin ais get".')}`);
|
|
441
|
+
}
|
|
442
|
+
else if (resource === 'ais' && operation === 'create') {
|
|
443
|
+
const body = mergeFields(dataOption(args), [
|
|
444
|
+
['name', option(args, '--name')],
|
|
445
|
+
['handle', option(args, '--handle')],
|
|
446
|
+
['avatarUrl', option(args, '--avatar-url')],
|
|
447
|
+
['description', option(args, '--description')],
|
|
448
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
449
|
+
]);
|
|
450
|
+
if (typeof body.name !== 'string' || !body.name.trim()) {
|
|
451
|
+
throw new Error('Pass an AI name with --name or in --data.');
|
|
226
452
|
}
|
|
227
|
-
result = await request(auth.apiKey, auth.
|
|
453
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/ais', { method: 'POST', body });
|
|
228
454
|
}
|
|
229
|
-
else if (resource === '
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
455
|
+
else if (resource === 'ais' && operation === 'update') {
|
|
456
|
+
const body = mergeFields(dataOption(args), [
|
|
457
|
+
['name', option(args, '--name')],
|
|
458
|
+
['handle', option(args, '--handle')],
|
|
459
|
+
['avatarUrl', option(args, '--avatar-url')],
|
|
460
|
+
['description', option(args, '--description')],
|
|
461
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
462
|
+
]);
|
|
463
|
+
if (Object.keys(body).length === 0) {
|
|
464
|
+
throw new Error('Pass at least one editable field or --data.');
|
|
233
465
|
}
|
|
234
|
-
result = await request(auth.apiKey, auth.
|
|
466
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[2], 'Pass an AI ID after "darwin ais update".')}`, { method: 'PATCH', body });
|
|
467
|
+
}
|
|
468
|
+
else if (resource === 'ais' && operation === 'activity') {
|
|
469
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[2], 'Pass an AI ID after "darwin ais activity".')}/activity`, {
|
|
470
|
+
query: {
|
|
471
|
+
limit: integerOption(args, '--limit'),
|
|
472
|
+
cursor: option(args, '--cursor'),
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else if (resource === 'ais' && operation === 'members') {
|
|
477
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID after "darwin ais members".')}/members`);
|
|
478
|
+
}
|
|
479
|
+
else if (resource === 'ais' && operation === 'invite') {
|
|
480
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID after "darwin ais invite".')}/invitations`, {
|
|
235
481
|
method: 'POST',
|
|
236
482
|
body: {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
kind: option(args, '--kind')?.toUpperCase(),
|
|
483
|
+
email: requiredOption(args, '--email'),
|
|
484
|
+
role: option(args, '--role'),
|
|
240
485
|
},
|
|
241
486
|
});
|
|
242
487
|
}
|
|
243
|
-
else if (resource === '
|
|
244
|
-
result = await request(auth.apiKey, auth.
|
|
245
|
-
|
|
488
|
+
else if (resource === 'ais' && operation === 'invitations') {
|
|
489
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID after "darwin ais invitations".')}/invitations`);
|
|
490
|
+
}
|
|
491
|
+
else if (resource === 'ais' && operation === 'revoke-invitation') {
|
|
492
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID.')}/invitations/${id(args[3], 'Pass an invitation ID.')}`, { method: 'DELETE' });
|
|
493
|
+
}
|
|
494
|
+
else if (resource === 'ais' && operation === 'update-member') {
|
|
495
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID.')}/members/${id(args[3], 'Pass a membership ID.')}`, {
|
|
496
|
+
method: 'PATCH',
|
|
497
|
+
body: { role: requiredOption(args, '--role') },
|
|
246
498
|
});
|
|
247
499
|
}
|
|
248
|
-
else if (resource === '
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
500
|
+
else if (resource === 'ais' && operation === 'remove-member') {
|
|
501
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID.')}/members/${id(args[3], 'Pass a membership ID.')}`, { method: 'DELETE' });
|
|
502
|
+
}
|
|
503
|
+
else if (resource === 'ais' && operation === 'policies') {
|
|
504
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID after "darwin ais policies".')}/access-policies`);
|
|
505
|
+
}
|
|
506
|
+
else if (resource === 'ais' && operation === 'create-policy') {
|
|
507
|
+
const body = mergeFields(dataOption(args), [
|
|
508
|
+
['name', option(args, '--name')],
|
|
509
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
510
|
+
['naturalLanguage', option(args, '--natural-language')],
|
|
511
|
+
]);
|
|
512
|
+
if (typeof body.name !== 'string' || typeof body.visibility !== 'string') {
|
|
513
|
+
throw new Error('Pass --name and --visibility, or provide both in --data.');
|
|
252
514
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
515
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID.')}/access-policies`, {
|
|
516
|
+
method: 'POST',
|
|
517
|
+
body,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
else if (resource === 'ais' && operation === 'update-policy') {
|
|
521
|
+
const body = mergeFields(dataOption(args), [
|
|
522
|
+
['name', option(args, '--name')],
|
|
523
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
524
|
+
['naturalLanguage', option(args, '--natural-language')],
|
|
525
|
+
]);
|
|
526
|
+
if (Object.keys(body).length === 0)
|
|
527
|
+
throw new Error('Pass editable policy fields or --data.');
|
|
528
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[2], 'Pass an AI ID.')}/access-policies/${id(args[3], 'Pass a policy ID.')}`, { method: 'PATCH', body });
|
|
529
|
+
}
|
|
530
|
+
else if (resource === 'ais' && operation === 'conversations') {
|
|
531
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/ais/${id(args[2], 'Pass an AI ID after "darwin ais conversations".')}/conversations`);
|
|
532
|
+
}
|
|
533
|
+
else if (resource === 'ais' && operation === 'create-conversation') {
|
|
534
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/ais/${id(args[2], 'Pass an AI ID after "darwin ais create-conversation".')}/conversations`, { method: 'POST' });
|
|
535
|
+
}
|
|
536
|
+
else if (resource === 'conversations' && operation === 'get') {
|
|
537
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/conversations/${id(args[2], 'Pass a conversation ID after "darwin conversations get".')}`, {
|
|
538
|
+
query: {
|
|
539
|
+
limit: integerOption(args, '--limit'),
|
|
540
|
+
cursor: option(args, '--cursor'),
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
else if (resource === 'conversations' && operation === 'message') {
|
|
545
|
+
const content = textArgument(args, 3, ['--request-id']);
|
|
546
|
+
if (!content)
|
|
547
|
+
throw new Error('Pass message text after the conversation ID.');
|
|
548
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/conversations/${id(args[2], 'Pass a conversation ID.')}/messages`, {
|
|
549
|
+
method: 'POST',
|
|
550
|
+
body: { content, requestId: option(args, '--request-id') },
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
else if (resource === 'conversations' && (operation === 'list' || operation === 'create')) {
|
|
554
|
+
const path = `/act/ais/${id(args[2], `Pass an AI ID after "darwin conversations ${operation}".`)}/conversations`;
|
|
555
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, path, {
|
|
556
|
+
method: operation === 'create' ? 'POST' : 'GET',
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'list') {
|
|
560
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/goals', {
|
|
561
|
+
query: {
|
|
562
|
+
...aiQuery(args),
|
|
563
|
+
mode: enumOption(args, '--mode', ['BUY', 'SELL', 'CHAT']),
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'get') {
|
|
568
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/goals/${id(args[2], `Pass a goal ID after "darwin ${resource} get".`)}`, { query: aiQuery(args) });
|
|
569
|
+
}
|
|
570
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'create') {
|
|
571
|
+
const body = mergeFields(dataOption(args), [
|
|
572
|
+
['aiId', option(args, '--ai')],
|
|
573
|
+
['intent', option(args, '--intent')],
|
|
574
|
+
['title', option(args, '--title')],
|
|
575
|
+
['mode', enumOption(args, '--mode', ['BUY', 'SELL', 'CHAT'])],
|
|
576
|
+
['type', enumOption(args, '--type', ['DEMAND', 'SUPPLY', 'CHAT'])],
|
|
577
|
+
['lifecycleStatus', enumOption(args, '--status', ['DRAFT', 'ACTIVE', 'PAUSED', 'COMPLETED', 'ARCHIVED'])],
|
|
578
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
579
|
+
]);
|
|
580
|
+
if (typeof body.intent !== 'string' || !body.intent.trim()) {
|
|
581
|
+
throw new Error('Pass a goal intent with --intent or in --data.');
|
|
582
|
+
}
|
|
583
|
+
if (option(args, '--kind') || body.kind !== undefined) {
|
|
584
|
+
throw new Error('Goal kind is not supported by v2. Use --mode BUY, SELL, or CHAT.');
|
|
256
585
|
}
|
|
257
|
-
|
|
586
|
+
if (body.type !== undefined) {
|
|
587
|
+
const type = String(body.type).toUpperCase();
|
|
588
|
+
if (!['DEMAND', 'SUPPLY', 'CHAT'].includes(type))
|
|
589
|
+
throw new Error('Goal type must be DEMAND, SUPPLY, or CHAT.');
|
|
590
|
+
const mode = type === 'DEMAND' ? 'BUY' : type === 'SUPPLY' ? 'SELL' : 'CHAT';
|
|
591
|
+
if (body.mode !== undefined && body.mode !== mode)
|
|
592
|
+
throw new Error('Goal --type and --mode disagree.');
|
|
593
|
+
body.mode = mode;
|
|
594
|
+
delete body.type;
|
|
595
|
+
}
|
|
596
|
+
if (body.agentId !== undefined) {
|
|
597
|
+
if (body.aiId !== undefined && body.aiId !== body.agentId)
|
|
598
|
+
throw new Error('Goal AI IDs disagree.');
|
|
599
|
+
body.aiId = body.agentId;
|
|
600
|
+
delete body.agentId;
|
|
601
|
+
}
|
|
602
|
+
if (body.listingIds !== undefined) {
|
|
603
|
+
if (body.capabilityIds !== undefined)
|
|
604
|
+
throw new Error('Pass capabilityIds only once.');
|
|
605
|
+
body.capabilityIds = body.listingIds;
|
|
606
|
+
delete body.listingIds;
|
|
607
|
+
}
|
|
608
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/goals', {
|
|
609
|
+
method: 'POST',
|
|
610
|
+
body,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'update') {
|
|
614
|
+
const body = mergeFields(dataOption(args), [
|
|
615
|
+
['intent', option(args, '--intent')],
|
|
616
|
+
['title', option(args, '--title')],
|
|
617
|
+
['mode', enumOption(args, '--mode', ['BUY', 'SELL', 'CHAT'])],
|
|
618
|
+
['lifecycleStatus', enumOption(args, '--status', ['DRAFT', 'ACTIVE', 'PAUSED', 'COMPLETED', 'ARCHIVED'])],
|
|
619
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
620
|
+
['pausedUntil', option(args, '--paused-until')],
|
|
621
|
+
]);
|
|
622
|
+
if (args.includes('--type') || body.type !== undefined) {
|
|
623
|
+
const type = String(option(args, '--type') ?? body.type).toUpperCase();
|
|
624
|
+
if (!['DEMAND', 'SUPPLY', 'CHAT'].includes(type))
|
|
625
|
+
throw new Error('Goal type must be DEMAND, SUPPLY, or CHAT.');
|
|
626
|
+
const mode = type === 'DEMAND' ? 'BUY' : type === 'SUPPLY' ? 'SELL' : 'CHAT';
|
|
627
|
+
if (body.mode !== undefined && body.mode !== mode)
|
|
628
|
+
throw new Error('Goal --type and --mode disagree.');
|
|
629
|
+
body.mode = mode;
|
|
630
|
+
delete body.type;
|
|
631
|
+
}
|
|
632
|
+
if (Object.keys(body).length === 0)
|
|
633
|
+
throw new Error('Pass editable goal fields or --data.');
|
|
634
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/goals/${id(args[2], `Pass a goal ID after "darwin ${resource} update".`)}`, { method: 'PATCH', body });
|
|
635
|
+
}
|
|
636
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'action') {
|
|
637
|
+
const action = args[3]?.toLowerCase();
|
|
638
|
+
if (!action || !['pause', 'resume', 'complete', 'archive'].includes(action)) {
|
|
639
|
+
throw new Error('Action must be pause, resume, complete, or archive.');
|
|
640
|
+
}
|
|
641
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/goals/${id(args[2], `Pass a goal ID after "darwin ${resource} action".`)}/${action}`, {
|
|
642
|
+
method: 'POST',
|
|
643
|
+
...(action === 'pause' && option(args, '--paused-until')
|
|
644
|
+
? { body: { pausedUntil: option(args, '--paused-until') } }
|
|
645
|
+
: {}),
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
else if ((resource === 'tasks' || resource === 'goals') && operation === 'publish') {
|
|
649
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/goals/${id(args[2], `Pass a goal ID after "darwin ${resource} publish".`)}/publication-requests`, { method: 'POST', body: dataOption(args), headers: idempotencyHeaders(args) });
|
|
650
|
+
}
|
|
651
|
+
else if (resource === 'deals' && operation === 'list') {
|
|
652
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/deals', { query: aiQuery(args) });
|
|
653
|
+
}
|
|
654
|
+
else if (resource === 'deals' && operation === 'get') {
|
|
655
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass a deal ID after "darwin deals get".')}`);
|
|
656
|
+
}
|
|
657
|
+
else if (resource === 'deals' && operation === 'create') {
|
|
658
|
+
const body = mergeFields(dataOption(args), [
|
|
659
|
+
['aiId', option(args, '--ai')],
|
|
660
|
+
['mode', enumOption(args, '--mode', ['BUY', 'SELL'])],
|
|
661
|
+
['direction', enumOption(args, '--direction', ['DEMAND', 'SUPPLY'])],
|
|
662
|
+
['title', option(args, '--title')],
|
|
663
|
+
['goalId', option(args, '--goal')],
|
|
664
|
+
['taskId', option(args, '--task')],
|
|
665
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
666
|
+
]);
|
|
667
|
+
if (body.mode === undefined && body.direction !== undefined) {
|
|
668
|
+
if (body.direction !== 'DEMAND' && body.direction !== 'SUPPLY') {
|
|
669
|
+
throw new Error('Deal direction must be DEMAND or SUPPLY.');
|
|
670
|
+
}
|
|
671
|
+
body.mode = body.direction === 'DEMAND' ? 'BUY' : 'SELL';
|
|
672
|
+
}
|
|
673
|
+
if (body.goalId === undefined && body.taskId !== undefined)
|
|
674
|
+
body.goalId = body.taskId;
|
|
675
|
+
delete body.direction;
|
|
676
|
+
delete body.taskId;
|
|
677
|
+
normalizeCapabilitySelections(body);
|
|
678
|
+
if (typeof body.mode !== 'string' || typeof body.title !== 'string') {
|
|
679
|
+
throw new Error('Pass --mode and --title, or provide both in --data. --direction remains a deprecated alias.');
|
|
680
|
+
}
|
|
681
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/deals', { method: 'POST', body });
|
|
682
|
+
}
|
|
683
|
+
else if (resource === 'deals' && operation === 'update') {
|
|
684
|
+
const body = mergeFields(dataOption(args), [
|
|
685
|
+
['title', option(args, '--title')],
|
|
686
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
687
|
+
]);
|
|
688
|
+
if (Object.keys(body).length === 0)
|
|
689
|
+
throw new Error('Pass editable deal fields or --data.');
|
|
690
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass a deal ID after "darwin deals update".')}`, { method: 'PATCH', body });
|
|
691
|
+
}
|
|
692
|
+
else if (resource === 'deals' && operation === 'action') {
|
|
693
|
+
const action = args[3]?.toUpperCase();
|
|
694
|
+
if (!action || !['SEND', 'ACCEPT', 'REJECT', 'WITHDRAW'].includes(action)) {
|
|
695
|
+
throw new Error('Action must be send, accept, reject, or withdraw.');
|
|
696
|
+
}
|
|
697
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass a deal ID after "darwin deals action".')}/${action.toLowerCase()}`, { method: 'POST' });
|
|
698
|
+
}
|
|
699
|
+
else if (resource === 'deals' && operation === 'payments') {
|
|
700
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass a deal ID after "darwin deals payments".')}/payments`);
|
|
701
|
+
}
|
|
702
|
+
else if (resource === 'transactions' && operation === 'list') {
|
|
703
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/transactions', { query: aiQuery(args) });
|
|
704
|
+
}
|
|
705
|
+
else if (resource === 'transactions' && operation === 'get') {
|
|
706
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/transactions/${id(args[2], 'Pass a transaction ID after "darwin transactions get".')}`, { query: aiQuery(args) });
|
|
707
|
+
}
|
|
708
|
+
else if (resource === 'transactions' && operation === 'action') {
|
|
709
|
+
const action = args[3]?.toUpperCase();
|
|
710
|
+
if (!action || !['CANCEL', 'REQUEST_REFUND'].includes(action)) {
|
|
711
|
+
throw new Error('Transaction action must be cancel or request_refund.');
|
|
712
|
+
}
|
|
713
|
+
const body = mergeFields(dataOption(args), [
|
|
714
|
+
['aiId', option(args, '--ai')],
|
|
715
|
+
['amountMinor', integerOption(args, '--amount-minor')],
|
|
716
|
+
]);
|
|
717
|
+
delete body.action;
|
|
718
|
+
if (typeof body.aiId !== 'string' || !body.aiId.trim()) {
|
|
719
|
+
throw new Error('Pass the transaction AI ID with --ai or aiId in --data.');
|
|
720
|
+
}
|
|
721
|
+
if (action === 'CANCEL')
|
|
722
|
+
delete body.amountMinor;
|
|
723
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/transactions/${id(args[2], 'Pass a transaction ID after "darwin transactions action".')}/${action === 'CANCEL' ? 'cancel' : 'refunds'}`, { method: 'POST', body, headers: idempotencyHeaders(args) });
|
|
724
|
+
}
|
|
725
|
+
else if (resource === 'outcomes' && operation === 'list') {
|
|
726
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/outcomes', { query: aiQuery(args) });
|
|
727
|
+
}
|
|
728
|
+
else if (resource === 'outcomes' && operation === 'get') {
|
|
729
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/outcomes/${id(args[2], 'Pass an outcome ID after "darwin outcomes get".')}`);
|
|
730
|
+
}
|
|
731
|
+
else if (resource === 'outcomes' && operation === 'evidence') {
|
|
732
|
+
const body = mergeFields(dataOption(args), [
|
|
733
|
+
['evidenceDigest', option(args, '--digest')],
|
|
734
|
+
['signedReference', option(args, '--signed-reference')],
|
|
735
|
+
['dealId', option(args, '--deal')],
|
|
736
|
+
]);
|
|
737
|
+
if (typeof body.evidenceDigest !== 'string')
|
|
738
|
+
throw new Error('Pass --digest or evidenceDigest in --data.');
|
|
739
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/outcomes/${id(args[2], 'Pass an outcome ID after "darwin outcomes evidence".')}/evidence`, { method: 'POST', body, headers: idempotencyHeaders(args) });
|
|
740
|
+
}
|
|
741
|
+
else if (resource === 'sessions' && operation === 'list') {
|
|
742
|
+
result = await request(auth.apiKey, auth.baseUrl, '/sessions', {
|
|
743
|
+
query: {
|
|
744
|
+
aiId: option(args, '--ai'),
|
|
745
|
+
status: lowerEnumOption(args, '--status', [
|
|
746
|
+
'pending_provider',
|
|
747
|
+
'planning',
|
|
748
|
+
'active',
|
|
749
|
+
'completed',
|
|
750
|
+
'canceled',
|
|
751
|
+
'failed',
|
|
752
|
+
]),
|
|
753
|
+
limit: integerOption(args, '--limit'),
|
|
754
|
+
cursor: option(args, '--cursor'),
|
|
755
|
+
},
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
else if (resource === 'sessions' && operation === 'invitations') {
|
|
759
|
+
result = await request(auth.apiKey, auth.baseUrl, '/session-invitations', {
|
|
760
|
+
query: {
|
|
761
|
+
aiId: option(args, '--ai'),
|
|
762
|
+
limit: integerOption(args, '--limit'),
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
else if (resource === 'sessions' && operation === 'invitation') {
|
|
767
|
+
const action = args[3]?.toLowerCase();
|
|
768
|
+
if (action !== 'accept' && action !== 'decline') {
|
|
769
|
+
throw new Error('Session invitation action must be accept or decline.');
|
|
770
|
+
}
|
|
771
|
+
result = await request(auth.apiKey, auth.baseUrl, `/session-invitations/${id(args[2], 'Pass an invitation ID after "darwin sessions invitation".')}/actions`, {
|
|
258
772
|
method: 'POST',
|
|
259
773
|
body: {
|
|
260
|
-
|
|
261
|
-
|
|
774
|
+
action,
|
|
775
|
+
...(option(args, '--ai') ? { aiId: option(args, '--ai') } : {}),
|
|
776
|
+
},
|
|
777
|
+
headers: idempotencyHeaders(args),
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
else if (resource === 'sessions' && operation === 'get') {
|
|
781
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions get".')}`);
|
|
782
|
+
}
|
|
783
|
+
else if (resource === 'sessions' && operation === 'participants') {
|
|
784
|
+
const sessionResult = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions participants".')}`);
|
|
785
|
+
const session = sessionResult &&
|
|
786
|
+
typeof sessionResult === 'object' &&
|
|
787
|
+
'session' in sessionResult &&
|
|
788
|
+
sessionResult.session &&
|
|
789
|
+
typeof sessionResult.session === 'object'
|
|
790
|
+
? sessionResult.session
|
|
791
|
+
: undefined;
|
|
792
|
+
result = {
|
|
793
|
+
participants: Array.isArray(session?.participants) ? session.participants : [],
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
else if (resource === 'sessions' && operation === 'create') {
|
|
797
|
+
const body = mergeFields(dataOption(args), [
|
|
798
|
+
['aiId', option(args, '--ai')],
|
|
799
|
+
['kind', lowerEnumOption(args, '--kind', ['direct', 'discovery', 'buy', 'sell', 'coordination'])],
|
|
800
|
+
['goalId', option(args, '--goal')],
|
|
801
|
+
['conversationId', option(args, '--conversation')],
|
|
802
|
+
['discoveryMode', lowerEnumOption(args, '--discovery-mode', ['none', 'if_needed', 'always'])],
|
|
803
|
+
['activationMode', lowerEnumOption(args, '--activation-mode', ['immediate', 'plan_only'])],
|
|
804
|
+
]);
|
|
805
|
+
const intentDescription = option(args, '--intent');
|
|
806
|
+
const intentTitle = option(args, '--title');
|
|
807
|
+
if (intentDescription || intentTitle) {
|
|
808
|
+
const existingIntent = body.intent && typeof body.intent === 'object' && !Array.isArray(body.intent)
|
|
809
|
+
? body.intent
|
|
810
|
+
: {};
|
|
811
|
+
const description = intentDescription ?? (typeof existingIntent.description === 'string' ? existingIntent.description : undefined);
|
|
812
|
+
if (!description) {
|
|
813
|
+
throw new Error('--title requires an intent description from --intent or --data.');
|
|
814
|
+
}
|
|
815
|
+
body.intent = {
|
|
816
|
+
...existingIntent,
|
|
817
|
+
title: intentTitle ?? (typeof existingIntent.title === 'string' ? existingIntent.title : description.slice(0, 120)),
|
|
818
|
+
description,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
const targetAIIds = repeatedOption(args, '--target');
|
|
822
|
+
if (targetAIIds.length > 0)
|
|
823
|
+
body.targetAIIds = targetAIIds;
|
|
824
|
+
const descriptor = option(args, '--discovery-descriptor');
|
|
825
|
+
if (descriptor !== undefined) {
|
|
826
|
+
body.discoveryDescriptor = jsonObjectOption(args, '--discovery-descriptor');
|
|
827
|
+
}
|
|
828
|
+
const existingDataPolicy = body.dataPolicy && typeof body.dataPolicy === 'object' && !Array.isArray(body.dataPolicy)
|
|
829
|
+
? body.dataPolicy
|
|
830
|
+
: {};
|
|
831
|
+
const dataPolicy = mergeFields({ ...existingDataPolicy }, [
|
|
832
|
+
['contentMode', lowerEnumOption(args, '--content-mode', ['managed', 'sealed'])],
|
|
833
|
+
[
|
|
834
|
+
'learningMode',
|
|
835
|
+
lowerEnumOption(args, '--learning-mode', [
|
|
836
|
+
'none',
|
|
837
|
+
'outcomes_only',
|
|
838
|
+
'derived_and_outcomes',
|
|
839
|
+
'content_and_outcomes',
|
|
840
|
+
]),
|
|
841
|
+
],
|
|
842
|
+
['keyManagement', lowerEnumOption(args, '--key-management', ['darwin_managed', 'tenant_managed'])],
|
|
843
|
+
]);
|
|
844
|
+
if (Object.keys(dataPolicy).length > 0)
|
|
845
|
+
body.dataPolicy = dataPolicy;
|
|
846
|
+
const effectiveContentMode = typeof dataPolicy.contentMode === 'string' ? dataPolicy.contentMode.toLowerCase() : 'sealed';
|
|
847
|
+
if (effectiveContentMode === 'sealed' && body.intent !== undefined) {
|
|
848
|
+
throw new Error('Sealed sessions cannot include plaintext intent. Remove --intent/intent and use --discovery-descriptor only when discovery is needed.');
|
|
849
|
+
}
|
|
850
|
+
if (effectiveContentMode === 'managed' && body.discoveryDescriptor !== undefined && body.intent === undefined) {
|
|
851
|
+
throw new Error('Managed session discovery descriptors require a structured intent.');
|
|
852
|
+
}
|
|
853
|
+
if (typeof body.kind !== 'string') {
|
|
854
|
+
throw new Error('Pass a session kind with --kind or in --data.');
|
|
855
|
+
}
|
|
856
|
+
result = await request(auth.apiKey, auth.baseUrl, '/sessions', {
|
|
857
|
+
method: 'POST',
|
|
858
|
+
body,
|
|
859
|
+
headers: idempotencyHeaders(args),
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
else if (resource === 'sessions' && operation === 'send') {
|
|
863
|
+
const content = textArgument(args, 3, [
|
|
864
|
+
'--kind',
|
|
865
|
+
'--context-scope',
|
|
866
|
+
'--idempotency-key',
|
|
867
|
+
'--data',
|
|
868
|
+
'--protected-content',
|
|
869
|
+
'--protected-content-file',
|
|
870
|
+
'--sealed-content',
|
|
871
|
+
'--sealed-content-file',
|
|
872
|
+
]);
|
|
873
|
+
const body = mergeFields(dataOption(args), [
|
|
874
|
+
[
|
|
875
|
+
'kind',
|
|
876
|
+
lowerEnumOption(args, '--kind', [
|
|
877
|
+
'message',
|
|
878
|
+
'question',
|
|
879
|
+
'clarification',
|
|
880
|
+
'proposal',
|
|
881
|
+
'counterproposal',
|
|
882
|
+
'notice',
|
|
883
|
+
'result',
|
|
884
|
+
'artifact',
|
|
885
|
+
]),
|
|
886
|
+
],
|
|
887
|
+
['content', content || undefined],
|
|
888
|
+
['contextScopeId', option(args, '--context-scope')],
|
|
889
|
+
]);
|
|
890
|
+
if (args.includes('--protected-content') && args.includes('--sealed-content')) {
|
|
891
|
+
throw new Error('Pass only one of --sealed-content or --protected-content.');
|
|
892
|
+
}
|
|
893
|
+
if (args.includes('--protected-content-file') && args.includes('--sealed-content-file')) {
|
|
894
|
+
throw new Error('Pass only one of --sealed-content-file or --protected-content-file.');
|
|
895
|
+
}
|
|
896
|
+
const protectedContentOption = args.includes('--protected-content') ? '--protected-content' : '--sealed-content';
|
|
897
|
+
if (args.includes(protectedContentOption)) {
|
|
898
|
+
body.protectedContent = jsonObjectOption(args, protectedContentOption);
|
|
899
|
+
}
|
|
900
|
+
const protectedContentFileOption = args.includes('--protected-content-file')
|
|
901
|
+
? '--protected-content-file'
|
|
902
|
+
: '--sealed-content-file';
|
|
903
|
+
const protectedContentFile = await jsonObjectFileOption(args, protectedContentFileOption);
|
|
904
|
+
if (protectedContentFile) {
|
|
905
|
+
if (body.protectedContent) {
|
|
906
|
+
throw new Error('Pass only one sealed-content value or file.');
|
|
907
|
+
}
|
|
908
|
+
body.protectedContent = protectedContentFile;
|
|
909
|
+
}
|
|
910
|
+
if (!body.kind)
|
|
911
|
+
body.kind = 'message';
|
|
912
|
+
const hasContent = typeof body.content === 'string' && body.content.trim().length > 0;
|
|
913
|
+
const hasSealedContent = !!body.protectedContent && typeof body.protectedContent === 'object' && !Array.isArray(body.protectedContent);
|
|
914
|
+
if ((!hasContent && !hasSealedContent) || (hasContent && hasSealedContent)) {
|
|
915
|
+
throw new Error('Pass either message text or sealed content, but not both.');
|
|
916
|
+
}
|
|
917
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions send".')}/interactions`, { method: 'POST', body, headers: idempotencyHeaders(args) });
|
|
918
|
+
}
|
|
919
|
+
else if (resource === 'sessions' && operation === 'watch') {
|
|
920
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions watch".')}/interactions`, {
|
|
921
|
+
query: {
|
|
922
|
+
limit: integerOption(args, '--limit'),
|
|
923
|
+
cursor: option(args, '--cursor'),
|
|
924
|
+
contextScopeId: option(args, '--context-scope'),
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
else if (resource === 'sessions' && operation === 'mesh') {
|
|
929
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions mesh".')}/mesh`);
|
|
930
|
+
}
|
|
931
|
+
else if (resource === 'sessions' && operation === 'replan') {
|
|
932
|
+
const body = dataOption(args);
|
|
933
|
+
const limit = integerOption(args, '--limit');
|
|
934
|
+
if (limit !== undefined)
|
|
935
|
+
body.limit = limit;
|
|
936
|
+
const excludeAIIds = repeatedOption(args, '--exclude-ai');
|
|
937
|
+
if (excludeAIIds.length > 0)
|
|
938
|
+
body.excludeAIIds = excludeAIIds;
|
|
939
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions replan".')}/resolutions`, {
|
|
940
|
+
method: 'POST',
|
|
941
|
+
body,
|
|
942
|
+
headers: idempotencyHeaders(args),
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
else if (resource === 'sessions' && ['complete', 'cancel'].includes(operation ?? '')) {
|
|
946
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], `Pass a session ID after "darwin sessions ${operation}".`)}/actions`, {
|
|
947
|
+
method: 'POST',
|
|
948
|
+
body: {
|
|
949
|
+
...dataOption(args),
|
|
950
|
+
action: operation?.toUpperCase(),
|
|
951
|
+
},
|
|
952
|
+
headers: idempotencyHeaders(args),
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
else if (resource === 'sessions' && operation === 'outcome') {
|
|
956
|
+
const body = mergeFields(dataOption(args), [
|
|
957
|
+
['completionState', lowerEnumOption(args, '--completion-state', ['completed', 'partial', 'failed', 'cancelled'])],
|
|
958
|
+
[
|
|
959
|
+
'durationBand',
|
|
960
|
+
lowerEnumOption(args, '--duration-band', ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months']),
|
|
961
|
+
],
|
|
962
|
+
['disputeState', lowerEnumOption(args, '--dispute-state', ['none', 'open', 'resolved'])],
|
|
963
|
+
['refundState', lowerEnumOption(args, '--refund-state', ['none', 'partial', 'full'])],
|
|
964
|
+
[
|
|
965
|
+
'evaluatorType',
|
|
966
|
+
lowerEnumOption(args, '--evaluator-type', ['requester', 'provider', 'platform', 'third_party']),
|
|
967
|
+
],
|
|
968
|
+
]);
|
|
969
|
+
const confidence = option(args, '--confidence');
|
|
970
|
+
if (confidence !== undefined) {
|
|
971
|
+
const parsed = Number(confidence);
|
|
972
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
|
|
973
|
+
throw new Error('--confidence must be a number from 0 to 1.');
|
|
974
|
+
}
|
|
975
|
+
body.confidence = parsed;
|
|
976
|
+
}
|
|
977
|
+
const success = booleanOption(args, '--success');
|
|
978
|
+
if (success !== undefined)
|
|
979
|
+
body.success = success;
|
|
980
|
+
if (args.includes('--scores'))
|
|
981
|
+
body.scores = jsonObjectOption(args, '--scores');
|
|
982
|
+
if (args.includes('--signals'))
|
|
983
|
+
body.signals = jsonArrayOption(args, '--signals');
|
|
984
|
+
if (args.includes('--cost-band'))
|
|
985
|
+
body.costBand = jsonObjectOption(args, '--cost-band');
|
|
986
|
+
const criterionResultsOption = args.includes('--criterion-results')
|
|
987
|
+
? '--criterion-results'
|
|
988
|
+
: '--success-criteria-results';
|
|
989
|
+
if (args.includes(criterionResultsOption)) {
|
|
990
|
+
body.criterionResults = jsonArrayOption(args, criterionResultsOption);
|
|
991
|
+
}
|
|
992
|
+
const decisionIds = repeatedOption(args, '--decision-id');
|
|
993
|
+
if (decisionIds.length > 0)
|
|
994
|
+
body.decisionIds = decisionIds;
|
|
995
|
+
const evidenceDigests = repeatedOption(args, '--evidence-digest');
|
|
996
|
+
if (evidenceDigests.some((value) => !/^[a-f0-9]{64}$/i.test(value))) {
|
|
997
|
+
throw new Error('--evidence-digest must be a 64-character SHA-256 hex digest.');
|
|
998
|
+
}
|
|
999
|
+
if (evidenceDigests.length > 0)
|
|
1000
|
+
body.evidenceDigests = evidenceDigests;
|
|
1001
|
+
if (typeof body.completionState !== 'string') {
|
|
1002
|
+
throw new Error('Pass --completion-state or include completionState in --data.');
|
|
1003
|
+
}
|
|
1004
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions outcome".')}/outcomes`, { method: 'POST', body, headers: idempotencyHeaders(args) });
|
|
1005
|
+
}
|
|
1006
|
+
else if (resource === 'sessions' && operation === 'feedback') {
|
|
1007
|
+
const content = textArgument(args, 3, [
|
|
1008
|
+
'--rating',
|
|
1009
|
+
'--outcome',
|
|
1010
|
+
'--context-scope',
|
|
1011
|
+
'--idempotency-key',
|
|
1012
|
+
'--data',
|
|
1013
|
+
'--protected-content',
|
|
1014
|
+
'--protected-content-file',
|
|
1015
|
+
'--sealed-content',
|
|
1016
|
+
'--sealed-content-file',
|
|
1017
|
+
]);
|
|
1018
|
+
const body = mergeFields(dataOption(args), [
|
|
1019
|
+
['outcomeId', option(args, '--outcome')],
|
|
1020
|
+
['contextScopeId', option(args, '--context-scope')],
|
|
1021
|
+
['content', content || undefined],
|
|
1022
|
+
]);
|
|
1023
|
+
const rating = integerOption(args, '--rating');
|
|
1024
|
+
if (rating !== undefined) {
|
|
1025
|
+
if (rating > 5)
|
|
1026
|
+
throw new Error('--rating must be an integer from 1 to 5.');
|
|
1027
|
+
body.rating = rating;
|
|
1028
|
+
}
|
|
1029
|
+
if (args.includes('--protected-content') && args.includes('--sealed-content')) {
|
|
1030
|
+
throw new Error('Pass only one of --sealed-content or --protected-content.');
|
|
1031
|
+
}
|
|
1032
|
+
if (args.includes('--protected-content-file') && args.includes('--sealed-content-file')) {
|
|
1033
|
+
throw new Error('Pass only one of --sealed-content-file or --protected-content-file.');
|
|
1034
|
+
}
|
|
1035
|
+
const protectedContentOption = args.includes('--protected-content') ? '--protected-content' : '--sealed-content';
|
|
1036
|
+
if (args.includes(protectedContentOption)) {
|
|
1037
|
+
body.protectedContent = jsonObjectOption(args, protectedContentOption);
|
|
1038
|
+
}
|
|
1039
|
+
const protectedContentFileOption = args.includes('--protected-content-file')
|
|
1040
|
+
? '--protected-content-file'
|
|
1041
|
+
: '--sealed-content-file';
|
|
1042
|
+
const protectedContentFile = await jsonObjectFileOption(args, protectedContentFileOption);
|
|
1043
|
+
if (protectedContentFile) {
|
|
1044
|
+
if (body.protectedContent)
|
|
1045
|
+
throw new Error('Pass only one sealed-content value or file.');
|
|
1046
|
+
body.protectedContent = protectedContentFile;
|
|
1047
|
+
}
|
|
1048
|
+
const hasContent = typeof body.content === 'string' && body.content.trim().length > 0;
|
|
1049
|
+
const hasProtectedContent = !!body.protectedContent && typeof body.protectedContent === 'object' && !Array.isArray(body.protectedContent);
|
|
1050
|
+
if (hasContent && hasProtectedContent) {
|
|
1051
|
+
throw new Error('Pass plaintext feedback or protected content, not both.');
|
|
1052
|
+
}
|
|
1053
|
+
if (body.rating === undefined && !hasContent && !hasProtectedContent) {
|
|
1054
|
+
throw new Error('Pass a rating, feedback text, or protected content.');
|
|
1055
|
+
}
|
|
1056
|
+
result = await request(auth.apiKey, auth.baseUrl, `/sessions/${id(args[2], 'Pass a session ID after "darwin sessions feedback".')}/feedback`, { method: 'POST', body, headers: idempotencyHeaders(args) });
|
|
1057
|
+
}
|
|
1058
|
+
else if (resource === 'directory' && operation === 'search') {
|
|
1059
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/search/directory/ais', {
|
|
1060
|
+
query: {
|
|
1061
|
+
query: textArgument(args, 2, ['--limit']) || undefined,
|
|
1062
|
+
limit: integerOption(args, '--limit'),
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
else if (resource === 'directory' && operation === 'get') {
|
|
1067
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/search/directory/ais/${id(args[2], 'Pass a directory AI ID.')}`);
|
|
1068
|
+
}
|
|
1069
|
+
else if (resource === 'offers' && operation === 'list') {
|
|
1070
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/deals', {
|
|
1071
|
+
query: { aiId: id(option(args, '--ai'), 'Pass an AI ID with --ai.') },
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
else if (resource === 'offers' && operation === 'get') {
|
|
1075
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass an offer ID after "darwin offers get".')}`);
|
|
1076
|
+
}
|
|
1077
|
+
else if (resource === 'offers' && operation === 'create') {
|
|
1078
|
+
const body = mergeFields(dataOption(args), [
|
|
1079
|
+
['direction', enumOption(args, '--direction', ['DEMAND', 'SUPPLY'])],
|
|
1080
|
+
['title', option(args, '--title')],
|
|
1081
|
+
['goalId', option(args, '--goal')],
|
|
1082
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
1083
|
+
]);
|
|
1084
|
+
if (typeof body.direction !== 'string' || typeof body.title !== 'string') {
|
|
1085
|
+
throw new Error('Pass --direction and --title, or provide both in --data.');
|
|
1086
|
+
}
|
|
1087
|
+
if (body.direction !== 'DEMAND' && body.direction !== 'SUPPLY') {
|
|
1088
|
+
throw new Error('Offer direction must be DEMAND or SUPPLY.');
|
|
1089
|
+
}
|
|
1090
|
+
body.mode = body.direction === 'DEMAND' ? 'BUY' : 'SELL';
|
|
1091
|
+
body.aiId = id(option(args, '--ai'), 'Pass an AI ID with --ai.');
|
|
1092
|
+
delete body.direction;
|
|
1093
|
+
normalizeCapabilitySelections(body);
|
|
1094
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/deals', { method: 'POST', body });
|
|
1095
|
+
}
|
|
1096
|
+
else if (resource === 'offers' && operation === 'update') {
|
|
1097
|
+
const body = mergeFields(dataOption(args), [
|
|
1098
|
+
['title', option(args, '--title')],
|
|
1099
|
+
['visibility', enumOption(args, '--visibility', ['PUBLIC', 'RESTRICTED', 'PRIVATE'])],
|
|
1100
|
+
]);
|
|
1101
|
+
if (Object.keys(body).length === 0)
|
|
1102
|
+
throw new Error('Pass editable offer fields or --data.');
|
|
1103
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass an offer ID after "darwin offers update".')}`, { method: 'PATCH', body });
|
|
1104
|
+
}
|
|
1105
|
+
else if (resource === 'offers' && operation === 'action') {
|
|
1106
|
+
const action = args[3]?.toUpperCase();
|
|
1107
|
+
if (!action || !['SEND', 'ACCEPT', 'REJECT', 'WITHDRAW'].includes(action)) {
|
|
1108
|
+
throw new Error('Action must be send, accept, reject, or withdraw.');
|
|
1109
|
+
}
|
|
1110
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/deals/${id(args[2], 'Pass an offer ID after "darwin offers action".')}/${action.toLowerCase()}`, { method: 'POST' });
|
|
1111
|
+
}
|
|
1112
|
+
else if (resource === 'payments' && operation === 'account') {
|
|
1113
|
+
result = await request(auth.apiKey, auth.baseUrl, `/ais/${id(option(args, '--ai'), 'Pass an AI ID with --ai.')}/payment-account`);
|
|
1114
|
+
}
|
|
1115
|
+
else if (resource === 'payments' && operation === 'list') {
|
|
1116
|
+
result = await request(auth.apiKey, auth.baseUrl, `/ais/${id(option(args, '--ai'), 'Pass an AI ID with --ai.')}/payments`);
|
|
1117
|
+
}
|
|
1118
|
+
else if (resource === 'payments' && operation === 'get') {
|
|
1119
|
+
result = await request(auth.apiKey, auth.baseUrl, `/payments/${id(args[2], 'Pass a payment ID after "darwin payments get".')}`);
|
|
1120
|
+
}
|
|
1121
|
+
else if (resource === 'fee-quotes' && operation === 'create') {
|
|
1122
|
+
const body = dataOption(args);
|
|
1123
|
+
if (Object.keys(body).length === 0)
|
|
1124
|
+
throw new Error('Pass fee quote inputs with --data.');
|
|
1125
|
+
result = await request(auth.apiKey, auth.baseUrl, '/fee-quotes', { method: 'POST', body });
|
|
1126
|
+
}
|
|
1127
|
+
else if (resource === 'fee-quotes' && operation === 'get') {
|
|
1128
|
+
result = await request(auth.apiKey, auth.baseUrl, `/fee-quotes/${id(args[2], 'Pass a fee quote ID.')}`);
|
|
1129
|
+
}
|
|
1130
|
+
else if (resource === 'fee-quotes' && operation === 'accept') {
|
|
1131
|
+
result = await request(auth.apiKey, auth.baseUrl, `/fee-quotes/${id(args[2], 'Pass a fee quote ID.')}/accept`, {
|
|
1132
|
+
method: 'POST',
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
else if (resource === 'supply' && operation === 'businesses' && args[2] === 'list') {
|
|
1136
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/ais');
|
|
1137
|
+
}
|
|
1138
|
+
else if (resource === 'supply' && operation === 'businesses' && args[2] === 'get') {
|
|
1139
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}`);
|
|
1140
|
+
}
|
|
1141
|
+
else if (resource === 'supply' && operation === 'businesses' && args[2] === 'create') {
|
|
1142
|
+
const body = mergeFields({ type: 'business', ...dataOption(args) }, [
|
|
1143
|
+
['name', option(args, '--name')],
|
|
1144
|
+
['handle', option(args, '--handle')],
|
|
1145
|
+
['description', option(args, '--description')],
|
|
1146
|
+
]);
|
|
1147
|
+
if (typeof body.name !== 'string')
|
|
1148
|
+
throw new Error('Pass --name or include name in --data.');
|
|
1149
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/host/ais', { method: 'POST', body });
|
|
1150
|
+
}
|
|
1151
|
+
else if (resource === 'supply' && operation === 'businesses' && args[2] === 'update') {
|
|
1152
|
+
const body = mergeFields(dataOption(args), [
|
|
1153
|
+
['name', option(args, '--name')],
|
|
1154
|
+
['handle', option(args, '--handle')],
|
|
1155
|
+
['description', option(args, '--description')],
|
|
1156
|
+
]);
|
|
1157
|
+
if (Object.keys(body).length === 0)
|
|
1158
|
+
throw new Error('Pass editable business fields or --data.');
|
|
1159
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}`, {
|
|
1160
|
+
method: 'PATCH',
|
|
1161
|
+
body,
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
else if (resource === 'host' && operation === 'capabilities' && args[2] === 'list') {
|
|
1165
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}/capabilities`, {
|
|
1166
|
+
query: {
|
|
1167
|
+
limit: integerOption(args, '--limit'),
|
|
1168
|
+
cursor: option(args, '--cursor'),
|
|
1169
|
+
q: option(args, '--query'),
|
|
1170
|
+
type: option(args, '--type'),
|
|
1171
|
+
status: option(args, '--status'),
|
|
262
1172
|
},
|
|
263
1173
|
});
|
|
264
1174
|
}
|
|
265
|
-
else if (resource === '
|
|
266
|
-
result = await request(auth.apiKey, auth.
|
|
1175
|
+
else if (resource === 'host' && operation === 'capabilities' && args[2] === 'get') {
|
|
1176
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}/capabilities/${id(args[4], 'Pass a capability ID.')}`);
|
|
1177
|
+
}
|
|
1178
|
+
else if (resource === 'host' && operation === 'capabilities' && args[2] === 'create') {
|
|
1179
|
+
const body = dataOption(args);
|
|
1180
|
+
if (Object.keys(body).length === 0)
|
|
1181
|
+
throw new Error('Pass capability fields with --data.');
|
|
1182
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}/capabilities`, {
|
|
1183
|
+
method: 'POST',
|
|
1184
|
+
body,
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
else if (resource === 'host' && operation === 'capabilities' && args[2] === 'update') {
|
|
1188
|
+
const body = dataOption(args);
|
|
1189
|
+
if (Object.keys(body).length === 0)
|
|
1190
|
+
throw new Error('Pass capability fields with --data.');
|
|
1191
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}/capabilities/${id(args[4], 'Pass a capability ID.')}`, { method: 'PATCH', body });
|
|
1192
|
+
}
|
|
1193
|
+
else if (resource === 'host' && operation === 'capabilities' && args[2] === 'archive') {
|
|
1194
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/host/ais/${id(args[3], 'Pass a business ID.')}/capabilities/${id(args[4], 'Pass a capability ID.')}`, { method: 'DELETE', query: { expectedRevision: integerOption(args, '--expected-revision') } });
|
|
1195
|
+
}
|
|
1196
|
+
else if (resource === 'supply' && operation === 'orders' && args[2] === 'list') {
|
|
1197
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/transactions', { query: { aiId: args[3] } });
|
|
1198
|
+
}
|
|
1199
|
+
else if (resource === 'supply' && operation === 'orders' && args[2] === 'get') {
|
|
1200
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/transactions/${id(args[4], 'Pass an order ID.')}`, {
|
|
1201
|
+
query: { aiId: args[3] },
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
else if (resource === 'supply' && operation === 'earnings' && args[2] === 'get') {
|
|
1205
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/ais/${id(args[3], 'Pass a business ID.')}/billing`);
|
|
1206
|
+
}
|
|
1207
|
+
else if (resource === 'applications' && operation === 'list') {
|
|
1208
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/account/applications');
|
|
1209
|
+
}
|
|
1210
|
+
else if (resource === 'applications' && operation === 'get') {
|
|
1211
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}`);
|
|
1212
|
+
}
|
|
1213
|
+
else if (resource === 'applications' && operation === 'create') {
|
|
1214
|
+
const body = mergeFields(dataOption(args), [['name', option(args, '--name')]]);
|
|
1215
|
+
if (typeof body.name !== 'string')
|
|
1216
|
+
throw new Error('Pass --name or include name in --data.');
|
|
1217
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/account/applications', { method: 'POST', body });
|
|
1218
|
+
}
|
|
1219
|
+
else if (resource === 'applications' && operation === 'update') {
|
|
1220
|
+
const body = mergeFields(dataOption(args), [['name', option(args, '--name')]]);
|
|
1221
|
+
if (Object.keys(body).length === 0)
|
|
1222
|
+
throw new Error('Pass editable application fields or --data.');
|
|
1223
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}`, {
|
|
1224
|
+
method: 'PATCH',
|
|
1225
|
+
body,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
else if (resource === 'applications' && operation === 'archive') {
|
|
1229
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}`, {
|
|
1230
|
+
method: 'DELETE',
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
else if (resource === 'applications' && operation === 'usage') {
|
|
1234
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/usage`, { query: { days: integerOption(args, '--days') } });
|
|
1235
|
+
}
|
|
1236
|
+
else if (resource === 'applications' && operation === 'webhooks') {
|
|
1237
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/webhooks`);
|
|
1238
|
+
}
|
|
1239
|
+
else if (resource === 'applications' && operation === 'create-webhook') {
|
|
1240
|
+
const body = mergeFields(dataOption(args), [['url', option(args, '--url')]]);
|
|
1241
|
+
if (typeof body.url !== 'string')
|
|
1242
|
+
throw new Error('Pass --url or include url in --data.');
|
|
1243
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/webhooks`, { method: 'POST', body });
|
|
1244
|
+
}
|
|
1245
|
+
else if (resource === 'applications' && operation === 'revoke-webhook') {
|
|
1246
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/webhooks/${id(args[3], 'Pass a webhook ID.')}`, { method: 'DELETE' });
|
|
1247
|
+
}
|
|
1248
|
+
else if (resource === 'applications' && operation === 'webhook-deliveries') {
|
|
1249
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/webhooks/${id(args[3], 'Pass a webhook ID.')}/deliveries`, {
|
|
1250
|
+
query: {
|
|
1251
|
+
limit: integerOption(args, '--limit'),
|
|
1252
|
+
cursor: option(args, '--cursor'),
|
|
1253
|
+
},
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
else if (resource === 'applications' && operation === 'retry-webhook') {
|
|
1257
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/account/applications/${id(args[2], 'Pass an application ID.')}/webhooks/${id(args[3], 'Pass a webhook ID.')}/deliveries/${id(args[4], 'Pass a delivery ID.')}/retry`, { method: 'POST' });
|
|
267
1258
|
}
|
|
268
1259
|
else if (resource === 'tools' && operation === 'list') {
|
|
269
|
-
result = await request(auth.apiKey, auth.
|
|
1260
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, '/act/tools');
|
|
270
1261
|
}
|
|
271
1262
|
else if (resource === 'tools' && operation === 'execute') {
|
|
272
1263
|
const tool = args[2]?.trim();
|
|
273
1264
|
if (!tool) {
|
|
274
1265
|
throw new Error('Pass a tool name after "darwin tools execute".');
|
|
275
1266
|
}
|
|
276
|
-
result = await request(auth.apiKey, auth.
|
|
1267
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, `/act/tools/${encodeURIComponent(tool)}/executions`, {
|
|
277
1268
|
method: 'POST',
|
|
278
1269
|
body: { input: jsonObjectOption(args, '--input') },
|
|
279
1270
|
});
|
|
280
1271
|
}
|
|
1272
|
+
else if (resource === 'api') {
|
|
1273
|
+
const method = operation?.toUpperCase();
|
|
1274
|
+
if (!method || !['GET', 'POST', 'PATCH', 'DELETE'].includes(method)) {
|
|
1275
|
+
throw new Error('API method must be GET, POST, PATCH, or DELETE.');
|
|
1276
|
+
}
|
|
1277
|
+
const path = args[2]?.trim();
|
|
1278
|
+
if (!path || path.includes('://') || !/^\/(?:search|act|host|account)(?:\/|$)/u.test(path)) {
|
|
1279
|
+
throw new Error('Pass a v2 API path under /search, /act, /host, or /account.');
|
|
1280
|
+
}
|
|
1281
|
+
const family = /^\/(search|act|host|account)(?:\/|$)/u.exec(path)?.[1];
|
|
1282
|
+
const v2Prefix = `${new URL(auth.v2BaseUrl).pathname.replace(/\/+$/u, '')}/${family}`;
|
|
1283
|
+
const resolved = new URL(`${auth.v2BaseUrl.replace(/\/+$/u, '')}/${path.replace(/^\/+/, '')}`);
|
|
1284
|
+
if (resolved.pathname !== v2Prefix && !resolved.pathname.startsWith(`${v2Prefix}/`)) {
|
|
1285
|
+
throw new Error('The API path must stay within the v2 endpoint.');
|
|
1286
|
+
}
|
|
1287
|
+
const body = dataOption(args);
|
|
1288
|
+
result = await request(auth.apiKey, auth.v2BaseUrl, path, {
|
|
1289
|
+
method: method,
|
|
1290
|
+
...(Object.keys(body).length > 0 ? { body } : {}),
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
281
1293
|
else {
|
|
282
1294
|
throw new Error(`Unknown command: ${args.join(' ')}`);
|
|
283
1295
|
}
|
|
1296
|
+
if (resource === 'tasks' && result && typeof result === 'object' && !Array.isArray(result)) {
|
|
1297
|
+
const { goal, goals, ...rest } = result;
|
|
1298
|
+
result = {
|
|
1299
|
+
...rest,
|
|
1300
|
+
...(goal && typeof goal === 'object' && !Array.isArray(goal)
|
|
1301
|
+
? { task: { ...goal, taskId: goal.id } }
|
|
1302
|
+
: {}),
|
|
1303
|
+
...(Array.isArray(goals)
|
|
1304
|
+
? {
|
|
1305
|
+
tasks: goals.map((item) => item && typeof item === 'object' && !Array.isArray(item) ? { ...item, taskId: item.id } : item),
|
|
1306
|
+
}
|
|
1307
|
+
: {}),
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
if (resource === 'offers' && result && typeof result === 'object' && !Array.isArray(result)) {
|
|
1311
|
+
const { deal, deals, ...rest } = result;
|
|
1312
|
+
result = {
|
|
1313
|
+
...rest,
|
|
1314
|
+
...(deal !== undefined ? { offer: deal } : {}),
|
|
1315
|
+
...(deals !== undefined ? { offers: deals } : {}),
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
284
1318
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
285
1319
|
}
|
|
286
1320
|
main().catch((error) => {
|