@sixb/cli-core 0.1.4

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -0
  3. package/dist/api-client.d.ts +23 -0
  4. package/dist/api-client.d.ts.map +1 -0
  5. package/dist/arguments.d.ts +18 -0
  6. package/dist/arguments.d.ts.map +1 -0
  7. package/dist/commands/actions.d.ts +3 -0
  8. package/dist/commands/actions.d.ts.map +1 -0
  9. package/dist/commands/files.d.ts +3 -0
  10. package/dist/commands/files.d.ts.map +1 -0
  11. package/dist/commands/index.d.ts +6 -0
  12. package/dist/commands/index.d.ts.map +1 -0
  13. package/dist/commands/metadata.d.ts +18 -0
  14. package/dist/commands/metadata.d.ts.map +1 -0
  15. package/dist/commands/objects.d.ts +3 -0
  16. package/dist/commands/objects.d.ts.map +1 -0
  17. package/dist/commands/ontology.d.ts +3 -0
  18. package/dist/commands/ontology.d.ts.map +1 -0
  19. package/dist/commands/project.d.ts +3 -0
  20. package/dist/commands/project.d.ts.map +1 -0
  21. package/dist/commands/runs.d.ts +3 -0
  22. package/dist/commands/runs.d.ts.map +1 -0
  23. package/dist/commands/shared.d.ts +15 -0
  24. package/dist/commands/shared.d.ts.map +1 -0
  25. package/dist/commands/telemetry.d.ts +3 -0
  26. package/dist/commands/telemetry.d.ts.map +1 -0
  27. package/dist/commands/workflows.d.ts +3 -0
  28. package/dist/commands/workflows.d.ts.map +1 -0
  29. package/dist/graph.d.ts +10 -0
  30. package/dist/graph.d.ts.map +1 -0
  31. package/dist/index.d.ts +11 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1400 -0
  34. package/dist/index.js.map +26 -0
  35. package/dist/output.d.ts +21 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/policies.d.ts +38 -0
  38. package/dist/policies.d.ts.map +1 -0
  39. package/dist/run.d.ts +20 -0
  40. package/dist/run.d.ts.map +1 -0
  41. package/dist/tsconfig.json +5 -0
  42. package/package.json +53 -0
  43. package/src/api-client.ts +177 -0
  44. package/src/arguments.ts +130 -0
  45. package/src/commands/actions.ts +94 -0
  46. package/src/commands/files.ts +52 -0
  47. package/src/commands/index.ts +57 -0
  48. package/src/commands/metadata.ts +212 -0
  49. package/src/commands/objects.ts +291 -0
  50. package/src/commands/ontology.ts +50 -0
  51. package/src/commands/project.ts +13 -0
  52. package/src/commands/runs.ts +84 -0
  53. package/src/commands/shared.ts +97 -0
  54. package/src/commands/telemetry.ts +96 -0
  55. package/src/commands/workflows.ts +34 -0
  56. package/src/graph.ts +359 -0
  57. package/src/index.ts +24 -0
  58. package/src/output.ts +51 -0
  59. package/src/policies.ts +17 -0
  60. package/src/run.ts +46 -0
