@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/README.md +9 -6
- package/dist/bin.cjs +237 -102
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-PQX6SKYJ.js → chunk-GAFC4EPJ.js} +227 -93
- package/dist/chunk-GAFC4EPJ.js.map +1 -0
- package/dist/index.cjs +239 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +3 -1
- package/dist/runtime/pi-agent.js +330317 -0
- package/package.json +2 -2
- package/skills/odla-migrate/references/phase-2-db.md +2 -2
- package/dist/chunk-PQX6SKYJ.js.map +0 -1
|
@@ -1775,12 +1775,16 @@ async function defaultReadOrigin(cwd) {
|
|
|
1775
1775
|
}
|
|
1776
1776
|
|
|
1777
1777
|
// src/code-runtime-config.ts
|
|
1778
|
-
var CODE_PI_IMAGE = "
|
|
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:
|
|
1782
|
+
image: CODE_NODE_IMAGE,
|
|
1782
1783
|
command: [
|
|
1783
1784
|
"node",
|
|
1785
|
+
// The clean source mount deliberately has no node_modules. Reuse the
|
|
1786
|
+
// semver copy inside this digest-pinned npm image without network access.
|
|
1787
|
+
"--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",
|
|
1784
1788
|
"--test",
|
|
1785
1789
|
"scripts/lib/directories.test.mjs",
|
|
1786
1790
|
"scripts/lib/internal-deps.test.mjs",
|
|
@@ -1795,15 +1799,95 @@ var CODE_BUILD_RECIPES = Object.freeze([{
|
|
|
1795
1799
|
pids: 128
|
|
1796
1800
|
}]);
|
|
1797
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
|
+
|
|
1798
1882
|
// src/code-connect.ts
|
|
1799
1883
|
import { existsSync as existsSync4 } from "fs";
|
|
1800
1884
|
import { cpus, hostname, totalmem } from "os";
|
|
1801
1885
|
import { resolve as resolve5 } from "path";
|
|
1802
1886
|
|
|
1803
|
-
// ../harness/dist/chunk-
|
|
1887
|
+
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
1804
1888
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
1805
1889
|
|
|
1806
|
-
// ../harness/dist/chunk-
|
|
1890
|
+
// ../harness/dist/chunk-GE6CCN7W.js
|
|
1807
1891
|
var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
|
|
1808
1892
|
var HarnessProtocolError = class extends Error {
|
|
1809
1893
|
name = "HarnessProtocolError";
|
|
@@ -1881,21 +1965,21 @@ function encodeAgentInput(message2) {
|
|
|
1881
1965
|
`;
|
|
1882
1966
|
}
|
|
1883
1967
|
|
|
1884
|
-
// ../harness/dist/chunk-
|
|
1885
|
-
import { execFile as execFile2, spawn as
|
|
1968
|
+
// ../harness/dist/chunk-NOBK6PCT.js
|
|
1969
|
+
import { execFile as execFile2, spawn as spawn3 } from "child_process";
|
|
1886
1970
|
import { constants } from "fs";
|
|
1887
1971
|
import { access } from "fs/promises";
|
|
1888
|
-
import { delimiter, join as
|
|
1972
|
+
import { delimiter, join as join3 } from "path";
|
|
1889
1973
|
import { getgid, getuid } from "process";
|
|
1890
|
-
import { mkdir, mkdtemp, realpath, rm, writeFile } from "fs/promises";
|
|
1891
|
-
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";
|
|
1892
1976
|
import { join as join22, resolve as resolve3, sep } from "path";
|
|
1893
1977
|
import { spawn as spawn22 } from "child_process";
|
|
1894
1978
|
import { isAbsolute as isAbsolute3 } from "path";
|
|
1895
|
-
import { chmod, copyFile, lstat, mkdir as mkdir2, mkdtemp as
|
|
1896
|
-
import { tmpdir as
|
|
1897
|
-
import { basename, join as
|
|
1898
|
-
import { spawn as
|
|
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";
|
|
1899
1983
|
var DIGEST_IMAGE = /^[a-z0-9][a-z0-9._/-]*(?::[a-zA-Z0-9._-]+)?@sha256:[0-9a-f]{64}$/;
|
|
1900
1984
|
function assertPinnedImage(image) {
|
|
1901
1985
|
if (!DIGEST_IMAGE.test(image)) throw new TypeError("container image must be pinned by sha256 digest");
|
|
@@ -1903,7 +1987,7 @@ function assertPinnedImage(image) {
|
|
|
1903
1987
|
async function commandAvailable(engine) {
|
|
1904
1988
|
for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
1905
1989
|
try {
|
|
1906
|
-
await access(
|
|
1990
|
+
await access(join3(directory, engine), constants.X_OK);
|
|
1907
1991
|
return true;
|
|
1908
1992
|
} catch {
|
|
1909
1993
|
}
|
|
@@ -1934,7 +2018,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
|
|
|
1934
2018
|
throw new TypeError("no rootless Podman found; install Podman or explicitly choose --engine docker after reviewing its daemon boundary");
|
|
1935
2019
|
}
|
|
1936
2020
|
if (platform === "darwin") {
|
|
1937
|
-
throw new TypeError("
|
|
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)");
|
|
1938
2022
|
}
|
|
1939
2023
|
throw new TypeError("no supported container engine found");
|
|
1940
2024
|
}
|
|
@@ -2027,7 +2111,7 @@ async function runContainerAttempt(options) {
|
|
|
2027
2111
|
await verifyContainerEngineBoundary(options.engine);
|
|
2028
2112
|
const args = buildContainerRunArgs(options);
|
|
2029
2113
|
const name = containerName(args);
|
|
2030
|
-
const child =
|
|
2114
|
+
const child = spawn3(options.engine, args, { stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
2031
2115
|
let stderr = "";
|
|
2032
2116
|
let outputBytes = 0;
|
|
2033
2117
|
let complete = null;
|
|
@@ -2045,7 +2129,7 @@ async function runContainerAttempt(options) {
|
|
|
2045
2129
|
child.stdin.write(encodeAgentInput(cancel));
|
|
2046
2130
|
}
|
|
2047
2131
|
const removeArgs = options.engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
|
|
2048
|
-
const killer =
|
|
2132
|
+
const killer = spawn3(options.engine, removeArgs, { stdio: "ignore", shell: false });
|
|
2049
2133
|
killer.unref();
|
|
2050
2134
|
};
|
|
2051
2135
|
const abort = () => stop("runner_cancelled");
|
|
@@ -2198,7 +2282,7 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
2198
2282
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
2199
2283
|
});
|
|
2200
2284
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
2201
|
-
const root = await
|
|
2285
|
+
const root = await mkdtemp2(join22(options.tempRoot ?? tmpdir2(), "odla-git-tree-"));
|
|
2202
2286
|
const targetRoot = join22(root, "source");
|
|
2203
2287
|
await mkdir(targetRoot);
|
|
2204
2288
|
let byteCount = 0;
|
|
@@ -2211,17 +2295,17 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
2211
2295
|
const target = resolve3(targetRoot, entry.path);
|
|
2212
2296
|
if (!target.startsWith(`${resolve3(targetRoot)}${sep}`)) throw new TypeError("Git tree path escapes workspace");
|
|
2213
2297
|
await mkdir(resolve3(target, ".."), { recursive: true });
|
|
2214
|
-
await
|
|
2298
|
+
await writeFile2(target, content, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
|
|
2215
2299
|
}
|
|
2216
2300
|
return {
|
|
2217
2301
|
root,
|
|
2218
2302
|
sourceDir: targetRoot,
|
|
2219
2303
|
fileCount: entries.length,
|
|
2220
2304
|
byteCount,
|
|
2221
|
-
cleanup: () =>
|
|
2305
|
+
cleanup: () => rm2(root, { recursive: true, force: true })
|
|
2222
2306
|
};
|
|
2223
2307
|
} catch (error) {
|
|
2224
|
-
await
|
|
2308
|
+
await rm2(root, { recursive: true, force: true });
|
|
2225
2309
|
throw error;
|
|
2226
2310
|
}
|
|
2227
2311
|
}
|
|
@@ -2232,7 +2316,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
2232
2316
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
2233
2317
|
if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
2234
2318
|
if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
|
|
2235
|
-
const path =
|
|
2319
|
+
const path = join32(dir, entry.name);
|
|
2236
2320
|
if (entry.isSymbolicLink()) continue;
|
|
2237
2321
|
if (entry.isDirectory()) {
|
|
2238
2322
|
await walk(path);
|
|
@@ -2255,7 +2339,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
2255
2339
|
return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
2256
2340
|
}
|
|
2257
2341
|
async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
2258
|
-
const child =
|
|
2342
|
+
const child = spawn32("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
2259
2343
|
cwd: sourceDir,
|
|
2260
2344
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2261
2345
|
shell: false
|
|
@@ -2302,14 +2386,14 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
2302
2386
|
}
|
|
2303
2387
|
async function copyTree(files, destination) {
|
|
2304
2388
|
for (const file of files) {
|
|
2305
|
-
const target =
|
|
2389
|
+
const target = join32(destination, file.relativePath);
|
|
2306
2390
|
await mkdir2(resolve22(target, ".."), { recursive: true });
|
|
2307
|
-
await
|
|
2391
|
+
await copyFile2(file.source, target);
|
|
2308
2392
|
await chmod(target, file.mode);
|
|
2309
2393
|
}
|
|
2310
2394
|
}
|
|
2311
2395
|
async function captureGitDiff(root, maxBytes) {
|
|
2312
|
-
const child =
|
|
2396
|
+
const child = spawn32("git", [
|
|
2313
2397
|
"diff",
|
|
2314
2398
|
"--no-index",
|
|
2315
2399
|
"--binary",
|
|
@@ -2345,9 +2429,9 @@ async function stageWorkspace(source, options = {}) {
|
|
|
2345
2429
|
const sourceDir = await realpath2(resolve22(source));
|
|
2346
2430
|
const sourceStat = await stat(sourceDir);
|
|
2347
2431
|
if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
|
|
2348
|
-
const root = await
|
|
2349
|
-
const baselineDir =
|
|
2350
|
-
const workspaceDir =
|
|
2432
|
+
const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
|
|
2433
|
+
const baselineDir = join32(root, "baseline");
|
|
2434
|
+
const workspaceDir = join32(root, "workspace");
|
|
2351
2435
|
await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
|
|
2352
2436
|
try {
|
|
2353
2437
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
@@ -2361,10 +2445,10 @@ async function stageWorkspace(source, options = {}) {
|
|
|
2361
2445
|
fileCount: files.length,
|
|
2362
2446
|
byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
|
|
2363
2447
|
patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
|
|
2364
|
-
cleanup: () =>
|
|
2448
|
+
cleanup: () => rm22(root, { recursive: true, force: true })
|
|
2365
2449
|
};
|
|
2366
2450
|
} catch (error) {
|
|
2367
|
-
await
|
|
2451
|
+
await rm22(root, { recursive: true, force: true });
|
|
2368
2452
|
throw error;
|
|
2369
2453
|
}
|
|
2370
2454
|
}
|
|
@@ -2377,9 +2461,9 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
2377
2461
|
sourceFiles(baselineDirSource, maxFiles, maxBytes),
|
|
2378
2462
|
sourceFiles(workspaceDirSource, maxFiles, maxBytes)
|
|
2379
2463
|
]);
|
|
2380
|
-
const root = await
|
|
2381
|
-
const baselineDir =
|
|
2382
|
-
const workspaceDir =
|
|
2464
|
+
const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
|
|
2465
|
+
const baselineDir = join32(root, "baseline");
|
|
2466
|
+
const workspaceDir = join32(root, "workspace");
|
|
2383
2467
|
await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
|
|
2384
2468
|
try {
|
|
2385
2469
|
await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
|
|
@@ -2390,17 +2474,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
2390
2474
|
fileCount: workspaceFiles.length,
|
|
2391
2475
|
byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
|
|
2392
2476
|
patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
|
|
2393
|
-
cleanup: () =>
|
|
2477
|
+
cleanup: () => rm22(root, { recursive: true, force: true })
|
|
2394
2478
|
};
|
|
2395
2479
|
} catch (error) {
|
|
2396
|
-
await
|
|
2480
|
+
await rm22(root, { recursive: true, force: true });
|
|
2397
2481
|
throw error;
|
|
2398
2482
|
}
|
|
2399
2483
|
}
|
|
2400
2484
|
|
|
2401
|
-
// ../harness/dist/chunk-
|
|
2402
|
-
import { createHash } from "crypto";
|
|
2403
|
-
import { readFile, readdir as readdir2 } from "fs/promises";
|
|
2485
|
+
// ../harness/dist/chunk-6J2ZETST.js
|
|
2486
|
+
import { createHash as createHash2 } from "crypto";
|
|
2487
|
+
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
2404
2488
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
2405
2489
|
|
|
2406
2490
|
// ../camel/dist/chunk-7FHPOQVP.js
|
|
@@ -2735,21 +2819,21 @@ function validateSnapshot(snapshot, limits) {
|
|
|
2735
2819
|
}
|
|
2736
2820
|
}
|
|
2737
2821
|
|
|
2738
|
-
// ../harness/dist/chunk-
|
|
2822
|
+
// ../harness/dist/chunk-6J2ZETST.js
|
|
2739
2823
|
import { spawn as spawn4 } from "child_process";
|
|
2740
2824
|
import { lstat as lstat2 } from "fs/promises";
|
|
2741
2825
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
2742
2826
|
import { spawn as spawn23 } from "child_process";
|
|
2743
2827
|
import { getgid as getgid2, getuid as getuid2 } from "process";
|
|
2744
2828
|
import { randomUUID } from "crypto";
|
|
2745
|
-
import { createHash as
|
|
2829
|
+
import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
|
|
2746
2830
|
import { createReadStream } from "fs";
|
|
2747
2831
|
import { lstat as lstat22 } from "fs/promises";
|
|
2748
2832
|
import { join as join4 } from "path";
|
|
2749
|
-
import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as
|
|
2833
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
2750
2834
|
import { tmpdir as tmpdir3 } from "os";
|
|
2751
2835
|
import { dirname as dirname4, join as join23, resolve as resolve32, sep as sep22 } from "path";
|
|
2752
|
-
import { readFile as
|
|
2836
|
+
import { readFile as readFile22, readdir as readdir22, stat as stat2 } from "fs/promises";
|
|
2753
2837
|
import { relative as relative22, resolve as resolve42 } from "path";
|
|
2754
2838
|
|
|
2755
2839
|
// ../camel/dist/chunk-LAXU2AVK.js
|
|
@@ -3028,7 +3112,7 @@ function looksLikeDestination(value) {
|
|
|
3028
3112
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
|
|
3029
3113
|
}
|
|
3030
3114
|
|
|
3031
|
-
// ../harness/dist/chunk-
|
|
3115
|
+
// ../harness/dist/chunk-6J2ZETST.js
|
|
3032
3116
|
import { createHash as createHash3 } from "crypto";
|
|
3033
3117
|
async function digestStagedWorkspace(root, limits) {
|
|
3034
3118
|
const files = [];
|
|
@@ -3045,10 +3129,10 @@ async function digestStagedWorkspace(root, limits) {
|
|
|
3045
3129
|
}
|
|
3046
3130
|
};
|
|
3047
3131
|
await walk(resolve4(root));
|
|
3048
|
-
const hash =
|
|
3132
|
+
const hash = createHash2("sha256");
|
|
3049
3133
|
let bytes = 0;
|
|
3050
3134
|
for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
|
|
3051
|
-
const content = await
|
|
3135
|
+
const content = await readFile2(file.target);
|
|
3052
3136
|
bytes += Buffer.byteLength(file.path) + content.byteLength;
|
|
3053
3137
|
if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
|
|
3054
3138
|
hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content.byteLength}:`);
|
|
@@ -3682,7 +3766,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
3682
3766
|
}
|
|
3683
3767
|
function hashFile(path) {
|
|
3684
3768
|
return new Promise((accept, reject) => {
|
|
3685
|
-
const hash =
|
|
3769
|
+
const hash = createHash22("sha256");
|
|
3686
3770
|
const stream = createReadStream(path);
|
|
3687
3771
|
stream.on("data", (chunk) => {
|
|
3688
3772
|
hash.update(chunk);
|
|
@@ -3740,7 +3824,7 @@ function digestJson(value) {
|
|
|
3740
3824
|
return digestBytes(JSON.stringify(value));
|
|
3741
3825
|
}
|
|
3742
3826
|
function digestBytes(value) {
|
|
3743
|
-
return `sha256:${
|
|
3827
|
+
return `sha256:${createHash22("sha256").update(value).digest("hex")}`;
|
|
3744
3828
|
}
|
|
3745
3829
|
function integer(value, minimum, maximum) {
|
|
3746
3830
|
return Number.isSafeInteger(value) && value >= minimum && value <= maximum;
|
|
@@ -3861,7 +3945,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
3861
3945
|
const target = resolve32(sourceDir, file.path);
|
|
3862
3946
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code source path escapes its root");
|
|
3863
3947
|
await mkdir3(dirname4(target), { recursive: true });
|
|
3864
|
-
await
|
|
3948
|
+
await writeFile3(target, file.content, { flag: "wx", mode: 420 });
|
|
3865
3949
|
}
|
|
3866
3950
|
for (const reference of snapshot.references ?? []) {
|
|
3867
3951
|
validateAlias(reference.alias);
|
|
@@ -3876,7 +3960,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
3876
3960
|
const target = resolve32(sourceDir, path);
|
|
3877
3961
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
|
|
3878
3962
|
await mkdir3(dirname4(target), { recursive: true });
|
|
3879
|
-
await
|
|
3963
|
+
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
3880
3964
|
}
|
|
3881
3965
|
}
|
|
3882
3966
|
return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
|
|
@@ -3903,7 +3987,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
3903
3987
|
const target = resolve32(root, path);
|
|
3904
3988
|
if (!target.startsWith(`${resolve32(root)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
|
|
3905
3989
|
await mkdir3(dirname4(target), { recursive: true });
|
|
3906
|
-
await
|
|
3990
|
+
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
3907
3991
|
}
|
|
3908
3992
|
}
|
|
3909
3993
|
}
|
|
@@ -4093,7 +4177,7 @@ async function read(context, request, options, policy) {
|
|
|
4093
4177
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
4094
4178
|
throw new TypeError("file is not a bounded regular source file");
|
|
4095
4179
|
}
|
|
4096
|
-
const source = await
|
|
4180
|
+
const source = await readFile22(target);
|
|
4097
4181
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
4098
4182
|
const lines = source.toString("utf8").split("\n");
|
|
4099
4183
|
const content = lines.slice(startLine - 1, endLine).join("\n");
|
|
@@ -4219,6 +4303,7 @@ function codeCommandMetadata(payload, resume) {
|
|
|
4219
4303
|
const role = payload.role;
|
|
4220
4304
|
const title = payload.title;
|
|
4221
4305
|
const prompt = payload.prompt;
|
|
4306
|
+
const maxTokensPerInteraction = payload.maxTokensPerInteraction ?? 32e3;
|
|
4222
4307
|
if (role !== "coding" && role !== "review" || typeof title !== "string" || typeof prompt !== "string") {
|
|
4223
4308
|
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} metadata`);
|
|
4224
4309
|
}
|
|
@@ -4230,10 +4315,14 @@ function codeCommandMetadata(payload, resume) {
|
|
|
4230
4315
|
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)) {
|
|
4231
4316
|
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} trusted base`);
|
|
4232
4317
|
}
|
|
4318
|
+
if (!Number.isSafeInteger(maxTokensPerInteraction) || Number(maxTokensPerInteraction) < 4e3 || Number(maxTokensPerInteraction) > 2e5) {
|
|
4319
|
+
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} interaction token limit`);
|
|
4320
|
+
}
|
|
4233
4321
|
return {
|
|
4234
4322
|
role,
|
|
4235
4323
|
title,
|
|
4236
4324
|
prompt,
|
|
4325
|
+
maxTokensPerInteraction: Number(maxTokensPerInteraction),
|
|
4237
4326
|
planningInputDigest: typeof planning === "string" && /^sha256:[0-9a-f]{64}$/.test(planning) ? planning : null,
|
|
4238
4327
|
attestationDigest: typeof attestation === "string" ? attestation : "resume",
|
|
4239
4328
|
repository,
|
|
@@ -4309,6 +4398,57 @@ function createCodeRuntimeToolBroker(input, lease, role) {
|
|
|
4309
4398
|
});
|
|
4310
4399
|
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" }) };
|
|
4311
4400
|
}
|
|
4401
|
+
async function handleCodeRuntimeInference(input) {
|
|
4402
|
+
const { command, metadata: metadata2, request, state } = input;
|
|
4403
|
+
if (state.tokens >= metadata2.maxTokensPerInteraction) {
|
|
4404
|
+
if (!state.noticeEmitted) {
|
|
4405
|
+
state.noticeEmitted = true;
|
|
4406
|
+
await input.event({
|
|
4407
|
+
type: "message",
|
|
4408
|
+
actor: "system",
|
|
4409
|
+
body: `Pi paused at the ${metadata2.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
|
|
4410
|
+
}).catch(() => void 0);
|
|
4411
|
+
}
|
|
4412
|
+
return {
|
|
4413
|
+
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
4414
|
+
type: "inference.response",
|
|
4415
|
+
requestId: request.requestId,
|
|
4416
|
+
response: {
|
|
4417
|
+
id: `budget:${command.commandId}`,
|
|
4418
|
+
provider: "openai",
|
|
4419
|
+
model: "interaction-budget",
|
|
4420
|
+
role: "assistant",
|
|
4421
|
+
content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
|
|
4422
|
+
stopReason: "end_turn",
|
|
4423
|
+
usage: { inputTokens: 0, outputTokens: 0 }
|
|
4424
|
+
}
|
|
4425
|
+
};
|
|
4426
|
+
}
|
|
4427
|
+
const startedAt = Date.now();
|
|
4428
|
+
const response2 = await input.control.infer(command.sessionId, {
|
|
4429
|
+
requestId: request.requestId,
|
|
4430
|
+
interactionId: command.commandId,
|
|
4431
|
+
call: request.call
|
|
4432
|
+
});
|
|
4433
|
+
state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
4434
|
+
await input.event({
|
|
4435
|
+
type: "usage",
|
|
4436
|
+
provider: response2.receipt.provider,
|
|
4437
|
+
model: response2.receipt.model,
|
|
4438
|
+
inputTokens: response2.receipt.inputTokens,
|
|
4439
|
+
outputTokens: response2.receipt.outputTokens,
|
|
4440
|
+
durationMs: Date.now() - startedAt,
|
|
4441
|
+
interactionId: command.commandId,
|
|
4442
|
+
interactionTokens: state.tokens,
|
|
4443
|
+
interactionMaxTokens: metadata2.maxTokensPerInteraction
|
|
4444
|
+
}).catch(() => void 0);
|
|
4445
|
+
return {
|
|
4446
|
+
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
4447
|
+
type: "inference.response",
|
|
4448
|
+
requestId: request.requestId,
|
|
4449
|
+
response: response2.response
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4312
4452
|
async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
4313
4453
|
const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
|
|
4314
4454
|
refs.push(eventId);
|
|
@@ -4338,6 +4478,7 @@ function runtimeResultError(value) {
|
|
|
4338
4478
|
var CodePiRuntimeEngine = class {
|
|
4339
4479
|
constructor(options) {
|
|
4340
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");
|
|
4341
4482
|
this.#run = options.runAttempt ?? runContainerAttempt;
|
|
4342
4483
|
this.#buildPolicyDigest = digestRuntimeValue(JSON.stringify(options.recipes));
|
|
4343
4484
|
this.#checkpoints = new CodeRuntimeCheckpointManager({
|
|
@@ -4420,6 +4561,7 @@ var CodePiRuntimeEngine = class {
|
|
|
4420
4561
|
acknowledged: false,
|
|
4421
4562
|
role: metadata2.role,
|
|
4422
4563
|
title: metadata2.title,
|
|
4564
|
+
maxTokensPerInteraction: metadata2.maxTokensPerInteraction,
|
|
4423
4565
|
baseCommitSha: metadata2.baseCommitSha,
|
|
4424
4566
|
repository: metadata2.repository,
|
|
4425
4567
|
sourceTreeDigest: metadata2.sourceTreeDigest,
|
|
@@ -4456,6 +4598,11 @@ var CodePiRuntimeEngine = class {
|
|
|
4456
4598
|
if (!active || typeof prompt !== "string" || !prompt.trim() || prompt.length > 2e4) {
|
|
4457
4599
|
throw new TypeError("prompt requires an active Code session and bounded text");
|
|
4458
4600
|
}
|
|
4601
|
+
const requestedLimit = command.payload.maxTokensPerInteraction ?? active.maxTokensPerInteraction;
|
|
4602
|
+
if (!Number.isSafeInteger(requestedLimit) || Number(requestedLimit) < 4e3 || Number(requestedLimit) > 2e5) {
|
|
4603
|
+
throw new TypeError("prompt requires a valid interaction token limit");
|
|
4604
|
+
}
|
|
4605
|
+
active.maxTokensPerInteraction = Number(requestedLimit);
|
|
4459
4606
|
await active.done;
|
|
4460
4607
|
active.abort = new AbortController();
|
|
4461
4608
|
active.acknowledged = false;
|
|
@@ -4464,6 +4611,7 @@ var CodePiRuntimeEngine = class {
|
|
|
4464
4611
|
role: active.role,
|
|
4465
4612
|
title: active.title,
|
|
4466
4613
|
prompt,
|
|
4614
|
+
maxTokensPerInteraction: active.maxTokensPerInteraction,
|
|
4467
4615
|
planningInputDigest: active.planningInputDigest,
|
|
4468
4616
|
attestationDigest: "follow-up",
|
|
4469
4617
|
repository: active.repository,
|
|
@@ -4492,9 +4640,11 @@ var CodePiRuntimeEngine = class {
|
|
|
4492
4640
|
}, lease, metadata2.role);
|
|
4493
4641
|
const startedAt = Date.now();
|
|
4494
4642
|
let completionSeen = false;
|
|
4643
|
+
const interaction = { tokens: 0, noticeEmitted: false };
|
|
4495
4644
|
const result = await this.#run({
|
|
4496
4645
|
engine: this.options.engine,
|
|
4497
4646
|
image: this.options.image,
|
|
4647
|
+
allowUnpinnedImage: this.options.imageAuthorization === "cli_embedded",
|
|
4498
4648
|
workspaceDir: active.workspace.workspaceDir,
|
|
4499
4649
|
workspaceAccess: "none",
|
|
4500
4650
|
task: lease.task,
|
|
@@ -4507,25 +4657,18 @@ var CodePiRuntimeEngine = class {
|
|
|
4507
4657
|
}, active.conversationRefs),
|
|
4508
4658
|
onMessage: async (output) => {
|
|
4509
4659
|
if (output.type === "inference.request") {
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4660
|
+
return handleCodeRuntimeInference({
|
|
4661
|
+
command,
|
|
4662
|
+
metadata: metadata2,
|
|
4663
|
+
request: output,
|
|
4664
|
+
state: interaction,
|
|
4665
|
+
control: this.options.control,
|
|
4666
|
+
event: (event) => this.#event(
|
|
4667
|
+
command,
|
|
4668
|
+
event,
|
|
4669
|
+
active.conversationRefs
|
|
4670
|
+
)
|
|
4514
4671
|
});
|
|
4515
|
-
await this.#event(command, {
|
|
4516
|
-
type: "usage",
|
|
4517
|
-
provider: response2.receipt.provider,
|
|
4518
|
-
model: response2.receipt.model,
|
|
4519
|
-
inputTokens: response2.receipt.inputTokens,
|
|
4520
|
-
outputTokens: response2.receipt.outputTokens,
|
|
4521
|
-
durationMs: Date.now() - inferenceStarted
|
|
4522
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
4523
|
-
return {
|
|
4524
|
-
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
4525
|
-
type: "inference.response",
|
|
4526
|
-
requestId: output.requestId,
|
|
4527
|
-
response: response2.response
|
|
4528
|
-
};
|
|
4529
4672
|
}
|
|
4530
4673
|
if (output.type === "tool.request") {
|
|
4531
4674
|
const toolStarted = Date.now();
|
|
@@ -4795,20 +4938,6 @@ function digestText(value) {
|
|
|
4795
4938
|
return `sha256:${createHash4("sha256").update(value).digest("hex")}`;
|
|
4796
4939
|
}
|
|
4797
4940
|
|
|
4798
|
-
// src/code-images.ts
|
|
4799
|
-
import { execFile as execFile4 } from "child_process";
|
|
4800
|
-
async function prepareCodeImages(engine, images) {
|
|
4801
|
-
for (const image of images) {
|
|
4802
|
-
await new Promise((accept, reject) => {
|
|
4803
|
-
const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
|
|
4804
|
-
execFile4(engine, args, { encoding: "utf8", maxBuffer: 2 * 1024 * 1024, timeout: 10 * 6e4 }, (error) => {
|
|
4805
|
-
if (error) reject(new Error(`could not prepare pinned Code image ${image}`));
|
|
4806
|
-
else accept();
|
|
4807
|
-
});
|
|
4808
|
-
});
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
|
|
4812
4941
|
// src/code-connect.ts
|
|
4813
4942
|
async function codeConnect(options) {
|
|
4814
4943
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -4841,10 +4970,11 @@ async function codeConnect(options) {
|
|
|
4841
4970
|
const out = options.stdout ?? console;
|
|
4842
4971
|
const doFetch = options.fetch ?? fetch;
|
|
4843
4972
|
const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
|
|
4844
|
-
await (options.prepareImages ?? prepareCodeImages)(
|
|
4845
|
-
|
|
4846
|
-
...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))
|
|
4847
|
-
|
|
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");
|
|
4848
4978
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
4849
4979
|
const hostName = (options.name ?? hostname()).trim();
|
|
4850
4980
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
@@ -4888,7 +5018,8 @@ async function codeConnect(options) {
|
|
|
4888
5018
|
source: descriptor2,
|
|
4889
5019
|
images: {
|
|
4890
5020
|
ready: true,
|
|
4891
|
-
pi:
|
|
5021
|
+
pi: piImage,
|
|
5022
|
+
piSource: "cli_embedded",
|
|
4892
5023
|
recipes: CODE_BUILD_RECIPES.map((recipe2) => ({ id: recipe2.id, image: recipe2.image }))
|
|
4893
5024
|
}
|
|
4894
5025
|
};
|
|
@@ -4903,6 +5034,7 @@ async function codeConnect(options) {
|
|
|
4903
5034
|
engine,
|
|
4904
5035
|
capabilities,
|
|
4905
5036
|
localSource,
|
|
5037
|
+
piImage,
|
|
4906
5038
|
heartbeatMs,
|
|
4907
5039
|
once: options.once === true,
|
|
4908
5040
|
signal: options.signal,
|
|
@@ -4932,7 +5064,8 @@ async function runCodeRuntime(input) {
|
|
|
4932
5064
|
const commandEngine = new CodePiRuntimeEngine({
|
|
4933
5065
|
control,
|
|
4934
5066
|
engine: input.engine,
|
|
4935
|
-
image:
|
|
5067
|
+
image: input.piImage ?? input.capabilities.images.pi,
|
|
5068
|
+
imageAuthorization: "cli_embedded",
|
|
4936
5069
|
recipes: CODE_BUILD_RECIPES,
|
|
4937
5070
|
recipeAuthorization: "registered_recipe",
|
|
4938
5071
|
localSource: input.localSource,
|
|
@@ -6310,7 +6443,7 @@ function securityExecutionDigest(value) {
|
|
|
6310
6443
|
import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
6311
6444
|
import { homedir } from "os";
|
|
6312
6445
|
import { dirname as dirname6, isAbsolute as isAbsolute5, join as join7, relative as relative5, resolve as resolve9, sep as sep5 } from "path";
|
|
6313
|
-
import { fileURLToPath } from "url";
|
|
6446
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6314
6447
|
|
|
6315
6448
|
// src/skill-adapters.ts
|
|
6316
6449
|
var PROJECT_INSTRUCTIONS = `<!-- odla-ai agent setup:start -->
|
|
@@ -6366,7 +6499,7 @@ runbook. Do not substitute a remote documentation page for the installed copy.
|
|
|
6366
6499
|
var AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
|
|
6367
6500
|
function installSkill(options = {}) {
|
|
6368
6501
|
const out = options.stdout ?? console;
|
|
6369
|
-
const sourceDir = options.sourceDir ??
|
|
6502
|
+
const sourceDir = options.sourceDir ?? fileURLToPath2(new URL("../skills", import.meta.url));
|
|
6370
6503
|
const files = listFiles(sourceDir);
|
|
6371
6504
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
6372
6505
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
@@ -7753,6 +7886,7 @@ export {
|
|
|
7753
7886
|
inferGitHubRepository,
|
|
7754
7887
|
CODE_PI_IMAGE,
|
|
7755
7888
|
CODE_BUILD_RECIPES,
|
|
7889
|
+
prepareCodeImages,
|
|
7756
7890
|
codeConnect,
|
|
7757
7891
|
runCodeRuntime,
|
|
7758
7892
|
doctor,
|
|
@@ -7776,4 +7910,4 @@ export {
|
|
|
7776
7910
|
exitCodeFor,
|
|
7777
7911
|
runCli
|
|
7778
7912
|
};
|
|
7779
|
-
//# sourceMappingURL=chunk-
|
|
7913
|
+
//# sourceMappingURL=chunk-GAFC4EPJ.js.map
|