@odla-ai/cli 0.36.1 → 0.37.0

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/dist/bin.js CHANGED
@@ -174,7 +174,7 @@ function absoluteEntryPath(entryPath) {
174
174
 
175
175
  // src/bin.ts
176
176
  var argv = process.argv.slice(2);
177
- requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-BFGY7F6X.js")).runCli()).catch((err) => {
177
+ requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-QR44X5IB.js")).runCli()).catch((err) => {
178
178
  console.error(redactSecrets(`odla-ai: ${err instanceof Error ? err.message : String(err)}`));
179
179
  process.exitCode = exitCodeFor(err);
180
180
  });
@@ -551,6 +551,7 @@ var SCOPE_PURPOSE = {
551
551
  "app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
552
552
  "platform:runbook:write": "read and edit all of odla's operational runbooks, including admin-visible content",
553
553
  "app:device:enroll": "enrol this machine so it can mint its own short-lived credentials without asking you again",
554
+ "platform:device:enroll:extended": "enrol this machine with a credential that outlives the seven-day owner ceiling \u2014 a durable secret on a machine you are vouching for",
554
555
  "platform:ai:policy:write": "change System AI model routing",
555
556
  "platform:ai:policy:read": "read System AI model routing",
556
557
  "platform:ai:credential:write": "replace a stored AI provider key",
@@ -10287,7 +10288,7 @@ Usage:
10287
10288
  odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
10288
10289
  odla-ai security run [target] --self --ack-redacted-source
10289
10290
  odla-ai provision [--live] [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--no-open] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
10290
- odla-ai device enroll [--app <id>[,<id>...]] [--name <label>] [--capability <c>[,<c>...]] [--email <odla-account>] [--no-open] [--json]
10291
+ odla-ai device enroll [--app <id>[,<id>...]] [--name <label>] [--capability <c>[,<c>...]] [--device-ttl <30d|6w|2y|forever>] [--email <odla-account>] [--no-open] [--json]
10291
10292
  odla-ai device list [--email <odla-account>] [--json]
10292
10293
  odla-ai device revoke <device-id> [--email <odla-account>] [--json]
10293
10294
  odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
@@ -13378,6 +13379,24 @@ function renderAdvisories(out, advisories, env = process.env) {
13378
13379
  }
13379
13380
  }
13380
13381
 
13382
+ // src/device-ttl.ts
13383
+ var OWNER_DEVICE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
13384
+ var DAY_MS = 24 * 60 * 60 * 1e3;
13385
+ var UNITS = { d: DAY_MS, w: 7 * DAY_MS, y: 365 * DAY_MS };
13386
+ function parseDeviceTtl(raw) {
13387
+ if (raw === void 0 || raw === true) return void 0;
13388
+ const text3 = String(raw).trim().toLowerCase();
13389
+ if (!text3) return void 0;
13390
+ if (text3 === "forever" || text3 === "never") return 100 * 365 * DAY_MS;
13391
+ const match = /^(\d+)\s*([dwy])$/.exec(text3);
13392
+ if (!match) {
13393
+ throw new Error(
13394
+ `--device-ttl expects a duration like 30d, 6w, 2y, or "forever" (got "${raw}")`
13395
+ );
13396
+ }
13397
+ return Number(match[1]) * UNITS[match[2]];
13398
+ }
13399
+
13381
13400
  // src/device-command.ts
13382
13401
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
13383
13402
  import { dirname as dirname10 } from "path";
@@ -13397,7 +13416,17 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
13397
13416
  const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
13398
13417
  const apps = (stringOpt(parsed.options.app) ?? cfg.app.id).split(",").map((id2) => id2.trim()).filter(Boolean);
13399
13418
  if (apps.length === 0) throw new Error("device enroll needs --app <id>[,<id>\u2026]");
13400
- const token = await scopedToken2(parsed, deps, cfg, doFetch, out, `odla CLI (enroll ${name})`);
13419
+ const deviceTtlMs = parseDeviceTtl(parsed.options["device-ttl"]);
13420
+ const extended = deviceTtlMs !== void 0 && deviceTtlMs > OWNER_DEVICE_TTL_MS;
13421
+ const token = await scopedToken2(
13422
+ parsed,
13423
+ deps,
13424
+ cfg,
13425
+ doFetch,
13426
+ out,
13427
+ `odla CLI (enroll ${name})`,
13428
+ extended ? "platform:device:enroll:extended" : "app:device:enroll"
13429
+ );
13401
13430
  const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
13402
13431
  method: "POST",
13403
13432
  headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
@@ -13405,7 +13434,8 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
13405
13434
  name,
13406
13435
  platform: process16.platform,
13407
13436
  appIds: apps,
13408
- ...parsed.options.capability ? { capabilities: String(parsed.options.capability).split(",").map((c) => c.trim()).filter(Boolean) } : {}
13437
+ ...parsed.options.capability ? { capabilities: String(parsed.options.capability).split(",").map((c) => c.trim()).filter(Boolean) } : {},
13438
+ ...deviceTtlMs === void 0 ? {} : { deviceTtlMs }
13409
13439
  })
13410
13440
  });
13411
13441
  const body = await response2.json().catch(() => ({}));
@@ -13458,12 +13488,12 @@ async function revoke(parsed, deps, cfg, doFetch, out, json) {
13458
13488
  out.error(`device: revoked ${deviceId}; every credential it minted is revoked with it`);
13459
13489
  if (json) out.log(JSON.stringify({ deviceId, revoked: true }, null, 2));
13460
13490
  }
13461
- async function scopedToken2(parsed, deps, cfg, doFetch, out, label) {
13491
+ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app:device:enroll") {
13462
13492
  const { credentials } = await resolveOperatorContext(parsed, { allowMissingConfig: true });
13463
13493
  const scopedTokenFile = credentials.scopedTokenFile;
13464
13494
  return getScopedPlatformToken({
13465
13495
  platform: cfg.platformUrl,
13466
- scope: "app:device:enroll",
13496
+ scope,
13467
13497
  email: stringOpt(parsed.options.email),
13468
13498
  label,
13469
13499
  fetch: doFetch,
@@ -15266,4 +15296,4 @@ export {
15266
15296
  isTerminalHostedSecurityStatus,
15267
15297
  runCli
15268
15298
  };
15269
- //# sourceMappingURL=chunk-4QJ5NS64.js.map
15299
+ //# sourceMappingURL=chunk-OWTIDSL5.js.map