@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
@@ -0,0 +1,130 @@
1
+ import { fail } from "./output"
2
+
3
+ type OptionKinds = Readonly<Record<string, "string" | "boolean">>
4
+
5
+ /** Parse a command's options once, before reading files or calling the API. */
6
+ export function parseCommandArgs<const T extends OptionKinds>(
7
+ args: readonly string[],
8
+ kinds: T,
9
+ command: string,
10
+ positionalCount: number | readonly [number, number] = 0
11
+ ): {
12
+ positionals: string[]
13
+ options: { [K in keyof T]?: T[K] extends "boolean" ? boolean : string }
14
+ } {
15
+ const positionals: string[] = []
16
+ const options: Record<string, string | boolean> = {}
17
+ let positionalOnly = false
18
+ for (let index = 0; index < args.length; index++) {
19
+ const argument = args[index] ?? ""
20
+ if (!positionalOnly && argument === "--") {
21
+ positionalOnly = true
22
+ continue
23
+ }
24
+ if (positionalOnly || !argument.startsWith("-") || argument === "-") {
25
+ positionals.push(argument)
26
+ continue
27
+ }
28
+ const equals = argument.indexOf("=")
29
+ const flag = equals < 0 ? argument : argument.slice(0, equals)
30
+ if (!Object.hasOwn(kinds, flag)) fail(`Unknown ${command} option '${flag}'.`)
31
+ if (Object.hasOwn(options, flag)) fail(`${flag} may only be provided once.`)
32
+ if (kinds[flag] === "boolean") {
33
+ if (equals >= 0) fail(`${flag} does not accept a value.`)
34
+ options[flag] = true
35
+ } else {
36
+ const value = equals < 0 ? args[++index] : argument.slice(equals + 1)
37
+ if (!value || (equals < 0 && value.startsWith("--"))) {
38
+ fail(`${flag} requires a value.`)
39
+ }
40
+ options[flag] = value
41
+ }
42
+ }
43
+ const [minimum, maximum] =
44
+ typeof positionalCount === "number" ? [positionalCount, positionalCount] : positionalCount
45
+ if (positionals.length < minimum || positionals.length > maximum) {
46
+ fail(`Invalid arguments for '${command}'.`)
47
+ }
48
+ return {
49
+ positionals,
50
+ // Every assigned option was checked against its declared kind above.
51
+ options: options as { [K in keyof T]?: T[K] extends "boolean" ? boolean : string },
52
+ }
53
+ }
54
+
55
+ export function requestsHelp(args: readonly string[]): boolean {
56
+ const separator = args.indexOf("--")
57
+ const beforeSeparator = separator < 0 ? args : args.slice(0, separator)
58
+ return isHelp(args[0]) || beforeSeparator.includes("--help") || beforeSeparator.includes("-h")
59
+ }
60
+
61
+ /** Argument validation shared by every instance command mode. */
62
+
63
+ const RFC3339_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/
64
+
65
+ export function isHelp(value: string | undefined): boolean {
66
+ return value === "-h" || value === "--help" || value === "help"
67
+ }
68
+
69
+ export function requireExact(args: readonly string[], count: number, message: string): void {
70
+ if (args.length !== count) fail(message)
71
+ }
72
+
73
+ export function integerInRange(
74
+ flag: string,
75
+ value: string,
76
+ minimum: number,
77
+ maximum: number
78
+ ): number {
79
+ if (!/^[0-9]+$/.test(value)) {
80
+ fail(`${flag} must be an integer from ${minimum} through ${maximum}.`)
81
+ }
82
+ const parsed = Number(value)
83
+ if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
84
+ fail(`${flag} must be an integer from ${minimum} through ${maximum}.`)
85
+ }
86
+ return parsed
87
+ }
88
+
89
+ export function nonNegativeInteger(flag: string, value: string): number {
90
+ if (!/^[0-9]+$/.test(value)) {
91
+ fail(`${flag} must be a non-negative integer.`)
92
+ }
93
+ const parsed = Number(value)
94
+ if (!Number.isSafeInteger(parsed)) {
95
+ fail(`${flag} must be a non-negative safe integer.`)
96
+ }
97
+ return parsed
98
+ }
99
+
100
+ export function rfc3339Value(flag: string, value: string): string {
101
+ if (!RFC3339_TIMESTAMP.test(value) || Number.isNaN(Date.parse(value))) {
102
+ fail(`${flag} must be an RFC 3339 timestamp.`)
103
+ }
104
+ return value
105
+ }
106
+
107
+ export function requireOrderedRange(
108
+ afterFlag: string,
109
+ after: string | undefined,
110
+ beforeFlag: string,
111
+ before: string | undefined
112
+ ): void {
113
+ if (after && before && Date.parse(after) > Date.parse(before)) {
114
+ fail(`${afterFlag} must be before or equal to ${beforeFlag}.`)
115
+ }
116
+ }
117
+
118
+ export function enumValue<const T extends readonly string[]>(
119
+ flag: string,
120
+ value: string,
121
+ allowed: T
122
+ ): T[number] {
123
+ if (!allowed.includes(value)) fail(`${flag} must be ${formatAlternatives(allowed)}.`)
124
+ return value as T[number]
125
+ }
126
+
127
+ function formatAlternatives(values: readonly string[]): string {
128
+ if (values.length < 2) return values[0] ?? "a supported value"
129
+ return `${values.slice(0, -1).join(", ")}, or ${values.at(-1)}`
130
+ }
@@ -0,0 +1,94 @@
1
+ import type { ApiClient } from "../api-client"
2
+ import { isHelp, parseCommandArgs, requestsHelp } from "../arguments"
3
+ import { CliError, EXIT_API, fail, writeJson, writeText } from "../output"
4
+ import { GROUP_HELP } from "./metadata"
5
+ import { asRecord, parseQueryOptions, readJson } from "./shared"
6
+
7
+ export async function actions(api: ApiClient, args: readonly string[]): Promise<void> {
8
+ const [sub, ...rest] = args
9
+ if (!sub || isHelp(sub) || requestsHelp(rest)) return writeText(GROUP_HELP.actions)
10
+ if (sub === "get") {
11
+ const { positionals } = parseCommandArgs(rest, {}, "actions get", 1)
12
+ return writeJson(await api.get(`/api/actions/${encodeURIComponent(positionals[0] ?? "")}`))
13
+ }
14
+ if (sub === "list") {
15
+ const options = parseQueryOptions(rest, { "--type": "objectTypeId" }, "actions list")
16
+ const response = await api.get("/api/actions")
17
+ if (!options.objectTypeId) return writeJson(response)
18
+ if (!Array.isArray(response)) fail("The actions API returned an invalid response.")
19
+ return writeJson(
20
+ response.filter((value) => asRecord(value).objectTypeId === options.objectTypeId)
21
+ )
22
+ }
23
+ if (sub === "request") {
24
+ const { positionals, options } = parseCommandArgs(
25
+ rest,
26
+ {
27
+ "--subject-type": "string",
28
+ "--subject-id": "string",
29
+ "--file": "string",
30
+ "--run-id": "string",
31
+ "--wait": "boolean",
32
+ },
33
+ "actions request",
34
+ 1
35
+ )
36
+ const actionId = positionals[0] ?? ""
37
+ const subjectType = options["--subject-type"]
38
+ const subjectId = options["--subject-id"]
39
+ const paramsSource = options["--file"]
40
+ const runId = options["--run-id"]
41
+ const wait = options["--wait"]
42
+ if (Boolean(subjectType) !== Boolean(subjectId)) {
43
+ fail("--subject-type and --subject-id must be provided together.")
44
+ }
45
+ const params = paramsSource ? await readJson(paramsSource) : {}
46
+ if (Array.isArray(params) || typeof params !== "object" || params === null) {
47
+ fail("Action params must be a JSON object.")
48
+ }
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
+ throw new CliError(
61
+ {
62
+ code: "invalid_api_response",
63
+ message: "The Action request response did not contain a run id.",
64
+ },
65
+ EXIT_API
66
+ )
67
+ }
68
+ return writeJson(await waitForActionRun(api, requestedRunId))
69
+ }
70
+ fail(`Unknown actions command '${sub}'.`)
71
+ }
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
+ }
@@ -0,0 +1,52 @@
1
+ import { access } from "node:fs/promises"
2
+ import type { ApiClient } from "../api-client"
3
+ import { isHelp, parseCommandArgs, requestsHelp, requireExact } from "../arguments"
4
+ import { fail, writeJson, writeText } from "../output"
5
+ import { GROUP_HELP } from "./metadata"
6
+
7
+ export async function files(api: ApiClient, args: readonly string[]): Promise<void> {
8
+ const [sub, ...rest] = args
9
+ if (!sub || isHelp(sub) || requestsHelp(rest)) return writeText(GROUP_HELP.files)
10
+ if (sub === "upload") {
11
+ const { positionals, options } = parseCommandArgs(
12
+ rest,
13
+ { "--logical-path": "string" },
14
+ "files upload",
15
+ 1
16
+ )
17
+ const source = positionals[0] ?? ""
18
+ try {
19
+ await access(source)
20
+ } catch {
21
+ fail(`Upload file '${source}' does not exist.`)
22
+ }
23
+ return writeJson(await api.upload("/api/files", source, options["--logical-path"]))
24
+ }
25
+ if (sub === "download") {
26
+ const { positionals, options } = parseCommandArgs(
27
+ rest,
28
+ { "--path": "string", "--output": "string" },
29
+ "files download",
30
+ [2, 3]
31
+ )
32
+ const context = positionals[0]
33
+ let route: string
34
+ if (context === "object") {
35
+ requireExact(positionals, 3, "files download object requires object type and primary id.")
36
+ const type = positionals[1] ?? ""
37
+ const id = positionals[2] ?? ""
38
+ route = `/api/objects/${encodeURIComponent(type)}/${encodeURIComponent(id)}/files/content`
39
+ } else if (context === "action-run" || context === "workflow-run") {
40
+ requireExact(positionals, 2, `files download ${context} requires exactly one run id.`)
41
+ const id = positionals[1] ?? ""
42
+ route = `/api/${context}s/${encodeURIComponent(id)}/files/content`
43
+ } else fail(`Unknown file download context '${context}'.`)
44
+ const path = options["--path"]
45
+ const output = options["--output"]
46
+ if (!path) fail("files download requires --path <json-pointer>.")
47
+ if (!output) fail("files download requires --output <local-path>.")
48
+ await api.download(route, output, { path })
49
+ return writeJson({ downloaded: true, output })
50
+ }
51
+ fail(`Unknown files command '${sub}'.`)
52
+ }
@@ -0,0 +1,57 @@
1
+ import type { ApiClient } from "../api-client"
2
+ import { fail } from "../output"
3
+ import { actions } from "./actions"
4
+ import { files } from "./files"
5
+ import { objects } from "./objects"
6
+ import { ontology } from "./ontology"
7
+ import { project } from "./project"
8
+ import { runs } from "./runs"
9
+ import { telemetry } from "./telemetry"
10
+ import { workflows } from "./workflows"
11
+
12
+ export const INSTANCE_COMMANDS = [
13
+ "project",
14
+ "ontology",
15
+ "objects",
16
+ "telemetry",
17
+ "actions",
18
+ "action-runs",
19
+ "files",
20
+ "workflows",
21
+ "workflow-runs",
22
+ ] as const
23
+
24
+ export type InstanceCommand = (typeof INSTANCE_COMMANDS)[number]
25
+
26
+ export function isInstanceCommand(command: string): command is InstanceCommand {
27
+ return (INSTANCE_COMMANDS as readonly string[]).includes(command)
28
+ }
29
+
30
+ export async function dispatch(
31
+ api: ApiClient,
32
+ command: string,
33
+ args: readonly string[]
34
+ ): Promise<void> {
35
+ switch (command) {
36
+ case "project":
37
+ return project(api, args)
38
+ case "ontology":
39
+ return ontology(api, args)
40
+ case "objects":
41
+ return objects(api, args)
42
+ case "telemetry":
43
+ return telemetry(api, args)
44
+ case "actions":
45
+ return actions(api, args)
46
+ case "action-runs":
47
+ return runs(api, "action", args)
48
+ case "files":
49
+ return files(api, args)
50
+ case "workflows":
51
+ return workflows(api, args)
52
+ case "workflow-runs":
53
+ return runs(api, "workflow", args)
54
+ default:
55
+ fail(`Unknown command '${command}'. Run 'sixb --help'.`)
56
+ }
57
+ }
@@ -0,0 +1,212 @@
1
+ import {
2
+ CLI_LIMITS,
3
+ DEFAULT_LIST_ORDER,
4
+ DEFAULT_OBJECT_ORDER_BY,
5
+ DEFAULT_TELEMETRY_ORDER,
6
+ } from "../policies"
7
+
8
+ const SANDBOX_MAIN_HELP = `Sixb agent CLI
9
+
10
+ Usage:
11
+ sixb <command> [options]
12
+
13
+ The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.
14
+ API commands emit JSON. Help, version, and examples emit text. Downloads write to --output and
15
+ emit a JSON receipt.
16
+
17
+ Discovery:
18
+ sixb doctor Check the sandbox and API gateway
19
+ sixb context Print the current run context
20
+ sixb project show Show project metadata
21
+ sixb ontology list Compact visible type and relationship catalog
22
+ sixb ontology get <type> Inspect one object type, properties, links, and actions
23
+
24
+ Objects:
25
+ sixb objects inspect <type> <id> Inspect an object and its related graph in one command
26
+ sixb objects list [options] Browse materialized objects
27
+ sixb objects get <type> <id>... Exact lookup through opaque object references
28
+ sixb objects search <text> Search visible objects
29
+ sixb objects query --file <file|-> Execute query IR from JSON
30
+ sixb objects count --file <file|-> Count a query without returning rows
31
+ sixb objects exists --file <file|-> Test whether a query has a match
32
+ sixb objects facets --file <file|-> Run a {query, facets} request
33
+ sixb objects links <type> <id> Read exact persisted links
34
+
35
+ Telemetry:
36
+ sixb telemetry latest <type> <id> <property>
37
+ sixb telemetry history <type> <id> <property> [options]
38
+ sixb telemetry query --file <file|->
39
+
40
+ Actions and workflows:
41
+ sixb actions list|get|request ...
42
+ sixb action-runs list|get ...
43
+ sixb workflows list|get|start ...
44
+ sixb workflow-runs list|get ...
45
+
46
+ Files:
47
+ sixb files upload|download ...
48
+
49
+ Run \`sixb <group> --help\` or \`sixb <group> <command> --help\` for exact arguments.
50
+ For query IR, run \`sixb objects query --help\` and \`sixb objects query --example list\`.`
51
+
52
+ const LOCAL_MAIN_HELP = SANDBOX_MAIN_HELP.replace("Sixb agent CLI", "Sixb instance CLI")
53
+ .replace(
54
+ "The CLI uses the run-scoped SIXB_API_BASE_URL. Do not configure authentication or another origin.",
55
+ "The CLI uses the selected local profile and its API credentials."
56
+ )
57
+ .replace(
58
+ " sixb doctor Check the sandbox and API gateway\n sixb context Print the current run context\n",
59
+ ""
60
+ )
61
+
62
+ export function renderInstanceHelp(mode: "local" | "sandbox"): string {
63
+ return mode === "sandbox" ? SANDBOX_MAIN_HELP : LOCAL_MAIN_HELP
64
+ }
65
+
66
+ export const OBJECTS_HELP = `Usage:
67
+ sixb objects inspect <object-type> <primary-id> [options]
68
+ sixb objects list [options]
69
+ sixb objects get <object-type> <primary-id>...
70
+ sixb objects search <text> [--limit <1-${CLI_LIMITS.search.maximum}>]
71
+ sixb objects query --file <path|-> [--include-total|--no-total]
72
+ sixb objects query --example <name>
73
+ sixb objects count --file <path|->
74
+ sixb objects exists --file <path|->
75
+ sixb objects facets --file <path|->
76
+ sixb objects facets --example
77
+ sixb objects links <object-type> <primary-id> [options]
78
+
79
+ List options:
80
+ --type <id> Exact ontology object type id
81
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
82
+ --offset <n> Non-negative; defaults to 0
83
+ --order-by createdAt|updatedAt|primaryId Defaults to ${DEFAULT_OBJECT_ORDER_BY}
84
+ --order asc|desc Defaults to ${DEFAULT_LIST_ORDER}
85
+ --id-prefix <value> Primary-id prefix
86
+ --id-suffix <value> Primary-id suffix
87
+ --created-after|--created-before <RFC3339>
88
+ --updated-after|--updated-before <RFC3339>
89
+
90
+ Links options:
91
+ --link <link-id>
92
+ --direction outgoing|incoming|both Defaults to both
93
+ --page-size <1-${CLI_LIMITS.linkPage.maximum}> Defaults to ${CLI_LIMITS.linkPage.default}
94
+ --page-token <token> Continue an edge page
95
+ --include-objects Include selected and current-page endpoint objects
96
+
97
+ Inspect options:
98
+ --depth <0-${CLI_LIMITS.inspect.depth.maximum}> Defaults to ${CLI_LIMITS.inspect.depth.default}; use 0 for only the object
99
+ --max-objects <1-${CLI_LIMITS.inspect.objects.maximum}> Defaults to ${CLI_LIMITS.inspect.objects.default}
100
+ --max-links <1-${CLI_LIMITS.inspect.links.maximum}> Defaults to ${CLI_LIMITS.inspect.links.default}
101
+ --full Include timestamps and encountered type definitions
102
+
103
+ Use \`objects get\` first when context provides an exact object reference. Use \`objects inspect\`
104
+ only when related objects are needed; it follows both relationship directions to depth 2 by
105
+ default and returns a bounded graph.
106
+ Inspect omits materialization timestamps and ontology definitions by default. Use \`--full\` when
107
+ storage timestamps, declared links, or available actions are needed.
108
+
109
+ Search returns at most ${CLI_LIMITS.search.maximum} matches and defaults to ${CLI_LIMITS.search.default}.
110
+
111
+ \`objects get\` uses a refs query without identity URL paths. Opaque ids containing :, /, #, ?, or
112
+ % are safe. Identifiers are case-sensitive.`
113
+
114
+ export const QUERY_HELP = `Usage:
115
+ sixb objects query --file <path|-> [--include-total|--no-total]
116
+ sixb objects query --example <exact|filter|incoming|expand|sort|page>
117
+ sixb objects query --example list
118
+
119
+ Input is a query node. A full {"query": ...} request is also accepted.
120
+
121
+ Query nodes compose through input:
122
+ start {"kind":"start","objectTypeId":"Type","includeSubtypes":false}
123
+ refs {"kind":"refs","refs":[{"objectTypeId":"Type","primaryId":"opaque:id"}]}
124
+ filter {"kind":"filter","input":<query>,"predicate":<predicate>}
125
+ text {"kind":"text","input":<query>,"query":"words","fields":["name"]}
126
+ traverse {"kind":"traverse","input":<query>,"linkId":"link","direction":"outgoing"}
127
+ vector, set, sort, limit, page, project, and expand are also supported.
128
+
129
+ Predicates use op, not kind:
130
+ and/or, not, {"op":"eq","propertyId":"status","value":"open"}, neq, lt, lte, gt, gte,
131
+ in, exists, and contains.
132
+
133
+ Traversal and expansion directions are outgoing or incoming. For incoming relationships, the link
134
+ is declared on the child/source type; add sourceObjectTypeId when needed to disambiguate it.
135
+
136
+ Run \`sixb ontology get <type>\` first; never guess property or link ids. Use refs for exact
137
+ identities. Put limits and pages inside the query tree.`
138
+
139
+ export const GROUP_HELP = {
140
+ doctor: "Usage: sixb doctor",
141
+ context: "Usage: sixb context",
142
+ project: "Usage: sixb project show",
143
+ ontology: `Usage:
144
+ sixb ontology list [--full]
145
+ sixb ontology get <object-type>`,
146
+ telemetry: `Usage:
147
+ sixb telemetry latest <object-type> <primary-id> <property-id>
148
+ sixb telemetry history <object-type> <primary-id> <property-id> [options]
149
+ sixb telemetry query --file <path|->
150
+
151
+ History options:
152
+ --from <RFC3339>
153
+ --to <RFC3339>
154
+ --limit <1-${CLI_LIMITS.telemetryHistory.maximum}> Defaults to ${CLI_LIMITS.telemetryHistory.default}
155
+ --order <asc|desc> Timestamp order; defaults to ${DEFAULT_TELEMETRY_ORDER}`,
156
+ actions: `Usage:
157
+ sixb actions list [--type <object-type>]
158
+ sixb actions get <action-id>
159
+ sixb actions request <action-id> [--subject-type <type> --subject-id <id>] [--file <path|->] [--run-id <id>] [--wait]
160
+
161
+ \`actions get\` includes inputSchema, the exact JSON shape accepted by the Action. The JSON file
162
+ contains that parameter object; use - to read standard input. --wait returns the terminal Action
163
+ run and waits at most 25 seconds.`,
164
+ "action-runs": `Usage:
165
+ sixb action-runs list [options]
166
+ sixb action-runs get <run-id>
167
+
168
+ List options:
169
+ --action <action-id>
170
+ --type <object-type>
171
+ --id <primary-id>
172
+ --status queued|running|succeeded|failed|cancelled
173
+ --started-after|--started-before <RFC3339>
174
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
175
+ --offset <n> Non-negative; defaults to 0
176
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
177
+ files: `Usage:
178
+ sixb files upload <local-path> [--logical-path <path>]
179
+ sixb files download object <type> <id> --path <json-pointer> --output <local-path>
180
+ sixb files download action-run|workflow-run <run-id> --path <json-pointer> --output <local-path>`,
181
+ workflows: `Usage:
182
+ sixb workflows list
183
+ sixb workflows get <workflow-id>
184
+ sixb workflows start <workflow-id> [--file <path|->]
185
+
186
+ The JSON file contains the workflow input object. Use - to read standard input.`,
187
+ "workflow-runs": `Usage:
188
+ sixb workflow-runs list [options]
189
+ sixb workflow-runs get <run-id>
190
+
191
+ List options:
192
+ --workflow <workflow-id>
193
+ --status queued|running|waiting|succeeded|failed|cancelled
194
+ --started-after|--started-before <RFC3339>
195
+ --limit <1-${CLI_LIMITS.list.maximum}> Defaults to ${CLI_LIMITS.list.default}
196
+ --offset <n> Non-negative; defaults to 0
197
+ --order <asc|desc> Started-time order; defaults to ${DEFAULT_LIST_ORDER}`,
198
+ } as const
199
+
200
+ export const QUERY_EXAMPLES: Readonly<Record<string, string>> = {
201
+ exact:
202
+ '{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]}',
203
+ filter: `{"kind":"limit","input":{"kind":"filter","input":{"kind":"start","objectTypeId":"Customer"},"predicate":{"op":"eq","propertyId":"status","value":"active"}},"limit":${CLI_LIMITS.list.default}}`,
204
+ incoming:
205
+ '{"kind":"traverse","input":{"kind":"refs","refs":[{"objectTypeId":"RepositoryIssue","primaryId":"github:issue:owner/repo#297"}]},"linkId":"issue","direction":"incoming","sourceObjectTypeId":"RepositoryComment"}',
206
+ 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}}]}`,
207
+ sort: `{"kind":"limit","input":{"kind":"sort","input":{"kind":"start","objectTypeId":"Customer"},"fields":[{"kind":"property","propertyId":"name","direction":"asc"}]},"limit":${CLI_LIMITS.list.default}}`,
208
+ page: `{"kind":"page","input":{"kind":"start","objectTypeId":"Customer"},"pageSize":${CLI_LIMITS.list.default}}`,
209
+ }
210
+
211
+ export const FACETS_EXAMPLE =
212
+ '{"query":{"kind":"start","objectTypeId":"WorkOrder"},"facets":[{"propertyId":"status","limit":10}]}'