@odla-ai/cli 0.44.0 → 0.46.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.cjs CHANGED
@@ -99,17 +99,173 @@ var init_runbook_requires = __esm({
99
99
  }
100
100
  });
101
101
 
102
+ // src/odla-home.ts
103
+ function odlaHome(env = import_node_process.default.env) {
104
+ if (env.VITEST && !env.ODLA_HOME) {
105
+ throw new Error(
106
+ "ODLA_HOME must be set under test \u2014 resolving the real ~/.odla would write to the developer's machine"
107
+ );
108
+ }
109
+ return env.ODLA_HOME ?? (0, import_node_path.join)(env.HOME ?? (0, import_node_os.homedir)(), ".odla");
110
+ }
111
+ function odlaHomePath(segments, env = import_node_process.default.env) {
112
+ return (0, import_node_path.join)(odlaHome(env), ...segments);
113
+ }
114
+ function identityFile(env) {
115
+ return odlaHomePath(["identity.json"], env);
116
+ }
117
+ function deviceSessionFile(env) {
118
+ return odlaHomePath(["session.json"], env);
119
+ }
120
+ function appTokenFile(appId, env) {
121
+ return odlaHomePath(["apps", safeSegment(appId), "dev-token.json"], env);
122
+ }
123
+ function appCredentialsFile(appId, env) {
124
+ return odlaHomePath(["apps", safeSegment(appId), "credentials.json"], env);
125
+ }
126
+ function scopedTokenFile(env) {
127
+ return odlaHomePath(["admin-token.local.json"], env);
128
+ }
129
+ function pmContextFile(env) {
130
+ return odlaHomePath(["pm-context.json"], env);
131
+ }
132
+ function adoptRepoLocalCache(legacyPath, machinePath, out) {
133
+ if (!(0, import_node_fs.existsSync)(legacyPath) || legacyPath === machinePath) return false;
134
+ const superseded2 = (0, import_node_fs.existsSync)(machinePath);
135
+ if (!superseded2) {
136
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(machinePath), { recursive: true });
137
+ (0, import_node_fs.copyFileSync)(legacyPath, machinePath);
138
+ (0, import_node_fs.chmodSync)(machinePath, 384);
139
+ }
140
+ (0, import_node_fs.rmSync)(legacyPath, { force: true });
141
+ out?.error(
142
+ superseded2 ? `auth: removed superseded ${legacyPath}; this machine's credentials live in ${odlaHome()}` : `auth: moved ${legacyPath} into ${machinePath}; credentials are per machine now, not per worktree`
143
+ );
144
+ return true;
145
+ }
146
+ function safeSegment(value2) {
147
+ const clean4 = value2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
148
+ if (!clean4) throw new Error(`"${value2}" is not a usable app id`);
149
+ return clean4;
150
+ }
151
+ var import_node_fs, import_node_os, import_node_path, import_node_process;
152
+ var init_odla_home = __esm({
153
+ "src/odla-home.ts"() {
154
+ "use strict";
155
+ init_cjs_shims();
156
+ import_node_fs = require("fs");
157
+ import_node_os = require("os");
158
+ import_node_path = require("path");
159
+ import_node_process = __toESM(require("process"), 1);
160
+ }
161
+ });
162
+
102
163
  // src/version.ts
