@odla-ai/cli 0.16.2 → 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/dist/index.cjs CHANGED
@@ -61,6 +61,7 @@ __export(index_exports, {
61
61
  isTerminalHostedSecurityStatus: () => isTerminalHostedSecurityStatus,
62
62
  listGitHubSecuritySources: () => listGitHubSecuritySources,
63
63
  listHostedSecurityJobs: () => listHostedSecurityJobs,
64
+ prepareCodeImages: () => prepareCodeImages,
64
65
  printCapabilities: () => printCapabilities,
65
66
  provision: () => provision,
66
67
  redactSecrets: () => redactSecrets,
@@ -1992,13 +1993,13 @@ function printStatus(status, json, out) {
1992
1993
 
1993
1994
  // src/code-connect.ts
1994
1995
  var import_node_fs7 = require("fs");
1995
- var import_node_os = require("os");
1996
- var import_node_path4 = require("path");
1996
+ var import_node_os2 = require("os");
1997
+ var import_node_path5 = require("path");
1997
1998
 
1998
- // ../harness/dist/chunk-7SN5SG4N.js
1999
+ // ../harness/dist/chunk-QTUEF2HZ.js
1999
2000
  var HARNESS_PROTOCOL_VERSION = 1;
2000
2001
 
2001
- // ../harness/dist/chunk-HX4QYGWE.js
2002
+ // ../harness/dist/chunk-GE6CCN7W.js
2002
2003
  var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
2003
2004
  var HarnessProtocolError = class extends Error {
2004
2005
  name = "HarnessProtocolError";
@@ -2076,7 +2077,7 @@ function encodeAgentInput(message2) {
2076
2077
  `;
2077
2078
  }
2078
2079
 
2079
- // ../harness/dist/chunk-QWWRL7M4.js
2080
+ // ../harness/dist/chunk-NOBK6PCT.js
2080
2081
  var import_child_process = require("child_process");
2081
2082
  var import_fs = require("fs");
2082
2083
  var import_promises2 = require("fs/promises");
@@ -2129,7 +2130,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
2129
2130
  throw new TypeError("no rootless Podman found; install Podman or explicitly choose --engine docker after reviewing its daemon boundary");
2130
2131
  }
2131
2132
  if (platform === "darwin") {
2132
- throw new TypeError("no Apple container or Podman Machine found; install one or explicitly choose --engine docker");
2133
+ throw new TypeError("Apple container is not installed; on Apple Silicon macOS 26 run `brew install container`, then retry (Podman Machine is the fallback)");
2133
2134
  }
2134
2135
  throw new TypeError("no supported container engine found");
2135
2136
  }
@@ -2593,7 +2594,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2593
2594
  }
2594
2595
  }
2595
2596
 
2596
- // ../harness/dist/chunk-T2RD6ZAW.js
2597
+ // ../harness/dist/chunk-6J2ZETST.js
2597
2598
  var import_crypto = require("crypto");
2598
2599
  var import_promises5 = require("fs/promises");
2599
2600
  var import_path5 = require("path");
@@ -2930,7 +2931,7 @@ function validateSnapshot(snapshot, limits) {
2930
2931
  }
2931
2932
  }
2932
2933
 
2933
- // ../harness/dist/chunk-T2RD6ZAW.js
2934
+ // ../harness/dist/chunk-6J2ZETST.js
2934
2935
  var import_child_process4 = require("child_process");
2935
2936
  var import_promises6 = require("fs/promises");
2936
2937
  var import_path6 = require("path");
@@ -3223,7 +3224,7 @@ function looksLikeDestination(value) {
3223
3224
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
3224
3225
  }
3225
3226
 
3226
- // ../harness/dist/chunk-T2RD6ZAW.js
3227
+ // ../harness/dist/chunk-6J2ZETST.js
3227
3228
  var import_crypto4 = require("crypto");
3228
3229
  async function digestStagedWorkspace(root, limits) {
3229
3230
  const files = [];
@@ -4414,6 +4415,7 @@ function codeCommandMetadata(payload, resume) {
4414
4415
  const role = payload.role;
4415
4416
  const title = payload.title;
4416
4417
  const prompt = payload.prompt;
4418
+ const maxTokensPerInteraction = payload.maxTokensPerInteraction ?? 32e3;
4417
4419
  if (role !== "coding" && role !== "review" || typeof title !== "string" || typeof prompt !== "string") {
4418
4420
  throw new TypeError(`invalid Code ${resume ? "resume" : "start"} metadata`);
4419
4421
  }
@@ -4425,10 +4427,14 @@ function codeCommandMetadata(payload, resume) {
4425
4427
  if (typeof repository !== "string" || !repository.includes("/") || typeof baseCommitSha !== "string" || !/^[0-9a-f]{40}$/.test(baseCommitSha) || typeof sourceTreeDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(sourceTreeDigest)) {
4426
4428
  throw new TypeError(`invalid Code ${resume ? "resume" : "start"} trusted base`);
4427
4429
  }
4430
+ if (!Number.isSafeInteger(maxTokensPerInteraction) || Number(maxTokensPerInteraction) < 4e3 || Number(maxTokensPerInteraction) > 2e5) {
4431
+ throw new TypeError(`invalid Code ${resume ? "resume" : "start"} interaction token limit`);
4432
+ }
4428
4433
  return {
4429
4434
  role,
4430
4435
  title,
4431
4436
  prompt,
4437
+ maxTokensPerInteraction: Number(maxTokensPerInteraction),
4432
4438
  planningInputDigest: typeof planning === "string" && /^sha256:[0-9a-f]{64}$/.test(planning) ? planning : null,
4433
4439
  attestationDigest: typeof attestation === "string" ? attestation : "resume",
4434
4440
  repository,
@@ -4504,6 +4510,57 @@ function createCodeRuntimeToolBroker(input, lease, role) {
4504
4510
  });
4505
4511
  return role === "coding" ? broker : { execute: (context, request) => request.tool === "sandbox.read" ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
4506
4512
  }
4513
+ async function handleCodeRuntimeInference(input) {
4514
+ const { command, metadata: metadata2, request, state } = input;
4515
+ if (state.tokens >= metadata2.maxTokensPerInteraction) {
4516
+ if (!state.noticeEmitted) {
4517
+ state.noticeEmitted = true;
4518
+ await input.event({
4519
+ type: "message",
4520
+ actor: "system",
4521
+ body: `Pi paused at the ${metadata2.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
4522
+ }).catch(() => void 0);
4523
+ }
4524
+ return {
4525
+ protocolVersion: HARNESS_PROTOCOL_VERSION,
4526
+ type: "inference.response",
4527
+ requestId: request.requestId,
4528
+ response: {
4529
+ id: `budget:${command.commandId}`,
4530
+ provider: "openai",
4531
+ model: "interaction-budget",
4532
+ role: "assistant",
4533
+ content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
4534
+ stopReason: "end_turn",
4535
+ usage: { inputTokens: 0, outputTokens: 0 }
4536
+ }
4537
+ };
4538
+ }
4539
+ const startedAt = Date.now();
4540
+ const response2 = await input.control.infer(command.sessionId, {
4541
+ requestId: request.requestId,
4542
+ interactionId: command.commandId,
4543
+ call: request.call
4544
+ });
4545
+ state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
4546
+ await input.event({
4547
+ type: "usage",
4548
+ provider: response2.receipt.provider,
4549
+ model: response2.receipt.model,
4550
+ inputTokens: response2.receipt.inputTokens,
4551
+ outputTokens: response2.receipt.outputTokens,
4552
+ durationMs: Date.now() - startedAt,
4553
+ interactionId: command.commandId,
4554
+ interactionTokens: state.tokens,
4555
+ interactionMaxTokens: metadata2.maxTokensPerInteraction
4556
+ }).catch(() => void 0);
4557
+ return {
4558
+ protocolVersion: HARNESS_PROTOCOL_VERSION,
4559
+ type: "inference.response",
4560
+ requestId: request.requestId,
4561
+ response: response2.response
4562
+ };
4563
+ }
4507
4564
  async function appendCodeRuntimeEvent(control, command, event, refs) {
4508
4565
  const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
4509
4566
  refs.push(eventId);
@@ -4533,6 +4590,7 @@ function runtimeResultError(value) {
4533
4590
  var CodePiRuntimeEngine = class {
4534
4591
  constructor(options) {
4535
4592
  this.options = options;
4593
+ 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");
4536
4594
  this.#run = options.runAttempt ?? runContainerAttempt;
4537
4595
  this.#buildPolicyDigest = digestRuntimeValue(JSON.stringify(options.recipes));
4538
4596
  this.#checkpoints = new CodeRuntimeCheckpointManager({
@@ -4615,6 +4673,7 @@ var CodePiRuntimeEngine = class {
4615
4673
  acknowledged: false,
4616
4674
  role: metadata2.role,
4617
4675
  title: metadata2.title,
4676
+ maxTokensPerInteraction: metadata2.maxTokensPerInteraction,
4618
4677
  baseCommitSha: metadata2.baseCommitSha,
4619
4678
  repository: metadata2.repository,
4620
4679
  sourceTreeDigest: metadata2.sourceTreeDigest,
@@ -4651,6 +4710,11 @@ var CodePiRuntimeEngine = class {
4651
4710
  if (!active || typeof prompt !== "string" || !prompt.trim() || prompt.length > 2e4) {
4652
4711
  throw new TypeError("prompt requires an active Code session and bounded text");
4653
4712
  }
4713
+ const requestedLimit = command.payload.maxTokensPerInteraction ?? active.maxTokensPerInteraction;
4714
+ if (!Number.isSafeInteger(requestedLimit) || Number(requestedLimit) < 4e3 || Number(requestedLimit) > 2e5) {
4715
+ throw new TypeError("prompt requires a valid interaction token limit");
4716
+ }
4717
+ active.maxTokensPerInteraction = Number(requestedLimit);
4654
4718
  await active.done;
4655
4719
  active.abort = new AbortController();
4656
4720
  active.acknowledged = false;
@@ -4659,6 +4723,7 @@ var CodePiRuntimeEngine = class {
4659
4723
  role: active.role,
4660
4724
  title: active.title,
4661
4725
  prompt,
4726
+ maxTokensPerInteraction: active.maxTokensPerInteraction,
4662
4727
  planningInputDigest: active.planningInputDigest,
4663
4728
  attestationDigest: "follow-up",
4664
4729
  repository: active.repository,
@@ -4687,9 +4752,11 @@ var CodePiRuntimeEngine = class {
4687
4752
  }, lease, metadata2.role);
4688
4753
  const startedAt = Date.now();
4689
4754
  let completionSeen = false;
4755
+ const interaction = { tokens: 0, noticeEmitted: false };
4690
4756
  const result = await this.#run({
4691
4757
  engine: this.options.engine,
4692
4758
  image: this.options.image,
4759
+ allowUnpinnedImage: this.options.imageAuthorization === "cli_embedded",
4693
4760
  workspaceDir: active.workspace.workspaceDir,
4694
4761
  workspaceAccess: "none",
4695
4762
  task: lease.task,
@@ -4702,25 +4769,18 @@ var CodePiRuntimeEngine = class {
4702
4769
  }, active.conversationRefs),
4703
4770
  onMessage: async (output) => {
4704
4771
  if (output.type === "inference.request") {
4705
- const inferenceStarted = Date.now();
4706
- const response2 = await this.options.control.infer(command.sessionId, {
4707
- requestId: output.requestId,
4708
- call: output.call
4772
+ return handleCodeRuntimeInference({
4773
+ command,
4774
+ metadata: metadata2,
4775
+ request: output,
4776
+ state: interaction,
4777
+ control: this.options.control,
4778
+ event: (event) => this.#event(
4779
+ command,
4780
+ event,
4781
+ active.conversationRefs
4782
+ )
4709
4783
  });
4710
- await this.#event(command, {
4711
- type: "usage",
4712
- provider: response2.receipt.provider,
4713
- model: response2.receipt.model,
4714
- inputTokens: response2.receipt.inputTokens,
4715
- outputTokens: response2.receipt.outputTokens,
4716
- durationMs: Date.now() - inferenceStarted
4717
- }, active.conversationRefs).catch(() => void 0);
4718
- return {
4719
- protocolVersion: HARNESS_PROTOCOL_VERSION,
4720
- type: "inference.response",
4721
- requestId: output.requestId,
4722
- response: response2.response
4723
- };
4724
4784
  }
4725
4785
  if (output.type === "tool.request") {
4726
4786
  const toolStarted = Date.now();
@@ -5206,25 +5266,23 @@ function digestText(value) {
5206
5266
 
5207
5267
  // src/code-images.ts
5208
5268
  var import_node_child_process4 = require("child_process");
5209
- async function prepareCodeImages(engine, images) {
5210
- for (const image of images) {
5211
- await new Promise((accept, reject) => {
5212
- const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
5213
- (0, import_node_child_process4.execFile)(engine, args, { encoding: "utf8", maxBuffer: 2 * 1024 * 1024, timeout: 10 * 6e4 }, (error) => {
5214
- if (error) reject(new Error(`could not prepare pinned Code image ${image}`));
5215
- else accept();
5216
- });
5217
- });
5218
- }
5219
- }
5269
+ var import_node_crypto2 = require("crypto");
5270
+ var import_promises10 = require("fs/promises");
5271
+ var import_node_os = require("os");
5272
+ var import_node_path4 = require("path");
5273
+ var import_node_url2 = require("url");
5220
5274
 
5221
5275
  // src/code-runtime-config.ts
5222
- var CODE_PI_IMAGE = "ghcr.io/cory/odla-pi-agent@sha256:e7858b4f0291543171b2659b47c5d00f97acc4767cafd9b74ae5228899e0cde4";
5276
+ var CODE_PI_IMAGE = "odla-ai/pi-agent:embedded";
5277
+ var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
5223
5278
  var CODE_BUILD_RECIPES = Object.freeze([{
5224
5279
  id: "odla-code-contracts",
5225
- image: "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd",
5280
+ image: CODE_NODE_IMAGE,
5226
5281
  command: [
5227
5282
  "node",
5283
+ // The clean source mount deliberately has no node_modules. Reuse the
5284
+ // semver copy inside this digest-pinned npm image without network access.
5285
+ "--import=data:text/javascript,import%20Module%20from%20%22node%3Amodule%22%3Bprocess.env.NODE_PATH%3D%22%2Fusr%2Flocal%2Flib%2Fnode_modules%2Fnpm%2Fnode_modules%22%3BModule._initPaths%28%29",
5228
5286
  "--test",
5229
5287
  "scripts/lib/directories.test.mjs",
5230
5288
  "scripts/lib/internal-deps.test.mjs",
@@ -5239,10 +5297,84 @@ var CODE_BUILD_RECIPES = Object.freeze([{
5239
5297
  pids: 128
5240
5298
  }]);
5241
5299
 
5300
+ // src/code-images.ts
5301
+ var runCodeImageCommand = (command, args, stdio) => new Promise((accept, reject) => {
5302
+ const child = (0, import_node_child_process4.spawn)(command, [...args], { shell: false, stdio });
5303
+ child.once("error", reject);
5304
+ child.once("exit", (code, signal) => {
5305
+ if (code === 0) accept();
5306
+ else reject(new Error(`${command} ${args.join(" ")} exited ${code ?? signal ?? "without a status"}`));
5307
+ });
5308
+ });
5309
+ async function prepareCodeImages(engine, images, run = runCodeImageCommand, buildEmbedded = buildEmbeddedPiImage, nameEmbedded = embeddedPiImageName) {
5310
+ if (engine === "container") {
5311
+ try {
5312
+ await run(engine, ["system", "start"], "inherit");
5313
+ } catch {
5314
+ throw new Error("Apple container could not start; run `container system start` once to complete its lightweight VM setup, then retry");
5315
+ }
5316
+ }
5317
+ const prepared = [];
5318
+ for (const image of images) {
5319
+ const runtimeImage = image === CODE_PI_IMAGE ? await nameEmbedded() : image;
5320
+ const inspectArgs = ["image", "inspect", runtimeImage];
5321
+ try {
5322
+ await run(engine, inspectArgs, "ignore");
5323
+ prepared.push(runtimeImage);
5324
+ continue;
5325
+ } catch {
5326
+ }
5327
+ if (image === CODE_PI_IMAGE) {
5328
+ try {
5329
+ await buildEmbedded(engine, runtimeImage, run);
5330
+ } catch (error) {
5331
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
5332
+ throw new Error(`could not prepare CLI-embedded Code image${detail}`);
5333
+ }
5334
+ prepared.push(runtimeImage);
5335
+ continue;
5336
+ }
5337
+ const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
5338
+ try {
5339
+ await run(engine, args, "inherit");
5340
+ } catch (error) {
5341
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
5342
+ throw new Error(`could not prepare pinned Code image ${image}${detail}`);
5343
+ }
5344
+ prepared.push(image);
5345
+ }
5346
+ return prepared;
5347
+ }
5348
+ function embeddedPiAssetPath() {
5349
+ return (0, import_node_url2.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
5350
+ }
5351
+ async function embeddedPiImageName() {
5352
+ const bundle = await (0, import_promises10.readFile)(embeddedPiAssetPath()).catch(() => {
5353
+ throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
5354
+ });
5355
+ return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto2.createHash)("sha256").update(bundle).digest("hex")}`;
5356
+ }
5357
+ async function buildEmbeddedPiImage(engine, image, run) {
5358
+ const context = await (0, import_promises10.mkdtemp)((0, import_node_path4.join)((0, import_node_os.tmpdir)(), "odla-code-pi-"));
5359
+ try {
5360
+ await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path4.join)(context, "pi-agent.js"));
5361
+ await (0, import_promises10.writeFile)((0, import_node_path4.join)(context, "Dockerfile"), [
5362
+ `FROM ${CODE_NODE_IMAGE}`,
5363
+ "COPY pi-agent.js /opt/odla/pi-agent.js",
5364
+ "WORKDIR /workspace",
5365
+ 'ENTRYPOINT ["node", "/opt/odla/pi-agent.js"]',
5366
+ ""
5367
+ ].join("\n"), { mode: 384 });
5368
+ await run(engine, ["build", "--tag", image, context], "inherit");
5369
+ } finally {
5370
+ await (0, import_promises10.rm)(context, { recursive: true, force: true });
5371
+ }
5372
+ }
5373
+
5242
5374
  // src/code-connect.ts
5243
5375
  async function codeConnect(options) {
5244
5376
  const cwd = options.cwd ?? process.cwd();
5245
- const configPath = (0, import_node_path4.resolve)(cwd, options.configPath);
5377
+ const configPath = (0, import_node_path5.resolve)(cwd, options.configPath);
5246
5378
  const cfg = (0, import_node_fs7.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
5247
5379
  const requestedAppId = options.appId?.trim();
5248
5380
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -5271,12 +5403,13 @@ async function codeConnect(options) {
5271
5403
  const out = options.stdout ?? console;
5272
5404
  const doFetch = options.fetch ?? fetch;
5273
5405
  const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
5274
- await (options.prepareImages ?? prepareCodeImages)(engine, [
5275
- CODE_PI_IMAGE,
5276
- ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))
5277
- ]);
5406
+ const [piImage] = await (options.prepareImages ?? prepareCodeImages)(
5407
+ engine,
5408
+ [CODE_PI_IMAGE, ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))]
5409
+ );
5410
+ 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");
5278
5411
  const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
5279
- const hostName = (options.name ?? (0, import_node_os.hostname)()).trim();
5412
+ const hostName = (options.name ?? (0, import_node_os2.hostname)()).trim();
5280
5413
  if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
5281
5414
  const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
5282
5415
  const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
@@ -5313,12 +5446,13 @@ async function codeConnect(options) {
5313
5446
  platform: hostPlatform,
5314
5447
  arch: process.arch,
5315
5448
  engines: [engine],
5316
- cpuCount: (0, import_node_os.cpus)().length,
5317
- memoryBytes: (0, import_node_os.totalmem)(),
5449
+ cpuCount: (0, import_node_os2.cpus)().length,
5450
+ memoryBytes: (0, import_node_os2.totalmem)(),
5318
5451
  source: descriptor2,
5319
5452
  images: {
5320
5453
  ready: true,
5321
- pi: CODE_PI_IMAGE,
5454
+ pi: piImage,
5455
+ piSource: "cli_embedded",
5322
5456
  recipes: CODE_BUILD_RECIPES.map((recipe2) => ({ id: recipe2.id, image: recipe2.image }))
5323
5457
  }
5324
5458
  };
@@ -5333,6 +5467,7 @@ async function codeConnect(options) {
5333
5467
  engine,
5334
5468
  capabilities,
5335
5469
  localSource,
5470
+ piImage,
5336
5471
  heartbeatMs,
5337
5472
  once: options.once === true,
5338
5473
  signal: options.signal,
@@ -5362,7 +5497,8 @@ async function runCodeRuntime(input) {
5362
5497
  const commandEngine = new CodePiRuntimeEngine({
5363
5498
  control,
5364
5499
  engine: input.engine,
5365
- image: CODE_PI_IMAGE,
5500
+ image: input.piImage ?? input.capabilities.images.pi,
5501
+ imageAuthorization: "cli_embedded",
5366
5502
  recipes: CODE_BUILD_RECIPES,
5367
5503
  recipeAuthorization: "registered_recipe",
5368
5504
  localSource: input.localSource,
@@ -5459,12 +5595,12 @@ async function codeCommand(parsed, dependencies) {
5459
5595
  // src/doctor-checks.ts
5460
5596
  var import_node_child_process6 = require("child_process");
5461
5597
  var import_node_fs9 = require("fs");
5462
- var import_node_path6 = require("path");
5598
+ var import_node_path7 = require("path");
5463
5599
 
5464
5600
  // src/wrangler.ts
5465
5601
  var import_node_child_process5 = require("child_process");
5466
5602
  var import_node_fs8 = require("fs");
5467
- var import_node_path5 = require("path");
5603
+ var import_node_path6 = require("path");
5468
5604
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
5469
5605
  const child = (0, import_node_child_process5.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
5470
5606
  let stdout = "";
@@ -5478,7 +5614,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
5478
5614
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
5479
5615
  function findWranglerConfig(rootDir) {
5480
5616
  for (const name of WRANGLER_CONFIG_FILES) {
5481
- const path = (0, import_node_path5.join)(rootDir, name);
5617
+ const path = (0, import_node_path6.join)(rootDir, name);
5482
5618
  if ((0, import_node_fs8.existsSync)(path)) return path;
5483
5619
  }
5484
5620
  return null;
@@ -5584,10 +5720,10 @@ function wranglerWarnings(rootDir) {
5584
5720
  for (const { label, block } of blocks) {
5585
5721
  const assets = block.assets;
5586
5722
  if (assets?.directory) {
5587
- const dir = (0, import_node_path6.resolve)(rootDir, assets.directory);
5588
- if (dir === (0, import_node_path6.resolve)(rootDir)) {
5723
+ const dir = (0, import_node_path7.resolve)(rootDir, assets.directory);
5724
+ if (dir === (0, import_node_path7.resolve)(rootDir)) {
5589
5725
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
5590
- } else if ((0, import_node_fs9.existsSync)((0, import_node_path6.join)(dir, "node_modules"))) {
5726
+ } else if ((0, import_node_fs9.existsSync)((0, import_node_path7.join)(dir, "node_modules"))) {
5591
5727
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
5592
5728
  }
5593
5729
  }
@@ -5622,7 +5758,7 @@ function o11yProjectWarnings(rootDir) {
5622
5758
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
5623
5759
  return warnings;
5624
5760
  }
5625
- const main = typeof config.main === "string" ? (0, import_node_path6.resolve)(rootDir, config.main) : null;
5761
+ const main = typeof config.main === "string" ? (0, import_node_path7.resolve)(rootDir, config.main) : null;
5626
5762
  if (!main || !(0, import_node_fs9.existsSync)(main)) {
5627
5763
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
5628
5764
  } else {
@@ -5652,7 +5788,7 @@ function calendarProjectWarnings(rootDir) {
5652
5788
  }
5653
5789
  function readPackageJson(rootDir) {
5654
5790
  try {
5655
- return JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path6.join)(rootDir, "package.json"), "utf8"));
5791
+ return JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path7.join)(rootDir, "package.json"), "utf8"));
5656
5792
  } catch {
5657
5793
  return null;
5658
5794
  }
@@ -5854,11 +5990,11 @@ function harnessOption(value, flag) {
5854
5990
 
5855
5991
  // src/init.ts
5856
5992
  var import_node_fs10 = require("fs");
5857
- var import_node_path7 = require("path");
5993
+ var import_node_path8 = require("path");
5858
5994
  function initProject(options) {
5859
5995
  const out = options.stdout ?? console;
5860
- const rootDir = (0, import_node_path7.resolve)(options.rootDir ?? process.cwd());
5861
- const configPath = (0, import_node_path7.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5996
+ const rootDir = (0, import_node_path8.resolve)(options.rootDir ?? process.cwd());
5997
+ const configPath = (0, import_node_path8.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
5862
5998
  if ((0, import_node_fs10.existsSync)(configPath) && !options.force) {
5863
5999
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
5864
6000
  }
@@ -5869,12 +6005,12 @@ function initProject(options) {
5869
6005
  const services = options.services?.length ? options.services : ["db", "ai"];
5870
6006
  if (services.includes("calendar") && !services.includes("db")) throw new Error("--services calendar requires db");
5871
6007
  const aiProvider = options.aiProvider ?? "anthropic";
5872
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.dirname)(configPath), { recursive: true });
5873
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.resolve)(rootDir, "src/odla"), { recursive: true });
5874
- (0, import_node_fs10.mkdirSync)((0, import_node_path7.resolve)(rootDir, ".odla"), { recursive: true });
6008
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(configPath), { recursive: true });
6009
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.resolve)(rootDir, "src/odla"), { recursive: true });
6010
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.resolve)(rootDir, ".odla"), { recursive: true });
5875
6011
  (0, import_node_fs10.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
5876
- writeIfMissing((0, import_node_path7.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
5877
- writeIfMissing((0, import_node_path7.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
6012
+ writeIfMissing((0, import_node_path8.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
6013
+ writeIfMissing((0, import_node_path8.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
5878
6014
  ensureGitignore(rootDir);
5879
6015
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
5880
6016
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -6524,7 +6660,7 @@ async function resolveVaultWrite(options) {
6524
6660
  }
6525
6661
 
6526
6662
  // src/security-command-context.ts
6527
- var import_promises10 = require("readline/promises");
6663
+ var import_promises11 = require("readline/promises");
6528
6664
  async function hostedSecurityContext(parsed, dependencies) {
6529
6665
  const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
6530
6666
  const cfg = await loadProjectConfig(configPath);
@@ -6550,7 +6686,7 @@ async function hostedSecurityContext(parsed, dependencies) {
6550
6686
  async function interactiveConfirmation(message2, dependencies) {
6551
6687
  if (dependencies.confirm) return dependencies.confirm(message2);
6552
6688
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
6553
- const prompt = (0, import_promises10.createInterface)({ input: process.stdin, output: process.stdout });
6689
+ const prompt = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stdout });
6554
6690
  try {
6555
6691
  const answer = await prompt.question(`${message2} [y/N] `);
6556
6692
  return /^y(?:es)?$/i.test(answer.trim());
@@ -6677,7 +6813,7 @@ function hostedSeverity(value, flag) {
6677
6813
  var import_security2 = require("@odla-ai/security");
6678
6814
 
6679
6815
  // src/security.ts
6680
- var import_node_path8 = require("path");
6816
+ var import_node_path9 = require("path");
6681
6817
  var import_security = require("@odla-ai/security");
6682
6818
  var import_node3 = require("@odla-ai/security/node");
6683
6819
  async function runHostedSecurity(options) {
@@ -6689,9 +6825,9 @@ async function runHostedSecurity(options) {
6689
6825
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
6690
6826
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
6691
6827
  const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
6692
- const target = (0, import_node_path8.resolve)(options.target ?? cfg?.rootDir ?? ".");
6693
- const output = (0, import_node_path8.resolve)(options.out ?? (0, import_node_path8.resolve)(target, ".odla/security/hosted"));
6694
- const outputRelative = (0, import_node_path8.relative)(target, output).split(import_node_path8.sep).join("/");
6828
+ const target = (0, import_node_path9.resolve)(options.target ?? cfg?.rootDir ?? ".");
6829
+ const output = (0, import_node_path9.resolve)(options.out ?? (0, import_node_path9.resolve)(target, ".odla/security/hosted"));
6830
+ const outputRelative = (0, import_node_path9.relative)(target, output).split(import_node_path9.sep).join("/");
6695
6831
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
6696
6832
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
6697
6833
  const tokenRequest = {
@@ -6703,7 +6839,7 @@ async function runHostedSecurity(options) {
6703
6839
  };
6704
6840
  const token = await injectedToken(options, tokenRequest);
6705
6841
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
6706
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path8.isAbsolute)(outputRelative) ? [outputRelative] : []
6842
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path9.isAbsolute)(outputRelative) ? [outputRelative] : []
6707
6843
  });
6708
6844
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
6709
6845
  platform,
@@ -6721,7 +6857,7 @@ async function runHostedSecurity(options) {
6721
6857
  });
6722
6858
  const harness = (0, import_security.createSecurityHarness)({
6723
6859
  profile,
6724
- store: new import_node3.FileRunStore((0, import_node_path8.resolve)(output, "state")),
6860
+ store: new import_node3.FileRunStore((0, import_node_path9.resolve)(output, "state")),
6725
6861
  discoveryReasoner: hosted.discoveryReasoner,
6726
6862
  validationReasoner: hosted.validationReasoner,
6727
6863
  policy: {
@@ -6745,7 +6881,7 @@ async function runHostedSecurity(options) {
6745
6881
  function selectEnv(requested, declared, configPath, rootDir) {
6746
6882
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
6747
6883
  if (!env || !declared.includes(env)) {
6748
- const shown = (0, import_node_path8.relative)(rootDir, configPath) || configPath;
6884
+ const shown = (0, import_node_path9.relative)(rootDir, configPath) || configPath;
6749
6885
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
6750
6886
  }
6751
6887
  return env;
@@ -6774,7 +6910,7 @@ function printSummary(out, appId, env, run, report2, output) {
6774
6910
  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}`);
6775
6911
  if (report2.callBudget) out.log(` calls: discovery=${formatBudget(report2.callBudget.discovery)} validation=${formatBudget(report2.callBudget.validation)}`);
6776
6912
  out.log(` findings: confirmed=${report2.metrics.confirmed} needs_reproduction=${report2.metrics.needsReproduction} candidates=${report2.metrics.candidates}`);
6777
- out.log(` report: ${(0, import_node_path8.resolve)(output, "REPORT.md")}`);
6913
+ out.log(` report: ${(0, import_node_path9.resolve)(output, "REPORT.md")}`);
6778
6914
  }
6779
6915
  function formatBudget(usage) {
6780
6916
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
@@ -7224,9 +7360,9 @@ async function securityStatus(parsed, dependencies) {
7224
7360
 
7225
7361
  // src/skill.ts
7226
7362
  var import_node_fs11 = require("fs");
7227
- var import_node_os2 = require("os");
7228
- var import_node_path9 = require("path");
7229
- var import_node_url2 = require("url");
7363
+ var import_node_os3 = require("os");
7364
+ var import_node_path10 = require("path");
7365
+ var import_node_url3 = require("url");
7230
7366
 
7231
7367
  // src/skill-adapters.ts
7232
7368
  var PROJECT_INSTRUCTIONS = `<!-- odla-ai agent setup:start -->
@@ -7282,12 +7418,12 @@ runbook. Do not substitute a remote documentation page for the installed copy.
7282
7418
  var AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
7283
7419
  function installSkill(options = {}) {
7284
7420
  const out = options.stdout ?? console;
7285
- const sourceDir = options.sourceDir ?? (0, import_node_url2.fileURLToPath)(new URL("../skills", importMetaUrl));
7421
+ const sourceDir = options.sourceDir ?? (0, import_node_url3.fileURLToPath)(new URL("../skills", importMetaUrl));
7286
7422
  const files = listFiles(sourceDir);
7287
7423
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
7288
7424
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
7289
- const root = (0, import_node_path9.resolve)(options.dir ?? process.cwd());
7290
- const home = (0, import_node_path9.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
7425
+ const root = (0, import_node_path10.resolve)(options.dir ?? process.cwd());
7426
+ const home = (0, import_node_path10.resolve)(options.homeDir ?? (0, import_node_os3.homedir)());
7291
7427
  const plans = /* @__PURE__ */ new Map();
7292
7428
  const targets = /* @__PURE__ */ new Map();
7293
7429
  const rememberTarget = (harness, target) => {
@@ -7301,48 +7437,48 @@ function installSkill(options = {}) {
7301
7437
  plans.set(target, { target, content, boundary, managedMerge });
7302
7438
  };
7303
7439
  const planSkillTree = (targetDir2, boundary = root) => {
7304
- 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);
7440
+ 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);
7305
7441
  };
7306
7442
  let targetDir;
7307
7443
  if (options.global) {
7308
- const claudeRoot = (0, import_node_path9.join)(home, ".claude", "skills");
7309
- const codexRoot = (0, import_node_path9.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path9.join)(home, ".codex"), "skills");
7444
+ const claudeRoot = (0, import_node_path10.join)(home, ".claude", "skills");
7445
+ const codexRoot = (0, import_node_path10.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path10.join)(home, ".codex"), "skills");
7310
7446
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
7311
7447
  for (const harness of harnesses) {
7312
7448
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
7313
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path9.dirname)((0, import_node_path9.dirname)(codexRoot)));
7449
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path10.dirname)((0, import_node_path10.dirname)(codexRoot)));
7314
7450
  rememberTarget(harness, skillRoot);
7315
7451
  }
7316
7452
  } else {
7317
- const sharedRoot = (0, import_node_path9.join)(root, ".agents", "skills");
7453
+ const sharedRoot = (0, import_node_path10.join)(root, ".agents", "skills");
7318
7454
  planSkillTree(sharedRoot);
7319
- const claudeRoot = (0, import_node_path9.join)(root, ".claude", "skills");
7455
+ const claudeRoot = (0, import_node_path10.join)(root, ".claude", "skills");
7320
7456
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
7321
7457
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
7322
7458
  if (harnesses.includes("claude")) {
7323
7459
  for (const skill of skillNames(files)) {
7324
- const canonical = (0, import_node_fs11.readFileSync)((0, import_node_path9.join)(sourceDir, skill, "SKILL.md"), "utf8");
7325
- plan((0, import_node_path9.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
7460
+ const canonical = (0, import_node_fs11.readFileSync)((0, import_node_path10.join)(sourceDir, skill, "SKILL.md"), "utf8");
7461
+ plan((0, import_node_path10.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
7326
7462
  }
7327
7463
  rememberTarget("claude", claudeRoot);
7328
7464
  }
7329
7465
  if (harnesses.includes("cursor")) {
7330
- const cursorRule = (0, import_node_path9.join)(root, ".cursor", "rules", "odla.mdc");
7466
+ const cursorRule = (0, import_node_path10.join)(root, ".cursor", "rules", "odla.mdc");
7331
7467
  plan(cursorRule, CURSOR_RULE);
7332
7468
  rememberTarget("cursor", cursorRule);
7333
7469
  }
7334
7470
  if (harnesses.includes("agents")) {
7335
- const agentsFile = (0, import_node_path9.join)(root, "AGENTS.md");
7471
+ const agentsFile = (0, import_node_path10.join)(root, "AGENTS.md");
7336
7472
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7337
7473
  rememberTarget("agents", agentsFile);
7338
7474
  }
7339
7475
  if (harnesses.includes("copilot")) {
7340
- const copilotFile = (0, import_node_path9.join)(root, ".github", "copilot-instructions.md");
7476
+ const copilotFile = (0, import_node_path10.join)(root, ".github", "copilot-instructions.md");
7341
7477
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7342
7478
  rememberTarget("copilot", copilotFile);
7343
7479
  }
7344
7480
  if (harnesses.includes("gemini")) {
7345
- const geminiFile = (0, import_node_path9.join)(root, "GEMINI.md");
7481
+ const geminiFile = (0, import_node_path10.join)(root, "GEMINI.md");
7346
7482
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
7347
7483
  rememberTarget("gemini", geminiFile);
7348
7484
  }
@@ -7378,7 +7514,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
7378
7514
  }
7379
7515
  for (const file of plans.values()) {
7380
7516
  if (!(0, import_node_fs11.existsSync)(file.target) || (0, import_node_fs11.readFileSync)(file.target, "utf8") !== file.content) {
7381
- (0, import_node_fs11.mkdirSync)((0, import_node_path9.dirname)(file.target), { recursive: true });
7517
+ (0, import_node_fs11.mkdirSync)((0, import_node_path10.dirname)(file.target), { recursive: true });
7382
7518
  (0, import_node_fs11.writeFileSync)(file.target, file.content);
7383
7519
  }
7384
7520
  }
@@ -7398,7 +7534,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
7398
7534
  };
7399
7535
  }
7400
7536
  function pathsUnder(root, paths) {
7401
- 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();
7537
+ 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();
7402
7538
  }
7403
7539
  function normalizeHarnesses(values, global) {
7404
7540
  const requested = values?.length ? values : ["claude"];
@@ -7443,13 +7579,13 @@ function managedFileContent(path, block, force, boundary) {
7443
7579
  return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
7444
7580
  }
7445
7581
  function symlinkedComponent(boundary, target) {
7446
- const rel = (0, import_node_path9.relative)(boundary, target);
7447
- if (rel === ".." || rel.startsWith(`..${import_node_path9.sep}`) || (0, import_node_path9.isAbsolute)(rel)) {
7582
+ const rel = (0, import_node_path10.relative)(boundary, target);
7583
+ if (rel === ".." || rel.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(rel)) {
7448
7584
  throw new Error(`agent setup target escapes its install root: ${target}`);
7449
7585
  }
7450
7586
  let current = boundary;
7451
- for (const part of rel.split(import_node_path9.sep).filter(Boolean)) {
7452
- current = (0, import_node_path9.join)(current, part);
7587
+ for (const part of rel.split(import_node_path10.sep).filter(Boolean)) {
7588
+ current = (0, import_node_path10.join)(current, part);
7453
7589
  try {
7454
7590
  if ((0, import_node_fs11.lstatSync)(current).isSymbolicLink()) return current;
7455
7591
  } catch (error) {
@@ -7466,9 +7602,9 @@ function listFiles(dir) {
7466
7602
  const results = [];
7467
7603
  const walk = (current) => {
7468
7604
  for (const entry of (0, import_node_fs11.readdirSync)(current, { withFileTypes: true })) {
7469
- const path = (0, import_node_path9.join)(current, entry.name);
7605
+ const path = (0, import_node_path10.join)(current, entry.name);
7470
7606
  if (entry.isDirectory()) walk(path);
7471
- else results.push((0, import_node_path9.relative)(dir, path));
7607
+ else results.push((0, import_node_path10.relative)(dir, path));
7472
7608
  }
7473
7609
  };
7474
7610
  walk(dir);
@@ -7840,6 +7976,7 @@ async function calendarCommand(parsed, dependencies) {
7840
7976
  isTerminalHostedSecurityStatus,
7841
7977
  listGitHubSecuritySources,
7842
7978
  listHostedSecurityJobs,
7979
+ prepareCodeImages,
7843
7980
  printCapabilities,
7844
7981
  provision,
7845
7982
  redactSecrets,