@sixb/cli-core 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -16
- package/dist/api-client.d.ts +3 -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/bin/sixb +26 -0
- package/dist/commands/data.d.ts +5 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/files.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/memory.d.ts +3 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/metadata.d.ts +14 -5
- 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/runs.d.ts +8 -1
- package/dist/commands/runs.d.ts.map +1 -1
- package/dist/commands/shared.d.ts +4 -0
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/system.d.ts +21 -0
- package/dist/commands/system.d.ts.map +1 -0
- 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/generated/sixb.mjs +2547 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +800 -62
- package/dist/index.js.map +24 -18
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/memory.d.ts +21 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/output.d.ts +12 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/policies.d.ts +14 -0
- package/dist/policies.d.ts.map +1 -1
- package/dist/run.d.ts +1 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/sandbox-cli.d.ts +8 -0
- package/dist/sandbox-cli.d.ts.map +1 -0
- package/package.json +15 -1
- package/src/api-client.ts +83 -4
- package/src/arguments.ts +17 -6
- package/src/bin/sixb +26 -0
- package/src/commands/actions.ts +5 -5
- package/src/commands/data.ts +90 -0
- package/src/commands/files.ts +27 -3
- package/src/commands/index.ts +24 -0
- package/src/commands/memory.ts +358 -0
- package/src/commands/metadata.ts +137 -26
- package/src/commands/models.ts +508 -0
- package/src/commands/objects.ts +47 -2
- package/src/commands/ontology.ts +2 -2
- package/src/commands/runs.ts +22 -16
- package/src/commands/shared.ts +17 -7
- package/src/commands/system.ts +67 -0
- package/src/commands/telemetry.ts +2 -2
- package/src/commands/users.ts +87 -0
- package/src/commands/workflows.ts +12 -6
- package/src/generated/sixb.mjs +2547 -0
- package/src/index.ts +12 -3
- package/src/main.ts +39 -0
- package/src/memory.ts +30 -0
- package/src/output.ts +21 -0
- package/src/policies.ts +5 -0
- package/src/run.ts +3 -2
- package/src/sandbox-cli.ts +18 -0
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { randomUUID } from "crypto";
|
|
4
4
|
import { createWriteStream } from "fs";
|
|
5
5
|
import { readFile, rename, rm } from "fs/promises";
|
|
6
|
+
import { request as httpRequest } from "http";
|
|
6
7
|
import { basename, dirname, join } from "path";
|
|
7
8
|
import { Readable } from "stream";
|
|
8
9
|
import { pipeline } from "stream/promises";
|
|
@@ -11,6 +12,7 @@ import { pipeline } from "stream/promises";
|
|
|
11
12
|
var INSTANCE_CLI_VERSION = "1";
|
|
12
13
|
var EXIT_USAGE = 2;
|
|
13
14
|
var EXIT_API = 3;
|
|
15
|
+
var EXIT_AWAITING_APPROVAL = 4;
|
|
14
16
|
|
|
15
17
|
class CliError extends Error {
|
|
16
18
|
body;
|
|
@@ -29,12 +31,29 @@ function writeJson(value) {
|
|
|
29
31
|
process.stdout.write(`${JSON.stringify(value)}
|
|
30
32
|
`);
|
|
31
33
|
}
|
|
34
|
+
|
|
35
|
+
class CliExit extends Error {
|
|
36
|
+
exitCode;
|
|
37
|
+
constructor(exitCode) {
|
|
38
|
+
super(`The Sixb CLI exited with code ${exitCode}.`);
|
|
39
|
+
this.exitCode = exitCode;
|
|
40
|
+
this.name = "CliExit";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function writeRequestResult(value) {
|
|
44
|
+
writeJson(value);
|
|
45
|
+
const status = typeof value === "object" && value !== null ? Reflect.get(value, "status") : null;
|
|
46
|
+
if (status === "awaiting_approval")
|
|
47
|
+
throw new CliExit(EXIT_AWAITING_APPROVAL);
|
|
48
|
+
}
|
|
32
49
|
function writeText(value) {
|
|
33
50
|
process.stdout.write(value.endsWith(`
|
|
34
51
|
`) ? value : `${value}
|
|
35
52
|
`);
|
|
36
53
|
}
|
|
37
54
|
function reportError(error) {
|
|
55
|
+
if (error instanceof CliExit)
|
|
56
|
+
return error.exitCode;
|
|
38
57
|
const cliError = error instanceof CliError ? error : new CliError({
|
|
39
58
|
code: "internal_error",
|
|
40
59
|
message: error instanceof Error ? error.message : "The Sixb CLI failed unexpectedly."
|
|
@@ -45,14 +64,19 @@ function reportError(error) {
|
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
// src/api-client.ts
|
|
67
|
+
var UNIX_BASE_URL_PREFIX = "unix:";
|
|
68
|
+
|
|
48
69
|
class ApiClient {
|
|
49
70
|
baseUrl;
|
|
71
|
+
socketPath;
|
|
50
72
|
authorization;
|
|
51
73
|
missingBaseUrlMessage;
|
|
52
74
|
unavailableMessage;
|
|
53
75
|
unavailableHint;
|
|
54
76
|
constructor(options) {
|
|
55
|
-
|
|
77
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
78
|
+
this.socketPath = baseUrl.startsWith(UNIX_BASE_URL_PREFIX) ? baseUrl.slice(UNIX_BASE_URL_PREFIX.length) : undefined;
|
|
79
|
+
this.baseUrl = this.socketPath === undefined ? baseUrl : "http://localhost";
|
|
56
80
|
this.authorization = options.authorization;
|
|
57
81
|
this.missingBaseUrlMessage = options.missingBaseUrlMessage;
|
|
58
82
|
this.unavailableMessage = options.unavailableMessage;
|
|
@@ -70,9 +94,10 @@ class ApiClient {
|
|
|
70
94
|
...options.signal ? { signal: options.signal } : {}
|
|
71
95
|
});
|
|
72
96
|
}
|
|
73
|
-
async upload(path, source, logicalPath) {
|
|
97
|
+
async upload(path, source, logicalPath, mediaType) {
|
|
74
98
|
const form = new FormData;
|
|
75
|
-
|
|
99
|
+
const bytes = new Uint8Array(await readFile(source));
|
|
100
|
+
form.append("file", new Blob([bytes], mediaType ? { type: mediaType } : undefined), basename(source));
|
|
76
101
|
if (logicalPath)
|
|
77
102
|
form.append("logicalPath", logicalPath);
|
|
78
103
|
return this.json(this.url(path), { method: "POST", body: form });
|
|
@@ -117,7 +142,7 @@ class ApiClient {
|
|
|
117
142
|
const headers = new Headers(init.headers);
|
|
118
143
|
if (this.authorization)
|
|
119
144
|
headers.set("authorization", this.authorization);
|
|
120
|
-
response = await
|
|
145
|
+
response = await send(url, { ...init, headers }, this.socketPath);
|
|
121
146
|
} catch (error) {
|
|
122
147
|
if (init.signal?.aborted)
|
|
123
148
|
throw error;
|
|
@@ -174,6 +199,48 @@ function stringField(record, key) {
|
|
|
174
199
|
const value = record[key];
|
|
175
200
|
return typeof value === "string" ? value : undefined;
|
|
176
201
|
}
|
|
202
|
+
function send(url, init, socketPath) {
|
|
203
|
+
if (socketPath === undefined)
|
|
204
|
+
return fetch(url, init);
|
|
205
|
+
if (typeof Bun !== "undefined")
|
|
206
|
+
return fetch(url, { ...init, unix: socketPath });
|
|
207
|
+
return sendOverNodeSocket(url, init, socketPath);
|
|
208
|
+
}
|
|
209
|
+
async function sendOverNodeSocket(url, init, socketPath) {
|
|
210
|
+
const outgoing = new Request(url, init);
|
|
211
|
+
const body = outgoing.body ? Buffer.from(await outgoing.arrayBuffer()) : undefined;
|
|
212
|
+
const headers = Object.fromEntries(outgoing.headers);
|
|
213
|
+
if (body)
|
|
214
|
+
headers["content-length"] = String(body.byteLength);
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
const request = httpRequest({
|
|
217
|
+
socketPath,
|
|
218
|
+
method: outgoing.method,
|
|
219
|
+
path: `${url.pathname}${url.search}`,
|
|
220
|
+
headers: { ...headers, host: "localhost" },
|
|
221
|
+
...init.signal ? { signal: init.signal } : {}
|
|
222
|
+
}, (response) => {
|
|
223
|
+
const responseHeaders = new Headers;
|
|
224
|
+
for (const [name, value] of Object.entries(response.headers)) {
|
|
225
|
+
for (const each of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
|
|
226
|
+
responseHeaders.append(name, each);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const status = response.statusCode ?? 502;
|
|
230
|
+
const empty = status === 204 || status === 304 || outgoing.method === "HEAD";
|
|
231
|
+
resolve(new Response(empty ? null : nodeBody(response), {
|
|
232
|
+
status,
|
|
233
|
+
statusText: response.statusMessage ?? "",
|
|
234
|
+
headers: responseHeaders
|
|
235
|
+
}));
|
|
236
|
+
});
|
|
237
|
+
request.on("error", reject);
|
|
238
|
+
request.end(body);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function nodeBody(response) {
|
|
242
|
+
return Readable.toWeb(response);
|
|
243
|
+
}
|
|
177
244
|
// src/arguments.ts
|
|
178
245
|
function parseCommandArgs(args, kinds, command, positionalCount = 0) {
|
|
179
246
|
const positionals = [];
|
|
@@ -193,8 +260,9 @@ function parseCommandArgs(args, kinds, command, positionalCount = 0) {
|
|
|
193
260
|
const flag = equals < 0 ? argument : argument.slice(0, equals);
|
|
194
261
|
if (!Object.hasOwn(kinds, flag))
|
|
195
262
|
fail(`Unknown ${command} option '${flag}'.`);
|
|
196
|
-
if (Object.hasOwn(options, flag))
|
|
263
|
+
if (Object.hasOwn(options, flag) && kinds[flag] !== "strings") {
|
|
197
264
|
fail(`${flag} may only be provided once.`);
|
|
265
|
+
}
|
|
198
266
|
if (kinds[flag] === "boolean") {
|
|
199
267
|
if (equals >= 0)
|
|
200
268
|
fail(`${flag} does not accept a value.`);
|
|
@@ -204,7 +272,8 @@ function parseCommandArgs(args, kinds, command, positionalCount = 0) {
|
|
|
204
272
|
if (!value || equals < 0 && value.startsWith("--")) {
|
|
205
273
|
fail(`${flag} requires a value.`);
|
|
206
274
|
}
|
|
207
|
-
options[flag]
|
|
275
|
+
const previous = options[flag];
|
|
276
|
+
options[flag] = kinds[flag] === "strings" ? [...Array.isArray(previous) ? previous : [], value] : value;
|
|
208
277
|
}
|
|
209
278
|
}
|
|
210
279
|
const [minimum, maximum] = typeof positionalCount === "number" ? [positionalCount, positionalCount] : positionalCount;
|
|
@@ -282,6 +351,9 @@ var CLI_LIMITS = {
|
|
|
282
351
|
vectorSearchText: { maximum: 8000 },
|
|
283
352
|
telemetryHistory: { default: 100, maximum: 1000 },
|
|
284
353
|
linkPage: { default: 100, maximum: 1000 },
|
|
354
|
+
users: { default: 20, maximum: 200 },
|
|
355
|
+
datasetVersions: { default: 20, maximum: 100 },
|
|
356
|
+
datasetRows: { default: 100, maximum: 1000 },
|
|
285
357
|
inspect: {
|
|
286
358
|
depth: { default: 2, maximum: 3 },
|
|
287
359
|
objects: { default: 20, maximum: 100 },
|
|
@@ -294,17 +366,17 @@ var DEFAULT_OBJECT_ORDER_BY = "updatedAt";
|
|
|
294
366
|
var DEFAULT_TELEMETRY_ORDER = "desc";
|
|
295
367
|
|
|
296
368
|
// src/commands/metadata.ts
|
|
297
|
-
var SANDBOX_MAIN_HELP = `Sixb
|
|
369
|
+
var SANDBOX_MAIN_HELP = `Sixb CLI
|
|
298
370
|
|
|
299
371
|
Usage:
|
|
300
372
|
sixb <command> [options]
|
|
301
373
|
|
|
302
|
-
The CLI uses the run
|
|
374
|
+
The CLI uses the run's SIXB_API_BASE_URL, which adds its credentials. Do not configure authentication or another origin.
|
|
303
375
|
API commands emit JSON. Help, version, and examples emit text. Downloads write to --output and
|
|
304
376
|
emit a JSON receipt.
|
|
305
377
|
|
|
306
378
|
Discovery:
|
|
307
|
-
sixb
|
|
379
|
+
sixb status Check the sandbox and its API access
|
|
308
380
|
sixb context Print the current run context
|
|
309
381
|
sixb project show Show project metadata
|
|
310
382
|
sixb ontology list Compact visible type and relationship catalog
|
|
@@ -317,6 +389,8 @@ Objects:
|
|
|
317
389
|
sixb objects inspect <type> <id> Inspect an object and its related graph in one command
|
|
318
390
|
sixb objects list [options] Browse materialized objects
|
|
319
391
|
sixb objects get <type> <id>... Exact lookup through opaque object references
|
|
392
|
+
sixb objects get-by <type> <property> <value>
|
|
393
|
+
The object holding a value of a unique property
|
|
320
394
|
sixb objects search <text> Search visible objects by id prefix and full text
|
|
321
395
|
sixb objects search <text> --type <type>
|
|
322
396
|
Semantic search through the type's vector profile
|
|
@@ -337,19 +411,27 @@ Actions and workflows:
|
|
|
337
411
|
sixb workflows list|get|start ...
|
|
338
412
|
sixb workflow-runs list|get ...
|
|
339
413
|
|
|
414
|
+
Data:
|
|
415
|
+
sixb datasets list|get|versions|rows ...
|
|
416
|
+
sixb syncs list|get ... Syncs you may run, each with its latest run
|
|
417
|
+
sixb sync-runs list ...
|
|
418
|
+
sixb pipelines list|get ... Pipelines you may run, each with its latest run
|
|
419
|
+
sixb pipeline-runs list|get ...
|
|
420
|
+
|
|
340
421
|
Files:
|
|
341
422
|
sixb files upload|download ...
|
|
342
423
|
|
|
424
|
+
Members:
|
|
425
|
+
sixb users search <text> Find members by name or email
|
|
426
|
+
sixb users list|get ...
|
|
427
|
+
|
|
428
|
+
A user parameter or property is {"type":"user","id":"<user-id>"}. Find the id with
|
|
429
|
+
\`sixb users search <name>\`.
|
|
430
|
+
|
|
343
431
|
Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
|
|
344
432
|
For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
`, "").replace(`
|
|
348
|
-
When you do not know a type's properties or links, read its file under $SIXB_ONTOLOGY_DIR. Use
|
|
349
|
-
\`sixb ontology get\` for a type that has no file there.
|
|
350
|
-
`, "");
|
|
351
|
-
function renderInstanceHelp(mode) {
|
|
352
|
-
return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP;
|
|
433
|
+
function renderInstanceHelp() {
|
|
434
|
+
return SANDBOX_MAIN_HELP;
|
|
353
435
|
}
|
|
354
436
|
var SEARCH_DETAILS = `Without --type, search matches primary-id prefixes and full-text fields across visible types.
|
|
355
437
|
With --type, the server embeds the text with the profile's model and ranks results by score,
|
|
@@ -364,10 +446,18 @@ var SEARCH_HELP = `Usage:
|
|
|
364
446
|
profiles.
|
|
365
447
|
|
|
366
448
|
${SEARCH_DETAILS}`;
|
|
449
|
+
var GET_BY_HELP = `Usage:
|
|
450
|
+
sixb objects get-by <object-type> <property> <value> [--json]
|
|
451
|
+
|
|
452
|
+
Prints the object of exactly <object-type> holding <value> for a property marked unique in its
|
|
453
|
+
ontology doc, or null when no visible object holds it. Values match exactly, case included.
|
|
454
|
+
The value is a string; with --json it is parsed as JSON, for an integer (42) or a user
|
|
455
|
+
reference ({"type":"user","id":"usr_1"}).`;
|
|
367
456
|
var OBJECTS_HELP = `Usage:
|
|
368
457
|
sixb objects inspect <object-type> <primary-id> [options]
|
|
369
458
|
sixb objects list [options]
|
|
370
459
|
sixb objects get <object-type> <primary-id>...
|
|
460
|
+
sixb objects get-by <object-type> <property> <value> [--json]
|
|
371
461
|
sixb objects search <text> [--type <object-type> [--vector-profile <name>]] [--limit <n>]
|
|
372
462
|
sixb objects query --file <path|-> [--include-total|--no-total]
|
|
373
463
|
sixb objects query --example <name>
|
|
@@ -434,7 +524,7 @@ Query nodes compose through input:
|
|
|
434
524
|
limit {"kind":"limit","input":<query>,"limit":${CLI_LIMITS.list.default}}
|
|
435
525
|
page {"kind":"page","input":<query>,"pageSize":${CLI_LIMITS.list.default},"pageToken":"from nextPageToken"}
|
|
436
526
|
project {"kind":"project","input":<query>,"properties":["name","status"]}
|
|
437
|
-
expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}]}
|
|
527
|
+
expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}],"users":["assignee"]}
|
|
438
528
|
vector {"kind":"vector","input":<start|filter>,"vector":"text","profile":"name","k":10}
|
|
439
529
|
rerank {"kind":"rerank","input":<vector>,"model":{"provider":"provider","modelId":"model"}}
|
|
440
530
|
|
|
@@ -445,11 +535,16 @@ Predicates use op, not kind:
|
|
|
445
535
|
{"op":"in","propertyId":"status","values":["open","blocked"]}
|
|
446
536
|
{"op":"exists","propertyId":"dueDate","value":true}
|
|
447
537
|
{"op":"contains","propertyId":"tags","value":"urgent"}
|
|
538
|
+
{"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}} the caller, on a user reference: neq, in, contains too
|
|
448
539
|
|
|
449
540
|
Every field shown is required except includeSubtypes, fields, pageToken, properties, a sort
|
|
450
|
-
direction,
|
|
541
|
+
direction, an expansion's limit, and users. Sort by relevance after text or vector with
|
|
451
542
|
{"kind":"relevance"}. Wrap the outermost node in project to return only the properties you need.
|
|
452
543
|
|
|
544
|
+
An expand's users, or an expansion's, lists user-reference properties: each row then carries the
|
|
545
|
+
members' profiles under users (id, email, displayName, avatarUrl, status). Without access to the
|
|
546
|
+
member directory, users is empty.
|
|
547
|
+
|
|
453
548
|
Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
|
|
454
549
|
is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
|
|
455
550
|
|
|
@@ -460,7 +555,7 @@ identities. Put limits and pages inside the query tree.
|
|
|
460
555
|
Vector profile names are the keys of the type's search.vectors. Vector input is a start node,
|
|
461
556
|
optionally under filters; k bounds the ranked matches and results carry a score.`;
|
|
462
557
|
var GROUP_HELP = {
|
|
463
|
-
|
|
558
|
+
status: "Usage: sixb status",
|
|
464
559
|
context: "Usage: sixb context",
|
|
465
560
|
project: "Usage: sixb project show",
|
|
466
561
|
ontology: `Usage:
|
|
@@ -484,7 +579,8 @@ History options:
|
|
|
484
579
|
\`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
|
|
485
580
|
contains that parameter object; use - to read standard input. \`actions request\` runs the Action
|
|
486
581
|
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
|
|
582
|
+
takes longer keeps running, and the error names the run to read with \`action-runs get\`. A request
|
|
583
|
+
that waits for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
|
|
488
584
|
"action-runs": `Usage:
|
|
489
585
|
sixb action-runs list [options]
|
|
490
586
|
sixb action-runs get <run-id>
|
|
@@ -501,13 +597,54 @@ List options:
|
|
|
501
597
|
files: `Usage:
|
|
502
598
|
sixb files upload <local-path> [--logical-path <path>]
|
|
503
599
|
sixb files download object <type> <id> --path <json-pointer> --output <local-path>
|
|
504
|
-
sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path
|
|
600
|
+
sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path>
|
|
601
|
+
sixb files download url <content-url> --output <local-path>`,
|
|
505
602
|
workflows: `Usage:
|
|
506
603
|
sixb workflows list
|
|
507
604
|
sixb workflows get <workflow-id>
|
|
508
|
-
sixb workflows start <workflow-id> [--file <path|->]
|
|
605
|
+
sixb workflows start <workflow-id> [--file <path|->] [--run-id <id>]
|
|
509
606
|
|
|
510
|
-
The JSON file contains the workflow input object. Use - to read standard input
|
|
607
|
+
The JSON file contains the workflow input object. Use - to read standard input. A start that waits
|
|
608
|
+
for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
|
|
609
|
+
users: `Usage:
|
|
610
|
+
sixb users list [options]
|
|
611
|
+
sixb users search <text> [options]
|
|
612
|
+
sixb users get <user-id|email>
|
|
613
|
+
|
|
614
|
+
Options:
|
|
615
|
+
--status active|suspended|all Defaults to active
|
|
616
|
+
--limit <1-${CLI_LIMITS.users.maximum}> Defaults to ${CLI_LIMITS.users.default}
|
|
617
|
+
--offset <n> Non-negative; defaults to 0
|
|
618
|
+
|
|
619
|
+
\`users search\` matches part of a display name or email, ignoring case; \`users get\` takes an
|
|
620
|
+
exact email, ignoring case, and finds suspended members too. Each member is
|
|
621
|
+
{"id","email","displayName","avatarUrl","status"}; a user parameter takes
|
|
622
|
+
{"type":"user","id":"<id>"}. Reading members requires can.view(project.users).`,
|
|
623
|
+
models: `Usage:
|
|
624
|
+
sixb models list [--kind language|image|speech|transcription]
|
|
625
|
+
sixb models get <model> [--kind <kind>]
|
|
626
|
+
sixb models text "<prompt>"|--file <path|-> [--model <tier|model>] [--instructions <text>]
|
|
627
|
+
[--max-output-tokens <n>] [--reasoning <level>]
|
|
628
|
+
sixb models image "<prompt>" --output <path> [--model <model>] [--n <n>]
|
|
629
|
+
[--size <size>] [--aspect-ratio <ratio>] [--quality <q>] [--style <s>]
|
|
630
|
+
[--seed <n>] [--image <file>]...
|
|
631
|
+
sixb models speech "<text>" --output <path> [--model <model>] [--voice <voice>]
|
|
632
|
+
[--format mp3|wav|ogg|aac|flac] [--instructions <text>]
|
|
633
|
+
[--speed <n>] [--language <code>]
|
|
634
|
+
sixb models transcribe <file> [--model <model>]
|
|
635
|
+
sixb models calls files <call-id> [--output <path>]
|
|
636
|
+
|
|
637
|
+
<model> is a model id, or provider:model-id when two providers share one; without --model, the
|
|
638
|
+
kind's default answers. \`list\` shows each model's kind, default, description and price; \`get\`
|
|
639
|
+
adds the sizes, voices and other options a request can use. Prices are in USD.
|
|
640
|
+
|
|
641
|
+
\`text\` also takes a tier (light, standard or heavy) for --model, and reads a long prompt from
|
|
642
|
+
--file: a path, or - for standard input.
|
|
643
|
+
|
|
644
|
+
--output is a file, numbered when a call returns several, or a directory ending in /. The
|
|
645
|
+
extension follows the file's type, and the result lists the paths written. --image uploads a
|
|
646
|
+
reference image; repeat it for several. \`calls files\` lists a past call's files, or downloads
|
|
647
|
+
them again.`,
|
|
511
648
|
"workflow-runs": `Usage:
|
|
512
649
|
sixb workflow-runs list [options]
|
|
513
650
|
sixb workflow-runs get <run-id>
|
|
@@ -518,7 +655,57 @@ List options:
|
|
|
518
655
|
--started-after|--started-before <RFC3339>
|
|
519
656
|
--limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
|
|
520
657
|
--offset <n> Non-negative; defaults to 0
|
|
521
|
-
--order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}
|
|
658
|
+
--order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
|
|
659
|
+
datasets: `Usage:
|
|
660
|
+
sixb datasets list
|
|
661
|
+
sixb datasets get <dataset-id>
|
|
662
|
+
sixb datasets versions <dataset-id> [--limit <1-${CLI_LIMITS.datasetVersions.maximum}>]
|
|
663
|
+
sixb datasets rows <dataset-id> [options]
|
|
664
|
+
|
|
665
|
+
Rows options:
|
|
666
|
+
--version <version-id> Defaults to the latest version
|
|
667
|
+
--columns <a,b,...> Defaults to every column
|
|
668
|
+
--limit <1-${CLI_LIMITS.datasetRows.maximum}> Defaults to ${CLI_LIMITS.datasetRows.default}
|
|
669
|
+
--offset <n> Non-negative; defaults to 0
|
|
670
|
+
|
|
671
|
+
\`list\` and \`get\` show a dataset's schema, its latest version, and the syncs and pipelines you
|
|
672
|
+
may run that read or write it. \`versions\` lists the newest first. \`rows\` leaves out the
|
|
673
|
+
columns your clearances do not cover and names them under redactions.`,
|
|
674
|
+
syncs: `Usage:
|
|
675
|
+
sixb syncs list
|
|
676
|
+
sixb syncs get <sync-id>
|
|
677
|
+
|
|
678
|
+
Lists the syncs you may run, each with its latest run. \`sixb sync-runs list\` shows the history.`,
|
|
679
|
+
"sync-runs": `Usage:
|
|
680
|
+
sixb sync-runs list [options]
|
|
681
|
+
|
|
682
|
+
List options:
|
|
683
|
+
--sync <sync-id>
|
|
684
|
+
--dataset <dataset-id>
|
|
685
|
+
--status queued|running|succeeded|failed|cancelled
|
|
686
|
+
--started-after|--started-before <RFC3339>
|
|
687
|
+
--limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
|
|
688
|
+
--offset <n> Non-negative; defaults to 0
|
|
689
|
+
--order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
|
|
690
|
+
pipelines: `Usage:
|
|
691
|
+
sixb pipelines list
|
|
692
|
+
sixb pipelines get <pipeline-id>
|
|
693
|
+
|
|
694
|
+
Lists the pipelines you may run, each with its latest run. \`sixb pipeline-runs\` shows the
|
|
695
|
+
history and each run's steps.`,
|
|
696
|
+
"pipeline-runs": `Usage:
|
|
697
|
+
sixb pipeline-runs list [options]
|
|
698
|
+
sixb pipeline-runs get <run-id>
|
|
699
|
+
|
|
700
|
+
List options:
|
|
701
|
+
--pipeline <pipeline-id>
|
|
702
|
+
--status queued|running|succeeded|failed|cancelled
|
|
703
|
+
--started-after|--started-before <RFC3339>
|
|
704
|
+
--limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
|
|
705
|
+
--offset <n> Non-negative; defaults to 0
|
|
706
|
+
--order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}
|
|
707
|
+
|
|
708
|
+
\`get\` includes the run's steps.`
|
|
522
709
|
};
|
|
523
710
|
var QUERY_EXAMPLES = {
|
|
524
711
|
exact: '{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]}',
|
|
@@ -527,9 +714,11 @@ var QUERY_EXAMPLES = {
|
|
|
527
714
|
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
715
|
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
716
|
exists: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"exists","propertyId":"accountManager","value":false}},"limit":${CLI_LIMITS.list.default}}`,
|
|
717
|
+
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
718
|
project: `{"kind":"project","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Customer"},"limit":${CLI_LIMITS.list.default}},"properties":["name","status"]}`,
|
|
531
719
|
incoming: '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
|
|
532
720
|
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}}]}`,
|
|
721
|
+
users: `{"kind":"expand","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Task"},"limit":${CLI_LIMITS.list.default}},"expansions":[],"users":["assignee"]}`,
|
|
533
722
|
sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
|
|
534
723
|
page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
|
|
535
724
|
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}}`
|
|
@@ -538,6 +727,11 @@ var FACETS_EXAMPLE = '{"query":{"kind":"start","objectTypeId":"WorkOrder"},"face
|
|
|
538
727
|
|
|
539
728
|
// src/commands/shared.ts
|
|
540
729
|
import { readFile as readFile2 } from "fs/promises";
|
|
730
|
+
function idSegment(value) {
|
|
731
|
+
if (!value || value === "." || value === "..")
|
|
732
|
+
fail(`'${value ?? ""}' is not an id.`);
|
|
733
|
+
return encodeURIComponent(value);
|
|
734
|
+
}
|
|
541
735
|
function parseQueryOptions(args, names, command) {
|
|
542
736
|
const kinds = Object.fromEntries(Object.keys(names).map((flag) => [flag, "string"]));
|
|
543
737
|
const { options } = parseCommandArgs(args, kinds, command);
|
|
@@ -567,20 +761,22 @@ function singleFileOption(args, command) {
|
|
|
567
761
|
return options["--file"];
|
|
568
762
|
}
|
|
569
763
|
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
|
-
}
|
|
764
|
+
const text = await readText(source, "JSON file");
|
|
578
765
|
try {
|
|
579
766
|
return JSON.parse(text);
|
|
580
767
|
} catch {
|
|
581
768
|
fail(source === "-" ? "Standard input is not valid JSON." : `JSON file '${source}' is not valid JSON.`, "invalid_json");
|
|
582
769
|
}
|
|
583
770
|
}
|
|
771
|
+
async function readText(source, noun) {
|
|
772
|
+
try {
|
|
773
|
+
return source === "-" ? await readStdin() : await readFile2(source, "utf8");
|
|
774
|
+
} catch (error) {
|
|
775
|
+
if (isFileError(error, "ENOENT"))
|
|
776
|
+
fail(`${noun} '${source}' does not exist.`);
|
|
777
|
+
throw error;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
584
780
|
function asRecord2(value) {
|
|
585
781
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
586
782
|
}
|
|
@@ -609,7 +805,7 @@ async function actions(api, args, requestWaitMs = ACTION_REQUEST_WAIT_MS) {
|
|
|
609
805
|
return writeText(GROUP_HELP.actions);
|
|
610
806
|
if (sub === "get") {
|
|
611
807
|
const { positionals } = parseCommandArgs(rest, {}, "actions get", 1);
|
|
612
|
-
return writeJson(await api.get(`/api/actions/${
|
|
808
|
+
return writeJson(await api.get(`/api/actions/${idSegment(positionals[0])}`));
|
|
613
809
|
}
|
|
614
810
|
if (sub === "list") {
|
|
615
811
|
const options = parseQueryOptions(rest, { "--type": "objectTypeId" }, "actions list");
|
|
@@ -642,7 +838,7 @@ async function actions(api, args, requestWaitMs = ACTION_REQUEST_WAIT_MS) {
|
|
|
642
838
|
const requestedRunId = runId ?? `act_${randomUUID2()}`;
|
|
643
839
|
const waiting = AbortSignal.timeout(requestWaitMs);
|
|
644
840
|
try {
|
|
645
|
-
return
|
|
841
|
+
return writeRequestResult(await api.post(`/api/actions/${idSegment(actionId)}`, {
|
|
646
842
|
params,
|
|
647
843
|
...subjectType && subjectId ? { subject: { kind: "object", objectTypeId: subjectType, primaryId: subjectId } } : {},
|
|
648
844
|
runId: requestedRunId
|
|
@@ -660,6 +856,59 @@ async function actions(api, args, requestWaitMs = ACTION_REQUEST_WAIT_MS) {
|
|
|
660
856
|
fail(`Unknown actions command '${sub}'.`);
|
|
661
857
|
}
|
|
662
858
|
|
|
859
|
+
// src/commands/data.ts
|
|
860
|
+
async function datasets(api, args) {
|
|
861
|
+
const [sub, ...rest] = args;
|
|
862
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
863
|
+
return writeText(GROUP_HELP.datasets);
|
|
864
|
+
if (sub === "list") {
|
|
865
|
+
parseCommandArgs(rest, {}, "datasets list");
|
|
866
|
+
return writeJson(await api.get("/api/datasets"));
|
|
867
|
+
}
|
|
868
|
+
if (sub === "get") {
|
|
869
|
+
const { positionals } = parseCommandArgs(rest, {}, "datasets get", 1);
|
|
870
|
+
return writeJson(await api.get(`/api/datasets/${idSegment(positionals[0])}`));
|
|
871
|
+
}
|
|
872
|
+
if (sub === "versions") {
|
|
873
|
+
const { positionals, options } = parseCommandArgs(rest, { "--limit": "string" }, "datasets versions", 1);
|
|
874
|
+
const { default: fallback, maximum } = CLI_LIMITS.datasetVersions;
|
|
875
|
+
const limit = integerInRange("--limit", options["--limit"] ?? String(fallback), 1, maximum);
|
|
876
|
+
return writeJson(await api.get(`/api/datasets/${idSegment(positionals[0])}/versions`, { limit: String(limit) }));
|
|
877
|
+
}
|
|
878
|
+
if (sub === "rows") {
|
|
879
|
+
const { positionals, options } = parseCommandArgs(rest, { "--version": "string", "--columns": "string", "--limit": "string", "--offset": "string" }, "datasets rows", 1);
|
|
880
|
+
const { default: fallback, maximum } = CLI_LIMITS.datasetRows;
|
|
881
|
+
const query = {
|
|
882
|
+
limit: String(integerInRange("--limit", options["--limit"] ?? String(fallback), 1, maximum)),
|
|
883
|
+
...options["--version"] === undefined ? {} : { versionId: options["--version"] },
|
|
884
|
+
...options["--columns"] === undefined ? {} : { columns: columnList(options["--columns"]) },
|
|
885
|
+
...options["--offset"] === undefined ? {} : { offset: String(nonNegativeInteger("--offset", options["--offset"])) }
|
|
886
|
+
};
|
|
887
|
+
return writeJson(await api.get(`/api/datasets/${idSegment(positionals[0])}/rows`, query));
|
|
888
|
+
}
|
|
889
|
+
fail(`Unknown datasets command '${sub}'.`);
|
|
890
|
+
}
|
|
891
|
+
async function runnables(api, group, args) {
|
|
892
|
+
const [sub, ...rest] = args;
|
|
893
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
894
|
+
return writeText(GROUP_HELP[group]);
|
|
895
|
+
if (sub === "list") {
|
|
896
|
+
parseCommandArgs(rest, {}, `${group} list`);
|
|
897
|
+
return writeJson(await api.get(`/api/${group}`));
|
|
898
|
+
}
|
|
899
|
+
if (sub === "get") {
|
|
900
|
+
const { positionals } = parseCommandArgs(rest, {}, `${group} get`, 1);
|
|
901
|
+
return writeJson(await api.get(`/api/${group}/${idSegment(positionals[0])}`));
|
|
902
|
+
}
|
|
903
|
+
fail(`Unknown ${group} command '${sub}'.`);
|
|
904
|
+
}
|
|
905
|
+
function columnList(value) {
|
|
906
|
+
const columns = value.split(",").map((column) => column.trim());
|
|
907
|
+
if (columns.some((column) => !column))
|
|
908
|
+
fail("--columns takes column names separated by commas.");
|
|
909
|
+
return columns.join(",");
|
|
910
|
+
}
|
|
911
|
+
|
|
663
912
|
// src/commands/files.ts
|
|
664
913
|
import { access } from "fs/promises";
|
|
665
914
|
async function files(api, args) {
|
|
@@ -679,20 +928,28 @@ async function files(api, args) {
|
|
|
679
928
|
if (sub === "download") {
|
|
680
929
|
const { positionals, options } = parseCommandArgs(rest, { "--path": "string", "--output": "string" }, "files download", [2, 3]);
|
|
681
930
|
const context = positionals[0];
|
|
931
|
+
const output = options["--output"];
|
|
932
|
+
if (context === "url") {
|
|
933
|
+
requireExact(positionals, 2, "files download url requires exactly one content URL.");
|
|
934
|
+
const url = parseContentUrl(positionals[1] ?? "");
|
|
935
|
+
if (!output)
|
|
936
|
+
fail("files download requires --output <local-path>.");
|
|
937
|
+
await api.download(url.pathname, output, Object.fromEntries(url.searchParams));
|
|
938
|
+
return writeJson({ downloaded: true, output });
|
|
939
|
+
}
|
|
682
940
|
let route;
|
|
683
941
|
if (context === "object") {
|
|
684
942
|
requireExact(positionals, 3, "files download object requires object type and primary id.");
|
|
685
943
|
const type = positionals[1] ?? "";
|
|
686
944
|
const id = positionals[2] ?? "";
|
|
687
|
-
route = `/api/objects/${
|
|
945
|
+
route = `/api/objects/${idSegment(type)}/${idSegment(id)}/files/content`;
|
|
688
946
|
} else if (context === "action-run" || context === "workflow-run") {
|
|
689
947
|
requireExact(positionals, 2, `files download ${context} requires exactly one run id.`);
|
|
690
948
|
const id = positionals[1] ?? "";
|
|
691
|
-
route = `/api/${context}s/${
|
|
949
|
+
route = `/api/${context}s/${idSegment(id)}/files/content`;
|
|
692
950
|
} else
|
|
693
951
|
fail(`Unknown file download context '${context}'.`);
|
|
694
952
|
const path = options["--path"];
|
|
695
|
-
const output = options["--output"];
|
|
696
953
|
if (!path)
|
|
697
954
|
fail("files download requires --path <json-pointer>.");
|
|
698
955
|
if (!output)
|
|
@@ -702,6 +959,352 @@ async function files(api, args) {
|
|
|
702
959
|
}
|
|
703
960
|
fail(`Unknown files command '${sub}'.`);
|
|
704
961
|
}
|
|
962
|
+
function parseContentUrl(value) {
|
|
963
|
+
let url;
|
|
964
|
+
try {
|
|
965
|
+
url = new URL(value);
|
|
966
|
+
} catch {
|
|
967
|
+
fail(`'${value}' is not a URL.`);
|
|
968
|
+
}
|
|
969
|
+
if (!url.pathname.startsWith("/api/") || !url.pathname.endsWith("/files/content")) {
|
|
970
|
+
fail(`'${value}' is not a Sixb file content URL.`);
|
|
971
|
+
}
|
|
972
|
+
return url;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/commands/models.ts
|
|
976
|
+
import { access as access2, mkdir, stat } from "fs/promises";
|
|
977
|
+
import { dirname as dirname2, extname, join as join2, parse } from "path";
|
|
978
|
+
var MODEL_KINDS = ["language", "image", "speech", "transcription"];
|
|
979
|
+
async function models(api, args) {
|
|
980
|
+
const [sub, ...rest] = args;
|
|
981
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
982
|
+
return writeText(GROUP_HELP.models);
|
|
983
|
+
if (sub === "list") {
|
|
984
|
+
const { options } = parseCommandArgs(rest, { "--kind": "string" }, "models list", 0);
|
|
985
|
+
const kind = options["--kind"] === undefined ? undefined : modelKind(options["--kind"]);
|
|
986
|
+
const entries = catalogEntries(await api.get("/api/models"));
|
|
987
|
+
return writeJson(entries.filter((entry) => kind === undefined || entry.kind === kind).map(({ kind, entry }) => ({
|
|
988
|
+
kind,
|
|
989
|
+
model: entry.modelId,
|
|
990
|
+
provider: entry.provider,
|
|
991
|
+
...entry.tier ? { tier: entry.tier } : {},
|
|
992
|
+
...entry.isDefault ? { default: true } : {},
|
|
993
|
+
...entry.description ? { description: entry.description } : {},
|
|
994
|
+
price: describePrice(entry.rateCard)
|
|
995
|
+
})));
|
|
996
|
+
}
|
|
997
|
+
if (sub === "get") {
|
|
998
|
+
const { positionals, options } = parseCommandArgs(rest, { "--kind": "string" }, "models get", 1);
|
|
999
|
+
const kind = options["--kind"] === undefined ? undefined : modelKind(options["--kind"]);
|
|
1000
|
+
const entries = catalogEntries(await api.get("/api/models"));
|
|
1001
|
+
const { kind: found, entry } = findModel(entries, positionals[0] ?? "", kind);
|
|
1002
|
+
const { provider, modelId, isDefault, rateCard, ...details } = entry;
|
|
1003
|
+
return writeJson({
|
|
1004
|
+
kind: found,
|
|
1005
|
+
model: modelId,
|
|
1006
|
+
provider,
|
|
1007
|
+
default: isDefault,
|
|
1008
|
+
...details,
|
|
1009
|
+
price: describePrice(rateCard),
|
|
1010
|
+
...rateCard ? { rateCard } : {}
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
if (sub === "text")
|
|
1014
|
+
return generateText(api, rest);
|
|
1015
|
+
if (sub === "image")
|
|
1016
|
+
return generateImages(api, rest);
|
|
1017
|
+
if (sub === "speech")
|
|
1018
|
+
return generateSpeech(api, rest);
|
|
1019
|
+
if (sub === "transcribe")
|
|
1020
|
+
return transcribe(api, rest);
|
|
1021
|
+
if (sub === "calls")
|
|
1022
|
+
return calls(api, rest);
|
|
1023
|
+
fail(`Unknown models command '${sub}'.`);
|
|
1024
|
+
}
|
|
1025
|
+
var SPEECH_FORMATS = {
|
|
1026
|
+
mp3: "audio/mpeg",
|
|
1027
|
+
wav: "audio/wav",
|
|
1028
|
+
ogg: "audio/ogg",
|
|
1029
|
+
aac: "audio/aac",
|
|
1030
|
+
flac: "audio/flac"
|
|
1031
|
+
};
|
|
1032
|
+
var SPEECH_FORMAT_NAMES = Object.keys(SPEECH_FORMATS);
|
|
1033
|
+
var MEDIA_TYPES = {
|
|
1034
|
+
".png": "image/png",
|
|
1035
|
+
".jpg": "image/jpeg",
|
|
1036
|
+
".jpeg": "image/jpeg",
|
|
1037
|
+
".webp": "image/webp",
|
|
1038
|
+
".gif": "image/gif",
|
|
1039
|
+
".mp3": "audio/mpeg",
|
|
1040
|
+
".wav": "audio/wav",
|
|
1041
|
+
".ogg": "audio/ogg",
|
|
1042
|
+
".aac": "audio/aac",
|
|
1043
|
+
".flac": "audio/flac",
|
|
1044
|
+
".opus": "audio/opus",
|
|
1045
|
+
".m4a": "audio/mp4",
|
|
1046
|
+
".webm": "audio/webm"
|
|
1047
|
+
};
|
|
1048
|
+
var EXTENSIONS = Object.fromEntries(Object.entries(MEDIA_TYPES).filter(([extension]) => extension !== ".jpeg").map(([extension, mediaType]) => [mediaType, extension]));
|
|
1049
|
+
async function generateText(api, args) {
|
|
1050
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
1051
|
+
"--model": "string",
|
|
1052
|
+
"--instructions": "string",
|
|
1053
|
+
"--max-output-tokens": "string",
|
|
1054
|
+
"--reasoning": "string",
|
|
1055
|
+
"--file": "string"
|
|
1056
|
+
}, "models text", [0, 1]);
|
|
1057
|
+
const prompt = await readPrompt(positionals[0], options["--file"]);
|
|
1058
|
+
const maxOutputTokens = options["--max-output-tokens"];
|
|
1059
|
+
const result = await api.post("/api/models/language/generations", {
|
|
1060
|
+
...await languageModelRef(api, options["--model"]),
|
|
1061
|
+
prompt,
|
|
1062
|
+
...options["--instructions"] === undefined ? {} : { instructions: options["--instructions"] },
|
|
1063
|
+
...maxOutputTokens === undefined ? {} : { maxOutputTokens: integerInRange("--max-output-tokens", maxOutputTokens, 1, 1e6) },
|
|
1064
|
+
...options["--reasoning"] === undefined ? {} : { reasoning: options["--reasoning"] }
|
|
1065
|
+
});
|
|
1066
|
+
writeJson({
|
|
1067
|
+
callId: result.callId,
|
|
1068
|
+
model: result.model.modelId,
|
|
1069
|
+
text: result.text,
|
|
1070
|
+
finishReason: result.finishReason,
|
|
1071
|
+
cost: describeCost(result.cost)
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
async function generateImages(api, args) {
|
|
1075
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
1076
|
+
"--output": "string",
|
|
1077
|
+
"--model": "string",
|
|
1078
|
+
"--n": "string",
|
|
1079
|
+
"--size": "string",
|
|
1080
|
+
"--aspect-ratio": "string",
|
|
1081
|
+
"--quality": "string",
|
|
1082
|
+
"--style": "string",
|
|
1083
|
+
"--seed": "string",
|
|
1084
|
+
"--image": "strings"
|
|
1085
|
+
}, "models image", 1);
|
|
1086
|
+
const output = requireOutput(options["--output"], "models image");
|
|
1087
|
+
const seed = options["--seed"];
|
|
1088
|
+
if (seed !== undefined && !/^-?[0-9]+$/.test(seed))
|
|
1089
|
+
fail("--seed must be an integer.");
|
|
1090
|
+
const model = await modelRef(api, options["--model"], "image");
|
|
1091
|
+
const images = [];
|
|
1092
|
+
for (const path of options["--image"] ?? [])
|
|
1093
|
+
images.push(await uploadInput(api, path, "image"));
|
|
1094
|
+
const result = await api.post("/api/models/image/generations", {
|
|
1095
|
+
...model,
|
|
1096
|
+
prompt: positionals[0],
|
|
1097
|
+
...options["--n"] === undefined ? {} : { n: integerInRange("--n", options["--n"], 1, 100) },
|
|
1098
|
+
...optional("size", options["--size"]),
|
|
1099
|
+
...optional("aspectRatio", options["--aspect-ratio"]),
|
|
1100
|
+
...optional("quality", options["--quality"]),
|
|
1101
|
+
...optional("style", options["--style"]),
|
|
1102
|
+
...seed === undefined ? {} : { seed: Number(seed) },
|
|
1103
|
+
...images.length > 0 ? { images } : {}
|
|
1104
|
+
});
|
|
1105
|
+
writeJson(await savedResult(api, result, output));
|
|
1106
|
+
}
|
|
1107
|
+
async function generateSpeech(api, args) {
|
|
1108
|
+
const { positionals, options } = parseCommandArgs(args, {
|
|
1109
|
+
"--output": "string",
|
|
1110
|
+
"--model": "string",
|
|
1111
|
+
"--voice": "string",
|
|
1112
|
+
"--format": "string",
|
|
1113
|
+
"--instructions": "string",
|
|
1114
|
+
"--speed": "string",
|
|
1115
|
+
"--language": "string"
|
|
1116
|
+
}, "models speech", 1);
|
|
1117
|
+
const output = requireOutput(options["--output"], "models speech");
|
|
1118
|
+
const format = options["--format"];
|
|
1119
|
+
const speed = options["--speed"];
|
|
1120
|
+
if (speed !== undefined && !(Number(speed) > 0))
|
|
1121
|
+
fail("--speed must be a positive number.");
|
|
1122
|
+
const result = await api.post("/api/models/audio/speech", {
|
|
1123
|
+
...await modelRef(api, options["--model"], "speech"),
|
|
1124
|
+
text: positionals[0],
|
|
1125
|
+
...optional("voice", options["--voice"]),
|
|
1126
|
+
...format === undefined ? {} : { mediaType: SPEECH_FORMATS[enumValue("--format", format, SPEECH_FORMAT_NAMES)] },
|
|
1127
|
+
...optional("instructions", options["--instructions"]),
|
|
1128
|
+
...speed === undefined ? {} : { speed: Number(speed) },
|
|
1129
|
+
...optional("language", options["--language"])
|
|
1130
|
+
});
|
|
1131
|
+
writeJson(await savedResult(api, result, output));
|
|
1132
|
+
}
|
|
1133
|
+
async function transcribe(api, args) {
|
|
1134
|
+
const { positionals, options } = parseCommandArgs(args, { "--model": "string" }, "models transcribe", 1);
|
|
1135
|
+
const model = await modelRef(api, options["--model"], "transcription");
|
|
1136
|
+
const audio = await uploadInput(api, positionals[0] ?? "", "audio");
|
|
1137
|
+
const result = await api.post("/api/models/audio/transcriptions", {
|
|
1138
|
+
...model,
|
|
1139
|
+
audio
|
|
1140
|
+
});
|
|
1141
|
+
writeJson({
|
|
1142
|
+
callId: result.callId,
|
|
1143
|
+
model: result.model.modelId,
|
|
1144
|
+
text: result.text,
|
|
1145
|
+
cost: describeCost(result.cost)
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
async function calls(api, args) {
|
|
1149
|
+
const [sub, ...rest] = args;
|
|
1150
|
+
if (sub !== "files")
|
|
1151
|
+
fail(`Unknown models calls command '${sub ?? ""}'. Use 'models calls files'.`);
|
|
1152
|
+
const { positionals, options } = parseCommandArgs(rest, { "--output": "string" }, "models calls files", 1);
|
|
1153
|
+
const callId = positionals[0] ?? "";
|
|
1154
|
+
const call = await api.get(`/api/ai/model-calls/${idSegment(callId)}/files`);
|
|
1155
|
+
const output = options["--output"];
|
|
1156
|
+
if (output === undefined)
|
|
1157
|
+
return writeJson(call);
|
|
1158
|
+
writeJson({ callId: call.callId, files: await saveFiles(api, call.callId, call.files, output) });
|
|
1159
|
+
}
|
|
1160
|
+
async function modelRef(api, reference, kind) {
|
|
1161
|
+
if (reference === undefined)
|
|
1162
|
+
return {};
|
|
1163
|
+
const { entry } = findModel(catalogEntries(await api.get("/api/models")), reference, kind);
|
|
1164
|
+
return { model: { provider: entry.provider, modelId: entry.modelId } };
|
|
1165
|
+
}
|
|
1166
|
+
var LANGUAGE_TIERS = ["light", "standard", "heavy"];
|
|
1167
|
+
async function languageModelRef(api, reference) {
|
|
1168
|
+
const tier = LANGUAGE_TIERS.find((candidate) => candidate === reference);
|
|
1169
|
+
if (tier !== undefined)
|
|
1170
|
+
return { model: tier };
|
|
1171
|
+
return modelRef(api, reference, "language");
|
|
1172
|
+
}
|
|
1173
|
+
async function readPrompt(argument, source) {
|
|
1174
|
+
if (argument !== undefined && source !== undefined) {
|
|
1175
|
+
fail("models text takes its prompt as an argument or from --file, not both.");
|
|
1176
|
+
}
|
|
1177
|
+
const prompt = source === undefined ? argument : await readText(source, "Prompt file");
|
|
1178
|
+
if (prompt === undefined) {
|
|
1179
|
+
fail('models text requires a prompt: "<prompt>", or --file <path|-> to read it.');
|
|
1180
|
+
}
|
|
1181
|
+
if (!prompt.trim())
|
|
1182
|
+
fail("The prompt is empty.");
|
|
1183
|
+
return prompt;
|
|
1184
|
+
}
|
|
1185
|
+
function requireOutput(output, command) {
|
|
1186
|
+
if (!output)
|
|
1187
|
+
fail(`${command} requires --output <path>: a file, or a directory ending in /.`);
|
|
1188
|
+
return output;
|
|
1189
|
+
}
|
|
1190
|
+
function optional(field, value) {
|
|
1191
|
+
return value === undefined ? {} : { [field]: value };
|
|
1192
|
+
}
|
|
1193
|
+
async function uploadInput(api, path, kind) {
|
|
1194
|
+
try {
|
|
1195
|
+
await access2(path);
|
|
1196
|
+
} catch {
|
|
1197
|
+
fail(`Input file '${path}' does not exist.`);
|
|
1198
|
+
}
|
|
1199
|
+
const mediaType = MEDIA_TYPES[extname(path).toLowerCase()];
|
|
1200
|
+
if (!mediaType?.startsWith(`${kind}/`)) {
|
|
1201
|
+
const accepted = Object.entries(MEDIA_TYPES).filter(([, type]) => type.startsWith(`${kind}/`)).map(([extension]) => extension);
|
|
1202
|
+
fail(`'${path}' is not an ${kind} file. Use ${accepted.join(", ")}.`);
|
|
1203
|
+
}
|
|
1204
|
+
return api.upload("/api/files", path, undefined, mediaType);
|
|
1205
|
+
}
|
|
1206
|
+
async function savedResult(api, result, output) {
|
|
1207
|
+
return {
|
|
1208
|
+
callId: result.callId,
|
|
1209
|
+
model: result.model.modelId,
|
|
1210
|
+
files: await saveFiles(api, result.callId, result.files ?? [], output),
|
|
1211
|
+
...result.warnings?.length ? { warnings: result.warnings } : {},
|
|
1212
|
+
cost: describeCost(result.cost)
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
async function saveFiles(api, callId, files, output) {
|
|
1216
|
+
const directory = output.endsWith("/") || (await stat(output).catch(() => null))?.isDirectory();
|
|
1217
|
+
const paths = files.map((file, index) => {
|
|
1218
|
+
const number = files.length > 1 ? `-${index + 1}` : "";
|
|
1219
|
+
const extension = (file.mediaType && EXTENSIONS[file.mediaType]) ?? "";
|
|
1220
|
+
if (directory)
|
|
1221
|
+
return join2(output, `${callId}${number}${extension || ".bin"}`);
|
|
1222
|
+
const { dir, name, ext } = parse(output);
|
|
1223
|
+
return join2(dir, `${name}${number}${extension || ext}`);
|
|
1224
|
+
});
|
|
1225
|
+
for (const [index, path] of paths.entries()) {
|
|
1226
|
+
await mkdir(dirname2(path), { recursive: true });
|
|
1227
|
+
await api.download(`/api/ai/model-calls/${idSegment(callId)}/files/content`, path, {
|
|
1228
|
+
path: `/files/${index}`
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
return paths;
|
|
1232
|
+
}
|
|
1233
|
+
function describeCost(cost) {
|
|
1234
|
+
if (!cost.money)
|
|
1235
|
+
return "unknown";
|
|
1236
|
+
const nanos = BigInt(cost.money.amountNanos);
|
|
1237
|
+
const whole = nanos / 1000000000n;
|
|
1238
|
+
const fraction = (nanos % 1000000000n).toString().padStart(9, "0").replace(/0+$/, "");
|
|
1239
|
+
return `$${whole}${fraction ? `.${fraction}` : ""}`;
|
|
1240
|
+
}
|
|
1241
|
+
function modelKind(value) {
|
|
1242
|
+
const kind = MODEL_KINDS.find((candidate) => candidate === value);
|
|
1243
|
+
if (!kind)
|
|
1244
|
+
fail(`Unknown model kind '${value}'. Use one of: ${MODEL_KINDS.join(", ")}.`);
|
|
1245
|
+
return kind;
|
|
1246
|
+
}
|
|
1247
|
+
function catalogEntries(catalog) {
|
|
1248
|
+
return [
|
|
1249
|
+
...catalog.language.map((entry) => ({ kind: "language", entry })),
|
|
1250
|
+
...catalog.image.map((entry) => ({ kind: "image", entry })),
|
|
1251
|
+
...catalog.audio.speech.map((entry) => ({ kind: "speech", entry })),
|
|
1252
|
+
...catalog.audio.transcription.map((entry) => ({ kind: "transcription", entry }))
|
|
1253
|
+
];
|
|
1254
|
+
}
|
|
1255
|
+
function findModel(entries, reference, kind) {
|
|
1256
|
+
const ofKind = entries.filter((candidate) => kind === undefined || candidate.kind === kind);
|
|
1257
|
+
let matches = ofKind.filter((candidate) => candidate.entry.modelId === reference);
|
|
1258
|
+
const separator = reference.indexOf(":");
|
|
1259
|
+
if (matches.length === 0 && separator > 0) {
|
|
1260
|
+
const provider = reference.slice(0, separator);
|
|
1261
|
+
const modelId = reference.slice(separator + 1);
|
|
1262
|
+
matches = ofKind.filter((candidate) => candidate.entry.provider === provider && candidate.entry.modelId === modelId);
|
|
1263
|
+
}
|
|
1264
|
+
const [match] = matches;
|
|
1265
|
+
if (!match) {
|
|
1266
|
+
fail(`No ${kind === undefined ? "" : `${kind} `}model '${reference}' is configured. Run 'sixb models list'.`);
|
|
1267
|
+
}
|
|
1268
|
+
if (matches.length > 1) {
|
|
1269
|
+
const kinds = new Set(matches.map((candidate) => candidate.kind));
|
|
1270
|
+
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}.`);
|
|
1271
|
+
}
|
|
1272
|
+
return match;
|
|
1273
|
+
}
|
|
1274
|
+
function describePrice(card) {
|
|
1275
|
+
if (!card)
|
|
1276
|
+
return "price unknown";
|
|
1277
|
+
const parts = [
|
|
1278
|
+
variantPrice(card.images, "per image", "at"),
|
|
1279
|
+
variantPrice(card.characters, "per 1M characters", "for"),
|
|
1280
|
+
variantPrice(card.audioInputSeconds, "per second of audio", "for"),
|
|
1281
|
+
card.input !== undefined && card.output !== undefined ? `${dollars(tokenPrice(card.input))} input \xB7 ${dollars(tokenPrice(card.output))} output per 1M tokens` : undefined,
|
|
1282
|
+
card.audioOutput === undefined ? undefined : `${dollars(tokenPrice(card.audioOutput))} per 1M audio tokens`
|
|
1283
|
+
].filter((part) => part !== undefined);
|
|
1284
|
+
return parts.length === 0 ? "price unknown" : parts.join(" \xB7 ");
|
|
1285
|
+
}
|
|
1286
|
+
function variantPrice(price, unit, joiner) {
|
|
1287
|
+
if (price === undefined)
|
|
1288
|
+
return;
|
|
1289
|
+
if (typeof price === "string")
|
|
1290
|
+
return `${dollars(price)} ${unit}`;
|
|
1291
|
+
const only = price.default;
|
|
1292
|
+
if (only !== undefined && price.variants.every((variant) => variant.price === only)) {
|
|
1293
|
+
return `${dollars(only)} ${unit}`;
|
|
1294
|
+
}
|
|
1295
|
+
const variants = price.variants.map(({ price: amount, ...dimensions }) => {
|
|
1296
|
+
const label = Object.values(dimensions).join(" ");
|
|
1297
|
+
return `${dollars(amount)} ${joiner} ${label}`;
|
|
1298
|
+
});
|
|
1299
|
+
const otherwise = price.default === undefined ? [] : [`otherwise ${dollars(price.default)}`];
|
|
1300
|
+
return `${unit}: ${[...variants, ...otherwise].join(", ")}`;
|
|
1301
|
+
}
|
|
1302
|
+
function tokenPrice(price) {
|
|
1303
|
+
return typeof price === "string" ? price : price.default;
|
|
1304
|
+
}
|
|
1305
|
+
function dollars(amount) {
|
|
1306
|
+
return `$${amount}`;
|
|
1307
|
+
}
|
|
705
1308
|
|
|
706
1309
|
// src/graph.ts
|
|
707
1310
|
async function inspectGraph(api, objectTypeId, primaryId, options) {
|
|
@@ -968,6 +1571,8 @@ async function objects(api, args) {
|
|
|
968
1571
|
return objectsList(api, rest);
|
|
969
1572
|
case "get":
|
|
970
1573
|
return objectsGet(api, rest);
|
|
1574
|
+
case "get-by":
|
|
1575
|
+
return objectsGetBy(api, rest);
|
|
971
1576
|
case "search":
|
|
972
1577
|
return objectsSearch(api, rest);
|
|
973
1578
|
case "query":
|
|
@@ -1058,6 +1663,31 @@ async function objectsGet(api, args) {
|
|
|
1058
1663
|
includeTotal: false
|
|
1059
1664
|
}));
|
|
1060
1665
|
}
|
|
1666
|
+
async function objectsGetBy(api, args) {
|
|
1667
|
+
if (requestsHelp(args))
|
|
1668
|
+
return writeText(GET_BY_HELP);
|
|
1669
|
+
const { positionals, options } = parseCommandArgs(args, { "--json": "boolean" }, "objects get-by", 3);
|
|
1670
|
+
const [objectTypeId = "", propertyId = "", text = ""] = positionals;
|
|
1671
|
+
const value = options["--json"] ? parseJsonValue(text) : text;
|
|
1672
|
+
const path = `/api/objects/${encodeURIComponent(objectTypeId)}/by/${encodeURIComponent(propertyId)}`;
|
|
1673
|
+
try {
|
|
1674
|
+
writeJson(await api.post(path, { value }));
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
if (!isObjectNotFound(error))
|
|
1677
|
+
throw error;
|
|
1678
|
+
writeJson(null);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
function parseJsonValue(text) {
|
|
1682
|
+
try {
|
|
1683
|
+
return JSON.parse(text);
|
|
1684
|
+
} catch {
|
|
1685
|
+
fail(`--json needs the value as JSON, such as 42 or '{"type":"user","id":"usr_1"}'.`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
function isObjectNotFound(error) {
|
|
1689
|
+
return error instanceof CliError && error.body.status === 404 && error.body.code === "object.not_found";
|
|
1690
|
+
}
|
|
1061
1691
|
async function objectsSearch(api, args) {
|
|
1062
1692
|
if (requestsHelp(args))
|
|
1063
1693
|
return writeText(SEARCH_HELP);
|
|
@@ -1238,7 +1868,7 @@ async function ontology(api, args) {
|
|
|
1238
1868
|
if (requestsHelp(rest))
|
|
1239
1869
|
return writeText("Usage: sixb ontology get <object-type>");
|
|
1240
1870
|
const { positionals } = parseCommandArgs(rest, {}, "ontology get", 1);
|
|
1241
|
-
return writeJson(await api.get(`/api/object-types/${
|
|
1871
|
+
return writeJson(await api.get(`/api/object-types/${idSegment(positionals[0])}`));
|
|
1242
1872
|
}
|
|
1243
1873
|
fail(`Unknown ontology command '${sub}'.`);
|
|
1244
1874
|
}
|
|
@@ -1264,12 +1894,25 @@ var WORKFLOW_RUN_STATUSES = [
|
|
|
1264
1894
|
"failed",
|
|
1265
1895
|
"cancelled"
|
|
1266
1896
|
];
|
|
1897
|
+
var DATA_RUN_STATUSES = ["queued", "running", "succeeded", "failed", "cancelled"];
|
|
1898
|
+
var STATUSES = {
|
|
1899
|
+
action: ACTION_RUN_STATUSES,
|
|
1900
|
+
workflow: WORKFLOW_RUN_STATUSES,
|
|
1901
|
+
sync: DATA_RUN_STATUSES,
|
|
1902
|
+
pipeline: DATA_RUN_STATUSES
|
|
1903
|
+
};
|
|
1904
|
+
var FILTERS = {
|
|
1905
|
+
action: { "--action": "actionId", "--type": "objectTypeId", "--id": "primaryId" },
|
|
1906
|
+
workflow: { "--workflow": "workflowId" },
|
|
1907
|
+
sync: { "--sync": "syncId", "--dataset": "datasetId" },
|
|
1908
|
+
pipeline: { "--pipeline": "pipelineId" }
|
|
1909
|
+
};
|
|
1267
1910
|
async function runs(api, kind, args) {
|
|
1268
1911
|
const [sub, ...rest] = args;
|
|
1269
1912
|
const group = `${kind}-runs`;
|
|
1270
1913
|
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
1271
1914
|
return writeText(GROUP_HELP[group]);
|
|
1272
|
-
if (sub === "get") {
|
|
1915
|
+
if (sub === "get" && kind !== "sync") {
|
|
1273
1916
|
const { positionals } = parseCommandArgs(rest, {}, `${group} get`, 1);
|
|
1274
1917
|
return writeJson(await api.get(`/api/${group}/${encodeURIComponent(positionals[0] ?? "")}`));
|
|
1275
1918
|
}
|
|
@@ -1282,21 +1925,14 @@ async function runs(api, kind, args) {
|
|
|
1282
1925
|
"--offset": "offset",
|
|
1283
1926
|
"--order": "order"
|
|
1284
1927
|
};
|
|
1285
|
-
const
|
|
1286
|
-
...common,
|
|
1287
|
-
"--action": "actionId",
|
|
1288
|
-
"--type": "objectTypeId",
|
|
1289
|
-
"--id": "primaryId"
|
|
1290
|
-
};
|
|
1291
|
-
const workflow = { ...common, "--workflow": "workflowId" };
|
|
1292
|
-
const options = normalizeWindowOptions(parseQueryOptions(rest, kind === "action" ? action : workflow, `${group} list`), {
|
|
1928
|
+
const options = normalizeWindowOptions(parseQueryOptions(rest, { ...common, ...FILTERS[kind] }, `${group} list`), {
|
|
1293
1929
|
defaultLimit: CLI_LIMITS.list.default,
|
|
1294
1930
|
maximumLimit: CLI_LIMITS.list.maximum,
|
|
1295
1931
|
defaultOrder: DEFAULT_LIST_ORDER,
|
|
1296
1932
|
offset: true
|
|
1297
1933
|
});
|
|
1298
1934
|
if (options.status !== undefined) {
|
|
1299
|
-
options.status = enumValue("--status", options.status, kind
|
|
1935
|
+
options.status = enumValue("--status", options.status, STATUSES[kind]);
|
|
1300
1936
|
}
|
|
1301
1937
|
for (const [name, flag] of [
|
|
1302
1938
|
["startedAfter", "--started-after"],
|
|
@@ -1350,7 +1986,7 @@ async function telemetry(api, args) {
|
|
|
1350
1986
|
fail(`Unknown telemetry command '${sub}'.`);
|
|
1351
1987
|
}
|
|
1352
1988
|
function telemetryPath(args, terminal) {
|
|
1353
|
-
return `/api/objects/${
|
|
1989
|
+
return `/api/objects/${idSegment(args[0])}/${idSegment(args[1])}/telemetry/${idSegment(args[2])}/${terminal}`;
|
|
1354
1990
|
}
|
|
1355
1991
|
function normalizeTelemetryQueryInput(input) {
|
|
1356
1992
|
if (Array.isArray(input) || typeof input !== "object" || input === null) {
|
|
@@ -1379,7 +2015,59 @@ function normalizeTelemetryQueryInput(input) {
|
|
|
1379
2015
|
return body;
|
|
1380
2016
|
}
|
|
1381
2017
|
|
|
2018
|
+
// src/commands/users.ts
|
|
2019
|
+
var USER_STATUSES = ["active", "suspended", "all"];
|
|
2020
|
+
var LIST_FLAGS = {
|
|
2021
|
+
"--status": "string",
|
|
2022
|
+
"--limit": "string",
|
|
2023
|
+
"--offset": "string"
|
|
2024
|
+
};
|
|
2025
|
+
async function users(api, args) {
|
|
2026
|
+
const [sub, ...rest] = args;
|
|
2027
|
+
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
2028
|
+
return writeText(GROUP_HELP.users);
|
|
2029
|
+
if (sub === "get") {
|
|
2030
|
+
const { positionals } = parseCommandArgs(rest, {}, "users get", 1);
|
|
2031
|
+
const user = positionals[0]?.trim();
|
|
2032
|
+
if (!user)
|
|
2033
|
+
fail("users get requires a user id or email.");
|
|
2034
|
+
if (!user.includes("@")) {
|
|
2035
|
+
return writeJson(await api.get(`/api/users/${idSegment(user)}`));
|
|
2036
|
+
}
|
|
2037
|
+
const page = await api.get("/api/users", { email: user });
|
|
2038
|
+
const [member] = isRecord(page) && Array.isArray(page.users) ? page.users : [];
|
|
2039
|
+
if (member === undefined) {
|
|
2040
|
+
throw new CliError({ code: "not_found", message: `No member has the email '${user}'.` }, EXIT_API);
|
|
2041
|
+
}
|
|
2042
|
+
return writeJson(member);
|
|
2043
|
+
}
|
|
2044
|
+
if (sub === "list") {
|
|
2045
|
+
const { options } = parseCommandArgs(rest, LIST_FLAGS, "users list");
|
|
2046
|
+
return writeJson(await api.get("/api/users", listQuery(options)));
|
|
2047
|
+
}
|
|
2048
|
+
if (sub === "search") {
|
|
2049
|
+
const { positionals, options } = parseCommandArgs(rest, LIST_FLAGS, "users search", 1);
|
|
2050
|
+
const search = positionals[0]?.trim();
|
|
2051
|
+
if (!search)
|
|
2052
|
+
fail("users search requires the text to find in a name or email.");
|
|
2053
|
+
return writeJson(await api.get("/api/users", { ...listQuery(options), search }));
|
|
2054
|
+
}
|
|
2055
|
+
fail(`Unknown users command '${sub}'.`);
|
|
2056
|
+
}
|
|
2057
|
+
function listQuery(options) {
|
|
2058
|
+
const limit = integerInRange("--limit", options["--limit"] ?? String(CLI_LIMITS.users.default), 1, CLI_LIMITS.users.maximum);
|
|
2059
|
+
return {
|
|
2060
|
+
status: enumValue("--status", options["--status"] ?? "active", USER_STATUSES),
|
|
2061
|
+
limit: String(limit),
|
|
2062
|
+
...options["--offset"] === undefined ? {} : { offset: String(nonNegativeInteger("--offset", options["--offset"])) }
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
function isRecord(value) {
|
|
2066
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
1382
2069
|
// src/commands/workflows.ts
|
|
2070
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1383
2071
|
async function workflows(api, args) {
|
|
1384
2072
|
const [sub, ...rest] = args;
|
|
1385
2073
|
if (!sub || isHelp(sub) || requestsHelp(rest))
|
|
@@ -1392,18 +2080,22 @@ async function workflows(api, args) {
|
|
|
1392
2080
|
const {
|
|
1393
2081
|
positionals: [workflowId]
|
|
1394
2082
|
} = parseCommandArgs(rest, {}, "workflows get", 1);
|
|
1395
|
-
return writeJson(await api.get(`/api/workflows/${
|
|
2083
|
+
return writeJson(await api.get(`/api/workflows/${idSegment(workflowId)}`));
|
|
1396
2084
|
}
|
|
1397
2085
|
if (sub === "start") {
|
|
1398
2086
|
const {
|
|
1399
2087
|
positionals: [workflowId],
|
|
1400
2088
|
options
|
|
1401
|
-
} = parseCommandArgs(rest, { "--file": "string" }, "workflows start", 1);
|
|
2089
|
+
} = parseCommandArgs(rest, { "--file": "string", "--run-id": "string" }, "workflows start", 1);
|
|
1402
2090
|
const input = options["--file"] ? await readJson(options["--file"]) : {};
|
|
1403
2091
|
if (Array.isArray(input) || typeof input !== "object" || input === null) {
|
|
1404
2092
|
fail("Workflow input must be a JSON object.");
|
|
1405
2093
|
}
|
|
1406
|
-
|
|
2094
|
+
const runId = options["--run-id"] ?? `run_${randomUUID3()}`;
|
|
2095
|
+
return writeRequestResult(await api.post(`/api/workflows/${idSegment(workflowId)}/runs`, {
|
|
2096
|
+
input,
|
|
2097
|
+
runId
|
|
2098
|
+
}));
|
|
1407
2099
|
}
|
|
1408
2100
|
fail(`Unknown workflows command '${sub}'.`);
|
|
1409
2101
|
}
|
|
@@ -1418,7 +2110,14 @@ var INSTANCE_COMMANDS = [
|
|
|
1418
2110
|
"action-runs",
|
|
1419
2111
|
"files",
|
|
1420
2112
|
"workflows",
|
|
1421
|
-
"workflow-runs"
|
|
2113
|
+
"workflow-runs",
|
|
2114
|
+
"users",
|
|
2115
|
+
"models",
|
|
2116
|
+
"datasets",
|
|
2117
|
+
"syncs",
|
|
2118
|
+
"sync-runs",
|
|
2119
|
+
"pipelines",
|
|
2120
|
+
"pipeline-runs"
|
|
1422
2121
|
];
|
|
1423
2122
|
function isInstanceCommand(command) {
|
|
1424
2123
|
return INSTANCE_COMMANDS.includes(command);
|
|
@@ -1443,6 +2142,20 @@ async function dispatch(api, command, args) {
|
|
|
1443
2142
|
return workflows(api, args);
|
|
1444
2143
|
case "workflow-runs":
|
|
1445
2144
|
return runs(api, "workflow", args);
|
|
2145
|
+
case "users":
|
|
2146
|
+
return users(api, args);
|
|
2147
|
+
case "models":
|
|
2148
|
+
return models(api, args);
|
|
2149
|
+
case "datasets":
|
|
2150
|
+
return datasets(api, args);
|
|
2151
|
+
case "syncs":
|
|
2152
|
+
return runnables(api, "syncs", args);
|
|
2153
|
+
case "sync-runs":
|
|
2154
|
+
return runs(api, "sync", args);
|
|
2155
|
+
case "pipelines":
|
|
2156
|
+
return runnables(api, "pipelines", args);
|
|
2157
|
+
case "pipeline-runs":
|
|
2158
|
+
return runs(api, "pipeline", args);
|
|
1446
2159
|
default:
|
|
1447
2160
|
fail(`Unknown command '${command}'. Run 'sixb --help'.`);
|
|
1448
2161
|
}
|
|
@@ -1457,29 +2170,54 @@ function createInstanceApiClient(mode) {
|
|
|
1457
2170
|
baseUrl: mode.baseUrl,
|
|
1458
2171
|
...mode.kind === "local" && mode.token ? { authorization: `Bearer ${mode.token}` } : {},
|
|
1459
2172
|
missingBaseUrlMessage: mode.kind === "sandbox" ? "SIXB_API_BASE_URL is not set." : "The selected Sixb profile has no API URL.",
|
|
1460
|
-
unavailableMessage: mode.kind === "sandbox" ? "The Sixb API
|
|
1461
|
-
unavailableHint: mode.kind === "sandbox" ? "Run 'sixb
|
|
2173
|
+
unavailableMessage: mode.kind === "sandbox" ? "The Sixb API could not be reached from the sandbox." : "The Sixb API could not be reached.",
|
|
2174
|
+
unavailableHint: mode.kind === "sandbox" ? "Run 'sixb status' to verify the sandbox runtime and its API access." : "Run 'sixb status' to verify the current profile."
|
|
1462
2175
|
});
|
|
1463
2176
|
}
|
|
2177
|
+
|
|
2178
|
+
// src/commands/system.ts
|
|
2179
|
+
var AGENT_RUNTIME_PROFILE = "sixb-agent-runtime/v1";
|
|
2180
|
+
// src/memory.ts
|
|
2181
|
+
import { createHash } from "crypto";
|
|
2182
|
+
var AGENT_MEMORY_MANIFEST = "context/memory.json";
|
|
2183
|
+
var AGENT_MEMORY_ENTRY = "MEMORY.md";
|
|
2184
|
+
var AGENT_MEMORY_STARTER = `# Memory
|
|
2185
|
+
|
|
2186
|
+
`;
|
|
2187
|
+
function agentMemoryFileHash(contents) {
|
|
2188
|
+
return createHash("sha256").update(contents).digest("hex");
|
|
2189
|
+
}
|
|
2190
|
+
// src/sandbox-cli.ts
|
|
2191
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2192
|
+
var cachedAssets;
|
|
2193
|
+
function loadSandboxCliAssets() {
|
|
2194
|
+
cachedAssets ??= Promise.all([
|
|
2195
|
+
readFile3(new URL("./bin/sixb", import.meta.url), "utf8"),
|
|
2196
|
+
readFile3(new URL("./generated/sixb.mjs", import.meta.url))
|
|
2197
|
+
]).then(([launcher, artifact]) => ({ launcher, artifact }));
|
|
2198
|
+
return cachedAssets;
|
|
2199
|
+
}
|
|
1464
2200
|
export {
|
|
2201
|
+
AGENT_MEMORY_ENTRY,
|
|
2202
|
+
AGENT_MEMORY_MANIFEST,
|
|
2203
|
+
AGENT_MEMORY_STARTER,
|
|
2204
|
+
AGENT_RUNTIME_PROFILE,
|
|
1465
2205
|
ApiClient,
|
|
1466
2206
|
CliError,
|
|
1467
2207
|
EXIT_API,
|
|
1468
2208
|
EXIT_USAGE,
|
|
1469
2209
|
INSTANCE_CLI_VERSION,
|
|
1470
2210
|
INSTANCE_COMMANDS,
|
|
1471
|
-
|
|
2211
|
+
agentMemoryFileHash,
|
|
1472
2212
|
createInstanceApiClient,
|
|
1473
2213
|
fail,
|
|
1474
|
-
isFileError,
|
|
1475
|
-
isHelp,
|
|
1476
2214
|
isInstanceCommand,
|
|
2215
|
+
loadSandboxCliAssets,
|
|
1477
2216
|
renderInstanceHelp,
|
|
1478
2217
|
reportError,
|
|
1479
2218
|
requestsHelp,
|
|
1480
2219
|
runInstanceCli,
|
|
1481
|
-
writeJson
|
|
1482
|
-
writeText
|
|
2220
|
+
writeJson
|
|
1483
2221
|
};
|
|
1484
2222
|
|
|
1485
|
-
//# debugId=
|
|
2223
|
+
//# debugId=3808833041577FAB64756E2164756E21
|