@ornncompute/cli 0.2.15 → 0.2.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ornncompute/cli",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Command-line interface for Ornn compute access workflows.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -7813,6 +7813,9 @@ function writeNodeKeyStatus(stdout, status) {
7813
7813
  }
7814
7814
 
7815
7815
  function sshInvocationForMachine(machine, options = {}) {
7816
+ if (String(machineState(machine, "")).toLowerCase() !== "running") {
7817
+ return null;
7818
+ }
7816
7819
  const host = machineHost(machine);
7817
7820
  if (host) {
7818
7821
  const args = [];
package/src/update.mjs CHANGED
@@ -125,7 +125,7 @@ export function detectInstallContext(binPath, env = process.env) {
125
125
  if (isNpmGlobalInstall(lower, env)) {
126
126
  return { kind: "npm", binPath: resolved };
127
127
  }
128
- if (isProjectInstall(resolved, lower)) {
128
+ if (isProjectInstall(resolved)) {
129
129
  return { kind: "project", binPath: resolved };
130
130
  }
131
131
  if (has(".bun") || isUnderDir(lower, env.BUN_INSTALL)) {
@@ -140,14 +140,15 @@ export function detectInstallContext(binPath, env = process.env) {
140
140
  return { kind: "npm", binPath: resolved };
141
141
  }
142
142
 
143
- function isProjectInstall(resolvedPath, lower) {
143
+ function isProjectInstall(resolvedPath) {
144
144
  // Global layouts have no package.json above node_modules; projects do.
145
- const index = lower.indexOf("/node_modules/");
146
- if (index < 0) {
145
+ const parts = resolvedPath.replaceAll("\\", "/").split("/");
146
+ const index = parts.findIndex((segment) => segment.toLowerCase() === "node_modules");
147
+ if (index <= 0) {
147
148
  return false;
148
149
  }
149
150
  try {
150
- return existsSync(join(resolvedPath.slice(0, index), "package.json"));
151
+ return existsSync(join(parts.slice(0, index).join("/"), "package.json"));
151
152
  } catch {
152
153
  return false;
153
154
  }
@@ -212,9 +213,13 @@ function stableInstallRoot(binPath) {
212
213
  try {
213
214
  resolved = realpathSync(binPath);
214
215
  } catch {}
215
- const normalized = resolved.replaceAll("\\", "/");
216
- const markerIndex = normalized.toLowerCase().indexOf("/node_modules/");
217
- return markerIndex > 0 ? normalized.slice(0, markerIndex) : null;
216
+ const parts = resolved.replaceAll("\\", "/").split("/");
217
+ const index = parts.findIndex((segment) => segment.toLowerCase() === "node_modules");
218
+ if (index <= 0) {
219
+ return null;
220
+ }
221
+ const prefix = parts.slice(0, index).join("/");
222
+ return prefix.length > 0 ? prefix : null;
218
223
  }
219
224
 
220
225
  function pathIdentity(path) {
@@ -200,7 +200,21 @@ export const sshKeyAddCapability = defineStaffMcpCapability({
200
200
  // (gateway `add_reservation_key`), which this tool previously bypassed by
201
201
  // posting `public_key` into a body that only reads `sshKeyId`/`userId`.
202
202
  let sshKeyId = parsed.sshKeyId;
203
- if (!sshKeyId) {
203
+ if (sshKeyId) {
204
+ // Commerce stores the id as a soft key without checking authorization,
205
+ // so gate it here the way gateway `add_reservation_key` does.
206
+ const listed = (await ctx.fetch(`/v1/organizations/${seg(parsed.tenantId)}/ssh-keys`, {
207
+ method: "GET",
208
+ }));
209
+ const key = (listed.ssh_keys ?? []).find((candidate) => candidate.id === sshKeyId);
210
+ if (!key) {
211
+ throw new Error("SSH key not found.");
212
+ }
213
+ if (key.status !== "active" || key.revoked_at) {
214
+ throw new Error("SSH key is not active.");
215
+ }
216
+ }
217
+ else {
204
218
  const registered = (await ctx.fetch(`/v1/organizations/${seg(parsed.tenantId)}/ssh-keys`, {
205
219
  method: "POST",
206
220
  body: { public_key: parsed.publicKey, ...mapBody(parsed, { label: "label" }) },
@@ -263,7 +263,7 @@ export const reservationListCapability = defineStaffMcpCapability({
263
263
  const parsed = reservationListInput.parse(input ?? {});
264
264
  const parts = [];
265
265
  if (parsed.organizationId) {
266
- parts.push(`organization_id=${encodeURIComponent(parsed.organizationId)}`);
266
+ parts.push(`organizationId=${encodeURIComponent(parsed.organizationId)}`);
267
267
  }
268
268
  if (parsed.status)
269
269
  parts.push(`status=${encodeURIComponent(parsed.status)}`);
@@ -1,7 +1,6 @@
1
1
  export declare function buildEnrollmentInstallCommand(options: {
2
2
  token: string;
3
3
  base?: string;
4
- nodeMode?: string;
5
4
  ip?: string;
6
5
  restoreNodeId?: string;
7
6
  force?: boolean;
@@ -24,10 +24,6 @@ const emptyInput = z.object({});
24
24
  export function buildEnrollmentInstallCommand(options) {
25
25
  const base = (options.base ?? "https://api.ornn.com/v1/orchestrator").replace(/\/+$/, "");
26
26
  const params = [`token=${encodeURIComponent(options.token)}`];
27
- if (options.nodeMode) {
28
- const mode = options.nodeMode === "vm" ? "virtual-machine" : options.nodeMode;
29
- params.push(`node_mode=${encodeURIComponent(mode)}`);
30
- }
31
27
  if (options.ip)
32
28
  params.push(`ip=${encodeURIComponent(options.ip)}`);
33
29
  if (options.restoreNodeId)
@@ -122,7 +118,6 @@ export const tokensCreateCapability = defineCapability({
122
118
  ? buildEnrollmentInstallCommand({
123
119
  token,
124
120
  base: parsed.installer_base ?? undefined,
125
- nodeMode: nodeMode ?? undefined,
126
121
  ip: parsed.ip ?? undefined,
127
122
  force: payload.remote,
128
123
  })