@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/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((resolve12, reject) => {
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
- resolve12();
257
+ resolve13();
258
258
  });
259
259
  });
260
260
  }
@@ -2175,6 +2175,153 @@ async function appCommand(parsed, dependencies = {}) {
2175
2175
  else await appRestore(options);
2176
2176
  }
2177
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
+
2178
2325
  // src/calendar-errors.ts
2179
2326
  var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
2180
2327
  "calendar_google_oauth_not_configured",
@@ -2448,8 +2595,8 @@ function credential(value2) {
2448
2595
  // src/calendar-poll.ts
2449
2596
  async function waitForCalendarPoll(milliseconds, signal) {
2450
2597
  if (signal?.aborted) throw signal.reason ?? new Error("calendar connection aborted");
2451
- await new Promise((resolve12, reject) => {
2452
- const timer = setTimeout(resolve12, milliseconds);
2598
+ await new Promise((resolve13, reject) => {
2599
+ const timer = setTimeout(resolve13, milliseconds);
2453
2600
  signal?.addEventListener("abort", () => {
2454
2601
  clearTimeout(timer);
2455
2602
  reject(signal.reason ?? new Error("calendar connection aborted"));
@@ -2703,7 +2850,7 @@ function printGroup(out, heading, items) {
2703
2850
 
2704
2851
  // src/config-operation-command.ts
2705
2852
  var import_apps6 = require("@odla-ai/apps");
2706
- var import_node_path7 = require("path");
2853
+ var import_node_path8 = require("path");
2707
2854
 
2708
2855
  // src/version.ts
2709
2856
  var import_node_fs9 = require("fs");
@@ -3082,7 +3229,7 @@ async function configOperationWait(options) {
3082
3229
  assertOperationId(options.operationId);
3083
3230
  const cfg = await loadProjectConfig(options.configPath);
3084
3231
  const client = await operationClient(cfg, options, "wait");
3085
- const wait2 = options.pollWait ?? ((ms) => new Promise((resolve12) => setTimeout(resolve12, ms)));
3232
+ const wait2 = options.pollWait ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
3086
3233
  const now = () => (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
3087
3234
  const deadline = now() + (options.timeoutSeconds ?? DEFAULT_WAIT_SECONDS) * 1e3;
3088
3235
  const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1e3;
@@ -3113,7 +3260,7 @@ async function operationClient(cfg, options, purpose) {
3113
3260
  platform: cfg.platformUrl,
3114
3261
  scope: "app:config:write",
3115
3262
  token: options.token,
3116
- tokenFile: (0, import_node_path7.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3263
+ tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3117
3264
  rootDir: cfg.rootDir,
3118
3265
  email: options.email,
3119
3266
  open: options.open,
@@ -3168,7 +3315,7 @@ function record3(value2) {
3168
3315
 
3169
3316
  // src/config-reconcile-command.ts
3170
3317
  var import_apps8 = require("@odla-ai/apps");
3171
- var import_node_path8 = require("path");
3318
+ var import_node_path9 = require("path");
3172
3319
 
3173
3320
  // src/config-reconcile.ts
3174
3321
  var import_apps7 = require("@odla-ai/apps");
@@ -3464,7 +3611,7 @@ async function inspectConfig(options) {
3464
3611
  platform: cfg.platformUrl,
3465
3612
  scope: "app:config:read",
3466
3613
  token: options.token,
3467
- tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3614
+ tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3468
3615
  rootDir: cfg.rootDir,
3469
3616
  email: options.email,
3470
3617
  open: options.open,
@@ -3597,12 +3744,12 @@ function quoteArg2(value2) {
3597
3744
  // src/doctor-checks.ts
3598
3745
  var import_node_child_process3 = require("child_process");
3599
3746
  var import_node_fs12 = require("fs");
3600
- var import_node_path10 = require("path");
3747
+ var import_node_path11 = require("path");
3601
3748
 
3602
3749
  // src/wrangler.ts
3603
3750
  var import_node_child_process2 = require("child_process");
3604
3751
  var import_node_fs11 = require("fs");
3605
- var import_node_path9 = require("path");
3752
+ var import_node_path10 = require("path");
3606
3753
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
3607
3754
  const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
3608
3755
  let stdout = "";
@@ -3616,7 +3763,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
3616
3763
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
3617
3764
  function findWranglerConfig(rootDir) {
3618
3765
  for (const name of WRANGLER_CONFIG_FILES) {
3619
- const path = (0, import_node_path9.join)(rootDir, name);
3766
+ const path = (0, import_node_path10.join)(rootDir, name);
3620
3767
  if ((0, import_node_fs11.existsSync)(path)) return path;
3621
3768
  }
3622
3769
  return null;
@@ -3729,10 +3876,10 @@ function wranglerWarnings(rootDir) {
3729
3876
  for (const { label, block } of blocks) {
3730
3877
  const assets = block.assets;
3731
3878
  if (assets?.directory) {
3732
- const dir = (0, import_node_path10.resolve)(rootDir, assets.directory);
3733
- if (dir === (0, import_node_path10.resolve)(rootDir)) {
3879
+ const dir = (0, import_node_path11.resolve)(rootDir, assets.directory);
3880
+ if (dir === (0, import_node_path11.resolve)(rootDir)) {
3734
3881
  warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
3735
- } else if ((0, import_node_fs12.existsSync)((0, import_node_path10.join)(dir, "node_modules"))) {
3882
+ } else if ((0, import_node_fs12.existsSync)((0, import_node_path11.join)(dir, "node_modules"))) {
3736
3883
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
3737
3884
  }
3738
3885
  }
@@ -3767,7 +3914,7 @@ function o11yProjectWarnings(rootDir) {
3767
3914
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
3768
3915
  return warnings;
3769
3916
  }
3770
- const main = typeof config.main === "string" ? (0, import_node_path10.resolve)(rootDir, config.main) : null;
3917
+ const main = typeof config.main === "string" ? (0, import_node_path11.resolve)(rootDir, config.main) : null;
3771
3918
  if (!main || !(0, import_node_fs12.existsSync)(main)) {
3772
3919
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
3773
3920
  } else {
@@ -3797,7 +3944,7 @@ function calendarProjectWarnings(rootDir) {
3797
3944
  }
3798
3945
  function readPackageJson(rootDir) {
3799
3946
  try {
3800
- return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path10.join)(rootDir, "package.json"), "utf8"));
3947
+ return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(rootDir, "package.json"), "utf8"));
3801
3948
  } catch {
3802
3949
  return null;
3803
3950
  }
@@ -4025,12 +4172,12 @@ function harnessOption(value2, flag) {
4025
4172
 
4026
4173
  // src/init.ts
4027
4174
  var import_node_fs13 = require("fs");
4028
- var import_node_path11 = require("path");
4175
+ var import_node_path12 = require("path");
4029
4176
  var import_apps9 = require("@odla-ai/apps");
4030
4177
  function initProject(options) {
4031
4178
  const out = options.stdout ?? console;
4032
- const rootDir = (0, import_node_path11.resolve)(options.rootDir ?? process.cwd());
4033
- const configPath = (0, import_node_path11.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
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");
4034
4181
  if ((0, import_node_fs13.existsSync)(configPath) && !options.force) {
4035
4182
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
4036
4183
  }
@@ -4047,12 +4194,12 @@ function initProject(options) {
4047
4194
  }
4048
4195
  }
4049
4196
  const aiProvider = options.aiProvider ?? "anthropic";
4050
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(configPath), { recursive: true });
4051
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.resolve)(rootDir, "src/odla"), { recursive: true });
4052
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.resolve)(rootDir, ".odla"), { recursive: true });
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 });
4053
4200
  (0, import_node_fs13.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
4054
- writeIfMissing((0, import_node_path11.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
4055
- writeIfMissing((0, import_node_path11.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
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());
4056
4203
  ensureGitignore(rootDir);
4057
4204
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
4058
4205
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -4293,7 +4440,7 @@ async function resolveVaultWrite(options) {
4293
4440
  // src/skill.ts
4294
4441
  var import_node_fs14 = require("fs");
4295
4442
  var import_node_os2 = require("os");
4296
- var import_node_path12 = require("path");
4443
+ var import_node_path13 = require("path");
4297
4444
  var import_node_url2 = require("url");
4298
4445
 
4299
4446
  // src/skill-adapters.ts
@@ -4372,8 +4519,8 @@ function installSkill(options = {}) {
4372
4519
  const files = listFiles(sourceDir);
4373
4520
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
4374
4521
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
4375
- const root = (0, import_node_path12.resolve)(options.dir ?? process.cwd());
4376
- const home = (0, import_node_path12.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
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)());
4377
4524
  const plans = /* @__PURE__ */ new Map();
4378
4525
  const targets = /* @__PURE__ */ new Map();
4379
4526
  const rememberTarget = (harness, target) => {
@@ -4387,48 +4534,48 @@ function installSkill(options = {}) {
4387
4534
  plans.set(target, { target, content: content2, boundary, managedMerge });
4388
4535
  };
4389
4536
  const planSkillTree = (targetDir2, boundary = root) => {
4390
- 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);
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);
4391
4538
  };
4392
4539
  let targetDir;
4393
4540
  if (options.global) {
4394
- const claudeRoot = (0, import_node_path12.join)(home, ".claude", "skills");
4395
- const codexRoot = (0, import_node_path12.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path12.join)(home, ".codex"), "skills");
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");
4396
4543
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
4397
4544
  for (const harness of harnesses) {
4398
4545
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
4399
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path12.dirname)((0, import_node_path12.dirname)(codexRoot)));
4546
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path13.dirname)((0, import_node_path13.dirname)(codexRoot)));
4400
4547
  rememberTarget(harness, skillRoot);
4401
4548
  }
4402
4549
  } else {
4403
- const sharedRoot = (0, import_node_path12.join)(root, ".agents", "skills");
4550
+ const sharedRoot = (0, import_node_path13.join)(root, ".agents", "skills");
4404
4551
  planSkillTree(sharedRoot);
4405
- const claudeRoot = (0, import_node_path12.join)(root, ".claude", "skills");
4552
+ const claudeRoot = (0, import_node_path13.join)(root, ".claude", "skills");
4406
4553
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
4407
4554
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
4408
4555
  if (harnesses.includes("claude")) {
4409
4556
  for (const skill of skillNames(files)) {
4410
- const canonical = (0, import_node_fs14.readFileSync)((0, import_node_path12.join)(sourceDir, skill, "SKILL.md"), "utf8");
4411
- plan((0, import_node_path12.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
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));
4412
4559
  }
4413
4560
  rememberTarget("claude", claudeRoot);
4414
4561
  }
4415
4562
  if (harnesses.includes("cursor")) {
4416
- const cursorRule = (0, import_node_path12.join)(root, ".cursor", "rules", "odla.mdc");
4563
+ const cursorRule = (0, import_node_path13.join)(root, ".cursor", "rules", "odla.mdc");
4417
4564
  plan(cursorRule, CURSOR_RULE);
4418
4565
  rememberTarget("cursor", cursorRule);
4419
4566
  }
4420
4567
  if (harnesses.includes("agents")) {
4421
- const agentsFile = (0, import_node_path12.join)(root, "AGENTS.md");
4568
+ const agentsFile = (0, import_node_path13.join)(root, "AGENTS.md");
4422
4569
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4423
4570
  rememberTarget("agents", agentsFile);
4424
4571
  }
4425
4572
  if (harnesses.includes("copilot")) {
4426
- const copilotFile = (0, import_node_path12.join)(root, ".github", "copilot-instructions.md");
4573
+ const copilotFile = (0, import_node_path13.join)(root, ".github", "copilot-instructions.md");
4427
4574
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4428
4575
  rememberTarget("copilot", copilotFile);
4429
4576
  }
4430
4577
  if (harnesses.includes("gemini")) {
4431
- const geminiFile = (0, import_node_path12.join)(root, "GEMINI.md");
4578
+ const geminiFile = (0, import_node_path13.join)(root, "GEMINI.md");
4432
4579
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4433
4580
  rememberTarget("gemini", geminiFile);
4434
4581
  }
@@ -4464,7 +4611,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4464
4611
  }
4465
4612
  for (const file of plans.values()) {
4466
4613
  if (!(0, import_node_fs14.existsSync)(file.target) || (0, import_node_fs14.readFileSync)(file.target, "utf8") !== file.content) {
4467
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.dirname)(file.target), { recursive: true });
4614
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(file.target), { recursive: true });
4468
4615
  (0, import_node_fs14.writeFileSync)(file.target, file.content);
4469
4616
  }
4470
4617
  }
@@ -4484,7 +4631,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4484
4631
  };
4485
4632
  }
4486
4633
  function pathsUnder(root, paths) {
4487
- 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();
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();
4488
4635
  }
4489
4636
  function normalizeHarnesses(values, global) {
4490
4637
  const requested = values?.length ? values : ["claude"];
@@ -4529,13 +4676,13 @@ function managedFileContent(path, block, force, boundary) {
4529
4676
  return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
4530
4677
  }
4531
4678
  function symlinkedComponent(boundary, target) {
4532
- const rel = (0, import_node_path12.relative)(boundary, target);
4533
- if (rel === ".." || rel.startsWith(`..${import_node_path12.sep}`) || (0, import_node_path12.isAbsolute)(rel)) {
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)) {
4534
4681
  throw new Error(`agent setup target escapes its install root: ${target}`);
4535
4682
  }
4536
4683
  let current = boundary;
4537
- for (const part of rel.split(import_node_path12.sep).filter(Boolean)) {
4538
- current = (0, import_node_path12.join)(current, part);
4684
+ for (const part of rel.split(import_node_path13.sep).filter(Boolean)) {
4685
+ current = (0, import_node_path13.join)(current, part);
4539
4686
  try {
4540
4687
  if ((0, import_node_fs14.lstatSync)(current).isSymbolicLink()) return current;
4541
4688
  } catch (error) {
@@ -4552,9 +4699,9 @@ function listFiles(dir) {
4552
4699
  const results = [];
4553
4700
  const walk = (current) => {
4554
4701
  for (const entry of (0, import_node_fs14.readdirSync)(current, { withFileTypes: true })) {
4555
- const path = (0, import_node_path12.join)(current, entry.name);
4702
+ const path = (0, import_node_path13.join)(current, entry.name);
4556
4703
  if (entry.isDirectory()) walk(path);
4557
- else results.push((0, import_node_path12.relative)(dir, path));
4704
+ else results.push((0, import_node_path13.relative)(dir, path));
4558
4705
  }
4559
4706
  };
4560
4707
  walk(dir);
@@ -4866,7 +5013,7 @@ async function projectCommand(command, parsed, deps) {
4866
5013
  // src/code-connect.ts
4867
5014
  var import_node_fs15 = require("fs");
4868
5015
  var import_node_os4 = require("os");
4869
- var import_node_path14 = require("path");
5016
+ var import_node_path15 = require("path");
4870
5017
 
4871
5018
  // ../harness/dist/chunk-QTUEF2HZ.js
4872
5019
  var HARNESS_PROTOCOL_VERSION = 1;
@@ -4952,15 +5099,15 @@ function encodeAgentInput(message2) {
4952
5099
  // ../harness/dist/chunk-PHXQH4YM.js
4953
5100
  var import_child_process = require("child_process");
4954
5101
  var import_fs = require("fs");
4955
- var import_promises2 = require("fs/promises");
5102
+ var import_promises3 = require("fs/promises");
4956
5103
  var import_path = require("path");
4957
5104
  var import_process = require("process");
4958
- var import_promises3 = require("fs/promises");
5105
+ var import_promises4 = require("fs/promises");
4959
5106
  var import_os = require("os");
4960
5107
  var import_path2 = require("path");
4961
5108
  var import_child_process2 = require("child_process");
4962
5109
  var import_path3 = require("path");
4963
- var import_promises4 = require("fs/promises");
5110
+ var import_promises5 = require("fs/promises");
4964
5111
  var import_os2 = require("os");
4965
5112
  var import_path4 = require("path");
4966
5113
  var import_child_process3 = require("child_process");
@@ -4971,7 +5118,7 @@ function assertPinnedImage(image) {
4971
5118
  async function commandAvailable(engine) {
4972
5119
  for (const directory of (process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean)) {
4973
5120
  try {
4974
- await (0, import_promises2.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
5121
+ await (0, import_promises3.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
4975
5122
  return true;
4976
5123
  } catch {
4977
5124
  }
@@ -5257,7 +5404,7 @@ async function gitBlobs(cwd, entries, maxBytes) {
5257
5404
  }
5258
5405
  async function materializeGitTree(source, commitSha, options = {}) {
5259
5406
  if (!/^[0-9a-f]{40}$/.test(commitSha)) throw new TypeError("Git tree requires an exact commit SHA");
5260
- const sourceDir = await (0, import_promises3.realpath)((0, import_path2.resolve)(source));
5407
+ const sourceDir = await (0, import_promises4.realpath)((0, import_path2.resolve)(source));
5261
5408
  const maxFiles = options.maxFiles ?? 2e4;
5262
5409
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5263
5410
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
@@ -5266,9 +5413,9 @@ async function materializeGitTree(source, commitSha, options = {}) {
5266
5413
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
5267
5414
  });
5268
5415
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
5269
- const root = await (0, import_promises3.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
5416
+ const root = await (0, import_promises4.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
5270
5417
  const targetRoot = (0, import_path2.join)(root, "source");
5271
- await (0, import_promises3.mkdir)(targetRoot);
5418
+ await (0, import_promises4.mkdir)(targetRoot);
5272
5419
  let byteCount = 0;
5273
5420
  try {
5274
5421
  const blobs = await gitBlobs(sourceDir, entries, maxBytes);
@@ -5278,18 +5425,18 @@ async function materializeGitTree(source, commitSha, options = {}) {
5278
5425
  if (byteCount > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
5279
5426
  const target = (0, import_path2.resolve)(targetRoot, entry.path);
5280
5427
  if (!target.startsWith(`${(0, import_path2.resolve)(targetRoot)}${import_path2.sep}`)) throw new TypeError("Git tree path escapes workspace");
5281
- await (0, import_promises3.mkdir)((0, import_path2.resolve)(target, ".."), { recursive: true });
5282
- await (0, import_promises3.writeFile)(target, content2, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
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 });
5283
5430
  }
5284
5431
  return {
5285
5432
  root,
5286
5433
  sourceDir: targetRoot,
5287
5434
  fileCount: entries.length,
5288
5435
  byteCount,
5289
- cleanup: () => (0, import_promises3.rm)(root, { recursive: true, force: true })
5436
+ cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5290
5437
  };
5291
5438
  } catch (error) {
5292
- await (0, import_promises3.rm)(root, { recursive: true, force: true });
5439
+ await (0, import_promises4.rm)(root, { recursive: true, force: true });
5293
5440
  throw error;
5294
5441
  }
5295
5442
  }
@@ -5297,7 +5444,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5297
5444
  const files = [];
5298
5445
  let bytes = 0;
5299
5446
  const walk = async (dir) => {
5300
- for (const entry of await (0, import_promises4.readdir)(dir, { withFileTypes: true })) {
5447
+ for (const entry of await (0, import_promises5.readdir)(dir, { withFileTypes: true })) {
5301
5448
  if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
5302
5449
  if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
5303
5450
  const path = (0, import_path4.join)(dir, entry.name);
@@ -5307,7 +5454,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5307
5454
  continue;
5308
5455
  }
5309
5456
  if (!entry.isFile()) continue;
5310
- const metadata2 = await (0, import_promises4.stat)(path);
5457
+ const metadata2 = await (0, import_promises5.stat)(path);
5311
5458
  bytes += metadata2.size;
5312
5459
  if (files.length + 1 > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
5313
5460
  if (bytes > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
@@ -5356,7 +5503,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5356
5503
  if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
5357
5504
  let metadata2;
5358
5505
  try {
5359
- metadata2 = await (0, import_promises4.lstat)(source);
5506
+ metadata2 = await (0, import_promises5.lstat)(source);
5360
5507
  } catch (error) {
5361
5508
  if (error.code === "ENOENT") continue;
5362
5509
  throw error;
@@ -5371,9 +5518,9 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5371
5518
  async function copyTree(files, destination) {
5372
5519
  for (const file of files) {
5373
5520
  const target = (0, import_path4.join)(destination, file.relativePath);
5374
- await (0, import_promises4.mkdir)((0, import_path4.resolve)(target, ".."), { recursive: true });
5375
- await (0, import_promises4.copyFile)(file.source, target);
5376
- await (0, import_promises4.chmod)(target, file.mode);
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);
5377
5524
  }
5378
5525
  }
5379
5526
  async function captureGitDiff(root, maxBytes) {
@@ -5410,13 +5557,13 @@ async function captureGitDiff(root, maxBytes) {
5410
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");
5411
5558
  }
5412
5559
  async function stageWorkspace(source, options = {}) {
5413
- const sourceDir = await (0, import_promises4.realpath)((0, import_path4.resolve)(source));
5414
- const sourceStat = await (0, import_promises4.stat)(sourceDir);
5560
+ const sourceDir = await (0, import_promises5.realpath)((0, import_path4.resolve)(source));
5561
+ const sourceStat = await (0, import_promises5.stat)(sourceDir);
5415
5562
  if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
5416
- const root = await (0, import_promises4.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5563
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5417
5564
  const baselineDir = (0, import_path4.join)(root, "baseline");
5418
5565
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5419
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5566
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5420
5567
  try {
5421
5568
  const maxFiles = options.maxFiles ?? 2e4;
5422
5569
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
@@ -5429,26 +5576,26 @@ async function stageWorkspace(source, options = {}) {
5429
5576
  fileCount: files.length,
5430
5577
  byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
5431
5578
  patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
5432
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5579
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5433
5580
  };
5434
5581
  } catch (error) {
5435
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5582
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5436
5583
  throw error;
5437
5584
  }
5438
5585
  }
5439
5586
  async function stageWorkspacePair(baselineSource, workspaceSource, options = {}) {
5440
- const baselineDirSource = await (0, import_promises4.realpath)((0, import_path4.resolve)(baselineSource));
5441
- const workspaceDirSource = await (0, import_promises4.realpath)((0, import_path4.resolve)(workspaceSource));
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));
5442
5589
  const maxFiles = options.maxFiles ?? 2e4;
5443
5590
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5444
5591
  const [baselineFiles, workspaceFiles] = await Promise.all([
5445
5592
  sourceFiles(baselineDirSource, maxFiles, maxBytes),
5446
5593
  sourceFiles(workspaceDirSource, maxFiles, maxBytes)
5447
5594
  ]);
5448
- const root = await (0, import_promises4.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5595
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5449
5596
  const baselineDir = (0, import_path4.join)(root, "baseline");
5450
5597
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5451
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5598
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5452
5599
  try {
5453
5600
  await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
5454
5601
  return {
@@ -5458,17 +5605,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
5458
5605
  fileCount: workspaceFiles.length,
5459
5606
  byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
5460
5607
  patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
5461
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5608
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5462
5609
  };
5463
5610
  } catch (error) {
5464
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5611
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5465
5612
  throw error;
5466
5613
  }
5467
5614
  }
5468
5615
 
5469
5616
  // ../harness/dist/chunk-GMVZ4LZH.js
5470
5617
  var import_crypto = require("crypto");
5471
- var import_promises5 = require("fs/promises");
5618
+ var import_promises6 = require("fs/promises");
5472
5619
  var import_path5 = require("path");
5473
5620
 
5474
5621
  // ../camel/dist/chunk-7FHPOQVP.js
@@ -5805,19 +5952,19 @@ function validateSnapshot(snapshot, limits) {
5805
5952
 
5806
5953
  // ../harness/dist/chunk-GMVZ4LZH.js
5807
5954
  var import_child_process4 = require("child_process");
5808
- var import_promises6 = require("fs/promises");
5955
+ var import_promises7 = require("fs/promises");
5809
5956
  var import_path6 = require("path");
5810
5957
  var import_child_process5 = require("child_process");
5811
5958
  var import_process2 = require("process");
5812
5959
  var import_crypto2 = require("crypto");
5813
5960
  var import_crypto3 = require("crypto");
5814
5961
  var import_fs2 = require("fs");
5815
- var import_promises7 = require("fs/promises");
5816
- var import_path7 = require("path");
5817
5962
  var import_promises8 = require("fs/promises");
5963
+ var import_path7 = require("path");
5964
+ var import_promises9 = require("fs/promises");
5818
5965
  var import_os3 = require("os");
5819
5966
  var import_path8 = require("path");
5820
- var import_promises9 = require("fs/promises");
5967
+ var import_promises10 = require("fs/promises");
5821
5968
  var import_path9 = require("path");
5822
5969
 
5823
5970
  // ../camel/dist/chunk-LAXU2AVK.js
@@ -6101,7 +6248,7 @@ var import_crypto4 = require("crypto");
6101
6248
  async function digestStagedWorkspace(root, limits) {
6102
6249
  const files = [];
6103
6250
  const walk = async (directory) => {
6104
- const entries = await (0, import_promises5.readdir)(directory, { withFileTypes: true });
6251
+ const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
6105
6252
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
6106
6253
  if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
6107
6254
  const target = (0, import_path5.resolve)(directory, entry.name);
@@ -6116,7 +6263,7 @@ async function digestStagedWorkspace(root, limits) {
6116
6263
  const hash = (0, import_crypto.createHash)("sha256");
6117
6264
  let bytes = 0;
6118
6265
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
6119
- const content2 = await (0, import_promises5.readFile)(file.target);
6266
+ const content2 = await (0, import_promises6.readFile)(file.target);
6120
6267
  bytes += Buffer.byteLength(file.path) + content2.byteLength;
6121
6268
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
6122
6269
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content2.byteLength}:`);
@@ -6442,7 +6589,7 @@ async function applyCodePatch(workspaceDir, patch2, paths) {
6442
6589
  await gitApply(workspaceDir, patch2, false);
6443
6590
  for (const path of paths) {
6444
6591
  try {
6445
- const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
6592
+ const info = await (0, import_promises7.lstat)(resolveCodePath(workspaceDir, path));
6446
6593
  if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
6447
6594
  throw new TypeError("patch created a non-regular workspace entry");
6448
6595
  }
@@ -6733,7 +6880,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
6733
6880
  for (const artifact of recipe2.expectedArtifacts ?? []) {
6734
6881
  try {
6735
6882
  const path = (0, import_path7.join)(workspaceDir, artifact.path);
6736
- const info = await (0, import_promises7.lstat)(path);
6883
+ const info = await (0, import_promises8.lstat)(path);
6737
6884
  if (!info.isFile() || info.isSymbolicLink()) {
6738
6885
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
6739
6886
  } else if (info.size > artifact.maximumBytes) {
@@ -6914,9 +7061,9 @@ var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_mod
6914
7061
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
6915
7062
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.tmpdir)()) {
6916
7063
  if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
6917
- const root = await (0, import_promises8.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
7064
+ const root = await (0, import_promises9.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
6918
7065
  const sourceDir = (0, import_path8.join)(root, "source");
6919
- await (0, import_promises8.mkdir)(sourceDir);
7066
+ await (0, import_promises9.mkdir)(sourceDir);
6920
7067
  const seen = /* @__PURE__ */ new Set();
6921
7068
  let bytes = 0;
6922
7069
  try {
@@ -6928,8 +7075,8 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
6928
7075
  if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
6929
7076
  const target = (0, import_path8.resolve)(sourceDir, file.path);
6930
7077
  if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code source path escapes its root");
6931
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
6932
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 420 });
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 });
6933
7080
  }
6934
7081
  for (const reference of snapshot.references ?? []) {
6935
7082
  validateAlias(reference.alias);
@@ -6943,13 +7090,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
6943
7090
  if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
6944
7091
  const target = (0, import_path8.resolve)(sourceDir, path);
6945
7092
  if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
6946
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
6947
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
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 });
6948
7095
  }
6949
7096
  }
6950
- return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
7097
+ return { sourceDir, cleanup: () => (0, import_promises9.rm)(root, { recursive: true, force: true }) };
6951
7098
  } catch (cause) {
6952
- await (0, import_promises8.rm)(root, { recursive: true, force: true });
7099
+ await (0, import_promises9.rm)(root, { recursive: true, force: true });
6953
7100
  throw cause;
6954
7101
  }
6955
7102
  }
@@ -6970,8 +7117,8 @@ async function attachCodeRuntimeReferences(workspace, references) {
6970
7117
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
6971
7118
  const target = (0, import_path8.resolve)(root, path);
6972
7119
  if (!target.startsWith(`${(0, import_path8.resolve)(root)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
6973
- await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
6974
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
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 });
6975
7122
  }
6976
7123
  }
6977
7124
  }
@@ -7157,11 +7304,11 @@ async function read(context, request2, options, policy) {
7157
7304
  const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
7158
7305
  if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
7159
7306
  const target = resolveCodePath(context.workspaceDir, path);
7160
- const info = await (0, import_promises9.stat)(target);
7307
+ const info = await (0, import_promises10.stat)(target);
7161
7308
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
7162
7309
  throw new TypeError("file is not a bounded regular source file");
7163
7310
  }
7164
- const source = await (0, import_promises9.readFile)(target);
7311
+ const source = await (0, import_promises10.readFile)(target);
7165
7312
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
7166
7313
  const lines = source.toString("utf8").split("\n");
7167
7314
  const content2 = lines.slice(startLine - 1, endLine).join("\n");
@@ -7238,7 +7385,7 @@ function policyContext(context, request2, options, extra) {
7238
7385
  async function registeredFiles(root, limit) {
7239
7386
  const paths = [];
7240
7387
  const walk = async (directory) => {
7241
- for (const entry of await (0, import_promises9.readdir)(directory, { withFileTypes: true })) {
7388
+ for (const entry of await (0, import_promises10.readdir)(directory, { withFileTypes: true })) {
7242
7389
  if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
7243
7390
  const target = (0, import_path9.resolve)(directory, entry.name);
7244
7391
  if (entry.isDirectory()) await walk(target);
@@ -7837,8 +7984,8 @@ function hostedPollTimeout(value2 = 10 * 6e4) {
7837
7984
  }
7838
7985
  async function waitForHostedPoll(milliseconds, signal) {
7839
7986
  if (signal?.aborted) throw signal.reason ?? new DOMException("aborted", "AbortError");
7840
- await new Promise((resolve12, reject) => {
7841
- const timer = setTimeout(resolve12, milliseconds);
7987
+ await new Promise((resolve13, reject) => {
7988
+ const timer = setTimeout(resolve13, milliseconds);
7842
7989
  signal?.addEventListener("abort", () => {
7843
7990
  clearTimeout(timer);
7844
7991
  reject(signal.reason ?? new DOMException("aborted", "AbortError"));
@@ -8023,9 +8170,9 @@ function digestText(value2) {
8023
8170
  // src/code-images.ts
8024
8171
  var import_node_child_process6 = require("child_process");
8025
8172
  var import_node_crypto3 = require("crypto");
8026
- var import_promises10 = require("fs/promises");
8173
+ var import_promises11 = require("fs/promises");
8027
8174
  var import_node_os3 = require("os");
8028
- var import_node_path13 = require("path");
8175
+ var import_node_path14 = require("path");
8029
8176
  var import_node_url3 = require("url");
8030
8177
 
8031
8178
  // src/code-runtime-config.ts
@@ -8105,16 +8252,16 @@ function embeddedPiAssetPath() {
8105
8252
  return (0, import_node_url3.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
8106
8253
  }
8107
8254
  async function embeddedPiImageName() {
8108
- const bundle = await (0, import_promises10.readFile)(embeddedPiAssetPath()).catch(() => {
8255
+ const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
8109
8256
  throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
8110
8257
  });
8111
8258
  return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
8112
8259
  }
8113
8260
  async function buildEmbeddedPiImage(engine, image, run) {
8114
- const context = await (0, import_promises10.mkdtemp)((0, import_node_path13.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
8261
+ const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
8115
8262
  try {
8116
- await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path13.join)(context, "pi-agent.js"));
8117
- await (0, import_promises10.writeFile)((0, import_node_path13.join)(context, "Dockerfile"), [
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"), [
8118
8265
  `FROM ${CODE_NODE_IMAGE}`,
8119
8266
  "COPY pi-agent.js /opt/odla/pi-agent.js",
8120
8267
  "WORKDIR /workspace",
@@ -8123,14 +8270,14 @@ async function buildEmbeddedPiImage(engine, image, run) {
8123
8270
  ].join("\n"), { mode: 384 });
8124
8271
  await run(engine, ["build", "--tag", image, context], "inherit");
8125
8272
  } finally {
8126
- await (0, import_promises10.rm)(context, { recursive: true, force: true });
8273
+ await (0, import_promises11.rm)(context, { recursive: true, force: true });
8127
8274
  }
8128
8275
  }
8129
8276
 
8130
8277
  // src/code-connect.ts
8131
8278
  async function codeConnect(options) {
8132
8279
  const cwd = options.cwd ?? process.cwd();
8133
- const configPath = (0, import_node_path14.resolve)(cwd, options.configPath);
8280
+ const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
8134
8281
  const cfg = (0, import_node_fs15.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
8135
8282
  const requestedAppId = options.appId?.trim();
8136
8283
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -8499,10 +8646,8 @@ function requireName(parsed) {
8499
8646
  return name;
8500
8647
  }
8501
8648
 
8502
- // src/help.ts
8503
- function printHelp(output = console) {
8504
- output.log(`odla-ai
8505
-
8649
+ // src/help-usage.ts
8650
+ var USAGE_SECTION = `
8506
8651
  Start here:
8507
8652
  odla-ai runbook ask "<question>" The current procedure, from odla's own
8508
8653
  runbooks. Ask BEFORE searching the web or
@@ -8536,6 +8681,7 @@ Usage:
8536
8681
  odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
8537
8682
  odla-ai app owners add <email> [--email <odla-account>] [--json]
8538
8683
  odla-ai app owners remove <email> [--email <odla-account>] [--json]
8684
+ odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
8539
8685
  odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8540
8686
  odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8541
8687
  odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
@@ -8615,8 +8761,12 @@ Usage:
8615
8761
  odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
8616
8762
  odla-ai secrets set <name> --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8617
8763
  odla-ai secrets set-clerk-key --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8618
- odla-ai version
8764
+ odla-ai version`;
8619
8765
 
8766
+ // src/help.ts
8767
+ function printHelp(output = console) {
8768
+ output.log(`odla-ai
8769
+ ${USAGE_SECTION}
8620
8770
  Commands:
8621
8771
  agent Inspect durable agent wakeups and explicitly requeue a
8622
8772
  dead-lettered job; JSON output is stable for remote operators.
@@ -9065,7 +9215,7 @@ function jsonl(ctx, parsed, value2) {
9065
9215
  }
9066
9216
  async function discussWatch(ctx, topicId, parsed) {
9067
9217
  if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
9068
- const sleep = ctx.sleep ?? ((ms) => new Promise((resolve12) => setTimeout(resolve12, ms)));
9218
+ const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
9069
9219
  const now = ctx.now ?? Date.now;
9070
9220
  const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
9071
9221
  const timeoutSeconds = numberOpt2(parsed, "timeout");
@@ -10537,6 +10687,7 @@ var COMMAND_SURFACE = {
10537
10687
  promote: {},
10538
10688
  owners: { list: {}, add: {}, remove: {} }
10539
10689
  },
10690
+ brand: { design: { unpack: {} } },
10540
10691
  calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
10541
10692
  capabilities: {},
10542
10693
  code: { connect: {} },
@@ -10833,7 +10984,7 @@ async function runbookRemove(ctx, slug) {
10833
10984
 
10834
10985
  // src/runbook-import.ts
10835
10986
  var import_node_fs18 = require("fs");
10836
- var import_node_path15 = require("path");
10987
+ var import_node_path16 = require("path");
10837
10988
  function parseRunbook(text2, slug) {
10838
10989
  let rest = text2;
10839
10990
  const meta = {};
@@ -10862,8 +11013,8 @@ function readRunbookDir(dir) {
10862
11013
  const files = (0, import_node_fs18.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
10863
11014
  if (!files.length) throw new Error(`no .md files in ${dir}`);
10864
11015
  return files.map((file) => {
10865
- const slug = (0, import_node_path15.basename)(file, ".md");
10866
- const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0, import_node_path15.join)(dir, file), "utf8"), slug);
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);
10867
11018
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
10868
11019
  });
10869
11020
  }
@@ -10937,7 +11088,7 @@ async function upsert(ctx, r, visibility) {
10937
11088
  // src/runbook-impact.ts
10938
11089
  var import_node_child_process7 = require("child_process");
10939
11090
  var import_node_fs19 = require("fs");
10940
- var import_node_path16 = require("path");
11091
+ var import_node_path17 = require("path");
10941
11092
 
10942
11093
  // src/runbook-impact-scan.ts
10943
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$]*)/;
@@ -11106,7 +11257,7 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
11106
11257
  }
11107
11258
  function manifestLabeller(root) {
11108
11259
  return (workspace) => {
11109
- const manifest = (0, import_node_path16.join)(root, workspace, "package.json");
11260
+ const manifest = (0, import_node_path17.join)(root, workspace, "package.json");
11110
11261
  if (!(0, import_node_fs19.existsSync)(manifest)) return void 0;
11111
11262
  try {
11112
11263
  const name = JSON.parse((0, import_node_fs19.readFileSync)(manifest, "utf8")).name;
@@ -11176,7 +11327,7 @@ function report3(ctx, impacts) {
11176
11327
  async function runbookImpact(ctx, options, deps = {}) {
11177
11328
  const cwd = deps.cwd ?? process.cwd();
11178
11329
  const runGit = deps.runGit ?? gitRunner(cwd);
11179
- const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path16.join)(cwd, path), "utf8"));
11330
+ const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(cwd, path), "utf8"));
11180
11331
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
11181
11332
  if (!surfaces.length) {
11182
11333
  return ctx.out.log(
@@ -11311,7 +11462,7 @@ async function runbookComment(ctx, slug, body) {
11311
11462
  var import_node_child_process8 = require("child_process");
11312
11463
  var import_node_fs20 = require("fs");
11313
11464
  var import_node_os5 = require("os");
11314
- var import_node_path17 = require("path");
11465
+ var import_node_path18 = require("path");
11315
11466
  var import_node_process12 = __toESM(require("process"), 1);
11316
11467
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
11317
11468
  function resolveEditor(env = import_node_process12.default.env) {
@@ -11337,8 +11488,8 @@ function editText(initial, slug, deps = {}) {
11337
11488
  );
11338
11489
  if (!interactive())
11339
11490
  throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
11340
- const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path17.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
11341
- const file = (0, import_node_path17.join)(dir, `${slug}.md`);
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`);
11342
11493
  try {
11343
11494
  (0, import_node_fs20.writeFileSync)(file, initial, { mode: 384 });
11344
11495
  const code = defaultRunOrInjected(deps)(editor, file);
@@ -11680,7 +11831,7 @@ async function runbookCommand(parsed, deps = {}) {
11680
11831
  }
11681
11832
 
11682
11833
  // src/security-command-context.ts
11683
- var import_promises11 = require("readline/promises");
11834
+ var import_promises12 = require("readline/promises");
11684
11835
  async function hostedSecurityContext(parsed, dependencies) {
11685
11836
  const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
11686
11837
  const cfg = await loadProjectConfig(configPath);
@@ -11706,7 +11857,7 @@ async function hostedSecurityContext(parsed, dependencies) {
11706
11857
  async function interactiveConfirmation(message2, dependencies) {
11707
11858
  if (dependencies.confirm) return dependencies.confirm(message2);
11708
11859
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
11709
- const prompt = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stdout });
11860
+ const prompt = (0, import_promises12.createInterface)({ input: process.stdin, output: process.stdout });
11710
11861
  try {
11711
11862
  const answer = await prompt.question(`${message2} [y/N] `);
11712
11863
  return /^y(?:es)?$/i.test(answer.trim());
@@ -11833,7 +11984,7 @@ function hostedSeverity(value2, flag) {
11833
11984
  var import_security2 = require("@odla-ai/security");
11834
11985
 
11835
11986
  // src/security.ts
11836
- var import_node_path18 = require("path");
11987
+ var import_node_path19 = require("path");
11837
11988
  var import_security = require("@odla-ai/security");
11838
11989
  var import_node3 = require("@odla-ai/security/node");
11839
11990
  async function runHostedSecurity(options) {
@@ -11845,9 +11996,9 @@ async function runHostedSecurity(options) {
11845
11996
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
11846
11997
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
11847
11998
  const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
11848
- const target = (0, import_node_path18.resolve)(options.target ?? cfg?.rootDir ?? ".");
11849
- const output = (0, import_node_path18.resolve)(options.out ?? (0, import_node_path18.resolve)(target, ".odla/security/hosted"));
11850
- const outputRelative = (0, import_node_path18.relative)(target, output).split(import_node_path18.sep).join("/");
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("/");
11851
12002
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
11852
12003
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
11853
12004
  const tokenRequest = {
@@ -11859,7 +12010,7 @@ async function runHostedSecurity(options) {
11859
12010
  };
11860
12011
  const token = await injectedToken(options, tokenRequest);
11861
12012
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
11862
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path18.isAbsolute)(outputRelative) ? [outputRelative] : []
12013
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path19.isAbsolute)(outputRelative) ? [outputRelative] : []
11863
12014
  });
11864
12015
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
11865
12016
  platform,
@@ -11877,7 +12028,7 @@ async function runHostedSecurity(options) {
11877
12028
  });
11878
12029
  const harness = (0, import_security.createSecurityHarness)({
11879
12030
  profile,
11880
- store: new import_node3.FileRunStore((0, import_node_path18.resolve)(output, "state")),
12031
+ store: new import_node3.FileRunStore((0, import_node_path19.resolve)(output, "state")),
11881
12032
  discoveryReasoner: hosted.discoveryReasoner,
11882
12033
  validationReasoner: hosted.validationReasoner,
11883
12034
  policy: {
@@ -11901,7 +12052,7 @@ async function runHostedSecurity(options) {
11901
12052
  function selectEnv(requested, declared, configPath, rootDir) {
11902
12053
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
11903
12054
  if (!env || !declared.includes(env)) {
11904
- const shown = (0, import_node_path18.relative)(rootDir, configPath) || configPath;
12055
+ const shown = (0, import_node_path19.relative)(rootDir, configPath) || configPath;
11905
12056
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
11906
12057
  }
11907
12058
  return env;
@@ -11930,7 +12081,7 @@ function printSummary(out, appId, env, run, report4, output) {
11930
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}`);
11931
12082
  if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
11932
12083
  out.log(` findings: confirmed=${report4.metrics.confirmed} needs_reproduction=${report4.metrics.needsReproduction} candidates=${report4.metrics.candidates}`);
11933
- out.log(` report: ${(0, import_node_path18.resolve)(output, "REPORT.md")}`);
12084
+ out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
11934
12085
  }
11935
12086
  function formatBudget(usage) {
11936
12087
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
@@ -12431,6 +12582,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
12431
12582
  await securityCommand(parsed, runtime);
12432
12583
  return;
12433
12584
  }
12585
+ if (command === "brand") {
12586
+ await brandCommand(parsed, runtime);
12587
+ return;
12588
+ }
12434
12589
  if (command === "pm") {
12435
12590
  await pmCommand(parsed, runtime);
12436
12591
  return;