@odla-ai/cli 0.26.1 → 0.27.0
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 +0 -19
- package/dist/bin.cjs +348 -351
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-ZAZBUURZ.js → chunk-DS64H4FY.js} +230 -228
- package/dist/chunk-DS64H4FY.js.map +1 -0
- package/dist/index.cjs +348 -351
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-ZAZBUURZ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -314,7 +314,7 @@ var import_node_process2 = __toESM(require("process"), 1);
|
|
|
314
314
|
async function openUrl(url, options = {}) {
|
|
315
315
|
const command = openerFor(options.platform ?? import_node_process2.default.platform);
|
|
316
316
|
const doSpawn = options.spawnImpl ?? import_node_child_process.spawn;
|
|
317
|
-
await new Promise((
|
|
317
|
+
await new Promise((resolve13, reject) => {
|
|
318
318
|
const child = doSpawn(command.cmd, [...command.args, url], {
|
|
319
319
|
stdio: "ignore",
|
|
320
320
|
detached: true
|
|
@@ -322,7 +322,7 @@ async function openUrl(url, options = {}) {
|
|
|
322
322
|
child.once("error", reject);
|
|
323
323
|
child.once("spawn", () => {
|
|
324
324
|
child.unref();
|
|
325
|
-
|
|
325
|
+
resolve13();
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
}
|
|
@@ -560,7 +560,6 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
560
560
|
}
|
|
561
561
|
var SCOPE_PURPOSE = {
|
|
562
562
|
"platform:status:read": "read the platform fleet health and deployment snapshot",
|
|
563
|
-
"platform:chat:credential:write": "rotate the built-in Discussion responder credential",
|
|
564
563
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
565
564
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
566
565
|
"platform:runbook:write": "add or edit odla's operational runbooks",
|
|
@@ -2244,6 +2243,153 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2244
2243
|
else await appRestore(options);
|
|
2245
2244
|
}
|
|
2246
2245
|
|
|
2246
|
+
// src/brand-command.ts
|
|
2247
|
+
var import_promises2 = require("fs/promises");
|
|
2248
|
+
var import_node_path7 = require("path");
|
|
2249
|
+
|
|
2250
|
+
// src/brand-design-unpack.ts
|
|
2251
|
+
var import_node_zlib = require("zlib");
|
|
2252
|
+
var import_brand = require("@odla-ai/brand");
|
|
2253
|
+
var EXTENSIONS = {
|
|
2254
|
+
"image/png": "png",
|
|
2255
|
+
"image/jpeg": "jpg",
|
|
2256
|
+
"image/gif": "gif",
|
|
2257
|
+
"image/webp": "webp",
|
|
2258
|
+
"image/svg+xml": "svg",
|
|
2259
|
+
"image/avif": "avif",
|
|
2260
|
+
"font/woff2": "woff2",
|
|
2261
|
+
"font/woff": "woff",
|
|
2262
|
+
"font/ttf": "ttf",
|
|
2263
|
+
"font/otf": "otf",
|
|
2264
|
+
"text/javascript": "js",
|
|
2265
|
+
"application/javascript": "js",
|
|
2266
|
+
"text/css": "css",
|
|
2267
|
+
"text/html": "html",
|
|
2268
|
+
"application/json": "json"
|
|
2269
|
+
};
|
|
2270
|
+
var encode = (text2) => new TextEncoder().encode(text2);
|
|
2271
|
+
function assetFileName(uuid, mime) {
|
|
2272
|
+
const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
|
|
2273
|
+
return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
|
|
2274
|
+
}
|
|
2275
|
+
function decodePayload(data, compressed) {
|
|
2276
|
+
const raw = Buffer.from(data, "base64");
|
|
2277
|
+
return new Uint8Array(compressed ? (0, import_node_zlib.gunzipSync)(raw) : raw);
|
|
2278
|
+
}
|
|
2279
|
+
function rewriteReferences(template, assetPaths, pagePaths) {
|
|
2280
|
+
let out = template;
|
|
2281
|
+
for (const [uuid, path] of pagePaths) {
|
|
2282
|
+
out = out.split(`about:blank#${uuid}`).join(path);
|
|
2283
|
+
}
|
|
2284
|
+
for (const [uuid, path] of assetPaths) {
|
|
2285
|
+
out = out.split(uuid).join(path);
|
|
2286
|
+
}
|
|
2287
|
+
return out;
|
|
2288
|
+
}
|
|
2289
|
+
function tokensCss(digest) {
|
|
2290
|
+
if (Object.keys(digest.tokens.light).length === 0) return null;
|
|
2291
|
+
return (0, import_brand.renderTokensCss)(digest.tokens.light, { dark: digest.tokens.dark });
|
|
2292
|
+
}
|
|
2293
|
+
function unpackDesign(html) {
|
|
2294
|
+
const bundle = (0, import_brand.parseDesignBundle)(html);
|
|
2295
|
+
const manifest = (0, import_brand.readDesignManifest)(html);
|
|
2296
|
+
const digest = (0, import_brand.digestDesignBundle)(bundle);
|
|
2297
|
+
const pageUuids = new Set(bundle.pageOrder);
|
|
2298
|
+
const files = [];
|
|
2299
|
+
const failed = [];
|
|
2300
|
+
const assetPaths = /* @__PURE__ */ new Map();
|
|
2301
|
+
const pagePaths = /* @__PURE__ */ new Map();
|
|
2302
|
+
for (const [uuid, entry] of Object.entries(manifest)) {
|
|
2303
|
+
let bytes;
|
|
2304
|
+
try {
|
|
2305
|
+
bytes = decodePayload(entry.data, entry.compressed);
|
|
2306
|
+
} catch {
|
|
2307
|
+
failed.push(uuid);
|
|
2308
|
+
continue;
|
|
2309
|
+
}
|
|
2310
|
+
if (pageUuids.has(uuid)) {
|
|
2311
|
+
const path2 = `pages/${assetFileName(uuid, "text/html")}`;
|
|
2312
|
+
pagePaths.set(uuid, `./${path2}`);
|
|
2313
|
+
files.push({ path: path2, bytes });
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
const path = `assets/${assetFileName(uuid, entry.mime)}`;
|
|
2317
|
+
assetPaths.set(uuid, `./${path}`);
|
|
2318
|
+
files.push({ path, bytes });
|
|
2319
|
+
}
|
|
2320
|
+
files.push({
|
|
2321
|
+
path: "index.html",
|
|
2322
|
+
bytes: encode(rewriteReferences(bundle.template, assetPaths, pagePaths))
|
|
2323
|
+
});
|
|
2324
|
+
files.push({ path: "digest.json", bytes: encode(`${JSON.stringify(digest, null, 2)}
|
|
2325
|
+
`) });
|
|
2326
|
+
const css = tokensCss(digest);
|
|
2327
|
+
if (css !== null) files.push({ path: "tokens.css", bytes: encode(css) });
|
|
2328
|
+
if (bundle.thumbnailSvg) files.push({ path: "thumbnail.svg", bytes: encode(bundle.thumbnailSvg) });
|
|
2329
|
+
return { files, digest, failed };
|
|
2330
|
+
}
|
|
2331
|
+
function describeUnpack(result, outDir) {
|
|
2332
|
+
const { digest } = result;
|
|
2333
|
+
const lines = [
|
|
2334
|
+
`Unpacked ${digest.title ? `"${digest.title}"` : "design"} into ${outDir}`,
|
|
2335
|
+
` index.html the design, offline-runnable (${digest.templateBytes} bytes)`,
|
|
2336
|
+
` assets/ ${digest.assetCount} embedded files (${digest.assetBytes} bytes)`,
|
|
2337
|
+
` digest.json ${Object.keys(digest.tokens.light).length} --ui-* tokens, ${digest.props.length} props, ${digest.outline.length} headings`
|
|
2338
|
+
];
|
|
2339
|
+
if (result.files.some((f) => f.path === "tokens.css"))
|
|
2340
|
+
lines.push(" tokens.css the design's tokens as an @odla-ai/ui theme sheet");
|
|
2341
|
+
if (digest.fonts.length > 0) lines.push(`Typefaces: ${digest.fonts.join(", ")}`);
|
|
2342
|
+
if (result.failed.length > 0)
|
|
2343
|
+
lines.push(`WARNING: ${result.failed.length} embedded asset(s) could not be decoded.`);
|
|
2344
|
+
return lines;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/brand-command.ts
|
|
2348
|
+
var USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
2349
|
+
async function readBundle(source, deps) {
|
|
2350
|
+
if (source !== "-") return (0, import_promises2.readFile)((0, import_node_path7.resolve)(source), "utf8");
|
|
2351
|
+
const readStdin = deps.readStdin;
|
|
2352
|
+
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
2353
|
+
return readStdin();
|
|
2354
|
+
}
|
|
2355
|
+
async function writeAll(result, outDir) {
|
|
2356
|
+
for (const file of result.files) {
|
|
2357
|
+
const target = (0, import_node_path7.resolve)(outDir, file.path);
|
|
2358
|
+
await (0, import_promises2.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
|
|
2359
|
+
await (0, import_promises2.writeFile)(target, file.bytes);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
async function designUnpack(parsed, deps) {
|
|
2363
|
+
assertArgs(parsed, ["out", "json"], 4);
|
|
2364
|
+
const source = parsed.positionals[3];
|
|
2365
|
+
if (!source) throw new Error(USAGE);
|
|
2366
|
+
const outDir = (0, import_node_path7.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
2367
|
+
const result = unpackDesign(await readBundle(source, deps));
|
|
2368
|
+
await writeAll(result, outDir);
|
|
2369
|
+
const out = deps.stdout ?? console;
|
|
2370
|
+
if (parsed.options.json === true) {
|
|
2371
|
+
out.log(
|
|
2372
|
+
JSON.stringify({
|
|
2373
|
+
outDir,
|
|
2374
|
+
files: result.files.map((f) => f.path),
|
|
2375
|
+
failed: result.failed,
|
|
2376
|
+
digest: result.digest
|
|
2377
|
+
})
|
|
2378
|
+
);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
for (const line of describeUnpack(result, outDir)) out.log(line);
|
|
2382
|
+
}
|
|
2383
|
+
async function brandCommand(parsed, deps) {
|
|
2384
|
+
const subject = parsed.positionals[1];
|
|
2385
|
+
const action2 = parsed.positionals[2];
|
|
2386
|
+
if (subject === "design" && action2 === "unpack") {
|
|
2387
|
+
await designUnpack(parsed, deps);
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
throw new Error(USAGE);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2247
2393
|
// src/calendar-errors.ts
|
|
2248
2394
|
var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
|
|
2249
2395
|
"calendar_google_oauth_not_configured",
|
|
@@ -2517,8 +2663,8 @@ function credential(value2) {
|
|
|
2517
2663
|
// src/calendar-poll.ts
|
|
2518
2664
|
async function waitForCalendarPoll(milliseconds, signal) {
|
|
2519
2665
|
if (signal?.aborted) throw signal.reason ?? new Error("calendar connection aborted");
|
|
2520
|
-
await new Promise((
|
|
2521
|
-
const timer = setTimeout(
|
|
2666
|
+
await new Promise((resolve13, reject) => {
|
|
2667
|
+
const timer = setTimeout(resolve13, milliseconds);
|
|
2522
2668
|
signal?.addEventListener("abort", () => {
|
|
2523
2669
|
clearTimeout(timer);
|
|
2524
2670
|
reject(signal.reason ?? new Error("calendar connection aborted"));
|
|
@@ -2772,7 +2918,7 @@ function printGroup(out, heading, items) {
|
|
|
2772
2918
|
|
|
2773
2919
|
// src/config-operation-command.ts
|
|
2774
2920
|
var import_apps6 = require("@odla-ai/apps");
|
|
2775
|
-
var
|
|
2921
|
+
var import_node_path8 = require("path");
|
|
2776
2922
|
|
|
2777
2923
|
// src/version.ts
|
|
2778
2924
|
var import_node_fs9 = require("fs");
|
|
@@ -2811,9 +2957,9 @@ function canonicalValue(value2) {
|
|
|
2811
2957
|
}
|
|
2812
2958
|
if (Array.isArray(value2)) return value2.map(canonicalValue);
|
|
2813
2959
|
if (value2 && typeof value2 === "object") {
|
|
2814
|
-
const
|
|
2960
|
+
const record10 = value2;
|
|
2815
2961
|
return Object.fromEntries(
|
|
2816
|
-
Object.keys(
|
|
2962
|
+
Object.keys(record10).filter((key) => record10[key] !== void 0).sort().map((key) => [key, canonicalValue(record10[key])])
|
|
2817
2963
|
);
|
|
2818
2964
|
}
|
|
2819
2965
|
throw new TypeError("canonical JSON rejects unsupported values");
|
|
@@ -3151,7 +3297,7 @@ async function configOperationWait(options) {
|
|
|
3151
3297
|
assertOperationId(options.operationId);
|
|
3152
3298
|
const cfg = await loadProjectConfig(options.configPath);
|
|
3153
3299
|
const client = await operationClient(cfg, options, "wait");
|
|
3154
|
-
const wait2 = options.pollWait ?? ((ms) => new Promise((
|
|
3300
|
+
const wait2 = options.pollWait ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
|
|
3155
3301
|
const now = () => (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
3156
3302
|
const deadline = now() + (options.timeoutSeconds ?? DEFAULT_WAIT_SECONDS) * 1e3;
|
|
3157
3303
|
const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1e3;
|
|
@@ -3182,7 +3328,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3182
3328
|
platform: cfg.platformUrl,
|
|
3183
3329
|
scope: "app:config:write",
|
|
3184
3330
|
token: options.token,
|
|
3185
|
-
tokenFile: (0,
|
|
3331
|
+
tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3186
3332
|
rootDir: cfg.rootDir,
|
|
3187
3333
|
email: options.email,
|
|
3188
3334
|
open: options.open,
|
|
@@ -3237,7 +3383,7 @@ function record3(value2) {
|
|
|
3237
3383
|
|
|
3238
3384
|
// src/config-reconcile-command.ts
|
|
3239
3385
|
var import_apps8 = require("@odla-ai/apps");
|
|
3240
|
-
var
|
|
3386
|
+
var import_node_path9 = require("path");
|
|
3241
3387
|
|
|
3242
3388
|
// src/config-reconcile.ts
|
|
3243
3389
|
var import_apps7 = require("@odla-ai/apps");
|
|
@@ -3533,7 +3679,7 @@ async function inspectConfig(options) {
|
|
|
3533
3679
|
platform: cfg.platformUrl,
|
|
3534
3680
|
scope: "app:config:read",
|
|
3535
3681
|
token: options.token,
|
|
3536
|
-
tokenFile: (0,
|
|
3682
|
+
tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3537
3683
|
rootDir: cfg.rootDir,
|
|
3538
3684
|
email: options.email,
|
|
3539
3685
|
open: options.open,
|
|
@@ -3666,12 +3812,12 @@ function quoteArg2(value2) {
|
|
|
3666
3812
|
// src/doctor-checks.ts
|
|
3667
3813
|
var import_node_child_process3 = require("child_process");
|
|
3668
3814
|
var import_node_fs12 = require("fs");
|
|
3669
|
-
var
|
|
3815
|
+
var import_node_path11 = require("path");
|
|
3670
3816
|
|
|
3671
3817
|
// src/wrangler.ts
|
|
3672
3818
|
var import_node_child_process2 = require("child_process");
|
|
3673
3819
|
var import_node_fs11 = require("fs");
|
|
3674
|
-
var
|
|
3820
|
+
var import_node_path10 = require("path");
|
|
3675
3821
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
3676
3822
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
3677
3823
|
let stdout = "";
|
|
@@ -3685,7 +3831,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
3685
3831
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
3686
3832
|
function findWranglerConfig(rootDir) {
|
|
3687
3833
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
3688
|
-
const path = (0,
|
|
3834
|
+
const path = (0, import_node_path10.join)(rootDir, name);
|
|
3689
3835
|
if ((0, import_node_fs11.existsSync)(path)) return path;
|
|
3690
3836
|
}
|
|
3691
3837
|
return null;
|
|
@@ -3798,10 +3944,10 @@ function wranglerWarnings(rootDir) {
|
|
|
3798
3944
|
for (const { label, block } of blocks) {
|
|
3799
3945
|
const assets = block.assets;
|
|
3800
3946
|
if (assets?.directory) {
|
|
3801
|
-
const dir = (0,
|
|
3802
|
-
if (dir === (0,
|
|
3947
|
+
const dir = (0, import_node_path11.resolve)(rootDir, assets.directory);
|
|
3948
|
+
if (dir === (0, import_node_path11.resolve)(rootDir)) {
|
|
3803
3949
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
3804
|
-
} else if ((0, import_node_fs12.existsSync)((0,
|
|
3950
|
+
} else if ((0, import_node_fs12.existsSync)((0, import_node_path11.join)(dir, "node_modules"))) {
|
|
3805
3951
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
3806
3952
|
}
|
|
3807
3953
|
}
|
|
@@ -3836,7 +3982,7 @@ function o11yProjectWarnings(rootDir) {
|
|
|
3836
3982
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
3837
3983
|
return warnings;
|
|
3838
3984
|
}
|
|
3839
|
-
const main = typeof config.main === "string" ? (0,
|
|
3985
|
+
const main = typeof config.main === "string" ? (0, import_node_path11.resolve)(rootDir, config.main) : null;
|
|
3840
3986
|
if (!main || !(0, import_node_fs12.existsSync)(main)) {
|
|
3841
3987
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
3842
3988
|
} else {
|
|
@@ -3866,7 +4012,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
3866
4012
|
}
|
|
3867
4013
|
function readPackageJson(rootDir) {
|
|
3868
4014
|
try {
|
|
3869
|
-
return JSON.parse((0, import_node_fs12.readFileSync)((0,
|
|
4015
|
+
return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(rootDir, "package.json"), "utf8"));
|
|
3870
4016
|
} catch {
|
|
3871
4017
|
return null;
|
|
3872
4018
|
}
|
|
@@ -4094,12 +4240,12 @@ function harnessOption(value2, flag) {
|
|
|
4094
4240
|
|
|
4095
4241
|
// src/init.ts
|
|
4096
4242
|
var import_node_fs13 = require("fs");
|
|
4097
|
-
var
|
|
4243
|
+
var import_node_path12 = require("path");
|
|
4098
4244
|
var import_apps9 = require("@odla-ai/apps");
|
|
4099
4245
|
function initProject(options) {
|
|
4100
4246
|
const out = options.stdout ?? console;
|
|
4101
|
-
const rootDir = (0,
|
|
4102
|
-
const configPath = (0,
|
|
4247
|
+
const rootDir = (0, import_node_path12.resolve)(options.rootDir ?? process.cwd());
|
|
4248
|
+
const configPath = (0, import_node_path12.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4103
4249
|
if ((0, import_node_fs13.existsSync)(configPath) && !options.force) {
|
|
4104
4250
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
4105
4251
|
}
|
|
@@ -4116,12 +4262,12 @@ function initProject(options) {
|
|
|
4116
4262
|
}
|
|
4117
4263
|
}
|
|
4118
4264
|
const aiProvider = options.aiProvider ?? "anthropic";
|
|
4119
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4120
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4121
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4265
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(configPath), { recursive: true });
|
|
4266
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
4267
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, ".odla"), { recursive: true });
|
|
4122
4268
|
(0, import_node_fs13.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
4123
|
-
writeIfMissing((0,
|
|
4124
|
-
writeIfMissing((0,
|
|
4269
|
+
writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
4270
|
+
writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
4125
4271
|
ensureGitignore(rootDir);
|
|
4126
4272
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
4127
4273
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
@@ -4362,7 +4508,7 @@ async function resolveVaultWrite(options) {
|
|
|
4362
4508
|
// src/skill.ts
|
|
4363
4509
|
var import_node_fs14 = require("fs");
|
|
4364
4510
|
var import_node_os2 = require("os");
|
|
4365
|
-
var
|
|
4511
|
+
var import_node_path13 = require("path");
|
|
4366
4512
|
var import_node_url2 = require("url");
|
|
4367
4513
|
|
|
4368
4514
|
// src/skill-adapters.ts
|
|
@@ -4441,8 +4587,8 @@ function installSkill(options = {}) {
|
|
|
4441
4587
|
const files = listFiles(sourceDir);
|
|
4442
4588
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
4443
4589
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
4444
|
-
const root = (0,
|
|
4445
|
-
const home = (0,
|
|
4590
|
+
const root = (0, import_node_path13.resolve)(options.dir ?? process.cwd());
|
|
4591
|
+
const home = (0, import_node_path13.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
|
|
4446
4592
|
const plans = /* @__PURE__ */ new Map();
|
|
4447
4593
|
const targets = /* @__PURE__ */ new Map();
|
|
4448
4594
|
const rememberTarget = (harness, target) => {
|
|
@@ -4456,48 +4602,48 @@ function installSkill(options = {}) {
|
|
|
4456
4602
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
4457
4603
|
};
|
|
4458
4604
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
4459
|
-
for (const rel of files) plan((0,
|
|
4605
|
+
for (const rel of files) plan((0, import_node_path13.join)(targetDir2, rel), (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(sourceDir, rel), "utf8"), false, boundary);
|
|
4460
4606
|
};
|
|
4461
4607
|
let targetDir;
|
|
4462
4608
|
if (options.global) {
|
|
4463
|
-
const claudeRoot = (0,
|
|
4464
|
-
const codexRoot = (0,
|
|
4609
|
+
const claudeRoot = (0, import_node_path13.join)(home, ".claude", "skills");
|
|
4610
|
+
const codexRoot = (0, import_node_path13.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path13.join)(home, ".codex"), "skills");
|
|
4465
4611
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
4466
4612
|
for (const harness of harnesses) {
|
|
4467
4613
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
4468
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
4614
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path13.dirname)((0, import_node_path13.dirname)(codexRoot)));
|
|
4469
4615
|
rememberTarget(harness, skillRoot);
|
|
4470
4616
|
}
|
|
4471
4617
|
} else {
|
|
4472
|
-
const sharedRoot = (0,
|
|
4618
|
+
const sharedRoot = (0, import_node_path13.join)(root, ".agents", "skills");
|
|
4473
4619
|
planSkillTree(sharedRoot);
|
|
4474
|
-
const claudeRoot = (0,
|
|
4620
|
+
const claudeRoot = (0, import_node_path13.join)(root, ".claude", "skills");
|
|
4475
4621
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
4476
4622
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
4477
4623
|
if (harnesses.includes("claude")) {
|
|
4478
4624
|
for (const skill of skillNames(files)) {
|
|
4479
|
-
const canonical = (0, import_node_fs14.readFileSync)((0,
|
|
4480
|
-
plan((0,
|
|
4625
|
+
const canonical = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
4626
|
+
plan((0, import_node_path13.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
|
|
4481
4627
|
}
|
|
4482
4628
|
rememberTarget("claude", claudeRoot);
|
|
4483
4629
|
}
|
|
4484
4630
|
if (harnesses.includes("cursor")) {
|
|
4485
|
-
const cursorRule = (0,
|
|
4631
|
+
const cursorRule = (0, import_node_path13.join)(root, ".cursor", "rules", "odla.mdc");
|
|
4486
4632
|
plan(cursorRule, CURSOR_RULE);
|
|
4487
4633
|
rememberTarget("cursor", cursorRule);
|
|
4488
4634
|
}
|
|
4489
4635
|
if (harnesses.includes("agents")) {
|
|
4490
|
-
const agentsFile = (0,
|
|
4636
|
+
const agentsFile = (0, import_node_path13.join)(root, "AGENTS.md");
|
|
4491
4637
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4492
4638
|
rememberTarget("agents", agentsFile);
|
|
4493
4639
|
}
|
|
4494
4640
|
if (harnesses.includes("copilot")) {
|
|
4495
|
-
const copilotFile = (0,
|
|
4641
|
+
const copilotFile = (0, import_node_path13.join)(root, ".github", "copilot-instructions.md");
|
|
4496
4642
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4497
4643
|
rememberTarget("copilot", copilotFile);
|
|
4498
4644
|
}
|
|
4499
4645
|
if (harnesses.includes("gemini")) {
|
|
4500
|
-
const geminiFile = (0,
|
|
4646
|
+
const geminiFile = (0, import_node_path13.join)(root, "GEMINI.md");
|
|
4501
4647
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4502
4648
|
rememberTarget("gemini", geminiFile);
|
|
4503
4649
|
}
|
|
@@ -4533,7 +4679,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
4533
4679
|
}
|
|
4534
4680
|
for (const file of plans.values()) {
|
|
4535
4681
|
if (!(0, import_node_fs14.existsSync)(file.target) || (0, import_node_fs14.readFileSync)(file.target, "utf8") !== file.content) {
|
|
4536
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
4682
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(file.target), { recursive: true });
|
|
4537
4683
|
(0, import_node_fs14.writeFileSync)(file.target, file.content);
|
|
4538
4684
|
}
|
|
4539
4685
|
}
|
|
@@ -4553,7 +4699,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
4553
4699
|
};
|
|
4554
4700
|
}
|
|
4555
4701
|
function pathsUnder(root, paths) {
|
|
4556
|
-
return [...paths].map((path) => (0,
|
|
4702
|
+
return [...paths].map((path) => (0, import_node_path13.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path13.sep}`) && !(0, import_node_path13.isAbsolute)(path)).sort();
|
|
4557
4703
|
}
|
|
4558
4704
|
function normalizeHarnesses(values, global) {
|
|
4559
4705
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -4598,13 +4744,13 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
4598
4744
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
4599
4745
|
}
|
|
4600
4746
|
function symlinkedComponent(boundary, target) {
|
|
4601
|
-
const rel = (0,
|
|
4602
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
4747
|
+
const rel = (0, import_node_path13.relative)(boundary, target);
|
|
4748
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path13.sep}`) || (0, import_node_path13.isAbsolute)(rel)) {
|
|
4603
4749
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
4604
4750
|
}
|
|
4605
4751
|
let current = boundary;
|
|
4606
|
-
for (const part of rel.split(
|
|
4607
|
-
current = (0,
|
|
4752
|
+
for (const part of rel.split(import_node_path13.sep).filter(Boolean)) {
|
|
4753
|
+
current = (0, import_node_path13.join)(current, part);
|
|
4608
4754
|
try {
|
|
4609
4755
|
if ((0, import_node_fs14.lstatSync)(current).isSymbolicLink()) return current;
|
|
4610
4756
|
} catch (error) {
|
|
@@ -4621,9 +4767,9 @@ function listFiles(dir) {
|
|
|
4621
4767
|
const results = [];
|
|
4622
4768
|
const walk = (current) => {
|
|
4623
4769
|
for (const entry of (0, import_node_fs14.readdirSync)(current, { withFileTypes: true })) {
|
|
4624
|
-
const path = (0,
|
|
4770
|
+
const path = (0, import_node_path13.join)(current, entry.name);
|
|
4625
4771
|
if (entry.isDirectory()) walk(path);
|
|
4626
|
-
else results.push((0,
|
|
4772
|
+
else results.push((0, import_node_path13.relative)(dir, path));
|
|
4627
4773
|
}
|
|
4628
4774
|
};
|
|
4629
4775
|
walk(dir);
|
|
@@ -4935,7 +5081,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
4935
5081
|
// src/code-connect.ts
|
|
4936
5082
|
var import_node_fs15 = require("fs");
|
|
4937
5083
|
var import_node_os4 = require("os");
|
|
4938
|
-
var
|
|
5084
|
+
var import_node_path15 = require("path");
|
|
4939
5085
|
|
|
4940
5086
|
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
4941
5087
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -5021,15 +5167,15 @@ function encodeAgentInput(message2) {
|
|
|
5021
5167
|
// ../harness/dist/chunk-PHXQH4YM.js
|
|
5022
5168
|
var import_child_process = require("child_process");
|
|
5023
5169
|
var import_fs = require("fs");
|
|
5024
|
-
var
|
|
5170
|
+
var import_promises3 = require("fs/promises");
|
|
5025
5171
|
var import_path = require("path");
|
|
5026
5172
|
var import_process = require("process");
|
|
5027
|
-
var
|
|
5173
|
+
var import_promises4 = require("fs/promises");
|
|
5028
5174
|
var import_os = require("os");
|
|
5029
5175
|
var import_path2 = require("path");
|
|
5030
5176
|
var import_child_process2 = require("child_process");
|
|
5031
5177
|
var import_path3 = require("path");
|
|
5032
|
-
var
|
|
5178
|
+
var import_promises5 = require("fs/promises");
|
|
5033
5179
|
var import_os2 = require("os");
|
|
5034
5180
|
var import_path4 = require("path");
|
|
5035
5181
|
var import_child_process3 = require("child_process");
|
|
@@ -5040,7 +5186,7 @@ function assertPinnedImage(image) {
|
|
|
5040
5186
|
async function commandAvailable(engine) {
|
|
5041
5187
|
for (const directory of (process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean)) {
|
|
5042
5188
|
try {
|
|
5043
|
-
await (0,
|
|
5189
|
+
await (0, import_promises3.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
|
|
5044
5190
|
return true;
|
|
5045
5191
|
} catch {
|
|
5046
5192
|
}
|
|
@@ -5326,18 +5472,18 @@ async function gitBlobs(cwd, entries, maxBytes) {
|
|
|
5326
5472
|
}
|
|
5327
5473
|
async function materializeGitTree(source, commitSha, options = {}) {
|
|
5328
5474
|
if (!/^[0-9a-f]{40}$/.test(commitSha)) throw new TypeError("Git tree requires an exact commit SHA");
|
|
5329
|
-
const sourceDir = await (0,
|
|
5475
|
+
const sourceDir = await (0, import_promises4.realpath)((0, import_path2.resolve)(source));
|
|
5330
5476
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5331
5477
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
5332
5478
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
5333
|
-
const entries = inventory.flatMap((
|
|
5334
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
5479
|
+
const entries = inventory.flatMap((record10) => {
|
|
5480
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record10);
|
|
5335
5481
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
5336
5482
|
});
|
|
5337
5483
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
5338
|
-
const root = await (0,
|
|
5484
|
+
const root = await (0, import_promises4.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
|
|
5339
5485
|
const targetRoot = (0, import_path2.join)(root, "source");
|
|
5340
|
-
await (0,
|
|
5486
|
+
await (0, import_promises4.mkdir)(targetRoot);
|
|
5341
5487
|
let byteCount = 0;
|
|
5342
5488
|
try {
|
|
5343
5489
|
const blobs = await gitBlobs(sourceDir, entries, maxBytes);
|
|
@@ -5347,18 +5493,18 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
5347
5493
|
if (byteCount > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
|
|
5348
5494
|
const target = (0, import_path2.resolve)(targetRoot, entry.path);
|
|
5349
5495
|
if (!target.startsWith(`${(0, import_path2.resolve)(targetRoot)}${import_path2.sep}`)) throw new TypeError("Git tree path escapes workspace");
|
|
5350
|
-
await (0,
|
|
5351
|
-
await (0,
|
|
5496
|
+
await (0, import_promises4.mkdir)((0, import_path2.resolve)(target, ".."), { recursive: true });
|
|
5497
|
+
await (0, import_promises4.writeFile)(target, content2, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
|
|
5352
5498
|
}
|
|
5353
5499
|
return {
|
|
5354
5500
|
root,
|
|
5355
5501
|
sourceDir: targetRoot,
|
|
5356
5502
|
fileCount: entries.length,
|
|
5357
5503
|
byteCount,
|
|
5358
|
-
cleanup: () => (0,
|
|
5504
|
+
cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
|
|
5359
5505
|
};
|
|
5360
5506
|
} catch (error) {
|
|
5361
|
-
await (0,
|
|
5507
|
+
await (0, import_promises4.rm)(root, { recursive: true, force: true });
|
|
5362
5508
|
throw error;
|
|
5363
5509
|
}
|
|
5364
5510
|
}
|
|
@@ -5366,7 +5512,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5366
5512
|
const files = [];
|
|
5367
5513
|
let bytes = 0;
|
|
5368
5514
|
const walk = async (dir) => {
|
|
5369
|
-
for (const entry of await (0,
|
|
5515
|
+
for (const entry of await (0, import_promises5.readdir)(dir, { withFileTypes: true })) {
|
|
5370
5516
|
if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
5371
5517
|
if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
|
|
5372
5518
|
const path = (0, import_path4.join)(dir, entry.name);
|
|
@@ -5376,7 +5522,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5376
5522
|
continue;
|
|
5377
5523
|
}
|
|
5378
5524
|
if (!entry.isFile()) continue;
|
|
5379
|
-
const metadata2 = await (0,
|
|
5525
|
+
const metadata2 = await (0, import_promises5.stat)(path);
|
|
5380
5526
|
bytes += metadata2.size;
|
|
5381
5527
|
if (files.length + 1 > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
5382
5528
|
if (bytes > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
|
|
@@ -5425,7 +5571,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5425
5571
|
if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
|
|
5426
5572
|
let metadata2;
|
|
5427
5573
|
try {
|
|
5428
|
-
metadata2 = await (0,
|
|
5574
|
+
metadata2 = await (0, import_promises5.lstat)(source);
|
|
5429
5575
|
} catch (error) {
|
|
5430
5576
|
if (error.code === "ENOENT") continue;
|
|
5431
5577
|
throw error;
|
|
@@ -5440,9 +5586,9 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5440
5586
|
async function copyTree(files, destination) {
|
|
5441
5587
|
for (const file of files) {
|
|
5442
5588
|
const target = (0, import_path4.join)(destination, file.relativePath);
|
|
5443
|
-
await (0,
|
|
5444
|
-
await (0,
|
|
5445
|
-
await (0,
|
|
5589
|
+
await (0, import_promises5.mkdir)((0, import_path4.resolve)(target, ".."), { recursive: true });
|
|
5590
|
+
await (0, import_promises5.copyFile)(file.source, target);
|
|
5591
|
+
await (0, import_promises5.chmod)(target, file.mode);
|
|
5446
5592
|
}
|
|
5447
5593
|
}
|
|
5448
5594
|
async function captureGitDiff(root, maxBytes) {
|
|
@@ -5479,13 +5625,13 @@ async function captureGitDiff(root, maxBytes) {
|
|
|
5479
5625
|
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");
|
|
5480
5626
|
}
|
|
5481
5627
|
async function stageWorkspace(source, options = {}) {
|
|
5482
|
-
const sourceDir = await (0,
|
|
5483
|
-
const sourceStat = await (0,
|
|
5628
|
+
const sourceDir = await (0, import_promises5.realpath)((0, import_path4.resolve)(source));
|
|
5629
|
+
const sourceStat = await (0, import_promises5.stat)(sourceDir);
|
|
5484
5630
|
if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
|
|
5485
|
-
const root = await (0,
|
|
5631
|
+
const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
|
|
5486
5632
|
const baselineDir = (0, import_path4.join)(root, "baseline");
|
|
5487
5633
|
const workspaceDir = (0, import_path4.join)(root, "workspace");
|
|
5488
|
-
await Promise.all([(0,
|
|
5634
|
+
await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
|
|
5489
5635
|
try {
|
|
5490
5636
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5491
5637
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
@@ -5498,26 +5644,26 @@ async function stageWorkspace(source, options = {}) {
|
|
|
5498
5644
|
fileCount: files.length,
|
|
5499
5645
|
byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
|
|
5500
5646
|
patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
|
|
5501
|
-
cleanup: () => (0,
|
|
5647
|
+
cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
|
|
5502
5648
|
};
|
|
5503
5649
|
} catch (error) {
|
|
5504
|
-
await (0,
|
|
5650
|
+
await (0, import_promises5.rm)(root, { recursive: true, force: true });
|
|
5505
5651
|
throw error;
|
|
5506
5652
|
}
|
|
5507
5653
|
}
|
|
5508
5654
|
async function stageWorkspacePair(baselineSource, workspaceSource, options = {}) {
|
|
5509
|
-
const baselineDirSource = await (0,
|
|
5510
|
-
const workspaceDirSource = await (0,
|
|
5655
|
+
const baselineDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(baselineSource));
|
|
5656
|
+
const workspaceDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(workspaceSource));
|
|
5511
5657
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5512
5658
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
5513
5659
|
const [baselineFiles, workspaceFiles] = await Promise.all([
|
|
5514
5660
|
sourceFiles(baselineDirSource, maxFiles, maxBytes),
|
|
5515
5661
|
sourceFiles(workspaceDirSource, maxFiles, maxBytes)
|
|
5516
5662
|
]);
|
|
5517
|
-
const root = await (0,
|
|
5663
|
+
const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
|
|
5518
5664
|
const baselineDir = (0, import_path4.join)(root, "baseline");
|
|
5519
5665
|
const workspaceDir = (0, import_path4.join)(root, "workspace");
|
|
5520
|
-
await Promise.all([(0,
|
|
5666
|
+
await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
|
|
5521
5667
|
try {
|
|
5522
5668
|
await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
|
|
5523
5669
|
return {
|
|
@@ -5527,17 +5673,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
5527
5673
|
fileCount: workspaceFiles.length,
|
|
5528
5674
|
byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
|
|
5529
5675
|
patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
|
|
5530
|
-
cleanup: () => (0,
|
|
5676
|
+
cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
|
|
5531
5677
|
};
|
|
5532
5678
|
} catch (error) {
|
|
5533
|
-
await (0,
|
|
5679
|
+
await (0, import_promises5.rm)(root, { recursive: true, force: true });
|
|
5534
5680
|
throw error;
|
|
5535
5681
|
}
|
|
5536
5682
|
}
|
|
5537
5683
|
|
|
5538
5684
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
5539
5685
|
var import_crypto = require("crypto");
|
|
5540
|
-
var
|
|
5686
|
+
var import_promises6 = require("fs/promises");
|
|
5541
5687
|
var import_path5 = require("path");
|
|
5542
5688
|
|
|
5543
5689
|
// ../camel/dist/chunk-7FHPOQVP.js
|
|
@@ -5579,8 +5725,8 @@ function normalize(value2) {
|
|
|
5579
5725
|
if (Array.isArray(value2)) return value2.map(normalize);
|
|
5580
5726
|
if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
|
|
5581
5727
|
if (typeof value2 === "object") {
|
|
5582
|
-
const
|
|
5583
|
-
return Object.fromEntries(Object.keys(
|
|
5728
|
+
const record10 = value2;
|
|
5729
|
+
return Object.fromEntries(Object.keys(record10).filter((key) => record10[key] !== void 0).sort().map((key) => [key, normalize(record10[key])]));
|
|
5584
5730
|
}
|
|
5585
5731
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
5586
5732
|
}
|
|
@@ -5874,19 +6020,19 @@ function validateSnapshot(snapshot, limits) {
|
|
|
5874
6020
|
|
|
5875
6021
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
5876
6022
|
var import_child_process4 = require("child_process");
|
|
5877
|
-
var
|
|
6023
|
+
var import_promises7 = require("fs/promises");
|
|
5878
6024
|
var import_path6 = require("path");
|
|
5879
6025
|
var import_child_process5 = require("child_process");
|
|
5880
6026
|
var import_process2 = require("process");
|
|
5881
6027
|
var import_crypto2 = require("crypto");
|
|
5882
6028
|
var import_crypto3 = require("crypto");
|
|
5883
6029
|
var import_fs2 = require("fs");
|
|
5884
|
-
var import_promises7 = require("fs/promises");
|
|
5885
|
-
var import_path7 = require("path");
|
|
5886
6030
|
var import_promises8 = require("fs/promises");
|
|
6031
|
+
var import_path7 = require("path");
|
|
6032
|
+
var import_promises9 = require("fs/promises");
|
|
5887
6033
|
var import_os3 = require("os");
|
|
5888
6034
|
var import_path8 = require("path");
|
|
5889
|
-
var
|
|
6035
|
+
var import_promises10 = require("fs/promises");
|
|
5890
6036
|
var import_path9 = require("path");
|
|
5891
6037
|
|
|
5892
6038
|
// ../camel/dist/chunk-LAXU2AVK.js
|
|
@@ -6170,7 +6316,7 @@ var import_crypto4 = require("crypto");
|
|
|
6170
6316
|
async function digestStagedWorkspace(root, limits) {
|
|
6171
6317
|
const files = [];
|
|
6172
6318
|
const walk = async (directory) => {
|
|
6173
|
-
const entries = await (0,
|
|
6319
|
+
const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
|
|
6174
6320
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
6175
6321
|
if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
|
|
6176
6322
|
const target = (0, import_path5.resolve)(directory, entry.name);
|
|
@@ -6185,7 +6331,7 @@ async function digestStagedWorkspace(root, limits) {
|
|
|
6185
6331
|
const hash = (0, import_crypto.createHash)("sha256");
|
|
6186
6332
|
let bytes = 0;
|
|
6187
6333
|
for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
|
|
6188
|
-
const content2 = await (0,
|
|
6334
|
+
const content2 = await (0, import_promises6.readFile)(file.target);
|
|
6189
6335
|
bytes += Buffer.byteLength(file.path) + content2.byteLength;
|
|
6190
6336
|
if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
|
|
6191
6337
|
hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content2.byteLength}:`);
|
|
@@ -6511,7 +6657,7 @@ async function applyCodePatch(workspaceDir, patch2, paths) {
|
|
|
6511
6657
|
await gitApply(workspaceDir, patch2, false);
|
|
6512
6658
|
for (const path of paths) {
|
|
6513
6659
|
try {
|
|
6514
|
-
const info = await (0,
|
|
6660
|
+
const info = await (0, import_promises7.lstat)(resolveCodePath(workspaceDir, path));
|
|
6515
6661
|
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
|
|
6516
6662
|
throw new TypeError("patch created a non-regular workspace entry");
|
|
6517
6663
|
}
|
|
@@ -6802,7 +6948,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
6802
6948
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
6803
6949
|
try {
|
|
6804
6950
|
const path = (0, import_path7.join)(workspaceDir, artifact.path);
|
|
6805
|
-
const info = await (0,
|
|
6951
|
+
const info = await (0, import_promises8.lstat)(path);
|
|
6806
6952
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
6807
6953
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
6808
6954
|
} else if (info.size > artifact.maximumBytes) {
|
|
@@ -6983,9 +7129,9 @@ var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_mod
|
|
|
6983
7129
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
6984
7130
|
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.tmpdir)()) {
|
|
6985
7131
|
if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
|
|
6986
|
-
const root = await (0,
|
|
7132
|
+
const root = await (0, import_promises9.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
|
|
6987
7133
|
const sourceDir = (0, import_path8.join)(root, "source");
|
|
6988
|
-
await (0,
|
|
7134
|
+
await (0, import_promises9.mkdir)(sourceDir);
|
|
6989
7135
|
const seen = /* @__PURE__ */ new Set();
|
|
6990
7136
|
let bytes = 0;
|
|
6991
7137
|
try {
|
|
@@ -6997,8 +7143,8 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
|
|
|
6997
7143
|
if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
|
|
6998
7144
|
const target = (0, import_path8.resolve)(sourceDir, file.path);
|
|
6999
7145
|
if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
7000
|
-
await (0,
|
|
7001
|
-
await (0,
|
|
7146
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7147
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
7002
7148
|
}
|
|
7003
7149
|
for (const reference of snapshot.references ?? []) {
|
|
7004
7150
|
validateAlias(reference.alias);
|
|
@@ -7012,13 +7158,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
|
|
|
7012
7158
|
if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
|
|
7013
7159
|
const target = (0, import_path8.resolve)(sourceDir, path);
|
|
7014
7160
|
if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
7015
|
-
await (0,
|
|
7016
|
-
await (0,
|
|
7161
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7162
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
7017
7163
|
}
|
|
7018
7164
|
}
|
|
7019
|
-
return { sourceDir, cleanup: () => (0,
|
|
7165
|
+
return { sourceDir, cleanup: () => (0, import_promises9.rm)(root, { recursive: true, force: true }) };
|
|
7020
7166
|
} catch (cause) {
|
|
7021
|
-
await (0,
|
|
7167
|
+
await (0, import_promises9.rm)(root, { recursive: true, force: true });
|
|
7022
7168
|
throw cause;
|
|
7023
7169
|
}
|
|
7024
7170
|
}
|
|
@@ -7039,8 +7185,8 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
7039
7185
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
7040
7186
|
const target = (0, import_path8.resolve)(root, path);
|
|
7041
7187
|
if (!target.startsWith(`${(0, import_path8.resolve)(root)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
7042
|
-
await (0,
|
|
7043
|
-
await (0,
|
|
7188
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7189
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
7044
7190
|
}
|
|
7045
7191
|
}
|
|
7046
7192
|
}
|
|
@@ -7226,11 +7372,11 @@ async function read(context, request2, options, policy) {
|
|
|
7226
7372
|
const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
|
|
7227
7373
|
if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
|
|
7228
7374
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
7229
|
-
const info = await (0,
|
|
7375
|
+
const info = await (0, import_promises10.stat)(target);
|
|
7230
7376
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
7231
7377
|
throw new TypeError("file is not a bounded regular source file");
|
|
7232
7378
|
}
|
|
7233
|
-
const source = await (0,
|
|
7379
|
+
const source = await (0, import_promises10.readFile)(target);
|
|
7234
7380
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
7235
7381
|
const lines = source.toString("utf8").split("\n");
|
|
7236
7382
|
const content2 = lines.slice(startLine - 1, endLine).join("\n");
|
|
@@ -7307,7 +7453,7 @@ function policyContext(context, request2, options, extra) {
|
|
|
7307
7453
|
async function registeredFiles(root, limit) {
|
|
7308
7454
|
const paths = [];
|
|
7309
7455
|
const walk = async (directory) => {
|
|
7310
|
-
for (const entry of await (0,
|
|
7456
|
+
for (const entry of await (0, import_promises10.readdir)(directory, { withFileTypes: true })) {
|
|
7311
7457
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
7312
7458
|
const target = (0, import_path9.resolve)(directory, entry.name);
|
|
7313
7459
|
if (entry.isDirectory()) await walk(target);
|
|
@@ -7906,8 +8052,8 @@ function hostedPollTimeout(value2 = 10 * 6e4) {
|
|
|
7906
8052
|
}
|
|
7907
8053
|
async function waitForHostedPoll(milliseconds, signal) {
|
|
7908
8054
|
if (signal?.aborted) throw signal.reason ?? new DOMException("aborted", "AbortError");
|
|
7909
|
-
await new Promise((
|
|
7910
|
-
const timer = setTimeout(
|
|
8055
|
+
await new Promise((resolve13, reject) => {
|
|
8056
|
+
const timer = setTimeout(resolve13, milliseconds);
|
|
7911
8057
|
signal?.addEventListener("abort", () => {
|
|
7912
8058
|
clearTimeout(timer);
|
|
7913
8059
|
reject(signal.reason ?? new DOMException("aborted", "AbortError"));
|
|
@@ -8092,9 +8238,9 @@ function digestText(value2) {
|
|
|
8092
8238
|
// src/code-images.ts
|
|
8093
8239
|
var import_node_child_process6 = require("child_process");
|
|
8094
8240
|
var import_node_crypto3 = require("crypto");
|
|
8095
|
-
var
|
|
8241
|
+
var import_promises11 = require("fs/promises");
|
|
8096
8242
|
var import_node_os3 = require("os");
|
|
8097
|
-
var
|
|
8243
|
+
var import_node_path14 = require("path");
|
|
8098
8244
|
var import_node_url3 = require("url");
|
|
8099
8245
|
|
|
8100
8246
|
// src/code-runtime-config.ts
|
|
@@ -8174,16 +8320,16 @@ function embeddedPiAssetPath() {
|
|
|
8174
8320
|
return (0, import_node_url3.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
|
|
8175
8321
|
}
|
|
8176
8322
|
async function embeddedPiImageName() {
|
|
8177
|
-
const bundle = await (0,
|
|
8323
|
+
const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
|
|
8178
8324
|
throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
|
|
8179
8325
|
});
|
|
8180
8326
|
return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
|
|
8181
8327
|
}
|
|
8182
8328
|
async function buildEmbeddedPiImage(engine, image, run) {
|
|
8183
|
-
const context = await (0,
|
|
8329
|
+
const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
|
|
8184
8330
|
try {
|
|
8185
|
-
await (0,
|
|
8186
|
-
await (0,
|
|
8331
|
+
await (0, import_promises11.copyFile)(embeddedPiAssetPath(), (0, import_node_path14.join)(context, "pi-agent.js"));
|
|
8332
|
+
await (0, import_promises11.writeFile)((0, import_node_path14.join)(context, "Dockerfile"), [
|
|
8187
8333
|
`FROM ${CODE_NODE_IMAGE}`,
|
|
8188
8334
|
"COPY pi-agent.js /opt/odla/pi-agent.js",
|
|
8189
8335
|
"WORKDIR /workspace",
|
|
@@ -8192,14 +8338,14 @@ async function buildEmbeddedPiImage(engine, image, run) {
|
|
|
8192
8338
|
].join("\n"), { mode: 384 });
|
|
8193
8339
|
await run(engine, ["build", "--tag", image, context], "inherit");
|
|
8194
8340
|
} finally {
|
|
8195
|
-
await (0,
|
|
8341
|
+
await (0, import_promises11.rm)(context, { recursive: true, force: true });
|
|
8196
8342
|
}
|
|
8197
8343
|
}
|
|
8198
8344
|
|
|
8199
8345
|
// src/code-connect.ts
|
|
8200
8346
|
async function codeConnect(options) {
|
|
8201
8347
|
const cwd = options.cwd ?? process.cwd();
|
|
8202
|
-
const configPath = (0,
|
|
8348
|
+
const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
|
|
8203
8349
|
const cfg = (0, import_node_fs15.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
8204
8350
|
const requestedAppId = options.appId?.trim();
|
|
8205
8351
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
@@ -8568,10 +8714,8 @@ function requireName(parsed) {
|
|
|
8568
8714
|
return name;
|
|
8569
8715
|
}
|
|
8570
8716
|
|
|
8571
|
-
// src/help.ts
|
|
8572
|
-
|
|
8573
|
-
output.log(`odla-ai
|
|
8574
|
-
|
|
8717
|
+
// src/help-usage.ts
|
|
8718
|
+
var USAGE_SECTION = `
|
|
8575
8719
|
Start here:
|
|
8576
8720
|
odla-ai runbook ask "<question>" The current procedure, from odla's own
|
|
8577
8721
|
runbooks. Ask BEFORE searching the web or
|
|
@@ -8605,6 +8749,7 @@ Usage:
|
|
|
8605
8749
|
odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
8606
8750
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
8607
8751
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
8752
|
+
odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
|
|
8608
8753
|
odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
8609
8754
|
odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
8610
8755
|
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
@@ -8640,7 +8785,6 @@ Usage:
|
|
|
8640
8785
|
odla-ai context remove <name> --yes [--json]
|
|
8641
8786
|
odla-ai o11y status [--app <id>] [--context <name>] [--platform https://odla.ai] [--env prod] [--minutes 60] [--json]
|
|
8642
8787
|
odla-ai platform status [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
8643
|
-
odla-ai platform chat-credentials rotate [--context <name>] [--email <odla-account>] [--wrangler-config <path>] [--expected-version <id>] [--json] --yes
|
|
8644
8788
|
odla-ai whoami [--context <name>] [--platform https://odla.ai] [--json]
|
|
8645
8789
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
8646
8790
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
@@ -8685,8 +8829,12 @@ Usage:
|
|
|
8685
8829
|
odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
|
|
8686
8830
|
odla-ai secrets set <name> --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
|
|
8687
8831
|
odla-ai secrets set-clerk-key --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
|
|
8688
|
-
odla-ai version
|
|
8832
|
+
odla-ai version`;
|
|
8689
8833
|
|
|
8834
|
+
// src/help.ts
|
|
8835
|
+
function printHelp(output = console) {
|
|
8836
|
+
output.log(`odla-ai
|
|
8837
|
+
${USAGE_SECTION}
|
|
8690
8838
|
Commands:
|
|
8691
8839
|
agent Inspect durable agent wakeups and explicitly requeue a
|
|
8692
8840
|
dead-lettered job; JSON output is stable for remote operators.
|
|
@@ -9135,7 +9283,7 @@ function jsonl(ctx, parsed, value2) {
|
|
|
9135
9283
|
}
|
|
9136
9284
|
async function discussWatch(ctx, topicId, parsed) {
|
|
9137
9285
|
if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
|
|
9138
|
-
const sleep = ctx.sleep ?? ((ms) => new Promise((
|
|
9286
|
+
const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
|
|
9139
9287
|
const now = ctx.now ?? Date.now;
|
|
9140
9288
|
const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
|
|
9141
9289
|
const timeoutSeconds = numberOpt2(parsed, "timeout");
|
|
@@ -9473,8 +9621,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
9473
9621
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
9474
9622
|
}
|
|
9475
9623
|
async function pmGet(ctx, entity, id) {
|
|
9476
|
-
const { record:
|
|
9477
|
-
emit2(ctx,
|
|
9624
|
+
const { record: record10 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
9625
|
+
emit2(ctx, record10, () => printRecord(ctx, entity, record10));
|
|
9478
9626
|
}
|
|
9479
9627
|
async function pmSet(ctx, entity, id, parsed) {
|
|
9480
9628
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
@@ -9519,9 +9667,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
9519
9667
|
]);
|
|
9520
9668
|
const handoff = {
|
|
9521
9669
|
appId,
|
|
9522
|
-
unmetGoals: goals.filter((
|
|
9523
|
-
activeTasks: tasks.filter((
|
|
9524
|
-
openBugs: bugs.filter((
|
|
9670
|
+
unmetGoals: goals.filter((record10) => record10.status !== "met"),
|
|
9671
|
+
activeTasks: tasks.filter((record10) => record10.column !== "done"),
|
|
9672
|
+
openBugs: bugs.filter((record10) => record10.status !== "fixed" && record10.status !== "wontfix")
|
|
9525
9673
|
};
|
|
9526
9674
|
const result = {
|
|
9527
9675
|
...handoff,
|
|
@@ -9540,10 +9688,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
9540
9688
|
]) {
|
|
9541
9689
|
ctx.out.log(`${label}:`);
|
|
9542
9690
|
if (!records.length) ctx.out.log("- (none)");
|
|
9543
|
-
else for (const
|
|
9691
|
+
else for (const record10 of records) printRecord(
|
|
9544
9692
|
ctx,
|
|
9545
9693
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
9546
|
-
|
|
9694
|
+
record10
|
|
9547
9695
|
);
|
|
9548
9696
|
}
|
|
9549
9697
|
});
|
|
@@ -9734,167 +9882,12 @@ function age(input) {
|
|
|
9734
9882
|
return `${Math.round(input / 6e4)}m`;
|
|
9735
9883
|
}
|
|
9736
9884
|
|
|
9737
|
-
// src/platform-chat-credential-command.ts
|
|
9738
|
-
var import_node_process10 = __toESM(require("process"), 1);
|
|
9739
|
-
async function rotatePlatformChatCredential(parsed, deps) {
|
|
9740
|
-
assertArgs(
|
|
9741
|
-
parsed,
|
|
9742
|
-
[
|
|
9743
|
-
"config",
|
|
9744
|
-
"context",
|
|
9745
|
-
"platform",
|
|
9746
|
-
"token",
|
|
9747
|
-
"email",
|
|
9748
|
-
"open",
|
|
9749
|
-
"json",
|
|
9750
|
-
"yes",
|
|
9751
|
-
"wrangler-config",
|
|
9752
|
-
"expected-version"
|
|
9753
|
-
],
|
|
9754
|
-
3
|
|
9755
|
-
);
|
|
9756
|
-
if (parsed.options.yes !== true) {
|
|
9757
|
-
throw new Error(
|
|
9758
|
-
"platform chat-credentials rotate changes the production chat secret; pass --yes"
|
|
9759
|
-
);
|
|
9760
|
-
}
|
|
9761
|
-
const context = await resolveOperatorContext(parsed, {
|
|
9762
|
-
allowMissingConfig: true
|
|
9763
|
-
});
|
|
9764
|
-
const platform = context.platform.value;
|
|
9765
|
-
const doFetch = deps.fetch ?? fetch;
|
|
9766
|
-
const out = deps.stdout ?? console;
|
|
9767
|
-
const run = deps.runner ?? defaultRunner;
|
|
9768
|
-
const cwd = import_node_process10.default.cwd();
|
|
9769
|
-
const wranglerConfig = stringOpt(parsed.options["wrangler-config"]) ?? "packages/chat-agent/wrangler.jsonc";
|
|
9770
|
-
if (!await wranglerLoggedIn(run, cwd)) {
|
|
9771
|
-
throw new Error(
|
|
9772
|
-
'Wrangler is not authenticated; run "npx wrangler login" and retry'
|
|
9773
|
-
);
|
|
9774
|
-
}
|
|
9775
|
-
const priorHealth = await doFetch(`${platform}/health/services/chat`);
|
|
9776
|
-
const priorVersion = priorHealth.headers.get("x-odla-worker-version-id");
|
|
9777
|
-
await priorHealth.body?.cancel().catch(() => {
|
|
9778
|
-
});
|
|
9779
|
-
if (!priorVersion) {
|
|
9780
|
-
throw new Error(
|
|
9781
|
-
"chat health did not identify its current Worker version; refusing to rotate"
|
|
9782
|
-
);
|
|
9783
|
-
}
|
|
9784
|
-
const expectedVersion = stringOpt(parsed.options["expected-version"]);
|
|
9785
|
-
if (expectedVersion && priorVersion !== expectedVersion) {
|
|
9786
|
-
throw new Error(
|
|
9787
|
-
`chat health answered from version ${priorVersion}; expected ${expectedVersion}`
|
|
9788
|
-
);
|
|
9789
|
-
}
|
|
9790
|
-
const token = await resolveAdminPlatformToken({
|
|
9791
|
-
platform,
|
|
9792
|
-
scope: "platform:chat:credential:write",
|
|
9793
|
-
token: stringOpt(parsed.options.token),
|
|
9794
|
-
tokenFile: context.credentials.scopedTokenFile,
|
|
9795
|
-
email: stringOpt(parsed.options.email),
|
|
9796
|
-
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
9797
|
-
fetch: doFetch,
|
|
9798
|
-
stdout: out,
|
|
9799
|
-
openApprovalUrl: deps.openUrl,
|
|
9800
|
-
label: "odla CLI (rotate built-in Discussion responder)"
|
|
9801
|
-
});
|
|
9802
|
-
const mintedResponse = await doFetch(
|
|
9803
|
-
`${platform}/registry/platform/chat-credentials/rotate`,
|
|
9804
|
-
{
|
|
9805
|
-
method: "POST",
|
|
9806
|
-
headers: { authorization: `Bearer ${token}` }
|
|
9807
|
-
}
|
|
9808
|
-
);
|
|
9809
|
-
const minted = await mintedResponse.json().catch(() => null);
|
|
9810
|
-
if (!mintedResponse.ok) {
|
|
9811
|
-
throw new Error(
|
|
9812
|
-
`mint Discussion credential failed (HTTP ${mintedResponse.status}): ${apiMessage(minted)}`
|
|
9813
|
-
);
|
|
9814
|
-
}
|
|
9815
|
-
if (!isPlatformChatCredential(minted)) {
|
|
9816
|
-
throw new Error(
|
|
9817
|
-
"platform returned an invalid odla.platform-chat-credential/v1 envelope"
|
|
9818
|
-
);
|
|
9819
|
-
}
|
|
9820
|
-
const put = await wranglerPutSecret(run, {
|
|
9821
|
-
name: "ODLA_BOT_TOKENS",
|
|
9822
|
-
value: JSON.stringify({
|
|
9823
|
-
[minted.appId]: { [minted.principalId]: minted.key }
|
|
9824
|
-
}),
|
|
9825
|
-
configPath: wranglerConfig,
|
|
9826
|
-
cwd
|
|
9827
|
-
});
|
|
9828
|
-
if (put.code !== 0) {
|
|
9829
|
-
throw new Error(
|
|
9830
|
-
"Wrangler could not replace ODLA_BOT_TOKENS; the new scoped key was not activated"
|
|
9831
|
-
);
|
|
9832
|
-
}
|
|
9833
|
-
const version = await waitForRotatedChatHealth({
|
|
9834
|
-
fetch: doFetch,
|
|
9835
|
-
platform,
|
|
9836
|
-
priorVersion,
|
|
9837
|
-
wait: deps.pollWait
|
|
9838
|
-
});
|
|
9839
|
-
const result = {
|
|
9840
|
-
schemaVersion: "odla.platform-chat-credential-rotation/v1",
|
|
9841
|
-
appId: minted.appId,
|
|
9842
|
-
appIncarnation: minted.appIncarnation,
|
|
9843
|
-
principalId: minted.principalId,
|
|
9844
|
-
health: { ok: true, service: "odla-chat-agent", version }
|
|
9845
|
-
};
|
|
9846
|
-
if (parsed.options.json === true) {
|
|
9847
|
-
out.log(JSON.stringify(result, null, 2));
|
|
9848
|
-
} else {
|
|
9849
|
-
out.log(
|
|
9850
|
-
`rotated ${result.appId} ${result.principalId} ${result.health.version}`
|
|
9851
|
-
);
|
|
9852
|
-
}
|
|
9853
|
-
}
|
|
9854
|
-
async function waitForRotatedChatHealth(opts) {
|
|
9855
|
-
const wait2 = opts.wait ?? ((milliseconds) => new Promise((resolve12) => setTimeout(resolve12, milliseconds)));
|
|
9856
|
-
let lastStatus = 0;
|
|
9857
|
-
let lastVersion = null;
|
|
9858
|
-
let lastError = "private_service_unready";
|
|
9859
|
-
for (let attempt = 0; attempt < 60; attempt++) {
|
|
9860
|
-
const response2 = await opts.fetch(
|
|
9861
|
-
`${opts.platform}/health/services/chat`
|
|
9862
|
-
);
|
|
9863
|
-
const body = await response2.json().catch(() => null);
|
|
9864
|
-
const version = response2.headers.get("x-odla-worker-version-id");
|
|
9865
|
-
if (response2.ok && record7(body) && body.ok === true && body.service === "odla-chat-agent" && version && version !== opts.priorVersion) {
|
|
9866
|
-
return version;
|
|
9867
|
-
}
|
|
9868
|
-
lastStatus = response2.status;
|
|
9869
|
-
lastVersion = version;
|
|
9870
|
-
lastError = record7(body) && typeof body.error === "string" ? body.error : "private_service_unready";
|
|
9871
|
-
if (attempt < 59) await wait2(5e3);
|
|
9872
|
-
}
|
|
9873
|
-
throw new Error(
|
|
9874
|
-
`chat health did not converge on the rotated Worker deployment (HTTP ${lastStatus}, version ${lastVersion ?? "unknown"}, ${lastError})`
|
|
9875
|
-
);
|
|
9876
|
-
}
|
|
9877
|
-
function isPlatformChatCredential(value2) {
|
|
9878
|
-
return record7(value2) && value2.schemaVersion === "odla.platform-chat-credential/v1" && value2.appId === "odla-pm" && typeof value2.appIncarnation === "string" && value2.appIncarnation.length > 0 && value2.principalId === "agent_odla" && typeof value2.key === "string" && value2.key.startsWith("odla_sk_");
|
|
9879
|
-
}
|
|
9880
|
-
function apiMessage(value2) {
|
|
9881
|
-
if (!record7(value2)) return "request failed";
|
|
9882
|
-
const error = record7(value2.error) ? value2.error : value2;
|
|
9883
|
-
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
9884
|
-
}
|
|
9885
|
-
function record7(value2) {
|
|
9886
|
-
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
9887
|
-
}
|
|
9888
|
-
|
|
9889
9885
|
// src/platform-command.ts
|
|
9890
9886
|
async function platformCommand(parsed, deps = {}) {
|
|
9891
9887
|
const action2 = parsed.positionals[1];
|
|
9892
9888
|
if (action2 === "status") {
|
|
9893
9889
|
return platformStatus(parsed, deps);
|
|
9894
9890
|
}
|
|
9895
|
-
if (action2 === "chat-credentials" && parsed.positionals[2] === "rotate") {
|
|
9896
|
-
return rotatePlatformChatCredential(parsed, deps);
|
|
9897
|
-
}
|
|
9898
9891
|
throw new Error(
|
|
9899
9892
|
`unknown platform action "${[
|
|
9900
9893
|
action2,
|
|
@@ -9932,7 +9925,7 @@ async function platformStatus(parsed, deps) {
|
|
|
9932
9925
|
const body = await response2.json().catch(() => null);
|
|
9933
9926
|
if (!response2.ok) {
|
|
9934
9927
|
throw new Error(
|
|
9935
|
-
`read platform status failed (HTTP ${response2.status}): ${
|
|
9928
|
+
`read platform status failed (HTTP ${response2.status}): ${apiMessage(body)}`
|
|
9936
9929
|
);
|
|
9937
9930
|
}
|
|
9938
9931
|
if (!isPlatformStatus(body)) {
|
|
@@ -9945,17 +9938,17 @@ async function platformStatus(parsed, deps) {
|
|
|
9945
9938
|
}
|
|
9946
9939
|
}
|
|
9947
9940
|
function isPlatformStatus(value2) {
|
|
9948
|
-
if (!
|
|
9949
|
-
if (!
|
|
9950
|
-
if (!
|
|
9941
|
+
if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
|
|
9942
|
+
if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
|
|
9943
|
+
if (!record7(value2.catalog) || !record7(value2.summary)) return false;
|
|
9951
9944
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
9952
9945
|
}
|
|
9953
|
-
function
|
|
9954
|
-
if (!
|
|
9955
|
-
const error =
|
|
9946
|
+
function apiMessage(value2) {
|
|
9947
|
+
if (!record7(value2)) return "request failed";
|
|
9948
|
+
const error = record7(value2.error) ? value2.error : value2;
|
|
9956
9949
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
9957
9950
|
}
|
|
9958
|
-
function
|
|
9951
|
+
function record7(value2) {
|
|
9959
9952
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
9960
9953
|
}
|
|
9961
9954
|
|
|
@@ -9996,7 +9989,7 @@ function statusVerdict(reads) {
|
|
|
9996
9989
|
severity: "degraded"
|
|
9997
9990
|
});
|
|
9998
9991
|
}
|
|
9999
|
-
const performance =
|
|
9992
|
+
const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
10000
9993
|
if (performance?.status === "unavailable") {
|
|
10001
9994
|
reasons.push({
|
|
10002
9995
|
source: "liveSync",
|
|
@@ -10077,7 +10070,7 @@ function statusVerdict(reads) {
|
|
|
10077
10070
|
reasons
|
|
10078
10071
|
};
|
|
10079
10072
|
}
|
|
10080
|
-
function
|
|
10073
|
+
function record8(value2) {
|
|
10081
10074
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10082
10075
|
}
|
|
10083
10076
|
function numeric2(value2) {
|
|
@@ -10105,7 +10098,7 @@ function printO11yStatus(status, out) {
|
|
|
10105
10098
|
out.log(
|
|
10106
10099
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
10107
10100
|
);
|
|
10108
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
10101
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
|
|
10109
10102
|
const requests = routes.reduce(
|
|
10110
10103
|
(total, row) => total + numeric3(row.requests),
|
|
10111
10104
|
0
|
|
@@ -10117,39 +10110,39 @@ function printO11yStatus(status, out) {
|
|
|
10117
10110
|
out.log(
|
|
10118
10111
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
10119
10112
|
);
|
|
10120
|
-
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(
|
|
10113
|
+
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
|
|
10121
10114
|
out.log(
|
|
10122
10115
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
10123
10116
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
10124
10117
|
).join(", ") : "none observed"}`
|
|
10125
10118
|
);
|
|
10126
10119
|
out.log(liveSyncLine(status.liveSync));
|
|
10127
|
-
const canaryDurations =
|
|
10120
|
+
const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
10128
10121
|
out.log(
|
|
10129
10122
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
10130
10123
|
);
|
|
10131
|
-
const collectorIngest =
|
|
10132
|
-
const collectorStorage =
|
|
10124
|
+
const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
10125
|
+
const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
10133
10126
|
out.log(
|
|
10134
10127
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
10135
10128
|
);
|
|
10136
|
-
const providerMetrics =
|
|
10137
|
-
const providerCapacity =
|
|
10138
|
-
const workerMemory =
|
|
10129
|
+
const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
10130
|
+
const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
|
|
10131
|
+
const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
|
|
10139
10132
|
out.log(
|
|
10140
10133
|
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric3(providerMetrics.requests)} invocations ${numeric3(providerMetrics.errors)} runtime errors ${optionalBytes(workerMemory.headroomBytes)} isolate memory headroom`
|
|
10141
10134
|
);
|
|
10142
10135
|
for (const line of providerCapacityLines(status.providerCapacity)) {
|
|
10143
10136
|
out.log(line);
|
|
10144
10137
|
}
|
|
10145
|
-
const coverage =
|
|
10146
|
-
const coverageCounts =
|
|
10147
|
-
const coverageBudget =
|
|
10138
|
+
const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
|
|
10139
|
+
const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
|
|
10140
|
+
const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
|
|
10148
10141
|
out.log(
|
|
10149
10142
|
`request-coverage ${status.providerReconciliation.httpStatus} ${String(status.providerReconciliation.body.status ?? status.providerReconciliation.body.error ?? "unavailable")} ${optionalPercent(coverage.applicationCoverage)} application/provider ${numeric3(coverageCounts.applicationRequests)}/${numeric3(coverageCounts.providerRequests)} requests \xB1${optionalPercent(coverageBudget.maxRelativeError)} budget`
|
|
10150
10143
|
);
|
|
10151
10144
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
10152
|
-
const providerFreshness =
|
|
10145
|
+
const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
10153
10146
|
out.log(
|
|
10154
10147
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
10155
10148
|
);
|
|
@@ -10158,17 +10151,17 @@ function printO11yStatus(status, out) {
|
|
|
10158
10151
|
);
|
|
10159
10152
|
}
|
|
10160
10153
|
function providerCapacityLines(read3) {
|
|
10161
|
-
const resources =
|
|
10162
|
-
const durableObjects =
|
|
10163
|
-
const periodic =
|
|
10164
|
-
const storage =
|
|
10165
|
-
const d1 =
|
|
10166
|
-
const d1Activity =
|
|
10167
|
-
const d1Storage =
|
|
10168
|
-
const d1Latency =
|
|
10169
|
-
const r2 =
|
|
10170
|
-
const r2Operations =
|
|
10171
|
-
const r2Storage =
|
|
10154
|
+
const resources = record9(read3.body.resources) ? read3.body.resources : {};
|
|
10155
|
+
const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
|
|
10156
|
+
const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
|
|
10157
|
+
const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
|
|
10158
|
+
const d1 = record9(resources.d1) ? resources.d1 : {};
|
|
10159
|
+
const d1Activity = record9(d1.activity) ? d1.activity : {};
|
|
10160
|
+
const d1Storage = record9(d1.storage) ? d1.storage : {};
|
|
10161
|
+
const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
|
|
10162
|
+
const r2 = record9(resources.r2) ? resources.r2 : {};
|
|
10163
|
+
const r2Operations = record9(r2.operations) ? r2.operations : {};
|
|
10164
|
+
const r2Storage = record9(r2.storage) ? r2.storage : {};
|
|
10172
10165
|
const status = String(
|
|
10173
10166
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
10174
10167
|
);
|
|
@@ -10179,11 +10172,11 @@ function providerCapacityLines(read3) {
|
|
|
10179
10172
|
];
|
|
10180
10173
|
}
|
|
10181
10174
|
function liveSyncLine(read3) {
|
|
10182
|
-
const performance =
|
|
10183
|
-
const commitToSend =
|
|
10175
|
+
const performance = record9(read3.body.performance) ? read3.body.performance : {};
|
|
10176
|
+
const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
|
|
10184
10177
|
return `live-sync ${read3.httpStatus} ${String(read3.body.status ?? read3.body.error ?? "unavailable")} ${numeric3(read3.body.activeConnections)} active ${optionalNumeric(commitToSend.p95)} commit-to-send p95 ${numeric3(performance.sendFailures)} send failures`;
|
|
10185
10178
|
}
|
|
10186
|
-
function
|
|
10179
|
+
function record9(value2) {
|
|
10187
10180
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10188
10181
|
}
|
|
10189
10182
|
function numeric3(value2) {
|
|
@@ -10368,7 +10361,7 @@ async function read2(url, headers, doFetch) {
|
|
|
10368
10361
|
// src/provision.ts
|
|
10369
10362
|
var import_apps12 = require("@odla-ai/apps");
|
|
10370
10363
|
var import_ai3 = require("@odla-ai/ai");
|
|
10371
|
-
var
|
|
10364
|
+
var import_node_process10 = __toESM(require("process"), 1);
|
|
10372
10365
|
|
|
10373
10366
|
// src/integration-provision.ts
|
|
10374
10367
|
var import_db3 = require("@odla-ai/db");
|
|
@@ -10675,7 +10668,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10675
10668
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
10676
10669
|
}
|
|
10677
10670
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
10678
|
-
const key =
|
|
10671
|
+
const key = import_node_process10.default.env[cfg.ai.keyEnv];
|
|
10679
10672
|
if (key) {
|
|
10680
10673
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
10681
10674
|
await (0, import_ai3.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -10717,7 +10710,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10717
10710
|
|
|
10718
10711
|
// src/record.ts
|
|
10719
10712
|
var import_node_fs16 = require("fs");
|
|
10720
|
-
var
|
|
10713
|
+
var import_node_process11 = __toESM(require("process"), 1);
|
|
10721
10714
|
|
|
10722
10715
|
// src/surface.ts
|
|
10723
10716
|
var PM_ACTIONS = {
|
|
@@ -10762,6 +10755,7 @@ var COMMAND_SURFACE = {
|
|
|
10762
10755
|
promote: {},
|
|
10763
10756
|
owners: { list: {}, add: {}, remove: {} }
|
|
10764
10757
|
},
|
|
10758
|
+
brand: { design: { unpack: {} } },
|
|
10765
10759
|
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
10766
10760
|
capabilities: {},
|
|
10767
10761
|
code: { connect: {} },
|
|
@@ -10785,8 +10779,7 @@ var COMMAND_SURFACE = {
|
|
|
10785
10779
|
o11y: { status: {} },
|
|
10786
10780
|
operations: { get: {}, wait: {} },
|
|
10787
10781
|
platform: {
|
|
10788
|
-
status: {}
|
|
10789
|
-
"chat-credentials": { rotate: {} }
|
|
10782
|
+
status: {}
|
|
10790
10783
|
},
|
|
10791
10784
|
pm: {
|
|
10792
10785
|
...PM_ENTITIES,
|
|
@@ -10875,7 +10868,7 @@ function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
|
10875
10868
|
|
|
10876
10869
|
// src/record.ts
|
|
10877
10870
|
function recordInvocation(parsed) {
|
|
10878
|
-
const file =
|
|
10871
|
+
const file = import_node_process11.default.env.ODLA_CLI_RECORD;
|
|
10879
10872
|
if (!file) return;
|
|
10880
10873
|
try {
|
|
10881
10874
|
const entry = {
|
|
@@ -11068,7 +11061,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
11068
11061
|
|
|
11069
11062
|
// src/runbook-import.ts
|
|
11070
11063
|
var import_node_fs18 = require("fs");
|
|
11071
|
-
var
|
|
11064
|
+
var import_node_path16 = require("path");
|
|
11072
11065
|
function parseRunbook(text2, slug) {
|
|
11073
11066
|
let rest = text2;
|
|
11074
11067
|
const meta = {};
|
|
@@ -11097,8 +11090,8 @@ function readRunbookDir(dir) {
|
|
|
11097
11090
|
const files = (0, import_node_fs18.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
11098
11091
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
11099
11092
|
return files.map((file) => {
|
|
11100
|
-
const slug = (0,
|
|
11101
|
-
const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0,
|
|
11093
|
+
const slug = (0, import_node_path16.basename)(file, ".md");
|
|
11094
|
+
const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0, import_node_path16.join)(dir, file), "utf8"), slug);
|
|
11102
11095
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
11103
11096
|
});
|
|
11104
11097
|
}
|
|
@@ -11172,7 +11165,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
11172
11165
|
// src/runbook-impact.ts
|
|
11173
11166
|
var import_node_child_process7 = require("child_process");
|
|
11174
11167
|
var import_node_fs19 = require("fs");
|
|
11175
|
-
var
|
|
11168
|
+
var import_node_path17 = require("path");
|
|
11176
11169
|
|
|
11177
11170
|
// src/runbook-impact-scan.ts
|
|
11178
11171
|
var DECL = /^[+-]\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/;
|
|
@@ -11341,7 +11334,7 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
|
|
|
11341
11334
|
}
|
|
11342
11335
|
function manifestLabeller(root) {
|
|
11343
11336
|
return (workspace) => {
|
|
11344
|
-
const manifest = (0,
|
|
11337
|
+
const manifest = (0, import_node_path17.join)(root, workspace, "package.json");
|
|
11345
11338
|
if (!(0, import_node_fs19.existsSync)(manifest)) return void 0;
|
|
11346
11339
|
try {
|
|
11347
11340
|
const name = JSON.parse((0, import_node_fs19.readFileSync)(manifest, "utf8")).name;
|
|
@@ -11411,7 +11404,7 @@ function report3(ctx, impacts) {
|
|
|
11411
11404
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
11412
11405
|
const cwd = deps.cwd ?? process.cwd();
|
|
11413
11406
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
11414
|
-
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0,
|
|
11407
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(cwd, path), "utf8"));
|
|
11415
11408
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
11416
11409
|
if (!surfaces.length) {
|
|
11417
11410
|
return ctx.out.log(
|
|
@@ -11546,10 +11539,10 @@ async function runbookComment(ctx, slug, body) {
|
|
|
11546
11539
|
var import_node_child_process8 = require("child_process");
|
|
11547
11540
|
var import_node_fs20 = require("fs");
|
|
11548
11541
|
var import_node_os5 = require("os");
|
|
11549
|
-
var
|
|
11550
|
-
var
|
|
11542
|
+
var import_node_path18 = require("path");
|
|
11543
|
+
var import_node_process12 = __toESM(require("process"), 1);
|
|
11551
11544
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11552
|
-
function resolveEditor(env =
|
|
11545
|
+
function resolveEditor(env = import_node_process12.default.env) {
|
|
11553
11546
|
for (const name of EDITOR_ENV) {
|
|
11554
11547
|
const value2 = env[name];
|
|
11555
11548
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11563,8 +11556,8 @@ function defaultRun(command, path) {
|
|
|
11563
11556
|
return result.status ?? 0;
|
|
11564
11557
|
}
|
|
11565
11558
|
function editText(initial, slug, deps = {}) {
|
|
11566
|
-
const env = deps.env ??
|
|
11567
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11559
|
+
const env = deps.env ?? import_node_process12.default.env;
|
|
11560
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process12.default.stdin.isTTY));
|
|
11568
11561
|
const editor = resolveEditor(env);
|
|
11569
11562
|
if (!editor)
|
|
11570
11563
|
throw new Error(
|
|
@@ -11572,8 +11565,8 @@ function editText(initial, slug, deps = {}) {
|
|
|
11572
11565
|
);
|
|
11573
11566
|
if (!interactive())
|
|
11574
11567
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
11575
|
-
const dir = (0, import_node_fs20.mkdtempSync)((0,
|
|
11576
|
-
const file = (0,
|
|
11568
|
+
const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path18.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
|
|
11569
|
+
const file = (0, import_node_path18.join)(dir, `${slug}.md`);
|
|
11577
11570
|
try {
|
|
11578
11571
|
(0, import_node_fs20.writeFileSync)(file, initial, { mode: 384 });
|
|
11579
11572
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
@@ -11915,7 +11908,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
11915
11908
|
}
|
|
11916
11909
|
|
|
11917
11910
|
// src/security-command-context.ts
|
|
11918
|
-
var
|
|
11911
|
+
var import_promises12 = require("readline/promises");
|
|
11919
11912
|
async function hostedSecurityContext(parsed, dependencies) {
|
|
11920
11913
|
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
11921
11914
|
const cfg = await loadProjectConfig(configPath);
|
|
@@ -11941,7 +11934,7 @@ async function hostedSecurityContext(parsed, dependencies) {
|
|
|
11941
11934
|
async function interactiveConfirmation(message2, dependencies) {
|
|
11942
11935
|
if (dependencies.confirm) return dependencies.confirm(message2);
|
|
11943
11936
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
11944
|
-
const prompt = (0,
|
|
11937
|
+
const prompt = (0, import_promises12.createInterface)({ input: process.stdin, output: process.stdout });
|
|
11945
11938
|
try {
|
|
11946
11939
|
const answer = await prompt.question(`${message2} [y/N] `);
|
|
11947
11940
|
return /^y(?:es)?$/i.test(answer.trim());
|
|
@@ -12068,7 +12061,7 @@ function hostedSeverity(value2, flag) {
|
|
|
12068
12061
|
var import_security2 = require("@odla-ai/security");
|
|
12069
12062
|
|
|
12070
12063
|
// src/security.ts
|
|
12071
|
-
var
|
|
12064
|
+
var import_node_path19 = require("path");
|
|
12072
12065
|
var import_security = require("@odla-ai/security");
|
|
12073
12066
|
var import_node3 = require("@odla-ai/security/node");
|
|
12074
12067
|
async function runHostedSecurity(options) {
|
|
@@ -12080,9 +12073,9 @@ async function runHostedSecurity(options) {
|
|
|
12080
12073
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
12081
12074
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
12082
12075
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
12083
|
-
const target = (0,
|
|
12084
|
-
const output = (0,
|
|
12085
|
-
const outputRelative = (0,
|
|
12076
|
+
const target = (0, import_node_path19.resolve)(options.target ?? cfg?.rootDir ?? ".");
|
|
12077
|
+
const output = (0, import_node_path19.resolve)(options.out ?? (0, import_node_path19.resolve)(target, ".odla/security/hosted"));
|
|
12078
|
+
const outputRelative = (0, import_node_path19.relative)(target, output).split(import_node_path19.sep).join("/");
|
|
12086
12079
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
12087
12080
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
12088
12081
|
const tokenRequest = {
|
|
@@ -12094,7 +12087,7 @@ async function runHostedSecurity(options) {
|
|
|
12094
12087
|
};
|
|
12095
12088
|
const token = await injectedToken(options, tokenRequest);
|
|
12096
12089
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
12097
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
12090
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path19.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
12098
12091
|
});
|
|
12099
12092
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
12100
12093
|
platform,
|
|
@@ -12112,7 +12105,7 @@ async function runHostedSecurity(options) {
|
|
|
12112
12105
|
});
|
|
12113
12106
|
const harness = (0, import_security.createSecurityHarness)({
|
|
12114
12107
|
profile,
|
|
12115
|
-
store: new import_node3.FileRunStore((0,
|
|
12108
|
+
store: new import_node3.FileRunStore((0, import_node_path19.resolve)(output, "state")),
|
|
12116
12109
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
12117
12110
|
validationReasoner: hosted.validationReasoner,
|
|
12118
12111
|
policy: {
|
|
@@ -12136,7 +12129,7 @@ async function runHostedSecurity(options) {
|
|
|
12136
12129
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
12137
12130
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
12138
12131
|
if (!env || !declared.includes(env)) {
|
|
12139
|
-
const shown = (0,
|
|
12132
|
+
const shown = (0, import_node_path19.relative)(rootDir, configPath) || configPath;
|
|
12140
12133
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
12141
12134
|
}
|
|
12142
12135
|
return env;
|
|
@@ -12165,7 +12158,7 @@ function printSummary(out, appId, env, run, report4, output) {
|
|
|
12165
12158
|
out.log(` coverage: ${report4.coverageStatus} ${complete}/${report4.coverage.length} blocked=${report4.metrics.blockedCells} shallow=${report4.metrics.shallowCells} unscheduled=${report4.metrics.unscheduledCells} budget_exhausted=${report4.metrics.budgetExhaustedCells}`);
|
|
12166
12159
|
if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
|
|
12167
12160
|
out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates}`);
|
|
12168
|
-
out.log(` report: ${(0,
|
|
12161
|
+
out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
|
|
12169
12162
|
}
|
|
12170
12163
|
function formatBudget(usage) {
|
|
12171
12164
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
@@ -12677,6 +12670,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12677
12670
|
await securityCommand(parsed, runtime);
|
|
12678
12671
|
return;
|
|
12679
12672
|
}
|
|
12673
|
+
if (command === "brand") {
|
|
12674
|
+
await brandCommand(parsed, runtime);
|
|
12675
|
+
return;
|
|
12676
|
+
}
|
|
12680
12677
|
if (command === "pm") {
|
|
12681
12678
|
await pmCommand(parsed, runtime);
|
|
12682
12679
|
return;
|