@odla-ai/cli 0.26.2 → 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/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((resolve12, reject) => {
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
- resolve12();
325
+ resolve13();
326
326
  });
327
327
  });
328
328
  }
@@ -2243,6 +2243,153 @@ async function appCommand(parsed, dependencies = {}) {
2243
2243
  else await appRestore(options);
2244
2244
  }
2245
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
+
2246
2393
  // src/calendar-errors.ts
2247
2394
  var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
2248
2395
  "calendar_google_oauth_not_configured",
@@ -2516,8 +2663,8 @@ function credential(value2) {
2516
2663
  // src/calendar-poll.ts
2517
2664
  async function waitForCalendarPoll(milliseconds, signal) {
2518
2665
  if (signal?.aborted) throw signal.reason ?? new Error("calendar connection aborted");
2519
- await new Promise((resolve12, reject) => {
2520
- const timer = setTimeout(resolve12, milliseconds);
2666
+ await new Promise((resolve13, reject) => {
2667
+ const timer = setTimeout(resolve13, milliseconds);
2521
2668
  signal?.addEventListener("abort", () => {
2522
2669
  clearTimeout(timer);
2523
2670
  reject(signal.reason ?? new Error("calendar connection aborted"));
@@ -2771,7 +2918,7 @@ function printGroup(out, heading, items) {
2771
2918
 
2772
2919
  // src/config-operation-command.ts
2773
2920
  var import_apps6 = require("@odla-ai/apps");
2774
- var import_node_path7 = require("path");
2921
+ var import_node_path8 = require("path");
2775
2922
 
2776
2923
  // src/version.ts
2777
2924
  var import_node_fs9 = require("fs");
@@ -3150,7 +3297,7 @@ async function configOperationWait(options) {
3150
3297
  assertOperationId(options.operationId);
3151
3298
  const cfg = await loadProjectConfig(options.configPath);
3152
3299
  const client = await operationClient(cfg, options, "wait");
3153
- const wait2 = options.pollWait ?? ((ms) => new Promise((resolve12) => setTimeout(resolve12, ms)));
3300
+ const wait2 = options.pollWait ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
3154
3301
  const now = () => (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
3155
3302
  const deadline = now() + (options.timeoutSeconds ?? DEFAULT_WAIT_SECONDS) * 1e3;
3156
3303
  const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1e3;
@@ -3181,7 +3328,7 @@ async function operationClient(cfg, options, purpose) {
3181
3328
  platform: cfg.platformUrl,
3182
3329
  scope: "app:config:write",
3183
3330
  token: options.token,
3184
- tokenFile: (0, import_node_path7.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3331
+ tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3185
3332
  rootDir: cfg.rootDir,
3186
3333
  email: options.email,
3187
3334
  open: options.open,
@@ -3236,7 +3383,7 @@ function record3(value2) {
3236
3383
 
3237
3384
  // src/config-reconcile-command.ts
3238
3385
  var import_apps8 = require("@odla-ai/apps");
3239
- var import_node_path8 = require("path");
3386
+ var import_node_path9 = require("path");
3240
3387
 
3241
3388
  // src/config-reconcile.ts
3242
3389
  var import_apps7 = require("@odla-ai/apps");
@@ -3532,7 +3679,7 @@ async function inspectConfig(options) {
3532
3679
  platform: cfg.platformUrl,
3533
3680
  scope: "app:config:read",
3534
3681
  token: options.token,
3535
- tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3682
+ tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3536
3683
  rootDir: cfg.rootDir,
3537
3684
  email: options.email,
3538
3685
  open: options.open,
@@ -3665,12 +3812,12 @@ function quoteArg2(value2) {
3665
3812
  // src/doctor-checks.ts
3666
3813
  var import_node_child_process3 = require("child_process");
3667
3814
  var import_node_fs12 = require("fs");
3668
- var import_node_path10 = require("path");
3815
+ var import_node_path11 = require("path");
3669
3816
 
3670
3817
  // src/wrangler.ts
3671
3818
  var import_node_child_process2 = require("child_process");
3672
3819
  var import_node_fs11 = require("fs");
3673
- var import_node_path9 = require("path");
3820
+ var import_node_path10 = require("path");
3674
3821
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
3675
3822
  const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
3676
3823
  let stdout = "";
@@ -3684,7 +3831,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
3684
3831
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
3685
3832
  function findWranglerConfig(rootDir) {
3686
3833
  for (const name of WRANGLER_CONFIG_FILES) {
3687
- const path = (0, import_node_path9.join)(rootDir, name);
3834
+ const path = (0, import_node_path10.join)(rootDir, name);
3688
3835
  if ((0, import_node_fs11.existsSync)(path)) return path;
3689
3836
  }
3690
3837
  return null;
@@ -3797,10 +3944,10 @@ function wranglerWarnings(rootDir) {
3797
3944
  for (const { label, block } of blocks) {
3798
3945
  const assets = block.assets;
3799
3946
  if (assets?.directory) {
3800
- const dir = (0, import_node_path10.resolve)(rootDir, assets.directory);
3801
- if (dir === (0, import_node_path10.resolve)(rootDir)) {
3947
+ const dir = (0, import_node_path11.resolve)(rootDir, assets.directory);
3948
+ if (dir === (0, import_node_path11.resolve)(rootDir)) {
3802
3949
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
3803
- } else if ((0, import_node_fs12.existsSync)((0, import_node_path10.join)(dir, "node_modules"))) {
3950
+ } else if ((0, import_node_fs12.existsSync)((0, import_node_path11.join)(dir, "node_modules"))) {
3804
3951
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
3805
3952
  }
3806
3953
  }
@@ -3835,7 +3982,7 @@ function o11yProjectWarnings(rootDir) {
3835
3982
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
3836
3983
  return warnings;
3837
3984
  }
3838
- const main = typeof config.main === "string" ? (0, import_node_path10.resolve)(rootDir, config.main) : null;
3985
+ const main = typeof config.main === "string" ? (0, import_node_path11.resolve)(rootDir, config.main) : null;
3839
3986
  if (!main || !(0, import_node_fs12.existsSync)(main)) {
3840
3987
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
3841
3988
  } else {
@@ -3865,7 +4012,7 @@ function calendarProjectWarnings(rootDir) {
3865
4012
  }
3866
4013
  function readPackageJson(rootDir) {
3867
4014
  try {
3868
- return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path10.join)(rootDir, "package.json"), "utf8"));
4015
+ return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(rootDir, "package.json"), "utf8"));
3869
4016
  } catch {
3870
4017
  return null;
3871
4018
  }
@@ -4093,12 +4240,12 @@ function harnessOption(value2, flag) {
4093
4240
 
4094
4241
  // src/init.ts
4095
4242
  var import_node_fs13 = require("fs");
4096
- var import_node_path11 = require("path");
4243
+ var import_node_path12 = require("path");
4097
4244
  var import_apps9 = require("@odla-ai/apps");
4098
4245
  function initProject(options) {
4099
4246
  const out = options.stdout ?? console;
4100
- const rootDir = (0, import_node_path11.resolve)(options.rootDir ?? process.cwd());
4101
- const configPath = (0, import_node_path11.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
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");
4102
4249
  if ((0, import_node_fs13.existsSync)(configPath) && !options.force) {
4103
4250
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
4104
4251
  }
@@ -4115,12 +4262,12 @@ function initProject(options) {
4115
4262
  }
4116
4263
  }
4117
4264
  const aiProvider = options.aiProvider ?? "anthropic";
4118
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(configPath), { recursive: true });
4119
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.resolve)(rootDir, "src/odla"), { recursive: true });
4120
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.resolve)(rootDir, ".odla"), { recursive: true });
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 });
4121
4268
  (0, import_node_fs13.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
4122
- writeIfMissing((0, import_node_path11.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
4123
- writeIfMissing((0, import_node_path11.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
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());
4124
4271
  ensureGitignore(rootDir);
4125
4272
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
4126
4273
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -4361,7 +4508,7 @@ async function resolveVaultWrite(options) {
4361
4508
  // src/skill.ts
4362
4509
  var import_node_fs14 = require("fs");
4363
4510
  var import_node_os2 = require("os");
4364
- var import_node_path12 = require("path");
4511
+ var import_node_path13 = require("path");
4365
4512
  var import_node_url2 = require("url");
4366
4513
 
4367
4514
  // src/skill-adapters.ts
@@ -4440,8 +4587,8 @@ function installSkill(options = {}) {
4440
4587
  const files = listFiles(sourceDir);
4441
4588
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
4442
4589
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
4443
- const root = (0, import_node_path12.resolve)(options.dir ?? process.cwd());
4444
- const home = (0, import_node_path12.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
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)());
4445
4592
  const plans = /* @__PURE__ */ new Map();
4446
4593
  const targets = /* @__PURE__ */ new Map();
4447
4594
  const rememberTarget = (harness, target) => {
@@ -4455,48 +4602,48 @@ function installSkill(options = {}) {
4455
4602
  plans.set(target, { target, content: content2, boundary, managedMerge });
4456
4603
  };
4457
4604
  const planSkillTree = (targetDir2, boundary = root) => {
4458
- for (const rel of files) plan((0, import_node_path12.join)(targetDir2, rel), (0, import_node_fs14.readFileSync)((0, import_node_path12.join)(sourceDir, rel), "utf8"), false, boundary);
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);
4459
4606
  };
4460
4607
  let targetDir;
4461
4608
  if (options.global) {
4462
- const claudeRoot = (0, import_node_path12.join)(home, ".claude", "skills");
4463
- const codexRoot = (0, import_node_path12.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path12.join)(home, ".codex"), "skills");
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");
4464
4611
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
4465
4612
  for (const harness of harnesses) {
4466
4613
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
4467
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path12.dirname)((0, import_node_path12.dirname)(codexRoot)));
4614
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path13.dirname)((0, import_node_path13.dirname)(codexRoot)));
4468
4615
  rememberTarget(harness, skillRoot);
4469
4616
  }
4470
4617
  } else {
4471
- const sharedRoot = (0, import_node_path12.join)(root, ".agents", "skills");
4618
+ const sharedRoot = (0, import_node_path13.join)(root, ".agents", "skills");
4472
4619
  planSkillTree(sharedRoot);
4473
- const claudeRoot = (0, import_node_path12.join)(root, ".claude", "skills");
4620
+ const claudeRoot = (0, import_node_path13.join)(root, ".claude", "skills");
4474
4621
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
4475
4622
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
4476
4623
  if (harnesses.includes("claude")) {
4477
4624
  for (const skill of skillNames(files)) {
4478
- const canonical = (0, import_node_fs14.readFileSync)((0, import_node_path12.join)(sourceDir, skill, "SKILL.md"), "utf8");
4479
- plan((0, import_node_path12.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
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));
4480
4627
  }
4481
4628
  rememberTarget("claude", claudeRoot);
4482
4629
  }
4483
4630
  if (harnesses.includes("cursor")) {
4484
- const cursorRule = (0, import_node_path12.join)(root, ".cursor", "rules", "odla.mdc");
4631
+ const cursorRule = (0, import_node_path13.join)(root, ".cursor", "rules", "odla.mdc");
4485
4632
  plan(cursorRule, CURSOR_RULE);
4486
4633
  rememberTarget("cursor", cursorRule);
4487
4634
  }
4488
4635
  if (harnesses.includes("agents")) {
4489
- const agentsFile = (0, import_node_path12.join)(root, "AGENTS.md");
4636
+ const agentsFile = (0, import_node_path13.join)(root, "AGENTS.md");
4490
4637
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4491
4638
  rememberTarget("agents", agentsFile);
4492
4639
  }
4493
4640
  if (harnesses.includes("copilot")) {
4494
- const copilotFile = (0, import_node_path12.join)(root, ".github", "copilot-instructions.md");
4641
+ const copilotFile = (0, import_node_path13.join)(root, ".github", "copilot-instructions.md");
4495
4642
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4496
4643
  rememberTarget("copilot", copilotFile);
4497
4644
  }
4498
4645
  if (harnesses.includes("gemini")) {
4499
- const geminiFile = (0, import_node_path12.join)(root, "GEMINI.md");
4646
+ const geminiFile = (0, import_node_path13.join)(root, "GEMINI.md");
4500
4647
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4501
4648
  rememberTarget("gemini", geminiFile);
4502
4649
  }
@@ -4532,7 +4679,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4532
4679
  }
4533
4680
  for (const file of plans.values()) {
4534
4681
  if (!(0, import_node_fs14.existsSync)(file.target) || (0, import_node_fs14.readFileSync)(file.target, "utf8") !== file.content) {
4535
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.dirname)(file.target), { recursive: true });
4682
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(file.target), { recursive: true });
4536
4683
  (0, import_node_fs14.writeFileSync)(file.target, file.content);
4537
4684
  }
4538
4685
  }
@@ -4552,7 +4699,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4552
4699
  };
4553
4700
  }
4554
4701
  function pathsUnder(root, paths) {
4555
- return [...paths].map((path) => (0, import_node_path12.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path12.sep}`) && !(0, import_node_path12.isAbsolute)(path)).sort();
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();
4556
4703
  }
4557
4704
  function normalizeHarnesses(values, global) {
4558
4705
  const requested = values?.length ? values : ["claude"];
@@ -4597,13 +4744,13 @@ function managedFileContent(path, block, force, boundary) {
4597
4744
  return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
4598
4745
  }
4599
4746
  function symlinkedComponent(boundary, target) {
4600
- const rel = (0, import_node_path12.relative)(boundary, target);
4601
- if (rel === ".." || rel.startsWith(`..${import_node_path12.sep}`) || (0, import_node_path12.isAbsolute)(rel)) {
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)) {
4602
4749
  throw new Error(`agent setup target escapes its install root: ${target}`);
4603
4750
  }
4604
4751
  let current = boundary;
4605
- for (const part of rel.split(import_node_path12.sep).filter(Boolean)) {
4606
- current = (0, import_node_path12.join)(current, part);
4752
+ for (const part of rel.split(import_node_path13.sep).filter(Boolean)) {
4753
+ current = (0, import_node_path13.join)(current, part);
4607
4754
  try {
4608
4755
  if ((0, import_node_fs14.lstatSync)(current).isSymbolicLink()) return current;
4609
4756
  } catch (error) {
@@ -4620,9 +4767,9 @@ function listFiles(dir) {
4620
4767
  const results = [];
4621
4768
  const walk = (current) => {
4622
4769
  for (const entry of (0, import_node_fs14.readdirSync)(current, { withFileTypes: true })) {
4623
- const path = (0, import_node_path12.join)(current, entry.name);
4770
+ const path = (0, import_node_path13.join)(current, entry.name);
4624
4771
  if (entry.isDirectory()) walk(path);
4625
- else results.push((0, import_node_path12.relative)(dir, path));
4772
+ else results.push((0, import_node_path13.relative)(dir, path));
4626
4773
  }
4627
4774
  };
4628
4775
  walk(dir);
@@ -4934,7 +5081,7 @@ async function projectCommand(command, parsed, deps) {
4934
5081
  // src/code-connect.ts
4935
5082
  var import_node_fs15 = require("fs");
4936
5083
  var import_node_os4 = require("os");
4937
- var import_node_path14 = require("path");
5084
+ var import_node_path15 = require("path");
4938
5085
 
4939
5086
  // ../harness/dist/chunk-QTUEF2HZ.js
4940
5087
  var HARNESS_PROTOCOL_VERSION = 1;
@@ -5020,15 +5167,15 @@ function encodeAgentInput(message2) {
5020
5167
  // ../harness/dist/chunk-PHXQH4YM.js
5021
5168
  var import_child_process = require("child_process");
5022
5169
  var import_fs = require("fs");
5023
- var import_promises2 = require("fs/promises");
5170
+ var import_promises3 = require("fs/promises");
5024
5171
  var import_path = require("path");
5025
5172
  var import_process = require("process");
5026
- var import_promises3 = require("fs/promises");
5173
+ var import_promises4 = require("fs/promises");
5027
5174
  var import_os = require("os");
5028
5175
  var import_path2 = require("path");
5029
5176
  var import_child_process2 = require("child_process");
5030
5177
  var import_path3 = require("path");
5031
- var import_promises4 = require("fs/promises");
5178
+ var import_promises5 = require("fs/promises");
5032
5179
  var import_os2 = require("os");
5033
5180
  var import_path4 = require("path");
5034
5181
  var import_child_process3 = require("child_process");
@@ -5039,7 +5186,7 @@ function assertPinnedImage(image) {
5039
5186
  async function commandAvailable(engine) {
5040
5187
  for (const directory of (process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean)) {
5041
5188
  try {
5042
- await (0, import_promises2.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
5189
+ await (0, import_promises3.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
5043
5190
  return true;
5044
5191
  } catch {
5045
5192
  }
@@ -5325,7 +5472,7 @@ async function gitBlobs(cwd, entries, maxBytes) {
5325
5472
  }
5326
5473
  async function materializeGitTree(source, commitSha, options = {}) {
5327
5474
  if (!/^[0-9a-f]{40}$/.test(commitSha)) throw new TypeError("Git tree requires an exact commit SHA");
5328
- const sourceDir = await (0, import_promises3.realpath)((0, import_path2.resolve)(source));
5475
+ const sourceDir = await (0, import_promises4.realpath)((0, import_path2.resolve)(source));
5329
5476
  const maxFiles = options.maxFiles ?? 2e4;
5330
5477
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5331
5478
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
@@ -5334,9 +5481,9 @@ async function materializeGitTree(source, commitSha, options = {}) {
5334
5481
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
5335
5482
  });
5336
5483
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
5337
- const root = await (0, import_promises3.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
5484
+ const root = await (0, import_promises4.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
5338
5485
  const targetRoot = (0, import_path2.join)(root, "source");
5339
- await (0, import_promises3.mkdir)(targetRoot);
5486
+ await (0, import_promises4.mkdir)(targetRoot);
5340
5487
  let byteCount = 0;
5341
5488
  try {
5342
5489
  const blobs = await gitBlobs(sourceDir, entries, maxBytes);
@@ -5346,18 +5493,18 @@ async function materializeGitTree(source, commitSha, options = {}) {
5346
5493
  if (byteCount > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
5347
5494
  const target = (0, import_path2.resolve)(targetRoot, entry.path);
5348
5495
  if (!target.startsWith(`${(0, import_path2.resolve)(targetRoot)}${import_path2.sep}`)) throw new TypeError("Git tree path escapes workspace");
5349
- await (0, import_promises3.mkdir)((0, import_path2.resolve)(target, ".."), { recursive: true });
5350
- await (0, import_promises3.writeFile)(target, content2, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
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 });
5351
5498
  }
5352
5499
  return {
5353
5500
  root,
5354
5501
  sourceDir: targetRoot,
5355
5502
  fileCount: entries.length,
5356
5503
  byteCount,
5357
- cleanup: () => (0, import_promises3.rm)(root, { recursive: true, force: true })
5504
+ cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5358
5505
  };
5359
5506
  } catch (error) {
5360
- await (0, import_promises3.rm)(root, { recursive: true, force: true });
5507
+ await (0, import_promises4.rm)(root, { recursive: true, force: true });
5361
5508
  throw error;
5362
5509
  }
5363
5510
  }
@@ -5365,7 +5512,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5365
5512
  const files = [];
5366
5513
  let bytes = 0;
5367
5514
  const walk = async (dir) => {
5368
- for (const entry of await (0, import_promises4.readdir)(dir, { withFileTypes: true })) {
5515
+ for (const entry of await (0, import_promises5.readdir)(dir, { withFileTypes: true })) {
5369
5516
  if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
5370
5517
  if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
5371
5518
  const path = (0, import_path4.join)(dir, entry.name);
@@ -5375,7 +5522,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5375
5522
  continue;
5376
5523
  }
5377
5524
  if (!entry.isFile()) continue;
5378
- const metadata2 = await (0, import_promises4.stat)(path);
5525
+ const metadata2 = await (0, import_promises5.stat)(path);
5379
5526
  bytes += metadata2.size;
5380
5527
  if (files.length + 1 > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
5381
5528
  if (bytes > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
@@ -5424,7 +5571,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5424
5571
  if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
5425
5572
  let metadata2;
5426
5573
  try {
5427
- metadata2 = await (0, import_promises4.lstat)(source);
5574
+ metadata2 = await (0, import_promises5.lstat)(source);
5428
5575
  } catch (error) {
5429
5576
  if (error.code === "ENOENT") continue;
5430
5577
  throw error;
@@ -5439,9 +5586,9 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5439
5586
  async function copyTree(files, destination) {
5440
5587
  for (const file of files) {
5441
5588
  const target = (0, import_path4.join)(destination, file.relativePath);
5442
- await (0, import_promises4.mkdir)((0, import_path4.resolve)(target, ".."), { recursive: true });
5443
- await (0, import_promises4.copyFile)(file.source, target);
5444
- await (0, import_promises4.chmod)(target, file.mode);
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);
5445
5592
  }
5446
5593
  }
5447
5594
  async function captureGitDiff(root, maxBytes) {
@@ -5478,13 +5625,13 @@ async function captureGitDiff(root, maxBytes) {
5478
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");
5479
5626
  }
5480
5627
  async function stageWorkspace(source, options = {}) {
5481
- const sourceDir = await (0, import_promises4.realpath)((0, import_path4.resolve)(source));
5482
- const sourceStat = await (0, import_promises4.stat)(sourceDir);
5628
+ const sourceDir = await (0, import_promises5.realpath)((0, import_path4.resolve)(source));
5629
+ const sourceStat = await (0, import_promises5.stat)(sourceDir);
5483
5630
  if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
5484
- const root = await (0, import_promises4.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5631
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5485
5632
  const baselineDir = (0, import_path4.join)(root, "baseline");
5486
5633
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5487
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5634
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5488
5635
  try {
5489
5636
  const maxFiles = options.maxFiles ?? 2e4;
5490
5637
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
@@ -5497,26 +5644,26 @@ async function stageWorkspace(source, options = {}) {
5497
5644
  fileCount: files.length,
5498
5645
  byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
5499
5646
  patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
5500
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5647
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5501
5648
  };
5502
5649
  } catch (error) {
5503
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5650
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5504
5651
  throw error;
5505
5652
  }
5506
5653
  }
5507
5654
  async function stageWorkspacePair(baselineSource, workspaceSource, options = {}) {
5508
- const baselineDirSource = await (0, import_promises4.realpath)((0, import_path4.resolve)(baselineSource));
5509
- const workspaceDirSource = await (0, import_promises4.realpath)((0, import_path4.resolve)(workspaceSource));
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));
5510
5657
  const maxFiles = options.maxFiles ?? 2e4;
5511
5658
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5512
5659
  const [baselineFiles, workspaceFiles] = await Promise.all([
5513
5660
  sourceFiles(baselineDirSource, maxFiles, maxBytes),
5514
5661
  sourceFiles(workspaceDirSource, maxFiles, maxBytes)
5515
5662
  ]);
5516
- const root = await (0, import_promises4.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5663
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5517
5664
  const baselineDir = (0, import_path4.join)(root, "baseline");
5518
5665
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5519
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5666
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5520
5667
  try {
5521
5668
  await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
5522
5669
  return {
@@ -5526,17 +5673,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
5526
5673
  fileCount: workspaceFiles.length,
5527
5674
  byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
5528
5675
  patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
5529
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5676
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5530
5677
  };
5531
5678
  } catch (error) {
5532
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5679
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5533
5680
  throw error;
5534
5681
  }
5535
5682
  }
5536
5683
 
5537
5684
  // ../harness/dist/chunk-GMVZ4LZH.js
5538
5685
  var import_crypto = require("crypto");
5539
- var import_promises5 = require("fs/promises");
5686
+ var import_promises6 = require("fs/promises");
5540
5687
  var import_path5 = require("path");
5541
5688
 
5542
5689
  // ../camel/dist/chunk-7FHPOQVP.js
@@ -5873,19 +6020,19 @@ function validateSnapshot(snapshot, limits) {
5873
6020
 
5874
6021
  // ../harness/dist/chunk-GMVZ4LZH.js
5875
6022
  var import_child_process4 = require("child_process");
5876
- var import_promises6 = require("fs/promises");
6023
+ var import_promises7 = require("fs/promises");
5877
6024
  var import_path6 = require("path");
5878
6025
  var import_child_process5 = require("child_process");
5879
6026
  var import_process2 = require("process");
5880
6027
  var import_crypto2 = require("crypto");
5881
6028
  var import_crypto3 = require("crypto");
5882
6029
  var import_fs2 = require("fs");
5883
- var import_promises7 = require("fs/promises");
5884
- var import_path7 = require("path");
5885
6030
  var import_promises8 = require("fs/promises");
6031
+ var import_path7 = require("path");
6032
+ var import_promises9 = require("fs/promises");
5886
6033
  var import_os3 = require("os");
5887
6034
  var import_path8 = require("path");
5888
- var import_promises9 = require("fs/promises");
6035
+ var import_promises10 = require("fs/promises");
5889
6036
  var import_path9 = require("path");
5890
6037
 
5891
6038
  // ../camel/dist/chunk-LAXU2AVK.js
@@ -6169,7 +6316,7 @@ var import_crypto4 = require("crypto");
6169
6316
  async function digestStagedWorkspace(root, limits) {
6170
6317
  const files = [];
6171
6318
  const walk = async (directory) => {
6172
- const entries = await (0, import_promises5.readdir)(directory, { withFileTypes: true });
6319
+ const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
6173
6320
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
6174
6321
  if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
6175
6322
  const target = (0, import_path5.resolve)(directory, entry.name);
@@ -6184,7 +6331,7 @@ async function digestStagedWorkspace(root, limits) {
6184
6331
  const hash = (0, import_crypto.createHash)("sha256");
6185
6332
  let bytes = 0;
6186
6333
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
6187
- const content2 = await (0, import_promises5.readFile)(file.target);
6334
+ const content2 = await (0, import_promises6.readFile)(file.target);
6188
6335
  bytes += Buffer.byteLength(file.path) + content2.byteLength;
6189
6336
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
6190
6337
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content2.byteLength}:`);
@@ -6510,7 +6657,7 @@ async function applyCodePatch(workspaceDir, patch2, paths) {
6510
6657
  await gitApply(workspaceDir, patch2, false);
6511
6658
  for (const path of paths) {
6512
6659
  try {
6513
- const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
6660
+ const info = await (0, import_promises7.lstat)(resolveCodePath(workspaceDir, path));
6514
6661
  if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
6515
6662
  throw new TypeError("patch created a non-regular workspace entry");
6516
6663
  }
@@ -6801,7 +6948,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
6801
6948
  for (const artifact of recipe2.expectedArtifacts ?? []) {
6802
6949
  try {
6803
6950
  const path = (0, import_path7.join)(workspaceDir, artifact.path);
6804
- const info = await (0, import_promises7.lstat)(path);
6951
+ const info = await (0, import_promises8.lstat)(path);
6805
6952
  if (!info.isFile() || info.isSymbolicLink()) {
6806
6953
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
6807
6954
  } else if (info.size > artifact.maximumBytes) {
@@ -6982,9 +7129,9 @@ var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_mod
6982
7129
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
6983
7130
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.tmpdir)()) {
6984
7131
  if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
6985
- const root = await (0, import_promises8.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
7132
+ const root = await (0, import_promises9.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
6986
7133
  const sourceDir = (0, import_path8.join)(root, "source");
6987
- await (0, import_promises8.mkdir)(sourceDir);
7134
+ await (0, import_promises9.mkdir)(sourceDir);
6988
7135
  const seen = /* @__PURE__ */ new Set();
6989
7136
  let bytes = 0;
6990
7137
  try {
@@ -6996,8 +7143,8 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
6996
7143
  if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
6997
7144
  const target = (0, import_path8.resolve)(sourceDir, file.path);
6998
7145
  if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code source path escapes its root");
6999
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7000
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 420 });
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 });
7001
7148
  }
7002
7149
  for (const reference of snapshot.references ?? []) {
7003
7150
  validateAlias(reference.alias);
@@ -7011,13 +7158,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
7011
7158
  if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
7012
7159
  const target = (0, import_path8.resolve)(sourceDir, path);
7013
7160
  if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
7014
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7015
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
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 });
7016
7163
  }
7017
7164
  }
7018
- return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
7165
+ return { sourceDir, cleanup: () => (0, import_promises9.rm)(root, { recursive: true, force: true }) };
7019
7166
  } catch (cause) {
7020
- await (0, import_promises8.rm)(root, { recursive: true, force: true });
7167
+ await (0, import_promises9.rm)(root, { recursive: true, force: true });
7021
7168
  throw cause;
7022
7169
  }
7023
7170
  }
@@ -7038,8 +7185,8 @@ async function attachCodeRuntimeReferences(workspace, references) {
7038
7185
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
7039
7186
  const target = (0, import_path8.resolve)(root, path);
7040
7187
  if (!target.startsWith(`${(0, import_path8.resolve)(root)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
7041
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7042
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
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 });
7043
7190
  }
7044
7191
  }
7045
7192
  }
@@ -7225,11 +7372,11 @@ async function read(context, request2, options, policy) {
7225
7372
  const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
7226
7373
  if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
7227
7374
  const target = resolveCodePath(context.workspaceDir, path);
7228
- const info = await (0, import_promises9.stat)(target);
7375
+ const info = await (0, import_promises10.stat)(target);
7229
7376
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
7230
7377
  throw new TypeError("file is not a bounded regular source file");
7231
7378
  }
7232
- const source = await (0, import_promises9.readFile)(target);
7379
+ const source = await (0, import_promises10.readFile)(target);
7233
7380
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
7234
7381
  const lines = source.toString("utf8").split("\n");
7235
7382
  const content2 = lines.slice(startLine - 1, endLine).join("\n");
@@ -7306,7 +7453,7 @@ function policyContext(context, request2, options, extra) {
7306
7453
  async function registeredFiles(root, limit) {
7307
7454
  const paths = [];
7308
7455
  const walk = async (directory) => {
7309
- for (const entry of await (0, import_promises9.readdir)(directory, { withFileTypes: true })) {
7456
+ for (const entry of await (0, import_promises10.readdir)(directory, { withFileTypes: true })) {
7310
7457
  if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
7311
7458
  const target = (0, import_path9.resolve)(directory, entry.name);
7312
7459
  if (entry.isDirectory()) await walk(target);
@@ -7905,8 +8052,8 @@ function hostedPollTimeout(value2 = 10 * 6e4) {
7905
8052
  }
7906
8053
  async function waitForHostedPoll(milliseconds, signal) {
7907
8054
  if (signal?.aborted) throw signal.reason ?? new DOMException("aborted", "AbortError");
7908
- await new Promise((resolve12, reject) => {
7909
- const timer = setTimeout(resolve12, milliseconds);
8055
+ await new Promise((resolve13, reject) => {
8056
+ const timer = setTimeout(resolve13, milliseconds);
7910
8057
  signal?.addEventListener("abort", () => {
7911
8058
  clearTimeout(timer);
7912
8059
  reject(signal.reason ?? new DOMException("aborted", "AbortError"));
@@ -8091,9 +8238,9 @@ function digestText(value2) {
8091
8238
  // src/code-images.ts
8092
8239
  var import_node_child_process6 = require("child_process");
8093
8240
  var import_node_crypto3 = require("crypto");
8094
- var import_promises10 = require("fs/promises");
8241
+ var import_promises11 = require("fs/promises");
8095
8242
  var import_node_os3 = require("os");
8096
- var import_node_path13 = require("path");
8243
+ var import_node_path14 = require("path");
8097
8244
  var import_node_url3 = require("url");
8098
8245
 
8099
8246
  // src/code-runtime-config.ts
@@ -8173,16 +8320,16 @@ function embeddedPiAssetPath() {
8173
8320
  return (0, import_node_url3.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
8174
8321
  }
8175
8322
  async function embeddedPiImageName() {
8176
- const bundle = await (0, import_promises10.readFile)(embeddedPiAssetPath()).catch(() => {
8323
+ const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
8177
8324
  throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
8178
8325
  });
8179
8326
  return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
8180
8327
  }
8181
8328
  async function buildEmbeddedPiImage(engine, image, run) {
8182
- const context = await (0, import_promises10.mkdtemp)((0, import_node_path13.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
8329
+ const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
8183
8330
  try {
8184
- await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path13.join)(context, "pi-agent.js"));
8185
- await (0, import_promises10.writeFile)((0, import_node_path13.join)(context, "Dockerfile"), [
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"), [
8186
8333
  `FROM ${CODE_NODE_IMAGE}`,
8187
8334
  "COPY pi-agent.js /opt/odla/pi-agent.js",
8188
8335
  "WORKDIR /workspace",
@@ -8191,14 +8338,14 @@ async function buildEmbeddedPiImage(engine, image, run) {
8191
8338
  ].join("\n"), { mode: 384 });
8192
8339
  await run(engine, ["build", "--tag", image, context], "inherit");
8193
8340
  } finally {
8194
- await (0, import_promises10.rm)(context, { recursive: true, force: true });
8341
+ await (0, import_promises11.rm)(context, { recursive: true, force: true });
8195
8342
  }
8196
8343
  }
8197
8344
 
8198
8345
  // src/code-connect.ts
8199
8346
  async function codeConnect(options) {
8200
8347
  const cwd = options.cwd ?? process.cwd();
8201
- const configPath = (0, import_node_path14.resolve)(cwd, options.configPath);
8348
+ const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
8202
8349
  const cfg = (0, import_node_fs15.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
8203
8350
  const requestedAppId = options.appId?.trim();
8204
8351
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -8567,10 +8714,8 @@ function requireName(parsed) {
8567
8714
  return name;
8568
8715
  }
8569
8716
 
8570
- // src/help.ts
8571
- function printHelp(output = console) {
8572
- output.log(`odla-ai
8573
-
8717
+ // src/help-usage.ts
8718
+ var USAGE_SECTION = `
8574
8719
  Start here:
8575
8720
  odla-ai runbook ask "<question>" The current procedure, from odla's own
8576
8721
  runbooks. Ask BEFORE searching the web or
@@ -8604,6 +8749,7 @@ Usage:
8604
8749
  odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
8605
8750
  odla-ai app owners add <email> [--email <odla-account>] [--json]
8606
8751
  odla-ai app owners remove <email> [--email <odla-account>] [--json]
8752
+ odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
8607
8753
  odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8608
8754
  odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8609
8755
  odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
@@ -8683,8 +8829,12 @@ Usage:
8683
8829
  odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
8684
8830
  odla-ai secrets set <name> --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8685
8831
  odla-ai secrets set-clerk-key --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8686
- odla-ai version
8832
+ odla-ai version`;
8687
8833
 
8834
+ // src/help.ts
8835
+ function printHelp(output = console) {
8836
+ output.log(`odla-ai
8837
+ ${USAGE_SECTION}
8688
8838
  Commands:
8689
8839
  agent Inspect durable agent wakeups and explicitly requeue a
8690
8840
  dead-lettered job; JSON output is stable for remote operators.
@@ -9133,7 +9283,7 @@ function jsonl(ctx, parsed, value2) {
9133
9283
  }
9134
9284
  async function discussWatch(ctx, topicId, parsed) {
9135
9285
  if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
9136
- const sleep = ctx.sleep ?? ((ms) => new Promise((resolve12) => setTimeout(resolve12, ms)));
9286
+ const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
9137
9287
  const now = ctx.now ?? Date.now;
9138
9288
  const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
9139
9289
  const timeoutSeconds = numberOpt2(parsed, "timeout");
@@ -10605,6 +10755,7 @@ var COMMAND_SURFACE = {
10605
10755
  promote: {},
10606
10756
  owners: { list: {}, add: {}, remove: {} }
10607
10757
  },
10758
+ brand: { design: { unpack: {} } },
10608
10759
  calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
10609
10760
  capabilities: {},
10610
10761
  code: { connect: {} },
@@ -10910,7 +11061,7 @@ async function runbookRemove(ctx, slug) {
10910
11061
 
10911
11062
  // src/runbook-import.ts
10912
11063
  var import_node_fs18 = require("fs");
10913
- var import_node_path15 = require("path");
11064
+ var import_node_path16 = require("path");
10914
11065
  function parseRunbook(text2, slug) {
10915
11066
  let rest = text2;
10916
11067
  const meta = {};
@@ -10939,8 +11090,8 @@ function readRunbookDir(dir) {
10939
11090
  const files = (0, import_node_fs18.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
10940
11091
  if (!files.length) throw new Error(`no .md files in ${dir}`);
10941
11092
  return files.map((file) => {
10942
- const slug = (0, import_node_path15.basename)(file, ".md");
10943
- const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0, import_node_path15.join)(dir, file), "utf8"), slug);
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);
10944
11095
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
10945
11096
  });
10946
11097
  }
@@ -11014,7 +11165,7 @@ async function upsert(ctx, r, visibility) {
11014
11165
  // src/runbook-impact.ts
11015
11166
  var import_node_child_process7 = require("child_process");
11016
11167
  var import_node_fs19 = require("fs");
11017
- var import_node_path16 = require("path");
11168
+ var import_node_path17 = require("path");
11018
11169
 
11019
11170
  // src/runbook-impact-scan.ts
11020
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$]*)/;
@@ -11183,7 +11334,7 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
11183
11334
  }
11184
11335
  function manifestLabeller(root) {
11185
11336
  return (workspace) => {
11186
- const manifest = (0, import_node_path16.join)(root, workspace, "package.json");
11337
+ const manifest = (0, import_node_path17.join)(root, workspace, "package.json");
11187
11338
  if (!(0, import_node_fs19.existsSync)(manifest)) return void 0;
11188
11339
  try {
11189
11340
  const name = JSON.parse((0, import_node_fs19.readFileSync)(manifest, "utf8")).name;
@@ -11253,7 +11404,7 @@ function report3(ctx, impacts) {
11253
11404
  async function runbookImpact(ctx, options, deps = {}) {
11254
11405
  const cwd = deps.cwd ?? process.cwd();
11255
11406
  const runGit = deps.runGit ?? gitRunner(cwd);
11256
- const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path16.join)(cwd, path), "utf8"));
11407
+ const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(cwd, path), "utf8"));
11257
11408
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
11258
11409
  if (!surfaces.length) {
11259
11410
  return ctx.out.log(
@@ -11388,7 +11539,7 @@ async function runbookComment(ctx, slug, body) {
11388
11539
  var import_node_child_process8 = require("child_process");
11389
11540
  var import_node_fs20 = require("fs");
11390
11541
  var import_node_os5 = require("os");
11391
- var import_node_path17 = require("path");
11542
+ var import_node_path18 = require("path");
11392
11543
  var import_node_process12 = __toESM(require("process"), 1);
11393
11544
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
11394
11545
  function resolveEditor(env = import_node_process12.default.env) {
@@ -11414,8 +11565,8 @@ function editText(initial, slug, deps = {}) {
11414
11565
  );
11415
11566
  if (!interactive())
11416
11567
  throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
11417
- const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path17.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
11418
- const file = (0, import_node_path17.join)(dir, `${slug}.md`);
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`);
11419
11570
  try {
11420
11571
  (0, import_node_fs20.writeFileSync)(file, initial, { mode: 384 });
11421
11572
  const code = defaultRunOrInjected(deps)(editor, file);
@@ -11757,7 +11908,7 @@ async function runbookCommand(parsed, deps = {}) {
11757
11908
  }
11758
11909
 
11759
11910
  // src/security-command-context.ts
11760
- var import_promises11 = require("readline/promises");
11911
+ var import_promises12 = require("readline/promises");
11761
11912
  async function hostedSecurityContext(parsed, dependencies) {
11762
11913
  const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
11763
11914
  const cfg = await loadProjectConfig(configPath);
@@ -11783,7 +11934,7 @@ async function hostedSecurityContext(parsed, dependencies) {
11783
11934
  async function interactiveConfirmation(message2, dependencies) {
11784
11935
  if (dependencies.confirm) return dependencies.confirm(message2);
11785
11936
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
11786
- const prompt = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stdout });
11937
+ const prompt = (0, import_promises12.createInterface)({ input: process.stdin, output: process.stdout });
11787
11938
  try {
11788
11939
  const answer = await prompt.question(`${message2} [y/N] `);
11789
11940
  return /^y(?:es)?$/i.test(answer.trim());
@@ -11910,7 +12061,7 @@ function hostedSeverity(value2, flag) {
11910
12061
  var import_security2 = require("@odla-ai/security");
11911
12062
 
11912
12063
  // src/security.ts
11913
- var import_node_path18 = require("path");
12064
+ var import_node_path19 = require("path");
11914
12065
  var import_security = require("@odla-ai/security");
11915
12066
  var import_node3 = require("@odla-ai/security/node");
11916
12067
  async function runHostedSecurity(options) {
@@ -11922,9 +12073,9 @@ async function runHostedSecurity(options) {
11922
12073
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
11923
12074
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
11924
12075
  const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
11925
- const target = (0, import_node_path18.resolve)(options.target ?? cfg?.rootDir ?? ".");
11926
- const output = (0, import_node_path18.resolve)(options.out ?? (0, import_node_path18.resolve)(target, ".odla/security/hosted"));
11927
- const outputRelative = (0, import_node_path18.relative)(target, output).split(import_node_path18.sep).join("/");
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("/");
11928
12079
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
11929
12080
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
11930
12081
  const tokenRequest = {
@@ -11936,7 +12087,7 @@ async function runHostedSecurity(options) {
11936
12087
  };
11937
12088
  const token = await injectedToken(options, tokenRequest);
11938
12089
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
11939
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path18.isAbsolute)(outputRelative) ? [outputRelative] : []
12090
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path19.isAbsolute)(outputRelative) ? [outputRelative] : []
11940
12091
  });
11941
12092
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
11942
12093
  platform,
@@ -11954,7 +12105,7 @@ async function runHostedSecurity(options) {
11954
12105
  });
11955
12106
  const harness = (0, import_security.createSecurityHarness)({
11956
12107
  profile,
11957
- store: new import_node3.FileRunStore((0, import_node_path18.resolve)(output, "state")),
12108
+ store: new import_node3.FileRunStore((0, import_node_path19.resolve)(output, "state")),
11958
12109
  discoveryReasoner: hosted.discoveryReasoner,
11959
12110
  validationReasoner: hosted.validationReasoner,
11960
12111
  policy: {
@@ -11978,7 +12129,7 @@ async function runHostedSecurity(options) {
11978
12129
  function selectEnv(requested, declared, configPath, rootDir) {
11979
12130
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
11980
12131
  if (!env || !declared.includes(env)) {
11981
- const shown = (0, import_node_path18.relative)(rootDir, configPath) || configPath;
12132
+ const shown = (0, import_node_path19.relative)(rootDir, configPath) || configPath;
11982
12133
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
11983
12134
  }
11984
12135
  return env;
@@ -12007,7 +12158,7 @@ function printSummary(out, appId, env, run, report4, output) {
12007
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}`);
12008
12159
  if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
12009
12160
  out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates}`);
12010
- out.log(` report: ${(0, import_node_path18.resolve)(output, "REPORT.md")}`);
12161
+ out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
12011
12162
  }
12012
12163
  function formatBudget(usage) {
12013
12164
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
@@ -12519,6 +12670,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
12519
12670
  await securityCommand(parsed, runtime);
12520
12671
  return;
12521
12672
  }
12673
+ if (command === "brand") {
12674
+ await brandCommand(parsed, runtime);
12675
+ return;
12676
+ }
12522
12677
  if (command === "pm") {
12523
12678
  await pmCommand(parsed, runtime);
12524
12679
  return;