@sixb/cli-core 0.1.8 → 0.1.9
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-client.d.ts +2 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/arguments.d.ts +4 -2
- package/dist/arguments.d.ts.map +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/metadata.d.ts +5 -2
- package/dist/commands/metadata.d.ts.map +1 -1
- package/dist/commands/models.d.ts +79 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/objects.d.ts.map +1 -1
- package/dist/commands/shared.d.ts +2 -0
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/users.d.ts +3 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/workflows.d.ts.map +1 -1
- package/dist/index.js +529 -23
- package/dist/index.js.map +15 -13
- package/dist/output.d.ts +12 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/policies.d.ts +5 -0
- package/dist/policies.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api-client.ts +13 -2
- package/src/arguments.ts +17 -6
- package/src/commands/actions.ts +2 -2
- package/src/commands/index.ts +8 -0
- package/src/commands/metadata.ts +76 -5
- package/src/commands/models.ts +508 -0
- package/src/commands/objects.ts +47 -2
- package/src/commands/shared.ts +11 -7
- package/src/commands/users.ts +67 -0
- package/src/commands/workflows.ts +10 -4
- package/src/output.ts +21 -0
- package/src/policies.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { pipeline } from "stream/promises";
|
|
|
11
11
|
var INSTANCE_CLI_VERSION = "1";
|
|
12
12
|
var EXIT_USAGE = 2;
|
|
13
13
|
var EXIT_API = 3;
|
|
14
|
+
var EXIT_AWAITING_APPROVAL = 4;
|
|
14
15
|
|
|
15
16
|
class CliError extends Error {
|
|
16
17
|
body;
|
|
@@ -29,12 +30,29 @@ function writeJson(value) {
|
|
|
29
30
|
process.stdout.write(`${JSON.stringify(value)}
|
|
30
31
|
`);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
class CliExit extends Error {
|
|
35
|
+
exitCode;
|
|
36
|
+
constructor(exitCode) {
|
|
37
|
+
super(`The Sixb CLI exited with code ${exitCode}.`);
|
|
38
|
+
this.exitCode = exitCode;
|
|
39
|
+
this.name = "CliExit";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function writeRequestResult(value) {
|
|
43
|
+
writeJson(value);
|
|
44
|
+
const status = typeof value === "object" && value !== null ? Reflect.get(value, "status") : null;
|
|
45
|
+
if (status === "awaiting_approval")
|
|
46
|
+
throw new CliExit(EXIT_AWAITING_APPROVAL);
|
|
47
|
+
}
|
|
32
48
|
function writeText(value) {
|
|
33
49
|
process.stdout.write(value.endsWith(`
|
|
34
50
|
`) ? value : `${value}
|
|
35
51
|
`);
|
|
36
52
|
}
|
|
37
53
|
function reportError(error) {
|
|
54
|
+
if (error instanceof CliExit)
|
|
55
|
+
return error.exitCode;
|
|
38
56
|
const cliError = error instanceof CliError ? error : new CliError({
|
|
39
57
|
code: "internal_error",
|
|
40
58
|
message: error instanceof Error ? error.message : "The Sixb CLI failed unexpectedly."
|
|
@@ -70,9 +88,10 @@ class ApiClient {
|
|
|
70
88
|
...options.signal ? { signal: options.signal } : {}
|
|
71
89
|
});
|
|
72
90
|
}
|
|
73
|
-
async upload(path, source, logicalPath) {
|
|
91
|
+
async upload(path, source, logicalPath, mediaType) {
|
|
74
92
|
const form = new FormData;
|
|
75
|
-
|
|
93
|
+
const bytes = new Uint8Array(await readFile(source));
|
|
94
|
+
form.append("file", new Blob([bytes], mediaType ? { type: mediaType } : undefined), basename(source));
|
|
76
95
|
if (logicalPath)
|
|
77
96
|
form.append("logicalPath", logicalPath);
|
|
78
97
|
return this.json(this.url(path), { method: "POST", body: form });
|
|
@@ -193,8 +212,9 @@ function parseCommandArgs(args, kinds, command, positionalCount = 0) {
|
|
|
193
212
|
const flag = equals < 0 ? argument : argument.slice(0, equals);
|
|
194
213
|
if (!Object.hasOwn(kinds, flag))
|
|
195
214
|
fail(`Unknown ${command} option '${flag}'.`);
|
|
196
|
-
if (Object.hasOwn(options, flag))
|
|
215
|
+
if (Object.hasOwn(options, flag) && kinds[flag] !== "strings") {
|
|
197
216
|
fail(`${flag} may only be provided once.`);
|
|
217
|
+
}
|
|
198
218
|
if (kinds[flag] === "boolean") {
|
|
199
219
|
if (equals >= 0)
|
|
200
220
|
fail(`${flag} does not accept a value.`);
|
|
@@ -204,7 +224,8 @@ function parseCommandArgs(args, kinds, command, positionalCount = 0) {
|
|
|
204
224
|
if (!value || equals < 0 && value.startsWith("--")) {
|
|
205
225
|
fail(`${flag} requires a value.`);
|
|
206
226
|
}
|
|
207
|
-
options[flag]
|
|
227
|
+
const previous = options[flag];
|
|
228
|
+
options[flag] = kinds[flag] === "strings" ? [...Array.isArray(previous) ? previous : [], value] : value;
|
|
208
229
|
}
|
|
209
230
|
}
|
|
210
231
|
const [minimum, maximum] = typeof positionalCount === "number" ? [positionalCount, positionalCount] : positionalCount;
|
|
@@ -282,6 +303,7 @@ var CLI_LIMITS = {
|
|
|
282
303
|
vectorSearchText: { maximum: 8000 },
|
|
283
304
|
telemetryHistory: { default: 100, maximum: 1000 },
|
|
284
305
|
linkPage: { default: 100, maximum: 1000 },
|
|
306
|
+
users: { default: 20, maximum: 200 },
|
|
285
307
|
inspect: {
|
|
286
308
|
depth: { default: 2, maximum: 3 },
|
|
287
309
|
objects: { default: 20, maximum: 100 },
|
|
@@ -317,6 +339,8 @@ Objects:
|
|
|
317
339
|
sixb objects inspect <type> <id> Inspect an object and its related graph in one command
|
|
318
340
|
sixb objects list [options] Browse materialized objects
|
|
319
341
|
sixb objects get <type> <id>... Exact lookup through opaque object references
|
|
342
|
+
sixb objects get-by <type> <property> <value>
|
|
343
|
+
The object holding a value of a unique property
|
|
320
344
|
sixb objects search <text> Search visible objects by id prefix and full text
|
|
321
345
|
sixb objects search <text> --type <type>
|
|
322
346
|
Semantic search through the type's vector profile
|
|
@@ -340,6 +364,13 @@ Actions and workflows:
|
|
|
340
364
|
Files:
|
|
341
365
|
sixb files upload|download ...
|
|
342
366
|
|
|
367
|
+
Members:
|
|
368
|
+
sixb users search <text> Find members by name or email
|
|
369
|
+
sixb users list|get ...
|
|
370
|
+
|
|
371
|
+
A user parameter or property is {"type":"user","id":"<user-id>"}. Find the id with
|
|
372
|
+
\`sixb users search <name>\`.
|
|
373
|
+
|
|
343
374
|
Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
|
|
344
375
|
For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`;
|
|
345
376
|
var LOCAL_MAIN_HELP = SANDBOX_MAIN_HELP.replace("Sixb agent CLI", "Sixb instance CLI").replace("The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.", "The CLI uses the selected local profile and its API credentials.").replace(` sixb doctor Check the sandbox and API gateway
|
|
@@ -347,7 +378,16 @@ var LOCAL_MAIN_HELP = SANDBOX_MAIN_HELP.replace("Sixb agent CLI", "Sixb instance
|
|
|
347
378
|
`, "").replace(`
|
|
348
379
|
When you do not know a type's properties or links, read its file under $SIXB_ONTOLOGY_DIR. Use
|
|
349
380
|
\`sixb ontology get\` for a type that has no file there.
|
|
350
|
-
`, "")
|
|
381
|
+
`, "").replace(`Files:
|
|
382
|
+
sixb files upload|download ...
|
|
383
|
+
`, `Files:
|
|
384
|
+
sixb files upload|download ...
|
|
385
|
+
|
|
386
|
+
Models:
|
|
387
|
+
sixb models list [--kind <kind>] Configured models of every kind, with prices
|
|
388
|
+
sixb models get <model> One model's options and rate card
|
|
389
|
+
sixb models text|image|speech|transcribe ... Call a model; see sixb models --help
|
|
390
|
+
`);
|
|
351
391
|
function renderInstanceHelp(mode) {
|
|
352
392
|
return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP;
|
|
353
393
|
}
|
|
@@ -364,10 +404,18 @@ var SEARCH_HELP = `Usage:
|
|
|
364
404
|
profiles.
|
|
365
405
|
|
|
366
406
|
${SEARCH_DETAILS}`;
|
|
407
|
+
var GET_BY_HELP = `Usage:
|
|
408
|
+
sixb objects get-by <object-type> <property> <value> [--json]
|
|
409
|
+
|
|
410
|
+
Prints the object of exactly <object-type> holding <value> for a property marked unique in its
|
|
411
|
+
ontology doc, or null when no visible object holds it. Values match exactly, case included.
|
|
412
|
+
The value is a string; with --json it is parsed as JSON, for an integer (42) or a user
|
|
413
|
+
reference ({"type":"user","id":"usr_1"}).`;
|
|
367
414
|
var OBJECTS_HELP = `Usage:
|
|
368
415
|
sixb objects inspect <object-type> <primary-id> [options]
|
|
369
416
|
sixb objects list [options]
|
|
370
417
|
sixb objects get <object-type> <primary-id>...
|
|
418
|
+
sixb objects get-by <object-type> <property> <value> [--json]
|
|
371
419
|
sixb objects search <text> [--type <object-type> [--vector-profile <name>]] [--limit <n>]
|
|
372
420
|
sixb objects query --file <path|-> [--include-total|--no-total]
|
|
373
421
|
sixb objects query --example <name>
|
|
@@ -434,7 +482,7 @@ Query nodes compose through input:
|
|
|
434
482
|
limit {"kind":"limit","input":<query>,"limit":${CLI_LIMITS.list.default}}
|
|
435
483
|
page {"kind":"page","input":<query>,"pageSize":${CLI_LIMITS.list.default},"pageToken":"from nextPageToken"}
|
|
436
484
|
project {"kind":"project","input":<query>,"properties":["name","status"]}
|
|
437
|
-
expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}]}
|
|
485
|
+
expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}],"users":["assignee"]}
|
|
438
486
|
vector {"kind":"vector","input":<start|filter>,"vector":"text","profile":"name","k":10}
|
|
439
487
|
rerank {"kind":"rerank","input":<vector>,"model":{"provider":"provider","modelId":"model"}}
|
|
440
488
|
|
|
@@ -445,11 +493,16 @@ Predicates use op, not kind:
|
|
|
445
493
|
{"op":"in","propertyId":"status","values":["open","blocked"]}
|
|
446
494
|
{"op":"exists","propertyId":"dueDate","value":true}
|
|
447
495
|
{"op":"contains","propertyId":"tags","value":"urgent"}
|
|
496
|
+
{"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}} the caller, on a user reference: neq, in, contains too
|
|
448
497
|
|
|
449
498
|
Every field shown is required except includeSubtypes, fields, pageToken, properties, a sort
|
|
450
|
-
direction,
|
|
499
|
+
direction, an expansion's limit, and users. Sort by relevance after text or vector with
|
|
451
500
|
{"kind":"relevance"}. Wrap the outermost node in project to return only the properties you need.
|
|
452
501
|
|
|
502
|
+
An expand's users, or an expansion's, lists user-reference properties: each row then carries the
|
|
503
|
+
members' profiles under users (id, email, displayName, avatarUrl, status). Without access to the
|
|
504
|
+
member directory, users is empty.
|
|
505
|
+
|
|
453
506
|
Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
|
|
454
507
|
is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
|
|
455
508
|
|
|
@@ -484,7 +537,8 @@ History options:
|
|
|
484
537
|
\`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
|
|
485
538
|
contains that parameter object; use - to read standard input. \`actions request\` runs the Action
|
|
486
539
|
and prints its finished run, with its status and any error. It waits at most 25 seconds: a run that
|
|
487
|
-
takes longer keeps running, and the error names the run to read with \`action-runs get
|
|
540
|
+
takes longer keeps running, and the error names the run to read with \`action-runs get\`. A request
|
|
541
|
+
that waits for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
|
|
488
542
|
"action-runs": `Usage:
|
|
489
543
|
sixb action-runs list [options]
|
|
490
544
|
sixb action-runs get <run-id>
|
|
@@ -505,9 +559,48 @@ List options:
|
|
|
505
559
|
workflows: `Usage:
|
|
506
560
|
sixb workflows list
|
|
507
561
|
sixb workflows get <workflow-id>
|
|
508
|
-
sixb workflows start <workflow-id> [--file <path|->]
|
|
562
|
+
sixb workflows start <workflow-id> [--file <path|->] [--run-id <id>]
|
|
563
|
+
|
|
564
|
+
The JSON file contains the workflow input object. Use - to read standard input. A start that waits
|
|
565
|
+
for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
|
|
566
|
+
users: `Usage:
|
|
567
|
+
sixb users list [options]
|
|
568
|
+
sixb users search <text> [options]
|
|
569
|
+
sixb users get <user-id>
|
|
570
|
+
|
|
571
|
+
Options:
|
|
572
|
+
--status active|suspended|all Defaults to active
|
|
573
|
+
--limit <1-${CLI_LIMITS.users.maximum}> Defaults to ${CLI_LIMITS.users.default}
|
|
574
|
+
--offset <n> Non-negative; defaults to 0
|
|
575
|
+
|
|
576
|
+
\`users search\` matches part of a display name or email, ignoring case. Each member is
|
|
577
|
+
{"id","email","displayName","avatarUrl","status"}; a user parameter takes
|
|
578
|
+
{"type":"user","id":"<id>"}. Reading members requires can.view(project.users).`,
|
|
579
|
+
models: `Usage:
|
|
580
|
+
sixb models list [--kind language|image|speech|transcription]
|
|
581
|
+
sixb models get <model> [--kind <kind>]
|
|
582
|
+
sixb models text "<prompt>"|--file <path|-> [--model <tier|model>] [--instructions <text>]
|
|
583
|
+
[--max-output-tokens <n>] [--reasoning <level>]
|
|
584
|
+
sixb models image "<prompt>" --output <path> [--model <model>] [--n <n>]
|
|
585
|
+
[--size <size>] [--aspect-ratio <ratio>] [--quality <q>] [--style <s>]
|
|
586
|
+
[--seed <n>] [--image <file>]...
|
|
587
|
+
sixb models speech "<text>" --output <path> [--model <model>] [--voice <voice>]
|
|
588
|
+
[--format mp3|wav|ogg|aac|flac] [--instructions <text>]
|
|
589
|
+
[--speed <n>] [--language <code>]
|
|
590
|
+
sixb models transcribe <file> [--model <model>]
|
|
591
|
+
sixb models calls files <call-id> [--output <path>]
|
|
592
|
+
|
|
593
|
+
<model> is a model id, or provider:model-id when two providers share one; without --model, the
|
|
594
|
+
kind's default answers. \`list\` shows each model's kind, default, description and price; \`get\`
|
|
595
|
+
adds the sizes, voices and other options a request can use. Prices are in USD.
|
|
596
|
+
|
|
597
|
+
\`text\` also takes a tier (light, standard or heavy) for --model, and reads a long prompt from
|
|
598
|
+
--file: a path, or - for standard input.
|
|
509
599
|
|
|
510
|
-
|
|
600
|
+
--output is a file, numbered when a call returns several, or a directory ending in /. The
|
|
601
|
+
extension follows the file's type, and the result lists the paths written. --image uploads a
|
|
602
|
+
reference image; repeat it for several. \`calls files\` lists a past call's files, or downloads
|
|
603
|
+
them again.`,
|
|
511
604
|
"workflow-runs": `Usage:
|
|
512
605
|
sixb workflow-runs list [options]
|
|
513
606
|
sixb workflow-runs get <run-id>
|
|
@@ -527,9 +620,11 @@ var QUERY_EXAMPLES = {
|
|
|
527
620
|
not: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"not","item":{"op":"eq","propertyId":"status","value":"churned"}}},"limit":${CLI_LIMITS.list.default}}`,
|
|
528
621
|
in: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"in","propertyId":"status","values":["active","trial"]}},"limit":${CLI_LIMITS.list.default}}`,
|
|
529
622
|
exists: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"exists","propertyId":"accountManager","value":false}},"limit":${CLI_LIMITS.list.default}}`,
|
|
623
|
+
mine: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Task"},"predicate":{"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}}},"limit":${CLI_LIMITS.list.default}}`,
|
|
530
624
|
project: `{"kind":"project","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Customer"},"limit":${CLI_LIMITS.list.default}},"properties":["name","status"]}`,
|
|
531
625
|
incoming: '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
|
|
532
626
|
expand: `{"kind":"expand","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"expansions":[{"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment","limit":${CLI_LIMITS.list.default}}]}`,
|
|
627
|
+
users: `{"kind":"expand","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Task"},"limit":${CLI_LIMITS.list.default}},"expansions":[],"users":["assignee"]}`,
|
|
533
628
|
sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
|
|
534
629
|
page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
|
|
535
630
|
vector: `{"kind":"vector","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Product"},"predicate":{"op":"eq","propertyId":"status","value":"active"}},"vector":"waterproof trail shoes","profile":"content","k":${CLI_LIMITS.search.default}}`
|
|
@@ -567,20 +662,22 @@ function singleFileOption(args, command) {
|
|
|
567
662
|
return options["--file"];
|
|
568
663
|
}
|
|
569
664
|
async function readJson(source) {
|
|
570
|
-
|
|
571
|
-
try {
|
|
572
|
-
text = source === "-" ? await readStdin() : await readFile2(source, "utf8");
|
|
573
|
-
} catch (error) {
|
|
574
|
-
if (isFileError(error, "ENOENT"))
|
|
575
|
-
fail(`JSON file '${source}' does not exist.`);
|
|
576
|
-
throw error;
|
|
577
|
-
}
|
|
665
|
+
const text = await readText(source, "JSON file");
|
|
578
666
|
try {
|
|
579
667
|
return JSON.parse(text);
|
|
580
668
|
} catch {
|
|
581
669
|
fail(source === "-" ? "Standard input is not valid JSON." : `JSON file '${source}' is not valid JSON.`, "invalid_json");
|
|
582
670
|
}
|
|
583
671
|
}
|
|
672
|
+
async function readText(source, noun) {
|
|
673
|
+
try {
|
|
674
|
+
return source === "-" ? await readStdin() : await readFile2(source, "utf8");
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (isFileError(error, "ENOENT"))
|
|
677
|
+
fail(`${noun} '${source}' does not exist.`);
|
|
678
|
+
throw error;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
584
681
|
function asRecord2(value) {
|
|
585
682
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
586
683
|
}
|
|
@@ -642,7 +739,7 @@ async function actions(api, args, requestWaitMs = ACTION_REQUEST_WAIT_MS) {
|
|
|
642
739
|
const requestedRunId = runId ?? `act_${randomUUID2()}`;
|
|
643
740
|
const waiting = AbortSignal.timeout(requestWaitMs);
|
|
644
741
|
try {
|
|
645
|
-
return
|
|
742
|
+
return writeRequestResult(await api.post(`/api/actions/${encodeURIComponent(actionId)}`, {
|
|
646
743
|
params,
|
|
647
744
|
...subjectType && subjectId ? { subject: { kind: "object", objectTypeId: subjectType, primaryId: subjectId } } : {},
|
|
648
745
|
runId: requestedRunId
|
|
@@ -703,6 +800,340 @@ async function files(api, args) {
|
|
|
703
800
|
fail(`Unknown files command '${sub}'.`);
|
|
704
801
|
}
|
|
705
802
|
|
|
803
|
+
// src/commands/models.ts
|
|
804
|
+
import { access as access2, mkdir, stat } from "fs/promises";
|
|
805
|
+
import { dirname as dirname2, extname, join as join2, parse } from "path";
|
|
806
|
+
var MODEL_KINDS = ["language", "image", "speech", "transcription"];
|
|
807
|
+
async function models(api, args) {
|
|
808
|
+
const [sub, ...rest] = args;
|
|
809
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
810
|
+
return writeText(GROUP_HELP.models);
|
|
811
|
+
if (sub === "list") {
|
|
812
|
+
const { options } = parseCommandArgs(rest, { "--kind": "string" }, "models list", 0);
|
|
813
|
+
const kind = options["--kind"] === undefined ? undefined : modelKind(options["--kind"]);
|
|
814
|
+
const entries = catalogEntries(await api.get("/api/models"));
|
|
815
|
+
return writeJson(entries.filter((entry) => kind === undefined || entry.kind === kind).map(({ kind, entry }) => ({
|
|
816
|
+
kind,
|
|
817
|
+
model: entry.modelId,
|
|
818
|
+
provider: entry.provider,
|
|
819
|
+
...entry.tier ? { tier: entry.tier } : {},
|
|
820
|
+
...entry.isDefault ? { default: true } : {},
|
|
821
|
+
...entry.description ? { description: entry.description } : {},
|
|
822
|
+
price: describePrice(entry.rateCard)
|
|
823
|
+
})));
|
|
824
|
+
}
|
|
825
|
+
if (sub === "get") {
|
|
826
|
+
const { positionals, options } = parseCommandArgs(rest, { "--kind": "string" }, "models get", 1);
|
|
827
|
+
const kind = options["--kind"] === undefined ? undefined : modelKind(options["--kind"]);
|
|
828
|
+
const entries = catalogEntries(await api.get("/api/models"));
|
|
829
|
+
const { kind: found, entry } = findModel(entries, positionals[0] ?? "", kind);
|
|
830
|
+
const { provider, modelId, isDefault, rateCard, ...details } = entry;
|
|
831
|
+
return writeJson({
|
|
832
|
+
kind: found,
|
|
833
|
+
model: modelId,
|
|
834
|
+
provider,
|
|
835
|
+
default: isDefault,
|
|
836
|
+
...details,
|
|
837
|
+
price: describePrice(rateCard),
|
|
838
|
+
...rateCard ? { rateCard } : {}
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
if (sub === "text")
|
|
842
|
+
return generateText(api, rest);
|
|
843
|
+
if (sub === "image")
|
|
844
|
+
return generateImages(api, rest);
|
|
845
|
+
if (sub === "speech")
|
|
846
|
+
return generateSpeech(api, rest);
|
|
847
|
+
if (sub === "transcribe")
|
|
848
|
+
return transcribe(api, rest);
|
|
849
|
+
if (sub === "calls")
|
|
850
|
+
return calls(api, rest);
|
|
851
|
+
fail(`Unknown models command '${sub}'.`);
|
|
852
|
+
}
|
|
853
|
+
var SPEECH_FORMATS = {
|
|
854
|
+
mp3: "audio/mpeg",
|
|
855
|
+
wav: "audio/wav",
|
|
856
|
+
ogg: "audio/ogg",
|
|
857
|
+
aac: "audio/aac",
|
|
858
|
+
flac: "audio/flac"
|
|
859
|
+
};
|
|
860
|
+
var SPEECH_FORMAT_NAMES = Object.keys(SPEECH_FORMATS);
|
|
861
|
+
var MEDIA_TYPES = {
|
|
862
|
+
".png": "image/png",
|
|
863
|
+
".jpg": "image/jpeg",
|
|
864
|
+
".jpeg": "image/jpeg",
|
|
865
|
+
".webp": "image/webp",
|
|
866
|
+
".gif": "image/gif",
|
|
867
|
+
".mp3": "audio/mpeg",
|
|
868
|
+
".wav": "audio/wav",
|
|
869
|
+
".ogg": "audio/ogg",
|
|
870
|
+
".aac": "audio/aac",
|
|
871
|
+
".flac": "audio/flac",
|
|
872
|
+
".opus": "audio/opus",
|
|
873
|
+
".m4a": "audio/mp4",
|
|
874
|
+
".webm": "audio/webm"
|
|
875
|
+
};
|
|
876
|
+
var EXTENSIONS = Object.fromEntries(Object.entries(MEDIA_TYPES).filter(([extension]) => extension !== ".jpeg").map(([extension, mediaType]) => [mediaType, extension]));
|
|
877
|
+
async function generateText(api, args) {
|
|
878
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
879
|
+
"--model": "string",
|
|
880
|
+
"--instructions": "string",
|
|
881
|
+
"--max-output-tokens": "string",
|
|
882
|
+
"--reasoning": "string",
|
|
883
|
+
"--file": "string"
|
|
884
|
+
}, "models text", [0, 1]);
|
|
885
|
+
const prompt = await readPrompt(positionals[0], options["--file"]);
|
|
886
|
+
const maxOutputTokens = options["--max-output-tokens"];
|
|
887
|
+
const result = await api.post("/api/models/language/generations", {
|
|
888
|
+
...await languageModelRef(api, options["--model"]),
|
|
889
|
+
prompt,
|
|
890
|
+
...options["--instructions"] === undefined ? {} : { instructions: options["--instructions"] },
|
|
891
|
+
...maxOutputTokens === undefined ? {} : { maxOutputTokens: integerInRange("--max-output-tokens", maxOutputTokens, 1, 1e6) },
|
|
892
|
+
...options["--reasoning"] === undefined ? {} : { reasoning: options["--reasoning"] }
|
|
893
|
+
});
|
|
894
|
+
writeJson({
|
|
895
|
+
callId: result.callId,
|
|
896
|
+
model: result.model.modelId,
|
|
897
|
+
text: result.text,
|
|
898
|
+
finishReason: result.finishReason,
|
|
899
|
+
cost: describeCost(result.cost)
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
async function generateImages(api, args) {
|
|
903
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
904
|
+
"--output": "string",
|
|
905
|
+
"--model": "string",
|
|
906
|
+
"--n": "string",
|
|
907
|
+
"--size": "string",
|
|
908
|
+
"--aspect-ratio": "string",
|
|
909
|
+
"--quality": "string",
|
|
910
|
+
"--style": "string",
|
|
911
|
+
"--seed": "string",
|
|
912
|
+
"--image": "strings"
|
|
913
|
+
}, "models image", 1);
|
|
914
|
+
const output = requireOutput(options["--output"], "models image");
|
|
915
|
+
const seed = options["--seed"];
|
|
916
|
+
if (seed !== undefined && !/^-?[0-9]+$/.test(seed))
|
|
917
|
+
fail("--seed must be an integer.");
|
|
918
|
+
const model = await modelRef(api, options["--model"], "image");
|
|
919
|
+
const images = [];
|
|
920
|
+
for (const path of options["--image"] ?? [])
|
|
921
|
+
images.push(await uploadInput(api, path, "image"));
|
|
922
|
+
const result = await api.post("/api/models/image/generations", {
|
|
923
|
+
...model,
|
|
924
|
+
prompt: positionals[0],
|
|
925
|
+
...options["--n"] === undefined ? {} : { n: integerInRange("--n", options["--n"], 1, 100) },
|
|
926
|
+
...optional("size", options["--size"]),
|
|
927
|
+
...optional("aspectRatio", options["--aspect-ratio"]),
|
|
928
|
+
...optional("quality", options["--quality"]),
|
|
929
|
+
...optional("style", options["--style"]),
|
|
930
|
+
...seed === undefined ? {} : { seed: Number(seed) },
|
|
931
|
+
...images.length > 0 ? { images } : {}
|
|
932
|
+
});
|
|
933
|
+
writeJson(await savedResult(api, result, output));
|
|
934
|
+
}
|
|
935
|
+
async function generateSpeech(api, args) {
|
|
936
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
937
|
+
"--output": "string",
|
|
938
|
+
"--model": "string",
|
|
939
|
+
"--voice": "string",
|
|
940
|
+
"--format": "string",
|
|
941
|
+
"--instructions": "string",
|
|
942
|
+
"--speed": "string",
|
|
943
|
+
"--language": "string"
|
|
944
|
+
}, "models speech", 1);
|
|
945
|
+
const output = requireOutput(options["--output"], "models speech");
|
|
946
|
+
const format = options["--format"];
|
|
947
|
+
const speed = options["--speed"];
|
|
948
|
+
if (speed !== undefined && !(Number(speed) > 0))
|
|
949
|
+
fail("--speed must be a positive number.");
|
|
950
|
+
const result = await api.post("/api/models/audio/speech", {
|
|
951
|
+
...await modelRef(api, options["--model"], "speech"),
|
|
952
|
+
text: positionals[0],
|
|
953
|
+
...optional("voice", options["--voice"]),
|
|
954
|
+
...format === undefined ? {} : { mediaType: SPEECH_FORMATS[enumValue("--format", format, SPEECH_FORMAT_NAMES)] },
|
|
955
|
+
...optional("instructions", options["--instructions"]),
|
|
956
|
+
...speed === undefined ? {} : { speed: Number(speed) },
|
|
957
|
+
...optional("language", options["--language"])
|
|
958
|
+
});
|
|
959
|
+
writeJson(await savedResult(api, result, output));
|
|
960
|
+
}
|
|
961
|
+
async function transcribe(api, args) {
|
|
962
|
+
const { positionals, options } = parseCommandArgs(args, { "--model": "string" }, "models transcribe", 1);
|
|
963
|
+
const model = await modelRef(api, options["--model"], "transcription");
|
|
964
|
+
const audio = await uploadInput(api, positionals[0] ?? "", "audio");
|
|
965
|
+
const result = await api.post("/api/models/audio/transcriptions", {
|
|
966
|
+
...model,
|
|
967
|
+
audio
|
|
968
|
+
});
|
|
969
|
+
writeJson({
|
|
970
|
+
callId: result.callId,
|
|
971
|
+
model: result.model.modelId,
|
|
972
|
+
text: result.text,
|
|
973
|
+
cost: describeCost(result.cost)
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
async function calls(api, args) {
|
|
977
|
+
const [sub, ...rest] = args;
|
|
978
|
+
if (sub !== "files")
|
|
979
|
+
fail(`Unknown models calls command '${sub ?? ""}'. Use 'models calls files'.`);
|
|
980
|
+
const { positionals, options } = parseCommandArgs(rest, { "--output": "string" }, "models calls files", 1);
|
|
981
|
+
const callId = positionals[0] ?? "";
|
|
982
|
+
const call = await api.get(`/api/ai/model-calls/${encodeURIComponent(callId)}/files`);
|
|
983
|
+
const output = options["--output"];
|
|
984
|
+
if (output === undefined)
|
|
985
|
+
return writeJson(call);
|
|
986
|
+
writeJson({ callId: call.callId, files: await saveFiles(api, call.callId, call.files, output) });
|
|
987
|
+
}
|
|
988
|
+
async function modelRef(api, reference, kind) {
|
|
989
|
+
if (reference === undefined)
|
|
990
|
+
return {};
|
|
991
|
+
const { entry } = findModel(catalogEntries(await api.get("/api/models")), reference, kind);
|
|
992
|
+
return { model: { provider: entry.provider, modelId: entry.modelId } };
|
|
993
|
+
}
|
|
994
|
+
var LANGUAGE_TIERS = ["light", "standard", "heavy"];
|
|
995
|
+
async function languageModelRef(api, reference) {
|
|
996
|
+
const tier = LANGUAGE_TIERS.find((candidate) => candidate === reference);
|
|
997
|
+
if (tier !== undefined)
|
|
998
|
+
return { model: tier };
|
|
999
|
+
return modelRef(api, reference, "language");
|
|
1000
|
+
}
|
|
1001
|
+
async function readPrompt(argument, source) {
|
|
1002
|
+
if (argument !== undefined && source !== undefined) {
|
|
1003
|
+
fail("models text takes its prompt as an argument or from --file, not both.");
|
|
1004
|
+
}
|
|
1005
|
+
const prompt = source === undefined ? argument : await readText(source, "Prompt file");
|
|
1006
|
+
if (prompt === undefined) {
|
|
1007
|
+
fail('models text requires a prompt: "<prompt>", or --file <path|-> to read it.');
|
|
1008
|
+
}
|
|
1009
|
+
if (!prompt.trim())
|
|
1010
|
+
fail("The prompt is empty.");
|
|
1011
|
+
return prompt;
|
|
1012
|
+
}
|
|
1013
|
+
function requireOutput(output, command) {
|
|
1014
|
+
if (!output)
|
|
1015
|
+
fail(`${command} requires --output <path>: a file, or a directory ending in /.`);
|
|
1016
|
+
return output;
|
|
1017
|
+
}
|
|
1018
|
+
function optional(field, value) {
|
|
1019
|
+
return value === undefined ? {} : { [field]: value };
|
|
1020
|
+
}
|
|
1021
|
+
async function uploadInput(api, path, kind) {
|
|
1022
|
+
try {
|
|
1023
|
+
await access2(path);
|
|
1024
|
+
} catch {
|
|
1025
|
+
fail(`Input file '${path}' does not exist.`);
|
|
1026
|
+
}
|
|
1027
|
+
const mediaType = MEDIA_TYPES[extname(path).toLowerCase()];
|
|
1028
|
+
if (!mediaType?.startsWith(`${kind}/`)) {
|
|
1029
|
+
const accepted = Object.entries(MEDIA_TYPES).filter(([, type]) => type.startsWith(`${kind}/`)).map(([extension]) => extension);
|
|
1030
|
+
fail(`'${path}' is not an ${kind} file. Use ${accepted.join(", ")}.`);
|
|
1031
|
+
}
|
|
1032
|
+
return api.upload("/api/files", path, undefined, mediaType);
|
|
1033
|
+
}
|
|
1034
|
+
async function savedResult(api, result, output) {
|
|
1035
|
+
return {
|
|
1036
|
+
callId: result.callId,
|
|
1037
|
+
model: result.model.modelId,
|
|
1038
|
+
files: await saveFiles(api, result.callId, result.files ?? [], output),
|
|
1039
|
+
...result.warnings?.length ? { warnings: result.warnings } : {},
|
|
1040
|
+
cost: describeCost(result.cost)
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
async function saveFiles(api, callId, files, output) {
|
|
1044
|
+
const directory = output.endsWith("/") || (await stat(output).catch(() => null))?.isDirectory();
|
|
1045
|
+
const paths = files.map((file, index) => {
|
|
1046
|
+
const number = files.length > 1 ? `-${index + 1}` : "";
|
|
1047
|
+
const extension = (file.mediaType && EXTENSIONS[file.mediaType]) ?? "";
|
|
1048
|
+
if (directory)
|
|
1049
|
+
return join2(output, `${callId}${number}${extension || ".bin"}`);
|
|
1050
|
+
const { dir, name, ext } = parse(output);
|
|
1051
|
+
return join2(dir, `${name}${number}${extension || ext}`);
|
|
1052
|
+
});
|
|
1053
|
+
for (const [index, path] of paths.entries()) {
|
|
1054
|
+
await mkdir(dirname2(path), { recursive: true });
|
|
1055
|
+
await api.download(`/api/ai/model-calls/${encodeURIComponent(callId)}/files/content`, path, {
|
|
1056
|
+
path: `/files/${index}`
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
return paths;
|
|
1060
|
+
}
|
|
1061
|
+
function describeCost(cost) {
|
|
1062
|
+
if (!cost.money)
|
|
1063
|
+
return "unknown";
|
|
1064
|
+
const nanos = BigInt(cost.money.amountNanos);
|
|
1065
|
+
const whole = nanos / 1000000000n;
|
|
1066
|
+
const fraction = (nanos % 1000000000n).toString().padStart(9, "0").replace(/0+$/, "");
|
|
1067
|
+
return `$${whole}${fraction ? `.${fraction}` : ""}`;
|
|
1068
|
+
}
|
|
1069
|
+
function modelKind(value) {
|
|
1070
|
+
const kind = MODEL_KINDS.find((candidate) => candidate === value);
|
|
1071
|
+
if (!kind)
|
|
1072
|
+
fail(`Unknown model kind '${value}'. Use one of: ${MODEL_KINDS.join(", ")}.`);
|
|
1073
|
+
return kind;
|
|
1074
|
+
}
|
|
1075
|
+
function catalogEntries(catalog) {
|
|
1076
|
+
return [
|
|
1077
|
+
...catalog.language.map((entry) => ({ kind: "language", entry })),
|
|
1078
|
+
...catalog.image.map((entry) => ({ kind: "image", entry })),
|
|
1079
|
+
...catalog.audio.speech.map((entry) => ({ kind: "speech", entry })),
|
|
1080
|
+
...catalog.audio.transcription.map((entry) => ({ kind: "transcription", entry }))
|
|
1081
|
+
];
|
|
1082
|
+
}
|
|
1083
|
+
function findModel(entries, reference, kind) {
|
|
1084
|
+
const ofKind = entries.filter((candidate) => kind === undefined || candidate.kind === kind);
|
|
1085
|
+
let matches = ofKind.filter((candidate) => candidate.entry.modelId === reference);
|
|
1086
|
+
const separator = reference.indexOf(":");
|
|
1087
|
+
if (matches.length === 0 && separator > 0) {
|
|
1088
|
+
const provider = reference.slice(0, separator);
|
|
1089
|
+
const modelId = reference.slice(separator + 1);
|
|
1090
|
+
matches = ofKind.filter((candidate) => candidate.entry.provider === provider && candidate.entry.modelId === modelId);
|
|
1091
|
+
}
|
|
1092
|
+
const [match] = matches;
|
|
1093
|
+
if (!match) {
|
|
1094
|
+
fail(`No ${kind === undefined ? "" : `${kind} `}model '${reference}' is configured. Run 'sixb models list'.`);
|
|
1095
|
+
}
|
|
1096
|
+
if (matches.length > 1) {
|
|
1097
|
+
const kinds = new Set(matches.map((candidate) => candidate.kind));
|
|
1098
|
+
fail(kinds.size > 1 ? `'${reference}' is configured as ${[...kinds].join(" and ")}. Add --kind <kind>.` : `'${reference}' is configured by ${matches.map((candidate) => candidate.entry.provider).join(" and ")}. Use provider:${reference}.`);
|
|
1099
|
+
}
|
|
1100
|
+
return match;
|
|
1101
|
+
}
|
|
1102
|
+
function describePrice(card) {
|
|
1103
|
+
if (!card)
|
|
1104
|
+
return "price unknown";
|
|
1105
|
+
const parts = [
|
|
1106
|
+
variantPrice(card.images, "per image", "at"),
|
|
1107
|
+
variantPrice(card.characters, "per 1M characters", "for"),
|
|
1108
|
+
variantPrice(card.audioInputSeconds, "per second of audio", "for"),
|
|
1109
|
+
card.input !== undefined && card.output !== undefined ? `${dollars(tokenPrice(card.input))} input \xB7 ${dollars(tokenPrice(card.output))} output per 1M tokens` : undefined,
|
|
1110
|
+
card.audioOutput === undefined ? undefined : `${dollars(tokenPrice(card.audioOutput))} per 1M audio tokens`
|
|
1111
|
+
].filter((part) => part !== undefined);
|
|
1112
|
+
return parts.length === 0 ? "price unknown" : parts.join(" \xB7 ");
|
|
1113
|
+
}
|
|
1114
|
+
function variantPrice(price, unit, joiner) {
|
|
1115
|
+
if (price === undefined)
|
|
1116
|
+
return;
|
|
1117
|
+
if (typeof price === "string")
|
|
1118
|
+
return `${dollars(price)} ${unit}`;
|
|
1119
|
+
const only = price.default;
|
|
1120
|
+
if (only !== undefined && price.variants.every((variant) => variant.price === only)) {
|
|
1121
|
+
return `${dollars(only)} ${unit}`;
|
|
1122
|
+
}
|
|
1123
|
+
const variants = price.variants.map(({ price: amount, ...dimensions }) => {
|
|
1124
|
+
const label = Object.values(dimensions).join(" ");
|
|
1125
|
+
return `${dollars(amount)} ${joiner} ${label}`;
|
|
1126
|
+
});
|
|
1127
|
+
const otherwise = price.default === undefined ? [] : [`otherwise ${dollars(price.default)}`];
|
|
1128
|
+
return `${unit}: ${[...variants, ...otherwise].join(", ")}`;
|
|
1129
|
+
}
|
|
1130
|
+
function tokenPrice(price) {
|
|
1131
|
+
return typeof price === "string" ? price : price.default;
|
|
1132
|
+
}
|
|
1133
|
+
function dollars(amount) {
|
|
1134
|
+
return `$${amount}`;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
706
1137
|
// src/graph.ts
|
|
707
1138
|
async function inspectGraph(api, objectTypeId, primaryId, options) {
|
|
708
1139
|
if (options.depth === 0)
|
|
@@ -968,6 +1399,8 @@ async function objects(api, args) {
|
|
|
968
1399
|
return objectsList(api, rest);
|
|
969
1400
|
case "get":
|
|
970
1401
|
return objectsGet(api, rest);
|
|
1402
|
+
case "get-by":
|
|
1403
|
+
return objectsGetBy(api, rest);
|
|
971
1404
|
case "search":
|
|
972
1405
|
return objectsSearch(api, rest);
|
|
973
1406
|
case "query":
|
|
@@ -1058,6 +1491,31 @@ async function objectsGet(api, args) {
|
|
|
1058
1491
|
includeTotal: false
|
|
1059
1492
|
}));
|
|
1060
1493
|
}
|
|
1494
|
+
async function objectsGetBy(api, args) {
|
|
1495
|
+
if (requestsHelp(args))
|
|
1496
|
+
return writeText(GET_BY_HELP);
|
|
1497
|
+
const { positionals, options } = parseCommandArgs(args, { "--json": "boolean" }, "objects get-by", 3);
|
|
1498
|
+
const [objectTypeId = "", propertyId = "", text = ""] = positionals;
|
|
1499
|
+
const value = options["--json"] ? parseJsonValue(text) : text;
|
|
1500
|
+
const path = `/api/objects/${encodeURIComponent(objectTypeId)}/by/${encodeURIComponent(propertyId)}`;
|
|
1501
|
+
try {
|
|
1502
|
+
writeJson(await api.post(path, { value }));
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
if (!isObjectNotFound(error))
|
|
1505
|
+
throw error;
|
|
1506
|
+
writeJson(null);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function parseJsonValue(text) {
|
|
1510
|
+
try {
|
|
1511
|
+
return JSON.parse(text);
|
|
1512
|
+
} catch {
|
|
1513
|
+
fail(`--json needs the value as JSON, such as 42 or '{"type":"user","id":"usr_1"}'.`);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
function isObjectNotFound(error) {
|
|
1517
|
+
return error instanceof CliError && error.body.status === 404 && error.body.code === "object.not_found";
|
|
1518
|
+
}
|
|
1061
1519
|
async function objectsSearch(api, args) {
|
|
1062
1520
|
if (requestsHelp(args))
|
|
1063
1521
|
return writeText(SEARCH_HELP);
|
|
@@ -1379,7 +1837,45 @@ function normalizeTelemetryQueryInput(input) {
|
|
|
1379
1837
|
return body;
|
|
1380
1838
|
}
|
|
1381
1839
|
|
|
1840
|
+
// src/commands/users.ts
|
|
1841
|
+
var USER_STATUSES = ["active", "suspended", "all"];
|
|
1842
|
+
var LIST_FLAGS = {
|
|
1843
|
+
"--status": "string",
|
|
1844
|
+
"--limit": "string",
|
|
1845
|
+
"--offset": "string"
|
|
1846
|
+
};
|
|
1847
|
+
async function users(api, args) {
|
|
1848
|
+
const [sub, ...rest] = args;
|
|
1849
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
1850
|
+
return writeText(GROUP_HELP.users);
|
|
1851
|
+
if (sub === "get") {
|
|
1852
|
+
const { positionals } = parseCommandArgs(rest, {}, "users get", 1);
|
|
1853
|
+
return writeJson(await api.get(`/api/users/${encodeURIComponent(positionals[0] ?? "")}`));
|
|
1854
|
+
}
|
|
1855
|
+
if (sub === "list") {
|
|
1856
|
+
const { options } = parseCommandArgs(rest, LIST_FLAGS, "users list");
|
|
1857
|
+
return writeJson(await api.get("/api/users", listQuery(options)));
|
|
1858
|
+
}
|
|
1859
|
+
if (sub === "search") {
|
|
1860
|
+
const { positionals, options } = parseCommandArgs(rest, LIST_FLAGS, "users search", 1);
|
|
1861
|
+
const search = positionals[0]?.trim();
|
|
1862
|
+
if (!search)
|
|
1863
|
+
fail("users search requires the text to find in a name or email.");
|
|
1864
|
+
return writeJson(await api.get("/api/users", { ...listQuery(options), search }));
|
|
1865
|
+
}
|
|
1866
|
+
fail(`Unknown users command '${sub}'.`);
|
|
1867
|
+
}
|
|
1868
|
+
function listQuery(options) {
|
|
1869
|
+
const limit = integerInRange("--limit", options["--limit"] ?? String(CLI_LIMITS.users.default), 1, CLI_LIMITS.users.maximum);
|
|
1870
|
+
return {
|
|
1871
|
+
status: enumValue("--status", options["--status"] ?? "active", USER_STATUSES),
|
|
1872
|
+
limit: String(limit),
|
|
1873
|
+
...options["--offset"] === undefined ? {} : { offset: String(nonNegativeInteger("--offset", options["--offset"])) }
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1382
1877
|
// src/commands/workflows.ts
|
|
1878
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1383
1879
|
async function workflows(api, args) {
|
|
1384
1880
|
const [sub, ...rest] = args;
|
|
1385
1881
|
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
@@ -1398,12 +1894,16 @@ async function workflows(api, args) {
|
|
|
1398
1894
|
const {
|
|
1399
1895
|
positionals: [workflowId],
|
|
1400
1896
|
options
|
|
1401
|
-
} = parseCommandArgs(rest, { "--file": "string" }, "workflows start", 1);
|
|
1897
|
+
} = parseCommandArgs(rest, { "--file": "string", "--run-id": "string" }, "workflows start", 1);
|
|
1402
1898
|
const input = options["--file"] ? await readJson(options["--file"]) : {};
|
|
1403
1899
|
if (Array.isArray(input) || typeof input !== "object" || input === null) {
|
|
1404
1900
|
fail("Workflow input must be a JSON object.");
|
|
1405
1901
|
}
|
|
1406
|
-
|
|
1902
|
+
const runId = options["--run-id"] ?? `run_${randomUUID3()}`;
|
|
1903
|
+
return writeRequestResult(await api.post(`/api/workflows/${encodeURIComponent(workflowId ?? "")}/runs`, {
|
|
1904
|
+
input,
|
|
1905
|
+
runId
|
|
1906
|
+
}));
|
|
1407
1907
|
}
|
|
1408
1908
|
fail(`Unknown workflows command '${sub}'.`);
|
|
1409
1909
|
}
|
|
@@ -1418,7 +1918,9 @@ var INSTANCE_COMMANDS = [
|
|
|
1418
1918
|
"action-runs",
|
|
1419
1919
|
"files",
|
|
1420
1920
|
"workflows",
|
|
1421
|
-
"workflow-runs"
|
|
1921
|
+
"workflow-runs",
|
|
1922
|
+
"users",
|
|
1923
|
+
"models"
|
|
1422
1924
|
];
|
|
1423
1925
|
function isInstanceCommand(command) {
|
|
1424
1926
|
return INSTANCE_COMMANDS.includes(command);
|
|
@@ -1443,6 +1945,10 @@ async function dispatch(api, command, args) {
|
|
|
1443
1945
|
return workflows(api, args);
|
|
1444
1946
|
case "workflow-runs":
|
|
1445
1947
|
return runs(api, "workflow", args);
|
|
1948
|
+
case "users":
|
|
1949
|
+
return users(api, args);
|
|
1950
|
+
case "models":
|
|
1951
|
+
return models(api, args);
|
|
1446
1952
|
default:
|
|
1447
1953
|
fail(`Unknown command '${command}'. Run 'sixb --help'.`);
|
|
1448
1954
|
}
|
|
@@ -1482,4 +1988,4 @@ export {
|
|
|
1482
1988
|
writeText
|
|
1483
1989
|
};
|
|
1484
1990
|
|
|
1485
|
-
//# debugId=
|
|
1991
|
+
//# debugId=7F5CD0F40A1B96CA64756E2164756E21
|