@odla-ai/cli 0.16.3 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,8 +29,8 @@ For a project you keep working in, install it as a dev dependency so the
29
29
  shorter `npx odla-ai` form works and the version is pinned by your lockfile:
30
30
 
31
31
  ```bash
32
- npm view @odla-ai/cli@0.16.3 version
33
- npm i -D --save-exact @odla-ai/cli@0.16.3
32
+ npm view @odla-ai/cli@0.16.4 version
33
+ npm i -D --save-exact @odla-ai/cli@0.16.4
34
34
  ```
35
35
 
36
36
  ## Prerequisites
@@ -95,7 +95,8 @@ command copied from **Studio → Code → Terminal** while your shell is in the
95
95
  local Git checkout. `--platform`, `--app-id`, and `--env` make the requested
96
96
  scope unambiguous; without an explicit app, the CLI can infer it from
97
97
  `odla.config.mjs` or the Git remote. The terminal proves its isolation engine,
98
- prepares the pinned Pi and build images, and sends an outbound
98
+ builds the CLI-bundled Pi adapter into a content-addressed local image, prepares
99
+ the digest-pinned build images, and sends an outbound
99
100
  `app:code:host:connect` request to odla. The app owner approves that real
100
101
  request in Studio. The browser never mints or displays a host bearer.
101
102
 
@@ -105,9 +106,11 @@ the original checkout is never mounted into Pi. Pi receives no network,
105
106
  provider credential, or container-engine socket and accesses source only
106
107
  through the typed CaMeL broker. The session records the trusted Git base,
107
108
  developer-patch digest, snapshot digest, and the combined checkpoint patch.
108
- Later local edits do not silently alter an active session. Use `--engine
109
- docker` only after reviewing that daemon boundary. `--once` proves enrollment
110
- and one heartbeat without accepting queued work.
109
+ Later local edits do not silently alter an active session. The Pi adapter does
110
+ not come from a private registry and `code connect` never logs the developer
111
+ into one; the only first-run image download is its public, digest-pinned Node
112
+ base. Use `--engine docker` only after reviewing that daemon boundary. `--once`
113
+ proves enrollment and one heartbeat without accepting queued work.
111
114
 
112
115
  The approval and host credential records live in odla-ai/db and store hashes,
113
116
  not plaintext. The collected host bearer exists only in the running CLI
