@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/bin.cjs
CHANGED
|
@@ -246,7 +246,7 @@ var import_node_process2 = __toESM(require("process"), 1);
|
|
|
246
246
|
async function openUrl(url, options = {}) {
|
|
247
247
|
const command = openerFor(options.platform ?? import_node_process2.default.platform);
|
|
248
248
|
const doSpawn = options.spawnImpl ?? import_node_child_process.spawn;
|
|
249
|
-
await new Promise((
|
|
249
|
+
await new Promise((resolve13, reject) => {
|
|
250
250
|
const child = doSpawn(command.cmd, [...command.args, url], {
|
|
251
251
|
stdio: "ignore",
|
|
252
252
|
detached: true
|
|
@@ -254,7 +254,7 @@ async function openUrl(url, options = {}) {
|
|
|
254
254
|
child.once("error", reject);
|
|
255
255
|
child.once("spawn", () => {
|
|
256
256
|
child.unref();
|
|
257
|
-
|
|
257
|
+
resolve13();
|
|
258
258
|
});
|
|
259
259
|
});
|
|
260
260
|
}
|
|
@@ -492,7 +492,6 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
492
492
|
}
|
|
493
493
|
var SCOPE_PURPOSE = {
|
|
494
494
|
"platform:status:read": "read the platform fleet health and deployment snapshot",
|
|
495
|
-
"platform:chat:credential:write": "rotate the built-in Discussion responder credential",
|
|
496
495
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
497
496
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
498
497
|
"platform:runbook:write": "add or edit odla's operational runbooks",
|
|
@@ -2176,6 +2175,153 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2176
2175
|
else await appRestore(options);
|
|
2177
2176
|
}
|
|
2178
2177
|
|
|
2178
|
+
// src/brand-command.ts
|
|
2179
|
+
var import_promises2 = require("fs/promises");
|
|
2180
|
+
var import_node_path7 = require("path");
|
|
2181
|
+
|
|
2182
|
+
// src/brand-design-unpack.ts
|
|
2183
|
+
var import_node_zlib = require("zlib");
|
|
2184
|
+
var import_brand = require("@odla-ai/brand");
|
|
2185
|
+
var EXTENSIONS = {
|
|
2186
|
+
"image/png": "png",
|
|
2187
|
+
"image/jpeg": "jpg",
|
|
2188
|
+
"image/gif": "gif",
|
|
2189
|
+
"image/webp": "webp",
|
|
2190
|
+
"image/svg+xml": "svg",
|
|
2191
|
+
"image/avif": "avif",
|
|
2192
|
+
"font/woff2": "woff2",
|
|
2193
|
+
"font/woff": "woff",
|
|
2194
|
+
"font/ttf": "ttf",
|
|
2195
|
+
"font/otf": "otf",
|
|
2196
|
+
"text/javascript": "js",
|
|
2197
|
+
"application/javascript": "js",
|
|
2198
|
+
"text/css": "css",
|
|
2199
|
+
"text/html": "html",
|
|
2200
|
+
"application/json": "json"
|
|
2201
|
+
};
|
|
2202
|
+
var encode = (text2) => new TextEncoder().encode(text2);
|
|
2203
|
+
function assetFileName(uuid, mime) {
|
|
2204
|
+
const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
|
|
2205
|
+
return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
|
|
2206
|
+
}
|
|
2207
|
+
function decodePayload(data, compressed) {
|
|
2208
|
+
const raw = Buffer.from(data, "base64");
|
|
2209
|
+
return new Uint8Array(compressed ? (0, import_node_zlib.gunzipSync)(raw) : raw);
|
|
2210
|
+
}
|
|
2211
|
+
function rewriteReferences(template, assetPaths, pagePaths) {
|
|
2212
|
+
let out = template;
|
|
2213
|
+
for (const [uuid, path] of pagePaths) {
|
|
2214
|
+
out = out.split(`about:blank#${uuid}`).join(path);
|
|
2215
|
+
}
|
|
2216
|
+
for (const [uuid, path] of assetPaths) {
|
|
2217
|
+
out = out.split(uuid).join(path);
|
|
2218
|
+
}
|
|
2219
|
+
return out;
|
|
2220
|
+
}
|
|
2221
|
+
function tokensCss(digest) {
|
|
2222
|
+
if (Object.keys(digest.tokens.light).length === 0) return null;
|
|
2223
|
+
return (0, import_brand.renderTokensCss)(digest.tokens.light, { dark: digest.tokens.dark });
|
|
2224
|
+
}
|
|
2225
|
+
function unpackDesign(html) {
|
|
2226
|
+
const bundle = (0, import_brand.parseDesignBundle)(html);
|
|
2227
|
+
const manifest = (0, import_brand.readDesignManifest)(html);
|
|
2228
|
+
const digest = (0, import_brand.digestDesignBundle)(bundle);
|
|
2229
|
+
const pageUuids = new Set(bundle.pageOrder);
|
|
2230
|
+
const files = [];
|
|
2231
|
+
const failed = [];
|
|
2232
|
+
const assetPaths = /* @__PURE__ */ new Map();
|
|
2233
|
+
const pagePaths = /* @__PURE__ */ new Map();
|
|
2234
|
+
for (const [uuid, entry] of Object.entries(manifest)) {
|
|
2235
|
+
let bytes;
|
|
2236
|
+
try {
|
|
2237
|
+
bytes = decodePayload(entry.data, entry.compressed);
|
|
2238
|
+
} catch {
|
|
2239
|
+
failed.push(uuid);
|
|
2240
|
+
continue;
|
|
2241
|
+
}
|
|
2242
|
+
if (pageUuids.has(uuid)) {
|
|
2243
|
+
const path2 = `pages/${assetFileName(uuid, "text/html")}`;
|
|
2244
|
+
pagePaths.set(uuid, `./${path2}`);
|
|
2245
|
+
files.push({ path: path2, bytes });
|
|
2246
|
+
continue;
|
|
2247
|
+
}
|
|
2248
|
+
const path = `assets/${assetFileName(uuid, entry.mime)}`;
|
|
2249
|
+
assetPaths.set(uuid, `./${path}`);
|
|
2250
|
+
files.push({ path, bytes });
|
|
2251
|
+
}
|
|
2252
|
+
files.push({
|
|
2253
|
+
path: "index.html",
|
|
2254
|
+
bytes: encode(rewriteReferences(bundle.template, assetPaths, pagePaths))
|
|
2255
|
+
});
|
|
2256
|
+
files.push({ path: "digest.json", bytes: encode(`${JSON.stringify(digest, null, 2)}
|
|
2257
|
+
`) });
|
|
2258
|
+
const css = tokensCss(digest);
|
|
2259
|
+
if (css !== null) files.push({ path: "tokens.css", bytes: encode(css) });
|
|
2260
|
+
if (bundle.thumbnailSvg) files.push({ path: "thumbnail.svg", bytes: encode(bundle.thumbnailSvg) });
|
|
2261
|
+
return { files, digest, failed };
|
|
2262
|
+
}
|
|
2263
|
+
function describeUnpack(result, outDir) {
|
|
2264
|
+
const { digest } = result;
|
|
2265
|
+
const lines = [
|
|
2266
|
+
`Unpacked ${digest.title ? `"${digest.title}"` : "design"} into ${outDir}`,
|
|
2267
|
+
` index.html the design, offline-runnable (${digest.templateBytes} bytes)`,
|
|
2268
|
+
` assets/ ${digest.assetCount} embedded files (${digest.assetBytes} bytes)`,
|
|
2269
|
+
` digest.json ${Object.keys(digest.tokens.light).length} --ui-* tokens, ${digest.props.length} props, ${digest.outline.length} headings`
|
|
2270
|
+
];
|
|
2271
|
+
if (result.files.some((f) => f.path === "tokens.css"))
|
|
2272
|
+
lines.push(" tokens.css the design's tokens as an @odla-ai/ui theme sheet");
|
|
2273
|
+
if (digest.fonts.length > 0) lines.push(`Typefaces: ${digest.fonts.join(", ")}`);
|
|
2274
|
+
if (result.failed.length > 0)
|
|
2275
|
+
lines.push(`WARNING: ${result.failed.length} embedded asset(s) could not be decoded.`);
|
|
2276
|
+
return lines;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// src/brand-command.ts
|
|
2280
|
+
var USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
2281
|
+
async function readBundle(source, deps) {
|
|
2282
|
+
if (source !== "-") return (0, import_promises2.readFile)((0, import_node_path7.resolve)(source), "utf8");
|
|
2283
|
+
const readStdin = deps.readStdin;
|
|
2284
|
+
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
2285
|
+
return readStdin();
|
|
2286
|
+
}
|
|
2287
|
+
async function writeAll(result, outDir) {
|
|
2288
|
+
for (const file of result.files) {
|
|
2289
|
+
const target = (0, import_node_path7.resolve)(outDir, file.path);
|
|
2290
|
+
await (0, import_promises2.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
|
|
2291
|
+
await (0, import_promises2.writeFile)(target, file.bytes);
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
async function designUnpack(parsed, deps) {
|
|
2295
|
+
assertArgs(parsed, ["out", "json"], 4);
|
|
2296
|
+
const source = parsed.positionals[3];
|
|
2297
|
+
if (!source) throw new Error(USAGE);
|
|
2298
|
+
const outDir = (0, import_node_path7.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
2299
|
+
const result = unpackDesign(await readBundle(source, deps));
|
|
2300
|
+
await writeAll(result, outDir);
|
|
2301
|
+
const out = deps.stdout ?? console;
|
|
2302
|
+
if (parsed.options.json === true) {
|
|
2303
|
+
out.log(
|
|
2304
|
+
JSON.stringify({
|
|
2305
|
+
outDir,
|
|
2306
|
+
files: result.files.map((f) => f.path),
|
|
2307
|
+
failed: result.failed,
|
|
2308
|
+
digest: result.digest
|
|
2309
|
+
})
|
|
2310
|
+
);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
for (const line of describeUnpack(result, outDir)) out.log(line);
|
|
2314
|
+
}
|
|
2315
|
+
async function brandCommand(parsed, deps) {
|
|
2316
|
+
const subject = parsed.positionals[1];
|
|
2317
|
+
const action2 = parsed.positionals[2];
|
|
2318
|
+
if (subject === "design" && action2 === "unpack") {
|
|
2319
|
+
await designUnpack(parsed, deps);
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
throw new Error(USAGE);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2179
2325
|
// src/calendar-errors.ts
|
|
2180
2326
|
var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
|
|
2181
2327
|
"calendar_google_oauth_not_configured",
|
|
@@ -2449,8 +2595,8 @@ function credential(value2) {
|
|
|
2449
2595
|
// src/calendar-poll.ts
|
|
2450
2596
|
async function waitForCalendarPoll(milliseconds, signal) {
|
|
2451
2597
|
if (signal?.aborted) throw signal.reason ?? new Error("calendar connection aborted");
|
|
2452
|
-
await new Promise((
|
|
2453
|
-
const timer = setTimeout(
|
|
2598
|
+
await new Promise((resolve13, reject) => {
|
|
2599
|
+
const timer = setTimeout(resolve13, milliseconds);
|
|
2454
2600
|
signal?.addEventListener("abort", () => {
|
|
2455
2601
|
clearTimeout(timer);
|
|
2456
2602
|
reject(signal.reason ?? new Error("calendar connection aborted"));
|
|
@@ -2704,7 +2850,7 @@ function printGroup(out, heading, items) {
|
|
|
2704
2850
|
|
|
2705
2851
|
// src/config-operation-command.ts
|
|
2706
2852
|
var import_apps6 = require("@odla-ai/apps");
|
|
2707
|
-
var
|
|
2853
|
+
var import_node_path8 = require("path");
|
|
2708
2854
|
|
|
2709
2855
|
// src/version.ts
|
|
2710
2856
|
var import_node_fs9 = require("fs");
|
|
@@ -2743,9 +2889,9 @@ function canonicalValue(value2) {
|
|
|
2743
2889
|
}
|
|
2744
2890
|
if (Array.isArray(value2)) return value2.map(canonicalValue);
|
|
2745
2891
|
if (value2 && typeof value2 === "object") {
|
|
2746
|
-
const
|
|
2892
|
+
const record10 = value2;
|
|
2747
2893
|
return Object.fromEntries(
|
|
2748
|
-
Object.keys(
|
|
2894
|
+
Object.keys(record10).filter((key) => record10[key] !== void 0).sort().map((key) => [key, canonicalValue(record10[key])])
|
|
2749
2895
|
);
|
|
2750
2896
|
}
|
|
2751
2897
|
throw new TypeError("canonical JSON rejects unsupported values");
|
|
@@ -3083,7 +3229,7 @@ async function configOperationWait(options) {
|
|
|
3083
3229
|
assertOperationId(options.operationId);
|
|
3084
3230
|
const cfg = await loadProjectConfig(options.configPath);
|
|
3085
3231
|
const client = await operationClient(cfg, options, "wait");
|
|
3086
|
-
const wait2 = options.pollWait ?? ((ms) => new Promise((
|
|
3232
|
+
const wait2 = options.pollWait ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
|
|
3087
3233
|
const now = () => (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
3088
3234
|
const deadline = now() + (options.timeoutSeconds ?? DEFAULT_WAIT_SECONDS) * 1e3;
|
|
3089
3235
|
const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1e3;
|
|
@@ -3114,7 +3260,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3114
3260
|
platform: cfg.platformUrl,
|
|
3115
3261
|
scope: "app:config:write",
|
|
3116
3262
|
token: options.token,
|
|
3117
|
-
tokenFile: (0,
|
|
3263
|
+
tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3118
3264
|
rootDir: cfg.rootDir,
|
|
3119
3265
|
email: options.email,
|
|
3120
3266
|
open: options.open,
|
|
@@ -3169,7 +3315,7 @@ function record3(value2) {
|
|
|
3169
3315
|
|
|
3170
3316
|
// src/config-reconcile-command.ts
|
|
3171
3317
|
var import_apps8 = require("@odla-ai/apps");
|
|
3172
|
-
var
|
|
3318
|
+
var import_node_path9 = require("path");
|
|
3173
3319
|
|
|
3174
3320
|
// src/config-reconcile.ts
|
|
3175
3321
|
var import_apps7 = require("@odla-ai/apps");
|
|
@@ -3465,7 +3611,7 @@ async function inspectConfig(options) {
|
|
|
3465
3611
|
platform: cfg.platformUrl,
|
|
3466
3612
|
scope: "app:config:read",
|
|
3467
3613
|
token: options.token,
|
|
3468
|
-
tokenFile: (0,
|
|
3614
|
+
tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3469
3615
|
rootDir: cfg.rootDir,
|
|
3470
3616
|
email: options.email,
|
|
3471
3617
|
open: options.open,
|
|
@@ -3598,12 +3744,12 @@ function quoteArg2(value2) {
|
|
|
3598
3744
|
// src/doctor-checks.ts
|
|
3599
3745
|
var import_node_child_process3 = require("child_process");
|
|
3600
3746
|
var import_node_fs12 = require("fs");
|
|
3601
|
-
var
|
|
3747
|
+
var import_node_path11 = require("path");
|
|
3602
3748
|
|
|
3603
3749
|
// src/wrangler.ts
|
|
3604
3750
|
var import_node_child_process2 = require("child_process");
|
|
3605
3751
|
var import_node_fs11 = require("fs");
|
|
3606
|
-
var
|
|
3752
|
+
var import_node_path10 = require("path");
|
|
3607
3753
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
3608
3754
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
3609
3755
|
let stdout = "";
|
|
@@ -3617,7 +3763,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
3617
3763
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
3618
3764
|
function findWranglerConfig(rootDir) {
|
|
3619
3765
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
3620
|
-
const path = (0,
|
|
3766
|
+
const path = (0, import_node_path10.join)(rootDir, name);
|
|
3621
3767
|
if ((0, import_node_fs11.existsSync)(path)) return path;
|
|
3622
3768
|
}
|
|
3623
3769
|
return null;
|
|
@@ -3730,10 +3876,10 @@ function wranglerWarnings(rootDir) {
|
|
|
3730
3876
|
for (const { label, block } of blocks) {
|
|
3731
3877
|
const assets = block.assets;
|
|
3732
3878
|
if (assets?.directory) {
|
|
3733
|
-
const dir = (0,
|
|
3734
|
-
if (dir === (0,
|
|
3879
|
+
const dir = (0, import_node_path11.resolve)(rootDir, assets.directory);
|
|
3880
|
+
if (dir === (0, import_node_path11.resolve)(rootDir)) {
|
|
3735
3881
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
3736
|
-
} else if ((0, import_node_fs12.existsSync)((0,
|
|
3882
|
+
} else if ((0, import_node_fs12.existsSync)((0, import_node_path11.join)(dir, "node_modules"))) {
|
|
3737
3883
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
3738
3884
|
}
|
|
3739
3885
|
}
|
|
@@ -3768,7 +3914,7 @@ function o11yProjectWarnings(rootDir) {
|
|
|
3768
3914
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
3769
3915
|
return warnings;
|
|
3770
3916
|
}
|
|
3771
|
-
const main = typeof config.main === "string" ? (0,
|
|
3917
|
+
const main = typeof config.main === "string" ? (0, import_node_path11.resolve)(rootDir, config.main) : null;
|
|
3772
3918
|
if (!main || !(0, import_node_fs12.existsSync)(main)) {
|
|
3773
3919
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
3774
3920
|
} else {
|
|
@@ -3798,7 +3944,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
3798
3944
|
}
|
|
3799
3945
|
function readPackageJson(rootDir) {
|
|
3800
3946
|
try {
|
|
3801
|
-
return JSON.parse((0, import_node_fs12.readFileSync)((0,
|
|
3947
|
+
return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(rootDir, "package.json"), "utf8"));
|
|
3802
3948
|
} catch {
|
|
3803
3949
|
return null;
|
|
3804
3950
|
}
|
|
@@ -4026,12 +4172,12 @@ function harnessOption(value2, flag) {
|
|
|
4026
4172
|
|
|
4027
4173
|
// src/init.ts
|
|
4028
4174
|
var import_node_fs13 = require("fs");
|
|
4029
|
-
var
|
|
4175
|
+
var import_node_path12 = require("path");
|
|
4030
4176
|
var import_apps9 = require("@odla-ai/apps");
|
|
4031
4177
|
function initProject(options) {
|
|
4032
4178
|
const out = options.stdout ?? console;
|
|
4033
|
-
const rootDir = (0,
|
|
4034
|
-
const configPath = (0,
|
|
4179
|
+
const rootDir = (0, import_node_path12.resolve)(options.rootDir ?? process.cwd());
|
|
4180
|
+
const configPath = (0, import_node_path12.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4035
4181
|
if ((0, import_node_fs13.existsSync)(configPath) && !options.force) {
|
|
4036
4182
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
4037
4183
|
}
|
|
@@ -4048,12 +4194,12 @@ function initProject(options) {
|
|
|
4048
4194
|
}
|
|
4049
4195
|
}
|
|
4050
4196
|
const aiProvider = options.aiProvider ?? "anthropic";
|
|
4051
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4052
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4053
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
4197
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(configPath), { recursive: true });
|
|
4198
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
4199
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, ".odla"), { recursive: true });
|
|
4054
4200
|
(0, import_node_fs13.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
4055
|
-
writeIfMissing((0,
|
|
4056
|
-
writeIfMissing((0,
|
|
4201
|
+
writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
4202
|
+
writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
4057
4203
|
ensureGitignore(rootDir);
|
|
4058
4204
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
4059
4205
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
@@ -4294,7 +4440,7 @@ async function resolveVaultWrite(options) {
|
|
|
4294
4440
|
// src/skill.ts
|
|
4295
4441
|
var import_node_fs14 = require("fs");
|
|
4296
4442
|
var import_node_os2 = require("os");
|
|
4297
|
-
var
|
|
4443
|
+
var import_node_path13 = require("path");
|
|
4298
4444
|
var import_node_url2 = require("url");
|
|
4299
4445
|
|
|
4300
4446
|
// src/skill-adapters.ts
|
|
@@ -4373,8 +4519,8 @@ function installSkill(options = {}) {
|
|
|
4373
4519
|
const files = listFiles(sourceDir);
|
|
4374
4520
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
4375
4521
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
4376
|
-
const root = (0,
|
|
4377
|
-
const home = (0,
|
|
4522
|
+
const root = (0, import_node_path13.resolve)(options.dir ?? process.cwd());
|
|
4523
|
+
const home = (0, import_node_path13.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
|
|
4378
4524
|
const plans = /* @__PURE__ */ new Map();
|
|
4379
4525
|
const targets = /* @__PURE__ */ new Map();
|
|
4380
4526
|
const rememberTarget = (harness, target) => {
|
|
@@ -4388,48 +4534,48 @@ function installSkill(options = {}) {
|
|
|
4388
4534
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
4389
4535
|
};
|
|
4390
4536
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
4391
|
-
for (const rel of files) plan((0,
|
|
4537
|
+
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);
|
|
4392
4538
|
};
|
|
4393
4539
|
let targetDir;
|
|
4394
4540
|
if (options.global) {
|
|
4395
|
-
const claudeRoot = (0,
|
|
4396
|
-
const codexRoot = (0,
|
|
4541
|
+
const claudeRoot = (0, import_node_path13.join)(home, ".claude", "skills");
|
|
4542
|
+
const codexRoot = (0, import_node_path13.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path13.join)(home, ".codex"), "skills");
|
|
4397
4543
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
4398
4544
|
for (const harness of harnesses) {
|
|
4399
4545
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
4400
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
4546
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path13.dirname)((0, import_node_path13.dirname)(codexRoot)));
|
|
4401
4547
|
rememberTarget(harness, skillRoot);
|
|
4402
4548
|
}
|
|
4403
4549
|
} else {
|
|
4404
|
-
const sharedRoot = (0,
|
|
4550
|
+
const sharedRoot = (0, import_node_path13.join)(root, ".agents", "skills");
|
|
4405
4551
|
planSkillTree(sharedRoot);
|
|
4406
|
-
const claudeRoot = (0,
|
|
4552
|
+
const claudeRoot = (0, import_node_path13.join)(root, ".claude", "skills");
|
|
4407
4553
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
4408
4554
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
4409
4555
|
if (harnesses.includes("claude")) {
|
|
4410
4556
|
for (const skill of skillNames(files)) {
|
|
4411
|
-
const canonical = (0, import_node_fs14.readFileSync)((0,
|
|
4412
|
-
plan((0,
|
|
4557
|
+
const canonical = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
4558
|
+
plan((0, import_node_path13.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
|
|
4413
4559
|
}
|
|
4414
4560
|
rememberTarget("claude", claudeRoot);
|
|
4415
4561
|
}
|
|
4416
4562
|
if (harnesses.includes("cursor")) {
|
|
4417
|
-
const cursorRule = (0,
|
|
4563
|
+
const cursorRule = (0, import_node_path13.join)(root, ".cursor", "rules", "odla.mdc");
|
|
4418
4564
|
plan(cursorRule, CURSOR_RULE);
|
|
4419
4565
|
rememberTarget("cursor", cursorRule);
|
|
4420
4566
|
}
|
|
4421
4567
|
if (harnesses.includes("agents")) {
|
|
4422
|
-
const agentsFile = (0,
|
|
4568
|
+
const agentsFile = (0, import_node_path13.join)(root, "AGENTS.md");
|
|
4423
4569
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4424
4570
|
rememberTarget("agents", agentsFile);
|
|
4425
4571
|
}
|
|
4426
4572
|
if (harnesses.includes("copilot")) {
|
|
4427
|
-
const copilotFile = (0,
|
|
4573
|
+
const copilotFile = (0, import_node_path13.join)(root, ".github", "copilot-instructions.md");
|
|
4428
4574
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4429
4575
|
rememberTarget("copilot", copilotFile);
|
|
4430
4576
|
}
|
|
4431
4577
|
if (harnesses.includes("gemini")) {
|
|
4432
|
-
const geminiFile = (0,
|
|
4578
|
+
const geminiFile = (0, import_node_path13.join)(root, "GEMINI.md");
|
|
4433
4579
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4434
4580
|
rememberTarget("gemini", geminiFile);
|
|
4435
4581
|
}
|
|
@@ -4465,7 +4611,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
4465
4611
|
}
|
|
4466
4612
|
for (const file of plans.values()) {
|
|
4467
4613
|
if (!(0, import_node_fs14.existsSync)(file.target) || (0, import_node_fs14.readFileSync)(file.target, "utf8") !== file.content) {
|
|
4468
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
4614
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(file.target), { recursive: true });
|
|
4469
4615
|
(0, import_node_fs14.writeFileSync)(file.target, file.content);
|
|
4470
4616
|
}
|
|
4471
4617
|
}
|
|
@@ -4485,7 +4631,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
4485
4631
|
};
|
|
4486
4632
|
}
|
|
4487
4633
|
function pathsUnder(root, paths) {
|
|
4488
|
-
return [...paths].map((path) => (0,
|
|
4634
|
+
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();
|
|
4489
4635
|
}
|
|
4490
4636
|
function normalizeHarnesses(values, global) {
|
|
4491
4637
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -4530,13 +4676,13 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
4530
4676
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
4531
4677
|
}
|
|
4532
4678
|
function symlinkedComponent(boundary, target) {
|
|
4533
|
-
const rel = (0,
|
|
4534
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
4679
|
+
const rel = (0, import_node_path13.relative)(boundary, target);
|
|
4680
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path13.sep}`) || (0, import_node_path13.isAbsolute)(rel)) {
|
|
4535
4681
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
4536
4682
|
}
|
|
4537
4683
|
let current = boundary;
|
|
4538
|
-
for (const part of rel.split(
|
|
4539
|
-
current = (0,
|
|
4684
|
+
for (const part of rel.split(import_node_path13.sep).filter(Boolean)) {
|
|
4685
|
+
current = (0, import_node_path13.join)(current, part);
|
|
4540
4686
|
try {
|
|
4541
4687
|
if ((0, import_node_fs14.lstatSync)(current).isSymbolicLink()) return current;
|
|
4542
4688
|
} catch (error) {
|
|
@@ -4553,9 +4699,9 @@ function listFiles(dir) {
|
|
|
4553
4699
|
const results = [];
|
|
4554
4700
|
const walk = (current) => {
|
|
4555
4701
|
for (const entry of (0, import_node_fs14.readdirSync)(current, { withFileTypes: true })) {
|
|
4556
|
-
const path = (0,
|
|
4702
|
+
const path = (0, import_node_path13.join)(current, entry.name);
|
|
4557
4703
|
if (entry.isDirectory()) walk(path);
|
|
4558
|
-
else results.push((0,
|
|
4704
|
+
else results.push((0, import_node_path13.relative)(dir, path));
|
|
4559
4705
|
}
|
|
4560
4706
|
};
|
|
4561
4707
|
walk(dir);
|
|
@@ -4867,7 +5013,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
4867
5013
|
// src/code-connect.ts
|
|
4868
5014
|
var import_node_fs15 = require("fs");
|
|
4869
5015
|
var import_node_os4 = require("os");
|
|
4870
|
-
var
|
|
5016
|
+
var import_node_path15 = require("path");
|
|
4871
5017
|
|
|
4872
5018
|
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
4873
5019
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -4953,15 +5099,15 @@ function encodeAgentInput(message2) {
|
|
|
4953
5099
|
// ../harness/dist/chunk-PHXQH4YM.js
|
|
4954
5100
|
var import_child_process = require("child_process");
|
|
4955
5101
|
var import_fs = require("fs");
|
|
4956
|
-
var
|
|
5102
|
+
var import_promises3 = require("fs/promises");
|
|
4957
5103
|
var import_path = require("path");
|
|
4958
5104
|
var import_process = require("process");
|
|
4959
|
-
var
|
|
5105
|
+
var import_promises4 = require("fs/promises");
|
|
4960
5106
|
var import_os = require("os");
|
|
4961
5107
|
var import_path2 = require("path");
|
|
4962
5108
|
var import_child_process2 = require("child_process");
|
|
4963
5109
|
var import_path3 = require("path");
|
|
4964
|
-
var
|
|
5110
|
+
var import_promises5 = require("fs/promises");
|
|
4965
5111
|
var import_os2 = require("os");
|
|
4966
5112
|
var import_path4 = require("path");
|
|
4967
5113
|
var import_child_process3 = require("child_process");
|
|
@@ -4972,7 +5118,7 @@ function assertPinnedImage(image) {
|
|
|
4972
5118
|
async function commandAvailable(engine) {
|
|
4973
5119
|
for (const directory of (process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean)) {
|
|
4974
5120
|
try {
|
|
4975
|
-
await (0,
|
|
5121
|
+
await (0, import_promises3.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
|
|
4976
5122
|
return true;
|
|
4977
5123
|
} catch {
|
|
4978
5124
|
}
|
|
@@ -5258,18 +5404,18 @@ async function gitBlobs(cwd, entries, maxBytes) {
|
|
|
5258
5404
|
}
|
|
5259
5405
|
async function materializeGitTree(source, commitSha, options = {}) {
|
|
5260
5406
|
if (!/^[0-9a-f]{40}$/.test(commitSha)) throw new TypeError("Git tree requires an exact commit SHA");
|
|
5261
|
-
const sourceDir = await (0,
|
|
5407
|
+
const sourceDir = await (0, import_promises4.realpath)((0, import_path2.resolve)(source));
|
|
5262
5408
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5263
5409
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
5264
5410
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
5265
|
-
const entries = inventory.flatMap((
|
|
5266
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
5411
|
+
const entries = inventory.flatMap((record10) => {
|
|
5412
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record10);
|
|
5267
5413
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
5268
5414
|
});
|
|
5269
5415
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
5270
|
-
const root = await (0,
|
|
5416
|
+
const root = await (0, import_promises4.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
|
|
5271
5417
|
const targetRoot = (0, import_path2.join)(root, "source");
|
|
5272
|
-
await (0,
|
|
5418
|
+
await (0, import_promises4.mkdir)(targetRoot);
|
|
5273
5419
|
let byteCount = 0;
|
|
5274
5420
|
try {
|
|
5275
5421
|
const blobs = await gitBlobs(sourceDir, entries, maxBytes);
|
|
@@ -5279,18 +5425,18 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
5279
5425
|
if (byteCount > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
|
|
5280
5426
|
const target = (0, import_path2.resolve)(targetRoot, entry.path);
|
|
5281
5427
|
if (!target.startsWith(`${(0, import_path2.resolve)(targetRoot)}${import_path2.sep}`)) throw new TypeError("Git tree path escapes workspace");
|
|
5282
|
-
await (0,
|
|
5283
|
-
await (0,
|
|
5428
|
+
await (0, import_promises4.mkdir)((0, import_path2.resolve)(target, ".."), { recursive: true });
|
|
5429
|
+
await (0, import_promises4.writeFile)(target, content2, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
|
|
5284
5430
|
}
|
|
5285
5431
|
return {
|
|
5286
5432
|
root,
|
|
5287
5433
|
sourceDir: targetRoot,
|
|
5288
5434
|
fileCount: entries.length,
|
|
5289
5435
|
byteCount,
|
|
5290
|
-
cleanup: () => (0,
|
|
5436
|
+
cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
|
|
5291
5437
|
};
|
|
5292
5438
|
} catch (error) {
|
|
5293
|
-
await (0,
|
|
5439
|
+
await (0, import_promises4.rm)(root, { recursive: true, force: true });
|
|
5294
5440
|
throw error;
|
|
5295
5441
|
}
|
|
5296
5442
|
}
|
|
@@ -5298,7 +5444,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5298
5444
|
const files = [];
|
|
5299
5445
|
let bytes = 0;
|
|
5300
5446
|
const walk = async (dir) => {
|
|
5301
|
-
for (const entry of await (0,
|
|
5447
|
+
for (const entry of await (0, import_promises5.readdir)(dir, { withFileTypes: true })) {
|
|
5302
5448
|
if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
5303
5449
|
if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
|
|
5304
5450
|
const path = (0, import_path4.join)(dir, entry.name);
|
|
@@ -5308,7 +5454,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5308
5454
|
continue;
|
|
5309
5455
|
}
|
|
5310
5456
|
if (!entry.isFile()) continue;
|
|
5311
|
-
const metadata2 = await (0,
|
|
5457
|
+
const metadata2 = await (0, import_promises5.stat)(path);
|
|
5312
5458
|
bytes += metadata2.size;
|
|
5313
5459
|
if (files.length + 1 > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
5314
5460
|
if (bytes > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
|
|
@@ -5357,7 +5503,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5357
5503
|
if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
|
|
5358
5504
|
let metadata2;
|
|
5359
5505
|
try {
|
|
5360
|
-
metadata2 = await (0,
|
|
5506
|
+
metadata2 = await (0, import_promises5.lstat)(source);
|
|
5361
5507
|
} catch (error) {
|
|
5362
5508
|
if (error.code === "ENOENT") continue;
|
|
5363
5509
|
throw error;
|
|
@@ -5372,9 +5518,9 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
5372
5518
|
async function copyTree(files, destination) {
|
|
5373
5519
|
for (const file of files) {
|
|
5374
5520
|
const target = (0, import_path4.join)(destination, file.relativePath);
|
|
5375
|
-
await (0,
|
|
5376
|
-
await (0,
|
|
5377
|
-
await (0,
|
|
5521
|
+
await (0, import_promises5.mkdir)((0, import_path4.resolve)(target, ".."), { recursive: true });
|
|
5522
|
+
await (0, import_promises5.copyFile)(file.source, target);
|
|
5523
|
+
await (0, import_promises5.chmod)(target, file.mode);
|
|
5378
5524
|
}
|
|
5379
5525
|
}
|
|
5380
5526
|
async function captureGitDiff(root, maxBytes) {
|
|
@@ -5411,13 +5557,13 @@ async function captureGitDiff(root, maxBytes) {
|
|
|
5411
5557
|
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");
|
|
5412
5558
|
}
|
|
5413
5559
|
async function stageWorkspace(source, options = {}) {
|
|
5414
|
-
const sourceDir = await (0,
|
|
5415
|
-
const sourceStat = await (0,
|
|
5560
|
+
const sourceDir = await (0, import_promises5.realpath)((0, import_path4.resolve)(source));
|
|
5561
|
+
const sourceStat = await (0, import_promises5.stat)(sourceDir);
|
|
5416
5562
|
if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
|
|
5417
|
-
const root = await (0,
|
|
5563
|
+
const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
|
|
5418
5564
|
const baselineDir = (0, import_path4.join)(root, "baseline");
|
|
5419
5565
|
const workspaceDir = (0, import_path4.join)(root, "workspace");
|
|
5420
|
-
await Promise.all([(0,
|
|
5566
|
+
await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
|
|
5421
5567
|
try {
|
|
5422
5568
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5423
5569
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
@@ -5430,26 +5576,26 @@ async function stageWorkspace(source, options = {}) {
|
|
|
5430
5576
|
fileCount: files.length,
|
|
5431
5577
|
byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
|
|
5432
5578
|
patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
|
|
5433
|
-
cleanup: () => (0,
|
|
5579
|
+
cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
|
|
5434
5580
|
};
|
|
5435
5581
|
} catch (error) {
|
|
5436
|
-
await (0,
|
|
5582
|
+
await (0, import_promises5.rm)(root, { recursive: true, force: true });
|
|
5437
5583
|
throw error;
|
|
5438
5584
|
}
|
|
5439
5585
|
}
|
|
5440
5586
|
async function stageWorkspacePair(baselineSource, workspaceSource, options = {}) {
|
|
5441
|
-
const baselineDirSource = await (0,
|
|
5442
|
-
const workspaceDirSource = await (0,
|
|
5587
|
+
const baselineDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(baselineSource));
|
|
5588
|
+
const workspaceDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(workspaceSource));
|
|
5443
5589
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
5444
5590
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
5445
5591
|
const [baselineFiles, workspaceFiles] = await Promise.all([
|
|
5446
5592
|
sourceFiles(baselineDirSource, maxFiles, maxBytes),
|
|
5447
5593
|
sourceFiles(workspaceDirSource, maxFiles, maxBytes)
|
|
5448
5594
|
]);
|
|
5449
|
-
const root = await (0,
|
|
5595
|
+
const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
|
|
5450
5596
|
const baselineDir = (0, import_path4.join)(root, "baseline");
|
|
5451
5597
|
const workspaceDir = (0, import_path4.join)(root, "workspace");
|
|
5452
|
-
await Promise.all([(0,
|
|
5598
|
+
await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
|
|
5453
5599
|
try {
|
|
5454
5600
|
await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
|
|
5455
5601
|
return {
|
|
@@ -5459,17 +5605,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
5459
5605
|
fileCount: workspaceFiles.length,
|
|
5460
5606
|
byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
|
|
5461
5607
|
patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
|
|
5462
|
-
cleanup: () => (0,
|
|
5608
|
+
cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
|
|
5463
5609
|
};
|
|
5464
5610
|
} catch (error) {
|
|
5465
|
-
await (0,
|
|
5611
|
+
await (0, import_promises5.rm)(root, { recursive: true, force: true });
|
|
5466
5612
|
throw error;
|
|
5467
5613
|
}
|
|
5468
5614
|
}
|
|
5469
5615
|
|
|
5470
5616
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
5471
5617
|
var import_crypto = require("crypto");
|
|
5472
|
-
var
|
|
5618
|
+
var import_promises6 = require("fs/promises");
|
|
5473
5619
|
var import_path5 = require("path");
|
|
5474
5620
|
|
|
5475
5621
|
// ../camel/dist/chunk-7FHPOQVP.js
|
|
@@ -5511,8 +5657,8 @@ function normalize(value2) {
|
|
|
5511
5657
|
if (Array.isArray(value2)) return value2.map(normalize);
|
|
5512
5658
|
if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
|
|
5513
5659
|
if (typeof value2 === "object") {
|
|
5514
|
-
const
|
|
5515
|
-
return Object.fromEntries(Object.keys(
|
|
5660
|
+
const record10 = value2;
|
|
5661
|
+
return Object.fromEntries(Object.keys(record10).filter((key) => record10[key] !== void 0).sort().map((key) => [key, normalize(record10[key])]));
|
|
5516
5662
|
}
|
|
5517
5663
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
5518
5664
|
}
|
|
@@ -5806,19 +5952,19 @@ function validateSnapshot(snapshot, limits) {
|
|
|
5806
5952
|
|
|
5807
5953
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
5808
5954
|
var import_child_process4 = require("child_process");
|
|
5809
|
-
var
|
|
5955
|
+
var import_promises7 = require("fs/promises");
|
|
5810
5956
|
var import_path6 = require("path");
|
|
5811
5957
|
var import_child_process5 = require("child_process");
|
|
5812
5958
|
var import_process2 = require("process");
|
|
5813
5959
|
var import_crypto2 = require("crypto");
|
|
5814
5960
|
var import_crypto3 = require("crypto");
|
|
5815
5961
|
var import_fs2 = require("fs");
|
|
5816
|
-
var import_promises7 = require("fs/promises");
|
|
5817
|
-
var import_path7 = require("path");
|
|
5818
5962
|
var import_promises8 = require("fs/promises");
|
|
5963
|
+
var import_path7 = require("path");
|
|
5964
|
+
var import_promises9 = require("fs/promises");
|
|
5819
5965
|
var import_os3 = require("os");
|
|
5820
5966
|
var import_path8 = require("path");
|
|
5821
|
-
var
|
|
5967
|
+
var import_promises10 = require("fs/promises");
|
|
5822
5968
|
var import_path9 = require("path");
|
|
5823
5969
|
|
|
5824
5970
|
// ../camel/dist/chunk-LAXU2AVK.js
|
|
@@ -6102,7 +6248,7 @@ var import_crypto4 = require("crypto");
|
|
|
6102
6248
|
async function digestStagedWorkspace(root, limits) {
|
|
6103
6249
|
const files = [];
|
|
6104
6250
|
const walk = async (directory) => {
|
|
6105
|
-
const entries = await (0,
|
|
6251
|
+
const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
|
|
6106
6252
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
6107
6253
|
if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
|
|
6108
6254
|
const target = (0, import_path5.resolve)(directory, entry.name);
|
|
@@ -6117,7 +6263,7 @@ async function digestStagedWorkspace(root, limits) {
|
|
|
6117
6263
|
const hash = (0, import_crypto.createHash)("sha256");
|
|
6118
6264
|
let bytes = 0;
|
|
6119
6265
|
for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
|
|
6120
|
-
const content2 = await (0,
|
|
6266
|
+
const content2 = await (0, import_promises6.readFile)(file.target);
|
|
6121
6267
|
bytes += Buffer.byteLength(file.path) + content2.byteLength;
|
|
6122
6268
|
if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
|
|
6123
6269
|
hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content2.byteLength}:`);
|
|
@@ -6443,7 +6589,7 @@ async function applyCodePatch(workspaceDir, patch2, paths) {
|
|
|
6443
6589
|
await gitApply(workspaceDir, patch2, false);
|
|
6444
6590
|
for (const path of paths) {
|
|
6445
6591
|
try {
|
|
6446
|
-
const info = await (0,
|
|
6592
|
+
const info = await (0, import_promises7.lstat)(resolveCodePath(workspaceDir, path));
|
|
6447
6593
|
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
|
|
6448
6594
|
throw new TypeError("patch created a non-regular workspace entry");
|
|
6449
6595
|
}
|
|
@@ -6734,7 +6880,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
6734
6880
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
6735
6881
|
try {
|
|
6736
6882
|
const path = (0, import_path7.join)(workspaceDir, artifact.path);
|
|
6737
|
-
const info = await (0,
|
|
6883
|
+
const info = await (0, import_promises8.lstat)(path);
|
|
6738
6884
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
6739
6885
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
6740
6886
|
} else if (info.size > artifact.maximumBytes) {
|
|
@@ -6915,9 +7061,9 @@ var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_mod
|
|
|
6915
7061
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
6916
7062
|
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.tmpdir)()) {
|
|
6917
7063
|
if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
|
|
6918
|
-
const root = await (0,
|
|
7064
|
+
const root = await (0, import_promises9.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
|
|
6919
7065
|
const sourceDir = (0, import_path8.join)(root, "source");
|
|
6920
|
-
await (0,
|
|
7066
|
+
await (0, import_promises9.mkdir)(sourceDir);
|
|
6921
7067
|
const seen = /* @__PURE__ */ new Set();
|
|
6922
7068
|
let bytes = 0;
|
|
6923
7069
|
try {
|
|
@@ -6929,8 +7075,8 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
|
|
|
6929
7075
|
if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
|
|
6930
7076
|
const target = (0, import_path8.resolve)(sourceDir, file.path);
|
|
6931
7077
|
if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
6932
|
-
await (0,
|
|
6933
|
-
await (0,
|
|
7078
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7079
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
6934
7080
|
}
|
|
6935
7081
|
for (const reference of snapshot.references ?? []) {
|
|
6936
7082
|
validateAlias(reference.alias);
|
|
@@ -6944,13 +7090,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
|
|
|
6944
7090
|
if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
|
|
6945
7091
|
const target = (0, import_path8.resolve)(sourceDir, path);
|
|
6946
7092
|
if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
6947
|
-
await (0,
|
|
6948
|
-
await (0,
|
|
7093
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7094
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
6949
7095
|
}
|
|
6950
7096
|
}
|
|
6951
|
-
return { sourceDir, cleanup: () => (0,
|
|
7097
|
+
return { sourceDir, cleanup: () => (0, import_promises9.rm)(root, { recursive: true, force: true }) };
|
|
6952
7098
|
} catch (cause) {
|
|
6953
|
-
await (0,
|
|
7099
|
+
await (0, import_promises9.rm)(root, { recursive: true, force: true });
|
|
6954
7100
|
throw cause;
|
|
6955
7101
|
}
|
|
6956
7102
|
}
|
|
@@ -6971,8 +7117,8 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
6971
7117
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
6972
7118
|
const target = (0, import_path8.resolve)(root, path);
|
|
6973
7119
|
if (!target.startsWith(`${(0, import_path8.resolve)(root)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
6974
|
-
await (0,
|
|
6975
|
-
await (0,
|
|
7120
|
+
await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
7121
|
+
await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
6976
7122
|
}
|
|
6977
7123
|
}
|
|
6978
7124
|
}
|
|
@@ -7158,11 +7304,11 @@ async function read(context, request2, options, policy) {
|
|
|
7158
7304
|
const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
|
|
7159
7305
|
if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
|
|
7160
7306
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
7161
|
-
const info = await (0,
|
|
7307
|
+
const info = await (0, import_promises10.stat)(target);
|
|
7162
7308
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
7163
7309
|
throw new TypeError("file is not a bounded regular source file");
|
|
7164
7310
|
}
|
|
7165
|
-
const source = await (0,
|
|
7311
|
+
const source = await (0, import_promises10.readFile)(target);
|
|
7166
7312
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
7167
7313
|
const lines = source.toString("utf8").split("\n");
|
|
7168
7314
|
const content2 = lines.slice(startLine - 1, endLine).join("\n");
|
|
@@ -7239,7 +7385,7 @@ function policyContext(context, request2, options, extra) {
|
|
|
7239
7385
|
async function registeredFiles(root, limit) {
|
|
7240
7386
|
const paths = [];
|
|
7241
7387
|
const walk = async (directory) => {
|
|
7242
|
-
for (const entry of await (0,
|
|
7388
|
+
for (const entry of await (0, import_promises10.readdir)(directory, { withFileTypes: true })) {
|
|
7243
7389
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
7244
7390
|
const target = (0, import_path9.resolve)(directory, entry.name);
|
|
7245
7391
|
if (entry.isDirectory()) await walk(target);
|
|
@@ -7838,8 +7984,8 @@ function hostedPollTimeout(value2 = 10 * 6e4) {
|
|
|
7838
7984
|
}
|
|
7839
7985
|
async function waitForHostedPoll(milliseconds, signal) {
|
|
7840
7986
|
if (signal?.aborted) throw signal.reason ?? new DOMException("aborted", "AbortError");
|
|
7841
|
-
await new Promise((
|
|
7842
|
-
const timer = setTimeout(
|
|
7987
|
+
await new Promise((resolve13, reject) => {
|
|
7988
|
+
const timer = setTimeout(resolve13, milliseconds);
|
|
7843
7989
|
signal?.addEventListener("abort", () => {
|
|
7844
7990
|
clearTimeout(timer);
|
|
7845
7991
|
reject(signal.reason ?? new DOMException("aborted", "AbortError"));
|
|
@@ -8024,9 +8170,9 @@ function digestText(value2) {
|
|
|
8024
8170
|
// src/code-images.ts
|
|
8025
8171
|
var import_node_child_process6 = require("child_process");
|
|
8026
8172
|
var import_node_crypto3 = require("crypto");
|
|
8027
|
-
var
|
|
8173
|
+
var import_promises11 = require("fs/promises");
|
|
8028
8174
|
var import_node_os3 = require("os");
|
|
8029
|
-
var
|
|
8175
|
+
var import_node_path14 = require("path");
|
|
8030
8176
|
var import_node_url3 = require("url");
|
|
8031
8177
|
|
|
8032
8178
|
// src/code-runtime-config.ts
|
|
@@ -8106,16 +8252,16 @@ function embeddedPiAssetPath() {
|
|
|
8106
8252
|
return (0, import_node_url3.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
|
|
8107
8253
|
}
|
|
8108
8254
|
async function embeddedPiImageName() {
|
|
8109
|
-
const bundle = await (0,
|
|
8255
|
+
const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
|
|
8110
8256
|
throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
|
|
8111
8257
|
});
|
|
8112
8258
|
return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
|
|
8113
8259
|
}
|
|
8114
8260
|
async function buildEmbeddedPiImage(engine, image, run) {
|
|
8115
|
-
const context = await (0,
|
|
8261
|
+
const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
|
|
8116
8262
|
try {
|
|
8117
|
-
await (0,
|
|
8118
|
-
await (0,
|
|
8263
|
+
await (0, import_promises11.copyFile)(embeddedPiAssetPath(), (0, import_node_path14.join)(context, "pi-agent.js"));
|
|
8264
|
+
await (0, import_promises11.writeFile)((0, import_node_path14.join)(context, "Dockerfile"), [
|
|
8119
8265
|
`FROM ${CODE_NODE_IMAGE}`,
|
|
8120
8266
|
"COPY pi-agent.js /opt/odla/pi-agent.js",
|
|
8121
8267
|
"WORKDIR /workspace",
|
|
@@ -8124,14 +8270,14 @@ async function buildEmbeddedPiImage(engine, image, run) {
|
|
|
8124
8270
|
].join("\n"), { mode: 384 });
|
|
8125
8271
|
await run(engine, ["build", "--tag", image, context], "inherit");
|
|
8126
8272
|
} finally {
|
|
8127
|
-
await (0,
|
|
8273
|
+
await (0, import_promises11.rm)(context, { recursive: true, force: true });
|
|
8128
8274
|
}
|
|
8129
8275
|
}
|
|
8130
8276
|
|
|
8131
8277
|
// src/code-connect.ts
|
|
8132
8278
|
async function codeConnect(options) {
|
|
8133
8279
|
const cwd = options.cwd ?? process.cwd();
|
|
8134
|
-
const configPath = (0,
|
|
8280
|
+
const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
|
|
8135
8281
|
const cfg = (0, import_node_fs15.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
8136
8282
|
const requestedAppId = options.appId?.trim();
|
|
8137
8283
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
@@ -8500,10 +8646,8 @@ function requireName(parsed) {
|
|
|
8500
8646
|
return name;
|
|
8501
8647
|
}
|
|
8502
8648
|
|
|
8503
|
-
// src/help.ts
|
|
8504
|
-
|
|
8505
|
-
output.log(`odla-ai
|
|
8506
|
-
|
|
8649
|
+
// src/help-usage.ts
|
|
8650
|
+
var USAGE_SECTION = `
|
|
8507
8651
|
Start here:
|
|
8508
8652
|
odla-ai runbook ask "<question>" The current procedure, from odla's own
|
|
8509
8653
|
runbooks. Ask BEFORE searching the web or
|
|
@@ -8537,6 +8681,7 @@ Usage:
|
|
|
8537
8681
|
odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
8538
8682
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
8539
8683
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
8684
|
+
odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
|
|
8540
8685
|
odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
8541
8686
|
odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
8542
8687
|
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
@@ -8572,7 +8717,6 @@ Usage:
|
|
|
8572
8717
|
odla-ai context remove <name> --yes [--json]
|
|
8573
8718
|
odla-ai o11y status [--app <id>] [--context <name>] [--platform https://odla.ai] [--env prod] [--minutes 60] [--json]
|
|
8574
8719
|
odla-ai platform status [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
8575
|
-
odla-ai platform chat-credentials rotate [--context <name>] [--email <odla-account>] [--wrangler-config <path>] [--expected-version <id>] [--json] --yes
|
|
8576
8720
|
odla-ai whoami [--context <name>] [--platform https://odla.ai] [--json]
|
|
8577
8721
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
8578
8722
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
@@ -8617,8 +8761,12 @@ Usage:
|
|
|
8617
8761
|
odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
|
|
8618
8762
|
odla-ai secrets set <name> --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
|
|
8619
8763
|
odla-ai secrets set-clerk-key --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
|
|
8620
|
-
odla-ai version
|
|
8764
|
+
odla-ai version`;
|
|
8621
8765
|
|
|
8766
|
+
// src/help.ts
|
|
8767
|
+
function printHelp(output = console) {
|
|
8768
|
+
output.log(`odla-ai
|
|
8769
|
+
${USAGE_SECTION}
|
|
8622
8770
|
Commands:
|
|
8623
8771
|
agent Inspect durable agent wakeups and explicitly requeue a
|
|
8624
8772
|
dead-lettered job; JSON output is stable for remote operators.
|
|
@@ -9067,7 +9215,7 @@ function jsonl(ctx, parsed, value2) {
|
|
|
9067
9215
|
}
|
|
9068
9216
|
async function discussWatch(ctx, topicId, parsed) {
|
|
9069
9217
|
if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
|
|
9070
|
-
const sleep = ctx.sleep ?? ((ms) => new Promise((
|
|
9218
|
+
const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
|
|
9071
9219
|
const now = ctx.now ?? Date.now;
|
|
9072
9220
|
const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
|
|
9073
9221
|
const timeoutSeconds = numberOpt2(parsed, "timeout");
|
|
@@ -9405,8 +9553,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
9405
9553
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
9406
9554
|
}
|
|
9407
9555
|
async function pmGet(ctx, entity, id) {
|
|
9408
|
-
const { record:
|
|
9409
|
-
emit2(ctx,
|
|
9556
|
+
const { record: record10 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
9557
|
+
emit2(ctx, record10, () => printRecord(ctx, entity, record10));
|
|
9410
9558
|
}
|
|
9411
9559
|
async function pmSet(ctx, entity, id, parsed) {
|
|
9412
9560
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
@@ -9451,9 +9599,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
9451
9599
|
]);
|
|
9452
9600
|
const handoff = {
|
|
9453
9601
|
appId,
|
|
9454
|
-
unmetGoals: goals.filter((
|
|
9455
|
-
activeTasks: tasks.filter((
|
|
9456
|
-
openBugs: bugs.filter((
|
|
9602
|
+
unmetGoals: goals.filter((record10) => record10.status !== "met"),
|
|
9603
|
+
activeTasks: tasks.filter((record10) => record10.column !== "done"),
|
|
9604
|
+
openBugs: bugs.filter((record10) => record10.status !== "fixed" && record10.status !== "wontfix")
|
|
9457
9605
|
};
|
|
9458
9606
|
const result = {
|
|
9459
9607
|
...handoff,
|
|
@@ -9472,10 +9620,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
9472
9620
|
]) {
|
|
9473
9621
|
ctx.out.log(`${label}:`);
|
|
9474
9622
|
if (!records.length) ctx.out.log("- (none)");
|
|
9475
|
-
else for (const
|
|
9623
|
+
else for (const record10 of records) printRecord(
|
|
9476
9624
|
ctx,
|
|
9477
9625
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
9478
|
-
|
|
9626
|
+
record10
|
|
9479
9627
|
);
|
|
9480
9628
|
}
|
|
9481
9629
|
});
|
|
@@ -9666,167 +9814,12 @@ function age(input) {
|
|
|
9666
9814
|
return `${Math.round(input / 6e4)}m`;
|
|
9667
9815
|
}
|
|
9668
9816
|
|
|
9669
|
-
// src/platform-chat-credential-command.ts
|
|
9670
|
-
var import_node_process10 = __toESM(require("process"), 1);
|
|
9671
|
-
async function rotatePlatformChatCredential(parsed, deps) {
|
|
9672
|
-
assertArgs(
|
|
9673
|
-
parsed,
|
|
9674
|
-
[
|
|
9675
|
-
"config",
|
|
9676
|
-
"context",
|
|
9677
|
-
"platform",
|
|
9678
|
-
"token",
|
|
9679
|
-
"email",
|
|
9680
|
-
"open",
|
|
9681
|
-
"json",
|
|
9682
|
-
"yes",
|
|
9683
|
-
"wrangler-config",
|
|
9684
|
-
"expected-version"
|
|
9685
|
-
],
|
|
9686
|
-
3
|
|
9687
|
-
);
|
|
9688
|
-
if (parsed.options.yes !== true) {
|
|
9689
|
-
throw new Error(
|
|
9690
|
-
"platform chat-credentials rotate changes the production chat secret; pass --yes"
|
|
9691
|
-
);
|
|
9692
|
-
}
|
|
9693
|
-
const context = await resolveOperatorContext(parsed, {
|
|
9694
|
-
allowMissingConfig: true
|
|
9695
|
-
});
|
|
9696
|
-
const platform = context.platform.value;
|
|
9697
|
-
const doFetch = deps.fetch ?? fetch;
|
|
9698
|
-
const out = deps.stdout ?? console;
|
|
9699
|
-
const run = deps.runner ?? defaultRunner;
|
|
9700
|
-
const cwd = import_node_process10.default.cwd();
|
|
9701
|
-
const wranglerConfig = stringOpt(parsed.options["wrangler-config"]) ?? "packages/chat-agent/wrangler.jsonc";
|
|
9702
|
-
if (!await wranglerLoggedIn(run, cwd)) {
|
|
9703
|
-
throw new Error(
|
|
9704
|
-
'Wrangler is not authenticated; run "npx wrangler login" and retry'
|
|
9705
|
-
);
|
|
9706
|
-
}
|
|
9707
|
-
const priorHealth = await doFetch(`${platform}/health/services/chat`);
|
|
9708
|
-
const priorVersion = priorHealth.headers.get("x-odla-worker-version-id");
|
|
9709
|
-
await priorHealth.body?.cancel().catch(() => {
|
|
9710
|
-
});
|
|
9711
|
-
if (!priorVersion) {
|
|
9712
|
-
throw new Error(
|
|
9713
|
-
"chat health did not identify its current Worker version; refusing to rotate"
|
|
9714
|
-
);
|
|
9715
|
-
}
|
|
9716
|
-
const expectedVersion = stringOpt(parsed.options["expected-version"]);
|
|
9717
|
-
if (expectedVersion && priorVersion !== expectedVersion) {
|
|
9718
|
-
throw new Error(
|
|
9719
|
-
`chat health answered from version ${priorVersion}; expected ${expectedVersion}`
|
|
9720
|
-
);
|
|
9721
|
-
}
|
|
9722
|
-
const token = await resolveAdminPlatformToken({
|
|
9723
|
-
platform,
|
|
9724
|
-
scope: "platform:chat:credential:write",
|
|
9725
|
-
token: stringOpt(parsed.options.token),
|
|
9726
|
-
tokenFile: context.credentials.scopedTokenFile,
|
|
9727
|
-
email: stringOpt(parsed.options.email),
|
|
9728
|
-
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
9729
|
-
fetch: doFetch,
|
|
9730
|
-
stdout: out,
|
|
9731
|
-
openApprovalUrl: deps.openUrl,
|
|
9732
|
-
label: "odla CLI (rotate built-in Discussion responder)"
|
|
9733
|
-
});
|
|
9734
|
-
const mintedResponse = await doFetch(
|
|
9735
|
-
`${platform}/registry/platform/chat-credentials/rotate`,
|
|
9736
|
-
{
|
|
9737
|
-
method: "POST",
|
|
9738
|
-
headers: { authorization: `Bearer ${token}` }
|
|
9739
|
-
}
|
|
9740
|
-
);
|
|
9741
|
-
const minted = await mintedResponse.json().catch(() => null);
|
|
9742
|
-
if (!mintedResponse.ok) {
|
|
9743
|
-
throw new Error(
|
|
9744
|
-
`mint Discussion credential failed (HTTP ${mintedResponse.status}): ${apiMessage(minted)}`
|
|
9745
|
-
);
|
|
9746
|
-
}
|
|
9747
|
-
if (!isPlatformChatCredential(minted)) {
|
|
9748
|
-
throw new Error(
|
|
9749
|
-
"platform returned an invalid odla.platform-chat-credential/v1 envelope"
|
|
9750
|
-
);
|
|
9751
|
-
}
|
|
9752
|
-
const put = await wranglerPutSecret(run, {
|
|
9753
|
-
name: "ODLA_BOT_TOKENS",
|
|
9754
|
-
value: JSON.stringify({
|
|
9755
|
-
[minted.appId]: { [minted.principalId]: minted.key }
|
|
9756
|
-
}),
|
|
9757
|
-
configPath: wranglerConfig,
|
|
9758
|
-
cwd
|
|
9759
|
-
});
|
|
9760
|
-
if (put.code !== 0) {
|
|
9761
|
-
throw new Error(
|
|
9762
|
-
"Wrangler could not replace ODLA_BOT_TOKENS; the new scoped key was not activated"
|
|
9763
|
-
);
|
|
9764
|
-
}
|
|
9765
|
-
const version = await waitForRotatedChatHealth({
|
|
9766
|
-
fetch: doFetch,
|
|
9767
|
-
platform,
|
|
9768
|
-
priorVersion,
|
|
9769
|
-
wait: deps.pollWait
|
|
9770
|
-
});
|
|
9771
|
-
const result = {
|
|
9772
|
-
schemaVersion: "odla.platform-chat-credential-rotation/v1",
|
|
9773
|
-
appId: minted.appId,
|
|
9774
|
-
appIncarnation: minted.appIncarnation,
|
|
9775
|
-
principalId: minted.principalId,
|
|
9776
|
-
health: { ok: true, service: "odla-chat-agent", version }
|
|
9777
|
-
};
|
|
9778
|
-
if (parsed.options.json === true) {
|
|
9779
|
-
out.log(JSON.stringify(result, null, 2));
|
|
9780
|
-
} else {
|
|
9781
|
-
out.log(
|
|
9782
|
-
`rotated ${result.appId} ${result.principalId} ${result.health.version}`
|
|
9783
|
-
);
|
|
9784
|
-
}
|
|
9785
|
-
}
|
|
9786
|
-
async function waitForRotatedChatHealth(opts) {
|
|
9787
|
-
const wait2 = opts.wait ?? ((milliseconds) => new Promise((resolve12) => setTimeout(resolve12, milliseconds)));
|
|
9788
|
-
let lastStatus = 0;
|
|
9789
|
-
let lastVersion = null;
|
|
9790
|
-
let lastError = "private_service_unready";
|
|
9791
|
-
for (let attempt = 0; attempt < 60; attempt++) {
|
|
9792
|
-
const response2 = await opts.fetch(
|
|
9793
|
-
`${opts.platform}/health/services/chat`
|
|
9794
|
-
);
|
|
9795
|
-
const body = await response2.json().catch(() => null);
|
|
9796
|
-
const version = response2.headers.get("x-odla-worker-version-id");
|
|
9797
|
-
if (response2.ok && record7(body) && body.ok === true && body.service === "odla-chat-agent" && version && version !== opts.priorVersion) {
|
|
9798
|
-
return version;
|
|
9799
|
-
}
|
|
9800
|
-
lastStatus = response2.status;
|
|
9801
|
-
lastVersion = version;
|
|
9802
|
-
lastError = record7(body) && typeof body.error === "string" ? body.error : "private_service_unready";
|
|
9803
|
-
if (attempt < 59) await wait2(5e3);
|
|
9804
|
-
}
|
|
9805
|
-
throw new Error(
|
|
9806
|
-
`chat health did not converge on the rotated Worker deployment (HTTP ${lastStatus}, version ${lastVersion ?? "unknown"}, ${lastError})`
|
|
9807
|
-
);
|
|
9808
|
-
}
|
|
9809
|
-
function isPlatformChatCredential(value2) {
|
|
9810
|
-
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_");
|
|
9811
|
-
}
|
|
9812
|
-
function apiMessage(value2) {
|
|
9813
|
-
if (!record7(value2)) return "request failed";
|
|
9814
|
-
const error = record7(value2.error) ? value2.error : value2;
|
|
9815
|
-
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
9816
|
-
}
|
|
9817
|
-
function record7(value2) {
|
|
9818
|
-
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
9819
|
-
}
|
|
9820
|
-
|
|
9821
9817
|
// src/platform-command.ts
|
|
9822
9818
|
async function platformCommand(parsed, deps = {}) {
|
|
9823
9819
|
const action2 = parsed.positionals[1];
|
|
9824
9820
|
if (action2 === "status") {
|
|
9825
9821
|
return platformStatus(parsed, deps);
|
|
9826
9822
|
}
|
|
9827
|
-
if (action2 === "chat-credentials" && parsed.positionals[2] === "rotate") {
|
|
9828
|
-
return rotatePlatformChatCredential(parsed, deps);
|
|
9829
|
-
}
|
|
9830
9823
|
throw new Error(
|
|
9831
9824
|
`unknown platform action "${[
|
|
9832
9825
|
action2,
|
|
@@ -9864,7 +9857,7 @@ async function platformStatus(parsed, deps) {
|
|
|
9864
9857
|
const body = await response2.json().catch(() => null);
|
|
9865
9858
|
if (!response2.ok) {
|
|
9866
9859
|
throw new Error(
|
|
9867
|
-
`read platform status failed (HTTP ${response2.status}): ${
|
|
9860
|
+
`read platform status failed (HTTP ${response2.status}): ${apiMessage(body)}`
|
|
9868
9861
|
);
|
|
9869
9862
|
}
|
|
9870
9863
|
if (!isPlatformStatus(body)) {
|
|
@@ -9877,17 +9870,17 @@ async function platformStatus(parsed, deps) {
|
|
|
9877
9870
|
}
|
|
9878
9871
|
}
|
|
9879
9872
|
function isPlatformStatus(value2) {
|
|
9880
|
-
if (!
|
|
9881
|
-
if (!
|
|
9882
|
-
if (!
|
|
9873
|
+
if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
|
|
9874
|
+
if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
|
|
9875
|
+
if (!record7(value2.catalog) || !record7(value2.summary)) return false;
|
|
9883
9876
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
9884
9877
|
}
|
|
9885
|
-
function
|
|
9886
|
-
if (!
|
|
9887
|
-
const error =
|
|
9878
|
+
function apiMessage(value2) {
|
|
9879
|
+
if (!record7(value2)) return "request failed";
|
|
9880
|
+
const error = record7(value2.error) ? value2.error : value2;
|
|
9888
9881
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
9889
9882
|
}
|
|
9890
|
-
function
|
|
9883
|
+
function record7(value2) {
|
|
9891
9884
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
9892
9885
|
}
|
|
9893
9886
|
|
|
@@ -9928,7 +9921,7 @@ function statusVerdict(reads) {
|
|
|
9928
9921
|
severity: "degraded"
|
|
9929
9922
|
});
|
|
9930
9923
|
}
|
|
9931
|
-
const performance =
|
|
9924
|
+
const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
9932
9925
|
if (performance?.status === "unavailable") {
|
|
9933
9926
|
reasons.push({
|
|
9934
9927
|
source: "liveSync",
|
|
@@ -10009,7 +10002,7 @@ function statusVerdict(reads) {
|
|
|
10009
10002
|
reasons
|
|
10010
10003
|
};
|
|
10011
10004
|
}
|
|
10012
|
-
function
|
|
10005
|
+
function record8(value2) {
|
|
10013
10006
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10014
10007
|
}
|
|
10015
10008
|
function numeric2(value2) {
|
|
@@ -10037,7 +10030,7 @@ function printO11yStatus(status, out) {
|
|
|
10037
10030
|
out.log(
|
|
10038
10031
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
10039
10032
|
);
|
|
10040
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
10033
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
|
|
10041
10034
|
const requests = routes.reduce(
|
|
10042
10035
|
(total, row) => total + numeric3(row.requests),
|
|
10043
10036
|
0
|
|
@@ -10049,39 +10042,39 @@ function printO11yStatus(status, out) {
|
|
|
10049
10042
|
out.log(
|
|
10050
10043
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
10051
10044
|
);
|
|
10052
|
-
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(
|
|
10045
|
+
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
|
|
10053
10046
|
out.log(
|
|
10054
10047
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
10055
10048
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
10056
10049
|
).join(", ") : "none observed"}`
|
|
10057
10050
|
);
|
|
10058
10051
|
out.log(liveSyncLine(status.liveSync));
|
|
10059
|
-
const canaryDurations =
|
|
10052
|
+
const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
10060
10053
|
out.log(
|
|
10061
10054
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
10062
10055
|
);
|
|
10063
|
-
const collectorIngest =
|
|
10064
|
-
const collectorStorage =
|
|
10056
|
+
const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
10057
|
+
const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
10065
10058
|
out.log(
|
|
10066
10059
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
10067
10060
|
);
|
|
10068
|
-
const providerMetrics =
|
|
10069
|
-
const providerCapacity =
|
|
10070
|
-
const workerMemory =
|
|
10061
|
+
const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
10062
|
+
const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
|
|
10063
|
+
const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
|
|
10071
10064
|
out.log(
|
|
10072
10065
|
`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`
|
|
10073
10066
|
);
|
|
10074
10067
|
for (const line of providerCapacityLines(status.providerCapacity)) {
|
|
10075
10068
|
out.log(line);
|
|
10076
10069
|
}
|
|
10077
|
-
const coverage =
|
|
10078
|
-
const coverageCounts =
|
|
10079
|
-
const coverageBudget =
|
|
10070
|
+
const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
|
|
10071
|
+
const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
|
|
10072
|
+
const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
|
|
10080
10073
|
out.log(
|
|
10081
10074
|
`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`
|
|
10082
10075
|
);
|
|
10083
10076
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
10084
|
-
const providerFreshness =
|
|
10077
|
+
const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
10085
10078
|
out.log(
|
|
10086
10079
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
10087
10080
|
);
|
|
@@ -10090,17 +10083,17 @@ function printO11yStatus(status, out) {
|
|
|
10090
10083
|
);
|
|
10091
10084
|
}
|
|
10092
10085
|
function providerCapacityLines(read3) {
|
|
10093
|
-
const resources =
|
|
10094
|
-
const durableObjects =
|
|
10095
|
-
const periodic =
|
|
10096
|
-
const storage =
|
|
10097
|
-
const d1 =
|
|
10098
|
-
const d1Activity =
|
|
10099
|
-
const d1Storage =
|
|
10100
|
-
const d1Latency =
|
|
10101
|
-
const r2 =
|
|
10102
|
-
const r2Operations =
|
|
10103
|
-
const r2Storage =
|
|
10086
|
+
const resources = record9(read3.body.resources) ? read3.body.resources : {};
|
|
10087
|
+
const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
|
|
10088
|
+
const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
|
|
10089
|
+
const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
|
|
10090
|
+
const d1 = record9(resources.d1) ? resources.d1 : {};
|
|
10091
|
+
const d1Activity = record9(d1.activity) ? d1.activity : {};
|
|
10092
|
+
const d1Storage = record9(d1.storage) ? d1.storage : {};
|
|
10093
|
+
const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
|
|
10094
|
+
const r2 = record9(resources.r2) ? resources.r2 : {};
|
|
10095
|
+
const r2Operations = record9(r2.operations) ? r2.operations : {};
|
|
10096
|
+
const r2Storage = record9(r2.storage) ? r2.storage : {};
|
|
10104
10097
|
const status = String(
|
|
10105
10098
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
10106
10099
|
);
|
|
@@ -10111,11 +10104,11 @@ function providerCapacityLines(read3) {
|
|
|
10111
10104
|
];
|
|
10112
10105
|
}
|
|
10113
10106
|
function liveSyncLine(read3) {
|
|
10114
|
-
const performance =
|
|
10115
|
-
const commitToSend =
|
|
10107
|
+
const performance = record9(read3.body.performance) ? read3.body.performance : {};
|
|
10108
|
+
const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
|
|
10116
10109
|
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`;
|
|
10117
10110
|
}
|
|
10118
|
-
function
|
|
10111
|
+
function record9(value2) {
|
|
10119
10112
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10120
10113
|
}
|
|
10121
10114
|
function numeric3(value2) {
|
|
@@ -10300,7 +10293,7 @@ async function read2(url, headers, doFetch) {
|
|
|
10300
10293
|
// src/provision.ts
|
|
10301
10294
|
var import_apps12 = require("@odla-ai/apps");
|
|
10302
10295
|
var import_ai3 = require("@odla-ai/ai");
|
|
10303
|
-
var
|
|
10296
|
+
var import_node_process10 = __toESM(require("process"), 1);
|
|
10304
10297
|
|
|
10305
10298
|
// src/integration-provision.ts
|
|
10306
10299
|
var import_db3 = require("@odla-ai/db");
|
|
@@ -10607,7 +10600,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10607
10600
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
10608
10601
|
}
|
|
10609
10602
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
10610
|
-
const key =
|
|
10603
|
+
const key = import_node_process10.default.env[cfg.ai.keyEnv];
|
|
10611
10604
|
if (key) {
|
|
10612
10605
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
10613
10606
|
await (0, import_ai3.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -10649,7 +10642,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10649
10642
|
|
|
10650
10643
|
// src/record.ts
|
|
10651
10644
|
var import_node_fs16 = require("fs");
|
|
10652
|
-
var
|
|
10645
|
+
var import_node_process11 = __toESM(require("process"), 1);
|
|
10653
10646
|
|
|
10654
10647
|
// src/surface.ts
|
|
10655
10648
|
var PM_ACTIONS = {
|
|
@@ -10694,6 +10687,7 @@ var COMMAND_SURFACE = {
|
|
|
10694
10687
|
promote: {},
|
|
10695
10688
|
owners: { list: {}, add: {}, remove: {} }
|
|
10696
10689
|
},
|
|
10690
|
+
brand: { design: { unpack: {} } },
|
|
10697
10691
|
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
10698
10692
|
capabilities: {},
|
|
10699
10693
|
code: { connect: {} },
|
|
@@ -10717,8 +10711,7 @@ var COMMAND_SURFACE = {
|
|
|
10717
10711
|
o11y: { status: {} },
|
|
10718
10712
|
operations: { get: {}, wait: {} },
|
|
10719
10713
|
platform: {
|
|
10720
|
-
status: {}
|
|
10721
|
-
"chat-credentials": { rotate: {} }
|
|
10714
|
+
status: {}
|
|
10722
10715
|
},
|
|
10723
10716
|
pm: {
|
|
10724
10717
|
...PM_ENTITIES,
|
|
@@ -10798,7 +10791,7 @@ function invocationPath(words2) {
|
|
|
10798
10791
|
|
|
10799
10792
|
// src/record.ts
|
|
10800
10793
|
function recordInvocation(parsed) {
|
|
10801
|
-
const file =
|
|
10794
|
+
const file = import_node_process11.default.env.ODLA_CLI_RECORD;
|
|
10802
10795
|
if (!file) return;
|
|
10803
10796
|
try {
|
|
10804
10797
|
const entry = {
|
|
@@ -10991,7 +10984,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
10991
10984
|
|
|
10992
10985
|
// src/runbook-import.ts
|
|
10993
10986
|
var import_node_fs18 = require("fs");
|
|
10994
|
-
var
|
|
10987
|
+
var import_node_path16 = require("path");
|
|
10995
10988
|
function parseRunbook(text2, slug) {
|
|
10996
10989
|
let rest = text2;
|
|
10997
10990
|
const meta = {};
|
|
@@ -11020,8 +11013,8 @@ function readRunbookDir(dir) {
|
|
|
11020
11013
|
const files = (0, import_node_fs18.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
11021
11014
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
11022
11015
|
return files.map((file) => {
|
|
11023
|
-
const slug = (0,
|
|
11024
|
-
const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0,
|
|
11016
|
+
const slug = (0, import_node_path16.basename)(file, ".md");
|
|
11017
|
+
const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0, import_node_path16.join)(dir, file), "utf8"), slug);
|
|
11025
11018
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
11026
11019
|
});
|
|
11027
11020
|
}
|
|
@@ -11095,7 +11088,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
11095
11088
|
// src/runbook-impact.ts
|
|
11096
11089
|
var import_node_child_process7 = require("child_process");
|
|
11097
11090
|
var import_node_fs19 = require("fs");
|
|
11098
|
-
var
|
|
11091
|
+
var import_node_path17 = require("path");
|
|
11099
11092
|
|
|
11100
11093
|
// src/runbook-impact-scan.ts
|
|
11101
11094
|
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$]*)/;
|
|
@@ -11264,7 +11257,7 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
|
|
|
11264
11257
|
}
|
|
11265
11258
|
function manifestLabeller(root) {
|
|
11266
11259
|
return (workspace) => {
|
|
11267
|
-
const manifest = (0,
|
|
11260
|
+
const manifest = (0, import_node_path17.join)(root, workspace, "package.json");
|
|
11268
11261
|
if (!(0, import_node_fs19.existsSync)(manifest)) return void 0;
|
|
11269
11262
|
try {
|
|
11270
11263
|
const name = JSON.parse((0, import_node_fs19.readFileSync)(manifest, "utf8")).name;
|
|
@@ -11334,7 +11327,7 @@ function report3(ctx, impacts) {
|
|
|
11334
11327
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
11335
11328
|
const cwd = deps.cwd ?? process.cwd();
|
|
11336
11329
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
11337
|
-
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0,
|
|
11330
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(cwd, path), "utf8"));
|
|
11338
11331
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
11339
11332
|
if (!surfaces.length) {
|
|
11340
11333
|
return ctx.out.log(
|
|
@@ -11469,10 +11462,10 @@ async function runbookComment(ctx, slug, body) {
|
|
|
11469
11462
|
var import_node_child_process8 = require("child_process");
|
|
11470
11463
|
var import_node_fs20 = require("fs");
|
|
11471
11464
|
var import_node_os5 = require("os");
|
|
11472
|
-
var
|
|
11473
|
-
var
|
|
11465
|
+
var import_node_path18 = require("path");
|
|
11466
|
+
var import_node_process12 = __toESM(require("process"), 1);
|
|
11474
11467
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11475
|
-
function resolveEditor(env =
|
|
11468
|
+
function resolveEditor(env = import_node_process12.default.env) {
|
|
11476
11469
|
for (const name of EDITOR_ENV) {
|
|
11477
11470
|
const value2 = env[name];
|
|
11478
11471
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11486,8 +11479,8 @@ function defaultRun(command, path) {
|
|
|
11486
11479
|
return result.status ?? 0;
|
|
11487
11480
|
}
|
|
11488
11481
|
function editText(initial, slug, deps = {}) {
|
|
11489
|
-
const env = deps.env ??
|
|
11490
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11482
|
+
const env = deps.env ?? import_node_process12.default.env;
|
|
11483
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process12.default.stdin.isTTY));
|
|
11491
11484
|
const editor = resolveEditor(env);
|
|
11492
11485
|
if (!editor)
|
|
11493
11486
|
throw new Error(
|
|
@@ -11495,8 +11488,8 @@ function editText(initial, slug, deps = {}) {
|
|
|
11495
11488
|
);
|
|
11496
11489
|
if (!interactive())
|
|
11497
11490
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
11498
|
-
const dir = (0, import_node_fs20.mkdtempSync)((0,
|
|
11499
|
-
const file = (0,
|
|
11491
|
+
const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path18.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
|
|
11492
|
+
const file = (0, import_node_path18.join)(dir, `${slug}.md`);
|
|
11500
11493
|
try {
|
|
11501
11494
|
(0, import_node_fs20.writeFileSync)(file, initial, { mode: 384 });
|
|
11502
11495
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
@@ -11838,7 +11831,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
11838
11831
|
}
|
|
11839
11832
|
|
|
11840
11833
|
// src/security-command-context.ts
|
|
11841
|
-
var
|
|
11834
|
+
var import_promises12 = require("readline/promises");
|
|
11842
11835
|
async function hostedSecurityContext(parsed, dependencies) {
|
|
11843
11836
|
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
11844
11837
|
const cfg = await loadProjectConfig(configPath);
|
|
@@ -11864,7 +11857,7 @@ async function hostedSecurityContext(parsed, dependencies) {
|
|
|
11864
11857
|
async function interactiveConfirmation(message2, dependencies) {
|
|
11865
11858
|
if (dependencies.confirm) return dependencies.confirm(message2);
|
|
11866
11859
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
11867
|
-
const prompt = (0,
|
|
11860
|
+
const prompt = (0, import_promises12.createInterface)({ input: process.stdin, output: process.stdout });
|
|
11868
11861
|
try {
|
|
11869
11862
|
const answer = await prompt.question(`${message2} [y/N] `);
|
|
11870
11863
|
return /^y(?:es)?$/i.test(answer.trim());
|
|
@@ -11991,7 +11984,7 @@ function hostedSeverity(value2, flag) {
|
|
|
11991
11984
|
var import_security2 = require("@odla-ai/security");
|
|
11992
11985
|
|
|
11993
11986
|
// src/security.ts
|
|
11994
|
-
var
|
|
11987
|
+
var import_node_path19 = require("path");
|
|
11995
11988
|
var import_security = require("@odla-ai/security");
|
|
11996
11989
|
var import_node3 = require("@odla-ai/security/node");
|
|
11997
11990
|
async function runHostedSecurity(options) {
|
|
@@ -12003,9 +11996,9 @@ async function runHostedSecurity(options) {
|
|
|
12003
11996
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
12004
11997
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
12005
11998
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
12006
|
-
const target = (0,
|
|
12007
|
-
const output = (0,
|
|
12008
|
-
const outputRelative = (0,
|
|
11999
|
+
const target = (0, import_node_path19.resolve)(options.target ?? cfg?.rootDir ?? ".");
|
|
12000
|
+
const output = (0, import_node_path19.resolve)(options.out ?? (0, import_node_path19.resolve)(target, ".odla/security/hosted"));
|
|
12001
|
+
const outputRelative = (0, import_node_path19.relative)(target, output).split(import_node_path19.sep).join("/");
|
|
12009
12002
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
12010
12003
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
12011
12004
|
const tokenRequest = {
|
|
@@ -12017,7 +12010,7 @@ async function runHostedSecurity(options) {
|
|
|
12017
12010
|
};
|
|
12018
12011
|
const token = await injectedToken(options, tokenRequest);
|
|
12019
12012
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
12020
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
12013
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path19.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
12021
12014
|
});
|
|
12022
12015
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
12023
12016
|
platform,
|
|
@@ -12035,7 +12028,7 @@ async function runHostedSecurity(options) {
|
|
|
12035
12028
|
});
|
|
12036
12029
|
const harness = (0, import_security.createSecurityHarness)({
|
|
12037
12030
|
profile,
|
|
12038
|
-
store: new import_node3.FileRunStore((0,
|
|
12031
|
+
store: new import_node3.FileRunStore((0, import_node_path19.resolve)(output, "state")),
|
|
12039
12032
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
12040
12033
|
validationReasoner: hosted.validationReasoner,
|
|
12041
12034
|
policy: {
|
|
@@ -12059,7 +12052,7 @@ async function runHostedSecurity(options) {
|
|
|
12059
12052
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
12060
12053
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
12061
12054
|
if (!env || !declared.includes(env)) {
|
|
12062
|
-
const shown = (0,
|
|
12055
|
+
const shown = (0, import_node_path19.relative)(rootDir, configPath) || configPath;
|
|
12063
12056
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
12064
12057
|
}
|
|
12065
12058
|
return env;
|
|
@@ -12088,7 +12081,7 @@ function printSummary(out, appId, env, run, report4, output) {
|
|
|
12088
12081
|
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}`);
|
|
12089
12082
|
if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
|
|
12090
12083
|
out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates}`);
|
|
12091
|
-
out.log(` report: ${(0,
|
|
12084
|
+
out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
|
|
12092
12085
|
}
|
|
12093
12086
|
function formatBudget(usage) {
|
|
12094
12087
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
@@ -12589,6 +12582,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12589
12582
|
await securityCommand(parsed, runtime);
|
|
12590
12583
|
return;
|
|
12591
12584
|
}
|
|
12585
|
+
if (command === "brand") {
|
|
12586
|
+
await brandCommand(parsed, runtime);
|
|
12587
|
+
return;
|
|
12588
|
+
}
|
|
12592
12589
|
if (command === "pm") {
|
|
12593
12590
|
await pmCommand(parsed, runtime);
|
|
12594
12591
|
return;
|