@ornncompute/cli 1.0.110 → 1.0.113
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 +5 -2
- package/package.json +1 -1
- package/src/registry-commands.mjs +6 -9
- package/vendor/api-client/authorization.js +363 -0
- package/vendor/capabilities/cli.js +53 -39
package/README.md
CHANGED
|
@@ -129,6 +129,9 @@ billing stay in the web app. Launch consumes already allocated
|
|
|
129
129
|
capacity; internal reservation lookups remain necessary for access and
|
|
130
130
|
ownership.
|
|
131
131
|
|
|
132
|
+
Where upfront card checkout is enabled, complete Fixed term purchases in the
|
|
133
|
+
console to review the full price and finish any bank verification.
|
|
134
|
+
|
|
132
135
|
Only admins can create primary listings or reservations through
|
|
133
136
|
`ornn admin capacity-offers create` and `ornn admin reservations create`.
|
|
134
137
|
They post to the role-aware `POST /commerce/v2/listings` and Markets
|
|
@@ -462,8 +465,8 @@ request conflicts, and an expired incomplete request requires a new key.
|
|
|
462
465
|
The upload replay contract requires the matching gateway/orchestrator release;
|
|
463
466
|
see [the service contract](../../services/orchestrator/README.md#file-upload-replay-and-usage).
|
|
464
467
|
|
|
465
|
-
Staff can use `ornn admin ssh <node-id>` for certificate access on
|
|
466
|
-
|
|
468
|
+
Staff can use `ornn admin ssh <node-id>` for certificate access on nodes that trust
|
|
469
|
+
the environment's SSH CA. It requires an interactive staff login, creates a temporary
|
|
467
470
|
local key, obtains a 15-minute certificate through the gateway and launches
|
|
468
471
|
OpenSSH with normal host-key verification. Temporary files are removed when
|
|
469
472
|
the command exits. Shared and per-node key access remain available.
|
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { listApiKeys, createApiKey, revokeApiKey } from "../vendor/api-client/authorization.js";
|
|
1
2
|
import { spawn as spawnProcessDefault } from "node:child_process";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
|
|
@@ -26,25 +27,21 @@ async function login({
|
|
|
26
27
|
field: "organization",
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
|
-
const keysPath = `/v1/organizations/${encodeURIComponent(organizationId)}/api-keys`;
|
|
30
30
|
const name = `${KEY_NAME_PREFIX}${hostname}`.slice(0, 100);
|
|
31
31
|
|
|
32
|
-
const existing = await fetch
|
|
32
|
+
const existing = await listApiKeys(fetch, organizationId);
|
|
33
33
|
for (const key of existing?.api_keys ?? []) {
|
|
34
34
|
if (key?.name === name && typeof key.id === "string") {
|
|
35
|
-
await fetch
|
|
35
|
+
await revokeApiKey(fetch, organizationId, key.id);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
const created = await fetch
|
|
39
|
-
method: "POST",
|
|
40
|
-
body: { name, access: "read_write" },
|
|
41
|
-
});
|
|
38
|
+
const created = await createApiKey(fetch, organizationId, { name, access: "read_write" });
|
|
42
39
|
|
|
43
40
|
let exit;
|
|
44
41
|
try {
|
|
45
42
|
exit = await dockerLogin(spawnProcess, registry.host, created.token);
|
|
46
43
|
} catch (error) {
|
|
47
|
-
await fetch
|
|
44
|
+
await revokeApiKey(fetch, organizationId, created.id);
|
|
48
45
|
const missing = error?.code === "ENOENT";
|
|
49
46
|
interaction.error(
|
|
50
47
|
missing
|
|
@@ -54,7 +51,7 @@ async function login({
|
|
|
54
51
|
);
|
|
55
52
|
}
|
|
56
53
|
if (exit.code !== 0) {
|
|
57
|
-
await fetch
|
|
54
|
+
await revokeApiKey(fetch, organizationId, created.id);
|
|
58
55
|
interaction.error(`docker login ${registry.host} failed; no key was kept.`, {
|
|
59
56
|
code: "docker_login_failed",
|
|
60
57
|
exitCode: 1,
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// ../../packages/api-client/dist/authorization/paths.js
|
|
2
|
+
var organizationPath = (id) => `/v1/organizations/${encodeURIComponent(id)}`;
|
|
3
|
+
var personPath = (id) => `/v1/people/${encodeURIComponent(id)}`;
|
|
4
|
+
function withQuery(path, values) {
|
|
5
|
+
const query = new URLSearchParams();
|
|
6
|
+
for (const [key, value] of Object.entries(values)) {
|
|
7
|
+
if (value !== void 0 && value !== null && value !== "")
|
|
8
|
+
query.set(key, Array.isArray(value) ? value.join(",") : String(value));
|
|
9
|
+
}
|
|
10
|
+
return query.size ? `${path}?${query}` : path;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ../../packages/api-client/dist/authorization/organizations.js
|
|
14
|
+
function listOrganizations(request, query = {}, signal) {
|
|
15
|
+
return request(withQuery("/v1/organizations", query), { method: "GET", signal });
|
|
16
|
+
}
|
|
17
|
+
async function listSupplierOrganizations(request, signal) {
|
|
18
|
+
const rows = [];
|
|
19
|
+
for (let offset = 0; offset < 5e4; offset += 1e3) {
|
|
20
|
+
const page = await listOrganizations(request, { role: "supplier", limit: 1e3, offset, sort: "created_at", direction: "asc" }, signal);
|
|
21
|
+
if (!Array.isArray(page))
|
|
22
|
+
throw new Error("Invalid supplier directory response.");
|
|
23
|
+
rows.push(...page);
|
|
24
|
+
if (page.length < 1e3)
|
|
25
|
+
return rows;
|
|
26
|
+
}
|
|
27
|
+
throw new Error("The supplier directory is too large to load completely.");
|
|
28
|
+
}
|
|
29
|
+
async function listOrganizationsByIds(request, ids, signal) {
|
|
30
|
+
const unique = [
|
|
31
|
+
...new Set(ids.map((id) => id?.trim()).filter((id) => Boolean(id)))
|
|
32
|
+
];
|
|
33
|
+
const chunks = [];
|
|
34
|
+
for (let start = 0; start < unique.length; start += 100)
|
|
35
|
+
chunks.push(unique.slice(start, start + 100));
|
|
36
|
+
const pages = await Promise.all(chunks.map(async (chunk, index) => {
|
|
37
|
+
if (index > 0)
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 40));
|
|
39
|
+
return listOrganizations(request, { ids: chunk, limit: chunk.length }, signal);
|
|
40
|
+
}));
|
|
41
|
+
return pages.flat();
|
|
42
|
+
}
|
|
43
|
+
function countOrganizations(request, query = {}, signal) {
|
|
44
|
+
return request(withQuery("/v1/staff/v1/organizations/count", query), {
|
|
45
|
+
method: "GET",
|
|
46
|
+
signal
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function getOrganization(request, id, signal) {
|
|
50
|
+
return request(organizationPath(id), { method: "GET", signal });
|
|
51
|
+
}
|
|
52
|
+
function listOrganizationsForUser(request, query, signal) {
|
|
53
|
+
return request(withQuery("/v1/organizations/by-user", query), {
|
|
54
|
+
method: "GET",
|
|
55
|
+
signal
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function createOrganization(request, body, signal) {
|
|
59
|
+
return request("/v1/organizations", { method: "POST", body, signal });
|
|
60
|
+
}
|
|
61
|
+
function updateOrganization(request, id, body) {
|
|
62
|
+
return request(organizationPath(id), { method: "PATCH", body });
|
|
63
|
+
}
|
|
64
|
+
function listOrganizationMembers(request, id, signal) {
|
|
65
|
+
return request(`${organizationPath(id)}/members`, { method: "GET", signal });
|
|
66
|
+
}
|
|
67
|
+
function getOrganizationMembership(request, id, email) {
|
|
68
|
+
return request(`${organizationPath(id)}/members/${encodeURIComponent(email)}`, {
|
|
69
|
+
method: "GET"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function removeOrganizationMember(request, id, email) {
|
|
73
|
+
return request(`${organizationPath(id)}/members/${encodeURIComponent(email)}`, {
|
|
74
|
+
method: "DELETE"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ../../packages/api-client/dist/authorization/account.js
|
|
79
|
+
function getUserProfile(request, id, signal) {
|
|
80
|
+
return request(`${personPath(id)}/profile`, {
|
|
81
|
+
method: "GET",
|
|
82
|
+
signal
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function updateUserProfile(request, id, body) {
|
|
86
|
+
return request(`${personPath(id)}/profile`, {
|
|
87
|
+
method: "PATCH",
|
|
88
|
+
body
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function getUserPreferences(request, id, signal) {
|
|
92
|
+
return request(`${personPath(id)}/preferences`, {
|
|
93
|
+
method: "GET",
|
|
94
|
+
signal
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function updateUserPreferences(request, id, body) {
|
|
98
|
+
return request(`${personPath(id)}/preferences`, {
|
|
99
|
+
method: "PATCH",
|
|
100
|
+
body
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function getNotificationPreferences(request, id, signal) {
|
|
104
|
+
return request(`${organizationPath(id)}/notification-preferences`, {
|
|
105
|
+
method: "GET",
|
|
106
|
+
signal
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function updateNotificationPreferences(request, id, body) {
|
|
110
|
+
return request(`${organizationPath(id)}/notification-preferences`, {
|
|
111
|
+
method: "PATCH",
|
|
112
|
+
body
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function listApiKeys(request, id, signal) {
|
|
116
|
+
return request(`${organizationPath(id)}/api-keys`, {
|
|
117
|
+
method: "GET",
|
|
118
|
+
signal
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function createApiKey(request, id, body) {
|
|
122
|
+
return request(`${organizationPath(id)}/api-keys`, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
body
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function revokeApiKey(request, id, keyId) {
|
|
128
|
+
return request(`${organizationPath(id)}/api-keys/${encodeURIComponent(keyId)}`, {
|
|
129
|
+
method: "DELETE"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function listAvailabilityAlerts(request, id, signal) {
|
|
133
|
+
return request(`${personPath(id)}/availability-alerts`, { method: "GET", signal });
|
|
134
|
+
}
|
|
135
|
+
function setAvailabilityAlert(request, id, alert) {
|
|
136
|
+
return request(`${personPath(id)}/availability-alerts/${encodeURIComponent(alert.gpu_slug)}/${encodeURIComponent(alert.sale_type)}`, { method: "PUT", body: { enabled: alert.enabled } });
|
|
137
|
+
}
|
|
138
|
+
var personAvatarPath = (id) => `${personPath(id)}/avatar`;
|
|
139
|
+
var organizationAvatarPath = (id) => `${organizationPath(id)}/avatar`;
|
|
140
|
+
function getPersonAvatar(fetcher, id) {
|
|
141
|
+
return fetcher(personAvatarPath(id), { method: "GET" });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ../../packages/api-client/dist/authorization/invites.js
|
|
145
|
+
var invitePath = (id) => `/v1/invites/${encodeURIComponent(id)}`;
|
|
146
|
+
function listInvites(request, organizationId, signal) {
|
|
147
|
+
return request(withQuery("/v1/invites", { organization_id: organizationId }), {
|
|
148
|
+
method: "GET",
|
|
149
|
+
signal
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function getInvite(request, id) {
|
|
153
|
+
return request(invitePath(id), { method: "GET" });
|
|
154
|
+
}
|
|
155
|
+
function createInvite(request, body) {
|
|
156
|
+
return request("/v1/invites", { method: "POST", body });
|
|
157
|
+
}
|
|
158
|
+
function revokeInvite(request, organizationId, id) {
|
|
159
|
+
return request(withQuery(invitePath(id), { organization_id: organizationId }), {
|
|
160
|
+
method: "DELETE"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function acceptInvite(request, id, body = {}) {
|
|
164
|
+
return request(`${invitePath(id)}/accept`, { method: "POST", body });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ../../packages/api-client/dist/authorization/ssh-keys.js
|
|
168
|
+
function listSshKeys(request, id, query = {}, signal) {
|
|
169
|
+
return request(withQuery(`${organizationPath(id)}/ssh-keys`, query), {
|
|
170
|
+
method: "GET",
|
|
171
|
+
signal
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async function listAllSshKeys(request, id, signal) {
|
|
175
|
+
const ssh_keys = [];
|
|
176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
177
|
+
let cursor = null;
|
|
178
|
+
let has_revoked = false;
|
|
179
|
+
do {
|
|
180
|
+
const page = await listSshKeys(request, id, { status: "all", limit: 500, cursor }, signal);
|
|
181
|
+
ssh_keys.push(...page.ssh_keys);
|
|
182
|
+
has_revoked ||= page.has_revoked === true;
|
|
183
|
+
cursor = page.next_cursor ?? null;
|
|
184
|
+
if (cursor && seen.has(cursor))
|
|
185
|
+
throw new Error("SSH key pagination did not advance.");
|
|
186
|
+
if (cursor)
|
|
187
|
+
seen.add(cursor);
|
|
188
|
+
} while (cursor);
|
|
189
|
+
return { ssh_keys, next_cursor: null, has_revoked };
|
|
190
|
+
}
|
|
191
|
+
function createSshKey(request, id, body, query = {}) {
|
|
192
|
+
return request(withQuery(`${organizationPath(id)}/ssh-keys`, query), {
|
|
193
|
+
method: "POST",
|
|
194
|
+
body
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function revokeSshKey(request, id, keyId, query = {}) {
|
|
198
|
+
return request(withQuery(`${organizationPath(id)}/ssh-keys/${encodeURIComponent(keyId)}`, query), { method: "DELETE" });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ../../packages/api-client/dist/authorization/staff.js
|
|
202
|
+
var staffOrganizationPath = (id) => `/v1/staff/organizations/${encodeURIComponent(id)}`;
|
|
203
|
+
function searchStaffPeople(request, query, signal) {
|
|
204
|
+
return request(withQuery("/v1/staff/people", query), { method: "GET", signal });
|
|
205
|
+
}
|
|
206
|
+
function searchStaffOrganizations(request, query, signal) {
|
|
207
|
+
return request(withQuery("/v1/staff/organizations", query), {
|
|
208
|
+
method: "GET",
|
|
209
|
+
signal
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function getStaffOrganization(request, id, signal) {
|
|
213
|
+
return request(staffOrganizationPath(id), {
|
|
214
|
+
method: "GET",
|
|
215
|
+
signal
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function createStaffOrganization(request, body) {
|
|
219
|
+
return request("/v1/staff/organizations", {
|
|
220
|
+
method: "POST",
|
|
221
|
+
body
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function listStaffOrganizationMembers(request, id, signal) {
|
|
225
|
+
return request(`${staffOrganizationPath(id)}/members`, { method: "GET", signal });
|
|
226
|
+
}
|
|
227
|
+
function addStaffOrganizationMember(request, id, body) {
|
|
228
|
+
return request(`${staffOrganizationPath(id)}/members`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
body
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function removeStaffOrganizationMember(request, id, email) {
|
|
234
|
+
return request(`${staffOrganizationPath(id)}/members/${encodeURIComponent(email)}`, {
|
|
235
|
+
method: "DELETE"
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function listStaffOrganizationInvites(request, id, signal) {
|
|
239
|
+
return request(`${staffOrganizationPath(id)}/invites`, { method: "GET", signal });
|
|
240
|
+
}
|
|
241
|
+
function revokeStaffOrganizationInvite(request, id, inviteId) {
|
|
242
|
+
return request(`${staffOrganizationPath(id)}/invites/${encodeURIComponent(inviteId)}`, {
|
|
243
|
+
method: "DELETE"
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function createStaffInvite(request, body) {
|
|
247
|
+
return request("/v1/staff/invites", { method: "POST", body });
|
|
248
|
+
}
|
|
249
|
+
function updateOrganizationApproval(request, id, body) {
|
|
250
|
+
return request(`${organizationPath(id)}/approval`, { method: "POST", body });
|
|
251
|
+
}
|
|
252
|
+
function cancelOrganizationFollowUp(request, id) {
|
|
253
|
+
return request(`${organizationPath(id)}/follow-up/cancel`, {
|
|
254
|
+
method: "POST",
|
|
255
|
+
body: {}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function listSupplierApplications(request, query = {}, signal) {
|
|
259
|
+
return request(withQuery("/v1/supplier-applications", query), {
|
|
260
|
+
method: "GET",
|
|
261
|
+
signal
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function updateSupplierApplication(request, id, body) {
|
|
265
|
+
return request(`/v1/supplier-applications/${encodeURIComponent(id)}`, {
|
|
266
|
+
method: "PATCH",
|
|
267
|
+
body
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function approveSupplierApplicationFromSlack(fetcher, id, actorEmail) {
|
|
271
|
+
return fetcher(`/v1/staff/v1/supplier-applications/${encodeURIComponent(id)}`, {
|
|
272
|
+
method: "PATCH",
|
|
273
|
+
headers: { "Content-Type": "application/json" },
|
|
274
|
+
body: JSON.stringify({ approve_pending: true, actor_email: actorEmail })
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ../../packages/api-client/dist/authorization/onboarding.js
|
|
279
|
+
function submitOnboardingIntake(fetcher, body) {
|
|
280
|
+
return fetcher("/v1/onboarding/intake", {
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: { "Content-Type": "application/json" },
|
|
283
|
+
body: JSON.stringify(body)
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function listOnboardingSlots(fetcher, id, query) {
|
|
287
|
+
return fetcher(withQuery(`${organizationPath(id)}/onboarding-booking/slots`, query), {
|
|
288
|
+
method: "GET"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
function bookOnboardingCall(fetcher, id, body) {
|
|
292
|
+
return fetcher(`${organizationPath(id)}/onboarding-booking`, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/json" },
|
|
295
|
+
body: JSON.stringify(body)
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function cancelOnboardingCall(fetcher, id) {
|
|
299
|
+
return fetcher(`${organizationPath(id)}/onboarding-booking`, { method: "DELETE" });
|
|
300
|
+
}
|
|
301
|
+
function createSupplierApplication(fetcher, body, query = {}) {
|
|
302
|
+
return fetcher(withQuery("/v1/supplier-applications", query), {
|
|
303
|
+
method: "POST",
|
|
304
|
+
headers: { "Content-Type": "application/json" },
|
|
305
|
+
body: JSON.stringify(body)
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
export {
|
|
309
|
+
acceptInvite,
|
|
310
|
+
addStaffOrganizationMember,
|
|
311
|
+
approveSupplierApplicationFromSlack,
|
|
312
|
+
bookOnboardingCall,
|
|
313
|
+
cancelOnboardingCall,
|
|
314
|
+
cancelOrganizationFollowUp,
|
|
315
|
+
countOrganizations,
|
|
316
|
+
createApiKey,
|
|
317
|
+
createInvite,
|
|
318
|
+
createOrganization,
|
|
319
|
+
createSshKey,
|
|
320
|
+
createStaffInvite,
|
|
321
|
+
createStaffOrganization,
|
|
322
|
+
createSupplierApplication,
|
|
323
|
+
getInvite,
|
|
324
|
+
getNotificationPreferences,
|
|
325
|
+
getOrganization,
|
|
326
|
+
getOrganizationMembership,
|
|
327
|
+
getPersonAvatar,
|
|
328
|
+
getStaffOrganization,
|
|
329
|
+
getUserPreferences,
|
|
330
|
+
getUserProfile,
|
|
331
|
+
listAllSshKeys,
|
|
332
|
+
listApiKeys,
|
|
333
|
+
listAvailabilityAlerts,
|
|
334
|
+
listInvites,
|
|
335
|
+
listOnboardingSlots,
|
|
336
|
+
listOrganizationMembers,
|
|
337
|
+
listOrganizations,
|
|
338
|
+
listOrganizationsByIds,
|
|
339
|
+
listOrganizationsForUser,
|
|
340
|
+
listSshKeys,
|
|
341
|
+
listStaffOrganizationInvites,
|
|
342
|
+
listStaffOrganizationMembers,
|
|
343
|
+
listSupplierApplications,
|
|
344
|
+
listSupplierOrganizations,
|
|
345
|
+
organizationAvatarPath,
|
|
346
|
+
personAvatarPath,
|
|
347
|
+
removeOrganizationMember,
|
|
348
|
+
removeStaffOrganizationMember,
|
|
349
|
+
revokeApiKey,
|
|
350
|
+
revokeInvite,
|
|
351
|
+
revokeSshKey,
|
|
352
|
+
revokeStaffOrganizationInvite,
|
|
353
|
+
searchStaffOrganizations,
|
|
354
|
+
searchStaffPeople,
|
|
355
|
+
setAvailabilityAlert,
|
|
356
|
+
submitOnboardingIntake,
|
|
357
|
+
updateNotificationPreferences,
|
|
358
|
+
updateOrganization,
|
|
359
|
+
updateOrganizationApproval,
|
|
360
|
+
updateSupplierApplication,
|
|
361
|
+
updateUserPreferences,
|
|
362
|
+
updateUserProfile
|
|
363
|
+
};
|
|
@@ -72,7 +72,7 @@ var adminSshCapability = defineCapability({
|
|
|
72
72
|
audience: "admin",
|
|
73
73
|
category: "journey",
|
|
74
74
|
arguments: "semantic",
|
|
75
|
-
outcome: "Open an SSH session to
|
|
75
|
+
outcome: "Open an SSH session to a node that trusts the SSH CA with a 15-minute certificate.",
|
|
76
76
|
human: "ornn admin ssh <node-id>",
|
|
77
77
|
automation: "Requires an interactive terminal."
|
|
78
78
|
}
|
|
@@ -236,25 +236,50 @@ function ensureFacility(request, body2) {
|
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
// ../../packages/
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
239
|
+
// ../../packages/api-client/dist/authorization/paths.js
|
|
240
|
+
function withQuery(path, values) {
|
|
241
|
+
const query2 = new URLSearchParams();
|
|
242
|
+
for (const [key, value] of Object.entries(values)) {
|
|
243
|
+
if (value !== void 0 && value !== null && value !== "")
|
|
244
|
+
query2.set(key, Array.isArray(value) ? value.join(",") : String(value));
|
|
245
|
+
}
|
|
246
|
+
return query2.size ? `${path}?${query2}` : path;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ../../packages/api-client/dist/authorization/organizations.js
|
|
250
|
+
function listOrganizations(request, query2 = {}, signal) {
|
|
251
|
+
return request(withQuery("/v1/organizations", query2), { method: "GET", signal });
|
|
252
|
+
}
|
|
253
|
+
async function listSupplierOrganizations(request, signal) {
|
|
248
254
|
const rows = [];
|
|
249
255
|
for (let offset = 0; offset < 5e4; offset += 1e3) {
|
|
250
|
-
const page2 = await
|
|
256
|
+
const page2 = await listOrganizations(request, { role: "supplier", limit: 1e3, offset, sort: "created_at", direction: "asc" }, signal);
|
|
257
|
+
if (!Array.isArray(page2))
|
|
258
|
+
throw new Error("Invalid supplier directory response.");
|
|
251
259
|
rows.push(...page2);
|
|
252
260
|
if (page2.length < 1e3)
|
|
253
261
|
return rows;
|
|
254
262
|
}
|
|
255
|
-
throw new Error("
|
|
263
|
+
throw new Error("The supplier directory is too large to load completely.");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ../../packages/api-client/dist/authorization/staff.js
|
|
267
|
+
function createStaffInvite(request, body2) {
|
|
268
|
+
return request("/v1/staff/invites", { method: "POST", body: body2 });
|
|
256
269
|
}
|
|
257
270
|
|
|
271
|
+
// ../../packages/capabilities/dist/supplier-directory.js
|
|
272
|
+
function listSupplierOrganizationPage(fetch, input) {
|
|
273
|
+
return listOrganizations(fetch, {
|
|
274
|
+
role: "supplier",
|
|
275
|
+
...input,
|
|
276
|
+
q: input.q?.trim() || void 0,
|
|
277
|
+
sort: "created_at",
|
|
278
|
+
direction: "asc"
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
var listSupplierOrganizations2 = listSupplierOrganizations;
|
|
282
|
+
|
|
258
283
|
// ../../packages/capabilities/dist/capabilities/fleet.js
|
|
259
284
|
import { z as z2 } from "zod";
|
|
260
285
|
function queryPath(path, params) {
|
|
@@ -311,7 +336,6 @@ var listSuppliersCapability = defineCapability({
|
|
|
311
336
|
roles: ["admin"],
|
|
312
337
|
description: "List supplier organizations (id, name). Resolve supplier names to organization_id with this before nodes.list or facilities.list. When next_offset is present, pass it back as offset for the next page.",
|
|
313
338
|
mutation: "read",
|
|
314
|
-
http: { method: "GET", path: "/v1/organizations" },
|
|
315
339
|
slack: {
|
|
316
340
|
parameters: {
|
|
317
341
|
type: "object",
|
|
@@ -328,7 +352,7 @@ var listSuppliersCapability = defineCapability({
|
|
|
328
352
|
const paged = parsed.limit !== void 0 || parsed.offset !== void 0 || parsed.q !== void 0;
|
|
329
353
|
const limit = parsed.limit ?? 25;
|
|
330
354
|
const offset = parsed.offset ?? 0;
|
|
331
|
-
const rows = paged ? await listSupplierOrganizationPage(ctx.fetch, { ...parsed, limit, offset }) : await
|
|
355
|
+
const rows = paged ? await listSupplierOrganizationPage(ctx.fetch, { ...parsed, limit, offset }) : await listSupplierOrganizations2(ctx.fetch);
|
|
332
356
|
return {
|
|
333
357
|
suppliers: rows.map((org) => ({ id: org.id, name: org.name })),
|
|
334
358
|
...paged && rows.length === limit ? { next_offset: offset + rows.length } : {}
|
|
@@ -997,19 +1021,6 @@ var usersListInput = z5.object({
|
|
|
997
1021
|
limit: directoryLimit,
|
|
998
1022
|
offset: directoryOffset
|
|
999
1023
|
});
|
|
1000
|
-
function directoryPath(input) {
|
|
1001
|
-
const params = [`limit=${input.limit}`, `offset=${input.offset}`, "sort=name", "direction=asc"];
|
|
1002
|
-
if (input.q)
|
|
1003
|
-
params.push(`q=${encodeURIComponent(input.q)}`);
|
|
1004
|
-
if (input.role)
|
|
1005
|
-
params.push(`role=${encodeURIComponent(input.role)}`);
|
|
1006
|
-
if (input.approved !== void 0) {
|
|
1007
|
-
params.push(`approved=${input.approved}`);
|
|
1008
|
-
}
|
|
1009
|
-
if (input.ids)
|
|
1010
|
-
params.push(`ids=${encodeURIComponent(input.ids.join(","))}`);
|
|
1011
|
-
return `/v1/organizations?${params.join("&")}`;
|
|
1012
|
-
}
|
|
1013
1024
|
function directoryMembers(organization) {
|
|
1014
1025
|
return organization.members ?? [];
|
|
1015
1026
|
}
|
|
@@ -1027,7 +1038,6 @@ var organizationListCapability = defineCapability({
|
|
|
1027
1038
|
roles: ["admin"],
|
|
1028
1039
|
description: "List organizations from the Authorization directory. Search by company name, contact/member name, or email; filter by role, approval, or UUIDs. When next_offset is present, pass it back as offset for the next page. Use the organization id as organizationId when recording downtime, and ask the user to choose if matches are ambiguous.",
|
|
1029
1040
|
mutation: "read",
|
|
1030
|
-
http: { method: "GET", path: "/v1/organizations" },
|
|
1031
1041
|
slack: {
|
|
1032
1042
|
parameters: {
|
|
1033
1043
|
type: "object",
|
|
@@ -1054,8 +1064,12 @@ var organizationListCapability = defineCapability({
|
|
|
1054
1064
|
const parsed = organizationListInput.parse(input);
|
|
1055
1065
|
const limit = parsed.limit;
|
|
1056
1066
|
const offset = parsed.offset ?? 0;
|
|
1057
|
-
const rows = await ctx.fetch
|
|
1058
|
-
|
|
1067
|
+
const rows = await listOrganizations(ctx.fetch, {
|
|
1068
|
+
...parsed,
|
|
1069
|
+
limit,
|
|
1070
|
+
offset,
|
|
1071
|
+
sort: "name",
|
|
1072
|
+
direction: "asc"
|
|
1059
1073
|
});
|
|
1060
1074
|
return {
|
|
1061
1075
|
organizations: rows.map((organization) => ({
|
|
@@ -1079,7 +1093,6 @@ var usersListCapability = defineCapability({
|
|
|
1079
1093
|
roles: ["admin"],
|
|
1080
1094
|
description: "List users who belong to an organization, from the Authorization directory. Search by person name, email, or company name; use organization_id to restrict results to one organization. When next_offset is present, pass it back as offset for the next page.",
|
|
1081
1095
|
mutation: "read",
|
|
1082
|
-
http: { method: "GET", path: "/v1/organizations" },
|
|
1083
1096
|
slack: {
|
|
1084
1097
|
parameters: {
|
|
1085
1098
|
type: "object",
|
|
@@ -1102,12 +1115,14 @@ var usersListCapability = defineCapability({
|
|
|
1102
1115
|
const parsed = usersListInput.parse(input);
|
|
1103
1116
|
const limit = parsed.limit;
|
|
1104
1117
|
const offset = parsed.offset ?? 0;
|
|
1105
|
-
const rows = await ctx.fetch
|
|
1118
|
+
const rows = await listOrganizations(ctx.fetch, {
|
|
1106
1119
|
limit,
|
|
1107
1120
|
offset,
|
|
1108
1121
|
q: parsed.q,
|
|
1109
|
-
ids: parsed.organization_id ? [parsed.organization_id] : void 0
|
|
1110
|
-
|
|
1122
|
+
ids: parsed.organization_id ? [parsed.organization_id] : void 0,
|
|
1123
|
+
sort: "name",
|
|
1124
|
+
direction: "asc"
|
|
1125
|
+
});
|
|
1111
1126
|
const q = parsed.q?.toLowerCase();
|
|
1112
1127
|
const users = rows.flatMap((organization) => {
|
|
1113
1128
|
const members = directoryMembers(organization);
|
|
@@ -5349,7 +5364,7 @@ var createListingCapability = defineCapability({
|
|
|
5349
5364
|
const supplierQuery = (parsed.site_supplier ?? "").trim().toLowerCase();
|
|
5350
5365
|
const place = (parsed.site_nickname ?? parsed.site ?? "").trim();
|
|
5351
5366
|
if (!organizationId && supplierQuery) {
|
|
5352
|
-
const matches = (await
|
|
5367
|
+
const matches = (await listSupplierOrganizations2(ctx.fetch)).filter((org) => org.id.toLowerCase() === supplierQuery || org.name.toLowerCase() === supplierQuery);
|
|
5353
5368
|
if (matches.length !== 1) {
|
|
5354
5369
|
return { ok: false, error: matches.length ? "ambiguous_supplier" : "supplier_not_found" };
|
|
5355
5370
|
}
|
|
@@ -5493,7 +5508,6 @@ var organizationInviteCapability = defineCapability({
|
|
|
5493
5508
|
roles: ["admin"],
|
|
5494
5509
|
description: "Invite a person to an existing organization (emails by default; send_email=false returns a join link), or omit organization_id to create a signup link with organization_role=consumer or supplier. Resolve organization UUIDs first; ask if ambiguous. Executes immediately. Keep invitation links secret.",
|
|
5495
5510
|
mutation: "immediate",
|
|
5496
|
-
http: { method: "POST", path: "/v1/staff/invites" },
|
|
5497
5511
|
cli: {
|
|
5498
5512
|
command: ["admin", "organizations", "invite"],
|
|
5499
5513
|
vnext: {
|
|
@@ -5528,9 +5542,9 @@ var organizationInviteCapability = defineCapability({
|
|
|
5528
5542
|
input: organizationInviteInput,
|
|
5529
5543
|
execute: async (ctx, input) => {
|
|
5530
5544
|
const parsed = organizationInviteInput.parse(input);
|
|
5531
|
-
return ctx.fetch
|
|
5532
|
-
|
|
5533
|
-
|
|
5545
|
+
return createStaffInvite(ctx.fetch, {
|
|
5546
|
+
...parsed,
|
|
5547
|
+
send_email: parsed.send_email ?? parsed.organization_id !== void 0
|
|
5534
5548
|
});
|
|
5535
5549
|
}
|
|
5536
5550
|
});
|