package/dist/bin.cjs CHANGED
@@ -1939,8 +1939,8 @@ function printStatus(status, json, out) {
1939
1939
 
1940
1940
  // src/code-connect.ts
1941
1941
  var import_node_fs7 = require("fs");
1942
- var import_node_os = require("os");
1943
- var import_node_path4 = require("path");
1942
+ var import_node_os2 = require("os");
1943
+ var import_node_path5 = require("path");
1944
1944
 
1945
1945
  // ../harness/dist/chunk-QTUEF2HZ.js
1946
1946
  var HARNESS_PROTOCOL_VERSION = 1;
@@ -2023,7 +2023,7 @@ function encodeAgentInput(message2) {
2023
2023
  `;
2024
2024
  }
2025
2025
 
2026
- // ../harness/dist/chunk-IRFCMVKU.js
2026
+ // ../harness/dist/chunk-NOBK6PCT.js
2027
2027
  var import_child_process = require("child_process");
2028
2028
  var import_fs = require("fs");
2029
2029
  var import_promises2 = require("fs/promises");
@@ -2076,7 +2076,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
2076
2076
  throw new TypeError("no rootless Podman found; install Podman or explicitly choose --engine docker after reviewing its daemon boundary");
2077
2077
  }
2078
2078
  if (platform === "darwin") {
2079
- throw new TypeError("no Apple container or Podman Machine found; install one or explicitly choose --engine docker");
2079
+ throw new TypeError("Apple container is not installed; on Apple Silicon macOS 26 run `brew install container`, then retry (Podman Machine is the fallback)");
2080
2080
  }
2081
2081
  throw new TypeError("no supported container engine found");
2082
2082
  }
@@ -2540,7 +2540,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2540
2540
  }
2541
2541
  }
2542
2542
 
2543
- // ../harness/dist/chunk-4MOSEKB6.js
2543
+ // ../harness/dist/chunk-6J2ZETST.js
2544
2544
  var import_crypto = require("crypto");
2545
2545
  var import_promises5 = require("fs/promises");
2546
2546
  var import_path5 = require("path");
@@ -2877,7 +2877,7 @@ function validateSnapshot(snapshot, limits) {
2877
2877
  }
2878
2878
  }
2879
2879
 
2880
- // ../harness/dist/chunk-4MOSEKB6.js
2880
+ // ../harness/dist/chunk-6J2ZETST.js
2881
2881
  var import_child_process4 = require("child_process");
2882
2882
  var import_promises6 = require("fs/promises");
2883
2883
  var import_path6 = require("path");
@@ -3170,7 +3170,7 @@ function looksLikeDestination(value) {
3170
3170
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
3171
3171
  }
3172
3172
 
3173
- // ../harness/dist/chunk-4MOSEKB6.js
3173
+ // ../harness/dist/chunk-6J2ZETST.js
3174
3174
  var import_crypto4 = require("crypto");
3175
3175
  async function digestStagedWorkspace(root, limits) {
3176
3176
  const files = [];
@@ -4536,6 +4536,7 @@ function runtimeResultError(value) {
4536
4536
  var CodePiRuntimeEngine = class {
4537
4537
  constructor(options) {
4538
4538
  this.options = options;
4539
+ if (options.imageAuthorization === "cli_embedded" && !/^odla-ai\/pi-agent:embedded-sha256-[0-9a-f]{64}$/.test(options.image)) throw new TypeError("CLI-embedded Pi image must use its content-addressed local tag");
4539
4540
  this.#run = options.runAttempt ?? runContainerAttempt;
4540
4541
  this.#buildPolicyDigest = digestRuntimeValue(JSON.stringify(options.recipes));
4541
4542
  this.#checkpoints = new CodeRuntimeCheckpointManager({
@@ -4701,6 +4702,7 @@ var CodePiRuntimeEngine = class {
4701
4702
  const result = await this.#run({
4702
4703
  engine: this.options.engine,
4703
4704
  image: this.options.image,
4705
+ allowUnpinnedImage: this.options.imageAuthorization === "cli_embedded",
4704
4706
  workspaceDir: active.workspace.workspaceDir,
4705
4707
  workspaceAccess: "none",
4706
4708
  task: lease.task,
@@ -5210,23 +5212,18 @@ function digestText(value) {
5210
5212
 
5211
5213
  // src/code-images.ts
5212
5214
  var import_node_child_process4 = require("child_process");
5213
- async function prepareCodeImages(engine, images) {
5214
- for (const image of images) {
5215
- await new Promise((accept, reject) => {
5216
- const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
5217
- (0, import_node_child_process4.execFile)(engine, args, { encoding: "utf8", maxBuffer: 2 * 1024 * 1024, timeout: 10 * 6e4 }, (error) => {
5218
- if (error) reject(new Error(`could not prepare pinned Code image ${image}`));
5219
- else accept();
5220
- });
5221
- });
5222
- }
5223
- }
5215
+ var import_node_crypto2 = require("crypto");
5216
+ var import_promises10 = require("fs/promises");
5217
+ var import_node_os = require("os");
5218
+ var import_node_path4 = require("path");
5219
+ var import_node_url2 = require("url");
5224
5220
 
5225
5221
  // src/code-runtime-config.ts
5226
- var CODE_PI_IMAGE = "ghcr.io/cory/odla-pi-agent@sha256:2a52a1c8eed66ad167e7bccab4d603ed8fcd75c42dddfb320083436ed89b7c9c";
5222
+ var CODE_PI_IMAGE = "odla-ai/pi-agent:embedded";
5223
+ var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
5227
5224
  var CODE_BUILD_RECIPES = Object.freeze([{
5228
5225
  id: "odla-code-contracts",
5229
- image: "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd",
5226
+ image: CODE_NODE_IMAGE,
5230
5227
  command: [
5231
5228
  "node",
5232
5229
  // The clean source mount deliberately has no node_modules. Reuse the
@@ -5246,10 +5243,84 @@ var CODE_BUILD_RECIPES = Object.freeze([{
5246
5243
  pids: 128
5247
5244
  }]);
5248
5245
 
5246
+ // src/code-images.ts
5247
+ var runCodeImageCommand = (command, args, stdio) => new Promise((accept, reject) => {
5248
+ const child = (0, import_node_child_process4.spawn)(command, [...args], { shell: false, stdio });
5249
+ child.once("error", reject);
5250
+ child.once("exit", (code, signal) => {
5251
+ if (code === 0) accept();
5252
+ else reject(new Error(`${command} ${args.join(" ")} exited ${code ?? signal ?? "without a status"}`));
5253
+ });
5254
+ });
5255
+ async function prepareCodeImages(engine, images, run = runCodeImageCommand, buildEmbedded = buildEmbeddedPiImage, nameEmbedded = embeddedPiImageName) {
5256
+ if (engine === "container") {
5257
+ try {
5258
+ await run(engine, ["system", "start"], "inherit");
5259
+ } catch {
5260
+ throw new Error("Apple container could not start; run `container system start` once to complete its lightweight VM setup, then retry");
5261
+ }
5262
+ }
5263
+ const prepared = [];
5264
+ for (const image of images) {
5265
+ const runtimeImage = image === CODE_PI_IMAGE ? await nameEmbedded() : image;
5266
+ const inspectArgs = ["image", "inspect", runtimeImage];
5267
+ try {
5268
+ await run(engine, inspectArgs, "ignore");
5269
+ prepared.push(runtimeImage);
5270
+ continue;
5271
+ } catch {
5272
+ }
5273
+ if (image === CODE_PI_IMAGE) {
5274
+ try {
5275
+ await buildEmbedded(engine, runtimeImage, run);
5276
+ } catch (error) {
5277
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
5278
+ throw new Error(`could not prepare CLI-embedded Code image${detail}`);
5279
+ }
5280
+ prepared.push(runtimeImage);
5281
+ continue;
5282
+ }
5283
+ const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
5284
+ try {
5285
+ await run(engine, args, "inherit");
5286
+ } catch (error) {
5287
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
5288
+ throw new Error(`could not prepare pinned Code image ${image}${detail}`);
5289
+ }
5290
+ prepared.push(image);
5291
+ }
5292
+ return prepared;
5293
+ }
5294
+ function embeddedPiAssetPath() {
5295
+ return (0, import_node_url2.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
5296
+ }
5297
+ async function embeddedPiImageName() {
5298
+ const bundle = await (0, import_promises10.readFile)(embeddedPiAssetPath()).catch(() => {
5299
+ throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
5300
+ });
5301
+ return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto2.createHash)("sha256").update(bundle).digest("hex")}`;
5302
+ }
5303
+ async function buildEmbeddedPiImage(engine, image, run) {
5304
+ const context = await (0, import_promises10.mkdtemp)((0, import_node_path4.join)((0, import_node_os.tmpdir)(), "odla-code-pi-"));
5305
+ try {
5306
+ await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path4.join)(context, "pi-agent.js"));
5307
+ await (0, import_promises10.writeFile)((0, import_node_path4.join)(context, "Dockerfile"), [
5308
+ `FROM ${CODE_NODE_IMAGE}`,
5309
+ "COPY pi-agent.js /opt/odla/pi-agent.js",
5310
+ "WORKDIR /workspace",
5311
+ 'ENTRYPOINT ["node", "/opt/odla/pi-agent.js"]',
5312
+ ""
5313
+ ].join("\n"), { mode: 384 });
5314
+ await run(engine, ["build", "--tag", image, context], "inherit");
5315
+ } finally {
5316
+ await (0, import_promises10.rm)(context, { recursive: true, force: true });
5317
+ }
5318
+ }
5319
+
5249
5320
  // src/code-connect.ts
