@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.
- package/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/api-client.d.ts +23 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/arguments.d.ts +18 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/commands/actions.d.ts +3 -0
- package/dist/commands/actions.d.ts.map +1 -0
- package/dist/commands/files.d.ts +3 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/metadata.d.ts +18 -0
- package/dist/commands/metadata.d.ts.map +1 -0
- package/dist/commands/objects.d.ts +3 -0
- package/dist/commands/objects.d.ts.map +1 -0
- package/dist/commands/ontology.d.ts +3 -0
- package/dist/commands/ontology.d.ts.map +1 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/runs.d.ts +3 -0
- package/dist/commands/runs.d.ts.map +1 -0
- package/dist/commands/shared.d.ts +15 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/telemetry.d.ts +3 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/workflows.d.ts +3 -0
- package/dist/commands/workflows.d.ts.map +1 -0
- package/dist/graph.d.ts +10 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1400 -0
- package/dist/index.js.map +26 -0
- package/dist/output.d.ts +21 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/policies.d.ts +38 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/run.d.ts +20 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/package.json +53 -0
- package/src/api-client.ts +177 -0
- package/src/arguments.ts +130 -0
- package/src/commands/actions.ts +94 -0
- package/src/commands/files.ts +52 -0
- package/src/commands/index.ts +57 -0
- package/src/commands/metadata.ts +212 -0
- package/src/commands/objects.ts +291 -0
- package/src/commands/ontology.ts +50 -0
- package/src/commands/project.ts +13 -0
- package/src/commands/runs.ts +84 -0
- package/src/commands/shared.ts +97 -0
- package/src/commands/telemetry.ts +96 -0
- package/src/commands/workflows.ts +34 -0
- package/src/graph.ts +359 -0
- package/src/index.ts +24 -0
- package/src/output.ts +51 -0
- package/src/policies.ts +17 -0
- package/src/run.ts +46 -0
package/src/arguments.ts
ADDED
|
@@ -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}]}'
|