@sixb/cli-core 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.8",
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
@@ -41,9 +41,20 @@ export class ApiClient {
41
41
  })
42
42
  }
43
43
 
44
- 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> {
45
51
  const form = new FormData()
46
- 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
+ )
47
58
  if (logicalPath) form.append("logicalPath", logicalPath)
48
59
  return this.json(this.url(path), { method: "POST", body: form })
49
60
  }
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
 
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto"
2
2
  import type { ApiClient } from "../api-client"
3
3
  import { isHelp, parseCommandArgs, requestsHelp } from "../arguments"
4
- import { CliError, EXIT_API, fail, writeJson, writeText } from "../output"
4
+ import { CliError, EXIT_API, fail, writeJson, writeRequestResult, writeText } from "../output"
5
5
  import { GROUP_HELP } from "./metadata"
6
6
  import { asRecord, parseQueryOptions, readJson } from "./shared"
7
7
 
@@ -62,7 +62,7 @@ export async function actions(
62
62
  const requestedRunId = runId ?? `act_${randomUUID()}`
63
63
  const waiting = AbortSignal.timeout(requestWaitMs)
64
64
  try {
65
- return writeJson(
65
+ return writeRequestResult(
66
66
  await api.post(
67
67
  `/api/actions/${encodeURIComponent(actionId)}`,
68
68
  {
@@ -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
 
@@ -209,7 +238,8 @@ History options:
209
238
  \`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
210
239
  contains that parameter object; use - to read standard input. \`actions request\` runs the Action
211
240
  and prints its finished run, with its status and any error. It waits at most 25 seconds: a run that
212
- takes longer keeps running, and the error names the run to read with \`action-runs get\`.`,
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.`,
213
243
  "action-runs": `Usage:
214
244
  sixb action-runs list [options]
215
245
  sixb action-runs get <run-id>
@@ -230,9 +260,48 @@ List options:
230
260
  workflows: `Usage:
231
261
  sixb workflows list
232
262
  sixb workflows get <workflow-id>
233
- 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
234
276
 
235
- 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.`,
236
305
  "workflow-runs": `Usage:
237
306
  sixb workflow-runs list [options]
238
307
  sixb workflow-runs get <run-id>
@@ -254,10 +323,12 @@ export const QUERY_EXAMPLES: Readonly<Record<string, string>> = {
254
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}}`,
255
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}}`,
256
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}}`,
257
327
  project: `{"kind":"project","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Customer"},"limit":${CLI_LIMITS.list.default}},"properties":["name","status"]}`,
258
328
  incoming:
259
329
  '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
260
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"]}`,
261
332
  sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
262
333
  page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
263
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}}`,