5250
5321
  async function codeConnect(options) {
5251
5322
  const cwd = options.cwd ?? process.cwd();
5252
- const configPath = (0, import_node_path4.resolve)(cwd, options.configPath);
5323
+ const configPath = (0, import_node_path5.resolve)(cwd, options.configPath);
5253
5324
  const cfg = (0, import_node_fs7.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
5254
5325
  const requestedAppId = options.appId?.trim();
5255
5326
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -5278,12 +5349,13 @@ async function codeConnect(options) {
5278
5349
  const out = options.stdout ?? console;
5279
5350
  const doFetch = options.fetch ?? fetch;
5280
5351
  const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
5281
- await (options.prepareImages ?? prepareCodeImages)(engine, [
5282
- CODE_PI_IMAGE,
5283
- ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))
5284
- ]);
5352
+ const [piImage] = await (options.prepareImages ?? prepareCodeImages)(
5353
+ engine,
5354
+ [CODE_PI_IMAGE, ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))]
5355
+ );
5356
+ if (!piImage || !/^odla-ai\/pi-agent:embedded-sha256-[0-9a-f]{64}$/.test(piImage)) throw new Error("Code image preflight did not produce the content-addressed embedded Pi runtime");
5285
5357
  const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
5286
- const hostName = (options.name ?? (0, import_node_os.hostname)()).trim();
5358
+ const hostName = (options.name ?? (0, import_node_os2.hostname)()).trim();
5287
5359
  if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
