@odla-ai/cli 0.16.3 → 0.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  exitCodeFor,
4
4
  runCli
5
- } from "./chunk-YWC7F5T3.js";
5
+ } from "./chunk-GLY5BN6D.js";
6
6
 
7
7
  // src/bin.ts
8
8
  runCli().catch((err) => {
@@ -1775,10 +1775,11 @@ async function defaultReadOrigin(cwd) {
1775
1775
  }
1776
1776
 
1777
1777
  // src/code-runtime-config.ts
1778
- var CODE_PI_IMAGE = "ghcr.io/cory/odla-pi-agent@sha256:2a52a1c8eed66ad167e7bccab4d603ed8fcd75c42dddfb320083436ed89b7c9c";
1778
+ var CODE_PI_IMAGE = "odla-ai/pi-agent:embedded";
1779
+ var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
1779
1780
  var CODE_BUILD_RECIPES = Object.freeze([{
1780
1781
  id: "odla-code-contracts",
1781
- image: "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd",
1782
+ image: CODE_NODE_IMAGE,
1782
1783
  command: [
1783
1784
  "node",
1784
1785
  // The clean source mount deliberately has no node_modules. Reuse the
@@ -1798,6 +1799,86 @@ var CODE_BUILD_RECIPES = Object.freeze([{
1798
1799
  pids: 128
1799
1800
  }]);
1800
1801
 
1802
+ // src/code-images.ts
1803
+ import { spawn as spawn2 } from "child_process";
1804
+ import { createHash } from "crypto";
1805
+ import { copyFile, mkdtemp, readFile, rm, writeFile } from "fs/promises";
1806
+ import { tmpdir } from "os";
1807
+ import { join as join2 } from "path";
1808
+ import { fileURLToPath } from "url";
1809
+ var runCodeImageCommand = (command, args, stdio) => new Promise((accept, reject) => {
1810
+ const child = spawn2(command, [...args], { shell: false, stdio });
1811
+ child.once("error", reject);
1812
+ child.once("exit", (code, signal) => {
1813
+ if (code === 0) accept();
1814
+ else reject(new Error(`${command} ${args.join(" ")} exited ${code ?? signal ?? "without a status"}`));
1815
+ });
1816
+ });
1817
+ async function prepareCodeImages(engine, images, run = runCodeImageCommand, buildEmbedded = buildEmbeddedPiImage, nameEmbedded = embeddedPiImageName) {
1818
+ if (engine === "container") {
1819
+ try {
1820
+ await run(engine, ["system", "start"], "inherit");
1821
+ } catch {
1822
+ throw new Error("Apple container could not start; run `container system start` once to complete its lightweight VM setup, then retry");
1823
+ }
1824
+ }
1825
+ const prepared = [];
1826
+ for (const image of images) {
1827
+ const runtimeImage = image === CODE_PI_IMAGE ? await nameEmbedded() : image;
1828
+ const inspectArgs = ["image", "inspect", runtimeImage];
1829
+ try {
1830
+ await run(engine, inspectArgs, "ignore");
1831
+ prepared.push(runtimeImage);
1832
+ continue;
1833
+ } catch {
1834
+ }
1835
+ if (image === CODE_PI_IMAGE) {
1836
+ try {
1837
+ await buildEmbedded(engine, runtimeImage, run);
1838
+ } catch (error) {
1839
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
1840
+ throw new Error(`could not prepare CLI-embedded Code image${detail}`);
1841
+ }
1842
+ prepared.push(runtimeImage);
1843
+ continue;
1844
+ }
1845
+ const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
1846
+ try {
1847
+ await run(engine, args, "inherit");
1848
+ } catch (error) {
1849
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
1850
+ throw new Error(`could not prepare pinned Code image ${image}${detail}`);
1851
+ }
1852
+ prepared.push(image);
1853
+ }
1854
+ return prepared;
1855
+ }
1856
+ function embeddedPiAssetPath() {
1857
+ return fileURLToPath(new URL("./runtime/pi-agent.js", import.meta.url));
1858
+ }
1859
+ async function embeddedPiImageName() {
1860
+ const bundle = await readFile(embeddedPiAssetPath()).catch(() => {
1861
+ throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
1862
+ });
1863
+ return `odla-ai/pi-agent:embedded-sha256-${createHash("sha256").update(bundle).digest("hex")}`;
1864
+ }
1865
+ async function buildEmbeddedPiImage(engine, image, run) {
1866
+ const context = await mkdtemp(join2(tmpdir(), "odla-code-pi-"));
1867
+ try {
1868
+ await copyFile(embeddedPiAssetPath(), join2(context, "pi-agent.js"));
1869
+ await writeFile(join2(context, "Dockerfile"), [
1870
+ `FROM ${CODE_NODE_IMAGE}`,
1871
+ "COPY pi-agent.js /opt/odla/pi-agent.js",
1872
+ "WORKDIR /workspace",
1873
+ 'ENTRYPOINT ["node", "/opt/odla/pi-agent.js"]',
1874
+ ""
1875
+ ].join("\n"), { mode: 384 });
1876
+ await run(engine, ["build", "--tag", image, context], "inherit");
1877
+ } finally {
1878
+ await rm(context, { recursive: true, force: true });
1879
+ }
1880
+ }
1881
+
1801
1882
  // src/code-connect.ts
1802
1883
  import { existsSync as existsSync4 } from "fs";
1803
1884
  import { cpus, hostname, totalmem } from "os";
@@ -1884,21 +1965,21 @@ function encodeAgentInput(message2) {
1884
1965
  `;
1885
1966
  }
1886
1967
 
1887
- // ../harness/dist/chunk-IRFCMVKU.js
1888
- import { execFile as execFile2, spawn as spawn2 } from "child_process";
1968
+ // ../harness/dist/chunk-IONC45N6.js
1969
+ import { execFile as execFile2, spawn as spawn3 } from "child_process";
1889
1970
  import { constants } from "fs";
1890
1971
  import { access } from "fs/promises";
1891
- import { delimiter, join as join2 } from "path";
1972
+ import { delimiter, join as join3 } from "path";
1892
1973
  import { getgid, getuid } from "process";
1893
- import { mkdir, mkdtemp, realpath, rm, writeFile } from "fs/promises";
1894
- import { tmpdir } from "os";
1974
+ import { mkdir, mkdtemp as mkdtemp2, realpath, rm as rm2, writeFile as writeFile2 } from "fs/promises";
1975
+ import { tmpdir as tmpdir2 } from "os";
1895
1976
  import { join as join22, resolve as resolve3, sep } from "path";
1896
1977
  import { spawn as spawn22 } from "child_process";
1897
1978
  import { isAbsolute as isAbsolute3 } from "path";
1898
- import { chmod, copyFile, lstat, mkdir as mkdir2, mkdtemp as mkdtemp2, readdir, realpath as realpath2, rm as rm2, stat } from "fs/promises";
1899
- import { tmpdir as tmpdir2 } from "os";
1900
- import { basename, join as join3, relative as relative2, resolve as resolve22, sep as sep2 } from "path";
1901
- import { spawn as spawn3 } from "child_process";
1979
+ import { chmod, copyFile as copyFile2, lstat, mkdir as mkdir2, mkdtemp as mkdtemp22, readdir, realpath as realpath2, rm as rm22, stat } from "fs/promises";
1980
+ import { tmpdir as tmpdir22 } from "os";
1981
+ import { basename, join as join32, relative as relative2, resolve as resolve22, sep as sep2 } from "path";
1982
+ import { spawn as spawn32 } from "child_process";
1902
1983
  var DIGEST_IMAGE = /^[a-z0-9][a-z0-9._/-]*(?::[a-zA-Z0-9._-]+)?@sha256:[0-9a-f]{64}$/;
1903
1984
  function assertPinnedImage(image) {
1904
1985
  if (!DIGEST_IMAGE.test(image)) throw new TypeError("container image must be pinned by sha256 digest");
@@ -1906,7 +1987,7 @@ function assertPinnedImage(image) {
1906
1987
  async function commandAvailable(engine) {
1907
1988
  for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
1908
1989
  try {
1909
- await access(join2(directory, engine), constants.X_OK);
1990
+ await access(join3(directory, engine), constants.X_OK);
1910
1991
  return true;
1911
1992
  } catch {
1912
1993
  }
@@ -1937,7 +2018,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
1937
2018
  throw new TypeError("no rootless Podman found; install Podman or explicitly choose --engine docker after reviewing its daemon boundary");
1938
2019
  }
1939
2020
  if (platform === "darwin") {
1940
- throw new TypeError("no Apple container or Podman Machine found; install one or explicitly choose --engine docker");
2021
+ throw new TypeError("Apple container is not installed; on Apple Silicon macOS 26 run `brew install container`, then retry (Podman Machine is the fallback)");
1941
2022
  }
1942
2023
  throw new TypeError("no supported container engine found");
1943
2024
  }
@@ -2030,7 +2111,7 @@ async function runContainerAttempt(options) {
2030
2111
  await verifyContainerEngineBoundary(options.engine);
2031
2112
  const args = buildContainerRunArgs(options);
2032
2113
  const name = containerName(args);
2033
- const child = spawn2(options.engine, args, { stdio: ["pipe", "pipe", "pipe"], shell: false });
2114
+ const child = spawn3(options.engine, args, { stdio: ["pipe", "pipe", "pipe"], shell: false });
2034
2115
  let stderr = "";
2035
2116
  let outputBytes = 0;
2036
2117
  let complete = null;
@@ -2048,7 +2129,7 @@ async function runContainerAttempt(options) {
2048
2129
  child.stdin.write(encodeAgentInput(cancel));
2049
2130
  }
2050
2131
  const removeArgs = options.engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
2051
- const killer = spawn2(options.engine, removeArgs, { stdio: "ignore", shell: false });
2132
+ const killer = spawn3(options.engine, removeArgs, { stdio: "ignore", shell: false });
2052
2133
  killer.unref();
2053
2134
  };
2054
2135
  const abort = () => stop("runner_cancelled");
@@ -2201,7 +2282,7 @@ async function materializeGitTree(source, commitSha, options = {}) {
2201
2282
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
2202
2283
  });
2203
2284
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
2204
- const root = await mkdtemp(join22(options.tempRoot ?? tmpdir(), "odla-git-tree-"));
2285
+ const root = await mkdtemp2(join22(options.tempRoot ?? tmpdir2(), "odla-git-tree-"));
2205
2286
  const targetRoot = join22(root, "source");
2206
2287
  await mkdir(targetRoot);
2207
2288
  let byteCount = 0;
@@ -2214,17 +2295,17 @@ async function materializeGitTree(source, commitSha, options = {}) {
2214
2295
  const target = resolve3(targetRoot, entry.path);
2215
2296
  if (!target.startsWith(`${resolve3(targetRoot)}${sep}`)) throw new TypeError("Git tree path escapes workspace");
2216
2297
  await mkdir(resolve3(target, ".."), { recursive: true });
2217
- await writeFile(target, content, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
2298
+ await writeFile2(target, content, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
2218
2299
  }
2219
2300
  return {
2220
2301
  root,
2221
2302
  sourceDir: targetRoot,
2222
2303
  fileCount: entries.length,
2223
2304
  byteCount,
2224
- cleanup: () => rm(root, { recursive: true, force: true })
2305
+ cleanup: () => rm2(root, { recursive: true, force: true })
2225
2306
  };
2226
2307
  } catch (error) {
2227
- await rm(root, { recursive: true, force: true });
2308
+ await rm2(root, { recursive: true, force: true });
2228
2309
  throw error;
2229
2310
  }
2230
2311
  }
@@ -2235,7 +2316,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
2235
2316
  for (const entry of await readdir(dir, { withFileTypes: true })) {
2236
2317
  if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
2237
2318
  if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
2238
- const path = join3(dir, entry.name);
2319
+ const path = join32(dir, entry.name);
2239
2320
  if (entry.isSymbolicLink()) continue;
2240
2321
  if (entry.isDirectory()) {
2241
2322
  await walk(path);
@@ -2258,7 +2339,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
2258
2339
  return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
2259
2340
  }
2260
2341
  async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
2261
- const child = spawn3("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2342
+ const child = spawn32("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2262
2343
  cwd: sourceDir,
2263
2344
  stdio: ["ignore", "pipe", "pipe"],
2264
2345
  shell: false
@@ -2305,14 +2386,14 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
2305
2386
  }
2306
2387
  async function copyTree(files, destination) {
2307
2388
  for (const file of files) {
2308
- const target = join3(destination, file.relativePath);
2389
+ const target = join32(destination, file.relativePath);
2309
2390
  await mkdir2(resolve22(target, ".."), { recursive: true });
2310
- await copyFile(file.source, target);
2391
+ await copyFile2(file.source, target);
2311
2392
  await chmod(target, file.mode);
2312
2393
  }
2313
2394
  }
2314
2395
  async function captureGitDiff(root, maxBytes) {
2315
- const child = spawn3("git", [
2396
+ const child = spawn32("git", [
2316
2397
  "diff",
2317
2398
  "--no-index",
2318
2399
  "--binary",
@@ -2342,15 +2423,15 @@ async function captureGitDiff(root, maxBytes) {
2342
2423
  if (code !== 0 && code !== 1) {
2343
2424
  throw new Error(`git diff failed: ${Buffer.concat(stderr).toString("utf8").slice(0, 1e3)}`);
2344
2425
  }
2345
- return Buffer.concat(stdout).toString("utf8").replaceAll("a/baseline/", "a/").replaceAll("b/workspace/", "b/").replaceAll("--- a/baseline", "--- a").replaceAll("+++ b/workspace", "+++ b");
2426
+ return Buffer.concat(stdout).toString("utf8").replaceAll("a/baseline/", "a/").replaceAll("a/workspace/", "a/").replaceAll("b/baseline/", "b/").replaceAll("b/workspace/", "b/").replaceAll("--- a/baseline", "--- a").replaceAll("+++ b/workspace", "+++ b");
2346
2427
  }
2347
2428
  async function stageWorkspace(source, options = {}) {
2348
2429
  const sourceDir = await realpath2(resolve22(source));
2349
2430
  const sourceStat = await stat(sourceDir);
2350
2431
  if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
2351
- const root = await mkdtemp2(join3(options.tempRoot ?? tmpdir2(), "odla-harness-"));
2352
- const baselineDir = join3(root, "baseline");
2353
- const workspaceDir = join3(root, "workspace");
2432
+ const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
2433
+ const baselineDir = join32(root, "baseline");
2434
+ const workspaceDir = join32(root, "workspace");
2354
2435
  await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
2355
2436
  try {
2356
2437
  const maxFiles = options.maxFiles ?? 2e4;
@@ -2364,10 +2445,10 @@ async function stageWorkspace(source, options = {}) {
2364
2445
  fileCount: files.length,
2365
2446
  byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
2366
2447
  patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
2367
- cleanup: () => rm2(root, { recursive: true, force: true })
2448
+ cleanup: () => rm22(root, { recursive: true, force: true })
2368
2449
  };
2369
2450
  } catch (error) {
2370
- await rm2(root, { recursive: true, force: true });
2451
+ await rm22(root, { recursive: true, force: true });
2371
2452
  throw error;
2372
2453
  }
2373
2454
  }
@@ -2380,9 +2461,9 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2380
2461
  sourceFiles(baselineDirSource, maxFiles, maxBytes),
2381
2462
  sourceFiles(workspaceDirSource, maxFiles, maxBytes)
2382
2463
  ]);
2383
- const root = await mkdtemp2(join3(options.tempRoot ?? tmpdir2(), "odla-harness-"));
2384
- const baselineDir = join3(root, "baseline");
2385
- const workspaceDir = join3(root, "workspace");
2464
+ const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
2465
+ const baselineDir = join32(root, "baseline");
2466
+ const workspaceDir = join32(root, "workspace");
2386
2467
  await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
2387
2468
  try {
2388
2469
  await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
@@ -2393,17 +2474,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2393
2474
  fileCount: workspaceFiles.length,
2394
2475
  byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
2395
2476
  patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
2396
- cleanup: () => rm2(root, { recursive: true, force: true })
2477
+ cleanup: () => rm22(root, { recursive: true, force: true })
2397
2478
  };
2398
2479
  } catch (error) {
2399
- await rm2(root, { recursive: true, force: true });
2480
+ await rm22(root, { recursive: true, force: true });
2400
2481
  throw error;
2401
2482
  }
2402
2483
  }
2403
2484
 
2404
- // ../harness/dist/chunk-4MOSEKB6.js
2405
- import { createHash } from "crypto";
2406
- import { readFile, readdir as readdir2 } from "fs/promises";
2485
+ // ../harness/dist/chunk-QGDLTDYH.js
2486
+ import { createHash as createHash2 } from "crypto";
2487
+ import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
2407
2488
  import { relative as relative3, resolve as resolve4 } from "path";
2408
2489
 
2409
2490
  // ../camel/dist/chunk-7FHPOQVP.js
@@ -2738,21 +2819,21 @@ function validateSnapshot(snapshot, limits) {
2738
2819
  }
2739
2820
  }
2740
2821
 
2741
- // ../harness/dist/chunk-4MOSEKB6.js
2822
+ // ../harness/dist/chunk-QGDLTDYH.js
2742
2823
  import { spawn as spawn4 } from "child_process";
2743
2824
  import { lstat as lstat2 } from "fs/promises";
2744
2825
  import { resolve as resolve23, sep as sep3 } from "path";
2745
2826
  import { spawn as spawn23 } from "child_process";
2746
2827
  import { getgid as getgid2, getuid as getuid2 } from "process";
2747
2828
  import { randomUUID } from "crypto";
2748
- import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
2829
+ import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
2749
2830
  import { createReadStream } from "fs";
2750
2831
  import { lstat as lstat22 } from "fs/promises";
2751
2832
  import { join as join4 } from "path";
2752
- import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile2 } from "fs/promises";
2833
+ import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
2753
2834
  import { tmpdir as tmpdir3 } from "os";
2754
2835
  import { dirname as dirname4, join as join23, resolve as resolve32, sep as sep22 } from "path";
2755
- import { readFile as readFile2, readdir as readdir22, stat as stat2 } from "fs/promises";
2836
+ import { readFile as readFile22, readdir as readdir22, stat as stat2 } from "fs/promises";
2756
2837
  import { relative as relative22, resolve as resolve42 } from "path";
2757
2838
 
2758
2839
  // ../camel/dist/chunk-LAXU2AVK.js
@@ -3031,7 +3112,7 @@ function looksLikeDestination(value) {
3031
3112
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
3032
3113
  }
3033
3114
 
3034
- // ../harness/dist/chunk-4MOSEKB6.js
3115
+ // ../harness/dist/chunk-QGDLTDYH.js
3035
3116
  import { createHash as createHash3 } from "crypto";
3036
3117
  async function digestStagedWorkspace(root, limits) {
3037
3118
  const files = [];
@@ -3048,10 +3129,10 @@ async function digestStagedWorkspace(root, limits) {
3048
3129
  }
3049
3130
  };
3050
3131
  await walk(resolve4(root));
3051
- const hash = createHash("sha256");
3132
+ const hash = createHash2("sha256");
3052
3133
  let bytes = 0;
3053
3134
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
3054
- const content = await readFile(file.target);
3135
+ const content = await readFile2(file.target);
3055
3136
  bytes += Buffer.byteLength(file.path) + content.byteLength;
3056
3137
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
3057
3138
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content.byteLength}:`);
@@ -3685,7 +3766,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
3685
3766
  }
3686
3767
  function hashFile(path) {
3687
3768
  return new Promise((accept, reject) => {
3688
- const hash = createHash2("sha256");
3769
+ const hash = createHash22("sha256");
3689
3770
  const stream = createReadStream(path);
3690
3771
  stream.on("data", (chunk) => {
3691
3772
  hash.update(chunk);
@@ -3743,7 +3824,7 @@ function digestJson(value) {
3743
3824
  return digestBytes(JSON.stringify(value));
3744
3825
  }
3745
3826
  function digestBytes(value) {
3746
- return `sha256:${createHash2("sha256").update(value).digest("hex")}`;
3827
+ return `sha256:${createHash22("sha256").update(value).digest("hex")}`;
3747
3828
  }
3748
3829
  function integer(value, minimum, maximum) {
3749
3830
  return Number.isSafeInteger(value) && value >= minimum && value <= maximum;
@@ -3864,7 +3945,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
3864
3945
  const target = resolve32(sourceDir, file.path);
3865
3946
  if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code source path escapes its root");
3866
3947
  await mkdir3(dirname4(target), { recursive: true });
3867
- await writeFile2(target, file.content, { flag: "wx", mode: 420 });
3948
+ await writeFile3(target, file.content, { flag: "wx", mode: 420 });
3868
3949
  }
3869
3950
  for (const reference of snapshot.references ?? []) {
3870
3951
  validateAlias(reference.alias);
@@ -3879,7 +3960,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
3879
3960
  const target = resolve32(sourceDir, path);
3880
3961
  if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3881
3962
  await mkdir3(dirname4(target), { recursive: true });
3882
- await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3963
+ await writeFile3(target, file.content, { flag: "wx", mode: 292 });
3883
3964
  }
3884
3965
  }
3885
3966
  return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
@@ -3906,7 +3987,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
3906
3987
  const target = resolve32(root, path);
3907
3988
  if (!target.startsWith(`${resolve32(root)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3908
3989
  await mkdir3(dirname4(target), { recursive: true });
3909
- await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3990
+ await writeFile3(target, file.content, { flag: "wx", mode: 292 });
3910
3991
  }
3911
3992
  }
3912
3993
  }
@@ -4096,7 +4177,7 @@ async function read(context, request, options, policy) {
4096
4177
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
4097
4178
  throw new TypeError("file is not a bounded regular source file");
4098
4179
  }
4099
- const source = await readFile2(target);
4180
+ const source = await readFile22(target);
4100
4181
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
4101
4182
  const lines = source.toString("utf8").split("\n");
4102
4183
  const content = lines.slice(startLine - 1, endLine).join("\n");
@@ -4397,6 +4478,7 @@ function runtimeResultError(value) {
4397
4478
  var CodePiRuntimeEngine = class {
4398
4479
  constructor(options) {
4399
4480
  this.options = options;
4481
+ 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");
4400
4482
  this.#run = options.runAttempt ?? runContainerAttempt;
4401
4483
  this.#buildPolicyDigest = digestRuntimeValue(JSON.stringify(options.recipes));
4402
4484
  this.#checkpoints = new CodeRuntimeCheckpointManager({
@@ -4562,6 +4644,7 @@ var CodePiRuntimeEngine = class {
4562
4644
  const result = await this.#run({
4563
4645
  engine: this.options.engine,
4564
4646
  image: this.options.image,
4647
+ allowUnpinnedImage: this.options.imageAuthorization === "cli_embedded",
4565
4648
  workspaceDir: active.workspace.workspaceDir,
4566
4649
  workspaceAccess: "none",
4567
4650
  task: lease.task,
@@ -4855,20 +4938,6 @@ function digestText(value) {
4855
4938
  return `sha256:${createHash4("sha256").update(value).digest("hex")}`;
4856
4939
  }
4857
4940
 
4858
- // src/code-images.ts
4859
- import { execFile as execFile4 } from "child_process";
4860
- async function prepareCodeImages(engine, images) {
4861
- for (const image of images) {
4862
- await new Promise((accept, reject) => {
4863
- const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
4864
- execFile4(engine, args, { encoding: "utf8", maxBuffer: 2 * 1024 * 1024, timeout: 10 * 6e4 }, (error) => {
4865
- if (error) reject(new Error(`could not prepare pinned Code image ${image}`));
4866
- else accept();
4867
- });
4868
- });
4869
- }
4870
- }
4871
-
4872
4941
  // src/code-connect.ts
4873
4942
  async function codeConnect(options) {
4874
4943
  const cwd = options.cwd ?? process.cwd();
@@ -4901,10 +4970,11 @@ async function codeConnect(options) {
4901
4970
  const out = options.stdout ?? console;
4902
4971
  const doFetch = options.fetch ?? fetch;
4903
4972
  const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
4904
- await (options.prepareImages ?? prepareCodeImages)(engine, [
4905
- CODE_PI_IMAGE,
4906
- ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))
4907
- ]);
4973
+ const [piImage] = await (options.prepareImages ?? prepareCodeImages)(
4974
+ engine,
4975
+ [CODE_PI_IMAGE, ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))]
4976
+ );
4977
+ 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");
4908
4978
  const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
4909
4979
  const hostName = (options.name ?? hostname()).trim();
4910
4980
  if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
@@ -4948,7 +5018,8 @@ async function codeConnect(options) {
4948
5018
  source: descriptor2,
4949
5019
  images: {
4950
5020
  ready: true,
4951
- pi: CODE_PI_IMAGE,
5021
+ pi: piImage,
5022
+ piSource: "cli_embedded",
4952
5023
  recipes: CODE_BUILD_RECIPES.map((recipe2) => ({ id: recipe2.id, image: recipe2.image }))
4953
5024
  }
4954
5025
  };
@@ -4963,6 +5034,7 @@ async function codeConnect(options) {
4963
5034
  engine,
4964
5035
  capabilities,
4965
5036
  localSource,
5037
+ piImage,
4966
5038
  heartbeatMs,
4967
5039
  once: options.once === true,
4968
5040
  signal: options.signal,
@@ -4992,7 +5064,8 @@ async function runCodeRuntime(input) {
4992
5064
  const commandEngine = new CodePiRuntimeEngine({
4993
5065
  control,
4994
5066
  engine: input.engine,
4995
- image: CODE_PI_IMAGE,
5067
+ image: input.piImage ?? input.capabilities.images.pi,
5068
+ imageAuthorization: "cli_embedded",
4996
5069
  recipes: CODE_BUILD_RECIPES,
4997
5070
  recipeAuthorization: "registered_recipe",
4998
5071
  localSource: input.localSource,
@@ -6370,7 +6443,7 @@ function securityExecutionDigest(value) {
6370
6443
  import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync3 } from "fs";
6371
6444
  import { homedir } from "os";
6372
6445
  import { dirname as dirname6, isAbsolute as isAbsolute5, join as join7, relative as relative5, resolve as resolve9, sep as sep5 } from "path";
6373
- import { fileURLToPath } from "url";
6446
+ import { fileURLToPath as fileURLToPath2 } from "url";
6374
6447
 
6375
6448
  // src/skill-adapters.ts
6376
6449
  var PROJECT_INSTRUCTIONS = `<!-- odla-ai agent setup:start -->
@@ -6426,7 +6499,7 @@ runbook. Do not substitute a remote documentation page for the installed copy.
6426
6499
  var AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
6427
6500
  function installSkill(options = {}) {
6428
6501
  const out = options.stdout ?? console;
6429
- const sourceDir = options.sourceDir ?? fileURLToPath(new URL("../skills", import.meta.url));
6502
+ const sourceDir = options.sourceDir ?? fileURLToPath2(new URL("../skills", import.meta.url));
6430
6503
  const files = listFiles(sourceDir);
6431
6504
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
6432
6505
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
@@ -7813,6 +7886,7 @@ export {
7813
7886
  inferGitHubRepository,
7814
7887
  CODE_PI_IMAGE,
7815
7888
  CODE_BUILD_RECIPES,
7889
+ prepareCodeImages,
7816
7890
  codeConnect,
7817
7891
  runCodeRuntime,
7818
7892
  doctor,
@@ -7836,4 +7910,4 @@ export {
7836
7910
  exitCodeFor,
7837
7911
  runCli
7838
7912
  };
7839
- //# sourceMappingURL=chunk-YWC7F5T3.js.map
7913
+ //# sourceMappingURL=chunk-GLY5BN6D.js.map