@sixb/cli-core 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/output.d.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  export declare const INSTANCE_CLI_VERSION = "1";
3
3
  export declare const EXIT_USAGE = 2;
4
4
  export declare const EXIT_API = 3;
5
+ /** The request waits for its user's approval: it did not run. */
6
+ export declare const EXIT_AWAITING_APPROVAL = 4;
5
7
  export interface CliErrorBody {
6
8
  readonly code: string;
7
9
  readonly status?: number;
@@ -16,6 +18,16 @@ export declare class CliError extends Error {
16
18
  }
17
19
  export declare function fail(message: string, code?: string, hint?: string): never;
18
20
  export declare function writeJson(value: unknown): void;
21
+ /** Ends a command that already wrote its output, with an exit code that is not a success. */
22
+ export declare class CliExit extends Error {
23
+ readonly exitCode: number;
24
+ constructor(exitCode: number);
25
+ }
26
+ /**
27
+ * Print what a request that changes state answered. A request held for the user's approval prints
28
+ * as the API answered it, and exits with {@link EXIT_AWAITING_APPROVAL}: `a && b` does not run `b`.
29
+ */
30
+ export declare function writeRequestResult(value: unknown): void;
19
31
  export declare function writeText(value: string): void;
20
32
  export declare function reportError(error: unknown): number;
21
33
  //# sourceMappingURL=output.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,MAAM,CAAA;AACvC,eAAO,MAAM,UAAU,IAAI,CAAA;AAC3B,eAAO,MAAM,QAAQ,IAAI,CAAA;AAEzB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CACrC;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAEb,IAAI,EAAE,YAAY,EAAE,QAAQ,SAAa;CAMtD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAsB,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAEtF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAalD"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,MAAM,CAAA;AACvC,eAAO,MAAM,UAAU,IAAI,CAAA;AAC3B,eAAO,MAAM,QAAQ,IAAI,CAAA;AACzB,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CACrC;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAEb,IAAI,EAAE,YAAY,EAAE,QAAQ,SAAa;CAMtD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAsB,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAEtF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,6FAA6F;AAC7F,qBAAa,OAAQ,SAAQ,KAAK;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAItC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAIvD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAclD"}
@@ -20,6 +20,11 @@ export declare const CLI_LIMITS: {
20
20
  readonly default: 100;
21
21
  readonly maximum: 1000;
22
22
  };
23
+ /** The server lists at most 200 members per page. */
24
+ readonly users: {
25
+ readonly default: 20;
26
+ readonly maximum: 200;
27
+ };
23
28
  readonly inspect: {
24
29
  readonly depth: {
25
30
  readonly default: 2;
@@ -1 +1 @@
1
- {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,eAAO,MAAM,UAAU;;;;;;;;;IAGrB,+FAA+F;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUvF,CAAA;AAEV,eAAO,MAAM,kBAAkB,EAAG,MAAe,CAAA;AACjD,eAAO,MAAM,uBAAuB,EAAG,WAAoB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,EAAG,MAAe,CAAA"}
1
+ {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,eAAO,MAAM,UAAU;;;;;;;;;IAGrB,+FAA+F;;;;;;;;;;;;IAI/F,qDAAqD;;;;;;;;;;;;;;;;;;;;CAQ7C,CAAA;AAEV,eAAO,MAAM,kBAAkB,EAAG,MAAe,CAAA;AACjD,eAAO,MAAM,uBAAuB,EAAG,WAAoB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,EAAG,MAAe,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sixb/cli-core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Shared instance command runtime for Sixb CLIs",
5
5
  "keywords": [
6
6
  "sixb",
package/src/api-client.ts CHANGED
@@ -27,17 +27,34 @@ export class ApiClient {
27
27
  return this.json(url, { method: "GET" })
28
28
  }
29
29
 
30
- async post(path: string, body: unknown): Promise<unknown> {
30
+ /** `signal` stops waiting for the response; the abort is rethrown as it was raised. */
31
+ async post(
32
+ path: string,
33
+ body: unknown,
34
+ options: { readonly signal?: AbortSignal } = {}
35
+ ): Promise<unknown> {
31
36
  return this.json(this.url(path), {
32
37
  method: "POST",
33
38
  headers: { "content-type": "application/json" },
34
39
  body: JSON.stringify(body),
40
+ ...(options.signal ? { signal: options.signal } : {}),
35
41
  })
36
42
  }
37
43
 
38
- async upload(path: string, source: string, logicalPath?: string): Promise<unknown> {
44
+ /** `mediaType` labels the part; without it, the server reads the type from the file name. */
45
+ async upload(
46
+ path: string,
47
+ source: string,
48
+ logicalPath?: string,
49
+ mediaType?: string
50
+ ): Promise<unknown> {
39
51
  const form = new FormData()
40
- form.append("file", new Blob([new Uint8Array(await readFile(source))]), basename(source))
52
+ const bytes = new Uint8Array(await readFile(source))
53
+ form.append(
54
+ "file",
55
+ new Blob([bytes], mediaType ? { type: mediaType } : undefined),
56
+ basename(source)
57
+ )
41
58
  if (logicalPath) form.append("logicalPath", logicalPath)
42
59
  return this.json(this.url(path), { method: "POST", body: form })
43
60
  }
@@ -91,7 +108,8 @@ export class ApiClient {
91
108
  const headers = new Headers(init.headers)
92
109
  if (this.authorization) headers.set("authorization", this.authorization)
93
110
  response = await fetch(url, { ...init, headers })
94
- } catch {
111
+ } catch (error) {
112
+ if (init.signal?.aborted) throw error
95
113
  throw new CliError(
96
114
  {
97
115
  code: "api_unreachable",
package/src/arguments.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import { fail } from "./output"
2
2
 
3
- type OptionKinds = Readonly<Record<string, "string" | "boolean">>
3
+ /** `strings` may repeat, and collects every value in order. */
4
+ type OptionKinds = Readonly<Record<string, "string" | "strings" | "boolean">>
5
+
6
+ type OptionValue<TKind> = TKind extends "boolean"
7
+ ? boolean
8
+ : TKind extends "strings"
9
+ ? string[]
10
+ : string
4
11
 
5
12
  /** Parse a command's options once, before reading files or calling the API. */
6
13
  export function parseCommandArgs<const T extends OptionKinds>(
@@ -10,10 +17,10 @@ export function parseCommandArgs<const T extends OptionKinds>(
10
17
  positionalCount: number | readonly [number, number] = 0
11
18
  ): {
12
19
  positionals: string[]
13
- options: { [K in keyof T]?: T[K] extends "boolean" ? boolean : string }
20
+ options: { [K in keyof T]?: OptionValue<T[K]> }
14
21
  } {
15
22
  const positionals: string[] = []
16
- const options: Record<string, string | boolean> = {}
23
+ const options: Record<string, string | string[] | boolean> = {}
17
24
  let positionalOnly = false
18
25
  for (let index = 0; index < args.length; index++) {
19
26
  const argument = args[index] ?? ""
@@ -28,7 +35,9 @@ export function parseCommandArgs<const T extends OptionKinds>(
28
35
  const equals = argument.indexOf("=")
29
36
  const flag = equals < 0 ? argument : argument.slice(0, equals)
30
37
  if (!Object.hasOwn(kinds, flag)) fail(`Unknown ${command} option '${flag}'.`)
31
- if (Object.hasOwn(options, flag)) fail(`${flag} may only be provided once.`)
38
+ if (Object.hasOwn(options, flag) && kinds[flag] !== "strings") {
39
+ fail(`${flag} may only be provided once.`)
40
+ }
32
41
  if (kinds[flag] === "boolean") {
33
42
  if (equals >= 0) fail(`${flag} does not accept a value.`)
34
43
  options[flag] = true
@@ -37,7 +46,9 @@ export function parseCommandArgs<const T extends OptionKinds>(
37
46
  if (!value || (equals < 0 && value.startsWith("--"))) {
38
47
  fail(`${flag} requires a value.`)
39
48
  }
40
- options[flag] = value
49
+ const previous = options[flag]
50
+ options[flag] =
51
+ kinds[flag] === "strings" ? [...(Array.isArray(previous) ? previous : []), value] : value
41
52
  }
42
53
  }
43
54
  const [minimum, maximum] =
@@ -48,7 +59,7 @@ export function parseCommandArgs<const T extends OptionKinds>(
48
59
  return {
49
60
  positionals,
50
61
  // Every assigned option was checked against its declared kind above.
51
- options: options as { [K in keyof T]?: T[K] extends "boolean" ? boolean : string },
62
+ options: options as { [K in keyof T]?: OptionValue<T[K]> },
52
63
  }
53
64
  }
54
65
 
@@ -126,5 +137,6 @@ export function enumValue<const T extends readonly string[]>(
126
137
 
127
138
  function formatAlternatives(values: readonly string[]): string {
128
139
  if (values.length < 2) return values[0] ?? "a supported value"
140
+ if (values.length === 2) return `${values[0]} or ${values[1]}`
129
141
  return `${values.slice(0, -1).join(", ")}, or ${values.at(-1)}`
130
142
  }
@@ -1,10 +1,23 @@
1
+ import { randomUUID } from "node:crypto"
1
2
  import type { ApiClient } from "../api-client"
2
3
  import { isHelp, parseCommandArgs, requestsHelp } from "../arguments"
3
- import { CliError, EXIT_API, fail, writeJson, writeText } from "../output"
4
+ import { CliError, EXIT_API, fail, writeJson, writeRequestResult, writeText } from "../output"
4
5
  import { GROUP_HELP } from "./metadata"
5
6
  import { asRecord, parseQueryOptions, readJson } from "./shared"
6
7
 
7
- export async function actions(api: ApiClient, args: readonly string[]): Promise<void> {
8
+ /**
9
+ * How long `actions request` waits for the run it requested. An agent's command is killed after 30
10
+ * seconds, which is also how long an Action has to reach its boundary: stopping first leaves the
11
+ * agent with the run id, instead of a dead command and a request it can only repeat.
12
+ */
13
+ const ACTION_REQUEST_WAIT_MS = 25_000
14
+
15
+ export async function actions(
16
+ api: ApiClient,
17
+ args: readonly string[],
18
+ /** Test-only override of {@link ACTION_REQUEST_WAIT_MS}. */
19
+ requestWaitMs = ACTION_REQUEST_WAIT_MS
20
+ ): Promise<void> {
8
21
  const [sub, ...rest] = args
9
22
  if (!sub || isHelp(sub) || requestsHelp(rest)) return writeText(GROUP_HELP.actions)
10
23
  if (sub === "get") {
@@ -28,7 +41,6 @@ export async function actions(api: ApiClient, args: readonly string[]): Promise<
28
41
  "--subject-id": "string",
29
42
  "--file": "string",
30
43
  "--run-id": "string",
31
- "--wait": "boolean",
32
44
  },
33
45
  "actions request",
34
46
  1
@@ -38,7 +50,6 @@ export async function actions(api: ApiClient, args: readonly string[]): Promise<
38
50
  const subjectId = options["--subject-id"]
39
51
  const paramsSource = options["--file"]
40
52
  const runId = options["--run-id"]
41
- const wait = options["--wait"]
42
53
  if (Boolean(subjectType) !== Boolean(subjectId)) {
43
54
  fail("--subject-type and --subject-id must be provided together.")
44
55
  }
@@ -46,49 +57,40 @@ export async function actions(api: ApiClient, args: readonly string[]): Promise<
46
57
  if (Array.isArray(params) || typeof params !== "object" || params === null) {
47
58
  fail("Action params must be a JSON object.")
48
59
  }
49
- const requested = await api.post(`/api/actions/${encodeURIComponent(actionId)}`, {
50
- params,
51
- ...(subjectType && subjectId
52
- ? { subject: { kind: "object", objectTypeId: subjectType, primaryId: subjectId } }
53
- : {}),
54
- ...(runId ? { runId } : {}),
55
- })
56
- if (!wait) return writeJson(requested)
57
-
58
- const requestedRunId = asRecord(requested).runId
59
- if (typeof requestedRunId !== "string" || requestedRunId.length === 0) {
60
+ // The API answers once the Action has run, with its terminal run. Every request names its run,
61
+ // so that one this command stops waiting for can still be read: the run is not cancelled.
62
+ const requestedRunId = runId ?? `act_${randomUUID()}`
63
+ const waiting = AbortSignal.timeout(requestWaitMs)
64
+ try {
65
+ return writeRequestResult(
66
+ await api.post(
67
+ `/api/actions/${encodeURIComponent(actionId)}`,
68
+ {
69
+ params,
70
+ ...(subjectType && subjectId
71
+ ? { subject: { kind: "object", objectTypeId: subjectType, primaryId: subjectId } }
72
+ : {}),
73
+ runId: requestedRunId,
74
+ },
75
+ { signal: waiting }
76
+ )
77
+ )
78
+ } catch (error) {
79
+ if (!waiting.aborted) throw error
60
80
  throw new CliError(
61
81
  {
62
- code: "invalid_api_response",
63
- message: "The Action request response did not contain a run id.",
82
+ code: "action_wait_timeout",
83
+ message:
84
+ `Action run '${requestedRunId}' did not finish within ${requestWaitMs / 1_000} ` +
85
+ "seconds. It was not cancelled.",
86
+ hint:
87
+ `Read it with 'sixb action-runs get ${requestedRunId}': its record exists once it ` +
88
+ `has ended. A repeated request must pass '--run-id ${requestedRunId}', or the ` +
89
+ "Action runs twice.",
64
90
  },
65
91
  EXIT_API
66
92
  )
67
93
  }
68
- return writeJson(await waitForActionRun(api, requestedRunId))
69
94
  }
70
95
  fail(`Unknown actions command '${sub}'.`)
71
96
  }
72
-
73
- const ACTION_WAIT_TIMEOUT_MS = 25_000
74
- const ACTION_WAIT_POLL_MS = 250
75
- const TERMINAL_ACTION_STATUSES = new Set(["succeeded", "failed", "cancelled"])
76
-
77
- async function waitForActionRun(api: ApiClient, runId: string): Promise<unknown> {
78
- const startedAt = Date.now()
79
- while (Date.now() - startedAt < ACTION_WAIT_TIMEOUT_MS) {
80
- const run = await api.get(`/api/action-runs/${encodeURIComponent(runId)}`)
81
- const status = asRecord(run).status
82
- if (typeof status === "string" && TERMINAL_ACTION_STATUSES.has(status)) return run
83
- await new Promise((resolve) => setTimeout(resolve, ACTION_WAIT_POLL_MS))
84
- }
85
-
86
- throw new CliError(
87
- {
88
- code: "action_wait_timeout",
89
- message: `Action run '${runId}' did not finish within ${ACTION_WAIT_TIMEOUT_MS / 1_000} seconds.`,
90
- hint: `Inspect it with 'sixb action-runs get ${runId}'.`,
91
- },
92
- EXIT_API
93
- )
94
- }
@@ -2,11 +2,13 @@ import type { ApiClient } from "../api-client"
2
2
  import { fail } from "../output"
3
3
  import { actions } from "./actions"
4
4
  import { files } from "./files"
5
+ import { models } from "./models"
5
6
  import { objects } from "./objects"
6
7
  import { ontology } from "./ontology"
7
8
  import { project } from "./project"
8
9
  import { runs } from "./runs"
9
10
  import { telemetry } from "./telemetry"
11
+ import { users } from "./users"
10
12
  import { workflows } from "./workflows"
11
13
 
12
14
  export const INSTANCE_COMMANDS = [
@@ -19,6 +21,8 @@ export const INSTANCE_COMMANDS = [
19
21
  "files",
20
22
  "workflows",
21
23
  "workflow-runs",
24
+ "users",
25
+ "models",
22
26
  ] as const
23
27
 
24
28
  export type InstanceCommand = (typeof INSTANCE_COMMANDS)[number]
@@ -51,6 +55,10 @@ export async function dispatch(
51
55
  return workflows(api, args)
52
56
  case "workflow-runs":
53
57
  return runs(api, "workflow", args)
58
+ case "users":
59
+ return users(api, args)
60
+ case "models":
61
+ return models(api, args)
54
62
  default:
55
63
  fail(`Unknown command '${command}'. Run 'sixb --help'.`)
56
64
  }
@@ -28,6 +28,8 @@ Objects:
28
28
  sixb objects inspect <type> <id> Inspect an object and its related graph in one command
29
29
  sixb objects list [options] Browse materialized objects
30
30
  sixb objects get <type> <id>... Exact lookup through opaque object references
31
+ sixb objects get-by <type> <property> <value>
32
+ The object holding a value of a unique property
31
33
  sixb objects search <text> Search visible objects by id prefix and full text
32
34
  sixb objects search <text> --type <type>
33
35
  Semantic search through the type's vector profile
@@ -51,6 +53,13 @@ Actions and workflows:
51
53
  Files:
52
54
  sixb files upload|download ...
53
55
 
56
+ Members:
57
+ sixb users search <text> Find members by name or email
58
+ sixb users list|get ...
59
+
60
+ A user parameter or property is {"type":"user","id":"<user-id>"}. Find the id with
61
+ \`sixb users search <name>\`.
62
+
54
63
  Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
55
64
  For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`
56
65
 
@@ -68,6 +77,12 @@ const LOCAL_MAIN_HELP = SANDBOX_MAIN_HELP.replace("Sixb agent CLI", "Sixb instan
68
77
  ""
69
78
  )
70
79
 
80
+ // The agent reaches models through its run's gateway once the model routes allow it.
81
+ .replace(
82
+ "Files:\n sixb files upload|download ...\n",
83
+ "Files:\n sixb files upload|download ...\n\nModels:\n sixb models list [--kind <kind>] Configured models of every kind, with prices\n sixb models get <model> One model's options and rate card\n sixb models text|image|speech|transcribe ... Call a model; see sixb models --help\n"
84
+ )
85
+
71
86
  export function renderInstanceHelp(mode: "local" | "sandbox"): string {
72
87
  return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP
73
88
  }
@@ -87,10 +102,19 @@ profiles.
87
102
 
88
103
  ${SEARCH_DETAILS}`
89
104
 
105
+ export const GET_BY_HELP = `Usage:
106
+ sixb objects get-by <object-type> <property> <value> [--json]
107
+
108
+ Prints the object of exactly <object-type> holding <value> for a property marked unique in its
109
+ ontology doc, or null when no visible object holds it. Values match exactly, case included.
110
+ The value is a string; with --json it is parsed as JSON, for an integer (42) or a user
111
+ reference ({"type":"user","id":"usr_1"}).`
112
+
90
113
  export const OBJECTS_HELP = `Usage:
91
114
  sixb objects inspect <object-type> <primary-id> [options]
92
115
  sixb objects list [options]
93
116
  sixb objects get <object-type> <primary-id>...
117
+ sixb objects get-by <object-type> <property> <value> [--json]
94
118
  sixb objects search <text> [--type <object-type> [--vector-profile <name>]] [--limit <n>]
95
119
  sixb objects query --file <path|-> [--include-total|--no-total]
96
120
  sixb objects query --example <name>
@@ -158,7 +182,7 @@ Query nodes compose through input:
158
182
  limit {"kind":"limit","input":<query>,"limit":${CLI_LIMITS.list.default}}
159
183
  page {"kind":"page","input":<query>,"pageSize":${CLI_LIMITS.list.default},"pageToken":"from nextPageToken"}
160
184
  project {"kind":"project","input":<query>,"properties":["name","status"]}
161
- expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}]}
185
+ expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}],"users":["assignee"]}
162
186
  vector {"kind":"vector","input":<start|filter>,"vector":"text","profile":"name","k":10}
163
187
  rerank {"kind":"rerank","input":<vector>,"model":{"provider":"provider","modelId":"model"}}
164
188
 
@@ -169,11 +193,16 @@ Predicates use op, not kind:
169
193
  {"op":"in","propertyId":"status","values":["open","blocked"]}
170
194
  {"op":"exists","propertyId":"dueDate","value":true}
171
195
  {"op":"contains","propertyId":"tags","value":"urgent"}
196
+ {"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}} the caller, on a user reference: neq, in, contains too
172
197
 
173
198
  Every field shown is required except includeSubtypes, fields, pageToken, properties, a sort
174
- direction, and an expansion's limit. Sort by relevance after text or vector with
199
+ direction, an expansion's limit, and users. Sort by relevance after text or vector with
175
200
  {"kind":"relevance"}. Wrap the outermost node in project to return only the properties you need.
176
201
 
202
+ An expand's users, or an expansion's, lists user-reference properties: each row then carries the
203
+ members' profiles under users (id, email, displayName, avatarUrl, status). Without access to the
204
+ member directory, users is empty.
205
+
177
206
  Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
178
207
  is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
179
208
 
@@ -204,11 +233,13 @@ History options:
204
233
  actions: `Usage:
205
234
  sixb actions list [--type <object-type>]
206
235
  sixb actions get <action-id>
207
- sixb actions request <action-id> [--subject-type <type> --subject-id <id>] [--file <path|->] [--run-id <id>] [--wait]
236
+ sixb actions request <action-id> [--subject-type <type> --subject-id <id>] [--file <path|->] [--run-id <id>]
208
237
 
209
238
  \`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
210
- contains that parameter object; use - to read standard input. --wait returns the terminal Action
211
- run and waits at most 25 seconds.`,
239
+ contains that parameter object; use - to read standard input. \`actions request\` runs the Action
240
+ and prints its finished run, with its status and any error. It waits at most 25 seconds: a run that
241
+ takes longer keeps running, and the error names the run to read with \`action-runs get\`. A request
242
+ that waits for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
212
243
  "action-runs": `Usage:
213
244
  sixb action-runs list [options]
214
245
  sixb action-runs get <run-id>
@@ -217,7 +248,7 @@ List options:
217
248
  --action <action-id>
218
249
  --type <object-type>
219
250
  --id <primary-id>
220
- --status queued|running|succeeded|failed|cancelled
251
+ --status succeeded|failed
221
252
  --started-after|--started-before <RFC3339>
222
253
  --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
223
254
  --offset <n> Non-negative; defaults to 0
@@ -229,9 +260,48 @@ List options:
229
260
  workflows: `Usage:
230
261
  sixb workflows list
231
262
  sixb workflows get <workflow-id>
232
- sixb workflows start <workflow-id> [--file <path|->]
263
+ sixb workflows start <workflow-id> [--file <path|->] [--run-id <id>]
264
+
265
+ The JSON file contains the workflow input object. Use - to read standard input. A start that waits
266
+ for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
267
+ users: `Usage:
268
+ sixb users list [options]
269
+ sixb users search <text> [options]
270
+ sixb users get <user-id>
271
+
272
+ Options:
273
+ --status active|suspended|all Defaults to active
274
+ --limit <1-${CLI_LIMITS.users.maximum}> Defaults to ${CLI_LIMITS.users.default}
275
+ --offset <n> Non-negative; defaults to 0
233
276
 
234
- The JSON file contains the workflow input object. Use - to read standard input.`,
277
+ \`users search\` matches part of a display name or email, ignoring case. Each member is
278
+ {"id","email","displayName","avatarUrl","status"}; a user parameter takes
279
+ {"type":"user","id":"<id>"}. Reading members requires can.view(project.users).`,
280
+ models: `Usage:
281
+ sixb models list [--kind language|image|speech|transcription]
282
+ sixb models get <model> [--kind <kind>]
283
+ sixb models text "<prompt>"|--file <path|-> [--model <tier|model>] [--instructions <text>]
284
+ [--max-output-tokens <n>] [--reasoning <level>]
285
+ sixb models image "<prompt>" --output <path> [--model <model>] [--n <n>]
286
+ [--size <size>] [--aspect-ratio <ratio>] [--quality <q>] [--style <s>]
287
+ [--seed <n>] [--image <file>]...
288
+ sixb models speech "<text>" --output <path> [--model <model>] [--voice <voice>]
289
+ [--format mp3|wav|ogg|aac|flac] [--instructions <text>]
290
+ [--speed <n>] [--language <code>]
291
+ sixb models transcribe <file> [--model <model>]
292
+ sixb models calls files <call-id> [--output <path>]
293
+
294
+ <model> is a model id, or provider:model-id when two providers share one; without --model, the
295
+ kind's default answers. \`list\` shows each model's kind, default, description and price; \`get\`
296
+ adds the sizes, voices and other options a request can use. Prices are in USD.
297
+
298
+ \`text\` also takes a tier (light, standard or heavy) for --model, and reads a long prompt from
299
+ --file: a path, or - for standard input.
300
+
301
+ --output is a file, numbered when a call returns several, or a directory ending in /. The
302
+ extension follows the file's type, and the result lists the paths written. --image uploads a
303
+ reference image; repeat it for several. \`calls files\` lists a past call's files, or downloads
304
+ them again.`,
235
305
  "workflow-runs": `Usage:
236
306
  sixb workflow-runs list [options]
237
307
  sixb workflow-runs get <run-id>
@@ -253,10 +323,12 @@ export const QUERY_EXAMPLES: Readonly<Record<string, string>> = {
253
323
  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}}`,
254
324
  in: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"in","propertyId":"status","values":["active","trial"]}},"limit":${CLI_LIMITS.list.default}}`,
255
325
  exists: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"exists","propertyId":"accountManager","value":false}},"limit":${CLI_LIMITS.list.default}}`,
326
+ mine: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Task"},"predicate":{"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}}},"limit":${CLI_LIMITS.list.default}}`,
256
327
  project: `{"kind":"project","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Customer"},"limit":${CLI_LIMITS.list.default}},"properties":["name","status"]}`,
257
328
  incoming:
258
329
  '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
259
330
  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}}]}`,
331
+ users: `{"kind":"expand","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Task"},"limit":${CLI_LIMITS.list.default}},"expansions":[],"users":["assignee"]}`,
260
332
  sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
261
333
  page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
262
334
  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}}`,