103
164
  function cliVersion() {
104
- const pkg = JSON.parse((0, import_node_fs.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
165
+ const pkg = JSON.parse((0, import_node_fs2.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
105
166
  return pkg.version ?? "unknown";
106
167
  }
107
- var import_node_fs;
168
+ var import_node_fs2;
108
169
  var init_version = __esm({
109
170
  "src/version.ts"() {
110
171
  "use strict";
111
172
  init_cjs_shims();
112
- import_node_fs = require("fs");
173
+ import_node_fs2 = require("fs");
174
+ }
175
+ });
176
+
177
+ // src/update-notice.ts
178
+ function updateCacheFile(env) {
179
+ return odlaHomePath(["cli-update.json"], env);
180
+ }
181
+ function readUpdateCache(path) {
182
+ try {
183
+ const parsed = JSON.parse((0, import_node_fs3.readFileSync)(path, "utf8"));
184
+ if (typeof parsed.latest !== "string" || !VERSION.test(parsed.latest)) return null;
185
+ if (typeof parsed.checkedAt !== "number") return null;
186
+ return { latest: parsed.latest, checkedAt: parsed.checkedAt };
187
+ } catch {
188
+ return null;
189
+ }
190
+ }
191
+ function writeUpdateCache(path, cache2) {
192
+ (0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(path), { recursive: true });
193
+ (0, import_node_fs3.writeFileSync)(path, JSON.stringify(cache2));
194
+ }
195
+ function updateNotice(options = {}) {
196
+ const env = options.env ?? import_node_process2.default.env;
197
+ if (env.ODLA_CLI_UPDATE_CHECK === "0") return null;
198
+ const current = options.currentVersion ?? cliVersion();
199
+ if (!VERSION.test(current)) return null;
200
+ const path = updateCacheFile(env);
201
+ const cache2 = readUpdateCache(path);
202
+ const now = options.now ?? Date.now();
203
+ if ((!cache2 || now - cache2.checkedAt > INTERVAL_MS) && !refreshScheduled) {
204
+ refreshScheduled = true;
205
+ (options.refresh ?? spawnRefresh)(path, env.ODLA_CLI_REGISTRY_URL ?? REGISTRY_URL);
206
+ }
207
+ if (!cache2 || compareVersions(current, cache2.latest) >= 0) return null;
208
+ return renderNotice(current, cache2.latest, options.entryPath ?? import_node_process2.default.argv[1]);
209
+ }
210
+ function renderNotice(current, latest, entryPath) {
211
+ const resolved = resolvedEntryPath(entryPath);
212
+ const behindMajor = Number(latest.split(".")[0]) > Number(current.split(".")[0]);
213
+ const repair = isWorkspaceCli(resolved) ? `this is the workspace build at ${resolved} \u2014 rebase that worktree and rebuild it` : resolved.includes("/_npx/") ? `run npx --yes @odla-ai/cli@${latest} <command>` : `run npm i @odla-ai/cli@${latest}`;
214
+ const severity = behindMajor ? "a MAJOR version behind, so commands and flags this version has may no longer exist" : "behind";
215
+ return `odla-ai: ${current} is ${severity}; npm serves ${latest}. To update, ${repair}.`;
216
+ }
217
+ function spawnRefresh(cachePath, registryUrl) {
218
+ const script = `
219
+ const {mkdirSync,writeFileSync}=require("node:fs");
220
+ const {dirname}=require("node:path");
221
+ const [path,url]=process.argv.slice(1);
222
+ const done=setTimeout(()=>process.exit(0),5000); done.unref();
223
+ fetch(url,{headers:{accept:"application/vnd.npm.install-v1+json"}})
224
+ .then(r=>r.ok?r.json():null)
225
+ .then(b=>{
226
+ const v=b&&b.version;
227
+ if (typeof v!=="string"||!/^\\d+\\.\\d+\\.\\d+/.test(v)) return;
228
+ mkdirSync(dirname(path),{recursive:true});
229
+ writeFileSync(path,JSON.stringify({latest:v,checkedAt:Date.now()}));
230
+ })
231
+ .catch(()=>{});
232
+ `;
233
+ try {
234
+ (0, import_node_child_process.spawn)(import_node_process2.default.execPath, ["-e", script, cachePath, registryUrl], {
235
+ detached: true,
236
+ stdio: "ignore"
237
+ }).unref();
238
+ } catch {
239
+ }
240
+ }
241
+ function resolvedEntryPath(entryPath) {
242
+ if (!entryPath) return "unknown executable";
243
+ try {
244
+ return (0, import_node_fs3.realpathSync)(entryPath);
245
+ } catch {
246
+ return entryPath;
247
+ }
248
+ }
249
+ function isWorkspaceCli(entryPath) {
250
+ const normalized = entryPath.replaceAll("\\", "/");
251
+ return /\/packages\/cli\/(dist|bin)\//.test(normalized) && !normalized.includes("/node_modules/");
252
+ }
253
+ var import_node_child_process, import_node_fs3, import_node_path2, import_node_process2, REGISTRY_URL, INTERVAL_MS, VERSION, refreshScheduled;
254
+ var init_update_notice = __esm({
255
+ "src/update-notice.ts"() {
256
+ "use strict";
257
+ init_cjs_shims();
258
+ import_node_child_process = require("child_process");
259
+ import_node_fs3 = require("fs");
260
+ import_node_path2 = require("path");
261
+ import_node_process2 = __toESM(require("process"), 1);
262
+ init_odla_home();
263
+ init_runbook_requires();
264
+ init_version();
265
+ REGISTRY_URL = "https://registry.npmjs.org/@odla-ai%2fcli/latest";
266
+ INTERVAL_MS = 3 * 60 * 60 * 1e3;
267
+ VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
268
+ refreshScheduled = false;
113
269
  }
114
270
  });
115
271
 
@@ -232,8 +388,8 @@ var init_approval_prompt = __esm({
232
388
 
233
389
  // src/open.ts
234
390
  async function openUrl(url, options = {}) {
235
- const command = openerFor(options.platform ?? import_node_process.default.platform);
236
- const doSpawn = options.spawnImpl ?? import_node_child_process.spawn;
391
+ const command = openerFor(options.platform ?? import_node_process3.default.platform);
392
+ const doSpawn = options.spawnImpl ?? import_node_child_process2.spawn;
237
393
  await new Promise((resolve15, reject) => {
238
394
  const child = doSpawn(command.cmd, [...command.args, url], {
239
395
  stdio: "ignore",
@@ -251,13 +407,13 @@ function openerFor(platform) {
251
407
  if (platform === "win32") return { cmd: "cmd", args: ["/c", "start", ""] };
252
408
  return { cmd: "xdg-open", args: [] };
253
409
  }
254
- var import_node_child_process, import_node_process;
410
+ var import_node_child_process2, import_node_process3;
255
411
  var init_open = __esm({
256
412
  "src/open.ts"() {
257
413
  "use strict";
258
414
  init_cjs_shims();
259
- import_node_child_process = require("child_process");
260
- import_node_process = __toESM(require("process"), 1);
415
+ import_node_child_process2 = require("child_process");
416
+ import_node_process3 = __toESM(require("process"), 1);
261
417
  }
262
418
  });
263
419
 
@@ -265,7 +421,7 @@ var init_open = __esm({
265
421
  function approvalBrowser(options, host = {}) {
266
422
  if (options.open === false) return { open: false, reason: "disabled by --no-open" };
267
423
  if (options.open === true) return { open: true, mode: "forced" };
268
- const env = host.env ?? import_node_process2.default.env;
424
+ const env = host.env ?? import_node_process4.default.env;
269
425
  if (env.VITEST || env.NODE_ENV === "test") {
270
426
  return { open: false, reason: "test environment" };
271
427
  }
@@ -295,12 +451,12 @@ function handshakeUrl(platformUrl, userCode) {
295
451
  url.searchParams.set("code", userCode);
296
452
  return url.toString();
297
453
  }
298
- var import_node_process2;
454
+ var import_node_process4;
299
455
  var init_handshake_approval = __esm({
300
456
  "src/handshake-approval.ts"() {
301
457
  "use strict";
302
458
  init_cjs_shims();
303
- import_node_process2 = __toESM(require("process"), 1);
459
+ import_node_process4 = __toESM(require("process"), 1);
304
460
  init_approval_prompt();
305
461
  init_open();
306
462
  }
@@ -308,10 +464,10 @@ var init_handshake_approval = __esm({
308
464
 
309
465
  // src/handshake-state.ts
310
466
  function handshakeFile(cfg) {
311
- return (0, import_node_path2.join)((0, import_node_path2.dirname)(cfg.local.tokenFile), "handshake.local.json");
467
+ return (0, import_node_path4.join)((0, import_node_path4.dirname)(cfg.local.tokenFile), "handshake.local.json");
312
468
  }
313
469
  function clearPendingHandshake(path) {
314
- (0, import_node_fs4.rmSync)(path, { force: true });
470
+ (0, import_node_fs5.rmSync)(path, { force: true });
315
471
  }
316
472
  function minutesLeft(expiresAt) {
317
473
  return Math.max(1, Math.round((expiresAt - Date.now()) / 6e4));
@@ -331,31 +487,31 @@ function approvalReminder(out, pending, periodMs = 3e4) {
331
487
  timer.unref?.();
332
488
  return () => clearInterval(timer);
333
489
  }
334
- function handshakeWaitMs(waitSeconds, interactive = import_node_process3.default.stdout.isTTY === true) {
490
+ function handshakeWaitMs(waitSeconds, interactive = import_node_process5.default.stdout.isTTY === true) {
335
491
  if (waitSeconds !== void 0) return waitSeconds * 1e3;
336
492
  return interactive ? void 0 : 9e4;
337
493
  }
338
- var import_node_fs4, import_node_path2, import_node_process3;
494
+ var import_node_fs5, import_node_path4, import_node_process5;
339
495
  var init_handshake_state = __esm({
340
496
  "src/handshake-state.ts"() {
341
497
  "use strict";
342
498
  init_cjs_shims();
343
- import_node_fs4 = require("fs");
344
- import_node_path2 = require("path");
345
- import_node_process3 = __toESM(require("process"), 1);
499
+ import_node_fs5 = require("fs");
500
+ import_node_path4 = require("path");
501
+ import_node_process5 = __toESM(require("process"), 1);
346
502
  init_approval_prompt();
347
503
  }
348
504
  });
349
505
 
350
506
  // src/device-session.ts
351
- function deviceCredentialPath(env = import_node_process4.default.env) {
352
- return env.ODLA_DEVICE_CREDENTIAL ?? (0, import_node_path3.join)(env.HOME ?? (0, import_node_os.homedir)(), ".odla", "device.json");
507
+ function deviceCredentialPath(env = import_node_process6.default.env) {
508
+ return env.ODLA_DEVICE_CREDENTIAL ?? (0, import_node_path5.join)(env.HOME ?? (0, import_node_os2.homedir)(), ".odla", "device.json");
353
509
  }
354
- function readDeviceCredential(platform, env = import_node_process4.default.env) {
510
+ function readDeviceCredential(platform, env = import_node_process6.default.env) {
355
511
  const path = deviceCredentialPath(env);
356
- if (!(0, import_node_fs5.existsSync)(path)) return null;
512
+ if (!(0, import_node_fs6.existsSync)(path)) return null;
357
513
  try {
358
- const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf8"));
514
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
359
515
  if (typeof parsed.token !== "string" || !parsed.token.startsWith("odla_device_")) return null;
360
516
  if (parsed.platform !== platform) return null;
361
517
  return { ...parsed, token: parsed.token, platform: parsed.platform };
@@ -389,76 +545,15 @@ async function mintDeviceSession(platformUrl, credential2, doFetch) {
389
545
  ...Array.isArray(body.scopes) ? { scopes: body.scopes } : {}
390
546
  };
391
547
  }
392
- var import_node_fs5, import_node_os, import_node_path3, import_node_process4;
548
+ var import_node_fs6, import_node_os2, import_node_path5, import_node_process6;
393
549
  var init_device_session = __esm({
394
550
  "src/device-session.ts"() {
395
- "use strict";
396
- init_cjs_shims();
397
- import_node_fs5 = require("fs");
398
- import_node_os = require("os");
399
- import_node_path3 = require("path");
400
- import_node_process4 = __toESM(require("process"), 1);
401
- }
402
- });
403
-
404
- // src/odla-home.ts
405
- function odlaHome(env = import_node_process5.default.env) {
406
- if (env.VITEST && !env.ODLA_HOME) {
407
- throw new Error(
408
- "ODLA_HOME must be set under test \u2014 resolving the real ~/.odla would write to the developer's machine"
409
- );
410
- }
411
- return env.ODLA_HOME ?? (0, import_node_path4.join)(env.HOME ?? (0, import_node_os2.homedir)(), ".odla");
412
- }
413
- function odlaHomePath(segments, env = import_node_process5.default.env) {
414
- return (0, import_node_path4.join)(odlaHome(env), ...segments);
415
- }
416
- function identityFile(env) {
417
- return odlaHomePath(["identity.json"], env);
418
- }
419
- function deviceSessionFile(env) {
420
- return odlaHomePath(["session.json"], env);
421
- }
422
- function appTokenFile(appId, env) {
423
- return odlaHomePath(["apps", safeSegment(appId), "dev-token.json"], env);
424
- }
425
- function appCredentialsFile(appId, env) {
426
- return odlaHomePath(["apps", safeSegment(appId), "credentials.json"], env);
427
- }
428
- function scopedTokenFile(env) {
429
- return odlaHomePath(["admin-token.local.json"], env);
430
- }
431
- function pmContextFile(env) {
432
- return odlaHomePath(["pm-context.json"], env);
433
- }
434
- function adoptRepoLocalCache(legacyPath, machinePath, out) {
435
- if (!(0, import_node_fs6.existsSync)(legacyPath) || legacyPath === machinePath) return false;
436
- const superseded2 = (0, import_node_fs6.existsSync)(machinePath);
437
- if (!superseded2) {
438
- (0, import_node_fs6.mkdirSync)((0, import_node_path4.dirname)(machinePath), { recursive: true });
439
- (0, import_node_fs6.copyFileSync)(legacyPath, machinePath);
440
- (0, import_node_fs6.chmodSync)(machinePath, 384);
441
- }
442
- (0, import_node_fs6.rmSync)(legacyPath, { force: true });
443
- out?.error(
444
- superseded2 ? `auth: removed superseded ${legacyPath}; this machine's credentials live in ${odlaHome()}` : `auth: moved ${legacyPath} into ${machinePath}; credentials are per machine now, not per worktree`
445
- );
446
- return true;
447
- }
448
- function safeSegment(value2) {
449
- const clean4 = value2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
450
- if (!clean4) throw new Error(`"${value2}" is not a usable app id`);
451
- return clean4;
452
- }
453
- var import_node_fs6, import_node_os2, import_node_path4, import_node_process5;
454
- var init_odla_home = __esm({
455
- "src/odla-home.ts"() {
456
551
  "use strict";
457
552
  init_cjs_shims();
458
553
  import_node_fs6 = require("fs");
459
554
  import_node_os2 = require("os");
460
- import_node_path4 = require("path");
461
- import_node_process5 = __toESM(require("process"), 1);
555
+ import_node_path5 = require("path");
556
+ import_node_process6 = __toESM(require("process"), 1);
462
557
  }
463
558
  });
464
559
 
@@ -508,7 +603,7 @@ function mergeCredential(current, update) {
508
603
  return next;
509
604
  }
510
605
  function ensureGitignore(rootDir, localPaths = []) {
511
- const path = (0, import_node_path5.resolve)(rootDir, ".gitignore");
606
+ const path = (0, import_node_path6.resolve)(rootDir, ".gitignore");
512
607
  const existing = (0, import_node_fs7.existsSync)(path) ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
513
608
  const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
514
609
  const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
@@ -529,7 +624,7 @@ function o11yDevVars(cfg) {
529
624
  function resolveWriteDevVarsTarget(cfg, requested) {
530
625
  if (!requested) return null;
531
626
  if (requested === true) return cfg.local.devVarsFile;
532
- return (0, import_node_path5.resolve)((0, import_node_path5.dirname)(cfg.configPath), requested);
627
+ return (0, import_node_path6.resolve)((0, import_node_path6.dirname)(cfg.configPath), requested);
533
628
  }
534
629
  function writeDevVars(path, credentials, env, o11y) {
535
630
  const entry = credentials.envs[env];
@@ -557,28 +652,28 @@ function isManagedDevVar(line2) {
557
652
  return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
558
653
  }
559
654
  function writePrivateText(path, text4) {
560
- (0, import_node_fs7.mkdirSync)((0, import_node_path5.dirname)(path), { recursive: true });
655
+ (0, import_node_fs7.mkdirSync)((0, import_node_path6.dirname)(path), { recursive: true });
561
656
  const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
562
657
  (0, import_node_fs7.writeFileSync)(temporary, text4, { mode: 384 });
563
658
  (0, import_node_fs7.chmodSync)(temporary, 384);
564
659
  (0, import_node_fs7.renameSync)(temporary, path);
565
660
  }
566
661
  function gitignoreEntry(rootDir, path) {
567
- const rel = (0, import_node_path5.relative)((0, import_node_path5.resolve)(rootDir), (0, import_node_path5.resolve)(path));
568
- if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0, import_node_path5.isAbsolute)(rel)) return null;
662
+ const rel = (0, import_node_path6.relative)((0, import_node_path6.resolve)(rootDir), (0, import_node_path6.resolve)(path));
663
+ if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0, import_node_path6.isAbsolute)(rel)) return null;
569
664
  return rel.replaceAll("\\", "/");
570
665
  }
571
666
  function displayPath(path, rootDir = process.cwd()) {
572
- const rel = (0, import_node_path5.relative)(rootDir, path);
667
+ const rel = (0, import_node_path6.relative)(rootDir, path);
573
668
  return rel && !rel.startsWith("..") ? rel : path;
574
669
  }
575
- var import_node_fs7, import_node_path5, GITIGNORE_LINES, MANAGED_DEV_VARS;
670
+ var import_node_fs7, import_node_path6, GITIGNORE_LINES, MANAGED_DEV_VARS;
576
671
  var init_local = __esm({
577
672
  "src/local.ts"() {
578
673
  "use strict";
579
674
  init_cjs_shims();
580
675
  import_node_fs7 = require("fs");
581
- import_node_path5 = require("path");
676
+ import_node_path6 = require("path");
582
677
  GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
583
678
  MANAGED_DEV_VARS = /* @__PURE__ */ new Set([
584
679
  "ODLA_PLATFORM",
@@ -596,7 +691,7 @@ var init_local = __esm({
596
691
  });
597
692
 
598
693
  // src/auth-guidance.ts
599
- function machineAuthState(audience, env = import_node_process6.default.env) {
694
+ function machineAuthState(audience, env = import_node_process7.default.env) {
600
695
  const device = readDeviceCredential(audience, env);
601
696
  if (!device) return { enrolled: false };
602
697
  const session = readJsonFile(deviceSessionFile(env));
@@ -640,12 +735,12 @@ function lapseNotice(state2, now = Date.now()) {
640
735
  if (days < 0) return "this machine's enrollment has lapsed; the next command will ask for approval";
641
736
  return `idle for ${days} more day${days === 1 ? "" : "s"} before this machine needs approving again (using it resets the clock)`;
642
737
  }
643
- var import_node_process6, ENROL_EVERYTHING, ENROL_PLATFORM_WIDE;
738
+ var import_node_process7, ENROL_EVERYTHING, ENROL_PLATFORM_WIDE;
644
739
  var init_auth_guidance = __esm({
645
740
  "src/auth-guidance.ts"() {
646
741
  "use strict";
647
742
  init_cjs_shims();
648
- import_node_process6 = __toESM(require("process"), 1);
743
+ import_node_process7 = __toESM(require("process"), 1);
649
744
  init_device_session();
650
745
  init_odla_home();
651
746
  init_local();
@@ -687,7 +782,7 @@ var init_cached_credential = __esm({
687
782
  });
688
783
 
689
784
  // src/device-session-cache.ts
690
- async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = import_node_process7.default.env) {
785
+ async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = import_node_process8.default.env) {
691
786
  const path = deviceSessionFile(env);
692
787
  const cached = readJsonFile(path);
693
788
  if (cached?.token && cached.platform === audience && cached.deviceId === credential2.deviceId && (cached.expiresAt ?? 0) > Date.now() + SKEW_MS) return cached;
@@ -700,12 +795,12 @@ async function deviceSessionToken(platformUrl, audience, credential2, doFetch, e
700
795
  writePrivateJson(path, session);
701
796
  return session;
702
797
  }
703
- var import_node_process7, SKEW_MS;
798
+ var import_node_process8, SKEW_MS;
704
799
  var init_device_session_cache = __esm({
705
800
  "src/device-session-cache.ts"() {
706
801
  "use strict";
707
802
  init_cjs_shims();
708
- import_node_process7 = __toESM(require("process"), 1);
803
+ import_node_process8 = __toESM(require("process"), 1);
709
804
  init_odla_home();
710
805
  init_local();
711
806
  init_device_session();
@@ -714,23 +809,23 @@ var init_device_session_cache = __esm({
714
809
  });
715
810
 
716
811
  // src/machine-identity.ts
717
- function readMachineIdentity(audience, env = import_node_process8.default.env) {
812
+ function readMachineIdentity(audience, env = import_node_process9.default.env) {
718
813
  const stored = readJsonFile(identityFile(env));
719
814
  if (!stored || typeof stored.email !== "string" || !stored.email) return null;
720
815
  return stored.platform === audience ? { platform: audience, email: stored.email } : null;
721
816
  }
722
- function rememberMachineIdentity(audience, email, env = import_node_process8.default.env) {
817
+ function rememberMachineIdentity(audience, email, env = import_node_process9.default.env) {
723
818
  if (!email) return;
724
819
  const existing = readMachineIdentity(audience, env);
725
820
  if (existing?.email === email) return;
726
821
  writePrivateJson(identityFile(env), { platform: audience, email });
727
822
  }
728
- var import_node_process8;
823
+ var import_node_process9;
729
824
  var init_machine_identity = __esm({
730
825
  "src/machine-identity.ts"() {
731
826
  "use strict";
732
827
  init_cjs_shims();
733
- import_node_process8 = __toESM(require("process"), 1);
828
+ import_node_process9 = __toESM(require("process"), 1);
734
829
  init_odla_home();
735
830
  init_local();
736
831
  }
@@ -744,14 +839,14 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
744
839
  const cached = readJsonFile(cfg.local.tokenFile);
745
840
  if (!grantRequest.forceReview && !grantRequest.freshLogin) {
746
841
  if (options.token) return options.token;
747
- if (import_node_process9.default.env.ODLA_DEV_TOKEN) {
748
- const declared = import_node_process9.default.env.ODLA_DEV_TOKEN_AUDIENCE;
842
+ if (import_node_process10.default.env.ODLA_DEV_TOKEN) {
843
+ const declared = import_node_process10.default.env.ODLA_DEV_TOKEN_AUDIENCE;
749
844
  if (declared) {
750
845
  if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
751
846
  } else if (audience !== "https://odla.ai") {
752
847
  throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
753
848
  }
754
- return import_node_process9.default.env.ODLA_DEV_TOKEN;
849
+ return import_node_process10.default.env.ODLA_DEV_TOKEN;
755
850
  }
756
851
  const device = readDeviceCredential(audience);
757
852
  if (device) {
@@ -866,7 +961,7 @@ function stillPending(pending, email) {
866
961
  );
867
962
  }
868
963
  function handshakeEmail(value2, cached) {
869
- const email = (value2 ?? import_node_process9.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
964
+ const email = (value2 ?? import_node_process10.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
870
965
  if (/@users\.noreply\.github\.com$/i.test(email)) {
871
966
  throw new Error(
872
967
  `"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
@@ -895,14 +990,14 @@ function platformAudience(value2) {
895
990
  }
896
991
  return url.origin;
897
992
  }
898
- var import_db, import_node_crypto, import_node_process9;
993
+ var import_db, import_node_crypto, import_node_process10;
899
994
  var init_token = __esm({
900
995
  "src/token.ts"() {
901
996
  "use strict";
902
997
  init_cjs_shims();
903
998
  import_db = require("@odla-ai/db");
904
999
  import_node_crypto = require("crypto");
905
- import_node_process9 = __toESM(require("process"), 1);
1000
+ import_node_process10 = __toESM(require("process"), 1);
906
1001
  init_handshake_approval();
907
1002
  init_handshake_state();
908
1003
  init_cached_credential();
@@ -917,7 +1012,7 @@ var init_token = __esm({
917
1012
  async function secretInputValue(options, kind = "credential") {
918
1013
  if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
919
1014
  let value2;
920
- if (options.fromEnv) value2 = import_node_process10.default.env[options.fromEnv];
1015
+ if (options.fromEnv) value2 = import_node_process11.default.env[options.fromEnv];
921
1016
  else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
922
1017
  else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
923
1018
  value2 = value2?.replace(/[\r\n]+$/, "");
@@ -925,7 +1020,7 @@ async function secretInputValue(options, kind = "credential") {
925
1020
  if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
926
1021
  return value2;
927
1022
  }
928
- async function readSecretStream(kind, stream = import_node_process10.default.stdin) {
1023
+ async function readSecretStream(kind, stream = import_node_process11.default.stdin) {
929
1024
  let value2 = "";
930
1025
  for await (const chunk of stream) {
931
1026
  value2 += String(chunk);
@@ -933,12 +1028,12 @@ async function readSecretStream(kind, stream = import_node_process10.default.std
933
1028
  }
934
1029
  return value2;
935
1030
  }
936
- var import_node_process10, MAX_BYTES;
1031
+ var import_node_process11, MAX_BYTES;
937
1032
  var init_secret_input = __esm({
938
1033
  "src/secret-input.ts"() {
939
1034
  "use strict";
940
1035
  init_cjs_shims();
941
- import_node_process10 = __toESM(require("process"), 1);
1036
+ import_node_process11 = __toESM(require("process"), 1);
942
1037
  MAX_BYTES = 64 * 1024;
943
1038
  }
944
1039
  });
@@ -950,7 +1045,7 @@ async function getScopedPlatformToken(options) {
950
1045
  async function resolveAdminPlatformToken(options) {
951
1046
  const audience = platformAudience(options.platform);
952
1047
  if (options.token) return options.token;
953
- const fromEnv = import_node_process11.default.env.ODLA_ADMIN_TOKEN;
1048
+ const fromEnv = import_node_process12.default.env.ODLA_ADMIN_TOKEN;
954
1049
  if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
955
1050
  return scopedToken(
956
1051
  audience,
@@ -962,7 +1057,7 @@ async function resolveAdminPlatformToken(options) {
962
1057
  }
963
1058
  function audienceBoundEnvToken(token, platform) {
964
1059
  const audience = platformAudience(platform);
965
- const declared = import_node_process11.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
1060
+ const declared = import_node_process12.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
966
1061
  if (declared) {
967
1062
  if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
968
1063
  } else if (audience !== "https://odla.ai") {
@@ -972,9 +1067,9 @@ function audienceBoundEnvToken(token, platform) {
972
1067
  }
973
1068
  async function scopedToken(platform, scope, options, doFetch, out) {
974
1069
  const audience = platformAudience(platform);
975
- const rootDir = options.rootDir ?? import_node_process11.default.cwd();
1070
+ const rootDir = options.rootDir ?? import_node_process12.default.cwd();
976
1071
  const tokenFile = options.tokenFile ?? scopedTokenFile();
977
- adoptRepoLocalCache((0, import_node_path6.join)(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
1072
+ adoptRepoLocalCache((0, import_node_path7.join)(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
978
1073
  const device = readDeviceCredential(audience);
979
1074
  if (device && options.cache !== false) {
980
1075
  const session = await deviceSessionToken(platform, audience, device, doFetch);
@@ -1021,13 +1116,13 @@ async function scopedToken(platform, scope, options, doFetch, out) {
1021
1116
  }
1022
1117
  return token;
1023
1118
  }
1024
- var import_node_path6, import_node_process11, import_db2, SCOPE_PURPOSE;
1119
+ var import_node_path7, import_node_process12, import_db2, SCOPE_PURPOSE;
1025
1120
  var init_admin_ai_auth = __esm({
1026
1121
  "src/admin-ai-auth.ts"() {
1027
1122
  "use strict";
1028
1123
  init_cjs_shims();
1029
- import_node_path6 = require("path");
1030
- import_node_process11 = __toESM(require("process"), 1);
1124
+ import_node_path7 = require("path");
1125
+ import_node_process12 = __toESM(require("process"), 1);
1031
1126
  import_db2 = require("@odla-ai/db");
1032
1127
  init_local();
1033
1128
  init_handshake_approval();
@@ -1252,7 +1347,7 @@ var init_admin_ai_usage = __esm({
1252
1347
 
1253
1348
  // src/admin-ai.ts
1254
1349
  async function adminAi(options) {
1255
- const platform = platformAudience(options.platform ?? import_node_process12.default.env.ODLA_PLATFORM ?? "https://odla.ai");
1350
+ const platform = platformAudience(options.platform ?? import_node_process13.default.env.ODLA_PLATFORM ?? "https://odla.ai");
1256
1351
  const doFetch = options.fetch ?? fetch;
1257
1352
  const out = options.stdout ?? console;
1258
1353
  const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
@@ -1430,12 +1525,12 @@ function apiError3(action2, status, body) {
1430
1525
  function isRecord3(value2) {
1431
1526
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
1432
1527
  }
1433
- var import_node_process12;
1528
+ var import_node_process13;
1434
1529
  var init_admin_ai = __esm({
1435
1530
  "src/admin-ai.ts"() {
1436
1531
  "use strict";
1437
1532
  init_cjs_shims();
1438
- import_node_process12 = __toESM(require("process"), 1);
1533
+ import_node_process13 = __toESM(require("process"), 1);
1439
1534
  init_token();
1440
1535
  init_secret_input();
1441
1536
  init_admin_ai_auth();
@@ -2213,12 +2308,12 @@ var init_monitoring_validation = __esm({
2213
2308
 
2214
2309
  // src/config.ts
2215
2310
  async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
2216
- const resolved = (0, import_node_path7.resolve)(configPath);
2311
+ const resolved = (0, import_node_path8.resolve)(configPath);
2217
2312
  if (!(0, import_node_fs9.existsSync)(resolved)) {
2218
2313
  throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
2219
2314
  }
2220
2315
  const raw = await loadConfigModule(resolved);
2221
- const rootDir = (0, import_node_path7.dirname)(resolved);
2316
+ const rootDir = (0, import_node_path8.dirname)(resolved);
2222
2317
  validateRawConfig(raw, resolved);
2223
2318
  const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
2224
2319
  const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
@@ -2228,14 +2323,14 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
2228
2323
  validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
2229
2324
  validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
2230
2325
  const local = {
2231
- tokenFile: raw.local?.tokenFile ? (0, import_node_path7.resolve)(rootDir, raw.local.tokenFile) : appTokenFile(raw.app.id),
2232
- credentialsFile: raw.local?.credentialsFile ? (0, import_node_path7.resolve)(rootDir, raw.local.credentialsFile) : appCredentialsFile(raw.app.id),
2233
- devVarsFile: (0, import_node_path7.resolve)(rootDir, raw.local?.devVarsFile ?? ".dev.vars"),
2326
+ tokenFile: raw.local?.tokenFile ? (0, import_node_path8.resolve)(rootDir, raw.local.tokenFile) : appTokenFile(raw.app.id),
2327
+ credentialsFile: raw.local?.credentialsFile ? (0, import_node_path8.resolve)(rootDir, raw.local.credentialsFile) : appCredentialsFile(raw.app.id),
2328
+ devVarsFile: (0, import_node_path8.resolve)(rootDir, raw.local?.devVarsFile ?? ".dev.vars"),
2234
2329
  gitignore: raw.local?.gitignore ?? true
2235
2330
  };
2236
- adoptRepoLocalCache((0, import_node_path7.resolve)(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
2237
- adoptRepoLocalCache((0, import_node_path7.resolve)(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
2238
- (0, import_node_fs9.rmSync)((0, import_node_path7.resolve)(rootDir, ".odla/handshake.local.json"), { force: true });
2331
+ adoptRepoLocalCache((0, import_node_path8.resolve)(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
2332
+ adoptRepoLocalCache((0, import_node_path8.resolve)(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
2333
+ (0, import_node_fs9.rmSync)((0, import_node_path8.resolve)(rootDir, ".odla/handshake.local.json"), { force: true });
2239
2334
  return {
2240
2335
  ...raw,
2241
2336
  configPath: resolved,
@@ -2250,7 +2345,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
2250
2345
  async function resolveDataExport(cfg, value2, names) {
2251
2346
  if (value2 === void 0 || value2 === null || value2 === false) return void 0;
2252
2347
  if (typeof value2 !== "string") return value2;
2253
- const target = (0, import_node_path7.isAbsolute)(value2) ? value2 : (0, import_node_path7.resolve)(cfg.rootDir, value2);
2348
+ const target = (0, import_node_path8.isAbsolute)(value2) ? value2 : (0, import_node_path8.resolve)(cfg.rootDir, value2);
2254
2349
  if (target.endsWith(".json")) {
2255
2350
  return JSON.parse((0, import_node_fs9.readFileSync)(target, "utf8"));
2256
2351
  }
@@ -2341,13 +2436,13 @@ function trimSlash(value2) {
2341
2436
  function unique3(values) {
2342
2437
  return [...new Set(values.filter(Boolean))];
2343
2438
  }
2344
- var import_node_fs9, import_node_path7, import_node_url, import_apps, DEFAULT_PLATFORM, DEFAULT_ENVS, DEFAULT_SERVICES, configImportSerial, GOOGLE_CALENDAR_EVENTS_SCOPE, stderr;
2439
+ var import_node_fs9, import_node_path8, import_node_url, import_apps, DEFAULT_PLATFORM, DEFAULT_ENVS, DEFAULT_SERVICES, configImportSerial, GOOGLE_CALENDAR_EVENTS_SCOPE, stderr;
2345
2440
  var init_config = __esm({
2346
2441
  "src/config.ts"() {
2347
2442
  "use strict";
2348
2443
  init_cjs_shims();
2349
2444
  import_node_fs9 = require("fs");
2350
- import_node_path7 = require("path");
2445
+ import_node_path8 = require("path");
2351
2446
  import_node_url = require("url");
2352
2447
  import_apps = require("@odla-ai/apps");
2353
2448
  init_ai_config_validation();
@@ -2370,13 +2465,13 @@ var init_config = __esm({
2370
2465
 
2371
2466
  // src/operator-profiles.ts
2372
2467
  function operatorProfileFile() {
2373
- return (0, import_node_path8.resolve)(
2374
- clean(import_node_process13.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".odla", "contexts.json")
2468
+ return (0, import_node_path9.resolve)(
2469
+ clean(import_node_process14.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".odla", "contexts.json")
2375
2470
  );
2376
2471
  }
2377
2472
  function resolveOperatorProfile(parsed) {
2378
2473
  const fromFlag = clean(stringOpt(parsed.options.context));
2379
- const fromEnvironment = clean(import_node_process13.default.env.ODLA_CONTEXT);
2474
+ const fromEnvironment = clean(import_node_process14.default.env.ODLA_CONTEXT);
2380
2475
  const name = fromFlag ?? fromEnvironment ?? null;
2381
2476
  const file = operatorProfileFile();
2382
2477
  if (!name) {
@@ -2416,10 +2511,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
2416
2511
  return true;
2417
2512
  }
2418
2513
  function operatorCredentialFiles(selection) {
2419
- const base = selection.name ? (0, import_node_path8.join)((0, import_node_path8.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".odla");
2514
+ const base = selection.name ? (0, import_node_path9.join)((0, import_node_path9.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".odla");
2420
2515
  return {
2421
- developer: (0, import_node_path8.join)(base, "dev-token.json"),
2422
- scoped: (0, import_node_path8.join)(base, "admin-token.local.json")
2516
+ developer: (0, import_node_path9.join)(base, "dev-token.json"),
2517
+ scoped: (0, import_node_path9.join)(base, "admin-token.local.json")
2423
2518
  };
2424
2519
  }
2425
2520
  function assertOperatorName(value2, label) {
@@ -2491,15 +2586,15 @@ function clean(value2) {
2491
2586
  const normalized = value2?.trim();
2492
2587
  return normalized || void 0;
2493
2588
  }
2494
- var import_node_fs10, import_node_os3, import_node_path8, import_node_process13;
2589
+ var import_node_fs10, import_node_os3, import_node_path9, import_node_process14;
2495
2590
  var init_operator_profiles = __esm({
2496
2591
  "src/operator-profiles.ts"() {
2497
2592
  "use strict";
2498
2593
  init_cjs_shims();
2499
2594
  import_node_fs10 = require("fs");
2500
2595
  import_node_os3 = require("os");
2501
- import_node_path8 = require("path");
2502
- import_node_process13 = __toESM(require("process"), 1);
2596
+ import_node_path9 = require("path");
2597
+ import_node_process14 = __toESM(require("process"), 1);
2503
2598
  init_argv();
2504
2599
  init_local();
2505
2600
  init_token();
@@ -2510,7 +2605,7 @@ var init_operator_profiles = __esm({
2510
2605
  async function resolveOperatorContext(parsed, options = {}) {
2511
2606
  const profile = resolveOperatorProfile(parsed);
2512
2607
  const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
2513
- const configPath = (0, import_node_path9.resolve)(configArgument);
2608
+ const configPath = (0, import_node_path10.resolve)(configArgument);
2514
2609
  const explicitConfig = parsed.options.config !== void 0;
2515
2610
  const hasConfig = (0, import_node_fs11.existsSync)(configPath);
2516
2611
  if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
@@ -2518,13 +2613,13 @@ async function resolveOperatorContext(parsed, options = {}) {
2518
2613
  }
2519
2614
  const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
2520
2615
  const platformFlag = clean2(stringOpt(parsed.options.platform));
2521
- const platformEnvironment = clean2(import_node_process14.default.env.ODLA_PLATFORM_URL);
2616
+ const platformEnvironment = clean2(import_node_process15.default.env.ODLA_PLATFORM_URL);
2522
2617
  const platformValue = platformAudience(
2523
2618
  platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
2524
2619
  );
2525
2620
  const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
2526
2621
  const appFlag = clean2(stringOpt(parsed.options.app));
2527
- const appEnvironment = clean2(import_node_process14.default.env.ODLA_APP_ID);
2622
+ const appEnvironment = clean2(import_node_process15.default.env.ODLA_APP_ID);
2528
2623
  const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
2529
2624
  const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
2530
2625
  if (appValue) {
@@ -2538,16 +2633,16 @@ async function resolveOperatorContext(parsed, options = {}) {
2538
2633
  );
2539
2634
  }
2540
2635
  const envFlag = clean2(stringOpt(parsed.options.env));
2541
- const envEnvironment = clean2(import_node_process14.default.env.ODLA_ENV);
2636
+ const envEnvironment = clean2(import_node_process15.default.env.ODLA_ENV);
2542
2637
  const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
2543
2638
  const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
2544
2639
  if (environmentValue) {
2545
2640
  assertOperatorName(environmentValue, "environment");
2546
2641
  }
2547
- const rootDir = loaded?.rootDir ?? import_node_process14.default.cwd();
2642
+ const rootDir = loaded?.rootDir ?? import_node_process15.default.cwd();
2548
2643
  const profileCredentials = operatorCredentialFiles(profile);
2549
- const tokenFile = clean2(import_node_process14.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path9.resolve)(import_node_process14.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
2550
- const scopedTokenFile2 = clean2(import_node_process14.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path9.resolve)(import_node_process14.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path9.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
2644
+ const tokenFile = clean2(import_node_process15.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path10.resolve)(import_node_process15.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
2645
+ const scopedTokenFile2 = clean2(import_node_process15.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path10.resolve)(import_node_process15.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path10.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
2551
2646
  const cfg = loaded ? {
2552
2647
  ...loaded,
2553
2648
  platformUrl: platformValue,
@@ -2569,8 +2664,8 @@ async function resolveOperatorContext(parsed, options = {}) {
2569
2664
  services: [],
2570
2665
  local: {
2571
2666
  tokenFile,
2572
- credentialsFile: (0, import_node_path9.join)(rootDir, ".odla", "credentials.local.json"),
2573
- devVarsFile: (0, import_node_path9.join)(rootDir, ".dev.vars"),
2667
+ credentialsFile: (0, import_node_path10.join)(rootDir, ".odla", "credentials.local.json"),
2668
+ devVarsFile: (0, import_node_path10.join)(rootDir, ".dev.vars"),
2574
2669
  gitignore: true
2575
2670
  }
2576
2671
  };
@@ -2602,14 +2697,14 @@ function clean2(value2) {
2602
2697
  const normalized = value2?.trim();
2603
2698
  return normalized || void 0;
2604
2699
  }
2605
- var import_node_fs11, import_node_path9, import_node_process14, DEFAULT_PLATFORM2;
2700
+ var import_node_fs11, import_node_path10, import_node_process15, DEFAULT_PLATFORM2;
2606
2701
  var init_operator_context = __esm({
2607
2702
  "src/operator-context.ts"() {
2608
2703
  "use strict";
2609
2704
  init_cjs_shims();
2610
2705
  import_node_fs11 = require("fs");
2611
- import_node_path9 = require("path");
2612
- import_node_process14 = __toESM(require("process"), 1);
2706
+ import_node_path10 = require("path");
2707
+ import_node_process15 = __toESM(require("process"), 1);
2613
2708
  init_argv();
2614
2709
  init_config();
2615
2710
  init_operator_profiles();
@@ -2915,7 +3010,7 @@ async function authCommand(parsed, deps = {}) {
2915
3010
  const { cfg } = context;
2916
3011
  const out = deps.stdout ?? console;
2917
3012
  const doFetch = deps.fetch ?? fetch;
2918
- const email = stringOpt(parsed.options.email) ?? import_node_process15.default.env.ODLA_USER_EMAIL?.trim();
3013
+ const email = stringOpt(parsed.options.email) ?? import_node_process16.default.env.ODLA_USER_EMAIL?.trim();
2919
3014
  if (!email) {
2920
3015
  throw new Error(
2921
3016
  "auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
@@ -2954,12 +3049,12 @@ async function authCommand(parsed, deps = {}) {
2954
3049
  out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
2955
3050
  out.log(`odla account: ${identity.email ?? "not returned"}`);
2956
3051
  }
2957
- var import_node_process15;
3052
+ var import_node_process16;
2958
3053
  var init_auth_command = __esm({
2959
3054
  "src/auth-command.ts"() {
2960
3055
  "use strict";
2961
3056
  init_cjs_shims();
2962
- import_node_process15 = __toESM(require("process"), 1);
3057
+ import_node_process16 = __toESM(require("process"), 1);
2963
3058
  init_argv();
2964
3059
  init_operator_context();
2965
3060
  init_token();
@@ -3479,15 +3574,15 @@ var init_brand_design_unpack = __esm({
3479
3574
 
3480
3575
  // src/brand-command.ts
3481
3576
  async function readBundle(source, deps) {
3482
- if (source !== "-") return (0, import_promises.readFile)((0, import_node_path10.resolve)(source), "utf8");
3577
+ if (source !== "-") return (0, import_promises.readFile)((0, import_node_path11.resolve)(source), "utf8");
3483
3578
  const readStdin = deps.readStdin;
3484
3579
  if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
3485
3580
  return readStdin();
3486
3581
  }
3487
3582
  async function writeAll(result, outDir) {
3488
3583
  for (const file of result.files) {
3489
- const target = (0, import_node_path10.resolve)(outDir, file.path);
3490
- await (0, import_promises.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
3584
+ const target = (0, import_node_path11.resolve)(outDir, file.path);
3585
+ await (0, import_promises.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
3491
3586
  await (0, import_promises.writeFile)(target, file.bytes);
3492
3587
  }
3493
3588
  }
@@ -3495,7 +3590,7 @@ async function designUnpack(parsed, deps) {
3495
3590
  assertArgs(parsed, ["out", "json"], 4);
3496
3591
  const source = parsed.positionals[3];
3497
3592
  if (!source) throw new Error(USAGE);
3498
- const outDir = (0, import_node_path10.resolve)(stringOpt(parsed.options.out) ?? "design");
3593
+ const outDir = (0, import_node_path11.resolve)(stringOpt(parsed.options.out) ?? "design");
3499
3594
  const result = unpackDesign(await readBundle(source, deps));
3500
3595
  await writeAll(result, outDir);
3501
3596
  const out = deps.stdout ?? console;
@@ -3522,13 +3617,13 @@ async function brandCommand(parsed, deps) {
3522
3617
  if (subject !== "design") rejectWord(["brand"], subject);
3523
3618
  rejectWord(["brand", "design"], action2, USAGE);
3524
3619
  }
3525
- var import_promises, import_node_path10, USAGE;
3620
+ var import_promises, import_node_path11, USAGE;
3526
3621
  var init_brand_command = __esm({
3527
3622
  "src/brand-command.ts"() {
3528
3623
  "use strict";
3529
3624
  init_cjs_shims();
3530
3625
  import_promises = require("fs/promises");
3531
- import_node_path10 = require("path");
3626
+ import_node_path11 = require("path");
3532
3627
  init_argv();
3533
3628
  init_brand_design_unpack();
3534
3629
  init_surface();
@@ -4577,7 +4672,7 @@ async function operationClient(cfg, options, purpose) {
4577
4672
  platform: cfg.platformUrl,
4578
4673
  scope: "app:config:write",
4579
4674
  token: options.token,
4580
- tokenFile: (0, import_node_path11.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
4675
+ tokenFile: (0, import_node_path12.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
4581
4676
  rootDir: cfg.rootDir,
4582
4677
  email: options.email,
4583
4678
  open: options.open,
@@ -4629,13 +4724,13 @@ function normalizeRequestError(error) {
4629
4724
  function record4(value2) {
4630
4725
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
4631
4726
  }
4632
- var import_apps6, import_node_path11, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
4727
+ var import_apps6, import_node_path12, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
4633
4728
  var init_config_operation_command = __esm({
4634
4729
  "src/config-operation-command.ts"() {
4635
4730
  "use strict";
4636
4731
  init_cjs_shims();
4637
4732
  import_apps6 = require("@odla-ai/apps");
4638
- import_node_path11 = require("path");
4733
+ import_node_path12 = require("path");
4639
4734
  init_admin_ai_auth();
4640
4735
  init_version();
4641
4736
  init_config();
@@ -4958,7 +5053,7 @@ async function inspectConfig(options) {
4958
5053
  platform: cfg.platformUrl,
4959
5054
  scope: "app:config:read",
4960
5055
  token: options.token,
4961
- tokenFile: (0, import_node_path12.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
5056
+ tokenFile: (0, import_node_path13.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
4962
5057
  rootDir: cfg.rootDir,
4963
5058
  email: options.email,
4964
5059
  open: options.open,
@@ -5087,13 +5182,13 @@ function studioSettingsUrl(reconciliation) {
5087
5182
  function quoteArg2(value2) {
5088
5183
  return `'${value2.replace(/'/g, `'\\''`)}'`;
5089
5184
  }
5090
- var import_apps8, import_node_path12;
5185
+ var import_apps8, import_node_path13;
5091
5186
  var init_config_reconcile_command = __esm({
5092
5187
  "src/config-reconcile-command.ts"() {
5093
5188
  "use strict";
5094
5189
  init_cjs_shims();
5095
5190
  import_apps8 = require("@odla-ai/apps");
5096
- import_node_path12 = require("path");
5191
+ import_node_path13 = require("path");
5097
5192
  init_admin_ai_auth();
5098
5193
  init_config();
5099
5194
  init_config_reconcile_digest();
@@ -5106,7 +5201,7 @@ var init_config_reconcile_command = __esm({
5106
5201
  // src/wrangler.ts
5107
5202
  function findWranglerConfig(rootDir) {
5108
5203
  for (const name of WRANGLER_CONFIG_FILES) {
5109
- const path = (0, import_node_path13.join)(rootDir, name);
5204
+ const path = (0, import_node_path14.join)(rootDir, name);
5110
5205
  if ((0, import_node_fs14.existsSync)(path)) return path;
5111
5206
  }
5112
5207
  return null;
@@ -5218,16 +5313,16 @@ function wranglerBulkSecrets(run, opts) {
5218
5313
  ];
5219
5314
  return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
5220
5315
  }
5221
- var import_node_child_process2, import_node_fs14, import_node_path13, defaultRunner, WRANGLER_CONFIG_FILES;
5316
+ var import_node_child_process3, import_node_fs14, import_node_path14, defaultRunner, WRANGLER_CONFIG_FILES;
5222
5317
  var init_wrangler = __esm({
5223
5318
  "src/wrangler.ts"() {
5224
5319
  "use strict";
5225
5320
  init_cjs_shims();
5226
- import_node_child_process2 = require("child_process");
5321
+ import_node_child_process3 = require("child_process");
5227
5322
  import_node_fs14 = require("fs");
5228
- import_node_path13 = require("path");
5323
+ import_node_path14 = require("path");
5229
5324
  defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
5230
- const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
5325
+ const child = (0, import_node_child_process3.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
5231
5326
  let stdout = "";
5232
5327
  let stderr2 = "";
5233
5328
  child.stdout.on("data", (chunk) => stdout += chunk.toString());
@@ -5284,10 +5379,10 @@ function wranglerWarnings(rootDir) {
5284
5379
  for (const { label, block: block2 } of blocks) {
5285
5380
  const assets = block2.assets;
5286
5381
  if (assets?.directory) {
5287
- const dir = (0, import_node_path14.resolve)(rootDir, assets.directory);
5288
- if (dir === (0, import_node_path14.resolve)(rootDir)) {
5382
+ const dir = (0, import_node_path15.resolve)(rootDir, assets.directory);
5383
+ if (dir === (0, import_node_path15.resolve)(rootDir)) {
5289
5384
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
5290
- } else if ((0, import_node_fs15.existsSync)((0, import_node_path14.join)(dir, "node_modules"))) {
5385
+ } else if ((0, import_node_fs15.existsSync)((0, import_node_path15.join)(dir, "node_modules"))) {
5291
5386
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
5292
5387
  }
5293
5388
  }
@@ -5322,7 +5417,7 @@ function o11yProjectWarnings(rootDir) {
5322
5417
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
5323
5418
  return warnings;
5324
5419
  }
5325
- const main = typeof config.main === "string" ? (0, import_node_path14.resolve)(rootDir, config.main) : null;
5420
+ const main = typeof config.main === "string" ? (0, import_node_path15.resolve)(rootDir, config.main) : null;
5326
5421
  if (!main || !(0, import_node_fs15.existsSync)(main)) {
5327
5422
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
5328
5423
  } else {
@@ -5352,23 +5447,23 @@ function calendarProjectWarnings(rootDir) {
5352
5447
  }
5353
5448
  function readPackageJson(rootDir) {
5354
5449
  try {
5355
- return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(rootDir, "package.json"), "utf8"));
5450
+ return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path15.join)(rootDir, "package.json"), "utf8"));
5356
5451
  } catch {
5357
5452
  return null;
5358
5453
  }
5359
5454
  }
5360
- var import_node_child_process3, import_node_fs15, import_node_path14, defaultExec;
5455
+ var import_node_child_process4, import_node_fs15, import_node_path15, defaultExec;
5361
5456
  var init_doctor_checks = __esm({
5362
5457
  "src/doctor-checks.ts"() {
5363
5458
  "use strict";
5364
5459
  init_cjs_shims();
5365
- import_node_child_process3 = require("child_process");
5460
+ import_node_child_process4 = require("child_process");
5366
5461
  import_node_fs15 = require("fs");
5367
- import_node_path14 = require("path");
5462
+ import_node_path15 = require("path");
5368
5463
  init_redact();
5369
5464
  init_local();
5370
5465
  init_wrangler();
5371
- defaultExec = (cmd, args, cwd) => (0, import_node_child_process3.execFileSync)(cmd, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
5466
+ defaultExec = (cmd, args, cwd) => (0, import_node_child_process4.execFileSync)(cmd, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
5372
5467
  }
5373
5468
  });
5374
5469
 
@@ -5701,8 +5796,8 @@ var init_harness_options = __esm({
5701
5796
  // src/init.ts
5702
5797
  function initProject(options) {
5703
5798
  const out = options.stdout ?? console;
5704
- const rootDir = (0, import_node_path15.resolve)(options.rootDir ?? process.cwd());
5705
- const configPath = (0, import_node_path15.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5799
+ const rootDir = (0, import_node_path16.resolve)(options.rootDir ?? process.cwd());
5800
+ const configPath = (0, import_node_path16.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5706
5801
  if ((0, import_node_fs16.existsSync)(configPath) && !options.force) {
5707
5802
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
5708
5803
  }
@@ -5719,12 +5814,12 @@ function initProject(options) {
5719
5814
  }
5720
5815
  }
5721
5816
  const aiProvider = options.aiProvider;
5722
- (0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(configPath), { recursive: true });
5723
- (0, import_node_fs16.mkdirSync)((0, import_node_path15.resolve)(rootDir, "src/odla"), { recursive: true });
5724
- (0, import_node_fs16.mkdirSync)((0, import_node_path15.resolve)(rootDir, ".odla"), { recursive: true });
5817
+ (0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(configPath), { recursive: true });
5818
+ (0, import_node_fs16.mkdirSync)((0, import_node_path16.resolve)(rootDir, "src/odla"), { recursive: true });
5819
+ (0, import_node_fs16.mkdirSync)((0, import_node_path16.resolve)(rootDir, ".odla"), { recursive: true });
5725
5820
  (0, import_node_fs16.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
5726
- writeIfMissing((0, import_node_path15.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5727
- writeIfMissing((0, import_node_path15.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
5821
+ writeIfMissing((0, import_node_path16.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5822
+ writeIfMissing((0, import_node_path16.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
5728
5823
  ensureGitignore(rootDir);
5729
5824
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
5730
5825
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -5837,13 +5932,13 @@ function defaultKeyEnv(provider) {
5837
5932
  function relativeDisplay(path, rootDir) {
5838
5933
  return path.startsWith(rootDir) ? path.slice(rootDir.length + 1) : path;
5839
5934
  }
5840
- var import_node_fs16, import_node_path15, import_apps9;
5935
+ var import_node_fs16, import_node_path16, import_apps9;
5841
5936
  var init_init = __esm({
5842
5937
  "src/init.ts"() {
5843
5938
  "use strict";
5844
5939
  init_cjs_shims();
5845
5940
  import_node_fs16 = require("fs");
5846
- import_node_path15 = require("path");
5941
+ import_node_path16 = require("path");
5847
5942
  import_apps9 = require("@odla-ai/apps");
5848
5943
  init_local();
5849
5944
  }
@@ -6176,8 +6271,8 @@ function installSkill(options = {}) {
6176
6271
  const files = listFiles(sourceDir);
6177
6272
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
6178
6273
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
6179
- const root = (0, import_node_path16.resolve)(options.dir ?? process.cwd());
6180
- const home = (0, import_node_path16.resolve)(options.homeDir ?? (0, import_node_os4.homedir)());
6274
+ const root = (0, import_node_path17.resolve)(options.dir ?? process.cwd());
6275
+ const home = (0, import_node_path17.resolve)(options.homeDir ?? (0, import_node_os4.homedir)());
6181
6276
  const plans = /* @__PURE__ */ new Map();
6182
6277
  const targets = /* @__PURE__ */ new Map();
6183
6278
  const rememberTarget = (harness, target) => {
@@ -6191,48 +6286,48 @@ function installSkill(options = {}) {
6191
6286
  plans.set(target, { target, content: content2, boundary, managedMerge });
6192
6287
  };
6193
6288
  const planSkillTree = (targetDir2, boundary = root) => {
6194
- for (const rel of files) plan((0, import_node_path16.join)(targetDir2, rel), (0, import_node_fs17.readFileSync)((0, import_node_path16.join)(sourceDir, rel), "utf8"), false, boundary);
6289
+ for (const rel of files) plan((0, import_node_path17.join)(targetDir2, rel), (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(sourceDir, rel), "utf8"), false, boundary);
6195
6290
  };
6196
6291
  let targetDir;
6197
6292
  if (options.global) {
6198
- const claudeRoot = (0, import_node_path16.join)(home, ".claude", "skills");
6199
- const codexRoot = (0, import_node_path16.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path16.join)(home, ".codex"), "skills");
6293
+ const claudeRoot = (0, import_node_path17.join)(home, ".claude", "skills");
6294
+ const codexRoot = (0, import_node_path17.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path17.join)(home, ".codex"), "skills");
6200
6295
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
6201
6296
  for (const harness of harnesses) {
6202
6297
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
6203
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path16.dirname)((0, import_node_path16.dirname)(codexRoot)));
6298
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path17.dirname)((0, import_node_path17.dirname)(codexRoot)));
6204
6299
  rememberTarget(harness, skillRoot);
6205
6300
  }
6206
6301
  } else {
6207
- const sharedRoot = (0, import_node_path16.join)(root, ".agents", "skills");
6302
+ const sharedRoot = (0, import_node_path17.join)(root, ".agents", "skills");
6208
6303
  planSkillTree(sharedRoot);
6209
- const claudeRoot = (0, import_node_path16.join)(root, ".claude", "skills");
6304
+ const claudeRoot = (0, import_node_path17.join)(root, ".claude", "skills");
6210
6305
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
6211
6306
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
6212
6307
  if (harnesses.includes("claude")) {
6213
6308
  for (const skill of skillNames(files)) {
6214
- const canonical2 = (0, import_node_fs17.readFileSync)((0, import_node_path16.join)(sourceDir, skill, "SKILL.md"), "utf8");
6215
- plan((0, import_node_path16.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
6309
+ const canonical2 = (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(sourceDir, skill, "SKILL.md"), "utf8");
6310
+ plan((0, import_node_path17.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
6216
6311
  }
6217
6312
  rememberTarget("claude", claudeRoot);
6218
6313
  }
6219
6314
  if (harnesses.includes("cursor")) {
6220
- const cursorRule = (0, import_node_path16.join)(root, ".cursor", "rules", "odla.mdc");
6315
+ const cursorRule = (0, import_node_path17.join)(root, ".cursor", "rules", "odla.mdc");
6221
6316
  plan(cursorRule, CURSOR_RULE);
6222
6317
  rememberTarget("cursor", cursorRule);
6223
6318
  }
6224
6319
  if (harnesses.includes("agents")) {
6225
- const agentsFile = (0, import_node_path16.join)(root, "AGENTS.md");
6320
+ const agentsFile = (0, import_node_path17.join)(root, "AGENTS.md");
6226
6321
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
6227
6322
  rememberTarget("agents", agentsFile);
6228
6323
  }
6229
6324
  if (harnesses.includes("copilot")) {
6230
- const copilotFile = (0, import_node_path16.join)(root, ".github", "copilot-instructions.md");
6325
+ const copilotFile = (0, import_node_path17.join)(root, ".github", "copilot-instructions.md");
6231
6326
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
6232
6327
  rememberTarget("copilot", copilotFile);
6233
6328
  }
6234
6329
  if (harnesses.includes("gemini")) {
6235
- const geminiFile = (0, import_node_path16.join)(root, "GEMINI.md");
6330
+ const geminiFile = (0, import_node_path17.join)(root, "GEMINI.md");
6236
6331
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
6237
6332
  rememberTarget("gemini", geminiFile);
6238
6333
  }
@@ -6268,7 +6363,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
6268
6363
  }
6269
6364
  for (const file of plans.values()) {
6270
6365
  if (!(0, import_node_fs17.existsSync)(file.target) || (0, import_node_fs17.readFileSync)(file.target, "utf8") !== file.content) {
6271
- (0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(file.target), { recursive: true });
6366
+ (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(file.target), { recursive: true });
6272
6367
  (0, import_node_fs17.writeFileSync)(file.target, file.content);
6273
6368
  }
6274
6369
  }
@@ -6288,7 +6383,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
6288
6383
  };
6289
6384
  }
6290
6385
  function pathsUnder(root, paths2) {
6291
- return [...paths2].map((path) => (0, import_node_path16.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path16.sep}`) && !(0, import_node_path16.isAbsolute)(path)).sort();
6386
+ return [...paths2].map((path) => (0, import_node_path17.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path17.sep}`) && !(0, import_node_path17.isAbsolute)(path)).sort();
6292
6387
  }
6293
6388
  function normalizeHarnesses(values, global) {
6294
6389
  const requested = values?.length ? values : ["claude"];
@@ -6333,13 +6428,13 @@ function managedFileContent(path, block2, force, boundary) {
6333
6428
  return `${current.slice(0, startAt)}${block2}${current.slice(afterEnd)}`;
6334
6429
  }
6335
6430
  function symlinkedComponent(boundary, target) {
6336
- const rel = (0, import_node_path16.relative)(boundary, target);
6337
- if (rel === ".." || rel.startsWith(`..${import_node_path16.sep}`) || (0, import_node_path16.isAbsolute)(rel)) {
6431
+ const rel = (0, import_node_path17.relative)(boundary, target);
6432
+ if (rel === ".." || rel.startsWith(`..${import_node_path17.sep}`) || (0, import_node_path17.isAbsolute)(rel)) {
6338
6433
  throw new Error(`agent setup target escapes its install root: ${target}`);
6339
6434
  }
6340
6435
  let current = boundary;
6341
- for (const part of rel.split(import_node_path16.sep).filter(Boolean)) {
6342
- current = (0, import_node_path16.join)(current, part);
6436
+ for (const part of rel.split(import_node_path17.sep).filter(Boolean)) {
6437
+ current = (0, import_node_path17.join)(current, part);
6343
6438
  try {
6344
6439
  if ((0, import_node_fs17.lstatSync)(current).isSymbolicLink()) return current;
6345
6440
  } catch (error) {
@@ -6356,22 +6451,22 @@ function listFiles(dir) {
6356
6451
  const results = [];
6357
6452
  const walk = (current) => {
6358
6453
  for (const entry of (0, import_node_fs17.readdirSync)(current, { withFileTypes: true })) {
6359
- const path = (0, import_node_path16.join)(current, entry.name);
6454
+ const path = (0, import_node_path17.join)(current, entry.name);
6360
6455
  if (entry.isDirectory()) walk(path);
6361
- else results.push((0, import_node_path16.relative)(dir, path));
6456
+ else results.push((0, import_node_path17.relative)(dir, path));
6362
6457
  }
6363
6458
  };
6364
6459
  walk(dir);
6365
6460
  return results.sort();
6366
6461
  }
6367
- var import_node_fs17, import_node_os4, import_node_path16, import_node_url2, AGENT_HARNESSES;
6462
+ var import_node_fs17, import_node_os4, import_node_path17, import_node_url2, AGENT_HARNESSES;
6368
6463
  var init_skill = __esm({
6369
6464
  "src/skill.ts"() {
6370
6465
  "use strict";
6371
6466
  init_cjs_shims();
6372
6467
  import_node_fs17 = require("fs");
6373
6468
  import_node_os4 = require("os");
6374
- import_node_path16 = require("path");
6469
+ import_node_path17 = require("path");
6375
6470
  import_node_url2 = require("url");
6376
6471
  init_skill_adapters();
6377
6472
  AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
@@ -7941,7 +8036,7 @@ var init_dist2 = __esm({
7941
8036
  });
7942
8037
 
7943
8038
  // ../graph/dist/code/index.js
7944
- function dirname10(path) {
8039
+ function dirname11(path) {
7945
8040
  const at = path.lastIndexOf("/");
7946
8041
  return at <= 0 ? "." : path.slice(0, at);
7947
8042
  }
@@ -7957,7 +8052,7 @@ function join14(base, specifier) {
7957
8052
  }
7958
8053
  function resolveImport(fromPath, specifier, known) {
7959
8054
  if (!specifier.startsWith(".")) return null;
7960
- const base = join14(dirname10(fromPath), specifier);
8055
+ const base = join14(dirname11(fromPath), specifier);
7961
8056
  const candidates = [
7962
8057
  base,
7963
8058
  base.replace(/\.js$/, ".ts"),
@@ -11068,19 +11163,19 @@ async function inferGitHubRepository(cwd = process.cwd(), readOrigin = defaultRe
11068
11163
  return repositoryFromGitRemote(remote);
11069
11164
  }
11070
11165
  async function defaultReadOrigin(cwd) {
11071
- const result = await (0, import_node_util2.promisify)(import_node_child_process4.execFile)(
11166
+ const result = await (0, import_node_util2.promisify)(import_node_child_process5.execFile)(
11072
11167
  "git",
11073
11168
  ["remote", "get-url", "origin"],
11074
11169
  { cwd, encoding: "utf8" }
11075
11170
  );
11076
11171
  return result.stdout;
11077
11172
  }
11078
- var import_node_child_process4, import_node_util2;
11173
+ var import_node_child_process5, import_node_util2;
11079
11174
  var init_security_hosted_github = __esm({
11080
11175
  "src/security-hosted-github.ts"() {
11081
11176
  "use strict";
11082
11177
  init_cjs_shims();
11083
- import_node_child_process4 = require("child_process");
11178
+ import_node_child_process5 = require("child_process");
11084
11179
  import_node_util2 = require("util");
11085
11180
  init_security_hosted_request();
11086
11181
  }
@@ -11127,7 +11222,7 @@ async function prepareCodeLocalSource(cwd, repository, readHead = readGitHead) {
11127
11222
  }
11128
11223
  async function readGitHead(cwd) {
11129
11224
  const value2 = await new Promise((accept, reject) => {
11130
- (0, import_node_child_process5.execFile)("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8", maxBuffer: 16384 }, (error, stdout) => {
11225
+ (0, import_node_child_process6.execFile)("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8", maxBuffer: 16384 }, (error, stdout) => {
11131
11226
  if (error) reject(new Error("code connect requires a Git checkout with an initial commit"));
11132
11227
  else accept(stdout.trim());
11133
11228
  });
@@ -11138,12 +11233,12 @@ async function readGitHead(cwd) {
11138
11233
  function digestText(value2) {
11139
11234
  return `sha256:${(0, import_node_crypto3.createHash)("sha256").update(value2).digest("hex")}`;
11140
11235
  }
11141
- var import_node_child_process5, import_node_crypto3, SOURCE_LIMITS2;
11236
+ var import_node_child_process6, import_node_crypto3, SOURCE_LIMITS2;
11142
11237
  var init_code_local_source = __esm({
11143
11238
  "src/code-local-source.ts"() {
11144
11239
  "use strict";
11145
11240
  init_cjs_shims();
11146
- import_node_child_process5 = require("child_process");
11241
+ import_node_child_process6 = require("child_process");
11147
11242
  import_node_crypto3 = require("crypto");
11148
11243
  init_node();
11149
11244
  SOURCE_LIMITS2 = { maxFiles: 2e4, maxBytes: 512 * 1024 * 1024 };
@@ -11184,7 +11279,7 @@ var init_code_runtime_config = __esm({
11184
11279
  // src/code-connect.ts
11185
11280
  async function codeConnect(options) {
11186
11281
  const cwd = options.cwd ?? process.cwd();
11187
- const configPath = (0, import_node_path17.resolve)(cwd, options.configPath);
11282
+ const configPath = (0, import_node_path18.resolve)(cwd, options.configPath);
11188
11283
  const cfg = (0, import_node_fs18.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
11189
11284
  const requestedAppId = options.appId?.trim();
11190
11285
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -11350,14 +11445,14 @@ function apiFailure(action2, status, value2) {
11350
11445
  function record6(value2) {
11351
11446
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
11352
11447
  }
11353
- var import_node_fs18, import_node_os5, import_node_path17;
11448
+ var import_node_fs18, import_node_os5, import_node_path18;
11354
11449
  var init_code_connect = __esm({
11355
11450
  "src/code-connect.ts"() {
11356
11451
  "use strict";
11357
11452
  init_cjs_shims();
11358
11453
  import_node_fs18 = require("fs");
11359
11454
  import_node_os5 = require("os");
11360
- import_node_path17 = require("path");
11455
+ import_node_path18 = require("path");
11361
11456
  init_node();
11362
11457
  init_admin_ai_auth();
11363
11458
  init_config();
@@ -11672,7 +11767,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
11672
11767
  const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
11673
11768
  const source = clean3(
11674
11769
  stringOpt(parsed.options.token)
11675
- ) ? "flag" : clean3(import_node_process16.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
11770
+ ) ? "flag" : clean3(import_node_process17.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
11676
11771
  return {
11677
11772
  source,
11678
11773
  cacheFile: context.cfg.local.tokenFile,
@@ -11683,12 +11778,12 @@ function clean3(value2) {
11683
11778
  const normalized = value2?.trim();
11684
11779
  return normalized || void 0;
11685
11780
  }
11686
- var import_node_process16;
11781
+ var import_node_process17;
11687
11782
  var init_operator_credentials = __esm({
11688
11783
  "src/operator-credentials.ts"() {
11689
11784
  "use strict";
11690
11785
  init_cjs_shims();
11691
- import_node_process16 = __toESM(require("process"), 1);
11786
+ import_node_process17 = __toESM(require("process"), 1);
11692
11787
  init_argv();
11693
11788
  init_local();
11694
11789
  }
@@ -12268,9 +12363,17 @@ Safety:
12268
12363
  grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
12269
12364
  the local cache, prints and opens a fresh exact-project owner-review URL, then
12270
12365
  continues provisioning with the approved replacement credential.
12271
- Before a non-dry-run provision, the executable checks npm's current CLI
12272
- release. A confirmed stale client stops with a safe npx rerun command; a
12273
- workspace-linked client also identifies the worktree that must be updated.
12366
+ Every command says so on STDERR when this CLI is older than the one npm
12367
+ serves, naming the repair for how this executable was launched \u2014 rebuild the
12368
+ worktree, npm i the dependency, or npx the scoped package. The answer is read
12369
+ from a cache under ~/.odla refreshed in the background at most every three
12370
+ hours, so no invocation waits on the registry and stdout is never touched.
12371
+ Set ODLA_CLI_UPDATE_CHECK=0 to switch it off.
12372
+ Before a non-dry-run provision, the executable additionally checks npm LIVE
12373
+ rather than trusting that cache: a security-sensitive grant shape must not
12374
+ ride on a three-hour-old answer. A confirmed stale client stops with a safe
12375
+ npx rerun command; a workspace-linked client also identifies the worktree
12376
+ that must be updated.
12274
12377
  Run Code from a GitHub checkout already connected to an app in Studio; an
12275
12378
  odla.config.mjs may select the app explicitly but is not required. With an
12276
12379
  enrolled code.session device, the Studio repository selection authorizes the
@@ -13772,7 +13875,7 @@ function writePmProjectContext(rootDir, value2) {
13772
13875
  });
13773
13876
  }
13774
13877
  function adoptLegacySelection(rootDir) {
13775
- const legacy = (0, import_node_path18.resolve)(rootDir, ".odla", "pm-project.local.json");
13878
+ const legacy = (0, import_node_path19.resolve)(rootDir, ".odla", "pm-project.local.json");
13776
13879
  if (!(0, import_node_fs19.existsSync)(legacy)) return;
13777
13880
  const previous = readJsonFile(legacy);
13778
13881
  (0, import_node_fs19.rmSync)(legacy, { force: true });
@@ -13787,13 +13890,13 @@ function readSelections() {
13787
13890
  function isSelection(value2) {
13788
13891
  return !!value2 && typeof value2.appId === "string" && typeof value2.projectId === "string" && typeof value2.selectedAt === "string";
13789
13892
  }
13790
- var import_node_fs19, import_node_path18, pmProjectContextFile;
13893
+ var import_node_fs19, import_node_path19, pmProjectContextFile;
13791
13894
  var init_pm_project_context = __esm({
13792
13895
  "src/pm-project-context.ts"() {
13793
13896
  "use strict";
13794
13897
  init_cjs_shims();
13795
13898
  import_node_fs19 = require("fs");
13796
- import_node_path18 = require("path");
13899
+ import_node_path19 = require("path");
13797
13900
  init_local();
13798
13901
  init_odla_home();
13799
13902
  pmProjectContextFile = () => pmContextFile();
@@ -15371,7 +15474,7 @@ async function provision(options) {
15371
15474
  await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
15372
15475
  }
15373
15476
  if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
15374
- const key = import_node_process17.default.env[cfg.ai.keyEnv];
15477
+ const key = import_node_process18.default.env[cfg.ai.keyEnv];
15375
15478
  if (key) {
15376
15479
  const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
15377
15480
  await (0, import_ai6.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
@@ -15410,14 +15513,14 @@ async function provision(options) {
15410
15513
  }
15411
15514
  }
15412
15515
  }
15413
- var import_apps13, import_ai6, import_node_process17;
15516
+ var import_apps13, import_ai6, import_node_process18;
15414
15517
  var init_provision = __esm({
15415
15518
  "src/provision.ts"() {
15416
15519
  "use strict";
15417
15520
  init_cjs_shims();
15418
15521
  import_apps13 = require("@odla-ai/apps");
15419
15522
  import_ai6 = require("@odla-ai/ai");
15420
- import_node_process17 = __toESM(require("process"), 1);
15523
+ import_node_process18 = __toESM(require("process"), 1);
15421
15524
  init_config();
15422
15525
  init_calendar();
15423
15526
  init_calendar_errors();
@@ -15436,7 +15539,7 @@ var init_provision = __esm({
15436
15539
 
15437
15540
  // src/record.ts
15438
15541
  function recordInvocation(parsed) {
15439
- const file = import_node_process18.default.env.ODLA_CLI_RECORD;
15542
+ const file = import_node_process19.default.env.ODLA_CLI_RECORD;
15440
15543
  if (!file) return;
15441
15544
  try {
15442
15545
  const entry = {
@@ -15449,13 +15552,13 @@ function recordInvocation(parsed) {
15449
15552
  } catch {
15450
15553
  }
15451
15554
  }
15452
- var import_node_fs20, import_node_process18;
15555
+ var import_node_fs20, import_node_process19;
15453
15556
  var init_record = __esm({
15454
15557
  "src/record.ts"() {
15455
15558
  "use strict";
15456
15559
  init_cjs_shims();
15457
15560
  import_node_fs20 = require("fs");
15458
- import_node_process18 = __toESM(require("process"), 1);
15561
+ import_node_process19 = __toESM(require("process"), 1);
15459
15562
  init_surface();
15460
15563
  }
15461
15564
  });
@@ -15580,7 +15683,7 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
15580
15683
  headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
15581
15684
  body: JSON.stringify({
15582
15685
  name,
15583
- platform: import_node_process19.default.platform,
15686
+ platform: import_node_process20.default.platform,
15584
15687
  appIds: apps,
15585
15688
  ...capabilities ? { capabilities } : {},
15586
15689
  ...scopes ? { scopes } : {},
@@ -15592,7 +15695,7 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
15592
15695
  throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
15593
15696
  }
15594
15697
  const path = deviceCredentialPath();
15595
- (0, import_node_fs21.mkdirSync)((0, import_node_path19.dirname)(path), { recursive: true });
15698
+ (0, import_node_fs21.mkdirSync)((0, import_node_path20.dirname)(path), { recursive: true });
15596
15699
  (0, import_node_fs21.writeFileSync)(path, JSON.stringify({
15597
15700
  token: body.token,
15598
15701
  platform: cfg.platformUrl.replace(/\/$/, ""),
@@ -15699,9 +15802,9 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
15699
15802
  });
15700
15803
  }
15701
15804
  function defaultDeviceName() {
15702
- return `${import_node_process19.default.env.HOSTNAME ?? import_node_process19.default.env.HOST ?? "machine"}-${import_node_process19.default.platform}`;
15805
+ return `${import_node_process20.default.env.HOSTNAME ?? import_node_process20.default.env.HOST ?? "machine"}-${import_node_process20.default.platform}`;
15703
15806
  }
15704
- var import_db4, import_node_fs21, import_node_path19, import_node_process19;
15807
+ var import_db4, import_node_fs21, import_node_path20, import_node_process20;
15705
15808
  var init_device_command = __esm({
15706
15809
  "src/device-command.ts"() {
15707
15810
  "use strict";
@@ -15712,8 +15815,8 @@ var init_device_command = __esm({
15712
15815
  init_auth_guidance();
15713
15816
  init_advisory_output();
15714
15817
  import_node_fs21 = require("fs");
15715
- import_node_path19 = require("path");
15716
- import_node_process19 = __toESM(require("process"), 1);
15818
+ import_node_path20 = require("path");
15819
+ import_node_process20 = __toESM(require("process"), 1);
15717
15820
  init_argv();
15718
15821
  init_admin_ai_auth();
15719
15822
  init_device_session();
@@ -15900,8 +16003,8 @@ function readRunbookDir(dir) {
15900
16003
  const files = (0, import_node_fs23.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
15901
16004
  if (!files.length) throw new Error(`no .md files in ${dir}`);
15902
16005
  return files.map((file) => {
15903
- const slug = (0, import_node_path20.basename)(file, ".md");
15904
- const parsed = parseRunbook((0, import_node_fs23.readFileSync)((0, import_node_path20.join)(dir, file), "utf8"), slug);
16006
+ const slug = (0, import_node_path21.basename)(file, ".md");
16007
+ const parsed = parseRunbook((0, import_node_fs23.readFileSync)((0, import_node_path21.join)(dir, file), "utf8"), slug);
15905
16008
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
15906
16009
  });
15907
16010
  }
@@ -15966,13 +16069,13 @@ async function upsert(ctx, r, visibility) {
15966
16069
  );
15967
16070
  return "updated";
15968
16071
  }
15969
- var import_node_fs23, import_node_path20;
16072
+ var import_node_fs23, import_node_path21;
15970
16073
  var init_runbook_import = __esm({
15971
16074
  "src/runbook-import.ts"() {
15972
16075
  "use strict";
15973
16076
  init_cjs_shims();
15974
16077
  import_node_fs23 = require("fs");
15975
- import_node_path20 = require("path");
16078
+ import_node_path21 = require("path");
15976
16079
  init_runbook_actions();
15977
16080
  }
15978
16081
  });
@@ -16105,7 +16208,7 @@ var init_runbook_impact_scan = __esm({
16105
16208
 
16106
16209
  // src/runbook-impact.ts
16107
16210
  function gitRunner(cwd) {
16108
- return (args) => (0, import_node_child_process6.execFileSync)("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
16211
+ return (args) => (0, import_node_child_process7.execFileSync)("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
16109
16212
  }
16110
16213
  function collectDiff(runGit, base, read3) {
16111
16214
  let merged = "";
@@ -16150,7 +16253,7 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
16150
16253
  }
16151
16254
  function manifestLabeller(root) {
16152
16255
  return (workspace) => {
16153
- const manifest = (0, import_node_path21.join)(root, workspace, "package.json");
16256
+ const manifest = (0, import_node_path22.join)(root, workspace, "package.json");
16154
16257
  if (!(0, import_node_fs24.existsSync)(manifest)) return void 0;
16155
16258
  try {
16156
16259
  const name = JSON.parse((0, import_node_fs24.readFileSync)(manifest, "utf8")).name;
@@ -16219,7 +16322,7 @@ function report4(ctx, impacts) {
16219
16322
  async function runbookImpact(ctx, options, deps = {}) {
16220
16323
  const cwd = deps.cwd ?? process.cwd();
16221
16324
  const runGit = deps.runGit ?? gitRunner(cwd);
16222
- const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs24.readFileSync)((0, import_node_path21.join)(cwd, path), "utf8"));
16325
+ const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs24.readFileSync)((0, import_node_path22.join)(cwd, path), "utf8"));
16223
16326
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
16224
16327
  if (!surfaces.length) {
16225
16328
  return ctx.out.log(
@@ -16230,14 +16333,14 @@ async function runbookImpact(ctx, options, deps = {}) {
16230
16333
  if (ctx.json) return ctx.out.log(JSON.stringify({ base: options.base, impacts }, null, 2));
16231
16334
  report4(ctx, impacts);
16232
16335
  }
16233
- var import_node_child_process6, import_node_fs24, import_node_path21, SOURCE3, editHint;
16336
+ var import_node_child_process7, import_node_fs24, import_node_path22, SOURCE3, editHint;
16234
16337
  var init_runbook_impact = __esm({
16235
16338
  "src/runbook-impact.ts"() {
16236
16339
  "use strict";
16237
16340
  init_cjs_shims();
16238
- import_node_child_process6 = require("child_process");
16341
+ import_node_child_process7 = require("child_process");
16239
16342
  import_node_fs24 = require("fs");
16240
- import_node_path21 = require("path");
16343
+ import_node_path22 = require("path");
16241
16344
  init_runbook_impact_scan();
16242
16345
  init_runbook_actions();
16243
16346
  SOURCE3 = /\.(ts|tsx|js|jsx|mts|cts)$/;
@@ -16377,7 +16480,7 @@ var init_runbook_search_command = __esm({
16377
16480
  });
16378
16481
 
16379
16482
  // src/runbook-editor.ts
16380
- function resolveEditor(env = import_node_process20.default.env) {
16483
+ function resolveEditor(env = import_node_process21.default.env) {
16381
16484
  for (const name of EDITOR_ENV) {
16382
16485
  const value2 = env[name];
16383
16486
  if (value2 && value2.trim()) return value2.trim();
@@ -16386,13 +16489,13 @@ function resolveEditor(env = import_node_process20.default.env) {
16386
16489
  }
16387
16490
  function defaultRun(command, path) {
16388
16491
  const [bin, ...args] = command.split(/\s+/);
16389
- const result = (0, import_node_child_process7.spawnSync)(bin, [...args, path], { stdio: "inherit" });
16492
+ const result = (0, import_node_child_process8.spawnSync)(bin, [...args, path], { stdio: "inherit" });
16390
16493
  if (result.error) throw new Error(`could not start editor "${command}": ${result.error.message}`);
16391
16494
  return result.status ?? 0;
16392
16495
  }
16393
16496
  function editText(initial, slug, deps = {}) {
16394
- const env = deps.env ?? import_node_process20.default.env;
16395
- const interactive = deps.interactive ?? (() => Boolean(import_node_process20.default.stdin.isTTY));
16497
+ const env = deps.env ?? import_node_process21.default.env;
16498
+ const interactive = deps.interactive ?? (() => Boolean(import_node_process21.default.stdin.isTTY));
16396
16499
  const editor = resolveEditor(env);
16397
16500
  if (!editor)
16398
16501
  throw new Error(
@@ -16400,8 +16503,8 @@ function editText(initial, slug, deps = {}) {
16400
16503
  );
16401
16504
  if (!interactive())
16402
16505
  throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
16403
- const dir = (0, import_node_fs25.mkdtempSync)((0, import_node_path22.join)((0, import_node_os6.tmpdir)(), "odla-runbook-"));
16404
- const file = (0, import_node_path22.join)(dir, `${slug}.md`);
16506
+ const dir = (0, import_node_fs25.mkdtempSync)((0, import_node_path23.join)((0, import_node_os6.tmpdir)(), "odla-runbook-"));
16507
+ const file = (0, import_node_path23.join)(dir, `${slug}.md`);
16405
16508
  try {
16406
16509
  (0, import_node_fs25.writeFileSync)(file, initial, { mode: 384 });
16407
16510
  const code = defaultRunOrInjected(deps)(editor, file);
@@ -16412,16 +16515,16 @@ function editText(initial, slug, deps = {}) {
16412
16515
  (0, import_node_fs25.rmSync)(dir, { recursive: true, force: true });
16413
16516
  }
16414
16517
  }
16415
- var import_node_child_process7, import_node_fs25, import_node_os6, import_node_path22, import_node_process20, EDITOR_ENV, defaultRunOrInjected;
16518
+ var import_node_child_process8, import_node_fs25, import_node_os6, import_node_path23, import_node_process21, EDITOR_ENV, defaultRunOrInjected;
16416
16519
  var init_runbook_editor = __esm({
16417
16520
  "src/runbook-editor.ts"() {
16418
16521
  "use strict";
16419
16522
  init_cjs_shims();
16420
- import_node_child_process7 = require("child_process");
16523
+ import_node_child_process8 = require("child_process");
16421
16524
  import_node_fs25 = require("fs");
16422
16525
  import_node_os6 = require("os");
16423
- import_node_path22 = require("path");
16424
- import_node_process20 = __toESM(require("process"), 1);
16526
+ import_node_path23 = require("path");
16527
+ import_node_process21 = __toESM(require("process"), 1);
16425
16528
  EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
16426
16529
  defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
16427
16530
  }
@@ -16811,9 +16914,9 @@ async function runHostedSecurity(options) {
16811
16914
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
16812
16915
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
16813
16916
  const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
16814
- const target = (0, import_node_path23.resolve)(options.target ?? cfg?.rootDir ?? ".");
16815
- const output = (0, import_node_path23.resolve)(options.out ?? (0, import_node_path23.resolve)(target, ".odla/security/hosted"));
16816
- const outputRelative = (0, import_node_path23.relative)(target, output).split(import_node_path23.sep).join("/");
16917
+ const target = (0, import_node_path24.resolve)(options.target ?? cfg?.rootDir ?? ".");
16918
+ const output = (0, import_node_path24.resolve)(options.out ?? (0, import_node_path24.resolve)(target, ".odla/security/hosted"));
16919
+ const outputRelative = (0, import_node_path24.relative)(target, output).split(import_node_path24.sep).join("/");
16817
16920
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
16818
16921
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
16819
16922
  const tokenRequest = {
@@ -16825,7 +16928,7 @@ async function runHostedSecurity(options) {
16825
16928
  };
16826
16929
  const token = await injectedToken(options, tokenRequest);
16827
16930
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
16828
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path23.isAbsolute)(outputRelative) ? [outputRelative] : []
16931
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path24.isAbsolute)(outputRelative) ? [outputRelative] : []
16829
16932
  });
16830
16933
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
16831
16934
  platform,
@@ -16843,7 +16946,7 @@ async function runHostedSecurity(options) {
16843
16946
  });
16844
16947
  const harness = (0, import_security.createSecurityHarness)({
16845
16948
  profile,
16846
- store: new import_node3.FileRunStore((0, import_node_path23.resolve)(output, "state")),
16949
+ store: new import_node3.FileRunStore((0, import_node_path24.resolve)(output, "state")),
16847
16950
  discoveryReasoner: hosted.discoveryReasoner,
16848
16951
  validationReasoner: hosted.validationReasoner,
16849
16952
  policy: {
@@ -16867,7 +16970,7 @@ async function runHostedSecurity(options) {
16867
16970
  function selectEnv(requested, declared, configPath, rootDir) {
16868
16971
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
16869
16972
  if (!env || !declared.includes(env)) {
16870
- const shown = (0, import_node_path23.relative)(rootDir, configPath) || configPath;
16973
+ const shown = (0, import_node_path24.relative)(rootDir, configPath) || configPath;
16871
16974
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
16872
16975
  }
16873
16976
  return env;
@@ -16896,17 +16999,17 @@ function printSummary(out, appId, env, run, report5, output) {
16896
16999
  out.log(` coverage: ${report5.coverageStatus} ${complete}/${report5.coverage.length} blocked=${report5.metrics.blockedCells} shallow=${report5.metrics.shallowCells} unscheduled=${report5.metrics.unscheduledCells} budget_exhausted=${report5.metrics.budgetExhaustedCells}`);
16897
17000
  if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
16898
17001
  out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
16899
- out.log(` report: ${(0, import_node_path23.resolve)(output, "REPORT.md")}`);
17002
+ out.log(` report: ${(0, import_node_path24.resolve)(output, "REPORT.md")}`);
16900
17003
  }
16901
17004
  function formatBudget(usage) {
16902
17005
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
16903
17006
  }
16904
- var import_node_path23, import_security, import_node3;
17007
+ var import_node_path24, import_security, import_node3;
16905
17008
  var init_security = __esm({
16906
17009
  "src/security.ts"() {
16907
17010
  "use strict";
16908
17011
  init_cjs_shims();
16909
- import_node_path23 = require("path");
17012
+ import_node_path24 = require("path");
16910
17013
  import_security = require("@odla-ai/security");
16911
17014
  import_node3 = require("@odla-ai/security/node");
16912
17015
  init_config();
@@ -17405,6 +17508,11 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
17405
17508
  throw error;
17406
17509
  } finally {
17407
17510
  renderAdvisories(out, advisories);
17511
+ try {
17512
+ const behind = updateNotice();
17513
+ if (behind) out.error(behind);
17514
+ } catch {
17515
+ }
17408
17516
  }
17409
17517
  }
17410
17518
  async function dispatchCli(argv2, dependencies) {
@@ -17613,6 +17721,7 @@ var init_cli = __esm({
17613
17721
  init_security_command();
17614
17722
  init_whoami_command();
17615
17723
  init_surface();
17724
+ init_update_notice();
17616
17725
  init_exit_code();
17617
17726
  }
17618
17727
  });
@@ -17622,16 +17731,17 @@ init_cjs_shims();
17622
17731
 
17623
17732
  // src/cli-update.ts
17624
17733
  init_cjs_shims();
17625
- var import_node_fs2 = require("fs");
17626
17734
  init_runbook_requires();
17735
+ init_update_notice();
17627
17736
  init_version();
17628
17737
  var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org/@odla-ai%2fcli/latest";
17629
- var VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
17738
+ var VERSION2 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
17630
17739
  async function requireCurrentCliForProvision(argv2, options = {}) {
17631
17740
  if (argv2[0] !== "provision" || argv2.includes("--dry-run")) return;
17632
17741
  const current = options.currentVersion ?? cliVersion();
17633
- if (!VERSION.test(current)) return;
17742
+ if (!VERSION2.test(current)) return;
17634
17743
  const latest = await fetchLatestCliVersion(options);
17744
+ if (latest) rememberLatest(latest);
17635
17745
  if (!latest || compareVersions(current, latest) >= 0) return;
17636
17746
  const entryPath = resolvedEntryPath(options.entryPath ?? process.argv[1]);
17637
17747
  const workspace = isWorkspaceCli(entryPath);
@@ -17660,25 +17770,19 @@ async function fetchLatestCliVersion(options) {
17660
17770
  );
17661
17771
  if (!response2.ok) return null;
17662
17772
  const body = await response2.json();
17663
- return typeof body.version === "string" && VERSION.test(body.version) ? body.version : null;
17773
+ return typeof body.version === "string" && VERSION2.test(body.version) ? body.version : null;
17664
17774
  } catch {
17665
17775
  return null;
17666
17776
  } finally {
17667
17777
  clearTimeout(timeout);
17668
17778
  }
17669
17779
  }
17670
- function resolvedEntryPath(entryPath) {
17671
- if (!entryPath) return "unknown executable";
17780
+ function rememberLatest(latest) {
17672
17781
  try {
17673
- return (0, import_node_fs2.realpathSync)(entryPath);
17782
+ writeUpdateCache(updateCacheFile(), { latest, checkedAt: Date.now() });
17674
17783
  } catch {
17675
- return entryPath;
17676
17784
  }
17677
17785
  }
17678
- function isWorkspaceCli(entryPath) {
17679
- const normalized = entryPath.replaceAll("\\", "/");
17680
- return normalized.includes("/packages/cli/dist/bin.") && !normalized.includes("/node_modules/");
17681
- }
17682
17786
  function renderReleasedProvisionCommand(latest, argv2) {
17683
17787
  const safeArgs = [];
17684
17788
  for (let index = 0; index < argv2.length; index++) {
@@ -17706,8 +17810,9 @@ function shellQuote(value2) {
17706
17810
  // src/cli-runtime.ts
17707
17811
  init_cjs_shims();
17708
17812
  var import_node_module = require("module");
17709
- var import_node_fs3 = require("fs");
17710
- var import_node_path = require("path");
17813
+ var import_node_fs4 = require("fs");
17814
+ var import_node_path3 = require("path");
17815
+ init_update_notice();
17711
17816
  init_version();
17712
17817
  var EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
17713
17818
  var RUNTIME_MODULES = [
@@ -17766,26 +17871,26 @@ function installedRuntimeModules(entryPath) {
17766
17871
  }));
17767
17872
  }
17768
17873
  function findPackageManifest(fromPath, expectedName) {
17769
- let directory = (0, import_node_path.dirname)(resolvedEntryPath(fromPath));
17770
- const root = (0, import_node_path.parse)(directory).root;
17874
+ let directory = (0, import_node_path3.dirname)(resolvedEntryPath(fromPath));
17875
+ const root = (0, import_node_path3.parse)(directory).root;
17771
17876
  while (true) {
17772
- const path = (0, import_node_path.join)(directory, "package.json");
17773
- if ((0, import_node_fs3.existsSync)(path)) {
17877
+ const path = (0, import_node_path3.join)(directory, "package.json");
17878
+ if ((0, import_node_fs4.existsSync)(path)) {
17774
17879
  try {
17775
- const json = JSON.parse((0, import_node_fs3.readFileSync)(path, "utf8"));
17880
+ const json = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
17776
17881
  if (json?.name === expectedName && typeof json.version === "string") return { path, json };
17777
17882
  } catch {
17778
17883
  }
17779
17884
  }
17780
17885
  if (directory === root) return void 0;
17781
- directory = (0, import_node_path.dirname)(directory);
17886
+ directory = (0, import_node_path3.dirname)(directory);
17782
17887
  }
17783
17888
  }
17784
17889
  function absoluteEntryPath(entryPath) {
17785
- const candidate = entryPath || (0, import_node_path.join)(process.cwd(), "odla-ai-cli.js");
17786
- const absolute = (0, import_node_path.isAbsolute)(candidate) ? candidate : (0, import_node_path.resolve)(candidate);
17890
+ const candidate = entryPath || (0, import_node_path3.join)(process.cwd(), "odla-ai-cli.js");
17891
+ const absolute = (0, import_node_path3.isAbsolute)(candidate) ? candidate : (0, import_node_path3.resolve)(candidate);
17787
17892
  try {
17788
- return (0, import_node_fs3.realpathSync)(absolute);
17893
+ return (0, import_node_fs4.realpathSync)(absolute);
17789
17894
  } catch {
17790
17895
  return absolute;
17791
17896
  }