5288
5360
  const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
5289
5361
  const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
@@ -5320,12 +5392,13 @@ async function codeConnect(options) {
5320
5392
  platform: hostPlatform,
5321
5393
  arch: process.arch,
5322
5394
  engines: [engine],
5323
- cpuCount: (0, import_node_os.cpus)().length,
5324
- memoryBytes: (0, import_node_os.totalmem)(),
5395
+ cpuCount: (0, import_node_os2.cpus)().length,
5396
+ memoryBytes: (0, import_node_os2.totalmem)(),
5325
5397
  source: descriptor2,
5326
5398
  images: {
5327
5399
  ready: true,
5328
- pi: CODE_PI_IMAGE,
5400
+ pi: piImage,
5401
+ piSource: "cli_embedded",
5329
5402
  recipes: CODE_BUILD_RECIPES.map((recipe2) => ({ id: recipe2.id, image: recipe2.image }))
5330
5403
  }
5331
5404
  };
@@ -5340,6 +5413,7 @@ async function codeConnect(options) {
5340
5413
  engine,
5341
5414
  capabilities,
5342
5415
  localSource,
5416
+ piImage,
5343
5417
  heartbeatMs,
5344
5418
  once: options.once === true,
5345
5419
  signal: options.signal,
@@ -5369,7 +5443,8 @@ async function runCodeRuntime(input) {
5369
5443
  const commandEngine = new CodePiRuntimeEngine({
5370
5444
  control,
5371
5445
  engine: input.engine,
5372
- image: CODE_PI_IMAGE,
5446
+ image: input.piImage ?? input.capabilities.images.pi,
5447
+ imageAuthorization: "cli_embedded",
5373
5448
  recipes: CODE_BUILD_RECIPES,
5374
5449
  recipeAuthorization: "registered_recipe",
5375
5450
  localSource: input.localSource,
@@ -5466,12 +5541,12 @@ async function codeCommand(parsed, dependencies) {
5466
5541
  // src/doctor-checks.ts
5467
5542
  var import_node_child_process6 = require("child_process");
5468
5543
  var import_node_fs9 = require("fs");
5469
- var import_node_path6 = require("path");
5544
+ var import_node_path7 = require("path");
5470
5545
 
5471
5546
  // src/wrangler.ts
5472
5547
  var import_node_child_process5 = require("child_process");
5473
5548
  var import_node_fs8 = require("fs");
5474
- var import_node_path5 = require("path");
5549
+ var import_node_path6 = require("path");
5475
5550
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
5476
5551
  const child = (0, import_node_child_process5.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
5477
5552
  let stdout = "";
@@ -5485,7 +5560,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
5485
5560
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
5486
5561
  function findWranglerConfig(rootDir) {
5487
5562
  for (const name of WRANGLER_CONFIG_FILES) {
5488
- const path = (0, import_node_path5.join)(rootDir, name);
5563
+ const path = (0, import_node_path6.join)(rootDir, name);
5489
5564
  if ((0, import_node_fs8.existsSync)(path)) return path;
5490
5565
  }
5491
5566
  return null;
@@ -5591,10 +5666,10 @@ function wranglerWarnings(rootDir) {
5591
5666
  for (const { label, block } of blocks) {
5592
5667
  const assets = block.assets;
5593
5668
  if (assets?.directory) {
5594
- const dir = (0, import_node_path6.resolve)(rootDir, assets.directory);
5595
- if (dir === (0, import_node_path6.resolve)(rootDir)) {
5669
+ const dir = (0, import_node_path7.resolve)(rootDir, assets.directory);
5670
+ if (dir === (0, import_node_path7.resolve)(rootDir)) {
5596
5671
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
5597
- } else if ((0, import_node_fs9.existsSync)((0, import_node_path6.join)(dir, "node_modules"))) {
5672
+ } else if ((0, import_node_fs9.existsSync)((0, import_node_path7.join)(dir, "node_modules"))) {
5598
5673
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
5599
5674
  }
5600
5675
  }
@@ -5629,7 +5704,7 @@ function o11yProjectWarnings(rootDir) {
5629
5704
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
5630
5705
  return warnings;
5631
5706
  }
5632
- const main = typeof config.main === "string" ? (0, import_node_path6.resolve)(rootDir, config.main) : null;
5707
+ const main = typeof config.main === "string" ? (0, import_node_path7.resolve)(rootDir, config.main) : null;
5633
5708
  if (!main || !(0, import_node_fs9.existsSync)(main)) {
5634
5709
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
5635
5710
  } else {
@@ -5659,7 +5734,7 @@ function calendarProjectWarnings(rootDir) {
5659
5734
  }
5660
5735
  function readPackageJson(rootDir) {
5661
5736
  try {
5662
- return JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path6.join)(rootDir, "package.json"), "utf8"));
5737
+ return JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path7.join)(rootDir, "package.json"), "utf8"));
5663
5738
  } catch {
5664
5739
  return null;
5665
5740
  }
@@ -5861,11 +5936,11 @@ function harnessOption(value, flag) {
5861
5936
 
5862
5937
  // src/init.ts
5863
5938
  var import_node_fs10 = require("fs");
5864
- var import_node_path7 = require("path");
5939
+ var import_node_path8 = require("path");
5865
5940
  function initProject(options) {
5866
5941
  const out = options.stdout ?? console;
5867
- const rootDir = (0, import_node_path7.resolve)(options.rootDir ?? process.cwd());
5868
- const configPath = (0, import_node_path7.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5942
+ const rootDir = (0, import_node_path8.resolve)(options.rootDir ?? process.cwd());
5943
+ const configPath = (0, import_node_path8.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5869
5944
  if ((0, import_node_fs10.existsSync)(configPath) && !options.force) {
5870
5945
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
5871
5946
  }
@@ -5876,12 +5951,12 @@ function initProject(options) {
5876
5951
  const services = options.services?.length ? options.services : ["db", "ai"];
5877
5952
  if (services.includes("calendar") && !services.includes("db")) throw new Error("--services calendar requires db");
5878
5953
  const aiProvider = options.aiProvider ?? "anthropic";
5879
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.dirname)(configPath), { recursive: true });
5880
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.resolve)(rootDir, "src/odla"), { recursive: true });
5881
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.resolve)(rootDir, ".odla"), { recursive: true });
5954
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(configPath), { recursive: true });
5955
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.resolve)(rootDir, "src/odla"), { recursive: true });
5956
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.resolve)(rootDir, ".odla"), { recursive: true });
5882
5957
  (0, import_node_fs10.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
5883
- writeIfMissing((0, import_node_path7.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5884
- writeIfMissing((0, import_node_path7.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
5958
+ writeIfMissing((0, import_node_path8.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5959
+ writeIfMissing((0, import_node_path8.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
5885
5960
  ensureGitignore(rootDir);
5886
5961
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
5887
5962
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -6531,7 +6606,7 @@ async function resolveVaultWrite(options) {
6531
6606
  }
6532
6607
 
6533
6608
  // src/security-command-context.ts
6534
- var import_promises10 = require("readline/promises");
6609
+ var import_promises11 = require("readline/promises");
6535
6610
  async function hostedSecurityContext(parsed, dependencies) {
6536
6611
  const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
6537
6612
  const cfg = await loadProjectConfig(configPath);
@@ -6557,7 +6632,7 @@ async function hostedSecurityContext(parsed, dependencies) {
6557
6632
  async function interactiveConfirmation(message2, dependencies) {
6558
6633
  if (dependencies.confirm) return dependencies.confirm(message2);
6559
6634
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
6560
- const prompt = (0, import_promises10.createInterface)({ input: process.stdin, output: process.stdout });
6635
+ const prompt = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stdout });
6561
6636
  try {
6562
6637
  const answer = await prompt.question(`${message2} [y/N] `);
6563
6638
  return /^y(?:es)?$/i.test(answer.trim());
@@ -6684,7 +6759,7 @@ function hostedSeverity(value, flag) {
6684
6759
  var import_security2 = require("@odla-ai/security");
6685
6760
 
6686
6761
  // src/security.ts
6687
- var import_node_path8 = require("path");
6762
+ var import_node_path9 = require("path");
6688
6763
  var import_security = require("@odla-ai/security");
6689
6764
  var import_node3 = require("@odla-ai/security/node");
6690
6765
  async function runHostedSecurity(options) {
@@ -6696,9 +6771,9 @@ async function runHostedSecurity(options) {
6696
6771
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
6697
6772
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
6698
6773
  const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
6699
- const target = (0, import_node_path8.resolve)(options.target ?? cfg?.rootDir ?? ".");
6700
- const output = (0, import_node_path8.resolve)(options.out ?? (0, import_node_path8.resolve)(target, ".odla/security/hosted"));
6701
- const outputRelative = (0, import_node_path8.relative)(target, output).split(import_node_path8.sep).join("/");
6774
+ const target = (0, import_node_path9.resolve)(options.target ?? cfg?.rootDir ?? ".");
6775
+ const output = (0, import_node_path9.resolve)(options.out ?? (0, import_node_path9.resolve)(target, ".odla/security/hosted"));
6776
+ const outputRelative = (0, import_node_path9.relative)(target, output).split(import_node_path9.sep).join("/");
6702
6777
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
6703
6778
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
6704
6779
  const tokenRequest = {
@@ -6710,7 +6785,7 @@ async function runHostedSecurity(options) {
6710
6785
  };
6711
6786
  const token = await injectedToken(options, tokenRequest);
6712
6787
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
6713
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path8.isAbsolute)(outputRelative) ? [outputRelative] : []
6788
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path9.isAbsolute)(outputRelative) ? [outputRelative] : []
6714
6789
  });
6715
6790
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
6716
6791
  platform,
@@ -6728,7 +6803,7 @@ async function runHostedSecurity(options) {
6728
6803
  });
6729
6804
  const harness = (0, import_security.createSecurityHarness)({
6730
6805
  profile,
6731
- store: new import_node3.FileRunStore((0, import_node_path8.resolve)(output, "state")),
6806
+ store: new import_node3.FileRunStore((0, import_node_path9.resolve)(output, "state")),
6732
6807
  discoveryReasoner: hosted.discoveryReasoner,
6733
6808
  validationReasoner: hosted.validationReasoner,
6734
6809
  policy: {
@@ -6752,7 +6827,7 @@ async function runHostedSecurity(options) {
6752
6827
  function selectEnv(requested, declared, configPath, rootDir) {
6753
6828
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
6754
6829
  if (!env || !declared.includes(env)) {
6755
- const shown = (0, import_node_path8.relative)(rootDir, configPath) || configPath;
6830
+ const shown = (0, import_node_path9.relative)(rootDir, configPath) || configPath;
6756
6831
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
6757
6832
  }
6758
6833
  return env;
@@ -6781,7 +6856,7 @@ function printSummary(out, appId, env, run, report2, output) {
6781
6856
  out.log(` coverage: ${report2.coverageStatus} ${complete}/${report2.coverage.length} blocked=${report2.metrics.blockedCells} shallow=${report2.metrics.shallowCells} unscheduled=${report2.metrics.unscheduledCells} budget_exhausted=${report2.metrics.budgetExhaustedCells}`);
6782
6857
  if (report2.callBudget) out.log(` calls: discovery=${formatBudget(report2.callBudget.discovery)} validation=${formatBudget(report2.callBudget.validation)}`);
6783
6858
  out.log(` findings: confirmed=${report2.metrics.confirmed} needs_reproduction=${report2.metrics.needsReproduction} candidates=${report2.metrics.candidates}`);
6784
- out.log(` report: ${(0, import_node_path8.resolve)(output, "REPORT.md")}`);
6859
+ out.log(` report: ${(0, import_node_path9.resolve)(output, "REPORT.md")}`);
6785
6860
  }
6786
6861
  function formatBudget(usage) {
6787
6862
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
@@ -7220,9 +7295,9 @@ async function securityStatus(parsed, dependencies) {
7220
7295
 
7221
7296
  // src/skill.ts
7222
7297
  var import_node_fs11 = require("fs");
7223
- var import_node_os2 = require("os");
7224
- var import_node_path9 = require("path");
7225
- var import_node_url2 = require("url");
7298
+ var import_node_os3 = require("os");
7299
+ var import_node_path10 = require("path");
7300
+ var import_node_url3 = require("url");
7226
7301
 
7227
7302
  // src/skill-adapters.ts
7228
7303
  var PROJECT_INSTRUCTIONS = `<!-- odla-ai agent setup:start -->
@@ -7278,12 +7353,12 @@ runbook. Do not substitute a remote documentation page for the installed copy.
7278
7353
  var AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
7279
7354
  function installSkill(options = {}) {
7280
7355
  const out = options.stdout ?? console;
7281
- const sourceDir = options.sourceDir ?? (0, import_node_url2.fileURLToPath)(new URL("../skills", importMetaUrl));
7356
+ const sourceDir = options.sourceDir ?? (0, import_node_url3.fileURLToPath)(new URL("../skills", importMetaUrl));
7282
7357
  const files = listFiles(sourceDir);
7283
7358
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
7284
7359
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
7285
- const root = (0, import_node_path9.resolve)(options.dir ?? process.cwd());
7286
- const home = (0, import_node_path9.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
7360
+ const root = (0, import_node_path10.resolve)(options.dir ?? process.cwd());
7361
+ const home = (0, import_node_path10.resolve)(options.homeDir ?? (0, import_node_os3.homedir)());
7287
7362
  const plans = /* @__PURE__ */ new Map();
7288
7363
  const targets = /* @__PURE__ */ new Map();
7289
7364
  const rememberTarget = (harness, target) => {
@@ -7297,48 +7372,48 @@ function installSkill(options = {}) {
7297
7372
  plans.set(target, { target, content, boundary, managedMerge });
7298
7373
  };
7299
7374
  const planSkillTree = (targetDir2, boundary = root) => {
7300
- for (const rel of files) plan((0, import_node_path9.join)(targetDir2, rel), (0, import_node_fs11.readFileSync)((0, import_node_path9.join)(sourceDir, rel), "utf8"), false, boundary);
7375
+ for (const rel of files) plan((0, import_node_path10.join)(targetDir2, rel), (0, import_node_fs11.readFileSync)((0, import_node_path10.join)(sourceDir, rel), "utf8"), false, boundary);
7301
7376
  };
7302
7377
  let targetDir;
7303
7378
  if (options.global) {
7304
- const claudeRoot = (0, import_node_path9.join)(home, ".claude", "skills");
7305
- const codexRoot = (0, import_node_path9.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path9.join)(home, ".codex"), "skills");
7379
+ const claudeRoot = (0, import_node_path10.join)(home, ".claude", "skills");
7380
+ const codexRoot = (0, import_node_path10.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path10.join)(home, ".codex"), "skills");
7306
7381
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
7307
7382
  for (const harness of harnesses) {
7308
7383
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
7309
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path9.dirname)((0, import_node_path9.dirname)(codexRoot)));
7384
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path10.dirname)((0, import_node_path10.dirname)(codexRoot)));
7310
7385
  rememberTarget(harness, skillRoot);
7311
7386
  }
7312
7387
  } else {
7313
- const sharedRoot = (0, import_node_path9.join)(root, ".agents", "skills");
7388
+ const sharedRoot = (0, import_node_path10.join)(root, ".agents", "skills");
7314
7389
  planSkillTree(sharedRoot);
7315
- const claudeRoot = (0, import_node_path9.join)(root, ".claude", "skills");
7390
+ const claudeRoot = (0, import_node_path10.join)(root, ".claude", "skills");
7316
7391
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
7317
7392
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
7318
7393
  if (harnesses.includes("claude")) {
7319
7394
  for (const skill of skillNames(files)) {
7320
- const canonical = (0, import_node_fs11.readFileSync)((0, import_node_path9.join)(sourceDir, skill, "SKILL.md"), "utf8");
7321
- plan((0, import_node_path9.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
7395
+ const canonical = (0, import_node_fs11.readFileSync)((0, import_node_path10.join)(sourceDir, skill, "SKILL.md"), "utf8");
7396
+ plan((0, import_node_path10.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
7322
7397
  }
7323
7398
  rememberTarget("claude", claudeRoot);
7324
7399
  }
7325
7400
  if (harnesses.includes("cursor")) {
7326
- const cursorRule = (0, import_node_path9.join)(root, ".cursor", "rules", "odla.mdc");
7401
+ const cursorRule = (0, import_node_path10.join)(root, ".cursor", "rules", "odla.mdc");
7327
7402
  plan(cursorRule, CURSOR_RULE);
7328
7403
  rememberTarget("cursor", cursorRule);
7329
7404
  }
7330
7405
  if (harnesses.includes("agents")) {
7331
- const agentsFile = (0, import_node_path9.join)(root, "AGENTS.md");
7406
+ const agentsFile = (0, import_node_path10.join)(root, "AGENTS.md");
7332
7407
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7333
7408
  rememberTarget("agents", agentsFile);
7334
7409
  }
7335
7410
  if (harnesses.includes("copilot")) {
7336
- const copilotFile = (0, import_node_path9.join)(root, ".github", "copilot-instructions.md");
7411
+ const copilotFile = (0, import_node_path10.join)(root, ".github", "copilot-instructions.md");
7337
7412
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7338
7413
  rememberTarget("copilot", copilotFile);
7339
7414
  }
7340
7415
  if (harnesses.includes("gemini")) {
7341
- const geminiFile = (0, import_node_path9.join)(root, "GEMINI.md");
7416
+ const geminiFile = (0, import_node_path10.join)(root, "GEMINI.md");
7342
7417
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7343
7418
  rememberTarget("gemini", geminiFile);
7344
7419
  }
@@ -7374,7 +7449,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
7374
7449
  }
7375
7450
  for (const file of plans.values()) {
7376
7451
  if (!(0, import_node_fs11.existsSync)(file.target) || (0, import_node_fs11.readFileSync)(file.target, "utf8") !== file.content) {
7377
- (0, import_node_fs11.mkdirSync)((0, import_node_path9.dirname)(file.target), { recursive: true });
7452
+ (0, import_node_fs11.mkdirSync)((0, import_node_path10.dirname)(file.target), { recursive: true });
7378
7453
  (0, import_node_fs11.writeFileSync)(file.target, file.content);
7379
7454
  }
7380
7455
  }
@@ -7394,7 +7469,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
7394
7469
  };
7395
7470
  }
7396
7471
  function pathsUnder(root, paths) {
7397
- return [...paths].map((path) => (0, import_node_path9.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path9.sep}`) && !(0, import_node_path9.isAbsolute)(path)).sort();
7472
+ return [...paths].map((path) => (0, import_node_path10.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path10.sep}`) && !(0, import_node_path10.isAbsolute)(path)).sort();
7398
7473
  }
7399
7474
  function normalizeHarnesses(values, global) {
7400
7475
  const requested = values?.length ? values : ["claude"];
@@ -7439,13 +7514,13 @@ function managedFileContent(path, block, force, boundary) {
7439
7514
  return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
7440
7515
  }
7441
7516
  function symlinkedComponent(boundary, target) {
7442
- const rel = (0, import_node_path9.relative)(boundary, target);
7443
- if (rel === ".." || rel.startsWith(`..${import_node_path9.sep}`) || (0, import_node_path9.isAbsolute)(rel)) {
7517
+ const rel = (0, import_node_path10.relative)(boundary, target);
7518
+ if (rel === ".." || rel.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(rel)) {
7444
7519
  throw new Error(`agent setup target escapes its install root: ${target}`);
7445
7520
  }
7446
7521
  let current = boundary;
7447
- for (const part of rel.split(import_node_path9.sep).filter(Boolean)) {
7448
- current = (0, import_node_path9.join)(current, part);
7522
+ for (const part of rel.split(import_node_path10.sep).filter(Boolean)) {
7523
+ current = (0, import_node_path10.join)(current, part);
7449
7524
  try {
7450
7525
  if ((0, import_node_fs11.lstatSync)(current).isSymbolicLink()) return current;
7451
7526
  } catch (error) {
@@ -7462,9 +7537,9 @@ function listFiles(dir) {
7462
7537
  const results = [];
7463
7538
  const walk = (current) => {
7464
7539
  for (const entry of (0, import_node_fs11.readdirSync)(current, { withFileTypes: true })) {
7465
- const path = (0, import_node_path9.join)(current, entry.name);
7540
+ const path = (0, import_node_path10.join)(current, entry.name);
7466
7541
  if (entry.isDirectory()) walk(path);
7467
- else results.push((0, import_node_path9.relative)(dir, path));
7542
+ else results.push((0, import_node_path10.relative)(dir, path));
7468
7543
  }
7469
7544
  };
7470
7545
  walk(dir);