@ornncompute/cli 0.2.24 → 1.0.34
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/README.md +351 -426
- package/package.json +12 -6
- package/src/admin-capacity-reservations.mjs +305 -0
- package/src/admin-command-handlers.mjs +57 -0
- package/src/admin-enrollment.mjs +681 -0
- package/src/admin-mutations.mjs +187 -0
- package/src/admin-node-diagnostics.mjs +412 -0
- package/src/admin-node-lifecycle.mjs +905 -0
- package/src/admin-shell.mjs +456 -0
- package/src/api-client.mjs +81 -28
- package/src/atomic-output.mjs +112 -0
- package/src/auth-store.mjs +182 -11
- package/src/capability-catalog.mjs +7 -0
- package/src/catalog-shell.mjs +1363 -0
- package/src/cli.mjs +123 -10266
- package/src/connect-mode-handlers.mjs +170 -0
- package/src/device-auth.mjs +179 -62
- package/src/evidence-commands.mjs +1354 -0
- package/src/external-bucket-commands.mjs +1374 -0
- package/src/gpu-catalog.mjs +21 -0
- package/src/identity-file.mjs +28 -0
- package/src/interaction-runtime.mjs +310 -0
- package/src/kubernetes-commands.mjs +2235 -0
- package/src/launch-mode-handlers.mjs +119 -0
- package/src/machine-reference.mjs +285 -0
- package/src/mount-commands.mjs +1148 -0
- package/src/session-commands.mjs +565 -0
- package/src/slurm-commands.mjs +274 -0
- package/src/startup.mjs +34 -11
- package/src/storage-commands.mjs +1164 -0
- package/src/update.mjs +172 -24
- package/src/vm-commands.mjs +4308 -0
- package/src/vpn-secret-output.mjs +150 -0
- package/src/wait-runtime.mjs +231 -0
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +96 -0
- package/vendor/capabilities/capabilities/admin-enrollment.js +321 -0
- package/vendor/capabilities/capabilities/admin-node-diagnostics.js +88 -0
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +235 -0
- package/vendor/capabilities/capabilities/admin.js +89 -0
- package/vendor/capabilities/capabilities/cli.js +156 -0
- package/vendor/capabilities/capabilities/clusters.js +150 -1
- package/vendor/capabilities/capabilities/commerce-market.js +330 -0
- package/vendor/capabilities/capabilities/compute.js +522 -0
- package/vendor/capabilities/capabilities/evidence.js +145 -0
- package/vendor/capabilities/capabilities/external-buckets.js +191 -0
- package/vendor/capabilities/capabilities/fleet.js +77 -25
- package/vendor/capabilities/capabilities/identity.js +114 -3
- package/vendor/capabilities/capabilities/listings.js +141 -144
- package/vendor/capabilities/capabilities/market-shared.js +44 -0
- package/vendor/capabilities/capabilities/markets.js +449 -0
- package/vendor/capabilities/capabilities/mounts.js +176 -0
- package/vendor/capabilities/capabilities/networks.js +1264 -0
- package/vendor/capabilities/capabilities/observability.js +38 -8
- package/vendor/capabilities/capabilities/tokens.js +32 -17
- package/vendor/capabilities/capabilities/volumes.js +178 -0
- package/vendor/capabilities/capability.js +21 -14
- package/vendor/capabilities/cli-catalog.js +67 -0
- package/vendor/capabilities/cli-migrations.js +261 -0
- package/vendor/capabilities/cli.js +7 -0
- package/vendor/capabilities/software-catalog.js +36 -0
- package/vendor/capabilities/spec-catalog.js +28 -5
- package/vendor/capabilities/supplier-directory.js +20 -0
- package/src/catalog-dispatch.mjs +0 -605
- package/src/color.mjs +0 -71
- package/vendor/capabilities/capabilities/clusters.d.ts +0 -2
- package/vendor/capabilities/capabilities/fleet.d.ts +0 -8
- package/vendor/capabilities/capabilities/identity.d.ts +0 -3
- package/vendor/capabilities/capabilities/listings.d.ts +0 -51
- package/vendor/capabilities/capabilities/observability.d.ts +0 -5
- package/vendor/capabilities/capabilities/staff-access.d.ts +0 -29
- package/vendor/capabilities/capabilities/staff-access.js +0 -278
- package/vendor/capabilities/capabilities/staff-commerce.d.ts +0 -160
- package/vendor/capabilities/capabilities/staff-commerce.js +0 -689
- package/vendor/capabilities/capabilities/staff-fleet.d.ts +0 -140
- package/vendor/capabilities/capabilities/staff-fleet.js +0 -538
- package/vendor/capabilities/capabilities/staff-gtm.d.ts +0 -142
- package/vendor/capabilities/capabilities/staff-gtm.js +0 -521
- package/vendor/capabilities/capabilities/staff-shared.d.ts +0 -47
- package/vendor/capabilities/capabilities/staff-shared.js +0 -98
- package/vendor/capabilities/capabilities/tokens.d.ts +0 -10
- package/vendor/capabilities/capabilities/users.d.ts +0 -2
- package/vendor/capabilities/capability.d.ts +0 -93
- package/vendor/capabilities/catalog.d.ts +0 -3
- package/vendor/capabilities/catalog.js +0 -26
- package/vendor/capabilities/index.d.ts +0 -16
- package/vendor/capabilities/index.js +0 -16
- package/vendor/capabilities/names.d.ts +0 -6
- package/vendor/capabilities/role.d.ts +0 -6
- package/vendor/capabilities/role.js +0 -17
- package/vendor/capabilities/spec-catalog.d.ts +0 -78
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineCapability, } from "../capability.js";
|
|
3
|
+
const pageInput = z
|
|
4
|
+
.object({
|
|
5
|
+
$positionals: z.array(z.string()).optional(),
|
|
6
|
+
cursor: z.string().optional(),
|
|
7
|
+
limit: z.union([z.string(), z.number()]).optional(),
|
|
8
|
+
})
|
|
9
|
+
.strict();
|
|
10
|
+
const showInput = z
|
|
11
|
+
.object({
|
|
12
|
+
$positionals: z.array(z.string()).optional(),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
const verificationInput = z
|
|
16
|
+
.object({
|
|
17
|
+
$positionals: z.array(z.string()).optional(),
|
|
18
|
+
idempotencyKey: z.string().optional(),
|
|
19
|
+
})
|
|
20
|
+
.strict();
|
|
21
|
+
const disconnectInput = z
|
|
22
|
+
.object({
|
|
23
|
+
$positionals: z.array(z.string()).optional(),
|
|
24
|
+
idempotencyKey: z.string().optional(),
|
|
25
|
+
yes: z.boolean().optional(),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
const connectInput = z
|
|
29
|
+
.object({
|
|
30
|
+
$positionals: z.array(z.string()).optional(),
|
|
31
|
+
accessKeyId: z.string().optional(),
|
|
32
|
+
accountId: z.string().optional(),
|
|
33
|
+
bucket: z.string().optional(),
|
|
34
|
+
endpointUrl: z.string().optional(),
|
|
35
|
+
idempotencyKey: z.string().optional(),
|
|
36
|
+
name: z.string().optional(),
|
|
37
|
+
prefix: z.string().optional(),
|
|
38
|
+
readOnly: z.boolean().optional(),
|
|
39
|
+
readWrite: z.boolean().optional(),
|
|
40
|
+
region: z.string().optional(),
|
|
41
|
+
secretAccessKeyFile: z.string().optional(),
|
|
42
|
+
url: z.string().optional(),
|
|
43
|
+
yes: z.boolean().optional(),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
const credentialInput = z
|
|
47
|
+
.object({
|
|
48
|
+
$positionals: z.array(z.string()).optional(),
|
|
49
|
+
accessKeyId: z.string().optional(),
|
|
50
|
+
idempotencyKey: z.string().optional(),
|
|
51
|
+
secretAccessKeyFile: z.string().optional(),
|
|
52
|
+
yes: z.boolean().optional(),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
55
|
+
const importInput = z
|
|
56
|
+
.object({
|
|
57
|
+
$positionals: z.array(z.string()).optional(),
|
|
58
|
+
idempotencyKey: z.string().optional(),
|
|
59
|
+
noWait: z.boolean().optional(),
|
|
60
|
+
reservation: z.string().optional(),
|
|
61
|
+
timeout: z.string().optional(),
|
|
62
|
+
wait: z.boolean().optional(),
|
|
63
|
+
yes: z.boolean().optional(),
|
|
64
|
+
})
|
|
65
|
+
.strict();
|
|
66
|
+
function externalBucketExecutor(context) {
|
|
67
|
+
const interaction = context.cli?.interaction;
|
|
68
|
+
if (!interaction?.externalBuckets) {
|
|
69
|
+
throw new Error("This capability must execute through the Ornn CLI external-bucket runtime.");
|
|
70
|
+
}
|
|
71
|
+
return interaction.externalBuckets.bind(interaction);
|
|
72
|
+
}
|
|
73
|
+
function externalBucketCapability({ acceptedReferences = [], automation, command, description, human, id, input, mutation, outcome, related = [], resumableStates = [], review, }) {
|
|
74
|
+
return defineCapability({
|
|
75
|
+
id,
|
|
76
|
+
domain: "storage",
|
|
77
|
+
roles: ["user"],
|
|
78
|
+
description,
|
|
79
|
+
mutation,
|
|
80
|
+
input,
|
|
81
|
+
cli: {
|
|
82
|
+
command,
|
|
83
|
+
vnext: {
|
|
84
|
+
arguments: "semantic",
|
|
85
|
+
audience: "customer",
|
|
86
|
+
category: "resource",
|
|
87
|
+
outcome,
|
|
88
|
+
human,
|
|
89
|
+
automation,
|
|
90
|
+
acceptedReferences,
|
|
91
|
+
related,
|
|
92
|
+
resumableStates,
|
|
93
|
+
...(review ? { review } : {}),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
mcp: false,
|
|
97
|
+
slack: false,
|
|
98
|
+
execute: async (context, commandInput) => externalBucketExecutor(context)(id, commandInput),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const gcsImportGuidance = "GCS requires bucket-level read access for Ornn's Storage Transfer service agent, granted with gcloud. CLI 1.x GCS imports are currently blocked by unavailable source verification. Setup and version-specific steps: https://docs.ornn.com/cli#gcs-imports-and-storage-transfer-service";
|
|
102
|
+
export const externalBucketCapabilities = [
|
|
103
|
+
externalBucketCapability({
|
|
104
|
+
id: "storage.external-buckets.list",
|
|
105
|
+
command: ["external-buckets", "list"],
|
|
106
|
+
description: "List connected GCS, S3, and R2 buckets in one bounded page.",
|
|
107
|
+
mutation: "read",
|
|
108
|
+
input: pageInput,
|
|
109
|
+
outcome: "List external bucket connections without exposing stored credentials.",
|
|
110
|
+
human: "ornn external-buckets list [--limit <1-100>] [--cursor <cursor>]",
|
|
111
|
+
automation: "ornn external-buckets list --limit 50 --format json",
|
|
112
|
+
related: ["external-buckets show", "external-buckets connect"],
|
|
113
|
+
}),
|
|
114
|
+
externalBucketCapability({
|
|
115
|
+
id: "storage.external-buckets.show",
|
|
116
|
+
command: ["external-buckets", "show"],
|
|
117
|
+
description: "Inspect one connected external bucket by canonical ID or exact unique name.",
|
|
118
|
+
mutation: "read",
|
|
119
|
+
input: showInput,
|
|
120
|
+
outcome: "Inspect one credential-safe external bucket connection.",
|
|
121
|
+
human: "ornn external-buckets show <external-bucket>",
|
|
122
|
+
automation: "ornn external-buckets show <external-bucket-id> --format json",
|
|
123
|
+
acceptedReferences: ["canonical external bucket ID", "exact unique external bucket name"],
|
|
124
|
+
related: ["external-buckets verify", "external-buckets import"],
|
|
125
|
+
}),
|
|
126
|
+
externalBucketCapability({
|
|
127
|
+
id: "storage.external-buckets.connect",
|
|
128
|
+
command: ["external-buckets", "connect"],
|
|
129
|
+
description: "Connect a typed GCS, AWS S3, or Cloudflare R2 source without verifying it implicitly.",
|
|
130
|
+
mutation: "preview-confirm",
|
|
131
|
+
input: connectInput,
|
|
132
|
+
outcome: "Create or rediscover one external bucket connection with verification kept separate.",
|
|
133
|
+
human: "ornn external-buckets connect <gcs|s3|r2> --bucket <bucket> [--name <name>] [--prefix <prefix>] [--region <region>] [--account-id <id>|--endpoint-url <url>] [--access-key-id <id> --secret-access-key-file <path>] [--read-only|--read-write]",
|
|
134
|
+
automation: "ornn external-buckets connect <gcs|s3|r2> --bucket <bucket> <provider-inputs> --yes --idempotency-key <key> --format json",
|
|
135
|
+
review: `Review the exact source, access mode, and credential presence before connecting it. ${gcsImportGuidance}`,
|
|
136
|
+
related: ["external-buckets verify", "external-buckets import"],
|
|
137
|
+
}),
|
|
138
|
+
externalBucketCapability({
|
|
139
|
+
id: "storage.external-buckets.update-credentials",
|
|
140
|
+
command: ["external-buckets", "update-credentials"],
|
|
141
|
+
description: "Replace scoped S3 or R2 credentials without returning credential bytes.",
|
|
142
|
+
mutation: "preview-confirm",
|
|
143
|
+
input: credentialInput,
|
|
144
|
+
outcome: "Update one connection's credentials and require a separate verification.",
|
|
145
|
+
human: "ornn external-buckets update-credentials <external-bucket> --access-key-id <id> --secret-access-key-file <path>",
|
|
146
|
+
automation: "ornn external-buckets update-credentials <external-bucket-id> --access-key-id <id> --secret-access-key-file <path> --yes --idempotency-key <key> --format json",
|
|
147
|
+
acceptedReferences: ["canonical external bucket ID", "exact unique external bucket name"],
|
|
148
|
+
review: "Credential changes invalidate the prior verification and require renewed review.",
|
|
149
|
+
related: ["external-buckets verify"],
|
|
150
|
+
}),
|
|
151
|
+
externalBucketCapability({
|
|
152
|
+
id: "storage.external-buckets.verify",
|
|
153
|
+
command: ["external-buckets", "verify"],
|
|
154
|
+
description: "Verify current S3 or R2 source access as a separate operation.",
|
|
155
|
+
mutation: "preview-confirm",
|
|
156
|
+
input: verificationInput,
|
|
157
|
+
outcome: "Record current source-access verification without exposing credential material.",
|
|
158
|
+
human: "ornn external-buckets verify <external-bucket>",
|
|
159
|
+
automation: "ornn external-buckets verify <external-bucket-id> --idempotency-key <key> --format json",
|
|
160
|
+
acceptedReferences: ["canonical external bucket ID", "exact unique external bucket name"],
|
|
161
|
+
review: gcsImportGuidance,
|
|
162
|
+
related: ["external-buckets import", "external-buckets update-credentials"],
|
|
163
|
+
}),
|
|
164
|
+
externalBucketCapability({
|
|
165
|
+
id: "storage.external-buckets.import",
|
|
166
|
+
command: ["external-buckets", "import"],
|
|
167
|
+
description: "Import a verified GCS or S3 source into its isolated managed working volume.",
|
|
168
|
+
mutation: "preview-confirm",
|
|
169
|
+
input: importInput,
|
|
170
|
+
outcome: "Create or replay exactly one managed working copy with source writeback disabled.",
|
|
171
|
+
human: "ornn external-buckets import <gcs|s3|r2> <external-bucket> --reservation <canonical-reservation-id> [--no-wait] [--timeout <duration>]",
|
|
172
|
+
automation: "ornn external-buckets import <gcs|s3> <external-bucket-id> --reservation <canonical-reservation-id> --yes --idempotency-key <key> [--wait --timeout <duration>] --format json",
|
|
173
|
+
acceptedReferences: ["canonical external bucket ID", "exact unique external bucket name"],
|
|
174
|
+
review: `Review the isolated destination and explicit no-writeback guarantee before importing. ${gcsImportGuidance}`,
|
|
175
|
+
resumableStates: ["pending", "working", "partial", "failed", "interrupted"],
|
|
176
|
+
related: ["external-buckets show", "storage status", "volumes files list"],
|
|
177
|
+
}),
|
|
178
|
+
externalBucketCapability({
|
|
179
|
+
id: "storage.external-buckets.disconnect",
|
|
180
|
+
command: ["external-buckets", "disconnect"],
|
|
181
|
+
description: "Disconnect an external bucket record after its working volume is detached.",
|
|
182
|
+
mutation: "preview-confirm",
|
|
183
|
+
input: disconnectInput,
|
|
184
|
+
outcome: "Remove one external source connection without writing to its source bucket.",
|
|
185
|
+
human: "ornn external-buckets disconnect <external-bucket>",
|
|
186
|
+
automation: "ornn external-buckets disconnect <external-bucket-id> --yes --idempotency-key <key> --format json",
|
|
187
|
+
acceptedReferences: ["canonical external bucket ID", "exact unique external bucket name"],
|
|
188
|
+
review: "Disconnecting reduces access to the source and is blocked while mounts remain.",
|
|
189
|
+
related: ["storage detach", "external-buckets show"],
|
|
190
|
+
}),
|
|
191
|
+
];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { listSupplierOrganizations, listSupplierOrganizationPage } from "../supplier-directory.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { defineCapability, preview } from "../capability.js";
|
|
3
4
|
function queryPath(path, params) {
|
|
@@ -9,18 +10,27 @@ function queryPath(path, params) {
|
|
|
9
10
|
return parts.length ? `${path}?${parts.join("&")}` : path;
|
|
10
11
|
}
|
|
11
12
|
const emptyInput = z.object({});
|
|
13
|
+
const suppliersInput = z.object({
|
|
14
|
+
q: z.string().optional(),
|
|
15
|
+
offset: z.number().int().min(0).optional(),
|
|
16
|
+
limit: z.number().int().min(1).max(1000).optional(),
|
|
17
|
+
});
|
|
12
18
|
const facilitiesInput = z.object({
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
organization_id: z.string().optional(),
|
|
20
|
+
organizationId: z.string().optional(),
|
|
21
|
+
cursor: z.string().optional(),
|
|
22
|
+
limit: z.number().int().min(1).max(500).optional(),
|
|
15
23
|
});
|
|
16
24
|
const nodesInput = z.object({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
operator: z.string().optional(),
|
|
25
|
+
organization_id: z.string().optional(),
|
|
26
|
+
organizationId: z.string().optional(),
|
|
20
27
|
facility_id: z.string().optional(),
|
|
21
28
|
facilityId: z.string().optional(),
|
|
22
29
|
status: z.string().optional(),
|
|
23
30
|
email: z.string().optional(),
|
|
31
|
+
cursor: z.string().optional(),
|
|
32
|
+
max: z.number().int().positive().max(5000).optional(),
|
|
33
|
+
limit: z.number().int().positive().max(5000).optional(),
|
|
24
34
|
});
|
|
25
35
|
const nodeLifecycleInput = z.object({
|
|
26
36
|
node_id: z.string().min(1),
|
|
@@ -39,29 +49,53 @@ const nodeExecInput = z.object({
|
|
|
39
49
|
function nodeIdOf(input) {
|
|
40
50
|
return (input.node_id || input.nodeId || "").trim();
|
|
41
51
|
}
|
|
42
|
-
export const
|
|
43
|
-
id: "
|
|
52
|
+
export const listSuppliersCapability = defineCapability({
|
|
53
|
+
id: "supplier.list",
|
|
44
54
|
domain: "fleet",
|
|
45
|
-
roles: ["
|
|
46
|
-
description: "List
|
|
55
|
+
roles: ["admin"],
|
|
56
|
+
description: "List supplier organizations (id, name). Resolve supplier names to organization_id with this before nodes.list or facilities.list.",
|
|
47
57
|
mutation: "read",
|
|
48
|
-
http: { method: "GET", path: "/
|
|
49
|
-
slack: {
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
http: { method: "GET", path: "/v1/organizations" },
|
|
59
|
+
slack: {
|
|
60
|
+
parameters: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
q: { type: "string", description: "Search supplier names." },
|
|
64
|
+
offset: { type: "integer", description: "Use next_offset to fetch another page." },
|
|
65
|
+
limit: { type: "integer", description: "Page size (Slack maximum 10)." },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
input: suppliersInput,
|
|
70
|
+
execute: async (ctx, input) => {
|
|
71
|
+
const parsed = suppliersInput.parse(input);
|
|
72
|
+
const paged = parsed.limit !== undefined || parsed.offset !== undefined || parsed.q !== undefined;
|
|
73
|
+
const limit = parsed.limit ?? 25;
|
|
74
|
+
const offset = parsed.offset ?? 0;
|
|
75
|
+
const rows = paged
|
|
76
|
+
? await listSupplierOrganizationPage(ctx.fetch, { ...parsed, limit, offset })
|
|
77
|
+
: await listSupplierOrganizations(ctx.fetch);
|
|
78
|
+
return {
|
|
79
|
+
suppliers: rows.map((org) => ({ id: org.id, name: org.name })),
|
|
80
|
+
...(paged && rows.length === limit ? { next_offset: offset + rows.length } : {}),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
52
83
|
});
|
|
53
84
|
export const listFacilitiesCapability = defineCapability({
|
|
54
85
|
id: "facilities.list",
|
|
55
86
|
domain: "fleet",
|
|
56
|
-
roles: ["
|
|
57
|
-
description: "List facilities (id, display name, region,
|
|
87
|
+
roles: ["admin"],
|
|
88
|
+
description: "List facilities (id, display name, region, supplier organization id). Optional organization_id filter. Resolve facility names with this before nodes.list.",
|
|
58
89
|
mutation: "read",
|
|
59
90
|
http: { method: "GET", path: "/provisioning/facilities" },
|
|
60
91
|
slack: {
|
|
61
92
|
parameters: {
|
|
62
93
|
type: "object",
|
|
63
94
|
properties: {
|
|
64
|
-
|
|
95
|
+
organization_id: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Optional supplier organization UUID from supplier.list.",
|
|
98
|
+
},
|
|
65
99
|
},
|
|
66
100
|
},
|
|
67
101
|
},
|
|
@@ -69,49 +103,67 @@ export const listFacilitiesCapability = defineCapability({
|
|
|
69
103
|
execute: async (ctx, input) => {
|
|
70
104
|
const parsed = facilitiesInput.parse(input);
|
|
71
105
|
return ctx.fetch(queryPath("/provisioning/facilities", {
|
|
72
|
-
|
|
106
|
+
organizationId: parsed.organization_id ?? parsed.organizationId,
|
|
107
|
+
cursor: parsed.cursor,
|
|
108
|
+
limit: parsed.limit === undefined ? undefined : String(parsed.limit),
|
|
73
109
|
}), { method: "GET" });
|
|
74
110
|
},
|
|
75
111
|
});
|
|
76
112
|
export const listNodesCapability = defineCapability({
|
|
77
113
|
id: "nodes.list",
|
|
78
114
|
domain: "fleet",
|
|
79
|
-
roles: ["user", "
|
|
80
|
-
description: "List nodes. Users see their own reserved machines.
|
|
115
|
+
roles: ["user", "admin"],
|
|
116
|
+
description: "List nodes. Users see their own reserved machines. Admins see enrolled, ghost, and off-grid nodes. Call with no filters for the whole fleet. Optional email lists nodes for that user's organizations. Optional organization_id (supplier), facility_id, and status (ghost, off-grid, bare-metal) narrow the same list. Paginate large fleets with cursor + max: pass the response's next_cursor back as cursor to fetch the next page; next_cursor is omitted once the page is not full. Never invent nodes. Never print private keys.",
|
|
81
117
|
mutation: "read",
|
|
82
118
|
http: { method: "GET", path: "/nodes" },
|
|
83
119
|
slack: {
|
|
84
120
|
parameters: {
|
|
85
121
|
type: "object",
|
|
86
122
|
properties: {
|
|
87
|
-
|
|
123
|
+
organization_id: {
|
|
88
124
|
type: "string",
|
|
89
|
-
description: "Optional
|
|
125
|
+
description: "Optional supplier organization UUID from supplier.list.",
|
|
90
126
|
},
|
|
91
127
|
facility_id: {
|
|
92
128
|
type: "string",
|
|
93
129
|
description: "Optional commerce facility UUID from facilities.list.",
|
|
94
130
|
},
|
|
95
|
-
email: {
|
|
131
|
+
email: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Optional. List nodes for this user's organizations.",
|
|
134
|
+
},
|
|
96
135
|
status: {
|
|
97
136
|
type: "string",
|
|
98
137
|
enum: ["ghost", "off-grid", "bare-metal"],
|
|
99
138
|
description: "Optional orchestrator status filter. Default is all statuses.",
|
|
100
139
|
},
|
|
140
|
+
cursor: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Optional. Pass the previous response's next_cursor to fetch the next page.",
|
|
143
|
+
},
|
|
144
|
+
max: {
|
|
145
|
+
type: "integer",
|
|
146
|
+
description: "Optional page size (1-5000). Orchestrator default is 500.",
|
|
147
|
+
},
|
|
101
148
|
},
|
|
102
149
|
},
|
|
103
150
|
},
|
|
104
151
|
input: nodesInput,
|
|
105
152
|
execute: async (ctx, input) => {
|
|
106
153
|
const parsed = nodesInput.parse(input);
|
|
154
|
+
const pagination = {
|
|
155
|
+
cursor: parsed.cursor,
|
|
156
|
+
limit: parsed.max != null || parsed.limit != null ? String(parsed.max ?? parsed.limit) : undefined,
|
|
157
|
+
};
|
|
107
158
|
if (ctx.role === "user") {
|
|
108
|
-
return ctx.fetch("/nodes", { method: "GET" });
|
|
159
|
+
return ctx.fetch(queryPath("/nodes", pagination), { method: "GET" });
|
|
109
160
|
}
|
|
110
161
|
return ctx.fetch(queryPath("/nodes", {
|
|
111
|
-
|
|
162
|
+
organization_id: parsed.organization_id ?? parsed.organizationId,
|
|
112
163
|
facility_id: parsed.facility_id ?? parsed.facilityId,
|
|
113
164
|
status: parsed.status,
|
|
114
165
|
email: parsed.email,
|
|
166
|
+
...pagination,
|
|
115
167
|
}), { method: "GET" });
|
|
116
168
|
},
|
|
117
169
|
});
|
|
@@ -243,7 +295,7 @@ export const nodeTerminateCapability = defineCapability({
|
|
|
243
295
|
},
|
|
244
296
|
});
|
|
245
297
|
export const fleetCapabilities = [
|
|
246
|
-
|
|
298
|
+
listSuppliersCapability,
|
|
247
299
|
listFacilitiesCapability,
|
|
248
300
|
listNodesCapability,
|
|
249
301
|
nodeOffGridCapability,
|
|
@@ -1,10 +1,103 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineCapability } from "../capability.js";
|
|
3
3
|
const emptyInput = z.object({});
|
|
4
|
+
const contextInput = z.object({ organization: z.string().min(1).optional() });
|
|
5
|
+
const loginInput = z.object({
|
|
6
|
+
authBase: z.string().min(1).optional(),
|
|
7
|
+
noBrowser: z.boolean().optional(),
|
|
8
|
+
organization: z.string().min(1).optional(),
|
|
9
|
+
pollInterval: z.number().positive().optional(),
|
|
10
|
+
timeout: z.number().positive().optional(),
|
|
11
|
+
});
|
|
12
|
+
function executeSession(context, command, input) {
|
|
13
|
+
if (!context.cli?.session) {
|
|
14
|
+
throw new Error("This capability must execute through the Ornn CLI session runtime.");
|
|
15
|
+
}
|
|
16
|
+
return context.cli.session(command, input);
|
|
17
|
+
}
|
|
18
|
+
export const loginCapability = defineCapability({
|
|
19
|
+
id: "identity.login",
|
|
20
|
+
domain: "identity",
|
|
21
|
+
roles: ["user"],
|
|
22
|
+
description: "Authenticate the CLI with Ornn device authorization.",
|
|
23
|
+
mutation: "local",
|
|
24
|
+
http: { method: "POST", path: "/v1/cli/device/start" },
|
|
25
|
+
input: loginInput,
|
|
26
|
+
cli: {
|
|
27
|
+
command: ["login"],
|
|
28
|
+
vnext: {
|
|
29
|
+
audience: "customer",
|
|
30
|
+
category: "session",
|
|
31
|
+
arguments: "semantic",
|
|
32
|
+
outcome: "Authenticate in a browser and return to the terminal.",
|
|
33
|
+
human: "ornn login [--organization <exact-name>] [--no-browser] [--timeout <seconds>]",
|
|
34
|
+
automation: "ornn login --format json",
|
|
35
|
+
resumableStates: ["browser authorization"],
|
|
36
|
+
related: ["context", "logout"],
|
|
37
|
+
order: 1,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
mcp: false,
|
|
41
|
+
slack: false,
|
|
42
|
+
execute: async (context, input) => executeSession(context, "login", input),
|
|
43
|
+
});
|
|
44
|
+
export const contextCapability = defineCapability({
|
|
45
|
+
id: "identity.context",
|
|
46
|
+
domain: "identity",
|
|
47
|
+
roles: ["user"],
|
|
48
|
+
description: "Show and select the current server-verified organization context.",
|
|
49
|
+
mutation: "read",
|
|
50
|
+
http: { method: "GET", path: "/v1/cli/session" },
|
|
51
|
+
input: contextInput,
|
|
52
|
+
cli: {
|
|
53
|
+
command: ["context"],
|
|
54
|
+
vnext: {
|
|
55
|
+
audience: "customer",
|
|
56
|
+
category: "session",
|
|
57
|
+
arguments: "semantic",
|
|
58
|
+
outcome: "Show the current user, selected organization, approval, and available organizations.",
|
|
59
|
+
human: "ornn context [--organization <exact-name-or-id>]",
|
|
60
|
+
automation: "ornn context --organization <canonical-id> --format json",
|
|
61
|
+
acceptedReferences: [
|
|
62
|
+
"exact organization name (human)",
|
|
63
|
+
"canonical organization ID (automation)",
|
|
64
|
+
],
|
|
65
|
+
related: ["login", "logout"],
|
|
66
|
+
order: 2,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
mcp: false,
|
|
70
|
+
slack: false,
|
|
71
|
+
execute: async (context, input) => executeSession(context, "context", input),
|
|
72
|
+
});
|
|
73
|
+
export const logoutCapability = defineCapability({
|
|
74
|
+
id: "identity.logout",
|
|
75
|
+
domain: "identity",
|
|
76
|
+
roles: ["user"],
|
|
77
|
+
description: "Revoke the remote CLI session and clear its local bearer.",
|
|
78
|
+
mutation: "local",
|
|
79
|
+
http: { method: "DELETE", path: "/v1/cli/session" },
|
|
80
|
+
input: emptyInput,
|
|
81
|
+
cli: {
|
|
82
|
+
command: ["logout"],
|
|
83
|
+
vnext: {
|
|
84
|
+
audience: "customer",
|
|
85
|
+
category: "session",
|
|
86
|
+
outcome: "End the remote session and always remove the local bearer.",
|
|
87
|
+
human: "ornn logout",
|
|
88
|
+
automation: "ornn logout --format json",
|
|
89
|
+
related: ["login", "context"],
|
|
90
|
+
order: 3,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
mcp: false,
|
|
94
|
+
slack: false,
|
|
95
|
+
execute: async (context, input) => executeSession(context, "logout", input),
|
|
96
|
+
});
|
|
4
97
|
export const whoamiCapability = defineCapability({
|
|
5
98
|
id: "identity.whoami",
|
|
6
99
|
domain: "identity",
|
|
7
|
-
roles: ["user", "
|
|
100
|
+
roles: ["user", "admin"],
|
|
8
101
|
description: "Show the signed-in Ornn user and organization.",
|
|
9
102
|
mutation: "read",
|
|
10
103
|
http: { method: "GET", path: "/v1/cli/session" },
|
|
@@ -14,11 +107,29 @@ export const whoamiCapability = defineCapability({
|
|
|
14
107
|
export const statusCapability = defineCapability({
|
|
15
108
|
id: "identity.status",
|
|
16
109
|
domain: "identity",
|
|
17
|
-
roles: ["user", "
|
|
110
|
+
roles: ["user", "admin"],
|
|
18
111
|
description: "Show Ornn platform status.",
|
|
19
112
|
mutation: "read",
|
|
20
113
|
http: { method: "GET", path: "/v1/cli/status" },
|
|
21
114
|
input: emptyInput,
|
|
22
115
|
execute: async (ctx) => ctx.fetch("/v1/cli/status", { method: "GET" }),
|
|
116
|
+
cli: {
|
|
117
|
+
command: ["status"],
|
|
118
|
+
vnext: {
|
|
119
|
+
audience: "customer",
|
|
120
|
+
category: "platform",
|
|
121
|
+
outcome: "Check current Ornn platform availability.",
|
|
122
|
+
human: "ornn status",
|
|
123
|
+
automation: "ornn status --format json",
|
|
124
|
+
related: ["version", "resources"],
|
|
125
|
+
order: 10,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
23
128
|
});
|
|
24
|
-
export const identityCapabilities = [
|
|
129
|
+
export const identityCapabilities = [
|
|
130
|
+
whoamiCapability,
|
|
131
|
+
loginCapability,
|
|
132
|
+
contextCapability,
|
|
133
|
+
logoutCapability,
|
|
134
|
+
statusCapability,
|
|
135
|
+
];
|