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