@wrangled/cli 0.3.44 → 0.3.45
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/dist/api.js +140 -23
- package/dist/guide.js +5 -5
- package/dist/main.js +147 -125
- package/dist/playbooks.js +11 -11
- package/dist/runner/run.js +1 -1
- package/dist/skills.js +3 -8
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -4,7 +4,7 @@ import { mkdir, readFile, stat } from "node:fs/promises";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
|
-
import { api, ApiError, apiDownload, apiText, apiUpload, CliError, exitCodes, getSigned, IMPORT_LIMITS, IMPORT_NAME_PATTERN, putSigned,
|
|
7
|
+
import { api, ApiError, apiDownload, apiText, apiUpload, CliError, exitCodes, getSigned, IMPORT_LIMITS, IMPORT_NAME_PATTERN, putSigned, CLIENT_FILE, parseClientName, readSavedLogins, removeCredentials, resolveApiUrl, resolveConnection, resolveLogin, UnreadableCredentialsError, writeCredentials, entryTagFlags, streamSigned, } from "./api.js";
|
|
8
8
|
import { brand, envPrefix } from "./brand.js";
|
|
9
9
|
import { firstSymlink, inRunFolder, writeFileAtomic } from "./files.js";
|
|
10
10
|
import { guide, skill } from "./guide.js";
|
|
@@ -19,41 +19,47 @@ const SERVER_WAIT_CAP = 50;
|
|
|
19
19
|
const VOICE_USAGE = "voice [--kind opener|objection|pain|desire|phrase] [--since <YYYY-MM-DD>] [--sort common|useful] [--limit <n>] [--json]";
|
|
20
20
|
const commands = {
|
|
21
21
|
login: {
|
|
22
|
-
usage: `login (paste the key when asked, pipe it on stdin, or set ${envPrefix}_API_KEY)`,
|
|
22
|
+
usage: `login [--client <name>] (paste the key when asked, pipe it on stdin, or set ${envPrefix}_API_KEY); --client saves it under that client's name`,
|
|
23
23
|
async run(args, options) {
|
|
24
24
|
if (args.length > 0) {
|
|
25
25
|
throw new CliError(`login takes no arguments. Pipe the key on stdin or set ${envPrefix}_API_KEY.`, exitCodes.usage);
|
|
26
26
|
}
|
|
27
|
+
const client = options.client === undefined ? undefined : parseClientName(options.client);
|
|
27
28
|
const apiKey = await readKeyFromUser();
|
|
28
|
-
const
|
|
29
|
+
const saved = await readSavedLogins().catch((error) => {
|
|
29
30
|
if (error instanceof UnreadableCredentialsError)
|
|
30
31
|
return null;
|
|
31
32
|
throw error;
|
|
32
33
|
});
|
|
33
|
-
const apiUrl = resolveApiUrl(options.apiUrl,
|
|
34
|
+
const apiUrl = resolveApiUrl(options.apiUrl, (client === undefined ? undefined : saved?.clients[client]) ?? saved?.default ?? null);
|
|
34
35
|
const me = await api({ apiUrl, apiKey }, "/api/v1/me");
|
|
35
|
-
const path = await writeCredentials({ apiKey, apiUrl });
|
|
36
|
+
const path = await writeCredentials({ apiKey, apiUrl }, { client });
|
|
36
37
|
if (options.format === "json")
|
|
37
38
|
return printJson(me);
|
|
38
|
-
console.log(`Logged in to ${me.workspace.name} (${me.workspace.slug}) at ${apiUrl}.`);
|
|
39
|
+
console.log(`Logged in to ${me.workspace.name} (${me.workspace.slug}) at ${apiUrl}${client === undefined ? "" : ` as client ${client}`}.`);
|
|
39
40
|
console.log(`Saved credentials to ${path}.`);
|
|
40
41
|
},
|
|
41
42
|
},
|
|
42
43
|
logout: {
|
|
43
|
-
usage: "logout",
|
|
44
|
-
async run() {
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
usage: "logout [--client <name>] (without --client, removes the default login and keeps named clients)",
|
|
45
|
+
async run(_args, options) {
|
|
46
|
+
const client = options.client === undefined ? undefined : parseClientName(options.client);
|
|
47
|
+
await removeCredentials(client);
|
|
48
|
+
console.log(client === undefined ? "Logged out." : `Logged out of client ${client}.`);
|
|
47
49
|
},
|
|
48
50
|
},
|
|
49
51
|
whoami: {
|
|
50
|
-
usage: "whoami",
|
|
52
|
+
usage: "whoami (also says which client's key is in use and why)",
|
|
51
53
|
async run(_args, options) {
|
|
52
|
-
const
|
|
54
|
+
const { connection, choice } = await resolveLogin(options);
|
|
55
|
+
const me = await api(connection, "/api/v1/me");
|
|
56
|
+
const client = "client" in choice ? choice.client : null;
|
|
57
|
+
const clientFile = choice.source === "folder" ? choice.path : null;
|
|
53
58
|
if (options.format === "json")
|
|
54
|
-
return printJson(me);
|
|
59
|
+
return printJson({ ...me, client, source: choice.source, clientFile });
|
|
55
60
|
const { name, slug, id } = me.workspace;
|
|
56
61
|
console.log(renderTable(["workspace", "slug", "id", "library access"], [{ workspace: name, slug, id, "library access": me.knowledge ? "on" : "off" }]));
|
|
62
|
+
console.log(`\n${clientReason(choice)}`);
|
|
57
63
|
},
|
|
58
64
|
},
|
|
59
65
|
"knowledge search": {
|
|
@@ -72,7 +78,7 @@ const commands = {
|
|
|
72
78
|
query.set("library", options.library);
|
|
73
79
|
for (const [field, value] of filters)
|
|
74
80
|
query.set(field, value);
|
|
75
|
-
const answer = await api(await resolveConnection(options
|
|
81
|
+
const answer = await api(await resolveConnection(options), `/api/v1/knowledge/search?${query}`);
|
|
76
82
|
if (options.format === "json")
|
|
77
83
|
return printJson(answer);
|
|
78
84
|
console.log(answer.results.length === 0 ? "Nothing in the library matched." : answer.results.map(knowledgeBlock).join("\n\n"));
|
|
@@ -102,7 +108,7 @@ const commands = {
|
|
|
102
108
|
})),
|
|
103
109
|
}
|
|
104
110
|
: await readToolInput(options);
|
|
105
|
-
const result = await api(await resolveConnection(options
|
|
111
|
+
const result = await api(await resolveConnection(options), KNOWLEDGE_TAGS_PATH, body);
|
|
106
112
|
if (options.format === "json")
|
|
107
113
|
return printJson(result);
|
|
108
114
|
console.log(`Tagged ${result.tagged}, took off ${result.removed}.`);
|
|
@@ -121,7 +127,7 @@ const commands = {
|
|
|
121
127
|
if (options.search !== undefined)
|
|
122
128
|
query.set("search", options.search);
|
|
123
129
|
const path = `/api/v1/warehouse/schema${query.size > 0 ? `?${query}` : ""}`;
|
|
124
|
-
const connection = await resolveConnection(options
|
|
130
|
+
const connection = await resolveConnection(options);
|
|
125
131
|
if (options.summary) {
|
|
126
132
|
const summary = await api(connection, path);
|
|
127
133
|
if (options.format === "json")
|
|
@@ -162,7 +168,7 @@ const commands = {
|
|
|
162
168
|
throw new CliError("--all saves to a file: add --out <file.csv | file.ndjson | file.json>.", exitCodes.usage);
|
|
163
169
|
}
|
|
164
170
|
const sql = await readSql(args, options.file);
|
|
165
|
-
const connection = await resolveConnection(options
|
|
171
|
+
const connection = await resolveConnection(options);
|
|
166
172
|
if (options.all)
|
|
167
173
|
return saveWholeAnswer(connection, sql, options.out, options.format);
|
|
168
174
|
const text = await apiText(connection, "/api/v1/warehouse/query", { sql });
|
|
@@ -182,7 +188,7 @@ const commands = {
|
|
|
182
188
|
if (args.length > 0)
|
|
183
189
|
throw new CliError("Usage: funnel [--from <date>] [--to <date>] [--grain day|week|month]", exitCodes.usage);
|
|
184
190
|
const query = reportQuery(options, { grain: options.grain });
|
|
185
|
-
const { funnel } = await api(await resolveConnection(options
|
|
191
|
+
const { funnel } = await api(await resolveConnection(options), `/api/v1/reports/funnel${query}`);
|
|
186
192
|
if (options.format === "json")
|
|
187
193
|
return printJson({ funnel });
|
|
188
194
|
printReport(`Funnel by ${funnel.grain}`, funnel, ["period", "currency"], FUNNEL_NUMBERS);
|
|
@@ -195,7 +201,7 @@ const commands = {
|
|
|
195
201
|
if (args.length > 0)
|
|
196
202
|
throw new CliError("Usage: money [--from <date>] [--to <date>] [--grain day|week|month]", exitCodes.usage);
|
|
197
203
|
const query = reportQuery(options, { grain: options.grain });
|
|
198
|
-
const { money } = await api(await resolveConnection(options
|
|
204
|
+
const { money } = await api(await resolveConnection(options), `/api/v1/reports/money${query}`);
|
|
199
205
|
if (options.format === "json")
|
|
200
206
|
return printJson({ money });
|
|
201
207
|
printReport(`Money by ${money.grain}`, money, ["period", "currency"], MONEY_NUMBERS);
|
|
@@ -218,7 +224,7 @@ const commands = {
|
|
|
218
224
|
if (args.length > 0)
|
|
219
225
|
throw new CliError("Usage: campaigns [--from <date>] [--to <date>] [--channel <channel>] [--limit <n>]", exitCodes.usage);
|
|
220
226
|
const query = reportQuery(options, { channel: options.channel, limit: options.limit });
|
|
221
|
-
const { campaignResults } = await api(await resolveConnection(options
|
|
227
|
+
const { campaignResults } = await api(await resolveConnection(options), `/api/v1/reports/campaigns${query}`);
|
|
222
228
|
if (options.format === "json")
|
|
223
229
|
return printJson({ campaignResults });
|
|
224
230
|
const title = campaignResults.channel === null ? "Campaigns, best first" : `${campaignResults.channel} campaigns, best first`;
|
|
@@ -229,7 +235,7 @@ const commands = {
|
|
|
229
235
|
"tools list": {
|
|
230
236
|
usage: "tools list",
|
|
231
237
|
async run(_args, options) {
|
|
232
|
-
const { tools } = await api(await resolveConnection(options
|
|
238
|
+
const { tools } = await api(await resolveConnection(options), "/api/v1/tools");
|
|
233
239
|
if (options.format === "json")
|
|
234
240
|
return printJson({ tools });
|
|
235
241
|
console.log(renderTable(["id", "mode", "output", "title"], tools));
|
|
@@ -242,7 +248,7 @@ const commands = {
|
|
|
242
248
|
if (!tool || rest.length > 0)
|
|
243
249
|
throw new CliError("Usage: tools run <tool> [--input '<json>']", exitCodes.usage);
|
|
244
250
|
const input = await readToolInput(options);
|
|
245
|
-
const connection = await resolveConnection(options
|
|
251
|
+
const connection = await resolveConnection(options);
|
|
246
252
|
const body = {
|
|
247
253
|
input,
|
|
248
254
|
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
@@ -257,7 +263,7 @@ const commands = {
|
|
|
257
263
|
async run(args, options) {
|
|
258
264
|
if (args.length !== 1)
|
|
259
265
|
throw new CliError("Usage: tools get <run-id>", exitCodes.usage);
|
|
260
|
-
const connection = await resolveConnection(options
|
|
266
|
+
const connection = await resolveConnection(options);
|
|
261
267
|
const { run } = await api(connection, `/api/v1/runs/${encodeURIComponent(args[0])}`);
|
|
262
268
|
printRun(await waitForRun(connection, run, options.wait), options.format);
|
|
263
269
|
},
|
|
@@ -267,7 +273,7 @@ const commands = {
|
|
|
267
273
|
async run(args, options) {
|
|
268
274
|
if (args.length > 0)
|
|
269
275
|
throw new CliError("Usage: sources list", exitCodes.usage);
|
|
270
|
-
const { sources } = await api(await resolveConnection(options
|
|
276
|
+
const { sources } = await api(await resolveConnection(options), "/api/v1/sources");
|
|
271
277
|
if (options.format === "json")
|
|
272
278
|
return printJson({ sources });
|
|
273
279
|
if (sources.length === 0)
|
|
@@ -296,7 +302,7 @@ const commands = {
|
|
|
296
302
|
async run(args, options) {
|
|
297
303
|
if (args.length !== 1)
|
|
298
304
|
throw new CliError("Usage: sources show <id>", exitCodes.usage);
|
|
299
|
-
const connection = await resolveConnection(options
|
|
305
|
+
const connection = await resolveConnection(options);
|
|
300
306
|
const path = `/api/v1/sources/${encodeURIComponent(args[0])}`;
|
|
301
307
|
const { source } = await api(connection, path);
|
|
302
308
|
const { runs } = await api(connection, `${path}/runs?limit=${SOURCE_RUNS_SHOWN}`);
|
|
@@ -340,7 +346,7 @@ const commands = {
|
|
|
340
346
|
const [id, ...assignments] = args;
|
|
341
347
|
if (!id)
|
|
342
348
|
throw new CliError("Usage: sources settings <id> [<key>=<value> ...]", exitCodes.usage);
|
|
343
|
-
const connection = await resolveConnection(options
|
|
349
|
+
const connection = await resolveConnection(options);
|
|
344
350
|
const path = `/api/v1/sources/${encodeURIComponent(id)}/settings`;
|
|
345
351
|
let { settings } = await api(connection, path);
|
|
346
352
|
if (assignments.length > 0) {
|
|
@@ -366,7 +372,7 @@ const commands = {
|
|
|
366
372
|
async run(args, options) {
|
|
367
373
|
if (args.length > 0)
|
|
368
374
|
throw new CliError("Usage: intake list", exitCodes.usage);
|
|
369
|
-
const { endpoints } = await api(await resolveConnection(options
|
|
375
|
+
const { endpoints } = await api(await resolveConnection(options), INTAKE_PATH);
|
|
370
376
|
if (options.format === "json")
|
|
371
377
|
return printJson({ endpoints });
|
|
372
378
|
if (endpoints.length === 0)
|
|
@@ -396,7 +402,7 @@ const commands = {
|
|
|
396
402
|
...(retentionDays === undefined ? {} : { retentionDays }),
|
|
397
403
|
...(signingKey === undefined ? {} : { signingKey }),
|
|
398
404
|
};
|
|
399
|
-
printIntakeToken(await api(await resolveConnection(options
|
|
405
|
+
printIntakeToken(await api(await resolveConnection(options), INTAKE_PATH, body), options.format);
|
|
400
406
|
},
|
|
401
407
|
},
|
|
402
408
|
"intake rotate": {
|
|
@@ -405,7 +411,7 @@ const commands = {
|
|
|
405
411
|
if (args.length !== 1)
|
|
406
412
|
throw new CliError("Usage: intake rotate <id>", exitCodes.usage);
|
|
407
413
|
const path = `${intakePath(args[0])}/rotate`;
|
|
408
|
-
printIntakeToken(await api(await resolveConnection(options
|
|
414
|
+
printIntakeToken(await api(await resolveConnection(options), path, {}), options.format);
|
|
409
415
|
},
|
|
410
416
|
},
|
|
411
417
|
"intake signing-key": {
|
|
@@ -415,7 +421,7 @@ const commands = {
|
|
|
415
421
|
throw new CliError("Usage: intake signing-key <id> --signing-key-stdin", exitCodes.usage);
|
|
416
422
|
const signingKey = await readSigningKey();
|
|
417
423
|
const path = `${intakePath(args[0])}/signing-key`;
|
|
418
|
-
const { endpoint } = await api(await resolveConnection(options
|
|
424
|
+
const { endpoint } = await api(await resolveConnection(options), path, { signingKey }, "PUT");
|
|
419
425
|
if (options.format === "json")
|
|
420
426
|
return printJson({ endpoint });
|
|
421
427
|
console.log(`Replaced the ${endpoint.kind} signing key. Every ${endpoint.kind} address in the workspace now checks against it.`);
|
|
@@ -462,7 +468,7 @@ const commands = {
|
|
|
462
468
|
if (!options.yes) {
|
|
463
469
|
throw new CliError("This deletes the address and every event it has saved. Add --yes to confirm: intake delete <id> --yes", exitCodes.usage);
|
|
464
470
|
}
|
|
465
|
-
const result = await api(await resolveConnection(options
|
|
471
|
+
const result = await api(await resolveConnection(options), intakePath(args[0]), undefined, "DELETE");
|
|
466
472
|
if (options.format === "json")
|
|
467
473
|
return printJson(result);
|
|
468
474
|
console.log("Deleted the address and its saved events.");
|
|
@@ -476,7 +482,7 @@ const commands = {
|
|
|
476
482
|
throw new CliError(usage, exitCodes.usage);
|
|
477
483
|
const name = importName(options.table);
|
|
478
484
|
const file = await readImportFile(args[0]);
|
|
479
|
-
const connection = await resolveConnection(options
|
|
485
|
+
const connection = await resolveConnection(options);
|
|
480
486
|
const result = await uploadCsv(connection, name, file);
|
|
481
487
|
if (options.format === "json")
|
|
482
488
|
return printJson(result);
|
|
@@ -495,7 +501,7 @@ const commands = {
|
|
|
495
501
|
throw new CliError(`This drops the table import_${name} and all its rows. Add --yes to confirm: import remove --table ${name} --yes`, exitCodes.usage);
|
|
496
502
|
}
|
|
497
503
|
const path = `/api/v1/imports/csv?table=${encodeURIComponent(name)}`;
|
|
498
|
-
const result = await api(await resolveConnection(options
|
|
504
|
+
const result = await api(await resolveConnection(options), path, undefined, "DELETE");
|
|
499
505
|
if (options.format === "json")
|
|
500
506
|
return printJson(result);
|
|
501
507
|
console.log(`Removed ${result.table} and its rows.`);
|
|
@@ -512,7 +518,7 @@ const commands = {
|
|
|
512
518
|
if (options.limit)
|
|
513
519
|
query.set("limit", options.limit);
|
|
514
520
|
const path = `/api/v1/creatives${query.size ? `?${query}` : ""}`;
|
|
515
|
-
const { creatives } = await api(await resolveConnection(options
|
|
521
|
+
const { creatives } = await api(await resolveConnection(options), path);
|
|
516
522
|
if (options.format === "json")
|
|
517
523
|
return printJson({ creatives });
|
|
518
524
|
if (creatives.length === 0)
|
|
@@ -526,7 +532,7 @@ const commands = {
|
|
|
526
532
|
async run(args, options) {
|
|
527
533
|
if (args.length !== 1)
|
|
528
534
|
throw new CliError("Usage: creatives show <sku>", exitCodes.usage);
|
|
529
|
-
const { creative } = await api(await resolveConnection(options
|
|
535
|
+
const { creative } = await api(await resolveConnection(options), `/api/v1/creatives/${encodeURIComponent(args[0])}`);
|
|
530
536
|
if (options.format === "json")
|
|
531
537
|
return printJson({ creative });
|
|
532
538
|
const fields = {
|
|
@@ -543,7 +549,7 @@ const commands = {
|
|
|
543
549
|
async run(args, options) {
|
|
544
550
|
if (args.length !== 1)
|
|
545
551
|
throw new CliError("Usage: creatives download <sku | asset-path | asset-link> [--out <path>] [--open]", exitCodes.usage);
|
|
546
|
-
const connection = await resolveConnection(options
|
|
552
|
+
const connection = await resolveConnection(options);
|
|
547
553
|
const target = await downloadTarget(args[0], connection);
|
|
548
554
|
const { bytes, contentType } = await apiDownload(connection, `${ASSET_ROUTE}${target.assetPath.split("/").map(encodeURIComponent).join("/")}`);
|
|
549
555
|
const path = await savePath(options.out, extname(target.fileName) ? target.fileName : `${target.fileName}.${extensionFor(contentType)}`);
|
|
@@ -563,7 +569,7 @@ const commands = {
|
|
|
563
569
|
}
|
|
564
570
|
const lineage = options.lineage === undefined ? undefined : await parseJson(options.lineage, "--lineage");
|
|
565
571
|
const tags = options.tags === undefined ? undefined : await parseJson(options.tags, "--tags");
|
|
566
|
-
const { creatives } = await api(await resolveConnection(options
|
|
572
|
+
const { creatives } = await api(await resolveConnection(options), "/api/v1/creatives", {
|
|
567
573
|
runId: options.run,
|
|
568
574
|
...(lineage === undefined ? {} : { lineage }),
|
|
569
575
|
...(tags === undefined ? {} : { tags }),
|
|
@@ -579,7 +585,7 @@ const commands = {
|
|
|
579
585
|
if (args.length !== 1)
|
|
580
586
|
throw new CliError("Usage: creatives tag <sku> --file <tags.json> | < tags.json", exitCodes.usage);
|
|
581
587
|
const tags = await readToolInput(options);
|
|
582
|
-
const { creative } = await api(await resolveConnection(options
|
|
588
|
+
const { creative } = await api(await resolveConnection(options), `/api/v1/creatives/${encodeURIComponent(args[0])}/tags`, { tags }, "PATCH");
|
|
583
589
|
if (options.format === "json")
|
|
584
590
|
return printJson({ creative });
|
|
585
591
|
console.log(`Tagged ${creative.sku}. The tags read ${creative.tagsStatus} until a person confirms them in the app.`);
|
|
@@ -590,7 +596,7 @@ const commands = {
|
|
|
590
596
|
async run(args, options) {
|
|
591
597
|
if (args.length > 0 || !options.kind)
|
|
592
598
|
throw new CliError("Usage: content create --kind ad|email|sms|landing_page|video|social_post [...]", exitCodes.usage);
|
|
593
|
-
const connection = await resolveConnection(options
|
|
599
|
+
const connection = await resolveConnection(options);
|
|
594
600
|
const content = await api(connection, "/api/v1/content", {
|
|
595
601
|
kind: options.kind,
|
|
596
602
|
...(await contentInput(options, connection)),
|
|
@@ -608,7 +614,7 @@ const commands = {
|
|
|
608
614
|
}
|
|
609
615
|
if (options.language !== undefined && options.transcript === undefined)
|
|
610
616
|
throw new CliError("--language goes with --transcript.", exitCodes.usage);
|
|
611
|
-
const connection = await resolveConnection(options
|
|
617
|
+
const connection = await resolveConnection(options);
|
|
612
618
|
const content = await api(connection, "/api/v1/content/captures", {
|
|
613
619
|
kind: options.kind,
|
|
614
620
|
...(await contentInput(options, connection)),
|
|
@@ -624,7 +630,7 @@ const commands = {
|
|
|
624
630
|
async run(args, options) {
|
|
625
631
|
if (args.length > 0)
|
|
626
632
|
throw new CliError("Usage: guide", exitCodes.usage);
|
|
627
|
-
const guide = await api(await resolveConnection(options
|
|
633
|
+
const guide = await api(await resolveConnection(options), "/api/v1/guide");
|
|
628
634
|
if (options.format === "json")
|
|
629
635
|
return printJson(guide);
|
|
630
636
|
process.stdout.write(guide.text);
|
|
@@ -636,7 +642,7 @@ const commands = {
|
|
|
636
642
|
if (args.length > 0)
|
|
637
643
|
throw new CliError("Usage: brief [--from <date>] [--to <date>] [--channel <channel>] [--campaign <id>] [--top <n>]", exitCodes.usage);
|
|
638
644
|
const query = reportQuery(options, { channel: options.channel, campaignId: options.campaign, top: options.top });
|
|
639
|
-
const brief = await api(await resolveConnection(options
|
|
645
|
+
const brief = await api(await resolveConnection(options), `/api/v1/brief${query}`);
|
|
640
646
|
if (options.format === "json")
|
|
641
647
|
return printJson(brief);
|
|
642
648
|
process.stdout.write(brief.text);
|
|
@@ -652,7 +658,7 @@ const commands = {
|
|
|
652
658
|
for (const [name, value] of Object.entries({ step: options.step, funnel: options.funnel, page: options.page }))
|
|
653
659
|
if (value !== undefined)
|
|
654
660
|
query.set(name, value);
|
|
655
|
-
const brief = await api(await resolveConnection(options
|
|
661
|
+
const brief = await api(await resolveConnection(options), `/api/v1/pages/brief?${query}`);
|
|
656
662
|
if (options.format === "json")
|
|
657
663
|
return printJson(brief);
|
|
658
664
|
process.stdout.write(brief.text);
|
|
@@ -663,7 +669,7 @@ const commands = {
|
|
|
663
669
|
async run(args, options) {
|
|
664
670
|
if (args.length > 0)
|
|
665
671
|
throw new CliError("Usage: pages results [--from <date>] [--to <date>] [--page <address | page key | item id>]", exitCodes.usage);
|
|
666
|
-
const results = await api(await resolveConnection(options
|
|
672
|
+
const results = await api(await resolveConnection(options), `/api/v1/pages/results${reportQuery(options, { page: options.page })}`);
|
|
667
673
|
if (options.format === "json")
|
|
668
674
|
return printJson(results);
|
|
669
675
|
printPageResults(results);
|
|
@@ -674,7 +680,7 @@ const commands = {
|
|
|
674
680
|
async run(args, options) {
|
|
675
681
|
if (args.length > 0)
|
|
676
682
|
throw new CliError("Usage: messages results [--from <date>] [--to <date>] [--channel email|text] [--sequence <workflow id>]", exitCodes.usage);
|
|
677
|
-
const results = await api(await resolveConnection(options
|
|
683
|
+
const results = await api(await resolveConnection(options), `/api/v1/messages/results${reportQuery(options, { channel: options.channel, sequence: options.sequence })}`);
|
|
678
684
|
if (options.format === "json")
|
|
679
685
|
return printJson(results);
|
|
680
686
|
printMessageResults(results);
|
|
@@ -689,7 +695,7 @@ const commands = {
|
|
|
689
695
|
for (const [name, value] of Object.entries({ ...winnersPeriod(options), top: options.top, format: options.entryTags.format }))
|
|
690
696
|
if (value !== undefined)
|
|
691
697
|
query.set(name, value);
|
|
692
|
-
const report = await api(await resolveConnection(options
|
|
698
|
+
const report = await api(await resolveConnection(options), `/api/v1/winners${query.size ? `?${query}` : ""}`);
|
|
693
699
|
if (options.format === "json")
|
|
694
700
|
return printJson(report);
|
|
695
701
|
process.stdout.write(winnersText(report));
|
|
@@ -701,7 +707,7 @@ const commands = {
|
|
|
701
707
|
if (args.length > 0)
|
|
702
708
|
throw new CliError("Usage: winners send-to-exodus [--week <date> | --days <n> | --from <date> --to <date>] [--dry-run]", exitCodes.usage);
|
|
703
709
|
const period = Object.entries(winnersPeriod(options)).filter(([, value]) => value !== undefined);
|
|
704
|
-
const sent = await api(await resolveConnection(options
|
|
710
|
+
const sent = await api(await resolveConnection(options), "/api/v1/winners/exodus", { ...Object.fromEntries(period), dryRun: options.dryRun });
|
|
705
711
|
if (options.format === "json")
|
|
706
712
|
printJson(sent);
|
|
707
713
|
else
|
|
@@ -716,7 +722,7 @@ const commands = {
|
|
|
716
722
|
async run(args, options) {
|
|
717
723
|
if (args.length > 0)
|
|
718
724
|
throw new CliError("Usage: winners goals", exitCodes.usage);
|
|
719
|
-
printWinnersGoals(await api(await resolveConnection(options
|
|
725
|
+
printWinnersGoals(await api(await resolveConnection(options), "/api/v1/winners/goals"), options.format);
|
|
720
726
|
},
|
|
721
727
|
},
|
|
722
728
|
"winners goals set": {
|
|
@@ -724,7 +730,7 @@ const commands = {
|
|
|
724
730
|
async run(args, options) {
|
|
725
731
|
if (args.length === 0)
|
|
726
732
|
throw new CliError("Usage: winners goals set <goal> [<goal> ...]", exitCodes.usage);
|
|
727
|
-
printWinnersGoals(await api(await resolveConnection(options
|
|
733
|
+
printWinnersGoals(await api(await resolveConnection(options), "/api/v1/winners/goals", { goals: args }, "PUT"), options.format);
|
|
728
734
|
},
|
|
729
735
|
},
|
|
730
736
|
"winners goals reset": {
|
|
@@ -732,7 +738,7 @@ const commands = {
|
|
|
732
738
|
async run(args, options) {
|
|
733
739
|
if (args.length > 0)
|
|
734
740
|
throw new CliError("Usage: winners goals reset", exitCodes.usage);
|
|
735
|
-
printWinnersGoals(await api(await resolveConnection(options
|
|
741
|
+
printWinnersGoals(await api(await resolveConnection(options), "/api/v1/winners/goals", { goals: null }, "PUT"), options.format);
|
|
736
742
|
},
|
|
737
743
|
},
|
|
738
744
|
examples: {
|
|
@@ -740,7 +746,7 @@ const commands = {
|
|
|
740
746
|
async run(args, options) {
|
|
741
747
|
if (args.length > 0)
|
|
742
748
|
throw new CliError("Usage: examples", exitCodes.usage);
|
|
743
|
-
const { examples } = await api(await resolveConnection(options
|
|
749
|
+
const { examples } = await api(await resolveConnection(options), "/api/v1/examples");
|
|
744
750
|
if (options.format === "json")
|
|
745
751
|
return printJson({ examples });
|
|
746
752
|
for (const e of examples) {
|
|
@@ -758,7 +764,7 @@ const commands = {
|
|
|
758
764
|
throw new CliError("Usage: content search [--text <words>] [--kind <kind>] [--sort <column>] [...]; put several words in quotes", exitCodes.usage);
|
|
759
765
|
const filters = { text: options.text, mode: options.mode, kind: options.kind, platform: options.platform, origin: options.origin, from: options.from, to: options.to, sort: options.sort, order: options.order, limit: options.limit };
|
|
760
766
|
const query = new URLSearchParams(Object.entries(filters).filter((entry) => entry[1] !== undefined));
|
|
761
|
-
const { results } = await api(await resolveConnection(options
|
|
767
|
+
const { results } = await api(await resolveConnection(options), `/api/v1/content${query.size ? `?${query}` : ""}`);
|
|
762
768
|
if (options.format === "json")
|
|
763
769
|
return printJson({ results });
|
|
764
770
|
if (results.length === 0)
|
|
@@ -784,7 +790,7 @@ const commands = {
|
|
|
784
790
|
async run(args, options) {
|
|
785
791
|
if (args.length !== 1)
|
|
786
792
|
throw new CliError("Usage: content show <sku | item-id>", exitCodes.usage);
|
|
787
|
-
const content = await api(await resolveConnection(options
|
|
793
|
+
const content = await api(await resolveConnection(options), `/api/v1/content/${encodeURIComponent(args[0])}`);
|
|
788
794
|
printContent(content, options.format);
|
|
789
795
|
},
|
|
790
796
|
},
|
|
@@ -794,7 +800,7 @@ const commands = {
|
|
|
794
800
|
if (args.length !== 1)
|
|
795
801
|
throw new CliError("Usage: content pull <dir> [--kind <kind>] [--platform <platform>] [--origin discovered|made] [--media] [--full]", exitCodes.usage);
|
|
796
802
|
const filters = Object.fromEntries(Object.entries({ kind: options.kind, platform: options.platform, origin: options.origin }).filter((e) => e[1] !== undefined));
|
|
797
|
-
const report = await pullContent(await resolveConnection(options
|
|
803
|
+
const report = await pullContent(await resolveConnection(options), args[0], filters, { media: options.media, full: options.full });
|
|
798
804
|
if (options.format === "json")
|
|
799
805
|
return printJson(report);
|
|
800
806
|
const media = options.media ? `, ${report.media} new media file${report.media === 1 ? "" : "s"}` : "";
|
|
@@ -810,7 +816,7 @@ const commands = {
|
|
|
810
816
|
if (args.length !== 1)
|
|
811
817
|
throw new CliError("Usage: content diff <sku | item-id> [--from <n>] [--to <n>]", exitCodes.usage);
|
|
812
818
|
const query = new URLSearchParams(Object.entries({ from: options.from, to: options.to }).filter((entry) => entry[1] !== undefined));
|
|
813
|
-
const diff = await api(await resolveConnection(options
|
|
819
|
+
const diff = await api(await resolveConnection(options), `/api/v1/content/${encodeURIComponent(args[0])}/diff${query.size ? `?${query}` : ""}`);
|
|
814
820
|
if (options.format === "json")
|
|
815
821
|
return printJson(diff);
|
|
816
822
|
printContentDiff(diff);
|
|
@@ -821,7 +827,7 @@ const commands = {
|
|
|
821
827
|
async run(args, options) {
|
|
822
828
|
if (args.length !== 1)
|
|
823
829
|
throw new CliError("Usage: content edit <sku | item-id> [--body <text> | @file] [--note <why>] [...]", exitCodes.usage);
|
|
824
|
-
const connection = await resolveConnection(options
|
|
830
|
+
const connection = await resolveConnection(options);
|
|
825
831
|
const input = await contentInput(options, connection);
|
|
826
832
|
if (Object.keys(input).every((k) => k === "runId" || k === "note")) {
|
|
827
833
|
throw new CliError("content edit needs something to change, such as --body, --subject or --file.", exitCodes.usage);
|
|
@@ -835,7 +841,7 @@ const commands = {
|
|
|
835
841
|
async run(args, options) {
|
|
836
842
|
if (args.length !== 2)
|
|
837
843
|
throw new CliError("Usage: content status <sku | item-id> <status> [--platform <platform> [--object-type <type>] --external-id <id>]", exitCodes.usage);
|
|
838
|
-
const content = await api(await resolveConnection(options
|
|
844
|
+
const content = await api(await resolveConnection(options), `/api/v1/content/${encodeURIComponent(args[0])}/status`, {
|
|
839
845
|
status: args[1],
|
|
840
846
|
...(options.platform === undefined ? {} : { platform: options.platform }),
|
|
841
847
|
...(options.objectType === undefined ? {} : { objectType: options.objectType }),
|
|
@@ -853,7 +859,7 @@ const commands = {
|
|
|
853
859
|
const version = options.version === undefined ? undefined : Number(options.version);
|
|
854
860
|
if (version !== undefined && !(Number.isInteger(version) && version >= 1))
|
|
855
861
|
throw new CliError("--version must be a whole number from 1.", exitCodes.usage);
|
|
856
|
-
const { publication } = await api(await resolveConnection(options
|
|
862
|
+
const { publication } = await api(await resolveConnection(options), `/api/v1/content/${encodeURIComponent(args[0])}/publish`, {
|
|
857
863
|
publisher: options.to,
|
|
858
864
|
target: await parseJson(options.target, "--target"),
|
|
859
865
|
idempotencyKey: options.idempotencyKey,
|
|
@@ -869,7 +875,7 @@ const commands = {
|
|
|
869
875
|
async run(args, options) {
|
|
870
876
|
if (args.length !== 1)
|
|
871
877
|
throw new CliError("Usage: content reconcile <publication-id>", exitCodes.usage);
|
|
872
|
-
const { publication } = await api(await resolveConnection(options
|
|
878
|
+
const { publication } = await api(await resolveConnection(options), `/api/v1/publications/${encodeURIComponent(args[0])}/reconcile`, {});
|
|
873
879
|
printPublication(publication, options.format);
|
|
874
880
|
},
|
|
875
881
|
},
|
|
@@ -879,7 +885,7 @@ const commands = {
|
|
|
879
885
|
if (args.length !== 0 || options.plan === undefined || !options.idempotencyKey) {
|
|
880
886
|
throw new CliError("Usage: meta adset publish --plan '<json>' --idempotency-key <key> [--run <run-id>]", exitCodes.usage);
|
|
881
887
|
}
|
|
882
|
-
const { adsetPublication } = await api(await resolveConnection(options
|
|
888
|
+
const { adsetPublication } = await api(await resolveConnection(options), "/api/v1/meta/adsets", {
|
|
883
889
|
plan: await parseJson(options.plan, "--plan"),
|
|
884
890
|
idempotencyKey: options.idempotencyKey,
|
|
885
891
|
...(options.run === undefined ? {} : { runId: options.run }),
|
|
@@ -892,7 +898,7 @@ const commands = {
|
|
|
892
898
|
async run(args, options) {
|
|
893
899
|
if (args.length !== 1)
|
|
894
900
|
throw new CliError("Usage: meta adset show <adset-publication-id>", exitCodes.usage);
|
|
895
|
-
const { adsetPublication } = await api(await resolveConnection(options
|
|
901
|
+
const { adsetPublication } = await api(await resolveConnection(options), `/api/v1/meta/adsets/${encodeURIComponent(args[0])}`);
|
|
896
902
|
printAdSetPublication(adsetPublication, options.format);
|
|
897
903
|
},
|
|
898
904
|
},
|
|
@@ -901,7 +907,7 @@ const commands = {
|
|
|
901
907
|
async run(args, options) {
|
|
902
908
|
if (args.length !== 1)
|
|
903
909
|
throw new CliError("Usage: meta adset reconcile <adset-publication-id>", exitCodes.usage);
|
|
904
|
-
const { adsetPublication } = await api(await resolveConnection(options
|
|
910
|
+
const { adsetPublication } = await api(await resolveConnection(options), `/api/v1/meta/adsets/${encodeURIComponent(args[0])}/reconcile`, {});
|
|
905
911
|
printAdSetPublication(adsetPublication, options.format);
|
|
906
912
|
},
|
|
907
913
|
},
|
|
@@ -910,7 +916,7 @@ const commands = {
|
|
|
910
916
|
async run(args, options) {
|
|
911
917
|
if (args.length !== 1)
|
|
912
918
|
throw new CliError("Usage: content unpublish <deployment-id> [--force]", exitCodes.usage);
|
|
913
|
-
const { deployment } = await api(await resolveConnection(options
|
|
919
|
+
const { deployment } = await api(await resolveConnection(options), `/api/v1/deployments/${encodeURIComponent(args[0])}/unpublish`, options.force ? { force: true } : {});
|
|
914
920
|
if (options.format === "json")
|
|
915
921
|
return printJson({ deployment });
|
|
916
922
|
printFields({
|
|
@@ -928,7 +934,7 @@ const commands = {
|
|
|
928
934
|
async run(args, options) {
|
|
929
935
|
if (args.length !== 1)
|
|
930
936
|
throw new CliError("Usage: content publish-record <sku | item-id> --file <publication.json>", exitCodes.usage);
|
|
931
|
-
const { publication } = await api(await resolveConnection(options
|
|
937
|
+
const { publication } = await api(await resolveConnection(options), `/api/v1/content/${encodeURIComponent(args[0])}/publications`, await readToolInput(options));
|
|
932
938
|
printPublication(publication, options.format);
|
|
933
939
|
},
|
|
934
940
|
},
|
|
@@ -939,7 +945,7 @@ const commands = {
|
|
|
939
945
|
throw new CliError("Usage: content publish-finish <publication-id> --status sending|published|failed|not_sent_practice [--file <outcome.json>]", exitCodes.usage);
|
|
940
946
|
}
|
|
941
947
|
const outcome = await readToolInput(options);
|
|
942
|
-
const { publication } = await api(await resolveConnection(options
|
|
948
|
+
const { publication } = await api(await resolveConnection(options), `/api/v1/publications/${encodeURIComponent(args[0])}`, { ...outcome, status: options.status }, "PATCH");
|
|
943
949
|
printPublication(publication, options.format);
|
|
944
950
|
},
|
|
945
951
|
},
|
|
@@ -948,7 +954,7 @@ const commands = {
|
|
|
948
954
|
async run(args, options) {
|
|
949
955
|
if (args.length > 0)
|
|
950
956
|
throw new CliError("Usage: agent start", exitCodes.usage);
|
|
951
|
-
const start = await api(await resolveConnection(options
|
|
957
|
+
const start = await api(await resolveConnection(options), `${agentPath(options)}/runs`, {});
|
|
952
958
|
if (options.format === "json")
|
|
953
959
|
return printJson(start);
|
|
954
960
|
const { run, quota, brandProfile } = start;
|
|
@@ -968,7 +974,7 @@ const commands = {
|
|
|
968
974
|
async run(args, options) {
|
|
969
975
|
if (args.length !== 1)
|
|
970
976
|
throw new CliError("Usage: agent show <run-id>", exitCodes.usage);
|
|
971
|
-
const shown = await api(await resolveConnection(options
|
|
977
|
+
const shown = await api(await resolveConnection(options), `${agentPath(options)}/runs/${encodeURIComponent(args[0])}`);
|
|
972
978
|
if (options.format === "json")
|
|
973
979
|
return printJson(shown);
|
|
974
980
|
const { run, proposals } = shown;
|
|
@@ -982,7 +988,7 @@ const commands = {
|
|
|
982
988
|
usage: "agent runs [--limit <n>]",
|
|
983
989
|
async run(_args, options) {
|
|
984
990
|
const query = options.limit ? `?limit=${encodeURIComponent(options.limit)}` : "";
|
|
985
|
-
const { runs } = await api(await resolveConnection(options
|
|
991
|
+
const { runs } = await api(await resolveConnection(options), `${agentPath(options)}/runs${query}`);
|
|
986
992
|
if (options.format === "json")
|
|
987
993
|
return printJson({ runs });
|
|
988
994
|
if (runs.length === 0)
|
|
@@ -997,7 +1003,7 @@ const commands = {
|
|
|
997
1003
|
throw new CliError("Usage: agent finish <run-id> --status succeeded|failed [--file report.md]", exitCodes.usage);
|
|
998
1004
|
}
|
|
999
1005
|
const report = await readText(options.file, "the report");
|
|
1000
|
-
const { run } = await api(await resolveConnection(options
|
|
1006
|
+
const { run } = await api(await resolveConnection(options), `${agentPath(options)}/runs/${encodeURIComponent(args[0])}/finish`, { status: options.status, report });
|
|
1001
1007
|
if (options.format === "json")
|
|
1002
1008
|
return printJson({ run });
|
|
1003
1009
|
console.log(`Finished run ${run.id} as ${run.status}.`);
|
|
@@ -1008,7 +1014,7 @@ const commands = {
|
|
|
1008
1014
|
async run(args, options) {
|
|
1009
1015
|
if (args.length > 0)
|
|
1010
1016
|
throw new CliError("Usage: agent draft --agent p3_refresh", exitCodes.usage);
|
|
1011
|
-
const body = await api(await resolveConnection(options
|
|
1017
|
+
const body = await api(await resolveConnection(options), `${agentPath(options)}/draft`);
|
|
1012
1018
|
if (options.format === "json")
|
|
1013
1019
|
return printJson(body);
|
|
1014
1020
|
const { draft } = body;
|
|
@@ -1049,7 +1055,7 @@ const commands = {
|
|
|
1049
1055
|
"brand get": {
|
|
1050
1056
|
usage: "brand get",
|
|
1051
1057
|
async run(_args, options) {
|
|
1052
|
-
const { profile } = await api(await resolveConnection(options
|
|
1058
|
+
const { profile } = await api(await resolveConnection(options), "/api/v1/brand-profile");
|
|
1053
1059
|
if (options.format === "json")
|
|
1054
1060
|
return printJson({ profile });
|
|
1055
1061
|
if (!profile)
|
|
@@ -1065,7 +1071,7 @@ const commands = {
|
|
|
1065
1071
|
throw new CliError("Usage: brand set [--file <path>] [--sources '<json>' | @file.json]", exitCodes.usage);
|
|
1066
1072
|
const markdown = await readText(options.file, "the brand profile");
|
|
1067
1073
|
const sources = options.sources === undefined ? [] : await parseJson(options.sources, "--sources");
|
|
1068
|
-
const { profile } = await api(await resolveConnection(options
|
|
1074
|
+
const { profile } = await api(await resolveConnection(options), "/api/v1/brand-profile", { markdown, sources }, "PUT");
|
|
1069
1075
|
if (options.format === "json")
|
|
1070
1076
|
return printJson({ profile });
|
|
1071
1077
|
console.log(`Saved brand profile version ${profile.version}.`);
|
|
@@ -1076,7 +1082,7 @@ const commands = {
|
|
|
1076
1082
|
async run(args, options) {
|
|
1077
1083
|
if (args.length !== 1)
|
|
1078
1084
|
throw new CliError("Usage: content add-page <url>", exitCodes.usage);
|
|
1079
|
-
const { page } = await api(await resolveConnection(options
|
|
1085
|
+
const { page } = await api(await resolveConnection(options), "/api/v1/content/pages", { url: args[0] });
|
|
1080
1086
|
if (options.format === "json")
|
|
1081
1087
|
return printJson({ page });
|
|
1082
1088
|
console.log(renderTable(["key", "status", "version", "changed", "headline", "blocks"], [{ ...page, blocks: page.blockCount }]));
|
|
@@ -1090,7 +1096,7 @@ const commands = {
|
|
|
1090
1096
|
async run(args, options) {
|
|
1091
1097
|
if (args.length !== 2)
|
|
1092
1098
|
throw new CliError("Usage: content track-link <sku> <url> [--channel facebook|email|sms|web] [--campaign <id>]", exitCodes.usage);
|
|
1093
|
-
const { url } = await api(await resolveConnection(options
|
|
1099
|
+
const { url } = await api(await resolveConnection(options), "/api/v1/content/track-link", {
|
|
1094
1100
|
sku: args[0],
|
|
1095
1101
|
url: args[1],
|
|
1096
1102
|
channel: options.channel,
|
|
@@ -1106,7 +1112,7 @@ const commands = {
|
|
|
1106
1112
|
async run(args, options) {
|
|
1107
1113
|
if (args.length !== 1)
|
|
1108
1114
|
throw new CliError("Usage: trail <plan-id | sku | item-id>", exitCodes.usage);
|
|
1109
|
-
const connection = await resolveConnection(options
|
|
1115
|
+
const connection = await resolveConnection(options);
|
|
1110
1116
|
const ref = args[0];
|
|
1111
1117
|
const trailOf = (query) => api(connection, `/api/v1/trail?${query}=${encodeURIComponent(ref)}`);
|
|
1112
1118
|
// Plans and items both have uuid ids: an id that names no plan is read as an item.
|
|
@@ -1126,7 +1132,7 @@ const commands = {
|
|
|
1126
1132
|
if (args.length > 0)
|
|
1127
1133
|
throw new CliError("Usage: funnels show [--version <n>]", exitCodes.usage);
|
|
1128
1134
|
const query = options.version === undefined ? "" : `?version=${wholeNumber(options.version, "--version")}`;
|
|
1129
|
-
const view = await api(await resolveConnection(options
|
|
1135
|
+
const view = await api(await resolveConnection(options), `${FUNNELS_PATH}${query}`);
|
|
1130
1136
|
if (options.format === "json")
|
|
1131
1137
|
return printJson(view);
|
|
1132
1138
|
process.stdout.write(view.text);
|
|
@@ -1137,7 +1143,7 @@ const commands = {
|
|
|
1137
1143
|
async run(args, options) {
|
|
1138
1144
|
if (args.length > 0)
|
|
1139
1145
|
throw new CliError("Usage: funnels numbers [--funnel <key>] [--from <date>] [--to <date>]", exitCodes.usage);
|
|
1140
|
-
const view = await api(await resolveConnection(options
|
|
1146
|
+
const view = await api(await resolveConnection(options), `${FUNNELS_PATH}/numbers${reportQuery(options, { funnel: options.funnel })}`);
|
|
1141
1147
|
if (options.format === "json")
|
|
1142
1148
|
return printJson(view);
|
|
1143
1149
|
process.stdout.write(view.text);
|
|
@@ -1148,7 +1154,7 @@ const commands = {
|
|
|
1148
1154
|
async run(args, options) {
|
|
1149
1155
|
if (args.length > 0 || options.from !== undefined)
|
|
1150
1156
|
throw new CliError("Usage: funnels leaks [--funnel <key>] [--to <date>]", exitCodes.usage);
|
|
1151
|
-
const view = await api(await resolveConnection(options
|
|
1157
|
+
const view = await api(await resolveConnection(options), `${FUNNELS_PATH}/leaks${reportQuery(options, { funnel: options.funnel })}`);
|
|
1152
1158
|
if (options.format === "json")
|
|
1153
1159
|
return printJson(view);
|
|
1154
1160
|
process.stdout.write(view.text);
|
|
@@ -1159,7 +1165,7 @@ const commands = {
|
|
|
1159
1165
|
async run(args, options) {
|
|
1160
1166
|
if (args.length > 0)
|
|
1161
1167
|
throw new CliError("Usage: funnels evidence", exitCodes.usage);
|
|
1162
|
-
const view = await api(await resolveConnection(options
|
|
1168
|
+
const view = await api(await resolveConnection(options), `${FUNNELS_PATH}/evidence`);
|
|
1163
1169
|
if (options.format === "json")
|
|
1164
1170
|
return printJson(view);
|
|
1165
1171
|
process.stdout.write(view.text);
|
|
@@ -1177,7 +1183,7 @@ const commands = {
|
|
|
1177
1183
|
const map = await parseJson(`@${options.file}`, "--file");
|
|
1178
1184
|
const evidence = options.evidence === undefined ? undefined : await parseJson(options.evidence, "--evidence");
|
|
1179
1185
|
const questions = options.questions === undefined ? undefined : await parseJson(options.questions, "--questions");
|
|
1180
|
-
const connection = await resolveConnection(options
|
|
1186
|
+
const connection = await resolveConnection(options);
|
|
1181
1187
|
const basedOn = options.basedOn === undefined ? null : wholeNumber(options.basedOn, "--based-on");
|
|
1182
1188
|
const saved = await api(connection, `${FUNNELS_PATH}/draft`, { map, note: options.note, evidence, based_on: basedOn, counts_from: options.countsFrom, questions }, "PUT");
|
|
1183
1189
|
if (options.format === "json")
|
|
@@ -1207,7 +1213,7 @@ const commands = {
|
|
|
1207
1213
|
"funnels versions": {
|
|
1208
1214
|
usage: "funnels versions [--diff <a> <b>] every version of the map, or what changed from version a to b",
|
|
1209
1215
|
async run(args, options) {
|
|
1210
|
-
const connection = await resolveConnection(options
|
|
1216
|
+
const connection = await resolveConnection(options);
|
|
1211
1217
|
if (options.diff !== undefined) {
|
|
1212
1218
|
if (args.length !== 1)
|
|
1213
1219
|
throw new CliError("Usage: funnels versions --diff <a> <b>", exitCodes.usage);
|
|
@@ -1254,7 +1260,7 @@ const commands = {
|
|
|
1254
1260
|
}
|
|
1255
1261
|
if (options.status)
|
|
1256
1262
|
query.set("status", options.status);
|
|
1257
|
-
const { facts } = await api(await resolveConnection(options
|
|
1263
|
+
const { facts } = await api(await resolveConnection(options), `${FACTS_PATH}${query.size ? `?${query}` : ""}`);
|
|
1258
1264
|
if (options.format === "json")
|
|
1259
1265
|
return printJson({ facts });
|
|
1260
1266
|
if (facts.length === 0)
|
|
@@ -1276,7 +1282,7 @@ const commands = {
|
|
|
1276
1282
|
query.set("sort", options.sort);
|
|
1277
1283
|
if (options.limit)
|
|
1278
1284
|
query.set("limit", options.limit);
|
|
1279
|
-
const read = await api(await resolveConnection(options
|
|
1285
|
+
const read = await api(await resolveConnection(options), `${VOICE_PATH}${query.size ? `?${query}` : ""}`);
|
|
1280
1286
|
if (options.format === "json")
|
|
1281
1287
|
return printJson(read);
|
|
1282
1288
|
if ("lines" in read) {
|
|
@@ -1301,7 +1307,7 @@ const commands = {
|
|
|
1301
1307
|
query.set("status", options.status);
|
|
1302
1308
|
if (options.limit)
|
|
1303
1309
|
query.set("limit", options.limit);
|
|
1304
|
-
const { tags } = await api(await resolveConnection(options
|
|
1310
|
+
const { tags } = await api(await resolveConnection(options), `${AD_TAGS_PATH}${query.size ? `?${query}` : ""}`);
|
|
1305
1311
|
if (options.format === "json")
|
|
1306
1312
|
return printJson({ tags });
|
|
1307
1313
|
if (tags.length === 0)
|
|
@@ -1314,7 +1320,7 @@ const commands = {
|
|
|
1314
1320
|
async run(args, options) {
|
|
1315
1321
|
if (args.length > 0)
|
|
1316
1322
|
throw new CliError("Usage: ads tag --file <tags.json> | < tags.json", exitCodes.usage);
|
|
1317
|
-
const result = await api(await resolveConnection(options
|
|
1323
|
+
const result = await api(await resolveConnection(options), AD_TAGS_PATH, await readToolInput(options));
|
|
1318
1324
|
if (options.format === "json")
|
|
1319
1325
|
return printJson(result);
|
|
1320
1326
|
console.log(`Proposed tags for ${result.proposed.length} ${result.proposed.length === 1 ? "ad" : "ads"}. A person confirms or rejects them in the app.`);
|
|
@@ -1328,7 +1334,7 @@ const commands = {
|
|
|
1328
1334
|
if (args.length > 0)
|
|
1329
1335
|
throw new CliError("Usage: ads autotag [--limit <n>] [--ad <id>] [--json]", exitCodes.usage);
|
|
1330
1336
|
const body = { ...(options.ad ? { ad_ids: [options.ad] } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) };
|
|
1331
|
-
const result = await api(await resolveConnection(options
|
|
1337
|
+
const result = await api(await resolveConnection(options), AD_AUTOTAG_PATH, body);
|
|
1332
1338
|
if (options.format === "json")
|
|
1333
1339
|
return printJson(result);
|
|
1334
1340
|
const plural = (n) => `${n} ${n === 1 ? "ad" : "ads"}`;
|
|
@@ -1355,7 +1361,7 @@ const commands = {
|
|
|
1355
1361
|
async run(args, options) {
|
|
1356
1362
|
if (args.length > 0)
|
|
1357
1363
|
throw new CliError("Usage: ads tag-formats [--limit <n>] [--json]", exitCodes.usage);
|
|
1358
|
-
const connection = await resolveConnection(options
|
|
1364
|
+
const connection = await resolveConnection(options);
|
|
1359
1365
|
const total = { tagged: 0, no_format: 0 };
|
|
1360
1366
|
let after = null;
|
|
1361
1367
|
for (;;) {
|
|
@@ -1384,7 +1390,7 @@ const commands = {
|
|
|
1384
1390
|
throw new CliError("Usage: content tags [<item-id>] [--kind email|text|page] [--status <status>] [--limit <n>] [--json]", exitCodes.usage);
|
|
1385
1391
|
const filters = { item: args[0], kind: options.kind, status: options.status, limit: options.limit };
|
|
1386
1392
|
const query = new URLSearchParams(Object.entries(filters).filter((entry) => entry[1] !== undefined));
|
|
1387
|
-
const { tags } = await api(await resolveConnection(options
|
|
1393
|
+
const { tags } = await api(await resolveConnection(options), `${CONTENT_TAGS_PATH}${query.size ? `?${query}` : ""}`);
|
|
1388
1394
|
if (options.format === "json")
|
|
1389
1395
|
return printJson({ tags });
|
|
1390
1396
|
if (tags.length === 0)
|
|
@@ -1398,7 +1404,7 @@ const commands = {
|
|
|
1398
1404
|
if (args.length > 0)
|
|
1399
1405
|
throw new CliError("Usage: content to-check [--limit <n>] [--json]", exitCodes.usage);
|
|
1400
1406
|
const query = new URLSearchParams({ to_check: "1", ...(options.limit ? { limit: options.limit } : {}) });
|
|
1401
|
-
const { tags } = await api(await resolveConnection(options
|
|
1407
|
+
const { tags } = await api(await resolveConnection(options), `${CONTENT_TAGS_PATH}?${query}`);
|
|
1402
1408
|
if (options.format === "json")
|
|
1403
1409
|
return printJson({ tags });
|
|
1404
1410
|
if (tags.length === 0)
|
|
@@ -1414,7 +1420,7 @@ const commands = {
|
|
|
1414
1420
|
...(options.kind ? { kinds: options.kind.split(",") } : {}),
|
|
1415
1421
|
...(options.limit ? { limit: Number(options.limit) } : {}),
|
|
1416
1422
|
};
|
|
1417
|
-
const result = await api(await resolveConnection(options
|
|
1423
|
+
const result = await api(await resolveConnection(options), CONTENT_AUTOTAG_PATH, body);
|
|
1418
1424
|
if (options.format === "json")
|
|
1419
1425
|
return printJson(result);
|
|
1420
1426
|
const plural = (n) => `${n} ${n === 1 ? "item" : "items"}`;
|
|
@@ -1439,7 +1445,7 @@ const commands = {
|
|
|
1439
1445
|
async run(args, options) {
|
|
1440
1446
|
if (args.length !== 1)
|
|
1441
1447
|
throw new CliError("Usage: facts show <id>", exitCodes.usage);
|
|
1442
|
-
const { fact } = await api(await resolveConnection(options
|
|
1448
|
+
const { fact } = await api(await resolveConnection(options), `${FACTS_PATH}/${encodeURIComponent(args[0])}`);
|
|
1443
1449
|
if (options.format === "json")
|
|
1444
1450
|
return printJson({ fact });
|
|
1445
1451
|
const nameKey = fact.key === NAME_KEY ? nameKeyLines(fact.value) : null;
|
|
@@ -1454,7 +1460,7 @@ const commands = {
|
|
|
1454
1460
|
async run(args, options) {
|
|
1455
1461
|
if (args.length > 0)
|
|
1456
1462
|
throw new CliError("Usage: facts propose --file <fact.json> | < fact.json", exitCodes.usage);
|
|
1457
|
-
const { fact } = await api(await resolveConnection(options
|
|
1463
|
+
const { fact } = await api(await resolveConnection(options), FACTS_PATH, await readToolInput(options));
|
|
1458
1464
|
if (options.format === "json")
|
|
1459
1465
|
return printJson({ fact });
|
|
1460
1466
|
console.log(`Proposed fact ${fact.id}. A person confirms or rejects it in the app.`);
|
|
@@ -1474,7 +1480,7 @@ const commands = {
|
|
|
1474
1480
|
for (const dimension of options.dimension ?? [])
|
|
1475
1481
|
params.append("dimension", dimension);
|
|
1476
1482
|
const query = params.size > 0 ? `?${params}` : "";
|
|
1477
|
-
const { lessons } = await api(await resolveConnection(options
|
|
1483
|
+
const { lessons } = await api(await resolveConnection(options), `${LESSONS_PATH}${query}`);
|
|
1478
1484
|
if (options.format === "json")
|
|
1479
1485
|
return printJson({ lessons });
|
|
1480
1486
|
if (lessons.length === 0) {
|
|
@@ -1489,7 +1495,7 @@ const commands = {
|
|
|
1489
1495
|
async run(args, options) {
|
|
1490
1496
|
if (args.length > 0)
|
|
1491
1497
|
throw new CliError("Usage: lessons add --file <lesson.json> | < lesson.json", exitCodes.usage);
|
|
1492
|
-
const recorded = await api(await resolveConnection(options
|
|
1498
|
+
const recorded = await api(await resolveConnection(options), LESSONS_PATH, await readToolInput(options));
|
|
1493
1499
|
if (options.format === "json")
|
|
1494
1500
|
return printJson(recorded);
|
|
1495
1501
|
const { lesson, outcome } = recorded;
|
|
@@ -1513,7 +1519,7 @@ const commands = {
|
|
|
1513
1519
|
query.set("funnel", options.funnel);
|
|
1514
1520
|
if (options.limit)
|
|
1515
1521
|
query.set("limit", options.limit);
|
|
1516
|
-
const { tests } = await api(await resolveConnection(options
|
|
1522
|
+
const { tests } = await api(await resolveConnection(options), `${TESTS_PATH}${query.size ? `?${query}` : ""}`);
|
|
1517
1523
|
if (options.format === "json")
|
|
1518
1524
|
return printJson({ tests });
|
|
1519
1525
|
if (tests.length === 0)
|
|
@@ -1526,7 +1532,7 @@ const commands = {
|
|
|
1526
1532
|
async run(args, options) {
|
|
1527
1533
|
if (args.length !== 1)
|
|
1528
1534
|
throw new CliError("Usage: tests show <id|test_id>", exitCodes.usage);
|
|
1529
|
-
const { test } = await api(await resolveConnection(options
|
|
1535
|
+
const { test } = await api(await resolveConnection(options), testPath(args[0]));
|
|
1530
1536
|
if (options.format === "json")
|
|
1531
1537
|
return printJson({ test });
|
|
1532
1538
|
printTest(test);
|
|
@@ -1537,7 +1543,7 @@ const commands = {
|
|
|
1537
1543
|
async run(args, options) {
|
|
1538
1544
|
if (args.length > 0)
|
|
1539
1545
|
throw new CliError("Usage: tests draft --file <test.json> | < test.json", exitCodes.usage);
|
|
1540
|
-
const { test } = await api(await resolveConnection(options
|
|
1546
|
+
const { test } = await api(await resolveConnection(options), TESTS_PATH, await readToolInput(options));
|
|
1541
1547
|
if (options.format === "json")
|
|
1542
1548
|
return printJson({ test });
|
|
1543
1549
|
console.log(`Drafted test ${test.test_id} (${test.id}).`);
|
|
@@ -1548,7 +1554,7 @@ const commands = {
|
|
|
1548
1554
|
async run(args, options) {
|
|
1549
1555
|
if (args.length !== 1)
|
|
1550
1556
|
throw new CliError("Usage: tests start <id|test_id>", exitCodes.usage);
|
|
1551
|
-
const { test } = await api(await resolveConnection(options
|
|
1557
|
+
const { test } = await api(await resolveConnection(options), `${testPath(args[0])}/start`, {});
|
|
1552
1558
|
if (options.format === "json")
|
|
1553
1559
|
return printJson({ test });
|
|
1554
1560
|
console.log(`Test ${test.test_id} is running from ${test.started_on}.`);
|
|
@@ -1559,7 +1565,7 @@ const commands = {
|
|
|
1559
1565
|
async run(args, options) {
|
|
1560
1566
|
if (args.length !== 1)
|
|
1561
1567
|
throw new CliError('Usage: tests abandon <id|test_id> [--note "<why>"]', exitCodes.usage);
|
|
1562
|
-
const { test } = await api(await resolveConnection(options
|
|
1568
|
+
const { test } = await api(await resolveConnection(options), `${testPath(args[0])}/abandon`, options.note ? { note: options.note } : {});
|
|
1563
1569
|
if (options.format === "json")
|
|
1564
1570
|
return printJson({ test });
|
|
1565
1571
|
console.log(`Abandoned test ${test.test_id}.`);
|
|
@@ -1570,7 +1576,7 @@ const commands = {
|
|
|
1570
1576
|
async run(args, options) {
|
|
1571
1577
|
if (args.length !== 1)
|
|
1572
1578
|
throw new CliError("Usage: tests results <id|test_id>", exitCodes.usage);
|
|
1573
|
-
const { results } = await api(await resolveConnection(options
|
|
1579
|
+
const { results } = await api(await resolveConnection(options), `${testPath(args[0])}/results`);
|
|
1574
1580
|
if (options.format === "json")
|
|
1575
1581
|
return printJson({ results });
|
|
1576
1582
|
printResults(results);
|
|
@@ -1582,7 +1588,7 @@ const commands = {
|
|
|
1582
1588
|
if (args.length !== 1 || !options.decision) {
|
|
1583
1589
|
throw new CliError('Usage: tests decide <id|test_id> --decision <scale|continue|iterate|hold|stop> [--note "<why>"]', exitCodes.usage);
|
|
1584
1590
|
}
|
|
1585
|
-
const decided = await api(await resolveConnection(options
|
|
1591
|
+
const decided = await api(await resolveConnection(options), `${testPath(args[0])}/decide`, options.note ? { decision: options.decision, note: options.note } : { decision: options.decision });
|
|
1586
1592
|
if (options.format === "json")
|
|
1587
1593
|
return printJson(decided);
|
|
1588
1594
|
const { test, lesson, next_test: next } = decided;
|
|
@@ -1598,7 +1604,7 @@ const commands = {
|
|
|
1598
1604
|
async run(args, options) {
|
|
1599
1605
|
if (args.length !== 1 || !options.note)
|
|
1600
1606
|
throw new CliError('Usage: lessons retire <id> --note "<why it is no longer true>"', exitCodes.usage);
|
|
1601
|
-
const { lesson } = await api(await resolveConnection(options
|
|
1607
|
+
const { lesson } = await api(await resolveConnection(options), `${LESSONS_PATH}/${encodeURIComponent(args[0])}/retire`, { reason: options.note });
|
|
1602
1608
|
if (options.format === "json")
|
|
1603
1609
|
return printJson({ lesson });
|
|
1604
1610
|
console.log(`Retired lesson ${lesson.id}.`);
|
|
@@ -1609,7 +1615,7 @@ const commands = {
|
|
|
1609
1615
|
async run(args, options) {
|
|
1610
1616
|
if (args.length > 0)
|
|
1611
1617
|
throw new CliError("Usage: grammar | grammar propose --file <values.json>", exitCodes.usage);
|
|
1612
|
-
const answer = await api(await resolveConnection(options
|
|
1618
|
+
const answer = await api(await resolveConnection(options), GRAMMAR_PATH);
|
|
1613
1619
|
if (options.format === "json")
|
|
1614
1620
|
return printJson(answer);
|
|
1615
1621
|
process.stdout.write(answer.text);
|
|
@@ -1620,7 +1626,7 @@ const commands = {
|
|
|
1620
1626
|
async run(args, options) {
|
|
1621
1627
|
if (args.length > 0)
|
|
1622
1628
|
throw new CliError("Usage: grammar propose --file <values.json> | < values.json", exitCodes.usage);
|
|
1623
|
-
const result = await api(await resolveConnection(options
|
|
1629
|
+
const result = await api(await resolveConnection(options), `${GRAMMAR_PATH}/proposals`, await readToolInput(options));
|
|
1624
1630
|
if (options.format === "json")
|
|
1625
1631
|
return printJson(result);
|
|
1626
1632
|
const named = (refs) => refs.map((r) => `${r.part} ${r.key}`).join(", ");
|
|
@@ -1634,7 +1640,7 @@ const commands = {
|
|
|
1634
1640
|
async run(args, options) {
|
|
1635
1641
|
if (args.length > 0)
|
|
1636
1642
|
throw new CliError("Usage: voice lists", exitCodes.usage);
|
|
1637
|
-
const lists = await api(await resolveConnection(options
|
|
1643
|
+
const lists = await api(await resolveConnection(options), VOICE_LISTS_PATH);
|
|
1638
1644
|
if (options.format === "json")
|
|
1639
1645
|
return printJson(lists);
|
|
1640
1646
|
const rows = [
|
|
@@ -1655,7 +1661,7 @@ const commands = {
|
|
|
1655
1661
|
if (!kind || !["competitor", "subreddit", "search_phrase"].includes(kind) || value === undefined || rest.length > 0) {
|
|
1656
1662
|
throw new CliError('Usage: voice suggest competitor|subreddit|search_phrase "<value>" [--note <why>]; put a value with spaces in quotes', exitCodes.usage);
|
|
1657
1663
|
}
|
|
1658
|
-
const result = await api(await resolveConnection(options
|
|
1664
|
+
const result = await api(await resolveConnection(options), VOICE_SUGGESTIONS_PATH, { kind, value, note: options.note });
|
|
1659
1665
|
if (options.format === "json")
|
|
1660
1666
|
return printJson(result);
|
|
1661
1667
|
if (result.outcome === "already_listed")
|
|
@@ -1670,7 +1676,7 @@ const commands = {
|
|
|
1670
1676
|
async run(args, options) {
|
|
1671
1677
|
if (args.length > 0)
|
|
1672
1678
|
throw new CliError("Usage: story | story list | story show <version> | story submit --file <story.json>", exitCodes.usage);
|
|
1673
|
-
const { story } = await api(await resolveConnection(options
|
|
1679
|
+
const { story } = await api(await resolveConnection(options), `${STORIES_PATH}/latest`);
|
|
1674
1680
|
if (options.format === "json")
|
|
1675
1681
|
return printJson({ story });
|
|
1676
1682
|
if (!story)
|
|
@@ -1683,7 +1689,7 @@ const commands = {
|
|
|
1683
1689
|
async run(args, options) {
|
|
1684
1690
|
if (args.length > 0)
|
|
1685
1691
|
throw new CliError("Usage: story list", exitCodes.usage);
|
|
1686
|
-
const { stories } = await api(await resolveConnection(options
|
|
1692
|
+
const { stories } = await api(await resolveConnection(options), STORIES_PATH);
|
|
1687
1693
|
if (options.format === "json")
|
|
1688
1694
|
return printJson({ stories });
|
|
1689
1695
|
if (stories.length === 0)
|
|
@@ -1706,7 +1712,7 @@ const commands = {
|
|
|
1706
1712
|
if (args.length !== 1)
|
|
1707
1713
|
throw new CliError("Usage: story show <version>", exitCodes.usage);
|
|
1708
1714
|
// `show latest` reaches the newest-story route, which answers null before the first story.
|
|
1709
|
-
const { story } = await api(await resolveConnection(options
|
|
1715
|
+
const { story } = await api(await resolveConnection(options), `${STORIES_PATH}/${encodeURIComponent(args[0])}`);
|
|
1710
1716
|
if (options.format === "json")
|
|
1711
1717
|
return printJson({ story });
|
|
1712
1718
|
if (!story)
|
|
@@ -1719,7 +1725,7 @@ const commands = {
|
|
|
1719
1725
|
async run(args, options) {
|
|
1720
1726
|
if (args.length > 0)
|
|
1721
1727
|
throw new CliError("Usage: story submit --file <story.json> | < story.json", exitCodes.usage);
|
|
1722
|
-
const { story } = await api(await resolveConnection(options
|
|
1728
|
+
const { story } = await api(await resolveConnection(options), STORIES_PATH, await readToolInput(options));
|
|
1723
1729
|
if (options.format === "json")
|
|
1724
1730
|
return printJson({ story });
|
|
1725
1731
|
console.log(`Saved story version ${story.version}.`);
|
|
@@ -1731,7 +1737,7 @@ const commands = {
|
|
|
1731
1737
|
if (args.length > 0 || !options.name || !options.goal || !options.channel) {
|
|
1732
1738
|
throw new CliError("Usage: plans create --name <name> --goal <text> --channel meta|ghl|klaviyo|web [--target '<json>'] [--landing <sku> | --landing-url <url>] [--funnel <key> --step <key>]", exitCodes.usage);
|
|
1733
1739
|
}
|
|
1734
|
-
const { plan } = await api(await resolveConnection(options
|
|
1740
|
+
const { plan } = await api(await resolveConnection(options), "/api/v1/launch-plans", {
|
|
1735
1741
|
name: options.name,
|
|
1736
1742
|
goal: await textOrFile(options.goal),
|
|
1737
1743
|
channel: options.channel,
|
|
@@ -1750,7 +1756,7 @@ const commands = {
|
|
|
1750
1756
|
async run(args, options) {
|
|
1751
1757
|
if (args.length !== 1)
|
|
1752
1758
|
throw new CliError("Usage: plans show <plan-id>", exitCodes.usage);
|
|
1753
|
-
const { plan } = await api(await resolveConnection(options
|
|
1759
|
+
const { plan } = await api(await resolveConnection(options), planPath(args[0]));
|
|
1754
1760
|
printPlan(plan, options.format);
|
|
1755
1761
|
},
|
|
1756
1762
|
},
|
|
@@ -1762,7 +1768,7 @@ const commands = {
|
|
|
1762
1768
|
const position = options.position === undefined ? undefined : Number(options.position);
|
|
1763
1769
|
if (position !== undefined && (!Number.isInteger(position) || position < 0))
|
|
1764
1770
|
throw new CliError("--position must be a whole number from 0.", exitCodes.usage);
|
|
1765
|
-
const { plan } = await api(await resolveConnection(options
|
|
1771
|
+
const { plan } = await api(await resolveConnection(options), `${planPath(args[0])}/items`, {
|
|
1766
1772
|
item: args[1],
|
|
1767
1773
|
...(options.role === undefined ? {} : { role: options.role }),
|
|
1768
1774
|
...(position === undefined ? {} : { position }),
|
|
@@ -1776,7 +1782,7 @@ const commands = {
|
|
|
1776
1782
|
if (args.length !== 2)
|
|
1777
1783
|
throw new CliError("Usage: plans remove <plan-id> <sku | item-id>", exitCodes.usage);
|
|
1778
1784
|
const path = `${planPath(args[0])}/items?item=${encodeURIComponent(args[1])}`;
|
|
1779
|
-
const { plan } = await api(await resolveConnection(options
|
|
1785
|
+
const { plan } = await api(await resolveConnection(options), path, undefined, "DELETE");
|
|
1780
1786
|
printPlan(plan, options.format);
|
|
1781
1787
|
},
|
|
1782
1788
|
},
|
|
@@ -1785,7 +1791,7 @@ const commands = {
|
|
|
1785
1791
|
async run(args, options) {
|
|
1786
1792
|
if (args.length !== 2)
|
|
1787
1793
|
throw new CliError("Usage: plans status <plan-id> draft|ready|publishing|live|done|cancelled", exitCodes.usage);
|
|
1788
|
-
const { plan } = await api(await resolveConnection(options
|
|
1794
|
+
const { plan } = await api(await resolveConnection(options), planPath(args[0]), { status: args[1] }, "PATCH");
|
|
1789
1795
|
printPlan(plan, options.format);
|
|
1790
1796
|
},
|
|
1791
1797
|
},
|
|
@@ -1794,7 +1800,7 @@ const commands = {
|
|
|
1794
1800
|
async run(args, options) {
|
|
1795
1801
|
if (args.length > 0)
|
|
1796
1802
|
throw new CliError("Usage: proposals create --input '<json>'", exitCodes.usage);
|
|
1797
|
-
const { proposal } = await api(await resolveConnection(options
|
|
1803
|
+
const { proposal } = await api(await resolveConnection(options), "/api/v1/proposals", await readToolInput(options));
|
|
1798
1804
|
if (options.format === "json")
|
|
1799
1805
|
return printJson({ proposal });
|
|
1800
1806
|
console.log(renderTable(PROPOSAL_COLUMNS, [proposalRow(proposal)]));
|
|
@@ -1813,7 +1819,7 @@ const commands = {
|
|
|
1813
1819
|
if (options.limit)
|
|
1814
1820
|
query.set("limit", options.limit);
|
|
1815
1821
|
const path = `/api/v1/proposals${query.size ? `?${query}` : ""}`;
|
|
1816
|
-
const { proposals } = await api(await resolveConnection(options
|
|
1822
|
+
const { proposals } = await api(await resolveConnection(options), path);
|
|
1817
1823
|
if (options.format === "json")
|
|
1818
1824
|
return printJson({ proposals });
|
|
1819
1825
|
if (proposals.length === 0)
|
|
@@ -1841,7 +1847,7 @@ const commands = {
|
|
|
1841
1847
|
if (options.harness !== undefined)
|
|
1842
1848
|
query.set("harnesses", options.harness);
|
|
1843
1849
|
const path = `/api/operator/skills/${name}/versions${query.size ? `?${query}` : ""}`;
|
|
1844
|
-
const published = await apiUpload(await operatorConnection(options
|
|
1850
|
+
const published = await apiUpload(await operatorConnection(options), path, zip, "application/zip");
|
|
1845
1851
|
if (options.format === "json")
|
|
1846
1852
|
return printJson(published);
|
|
1847
1853
|
console.log(`${published.name} version ${published.version}`);
|
|
@@ -1853,7 +1859,7 @@ const commands = {
|
|
|
1853
1859
|
async run(args, options) {
|
|
1854
1860
|
if (args.length > 0)
|
|
1855
1861
|
throw new CliError("Usage: skill list", exitCodes.usage);
|
|
1856
|
-
const { skills } = await api(await operatorConnection(options
|
|
1862
|
+
const { skills } = await api(await operatorConnection(options), "/api/operator/skills");
|
|
1857
1863
|
if (options.format === "json")
|
|
1858
1864
|
return printJson({ skills });
|
|
1859
1865
|
if (skills.length === 0)
|
|
@@ -1893,7 +1899,7 @@ const commands = {
|
|
|
1893
1899
|
async run(args, options) {
|
|
1894
1900
|
if (args.length > 0)
|
|
1895
1901
|
throw new CliError("Usage: worker list", exitCodes.usage);
|
|
1896
|
-
const { workers } = await api(await resolveConnection(options
|
|
1902
|
+
const { workers } = await api(await resolveConnection(options), "/api/v1/workers");
|
|
1897
1903
|
if (options.format === "json")
|
|
1898
1904
|
return printJson({ workers });
|
|
1899
1905
|
if (workers.length === 0)
|
|
@@ -1917,7 +1923,7 @@ const commands = {
|
|
|
1917
1923
|
async run(args, options) {
|
|
1918
1924
|
if (args.length !== 1)
|
|
1919
1925
|
throw new CliError("Usage: worker runs <hand-id | skill> [--limit <n>]", exitCodes.usage);
|
|
1920
|
-
const connection = await resolveConnection(options
|
|
1926
|
+
const connection = await resolveConnection(options);
|
|
1921
1927
|
const id = await workerId(connection, args[0]);
|
|
1922
1928
|
const query = options.limit === undefined ? "" : `?limit=${encodeURIComponent(options.limit)}`;
|
|
1923
1929
|
const { runs } = await api(connection, `/api/v1/workers/${encodeURIComponent(id)}/runs${query}`);
|
|
@@ -1933,7 +1939,7 @@ const commands = {
|
|
|
1933
1939
|
async run(args, options) {
|
|
1934
1940
|
if (args.length !== 1)
|
|
1935
1941
|
throw new CliError("Usage: worker run-now <hand-id | skill>", exitCodes.usage);
|
|
1936
|
-
const connection = await resolveConnection(options
|
|
1942
|
+
const connection = await resolveConnection(options);
|
|
1937
1943
|
const id = await workerId(connection, args[0]);
|
|
1938
1944
|
const queued = await api(connection, `/api/v1/workers/${encodeURIComponent(id)}/run-now`, {});
|
|
1939
1945
|
if (options.format === "json")
|
|
@@ -1993,6 +1999,18 @@ const commands = {
|
|
|
1993
1999
|
},
|
|
1994
2000
|
},
|
|
1995
2001
|
};
|
|
2002
|
+
function clientReason(choice) {
|
|
2003
|
+
switch (choice.source) {
|
|
2004
|
+
case "env":
|
|
2005
|
+
return `Using the key in ${envPrefix}_API_KEY, which beats --client, client files and saved logins.`;
|
|
2006
|
+
case "flag":
|
|
2007
|
+
return `Using client ${choice.client}, from --client.`;
|
|
2008
|
+
case "folder":
|
|
2009
|
+
return `Using client ${choice.client}, from the client file ${choice.path}.`;
|
|
2010
|
+
case "default":
|
|
2011
|
+
return `Using the default login (no --client flag and no ${CLIENT_FILE} file in this folder or above it).`;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1996
2014
|
/** Runs `work` with the runner's settings from the environment; SIGINT or SIGTERM stops the run in hand, reported failed. */
|
|
1997
2015
|
async function withRunner(options, work) {
|
|
1998
2016
|
const token = process.env[RUNNER_TOKEN_ENV];
|
|
@@ -2032,7 +2050,7 @@ async function installLibrarySkill(ref, options) {
|
|
|
2032
2050
|
if (options.codex && options.project)
|
|
2033
2051
|
throw new CliError("--codex installs a skill for every Codex run, under CODEX_HOME; drop --project", exitCodes.usage);
|
|
2034
2052
|
const query = ref.version === null ? "" : `?version=${ref.version}`;
|
|
2035
|
-
const download = await api(await operatorConnection(options
|
|
2053
|
+
const download = await api(await operatorConnection(options), `/api/operator/skills/${ref.name}/download${query}`);
|
|
2036
2054
|
const files = unpackSkill(download, await getSigned(download.url));
|
|
2037
2055
|
const { skillsDir, above } = options.codex ? { skillsDir: codexSkillsDir(), above: [] } : claudeSkillsDir(options.project);
|
|
2038
2056
|
const path = await installSkillFolder(skillsDir, download.name, files, above);
|
|
@@ -2509,6 +2527,8 @@ ${Object.values(commands)
|
|
|
2509
2527
|
Options:
|
|
2510
2528
|
--format table|json output format (default: table)
|
|
2511
2529
|
--api-url <url> API base URL
|
|
2530
|
+
--client <name> use that client's saved login (login --client <name> saves it); beats a ${CLIENT_FILE} file, which
|
|
2531
|
+
names the client for its folder and every folder below it; ${envPrefix}_API_KEY beats both
|
|
2512
2532
|
--wait <seconds> how long tools run and tools get wait for a run (default: ${DEFAULT_WAIT_SECONDS}; 0 returns at once)
|
|
2513
2533
|
--agent <kind> which agent the agent commands use: meta_ads (default) or p3_refresh; in a scheduled hand's run, skill
|
|
2514
2534
|
(the run's skill's agent) is the default
|
|
@@ -2619,6 +2639,7 @@ function parseOptions(argv) {
|
|
|
2619
2639
|
format: { type: "string", default: "table" },
|
|
2620
2640
|
json: { type: "boolean", default: false },
|
|
2621
2641
|
"api-url": { type: "string" },
|
|
2642
|
+
client: { type: "string" },
|
|
2622
2643
|
file: { type: "string", multiple: true },
|
|
2623
2644
|
project: { type: "boolean", default: false },
|
|
2624
2645
|
input: { type: "string" },
|
|
@@ -2727,6 +2748,7 @@ function parseOptions(argv) {
|
|
|
2727
2748
|
options: {
|
|
2728
2749
|
format: values.json ? "json" : values.format,
|
|
2729
2750
|
apiUrl: values["api-url"],
|
|
2751
|
+
client: values.client,
|
|
2730
2752
|
file: values.file?.at(-1),
|
|
2731
2753
|
files: values.file ?? [],
|
|
2732
2754
|
project: values.project,
|
|
@@ -2822,7 +2844,7 @@ function parseOptions(argv) {
|
|
|
2822
2844
|
async function changeCandidate(change, args, options) {
|
|
2823
2845
|
if (args.length !== 1 || !options.workspace)
|
|
2824
2846
|
throw new CliError(`Usage: worker ${change} <skill> --workspace <slug>`, exitCodes.usage);
|
|
2825
|
-
return api(await operatorConnection(options
|
|
2847
|
+
return api(await operatorConnection(options), `/api/operator/workers/${change}`, { workspace: options.workspace, skill: args[0] });
|
|
2826
2848
|
}
|
|
2827
2849
|
function findCommand(positionals) {
|
|
2828
2850
|
const three = positionals.slice(0, 3).join(" ");
|
|
@@ -3571,7 +3593,7 @@ function printIntakeToken({ endpoint, address, token, link, subscription }, form
|
|
|
3571
3593
|
console.error(`The token for ${endpoint.label} is shown only now. Rotate it if it is lost.`);
|
|
3572
3594
|
}
|
|
3573
3595
|
async function patchIntake(id, body, options, done) {
|
|
3574
|
-
const { endpoint } = await api(await resolveConnection(options
|
|
3596
|
+
const { endpoint } = await api(await resolveConnection(options), intakePath(id), body, "PATCH");
|
|
3575
3597
|
if (options.format === "json")
|
|
3576
3598
|
return printJson({ endpoint });
|
|
3577
3599
|
console.log(done(endpoint));
|