@sixb/cli-core 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +21 -16
  2. package/dist/api-client.d.ts +3 -1
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/arguments.d.ts +4 -2
  5. package/dist/arguments.d.ts.map +1 -1
  6. package/dist/bin/sixb +26 -0
  7. package/dist/commands/data.d.ts +5 -0
  8. package/dist/commands/data.d.ts.map +1 -0
  9. package/dist/commands/files.d.ts.map +1 -1
  10. package/dist/commands/index.d.ts +1 -1
  11. package/dist/commands/index.d.ts.map +1 -1
  12. package/dist/commands/memory.d.ts +3 -0
  13. package/dist/commands/memory.d.ts.map +1 -0
  14. package/dist/commands/metadata.d.ts +14 -5
  15. package/dist/commands/metadata.d.ts.map +1 -1
  16. package/dist/commands/models.d.ts +79 -0
  17. package/dist/commands/models.d.ts.map +1 -0
  18. package/dist/commands/objects.d.ts.map +1 -1
  19. package/dist/commands/runs.d.ts +8 -1
  20. package/dist/commands/runs.d.ts.map +1 -1
  21. package/dist/commands/shared.d.ts +4 -0
  22. package/dist/commands/shared.d.ts.map +1 -1
  23. package/dist/commands/system.d.ts +21 -0
  24. package/dist/commands/system.d.ts.map +1 -0
  25. package/dist/commands/users.d.ts +3 -0
  26. package/dist/commands/users.d.ts.map +1 -0
  27. package/dist/commands/workflows.d.ts.map +1 -1
  28. package/dist/generated/sixb.mjs +2547 -0
  29. package/dist/index.d.ts +8 -3
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +800 -62
  32. package/dist/index.js.map +24 -18
  33. package/dist/main.d.ts +3 -0
  34. package/dist/main.d.ts.map +1 -0
  35. package/dist/memory.d.ts +21 -0
  36. package/dist/memory.d.ts.map +1 -0
  37. package/dist/output.d.ts +12 -0
  38. package/dist/output.d.ts.map +1 -1
  39. package/dist/policies.d.ts +14 -0
  40. package/dist/policies.d.ts.map +1 -1
  41. package/dist/run.d.ts +1 -0
  42. package/dist/run.d.ts.map +1 -1
  43. package/dist/sandbox-cli.d.ts +8 -0
  44. package/dist/sandbox-cli.d.ts.map +1 -0
  45. package/package.json +15 -1
  46. package/src/api-client.ts +83 -4
  47. package/src/arguments.ts +17 -6
  48. package/src/bin/sixb +26 -0
  49. package/src/commands/actions.ts +5 -5
  50. package/src/commands/data.ts +90 -0
  51. package/src/commands/files.ts +27 -3
  52. package/src/commands/index.ts +24 -0
  53. package/src/commands/memory.ts +358 -0
  54. package/src/commands/metadata.ts +137 -26
  55. package/src/commands/models.ts +508 -0
  56. package/src/commands/objects.ts +47 -2
  57. package/src/commands/ontology.ts +2 -2
  58. package/src/commands/runs.ts +22 -16
  59. package/src/commands/shared.ts +17 -7
  60. package/src/commands/system.ts +67 -0
  61. package/src/commands/telemetry.ts +2 -2
  62. package/src/commands/users.ts +87 -0
  63. package/src/commands/workflows.ts +12 -6
  64. package/src/generated/sixb.mjs +2547 -0
  65. package/src/index.ts +12 -3
  66. package/src/main.ts +39 -0
  67. package/src/memory.ts +30 -0
  68. package/src/output.ts +21 -0
  69. package/src/policies.ts +5 -0
  70. package/src/run.ts +3 -2
  71. package/src/sandbox-cli.ts +18 -0
