@odla-ai/cli 0.44.0 → 0.45.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.
@@ -1,21 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ adoptRepoLocalCache,
4
+ appCredentialsFile,
5
+ appTokenFile,
3
6
  cliVersion,
4
7
  describeUnmet,
8
+ deviceSessionFile,
9
+ identityFile,
5
10
  looksSecret,
6
11
  parseRequires,
12
+ pmContextFile,
7
13
  redactSecrets,
8
14
  redactingOutput,
9
- unmetRequirements
10
- } from "./chunk-UKLSRQ5J.js";
15
+ scopedTokenFile,
16
+ unmetRequirements,
17
+ updateNotice
18
+ } from "./chunk-5MEO3BVF.js";
11
19
 
12
20
  // src/admin-ai.ts
13
- import process13 from "process";
21
+ import process12 from "process";
14
22
 
15
23
  // src/token.ts
16
24
  import { OdlaError, requestToken } from "@odla-ai/db";
17
25
  import { createHash } from "crypto";
18
- import process10 from "process";
26
+ import process9 from "process";
19
27
 
20
28
  // src/handshake-approval.ts
21
29
  import process3 from "process";
@@ -150,10 +158,10 @@ function handshakeWaitMs(waitSeconds, interactive = process4.stdout.isTTY === tr
150
158
  }
151
159
 
152
160
  // src/cached-credential.ts
153
- import { rmSync as rmSync3 } from "fs";
161
+ import { rmSync as rmSync2 } from "fs";
154
162
 
155
163
  // src/auth-guidance.ts
156
- import process7 from "process";
164
+ import process6 from "process";
157
165
 
158
166
  // src/device-session.ts
159
167
  import { existsSync, readFileSync } from "fs";
@@ -202,63 +210,9 @@ async function mintDeviceSession(platformUrl, credential2, doFetch) {
202
210
  };
203
211
  }
204
212
 
205
- // src/odla-home.ts
206
- import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, rmSync as rmSync2 } from "fs";
207
- import { homedir as homedir2 } from "os";
208
- import { dirname as dirname2, join as join3 } from "path";
209
- import process6 from "process";
210
- function odlaHome(env = process6.env) {
211
- if (env.VITEST && !env.ODLA_HOME) {
212
- throw new Error(
213
- "ODLA_HOME must be set under test \u2014 resolving the real ~/.odla would write to the developer's machine"
214
- );
215
- }
216
- return env.ODLA_HOME ?? join3(env.HOME ?? homedir2(), ".odla");
217
- }
218
- function odlaHomePath(segments, env = process6.env) {
219
- return join3(odlaHome(env), ...segments);
220
- }
221
- function identityFile(env) {
222
- return odlaHomePath(["identity.json"], env);
223
- }
224
- function deviceSessionFile(env) {
225
- return odlaHomePath(["session.json"], env);
226
- }
227
- function appTokenFile(appId, env) {
228
- return odlaHomePath(["apps", safeSegment(appId), "dev-token.json"], env);
229
- }
230
- function appCredentialsFile(appId, env) {
231
- return odlaHomePath(["apps", safeSegment(appId), "credentials.json"], env);
232
- }
233
- function scopedTokenFile(env) {
234
- return odlaHomePath(["admin-token.local.json"], env);
235
- }
236
- function pmContextFile(env) {
237
- return odlaHomePath(["pm-context.json"], env);
238
- }
239
- function adoptRepoLocalCache(legacyPath, machinePath, out) {
240
- if (!existsSync2(legacyPath) || legacyPath === machinePath) return false;
241
- const superseded2 = existsSync2(machinePath);
242
- if (!superseded2) {
243
- mkdirSync(dirname2(machinePath), { recursive: true });
244
- copyFileSync(legacyPath, machinePath);
245
- chmodSync(machinePath, 384);
246
- }
247
- rmSync2(legacyPath, { force: true });
248
- out?.error(
249
- 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`
250
- );
251
- return true;
252
- }
253
- function safeSegment(value2) {
254
- const clean4 = value2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
255
- if (!clean4) throw new Error(`"${value2}" is not a usable app id`);
256
- return clean4;
257
- }
258
-
259
213
  // src/local.ts
260
- import { chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync } from "fs";
261
- import { dirname as dirname3, isAbsolute, relative, resolve } from "path";
214
+ import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "fs";
215
+ import { dirname as dirname2, isAbsolute, relative, resolve } from "path";
262
216
  var GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
263
217
  function readJsonFile(path) {
264
218
  try {
@@ -272,7 +226,7 @@ function writePrivateJson(path, value2) {
272
226
  `);
273
227
  }