package/dist/index.js ADDED
@@ -0,0 +1,1400 @@
1
+ // @bun
2
+ // src/api-client.ts
3
+ import { randomUUID } from "crypto";
4
+ import { createWriteStream } from "fs";
5
+ import { readFile, rename, rm } from "fs/promises";
6
+ import { basename, dirname, join } from "path";
7
+ import { Readable } from "stream";
8
+ import { pipeline } from "stream/promises";
9
+
10
+ // src/output.ts
11
+ var INSTANCE_CLI_VERSION = "1";
12
+ var EXIT_USAGE = 2;
13
+ var EXIT_API = 3;
14
+
15
+ class CliError extends Error {
16
+ body;
17
+ exitCode;
18
+ constructor(body, exitCode = EXIT_USAGE) {
19
+ super(body.message);
20
+ this.name = "CliError";
21
+ this.body = body;
22
+ this.exitCode = exitCode;
23
+ }
24
+ }
25
+ function fail(message, code = "invalid_arguments", hint) {
26
+ throw new CliError({ code, message, ...hint ? { hint } : {} });
27
+ }
28
+ function writeJson(value) {
29
+ process.stdout.write(`${JSON.stringify(value)}
30
+ `);
31
+ }
32
+ function writeText(value) {
33
+ process.stdout.write(value.endsWith(`
34
+ `) ? value : `${value}
35
+ `);
36
+ }
37
+ function reportError(error) {
38
+ const cliError = error instanceof CliError ? error : new CliError({
39
+ code: "internal_error",
40
+ message: error instanceof Error ? error.message : "The Sixb CLI failed unexpectedly."
41
+ }, EXIT_API);
42
+ process.stderr.write(`${JSON.stringify({ error: cliError.body })}
43
+ `);
44
+ return cliError.exitCode;
45
+ }
46
+
47
+ // src/api-client.ts
48
+ class ApiClient {
49
+ baseUrl;
50
+ authorization;
51
+ missingBaseUrlMessage;
52
+ unavailableMessage;
53
+ unavailableHint;
54
+ constructor(options) {
55
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
56
+ this.authorization = options.authorization;
57
+ this.missingBaseUrlMessage = options.missingBaseUrlMessage;
58
+ this.unavailableMessage = options.unavailableMessage;
59
+ this.unavailableHint = options.unavailableHint;
60
+ }
61
+ async get(path, query) {
62
+ const url = this.url(path, query);
63
+ return this.json(url, { method: "GET" });
64
+ }
65
+ async post(path, body) {
66
+ return this.json(this.url(path), {
67
+ method: "POST",
68
+ headers: { "content-type": "application/json" },
69
+ body: JSON.stringify(body)
70
+ });
71
+ }
72
+ async upload(path, source, logicalPath) {
73
+ const form = new FormData;
74
+ form.append("file", new Blob([new Uint8Array(await readFile(source))]), basename(source));
75
+ if (logicalPath)
76
+ form.append("logicalPath", logicalPath);
77
+ return this.json(this.url(path), { method: "POST", body: form });
78
+ }
79
+ async download(path, output, query) {
80
+ const response = await this.fetch(this.url(path, query), { method: "GET" });
81
+ const temporary = join(dirname(output), `.${basename(output)}.sixb-${randomUUID()}.tmp`);
82
+ try {
83
+ const body = response.body ? Readable.fromWeb(response.body) : Readable.from([]);
84
+ await pipeline(body, createWriteStream(temporary, { flags: "wx" }));
85
+ await rename(temporary, output);
86
+ } catch (error) {
87
+ await rm(temporary, { force: true }).catch(() => {});
88
+ throw error;
89
+ }
90
+ }
91
+ url(path, query) {
92
+ if (!this.baseUrl)
93
+ fail(this.missingBaseUrlMessage, "runtime_unavailable");
94
+ validateApiPath(path);
95
+ const url = new URL(`${this.baseUrl}${path}`);
96
+ for (const [key, value] of Object.entries(query ?? {})) {
97
+ if (value !== undefined)
98
+ url.searchParams.set(key, value);
99
+ }
100
+ return url;
101
+ }
102
+ async json(url, init) {
103
+ const response = await this.fetch(url, init);
104
+ const text = await response.text();
105
+ if (!text)
106
+ return null;
107
+ try {
108
+ return JSON.parse(text);
109
+ } catch {
110
+ throw new CliError({ code: "invalid_api_response", message: "The Sixb API returned invalid JSON." }, EXIT_API);
111
+ }
112
+ }
113
+ async fetch(url, init) {
114
+ let response;
115
+ try {
116
+ const headers = new Headers(init.headers);
117
+ if (this.authorization)
118
+ headers.set("authorization", this.authorization);
119
+ response = await fetch(url, { ...init, headers });
120
+ } catch {
121
+ throw new CliError({
122
+ code: "api_unreachable",
123
+ message: this.unavailableMessage,
124
+ hint: this.unavailableHint
125
+ }, EXIT_API);
126
+ }
127
+ if (response.ok)
128
+ return response;
129
+ const body = await response.text();
130
+ let parsed;
131
+ try {
132
+ parsed = JSON.parse(body);
133
+ } catch {
134
+ parsed = undefined;
135
+ }
136
+ const record = asRecord(parsed);
137
+ const error = record.error;
138
+ const nested = asRecord(error);
139
+ const code = stringField(record, "code") ?? stringField(nested, "code") ?? "http_error";
140
+ const message = stringField(record, "message") ?? stringField(nested, "message") ?? (typeof error === "string" ? error : `The Sixb API request failed with HTTP ${response.status}.`);
141
+ const hint = stringField(record, "hint") ?? stringField(nested, "hint");
142
+ const issues = Array.isArray(record.issues) ? record.issues : Array.isArray(nested.issues) ? nested.issues : undefined;
143
+ throw new CliError({
144
+ code,
145
+ status: response.status,
146
+ message,
147
+ ...hint ? { hint } : {},
148
+ ...issues ? { issues } : {}
149
+ }, EXIT_API);
150
+ }
151
+ }
152
+ function validateApiPath(path) {
153
+ const invalid = () => fail("API paths must be relative and start with /api/.");
154
+ if (path.includes("\\") || path.includes("#"))
155
+ invalid();
156
+ const normalized = (() => {
157
+ try {
158
+ return new URL(path, "http://sixb.invalid");
159
+ } catch {
160
+ return invalid();
161
+ }
162
+ })();
163
+ if (normalized.origin !== "http://sixb.invalid" || normalized.pathname !== "/api" && !normalized.pathname.startsWith("/api/")) {
164
+ invalid();
165
+ }
166
+ }
167
+ function asRecord(value) {
168
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
169
+ }
170
+ function stringField(record, key) {
171
+ const value = record[key];
172
+ return typeof value === "string" ? value : undefined;
173
+ }
174
+ // src/arguments.ts
175
+ function parseCommandArgs(args, kinds, command, positionalCount = 0) {
176
+ const positionals = [];
177
+ const options = {};
178
+ let positionalOnly = false;
179
+ for (let index = 0;index < args.length; index++) {
180
+ const argument = args[index] ?? "";
181
+ if (!positionalOnly && argument === "--") {
182
+ positionalOnly = true;
183
+ continue;
184
+ }
185
+ if (positionalOnly || !argument.startsWith("-") || argument === "-") {
186
+ positionals.push(argument);
187
+ continue;
188
+ }
189
+ const equals = argument.indexOf("=");
190
+ const flag = equals < 0 ? argument : argument.slice(0, equals);
191
+ if (!Object.hasOwn(kinds, flag))
192
+ fail(`Unknown ${command} option '${flag}'.`);
193
+ if (Object.hasOwn(options, flag))
194
+ fail(`${flag} may only be provided once.`);
195
+ if (kinds[flag] === "boolean") {
196
+ if (equals >= 0)
197
+ fail(`${flag} does not accept a value.`);
198
+ options[flag] = true;
199
+ } else {
200
+ const value = equals < 0 ? args[++index] : argument.slice(equals + 1);
201
+ if (!value || equals < 0 && value.startsWith("--")) {
202
+ fail(`${flag} requires a value.`);
203
+ }
204
+ options[flag] = value;
205
+ }
206
+ }
207
+ const [minimum, maximum] = typeof positionalCount === "number" ? [positionalCount, positionalCount] : positionalCount;
208
+ if (positionals.length < minimum || positionals.length > maximum) {
209
+ fail(`Invalid arguments for '${command}'.`);
210
+ }
211
+ return {
212
+ positionals,
213
+ options
214
+ };
215
+ }
216
+ function requestsHelp(args) {
217
+ const separator = args.indexOf("--");
218
+ const beforeSeparator = separator < 0 ? args : args.slice(0, separator);
219
+ return isHelp(args[0]) || beforeSeparator.includes("--help") || beforeSeparator.includes("-h");
220
+ }
221
+ var RFC3339_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
222
+ function isHelp(value) {
223
+ return value === "-h" || value === "--help" || value === "help";
224
+ }
225
+ function requireExact(args, count, message) {
226
+ if (args.length !== count)
227
+ fail(message);
228
+ }
229
+ function integerInRange(flag, value, minimum, maximum) {
230
+ if (!/^[0-9]+$/.test(value)) {
231
+ fail(`${flag} must be an integer from ${minimum} through ${maximum}.`);
232
+ }
233
+ const parsed = Number(value);
234
+ if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
235
+ fail(`${flag} must be an integer from ${minimum} through ${maximum}.`);
236
+ }
237
+ return parsed;
238
+ }
239
+ function nonNegativeInteger(flag, value) {
240
+ if (!/^[0-9]+$/.test(value)) {
241
+ fail(`${flag} must be a non-negative integer.`);
242
+ }
243
+ const parsed = Number(value);
244
+ if (!Number.isSafeInteger(parsed)) {
245
+ fail(`${flag} must be a non-negative safe integer.`);
246
+ }
247
+ return parsed;
248
+ }
249
+ function rfc3339Value(flag, value) {
250
+ if (!RFC3339_TIMESTAMP.test(value) || Number.isNaN(Date.parse(value))) {
251
+ fail(`${flag} must be an RFC 3339 timestamp.`);
252
+ }
253
+ return value;
254
+ }
255
+ function requireOrderedRange(afterFlag, after, beforeFlag, before) {
256
+ if (after && before && Date.parse(after) > Date.parse(before)) {
257
+ fail(`${afterFlag} must be before or equal to ${beforeFlag}.`);
258
+ }
259
+ }
260
+ function enumValue(flag, value, allowed) {
261
+ if (!allowed.includes(value))
262
+ fail(`${flag} must be ${formatAlternatives(allowed)}.`);
263
+ return value;
264
+ }
265
+ function formatAlternatives(values) {
266
+ if (values.length < 2)
267
+ return values[0] ?? "a supported value";
268
+ return `${values.slice(0, -1).join(", ")}, or ${values.at(-1)}`;
269
+ }
270
+ // src/policies.ts
271
+ var CLI_LIMITS = {
272
+ list: { default: 20, maximum: 1000 },
273
+ search: { default: 20, maximum: 50 },
274
+ telemetryHistory: { default: 100, maximum: 1000 },
275
+ linkPage: { default: 100, maximum: 1000 },
276
+ inspect: {
277
+ depth: { default: 2, maximum: 3 },
278
+ objects: { default: 20, maximum: 100 },
279
+ links: { default: 50, maximum: 500 },
280
+ maximumPages: 10
281
+ }
282
+ };
283
+ var DEFAULT_LIST_ORDER = "desc";
284
+ var DEFAULT_OBJECT_ORDER_BY = "updatedAt";
285
+ var DEFAULT_TELEMETRY_ORDER = "desc";
286
+
287
+ // src/commands/metadata.ts
288
+ var SANDBOX_MAIN_HELP = `Sixb agent CLI
289
+
290
+ Usage:
291
+ sixb <command> [options]
292
+
293
+ The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.
294
+ API commands emit JSON. Help, version, and examples emit text. Downloads write to --output and
295
+ emit a JSON receipt.
296
+
297
+ Discovery:
298
+ sixb doctor Check the sandbox and API gateway
299
+ sixb context Print the current run context
300
+ sixb project show Show project metadata
301
+ sixb ontology list Compact visible type and relationship catalog
302
+ sixb ontology get <type> Inspect one object type, properties, links, and actions
303
+
304
+ Objects:
305
+ sixb objects inspect <type> <id> Inspect an object and its related graph in one command
306
+ sixb objects list [options] Browse materialized objects
307
+ sixb objects get <type> <id>... Exact lookup through opaque object references
308
+ sixb objects search <text> Search visible objects
309
+ sixb objects query --file <file|-> Execute query IR from JSON
310
+ sixb objects count --file <file|-> Count a query without returning rows
311
+ sixb objects exists --file <file|-> Test whether a query has a match
312
+ sixb objects facets --file <file|-> Run a {query, facets} request
313
+ sixb objects links <type> <id> Read exact persisted links
314
+
315
+ Telemetry:
316
+ sixb telemetry latest <type> <id> <property>
317
+ sixb telemetry history <type> <id> <property> [options]
318
+ sixb telemetry query --file <file|->
319
+
320
+ Actions and workflows:
321
+ sixb actions list|get|request ...
322
+ sixb action-runs list|get ...
323
+ sixb workflows list|get|start ...
324
+ sixb workflow-runs list|get ...
325
+
326
+ Files:
327
+ sixb files upload|download ...
328
+
329
+ Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
330
+ For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`;
331
+ 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
332
+ sixb context Print the current run context
333
+ `, "");
334
+ function renderInstanceHelp(mode) {
335
+ return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP;
336
+ }
337
+ var OBJECTS_HELP = `Usage:
338
+ sixb objects inspect <object-type> <primary-id> [options]
339
+ sixb objects list [options]
340
+ sixb objects get <object-type> <primary-id>...
341
+ sixb objects search <text> [--limit <1-${CLI_LIMITS.search.maximum}>]
342
+ sixb objects query --file <path|-> [--include-total|--no-total]
343
+ sixb objects query --example <name>
344
+ sixb objects count --file <path|->
345
+ sixb objects exists --file <path|->
346
+ sixb objects facets --file <path|->
347
+ sixb objects facets --example
348
+ sixb objects links <object-type> <primary-id> [options]
349
+
350
+ List options:
351
+ --type <id> Exact ontology object type id
352
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
353
+ --offset <n> Non-negative; defaults to 0
354
+ --order-by createdAt|updatedAt|primaryId Defaults to ${DEFAULT_OBJECT_ORDER_BY}
355
+ --order asc|desc Defaults to ${DEFAULT_LIST_ORDER}
356
+ --id-prefix <value> Primary-id prefix
357
+ --id-suffix <value> Primary-id suffix
358
+ --created-after|--created-before <RFC3339>
359
+ --updated-after|--updated-before <RFC3339>
360
+
361
+ Links options:
362
+ --link <link-id>
363
+ --direction outgoing|incoming|both Defaults to both
364
+ --page-size <1-${CLI_LIMITS.linkPage.maximum}> Defaults to ${CLI_LIMITS.linkPage.default}
365
+ --page-token <token> Continue an edge page
366
+ --include-objects Include selected and current-page endpoint objects
367
+
368
+ Inspect options:
369
+ --depth <0-${CLI_LIMITS.inspect.depth.maximum}> Defaults to ${CLI_LIMITS.inspect.depth.default}; use 0 for only the object
370
+ --max-objects <1-${CLI_LIMITS.inspect.objects.maximum}> Defaults to ${CLI_LIMITS.inspect.objects.default}
371
+ --max-links <1-${CLI_LIMITS.inspect.links.maximum}> Defaults to ${CLI_LIMITS.inspect.links.default}
372
+ --full Include timestamps and encountered type definitions
373
+
374
+ Use \`objects get\` first when context provides an exact object reference. Use \`objects inspect\`
375
+ only when related objects are needed; it follows both relationship directions to depth 2 by
376
+ default and returns a bounded graph.
377
+ Inspect omits materialization timestamps and ontology definitions by default. Use \`--full\` when
378
+ storage timestamps, declared links, or available actions are needed.
379
+
380
+ Search returns at most ${CLI_LIMITS.search.maximum} matches and defaults to ${CLI_LIMITS.search.default}.
381
+
382
+ \`objects get\` uses a refs query without identity URL paths. Opaque ids containing :, /, #, ?, or
383
+ % are safe. Identifiers are case-sensitive.`;
384
+ var QUERY_HELP = `Usage:
385
+ sixb objects query --file <path|-> [--include-total|--no-total]
386
+ sixb objects query --example <exact|filter|incoming|expand|sort|page>
387
+ sixb objects query --example list
388
+
389
+ Input is a query node. A full {"query": ...} request is also accepted.
390
+
391
+ Query nodes compose through input:
392
+ start {"kind":"start","objectTypeId":"Type","includeSubtypes":false}
393
+ refs {"kind":"refs","refs":[{"objectTypeId":"Type","primaryId":"opaque:id"}]}
394
+ filter {"kind":"filter","input":<query>,"predicate":<predicate>}
395
+ text {"kind":"text","input":<query>,"query":"words","fields":["name"]}
396
+ traverse {"kind":"traverse","input":<query>,"linkId":"link","direction":"outgoing"}
397
+ vector, set, sort, limit, page, project, and expand are also supported.
398
+
399
+ Predicates use op, not kind:
400
+ and/or, not, {"op":"eq","propertyId":"status","value":"open"}, neq, lt, lte, gt, gte,
401
+ in, exists, and contains.
402
+
403
+ Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
404
+ is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
405
+
406
+ Run \`sixb ontology get <type>\` first; never guess property or link ids. Use refs for exact
407
+ identities. Put limits and pages inside the query tree.`;
408
+ var GROUP_HELP = {
409
+ doctor: "Usage: sixb doctor",
410
+ context: "Usage: sixb context",
411
+ project: "Usage: sixb project show",
412
+ ontology: `Usage:
413
+ sixb ontology list [--full]
414
+ sixb ontology get <object-type>`,
415
+ telemetry: `Usage:
416
+ sixb telemetry latest <object-type> <primary-id> <property-id>
417
+ sixb telemetry history <object-type> <primary-id> <property-id> [options]
418
+ sixb telemetry query --file <path|->
419
+
420
+ History options:
421
+ --from <RFC3339>
422
+ --to <RFC3339>
423
+ --limit <1-${CLI_LIMITS.telemetryHistory.maximum}> Defaults to ${CLI_LIMITS.telemetryHistory.default}
424
+ --order <asc|desc> Timestamp order; defaults to ${DEFAULT_TELEMETRY_ORDER}`,
425
+ actions: `Usage:
426
+ sixb actions list [--type <object-type>]
427
+ sixb actions get <action-id>
428
+ sixb actions request <action-id> [--subject-type <type> --subject-id <id>] [--file <path|->] [--run-id <id>] [--wait]
429
+
430
+ \`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
431
+ contains that parameter object; use - to read standard input. --wait returns the terminal Action
432
+ run and waits at most 25 seconds.`,
433
+ "action-runs": `Usage:
434
+ sixb action-runs list [options]
435
+ sixb action-runs get <run-id>
436
+
437
+ List options:
438
+ --action <action-id>
439
+ --type <object-type>
440
+ --id <primary-id>
441
+ --status queued|running|succeeded|failed|cancelled
442
+ --started-after|--started-before <RFC3339>
443
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
444
+ --offset <n> Non-negative; defaults to 0
445
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
446
+ files: `Usage:
447
+ sixb files upload <local-path> [--logical-path <path>]
448
+ sixb files download object <type> <id> --path <json-pointer> --output <local-path>
449
+ sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path>`,
450
+ workflows: `Usage:
451
+ sixb workflows list
452
+ sixb workflows get <workflow-id>
453
+ sixb workflows start <workflow-id> [--file <path|->]
454
+
455
+ The JSON file contains the workflow input object. Use - to read standard input.`,
456
+ "workflow-runs": `Usage:
457
+ sixb workflow-runs list [options]
458
+ sixb workflow-runs get <run-id>
459
+
460
+ List options:
461
+ --workflow <workflow-id>
462
+ --status queued|running|waiting|succeeded|failed|cancelled
463
+ --started-after|--started-before <RFC3339>
464
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
465
+ --offset <n> Non-negative; defaults to 0
466
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`
467
+ };
468
+ var QUERY_EXAMPLES = {
469
+ exact: '{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]}',
470
+ filter: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"eq","propertyId":"status","value":"active"}},"limit":${CLI_LIMITS.list.default}}`,
471
+ incoming: '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
472
+ 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}}]}`,
473
+ sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
474
+ page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`
475
+ };
476
+ var FACETS_EXAMPLE = '{"query":{"kind":"start","objectTypeId":"WorkOrder"},"facets":[{"propertyId":"status","limit":10}]}';
477
+
478
+ // src/commands/shared.ts
479
+ import { readFile as readFile2 } from "fs/promises";
480
+ function parseQueryOptions(args, names, command) {
481
+ const kinds = Object.fromEntries(Object.keys(names).map((flag) => [flag, "string"]));
482
+ const { options } = parseCommandArgs(args, kinds, command);
483
+ const query = {};
484
+ for (const [flag, value] of Object.entries(options)) {
485
+ const name = names[flag];
486
+ if (name)
487
+ query[name] = value;
488
+ }
489
+ return query;
490
+ }
491
+ function normalizeWindowOptions(options, policy) {
492
+ const normalized = {
493
+ ...options,
494
+ limit: String(integerInRange("--limit", options.limit ?? String(policy.defaultLimit), 1, policy.maximumLimit)),
495
+ order: enumValue("--order", options.order ?? policy.defaultOrder, ["asc", "desc"])
496
+ };
497
+ if (policy.offset && options.offset !== undefined) {
498
+ normalized.offset = String(nonNegativeInteger("--offset", options.offset));
499
+ }
500
+ return normalized;
501
+ }
502
+ function singleFileOption(args, command) {
503
+ const { options } = parseCommandArgs(args, { "--file": "string" }, command);
504
+ if (!options["--file"])
505
+ fail(`${command} requires --file <path|->.`);
506
+ return options["--file"];
507
+ }
508
+ async function readJson(source) {
509
+ let text;
510
+ try {
511
+ text = source === "-" ? await readStdin() : await readFile2(source, "utf8");
512
+ } catch (error) {
513
+ if (isFileError(error, "ENOENT"))
514
+ fail(`JSON file '${source}' does not exist.`);
515
+ throw error;
516
+ }
517
+ try {
518
+ return JSON.parse(text);
519
+ } catch {
520
+ fail(source === "-" ? "Standard input is not valid JSON." : `JSON file '${source}' is not valid JSON.`, "invalid_json");
521
+ }
522
+ }
523
+ function asRecord2(value) {
524
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
525
+ }
526
+ function asRecords(value) {
527
+ return Array.isArray(value) ? value.map(asRecord2) : [];
528
+ }
529
+ function isFileError(error, code) {
530
+ return error instanceof Error && "code" in error && error.code === code;
531
+ }
532
+ async function readStdin() {
533
+ const chunks = [];
534
+ for await (const chunk of process.stdin) {
535
+ chunks.push(typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
536
+ }
537
+ return new TextDecoder().decode(Buffer.concat(chunks));
538
+ }
539
+
540
+ // src/commands/actions.ts
541
+ async function actions(api, args) {
542
+ const [sub, ...rest] = args;
543
+ if (!sub || isHelp(sub) || requestsHelp(rest))
544
+ return writeText(GROUP_HELP.actions);
545
+ if (sub === "get") {
546
+ const { positionals } = parseCommandArgs(rest, {}, "actions get", 1);
547
+ return writeJson(await api.get(`/api/actions/${encodeURIComponent(positionals[0] ?? "")}`));
548
+ }
549
+ if (sub === "list") {
550
+ const options = parseQueryOptions(rest, { "--type": "objectTypeId" }, "actions list");
551
+ const response = await api.get("/api/actions");
552
+ if (!options.objectTypeId)
553
+ return writeJson(response);
554
+ if (!Array.isArray(response))
555
+ fail("The actions API returned an invalid response.");
556
+ return writeJson(response.filter((value) => asRecord2(value).objectTypeId === options.objectTypeId));
557
+ }
558
+ if (sub === "request") {
559
+ const { positionals, options } = parseCommandArgs(rest, {
560
+ "--subject-type": "string",
561
+ "--subject-id": "string",
562
+ "--file": "string",
563
+ "--run-id": "string",
564
+ "--wait": "boolean"
565
+ }, "actions request", 1);
566
+ const actionId = positionals[0] ?? "";
567
+ const subjectType = options["--subject-type"];
568
+ const subjectId = options["--subject-id"];
569
+ const paramsSource = options["--file"];
570
+ const runId = options["--run-id"];
571
+ const wait = options["--wait"];
572
+ if (Boolean(subjectType) !== Boolean(subjectId)) {
573
+ fail("--subject-type and --subject-id must be provided together.");
574
+ }
575
+ const params = paramsSource ? await readJson(paramsSource) : {};
576
+ if (Array.isArray(params) || typeof params !== "object" || params === null) {
577
+ fail("Action params must be a JSON object.");
578
+ }
579
+ const requested = await api.post(`/api/actions/${encodeURIComponent(actionId)}`, {
580
+ params,
581
+ ...subjectType && subjectId ? { subject: { kind: "object", objectTypeId: subjectType, primaryId: subjectId } } : {},
582
+ ...runId ? { runId } : {}
583
+ });
584
+ if (!wait)
585
+ return writeJson(requested);
586
+ const requestedRunId = asRecord2(requested).runId;
587
+ if (typeof requestedRunId !== "string" || requestedRunId.length === 0) {
588
+ throw new CliError({
589
+ code: "invalid_api_response",
590
+ message: "The Action request response did not contain a run id."
591
+ }, EXIT_API);
592
+ }
593
+ return writeJson(await waitForActionRun(api, requestedRunId));
594
+ }
595
+ fail(`Unknown actions command '${sub}'.`);
596
+ }
597
+ var ACTION_WAIT_TIMEOUT_MS = 25000;
598
+ var ACTION_WAIT_POLL_MS = 250;
599
+ var TERMINAL_ACTION_STATUSES = new Set(["succeeded", "failed", "cancelled"]);
600
+ async function waitForActionRun(api, runId) {
601
+ const startedAt = Date.now();
602
+ while (Date.now() - startedAt < ACTION_WAIT_TIMEOUT_MS) {
603
+ const run = await api.get(`/api/action-runs/${encodeURIComponent(runId)}`);
604
+ const status = asRecord2(run).status;
605
+ if (typeof status === "string" && TERMINAL_ACTION_STATUSES.has(status))
606
+ return run;
607
+ await new Promise((resolve) => setTimeout(resolve, ACTION_WAIT_POLL_MS));
608
+ }
609
+ throw new CliError({
610
+ code: "action_wait_timeout",
611
+ message: `Action run '${runId}' did not finish within ${ACTION_WAIT_TIMEOUT_MS / 1000} seconds.`,
612
+ hint: `Inspect it with 'sixb action-runs get ${runId}'.`
613
+ }, EXIT_API);
614
+ }
615
+
616
+ // src/commands/files.ts
617
+ import { access } from "fs/promises";
618
+ async function files(api, args) {
619
+ const [sub, ...rest] = args;
620
+ if (!sub || isHelp(sub) || requestsHelp(rest))
621
+ return writeText(GROUP_HELP.files);
622
+ if (sub === "upload") {
623
+ const { positionals, options } = parseCommandArgs(rest, { "--logical-path": "string" }, "files upload", 1);
624
+ const source = positionals[0] ?? "";
625
+ try {
626
+ await access(source);
627
+ } catch {
628
+ fail(`Upload file '${source}' does not exist.`);
629
+ }
630
+ return writeJson(await api.upload("/api/files", source, options["--logical-path"]));
631
+ }
632
+ if (sub === "download") {
633
+ const { positionals, options } = parseCommandArgs(rest, { "--path": "string", "--output": "string" }, "files download", [2, 3]);
634
+ const context = positionals[0];
635
+ let route;
636
+ if (context === "object") {
637
+ requireExact(positionals, 3, "files download object requires object type and primary id.");
638
+ const type = positionals[1] ?? "";
639
+ const id = positionals[2] ?? "";
640
+ route = `/api/objects/${encodeURIComponent(type)}/${encodeURIComponent(id)}/files/content`;
641
+ } else if (context === "action-run" || context === "workflow-run") {
642
+ requireExact(positionals, 2, `files download ${context} requires exactly one run id.`);
643
+ const id = positionals[1] ?? "";
644
+ route = `/api/${context}s/${encodeURIComponent(id)}/files/content`;
645
+ } else
646
+ fail(`Unknown file download context '${context}'.`);
647
+ const path = options["--path"];
648
+ const output = options["--output"];
649
+ if (!path)
650
+ fail("files download requires --path <json-pointer>.");
651
+ if (!output)
652
+ fail("files download requires --output <local-path>.");
653
+ await api.download(route, output, { path });
654
+ return writeJson({ downloaded: true, output });
655
+ }
656
+ fail(`Unknown files command '${sub}'.`);
657
+ }
658
+
659
+ // src/graph.ts
660
+ async function inspectGraph(api, objectTypeId, primaryId, options) {
661
+ if (options.depth === 0)
662
+ return inspectRoot(api, objectTypeId, primaryId, options);
663
+ let refs = [{ objectTypeId, primaryId, distance: 0 }];
664
+ let frontier = refs;
665
+ const objects = new Map;
666
+ const links = new Map;
667
+ let objectsTruncated = false;
668
+ let linksTruncated = false;
669
+ let pagesTruncated = false;
670
+ let pagesRead = 0;
671
+ let linksExamined = 0;
672
+ for (let level = 0;level < options.depth && frontier.length > 0; level += 1) {
673
+ const levelObjects = new Map;
674
+ const levelLinks = new Map;
675
+ const seenPageTokens = new Set;
676
+ let pageToken;
677
+ let continuePaging = true;
678
+ while (continuePaging) {
679
+ const remainingLinks = options.maxLinks - linksExamined;
680
+ if (remainingLinks <= 0) {
681
+ linksTruncated = true;
682
+ break;
683
+ }
684
+ if (pagesRead >= CLI_LIMITS.inspect.maximumPages) {
685
+ pagesTruncated = true;
686
+ break;
687
+ }
688
+ const response = asLinksResponse(await api.post("/api/objects/query/links", {
689
+ query: {
690
+ kind: "refs",
691
+ refs: frontier.map(({ objectTypeId: type, primaryId: id }) => ({
692
+ objectTypeId: type,
693
+ primaryId: id
694
+ }))
695
+ },
696
+ direction: "both",
697
+ includeObjects: true,
698
+ pageSize: Math.min(CLI_LIMITS.linkPage.default, remainingLinks),
699
+ ...pageToken ? { pageToken } : {}
700
+ }));
701
+ pagesRead += 1;
702
+ const pageLinks = response.links.slice(0, remainingLinks);
703
+ linksExamined += pageLinks.length;
704
+ linksTruncated ||= response.links.length > pageLinks.length;
705
+ const relevantObjects = new Set(frontier.map(refKey));
706
+ for (const link of pageLinks) {
707
+ relevantObjects.add(refKey(link.source));
708
+ relevantObjects.add(refKey(link.target));
709
+ }
710
+ for (const object of response.objects) {
711
+ if (relevantObjects.has(refKey(object)))
712
+ levelObjects.set(refKey(object), object);
713
+ }
714
+ for (const link of pageLinks) {
715
+ const physical = {
716
+ sourceTypeId: link.source.objectTypeId,
717
+ sourceId: link.source.primaryId,
718
+ linkId: link.linkId,
719
+ targetTypeId: link.target.objectTypeId,
720
+ targetId: link.target.primaryId,
721
+ ...Object.hasOwn(link, "properties") ? { properties: link.properties } : {}
722
+ };
723
+ levelLinks.set(linkKey(physical), physical);
724
+ }
725
+ if (!response.hasMore) {
726
+ continuePaging = false;
727
+ continue;
728
+ }
729
+ if (linksExamined >= options.maxLinks) {
730
+ linksTruncated = true;
731
+ break;
732
+ }
733
+ if (pagesRead >= CLI_LIMITS.inspect.maximumPages) {
734
+ pagesTruncated = true;
735
+ break;
736
+ }
737
+ if (!response.nextPageToken) {
738
+ invalidApiResponse("The object-links API reported another page without a nextPageToken.");
739
+ }
740
+ if (seenPageTokens.has(response.nextPageToken)) {
741
+ invalidApiResponse("The object-links API repeated a nextPageToken while inspecting the graph.");
742
+ }
743
+ seenPageTokens.add(response.nextPageToken);
744
+ pageToken = response.nextPageToken;
745
+ }
746
+ for (const [key, object] of levelObjects)
747
+ objects.set(key, object);
748
+ for (const [key, link] of levelLinks)
749
+ links.set(key, link);
750
+ const frontierKeys = new Set(frontier.map(refKey));
751
+ const candidates = [];
752
+ for (const link of levelLinks.values()) {
753
+ const source = { objectTypeId: link.sourceTypeId, primaryId: link.sourceId };
754
+ const target = { objectTypeId: link.targetTypeId, primaryId: link.targetId };
755
+ if (frontierKeys.has(refKey(source)))
756
+ candidates.push({ ...target, distance: level + 1 });
757
+ else if (frontierKeys.has(refKey(target)))
758
+ candidates.push({ ...source, distance: level + 1 });
759
+ }
760
+ const seen = new Set(refs.map(refKey));
761
+ const all = dedupeRefs([...refs, ...candidates]);
762
+ objectsTruncated ||= all.length > options.maxObjects;
763
+ refs = all.slice(0, options.maxObjects);
764
+ frontier = linksTruncated || pagesTruncated ? [] : refs.filter((ref) => !seen.has(refKey(ref)));
765
+ }
766
+ const kept = new Set(refs.map(refKey));
767
+ const filteredLinks = [...links.values()].filter((link) => kept.has(refKey({ objectTypeId: link.sourceTypeId, primaryId: link.sourceId })) && kept.has(refKey({ objectTypeId: link.targetTypeId, primaryId: link.targetId }))).sort(compareLinks).map((link) => link.properties == null ? {
768
+ sourceTypeId: link.sourceTypeId,
769
+ sourceId: link.sourceId,
770
+ linkId: link.linkId,
771
+ targetTypeId: link.targetTypeId,
772
+ targetId: link.targetId
773
+ } : link);
774
+ const root = objects.get(refKey({ objectTypeId, primaryId }));
775
+ if (!root)
776
+ objectNotFound(objectTypeId, primaryId);
777
+ const relatedObjects = refs.filter((ref) => ref.distance > 0).flatMap((ref) => {
778
+ const object = objects.get(refKey(ref));
779
+ return object ? [{ ...object, distance: ref.distance }] : [];
780
+ });
781
+ const objectTypes = options.full ? await Promise.all([...new Set(refs.map((ref) => ref.objectTypeId))].sort((left, right) => left.localeCompare(right)).map((typeId) => api.get(`/api/object-types/${encodeURIComponent(typeId)}`))) : undefined;
782
+ return {
783
+ object: options.full ? root : compactObject(root),
784
+ relatedObjects: options.full ? relatedObjects : relatedObjects.map(compactObject),
785
+ links: filteredLinks,
786
+ graph: {
787
+ depth: options.depth,
788
+ maxObjects: options.maxObjects,
789
+ maxLinks: options.maxLinks,
790
+ maxPages: CLI_LIMITS.inspect.maximumPages,
791
+ objectCount: 1 + relatedObjects.length,
792
+ linkCount: filteredLinks.length,
793
+ pagesRead,
794
+ linksExamined,
795
+ truncated: objectsTruncated || linksTruncated || pagesTruncated,
796
+ truncation: {
797
+ objects: objectsTruncated,
798
+ links: linksTruncated,
799
+ pages: pagesTruncated
800
+ }
801
+ },
802
+ ...objectTypes ? { objectTypes } : {}
803
+ };
804
+ }
805
+ async function inspectRoot(api, objectTypeId, primaryId, options) {
806
+ const response = await api.post("/api/objects/query", {
807
+ query: { kind: "refs", refs: [{ objectTypeId, primaryId }] },
808
+ includeTotal: false
809
+ });
810
+ if (typeof response !== "object" || response === null || Array.isArray(response)) {
811
+ invalidApiResponse("The object query API returned an invalid response.");
812
+ }
813
+ const rows = response.objects;
814
+ if (!Array.isArray(rows))
815
+ invalidApiResponse("The object query API returned an invalid response.");
816
+ const root = rows.find((value) => isMaterializedObject(value) && value.objectTypeId === objectTypeId && value.primaryId === primaryId);
817
+ if (!root)
818
+ objectNotFound(objectTypeId, primaryId);
819
+ const objectTypes = options.full ? [await api.get(`/api/object-types/${encodeURIComponent(objectTypeId)}`)] : undefined;
820
+ return {
821
+ object: options.full ? root : compactObject(root),
822
+ relatedObjects: [],
823
+ links: [],
824
+ graph: {
825
+ depth: 0,
826
+ maxObjects: options.maxObjects,
827
+ maxLinks: options.maxLinks,
828
+ maxPages: CLI_LIMITS.inspect.maximumPages,
829
+ objectCount: 1,
830
+ linkCount: 0,
831
+ pagesRead: 0,
832
+ linksExamined: 0,
833
+ truncated: false,
834
+ truncation: { objects: false, links: false, pages: false }
835
+ },
836
+ ...objectTypes ? { objectTypes } : {}
837
+ };
838
+ }
839
+ function asLinksResponse(value) {
840
+ if (typeof value !== "object" || value === null || Array.isArray(value))
841
+ invalidLinksResponse();
842
+ const record = value;
843
+ if (!Array.isArray(record.objects) || !Array.isArray(record.links))
844
+ invalidLinksResponse();
845
+ if (typeof record.hasMore !== "boolean")
846
+ invalidLinksResponse();
847
+ if (record.nextPageToken !== undefined && typeof record.nextPageToken !== "string") {
848
+ invalidLinksResponse();
849
+ }
850
+ if (!record.objects.every(isMaterializedObject) || !record.links.every(isPhysicalLinkResponse)) {
851
+ invalidLinksResponse();
852
+ }
853
+ return record;
854
+ }
855
+ function invalidLinksResponse() {
856
+ invalidApiResponse("The object-links API returned an invalid response.");
857
+ }
858
+ function invalidApiResponse(message) {
859
+ throw new CliError({ code: "invalid_api_response", message }, EXIT_API);
860
+ }
861
+ function objectNotFound(objectTypeId, primaryId) {
862
+ throw new CliError({ code: "not_found", message: `Object '${objectTypeId}/${primaryId}' was not found.` }, EXIT_API);
863
+ }
864
+ function isMaterializedObject(value) {
865
+ if (typeof value !== "object" || value === null || Array.isArray(value))
866
+ return false;
867
+ const record = value;
868
+ return typeof record.objectTypeId === "string" && typeof record.primaryId === "string";
869
+ }
870
+ function isPhysicalLinkResponse(value) {
871
+ if (typeof value !== "object" || value === null || Array.isArray(value))
872
+ return false;
873
+ const record = value;
874
+ return typeof record.linkId === "string" && isObjectRef(record.source) && isObjectRef(record.target);
875
+ }
876
+ function isObjectRef(value) {
877
+ if (typeof value !== "object" || value === null || Array.isArray(value))
878
+ return false;
879
+ const record = value;
880
+ return typeof record.objectTypeId === "string" && typeof record.primaryId === "string";
881
+ }
882
+ function compactObject(object) {
883
+ const { createdAt: _createdAt, updatedAt: _updatedAt, ...compact } = object;
884
+ return compact;
885
+ }
886
+ function dedupeRefs(values) {
887
+ const unique = new Map;
888
+ for (const value of values) {
889
+ const key = refKey(value);
890
+ const current = unique.get(key);
891
+ if (!current || value.distance < current.distance)
892
+ unique.set(key, value);
893
+ }
894
+ return [...unique.values()].sort((left, right) => left.distance - right.distance || left.objectTypeId.localeCompare(right.objectTypeId) || left.primaryId.localeCompare(right.primaryId));
895
+ }
896
+ function refKey(ref) {
897
+ return JSON.stringify([ref.objectTypeId, ref.primaryId]);
898
+ }
899
+ function linkKey(link) {
900
+ return JSON.stringify([
901
+ link.sourceTypeId,
902
+ link.sourceId,
903
+ link.linkId,
904
+ link.targetTypeId,
905
+ link.targetId
906
+ ]);
907
+ }
908
+ function compareLinks(left, right) {
909
+ return linkKey(left).localeCompare(linkKey(right));
910
+ }
911
+
912
+ // src/commands/objects.ts
913
+ async function objects(api, args) {
914
+ const [sub, ...rest] = args;
915
+ if (!sub || isHelp(sub))
916
+ return writeText(OBJECTS_HELP);
917
+ switch (sub) {
918
+ case "inspect":
919
+ return objectsInspect(api, rest);
920
+ case "list":
921
+ return objectsList(api, rest);
922
+ case "get":
923
+ return objectsGet(api, rest);
924
+ case "search":
925
+ return objectsSearch(api, rest);
926
+ case "query":
927
+ return objectsQuery(api, rest);
928
+ case "count":
929
+ case "exists":
930
+ return objectsScalar(api, sub, rest);
931
+ case "facets":
932
+ return objectsFacets(api, rest);
933
+ case "links":
934
+ return objectsLinks(api, rest);
935
+ default:
936
+ fail(`Unknown objects command '${sub}'.`);
937
+ }
938
+ }
939
+ async function objectsInspect(api, args) {
940
+ if (requestsHelp(args))
941
+ return writeText(OBJECTS_HELP);
942
+ const { positionals, options } = parseCommandArgs(args, {
943
+ "--full": "boolean",
944
+ "--depth": "string",
945
+ "--max-objects": "string",
946
+ "--max-links": "string"
947
+ }, "objects inspect", 2);
948
+ const objectTypeId = positionals[0] ?? "";
949
+ const primaryId = positionals[1] ?? "";
950
+ const depth = integerInRange("--depth", options["--depth"] ?? String(CLI_LIMITS.inspect.depth.default), 0, CLI_LIMITS.inspect.depth.maximum);
951
+ const maxObjects = integerInRange("--max-objects", options["--max-objects"] ?? String(CLI_LIMITS.inspect.objects.default), 1, CLI_LIMITS.inspect.objects.maximum);
952
+ const maxLinks = integerInRange("--max-links", options["--max-links"] ?? String(CLI_LIMITS.inspect.links.default), 1, CLI_LIMITS.inspect.links.maximum);
953
+ const full = options["--full"] ?? false;
954
+ writeJson(await inspectGraph(api, objectTypeId, primaryId, {
955
+ depth,
956
+ maxObjects,
957
+ maxLinks,
958
+ full
959
+ }));
960
+ }
961
+ async function objectsList(api, args) {
962
+ if (requestsHelp(args))
963
+ return writeText(OBJECTS_HELP);
964
+ const optionNames = {
965
+ "--type": "objectTypeId",
966
+ "--limit": "limit",
967
+ "--offset": "offset",
968
+ "--order-by": "orderBy",
969
+ "--order": "order",
970
+ "--id-prefix": "idPrefix",
971
+ "--id-suffix": "idSuffix",
972
+ "--created-after": "createdAfter",
973
+ "--created-before": "createdBefore",
974
+ "--updated-after": "updatedAfter",
975
+ "--updated-before": "updatedBefore"
976
+ };
977
+ const options = normalizeWindowOptions(parseQueryOptions(args, optionNames, "objects list"), {
978
+ defaultLimit: CLI_LIMITS.list.default,
979
+ maximumLimit: CLI_LIMITS.list.maximum,
980
+ defaultOrder: DEFAULT_LIST_ORDER,
981
+ offset: true
982
+ });
983
+ options.orderBy = enumValue("--order-by", options.orderBy ?? DEFAULT_OBJECT_ORDER_BY, [
984
+ "createdAt",
985
+ "updatedAt",
986
+ "primaryId"
987
+ ]);
988
+ for (const [name, flag] of [
989
+ ["createdAfter", "--created-after"],
990
+ ["createdBefore", "--created-before"],
991
+ ["updatedAfter", "--updated-after"],
992
+ ["updatedBefore", "--updated-before"]
993
+ ]) {
994
+ if (options[name] !== undefined)
995
+ options[name] = rfc3339Value(flag, options[name]);
996
+ }
997
+ requireOrderedRange("--created-after", options.createdAfter, "--created-before", options.createdBefore);
998
+ requireOrderedRange("--updated-after", options.updatedAfter, "--updated-before", options.updatedBefore);
999
+ writeJson(await api.get("/api/objects", options));
1000
+ }
1001
+ async function objectsGet(api, args) {
1002
+ if (requestsHelp(args))
1003
+ return writeText("Usage: sixb objects get <object-type> <primary-id>...");
1004
+ const { positionals } = parseCommandArgs(args, {}, "objects get", [2, Number.POSITIVE_INFINITY]);
1005
+ const objectTypeId = positionals[0] ?? "";
1006
+ writeJson(await api.post("/api/objects/query", {
1007
+ query: {
1008
+ kind: "refs",
1009
+ refs: positionals.slice(1).map((primaryId) => ({ objectTypeId, primaryId }))
1010
+ },
1011
+ includeTotal: false
1012
+ }));
1013
+ }
1014
+ async function objectsSearch(api, args) {
1015
+ if (requestsHelp(args)) {
1016
+ return writeText(`Usage: sixb objects search <text> [--limit <1-${CLI_LIMITS.search.maximum}>]`);
1017
+ }
1018
+ const { positionals, options } = parseCommandArgs(args, { "--limit": "string" }, "objects search", 1);
1019
+ const limit = String(integerInRange("--limit", options["--limit"] ?? String(CLI_LIMITS.search.default), 1, CLI_LIMITS.search.maximum));
1020
+ writeJson(await api.get("/api/objects/search", { q: positionals[0], limit }));
1021
+ }
1022
+ async function objectsQuery(api, args) {
1023
+ if (requestsHelp(args))
1024
+ return writeText(QUERY_HELP);
1025
+ const { options } = parseCommandArgs(args, {
1026
+ "--example": "string",
1027
+ "--file": "string",
1028
+ "--include-total": "boolean",
1029
+ "--no-total": "boolean"
1030
+ }, "objects query");
1031
+ if (options["--include-total"] && options["--no-total"])
1032
+ fail("--include-total and --no-total cannot be used together.");
1033
+ if (options["--example"]) {
1034
+ if (Object.keys(options).length !== 1)
1035
+ fail("--example cannot be combined with query options.");
1036
+ const name = options["--example"];
1037
+ if (name === "list")
1038
+ return writeText(Object.keys(QUERY_EXAMPLES).join(" "));
1039
+ const example = QUERY_EXAMPLES[name];
1040
+ if (!example)
1041
+ fail(`Unknown query example '${name}'. Run 'sixb objects query --example list'.`);
1042
+ return writeText(example);
1043
+ }
1044
+ const source = options["--file"];
1045
+ const includeTotal = options["--include-total"] ?? false;
1046
+ if (!source)
1047
+ fail("objects query requires --file <path|->.");
1048
+ const input = await readJson(source);
1049
+ const record = asRecord2(input);
1050
+ const body = Object.hasOwn(record, "query") ? { ...record, ...Object.hasOwn(record, "includeTotal") ? {} : { includeTotal } } : { query: input, includeTotal };
1051
+ writeJson(await api.post("/api/objects/query", body));
1052
+ }
1053
+ async function objectsScalar(api, operation, args) {
1054
+ if (requestsHelp(args))
1055
+ return writeText(`Usage: sixb objects ${operation} --file <path|->`);
1056
+ const source = singleFileOption(args, `objects ${operation}`);
1057
+ const input = await readJson(source);
1058
+ const record = asRecord2(input);
1059
+ writeJson(await api.post(`/api/objects/query/${operation}`, {
1060
+ query: Object.hasOwn(record, "query") ? record.query : input
1061
+ }));
1062
+ }
1063
+ async function objectsFacets(api, args) {
1064
+ if (requestsHelp(args)) {
1065
+ return writeText(`Usage: sixb objects facets --file <path|->
1066
+ sixb objects facets --example`);
1067
+ }
1068
+ const { options } = parseCommandArgs(args, { "--file": "string", "--example": "boolean" }, "objects facets");
1069
+ if (options["--example"]) {
1070
+ if (options["--file"])
1071
+ fail("--example cannot be combined with --file.");
1072
+ return writeText(FACETS_EXAMPLE);
1073
+ }
1074
+ if (!options["--file"])
1075
+ fail("objects facets requires --file <path|->.");
1076
+ const body = await readJson(options["--file"]);
1077
+ const record = asRecord2(body);
1078
+ if (!Object.hasOwn(record, "query") || !Object.hasOwn(record, "facets")) {
1079
+ fail("objects facets input must contain query and facets.");
1080
+ }
1081
+ writeJson(await api.post("/api/objects/query/facets", body));
1082
+ }
1083
+ async function objectsLinks(api, args) {
1084
+ if (requestsHelp(args))
1085
+ return writeText(OBJECTS_HELP);
1086
+ const { positionals, options } = parseCommandArgs(args, {
1087
+ "--link": "string",
1088
+ "--direction": "string",
1089
+ "--page-size": "string",
1090
+ "--page-token": "string",
1091
+ "--include-objects": "boolean"
1092
+ }, "objects links", 2);
1093
+ const objectTypeId = positionals[0] ?? "";
1094
+ const primaryId = positionals[1] ?? "";
1095
+ const linkId = options["--link"];
1096
+ const direction = enumValue("--direction", options["--direction"] ?? "both", [
1097
+ "outgoing",
1098
+ "incoming",
1099
+ "both"
1100
+ ]);
1101
+ const pageSize = integerInRange("--page-size", options["--page-size"] ?? String(CLI_LIMITS.linkPage.default), 1, CLI_LIMITS.linkPage.maximum);
1102
+ const pageToken = options["--page-token"];
1103
+ const includeObjects = options["--include-objects"] ?? false;
1104
+ writeJson(await api.post("/api/objects/query/links", {
1105
+ query: { kind: "refs", refs: [{ objectTypeId, primaryId }] },
1106
+ direction,
1107
+ includeObjects,
1108
+ pageSize,
1109
+ ...linkId ? { linkId } : {},
1110
+ ...pageToken ? { pageToken } : {}
1111
+ }));
1112
+ }
1113
+
1114
+ // src/commands/ontology.ts
1115
+ async function ontology(api, args) {
1116
+ const [sub, ...rest] = args;
1117
+ if (!sub || isHelp(sub))
1118
+ return writeText(GROUP_HELP.ontology);
1119
+ if (sub === "list") {
1120
+ if (requestsHelp(rest))
1121
+ return writeText("Usage: sixb ontology list [--full]");
1122
+ const { options } = parseCommandArgs(rest, { "--full": "boolean" }, "ontology list");
1123
+ const full = options["--full"] ?? false;
1124
+ const value = await api.get("/api/object-types");
1125
+ if (full)
1126
+ return writeJson(value);
1127
+ if (!Array.isArray(value))
1128
+ fail("The ontology API returned an invalid response.");
1129
+ return writeJson(value.map((entry) => {
1130
+ const type = asRecord2(entry);
1131
+ const properties = asRecords(type.properties);
1132
+ return {
1133
+ id: type.id,
1134
+ name: type.name,
1135
+ description: type.description,
1136
+ primaryPropertyId: properties.find((property) => property.primary === true)?.id,
1137
+ links: asRecords(type.links).map(({ id, name, description, targetObjectTypeId, cardinality }) => ({
1138
+ id,
1139
+ name,
1140
+ ...description === undefined ? {} : { description },
1141
+ targetObjectTypeId,
1142
+ cardinality
1143
+ })),
1144
+ actions: asRecords(type.actions).map(({ id, name, description }) => ({
1145
+ id,
1146
+ name,
1147
+ ...description === undefined ? {} : { description }
1148
+ }))
1149
+ };
1150
+ }));
1151
+ }
1152
+ if (sub === "get") {
1153
+ if (requestsHelp(rest))
1154
+ return writeText("Usage: sixb ontology get <object-type>");
1155
+ const { positionals } = parseCommandArgs(rest, {}, "ontology get", 1);
1156
+ return writeJson(await api.get(`/api/object-types/${encodeURIComponent(positionals[0] ?? "")}`));
1157
+ }
1158
+ fail(`Unknown ontology command '${sub}'.`);
1159
+ }
1160
+
1161
+ // src/commands/project.ts
1162
+ async function project(api, args) {
1163
+ if (!args[0] || isHelp(args[0]) || args[0] === "show" && requestsHelp(args.slice(1))) {
1164
+ return writeText(GROUP_HELP.project);
1165
+ }
1166
+ if (args[0] !== "show")
1167
+ fail(`Unknown project command '${args[0]}'.`);
1168
+ parseCommandArgs(args.slice(1), {}, "project show");
1169
+ writeJson(await api.get("/api/project"));
1170
+ }
1171
+
1172
+ // src/commands/runs.ts
1173
+ var ACTION_RUN_STATUSES = ["queued", "running", "succeeded", "failed", "cancelled"];
1174
+ var WORKFLOW_RUN_STATUSES = [
1175
+ "queued",
1176
+ "running",
1177
+ "waiting",
1178
+ "succeeded",
1179
+ "failed",
1180
+ "cancelled"
1181
+ ];
1182
+ async function runs(api, kind, args) {
1183
+ const [sub, ...rest] = args;
1184
+ const group = `${kind}-runs`;
1185
+ if (!sub || isHelp(sub) || requestsHelp(rest))
1186
+ return writeText(GROUP_HELP[group]);
1187
+ if (sub === "get") {
1188
+ const { positionals } = parseCommandArgs(rest, {}, `${group} get`, 1);
1189
+ return writeJson(await api.get(`/api/${group}/${encodeURIComponent(positionals[0] ?? "")}`));
1190
+ }
1191
+ if (sub === "list") {
1192
+ const common = {
1193
+ "--status": "status",
1194
+ "--started-after": "startedAfter",
1195
+ "--started-before": "startedBefore",
1196
+ "--limit": "limit",
1197
+ "--offset": "offset",
1198
+ "--order": "order"
1199
+ };
1200
+ const action = {
1201
+ ...common,
1202
+ "--action": "actionId",
1203
+ "--type": "objectTypeId",
1204
+ "--id": "primaryId"
1205
+ };
1206
+ const workflow = { ...common, "--workflow": "workflowId" };
1207
+ const options = normalizeWindowOptions(parseQueryOptions(rest, kind === "action" ? action : workflow, `${group} list`), {
1208
+ defaultLimit: CLI_LIMITS.list.default,
1209
+ maximumLimit: CLI_LIMITS.list.maximum,
1210
+ defaultOrder: DEFAULT_LIST_ORDER,
1211
+ offset: true
1212
+ });
1213
+ if (options.status !== undefined) {
1214
+ options.status = enumValue("--status", options.status, kind === "action" ? ACTION_RUN_STATUSES : WORKFLOW_RUN_STATUSES);
1215
+ }
1216
+ for (const [name, flag] of [
1217
+ ["startedAfter", "--started-after"],
1218
+ ["startedBefore", "--started-before"]
1219
+ ]) {
1220
+ if (options[name] !== undefined)
1221
+ options[name] = rfc3339Value(flag, options[name]);
1222
+ }
1223
+ requireOrderedRange("--started-after", options.startedAfter, "--started-before", options.startedBefore);
1224
+ return writeJson(await api.get(`/api/${group}`, options));
1225
+ }
1226
+ fail(`Unknown ${group} command '${sub}'.`);
1227
+ }
1228
+
1229
+ // src/commands/telemetry.ts
1230
+ async function telemetry(api, args) {
1231
+ const [sub, ...rest] = args;
1232
+ if (!sub || isHelp(sub) || requestsHelp(rest))
1233
+ return writeText(GROUP_HELP.telemetry);
1234
+ if (sub === "latest") {
1235
+ const { positionals } = parseCommandArgs(rest, {}, "telemetry latest", 3);
1236
+ return writeJson(await api.get(telemetryPath(positionals, "latest")));
1237
+ }
1238
+ if (sub === "history") {
1239
+ const { positionals, options } = parseCommandArgs(rest, {
1240
+ "--from": "string",
1241
+ "--to": "string",
1242
+ "--limit": "string",
1243
+ "--order": "string"
1244
+ }, "telemetry history", 3);
1245
+ const query = normalizeWindowOptions({
1246
+ from: options["--from"],
1247
+ to: options["--to"],
1248
+ limit: options["--limit"],
1249
+ order: options["--order"]
1250
+ }, {
1251
+ defaultLimit: CLI_LIMITS.telemetryHistory.default,
1252
+ maximumLimit: CLI_LIMITS.telemetryHistory.maximum,
1253
+ defaultOrder: DEFAULT_TELEMETRY_ORDER
1254
+ });
1255
+ if (query.from !== undefined)
1256
+ query.from = rfc3339Value("--from", query.from);
1257
+ if (query.to !== undefined)
1258
+ query.to = rfc3339Value("--to", query.to);
1259
+ requireOrderedRange("--from", query.from, "--to", query.to);
1260
+ return writeJson(await api.get(telemetryPath(positionals, "history"), query));
1261
+ }
1262
+ if (sub === "query") {
1263
+ return writeJson(await api.post("/api/telemetry/history", normalizeTelemetryQueryInput(await readJson(singleFileOption(rest, "telemetry query")))));
1264
+ }
1265
+ fail(`Unknown telemetry command '${sub}'.`);
1266
+ }
1267
+ function telemetryPath(args, terminal) {
1268
+ return `/api/objects/${encodeURIComponent(args[0] ?? "")}/${encodeURIComponent(args[1] ?? "")}/telemetry/${encodeURIComponent(args[2] ?? "")}/${terminal}`;
1269
+ }
1270
+ function normalizeTelemetryQueryInput(input) {
1271
+ if (Array.isArray(input) || typeof input !== "object" || input === null) {
1272
+ fail("Telemetry query input must be a JSON object.");
1273
+ }
1274
+ const body = { ...input };
1275
+ const rawLimit = body.limitPerSeries;
1276
+ if (rawLimit !== undefined && typeof rawLimit !== "number") {
1277
+ fail("limitPerSeries must be a number.");
1278
+ }
1279
+ body.limitPerSeries = integerInRange("limitPerSeries", String(rawLimit ?? CLI_LIMITS.telemetryHistory.default), 1, CLI_LIMITS.telemetryHistory.maximum);
1280
+ const rawOrder = body.order;
1281
+ if (rawOrder !== undefined && typeof rawOrder !== "string") {
1282
+ fail("order must be a string.");
1283
+ }
1284
+ body.order = enumValue("order", rawOrder ?? DEFAULT_TELEMETRY_ORDER, ["asc", "desc"]);
1285
+ for (const field of ["from", "to"]) {
1286
+ const value = body[field];
1287
+ if (value === undefined)
1288
+ continue;
1289
+ if (typeof value !== "string")
1290
+ fail(`${field} must be an RFC 3339 timestamp.`);
1291
+ body[field] = rfc3339Value(field, value);
1292
+ }
1293
+ requireOrderedRange("from", body.from, "to", body.to);
1294
+ return body;
1295
+ }
1296
+
1297
+ // src/commands/workflows.ts
1298
+ async function workflows(api, args) {
1299
+ const [sub, ...rest] = args;
1300
+ if (!sub || isHelp(sub) || requestsHelp(rest))
1301
+ return writeText(GROUP_HELP.workflows);
1302
+ if (sub === "list") {
1303
+ parseCommandArgs(rest, {}, "workflows list");
1304
+ return writeJson(await api.get("/api/workflows"));
1305
+ }
1306
+ if (sub === "get") {
1307
+ const {
1308
+ positionals: [workflowId]
1309
+ } = parseCommandArgs(rest, {}, "workflows get", 1);
1310
+ return writeJson(await api.get(`/api/workflows/${encodeURIComponent(workflowId ?? "")}`));
1311
+ }
1312
+ if (sub === "start") {
1313
+ const {
1314
+ positionals: [workflowId],
1315
+ options
1316
+ } = parseCommandArgs(rest, { "--file": "string" }, "workflows start", 1);
1317
+ const input = options["--file"] ? await readJson(options["--file"]) : {};
1318
+ if (Array.isArray(input) || typeof input !== "object" || input === null) {
1319
+ fail("Workflow input must be a JSON object.");
1320
+ }
1321
+ return writeJson(await api.post(`/api/workflows/${encodeURIComponent(workflowId ?? "")}/runs`, { input }));
1322
+ }
1323
+ fail(`Unknown workflows command '${sub}'.`);
1324
+ }
1325
+
1326
+ // src/commands/index.ts
1327
+ var INSTANCE_COMMANDS = [
1328
+ "project",
1329
+ "ontology",
1330
+ "objects",
1331
+ "telemetry",
1332
+ "actions",
1333
+ "action-runs",
1334
+ "files",
1335
+ "workflows",
1336
+ "workflow-runs"
1337
+ ];
1338
+ function isInstanceCommand(command) {
1339
+ return INSTANCE_COMMANDS.includes(command);
1340
+ }
1341
+ async function dispatch(api, command, args) {
1342
+ switch (command) {
1343
+ case "project":
1344
+ return project(api, args);
1345
+ case "ontology":
1346
+ return ontology(api, args);
1347
+ case "objects":
1348
+ return objects(api, args);
1349
+ case "telemetry":
1350
+ return telemetry(api, args);
1351
+ case "actions":
1352
+ return actions(api, args);
1353
+ case "action-runs":
1354
+ return runs(api, "action", args);
1355
+ case "files":
1356
+ return files(api, args);
1357
+ case "workflows":
1358
+ return workflows(api, args);
1359
+ case "workflow-runs":
1360
+ return runs(api, "workflow", args);
1361
+ default:
1362
+ fail(`Unknown command '${command}'. Run 'sixb --help'.`);
1363
+ }
1364
+ }
1365
+ // src/run.ts
1366
+ async function runInstanceCli(input) {
1367
+ const [command, ...args] = input.args;
1368
+ await dispatch(createInstanceApiClient(input.mode), command ?? "", args);
1369
+ }
1370
+ function createInstanceApiClient(mode) {
1371
+ return new ApiClient({
1372
+ baseUrl: mode.baseUrl,
1373
+ ...mode.kind === "local" && mode.token ? { authorization: `Bearer ${mode.token}` } : {},
1374
+ missingBaseUrlMessage: mode.kind === "sandbox" ? "SIXB_API_BASE_URL is not set." : "The selected Sixb profile has no API URL.",
1375
+ unavailableMessage: mode.kind === "sandbox" ? "The Sixb API gateway could not be reached." : "The Sixb API could not be reached.",
1376
+ unavailableHint: mode.kind === "sandbox" ? "Run 'sixb doctor' to verify the sandbox runtime and gateway." : "Run 'sixb status' to verify the current profile."
1377
+ });
1378
+ }
1379
+ export {
1380
+ writeText,
1381
+ writeJson,
1382
+ runInstanceCli,
1383
+ requestsHelp,
1384
+ reportError,
1385
+ renderInstanceHelp,
1386
+ isInstanceCommand,
1387
+ isHelp,
1388
+ isFileError,
1389
+ fail,
1390
+ createInstanceApiClient,
1391
+ asRecord2 as asRecord,
1392
+ INSTANCE_COMMANDS,
1393
+ INSTANCE_CLI_VERSION,
1394
+ EXIT_USAGE,
1395
+ EXIT_API,
1396
+ CliError,
1397
+ ApiClient
1398
+ };
1399
+
1400
+ //# debugId=89FD14967D5A33DF64756E2164756E21