@odla-ai/cli 0.26.2 → 0.27.1

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
  }
@@ -338,6 +338,7 @@ async function freshHandshake(ctx, waitMs) {
338
338
  endpoint: ctx.cfg.platformUrl,
339
339
  email: ctx.email,
340
340
  label: `${ctx.cfg.app.id} provisioner`,
341
+ projectIds: [ctx.cfg.app.id],
341
342
  fetch: ctx.doFetch,
342
343
  waitMs,
343
344
  onCode: async ({ userCode, deviceCode, expiresIn, interval, verificationUriComplete }) => {
@@ -2175,6 +2176,153 @@ async function appCommand(parsed, dependencies = {}) {
2175
2176
  else await appRestore(options);
2176
2177
  }
2177
2178
 
2179
+ // src/brand-command.ts
2180
+ var import_promises2 = require("fs/promises");
2181
+ var import_node_path7 = require("path");
2182
+
2183
+ // src/brand-design-unpack.ts
2184
+ var import_node_zlib = require("zlib");
2185
+ var import_brand = require("@odla-ai/brand");
2186
+ var EXTENSIONS = {
2187
+ "image/png": "png",
2188
+ "image/jpeg": "jpg",
2189
+ "image/gif": "gif",
2190
+ "image/webp": "webp",
2191
+ "image/svg+xml": "svg",
2192
+ "image/avif": "avif",
2193
+ "font/woff2": "woff2",
2194
+ "font/woff": "woff",
2195
+ "font/ttf": "ttf",
2196
+ "font/otf": "otf",
2197
+ "text/javascript": "js",
2198
+ "application/javascript": "js",
2199
+ "text/css": "css",
2200
+ "text/html": "html",
2201
+ "application/json": "json"
2202
+ };
2203
+ var encode = (text2) => new TextEncoder().encode(text2);
2204
+ function assetFileName(uuid, mime) {
2205
+ const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
2206
+ return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
2207
+ }
2208
+ function decodePayload(data, compressed) {
2209
+ const raw = Buffer.from(data, "base64");
2210
+ return new Uint8Array(compressed ? (0, import_node_zlib.gunzipSync)(raw) : raw);
2211
+ }
2212
+ function rewriteReferences(template, assetPaths, pagePaths) {
2213
+ let out = template;
2214
+ for (const [uuid, path] of pagePaths) {
2215
+ out = out.split(`about:blank#${uuid}`).join(path);
2216
+ }
2217
+ for (const [uuid, path] of assetPaths) {
2218
+ out = out.split(uuid).join(path);
2219
+ }
2220
+ return out;
2221
+ }
2222
+ function tokensCss(digest) {
2223
+ if (Object.keys(digest.tokens.light).length === 0) return null;
2224
+ return (0, import_brand.renderTokensCss)(digest.tokens.light, { dark: digest.tokens.dark });
2225
+ }
2226
+ function unpackDesign(html) {
2227
+ const bundle = (0, import_brand.parseDesignBundle)(html);
2228
+ const manifest = (0, import_brand.readDesignManifest)(html);
2229
+ const digest = (0, import_brand.digestDesignBundle)(bundle);
2230
+ const pageUuids = new Set(bundle.pageOrder);
2231
+ const files = [];
2232
+ const failed = [];
2233
+ const assetPaths = /* @__PURE__ */ new Map();
2234
+ const pagePaths = /* @__PURE__ */ new Map();
2235
+ for (const [uuid, entry] of Object.entries(manifest)) {
2236
+ let bytes;
2237
+ try {
2238
+ bytes = decodePayload(entry.data, entry.compressed);
2239
+ } catch {
2240
+ failed.push(uuid);
2241
+ continue;
2242
+ }
2243
+ if (pageUuids.has(uuid)) {
2244
+ const path2 = `pages/${assetFileName(uuid, "text/html")}`;
2245
+ pagePaths.set(uuid, `./${path2}`);
2246
+ files.push({ path: path2, bytes });
2247
+ continue;
2248
+ }
2249
+ const path = `assets/${assetFileName(uuid, entry.mime)}`;
2250
+ assetPaths.set(uuid, `./${path}`);
2251
+ files.push({ path, bytes });
2252
+ }
2253
+ files.push({
2254
+ path: "index.html",
2255
+ bytes: encode(rewriteReferences(bundle.template, assetPaths, pagePaths))
2256
+ });
2257
+ files.push({ path: "digest.json", bytes: encode(`${JSON.stringify(digest, null, 2)}
2258
+ `) });
2259
+ const css = tokensCss(digest);
2260
+ if (css !== null) files.push({ path: "tokens.css", bytes: encode(css) });
2261
+ if (bundle.thumbnailSvg) files.push({ path: "thumbnail.svg", bytes: encode(bundle.thumbnailSvg) });
2262
+ return { files, digest, failed };
2263
+ }
2264
+ function describeUnpack(result, outDir) {
2265
+ const { digest } = result;
2266
+ const lines = [
2267
+ `Unpacked ${digest.title ? `"${digest.title}"` : "design"} into ${outDir}`,
2268
+ ` index.html the design, offline-runnable (${digest.templateBytes} bytes)`,
2269
+ ` assets/ ${digest.assetCount} embedded files (${digest.assetBytes} bytes)`,
2270
+ ` digest.json ${Object.keys(digest.tokens.light).length} --ui-* tokens, ${digest.props.length} props, ${digest.outline.length} headings`
2271
+ ];
2272
+ if (result.files.some((f) => f.path === "tokens.css"))
2273
+ lines.push(" tokens.css the design's tokens as an @odla-ai/ui theme sheet");
2274
+ if (digest.fonts.length > 0) lines.push(`Typefaces: ${digest.fonts.join(", ")}`);
2275
+ if (result.failed.length > 0)
2276
+ lines.push(`WARNING: ${result.failed.length} embedded asset(s) could not be decoded.`);
2277
+ return lines;
2278
+ }
2279
+
2280
+ // src/brand-command.ts
2281
+ var USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
2282
+ async function readBundle(source, deps) {
2283
+ if (source !== "-") return (0, import_promises2.readFile)((0, import_node_path7.resolve)(source), "utf8");
2284
+ const readStdin = deps.readStdin;
2285
+ if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
2286
+ return readStdin();
2287
+ }
2288
+ async function writeAll(result, outDir) {
2289
+ for (const file of result.files) {
2290
+ const target = (0, import_node_path7.resolve)(outDir, file.path);
2291
+ await (0, import_promises2.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
2292
+ await (0, import_promises2.writeFile)(target, file.bytes);
2293
+ }
2294
+ }
2295
+ async function designUnpack(parsed, deps) {
2296
+ assertArgs(parsed, ["out", "json"], 4);
2297
+ const source = parsed.positionals[3];
2298
+ if (!source) throw new Error(USAGE);
2299
+ const outDir = (0, import_node_path7.resolve)(stringOpt(parsed.options.out) ?? "design");
2300
+ const result = unpackDesign(await readBundle(source, deps));
2301
+ await writeAll(result, outDir);
2302
+ const out = deps.stdout ?? console;
2303
+ if (parsed.options.json === true) {
2304
+ out.log(
2305
+ JSON.stringify({
2306
+ outDir,
2307
+ files: result.files.map((f) => f.path),
2308
+ failed: result.failed,
2309
+ digest: result.digest
2310
+ })
2311
+ );
2312
+ return;
2313
+ }
2314
+ for (const line of describeUnpack(result, outDir)) out.log(line);
2315
+ }
2316
+ async function brandCommand(parsed, deps) {
2317
+ const subject = parsed.positionals[1];
2318
+ const action2 = parsed.positionals[2];
2319
+ if (subject === "design" && action2 === "unpack") {
2320
+ await designUnpack(parsed, deps);
2321
+ return;
2322
+ }
2323
+ throw new Error(USAGE);
2324
+ }
2325
+
2178
2326
  // src/calendar-errors.ts
2179
2327
  var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
2180
2328
  "calendar_google_oauth_not_configured",
@@ -2448,8 +2596,8 @@ function credential(value2) {
2448
2596
  // src/calendar-poll.ts
2449
2597
  async function waitForCalendarPoll(milliseconds, signal) {
2450
2598
  if (signal?.aborted) throw signal.reason ?? new Error("calendar connection aborted");
2451
- await new Promise((resolve12, reject) => {
2452
- const timer = setTimeout(resolve12, milliseconds);
2599
+ await new Promise((resolve13, reject) => {
2600
+ const timer = setTimeout(resolve13, milliseconds);
2453
2601
  signal?.addEventListener("abort", () => {
2454
2602
  clearTimeout(timer);
2455
2603
  reject(signal.reason ?? new Error("calendar connection aborted"));
@@ -2703,7 +2851,7 @@ function printGroup(out, heading, items) {
2703
2851
 
2704
2852
  // src/config-operation-command.ts
2705
2853
  var import_apps6 = require("@odla-ai/apps");
2706
- var import_node_path7 = require("path");
2854
+ var import_node_path8 = require("path");
2707
2855
 
2708
2856
  // src/version.ts
2709
2857
  var import_node_fs9 = require("fs");
@@ -3082,7 +3230,7 @@ async function configOperationWait(options) {
3082
3230
  assertOperationId(options.operationId);
3083
3231
  const cfg = await loadProjectConfig(options.configPath);
3084
3232
  const client = await operationClient(cfg, options, "wait");
3085
- const wait2 = options.pollWait ?? ((ms) => new Promise((resolve12) => setTimeout(resolve12, ms)));
3233
+ const wait2 = options.pollWait ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
3086
3234
  const now = () => (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
3087
3235
  const deadline = now() + (options.timeoutSeconds ?? DEFAULT_WAIT_SECONDS) * 1e3;
3088
3236
  const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1e3;
@@ -3113,7 +3261,7 @@ async function operationClient(cfg, options, purpose) {
3113
3261
  platform: cfg.platformUrl,
3114
3262
  scope: "app:config:write",
3115
3263
  token: options.token,
3116
- tokenFile: (0, import_node_path7.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3264
+ tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3117
3265
  rootDir: cfg.rootDir,
3118
3266
  email: options.email,
3119
3267
  open: options.open,
@@ -3168,7 +3316,7 @@ function record3(value2) {
3168
3316
 
3169
3317
  // src/config-reconcile-command.ts
3170
3318
  var import_apps8 = require("@odla-ai/apps");
3171
- var import_node_path8 = require("path");
3319
+ var import_node_path9 = require("path");
3172
3320
 
3173
3321
  // src/config-reconcile.ts
3174
3322
  var import_apps7 = require("@odla-ai/apps");
@@ -3464,7 +3612,7 @@ async function inspectConfig(options) {
3464
3612
  platform: cfg.platformUrl,
3465
3613
  scope: "app:config:read",
3466
3614
  token: options.token,
3467
- tokenFile: (0, import_node_path8.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3615
+ tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
3468
3616
  rootDir: cfg.rootDir,
3469
3617
  email: options.email,
3470
3618
  open: options.open,
@@ -3597,12 +3745,12 @@ function quoteArg2(value2) {
3597
3745
  // src/doctor-checks.ts
3598
3746
  var import_node_child_process3 = require("child_process");
3599
3747
  var import_node_fs12 = require("fs");
3600
- var import_node_path10 = require("path");
3748
+ var import_node_path11 = require("path");
3601
3749
 
3602
3750
  // src/wrangler.ts
3603
3751
  var import_node_child_process2 = require("child_process");
3604
3752
  var import_node_fs11 = require("fs");
3605
- var import_node_path9 = require("path");
3753
+ var import_node_path10 = require("path");
3606
3754
  var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
3607
3755
  const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
3608
3756
  let stdout = "";
@@ -3616,7 +3764,7 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
3616
3764
  var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
3617
3765
  function findWranglerConfig(rootDir) {
3618
3766
  for (const name of WRANGLER_CONFIG_FILES) {
3619
- const path = (0, import_node_path9.join)(rootDir, name);
3767
+ const path = (0, import_node_path10.join)(rootDir, name);
3620
3768
  if ((0, import_node_fs11.existsSync)(path)) return path;
3621
3769
  }
3622
3770
  return null;
@@ -3729,10 +3877,10 @@ function wranglerWarnings(rootDir) {
3729
3877
  for (const { label, block } of blocks) {
3730
3878
  const assets = block.assets;
3731
3879
  if (assets?.directory) {
3732
- const dir = (0, import_node_path10.resolve)(rootDir, assets.directory);
3733
- if (dir === (0, import_node_path10.resolve)(rootDir)) {
3880
+ const dir = (0, import_node_path11.resolve)(rootDir, assets.directory);
3881
+ if (dir === (0, import_node_path11.resolve)(rootDir)) {
3734
3882
  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"))) {
3883
+ } else if ((0, import_node_fs12.existsSync)((0, import_node_path11.join)(dir, "node_modules"))) {
3736
3884
  warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
3737
3885
  }
3738
3886
  }
@@ -3767,7 +3915,7 @@ function o11yProjectWarnings(rootDir) {
3767
3915
  warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
3768
3916
  return warnings;
3769
3917
  }
3770
- const main = typeof config.main === "string" ? (0, import_node_path10.resolve)(rootDir, config.main) : null;
3918
+ const main = typeof config.main === "string" ? (0, import_node_path11.resolve)(rootDir, config.main) : null;
3771
3919
  if (!main || !(0, import_node_fs12.existsSync)(main)) {
3772
3920
  warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
3773
3921
  } else {
@@ -3797,7 +3945,7 @@ function calendarProjectWarnings(rootDir) {
3797
3945
  }
3798
3946
  function readPackageJson(rootDir) {
3799
3947
  try {
3800
- return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path10.join)(rootDir, "package.json"), "utf8"));
3948
+ return JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(rootDir, "package.json"), "utf8"));
3801
3949
  } catch {
3802
3950
  return null;
3803
3951
  }
@@ -4025,12 +4173,12 @@ function harnessOption(value2, flag) {
4025
4173
 
4026
4174
  // src/init.ts
4027
4175
  var import_node_fs13 = require("fs");
4028
- var import_node_path11 = require("path");
4176
+ var import_node_path12 = require("path");
4029
4177
  var import_apps9 = require("@odla-ai/apps");
4030
4178
  function initProject(options) {
4031
4179
  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");
4180
+ const rootDir = (0, import_node_path12.resolve)(options.rootDir ?? process.cwd());
4181
+ const configPath = (0, import_node_path12.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
4034
4182
  if ((0, import_node_fs13.existsSync)(configPath) && !options.force) {
4035
4183
  throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
4036
4184
  }
@@ -4047,12 +4195,12 @@ function initProject(options) {
4047
4195
  }
4048
4196
  }
4049
4197
  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 });
4198
+ (0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(configPath), { recursive: true });
4199
+ (0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, "src/odla"), { recursive: true });
4200
+ (0, import_node_fs13.mkdirSync)((0, import_node_path12.resolve)(rootDir, ".odla"), { recursive: true });
4053
4201
  (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());
4202
+ writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
4203
+ writeIfMissing((0, import_node_path12.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
4056
4204
  ensureGitignore(rootDir);
4057
4205
  out.log(`created ${relativeDisplay(configPath, rootDir)}`);
4058
4206
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
@@ -4293,7 +4441,7 @@ async function resolveVaultWrite(options) {
4293
4441
  // src/skill.ts
4294
4442
  var import_node_fs14 = require("fs");
4295
4443
  var import_node_os2 = require("os");
4296
- var import_node_path12 = require("path");
4444
+ var import_node_path13 = require("path");
4297
4445
  var import_node_url2 = require("url");
4298
4446
 
4299
4447
  // src/skill-adapters.ts
@@ -4372,8 +4520,8 @@ function installSkill(options = {}) {
4372
4520
  const files = listFiles(sourceDir);
4373
4521
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
4374
4522
  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)());
4523
+ const root = (0, import_node_path13.resolve)(options.dir ?? process.cwd());
4524
+ const home = (0, import_node_path13.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
4377
4525
  const plans = /* @__PURE__ */ new Map();
4378
4526
  const targets = /* @__PURE__ */ new Map();
4379
4527
  const rememberTarget = (harness, target) => {
@@ -4387,48 +4535,48 @@ function installSkill(options = {}) {
4387
4535
  plans.set(target, { target, content: content2, boundary, managedMerge });
4388
4536
  };
4389
4537
  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);
4538
+ 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
4539
  };
4392
4540
  let targetDir;
4393
4541
  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");
4542
+ const claudeRoot = (0, import_node_path13.join)(home, ".claude", "skills");
4543
+ const codexRoot = (0, import_node_path13.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path13.join)(home, ".codex"), "skills");
4396
4544
  targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
4397
4545
  for (const harness of harnesses) {
4398
4546
  const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
4399
- planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path12.dirname)((0, import_node_path12.dirname)(codexRoot)));
4547
+ planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path13.dirname)((0, import_node_path13.dirname)(codexRoot)));
4400
4548
  rememberTarget(harness, skillRoot);
4401
4549
  }
4402
4550
  } else {
4403
- const sharedRoot = (0, import_node_path12.join)(root, ".agents", "skills");
4551
+ const sharedRoot = (0, import_node_path13.join)(root, ".agents", "skills");
4404
4552
  planSkillTree(sharedRoot);
4405
- const claudeRoot = (0, import_node_path12.join)(root, ".claude", "skills");
4553
+ const claudeRoot = (0, import_node_path13.join)(root, ".claude", "skills");
4406
4554
  targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
4407
4555
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
4408
4556
  if (harnesses.includes("claude")) {
4409
4557
  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));
4558
+ const canonical = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(sourceDir, skill, "SKILL.md"), "utf8");
4559
+ plan((0, import_node_path13.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
4412
4560
  }
4413
4561
  rememberTarget("claude", claudeRoot);
4414
4562
  }
4415
4563
  if (harnesses.includes("cursor")) {
4416
- const cursorRule = (0, import_node_path12.join)(root, ".cursor", "rules", "odla.mdc");
4564
+ const cursorRule = (0, import_node_path13.join)(root, ".cursor", "rules", "odla.mdc");
4417
4565
  plan(cursorRule, CURSOR_RULE);
4418
4566
  rememberTarget("cursor", cursorRule);
4419
4567
  }
4420
4568
  if (harnesses.includes("agents")) {
4421
- const agentsFile = (0, import_node_path12.join)(root, "AGENTS.md");
4569
+ const agentsFile = (0, import_node_path13.join)(root, "AGENTS.md");
4422
4570
  plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4423
4571
  rememberTarget("agents", agentsFile);
4424
4572
  }
4425
4573
  if (harnesses.includes("copilot")) {
4426
- const copilotFile = (0, import_node_path12.join)(root, ".github", "copilot-instructions.md");
4574
+ const copilotFile = (0, import_node_path13.join)(root, ".github", "copilot-instructions.md");
4427
4575
  plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4428
4576
  rememberTarget("copilot", copilotFile);
4429
4577
  }
4430
4578
  if (harnesses.includes("gemini")) {
4431
- const geminiFile = (0, import_node_path12.join)(root, "GEMINI.md");
4579
+ const geminiFile = (0, import_node_path13.join)(root, "GEMINI.md");
4432
4580
  plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
4433
4581
  rememberTarget("gemini", geminiFile);
4434
4582
  }
@@ -4464,7 +4612,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4464
4612
  }
4465
4613
  for (const file of plans.values()) {
4466
4614
  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 });
4615
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(file.target), { recursive: true });
4468
4616
  (0, import_node_fs14.writeFileSync)(file.target, file.content);
4469
4617
  }
4470
4618
  }
@@ -4484,7 +4632,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
4484
4632
  };
4485
4633
  }
4486
4634
  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();
4635
+ 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
4636
  }
4489
4637
  function normalizeHarnesses(values, global) {
4490
4638
  const requested = values?.length ? values : ["claude"];
@@ -4529,13 +4677,13 @@ function managedFileContent(path, block, force, boundary) {
4529
4677
  return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
4530
4678
  }
4531
4679
  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)) {
4680
+ const rel = (0, import_node_path13.relative)(boundary, target);
4681
+ if (rel === ".." || rel.startsWith(`..${import_node_path13.sep}`) || (0, import_node_path13.isAbsolute)(rel)) {
4534
4682
  throw new Error(`agent setup target escapes its install root: ${target}`);
4535
4683
  }
4536
4684
  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);
4685
+ for (const part of rel.split(import_node_path13.sep).filter(Boolean)) {
4686
+ current = (0, import_node_path13.join)(current, part);
4539
4687
  try {
4540
4688
  if ((0, import_node_fs14.lstatSync)(current).isSymbolicLink()) return current;
4541
4689
  } catch (error) {
@@ -4552,9 +4700,9 @@ function listFiles(dir) {
4552
4700
  const results = [];
4553
4701
  const walk = (current) => {
4554
4702
  for (const entry of (0, import_node_fs14.readdirSync)(current, { withFileTypes: true })) {
4555
- const path = (0, import_node_path12.join)(current, entry.name);
4703
+ const path = (0, import_node_path13.join)(current, entry.name);
4556
4704
  if (entry.isDirectory()) walk(path);
4557
- else results.push((0, import_node_path12.relative)(dir, path));
4705
+ else results.push((0, import_node_path13.relative)(dir, path));
4558
4706
  }
4559
4707
  };
4560
4708
  walk(dir);
@@ -4866,7 +5014,7 @@ async function projectCommand(command, parsed, deps) {
4866
5014
  // src/code-connect.ts
4867
5015
  var import_node_fs15 = require("fs");
4868
5016
  var import_node_os4 = require("os");
4869
- var import_node_path14 = require("path");
5017
+ var import_node_path15 = require("path");
4870
5018
 
4871
5019
  // ../harness/dist/chunk-QTUEF2HZ.js
4872
5020
  var HARNESS_PROTOCOL_VERSION = 1;
@@ -4952,15 +5100,15 @@ function encodeAgentInput(message2) {
4952
5100
  // ../harness/dist/chunk-PHXQH4YM.js
4953
5101
  var import_child_process = require("child_process");
4954
5102
  var import_fs = require("fs");
4955
- var import_promises2 = require("fs/promises");
5103
+ var import_promises3 = require("fs/promises");
4956
5104
  var import_path = require("path");
4957
5105
  var import_process = require("process");
4958
- var import_promises3 = require("fs/promises");
5106
+ var import_promises4 = require("fs/promises");
4959
5107
  var import_os = require("os");
4960
5108
  var import_path2 = require("path");
4961
5109
  var import_child_process2 = require("child_process");
4962
5110
  var import_path3 = require("path");
4963
- var import_promises4 = require("fs/promises");
5111
+ var import_promises5 = require("fs/promises");
4964
5112
  var import_os2 = require("os");
4965
5113
  var import_path4 = require("path");
4966
5114
  var import_child_process3 = require("child_process");
@@ -4971,7 +5119,7 @@ function assertPinnedImage(image) {
4971
5119
  async function commandAvailable(engine) {
4972
5120
  for (const directory of (process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean)) {
4973
5121
  try {
4974
- await (0, import_promises2.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
5122
+ await (0, import_promises3.access)((0, import_path.join)(directory, engine), import_fs.constants.X_OK);
4975
5123
  return true;
4976
5124
  } catch {
4977
5125
  }
@@ -5257,7 +5405,7 @@ async function gitBlobs(cwd, entries, maxBytes) {
5257
5405
  }
5258
5406
  async function materializeGitTree(source, commitSha, options = {}) {
5259
5407
  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));
5408
+ const sourceDir = await (0, import_promises4.realpath)((0, import_path2.resolve)(source));
5261
5409
  const maxFiles = options.maxFiles ?? 2e4;
5262
5410
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5263
5411
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
@@ -5266,9 +5414,9 @@ async function materializeGitTree(source, commitSha, options = {}) {
5266
5414
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
5267
5415
  });
5268
5416
  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-"));
5417
+ const root = await (0, import_promises4.mkdtemp)((0, import_path2.join)(options.tempRoot ?? (0, import_os.tmpdir)(), "odla-git-tree-"));
5270
5418
  const targetRoot = (0, import_path2.join)(root, "source");
5271
- await (0, import_promises3.mkdir)(targetRoot);
5419
+ await (0, import_promises4.mkdir)(targetRoot);
5272
5420
  let byteCount = 0;
5273
5421
  try {
5274
5422
  const blobs = await gitBlobs(sourceDir, entries, maxBytes);
@@ -5278,18 +5426,18 @@ async function materializeGitTree(source, commitSha, options = {}) {
5278
5426
  if (byteCount > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
5279
5427
  const target = (0, import_path2.resolve)(targetRoot, entry.path);
5280
5428
  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 });
5429
+ await (0, import_promises4.mkdir)((0, import_path2.resolve)(target, ".."), { recursive: true });
5430
+ await (0, import_promises4.writeFile)(target, content2, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
5283
5431
  }
5284
5432
  return {
5285
5433
  root,
5286
5434
  sourceDir: targetRoot,
5287
5435
  fileCount: entries.length,
5288
5436
  byteCount,
5289
- cleanup: () => (0, import_promises3.rm)(root, { recursive: true, force: true })
5437
+ cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5290
5438
  };
5291
5439
  } catch (error) {
5292
- await (0, import_promises3.rm)(root, { recursive: true, force: true });
5440
+ await (0, import_promises4.rm)(root, { recursive: true, force: true });
5293
5441
  throw error;
5294
5442
  }
5295
5443
  }
@@ -5297,7 +5445,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5297
5445
  const files = [];
5298
5446
  let bytes = 0;
5299
5447
  const walk = async (dir) => {
5300
- for (const entry of await (0, import_promises4.readdir)(dir, { withFileTypes: true })) {
5448
+ for (const entry of await (0, import_promises5.readdir)(dir, { withFileTypes: true })) {
5301
5449
  if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
5302
5450
  if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
5303
5451
  const path = (0, import_path4.join)(dir, entry.name);
@@ -5307,7 +5455,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
5307
5455
  continue;
5308
5456
  }
5309
5457
  if (!entry.isFile()) continue;
5310
- const metadata2 = await (0, import_promises4.stat)(path);
5458
+ const metadata2 = await (0, import_promises5.stat)(path);
5311
5459
  bytes += metadata2.size;
5312
5460
  if (files.length + 1 > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
5313
5461
  if (bytes > maxBytes) throw new Error(`workspace exceeds ${maxBytes} bytes`);
@@ -5356,7 +5504,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5356
5504
  if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
5357
5505
  let metadata2;
5358
5506
  try {
5359
- metadata2 = await (0, import_promises4.lstat)(source);
5507
+ metadata2 = await (0, import_promises5.lstat)(source);
5360
5508
  } catch (error) {
5361
5509
  if (error.code === "ENOENT") continue;
5362
5510
  throw error;
@@ -5371,9 +5519,9 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
5371
5519
  async function copyTree(files, destination) {
5372
5520
  for (const file of files) {
5373
5521
  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);
5522
+ await (0, import_promises5.mkdir)((0, import_path4.resolve)(target, ".."), { recursive: true });
5523
+ await (0, import_promises5.copyFile)(file.source, target);
5524
+ await (0, import_promises5.chmod)(target, file.mode);
5377
5525
  }
5378
5526
  }
5379
5527
  async function captureGitDiff(root, maxBytes) {
@@ -5410,13 +5558,13 @@ async function captureGitDiff(root, maxBytes) {
5410
5558
  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
5559
  }
5412
5560
  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);
5561
+ const sourceDir = await (0, import_promises5.realpath)((0, import_path4.resolve)(source));
5562
+ const sourceStat = await (0, import_promises5.stat)(sourceDir);
5415
5563
  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-"));
5564
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5417
5565
  const baselineDir = (0, import_path4.join)(root, "baseline");
5418
5566
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5419
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5567
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5420
5568
  try {
5421
5569
  const maxFiles = options.maxFiles ?? 2e4;
5422
5570
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
@@ -5429,26 +5577,26 @@ async function stageWorkspace(source, options = {}) {
5429
5577
  fileCount: files.length,
5430
5578
  byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
5431
5579
  patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
5432
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5580
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5433
5581
  };
5434
5582
  } catch (error) {
5435
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5583
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5436
5584
  throw error;
5437
5585
  }
5438
5586
  }
5439
5587
  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));
5588
+ const baselineDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(baselineSource));
5589
+ const workspaceDirSource = await (0, import_promises5.realpath)((0, import_path4.resolve)(workspaceSource));
5442
5590
  const maxFiles = options.maxFiles ?? 2e4;
5443
5591
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5444
5592
  const [baselineFiles, workspaceFiles] = await Promise.all([
5445
5593
  sourceFiles(baselineDirSource, maxFiles, maxBytes),
5446
5594
  sourceFiles(workspaceDirSource, maxFiles, maxBytes)
5447
5595
  ]);
5448
- const root = await (0, import_promises4.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5596
+ const root = await (0, import_promises5.mkdtemp)((0, import_path4.join)(options.tempRoot ?? (0, import_os2.tmpdir)(), "odla-harness-"));
5449
5597
  const baselineDir = (0, import_path4.join)(root, "baseline");
5450
5598
  const workspaceDir = (0, import_path4.join)(root, "workspace");
5451
- await Promise.all([(0, import_promises4.mkdir)(baselineDir), (0, import_promises4.mkdir)(workspaceDir)]);
5599
+ await Promise.all([(0, import_promises5.mkdir)(baselineDir), (0, import_promises5.mkdir)(workspaceDir)]);
5452
5600
  try {
5453
5601
  await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
5454
5602
  return {
@@ -5458,17 +5606,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
5458
5606
  fileCount: workspaceFiles.length,
5459
5607
  byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
5460
5608
  patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
5461
- cleanup: () => (0, import_promises4.rm)(root, { recursive: true, force: true })
5609
+ cleanup: () => (0, import_promises5.rm)(root, { recursive: true, force: true })
5462
5610
  };
5463
5611
  } catch (error) {
5464
- await (0, import_promises4.rm)(root, { recursive: true, force: true });
5612
+ await (0, import_promises5.rm)(root, { recursive: true, force: true });
5465
5613
  throw error;
5466
5614
  }
5467
5615
  }
5468
5616
 
5469
5617
  // ../harness/dist/chunk-GMVZ4LZH.js
5470
5618
  var import_crypto = require("crypto");
5471
- var import_promises5 = require("fs/promises");
5619
+ var import_promises6 = require("fs/promises");
5472
5620
  var import_path5 = require("path");
5473
5621
 
5474
5622
  // ../camel/dist/chunk-7FHPOQVP.js
@@ -5805,19 +5953,19 @@ function validateSnapshot(snapshot, limits) {
5805
5953
 
5806
5954
  // ../harness/dist/chunk-GMVZ4LZH.js
5807
5955
  var import_child_process4 = require("child_process");
5808
- var import_promises6 = require("fs/promises");
5956
+ var import_promises7 = require("fs/promises");
5809
5957
  var import_path6 = require("path");
5810
5958
  var import_child_process5 = require("child_process");
5811
5959
  var import_process2 = require("process");
5812
5960
  var import_crypto2 = require("crypto");
5813
5961
  var import_crypto3 = require("crypto");
5814
5962
  var import_fs2 = require("fs");
5815
- var import_promises7 = require("fs/promises");
5816
- var import_path7 = require("path");
5817
5963
  var import_promises8 = require("fs/promises");
5964
+ var import_path7 = require("path");
5965
+ var import_promises9 = require("fs/promises");
5818
5966
  var import_os3 = require("os");
5819
5967
  var import_path8 = require("path");
5820
- var import_promises9 = require("fs/promises");
5968
+ var import_promises10 = require("fs/promises");
5821
5969
  var import_path9 = require("path");
5822
5970
 
5823
5971
  // ../camel/dist/chunk-LAXU2AVK.js
@@ -6101,7 +6249,7 @@ var import_crypto4 = require("crypto");
6101
6249
  async function digestStagedWorkspace(root, limits) {
6102
6250
  const files = [];
6103
6251
  const walk = async (directory) => {
6104
- const entries = await (0, import_promises5.readdir)(directory, { withFileTypes: true });
6252
+ const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
6105
6253
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
6106
6254
  if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
6107
6255
  const target = (0, import_path5.resolve)(directory, entry.name);
@@ -6116,7 +6264,7 @@ async function digestStagedWorkspace(root, limits) {
6116
6264
  const hash = (0, import_crypto.createHash)("sha256");
6117
6265
  let bytes = 0;
6118
6266
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
6119
- const content2 = await (0, import_promises5.readFile)(file.target);
6267
+ const content2 = await (0, import_promises6.readFile)(file.target);
6120
6268
  bytes += Buffer.byteLength(file.path) + content2.byteLength;
6121
6269
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
6122
6270
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content2.byteLength}:`);
@@ -6442,7 +6590,7 @@ async function applyCodePatch(workspaceDir, patch2, paths) {
6442
6590
  await gitApply(workspaceDir, patch2, false);
6443
6591
  for (const path of paths) {
6444
6592
  try {
6445
- const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
6593
+ const info = await (0, import_promises7.lstat)(resolveCodePath(workspaceDir, path));
6446
6594
  if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
6447
6595
  throw new TypeError("patch created a non-regular workspace entry");
6448
6596
  }
@@ -6733,7 +6881,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
6733
6881
  for (const artifact of recipe2.expectedArtifacts ?? []) {
6734
6882
  try {
6735
6883
  const path = (0, import_path7.join)(workspaceDir, artifact.path);
6736
- const info = await (0, import_promises7.lstat)(path);
6884
+ const info = await (0, import_promises8.lstat)(path);
6737
6885
  if (!info.isFile() || info.isSymbolicLink()) {
6738
6886
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
6739
6887
  } else if (info.size > artifact.maximumBytes) {
@@ -6914,9 +7062,9 @@ var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_mod
6914
7062
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
6915
7063
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.tmpdir)()) {
6916
7064
  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-"));
7065
+ const root = await (0, import_promises9.mkdtemp)((0, import_path8.join)(tempRoot, "odla-code-source-"));
6918
7066
  const sourceDir = (0, import_path8.join)(root, "source");
6919
- await (0, import_promises8.mkdir)(sourceDir);
7067
+ await (0, import_promises9.mkdir)(sourceDir);
6920
7068
  const seen = /* @__PURE__ */ new Set();
6921
7069
  let bytes = 0;
6922
7070
  try {
@@ -6928,8 +7076,8 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
6928
7076
  if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
6929
7077
  const target = (0, import_path8.resolve)(sourceDir, file.path);
6930
7078
  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 });
7079
+ await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7080
+ await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 420 });
6933
7081
  }
6934
7082
  for (const reference of snapshot.references ?? []) {
6935
7083
  validateAlias(reference.alias);
@@ -6943,13 +7091,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
6943
7091
  if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
6944
7092
  const target = (0, import_path8.resolve)(sourceDir, path);
6945
7093
  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 });
7094
+ await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7095
+ await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
6948
7096
  }
6949
7097
  }
6950
- return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
7098
+ return { sourceDir, cleanup: () => (0, import_promises9.rm)(root, { recursive: true, force: true }) };
6951
7099
  } catch (cause) {
6952
- await (0, import_promises8.rm)(root, { recursive: true, force: true });
7100
+ await (0, import_promises9.rm)(root, { recursive: true, force: true });
6953
7101
  throw cause;
6954
7102
  }
6955
7103
  }
@@ -6970,8 +7118,8 @@ async function attachCodeRuntimeReferences(workspace, references) {
6970
7118
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
6971
7119
  const target = (0, import_path8.resolve)(root, path);
6972
7120
  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 });
7121
+ await (0, import_promises9.mkdir)((0, import_path8.dirname)(target), { recursive: true });
7122
+ await (0, import_promises9.writeFile)(target, file.content, { flag: "wx", mode: 292 });
6975
7123
  }
6976
7124
  }
6977
7125
  }
@@ -7157,11 +7305,11 @@ async function read(context, request2, options, policy) {
7157
7305
  const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
7158
7306
  if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
7159
7307
  const target = resolveCodePath(context.workspaceDir, path);
7160
- const info = await (0, import_promises9.stat)(target);
7308
+ const info = await (0, import_promises10.stat)(target);
7161
7309
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
7162
7310
  throw new TypeError("file is not a bounded regular source file");
7163
7311
  }
7164
- const source = await (0, import_promises9.readFile)(target);
7312
+ const source = await (0, import_promises10.readFile)(target);
7165
7313
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
7166
7314
  const lines = source.toString("utf8").split("\n");
7167
7315
  const content2 = lines.slice(startLine - 1, endLine).join("\n");
@@ -7238,7 +7386,7 @@ function policyContext(context, request2, options, extra) {
7238
7386
  async function registeredFiles(root, limit) {
7239
7387
  const paths = [];
7240
7388
  const walk = async (directory) => {
7241
- for (const entry of await (0, import_promises9.readdir)(directory, { withFileTypes: true })) {
7389
+ for (const entry of await (0, import_promises10.readdir)(directory, { withFileTypes: true })) {
7242
7390
  if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
7243
7391
  const target = (0, import_path9.resolve)(directory, entry.name);
7244
7392
  if (entry.isDirectory()) await walk(target);
@@ -7837,8 +7985,8 @@ function hostedPollTimeout(value2 = 10 * 6e4) {
7837
7985
  }
7838
7986
  async function waitForHostedPoll(milliseconds, signal) {
7839
7987
  if (signal?.aborted) throw signal.reason ?? new DOMException("aborted", "AbortError");
7840
- await new Promise((resolve12, reject) => {
7841
- const timer = setTimeout(resolve12, milliseconds);
7988
+ await new Promise((resolve13, reject) => {
7989
+ const timer = setTimeout(resolve13, milliseconds);
7842
7990
  signal?.addEventListener("abort", () => {
7843
7991
  clearTimeout(timer);
7844
7992
  reject(signal.reason ?? new DOMException("aborted", "AbortError"));
@@ -8023,9 +8171,9 @@ function digestText(value2) {
8023
8171
  // src/code-images.ts
8024
8172
  var import_node_child_process6 = require("child_process");
8025
8173
  var import_node_crypto3 = require("crypto");
8026
- var import_promises10 = require("fs/promises");
8174
+ var import_promises11 = require("fs/promises");
8027
8175
  var import_node_os3 = require("os");
8028
- var import_node_path13 = require("path");
8176
+ var import_node_path14 = require("path");
8029
8177
  var import_node_url3 = require("url");
8030
8178
 
8031
8179
  // src/code-runtime-config.ts
@@ -8105,16 +8253,16 @@ function embeddedPiAssetPath() {
8105
8253
  return (0, import_node_url3.fileURLToPath)(new URL("./runtime/pi-agent.js", importMetaUrl));
8106
8254
  }
8107
8255
  async function embeddedPiImageName() {
8108
- const bundle = await (0, import_promises10.readFile)(embeddedPiAssetPath()).catch(() => {
8256
+ const bundle = await (0, import_promises11.readFile)(embeddedPiAssetPath()).catch(() => {
8109
8257
  throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
8110
8258
  });
8111
8259
  return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto3.createHash)("sha256").update(bundle).digest("hex")}`;
8112
8260
  }
8113
8261
  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-"));
8262
+ const context = await (0, import_promises11.mkdtemp)((0, import_node_path14.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
8115
8263
  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"), [
8264
+ await (0, import_promises11.copyFile)(embeddedPiAssetPath(), (0, import_node_path14.join)(context, "pi-agent.js"));
8265
+ await (0, import_promises11.writeFile)((0, import_node_path14.join)(context, "Dockerfile"), [
8118
8266
  `FROM ${CODE_NODE_IMAGE}`,
8119
8267
  "COPY pi-agent.js /opt/odla/pi-agent.js",
8120
8268
  "WORKDIR /workspace",
@@ -8123,14 +8271,14 @@ async function buildEmbeddedPiImage(engine, image, run) {
8123
8271
  ].join("\n"), { mode: 384 });
8124
8272
  await run(engine, ["build", "--tag", image, context], "inherit");
8125
8273
  } finally {
8126
- await (0, import_promises10.rm)(context, { recursive: true, force: true });
8274
+ await (0, import_promises11.rm)(context, { recursive: true, force: true });
8127
8275
  }
8128
8276
  }
8129
8277
 
8130
8278
  // src/code-connect.ts
8131
8279
  async function codeConnect(options) {
8132
8280
  const cwd = options.cwd ?? process.cwd();
8133
- const configPath = (0, import_node_path14.resolve)(cwd, options.configPath);
8281
+ const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
8134
8282
  const cfg = (0, import_node_fs15.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
8135
8283
  const requestedAppId = options.appId?.trim();
8136
8284
  if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
@@ -8499,10 +8647,8 @@ function requireName(parsed) {
8499
8647
  return name;
8500
8648
  }
8501
8649
 
8502
- // src/help.ts
8503
- function printHelp(output = console) {
8504
- output.log(`odla-ai
8505
-
8650
+ // src/help-usage.ts
8651
+ var USAGE_SECTION = `
8506
8652
  Start here:
8507
8653
  odla-ai runbook ask "<question>" The current procedure, from odla's own
8508
8654
  runbooks. Ask BEFORE searching the web or
@@ -8536,6 +8682,7 @@ Usage:
8536
8682
  odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
8537
8683
  odla-ai app owners add <email> [--email <odla-account>] [--json]
8538
8684
  odla-ai app owners remove <email> [--email <odla-account>] [--json]
8685
+ odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
8539
8686
  odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8540
8687
  odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
8541
8688
  odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
@@ -8615,8 +8762,12 @@ Usage:
8615
8762
  odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
8616
8763
  odla-ai secrets set <name> --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8617
8764
  odla-ai secrets set-clerk-key --env <env> (--from-env <NAME>|--stdin) [--email <odla-account>] [--config odla.config.mjs] [--yes]
8618
- odla-ai version
8765
+ odla-ai version`;
8619
8766
 
8767
+ // src/help.ts
8768
+ function printHelp(output = console) {
8769
+ output.log(`odla-ai
8770
+ ${USAGE_SECTION}
8620
8771
  Commands:
8621
8772
  agent Inspect durable agent wakeups and explicitly requeue a
8622
8773
  dead-lettered job; JSON output is stable for remote operators.
@@ -9065,7 +9216,7 @@ function jsonl(ctx, parsed, value2) {
9065
9216
  }
9066
9217
  async function discussWatch(ctx, topicId, parsed) {
9067
9218
  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)));
9219
+ const sleep = ctx.sleep ?? ((ms) => new Promise((resolve13) => setTimeout(resolve13, ms)));
9069
9220
  const now = ctx.now ?? Date.now;
9070
9221
  const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
9071
9222
  const timeoutSeconds = numberOpt2(parsed, "timeout");
@@ -10537,6 +10688,7 @@ var COMMAND_SURFACE = {
10537
10688
  promote: {},
10538
10689
  owners: { list: {}, add: {}, remove: {} }
10539
10690
  },
10691
+ brand: { design: { unpack: {} } },
10540
10692
  calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
10541
10693
  capabilities: {},
10542
10694
  code: { connect: {} },
@@ -10833,7 +10985,7 @@ async function runbookRemove(ctx, slug) {
10833
10985
 
10834
10986
  // src/runbook-import.ts
10835
10987
  var import_node_fs18 = require("fs");
10836
- var import_node_path15 = require("path");
10988
+ var import_node_path16 = require("path");
10837
10989
  function parseRunbook(text2, slug) {
10838
10990
  let rest = text2;
10839
10991
  const meta = {};
@@ -10862,8 +11014,8 @@ function readRunbookDir(dir) {
10862
11014
  const files = (0, import_node_fs18.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
10863
11015
  if (!files.length) throw new Error(`no .md files in ${dir}`);
10864
11016
  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);
11017
+ const slug = (0, import_node_path16.basename)(file, ".md");
11018
+ const parsed = parseRunbook((0, import_node_fs18.readFileSync)((0, import_node_path16.join)(dir, file), "utf8"), slug);
10867
11019
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
10868
11020
  });
10869
11021
  }
@@ -10937,7 +11089,7 @@ async function upsert(ctx, r, visibility) {
10937
11089
  // src/runbook-impact.ts
10938
11090
  var import_node_child_process7 = require("child_process");
10939
11091
  var import_node_fs19 = require("fs");
10940
- var import_node_path16 = require("path");
11092
+ var import_node_path17 = require("path");
10941
11093
 
10942
11094
  // src/runbook-impact-scan.ts
10943
11095
  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 +11258,7 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
11106
11258
  }
11107
11259
  function manifestLabeller(root) {
11108
11260
  return (workspace) => {
11109
- const manifest = (0, import_node_path16.join)(root, workspace, "package.json");
11261
+ const manifest = (0, import_node_path17.join)(root, workspace, "package.json");
11110
11262
  if (!(0, import_node_fs19.existsSync)(manifest)) return void 0;
11111
11263
  try {
11112
11264
  const name = JSON.parse((0, import_node_fs19.readFileSync)(manifest, "utf8")).name;
@@ -11176,7 +11328,7 @@ function report3(ctx, impacts) {
11176
11328
  async function runbookImpact(ctx, options, deps = {}) {
11177
11329
  const cwd = deps.cwd ?? process.cwd();
11178
11330
  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"));
11331
+ const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(cwd, path), "utf8"));
11180
11332
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
11181
11333
  if (!surfaces.length) {
11182
11334
  return ctx.out.log(
@@ -11311,7 +11463,7 @@ async function runbookComment(ctx, slug, body) {
11311
11463
  var import_node_child_process8 = require("child_process");
11312
11464
  var import_node_fs20 = require("fs");
11313
11465
  var import_node_os5 = require("os");
11314
- var import_node_path17 = require("path");
11466
+ var import_node_path18 = require("path");
11315
11467
  var import_node_process12 = __toESM(require("process"), 1);
11316
11468
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
11317
11469
  function resolveEditor(env = import_node_process12.default.env) {
@@ -11337,8 +11489,8 @@ function editText(initial, slug, deps = {}) {
11337
11489
  );
11338
11490
  if (!interactive())
11339
11491
  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`);
11492
+ const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path18.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
11493
+ const file = (0, import_node_path18.join)(dir, `${slug}.md`);
11342
11494
  try {
11343
11495
  (0, import_node_fs20.writeFileSync)(file, initial, { mode: 384 });
11344
11496
  const code = defaultRunOrInjected(deps)(editor, file);
@@ -11680,7 +11832,7 @@ async function runbookCommand(parsed, deps = {}) {
11680
11832
  }
11681
11833
 
11682
11834
  // src/security-command-context.ts
11683
- var import_promises11 = require("readline/promises");
11835
+ var import_promises12 = require("readline/promises");
11684
11836
  async function hostedSecurityContext(parsed, dependencies) {
11685
11837
  const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
11686
11838
  const cfg = await loadProjectConfig(configPath);
@@ -11706,7 +11858,7 @@ async function hostedSecurityContext(parsed, dependencies) {
11706
11858
  async function interactiveConfirmation(message2, dependencies) {
11707
11859
  if (dependencies.confirm) return dependencies.confirm(message2);
11708
11860
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
11709
- const prompt = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stdout });
11861
+ const prompt = (0, import_promises12.createInterface)({ input: process.stdin, output: process.stdout });
11710
11862
  try {
11711
11863
  const answer = await prompt.question(`${message2} [y/N] `);
11712
11864
  return /^y(?:es)?$/i.test(answer.trim());
@@ -11833,7 +11985,7 @@ function hostedSeverity(value2, flag) {
11833
11985
  var import_security2 = require("@odla-ai/security");
11834
11986
 
11835
11987
  // src/security.ts
11836
- var import_node_path18 = require("path");
11988
+ var import_node_path19 = require("path");
11837
11989
  var import_security = require("@odla-ai/security");
11838
11990
  var import_node3 = require("@odla-ai/security/node");
11839
11991
  async function runHostedSecurity(options) {
@@ -11845,9 +11997,9 @@ async function runHostedSecurity(options) {
11845
11997
  const appId = selfAudit ? "odla-ai" : cfg.app.id;
11846
11998
  const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
11847
11999
  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("/");
12000
+ const target = (0, import_node_path19.resolve)(options.target ?? cfg?.rootDir ?? ".");
12001
+ const output = (0, import_node_path19.resolve)(options.out ?? (0, import_node_path19.resolve)(target, ".odla/security/hosted"));
12002
+ const outputRelative = (0, import_node_path19.relative)(target, output).split(import_node_path19.sep).join("/");
11851
12003
  if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
11852
12004
  const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
11853
12005
  const tokenRequest = {
@@ -11859,7 +12011,7 @@ async function runHostedSecurity(options) {
11859
12011
  };
11860
12012
  const token = await injectedToken(options, tokenRequest);
11861
12013
  const snapshot = await (0, import_node3.snapshotDirectory)(target, {
11862
- exclude: !outputRelative.startsWith("../") && !(0, import_node_path18.isAbsolute)(outputRelative) ? [outputRelative] : []
12014
+ exclude: !outputRelative.startsWith("../") && !(0, import_node_path19.isAbsolute)(outputRelative) ? [outputRelative] : []
11863
12015
  });
11864
12016
  const hosted = await (0, import_security.createPlatformSecurityReasoners)({
11865
12017
  platform,
@@ -11877,7 +12029,7 @@ async function runHostedSecurity(options) {
11877
12029
  });
11878
12030
  const harness = (0, import_security.createSecurityHarness)({
11879
12031
  profile,
11880
- store: new import_node3.FileRunStore((0, import_node_path18.resolve)(output, "state")),
12032
+ store: new import_node3.FileRunStore((0, import_node_path19.resolve)(output, "state")),
11881
12033
  discoveryReasoner: hosted.discoveryReasoner,
11882
12034
  validationReasoner: hosted.validationReasoner,
11883
12035
  policy: {
@@ -11901,7 +12053,7 @@ async function runHostedSecurity(options) {
11901
12053
  function selectEnv(requested, declared, configPath, rootDir) {
11902
12054
  const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
11903
12055
  if (!env || !declared.includes(env)) {
11904
- const shown = (0, import_node_path18.relative)(rootDir, configPath) || configPath;
12056
+ const shown = (0, import_node_path19.relative)(rootDir, configPath) || configPath;
11905
12057
  throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
11906
12058
  }
11907
12059
  return env;
@@ -11930,7 +12082,7 @@ function printSummary(out, appId, env, run, report4, output) {
11930
12082
  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
12083
  if (report4.callBudget) out.log(` calls: discovery=${formatBudget(report4.callBudget.discovery)} validation=${formatBudget(report4.callBudget.validation)}`);
11932
12084
  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")}`);
12085
+ out.log(` report: ${(0, import_node_path19.resolve)(output, "REPORT.md")}`);
11934
12086
  }
11935
12087
  function formatBudget(usage) {
11936
12088
  return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
@@ -12431,6 +12583,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
12431
12583
  await securityCommand(parsed, runtime);
12432
12584
  return;
12433
12585
  }
12586
+ if (command === "brand") {
12587
+ await brandCommand(parsed, runtime);
12588
+ return;
12589
+ }
12434
12590
  if (command === "pm") {
12435
12591
  await pmCommand(parsed, runtime);
12436
12592
  return;