274
228
  function readCredentials(path) {
275
- if (!existsSync3(path)) return null;
229
+ if (!existsSync2(path)) return null;
276
230
  let value2;
277
231
  try {
278
232
  value2 = JSON.parse(readFileSync2(path, "utf8"));
@@ -306,7 +260,7 @@ function mergeCredential(current, update) {
306
260
  }
307
261
  function ensureGitignore(rootDir, localPaths = []) {
308
262
  const path = resolve(rootDir, ".gitignore");
309
- const existing = existsSync3(path) ? readFileSync2(path, "utf8") : "";
263
+ const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
310
264
  const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
311
265
  const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
312
266
  const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
@@ -326,7 +280,7 @@ function o11yDevVars(cfg) {
326
280
  function resolveWriteDevVarsTarget(cfg, requested) {
327
281
  if (!requested) return null;
328
282
  if (requested === true) return cfg.local.devVarsFile;
329
- return resolve(dirname3(cfg.configPath), requested);
283
+ return resolve(dirname2(cfg.configPath), requested);
330
284
  }
331
285
  function writeDevVars(path, credentials, env, o11y) {
332
286
  const entry = credentials.envs[env];
@@ -340,7 +294,7 @@ function writeDevVars(path, credentials, env, o11y) {
340
294
  if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
341
295
  if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
342
296
  }
343
- const existing = existsSync3(path) ? readFileSync2(path, "utf8") : "";
297
+ const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
344
298
  const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
345
299
  while (retained.at(-1) === "") retained.pop();
346
300
  const prefix = retained.length ? `${retained.join("\n")}
@@ -366,10 +320,10 @@ function isManagedDevVar(line2) {
366
320
  return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
367
321
  }
368
322
  function writePrivateText(path, text4) {
369
- mkdirSync2(dirname3(path), { recursive: true });
323
+ mkdirSync(dirname2(path), { recursive: true });
370
324
  const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
371
325
  writeFileSync(temporary, text4, { mode: 384 });
372
- chmodSync2(temporary, 384);
326
+ chmodSync(temporary, 384);
373
327
  renameSync(temporary, path);
374
328
  }
375
329
  function gitignoreEntry(rootDir, path) {
@@ -385,7 +339,7 @@ function displayPath(path, rootDir = process.cwd()) {
385
339
  // src/auth-guidance.ts
386
340
  var ENROL_EVERYTHING = "npx odla-ai device enroll --no-open --wait 600";
387
341
  var ENROL_PLATFORM_WIDE = "npx odla-ai device enroll --platform-wide --device-ttl 6w --no-open --wait 600";
388
- function machineAuthState(audience, env = process7.env) {
342
+ function machineAuthState(audience, env = process6.env) {
389
343
  const device = readDeviceCredential(audience, env);
390
344
  if (!device) return { enrolled: false };
391
345
  const session = readJsonFile(deviceSessionFile(env));
@@ -443,7 +397,7 @@ function explainRejectedCredential(error) {
443
397
  const tokenFile = noted;
444
398
  if (!tokenFile || !isCredentialRejection(error)) return null;
445
399
  noted = null;
446
- rmSync3(tokenFile, { force: true });
400
+ rmSync2(tokenFile, { force: true });
447
401
  return [
448
402
  "auth: the cached credential was rejected by odla, so it was revoked before its cached expiry.",
449
403
  " The usual cause is a newer sign-in for this account: collecting a handshake retires the",
@@ -454,9 +408,9 @@ function explainRejectedCredential(error) {
454
408
  }
455
409
 
456
410
  // src/device-session-cache.ts
457
- import process8 from "process";
411
+ import process7 from "process";
458
412
  var SKEW_MS = 6e4;
459
- async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = process8.env) {
413
+ async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = process7.env) {
460
414
  const path = deviceSessionFile(env);
461
415
  const cached = readJsonFile(path);
462
416
  if (cached?.token && cached.platform === audience && cached.deviceId === credential2.deviceId && (cached.expiresAt ?? 0) > Date.now() + SKEW_MS) return cached;
@@ -471,13 +425,13 @@ async function deviceSessionToken(platformUrl, audience, credential2, doFetch, e
471
425
  }
472
426
 
473
427
  // src/machine-identity.ts
474
- import process9 from "process";
475
- function readMachineIdentity(audience, env = process9.env) {
428
+ import process8 from "process";
429
+ function readMachineIdentity(audience, env = process8.env) {
476
430
  const stored = readJsonFile(identityFile(env));
477
431
  if (!stored || typeof stored.email !== "string" || !stored.email) return null;
478
432
  return stored.platform === audience ? { platform: audience, email: stored.email } : null;
479
433
  }
480
- function rememberMachineIdentity(audience, email, env = process9.env) {
434
+ function rememberMachineIdentity(audience, email, env = process8.env) {
481
435
  if (!email) return;
482
436
  const existing = readMachineIdentity(audience, env);
483
437
  if (existing?.email === email) return;
@@ -492,14 +446,14 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
492
446
  const cached = readJsonFile(cfg.local.tokenFile);
493
447
  if (!grantRequest.forceReview && !grantRequest.freshLogin) {
494
448
  if (options.token) return options.token;
495
- if (process10.env.ODLA_DEV_TOKEN) {
496
- const declared = process10.env.ODLA_DEV_TOKEN_AUDIENCE;
449
+ if (process9.env.ODLA_DEV_TOKEN) {
450
+ const declared = process9.env.ODLA_DEV_TOKEN_AUDIENCE;
497
451
  if (declared) {
498
452
  if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
499
453
  } else if (audience !== "https://odla.ai") {
500
454
  throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
501
455
  }
502
- return process10.env.ODLA_DEV_TOKEN;
456
+ return process9.env.ODLA_DEV_TOKEN;
503
457
  }
504
458
  const device = readDeviceCredential(audience);
505
459
  if (device) {
@@ -614,7 +568,7 @@ function stillPending(pending, email) {
614
568
  );
615
569
  }
616
570
  function handshakeEmail(value2, cached) {
617
- const email = (value2 ?? process10.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
571
+ const email = (value2 ?? process9.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
618
572
  if (/@users\.noreply\.github\.com$/i.test(email)) {
619
573
  throw new Error(
620
574
  `"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
@@ -645,12 +599,12 @@ function platformAudience(value2) {
645
599
  }
646
600
 
647
601
  // src/secret-input.ts
648
- import process11 from "process";
602
+ import process10 from "process";
649
603
  var MAX_BYTES = 64 * 1024;
650
604
  async function secretInputValue(options, kind = "credential") {
651
605
  if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
652
606
  let value2;
653
- if (options.fromEnv) value2 = process11.env[options.fromEnv];
607
+ if (options.fromEnv) value2 = process10.env[options.fromEnv];
654
608
  else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
655
609
  else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
656
610
  value2 = value2?.replace(/[\r\n]+$/, "");
@@ -658,7 +612,7 @@ async function secretInputValue(options, kind = "credential") {
658
612
  if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
659
613
  return value2;
660
614
  }
661
- async function readSecretStream(kind, stream = process11.stdin) {
615
+ async function readSecretStream(kind, stream = process10.stdin) {
662
616
  let value2 = "";
663
617
  for await (const chunk of stream) {
664
618
  value2 += String(chunk);
@@ -668,8 +622,8 @@ async function readSecretStream(kind, stream = process11.stdin) {
668
622
  }
669
623
 
670
624
  // src/admin-ai-auth.ts
671
- import { join as join4 } from "path";
672
- import process12 from "process";
625
+ import { join as join3 } from "path";
626
+ import process11 from "process";
673
627
  import { requestToken as requestToken2 } from "@odla-ai/db";
674
628
  async function getScopedPlatformToken(options) {
675
629
  return resolveAdminPlatformToken(options);
@@ -677,7 +631,7 @@ async function getScopedPlatformToken(options) {
677
631
  async function resolveAdminPlatformToken(options) {
678
632
  const audience = platformAudience(options.platform);
679
633
  if (options.token) return options.token;
680
- const fromEnv = process12.env.ODLA_ADMIN_TOKEN;
634
+ const fromEnv = process11.env.ODLA_ADMIN_TOKEN;
681
635
  if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
682
636
  return scopedToken(
683
637
  audience,
@@ -689,7 +643,7 @@ async function resolveAdminPlatformToken(options) {
689
643
  }
690
644
  function audienceBoundEnvToken(token, platform) {
691
645
  const audience = platformAudience(platform);
692
- const declared = process12.env.ODLA_ADMIN_TOKEN_AUDIENCE;
646
+ const declared = process11.env.ODLA_ADMIN_TOKEN_AUDIENCE;
693
647
  if (declared) {
694
648
  if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
695
649
  } else if (audience !== "https://odla.ai") {
@@ -716,9 +670,9 @@ var SCOPE_PURPOSE = {
716
670
  };
717
671
  async function scopedToken(platform, scope, options, doFetch, out) {
718
672
  const audience = platformAudience(platform);
719
- const rootDir = options.rootDir ?? process12.cwd();
673
+ const rootDir = options.rootDir ?? process11.cwd();
720
674
  const tokenFile = options.tokenFile ?? scopedTokenFile();
721
- adoptRepoLocalCache(join4(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
675
+ adoptRepoLocalCache(join3(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
722
676
  const device = readDeviceCredential(audience);
723
677
  if (device && options.cache !== false) {
724
678
  const session = await deviceSessionToken(platform, audience, device, doFetch);
@@ -934,7 +888,7 @@ function isRecord2(value2) {
934
888
 
935
889
  // src/admin-ai.ts
936
890
  async function adminAi(options) {
937
- const platform = platformAudience(options.platform ?? process13.env.ODLA_PLATFORM ?? "https://odla.ai");
891
+ const platform = platformAudience(options.platform ?? process12.env.ODLA_PLATFORM ?? "https://odla.ai");
938
892
  const doFetch = options.fetch ?? fetch;
939
893
  const out = options.stdout ?? console;
940
894
  const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
@@ -1446,13 +1400,13 @@ async function adminSpend(parsed, ctx) {
1446
1400
  }
1447
1401
 
1448
1402
  // src/operator-context.ts
1449
- import { existsSync as existsSync6 } from "fs";
1450
- import { join as join6, resolve as resolve4 } from "path";
1451
- import process15 from "process";
1403
+ import { existsSync as existsSync5 } from "fs";
1404
+ import { join as join5, resolve as resolve4 } from "path";
1405
+ import process14 from "process";
1452
1406
 
1453
1407
  // src/config.ts
1454
- import { existsSync as existsSync4, rmSync as rmSync4, readFileSync as readFileSync3 } from "fs";
1455
- import { dirname as dirname4, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1408
+ import { existsSync as existsSync3, rmSync as rmSync3, readFileSync as readFileSync3 } from "fs";
1409
+ import { dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1456
1410
  import { pathToFileURL } from "url";
1457
1411
  import { appServiceDefinition, appServiceIds } from "@odla-ai/apps";
1458
1412
 
@@ -1862,11 +1816,11 @@ var stderr = { error: (message2) => {
1862
1816
  } };
1863
1817
  async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
1864
1818
  const resolved = resolve2(configPath);
1865
- if (!existsSync4(resolved)) {
1819
+ if (!existsSync3(resolved)) {
1866
1820
  throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
1867
1821
  }
1868
1822
  const raw = await loadConfigModule(resolved);
1869
- const rootDir = dirname4(resolved);
1823
+ const rootDir = dirname3(resolved);
1870
1824
  validateRawConfig(raw, resolved);
1871
1825
  const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
1872
1826
  const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
@@ -1883,7 +1837,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
1883
1837
  };
1884
1838
  adoptRepoLocalCache(resolve2(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
1885
1839
  adoptRepoLocalCache(resolve2(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
1886
- rmSync4(resolve2(rootDir, ".odla/handshake.local.json"), { force: true });
1840
+ rmSync3(resolve2(rootDir, ".odla/handshake.local.json"), { force: true });
1887
1841
  return {
1888
1842
  ...raw,
1889
1843
  configPath: resolved,
@@ -1991,18 +1945,18 @@ function unique3(values) {
1991
1945
  }
1992
1946
 
1993
1947
  // src/operator-profiles.ts
1994
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1995
- import { homedir as homedir3 } from "os";
1996
- import { dirname as dirname5, join as join5, resolve as resolve3 } from "path";
1997
- import process14 from "process";
1948
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1949
+ import { homedir as homedir2 } from "os";
1950
+ import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
1951
+ import process13 from "process";
1998
1952
  function operatorProfileFile() {
1999
1953
  return resolve3(
2000
- clean(process14.env.ODLA_CONTEXT_FILE) ?? join5(homedir3(), ".odla", "contexts.json")
1954
+ clean(process13.env.ODLA_CONTEXT_FILE) ?? join4(homedir2(), ".odla", "contexts.json")
2001
1955
  );
2002
1956
  }
2003
1957
  function resolveOperatorProfile(parsed) {
2004
1958
  const fromFlag = clean(stringOpt(parsed.options.context));
2005
- const fromEnvironment = clean(process14.env.ODLA_CONTEXT);
1959
+ const fromEnvironment = clean(process13.env.ODLA_CONTEXT);
2006
1960
  const name = fromFlag ?? fromEnvironment ?? null;
2007
1961
  const file = operatorProfileFile();
2008
1962
  if (!name) {
@@ -2042,10 +1996,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
2042
1996
  return true;
2043
1997
  }
2044
1998
  function operatorCredentialFiles(selection) {
2045
- const base = selection.name ? join5(dirname5(selection.file), "profiles", selection.name) : join5(homedir3(), ".odla");
1999
+ const base = selection.name ? join4(dirname4(selection.file), "profiles", selection.name) : join4(homedir2(), ".odla");
2046
2000
  return {
2047
- developer: join5(base, "dev-token.json"),
2048
- scoped: join5(base, "admin-token.local.json")
2001
+ developer: join4(base, "dev-token.json"),
2002
+ scoped: join4(base, "admin-token.local.json")
2049
2003
  };
2050
2004
  }
2051
2005
  function assertOperatorName(value2, label) {
@@ -2056,7 +2010,7 @@ function assertOperatorName(value2, label) {
2056
2010
  }
2057
2011
  }
2058
2012
  function readOperatorProfiles(file) {
2059
- if (!existsSync5(file)) return emptyProfiles();
2013
+ if (!existsSync4(file)) return emptyProfiles();
2060
2014
  let raw;
2061
2015
  try {
2062
2016
  raw = JSON.parse(readFileSync4(file, "utf8"));
@@ -2125,19 +2079,19 @@ async function resolveOperatorContext(parsed, options = {}) {
2125
2079
  const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
2126
2080
  const configPath = resolve4(configArgument);
2127
2081
  const explicitConfig = parsed.options.config !== void 0;
2128
- const hasConfig = existsSync6(configPath);
2082
+ const hasConfig = existsSync5(configPath);
2129
2083
  if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
2130
2084
  await loadProjectConfig(configArgument);
2131
2085
  }
2132
2086
  const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
2133
2087
  const platformFlag = clean2(stringOpt(parsed.options.platform));
2134
- const platformEnvironment = clean2(process15.env.ODLA_PLATFORM_URL);
2088
+ const platformEnvironment = clean2(process14.env.ODLA_PLATFORM_URL);
2135
2089
  const platformValue = platformAudience(
2136
2090
  platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
2137
2091
  );
2138
2092
  const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
2139
2093
  const appFlag = clean2(stringOpt(parsed.options.app));
2140
- const appEnvironment = clean2(process15.env.ODLA_APP_ID);
2094
+ const appEnvironment = clean2(process14.env.ODLA_APP_ID);
2141
2095
  const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
2142
2096
  const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
2143
2097
  if (appValue) {
@@ -2151,16 +2105,16 @@ async function resolveOperatorContext(parsed, options = {}) {
2151
2105
  );
2152
2106
  }
2153
2107
  const envFlag = clean2(stringOpt(parsed.options.env));
2154
- const envEnvironment = clean2(process15.env.ODLA_ENV);
2108
+ const envEnvironment = clean2(process14.env.ODLA_ENV);
2155
2109
  const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
2156
2110
  const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
2157
2111
  if (environmentValue) {
2158
2112
  assertOperatorName(environmentValue, "environment");
2159
2113
  }
2160
- const rootDir = loaded?.rootDir ?? process15.cwd();
2114
+ const rootDir = loaded?.rootDir ?? process14.cwd();
2161
2115
  const profileCredentials = operatorCredentialFiles(profile);
2162
- const tokenFile = clean2(process15.env.ODLA_DEV_TOKEN_FILE) ? resolve4(process15.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
2163
- const scopedTokenFile2 = clean2(process15.env.ODLA_ADMIN_TOKEN_FILE) ? resolve4(process15.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join6(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
2116
+ const tokenFile = clean2(process14.env.ODLA_DEV_TOKEN_FILE) ? resolve4(process14.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
2117
+ const scopedTokenFile2 = clean2(process14.env.ODLA_ADMIN_TOKEN_FILE) ? resolve4(process14.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join5(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
2164
2118
  const cfg = loaded ? {
2165
2119
  ...loaded,
2166
2120
  platformUrl: platformValue,
@@ -2182,8 +2136,8 @@ async function resolveOperatorContext(parsed, options = {}) {
2182
2136
  services: [],
2183
2137
  local: {
2184
2138
  tokenFile,
2185
- credentialsFile: join6(rootDir, ".odla", "credentials.local.json"),
2186
- devVarsFile: join6(rootDir, ".dev.vars"),
2139
+ credentialsFile: join5(rootDir, ".odla", "credentials.local.json"),
2140
+ devVarsFile: join5(rootDir, ".dev.vars"),
2187
2141
  gitignore: true
2188
2142
  }
2189
2143
  };
@@ -2307,7 +2261,7 @@ async function adminCommand(parsed, deps = {}) {
2307
2261
  }
2308
2262
 
2309
2263
  // src/auth-command.ts
2310
- import process16 from "process";
2264
+ import process15 from "process";
2311
2265
 
2312
2266
  // src/whoami-command.ts
2313
2267
  var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
@@ -2491,7 +2445,7 @@ async function authCommand(parsed, deps = {}) {
2491
2445
  const { cfg } = context;
2492
2446
  const out = deps.stdout ?? console;
2493
2447
  const doFetch = deps.fetch ?? fetch;
2494
- const email = stringOpt(parsed.options.email) ?? process16.env.ODLA_USER_EMAIL?.trim();
2448
+ const email = stringOpt(parsed.options.email) ?? process15.env.ODLA_USER_EMAIL?.trim();
2495
2449
  if (!email) {
2496
2450
  throw new Error(
2497
2451
  "auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
@@ -2859,7 +2813,7 @@ async function appCommand(parsed, dependencies = {}) {
2859
2813
 
2860
2814
  // src/brand-command.ts
2861
2815
  import { mkdir, readFile, writeFile } from "fs/promises";
2862
- import { dirname as dirname6, resolve as resolve5 } from "path";
2816
+ import { dirname as dirname5, resolve as resolve5 } from "path";
2863
2817
 
2864
2818
  // src/brand-design-unpack.ts
2865
2819
  import { gunzipSync } from "zlib";
@@ -2974,7 +2928,7 @@ async function readBundle(source, deps) {
2974
2928
  async function writeAll(result, outDir) {
2975
2929
  for (const file of result.files) {
2976
2930
  const target = resolve5(outDir, file.path);
2977
- await mkdir(dirname6(target), { recursive: true });
2931
+ await mkdir(dirname5(target), { recursive: true });
2978
2932
  await writeFile(target, file.bytes);
2979
2933
  }
2980
2934
  }
@@ -3541,7 +3495,7 @@ import {
3541
3495
  AppsError,
3542
3496
  createAppsClient
3543
3497
  } from "@odla-ai/apps";
3544
- import { join as join7 } from "path";
3498
+ import { join as join6 } from "path";
3545
3499
 
3546
3500
  // src/config-operation-error.ts
3547
3501
  var ConfigOperationCommandError = class extends Error {
@@ -3966,7 +3920,7 @@ async function operationClient(cfg, options, purpose) {
3966
3920
  platform: cfg.platformUrl,
3967
3921
  scope: "app:config:write",
3968
3922
  token: options.token,
3969
- tokenFile: join7(cfg.rootDir, ".odla", "admin-token.local.json"),
3923
+ tokenFile: join6(cfg.rootDir, ".odla", "admin-token.local.json"),
3970
3924
  rootDir: cfg.rootDir,
3971
3925
  email: options.email,
3972
3926
  open: options.open,
@@ -4021,7 +3975,7 @@ function record4(value2) {
4021
3975
 
4022
3976
  // src/config-reconcile-command.ts
4023
3977
  import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
4024
- import { join as join8 } from "path";
3978
+ import { join as join7 } from "path";
4025
3979
 
4026
3980
  // src/config-reconcile.ts
4027
3981
  import { appServiceIds as appServiceIds2, orderAppServices as orderAppServices2 } from "@odla-ai/apps";
@@ -4317,7 +4271,7 @@ async function inspectConfig(options) {
4317
4271
  platform: cfg.platformUrl,
4318
4272
  scope: "app:config:read",
4319
4273
  token: options.token,
4320
- tokenFile: join8(cfg.rootDir, ".odla", "admin-token.local.json"),
4274
+ tokenFile: join7(cfg.rootDir, ".odla", "admin-token.local.json"),
4321
4275
  rootDir: cfg.rootDir,
4322
4276
  email: options.email,
4323
4277
  open: options.open,
@@ -4449,13 +4403,13 @@ function quoteArg2(value2) {
4449
4403
 
4450
4404
  // src/doctor-checks.ts
4451
4405
  import { execFileSync } from "child_process";
4452
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
4453
- import { join as join10, resolve as resolve6 } from "path";
4406
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
4407
+ import { join as join9, resolve as resolve6 } from "path";
4454
4408
 
4455
4409
  // src/wrangler.ts
4456
4410
  import { spawn as spawn2 } from "child_process";
4457
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
4458
- import { join as join9 } from "path";
4411
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
4412
+ import { join as join8 } from "path";
4459
4413
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
4460
4414
  const child = spawn2(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
4461
4415
  let stdout = "";
@@ -4469,8 +4423,8 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
4469
4423
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
4470
4424
  function findWranglerConfig(rootDir) {
4471
4425
  for (const name of WRANGLER_CONFIG_FILES) {
4472
- const path = join9(rootDir, name);
4473
- if (existsSync7(path)) return path;
4426
+ const path = join8(rootDir, name);
4427
+ if (existsSync6(path)) return path;
4474
4428
  }
4475
4429
  return null;
4476
4430
  }
@@ -4630,7 +4584,7 @@ function wranglerWarnings(rootDir) {
4630
4584
  const dir = resolve6(rootDir, assets.directory);
4631
4585
  if (dir === resolve6(rootDir)) {
4632
4586
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
4633
- } else if (existsSync8(join10(dir, "node_modules"))) {
4587
+ } else if (existsSync7(join9(dir, "node_modules"))) {
4634
4588
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
4635
4589
  }
4636
4590
  }
@@ -4666,7 +4620,7 @@ function o11yProjectWarnings(rootDir) {
4666
4620
  return warnings;
4667
4621
  }
4668
4622
  const main = typeof config.main === "string" ? resolve6(rootDir, config.main) : null;
4669
- if (!main || !existsSync8(main)) {
4623
+ if (!main || !existsSync7(main)) {
4670
4624
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
4671
4625
  } else {
4672
4626
  let source = "";
@@ -4695,7 +4649,7 @@ function calendarProjectWarnings(rootDir) {
4695
4649
  }
4696
4650
  function readPackageJson(rootDir) {
4697
4651
  try {
4698
- return JSON.parse(readFileSync8(join10(rootDir, "package.json"), "utf8"));
4652
+ return JSON.parse(readFileSync8(join9(rootDir, "package.json"), "utf8"));
4699
4653
  } catch {
4700
4654
  return null;
4701
4655
  }
@@ -4989,14 +4943,14 @@ function harnessOption(value2, flag) {
4989
4943
  }
4990
4944
 
4991
4945
  // src/init.ts
4992
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
4993
- import { dirname as dirname7, resolve as resolve7 } from "path";
4946
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
4947
+ import { dirname as dirname6, resolve as resolve7 } from "path";
4994
4948
  import { appServiceDefinition as appServiceDefinition3, appServiceIds as appServiceIds3 } from "@odla-ai/apps";
4995
4949
  function initProject(options) {
4996
4950
  const out = options.stdout ?? console;
4997
4951
  const rootDir = resolve7(options.rootDir ?? process.cwd());
4998
4952
  const configPath = resolve7(rootDir, options.configPath ?? "odla.config.mjs");
4999
- if (existsSync9(configPath) && !options.force) {
4953
+ if (existsSync8(configPath) && !options.force) {
5000
4954
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
5001
4955
  }
5002
4956
  if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
@@ -5012,9 +4966,9 @@ function initProject(options) {
5012
4966
  }
5013
4967
  }
5014
4968
  const aiProvider = options.aiProvider;
5015
- mkdirSync3(dirname7(configPath), { recursive: true });
5016
- mkdirSync3(resolve7(rootDir, "src/odla"), { recursive: true });
5017
- mkdirSync3(resolve7(rootDir, ".odla"), { recursive: true });
4969
+ mkdirSync2(dirname6(configPath), { recursive: true });
4970
+ mkdirSync2(resolve7(rootDir, "src/odla"), { recursive: true });
4971
+ mkdirSync2(resolve7(rootDir, ".odla"), { recursive: true });
5018
4972
  writeFileSync2(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
5019
4973
  writeIfMissing(resolve7(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5020
4974
  writeIfMissing(resolve7(rootDir, "src/odla/rules.mjs"), rulesTemplate());
@@ -5024,7 +4978,7 @@ function initProject(options) {
5024
4978
  out.log("updated .gitignore for local odla credentials");
5025
4979
  }
5026
4980
  function writeIfMissing(path, text4) {
5027
- if (existsSync9(path)) return;
4981
+ if (existsSync8(path)) return;
5028
4982
  writeFileSync2(path, text4);
5029
4983
  }
5030
4984
  function configTemplate(input) {
@@ -5323,9 +5277,9 @@ function printReport(report5, out) {
5323
5277
  }
5324
5278
 
5325
5279
  // src/skill.ts
5326
- import { existsSync as existsSync10, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync9, readdirSync, writeFileSync as writeFileSync3 } from "fs";
5327
- import { homedir as homedir4 } from "os";
5328
- import { dirname as dirname8, isAbsolute as isAbsolute3, join as join11, relative as relative2, resolve as resolve8, sep } from "path";
5280
+ import { existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync, writeFileSync as writeFileSync3 } from "fs";
5281
+ import { homedir as homedir3 } from "os";
5282
+ import { dirname as dirname7, isAbsolute as isAbsolute3, join as join10, relative as relative2, resolve as resolve8, sep } from "path";
5329
5283
  import { fileURLToPath } from "url";
5330
5284
 
5331
5285
  // src/skill-adapters.ts
@@ -5425,7 +5379,7 @@ function installSkill(options = {}) {
5425
5379
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
5426
5380
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
5427
5381
  const root = resolve8(options.dir ?? process.cwd());
5428
- const home = resolve8(options.homeDir ?? homedir4());
5382
+ const home = resolve8(options.homeDir ?? homedir3());
5429
5383
  const plans = /* @__PURE__ */ new Map();
5430
5384
  const targets = /* @__PURE__ */ new Map();
5431
5385
  const rememberTarget = (harness, target) => {
@@ -5439,48 +5393,48 @@ function installSkill(options = {}) {
5439
5393
  plans.set(target, { target, content: content2, boundary, managedMerge });
5440
5394
  };
5441
5395
  const planSkillTree = (targetDir2, boundary = root) => {
5442
- for (const rel of files) plan(join11(targetDir2, rel), readFileSync9(join11(sourceDir, rel), "utf8"), false, boundary);
5396
+ for (const rel of files) plan(join10(targetDir2, rel), readFileSync9(join10(sourceDir, rel), "utf8"), false, boundary);
5443
5397
  };
5444
5398
  let targetDir;
5445
5399
  if (options.global) {
5446
- const claudeRoot = join11(home, ".claude", "skills");
5447
- const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ?? join11(home, ".codex"), "skills");
5400
+ const claudeRoot = join10(home, ".claude", "skills");
5401
+ const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ?? join10(home, ".codex"), "skills");
5448
5402
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
5449
5403
  for (const harness of harnesses) {
5450
5404
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
5451
- planSkillTree(skillRoot, harness === "claude" ? home : dirname8(dirname8(codexRoot)));
5405
+ planSkillTree(skillRoot, harness === "claude" ? home : dirname7(dirname7(codexRoot)));
5452
5406
  rememberTarget(harness, skillRoot);
5453
5407
  }
5454
5408
  } else {
5455
- const sharedRoot = join11(root, ".agents", "skills");
5409
+ const sharedRoot = join10(root, ".agents", "skills");
5456
5410
  planSkillTree(sharedRoot);
5457
- const claudeRoot = join11(root, ".claude", "skills");
5411
+ const claudeRoot = join10(root, ".claude", "skills");
5458
5412
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
5459
5413
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
5460
5414
  if (harnesses.includes("claude")) {
5461
5415
  for (const skill of skillNames(files)) {
5462
- const canonical2 = readFileSync9(join11(sourceDir, skill, "SKILL.md"), "utf8");
5463
- plan(join11(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
5416
+ const canonical2 = readFileSync9(join10(sourceDir, skill, "SKILL.md"), "utf8");
5417
+ plan(join10(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
5464
5418
  }
5465
5419
  rememberTarget("claude", claudeRoot);
5466
5420
  }
5467
5421
  if (harnesses.includes("cursor")) {
5468
- const cursorRule = join11(root, ".cursor", "rules", "odla.mdc");
5422
+ const cursorRule = join10(root, ".cursor", "rules", "odla.mdc");
5469
5423
  plan(cursorRule, CURSOR_RULE);
5470
5424
  rememberTarget("cursor", cursorRule);
5471
5425
  }
5472
5426
  if (harnesses.includes("agents")) {
5473
- const agentsFile = join11(root, "AGENTS.md");
5427
+ const agentsFile = join10(root, "AGENTS.md");
5474
5428
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
5475
5429
  rememberTarget("agents", agentsFile);
5476
5430
  }
5477
5431
  if (harnesses.includes("copilot")) {
5478
- const copilotFile = join11(root, ".github", "copilot-instructions.md");
5432
+ const copilotFile = join10(root, ".github", "copilot-instructions.md");
5479
5433
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
5480
5434
  rememberTarget("copilot", copilotFile);
5481
5435
  }
5482
5436
  if (harnesses.includes("gemini")) {
5483
- const geminiFile = join11(root, "GEMINI.md");
5437
+ const geminiFile = join10(root, "GEMINI.md");
5484
5438
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
5485
5439
  rememberTarget("gemini", geminiFile);
5486
5440
  }
@@ -5494,7 +5448,7 @@ function installSkill(options = {}) {
5494
5448
  conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
5495
5449
  continue;
5496
5450
  }
5497
- if (!existsSync10(file.target)) {
5451
+ if (!existsSync9(file.target)) {
5498
5452
  writtenPaths.add(file.target);
5499
5453
  continue;
5500
5454
  }
@@ -5515,8 +5469,8 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
5515
5469
  );
5516
5470
  }
5517
5471
  for (const file of plans.values()) {
5518
- if (!existsSync10(file.target) || readFileSync9(file.target, "utf8") !== file.content) {
5519
- mkdirSync4(dirname8(file.target), { recursive: true });
5472
+ if (!existsSync9(file.target) || readFileSync9(file.target, "utf8") !== file.content) {
5473
+ mkdirSync3(dirname7(file.target), { recursive: true });
5520
5474
  writeFileSync3(file.target, file.content);
5521
5475
  }
5522
5476
  }
@@ -5558,7 +5512,7 @@ function normalizeHarnesses(values, global) {
5558
5512
  function managedFileContent(path, block2, force, boundary) {
5559
5513
  const symlink = symlinkedComponent(boundary, path);
5560
5514
  if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
5561
- if (!existsSync10(path)) return `${block2}
5515
+ if (!existsSync9(path)) return `${block2}
5562
5516
  `;
5563
5517
  const current = readFileSync9(path, "utf8");
5564
5518
  const start = "<!-- odla-ai agent setup:start -->";
@@ -5587,7 +5541,7 @@ function symlinkedComponent(boundary, target) {
5587
5541
  }
5588
5542
  let current = boundary;
5589
5543
  for (const part of rel.split(sep).filter(Boolean)) {
5590
- current = join11(current, part);
5544
+ current = join10(current, part);
5591
5545
  try {
5592
5546
  if (lstatSync(current).isSymbolicLink()) return current;
5593
5547
  } catch (error) {
@@ -5600,11 +5554,11 @@ function skillNames(files) {
5600
5554
  return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
5601
5555
  }
5602
5556
  function listFiles(dir) {
5603
- if (!existsSync10(dir)) return [];
5557
+ if (!existsSync9(dir)) return [];
5604
5558
  const results = [];
5605
5559
  const walk = (current) => {
5606
5560
  for (const entry of readdirSync(current, { withFileTypes: true })) {
5607
- const path = join11(current, entry.name);
5561
+ const path = join10(current, entry.name);
5608
5562
  if (entry.isDirectory()) walk(path);
5609
5563
  else results.push(relative2(dir, path));
5610
5564
  }
@@ -5957,7 +5911,7 @@ async function projectCommand(command, parsed, deps) {
5957
5911
  }
5958
5912
 
5959
5913
  // src/code-connect.ts
5960
- import { existsSync as existsSync11 } from "fs";
5914
+ import { existsSync as existsSync10 } from "fs";
5961
5915
  import { cpus, hostname, totalmem } from "os";
5962
5916
  import { resolve as resolve11 } from "path";
5963
5917
 
@@ -5968,7 +5922,7 @@ var HARNESS_PROTOCOL_VERSION = 1;
5968
5922
  import { execFile, spawn as spawn3 } from "child_process";
5969
5923
  import { constants } from "fs";
5970
5924
  import { access } from "fs/promises";
5971
- import { delimiter, join as join12 } from "path";
5925
+ import { delimiter, join as join11 } from "path";
5972
5926
  import { getgid, getuid } from "process";
5973
5927
  import { mkdir as mkdir2, mkdtemp, realpath, rm, writeFile as writeFile2 } from "fs/promises";
5974
5928
  import { tmpdir } from "os";
@@ -5986,7 +5940,7 @@ function assertPinnedImage(image) {
5986
5940
  async function commandAvailable(engine) {
5987
5941
  for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
5988
5942
  try {
5989
- await access(join12(directory, engine), constants.X_OK);
5943
+ await access(join11(directory, engine), constants.X_OK);
5990
5944
  return true;
5991
5945
  } catch {
5992
5946
  }
@@ -6684,10 +6638,10 @@ import { randomUUID } from "crypto";
6684
6638
  import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
6685
6639
  import { createReadStream } from "fs";
6686
6640
  import { lstat as lstat22 } from "fs/promises";
6687
- import { join as join14 } from "path";
6641
+ import { join as join13 } from "path";
6688
6642
  import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
6689
6643
  import { tmpdir as tmpdir3 } from "os";
6690
- import { dirname as dirname10, join as join23, resolve as resolve32, sep as sep23 } from "path";
6644
+ import { dirname as dirname9, join as join23, resolve as resolve32, sep as sep23 } from "path";
6691
6645
  import {
6692
6646
  keepRecentExchanges,
6693
6647
  runAgent
@@ -7085,11 +7039,11 @@ function rollup(graph, kind, options = {}) {
7085
7039
  }
7086
7040
 
7087
7041
  // ../graph/dist/code/index.js
7088
- function dirname9(path) {
7042
+ function dirname8(path) {
7089
7043
  const at = path.lastIndexOf("/");
7090
7044
  return at <= 0 ? "." : path.slice(0, at);
7091
7045
  }
7092
- function join13(base, specifier) {
7046
+ function join12(base, specifier) {
7093
7047
  const parts = [];
7094
7048
  const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
7095
7049
  for (const segment of segments) {
@@ -7113,7 +7067,7 @@ var BARE_IMPORT = /^\s*import\s*["']([^"']+)["']/gm;
7113
7067
  var isSourcePath = (path) => SOURCE.test(path);
7114
7068
  function resolveImport(fromPath, specifier, known) {
7115
7069
  if (!specifier.startsWith(".")) return null;
7116
- const base = join13(dirname9(fromPath), specifier);
7070
+ const base = join12(dirname8(fromPath), specifier);
7117
7071
  const candidates = [
7118
7072
  base,
7119
7073
  base.replace(/\.js$/, ".ts"),
@@ -7917,7 +7871,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
7917
7871
  const receipts = [];
7918
7872
  for (const artifact of recipe2.expectedArtifacts ?? []) {
7919
7873
  try {
7920
- const path = join14(workspaceDir, artifact.path);
7874
+ const path = join13(workspaceDir, artifact.path);
7921
7875
  const info = await lstat22(path);
7922
7876
  if (!info.isFile() || info.isSymbolicLink()) {
7923
7877
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
@@ -8206,7 +8160,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
8206
8160
  if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
8207
8161
  const target = resolve32(sourceDir, file.path);
8208
8162
  if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code source path escapes its root");
8209
- await mkdir3(dirname10(target), { recursive: true });
8163
+ await mkdir3(dirname9(target), { recursive: true });
8210
8164
  await writeFile3(target, file.content, { flag: "wx", mode: 420 });
8211
8165
  }
8212
8166
  for (const reference of snapshot.references ?? []) {
@@ -8221,7 +8175,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
8221
8175
  if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
8222
8176
  const target = resolve32(sourceDir, path);
8223
8177
  if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
8224
- await mkdir3(dirname10(target), { recursive: true });
8178
+ await mkdir3(dirname9(target), { recursive: true });
8225
8179
  await writeFile3(target, file.content, { flag: "wx", mode: 292 });
8226
8180
  }
8227
8181
  }
@@ -8248,7 +8202,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
8248
8202
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
8249
8203
  const target = resolve32(root, path);
8250
8204
  if (!target.startsWith(`${resolve32(root)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
8251
- await mkdir3(dirname10(target), { recursive: true });
8205
+ await mkdir3(dirname9(target), { recursive: true });
8252
8206
  await writeFile3(target, file.content, { flag: "wx", mode: 292 });
8253
8207
  }
8254
8208
  }
@@ -10311,7 +10265,7 @@ var CODE_BUILD_RECIPES = Object.freeze([{
10311
10265
  async function codeConnect(options) {
10312
10266
  const cwd = options.cwd ?? process.cwd();
10313
10267
  const configPath = resolve11(cwd, options.configPath);
10314
- const cfg = existsSync11(configPath) ? await loadProjectConfig(configPath) : null;
10268
+ const cfg = existsSync10(configPath) ? await loadProjectConfig(configPath) : null;
10315
10269
  const requestedAppId = options.appId?.trim();
10316
10270
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
10317
10271
  throw new Error("--app-id must be a valid odla app id");
@@ -10740,13 +10694,13 @@ async function codeCommand(parsed, dependencies) {
10740
10694
  }
10741
10695
 
10742
10696
  // src/operator-credentials.ts
10743
- import process17 from "process";
10697
+ import process16 from "process";
10744
10698
  function developerTokenStatus(context, parsed, now = Date.now()) {
10745
10699
  const cached = readJsonFile(context.cfg.local.tokenFile);
10746
10700
  const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
10747
10701
  const source = clean3(
10748
10702
  stringOpt(parsed.options.token)
10749
- ) ? "flag" : clean3(process17.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
10703
+ ) ? "flag" : clean3(process16.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
10750
10704
  return {
10751
10705
  source,
10752
10706
  cacheFile: context.cfg.local.tokenFile,
@@ -11303,9 +11257,17 @@ Safety:
11303
11257
  grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
11304
11258
  the local cache, prints and opens a fresh exact-project owner-review URL, then
11305
11259
  continues provisioning with the approved replacement credential.
11306
- Before a non-dry-run provision, the executable checks npm's current CLI
11307
- release. A confirmed stale client stops with a safe npx rerun command; a
11308
- workspace-linked client also identifies the worktree that must be updated.
11260
+ Every command says so on STDERR when this CLI is older than the one npm
11261
+ serves, naming the repair for how this executable was launched \u2014 rebuild the
11262
+ worktree, npm i the dependency, or npx the scoped package. The answer is read
11263
+ from a cache under ~/.odla refreshed in the background at most every three
11264
+ hours, so no invocation waits on the registry and stdout is never touched.
11265
+ Set ODLA_CLI_UPDATE_CHECK=0 to switch it off.
11266
+ Before a non-dry-run provision, the executable additionally checks npm LIVE
11267
+ rather than trusting that cache: a security-sensitive grant shape must not
11268
+ ride on a three-hour-old answer. A confirmed stale client stops with a safe
11269
+ npx rerun command; a workspace-linked client also identifies the worktree
11270
+ that must be updated.
11309
11271
  Run Code from a GitHub checkout already connected to an app in Studio; an
11310
11272
  odla.config.mjs may select the app explicitly but is not required. With an
11311
11273
  enrolled code.session device, the Studio repository selection authorizes the
@@ -12640,7 +12602,7 @@ async function pmWatch(ctx, parsed) {
12640
12602
  }
12641
12603
 
12642
12604
  // src/pm-project-context.ts
12643
- import { existsSync as existsSync12, rmSync as rmSync5 } from "fs";
12605
+ import { existsSync as existsSync11, rmSync as rmSync4 } from "fs";
12644
12606
  import { resolve as resolve12 } from "path";
12645
12607
  var pmProjectContextFile = () => pmContextFile();
12646
12608
  function readPmProjectContext(rootDir) {
@@ -12657,9 +12619,9 @@ function writePmProjectContext(rootDir, value2) {
12657
12619
  }
12658
12620
  function adoptLegacySelection(rootDir) {
12659
12621
  const legacy = resolve12(rootDir, ".odla", "pm-project.local.json");
12660
- if (!existsSync12(legacy)) return;
12622
+ if (!existsSync11(legacy)) return;
12661
12623
  const previous = readJsonFile(legacy);
12662
- rmSync5(legacy, { force: true });
12624
+ rmSync4(legacy, { force: true });
12663
12625
  if (!isSelection(previous)) return;
12664
12626
  const selections = readSelections();
12665
12627
  if (selections[previous.appId]) return;
@@ -13689,7 +13651,7 @@ function percent(value2) {
13689
13651
  // src/provision.ts
13690
13652
  import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
13691
13653
  import { putSecret as putSecret2 } from "@odla-ai/ai";
13692
- import process18 from "process";
13654
+ import process17 from "process";
13693
13655
 
13694
13656
  // src/integration-provision.ts
13695
13657
  import { uuidv7 } from "@odla-ai/db";
@@ -14127,7 +14089,7 @@ async function provision(options) {
14127
14089
  await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
14128
14090
  }
14129
14091
  if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
14130
- const key = process18.env[cfg.ai.keyEnv];
14092
+ const key = process17.env[cfg.ai.keyEnv];
14131
14093
  if (key) {
14132
14094
  const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
14133
14095
  await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
@@ -14169,9 +14131,9 @@ async function provision(options) {
14169
14131
 
14170
14132
  // src/record.ts
14171
14133
  import { appendFileSync } from "fs";
14172
- import process19 from "process";
14134
+ import process18 from "process";
14173
14135
  function recordInvocation(parsed) {
14174
- const file = process19.env.ODLA_CLI_RECORD;
14136
+ const file = process18.env.ODLA_CLI_RECORD;
14175
14137
  if (!file) return;
14176
14138
  try {
14177
14139
  const entry = {
@@ -14242,9 +14204,9 @@ function parseDeviceTtl(raw) {
14242
14204
  }
14243
14205
 
14244
14206
  // src/device-command.ts
14245
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
14246
- import { dirname as dirname11 } from "path";
14247
- import process20 from "process";
14207
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
14208
+ import { dirname as dirname10 } from "path";
14209
+ import process19 from "process";
14248
14210
  async function deviceCommand(parsed, deps) {
14249
14211
  assertArgs(parsed, [
14250
14212
  "app",
@@ -14302,7 +14264,7 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
14302
14264
  headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
14303
14265
  body: JSON.stringify({
14304
14266
  name,
14305
- platform: process20.platform,
14267
+ platform: process19.platform,
14306
14268
  appIds: apps,
14307
14269
  ...capabilities ? { capabilities } : {},
14308
14270
  ...scopes ? { scopes } : {},
@@ -14314,14 +14276,14 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
14314
14276
  throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
14315
14277
  }
14316
14278
  const path = deviceCredentialPath();
14317
- mkdirSync5(dirname11(path), { recursive: true });
14279
+ mkdirSync4(dirname10(path), { recursive: true });
14318
14280
  writeFileSync4(path, JSON.stringify({
14319
14281
  token: body.token,
14320
14282
  platform: cfg.platformUrl.replace(/\/$/, ""),
14321
14283
  deviceId: body.device.deviceId,
14322
14284
  name
14323
14285
  }, null, 2));
14324
- chmodSync3(path, 384);
14286
+ chmodSync2(path, 384);
14325
14287
  rememberMachineIdentity(cfg.platformUrl.replace(/\/$/, ""), stringOpt(parsed.options.email));
14326
14288
  supersedeAdvisory("credential.expiring");
14327
14289
  const reach = body.device.appIds.includes(ALL_OWNED_APPS) ? "every app you own, now and later" : body.device.appIds.join(", ");
@@ -14421,7 +14383,7 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
14421
14383
  });
14422
14384
  }
14423
14385
  function defaultDeviceName() {
14424
- return `${process20.env.HOSTNAME ?? process20.env.HOST ?? "machine"}-${process20.platform}`;
14386
+ return `${process19.env.HOSTNAME ?? process19.env.HOST ?? "machine"}-${process19.platform}`;
14425
14387
  }
14426
14388
 
14427
14389
  // src/runbook-actions.ts
@@ -14565,7 +14527,7 @@ async function runbookRemove(ctx, slug) {
14565
14527
 
14566
14528
  // src/runbook-import.ts
14567
14529
  import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
14568
- import { basename as basename2, join as join15 } from "path";
14530
+ import { basename as basename2, join as join14 } from "path";
14569
14531
  function parseRunbook(text4, slug) {
14570
14532
  let rest = text4;
14571
14533
  const meta = {};
@@ -14595,7 +14557,7 @@ function readRunbookDir(dir) {
14595
14557
  if (!files.length) throw new Error(`no .md files in ${dir}`);
14596
14558
  return files.map((file) => {
14597
14559
  const slug = basename2(file, ".md");
14598
- const parsed = parseRunbook(readFileSync11(join15(dir, file), "utf8"), slug);
14560
+ const parsed = parseRunbook(readFileSync11(join14(dir, file), "utf8"), slug);
14599
14561
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
14600
14562
  });
14601
14563
  }
@@ -14663,8 +14625,8 @@ async function upsert(ctx, r, visibility) {
14663
14625
 
14664
14626
  // src/runbook-impact.ts
14665
14627
  import { execFileSync as execFileSync2 } from "child_process";
14666
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
14667
- import { join as join16 } from "path";
14628
+ import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
14629
+ import { join as join15 } from "path";
14668
14630
 
14669
14631
  // src/runbook-impact-scan.ts
14670
14632
  var DECL = /^[+-]\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/;
@@ -14833,8 +14795,8 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
14833
14795
  }
14834
14796
  function manifestLabeller(root) {
14835
14797
  return (workspace) => {
14836
- const manifest = join16(root, workspace, "package.json");
14837
- if (!existsSync13(manifest)) return void 0;
14798
+ const manifest = join15(root, workspace, "package.json");
14799
+ if (!existsSync12(manifest)) return void 0;
14838
14800
  try {
14839
14801
  const name = JSON.parse(readFileSync12(manifest, "utf8")).name;
14840
14802
  return typeof name === "string" ? name : void 0;
@@ -14903,7 +14865,7 @@ function report4(ctx, impacts) {
14903
14865
  async function runbookImpact(ctx, options, deps = {}) {
14904
14866
  const cwd = deps.cwd ?? process.cwd();
14905
14867
  const runGit = deps.runGit ?? gitRunner(cwd);
14906
- const read3 = deps.readRepoFile ?? ((path) => readFileSync12(join16(cwd, path), "utf8"));
14868
+ const read3 = deps.readRepoFile ?? ((path) => readFileSync12(join15(cwd, path), "utf8"));
14907
14869
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
14908
14870
  if (!surfaces.length) {
14909
14871
  return ctx.out.log(
@@ -15030,12 +14992,12 @@ async function runbookComment(ctx, slug, body) {
15030
14992
 
15031
14993
  // src/runbook-editor.ts
15032
14994
  import { spawnSync } from "child_process";
15033
- import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "fs";
14995
+ import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
15034
14996
  import { tmpdir as tmpdir4 } from "os";
15035
- import { join as join17 } from "path";
15036
- import process21 from "process";
14997
+ import { join as join16 } from "path";
14998
+ import process20 from "process";
15037
14999
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
15038
- function resolveEditor(env = process21.env) {
15000
+ function resolveEditor(env = process20.env) {
15039
15001
  for (const name of EDITOR_ENV) {
15040
15002
  const value2 = env[name];
15041
15003
  if (value2 && value2.trim()) return value2.trim();
@@ -15049,8 +15011,8 @@ function defaultRun(command, path) {
15049
15011
  return result.status ?? 0;
15050
15012
  }
15051
15013
  function editText(initial, slug, deps = {}) {
15052
- const env = deps.env ?? process21.env;
15053
- const interactive = deps.interactive ?? (() => Boolean(process21.stdin.isTTY));
15014
+ const env = deps.env ?? process20.env;
15015
+ const interactive = deps.interactive ?? (() => Boolean(process20.stdin.isTTY));
15054
15016
  const editor = resolveEditor(env);
15055
15017
  if (!editor)
15056
15018
  throw new Error(
@@ -15058,8 +15020,8 @@ function editText(initial, slug, deps = {}) {
15058
15020
  );
15059
15021
  if (!interactive())
15060
15022
  throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
15061
- const dir = mkdtempSync(join17(tmpdir4(), "odla-runbook-"));
15062
- const file = join17(dir, `${slug}.md`);
15023
+ const dir = mkdtempSync(join16(tmpdir4(), "odla-runbook-"));
15024
+ const file = join16(dir, `${slug}.md`);
15063
15025
  try {
15064
15026
  writeFileSync5(file, initial, { mode: 384 });
15065
15027
  const code = defaultRunOrInjected(deps)(editor, file);
@@ -15067,7 +15029,7 @@ function editText(initial, slug, deps = {}) {
15067
15029
  const edited = readFileSync13(file, "utf8");
15068
15030
  return edited === initial ? null : edited;
15069
15031
  } finally {
15070
- rmSync6(dir, { recursive: true, force: true });
15032
+ rmSync5(dir, { recursive: true, force: true });
15071
15033
  }
15072
15034
  }
15073
15035
  var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
@@ -15960,6 +15922,11 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
15960
15922
  throw error;
15961
15923
  } finally {
15962
15924
  renderAdvisories(out, advisories);
15925
+ try {
15926
+ const behind = updateNotice();
15927
+ if (behind) out.error(behind);
15928
+ } catch {
15929
+ }
15963
15930
  }
15964
15931
  }
15965
15932
  async function dispatchCli(argv, dependencies) {
@@ -16194,4 +16161,4 @@ export {
16194
16161
  isTerminalHostedSecurityStatus,
16195
16162
  runCli
16196
16163
  };
16197
- //# sourceMappingURL=chunk-7MUWCSGP.js.map
16164
+ //# sourceMappingURL=chunk-JHB6352A.js.map