@@ -0,0 +1,358 @@
1
+ import { lstat, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises"
2
+ import { dirname, join } from "node:path"
3
+ import { isHelp } from "../arguments"
4
+ import {
5
+ AGENT_MEMORY_ENTRY,
6
+ AGENT_MEMORY_MANIFEST,
7
+ AGENT_MEMORY_STARTER,
8
+ type AgentMemoryManifest,
9
+ type AgentMemoryManifestRepo,
10
+ type AgentMemoryRepo,
11
+ agentMemoryFileHash,
12
+ } from "../memory"
13
+ import { CliError, EXIT_API, fail, writeJson, writeText } from "../output"
14
+ import { createInstanceApiClient, type SandboxInstanceCliMode } from "../run"
15
+ import { asRecord, isFileError } from "./shared"
16
+
17
+ const HELP = `Usage:
18
+ sixb memory save <repo> -m <message> Save local edits as one revision
19
+ sixb memory pull [repo] Bring the local copy up to the latest revision
20
+
21
+ <repo> is user or project. Files live under $SIXB_MEMORY_DIR/<repo>/.
22
+
23
+ A save is rejected for files another conversation changed since they were loaded. Their
24
+ current contents are written next to yours as <file>.theirs: merge each into its file, delete
25
+ it, then save again. Changes others made to other files are brought in by save and pull.`
26
+
27
+ interface RemoteRepo {
28
+ readonly revision: number
29
+ readonly files: ReadonlyMap<string, string>
30
+ }
31
+
32
+ interface MemoryConflict {
33
+ readonly path: string
34
+ /** Where their version was written, or `null` when another save deleted the file. */
35
+ readonly theirs: string | null
36
+ }
37
+
38
+ const THEIRS = ".theirs"
39
+ const CONFLICT_HINT = "Merge each .theirs file into its file, delete it, then save again."
40
+
41
+ export async function memory(args: readonly string[], mode: SandboxInstanceCliMode): Promise<void> {
42
+ const [sub, ...rest] = args
43
+ if (!sub || isHelp(sub) || rest.some(isHelp)) return writeText(HELP)
44
+ const local = localMemory()
45
+ if (sub === "save") return save(local, rest, mode)
46
+ if (sub === "pull") return pull(local, rest, mode)
47
+ fail(`Unknown memory command '${sub}'. Run 'sixb memory --help'.`)
48
+ }
49
+
50
+ async function save(
51
+ local: LocalMemory,
52
+ args: readonly string[],
53
+ mode: SandboxInstanceCliMode
54
+ ): Promise<void> {
55
+ const { repo, message } = parseSaveArgs(args)
56
+ const manifest = await local.readManifest()
57
+ const loaded = manifest.repos[repo]
58
+ if (!loaded)
59
+ fail(
60
+ `Memory repo '${repo}' is not installed.`,
61
+ "invalid_arguments",
62
+ `Run 'sixb memory pull ${repo}'.`
63
+ )
64
+ const files = await local.readRepo(repo)
65
+ const unresolved = [...files.keys()].filter((path) => path.endsWith(THEIRS))
66
+ if (unresolved.length > 0) {
67
+ fail(`Resolve ${unresolved.join(", ")} first.`, "unresolved_conflicts", CONFLICT_HINT)
68
+ }
69
+ const changes = [...new Set([...files.keys(), ...Object.keys(loaded.files)])]
70
+ .sort()
71
+ .flatMap((path) => {
72
+ const contents = files.get(path)
73
+ const changed =
74
+ (contents === undefined ? undefined : agentMemoryFileHash(contents)) !== loaded.files[path]
75
+ return changed ? [{ path, contents: contents ?? null }] : []
76
+ })
77
+ if (changes.length === 0) {
78
+ return writeJson({
79
+ repo,
80
+ saved: false,
81
+ changed: [],
82
+ hint: "No file changed since it was loaded.",
83
+ })
84
+ }
85
+
86
+ const api = createInstanceApiClient(mode)
87
+ let saved: Record<string, unknown>
88
+ try {
89
+ saved = asRecord(
90
+ await api.post(`/api/agent-memory/${repo}/revisions`, {
91
+ baseRevision: loaded.revision,
92
+ message,
93
+ changes,
94
+ })
95
+ )
96
+ } catch (error) {
97
+ if (!(error instanceof CliError) || error.body.status !== 409) throw error
98
+ const { revision, conflicts } = await update(local, repo, manifest, await fetchRepo(api, repo))
99
+ process.exitCode = 1
100
+ return writeJson({ repo, saved: false, revision, conflicts, hint: CONFLICT_HINT })
101
+ }
102
+
103
+ // The saved files are now what the repo holds, so updating does not report them as conflicts.
104
+ const savedFiles = { ...loaded.files }
105
+ for (const change of changes) {
106
+ if (change.contents === null) delete savedFiles[change.path]
107
+ else savedFiles[change.path] = agentMemoryFileHash(change.contents)
108
+ }
109
+ const after: AgentMemoryManifest = {
110
+ repos: { ...manifest.repos, [repo]: { ...loaded, files: savedFiles } },
111
+ }
112
+ const { revision, conflicts } = await update(local, repo, after, await fetchRepo(api, repo))
113
+ const warnings = Array.isArray(saved.warnings) ? saved.warnings : []
114
+ writeJson({
115
+ repo,
116
+ saved: true,
117
+ revision,
118
+ changed: changes.map((change) => change.path),
119
+ ...(warnings.length > 0 ? { warnings } : {}),
120
+ ...(conflicts.length > 0 ? { conflicts, hint: CONFLICT_HINT } : {}),
121
+ })
122
+ }
123
+
124
+ async function pull(
125
+ local: LocalMemory,
126
+ args: readonly string[],
127
+ mode: SandboxInstanceCliMode
128
+ ): Promise<void> {
129
+ if (args.length > 1) fail("Invalid arguments for 'memory pull'.")
130
+ const manifest = await local.readManifest()
131
+ const repos = args[0] === undefined ? installedRepos(manifest) : [parseRepo(args[0])]
132
+ if (repos.length === 0) {
133
+ fail("No memory repo is installed.", "invalid_arguments", "Run 'sixb memory pull user'.")
134
+ }
135
+
136
+ const api = createInstanceApiClient(mode)
137
+ let current = manifest
138
+ for (const repo of repos) {
139
+ const result = await update(local, repo, current, await fetchRepo(api, repo))
140
+ current = result.manifest
141
+ writeJson({
142
+ repo,
143
+ revision: result.revision,
144
+ updated: result.updated,
145
+ conflicts: result.conflicts,
146
+ ...(result.conflicts.length > 0 ? { hint: CONFLICT_HINT } : {}),
147
+ })
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Bring the local copy of `repo` to `remote`, three ways against the manifest: a file only the
153
+ * repo changed is updated, a file only this copy changed is kept, and a file both changed gets
154
+ * the repo's version next to it as `.theirs`. The manifest then records `remote`, so the next save
155
+ * sends what this copy holds against the latest revision.
156
+ */
157
+ async function update(
158
+ local: LocalMemory,
159
+ repo: AgentMemoryRepo,
160
+ manifest: AgentMemoryManifest,
161
+ remote: RemoteRepo
162
+ ): Promise<{
163
+ readonly manifest: AgentMemoryManifest
164
+ readonly revision: number
165
+ readonly updated: readonly string[]
166
+ readonly conflicts: readonly MemoryConflict[]
167
+ }> {
168
+ const base = manifest.repos[repo]?.files ?? {}
169
+ const files = await local.readRepo(repo)
170
+ const updated: string[] = []
171
+ const conflicts: MemoryConflict[] = []
172
+
173
+ for (const path of [
174
+ ...new Set([...files.keys(), ...remote.files.keys(), ...Object.keys(base)]),
175
+ ].sort()) {
176
+ if (path.endsWith(THEIRS)) continue
177
+ const mine = files.get(path)
178
+ const theirs = remote.files.get(path)
179
+ const mineHash = mine === undefined ? undefined : agentMemoryFileHash(mine)
180
+ const theirsHash = theirs === undefined ? undefined : agentMemoryFileHash(theirs)
181
+ if (theirsHash === base[path] || theirsHash === mineHash) continue
182
+ if (mineHash === base[path]) {
183
+ await local.write(repo, path, theirs)
184
+ updated.push(path)
185
+ } else if (theirs === undefined) {
186
+ conflicts.push({ path, theirs: null })
187
+ } else {
188
+ await local.write(repo, `${path}${THEIRS}`, theirs)
189
+ conflicts.push({ path, theirs: `${path}${THEIRS}` })
190
+ }
191
+ }
192
+ if (
193
+ !remote.files.has(AGENT_MEMORY_ENTRY) &&
194
+ !(await local.readRepo(repo)).has(AGENT_MEMORY_ENTRY)
195
+ ) {
196
+ await local.write(repo, AGENT_MEMORY_ENTRY, AGENT_MEMORY_STARTER)
197
+ }
198
+
199
+ const next: AgentMemoryManifest = {
200
+ repos: {
201
+ ...manifest.repos,
202
+ [repo]: {
203
+ revision: remote.revision,
204
+ files: Object.fromEntries(
205
+ [...remote.files].map(([path, contents]) => [path, agentMemoryFileHash(contents)])
206
+ ),
207
+ },
208
+ },
209
+ }
210
+ await local.writeManifest(next)
211
+ return { manifest: next, revision: remote.revision, updated, conflicts }
212
+ }
213
+
214
+ async function fetchRepo(
215
+ api: ReturnType<typeof createInstanceApiClient>,
216
+ repo: AgentMemoryRepo
217
+ ): Promise<RemoteRepo> {
218
+ const body = asRecord(await api.get(`/api/agent-memory/${repo}`))
219
+ const files = Array.isArray(body.files) ? body.files.map(asRecord) : null
220
+ if (
221
+ typeof body.revision !== "number" ||
222
+ !files?.every((file) => typeof file.path === "string" && typeof file.contents === "string")
223
+ ) {
224
+ throw new CliError(
225
+ { code: "invalid_api_response", message: "The Sixb API returned an invalid memory repo." },
226
+ EXIT_API
227
+ )
228
+ }
229
+ return {
230
+ revision: body.revision,
231
+ files: new Map(files.map((file) => [String(file.path), String(file.contents)])),
232
+ }
233
+ }
234
+
235
+ interface LocalMemory {
236
+ readManifest(): Promise<AgentMemoryManifest>
237
+ writeManifest(manifest: AgentMemoryManifest): Promise<void>
238
+ /** Every file of the local copy, by path from the repo root. */
239
+ readRepo(repo: AgentMemoryRepo): Promise<Map<string, string>>
240
+ /** `undefined` deletes the file. */
241
+ write(repo: AgentMemoryRepo, path: string, contents: string | undefined): Promise<void>
242
+ }
243
+
244
+ function localMemory(): LocalMemory {
245
+ const root = process.env.SIXB_MEMORY_DIR
246
+ const contextDir = process.env.SIXB_CONTEXT_DIR
247
+ if (!root || !contextDir) {
248
+ fail("Agent memory is not available in this run.", "runtime_unavailable")
249
+ }
250
+ const manifestPath = join(contextDir, ...AGENT_MEMORY_MANIFEST.split("/"))
251
+ const fileOf = (repo: AgentMemoryRepo, path: string) => join(root, repo, ...path.split("/"))
252
+
253
+ return {
254
+ async readManifest() {
255
+ let text: string
256
+ try {
257
+ text = await readFile(manifestPath, "utf8")
258
+ } catch (error) {
259
+ if (isFileError(error, "ENOENT")) return { repos: {} }
260
+ throw error
261
+ }
262
+ try {
263
+ return parseManifest(JSON.parse(text))
264
+ } catch {
265
+ fail(`Memory manifest '${manifestPath}' is not valid.`, "invalid_json")
266
+ }
267
+ },
268
+
269
+ async writeManifest(manifest) {
270
+ await mkdir(dirname(manifestPath), { recursive: true })
271
+ await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
272
+ },
273
+
274
+ async readRepo(repo) {
275
+ const files = new Map<string, string>()
276
+ const walk = async (directory: string, prefix: string): Promise<void> => {
277
+ let entries: string[]
278
+ try {
279
+ entries = await readdir(directory)
280
+ } catch (error) {
281
+ if (isFileError(error, "ENOENT")) return
282
+ throw error
283
+ }
284
+ for (const name of entries) {
285
+ const path = prefix ? `${prefix}/${name}` : name
286
+ const stats = await lstat(join(directory, name))
287
+ if (stats.isDirectory()) await walk(join(directory, name), path)
288
+ else if (stats.isFile()) files.set(path, await readFile(join(directory, name), "utf8"))
289
+ else fail(`Memory file '${repo}/${path}' is not a regular file.`)
290
+ }
291
+ }
292
+ await walk(join(root, repo), "")
293
+ return files
294
+ },
295
+
296
+ async write(repo, path, contents) {
297
+ const file = fileOf(repo, path)
298
+ if (contents === undefined) {
299
+ await rm(file, { force: true })
300
+ return
301
+ }
302
+ await mkdir(dirname(file), { recursive: true })
303
+ await writeFile(file, contents)
304
+ },
305
+ }
306
+ }
307
+
308
+ function parseSaveArgs(args: readonly string[]): { repo: AgentMemoryRepo; message: string } {
309
+ const positionals: string[] = []
310
+ let message: string | undefined
311
+ for (let index = 0; index < args.length; index++) {
312
+ const argument = args[index] ?? ""
313
+ if (argument === "-m" || argument === "--message") {
314
+ if (message !== undefined) fail(`${argument} may only be provided once.`)
315
+ message = args[++index]
316
+ if (!message) fail(`${argument} requires a value.`)
317
+ } else if (argument.startsWith("-")) {
318
+ fail(`Unknown memory save option '${argument}'.`)
319
+ } else {
320
+ positionals.push(argument)
321
+ }
322
+ }
323
+ if (positionals.length !== 1) fail("Invalid arguments for 'memory save'.")
324
+ if (message === undefined || message.trim().length === 0) {
325
+ fail("memory save requires -m <what you learned>.")
326
+ }
327
+ return { repo: parseRepo(positionals[0] ?? ""), message }
328
+ }
329
+
330
+ function parseRepo(value: string): AgentMemoryRepo {
331
+ if (value === "user" || value === "project") return value
332
+ fail(`Unknown memory repo '${value}'. Use 'user' or 'project'.`)
333
+ }
334
+
335
+ function installedRepos(manifest: AgentMemoryManifest): AgentMemoryRepo[] {
336
+ return (["user", "project"] as const).filter((repo) => manifest.repos[repo] !== undefined)
337
+ }
338
+
339
+ function parseManifest(value: unknown): AgentMemoryManifest {
340
+ const repos = asRecord(asRecord(value).repos)
341
+ const parsed: Partial<Record<AgentMemoryRepo, AgentMemoryManifestRepo>> = {}
342
+ for (const repo of ["user", "project"] as const) {
343
+ if (repos[repo] === undefined) continue
344
+ const entry = asRecord(repos[repo])
345
+ const files = asRecord(entry.files)
346
+ if (
347
+ typeof entry.revision !== "number" ||
348
+ !Object.values(files).every((digest) => typeof digest === "string")
349
+ ) {
350
+ throw new Error("invalid memory manifest")
351
+ }
352
+ parsed[repo] = {
353
+ revision: entry.revision,
354
+ files: files as Record<string, string>,
355
+ }
356
+ }
357
+ return { repos: parsed }
358
+ }
@@ -5,17 +5,17 @@ import {
5
5
  DEFAULT_TELEMETRY_ORDER,
6
6
  } from "../policies"
7
7
 
8
- const SANDBOX_MAIN_HELP = `Sixb agent CLI
8
+ const SANDBOX_MAIN_HELP = `Sixb CLI
9
9
 
10
10
  Usage:
11
11
  sixb <command> [options]
12
12
 
13
- The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.
13
+ The CLI uses the run's SIXB_API_BASE_URL, which adds its credentials. Do not configure authentication or another origin.
14
14
  API commands emit JSON. Help, version, and examples emit text. Downloads write to --output and
15
15
  emit a JSON receipt.
16
16
 
17
17
  Discovery:
18
- sixb doctor Check the sandbox and API gateway
18
+ sixb status Check the sandbox and its API access
19
19
  sixb context Print the current run context
20
20
  sixb project show Show project metadata
21
21
  sixb ontology list Compact visible type and relationship catalog
@@ -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
@@ -48,28 +50,29 @@ Actions and workflows:
48
50
  sixb workflows list|get|start ...
49
51
  sixb workflow-runs list|get ...
50
52
 
53
+ Data:
54
+ sixb datasets list|get|versions|rows ...
55
+ sixb syncs list|get ... Syncs you may run, each with its latest run
56
+ sixb sync-runs list ...
57
+ sixb pipelines list|get ... Pipelines you may run, each with its latest run
58
+ sixb pipeline-runs list|get ...
59
+
51
60
  Files:
52
61
  sixb files upload|download ...
53
62
 
63
+ Members:
64
+ sixb users search <text> Find members by name or email
65
+ sixb users list|get ...
66
+
67
+ A user parameter or property is {"type":"user","id":"<user-id>"}. Find the id with
68
+ \`sixb users search <name>\`.
69
+
54
70
  Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
55
71
  For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`
56
72
 
57
- const LOCAL_MAIN_HELP = SANDBOX_MAIN_HELP.replace("Sixb agent CLI", "Sixb instance CLI")
58
- .replace(
59
- "The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.",
60
- "The CLI uses the selected local profile and its API credentials."
61
- )
62
- .replace(
63
- " sixb doctor Check the sandbox and API gateway\n sixb context Print the current run context\n",
64
- ""
65
- )
66
- .replace(
67
- "\nWhen you do not know a type's properties or links, read its file under $SIXB_ONTOLOGY_DIR. Use\n`sixb ontology get` for a type that has no file there.\n",
68
- ""
69
- )
70
-
71
- export function renderInstanceHelp(mode: "local" | "sandbox"): string {
72
- return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP
73
+ /** The sandbox CLI's help, which the agent's prompt includes. People's comes from @sixb/cli. */
74
+ export function renderInstanceHelp(): string {
75
+ return SANDBOX_MAIN_HELP
73
76
  }
74
77
 
75
78
  const SEARCH_DETAILS = `Without --type, search matches primary-id prefixes and full-text fields across visible types.
@@ -87,10 +90,19 @@ profiles.
87
90
 
88
91
  ${SEARCH_DETAILS}`
89
92
 
93
+ export const GET_BY_HELP = `Usage:
94
+ sixb objects get-by <object-type> <property> <value> [--json]
95
+
96
+ Prints the object of exactly <object-type> holding <value> for a property marked unique in its
97
+ ontology doc, or null when no visible object holds it. Values match exactly, case included.
98
+ The value is a string; with --json it is parsed as JSON, for an integer (42) or a user
99
+ reference ({"type":"user","id":"usr_1"}).`
100
+
90
101
  export const OBJECTS_HELP = `Usage:
91
102
  sixb objects inspect <object-type> <primary-id> [options]
92
103
  sixb objects list [options]
93
104
  sixb objects get <object-type> <primary-id>...
105
+ sixb objects get-by <object-type> <property> <value> [--json]
94
106
  sixb objects search <text> [--type <object-type> [--vector-profile <name>]] [--limit <n>]
95
107
  sixb objects query --file <path|-> [--include-total|--no-total]
96
108
  sixb objects query --example <name>
@@ -158,7 +170,7 @@ Query nodes compose through input:
158
170
  limit {"kind":"limit","input":<query>,"limit":${CLI_LIMITS.list.default}}
159
171
  page {"kind":"page","input":<query>,"pageSize":${CLI_LIMITS.list.default},"pageToken":"from nextPageToken"}
160
172
  project {"kind":"project","input":<query>,"properties":["name","status"]}
161
- expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}]}
173
+ expand {"kind":"expand","input":<query>,"expansions":[{"linkId":"link","direction":"outgoing","limit":5}],"users":["assignee"]}
162
174
  vector {"kind":"vector","input":<start|filter>,"vector":"text","profile":"name","k":10}
163
175
  rerank {"kind":"rerank","input":<vector>,"model":{"provider":"provider","modelId":"model"}}
164
176
 
@@ -169,11 +181,16 @@ Predicates use op, not kind:
169
181
  {"op":"in","propertyId":"status","values":["open","blocked"]}
170
182
  {"op":"exists","propertyId":"dueDate","value":true}
171
183
  {"op":"contains","propertyId":"tags","value":"urgent"}
184
+ {"op":"eq","propertyId":"assignee","value":{"type":"currentUser"}} the caller, on a user reference: neq, in, contains too
172
185
 
173
186
  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
187
+ direction, an expansion's limit, and users. Sort by relevance after text or vector with
175
188
  {"kind":"relevance"}. Wrap the outermost node in project to return only the properties you need.
176
189
 
190
+ An expand's users, or an expansion's, lists user-reference properties: each row then carries the
191
+ members' profiles under users (id, email, displayName, avatarUrl, status). Without access to the
192
+ member directory, users is empty.
193
+
177
194
  Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
178
195
  is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
179
196
 
@@ -185,7 +202,7 @@ Vector profile names are the keys of the type's search.vectors. Vector input is
185
202
  optionally under filters; k bounds the ranked matches and results carry a score.`
186
203
 
187
204
  export const GROUP_HELP = {
188
- doctor: "Usage: sixb doctor",
205
+ status: "Usage: sixb status",
189
206
  context: "Usage: sixb context",
190
207
  project: "Usage: sixb project show",
191
208
  ontology: `Usage:
@@ -209,7 +226,8 @@ History options:
209
226
  \`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
210
227
  contains that parameter object; use - to read standard input. \`actions request\` runs the Action
211
228
  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\`.`,
229
+ takes longer keeps running, and the error names the run to read with \`action-runs get\`. A request
230
+ that waits for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
213
231
  "action-runs": `Usage:
214
232
  sixb action-runs list [options]
215
233
  sixb action-runs get <run-id>
@@ -226,13 +244,54 @@ List options:
226
244
  files: `Usage:
227
245
  sixb files upload <local-path> [--logical-path <path>]
228
246
  sixb files download object <type> <id> --path <json-pointer> --output <local-path>
229
- sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path>`,
247
+ sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path>
248
+ sixb files download url <content-url> --output <local-path>`,
230
249
  workflows: `Usage:
231
250
  sixb workflows list
232
251
  sixb workflows get <workflow-id>
233
- sixb workflows start <workflow-id> [--file <path|->]
252
+ sixb workflows start <workflow-id> [--file <path|->] [--run-id <id>]
253
+
254
+ The JSON file contains the workflow input object. Use - to read standard input. A start that waits
255
+ for the user's approval prints \`"status": "awaiting_approval"\` and exits with code 4.`,
256
+ users: `Usage:
257
+ sixb users list [options]
258
+ sixb users search <text> [options]
259
+ sixb users get <user-id|email>
260
+
261
+ Options:
262
+ --status active|suspended|all Defaults to active
263
+ --limit <1-${CLI_LIMITS.users.maximum}> Defaults to ${CLI_LIMITS.users.default}
264
+ --offset <n> Non-negative; defaults to 0
234
265
 
235
- The JSON file contains the workflow input object. Use - to read standard input.`,
266
+ \`users search\` matches part of a display name or email, ignoring case; \`users get\` takes an
267
+ exact email, ignoring case, and finds suspended members too. Each member is
268
+ {"id","email","displayName","avatarUrl","status"}; a user parameter takes
269
+ {"type":"user","id":"<id>"}. Reading members requires can.view(project.users).`,
270
+ models: `Usage:
271
+ sixb models list [--kind language|image|speech|transcription]
272
+ sixb models get <model> [--kind <kind>]
273
+ sixb models text "<prompt>"|--file <path|-> [--model <tier|model>] [--instructions <text>]
274
+ [--max-output-tokens <n>] [--reasoning <level>]
275
+ sixb models image "<prompt>" --output <path> [--model <model>] [--n <n>]
276
+ [--size <size>] [--aspect-ratio <ratio>] [--quality <q>] [--style <s>]
277
+ [--seed <n>] [--image <file>]...
278
+ sixb models speech "<text>" --output <path> [--model <model>] [--voice <voice>]
279
+ [--format mp3|wav|ogg|aac|flac] [--instructions <text>]
280
+ [--speed <n>] [--language <code>]
281
+ sixb models transcribe <file> [--model <model>]
282
+ sixb models calls files <call-id> [--output <path>]
283
+
284
+ <model> is a model id, or provider:model-id when two providers share one; without --model, the
285
+ kind's default answers. \`list\` shows each model's kind, default, description and price; \`get\`
286
+ adds the sizes, voices and other options a request can use. Prices are in USD.
287
+
288
+ \`text\` also takes a tier (light, standard or heavy) for --model, and reads a long prompt from
289
+ --file: a path, or - for standard input.
290
+
291
+ --output is a file, numbered when a call returns several, or a directory ending in /. The
292
+ extension follows the file's type, and the result lists the paths written. --image uploads a
293
+ reference image; repeat it for several. \`calls files\` lists a past call's files, or downloads
294
+ them again.`,
236
295
  "workflow-runs": `Usage:
237
296
  sixb workflow-runs list [options]
238
297
  sixb workflow-runs get <run-id>
@@ -244,6 +303,56 @@ List options:
244
303
  --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
245
304
  --offset <n> Non-negative; defaults to 0
246
305
  --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
306
+ datasets: `Usage:
307
+ sixb datasets list
308
+ sixb datasets get <dataset-id>
309
+ sixb datasets versions <dataset-id> [--limit <1-${CLI_LIMITS.datasetVersions.maximum}>]
310
+ sixb datasets rows <dataset-id> [options]
311
+
312
+ Rows options:
313
+ --version <version-id> Defaults to the latest version
314
+ --columns <a,b,...> Defaults to every column
315
+ --limit <1-${CLI_LIMITS.datasetRows.maximum}> Defaults to ${CLI_LIMITS.datasetRows.default}
316
+ --offset <n> Non-negative; defaults to 0
317
+
318
+ \`list\` and \`get\` show a dataset's schema, its latest version, and the syncs and pipelines you
319
+ may run that read or write it. \`versions\` lists the newest first. \`rows\` leaves out the
320
+ columns your clearances do not cover and names them under redactions.`,
321
+ syncs: `Usage:
322
+ sixb syncs list
323
+ sixb syncs get <sync-id>
324
+
325
+ Lists the syncs you may run, each with its latest run. \`sixb sync-runs list\` shows the history.`,
326
+ "sync-runs": `Usage:
327
+ sixb sync-runs list [options]
328
+
329
+ List options:
330
+ --sync <sync-id>
331
+ --dataset <dataset-id>
332
+ --status queued|running|succeeded|failed|cancelled
333
+ --started-after|--started-before <RFC3339>
334
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
335
+ --offset <n> Non-negative; defaults to 0
336
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
337
+ pipelines: `Usage:
338
+ sixb pipelines list
339
+ sixb pipelines get <pipeline-id>
340
+
341
+ Lists the pipelines you may run, each with its latest run. \`sixb pipeline-runs\` shows the
342
+ history and each run's steps.`,
343
+ "pipeline-runs": `Usage:
344
+ sixb pipeline-runs list [options]
345
+ sixb pipeline-runs get <run-id>
346
+
347
+ List options:
348
+ --pipeline <pipeline-id>
349
+ --status queued|running|succeeded|failed|cancelled
350
+ --started-after|--started-before <RFC3339>
351
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
352
+ --offset <n> Non-negative; defaults to 0
353
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}
354
+
355
+ \`get\` includes the run's steps.`,
247
356
  } as const
248
357
 
249
358
  export const QUERY_EXAMPLES: Readonly<Record<string, string>> = {
@@ -254,10 +363,12 @@ export const QUERY_EXAMPLES: Readonly<Record<string, string>> = {
254
363
  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
364
  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
365
  exists: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"exists","propertyId":"accountManager","value":false}},"limit":${CLI_LIMITS.list.default}}`,
366
+ 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
367
  project: `{"kind":"project","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Customer"},"limit":${CLI_LIMITS.list.default}},"properties":["name","status"]}`,
258
368
  incoming:
259
369
  '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
260
370
  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}}]}`,
371
+ users: `{"kind":"expand","input":{"kind":"limit","input":{"kind":"start","objectTypeId":"Task"},"limit":${CLI_LIMITS.list.default}},"expansions":[],"users":["assignee"]}`,
261
372
  sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
262
373
  page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
263
374
  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}}`,