overlane 0.1.133 → 0.1.135

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.
Files changed (21) hide show
  1. package/dist/overlane.mjs +2462 -606
  2. package/package.json +1 -1
  3. package/plugins/claude/plugins/mvrx-harness/.claude-plugin/plugin.json +1 -1
  4. package/plugins/claude/plugins/mvrx-harness/hooks/hooks.json +10 -1
  5. package/plugins/claude/plugins/mvrx-harness/scripts/harness-codemap.mjs +72 -0
  6. package/plugins/claude/plugins/mvrx-harness/scripts/harness-context.mjs +35 -34
  7. package/plugins/claude/plugins/mvrx-harness/scripts/harness-guard.mjs +28 -28
  8. package/plugins/claude/plugins/mvrx-harness/scripts/harness-intake.mjs +35 -35
  9. package/plugins/claude/plugins/mvrx-harness/scripts/harness-quota.mjs +33 -33
  10. package/plugins/claude/plugins/mvrx-harness/scripts/harness-statusline.mjs +24 -24
  11. package/plugins/claude/plugins/mvrx-harness/scripts/harness-stop.mjs +28 -28
  12. package/plugins/codex/plugins/mvrx-harness/.codex-plugin/plugin.json +1 -1
  13. package/plugins/codex/plugins/mvrx-harness/hooks/hooks.json +11 -1
  14. package/plugins/codex/plugins/mvrx-harness/scripts/harness-codemap.mjs +72 -0
  15. package/plugins/codex/plugins/mvrx-harness/scripts/harness-context.mjs +35 -34
  16. package/plugins/codex/plugins/mvrx-harness/scripts/harness-guard.mjs +28 -28
  17. package/plugins/codex/plugins/mvrx-harness/scripts/harness-intake.mjs +35 -35
  18. package/plugins/codex/plugins/mvrx-harness/scripts/harness-quota.mjs +33 -33
  19. package/plugins/codex/plugins/mvrx-harness/scripts/harness-statusline.mjs +24 -24
  20. package/plugins/codex/plugins/mvrx-harness/scripts/harness-stop.mjs +28 -28
  21. package/plugins/release-manifest.json +32 -22
package/dist/overlane.mjs CHANGED
@@ -28,9 +28,9 @@ var __commonJS = (cb, mod) => function __require2() {
28
28
  throw mod = 0, e2;
29
29
  }
30
30
  };
31
- var __export = (target, all) => {
31
+ var __export = (target2, all) => {
32
32
  for (var name2 in all)
33
- __defProp(target, name2, { get: all[name2], enumerable: true });
33
+ __defProp(target2, name2, { get: all[name2], enumerable: true });
34
34
  };
35
35
  var __copyProps = (to, from, except, desc) => {
36
36
  if (from && typeof from === "object" || typeof from === "function") {
@@ -40,12 +40,12 @@ var __copyProps = (to, from, except, desc) => {
40
40
  }
41
41
  return to;
42
42
  };
43
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
43
+ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
44
44
  // If the importer is in node compatibility mode or this is not an ESM
45
45
  // file that has been converted to a CommonJS file using a Babel-
46
46
  // compatible transform (i.e. "__esModule" has not been set), then set
47
47
  // "default" to the CommonJS "module.exports" for node compatibility.
48
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
48
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target2, "default", { value: mod, enumerable: true }) : target2,
49
49
  mod
50
50
  ));
51
51
 
@@ -201,8 +201,8 @@ function defineLazy(object2, key, getter) {
201
201
  function objectClone(obj) {
202
202
  return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
203
203
  }
204
- function assignProp(target, prop, value) {
205
- Object.defineProperty(target, prop, {
204
+ function assignProp(target2, prop, value) {
205
+ Object.defineProperty(target2, prop, {
206
206
  value,
207
207
  writable: true,
208
208
  enumerable: true,
@@ -216,8 +216,8 @@ function rawShape(def) {
216
216
  function sourceShape(schema) {
217
217
  return rawShape(schema._zod.def) ?? schema._zod.def.shape;
218
218
  }
219
- function deferProp(target, key, getter) {
220
- Object.defineProperty(target, key, {
219
+ function deferProp(target2, key, getter) {
220
+ Object.defineProperty(target2, key, {
221
221
  get() {
222
222
  const value = getter();
223
223
  assignProp(this, key, value);
@@ -227,36 +227,36 @@ function deferProp(target, key, getter) {
227
227
  configurable: true
228
228
  });
229
229
  }
230
- function putProp(target, key, value) {
231
- if (key in target)
232
- assignProp(target, key, value);
230
+ function putProp(target2, key, value) {
231
+ if (key in target2)
232
+ assignProp(target2, key, value);
233
233
  else
234
- target[key] = value;
234
+ target2[key] = value;
235
235
  }
236
- function mirrorShape(target, source, keys, wrap) {
236
+ function mirrorShape(target2, source, keys, wrap) {
237
237
  const raw = sourceShape(source);
238
238
  for (const key of keys) {
239
239
  const desc = Object.getOwnPropertyDescriptor(raw, key);
240
240
  if (!desc.enumerable)
241
241
  continue;
242
242
  if (desc.get) {
243
- deferProp(target, key, () => {
243
+ deferProp(target2, key, () => {
244
244
  const value = source._zod.def.shape[key];
245
245
  return wrap ? wrap(value, key) : value;
246
246
  });
247
247
  } else
248
- putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);
248
+ putProp(target2, key, wrap ? wrap(desc.value, key) : desc.value);
249
249
  }
250
250
  }
251
- function mirrorProps(target, source) {
251
+ function mirrorProps(target2, source) {
252
252
  for (const key of Reflect.ownKeys(source)) {
253
253
  const desc = Object.getOwnPropertyDescriptor(source, key);
254
254
  if (!desc.enumerable)
255
255
  continue;
256
256
  if (desc.get)
257
- deferProp(target, key, () => source[key]);
257
+ deferProp(target2, key, () => source[key]);
258
258
  else
259
- putProp(target, key, desc.value);
259
+ putProp(target2, key, desc.value);
260
260
  }
261
261
  }
262
262
  function mergeDefs(...defs) {
@@ -365,35 +365,35 @@ function normalizeParams(_params) {
365
365
  return params;
366
366
  }
367
367
  function createTransparentProxy(getter) {
368
- let target;
368
+ let target2;
369
369
  return new Proxy({}, {
370
370
  get(_, prop, receiver) {
371
- target ?? (target = getter());
372
- return Reflect.get(target, prop, receiver);
371
+ target2 ?? (target2 = getter());
372
+ return Reflect.get(target2, prop, receiver);
373
373
  },
374
374
  set(_, prop, value, receiver) {
375
- target ?? (target = getter());
376
- return Reflect.set(target, prop, value, receiver);
375
+ target2 ?? (target2 = getter());
376
+ return Reflect.set(target2, prop, value, receiver);
377
377
  },
378
378
  has(_, prop) {
379
- target ?? (target = getter());
380
- return Reflect.has(target, prop);
379
+ target2 ?? (target2 = getter());
380
+ return Reflect.has(target2, prop);
381
381
  },
382
382
  deleteProperty(_, prop) {
383
- target ?? (target = getter());
384
- return Reflect.deleteProperty(target, prop);
383
+ target2 ?? (target2 = getter());
384
+ return Reflect.deleteProperty(target2, prop);
385
385
  },
386
386
  ownKeys(_) {
387
- target ?? (target = getter());
388
- return Reflect.ownKeys(target);
387
+ target2 ?? (target2 = getter());
388
+ return Reflect.ownKeys(target2);
389
389
  },
390
390
  getOwnPropertyDescriptor(_, prop) {
391
- target ?? (target = getter());
392
- return Reflect.getOwnPropertyDescriptor(target, prop);
391
+ target2 ?? (target2 = getter());
392
+ return Reflect.getOwnPropertyDescriptor(target2, prop);
393
393
  },
394
394
  defineProperty(_, prop, descriptor) {
395
- target ?? (target = getter());
396
- return Reflect.defineProperty(target, prop, descriptor);
395
+ target2 ?? (target2 = getter());
396
+ return Reflect.defineProperty(target2, prop, descriptor);
397
397
  }
398
398
  });
399
399
  }
@@ -948,10 +948,10 @@ function $constructor(name2, initializer3, proto, params) {
948
948
  let up = own2;
949
949
  while (up && up !== ctorProto)
950
950
  up = Object.getPrototypeOf(up);
951
- const target = up ?? own2;
952
- if (!initialized.has(target)) {
953
- initialized.add(target);
954
- members(target, protoMembers);
951
+ const target2 = up ?? own2;
952
+ if (!initialized.has(target2)) {
953
+ initialized.add(target2);
954
+ members(target2, protoMembers);
955
955
  }
956
956
  }
957
957
  const proto2 = _.prototype;
@@ -2083,8 +2083,8 @@ var init_doc = __esm({
2083
2083
  const lines = content.split("\n").filter((x) => x);
2084
2084
  const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
2085
2085
  const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
2086
- for (const line2 of dedented) {
2087
- this.content.push(line2);
2086
+ for (const line3 of dedented) {
2087
+ this.content.push(line3);
2088
2088
  }
2089
2089
  }
2090
2090
  compile() {
@@ -12935,8 +12935,8 @@ function withParser(schema, parser) {
12935
12935
  installCompiledUserMethods(clone2, schema, parser);
12936
12936
  return clone2;
12937
12937
  }
12938
- function installCompiledUserMethods(target, source, parser) {
12939
- const targetAny = target;
12938
+ function installCompiledUserMethods(target2, source, parser) {
12939
+ const targetAny = target2;
12940
12940
  const sourceAny = source;
12941
12941
  if (typeof sourceAny.safeParse === "function") {
12942
12942
  const originalSafeParse = sourceAny.safeParse;
@@ -13140,9 +13140,9 @@ function codePointLengthVar(doc, ctx, accessor, inDoubt) {
13140
13140
  doc.write(`const ${v} = typeof ${accessor} === "string" && ${inDoubt} ? ${cpLen}(${accessor}) : ${accessor}.length;`);
13141
13141
  return v;
13142
13142
  }
13143
- function numericOperand(value, label) {
13143
+ function numericOperand(value, label2) {
13144
13144
  if (typeof value !== "number" || !Number.isFinite(value)) {
13145
- throw new ZodCompileUnsupportedError(`${label} bound of type ${typeof value}`);
13145
+ throw new ZodCompileUnsupportedError(`${label2} bound of type ${typeof value}`);
13146
13146
  }
13147
13147
  return `${value}`;
13148
13148
  }
@@ -15566,26 +15566,26 @@ var init_api = __esm({
15566
15566
  });
15567
15567
 
15568
15568
  // ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/to-json-schema.js
15569
- function assignProps(target, ...sources) {
15569
+ function assignProps(target2, ...sources) {
15570
15570
  for (const source of sources) {
15571
15571
  for (const key of Reflect.ownKeys(source)) {
15572
15572
  if (Object.prototype.propertyIsEnumerable.call(source, key)) {
15573
- assignProp(target, key, source[key]);
15573
+ assignProp(target2, key, source[key]);
15574
15574
  }
15575
15575
  }
15576
15576
  }
15577
- return target;
15577
+ return target2;
15578
15578
  }
15579
15579
  function initializeContext(params) {
15580
- let target = params?.target ?? "draft-2020-12";
15581
- if (target === "draft-4")
15582
- target = "draft-04";
15583
- if (target === "draft-7")
15584
- target = "draft-07";
15580
+ let target2 = params?.target ?? "draft-2020-12";
15581
+ if (target2 === "draft-4")
15582
+ target2 = "draft-04";
15583
+ if (target2 === "draft-7")
15584
+ target2 = "draft-07";
15585
15585
  return {
15586
15586
  processors: params.processors ?? {},
15587
15587
  metadataRegistry: params?.metadata ?? globalRegistry,
15588
- target,
15588
+ target: target2,
15589
15589
  unrepresentable: params?.unrepresentable ?? "throw",
15590
15590
  override: params?.override ?? (() => {
15591
15591
  }),
@@ -16093,8 +16093,8 @@ var init_to_json_schema = __esm({
16093
16093
  return finalize(ctx, schema);
16094
16094
  };
16095
16095
  createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
16096
- const { libraryOptions, target } = params ?? {};
16097
- const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
16096
+ const { libraryOptions, target: target2 } = params ?? {};
16097
+ const ctx = initializeContext({ ...libraryOptions ?? {}, target: target2, io, processors });
16098
16098
  processSchema(schema, ctx);
16099
16099
  extractDefs(ctx, schema);
16100
16100
  return finalize(ctx, schema);
@@ -18164,8 +18164,8 @@ var init_schemas2 = __esm({
18164
18164
  catch(params) {
18165
18165
  return _catch2(this, params);
18166
18166
  },
18167
- pipe(target) {
18168
- return pipe(this, target);
18167
+ pipe(target2) {
18168
+ return pipe(this, target2);
18169
18169
  },
18170
18170
  readonly() {
18171
18171
  return readonly(this);
@@ -22114,7 +22114,7 @@ var init_issues = __esm({
22114
22114
  init_common();
22115
22115
  init_task_priority();
22116
22116
  init_task_contract();
22117
- prefixed = (prefix, label) => external_exports.string().regex(new RegExp(`^${prefix}_[A-Za-z0-9]{8,32}$`), `must look like ${prefix}_<id>`).describe(label);
22117
+ prefixed = (prefix, label2) => external_exports.string().regex(new RegExp(`^${prefix}_[A-Za-z0-9]{8,32}$`), `must look like ${prefix}_<id>`).describe(label2);
22118
22118
  IssueId = prefixed("iss", "issue identifier");
22119
22119
  WorkProjectId = prefixed("wpr", "work project identifier");
22120
22120
  CycleId = prefixed("cyc", "cycle identifier");
@@ -23553,7 +23553,7 @@ var init_wording = __esm({
23553
23553
  });
23554
23554
 
23555
23555
  // ../contracts/src/code-map-context.ts
23556
- var CodeMapDigest, CODE_MAP_DIGEST_PATH;
23556
+ var CodeMapDigest, CODE_MAP_DIGEST_PATH, CODE_INDEX_PATH;
23557
23557
  var init_code_map_context = __esm({
23558
23558
  "../contracts/src/code-map-context.ts"() {
23559
23559
  "use strict";
@@ -23571,6 +23571,7 @@ var init_code_map_context = __esm({
23571
23571
  seeds: external_exports.array(external_exports.string())
23572
23572
  }).strict();
23573
23573
  CODE_MAP_DIGEST_PATH = ".harness/code-map.json";
23574
+ CODE_INDEX_PATH = ".harness/code-index.json";
23574
23575
  }
23575
23576
  });
23576
23577
 
@@ -24381,28 +24382,28 @@ function stripComment(text) {
24381
24382
  }
24382
24383
  return text;
24383
24384
  }
24384
- function parseScalar(raw, line2) {
24385
+ function parseScalar(raw, line3) {
24385
24386
  const text = raw.trim();
24386
24387
  if (text === "" || text === "~" || text === "null") return null;
24387
24388
  if (text === "true") return true;
24388
24389
  if (text === "false") return false;
24389
24390
  if (/^-?\d+$/.test(text)) return Number(text);
24390
24391
  if (text.startsWith('"')) {
24391
- if (!text.endsWith('"') || text.length < 2) throw new YamlFail(line2, "unterminated double-quoted string");
24392
+ if (!text.endsWith('"') || text.length < 2) throw new YamlFail(line3, "unterminated double-quoted string");
24392
24393
  try {
24393
24394
  return JSON.parse(text);
24394
24395
  } catch {
24395
- throw new YamlFail(line2, "bad escape in double-quoted string");
24396
+ throw new YamlFail(line3, "bad escape in double-quoted string");
24396
24397
  }
24397
24398
  }
24398
24399
  if (text.startsWith("'")) {
24399
- if (!text.endsWith("'") || text.length < 2) throw new YamlFail(line2, "unterminated single-quoted string");
24400
+ if (!text.endsWith("'") || text.length < 2) throw new YamlFail(line3, "unterminated single-quoted string");
24400
24401
  return text.slice(1, -1).replaceAll("''", "'");
24401
24402
  }
24402
24403
  if (text === "{}") return {};
24403
24404
  if (text === "[]") return [];
24404
24405
  if (text.startsWith("[")) {
24405
- if (!text.endsWith("]")) throw new YamlFail(line2, "unterminated list; write the list on one line or use '- ' items");
24406
+ if (!text.endsWith("]")) throw new YamlFail(line3, "unterminated list; write the list on one line or use '- ' items");
24406
24407
  const inner = text.slice(1, -1);
24407
24408
  const items = [];
24408
24409
  let current2 = "";
@@ -24415,15 +24416,15 @@ function parseScalar(raw, line2) {
24415
24416
  quote3 = ch;
24416
24417
  current2 += ch;
24417
24418
  } else if (ch === ",") {
24418
- items.push(parseScalar(current2, line2));
24419
+ items.push(parseScalar(current2, line3));
24419
24420
  current2 = "";
24420
24421
  } else current2 += ch;
24421
24422
  }
24422
- if (current2.trim() !== "" || items.length > 0) items.push(parseScalar(current2, line2));
24423
+ if (current2.trim() !== "" || items.length > 0) items.push(parseScalar(current2, line3));
24423
24424
  return items;
24424
24425
  }
24425
- if (text.startsWith("{")) throw new YamlFail(line2, "flow maps are not supported; use an indented block");
24426
- if (/^[&*!%@`]/.test(text)) throw new YamlFail(line2, `'${text[0]}' (anchors, aliases and tags) is not supported`);
24426
+ if (text.startsWith("{")) throw new YamlFail(line3, "flow maps are not supported; use an indented block");
24427
+ if (/^[&*!%@`]/.test(text)) throw new YamlFail(line3, `'${text[0]}' (anchors, aliases and tags) is not supported`);
24427
24428
  return text;
24428
24429
  }
24429
24430
  function parseYaml(source) {
@@ -24432,10 +24433,10 @@ function parseYaml(source) {
24432
24433
  let i2 = 0;
24433
24434
  let pending = null;
24434
24435
  const keyOf = (path) => JSON.stringify(path);
24435
- function indentOf(raw, line2) {
24436
+ function indentOf(raw, line3) {
24436
24437
  let n = 0;
24437
24438
  while (raw[n] === " ") n += 1;
24438
- if (raw[n] === " ") throw new YamlFail(line2, "tabs are not allowed for indentation; use spaces");
24439
+ if (raw[n] === " ") throw new YamlFail(line3, "tabs are not allowed for indentation; use spaces");
24439
24440
  return n;
24440
24441
  }
24441
24442
  function peek() {
@@ -24549,8 +24550,8 @@ function parseYaml(source) {
24549
24550
  value,
24550
24551
  lineOf(path) {
24551
24552
  for (let n = path.length; n >= 0; n -= 1) {
24552
- const line2 = n === 0 ? 1 : lineMap.get(keyOf(path.slice(0, n)));
24553
- if (line2 !== void 0) return line2;
24553
+ const line3 = n === 0 ? 1 : lineMap.get(keyOf(path.slice(0, n)));
24554
+ if (line3 !== void 0) return line3;
24554
24555
  }
24555
24556
  return 1;
24556
24557
  }
@@ -24703,9 +24704,9 @@ var init_ci = __esm({
24703
24704
  jobs: external_exports.record(external_exports.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,63}$/), PipelineJob).refine((jobs) => Object.keys(jobs).length >= 1 && Object.keys(jobs).length <= 50, "between 1 and 50 jobs")
24704
24705
  }).strict();
24705
24706
  YamlFail = class extends Error {
24706
- constructor(line2, message) {
24707
+ constructor(line3, message) {
24707
24708
  super(message);
24708
- this.line = line2;
24709
+ this.line = line3;
24709
24710
  }
24710
24711
  line;
24711
24712
  };
@@ -25037,6 +25038,878 @@ var init_decision = __esm({
25037
25038
  }
25038
25039
  });
25039
25040
 
25041
+ // ../contracts/src/claims.ts
25042
+ function mergeTreeArgs(a, b) {
25043
+ return ["merge-tree", "--write-tree", "--name-only", "-z", a, b];
25044
+ }
25045
+ function parseMergeTree(stdout, exitCode, stderr = "") {
25046
+ const parts2 = stdout.split("\0");
25047
+ const oid = parts2[0]?.trim() ?? "";
25048
+ if (!/^[0-9a-f]{40,64}$/.test(oid) || exitCode !== 0 && exitCode !== 1) {
25049
+ const hint = stderr.trim() || stdout.trim() || `git exited ${exitCode}`;
25050
+ return { status: "error", files: [], message: hint.slice(0, 1e3) };
25051
+ }
25052
+ const files = [];
25053
+ let index = 1;
25054
+ const names = [];
25055
+ while (index < parts2.length && parts2[index] !== "") {
25056
+ names.push(parts2[index]);
25057
+ index += 1;
25058
+ }
25059
+ index += 1;
25060
+ const kinds = /* @__PURE__ */ new Map();
25061
+ while (index < parts2.length) {
25062
+ const count = Number(parts2[index]);
25063
+ if (!Number.isInteger(count) || count < 0) break;
25064
+ const paths = parts2.slice(index + 1, index + 1 + count);
25065
+ const type = parts2[index + 1 + count] ?? "";
25066
+ const kind = /^CONFLICT \((.+)\)$/.exec(type)?.[1];
25067
+ if (kind !== void 0) {
25068
+ for (const path of paths) if (!kinds.has(path)) kinds.set(path, kind);
25069
+ }
25070
+ index += count + 3;
25071
+ }
25072
+ for (const name2 of names) files.push({ path: name2, kind: kinds.get(name2) ?? "content" });
25073
+ if (exitCode === 0) return files.length === 0 ? { status: "clean", treeOid: oid, files: [] } : { status: "error", files: [], message: "git reported a clean exit with conflicted files" };
25074
+ return files.length === 0 ? { status: "error", treeOid: oid, files: [], message: "git reported conflicts but named no file" } : { status: "conflict", treeOid: oid, files: files.slice(0, 500) };
25075
+ }
25076
+ var CLAIM_SCOPE_KINDS, ClaimScopeKind, MAX_CLAIM_SCOPES, MAX_CLAIM_PATTERNS, MAX_PATTERN_LENGTH, ClaimScopeInput, CLAIM_POLICIES, ClaimPolicy, CLAIM_MODES, ClaimMode, CLAIM_STATUSES, ClaimStatus, CLAIM_LIMITS, ClaimSettings, DEFAULT_CLAIM_SETTINGS, PutClaimSettingsRequest, Ref2, Sha, Claim, AcquireClaimRequest, RenewClaimRequest, ReleaseClaimRequest, CLAIM_DECISIONS, Holder, ClaimOutcome, ClaimListResponse, MERGE_CHECK_STATUSES, MergeCheckStatus, ConflictedFile, MergeTreeResult, RecordMergeCheckRequest, RunMergeCheckRequest, MergeCheckRecord, READINESS_BLOCKERS, ReadinessBlocker, MergeReadiness, claimPaths;
25077
+ var init_claims = __esm({
25078
+ "../contracts/src/claims.ts"() {
25079
+ "use strict";
25080
+ init_zod();
25081
+ init_common();
25082
+ CLAIM_SCOPE_KINDS = ["path", "glob", "module"];
25083
+ ClaimScopeKind = external_exports.enum(CLAIM_SCOPE_KINDS);
25084
+ MAX_CLAIM_SCOPES = 32;
25085
+ MAX_CLAIM_PATTERNS = 128;
25086
+ MAX_PATTERN_LENGTH = 300;
25087
+ ClaimScopeInput = external_exports.object({
25088
+ kind: ClaimScopeKind,
25089
+ value: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
25090
+ /** For a module that is not in the code map: the paths or globs the caller says it covers. */
25091
+ paths: external_exports.array(external_exports.string().min(1).max(MAX_PATTERN_LENGTH)).max(MAX_CLAIM_PATTERNS).optional()
25092
+ }).strict();
25093
+ CLAIM_POLICIES = ["serialise", "refuse", "allow"];
25094
+ ClaimPolicy = external_exports.enum(CLAIM_POLICIES);
25095
+ CLAIM_MODES = ["exclusive", "shared"];
25096
+ ClaimMode = external_exports.enum(CLAIM_MODES);
25097
+ CLAIM_STATUSES = ["active", "queued", "released", "expired"];
25098
+ ClaimStatus = external_exports.enum(CLAIM_STATUSES);
25099
+ CLAIM_LIMITS = {
25100
+ defaultLeaseSeconds: 1800,
25101
+ maxLeaseSeconds: 4 * 3600,
25102
+ /** A claim never lives longer than this from its first grant, however often it is renewed. */
25103
+ maxLifetimeSeconds: 24 * 3600,
25104
+ /** A queued claim that has not been promoted by then expires. */
25105
+ queueSeconds: 4 * 3600,
25106
+ maxActivePerHolder: 20
25107
+ };
25108
+ ClaimSettings = external_exports.object({
25109
+ tenantId: TenantId,
25110
+ projectId: ProjectId,
25111
+ policy: ClaimPolicy,
25112
+ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds),
25113
+ updatedBy: external_exports.string().nullable(),
25114
+ updatedAt: Timestamp.nullable()
25115
+ }).strict();
25116
+ DEFAULT_CLAIM_SETTINGS = { policy: "serialise", leaseSeconds: CLAIM_LIMITS.defaultLeaseSeconds };
25117
+ PutClaimSettingsRequest = external_exports.object({ policy: ClaimPolicy, leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional() }).strict();
25118
+ Ref2 = external_exports.string().min(1).max(200);
25119
+ Sha = external_exports.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full lowercase commit SHA");
25120
+ Claim = external_exports.object({
25121
+ tenantId: TenantId,
25122
+ projectId: ProjectId,
25123
+ id: external_exports.string().min(1).max(80),
25124
+ taskId: external_exports.string().min(1).max(128),
25125
+ /** The principal that holds the claim: an agent token ID or a user ID. */
25126
+ holder: external_exports.string().min(1).max(200),
25127
+ /** A free label such as `codex` or `claude`, for people reading the list. */
25128
+ agent: external_exports.string().min(1).max(80).optional(),
25129
+ mode: ClaimMode,
25130
+ status: ClaimStatus,
25131
+ scopes: external_exports.array(ClaimScopeInput).min(1).max(MAX_CLAIM_SCOPES),
25132
+ patterns: external_exports.array(external_exports.string().min(1).max(MAX_PATTERN_LENGTH)).min(1).max(MAX_CLAIM_PATTERNS),
25133
+ resolvedFrom: external_exports.enum(["given", "code-map"]),
25134
+ /** The branch and head the work is on, once it has one; the pre-merge check compares heads. */
25135
+ branch: Ref2.optional(),
25136
+ headSha: Sha.optional(),
25137
+ /** Arrival order within the project; serialised claims are promoted in this order. */
25138
+ sequence: external_exports.number().int().positive(),
25139
+ leaseExpiresAt: Timestamp,
25140
+ /** The ceiling for renewals. */
25141
+ maxExpiresAt: Timestamp,
25142
+ /** The claims this one waits behind while it is queued. */
25143
+ waitingOn: external_exports.array(external_exports.string()).default([]),
25144
+ createdAt: Timestamp,
25145
+ updatedAt: Timestamp,
25146
+ releasedAt: Timestamp.optional(),
25147
+ releaseReason: external_exports.enum(["done", "released", "expired", "queue_timeout", "forced", "refused"]).optional()
25148
+ }).strict();
25149
+ AcquireClaimRequest = external_exports.object({
25150
+ requestId: external_exports.string().min(1).max(128),
25151
+ taskId: external_exports.string().min(1).max(128),
25152
+ scopes: external_exports.array(ClaimScopeInput).min(1).max(MAX_CLAIM_SCOPES),
25153
+ mode: ClaimMode.default("exclusive"),
25154
+ agent: external_exports.string().min(1).max(80).optional(),
25155
+ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional(),
25156
+ branch: Ref2.optional(),
25157
+ headSha: Sha.optional()
25158
+ }).strict();
25159
+ RenewClaimRequest = external_exports.object({ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional(), branch: Ref2.optional(), headSha: Sha.optional() }).strict();
25160
+ ReleaseClaimRequest = external_exports.object({ reason: external_exports.enum(["done", "released"]).default("released") }).strict();
25161
+ CLAIM_DECISIONS = ["granted", "queued", "refused"];
25162
+ Holder = external_exports.object({ claimId: external_exports.string(), taskId: external_exports.string(), holder: external_exports.string(), agent: external_exports.string().optional(), patterns: external_exports.array(external_exports.string()), leaseExpiresAt: Timestamp }).strict();
25163
+ ClaimOutcome = external_exports.object({
25164
+ tenantId: TenantId,
25165
+ projectId: ProjectId,
25166
+ decision: external_exports.enum(CLAIM_DECISIONS),
25167
+ claim: Claim.nullable(),
25168
+ /** Claims that overlap this one: what it waits behind, what refused it, or what it shares scope with under `allow`. */
25169
+ overlaps: external_exports.array(Holder),
25170
+ /** Position in the queue, 1 for next; 0 when granted or refused. */
25171
+ position: external_exports.number().int().nonnegative(),
25172
+ policy: ClaimPolicy,
25173
+ message: external_exports.string().min(1),
25174
+ nextCommand: external_exports.string().nullable(),
25175
+ replay: external_exports.boolean().default(false)
25176
+ }).strict();
25177
+ ClaimListResponse = external_exports.object({ tenantId: TenantId, projectId: ProjectId, policy: ClaimPolicy, claims: external_exports.array(Claim) }).strict();
25178
+ MERGE_CHECK_STATUSES = ["clean", "conflict", "error"];
25179
+ MergeCheckStatus = external_exports.enum(MERGE_CHECK_STATUSES);
25180
+ ConflictedFile = external_exports.object({ path: external_exports.string().min(1).max(1e3), kind: external_exports.string().min(1).max(80) }).strict();
25181
+ MergeTreeResult = external_exports.object({
25182
+ status: MergeCheckStatus,
25183
+ /** The tree git wrote for a clean merge or with conflict markers. Absent on an error. */
25184
+ treeOid: external_exports.string().regex(/^[0-9a-f]{40,64}$/).optional(),
25185
+ files: external_exports.array(ConflictedFile).max(500),
25186
+ message: external_exports.string().max(1e3).optional()
25187
+ }).strict();
25188
+ RecordMergeCheckRequest = external_exports.object({
25189
+ requestId: external_exports.string().min(1).max(128),
25190
+ a: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict(),
25191
+ b: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict(),
25192
+ result: MergeTreeResult
25193
+ }).strict();
25194
+ RunMergeCheckRequest = external_exports.object({ a: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict(), b: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict() }).strict();
25195
+ MergeCheckRecord = external_exports.object({
25196
+ tenantId: TenantId,
25197
+ projectId: ProjectId,
25198
+ id: external_exports.string().min(1).max(80),
25199
+ a: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().optional() }).strict(),
25200
+ b: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().optional() }).strict(),
25201
+ result: MergeTreeResult,
25202
+ source: external_exports.enum(["runner", "client"]),
25203
+ reportedBy: external_exports.string().min(1),
25204
+ checkedAt: Timestamp
25205
+ }).strict();
25206
+ READINESS_BLOCKERS = ["claim_queued", "claim_inactive", "conflict", "check_required", "check_error", "no_branch"];
25207
+ ReadinessBlocker = external_exports.object({ code: external_exports.enum(READINESS_BLOCKERS), message: external_exports.string().min(1), with: external_exports.string().optional(), files: external_exports.array(ConflictedFile).optional(), nextCommand: external_exports.string().nullable() }).strict();
25208
+ MergeReadiness = external_exports.object({ tenantId: TenantId, projectId: ProjectId, claimId: external_exports.string(), ready: external_exports.boolean(), blockers: external_exports.array(ReadinessBlocker), checkedPairs: external_exports.number().int().nonnegative() }).strict();
25209
+ claimPaths = {
25210
+ claims: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/claims`,
25211
+ claim: (tenantId, projectId, id2) => `${claimPaths.claims(tenantId, projectId)}/${encodeURIComponent(id2)}`,
25212
+ renew: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/renew`,
25213
+ release: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/release`,
25214
+ readiness: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/readiness`,
25215
+ settings: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/settings`,
25216
+ checks: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks`,
25217
+ pending: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks/pending`,
25218
+ runCheck: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks/run`
25219
+ };
25220
+ }
25221
+ });
25222
+
25223
+ // ../contracts/src/budgets.ts
25224
+ var BUDGET_SCOPES, BudgetScope, MAX_BUDGET_CENTS, BUDGET_EXIT_CODE, ScopeId, BudgetLimit, PutBudgetRequest, DeleteBudgetRequest, BudgetSubject, BUDGET_SOURCES, BudgetLine, BudgetBindingSchema, BUDGET_STOP_REASONS, BudgetStopReason, BudgetCheckRequest, BudgetDecision, BudgetStop, RecordSpendRequest, BudgetUsageLine, BudgetSummary, BudgetStopList, budgetPaths;
25225
+ var init_budgets = __esm({
25226
+ "../contracts/src/budgets.ts"() {
25227
+ "use strict";
25228
+ init_zod();
25229
+ init_common();
25230
+ BUDGET_SCOPES = ["task", "agent", "project", "team"];
25231
+ BudgetScope = external_exports.enum(BUDGET_SCOPES);
25232
+ MAX_BUDGET_CENTS = 1e8;
25233
+ BUDGET_EXIT_CODE = 6;
25234
+ ScopeId = external_exports.string().min(1).max(128);
25235
+ BudgetLimit = external_exports.object({
25236
+ tenantId: TenantId,
25237
+ scope: BudgetScope,
25238
+ scopeId: ScopeId,
25239
+ /** Task limits count the task's whole life; the others count the current billing period. */
25240
+ limitCents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS),
25241
+ updatedBy: external_exports.string().min(1),
25242
+ updatedAt: Timestamp
25243
+ }).strict();
25244
+ PutBudgetRequest = external_exports.object({
25245
+ requestId: external_exports.string().min(1).max(128),
25246
+ scope: BudgetScope,
25247
+ /** Omitted for the team. */
25248
+ scopeId: ScopeId.optional(),
25249
+ limitCents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS)
25250
+ }).strict().superRefine((request, ctx) => {
25251
+ if (request.scope !== "team" && request.scopeId === void 0) ctx.addIssue({ code: "custom", path: ["scopeId"], message: `a ${request.scope} budget names its ${request.scope}` });
25252
+ });
25253
+ DeleteBudgetRequest = external_exports.object({ requestId: external_exports.string().min(1).max(128), scope: BudgetScope, scopeId: ScopeId.optional() }).strict().superRefine((request, ctx) => {
25254
+ if (request.scope !== "team" && request.scopeId === void 0) ctx.addIssue({ code: "custom", path: ["scopeId"], message: `a ${request.scope} budget names its ${request.scope}` });
25255
+ });
25256
+ BudgetSubject = external_exports.object({
25257
+ projectId: ProjectId.optional(),
25258
+ taskId: external_exports.string().min(1).max(128).optional(),
25259
+ agentId: external_exports.string().min(1).max(128).optional()
25260
+ }).strict();
25261
+ BUDGET_SOURCES = ["budget", "billing_cap"];
25262
+ BudgetLine = external_exports.object({
25263
+ scope: BudgetScope,
25264
+ scopeId: ScopeId,
25265
+ /** `budget` is a control-plane limit; `billing_cap` is the MS-B5 spending cap read through the same check. */
25266
+ source: external_exports.enum(BUDGET_SOURCES),
25267
+ limitCents: external_exports.number().int().nonnegative(),
25268
+ spentCents: external_exports.number().int().nonnegative()
25269
+ }).strict();
25270
+ BudgetBindingSchema = external_exports.object({
25271
+ scope: BudgetScope,
25272
+ scopeId: ScopeId,
25273
+ source: external_exports.enum(BUDGET_SOURCES),
25274
+ limitCents: external_exports.number().int().nonnegative(),
25275
+ spentCents: external_exports.number().int().nonnegative(),
25276
+ roomCents: external_exports.number().int()
25277
+ }).strict();
25278
+ BUDGET_STOP_REASONS = ["budget", "billing_cap", "read_only", "plan_allowance"];
25279
+ BudgetStopReason = external_exports.enum(BUDGET_STOP_REASONS);
25280
+ BudgetCheckRequest = external_exports.object({
25281
+ requestId: external_exports.string().min(1).max(128),
25282
+ estimateCents: external_exports.number().int().nonnegative().max(MAX_BUDGET_CENTS),
25283
+ projectId: ProjectId.optional(),
25284
+ taskId: external_exports.string().min(1).max(128).optional(),
25285
+ /** For a person calling on an agent's behalf. An agent token is always checked as itself. */
25286
+ agentId: external_exports.string().min(1).max(128).optional(),
25287
+ /** What the call is, for the record (`inference`, `ci`, `review`). Free text, never a prompt. */
25288
+ kind: external_exports.string().min(1).max(64).optional(),
25289
+ /** When true and the call is allowed, the estimate is recorded as spent so the next check counts it. */
25290
+ commit: external_exports.boolean().default(false),
25291
+ /** A billing meter, to also check the plan allowance (reviews, CI minutes, hosted inference). */
25292
+ meter: external_exports.enum(["reviews", "ciMinutes", "docsCalls", "hostedInference"]).optional()
25293
+ }).strict();
25294
+ BudgetDecision = external_exports.object({
25295
+ tenantId: TenantId,
25296
+ allowed: external_exports.boolean(),
25297
+ exitCode: external_exports.union([external_exports.literal(0), external_exports.literal(BUDGET_EXIT_CODE)]),
25298
+ reason: BudgetStopReason.nullable(),
25299
+ message: external_exports.string().min(1),
25300
+ nextCommand: external_exports.string().nullable(),
25301
+ estimateCents: external_exports.number().int().nonnegative(),
25302
+ binding: BudgetBindingSchema.nullable(),
25303
+ checked: external_exports.array(BudgetBindingSchema),
25304
+ /** The ID of the stored stop record when refused. */
25305
+ stopId: external_exports.string().nullable(),
25306
+ /** True when this answer is a replay of an earlier request with the same ID. */
25307
+ replay: external_exports.boolean(),
25308
+ recorded: external_exports.boolean()
25309
+ }).strict();
25310
+ BudgetStop = external_exports.object({
25311
+ tenantId: TenantId,
25312
+ id: external_exports.string().min(1),
25313
+ reason: BudgetStopReason,
25314
+ scope: BudgetScope.nullable(),
25315
+ scopeId: external_exports.string().nullable(),
25316
+ limitCents: external_exports.number().int().nonnegative().nullable(),
25317
+ spentCents: external_exports.number().int().nonnegative().nullable(),
25318
+ estimateCents: external_exports.number().int().nonnegative(),
25319
+ projectId: external_exports.string().nullable(),
25320
+ taskId: external_exports.string().nullable(),
25321
+ agentId: external_exports.string().nullable(),
25322
+ kind: external_exports.string().nullable(),
25323
+ message: external_exports.string().min(1),
25324
+ requestedBy: external_exports.string().min(1),
25325
+ at: Timestamp
25326
+ }).strict();
25327
+ RecordSpendRequest = external_exports.object({
25328
+ requestId: external_exports.string().min(1).max(128),
25329
+ cents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS),
25330
+ projectId: ProjectId.optional(),
25331
+ taskId: external_exports.string().min(1).max(128).optional(),
25332
+ agentId: external_exports.string().min(1).max(128).optional(),
25333
+ kind: external_exports.string().min(1).max(64).optional()
25334
+ }).strict();
25335
+ BudgetUsageLine = external_exports.object({
25336
+ scope: BudgetScope,
25337
+ scopeId: ScopeId,
25338
+ source: external_exports.enum(BUDGET_SOURCES),
25339
+ limitCents: external_exports.number().int().nonnegative(),
25340
+ spentCents: external_exports.number().int().nonnegative(),
25341
+ remainingCents: external_exports.number().int()
25342
+ }).strict();
25343
+ BudgetSummary = external_exports.object({ tenantId: TenantId, periodStart: Timestamp, lines: external_exports.array(BudgetUsageLine) }).strict();
25344
+ BudgetStopList = external_exports.object({ tenantId: TenantId, stops: external_exports.array(BudgetStop) }).strict();
25345
+ budgetPaths = {
25346
+ root: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/budgets`,
25347
+ check: (tenantId) => `${budgetPaths.root(tenantId)}/check`,
25348
+ spend: (tenantId) => `${budgetPaths.root(tenantId)}/spend`,
25349
+ stops: (tenantId) => `${budgetPaths.root(tenantId)}/stops`
25350
+ };
25351
+ }
25352
+ });
25353
+
25354
+ // ../contracts/src/rules-sync.ts
25355
+ function rulesRevision(source) {
25356
+ return sha256Hex(normaliseText(source)).slice(0, 12);
25357
+ }
25358
+ function validateRulesSource(source) {
25359
+ const text = normaliseText(source);
25360
+ if (text.trim() === "") return { ok: false, problem: `${RULES_SOURCE_PATH} is empty` };
25361
+ if (new TextEncoder().encode(text).length > MAX_RULES_BYTES) return { ok: false, problem: `${RULES_SOURCE_PATH} is larger than ${MAX_RULES_BYTES / 1024} KB` };
25362
+ if (text.includes(MARKER)) return { ok: false, problem: `${RULES_SOURCE_PATH} contains the text "${MARKER}", which is reserved for the managed region markers` };
25363
+ return { ok: true, text: text.endsWith("\n") ? text : `${text}
25364
+ `, revision: rulesRevision(text) };
25365
+ }
25366
+ function regionBody(source) {
25367
+ return `<!-- Generated from ${RULES_SOURCE_PATH}. Edit that file, then run \`overlane rules sync\`. A hand edit in this region is reported as drift. -->
25368
+
25369
+ ${source}`;
25370
+ }
25371
+ function renderRegion(source) {
25372
+ const check2 = validateRulesSource(source);
25373
+ if (!check2.ok) throw new Error(check2.problem);
25374
+ const body2 = regionBody(check2.text);
25375
+ return `<!-- ${MARKER}:begin rev=${check2.revision} digest=${digestOf(body2)} -->
25376
+ ${body2}<!-- ${MARKER}:end -->
25377
+ `;
25378
+ }
25379
+ function scanRegion(fileText) {
25380
+ const lines = normaliseText(fileText).split("\n");
25381
+ const regions = [];
25382
+ let open2 = null;
25383
+ for (let i2 = 0; i2 < lines.length; i2 += 1) {
25384
+ const line3 = lines[i2];
25385
+ const begin = BEGIN.exec(line3);
25386
+ if (begin !== null) {
25387
+ if (open2 !== null) return { kind: "damaged", problem: `a second begin marker on line ${i2 + 1} before the end of the region from line ${open2.index + 1}` };
25388
+ open2 = { index: i2, revision: begin[1], digest: begin[2] };
25389
+ } else if (line3.trim().startsWith(`<!-- ${MARKER}:begin`)) {
25390
+ return { kind: "damaged", problem: `line ${i2 + 1} looks like a begin marker but is not well formed` };
25391
+ } else if (line3 === END_LINE) {
25392
+ if (open2 === null) return { kind: "damaged", problem: `an end marker on line ${i2 + 1} with no begin marker` };
25393
+ regions.push({ startLine: open2.index, endLine: i2, revision: open2.revision, digest: open2.digest, body: `${lines.slice(open2.index + 1, i2).join("\n")}
25394
+ ` });
25395
+ open2 = null;
25396
+ }
25397
+ }
25398
+ if (open2 !== null) return { kind: "damaged", problem: `the begin marker on line ${open2.index + 1} has no end marker` };
25399
+ if (regions.length > 1) return { kind: "damaged", problem: `${regions.length} managed regions in one file` };
25400
+ const only = regions[0];
25401
+ return only === void 0 ? { kind: "none" } : { kind: "one", region: only };
25402
+ }
25403
+ function firstDifference(found, expected) {
25404
+ const a = found.split("\n");
25405
+ const b = expected.split("\n");
25406
+ const length = Math.max(a.length, b.length);
25407
+ for (let i2 = 0; i2 < length; i2 += 1) {
25408
+ if ((a[i2] ?? "") !== (b[i2] ?? "")) return { line: i2 + 1, found: a[i2] ?? "", expected: b[i2] ?? "" };
25409
+ }
25410
+ return { line: 1, found: "", expected: "" };
25411
+ }
25412
+ function describeLine(text) {
25413
+ const clean2 = text.trim();
25414
+ return clean2.length > 80 ? `${clean2.slice(0, 77)}...` : clean2;
25415
+ }
25416
+ function inspectRegionTarget(target2, fileText, source) {
25417
+ const base4 = { id: target2.id, path: target2.path };
25418
+ if (fileText === null) return { ...base4, state: "missing", detail: `${target2.path} does not exist; sync creates it`, syncable: true };
25419
+ const scan = scanRegion(fileText);
25420
+ if (scan.kind === "damaged") return { ...base4, state: "damaged", detail: `${target2.path}: ${scan.problem}; fix the markers by hand, then run sync`, syncable: false };
25421
+ if (scan.kind === "none") return { ...base4, state: "unmanaged", detail: `${target2.path} has no managed region; sync appends one and keeps every existing line`, syncable: true };
25422
+ const { region } = scan;
25423
+ if (digestOf(region.body) !== region.digest) {
25424
+ const expected = regionBody(source.text);
25425
+ const diff = firstDifference(region.body, region.revision === source.revision ? expected : region.body);
25426
+ const where2 = region.revision === source.revision ? `line ${diff.line} of the region` : "the region";
25427
+ return {
25428
+ ...base4,
25429
+ state: "edited",
25430
+ detail: `${target2.path}: ${where2} was edited by hand${region.revision === source.revision ? ` (found "${describeLine(diff.found)}", the source says "${describeLine(diff.expected)}")` : ""}; move the change into ${RULES_SOURCE_PATH}, or run sync --overwrite-edits to replace it`,
25431
+ ...region.revision === source.revision ? { firstDifference: diff } : {},
25432
+ syncable: false
25433
+ };
25434
+ }
25435
+ if (region.revision !== source.revision) return { ...base4, state: "stale", detail: `${target2.path} was written from revision ${region.revision}; the source is ${source.revision}; sync updates it`, syncable: true };
25436
+ return { ...base4, state: "in_sync", detail: `${target2.path} matches revision ${source.revision}`, syncable: true };
25437
+ }
25438
+ function planRegionSync(target2, fileText, source, force) {
25439
+ const report2 = inspectRegionTarget(target2, fileText, source);
25440
+ const region = renderRegion(source.text);
25441
+ const base4 = { id: target2.id, path: target2.path };
25442
+ switch (report2.state) {
25443
+ case "in_sync":
25444
+ return { ...base4, action: "none", next: null, reason: report2.detail };
25445
+ case "missing":
25446
+ return { ...base4, action: "create", next: `${target2.preamble}${region}`, reason: "created" };
25447
+ case "unmanaged": {
25448
+ const text = normaliseText(fileText);
25449
+ const joined = text === "" ? "" : text.endsWith("\n\n") ? text : text.endsWith("\n") ? `${text}
25450
+ ` : `${text}
25451
+
25452
+ `;
25453
+ return { ...base4, action: "append", next: `${joined}${region}`, reason: "appended a managed region at the end; every existing line is kept" };
25454
+ }
25455
+ case "stale":
25456
+ case "edited": {
25457
+ if (report2.state === "edited" && !force) return { ...base4, action: "refuse", next: null, reason: report2.detail };
25458
+ const scan = scanRegion(fileText);
25459
+ if (scan.kind !== "one") return { ...base4, action: "refuse", next: null, reason: report2.detail };
25460
+ const lines = normaliseText(fileText).split("\n");
25461
+ const before = lines.slice(0, scan.region.startLine).join("\n");
25462
+ const after = lines.slice(scan.region.endLine + 1).join("\n");
25463
+ const next = `${before === "" ? "" : `${before}
25464
+ `}${region}${after === "" ? "" : after}`;
25465
+ const replaced = scan.region.body.split("\n").filter((l, i2, all) => !(i2 === all.length - 1 && l === ""));
25466
+ const written = new Set(regionBody(source.text).split("\n"));
25467
+ const going = replaced.filter((l) => l.trim() !== "" && !written.has(l));
25468
+ return {
25469
+ ...base4,
25470
+ action: report2.state === "edited" ? "force_replace" : "replace",
25471
+ next: next.endsWith("\n") ? next : `${next}
25472
+ `,
25473
+ ...report2.state === "edited" ? { replaces: { lines: replaced.length, firstLines: (going.length > 0 ? going : replaced).slice(0, 5) } } : {},
25474
+ reason: report2.state === "edited" ? "replaced a hand-edited region because --force was given" : "updated to the current revision"
25475
+ };
25476
+ }
25477
+ case "damaged":
25478
+ return { ...base4, action: "refuse", next: null, reason: report2.detail };
25479
+ }
25480
+ }
25481
+ function parseSettings(text) {
25482
+ if (text === null || text.trim() === "") return { ok: true, value: {} };
25483
+ try {
25484
+ const value = JSON.parse(text);
25485
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return { ok: false, problem: "is not a JSON object" };
25486
+ return { ok: true, value };
25487
+ } catch {
25488
+ return { ok: false, problem: "is not valid JSON (comments are not supported here)" };
25489
+ }
25490
+ }
25491
+ function contextFileNames(settings) {
25492
+ const context4 = settings["context"];
25493
+ if (context4 === null || typeof context4 !== "object" || Array.isArray(context4)) return { names: [], shape: "none" };
25494
+ const fileName = context4["fileName"];
25495
+ if (typeof fileName === "string") return { names: [fileName], shape: "string" };
25496
+ if (Array.isArray(fileName) && fileName.every((n) => typeof n === "string")) return { names: fileName, shape: "array" };
25497
+ return { names: [], shape: fileName === void 0 ? "none" : "other" };
25498
+ }
25499
+ function inspectGeminiSettings(target2, fileText) {
25500
+ const base4 = { id: target2.id, path: target2.path };
25501
+ if (fileText === null) return { ...base4, state: "missing", detail: `${target2.path} does not exist; sync creates it`, syncable: true };
25502
+ const parsed = parseSettings(fileText);
25503
+ if (!parsed.ok) return { ...base4, state: "damaged", detail: `${target2.path} ${parsed.problem}; fix it by hand, then run sync`, syncable: false };
25504
+ const { names, shape: shape2 } = contextFileNames(parsed.value);
25505
+ if (shape2 === "other") return { ...base4, state: "damaged", detail: `${target2.path}: context.fileName is neither a string nor a list of strings; fix it by hand, then run sync`, syncable: false };
25506
+ const missing = GEMINI_CONTEXT_FILES.filter((name2) => !names.includes(name2));
25507
+ if (missing.length === 0) return { ...base4, state: "in_sync", detail: `${target2.path} lists ${GEMINI_CONTEXT_FILES.join(" and ")}`, syncable: true };
25508
+ return { ...base4, state: "unmanaged", detail: `${target2.path}: context.fileName does not list ${missing.join(" and ")}; sync adds it and keeps every other setting`, syncable: true };
25509
+ }
25510
+ function planGeminiSettingsSync(target2, fileText) {
25511
+ const report2 = inspectGeminiSettings(target2, fileText);
25512
+ const base4 = { id: target2.id, path: target2.path };
25513
+ if (report2.state === "in_sync") return { ...base4, action: "none", next: null, reason: report2.detail };
25514
+ if (report2.state === "damaged") return { ...base4, action: "refuse", next: null, reason: report2.detail };
25515
+ const parsed = parseSettings(fileText);
25516
+ if (!parsed.ok) return { ...base4, action: "refuse", next: null, reason: report2.detail };
25517
+ const settings = { ...parsed.value };
25518
+ const existing = contextFileNames(settings).names;
25519
+ const context4 = settings["context"] !== null && typeof settings["context"] === "object" && !Array.isArray(settings["context"]) ? { ...settings["context"] } : {};
25520
+ const names = [...existing];
25521
+ for (const name2 of GEMINI_CONTEXT_FILES) if (!names.includes(name2)) names.push(name2);
25522
+ context4["fileName"] = names;
25523
+ settings["context"] = context4;
25524
+ return { ...base4, action: fileText === null ? "create" : "append", next: `${JSON.stringify(settings, null, 2)}
25525
+ `, reason: fileText === null ? "created" : "added the missing names to context.fileName; nothing else changed" };
25526
+ }
25527
+ function checkRules(source, input2) {
25528
+ if (source === null) return { configured: false, revision: null, sourceProblem: null, drifted: false, targets: [], nextCommand: `create ${RULES_SOURCE_PATH}, then run overlane rules sync` };
25529
+ const check2 = validateRulesSource(source);
25530
+ if (!check2.ok) return { configured: true, revision: null, sourceProblem: check2.problem, drifted: true, targets: [], nextCommand: `fix ${RULES_SOURCE_PATH}` };
25531
+ const targets = RULES_TARGETS.map((target2) => target2.kind === "region" ? inspectRegionTarget(target2, input2.files[target2.path] ?? null, check2) : inspectGeminiSettings(target2, input2.files[target2.path] ?? null));
25532
+ const drifted = targets.some((t) => t.state !== "in_sync");
25533
+ const needsForce = targets.some((t) => t.state === "edited");
25534
+ return { configured: true, revision: check2.revision, sourceProblem: null, drifted, targets, nextCommand: !drifted ? null : needsForce ? "overlane rules sync --overwrite-edits" : "overlane rules sync" };
25535
+ }
25536
+ function planRulesSync(source, input2, options = {}) {
25537
+ const check2 = validateRulesSource(source);
25538
+ if (!check2.ok) return { ok: false, problem: check2.problem };
25539
+ const steps = RULES_TARGETS.map((target2) => target2.kind === "region" ? planRegionSync(target2, input2.files[target2.path] ?? null, check2, options.force === true) : planGeminiSettingsSync(target2, input2.files[target2.path] ?? null));
25540
+ return { ok: true, plan: { steps, blocked: steps.some((s) => s.action === "refuse") } };
25541
+ }
25542
+ var RULES_SOURCE_PATH, MAX_RULES_BYTES, MARKER, RULES_TARGET_IDS, RulesTargetId, CURSOR_PREAMBLE, RULES_TARGETS, GEMINI_CONTEXT_FILES, normaliseText, digestOf, BEGIN, END_LINE, TARGET_STATES, TargetState, RulesCheckReport;
25543
+ var init_rules_sync = __esm({
25544
+ "../contracts/src/rules-sync.ts"() {
25545
+ "use strict";
25546
+ init_zod();
25547
+ init_audit();
25548
+ RULES_SOURCE_PATH = ".overlane/rules.md";
25549
+ MAX_RULES_BYTES = 64 * 1024;
25550
+ MARKER = "overlane-rules";
25551
+ RULES_TARGET_IDS = ["agents-md", "claude-md", "cursor", "gemini-md", "gemini-settings"];
25552
+ RulesTargetId = external_exports.enum(RULES_TARGET_IDS);
25553
+ CURSOR_PREAMBLE = "---\ndescription: Project rules, generated from .overlane/rules.md\nalwaysApply: true\n---\n\n";
25554
+ RULES_TARGETS = [
25555
+ { id: "agents-md", path: "AGENTS.md", kind: "region", reader: "Codex, Cursor, Gemini CLI and other agents that read AGENTS.md", preamble: "" },
25556
+ { id: "claude-md", path: "CLAUDE.md", kind: "region", reader: "Claude Code", preamble: "" },
25557
+ { id: "cursor", path: ".cursor/rules/overlane.mdc", kind: "region", reader: "Cursor", preamble: CURSOR_PREAMBLE },
25558
+ { id: "gemini-md", path: "GEMINI.md", kind: "region", reader: "Gemini CLI", preamble: "" },
25559
+ { id: "gemini-settings", path: ".gemini/settings.json", kind: "gemini-settings", reader: "Gemini CLI settings (context.fileName)", preamble: "" }
25560
+ ];
25561
+ GEMINI_CONTEXT_FILES = ["GEMINI.md", "AGENTS.md"];
25562
+ normaliseText = (text) => text.replace(/\r\n?/g, "\n");
25563
+ digestOf = (body2) => sha256Hex(body2).slice(0, 16);
25564
+ BEGIN = new RegExp(`^<!-- ${MARKER}:begin rev=([0-9a-f]{12}) digest=([0-9a-f]{16}) -->$`);
25565
+ END_LINE = `<!-- ${MARKER}:end -->`;
25566
+ TARGET_STATES = ["in_sync", "missing", "unmanaged", "stale", "edited", "damaged"];
25567
+ TargetState = external_exports.enum(TARGET_STATES);
25568
+ RulesCheckReport = external_exports.object({
25569
+ configured: external_exports.boolean(),
25570
+ revision: external_exports.string().nullable(),
25571
+ sourceProblem: external_exports.string().nullable(),
25572
+ drifted: external_exports.boolean(),
25573
+ targets: external_exports.array(
25574
+ external_exports.object({
25575
+ id: RulesTargetId,
25576
+ path: external_exports.string(),
25577
+ state: TargetState,
25578
+ detail: external_exports.string(),
25579
+ syncable: external_exports.boolean(),
25580
+ firstDifference: external_exports.object({ line: external_exports.number().int(), found: external_exports.string(), expected: external_exports.string() }).strict().optional()
25581
+ }).strict()
25582
+ ),
25583
+ nextCommand: external_exports.string().nullable()
25584
+ }).strict();
25585
+ }
25586
+ });
25587
+
25588
+ // ../contracts/src/pr-routing.ts
25589
+ var ROUTE_LANES, RouteLane, ROUTE_RISKS, RouteRisk, CHECK_STATES, CheckState, PrChecks, NO_CHECKS, SEG, END, SECRETS2, WORKFLOWS, MIGRATIONS, BILLING2, AUTH, INFRA, DOCS, TESTS, RiskSignal, DeterministicRisk, Blocker, MODEL_STATUSES, ModelStatus, APPROVAL_REQUIREMENTS, RouteDecision, ROUTE_APPROVER_ROLES, RoutingPolicy, PutRoutingPolicyRequest, ROUTE_APPROVAL_SOURCES, RouteApprovalInput, RoutePrRequest, StoredRoute, RouteApproveRequest, prRoutingPaths;
25590
+ var init_pr_routing = __esm({
25591
+ "../contracts/src/pr-routing.ts"() {
25592
+ "use strict";
25593
+ init_zod();
25594
+ init_autonomy();
25595
+ init_claims();
25596
+ init_common();
25597
+ init_review();
25598
+ ROUTE_LANES = ["blocked", "fast", "standard", "approver"];
25599
+ RouteLane = external_exports.enum(ROUTE_LANES);
25600
+ ROUTE_RISKS = ["low", "medium", "high"];
25601
+ RouteRisk = external_exports.enum(ROUTE_RISKS);
25602
+ CHECK_STATES = ["passed", "failed", "pending", "missing"];
25603
+ CheckState = external_exports.enum(CHECK_STATES);
25604
+ PrChecks = external_exports.object({
25605
+ ci: CheckState.default("missing"),
25606
+ secretScan: CheckState.default("missing"),
25607
+ guardrails: CheckState.default("missing"),
25608
+ /** The pre-merge conflict check (MS-C1.1): passed means no conflict. */
25609
+ conflict: CheckState.default("missing"),
25610
+ /** Unresolved critical review findings. */
25611
+ criticalFindings: external_exports.number().int().nonnegative().default(0),
25612
+ /** The Overlane review run finished for this head. */
25613
+ reviewDone: external_exports.boolean().default(false)
25614
+ }).strict();
25615
+ NO_CHECKS = { ci: "missing", secretScan: "missing", guardrails: "missing", conflict: "missing", criticalFindings: 0, reviewDone: false };
25616
+ SEG = "(?:^|/)";
25617
+ END = "(?:/|\\.|[-_]|$)";
25618
+ SECRETS2 = [new RegExp(`${SEG}\\.env(?:\\.|$)`), new RegExp(`${SEG}(?:secrets?|credentials?)${END}`), /\.(?:pem|key|p12|pfx|jks|keystore)$/, new RegExp(`${SEG}id_(?:rsa|ed25519|ecdsa)`), /\.tfvars$/, new RegExp(`${SEG}\\.npmrc$`)];
25619
+ WORKFLOWS = [/^\.github\/(?:workflows|actions)\//, /^\.gitlab-ci/, new RegExp(`${SEG}\\.circleci/`), new RegExp(`${SEG}azure-pipelines`), new RegExp(`${SEG}jenkinsfile`), /^\.buildkite\//];
25620
+ MIGRATIONS = [new RegExp(`${SEG}(?:migrations?|db/migrate)/`), /\.sql$/, new RegExp(`${SEG}schema\\.(?:sql|prisma)$`), new RegExp(`${SEG}drizzle/`)];
25621
+ BILLING2 = [new RegExp(`${SEG}(?:billing|payments?|stripe|invoices?|checkout|subscriptions?)${END}`)];
25622
+ AUTH = [new RegExp(`${SEG}(?:auth|authn|authz|oauth|identity|permissions?|rbac|acl|sessions?|login|sso|saml|tokens?)${END}`)];
25623
+ INFRA = [new RegExp(`${SEG}wrangler\\.(?:json|jsonc|toml)$`), new RegExp(`${SEG}dockerfile`), new RegExp(`${SEG}docker-compose`), /\.tf$/, new RegExp(`${SEG}(?:k8s|kubernetes|helm|terraform)/`)];
25624
+ DOCS = [/^docs\//, /^apps\/docs\//, /\.(?:md|mdx|txt|rst)$/, new RegExp(`${SEG}readme`)];
25625
+ TESTS = [new RegExp(`${SEG}(?:tests?|__tests__|spec|e2e)/`), /\.(?:test|spec)\.[cm]?[jt]sx?$/];
25626
+ RiskSignal = external_exports.object({ code: external_exports.string().min(1), message: external_exports.string().min(1) }).strict();
25627
+ DeterministicRisk = external_exports.object({
25628
+ level: RouteRisk,
25629
+ linesChanged: external_exports.number().int().nonnegative(),
25630
+ filesChanged: external_exports.number().int().nonnegative(),
25631
+ sensitive: external_exports.array(external_exports.object({ path: external_exports.string(), category: external_exports.string() }).strict()),
25632
+ kind: external_exports.enum(["docs", "tests", "code", "mixed", "empty"]),
25633
+ signals: external_exports.array(RiskSignal)
25634
+ }).strict();
25635
+ Blocker = external_exports.object({ code: external_exports.string().min(1), message: external_exports.string().min(1), nextCommand: external_exports.string().nullable() }).strict();
25636
+ MODEL_STATUSES = ["off", "skipped", "undecided", "failed", "used"];
25637
+ ModelStatus = external_exports.enum(MODEL_STATUSES);
25638
+ APPROVAL_REQUIREMENTS = ["none", "independent", "approver"];
25639
+ RouteDecision = external_exports.object({
25640
+ lane: RouteLane,
25641
+ /** Risk after the model, if it raised it; equals `deterministic.level` otherwise. */
25642
+ risk: RouteRisk,
25643
+ deterministic: DeterministicRisk,
25644
+ blockers: external_exports.array(Blocker),
25645
+ model: external_exports.object({ status: ModelStatus, level: RouteRisk.nullable(), raised: external_exports.boolean(), note: external_exports.string() }).strict(),
25646
+ approval: external_exports.object({ requirement: external_exports.enum(APPROVAL_REQUIREMENTS), reviewRequired: external_exports.boolean() }).strict(),
25647
+ reasons: external_exports.array(external_exports.string()),
25648
+ nextCommand: external_exports.string().nullable(),
25649
+ /** True when the model may still be asked: the checks are clean and the lane can still rise. */
25650
+ modelEligible: external_exports.boolean()
25651
+ }).strict();
25652
+ ROUTE_APPROVER_ROLES = ["owner", "admin"];
25653
+ RoutingPolicy = external_exports.object({
25654
+ approverUsers: external_exports.array(external_exports.string().min(1).max(200)).max(50).default([]),
25655
+ approverRoles: external_exports.array(external_exports.enum(ROUTE_APPROVER_ROLES)).max(2).default(["owner", "admin"]),
25656
+ /** Off by default: the approver lane is a person's. Turning it on lets an independent agent with evidence approve it too. */
25657
+ allowAgentApprover: external_exports.boolean().default(false),
25658
+ maxBatchSize: external_exports.number().int().min(2).max(20).default(5)
25659
+ }).strict();
25660
+ PutRoutingPolicyRequest = RoutingPolicy.partial().strict();
25661
+ ROUTE_APPROVAL_SOURCES = ["agent", "person", "model"];
25662
+ RouteApprovalInput = external_exports.object({
25663
+ /** Optional: the service derives it from who is signed in (an agent token is `agent`, a signed-in member `person`). `model` is accepted only to be refused. */
25664
+ source: external_exports.enum(ROUTE_APPROVAL_SOURCES).optional(),
25665
+ /** What was checked, for an agent's approval. */
25666
+ evidence: external_exports.array(external_exports.string().min(1).max(400)).max(20).default([]),
25667
+ reason: external_exports.string().max(400).default("")
25668
+ }).strict();
25669
+ RoutePrRequest = external_exports.object({
25670
+ requestId: external_exports.string().min(1).max(128),
25671
+ pull: PullRequestSnapshot,
25672
+ checks: PrChecks.default({ ...NO_CHECKS }),
25673
+ taskId: external_exports.string().min(1).max(128).optional(),
25674
+ branch: external_exports.string().min(1).max(250).optional(),
25675
+ baseRef: external_exports.string().min(1).max(250).optional(),
25676
+ /** The claimed scope patterns, when the task holds a claim (MS-C1.1). */
25677
+ scope: external_exports.array(external_exports.string().min(1).max(300)).max(128).optional(),
25678
+ /** The agent token that opened the pull request, so its lineage is known to the independence check. */
25679
+ authorAgentTokenId: external_exports.string().min(1).max(200).optional(),
25680
+ authorUserId: external_exports.string().min(1).max(200).optional()
25681
+ }).strict();
25682
+ StoredRoute = external_exports.object({
25683
+ tenantId: TenantId,
25684
+ projectId: ProjectId,
25685
+ repository: external_exports.string().min(1),
25686
+ number: external_exports.number().int().positive(),
25687
+ headSha: external_exports.string().min(1),
25688
+ title: external_exports.string(),
25689
+ author: external_exports.string(),
25690
+ taskId: external_exports.string().nullable(),
25691
+ branch: external_exports.string().nullable(),
25692
+ baseRef: external_exports.string().nullable(),
25693
+ route: RouteDecision,
25694
+ checks: PrChecks,
25695
+ approvals: external_exports.array(external_exports.object({ id: external_exports.string(), source: external_exports.enum(ROUTE_APPROVAL_SOURCES), approver: external_exports.string(), via: external_exports.string(), at: Timestamp }).strict()),
25696
+ /** The lane's requirement is met by an accepted approval at this head and the checks are still clean. */
25697
+ approved: external_exports.boolean(),
25698
+ routedAt: Timestamp
25699
+ }).strict();
25700
+ RouteApproveRequest = external_exports.object({
25701
+ requestId: external_exports.string().min(1).max(128),
25702
+ repository: external_exports.string().min(1).max(200),
25703
+ number: external_exports.number().int().positive(),
25704
+ headSha: external_exports.string().min(1).max(64),
25705
+ approval: RouteApprovalInput
25706
+ }).strict();
25707
+ prRoutingPaths = {
25708
+ routes: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/pr-routes`,
25709
+ route: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/route`,
25710
+ approve: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/approve`,
25711
+ batch: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/batch`,
25712
+ policy: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/policy`
25713
+ };
25714
+ }
25715
+ });
25716
+
25717
+ // ../contracts/src/egress.ts
25718
+ function normaliseHost(raw) {
25719
+ let host = raw.trim().toLowerCase();
25720
+ if (host === "") return { ok: false, reason: "invalid_host" };
25721
+ if (/[^\u0000-\u007f]/.test(host)) return { ok: false, reason: "non_ascii_host" };
25722
+ if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
25723
+ if (host.includes(":")) return /^[0-9a-f:.]+$/.test(host) && host.length >= 2 ? { ok: true, host, kind: "ip" } : { ok: false, reason: "invalid_host" };
25724
+ if (host.endsWith(".")) host = host.slice(0, -1);
25725
+ if (host === "" || host.length > 253 || host.endsWith(".") || /[\s/\\@#?%*]/.test(host)) return { ok: false, reason: "invalid_host" };
25726
+ const labels = host.split(".");
25727
+ const last = labels[labels.length - 1];
25728
+ if (/^(?:0x[0-9a-f]*|\d+)$/.test(last)) return /^[0-9a-fx.]+$/.test(host) ? { ok: true, host, kind: "ip" } : { ok: false, reason: "invalid_host" };
25729
+ if (!labels.every((label2) => LABEL.test(label2))) return { ok: false, reason: "invalid_host" };
25730
+ return { ok: true, host, kind: "name" };
25731
+ }
25732
+ function isMetadataHost(host) {
25733
+ return METADATA_NAMES.has(host) || METADATA_ADDRESSES.has(host);
25734
+ }
25735
+ function isInternalName(host) {
25736
+ return host === "localhost" || INTERNAL_SUFFIXES.some((suffix) => host.endsWith(suffix)) || !host.includes(".");
25737
+ }
25738
+ function isSharedHostingWildcard(pattern) {
25739
+ if (!pattern.startsWith("*.")) return false;
25740
+ const name2 = pattern.slice(2);
25741
+ return SHARED_HOSTING_SUFFIXES.some((suffix) => name2 === suffix || name2.endsWith(`.${suffix}`));
25742
+ }
25743
+ var LABEL, METADATA_NAMES, METADATA_ADDRESSES, INTERNAL_SUFFIXES, SHARED_HOSTING_SUFFIXES, HOST_PATTERN, MAX_EGRESS_RULES, EgressRule, EGRESS_MODES, EgressPolicy, EgressPolicyView, PutEgressPolicyRequest, EGRESS_REASONS, EgressReason, EgressDecision, EgressEvent, EvaluateEgressRequest, GRANT_SCOPES, GrantScope, MAX_GRANT_SECONDS, MAX_GRANT_HOSTS, DENIED_HEADERS, GrantAttach, SECRET_NAME, GrantRequest, GRANT_STATUSES, TaskGrant, ResolveRefusal, RevokeGrantRequest, LeakScanRequest, LeakFilesRequest, LeakFinding, LeakScanResult, egressPaths;
25744
+ var init_egress = __esm({
25745
+ "../contracts/src/egress.ts"() {
25746
+ "use strict";
25747
+ init_zod();
25748
+ init_common();
25749
+ LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
25750
+ METADATA_NAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata", "instance-data", "metadata.azure.com", "metadata.tencentyun.com"]);
25751
+ METADATA_ADDRESSES = /* @__PURE__ */ new Set(["169.254.169.254", "169.254.170.2", "100.100.100.200", "192.0.0.192", "fd00:ec2::254", "::ffff:169.254.169.254"]);
25752
+ INTERNAL_SUFFIXES = [".internal", ".local", ".localhost", ".localdomain", ".lan", ".home.arpa", ".corp", ".intranet", ".cluster.local"];
25753
+ SHARED_HOSTING_SUFFIXES = [
25754
+ "co.uk",
25755
+ "org.uk",
25756
+ "com.au",
25757
+ "co.jp",
25758
+ "co.nz",
25759
+ "co.in",
25760
+ "com.br",
25761
+ "github.io",
25762
+ "githubusercontent.com",
25763
+ "gitlab.io",
25764
+ "herokuapp.com",
25765
+ "vercel.app",
25766
+ "netlify.app",
25767
+ "workers.dev",
25768
+ "pages.dev",
25769
+ "web.app",
25770
+ "firebaseapp.com",
25771
+ "appspot.com",
25772
+ "azurewebsites.net",
25773
+ "cloudfront.net",
25774
+ "amazonaws.com",
25775
+ "blob.core.windows.net",
25776
+ "r2.dev",
25777
+ "ngrok.io",
25778
+ "ngrok-free.app",
25779
+ "trycloudflare.com",
25780
+ "onrender.com",
25781
+ "fly.dev",
25782
+ "glitch.me",
25783
+ "repl.co"
25784
+ ];
25785
+ HOST_PATTERN = external_exports.string().min(3).max(253).refine((value) => {
25786
+ const text = value.trim().toLowerCase();
25787
+ const name2 = text.startsWith("*.") ? text.slice(2) : text;
25788
+ const check2 = normaliseHost(name2);
25789
+ return check2.ok && check2.kind === "name" && name2.includes(".") && !isMetadataHost(name2) && !isInternalName(name2) && !isSharedHostingWildcard(text);
25790
+ }, "must be a host name such as registry.npmjs.org or *.example.com (no address, no internal name, no wildcard over a top-level or shared-hosting domain)");
25791
+ MAX_EGRESS_RULES = 100;
25792
+ EgressRule = external_exports.object({
25793
+ /** An exact host, or `*.example.com`, which matches any subdomain at any depth and never the apex `example.com`. */
25794
+ host: HOST_PATTERN.transform((value) => value.trim().toLowerCase()),
25795
+ /** Allowed destination ports. Default 443. */
25796
+ ports: external_exports.array(external_exports.number().int().min(1).max(65535)).min(1).max(8).default([443]),
25797
+ note: external_exports.string().max(200).optional()
25798
+ }).strict();
25799
+ EGRESS_MODES = ["enforce", "report"];
25800
+ EgressPolicy = external_exports.object({
25801
+ /** `enforce` denies what no rule allows. `report` records it and allows it, to learn what a job needs before enforcing. Raw addresses and metadata services are denied in both. */
25802
+ mode: external_exports.enum(EGRESS_MODES).default("enforce"),
25803
+ rules: external_exports.array(EgressRule).max(MAX_EGRESS_RULES).default([])
25804
+ }).strict();
25805
+ EgressPolicyView = EgressPolicy.extend({ tenantId: TenantId, projectId: ProjectId, version: external_exports.number().int().nonnegative(), updatedAt: Timestamp.nullable() }).strict();
25806
+ PutEgressPolicyRequest = EgressPolicy.extend({ expectedVersion: external_exports.number().int().nonnegative().optional() }).strict();
25807
+ EGRESS_REASONS = ["rule_match", "no_rule", "port_not_allowed", "report_only", "invalid_host", "raw_ip", "metadata", "internal_name", "private_address"];
25808
+ EgressReason = external_exports.enum(EGRESS_REASONS);
25809
+ EgressDecision = external_exports.object({
25810
+ outcome: external_exports.enum(["allowed", "denied", "reported"]),
25811
+ /** False only when the request must not go out. `reported` is allowed. */
25812
+ allowed: external_exports.boolean(),
25813
+ reason: EgressReason,
25814
+ host: external_exports.string(),
25815
+ port: external_exports.number().int(),
25816
+ rule: external_exports.string().nullable(),
25817
+ message: external_exports.string()
25818
+ }).strict();
25819
+ EgressEvent = external_exports.object({
25820
+ tenantId: TenantId,
25821
+ projectId: ProjectId,
25822
+ id: external_exports.string().min(1),
25823
+ at: Timestamp,
25824
+ source: external_exports.enum(["runner", "api"]),
25825
+ taskId: external_exports.string().nullable(),
25826
+ runId: external_exports.string().nullable(),
25827
+ host: external_exports.string(),
25828
+ port: external_exports.number().int(),
25829
+ outcome: external_exports.enum(["allowed", "denied", "reported"]),
25830
+ reason: EgressReason,
25831
+ rule: external_exports.string().nullable(),
25832
+ grantId: external_exports.string().nullable()
25833
+ }).strict();
25834
+ EvaluateEgressRequest = external_exports.object({
25835
+ host: external_exports.string().min(1).max(300),
25836
+ port: external_exports.number().int().min(1).max(65535).default(443),
25837
+ taskId: external_exports.string().min(1).max(128).optional(),
25838
+ runId: external_exports.string().min(1).max(128).optional(),
25839
+ /** What the name resolved to, when the caller looked it up. */
25840
+ resolved: external_exports.array(external_exports.string().min(1).max(64)).max(16).optional()
25841
+ }).strict();
25842
+ GRANT_SCOPES = ["task", "project", "team"];
25843
+ GrantScope = external_exports.enum(GRANT_SCOPES);
25844
+ MAX_GRANT_SECONDS = 24 * 3600;
25845
+ MAX_GRANT_HOSTS = 10;
25846
+ DENIED_HEADERS = /* @__PURE__ */ new Set(["host", "content-length", "transfer-encoding", "connection", "upgrade", "te", "trailer", "proxy-authorization", "cookie", "set-cookie", "x-forwarded-for", "x-forwarded-host"]);
25847
+ GrantAttach = external_exports.discriminatedUnion("kind", [
25848
+ external_exports.object({ kind: external_exports.literal("bearer") }).strict(),
25849
+ external_exports.object({ kind: external_exports.literal("header"), name: external_exports.string().regex(/^[a-z][a-z0-9-]{1,40}$/).refine((n) => !DENIED_HEADERS.has(n), "that header cannot carry a credential") }).strict()
25850
+ ]);
25851
+ SECRET_NAME = /^[A-Z][A-Z0-9_]{1,63}$/;
25852
+ GrantRequest = external_exports.object({
25853
+ requestId: external_exports.string().min(1).max(128),
25854
+ scope: GrantScope.default("task"),
25855
+ /** Required for a task grant. */
25856
+ taskId: external_exports.string().min(1).max(128).optional(),
25857
+ hosts: external_exports.array(HOST_PATTERN.transform((v) => v.trim().toLowerCase())).min(1).max(MAX_GRANT_HOSTS),
25858
+ /** The secret's name in the project's secret store. The value is never in this request or its answer. */
25859
+ secretName: external_exports.string().regex(SECRET_NAME, "an upper-case secret name such as NPM_TOKEN"),
25860
+ attach: GrantAttach.default({ kind: "bearer" }),
25861
+ ttlSeconds: external_exports.number().int().min(60).max(MAX_GRANT_SECONDS).default(900)
25862
+ }).strict().superRefine((request, ctx) => {
25863
+ if (request.scope === "task" && request.taskId === void 0) ctx.addIssue({ code: "custom", path: ["taskId"], message: "a task grant names its task" });
25864
+ if (request.scope !== "task" && request.taskId !== void 0) ctx.addIssue({ code: "custom", path: ["taskId"], message: "only a task grant names a task" });
25865
+ });
25866
+ GRANT_STATUSES = ["active", "expired", "revoked"];
25867
+ TaskGrant = external_exports.object({
25868
+ tenantId: TenantId,
25869
+ projectId: ProjectId,
25870
+ id: external_exports.string().min(1),
25871
+ scope: GrantScope,
25872
+ taskId: external_exports.string().nullable(),
25873
+ hosts: external_exports.array(external_exports.string()),
25874
+ secretName: external_exports.string(),
25875
+ attach: GrantAttach,
25876
+ status: external_exports.enum(GRANT_STATUSES),
25877
+ createdBy: external_exports.string(),
25878
+ createdAt: Timestamp,
25879
+ expiresAt: Timestamp,
25880
+ revokedAt: Timestamp.nullable(),
25881
+ uses: external_exports.number().int().nonnegative(),
25882
+ class: external_exports.enum(["grant", "secrets"])
25883
+ }).strict();
25884
+ ResolveRefusal = external_exports.enum(["no_grant", "expired", "revoked", "host_not_granted", "task_mismatch", "egress_denied", "secret_unavailable", "port_not_allowed"]);
25885
+ RevokeGrantRequest = external_exports.object({ reason: external_exports.string().max(200).optional() }).strict();
25886
+ LeakScanRequest = external_exports.object({
25887
+ texts: external_exports.array(external_exports.object({ name: external_exports.string().min(1).max(300), text: external_exports.string().max(2e6) }).strict()).max(200).default([]),
25888
+ /** The workspace's file listing. */
25889
+ files: external_exports.array(external_exports.string().min(1).max(500)).max(2e4).default([])
25890
+ }).strict();
25891
+ LeakFilesRequest = LeakScanRequest.pick({ files: true }).strict();
25892
+ LeakFinding = external_exports.object({
25893
+ kind: external_exports.enum(["minted_value", "credential_pattern", "credential_file"]),
25894
+ rule: external_exports.string(),
25895
+ place: external_exports.string(),
25896
+ line: external_exports.number().int().nullable(),
25897
+ grantId: external_exports.string().nullable()
25898
+ }).strict();
25899
+ LeakScanResult = external_exports.object({ tenantId: TenantId, projectId: ProjectId, clean: external_exports.boolean(), checkedGrants: external_exports.number().int().nonnegative(), findings: external_exports.array(LeakFinding) }).strict();
25900
+ egressPaths = {
25901
+ base: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/egress`,
25902
+ policy: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/policy`,
25903
+ evaluate: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/evaluate`,
25904
+ events: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/events`,
25905
+ grants: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/grants`,
25906
+ grant: (tenantId, projectId, id2) => `${egressPaths.grants(tenantId, projectId)}/${encodeURIComponent(id2)}`,
25907
+ revoke: (tenantId, projectId, id2) => `${egressPaths.grant(tenantId, projectId, id2)}/revoke`,
25908
+ leakScan: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/leak-scan`
25909
+ };
25910
+ }
25911
+ });
25912
+
25040
25913
  // ../contracts/src/index.ts
25041
25914
  var init_src = __esm({
25042
25915
  "../contracts/src/index.ts"() {
@@ -25078,6 +25951,11 @@ var init_src = __esm({
25078
25951
  init_ci();
25079
25952
  init_deploy();
25080
25953
  init_decision();
25954
+ init_claims();
25955
+ init_budgets();
25956
+ init_rules_sync();
25957
+ init_pr_routing();
25958
+ init_egress();
25081
25959
  }
25082
25960
  });
25083
25961
 
@@ -25348,13 +26226,13 @@ var require_directives = __commonJS({
25348
26226
  * @param onError - May be called even if the action was successful
25349
26227
  * @returns `true` on success
25350
26228
  */
25351
- add(line2, onError) {
26229
+ add(line3, onError) {
25352
26230
  if (this.atNextDocument) {
25353
26231
  this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" };
25354
26232
  this.tags = Object.assign({}, _Directives.defaultTags);
25355
26233
  this.atNextDocument = false;
25356
26234
  }
25357
- const parts2 = line2.trim().split(/[ \t]+/);
26235
+ const parts2 = line3.trim().split(/[ \t]+/);
25358
26236
  const name2 = parts2.shift();
25359
26237
  switch (name2) {
25360
26238
  case "%TAG": {
@@ -26951,9 +27829,9 @@ var require_stringifyCollection = __commonJS({
26951
27829
  } else {
26952
27830
  str = lines[0];
26953
27831
  for (let i2 = 1; i2 < lines.length; ++i2) {
26954
- const line2 = lines[i2];
26955
- str += line2 ? `
26956
- ${indent}${line2}` : "\n";
27832
+ const line3 = lines[i2];
27833
+ str += line3 ? `
27834
+ ${indent}${line3}` : "\n";
26957
27835
  }
26958
27836
  }
26959
27837
  if (comment) {
@@ -27011,7 +27889,7 @@ ${indent}${line2}` : "\n";
27011
27889
  str += ",";
27012
27890
  } else if (ctx.options.trailingComma) {
27013
27891
  if (ctx.options.lineWidth > 0) {
27014
- reqNewline || (reqNewline = lines.reduce((sum, line2) => sum + line2.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
27892
+ reqNewline || (reqNewline = lines.reduce((sum, line3) => sum + line3.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
27015
27893
  }
27016
27894
  if (reqNewline) {
27017
27895
  str += ",";
@@ -27027,14 +27905,14 @@ ${indent}${line2}` : "\n";
27027
27905
  return start2 + end;
27028
27906
  } else {
27029
27907
  if (!reqNewline) {
27030
- const len = lines.reduce((sum, line2) => sum + line2.length + 2, 2);
27908
+ const len = lines.reduce((sum, line3) => sum + line3.length + 2, 2);
27031
27909
  reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
27032
27910
  }
27033
27911
  if (reqNewline) {
27034
27912
  let str = start2;
27035
- for (const line2 of lines)
27036
- str += line2 ? `
27037
- ${indentStep}${indent}${line2}` : "\n";
27913
+ for (const line3 of lines)
27914
+ str += line3 ? `
27915
+ ${indentStep}${indent}${line3}` : "\n";
27038
27916
  return `${str}
27039
27917
  ${indent}${end}`;
27040
27918
  } else {
@@ -28777,10 +29655,10 @@ var require_errors = __commonJS({
28777
29655
  if (error62.pos[0] === -1)
28778
29656
  return;
28779
29657
  error62.linePos = error62.pos.map((pos) => lc.linePos(pos));
28780
- const { line: line2, col } = error62.linePos[0];
28781
- error62.message += ` at line ${line2}, column ${col}`;
29658
+ const { line: line3, col } = error62.linePos[0];
29659
+ error62.message += ` at line ${line3}, column ${col}`;
28782
29660
  let ci = col - 1;
28783
- let lineStr = src.substring(lc.lineStarts[line2 - 1], lc.lineStarts[line2]).replace(/[\n\r]+$/, "");
29661
+ let lineStr = src.substring(lc.lineStarts[line3 - 1], lc.lineStarts[line3]).replace(/[\n\r]+$/, "");
28784
29662
  if (ci >= 60 && lineStr.length > 80) {
28785
29663
  const trimStart = Math.min(ci - 39, lineStr.length - 79);
28786
29664
  lineStr = "\u2026" + lineStr.substring(trimStart);
@@ -28788,8 +29666,8 @@ var require_errors = __commonJS({
28788
29666
  }
28789
29667
  if (lineStr.length > 80)
28790
29668
  lineStr = lineStr.substring(0, 79) + "\u2026";
28791
- if (line2 > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
28792
- let prev = src.substring(lc.lineStarts[line2 - 2], lc.lineStarts[line2 - 1]);
29669
+ if (line3 > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
29670
+ let prev = src.substring(lc.lineStarts[line3 - 2], lc.lineStarts[line3 - 1]);
28793
29671
  if (prev.length > 80)
28794
29672
  prev = prev.substring(0, 79) + "\u2026\n";
28795
29673
  lineStr = prev + lineStr;
@@ -28797,7 +29675,7 @@ var require_errors = __commonJS({
28797
29675
  if (/[^ ]/.test(lineStr)) {
28798
29676
  let count = 1;
28799
29677
  const end = error62.linePos[1];
28800
- if (end?.line === line2 && end.col > col) {
29678
+ if (end?.line === line3 && end.col > col) {
28801
29679
  count = Math.max(1, Math.min(end.col - col, 80 - ci));
28802
29680
  }
28803
29681
  const pointer = " ".repeat(ci) + "^".repeat(count);
@@ -29746,8 +30624,8 @@ var require_resolve_flow_scalar = __commonJS({
29746
30624
  return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
29747
30625
  }
29748
30626
  function unfoldLines(source) {
29749
- const line2 = /(.*?)\r?\n/sy;
29750
- let match = line2.exec(source);
30627
+ const line3 = /(.*?)\r?\n/sy;
30628
+ let match = line3.exec(source);
29751
30629
  if (!match)
29752
30630
  return source;
29753
30631
  let trimEnd, trimBoth;
@@ -29760,8 +30638,8 @@ var require_resolve_flow_scalar = __commonJS({
29760
30638
  }
29761
30639
  let res = match[1].replace(trimEnd, "");
29762
30640
  let sep3 = " ";
29763
- let pos = line2.lastIndex;
29764
- while (match = line2.exec(source)) {
30641
+ let pos = line3.lastIndex;
30642
+ while (match = line3.exec(source)) {
29765
30643
  const lm = match[1].replace(trimBoth, "");
29766
30644
  if (lm === "") {
29767
30645
  if (sep3 === "\n")
@@ -29772,7 +30650,7 @@ var require_resolve_flow_scalar = __commonJS({
29772
30650
  res += sep3 + lm;
29773
30651
  sep3 = " ";
29774
30652
  }
29775
- pos = line2.lastIndex;
30653
+ pos = line3.lastIndex;
29776
30654
  }
29777
30655
  const last = /[ \t]*(.*)/sy;
29778
30656
  last.lastIndex = pos;
@@ -30902,40 +31780,40 @@ var require_lexer = __commonJS({
30902
31780
  }
30903
31781
  }
30904
31782
  *parseStream() {
30905
- let line2 = this.getLine();
30906
- if (line2 === null)
31783
+ let line3 = this.getLine();
31784
+ if (line3 === null)
30907
31785
  return this.setNext("stream");
30908
- if (line2[0] === cst.BOM) {
31786
+ if (line3[0] === cst.BOM) {
30909
31787
  yield* this.pushCount(1);
30910
- line2 = line2.substring(1);
31788
+ line3 = line3.substring(1);
30911
31789
  }
30912
- if (line2[0] === "%") {
30913
- let dirEnd = line2.length;
30914
- let cs = line2.indexOf("#");
31790
+ if (line3[0] === "%") {
31791
+ let dirEnd = line3.length;
31792
+ let cs = line3.indexOf("#");
30915
31793
  while (cs !== -1) {
30916
- const ch = line2[cs - 1];
31794
+ const ch = line3[cs - 1];
30917
31795
  if (ch === " " || ch === " ") {
30918
31796
  dirEnd = cs - 1;
30919
31797
  break;
30920
31798
  } else {
30921
- cs = line2.indexOf("#", cs + 1);
31799
+ cs = line3.indexOf("#", cs + 1);
30922
31800
  }
30923
31801
  }
30924
31802
  while (true) {
30925
- const ch = line2[dirEnd - 1];
31803
+ const ch = line3[dirEnd - 1];
30926
31804
  if (ch === " " || ch === " ")
30927
31805
  dirEnd -= 1;
30928
31806
  else
30929
31807
  break;
30930
31808
  }
30931
31809
  const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true));
30932
- yield* this.pushCount(line2.length - n);
31810
+ yield* this.pushCount(line3.length - n);
30933
31811
  this.pushNewline();
30934
31812
  return "stream";
30935
31813
  }
30936
31814
  if (this.atLineEnd()) {
30937
31815
  const sp = yield* this.pushSpaces(true);
30938
- yield* this.pushCount(line2.length - sp);
31816
+ yield* this.pushCount(line3.length - sp);
30939
31817
  yield* this.pushNewline();
30940
31818
  return "stream";
30941
31819
  }
@@ -30976,13 +31854,13 @@ var require_lexer = __commonJS({
30976
31854
  }
30977
31855
  *parseDocument() {
30978
31856
  yield* this.pushSpaces(true);
30979
- const line2 = this.getLine();
30980
- if (line2 === null)
31857
+ const line3 = this.getLine();
31858
+ if (line3 === null)
30981
31859
  return this.setNext("doc");
30982
31860
  let n = yield* this.pushIndicators();
30983
- switch (line2[n]) {
31861
+ switch (line3[n]) {
30984
31862
  case "#":
30985
- yield* this.pushCount(line2.length - n);
31863
+ yield* this.pushCount(line3.length - n);
30986
31864
  // fallthrough
30987
31865
  case void 0:
30988
31866
  yield* this.pushNewline();
@@ -31007,7 +31885,7 @@ var require_lexer = __commonJS({
31007
31885
  case ">":
31008
31886
  n += yield* this.parseBlockScalarHeader();
31009
31887
  n += yield* this.pushSpaces(true);
31010
- yield* this.pushCount(line2.length - n);
31888
+ yield* this.pushCount(line3.length - n);
31011
31889
  yield* this.pushNewline();
31012
31890
  return yield* this.parseBlockScalar();
31013
31891
  default:
@@ -31027,11 +31905,11 @@ var require_lexer = __commonJS({
31027
31905
  }
31028
31906
  sp += yield* this.pushSpaces(true);
31029
31907
  } while (nl + sp > 0);
31030
- const line2 = this.getLine();
31031
- if (line2 === null)
31908
+ const line3 = this.getLine();
31909
+ if (line3 === null)
31032
31910
  return this.setNext("flow");
31033
- if (indent !== -1 && indent < this.indentNext && line2[0] !== "#" || indent === 0 && (line2.startsWith("---") || line2.startsWith("...")) && isEmpty(line2[3])) {
31034
- const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line2[0] === "]" || line2[0] === "}");
31911
+ if (indent !== -1 && indent < this.indentNext && line3[0] !== "#" || indent === 0 && (line3.startsWith("---") || line3.startsWith("...")) && isEmpty(line3[3])) {
31912
+ const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line3[0] === "]" || line3[0] === "}");
31035
31913
  if (!atFlowEndMarker) {
31036
31914
  this.flowLevel = 0;
31037
31915
  yield cst.FLOW_END;
@@ -31039,17 +31917,17 @@ var require_lexer = __commonJS({
31039
31917
  }
31040
31918
  }
31041
31919
  let n = 0;
31042
- while (line2[n] === ",") {
31920
+ while (line3[n] === ",") {
31043
31921
  n += yield* this.pushCount(1);
31044
31922
  n += yield* this.pushSpaces(true);
31045
31923
  this.flowKey = false;
31046
31924
  }
31047
31925
  n += yield* this.pushIndicators();
31048
- switch (line2[n]) {
31926
+ switch (line3[n]) {
31049
31927
  case void 0:
31050
31928
  return "flow";
31051
31929
  case "#":
31052
- yield* this.pushCount(line2.length - n);
31930
+ yield* this.pushCount(line3.length - n);
31053
31931
  return "flow";
31054
31932
  case "{":
31055
31933
  case "[":
@@ -31459,12 +32337,12 @@ var require_parser = __commonJS({
31459
32337
  }
31460
32338
  return prev.splice(i2, prev.length);
31461
32339
  }
31462
- function arrayPushArray(target, source) {
32340
+ function arrayPushArray(target2, source) {
31463
32341
  if (source.length < 1e5)
31464
- Array.prototype.push.apply(target, source);
32342
+ Array.prototype.push.apply(target2, source);
31465
32343
  else
31466
32344
  for (let i2 = 0; i2 < source.length; ++i2)
31467
- target.push(source[i2]);
32345
+ target2.push(source[i2]);
31468
32346
  }
31469
32347
  function fixFlowSeqItems(fc) {
31470
32348
  if (fc.start.type === "flow-seq-start") {
@@ -32412,7 +33290,7 @@ var require_dist = __commonJS({
32412
33290
  });
32413
33291
 
32414
33292
  // src/assets.ts
32415
- import { basename as basename5, join as join24, resolve as resolve13 } from "node:path";
33293
+ import { basename as basename5, join as join25, resolve as resolve13 } from "node:path";
32416
33294
  import { fileURLToPath } from "node:url";
32417
33295
  function packageRoot(env = process.env) {
32418
33296
  const override = env["MVRX_PACKAGE_ROOT"];
@@ -32421,16 +33299,16 @@ function packageRoot(env = process.env) {
32421
33299
  }
32422
33300
  function assetDir(env = process.env) {
32423
33301
  const override = env["MVRX_ASSET_DIR"];
32424
- return override !== void 0 && override !== "" ? override : join24(packageRoot(env), "assets");
33302
+ return override !== void 0 && override !== "" ? override : join25(packageRoot(env), "assets");
32425
33303
  }
32426
33304
  function assetFile(name2) {
32427
- return join24(assetDir(), basename5(name2));
33305
+ return join25(assetDir(), basename5(name2));
32428
33306
  }
32429
33307
  var VERSION;
32430
33308
  var init_assets = __esm({
32431
33309
  "src/assets.ts"() {
32432
33310
  "use strict";
32433
- VERSION = true ? "0.1.133" : "0.0.0-dev";
33311
+ VERSION = true ? "0.1.135" : "0.0.0-dev";
32434
33312
  }
32435
33313
  });
32436
33314
 
@@ -32792,8 +33670,8 @@ var init_scalar = __esm({
32792
33670
  });
32793
33671
 
32794
33672
  // ../../node_modules/.pnpm/@bufbuild+protobuf@2.16.0/node_modules/@bufbuild/protobuf/dist/esm/reflect/unsafe.js
32795
- function unsafeIsSetExplicit(target, localName) {
32796
- return Object.prototype.hasOwnProperty.call(target, localName) && target[localName] !== void 0;
33673
+ function unsafeIsSetExplicit(target2, localName) {
33674
+ return Object.prototype.hasOwnProperty.call(target2, localName) && target2[localName] !== void 0;
32797
33675
  }
32798
33676
  function setOwn(obj, key, value) {
32799
33677
  if (key === "__proto__") {
@@ -34236,10 +35114,10 @@ function newField(proto, parentOrFile, reg, oneof, mapEntries) {
34236
35114
  enumerable: true,
34237
35115
  configurable: true
34238
35116
  });
34239
- const label = proto.label;
35117
+ const label2 = proto.label;
34240
35118
  const type = proto.type;
34241
35119
  const jstype = (_c = proto.options) === null || _c === void 0 ? void 0 : _c.jstype;
34242
- if (label === LABEL_REPEATED) {
35120
+ if (label2 === LABEL_REPEATED) {
34243
35121
  const mapEntry = type == TYPE_MESSAGE ? mapEntries === null || mapEntries === void 0 ? void 0 : mapEntries.get(trimLeadingDot(proto.typeName)) : void 0;
34244
35122
  if (mapEntry) {
34245
35123
  field2.fieldKind = "map";
@@ -35888,8 +36766,8 @@ var init_scip_symbol = __esm({
35888
36766
  function occurrenceRange(occurrence) {
35889
36767
  const typed = occurrence.typedRange;
35890
36768
  if (typed.case === "singleLineRange") {
35891
- const { line: line2, startCharacter, endCharacter } = typed.value;
35892
- return checked([line2, startCharacter, line2, endCharacter]);
36769
+ const { line: line3, startCharacter, endCharacter } = typed.value;
36770
+ return checked([line3, startCharacter, line3, endCharacter]);
35893
36771
  }
35894
36772
  if (typed.case === "multiLineRange") {
35895
36773
  const { startLine, startCharacter, endLine, endCharacter } = typed.value;
@@ -35900,8 +36778,8 @@ function occurrenceRange(occurrence) {
35900
36778
  function occurrenceEnclosingRange(occurrence) {
35901
36779
  const typed = occurrence.typedEnclosingRange;
35902
36780
  if (typed.case === "singleLineEnclosingRange") {
35903
- const { line: line2, startCharacter, endCharacter } = typed.value;
35904
- return checked([line2, startCharacter, line2, endCharacter]);
36781
+ const { line: line3, startCharacter, endCharacter } = typed.value;
36782
+ return checked([line3, startCharacter, line3, endCharacter]);
35905
36783
  }
35906
36784
  if (typed.case === "multiLineEnclosingRange") {
35907
36785
  const { startLine, startCharacter, endLine, endCharacter } = typed.value;
@@ -37111,9 +37989,9 @@ async function Module2(moduleArg = {}) {
37111
37989
  }, "loadWebAssemblyModule");
37112
37990
  var mergeLibSymbols = /* @__PURE__ */ __name((exports, libName2) => {
37113
37991
  for (var [sym, exp] of Object.entries(exports)) {
37114
- const setImport = /* @__PURE__ */ __name((target) => {
37115
- if (!isSymbolDefined(target)) {
37116
- wasmImports[target] = exp;
37992
+ const setImport = /* @__PURE__ */ __name((target2) => {
37993
+ if (!isSymbolDefined(target2)) {
37994
+ wasmImports[target2] = exp;
37117
37995
  }
37118
37996
  }, "setImport");
37119
37997
  setImport(sym);
@@ -37999,7 +38877,7 @@ var __defProp2, __name, Edit, SIZE_OF_SHORT, SIZE_OF_INT, SIZE_OF_CURSOR, SIZE_O
37999
38877
  var init_web_tree_sitter = __esm({
38000
38878
  "../../node_modules/.pnpm/web-tree-sitter@0.27.0/node_modules/web-tree-sitter/web-tree-sitter.js"() {
38001
38879
  __defProp2 = Object.defineProperty;
38002
- __name = (target, value) => __defProp2(target, "name", { value, configurable: true });
38880
+ __name = (target2, value) => __defProp2(target2, "name", { value, configurable: true });
38003
38881
  Edit = class {
38004
38882
  static {
38005
38883
  __name(this, "Edit");
@@ -40359,7 +41237,7 @@ var init_treesitter = __esm({
40359
41237
  import { execFile } from "node:child_process";
40360
41238
  import { createHash as createHash6 } from "node:crypto";
40361
41239
  import { lstat, readFile as readFile4 } from "node:fs/promises";
40362
- import { join as join25 } from "node:path";
41240
+ import { join as join26 } from "node:path";
40363
41241
  import { promisify } from "node:util";
40364
41242
  function shardKeyOf(shard) {
40365
41243
  return `sha256:${sha256Hex2(Buffer.from(`${shard.path}\0${shard.contentHash}\0${shard.precision}`, "utf8"))}`;
@@ -40386,7 +41264,7 @@ async function mapLimit(items, limit, task) {
40386
41264
  return results;
40387
41265
  }
40388
41266
  async function hashRegularFile(repoRoot, path) {
40389
- const absolute = join25(repoRoot, path);
41267
+ const absolute = join26(repoRoot, path);
40390
41268
  try {
40391
41269
  const stats = await lstat(absolute);
40392
41270
  if (!stats.isFile()) return null;
@@ -40424,7 +41302,7 @@ async function buildSnapshot(options) {
40424
41302
  const needParse = [...hashes.keys()].filter((path) => !byPath.has(path));
40425
41303
  const parsed = await mapLimit(needParse, 8, async (path) => {
40426
41304
  if (languageForPath(path) === null) return { path, shard: null };
40427
- const bytes = await readFile4(join25(options.repoRoot, path));
41305
+ const bytes = await readFile4(join26(options.repoRoot, path));
40428
41306
  if (bytes.byteLength > MAX_PARSE_BYTES) return { path, shard: null };
40429
41307
  const hash2 = `sha256:${sha256Hex2(bytes)}`;
40430
41308
  if (hash2 !== hashes.get(path)) throw new Error(`${path} changed while the snapshot was being built`);
@@ -40568,10 +41446,7 @@ var init_oidc = __esm({
40568
41446
  }
40569
41447
  });
40570
41448
 
40571
- // ../index-builder/src/context-map.ts
40572
- function stripNoise(text) {
40573
- return text.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ").replace(/"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|`(?:[^`\\]|\\.)*`/g, '""');
40574
- }
41449
+ // ../index-builder/src/rank.ts
40575
41450
  function wordsOf(text) {
40576
41451
  const parts2 = text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[^A-Za-z0-9]+/);
40577
41452
  const out2 = [];
@@ -40586,106 +41461,6 @@ function wordsOf(text) {
40586
41461
  }
40587
41462
  return out2;
40588
41463
  }
40589
- function resolveImport(from, spec) {
40590
- if (!spec.startsWith(".")) return null;
40591
- const parts2 = from.split("/").slice(0, -1);
40592
- for (const piece of spec.split("/")) {
40593
- if (piece === "." || piece === "") continue;
40594
- if (piece === "..") parts2.pop();
40595
- else parts2.push(piece);
40596
- }
40597
- return stripModuleExtension(parts2.join("/"));
40598
- }
40599
- function stripModuleExtension(path) {
40600
- return path.replace(/\.(?:[cm]?[jt]sx?|d\.ts)$/, "");
40601
- }
40602
- function reaches(visible, path) {
40603
- const bare = stripModuleExtension(path);
40604
- return visible.has(bare) || visible.has(path) || bare.endsWith("/index") && visible.has(bare.slice(0, -"/index".length));
40605
- }
40606
- function headerOf(content, symbol2) {
40607
- const lines = content.split("\n");
40608
- const start2 = symbol2.range[0];
40609
- const chunk = [];
40610
- for (let i2 = start2; i2 < Math.min(lines.length, start2 + 4); i2 += 1) {
40611
- const line2 = lines[i2] ?? "";
40612
- chunk.push(line2);
40613
- if (line2.includes("{")) break;
40614
- }
40615
- return chunk.join(" ");
40616
- }
40617
- async function buildRepoIndex(files) {
40618
- const symbols2 = /* @__PURE__ */ new Map();
40619
- const byName = /* @__PURE__ */ new Map();
40620
- const byFile = /* @__PURE__ */ new Map();
40621
- const imports = /* @__PURE__ */ new Map();
40622
- const ranges = /* @__PURE__ */ new Map();
40623
- const headers = /* @__PURE__ */ new Map();
40624
- const paths = [...files.keys()].filter((path) => languageForPath(path) !== null).sort();
40625
- for (const path of paths) {
40626
- const content = files.get(path);
40627
- const shard = await parseDeclarations(path, content);
40628
- if (shard === null) continue;
40629
- const targets = /* @__PURE__ */ new Set();
40630
- for (const match of content.matchAll(/from\s+["']([^"']+)["']/g)) {
40631
- const resolved = resolveImport(path, match[1] ?? "");
40632
- if (resolved !== null) targets.add(resolved);
40633
- }
40634
- imports.set(path, targets);
40635
- for (const symbol2 of shard.symbols) {
40636
- const hash2 = symbol2.symbol.indexOf("#");
40637
- const entry = { id: symbol2.symbol, name: symbol2.name, qualified: symbol2.symbol.slice(hash2 + 1), kind: symbol2.kind, path, line: symbol2.range[0] + 1, parent: symbol2.parent };
40638
- symbols2.set(entry.id, entry);
40639
- ranges.set(entry.id, symbol2);
40640
- byName.set(entry.name, [...byName.get(entry.name) ?? [], entry]);
40641
- byFile.set(path, [...byFile.get(path) ?? [], entry]);
40642
- if (symbol2.kind === "class" || symbol2.kind === "interface") headers.set(entry.id, headerOf(content, symbol2));
40643
- }
40644
- }
40645
- const callees = /* @__PURE__ */ new Map();
40646
- const callers = /* @__PURE__ */ new Map();
40647
- for (const entry of symbols2.values()) {
40648
- if (!CALLABLE.has(entry.kind)) continue;
40649
- const range = ranges.get(entry.id)?.enclosingRange;
40650
- if (range === void 0) continue;
40651
- const text = stripNoise(files.get(entry.path).split("\n").slice(range[0], range[2] + 1).join("\n"));
40652
- const visible = imports.get(entry.path) ?? /* @__PURE__ */ new Set();
40653
- const seen = /* @__PURE__ */ new Set();
40654
- for (const match of text.matchAll(/[A-Za-z_$][\w$]*/g)) {
40655
- const id2 = match[0];
40656
- if (KEYWORDS.has(id2) || seen.has(id2)) continue;
40657
- seen.add(id2);
40658
- for (const target of byName.get(id2) ?? []) {
40659
- if (target.id === entry.id || target.kind === "property" || target.kind === "parameter") continue;
40660
- if (target.path !== entry.path && !reaches(visible, target.path)) continue;
40661
- if (target.id === entry.parent) continue;
40662
- callees.set(entry.id, (callees.get(entry.id) ?? /* @__PURE__ */ new Set()).add(target.id));
40663
- callers.set(target.id, (callers.get(target.id) ?? /* @__PURE__ */ new Set()).add(entry.id));
40664
- }
40665
- }
40666
- }
40667
- const heritage = /* @__PURE__ */ new Map();
40668
- const heirs = /* @__PURE__ */ new Map();
40669
- const resolve16 = (from, name2) => {
40670
- const visible = imports.get(from.path) ?? /* @__PURE__ */ new Set();
40671
- return (byName.get(baseName(name2)) ?? []).find((candidate) => candidate.path === from.path || reaches(visible, candidate.path));
40672
- };
40673
- for (const [id2, header] of headers) {
40674
- const entry = symbols2.get(id2);
40675
- const stem2 = header.slice(header.indexOf(entry.name) + entry.name.length).replace(/^<[^>]*>/, "").replace(/\{.*$/, "");
40676
- const ext = /\bextends\s+([A-Za-z_$][\w$.]*)/.exec(stem2)?.[1];
40677
- const impl = /\bimplements\s+(.+)$/.exec(stem2)?.[1];
40678
- const extendsIds = ext === void 0 ? [] : [resolve16(entry, ext)?.id].filter((value) => value !== void 0);
40679
- const implementsIds = (impl === void 0 ? [] : impl.split(/,(?![^<]*>)/).map((part) => resolve16(entry, part.trim()))).filter((value) => value !== void 0).map((value) => value.id);
40680
- if (extendsIds.length === 0 && implementsIds.length === 0) continue;
40681
- heritage.set(id2, { extends: extendsIds, implements: implementsIds });
40682
- for (const target of extendsIds) heirs.set(target, { extendedBy: [...heirs.get(target)?.extendedBy ?? [], id2], implementedBy: heirs.get(target)?.implementedBy ?? [] });
40683
- for (const target of implementsIds) heirs.set(target, { extendedBy: heirs.get(target)?.extendedBy ?? [], implementedBy: [...heirs.get(target)?.implementedBy ?? [], id2] });
40684
- }
40685
- const words = /* @__PURE__ */ new Map();
40686
- for (const entry of symbols2.values()) for (const word of new Set(wordsOf(entry.name))) words.set(word, (words.get(word) ?? 0) + 1);
40687
- return { files: paths.length, symbols: symbols2, byName, byFile, callees, callers, heritage, heirs, imports, words, wordDocs: symbols2.size };
40688
- }
40689
41464
  function idf(index, word) {
40690
41465
  return Math.log(index.wordDocs / (1 + (index.words.get(word) ?? 0)));
40691
41466
  }
@@ -40739,8 +41514,8 @@ function pageRank(index, personal) {
40739
41514
  for (const [from, targets] of index.callees) {
40740
41515
  const a = index.symbols.get(from)?.path;
40741
41516
  if (a === void 0) continue;
40742
- for (const target of targets) {
40743
- const b = index.symbols.get(target)?.path;
41517
+ for (const target2 of targets) {
41518
+ const b = index.symbols.get(target2)?.path;
40744
41519
  if (b !== void 0) link(a, b, 1);
40745
41520
  }
40746
41521
  }
@@ -40770,14 +41545,14 @@ function callerLines(index, seed) {
40770
41545
  for (let depth = 1; depth <= 3 && frontier.length > 0 && lines.length < 12; depth += 1) {
40771
41546
  const next = [];
40772
41547
  for (const id2 of frontier) {
40773
- const target = index.symbols.get(id2);
41548
+ const target2 = index.symbols.get(id2);
40774
41549
  const callers = [...index.callers.get(id2) ?? []].filter((caller) => !seen.has(caller)).map((caller) => index.symbols.get(caller)).sort((a, b) => a.path.localeCompare(b.path) || a.line - b.line);
40775
41550
  for (const caller of callers.slice(0, 8)) {
40776
41551
  seen.add(caller.id);
40777
41552
  next.push(caller.id);
40778
- if (lines.length < 12) lines.push(`${" ".repeat(depth)}${caller.qualified} ${caller.path}:${caller.line} -> ${target.qualified}`);
41553
+ if (lines.length < 12) lines.push(`${" ".repeat(depth)}${caller.qualified} ${caller.path}:${caller.line} -> ${target2.qualified}`);
40779
41554
  }
40780
- if (callers.length > 8) lines.push(`${" ".repeat(depth)}(+${callers.length - 8} more callers of ${target.qualified})`);
41555
+ if (callers.length > 8) lines.push(`${" ".repeat(depth)}(+${callers.length - 8} more callers of ${target2.qualified})`);
40781
41556
  }
40782
41557
  frontier = next;
40783
41558
  }
@@ -40863,9 +41638,9 @@ function buildCodeMap(index, task, options = DEFAULT_MAP_OPTIONS) {
40863
41638
  const top = ordered[0]?.[1] ?? 0;
40864
41639
  for (const [path, mass] of ordered) {
40865
41640
  if (mass <= 0 || mass < top * 0.05 || related.length >= MAX_RELATED_FILES) break;
40866
- const line2 = outline(index, path);
40867
- if (!fits(["", taskFree ? "Central files:" : "Related files:", ...related, line2])) break;
40868
- related.push(line2);
41641
+ const line3 = outline(index, path);
41642
+ if (!fits(["", taskFree ? "Central files:" : "Related files:", ...related, line3])) break;
41643
+ related.push(line3);
40869
41644
  }
40870
41645
  if (related.length > 0) lines.push("", taskFree ? "Central files:" : "Related files:", ...related);
40871
41646
  const text = `${lines.join("\n")}
@@ -40883,44 +41658,167 @@ function buildCodeMapDigest(input2) {
40883
41658
  seeds: map2.seeds
40884
41659
  };
40885
41660
  }
40886
- var KEYWORDS, CALLABLE, SEEDABLE, MIN_WORD_IDF, MIN_SOFT_SCORE, MAX_RELATED_FILES, STOP, baseName, DEFAULT_MAP_OPTIONS, estimateTokens, where;
40887
- var init_context_map = __esm({
40888
- "../index-builder/src/context-map.ts"() {
41661
+ function serializeRepoIndex(index) {
41662
+ const position = /* @__PURE__ */ new Map();
41663
+ for (const id2 of index.symbols.keys()) position.set(id2, position.size);
41664
+ const at = (id2) => position.get(id2) ?? -1;
41665
+ const symbols2 = [...index.symbols.values()].map((s) => [s.name, s.qualified, s.kind, s.path, s.line, s.parent === void 0 ? -1 : at(s.parent)]);
41666
+ const callees = [...index.callees].map(([from, to]) => [at(from), ...[...to].map(at)]);
41667
+ const heritage = [...index.heritage].map(([id2, h]) => ({ id: at(id2), extends: h.extends.map(at), implements: h.implements.map(at) }));
41668
+ return { schemaVersion: "1", files: index.files, wordDocs: index.wordDocs, symbols: symbols2, callees, heritage, paths: [...index.imports.keys()], words: [...index.words] };
41669
+ }
41670
+ var SEEDABLE, MIN_WORD_IDF, MIN_SOFT_SCORE, MAX_RELATED_FILES, STOP, DEFAULT_MAP_OPTIONS, estimateTokens, where;
41671
+ var init_rank = __esm({
41672
+ "../index-builder/src/rank.ts"() {
40889
41673
  "use strict";
40890
41674
  init_src();
40891
- init_languages();
40892
- init_treesitter();
40893
- KEYWORDS = /* @__PURE__ */ new Set(["const", "let", "var", "function", "return", "if", "else", "new", "this", "class", "extends", "implements", "import", "export", "from", "type", "interface", "async", "await", "string", "number", "boolean", "void", "null", "undefined", "true", "false", "private", "protected", "public", "readonly", "static", "abstract", "super", "typeof", "for", "of", "in", "while", "throw", "try", "catch", "as"]);
40894
- CALLABLE = /* @__PURE__ */ new Set(["function", "method", "constant", "variable"]);
40895
41675
  SEEDABLE = /* @__PURE__ */ new Set(["function", "method", "class", "interface", "type", "constant"]);
40896
41676
  MIN_WORD_IDF = 2.5;
40897
41677
  MIN_SOFT_SCORE = 3.5;
40898
41678
  MAX_RELATED_FILES = 14;
40899
41679
  STOP = /* @__PURE__ */ new Set(["the", "a", "an", "of", "to", "in", "is", "are", "and", "or", "for", "that", "this", "it", "its", "by", "be", "on", "as", "at", "with", "from", "which", "what", "when", "where", "who", "how", "do", "does", "not", "any", "each", "every", "all", "also", "than", "then", "so", "if", "into", "file", "path", "answer", "end", "final", "message", "line", "start", "follow", "separate", "comma", "give", "name", "modify", "files", "repository", "source", "code", "change", "update", "make", "use", "uses", "used", "other", "only", "about", "will", "can", "should", "need", "needs", "happen", "merely", "share", "same", "return", "returns", "pass", "passes"]);
40900
- baseName = (name2) => name2.replace(/<.*$/, "").split(".").pop() ?? name2;
40901
41680
  DEFAULT_MAP_OPTIONS = { maxTokens: 3400, maxSeeds: 6 };
40902
41681
  estimateTokens = estimateCodeMapTokens;
40903
41682
  where = (symbol2) => `${symbol2.qualified} ${symbol2.path}:${symbol2.line}`;
40904
41683
  }
40905
41684
  });
40906
41685
 
41686
+ // ../index-builder/src/context-map.ts
41687
+ function stripNoise(text) {
41688
+ return text.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ").replace(/"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|`(?:[^`\\]|\\.)*`/g, '""');
41689
+ }
41690
+ function resolveImport(from, spec) {
41691
+ if (!spec.startsWith(".")) return null;
41692
+ const parts2 = from.split("/").slice(0, -1);
41693
+ for (const piece of spec.split("/")) {
41694
+ if (piece === "." || piece === "") continue;
41695
+ if (piece === "..") parts2.pop();
41696
+ else parts2.push(piece);
41697
+ }
41698
+ return stripModuleExtension(parts2.join("/"));
41699
+ }
41700
+ function stripModuleExtension(path) {
41701
+ return path.replace(/\.(?:[cm]?[jt]sx?|d\.ts)$/, "");
41702
+ }
41703
+ function reaches(visible, path) {
41704
+ const bare = stripModuleExtension(path);
41705
+ return visible.has(bare) || visible.has(path) || bare.endsWith("/index") && visible.has(bare.slice(0, -"/index".length));
41706
+ }
41707
+ function headerOf(content, symbol2) {
41708
+ const lines = content.split("\n");
41709
+ const start2 = symbol2.range[0];
41710
+ const chunk = [];
41711
+ for (let i2 = start2; i2 < Math.min(lines.length, start2 + 4); i2 += 1) {
41712
+ const line3 = lines[i2] ?? "";
41713
+ chunk.push(line3);
41714
+ if (line3.includes("{")) break;
41715
+ }
41716
+ return chunk.join(" ");
41717
+ }
41718
+ async function buildRepoIndex(files) {
41719
+ const symbols2 = /* @__PURE__ */ new Map();
41720
+ const byName = /* @__PURE__ */ new Map();
41721
+ const byFile = /* @__PURE__ */ new Map();
41722
+ const imports = /* @__PURE__ */ new Map();
41723
+ const ranges = /* @__PURE__ */ new Map();
41724
+ const headers = /* @__PURE__ */ new Map();
41725
+ const paths = [...files.keys()].filter((path) => languageForPath(path) !== null).sort();
41726
+ for (const path of paths) {
41727
+ const content = files.get(path);
41728
+ const shard = await parseDeclarations(path, content);
41729
+ if (shard === null) continue;
41730
+ const targets = /* @__PURE__ */ new Set();
41731
+ for (const match of content.matchAll(/from\s+["']([^"']+)["']/g)) {
41732
+ const resolved = resolveImport(path, match[1] ?? "");
41733
+ if (resolved !== null) targets.add(resolved);
41734
+ }
41735
+ imports.set(path, targets);
41736
+ for (const symbol2 of shard.symbols) {
41737
+ const hash2 = symbol2.symbol.indexOf("#");
41738
+ const entry = { id: symbol2.symbol, name: symbol2.name, qualified: symbol2.symbol.slice(hash2 + 1), kind: symbol2.kind, path, line: symbol2.range[0] + 1, parent: symbol2.parent };
41739
+ symbols2.set(entry.id, entry);
41740
+ ranges.set(entry.id, symbol2);
41741
+ byName.set(entry.name, [...byName.get(entry.name) ?? [], entry]);
41742
+ byFile.set(path, [...byFile.get(path) ?? [], entry]);
41743
+ if (symbol2.kind === "class" || symbol2.kind === "interface") headers.set(entry.id, headerOf(content, symbol2));
41744
+ }
41745
+ }
41746
+ const callees = /* @__PURE__ */ new Map();
41747
+ const callers = /* @__PURE__ */ new Map();
41748
+ for (const entry of symbols2.values()) {
41749
+ if (!CALLABLE.has(entry.kind)) continue;
41750
+ const range = ranges.get(entry.id)?.enclosingRange;
41751
+ if (range === void 0) continue;
41752
+ const text = stripNoise(files.get(entry.path).split("\n").slice(range[0], range[2] + 1).join("\n"));
41753
+ const visible = imports.get(entry.path) ?? /* @__PURE__ */ new Set();
41754
+ const seen = /* @__PURE__ */ new Set();
41755
+ for (const match of text.matchAll(/[A-Za-z_$][\w$]*/g)) {
41756
+ const id2 = match[0];
41757
+ if (KEYWORDS.has(id2) || seen.has(id2)) continue;
41758
+ seen.add(id2);
41759
+ for (const target2 of byName.get(id2) ?? []) {
41760
+ if (target2.id === entry.id || target2.kind === "property" || target2.kind === "parameter") continue;
41761
+ if (target2.path !== entry.path && !reaches(visible, target2.path)) continue;
41762
+ if (target2.id === entry.parent) continue;
41763
+ callees.set(entry.id, (callees.get(entry.id) ?? /* @__PURE__ */ new Set()).add(target2.id));
41764
+ callers.set(target2.id, (callers.get(target2.id) ?? /* @__PURE__ */ new Set()).add(entry.id));
41765
+ }
41766
+ }
41767
+ }
41768
+ const heritage = /* @__PURE__ */ new Map();
41769
+ const heirs = /* @__PURE__ */ new Map();
41770
+ const resolve16 = (from, name2) => {
41771
+ const visible = imports.get(from.path) ?? /* @__PURE__ */ new Set();
41772
+ return (byName.get(baseName(name2)) ?? []).find((candidate) => candidate.path === from.path || reaches(visible, candidate.path));
41773
+ };
41774
+ for (const [id2, header] of headers) {
41775
+ const entry = symbols2.get(id2);
41776
+ const stem2 = header.slice(header.indexOf(entry.name) + entry.name.length).replace(/^<[^>]*>/, "").replace(/\{.*$/, "");
41777
+ const ext = /\bextends\s+([A-Za-z_$][\w$.]*)/.exec(stem2)?.[1];
41778
+ const impl = /\bimplements\s+(.+)$/.exec(stem2)?.[1];
41779
+ const extendsIds = ext === void 0 ? [] : [resolve16(entry, ext)?.id].filter((value) => value !== void 0);
41780
+ const implementsIds = (impl === void 0 ? [] : impl.split(/,(?![^<]*>)/).map((part) => resolve16(entry, part.trim()))).filter((value) => value !== void 0).map((value) => value.id);
41781
+ if (extendsIds.length === 0 && implementsIds.length === 0) continue;
41782
+ heritage.set(id2, { extends: extendsIds, implements: implementsIds });
41783
+ for (const target2 of extendsIds) heirs.set(target2, { extendedBy: [...heirs.get(target2)?.extendedBy ?? [], id2], implementedBy: heirs.get(target2)?.implementedBy ?? [] });
41784
+ for (const target2 of implementsIds) heirs.set(target2, { extendedBy: heirs.get(target2)?.extendedBy ?? [], implementedBy: [...heirs.get(target2)?.implementedBy ?? [], id2] });
41785
+ }
41786
+ const words = /* @__PURE__ */ new Map();
41787
+ for (const entry of symbols2.values()) for (const word of new Set(wordsOf(entry.name))) words.set(word, (words.get(word) ?? 0) + 1);
41788
+ return { files: paths.length, symbols: symbols2, byName, byFile, callees, callers, heritage, heirs, imports, words, wordDocs: symbols2.size };
41789
+ }
41790
+ var KEYWORDS, CALLABLE, baseName;
41791
+ var init_context_map = __esm({
41792
+ "../index-builder/src/context-map.ts"() {
41793
+ "use strict";
41794
+ init_src();
41795
+ init_languages();
41796
+ init_treesitter();
41797
+ init_rank();
41798
+ init_rank();
41799
+ KEYWORDS = /* @__PURE__ */ new Set(["const", "let", "var", "function", "return", "if", "else", "new", "this", "class", "extends", "implements", "import", "export", "from", "type", "interface", "async", "await", "string", "number", "boolean", "void", "null", "undefined", "true", "false", "private", "protected", "public", "readonly", "static", "abstract", "super", "typeof", "for", "of", "in", "while", "throw", "try", "catch", "as"]);
41800
+ CALLABLE = /* @__PURE__ */ new Set(["function", "method", "constant", "variable"]);
41801
+ baseName = (name2) => name2.replace(/<.*$/, "").split(".").pop() ?? name2;
41802
+ }
41803
+ });
41804
+
40907
41805
  // ../index-builder/src/context-map-build.ts
40908
41806
  import { execFile as execFile2 } from "node:child_process";
40909
41807
  import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
40910
- import { dirname as dirname9, join as join26 } from "node:path";
41808
+ import { dirname as dirname10, join as join27 } from "node:path";
40911
41809
  import { promisify as promisify2 } from "node:util";
40912
- async function git3(root, args2) {
41810
+ async function git5(root, args2) {
40913
41811
  const { stdout } = await execFileAsync2("git", args2, { cwd: root, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
40914
41812
  return stdout.trim();
40915
41813
  }
40916
- async function buildDigestForCheckout(options) {
41814
+ async function buildArtifactsForCheckout(options) {
40917
41815
  const tracked = (await execFileAsync2("git", ["ls-files", "-z"], { cwd: options.root, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 })).stdout.split("\0").filter((path) => path !== "");
40918
41816
  const files = /* @__PURE__ */ new Map();
40919
41817
  let uncovered = 0;
40920
41818
  for (const path of tracked) {
40921
41819
  if (languageForPath(path) === null) continue;
40922
41820
  try {
40923
- const bytes = await readFile5(join26(options.root, path));
41821
+ const bytes = await readFile5(join27(options.root, path));
40924
41822
  if (bytes.byteLength > MAX_PARSE_BYTES) {
40925
41823
  uncovered += 1;
40926
41824
  continue;
@@ -40931,20 +41829,28 @@ async function buildDigestForCheckout(options) {
40931
41829
  }
40932
41830
  }
40933
41831
  const index = await buildRepoIndex(files);
40934
- const commitSha = await git3(options.root, ["rev-parse", "HEAD"]);
41832
+ const commitSha = await git5(options.root, ["rev-parse", "HEAD"]);
40935
41833
  let branch;
40936
41834
  try {
40937
- branch = await git3(options.root, ["symbolic-ref", "--short", "-q", "HEAD"]) || void 0;
41835
+ branch = await git5(options.root, ["symbolic-ref", "--short", "-q", "HEAD"]) || void 0;
40938
41836
  } catch {
40939
41837
  branch = void 0;
40940
41838
  }
40941
- return CodeMapDigest.parse(
40942
- buildCodeMapDigest({ index, commitSha, ...branch === void 0 ? {} : { branch }, uncovered, builtAt: options.builtAt ?? (/* @__PURE__ */ new Date()).toISOString(), ...options.task === void 0 ? {} : { task: options.task } })
40943
- );
41839
+ const builtAt = options.builtAt ?? (/* @__PURE__ */ new Date()).toISOString();
41840
+ const digest = CodeMapDigest.parse(buildCodeMapDigest({ index, commitSha, ...branch === void 0 ? {} : { branch }, uncovered, builtAt, ...options.task === void 0 ? {} : { task: options.task } }));
41841
+ const indexFile = { schemaVersion: "1", revision: { commitSha, ...branch === void 0 ? {} : { branch } }, builtAt, uncovered, index: serializeRepoIndex(index) };
41842
+ return { digest, indexFile };
41843
+ }
41844
+ async function writeIndexFile(root, indexFile) {
41845
+ const path = join27(root, CODE_INDEX_PATH);
41846
+ await mkdir3(dirname10(path), { recursive: true });
41847
+ await writeFile3(path, `${JSON.stringify(indexFile)}
41848
+ `);
41849
+ return path;
40944
41850
  }
40945
41851
  async function writeDigest(root, digest, out2) {
40946
- const path = out2 ?? join26(root, CODE_MAP_DIGEST_PATH);
40947
- await mkdir3(dirname9(path), { recursive: true });
41852
+ const path = out2 ?? join27(root, CODE_MAP_DIGEST_PATH);
41853
+ await mkdir3(dirname10(path), { recursive: true });
40948
41854
  await writeFile3(path, `${JSON.stringify(digest)}
40949
41855
  `);
40950
41856
  return path;
@@ -40964,10 +41870,10 @@ var init_context_map_build = __esm({
40964
41870
  // ../index-builder/src/run.ts
40965
41871
  import { execFile as execFile3 } from "node:child_process";
40966
41872
  import { mkdir as mkdir4, readdir as readdir4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
40967
- import { join as join27, resolve as resolve14 } from "node:path";
41873
+ import { join as join28, resolve as resolve14 } from "node:path";
40968
41874
  import { parseArgs as parseArgs2 } from "node:util";
40969
41875
  import { promisify as promisify3 } from "node:util";
40970
- async function git4(root, args2) {
41876
+ async function git6(root, args2) {
40971
41877
  const { stdout } = await execFileAsync3("git", args2, { cwd: root, encoding: "utf8" });
40972
41878
  return stdout.trim();
40973
41879
  }
@@ -40978,7 +41884,7 @@ function parseRepository(value) {
40978
41884
  }
40979
41885
  async function repositoryFromRemote(root) {
40980
41886
  try {
40981
- const url2 = await git4(root, ["remote", "get-url", "origin"]);
41887
+ const url2 = await git6(root, ["remote", "get-url", "origin"]);
40982
41888
  return /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/.exec(url2)?.slice(1, 3).join("/");
40983
41889
  } catch {
40984
41890
  return void 0;
@@ -40990,7 +41896,7 @@ async function snapshotFromArgs(values, env) {
40990
41896
  if (projectId !== void 0 && !ProjectId.safeParse(projectId).success) throw new UsageError2(`--project must be a project identifier, got "${projectId}"`);
40991
41897
  const repoText = projectId !== void 0 ? typeof values["repo"] === "string" ? values["repo"] : void 0 : (typeof values["repo"] === "string" ? values["repo"] : void 0) ?? env["GITHUB_REPOSITORY"] ?? await repositoryFromRemote(root);
40992
41898
  if (!repoText && projectId === void 0) throw new UsageError2("cannot determine the repository; pass --project ID, or --repo owner/name");
40993
- const commit = (typeof values["commit"] === "string" ? values["commit"] : void 0) ?? env["GITHUB_SHA"] ?? await git4(root, ["rev-parse", "HEAD"]);
41899
+ const commit = (typeof values["commit"] === "string" ? values["commit"] : void 0) ?? env["GITHUB_SHA"] ?? await git6(root, ["rev-parse", "HEAD"]);
40994
41900
  const branch = typeof values["branch"] === "string" ? values["branch"] : void 0;
40995
41901
  const tenant = (typeof values["tenant"] === "string" ? values["tenant"] : void 0) ?? env["HARNESS_TENANT"];
40996
41902
  if (!tenant) throw new UsageError2("missing --tenant (or HARNESS_TENANT)");
@@ -41019,9 +41925,10 @@ async function runCli2(argv, env, out2 = console.log, err2 = console.error) {
41019
41925
  }
41020
41926
  if (command === "map") {
41021
41927
  const root = resolve14(values.root ?? process.cwd());
41022
- const digest = await buildDigestForCheckout({ root, ...values.task === void 0 ? {} : { task: values.task } });
41928
+ const { digest, indexFile } = await buildArtifactsForCheckout({ root, ...values.task === void 0 ? {} : { task: values.task } });
41023
41929
  const path = await writeDigest(root, digest, values.out === void 0 ? void 0 : resolve14(values.out));
41024
- out2(`wrote the code map digest for ${digest.revision.commitSha.slice(0, 12)} (${digest.coverage.files} files, ${digest.coverage.symbols} symbols, ${digest.lines.length} lines) to ${path}`);
41930
+ const indexPath = values.out === void 0 ? await writeIndexFile(root, indexFile) : null;
41931
+ out2(`wrote the code map digest for ${digest.revision.commitSha.slice(0, 12)} (${digest.coverage.files} files, ${digest.coverage.symbols} symbols, ${digest.lines.length} lines) to ${path}${indexPath === null ? "" : `, and the ranking index to ${indexPath}`}`);
41025
41932
  return 0;
41026
41933
  }
41027
41934
  if (command === "upload") {
@@ -41047,13 +41954,13 @@ ${USAGE}`);
41047
41954
  }
41048
41955
  }
41049
41956
  async function writeSnapshot(dir, built) {
41050
- await mkdir4(join27(dir, "shards"), { recursive: true });
41051
- for (const name2 of await readdir4(join27(dir, "shards"))) await rm2(join27(dir, "shards", name2));
41052
- await writeFile4(join27(dir, "manifest.json"), JSON.stringify(built.manifest, null, 2));
41053
- await writeFile4(join27(dir, "uncovered.json"), JSON.stringify(built.uncovered, null, 2));
41957
+ await mkdir4(join28(dir, "shards"), { recursive: true });
41958
+ for (const name2 of await readdir4(join28(dir, "shards"))) await rm2(join28(dir, "shards", name2));
41959
+ await writeFile4(join28(dir, "manifest.json"), JSON.stringify(built.manifest, null, 2));
41960
+ await writeFile4(join28(dir, "uncovered.json"), JSON.stringify(built.uncovered, null, 2));
41054
41961
  for (const shard of built.shards) {
41055
41962
  const name2 = shardKeyOf(shard).slice("sha256:".length);
41056
- await writeFile4(join27(dir, "shards", `${name2}.json`), JSON.stringify(shard));
41963
+ await writeFile4(join28(dir, "shards", `${name2}.json`), JSON.stringify(shard));
41057
41964
  }
41058
41965
  }
41059
41966
  var execFileAsync3, USAGE, UsageError2, OPTIONS;
@@ -41122,17 +42029,17 @@ var init_chunk_Br0eD_fh = __esm({
41122
42029
  __hasOwnProp2 = Object.prototype.hasOwnProperty;
41123
42030
  __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
41124
42031
  __exportAll = (all, symbols2) => {
41125
- let target = {};
42032
+ let target2 = {};
41126
42033
  for (var name2 in all) {
41127
- __defProp3(target, name2, {
42034
+ __defProp3(target2, name2, {
41128
42035
  get: all[name2],
41129
42036
  enumerable: true
41130
42037
  });
41131
42038
  }
41132
42039
  if (symbols2) {
41133
- __defProp3(target, Symbol.toStringTag, { value: "Module" });
42040
+ __defProp3(target2, Symbol.toStringTag, { value: "Module" });
41134
42041
  }
41135
- return target;
42042
+ return target2;
41136
42043
  };
41137
42044
  __copyProps2 = (to, from, except, desc) => {
41138
42045
  if (from && typeof from === "object" || typeof from === "function") {
@@ -41148,10 +42055,10 @@ var init_chunk_Br0eD_fh = __esm({
41148
42055
  }
41149
42056
  return to;
41150
42057
  };
41151
- __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", {
42058
+ __toESM2 = (mod, isNodeMode, target2) => (target2 = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(isNodeMode || !mod || !mod.__esModule ? __defProp3(target2, "default", {
41152
42059
  value: mod,
41153
42060
  enumerable: true
41154
- }) : target, mod));
42061
+ }) : target2, mod));
41155
42062
  }
41156
42063
  });
41157
42064
 
@@ -44815,8 +45722,8 @@ function manualInputRequiredValue(decoded) {
44815
45722
  ...decoded.requestState !== void 0 && { requestState: decoded.requestState }
44816
45723
  };
44817
45724
  }
44818
- function deserializeMessage(line2) {
44819
- return JSONRPCMessageSchema.parse(JSON.parse(line2));
45725
+ function deserializeMessage(line3) {
45726
+ return JSONRPCMessageSchema.parse(JSON.parse(line3));
44820
45727
  }
44821
45728
  function serializeMessage(message) {
44822
45729
  return JSON.stringify(message) + "\n";
@@ -46755,10 +47662,10 @@ var init_src_CX2iR2pK = __esm({
46755
47662
  while (this._buffer) {
46756
47663
  const index = this._buffer.indexOf("\n");
46757
47664
  if (index === -1) return null;
46758
- const line2 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
47665
+ const line3 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
46759
47666
  this._buffer = this._buffer.subarray(index + 1);
46760
47667
  try {
46761
- return deserializeMessage(line2);
47668
+ return deserializeMessage(line3);
46762
47669
  } catch (error62) {
46763
47670
  if (error62 instanceof SyntaxError) continue;
46764
47671
  throw error62;
@@ -46981,7 +47888,7 @@ var init_ajvProvider_CEoC_sr = __esm({
46981
47888
  }
46982
47889
  };
46983
47890
  exports.ValueScopeName = ValueScopeName;
46984
- const line2 = (0, code_1._)`\n`;
47891
+ const line3 = (0, code_1._)`\n`;
46985
47892
  var ValueScope = class extends Scope {
46986
47893
  constructor(opts) {
46987
47894
  super(opts);
@@ -46989,7 +47896,7 @@ var init_ajvProvider_CEoC_sr = __esm({
46989
47896
  this._scope = opts.scope;
46990
47897
  this.opts = {
46991
47898
  ...opts,
46992
- _n: opts.lines ? line2 : code_1.nil
47899
+ _n: opts.lines ? line3 : code_1.nil
46993
47900
  };
46994
47901
  }
46995
47902
  get() {
@@ -47208,9 +48115,9 @@ var init_ajvProvider_CEoC_sr = __esm({
47208
48115
  }
47209
48116
  };
47210
48117
  var Label2 = class extends Node2 {
47211
- constructor(label) {
48118
+ constructor(label2) {
47212
48119
  super();
47213
- this.label = label;
48120
+ this.label = label2;
47214
48121
  this.names = {};
47215
48122
  }
47216
48123
  render({ _n }) {
@@ -47218,9 +48125,9 @@ var init_ajvProvider_CEoC_sr = __esm({
47218
48125
  }
47219
48126
  };
47220
48127
  var Break = class extends Node2 {
47221
- constructor(label) {
48128
+ constructor(label2) {
47222
48129
  super();
47223
- this.label = label;
48130
+ this.label = label2;
47224
48131
  this.names = {};
47225
48132
  }
47226
48133
  render({ _n }) {
@@ -47585,11 +48492,11 @@ var init_ajvProvider_CEoC_sr = __esm({
47585
48492
  endFor() {
47586
48493
  return this._endBlockNode(For);
47587
48494
  }
47588
- label(label) {
47589
- return this._leafNode(new Label2(label));
48495
+ label(label2) {
48496
+ return this._leafNode(new Label2(label2));
47590
48497
  }
47591
- break(label) {
47592
- return this._leafNode(new Break(label));
48498
+ break(label2) {
48499
+ return this._leafNode(new Break(label2));
47593
48500
  }
47594
48501
  return(value) {
47595
48502
  const node2 = new Return();
@@ -49357,7 +50264,7 @@ var init_ajvProvider_CEoC_sr = __esm({
49357
50264
  }
49358
50265
  }
49359
50266
  exports.compileSchema = compileSchema;
49360
- function resolveRef2(root, baseId, ref) {
50267
+ function resolveRef3(root, baseId, ref) {
49361
50268
  var _a3;
49362
50269
  ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
49363
50270
  const schOrFunc = root.refs[ref];
@@ -49376,7 +50283,7 @@ var init_ajvProvider_CEoC_sr = __esm({
49376
50283
  if (_sch === void 0) return;
49377
50284
  return root.refs[ref] = inlineOrCompile.call(this, _sch);
49378
50285
  }
49379
- exports.resolveRef = resolveRef2;
50286
+ exports.resolveRef = resolveRef3;
49380
50287
  function inlineOrCompile(sch) {
49381
50288
  if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs)) return sch.schema;
49382
50289
  return sch.validate ? sch : compileSchema.call(this, sch);
@@ -49845,49 +50752,49 @@ var init_ajvProvider_CEoC_sr = __esm({
49845
50752
  return serialize(resolved, schemelessOptions);
49846
50753
  }
49847
50754
  function resolveComponent(base4, relative4, options, skipNormalization) {
49848
- const target = {};
50755
+ const target2 = {};
49849
50756
  if (!skipNormalization) {
49850
50757
  base4 = parse5(serialize(base4, options), options);
49851
50758
  relative4 = parse5(serialize(relative4, options), options);
49852
50759
  }
49853
50760
  options = options || {};
49854
50761
  if (!options.tolerant && relative4.scheme) {
49855
- target.scheme = relative4.scheme;
49856
- target.userinfo = relative4.userinfo;
49857
- target.host = relative4.host;
49858
- target.port = relative4.port;
49859
- target.path = removeDotSegments(relative4.path || "");
49860
- target.query = relative4.query;
50762
+ target2.scheme = relative4.scheme;
50763
+ target2.userinfo = relative4.userinfo;
50764
+ target2.host = relative4.host;
50765
+ target2.port = relative4.port;
50766
+ target2.path = removeDotSegments(relative4.path || "");
50767
+ target2.query = relative4.query;
49861
50768
  } else {
49862
50769
  if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
49863
- target.userinfo = relative4.userinfo;
49864
- target.host = relative4.host;
49865
- target.port = relative4.port;
49866
- target.path = removeDotSegments(relative4.path || "");
49867
- target.query = relative4.query;
50770
+ target2.userinfo = relative4.userinfo;
50771
+ target2.host = relative4.host;
50772
+ target2.port = relative4.port;
50773
+ target2.path = removeDotSegments(relative4.path || "");
50774
+ target2.query = relative4.query;
49868
50775
  } else {
49869
50776
  if (!relative4.path) {
49870
- target.path = base4.path;
49871
- if (relative4.query !== void 0) target.query = relative4.query;
49872
- else target.query = base4.query;
50777
+ target2.path = base4.path;
50778
+ if (relative4.query !== void 0) target2.query = relative4.query;
50779
+ else target2.query = base4.query;
49873
50780
  } else {
49874
- if (relative4.path[0] === "/") target.path = removeDotSegments(relative4.path);
50781
+ if (relative4.path[0] === "/") target2.path = removeDotSegments(relative4.path);
49875
50782
  else {
49876
- if ((base4.userinfo !== void 0 || base4.host !== void 0 || base4.port !== void 0) && !base4.path) target.path = "/" + relative4.path;
49877
- else if (!base4.path) target.path = relative4.path;
49878
- else target.path = base4.path.slice(0, base4.path.lastIndexOf("/") + 1) + relative4.path;
49879
- target.path = removeDotSegments(target.path);
50783
+ if ((base4.userinfo !== void 0 || base4.host !== void 0 || base4.port !== void 0) && !base4.path) target2.path = "/" + relative4.path;
50784
+ else if (!base4.path) target2.path = relative4.path;
50785
+ else target2.path = base4.path.slice(0, base4.path.lastIndexOf("/") + 1) + relative4.path;
50786
+ target2.path = removeDotSegments(target2.path);
49880
50787
  }
49881
- target.query = relative4.query;
50788
+ target2.query = relative4.query;
49882
50789
  }
49883
- target.userinfo = base4.userinfo;
49884
- target.host = base4.host;
49885
- target.port = base4.port;
50790
+ target2.userinfo = base4.userinfo;
50791
+ target2.host = base4.host;
50792
+ target2.port = base4.port;
49886
50793
  }
49887
- target.scheme = base4.scheme;
50794
+ target2.scheme = base4.scheme;
49888
50795
  }
49889
- target.fragment = relative4.fragment;
49890
- return target;
50796
+ target2.fragment = relative4.fragment;
50797
+ return target2;
49891
50798
  }
49892
50799
  function equal(uriA, uriB, options) {
49893
50800
  if (typeof uriA === "string") {
@@ -55899,9 +56806,9 @@ var init_cloud = __esm({
55899
56806
 
55900
56807
  // ../code-shim/src/git.ts
55901
56808
  import { execFileSync } from "node:child_process";
55902
- import { existsSync as existsSync8 } from "node:fs";
55903
- import { join as join31 } from "node:path";
55904
- function git5(root, args2) {
56809
+ import { existsSync as existsSync9 } from "node:fs";
56810
+ import { join as join32 } from "node:path";
56811
+ function git7(root, args2) {
55905
56812
  try {
55906
56813
  return execFileSync("git", args2, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024, timeout: 1e4 });
55907
56814
  } catch {
@@ -55909,12 +56816,12 @@ function git5(root, args2) {
55909
56816
  }
55910
56817
  }
55911
56818
  function headCommit2(root) {
55912
- const out2 = git5(root, ["rev-parse", "--verify", "HEAD"]);
56819
+ const out2 = git7(root, ["rev-parse", "--verify", "HEAD"]);
55913
56820
  const sha = out2?.trim();
55914
56821
  return sha !== void 0 && /^[0-9a-f]{40,64}$/.test(sha) ? sha : null;
55915
56822
  }
55916
56823
  function originRepository(root) {
55917
- const url2 = git5(root, ["remote", "get-url", "origin"])?.trim();
56824
+ const url2 = git7(root, ["remote", "get-url", "origin"])?.trim();
55918
56825
  if (url2 === void 0 || url2.length === 0) return null;
55919
56826
  const match = /[:/]([^/:]+)\/([^/]+?)(?:\.git)?\/?$/.exec(url2);
55920
56827
  const owner = match?.[1];
@@ -55960,9 +56867,9 @@ function parseStatus(raw, exists) {
55960
56867
  }
55961
56868
  function readWorkingCopyState(root) {
55962
56869
  const head = headCommit2(root);
55963
- const raw = git5(root, ["status", "--porcelain=v2", "-z", "--untracked-files=all"]);
56870
+ const raw = git7(root, ["status", "--porcelain=v2", "-z", "--untracked-files=all"]);
55964
56871
  if (raw === null) return { head, dirty: /* @__PURE__ */ new Set(), deleted: /* @__PURE__ */ new Set(), renamed: /* @__PURE__ */ new Map() };
55965
- return { head, ...parseStatus(raw, (path) => existsSync8(join31(root, path))) };
56872
+ return { head, ...parseStatus(raw, (path) => existsSync9(join32(root, path))) };
55966
56873
  }
55967
56874
  var init_git2 = __esm({
55968
56875
  "../code-shim/src/git.ts"() {
@@ -55971,18 +56878,18 @@ var init_git2 = __esm({
55971
56878
  });
55972
56879
 
55973
56880
  // ../code-shim/src/config.ts
55974
- import { readFileSync as readFileSync14 } from "node:fs";
56881
+ import { readFileSync as readFileSync15 } from "node:fs";
55975
56882
  import { homedir as homedir2 } from "node:os";
55976
- import { join as join32 } from "node:path";
56883
+ import { join as join33 } from "node:path";
55977
56884
  function configDir(env) {
55978
- const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join32(homedir2(), ".config");
55979
- return join32(base4, "mvrx-harness");
56885
+ const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join33(homedir2(), ".config");
56886
+ return join33(base4, "mvrx-harness");
55980
56887
  }
55981
56888
  function findToken2(env, serviceUrl, now) {
55982
56889
  const fromEnv = env["HARNESS_TOKEN"];
55983
56890
  if (fromEnv !== void 0 && fromEnv.length > 0) return fromEnv;
55984
56891
  try {
55985
- const file2 = JSON.parse(readFileSync14(join32(configDir(env), "credentials.json"), "utf8"));
56892
+ const file2 = JSON.parse(readFileSync15(join33(configDir(env), "credentials.json"), "utf8"));
55986
56893
  const stored = file2[serviceUrl];
55987
56894
  if (stored === void 0 || typeof stored.accessToken !== "string" || stored.accessToken.length === 0) return null;
55988
56895
  if (typeof stored.expiresAt === "number" && stored.expiresAt <= now) return null;
@@ -55994,7 +56901,7 @@ function findToken2(env, serviceUrl, now) {
55994
56901
  function resolveCloudConfig(root, env, now) {
55995
56902
  let binding;
55996
56903
  try {
55997
- const parsed = ProjectBinding.safeParse(JSON.parse(readFileSync14(join32(root, ".harness", "project.json"), "utf8")));
56904
+ const parsed = ProjectBinding.safeParse(JSON.parse(readFileSync15(join33(root, ".harness", "project.json"), "utf8")));
55998
56905
  if (!parsed.success) return null;
55999
56906
  binding = parsed.data;
56000
56907
  } catch {
@@ -56016,7 +56923,7 @@ var init_config = __esm({
56016
56923
  });
56017
56924
 
56018
56925
  // ../code-shim/src/local.ts
56019
- import { readFileSync as readFileSync15, statSync as statSync3 } from "node:fs";
56926
+ import { readFileSync as readFileSync16, statSync as statSync3 } from "node:fs";
56020
56927
  import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve15, sep as sep2 } from "node:path";
56021
56928
  function resolveInside(root, path) {
56022
56929
  const absolute = resolve15(root, path);
@@ -56052,12 +56959,12 @@ function scanWord(content, name2, language) {
56052
56959
  const hashComments = language === "python";
56053
56960
  const slashComments = language !== "python";
56054
56961
  const out2 = [];
56055
- let line2 = 0;
56962
+ let line3 = 0;
56056
56963
  let lineStart = 0;
56057
56964
  let i2 = 0;
56058
56965
  const n = content.length;
56059
56966
  const newline = (at) => {
56060
- line2 += 1;
56967
+ line3 += 1;
56061
56968
  lineStart = at + 1;
56062
56969
  };
56063
56970
  while (i2 < n) {
@@ -56092,7 +56999,7 @@ function scanWord(content, name2, language) {
56092
56999
  } else if (/[A-Za-z_$]/.test(c)) {
56093
57000
  const start2 = i2;
56094
57001
  while (i2 < n && IDENT_CHAR.test(content.charAt(i2))) i2 += 1;
56095
- if (content.slice(start2, i2) === name2) out2.push([line2, start2 - lineStart, line2, i2 - lineStart]);
57002
+ if (content.slice(start2, i2) === name2) out2.push([line3, start2 - lineStart, line3, i2 - lineStart]);
56096
57003
  } else {
56097
57004
  i2 += 1;
56098
57005
  }
@@ -56136,7 +57043,7 @@ var init_local = __esm({
56136
57043
  let shard;
56137
57044
  try {
56138
57045
  this.parses += 1;
56139
- shard = await this.parse(path, readFileSync15(absolute, "utf8"));
57046
+ shard = await this.parse(path, readFileSync16(absolute, "utf8"));
56140
57047
  } catch {
56141
57048
  shard = null;
56142
57049
  }
@@ -56148,7 +57055,7 @@ var init_local = __esm({
56148
57055
  const absolute = resolveInside(this.root, path);
56149
57056
  if (absolute === null) return null;
56150
57057
  try {
56151
- return readFileSync15(absolute, "utf8");
57058
+ return readFileSync16(absolute, "utf8");
56152
57059
  } catch {
56153
57060
  return null;
56154
57061
  }
@@ -56158,7 +57065,7 @@ var init_local = __esm({
56158
57065
  const absolute = resolveInside(this.root, path);
56159
57066
  if (absolute === null) return void 0;
56160
57067
  try {
56161
- const lines = readFileSync15(absolute, "utf8").split("\n");
57068
+ const lines = readFileSync16(absolute, "utf8").split("\n");
56162
57069
  const start2 = range[0];
56163
57070
  if (start2 >= lines.length) return void 0;
56164
57071
  const end = Math.min(Math.max(range[2], start2), start2 + maxLines - 1, lines.length - 1);
@@ -56385,25 +57292,25 @@ var init_nav = __esm({
56385
57292
 
56386
57293
  // ../code-shim/src/edit.ts
56387
57294
  import { createHash as createHash8 } from "node:crypto";
56388
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
57295
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
56389
57296
  function lineStarts(text) {
56390
57297
  const starts = [0];
56391
57298
  for (let i2 = 0; i2 < text.length; i2 += 1) if (text[i2] === "\n") starts.push(i2 + 1);
56392
57299
  return starts;
56393
57300
  }
56394
- function offsetOf(starts, text, line2, character) {
56395
- const start2 = starts[line2];
57301
+ function offsetOf(starts, text, line3, character) {
57302
+ const start2 = starts[line3];
56396
57303
  if (start2 === void 0) return null;
56397
- const next = starts[line2 + 1] ?? text.length + 1;
57304
+ const next = starts[line3 + 1] ?? text.length + 1;
56398
57305
  return Math.min(start2 + character, next - 1);
56399
57306
  }
56400
57307
  function positionAt(text, offset) {
56401
57308
  const starts = lineStarts(text.slice(0, offset));
56402
- const line2 = starts.length - 1;
56403
- return [line2, offset - (starts[line2] ?? 0)];
57309
+ const line3 = starts.length - 1;
57310
+ return [line3, offset - (starts[line3] ?? 0)];
56404
57311
  }
56405
- function lineEnd(starts, text, line2) {
56406
- const next = starts[line2 + 1];
57312
+ function lineEnd(starts, text, line3) {
57313
+ const next = starts[line3 + 1];
56407
57314
  return next === void 0 ? text.length : next - 1;
56408
57315
  }
56409
57316
  async function locate(index, path, namePath) {
@@ -56426,7 +57333,7 @@ async function editSymbol(index, args2) {
56426
57333
  if (!found.ok) return found;
56427
57334
  const absolute = resolveInside(index.root, args2.path);
56428
57335
  if (absolute === null) return { ok: false, reason: `path ${args2.path} is outside the repository` };
56429
- const original = readFileSync16(absolute, "utf8");
57336
+ const original = readFileSync17(absolute, "utf8");
56430
57337
  const starts = lineStarts(original);
56431
57338
  const range = found.symbol.enclosingRange ?? found.symbol.range;
56432
57339
  const from = offsetOf(starts, original, range[0], range[1]);
@@ -56454,7 +57361,7 @@ ${args2.content.replace(/\n+$/, "")}`;
56454
57361
  const before = positionAt(next, changedStart);
56455
57362
  const after = positionAt(next, insertAt + inserted.length);
56456
57363
  const bytes = Buffer.from(next, "utf8");
56457
- writeFileSync9(absolute, bytes);
57364
+ writeFileSync10(absolute, bytes);
56458
57365
  return {
56459
57366
  ok: true,
56460
57367
  path: args2.path,
@@ -56482,10 +57389,10 @@ async function renameSymbol(deps, args2) {
56482
57389
  }
56483
57390
  const targets = [{ path: args2.path, range: found.symbol.range }, ...answer.references.map((hit) => ({ path: hit.path, range: hit.range }))];
56484
57391
  const byFile = /* @__PURE__ */ new Map();
56485
- for (const target of targets) {
56486
- const list = byFile.get(target.path) ?? [];
56487
- if (!list.some((r) => r.join(",") === target.range.join(","))) list.push(target.range);
56488
- byFile.set(target.path, list);
57392
+ for (const target2 of targets) {
57393
+ const list = byFile.get(target2.path) ?? [];
57394
+ if (!list.some((r) => r.join(",") === target2.range.join(","))) list.push(target2.range);
57395
+ byFile.set(target2.path, list);
56489
57396
  }
56490
57397
  const plan3 = [];
56491
57398
  for (const [path, ranges] of byFile) {
@@ -56493,7 +57400,7 @@ async function renameSymbol(deps, args2) {
56493
57400
  if (absolute === null) return { ok: false, reason: `reference path ${path} is outside the repository` };
56494
57401
  let text;
56495
57402
  try {
56496
- text = readFileSync16(absolute, "utf8");
57403
+ text = readFileSync17(absolute, "utf8");
56497
57404
  } catch {
56498
57405
  return { ok: false, reason: `cannot read ${path}` };
56499
57406
  }
@@ -56515,7 +57422,7 @@ async function renameSymbol(deps, args2) {
56515
57422
  let next = item.text;
56516
57423
  for (const offset of offsets) next = next.slice(0, offset) + args2.new_name + next.slice(offset + oldName.length);
56517
57424
  const bytes = Buffer.from(next, "utf8");
56518
- writeFileSync9(item.absolute, bytes);
57425
+ writeFileSync10(item.absolute, bytes);
56519
57426
  filesChanged.push({ path: item.path, edits: offsets.length, sha256: sha2563(bytes) });
56520
57427
  edits += offsets.length;
56521
57428
  }
@@ -57113,12 +58020,12 @@ function toErrorBody(error62, command) {
57113
58020
  }
57114
58021
 
57115
58022
  // ../cli/src/deps.ts
57116
- function say(deps, line2 = "") {
57117
- deps.stdout(`${line2}
58023
+ function say(deps, line3 = "") {
58024
+ deps.stdout(`${line3}
57118
58025
  `);
57119
58026
  }
57120
- function warn(deps, line2) {
57121
- deps.stderr(`${redact(line2)}
58027
+ function warn(deps, line3) {
58028
+ deps.stderr(`${redact(line3)}
57122
58029
  `);
57123
58030
  }
57124
58031
 
@@ -57140,8 +58047,8 @@ function parseAgentList(value) {
57140
58047
  async function detectAgent(deps, agent, cwd) {
57141
58048
  const result = await deps.exec(agent, ["--version"], { cwd });
57142
58049
  if (result.code === 0) {
57143
- const line2 = result.stdout.trim().split("\n")[0] ?? "";
57144
- return { agent, installed: true, detail: line2.length > 0 ? line2 : "version not reported" };
58050
+ const line3 = result.stdout.trim().split("\n")[0] ?? "";
58051
+ return { agent, installed: true, detail: line3.length > 0 ? line3 : "version not reported" };
57145
58052
  }
57146
58053
  const reason = result.code === 127 ? `\`${agent}\` was not found on PATH` : `\`${agent} --version\` failed (exit ${result.code}): ${result.stderr.trim()}`;
57147
58054
  return { agent, installed: false, detail: reason };
@@ -57332,17 +58239,17 @@ var HEADER = /^\s*\[(?!\[)(.+?)\]\s*(?:#.*)?$/;
57332
58239
  function readTables(text) {
57333
58240
  const tables = [];
57334
58241
  let current2 = null;
57335
- for (const line2 of text.split("\n")) {
57336
- const match = HEADER.exec(line2);
58242
+ for (const line3 of text.split("\n")) {
58243
+ const match = HEADER.exec(line3);
57337
58244
  if (match !== null && match[1] !== void 0) {
57338
58245
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
57339
58246
  const path = parseKeyPath(match[1]);
57340
58247
  current2 = path === null ? null : { path, lines: [] };
57341
- } else if (/^\s*\[\[/.test(line2)) {
58248
+ } else if (/^\s*\[\[/.test(line3)) {
57342
58249
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
57343
58250
  current2 = null;
57344
58251
  } else if (current2 !== null) {
57345
- current2.lines.push(line2);
58252
+ current2.lines.push(line3);
57346
58253
  }
57347
58254
  }
57348
58255
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
@@ -57831,9 +58738,9 @@ function namesCommit(stdout, commit) {
57831
58738
  }
57832
58739
  async function assessAgent(deps, input2) {
57833
58740
  const { agent } = input2.info;
57834
- const label = AGENT_LABEL[agent];
58741
+ const label2 = AGENT_LABEL[agent];
57835
58742
  if (!input2.info.installed) {
57836
- return report(agent, "incompatible", input2.info.detail, `install ${label} (\`${agent}\`) and run \`overlane doctor\` again`);
58743
+ return report(agent, "incompatible", input2.info.detail, `install ${label2} (\`${agent}\`) and run \`overlane doctor\` again`);
57837
58744
  }
57838
58745
  if (input2.binding.kind !== "ok") {
57839
58746
  const why = input2.binding.kind === "missing" ? "no .harness/project.json" : `invalid .harness/project.json (${input2.binding.detail})`;
@@ -57843,7 +58750,7 @@ async function assessAgent(deps, input2) {
57843
58750
  if (input2.service === "rejected") return report(agent, "blocked", "the service rejected the stored token", "run `overlane connect` to sign in again");
57844
58751
  const enabled = agent === "claude" ? readClaudeState(input2.root).plugin : readCodexState(deps, input2.root).projectEnabled;
57845
58752
  if (!enabled) {
57846
- return report(agent, "blocked", `the Overlane plugin (id mvrx-harness) is not installed and enabled for ${label} in this project`, "run `overlane connect`");
58753
+ return report(agent, "blocked", `the Overlane plugin (id mvrx-harness) is not installed and enabled for ${label2} in this project`, "run `overlane connect`");
57847
58754
  }
57848
58755
  if (agent === "codex" && !codexHookTrusted(deps)) {
57849
58756
  return report(agent, "needs_trust", "Codex has not trusted the Overlane hook (listed as mvrx-harness)", "open codex and run /hooks to trust the mvrx-harness hook");
@@ -58119,9 +59026,9 @@ async function connect(deps, options) {
58119
59026
  $ ${formatCommand(step2)}`);
58120
59027
  if (codexEdit !== null) {
58121
59028
  say(deps, ` codex: enable the plugin for this project, appending to ${CODEX_CONFIG_FILE} (other content untouched)`);
58122
- for (const line2 of codexEdit.block.split("\n")) if (line2.length > 0) say(deps, ` + ${line2}`);
59029
+ for (const line3 of codexEdit.block.split("\n")) if (line3.length > 0) say(deps, ` + ${line3}`);
58123
59030
  }
58124
- for (const line2 of present) say(deps, ` skip: ${line2}`);
59031
+ for (const line3 of present) say(deps, ` skip: ${line3}`);
58125
59032
  say(deps, ` write ${BINDING_FILE}: project ID, tenant ID, service URL, release digest and the release-signing public key from the service`);
58126
59033
  say(deps, " write .harness/owned.json: the entries overlane added, so `overlane disconnect` removes only those");
58127
59034
  if (excludeNeeded) say(deps, ` append ${EXCLUDE_LINE} to ${await excludePath(deps, root)}`);
@@ -58149,7 +59056,7 @@ async function connect(deps, options) {
58149
59056
  writes,
58150
59057
  notes: [
58151
59058
  ...existingToken === null ? [`Sign-in: browser sign-in to ${serviceUrl} (OAuth with PKCE); set HARNESS_TOKEN to skip it.`] : [],
58152
- ...present.map((line2) => `skip: ${line2}`)
59059
+ ...present.map((line3) => `skip: ${line3}`)
58153
59060
  ]
58154
59061
  });
58155
59062
  return { data: plan3 };
@@ -58221,7 +59128,7 @@ async function connect(deps, options) {
58221
59128
  for (const info2 of infos) {
58222
59129
  reports.push(await assessAgent(deps, { root, marketplaceRoot: marketplaceRoot2, info: info2, binding: { kind: "ok", binding }, hasToken: true, service: "reachable" }));
58223
59130
  }
58224
- for (const line2 of renderReports(reports)) say(deps, ` ${line2}`);
59131
+ for (const line3 of renderReports(reports)) say(deps, ` ${line3}`);
58225
59132
  return {
58226
59133
  data: {
58227
59134
  project: projectSummary(project),
@@ -58513,10 +59420,10 @@ function deepMerge(existing, incoming) {
58513
59420
  }
58514
59421
  return incoming;
58515
59422
  }
58516
- function setAt(target, key, value) {
59423
+ function setAt(target2, key, value) {
58517
59424
  const [head, ...rest] = key;
58518
- if (head === void 0) return target;
58519
- const out2 = { ...target };
59425
+ if (head === void 0) return target2;
59426
+ const out2 = { ...target2 };
58520
59427
  if (rest.length === 0) {
58521
59428
  out2[head] = deepMerge(out2[head], value);
58522
59429
  return out2;
@@ -58708,7 +59615,7 @@ var clientInstallCommand = defineCommand({
58708
59615
  }
58709
59616
  const files = applyPlan(dir, ops);
58710
59617
  for (const file2 of files) say(inv.deps, `${file2.action.padEnd(9)} ${file2.path}`);
58711
- for (const line2 of NEXT[name2]) say(inv.deps, line2);
59618
+ for (const line3 of NEXT[name2]) say(inv.deps, line3);
58712
59619
  return { data: { client: name2, surface, dir, files, next: NEXT[name2] } };
58713
59620
  }
58714
59621
  });
@@ -58754,12 +59661,12 @@ async function verifyRelease(record3, publicKey) {
58754
59661
  }
58755
59662
  return manifest;
58756
59663
  }
58757
- function diffFiles(installed, target) {
59664
+ function diffFiles(installed, target2) {
58758
59665
  const before = new Map(installed.map((file2) => [file2.path, file2]));
58759
- const after = new Set(target.map((file2) => file2.path));
59666
+ const after = new Set(target2.map((file2) => file2.path));
58760
59667
  const added = [];
58761
59668
  const changed = [];
58762
- for (const file2 of target) {
59669
+ for (const file2 of target2) {
58763
59670
  const old = before.get(file2.path);
58764
59671
  if (old === void 0) added.push(file2);
58765
59672
  else if (old.digest !== file2.digest) changed.push(file2);
@@ -58796,8 +59703,8 @@ function diskDigest(marketplaceRoot2, file2) {
58796
59703
  return null;
58797
59704
  }
58798
59705
  }
58799
- function editedManagedFiles(marketplaceRoot2, installed, target) {
58800
- const wanted = new Map(target.map((file2) => [file2.path, file2.digest]));
59706
+ function editedManagedFiles(marketplaceRoot2, installed, target2) {
59707
+ const wanted = new Map(target2.map((file2) => [file2.path, file2.digest]));
58801
59708
  const edited = [];
58802
59709
  for (const file2 of installed) {
58803
59710
  const actual = diskDigest(marketplaceRoot2, file2);
@@ -58805,9 +59712,9 @@ function editedManagedFiles(marketplaceRoot2, installed, target) {
58805
59712
  }
58806
59713
  return edited;
58807
59714
  }
58808
- function mismatchedFiles(marketplaceRoot2, target) {
59715
+ function mismatchedFiles(marketplaceRoot2, target2) {
58809
59716
  const bad = [];
58810
- for (const file2 of target) {
59717
+ for (const file2 of target2) {
58811
59718
  const actual = diskDigest(marketplaceRoot2, file2);
58812
59719
  if (actual !== null && actual !== file2.digest) bad.push(file2.path);
58813
59720
  }
@@ -58926,7 +59833,7 @@ async function verifySourceTree(deps, marketplaceRoot2, files, digest) {
58926
59833
  else if (actual !== file2.digest) problems.push(`${file2.path} differs`);
58927
59834
  }
58928
59835
  const listed = new Set(files.map((file2) => file2.path));
58929
- const tracked = (await git(deps, marketplaceRoot2, ["ls-tree", "-r", "--name-only", "HEAD"])).text.split("\n").filter((line2) => line2.length > 0);
59836
+ const tracked = (await git(deps, marketplaceRoot2, ["ls-tree", "-r", "--name-only", "HEAD"])).text.split("\n").filter((line3) => line3.length > 0);
58930
59837
  for (const path of tracked) {
58931
59838
  if (!listed.has(path) && !path.startsWith(`${SIDECAR_DIR}/`)) problems.push(`${path} is not in the release`);
58932
59839
  }
@@ -58963,8 +59870,8 @@ function showDiff(deps, diff) {
58963
59870
  for (const file2 of files) say(deps, ` ${sign} ${file2.path} (${file2.kind})`);
58964
59871
  }
58965
59872
  }
58966
- function approvalCommand(target, explicitTo) {
58967
- return `overlane release update${explicitTo ? ` --to ${target}` : ""} --approve ${target}`;
59873
+ function approvalCommand(target2, explicitTo) {
59874
+ return `overlane release update${explicitTo ? ` --to ${target2}` : ""} --approve ${target2}`;
58968
59875
  }
58969
59876
  async function installRelease(deps, ctx, targetDigest, options, isPin) {
58970
59877
  const { root, binding } = ctx;
@@ -59019,7 +59926,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59019
59926
  refusals.push(`approval_required: --approve ${options.approve} does not name this release; run: ${approvalCommand(manifest.digest, options.to !== void 0)}`);
59020
59927
  }
59021
59928
  if (options.dryRun) {
59022
- for (const line2 of refusals) say(deps, ` would refuse - ${line2}`);
59929
+ for (const line3 of refusals) say(deps, ` would refuse - ${line3}`);
59023
59930
  say(deps, refusals.length === 0 ? "Dry run: this update would install; nothing was changed." : "Dry run: nothing was changed.");
59024
59931
  const files = { added: diff.added.length, changed: diff.changed.length, removed: diff.removed.length };
59025
59932
  return {
@@ -59029,7 +59936,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59029
59936
  requests: [{ method: "GET", path: releasePaths.one(binding.tenantId, targetDigest) }],
59030
59937
  commands: ["claude", "codex"].flatMap((agent) => updateSteps(agent === "claude" ? "claude" : "codex").map((step2) => `${step2.command} ${step2.args.join(" ")}`)),
59031
59938
  writes: [{ path: RELEASE_FILE, action: "write", description: "the installed release record, written only after the commands succeed" }],
59032
- notes: refusals.map((line2) => `would refuse - ${line2}`)
59939
+ notes: refusals.map((line3) => `would refuse - ${line3}`)
59033
59940
  }),
59034
59941
  release: { version: manifest.version, digest: manifest.digest, changeKinds: [...kinds], restart: manifest.restart, files },
59035
59942
  wouldInstall: refusals.length === 0,
@@ -59037,7 +59944,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59037
59944
  }
59038
59945
  };
59039
59946
  }
59040
- const taskRefusal = refusals.find((line2) => line2.startsWith("task_in_progress"));
59947
+ const taskRefusal = refusals.find((line3) => line3.startsWith("task_in_progress"));
59041
59948
  if (taskRefusal !== void 0) throw new ReleaseRefusal("task_in_progress", taskRefusal.slice("task_in_progress: ".length));
59042
59949
  const first = refusals[0];
59043
59950
  if (first !== void 0) {
@@ -59266,14 +60173,14 @@ function sectionsOf(body2, level = 2) {
59266
60173
  const sections = /* @__PURE__ */ new Map([["", []]]);
59267
60174
  let current2 = "";
59268
60175
  let fenced = false;
59269
- for (const line2 of body2.replace(/\r\n/g, "\n").split("\n")) {
59270
- if (/^\s*```/.test(line2)) fenced = !fenced;
59271
- if (!fenced && line2.startsWith(marker)) {
59272
- current2 = line2.slice(marker.length).trim();
60176
+ for (const line3 of body2.replace(/\r\n/g, "\n").split("\n")) {
60177
+ if (/^\s*```/.test(line3)) fenced = !fenced;
60178
+ if (!fenced && line3.startsWith(marker)) {
60179
+ current2 = line3.slice(marker.length).trim();
59273
60180
  if (!sections.has(current2)) sections.set(current2, []);
59274
60181
  continue;
59275
60182
  }
59276
- sections.get(current2)?.push(line2);
60183
+ sections.get(current2)?.push(line3);
59277
60184
  }
59278
60185
  return sections;
59279
60186
  }
@@ -59282,10 +60189,10 @@ function sectionText(lines) {
59282
60189
  }
59283
60190
  function bulletsOf(lines, where2) {
59284
60191
  const items = [];
59285
- for (const line2 of lines ?? []) {
59286
- if (line2.trim() === "") continue;
59287
- const match = /^- (.*)$/.exec(line2);
59288
- if (!match) throw new KitError(`${where2}: expected a "- " bullet, found "${line2}"`);
60192
+ for (const line3 of lines ?? []) {
60193
+ if (line3.trim() === "") continue;
60194
+ const match = /^- (.*)$/.exec(line3);
60195
+ if (!match) throw new KitError(`${where2}: expected a "- " bullet, found "${line3}"`);
59289
60196
  items.push(match[1] ?? "");
59290
60197
  }
59291
60198
  return items;
@@ -59388,10 +60295,10 @@ function parseMilestones(text, where2) {
59388
60295
  let current2;
59389
60296
  let inAcceptance = false;
59390
60297
  let fenced = false;
59391
- for (const line2 of body2.split("\n")) {
59392
- if (/^\s*```/.test(line2)) fenced = !fenced;
60298
+ for (const line3 of body2.split("\n")) {
60299
+ if (/^\s*```/.test(line3)) fenced = !fenced;
59393
60300
  if (fenced) continue;
59394
- const heading = /^## ([^:]+): (.+)$/.exec(line2);
60301
+ const heading = /^## ([^:]+): (.+)$/.exec(line3);
59395
60302
  if (heading) {
59396
60303
  current2 = { id: (heading[1] ?? "").trim(), title: (heading[2] ?? "").trim(), acceptance: [], dependsOn: [] };
59397
60304
  milestones.push(current2);
@@ -59399,15 +60306,15 @@ function parseMilestones(text, where2) {
59399
60306
  continue;
59400
60307
  }
59401
60308
  if (!current2) continue;
59402
- if (/^### Acceptance\s*$/.test(line2)) {
60309
+ if (/^### Acceptance\s*$/.test(line3)) {
59403
60310
  inAcceptance = true;
59404
60311
  continue;
59405
60312
  }
59406
- if (line2.trim() === "") continue;
59407
- const depends = /^Depends on: (.+)$/.exec(line2);
60313
+ if (line3.trim() === "") continue;
60314
+ const depends = /^Depends on: (.+)$/.exec(line3);
59408
60315
  if (depends && !inAcceptance) current2.dependsOn = (depends[1] ?? "").split(",").map((id2) => id2.trim());
59409
- else if (inAcceptance && line2.startsWith("- ")) current2.acceptance.push(line2.slice(2));
59410
- else throw new KitError(`${where2}: unexpected line under ${current2.id}: "${line2}"`);
60316
+ else if (inAcceptance && line3.startsWith("- ")) current2.acceptance.push(line3.slice(2));
60317
+ else throw new KitError(`${where2}: unexpected line under ${current2.id}: "${line3}"`);
59411
60318
  }
59412
60319
  return milestones;
59413
60320
  }
@@ -59560,18 +60467,18 @@ async function detectCommands(repoDir) {
59560
60467
  if (typeof run3 !== "string") continue;
59561
60468
  const dir = stepDir ?? jobDir;
59562
60469
  const logical = run3.replace(/\\\n\s*/g, " ").split("\n").map((l) => l.trim()).filter((l) => l !== "" && !l.startsWith("#"));
59563
- for (const line2 of logical) {
59564
- const kind = classifyCommand(line2);
60470
+ for (const line3 of logical) {
60471
+ const kind = classifyCommand(line3);
59565
60472
  if (kind === void 0) continue;
59566
- const alias = /^(pnpm|npm|yarn|bun)(?: run)? ([\w:-]+)$/.exec(line2);
59567
- const normalized = alias && kind !== "install" ? `${alias[1]} run ${alias[2]}` : line2;
60473
+ const alias = /^(pnpm|npm|yarn|bun)(?: run)? ([\w:-]+)$/.exec(line3);
60474
+ const normalized = alias && kind !== "install" ? `${alias[1]} run ${alias[2]}` : line3;
59568
60475
  const command = dir && dir !== "." ? `cd ${dir} && ${normalized}` : normalized;
59569
60476
  if (kind === "install") {
59570
60477
  if (install === void 0) install = command;
59571
60478
  if (!sources.includes(source)) sources.push(source);
59572
60479
  continue;
59573
60480
  }
59574
- const script = scriptBodies.get(line2);
60481
+ const script = scriptBodies.get(line3);
59575
60482
  if (script !== void 0 && dir === void 0 || dir === void 0 && found[kind].includes(command)) {
59576
60483
  if (!sources.includes(source)) sources.push(source);
59577
60484
  continue;
@@ -59672,8 +60579,8 @@ function parseTasksFile(text, where2) {
59672
60579
  }
59673
60580
  return state.current;
59674
60581
  };
59675
- for (const line2 of text.replace(BLOCK_PATTERN, "").replace(/\r\n/g, "\n").split("\n")) {
59676
- const heading = /^##\s+Phase\s+(\d+)\s*[:\u2013\u2014-]\s*(.*?)\s*$/i.exec(line2);
60582
+ for (const line3 of text.replace(BLOCK_PATTERN, "").replace(/\r\n/g, "\n").split("\n")) {
60583
+ const heading = /^##\s+Phase\s+(\d+)\s*[:\u2013\u2014-]\s*(.*?)\s*$/i.exec(line3);
59677
60584
  if (heading) {
59678
60585
  let title2 = heading[2] ?? "";
59679
60586
  const tag = /\s*\{#([^}\s]+)\}\s*$/.exec(title2);
@@ -59682,12 +60589,12 @@ function parseTasksFile(text, where2) {
59682
60589
  phases.push(state.current);
59683
60590
  continue;
59684
60591
  }
59685
- const checkpoint = /^\*\*Checkpoint\*\*:\s*(.+?)\s*$/.exec(line2);
60592
+ const checkpoint = /^\*\*Checkpoint\*\*:\s*(.+?)\s*$/.exec(line3);
59686
60593
  if (checkpoint && state.current) {
59687
60594
  state.current.checkpoint = checkpoint[1];
59688
60595
  continue;
59689
60596
  }
59690
- const task = /^\s*- \[[ xX]\]\s+([A-Za-z]*\d[\w.:-]*)((?:\s+\[(?:P|US\d+)\])*)\s+(\S.*?)\s*$/.exec(line2);
60597
+ const task = /^\s*- \[[ xX]\]\s+([A-Za-z]*\d[\w.:-]*)((?:\s+\[(?:P|US\d+)\])*)\s+(\S.*?)\s*$/.exec(line3);
59691
60598
  if (!task) continue;
59692
60599
  const markers = task[2] ?? "";
59693
60600
  let title = task[3] ?? "";
@@ -59705,25 +60612,25 @@ function parseStories(specText) {
59705
60612
  const stories = /* @__PURE__ */ new Map();
59706
60613
  let story;
59707
60614
  let collecting = false;
59708
- for (const line2 of specText.replace(/\r\n/g, "\n").split("\n")) {
59709
- const heading = /^###\s+User Story\s+(\d+)\b/i.exec(line2);
60615
+ for (const line3 of specText.replace(/\r\n/g, "\n").split("\n")) {
60616
+ const heading = /^###\s+User Story\s+(\d+)\b/i.exec(line3);
59710
60617
  if (heading) {
59711
60618
  story = [];
59712
60619
  stories.set(Number(heading[1]), story);
59713
60620
  collecting = false;
59714
60621
  continue;
59715
60622
  }
59716
- if (/^#{1,3}\s/.test(line2) || /^---\s*$/.test(line2)) {
60623
+ if (/^#{1,3}\s/.test(line3) || /^---\s*$/.test(line3)) {
59717
60624
  collecting = false;
59718
- if (!/^---/.test(line2)) story = void 0;
60625
+ if (!/^---/.test(line3)) story = void 0;
59719
60626
  continue;
59720
60627
  }
59721
60628
  if (!story) continue;
59722
- if (/\*\*Acceptance Scenarios\*\*/i.test(line2)) {
60629
+ if (/\*\*Acceptance Scenarios\*\*/i.test(line3)) {
59723
60630
  collecting = true;
59724
60631
  continue;
59725
60632
  }
59726
- const item = /^\s*\d+\.\s+(.+?)\s*$/.exec(line2);
60633
+ const item = /^\s*\d+\.\s+(.+?)\s*$/.exec(line3);
59727
60634
  if (collecting && item) story.push((item[1] ?? "").replace(/\*\*/g, ""));
59728
60635
  }
59729
60636
  return stories;
@@ -59744,8 +60651,8 @@ function parseConstitution(text) {
59744
60651
  constraints.push(body2 && !/^\[.*\]$/.test(body2) ? `${key}: ${body2}` : key);
59745
60652
  }
59746
60653
  } else {
59747
- for (const line2 of principles) {
59748
- const bullet = /^- (.+?)\s*$/.exec(line2);
60654
+ for (const line3 of principles) {
60655
+ const bullet = /^- (.+?)\s*$/.exec(line3);
59749
60656
  if (bullet) constraints.push(bullet[1] ?? "");
59750
60657
  }
59751
60658
  }
@@ -59872,10 +60779,10 @@ async function importSpecKit(repoDir) {
59872
60779
  }
59873
60780
 
59874
60781
  // ../cli/src/session.ts
59875
- async function gitRoot(deps, label) {
60782
+ async function gitRoot(deps, label2) {
59876
60783
  const top = await deps.exec("git", ["rev-parse", "--show-toplevel"], { cwd: deps.paths.cwd });
59877
60784
  const root = top.stdout.trim();
59878
- if (top.code !== 0 || root.length === 0) throw new UsageError(`not inside a git repository; run overlane ${label} from the connected project`);
60785
+ if (top.code !== 0 || root.length === 0) throw new UsageError(`not inside a git repository; run overlane ${label2} from the connected project`);
59879
60786
  return root;
59880
60787
  }
59881
60788
  function boundProject(deps, root) {
@@ -59890,11 +60797,11 @@ function boundProject(deps, root) {
59890
60797
  const override = deps.env["HARNESS_SERVICE_URL"];
59891
60798
  return { root, binding: bound.binding, serviceUrl: override !== void 0 && override.length > 0 ? override : bound.binding.serviceUrl };
59892
60799
  }
59893
- async function offlineProject(deps, label) {
59894
- return boundProject(deps, await gitRoot(deps, label));
60800
+ async function offlineProject(deps, label2) {
60801
+ return boundProject(deps, await gitRoot(deps, label2));
59895
60802
  }
59896
- async function connectedProject(deps, label) {
59897
- const project = await offlineProject(deps, label);
60803
+ async function connectedProject(deps, label2) {
60804
+ const project = await offlineProject(deps, label2);
59898
60805
  const token = await requireToken(deps, project.binding.serviceUrl);
59899
60806
  return { ...project, token: token.token };
59900
60807
  }
@@ -59945,24 +60852,24 @@ function current(window3, updatedAt, now) {
59945
60852
  function usageWarnings(usage, now) {
59946
60853
  if (usage === null) return [];
59947
60854
  const out2 = [];
59948
- const check2 = (label, window3, threshold) => {
60855
+ const check2 = (label2, window3, threshold) => {
59949
60856
  if (window3 === void 0 || window3.usedPercentage < threshold || !current(window3, usage.updatedAt, now)) return;
59950
60857
  const resets = window3.resetsAt === void 0 ? "" : `, resets ${new Date(window3.resetsAt * 1e3).toISOString()}`;
59951
- out2.push(`Claude ${label} usage is ${Math.round(window3.usedPercentage)}% (warning at ${threshold}%${resets}). Consider \`overlane task checkpoint\` or \`overlane task handoff\` before it runs out.`);
60858
+ out2.push(`Claude ${label2} usage is ${Math.round(window3.usedPercentage)}% (warning at ${threshold}%${resets}). Consider \`overlane task checkpoint\` or \`overlane task handoff\` before it runs out.`);
59952
60859
  };
59953
60860
  check2("5-hour", usage.fiveHour, FIVE_HOUR_WARN_PERCENT);
59954
60861
  check2("weekly", usage.sevenDay, WEEKLY_WARN_PERCENT);
59955
60862
  return out2;
59956
60863
  }
59957
60864
  function warnUsage(deps) {
59958
- for (const line2 of usageWarnings(readUsage(deps), deps.now())) warn(deps, `warning: ${line2}`);
60865
+ for (const line3 of usageWarnings(readUsage(deps), deps.now())) warn(deps, `warning: ${line3}`);
59959
60866
  }
59960
60867
  function currentUsagePercent(usage, now) {
59961
60868
  if (usage === null) return null;
59962
60869
  let best = null;
59963
- for (const [label, window3] of [["5-hour", usage.fiveHour], ["weekly", usage.sevenDay]]) {
60870
+ for (const [label2, window3] of [["5-hour", usage.fiveHour], ["weekly", usage.sevenDay]]) {
59964
60871
  if (window3 === void 0 || !current(window3, usage.updatedAt, now)) continue;
59965
- if (best === null || window3.usedPercentage > best.percent) best = { percent: window3.usedPercentage, label };
60872
+ if (best === null || window3.usedPercentage > best.percent) best = { percent: window3.usedPercentage, label: label2 };
59966
60873
  }
59967
60874
  return best;
59968
60875
  }
@@ -60077,9 +60984,9 @@ async function inspectWorkingCopy(deps, root) {
60077
60984
  const digest = await workingTreeDigest(deps, root);
60078
60985
  const status = await git2(deps, root, ["status", "--porcelain", "--untracked-files=all"]);
60079
60986
  if (digest === null || status === null) return null;
60080
- const clean2 = status.split("\n").every((line2) => line2.length === 0 || isHarnessPath(line2.slice(3)));
60987
+ const clean2 = status.split("\n").every((line3) => line3.length === 0 || isHarnessPath(line3.slice(3)));
60081
60988
  const remotes = await git2(deps, root, ["branch", "-r", "--contains", "HEAD"]) ?? "";
60082
- const pushed = remotes.split("\n").some((line2) => line2.trim().length > 0 && !line2.includes("->"));
60989
+ const pushed = remotes.split("\n").some((line3) => line3.trim().length > 0 && !line3.includes("->"));
60083
60990
  return { commitSha: head, workingTreeDigest: digest, clean: clean2, pushed };
60084
60991
  }
60085
60992
  function recoveryRoute(facts2) {
@@ -60568,28 +61475,28 @@ function resumeCommand(agent, taskId) {
60568
61475
  async function handoffTask(deps, input2) {
60569
61476
  const { to } = input2;
60570
61477
  if (to !== void 0 && to !== "claude" && to !== "codex") throw new UsageError("--to must be claude or codex");
60571
- const target = to ?? otherAgent(detectExecutorName(deps.env));
61478
+ const target2 = to ?? otherAgent(detectExecutorName(deps.env));
60572
61479
  const written = await writeCheckpoint(deps, {
60573
- summary: typeof input2.summary === "string" ? input2.summary : `Handed off to ${target}`,
61480
+ summary: typeof input2.summary === "string" ? input2.summary : `Handed off to ${target2}`,
60574
61481
  next: typeof input2.next === "string" ? input2.next : "Resume the task",
60575
61482
  pause: "handoff",
60576
61483
  dryRun: input2.dryRun
60577
61484
  });
60578
61485
  if (written.kind === "planned") return written.outcome;
60579
61486
  say(deps, `Task ${written.taskId} is paused for handoff at ${written.copy.commitSha.slice(0, 12)}, ${describeRoute(written.route.route, written.route.localOnly)}.`);
60580
- say(deps, `To continue it in ${target}, run this yourself from this working tree:`);
60581
- say(deps, ` ${resumeCommand(target, written.taskId)}`);
61487
+ say(deps, `To continue it in ${target2}, run this yourself from this working tree:`);
61488
+ say(deps, ` ${resumeCommand(target2, written.taskId)}`);
60582
61489
  say(deps, "overlane does not start the other agent. Moving work to another vendor's agent sends code and context to that vendor (REQ-TSK-09).");
60583
61490
  if (written.route.localOnly) say(deps, "The work is not pushed or committed, so only this working tree can continue it; nothing was committed or uploaded for you (REQ-TSK-12).");
60584
61491
  warnUsage(deps);
60585
61492
  return {
60586
61493
  data: {
60587
61494
  taskId: written.taskId,
60588
- to: target,
61495
+ to: target2,
60589
61496
  commitSha: written.copy.commitSha,
60590
61497
  route: written.route.route,
60591
61498
  localOnly: written.route.localOnly,
60592
- resumeCommand: resumeCommand(target, written.taskId)
61499
+ resumeCommand: resumeCommand(target2, written.taskId)
60593
61500
  }
60594
61501
  };
60595
61502
  }
@@ -61126,8 +62033,8 @@ async function buildContext(deps, version2) {
61126
62033
  const answers = [releases, policy, next, taskView].filter((value) => value !== null);
61127
62034
  const serviceStatus = answers.some((value) => !isMissing2(value)) ? "reachable" : answers.every((value) => value === "unauthenticated") ? "unauthenticated" : "unreachable";
61128
62035
  const pinnedByService = isMissing2(releases) ? releases : pinnedDigest(releases, binding);
61129
- const target = isMissing2(pinnedByService) ? binding.releaseDigest : pinnedByService;
61130
- const inSync = installed === null ? "unknown" : installed.digest === target;
62036
+ const target2 = isMissing2(pinnedByService) ? binding.releaseDigest : pinnedByService;
62037
+ const inSync = installed === null ? "unknown" : installed.digest === target2;
61131
62038
  const nextBlock = isMissing2(next) ? next : next.policyStatus === "paused" ? null : next.task === null ? null : { taskId: next.task.taskId, title: next.task.title, milestone: next.task.milestone };
61132
62039
  const policyBlock = !isMissing2(policy) ? { status: policy.status, budgets: budgetsOf(policy), source: "service" } : run3 !== null ? { status: policy, budgets: budgetsOf(run3.policy), source: "run snapshot" } : { status: policy, budgets: policy, source: "none" };
61133
62040
  const taskBlock = local2 === null ? { active: false } : { active: true, taskId: local2.taskId, state: isMissing2(taskView) ? taskView : taskView === null ? "unreachable" : taskView.state, holder: local2.holder };
@@ -61188,16 +62095,16 @@ var ECOSYSTEM_FLAG = { name: "ecosystem", type: "string", enum: DOCS_ECOSYSTEMS,
61188
62095
  var PROJECT_FLAG = { name: "project", type: "string", value: "PROJECT_ID", description: "The project whose lockfile pins choose the version" };
61189
62096
  var common = { needsAuth: true, needsNetwork: true };
61190
62097
  async function authed(inv) {
61191
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61192
- const token = await requireToken(inv.deps, target.storeKey);
61193
- return { target, call: serviceCall(inv.deps, target.service, token.token) };
62098
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62099
+ const token = await requireToken(inv.deps, target2.storeKey);
62100
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
61194
62101
  }
61195
62102
  async function tenantOf(call2, inv) {
61196
62103
  return textFlag(inv, "team") ?? (await call2(WhoAmI, "GET", apiPaths.whoami)).tenantId;
61197
62104
  }
61198
62105
  async function plan(inv, method, path, body2, note) {
61199
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61200
- return planOutcome(inv.deps, dryRunPlan({ service: target.service, requests: [{ method, path, ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
62106
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62107
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method, path, ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
61201
62108
  }
61202
62109
  function libraryArg(inv) {
61203
62110
  const library = inv.positionals[0];
@@ -61555,9 +62462,9 @@ function shape(token) {
61555
62462
  return { tokenId: token.tokenId, label: token.label, scope: scopeText(token), tools: token.tools, expiresAt: token.expiresAt, ...token.revokedAt === void 0 ? {} : { revokedAt: token.revokedAt } };
61556
62463
  }
61557
62464
  async function authed2(inv) {
61558
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61559
- const token = await requireToken(inv.deps, target.storeKey);
61560
- return { target, call: serviceCall(inv.deps, target.service, token.token) };
62465
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62466
+ const token = await requireToken(inv.deps, target2.storeKey);
62467
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
61561
62468
  }
61562
62469
  async function tenantOf2(call2, inv) {
61563
62470
  const given = textFlag(inv, "team");
@@ -61575,28 +62482,28 @@ var authLoginCommand = defineCommand({
61575
62482
  keywords: ["sign in", "log in", "login", "authenticate", "browser", "device code", "credentials", "session", "expired", "reauthenticate"],
61576
62483
  result: AuthLoginResult,
61577
62484
  handler: async (inv) => {
61578
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
62485
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
61579
62486
  if (inv.dryRun) {
61580
62487
  return planOutcome(
61581
62488
  inv.deps,
61582
62489
  dryRunPlan({
61583
- service: target.service,
62490
+ service: target2.service,
61584
62491
  requests: [
61585
62492
  { method: "POST", path: "/auth/device", note: "ask for a device code" },
61586
62493
  { method: "POST", path: "/auth/device/token", note: "poll until the browser approval, then receive the session once" }
61587
62494
  ],
61588
- writes: [{ path: `${describeStore(inv.deps, "keychain")} (account ${target.storeKey})`, action: "write", description: "store the session token; it falls back to a 0600 file when no keychain exists" }],
62495
+ writes: [{ path: `${describeStore(inv.deps, "keychain")} (account ${target2.storeKey})`, action: "write", description: "store the session token; it falls back to a 0600 file when no keychain exists" }],
61589
62496
  notes: ["The browser is opened to approve the code. No credentials are needed for this step."]
61590
62497
  })
61591
62498
  );
61592
62499
  }
61593
62500
  const tokenEnvSet = (inv.deps.env["HARNESS_TOKEN"] ?? "") !== "";
61594
- const login = await deviceLogin(inv.deps, target.service, { openBrowser: inv.values["no-browser"] !== true });
61595
- const how = await saveTokens(inv.deps, target.storeKey, { accessToken: login.accessToken, expiresAt: login.expiresAt });
62501
+ const login = await deviceLogin(inv.deps, target2.service, { openBrowser: inv.values["no-browser"] !== true });
62502
+ const how = await saveTokens(inv.deps, target2.storeKey, { accessToken: login.accessToken, expiresAt: login.expiresAt });
61596
62503
  if (tokenEnvSet) warn(inv.deps, "HARNESS_TOKEN is set and takes priority over this sign-in.");
61597
- say(inv.deps, `Signed in to ${target.service} (stored in the ${how === "keychain" ? "OS keychain" : "0600 credentials file"}).`);
62504
+ say(inv.deps, `Signed in to ${target2.service} (stored in the ${how === "keychain" ? "OS keychain" : "0600 credentials file"}).`);
61598
62505
  return {
61599
- data: { signedIn: true, service: target.service, how, tenantId: login.tenantId, expiresAt: new Date(login.expiresAt).toISOString(), tokenEnvSet, next: "overlane context" }
62506
+ data: { signedIn: true, service: target2.service, how, tenantId: login.tenantId, expiresAt: new Date(login.expiresAt).toISOString(), tokenEnvSet, next: "overlane context" }
61600
62507
  };
61601
62508
  }
61602
62509
  });
@@ -61611,24 +62518,24 @@ var authLogoutCommand = defineCommand({
61611
62518
  keywords: ["sign out", "log out", "logout", "forget credentials", "remove token", "end session"],
61612
62519
  result: AuthLogoutResult,
61613
62520
  handler: async (inv) => {
61614
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
62521
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
61615
62522
  if (inv.dryRun) {
61616
62523
  return planOutcome(
61617
62524
  inv.deps,
61618
62525
  dryRunPlan({
61619
- service: target.service,
62526
+ service: target2.service,
61620
62527
  requests: [{ method: "POST", path: "/v1/auth/logout", note: "revoke the stored session (best effort)" }],
61621
- writes: [{ path: `${describeStore(inv.deps, "keychain")} (account ${target.storeKey})`, action: "delete", description: "delete the stored session" }]
62528
+ writes: [{ path: `${describeStore(inv.deps, "keychain")} (account ${target2.storeKey})`, action: "delete", description: "delete the stored session" }]
61622
62529
  })
61623
62530
  );
61624
62531
  }
61625
62532
  const tokenEnvSet = (inv.deps.env["HARNESS_TOKEN"] ?? "") !== "";
61626
- const loaded = await loadTokens(inv.deps, target.storeKey).catch(() => null);
62533
+ const loaded = await loadTokens(inv.deps, target2.storeKey).catch(() => null);
61627
62534
  let revoked = "not_needed";
61628
62535
  if (loaded !== null && loaded.tokens.accessToken.startsWith(SERVER_TOKEN_PREFIX)) {
61629
62536
  rememberSecret(loaded.tokens.accessToken);
61630
62537
  try {
61631
- const response = await inv.deps.fetch(`${target.service}/v1/auth/logout`, {
62538
+ const response = await inv.deps.fetch(`${target2.service}/v1/auth/logout`, {
61632
62539
  method: "POST",
61633
62540
  headers: { authorization: `Bearer ${loaded.tokens.accessToken}`, accept: "application/json" },
61634
62541
  signal: AbortSignal.timeout(LOGOUT_TIMEOUT_MS)
@@ -61638,11 +62545,11 @@ var authLogoutCommand = defineCommand({
61638
62545
  revoked = "unreachable";
61639
62546
  }
61640
62547
  }
61641
- await deleteTokens(inv.deps, target.storeKey);
62548
+ await deleteTokens(inv.deps, target2.storeKey);
61642
62549
  if (revoked === "unreachable") warn(inv.deps, "The service could not be reached, so the session may stay valid there until it expires.");
61643
62550
  if (tokenEnvSet) warn(inv.deps, "HARNESS_TOKEN is still set in this environment.");
61644
62551
  say(inv.deps, loaded === null ? "Nothing was stored; you were not signed in from this machine." : "Signed out.");
61645
- return { data: { signedOut: loaded !== null, revoked, service: target.service, tokenEnvSet, next: "overlane auth login" } };
62552
+ return { data: { signedOut: loaded !== null, revoked, service: target2.service, tokenEnvSet, next: "overlane auth login" } };
61646
62553
  }
61647
62554
  });
61648
62555
  var authTokenCommand = defineCommand({
@@ -61658,10 +62565,10 @@ var authTokenCommand = defineCommand({
61658
62565
  result: AuthTokenResult,
61659
62566
  text: (d) => [d.token],
61660
62567
  handler: async (inv) => {
61661
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61662
- const found = await findToken(inv.deps, target.storeKey);
61663
- if (found === null) throw new AuthRequiredError(`no credentials for ${target.service}; run \`overlane auth login\` or set HARNESS_TOKEN`, { hint: "overlane auth login" });
61664
- const stored = found.source === "environment" ? null : await loadTokens(inv.deps, target.storeKey).catch(() => null);
62568
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62569
+ const found = await findToken(inv.deps, target2.storeKey);
62570
+ if (found === null) throw new AuthRequiredError(`no credentials for ${target2.service}; run \`overlane auth login\` or set HARNESS_TOKEN`, { hint: "overlane auth login" });
62571
+ const stored = found.source === "environment" ? null : await loadTokens(inv.deps, target2.storeKey).catch(() => null);
61665
62572
  const expiresAt = stored?.tokens.expiresAt;
61666
62573
  return { data: { token: found.token, how: found.source, ...expiresAt === void 0 ? {} : { expiresAt: new Date(expiresAt).toISOString() } } };
61667
62574
  }
@@ -61703,8 +62610,8 @@ var teamInviteCommand = defineCommand({
61703
62610
  const role = textFlag(inv, "role") ?? "member";
61704
62611
  const body2 = { email: email3, role };
61705
62612
  if (inv.dryRun) {
61706
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61707
- return planOutcome(inv.deps, dryRunPlan({ service: target.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/invitations`, body: body2 }] }));
62613
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62614
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/invitations`, body: body2 }] }));
61708
62615
  }
61709
62616
  const { call: call2 } = await authed2(inv);
61710
62617
  const tenantId = await tenantOf2(call2, inv);
@@ -61749,20 +62656,20 @@ var tokenCreateCommand = defineCommand({
61749
62656
  keywords: ["agent token", "api key", "create token", "mint token", "service account", "ci token", "bot credentials", "HARNESS_TOKEN"],
61750
62657
  result: TokenCreateResult,
61751
62658
  handler: async (inv) => {
61752
- const label = textFlag(inv, "label");
61753
- if (label === void 0) throw new UsageError("--label is required", { hint: "overlane schema token create" });
62659
+ const label2 = textFlag(inv, "label");
62660
+ if (label2 === void 0) throw new UsageError("--label is required", { hint: "overlane schema token create" });
61754
62661
  const days = Number(textFlag(inv, "ttl-days") ?? "7");
61755
62662
  if (!Number.isInteger(days) || days < 1 || days > 90) throw new UsageError("--ttl-days must be a whole number from 1 to 90", { hint: "overlane schema token create" });
61756
62663
  const project = textFlag(inv, "project");
61757
62664
  const request = {
61758
- label,
62665
+ label: label2,
61759
62666
  scope: project === void 0 ? { kind: "team" } : { kind: "project", projectId: project },
61760
62667
  tools: listFlag(inv, "tool"),
61761
62668
  ttlSeconds: days * 86400
61762
62669
  };
61763
62670
  if (inv.dryRun) {
61764
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61765
- return planOutcome(inv.deps, dryRunPlan({ service: target.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens`, body: request }] }));
62671
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62672
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens`, body: request }] }));
61766
62673
  }
61767
62674
  const { call: call2 } = await authed2(inv);
61768
62675
  const tenantId = await tenantOf2(call2, inv);
@@ -61808,8 +62715,8 @@ var tokenRevokeCommand = defineCommand({
61808
62715
  const id2 = inv.positionals[0];
61809
62716
  if (id2 === void 0) throw new UsageError("a token ID is required", { hint: "overlane token list" });
61810
62717
  if (inv.dryRun) {
61811
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
61812
- return planOutcome(inv.deps, dryRunPlan({ service: target.service, requests: [{ method: "DELETE", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens/${id2}` }] }));
62718
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62719
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "DELETE", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens/${id2}` }] }));
61813
62720
  }
61814
62721
  const { call: call2 } = await authed2(inv);
61815
62722
  const tenantId = await tenantOf2(call2, inv);
@@ -62047,10 +62954,10 @@ var billingCapsSetCommand = defineCommand({
62047
62954
  result: withDryRun(Cap),
62048
62955
  text: (d) => "limitCents" in d ? [`cap ${d.scope}${d.scopeId === void 0 ? "" : ` ${d.scopeId}`}: ${usd(d.limitCents)} per period`] : [],
62049
62956
  handler: async (inv) => {
62050
- const target = capTarget(inv);
62957
+ const target2 = capTarget(inv);
62051
62958
  const usdText = textFlag(inv, "usd");
62052
62959
  if (usdText === void 0) throw new UsageError("set needs --usd", { hint: "overlane billing caps set --scope team --usd 50" });
62053
- const body2 = { requestId: requestId3(), ...target, limitCents: parseUsdToCents(usdText) };
62960
+ const body2 = { requestId: requestId3(), ...target2, limitCents: parseUsdToCents(usdText) };
62054
62961
  if (inv.dryRun) return planned(inv, "PUT", billingPaths.caps, body2);
62055
62962
  const { call: call2, tenantId } = await connected(inv);
62056
62963
  return { data: await call2(Cap, "PUT", billingPaths.caps(tenantId), body2) };
@@ -62096,12 +63003,12 @@ import { readFileSync as readFileSync8, statSync } from "node:fs";
62096
63003
  import { resolve as resolve8 } from "node:path";
62097
63004
  var common3 = { needsAuth: true, needsNetwork: true };
62098
63005
  var requestId4 = () => `cli-${randomUUID4()}`;
62099
- async function connected2(inv, label) {
62100
- const project = await connectedProject(inv.deps, label);
63006
+ async function connected2(inv, label2) {
63007
+ const project = await connectedProject(inv.deps, label2);
62101
63008
  return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
62102
63009
  }
62103
- async function planned2(inv, label, method, path, body2, note) {
62104
- const project = await offlineProject(inv.deps, label);
63010
+ async function planned2(inv, label2, method, path, body2, note) {
63011
+ const project = await offlineProject(inv.deps, label2);
62105
63012
  return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId, project.binding.projectId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
62106
63013
  }
62107
63014
  var APPROVAL_FLAG = { name: "approval", type: "string", value: "ID", description: "Run an action that an approval covers; get the ID from `overlane guard approvals list`" };
@@ -63022,7 +63929,7 @@ var deployTargetsCommand = defineCommand({
63022
63929
  ...common3,
63023
63930
  keywords: ["deploy targets", "environments", "where do we deploy", "staging production"],
63024
63931
  result: TargetListResponse,
63025
- text: (d) => [...d.targets.map((target) => `target ${target.name} (${target.config.kind}) ${target.targetId}`), ...d.environments.map((env) => `environment ${env.name}${env.protected ? " (protected)" : ""}${env.kind === "preview" ? ` (preview of ${env.branch})` : ""} -> ${env.targetId}`)],
63932
+ text: (d) => [...d.targets.map((target2) => `target ${target2.name} (${target2.config.kind}) ${target2.targetId}`), ...d.environments.map((env) => `environment ${env.name}${env.protected ? " (protected)" : ""}${env.kind === "preview" ? ` (preview of ${env.branch})` : ""} -> ${env.targetId}`)],
63026
63933
  handler: async (inv) => {
63027
63934
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63028
63935
  return { data: await call2(TargetListResponse, "GET", deployPaths.targets(tenantId, projectId)) };
@@ -63074,9 +63981,9 @@ var deployEnvCreateCommand = defineCommand({
63074
63981
  text: (d) => "environment" in d ? [`Created ${d.environment.name}${d.environment.protected ? " (protected)" : ""}.`] : [],
63075
63982
  handler: async (inv) => {
63076
63983
  const name2 = arg3(inv, 0, "an environment name", "overlane deploy env create staging --target site");
63077
- const target = textFlag(inv, "target");
63078
- if (target === void 0) throw new UsageError("--target is required", { hint: "overlane deploy targets" });
63079
- const body2 = { name: name2, targetId: target, ...boolFlag(inv, "protected") ? { protected: true } : {} };
63984
+ const target2 = textFlag(inv, "target");
63985
+ if (target2 === void 0) throw new UsageError("--target is required", { hint: "overlane deploy targets" });
63986
+ const body2 = { name: name2, targetId: target2, ...boolFlag(inv, "protected") ? { protected: true } : {} };
63080
63987
  if (inv.dryRun) return planned2(inv, "deploy", "POST", deployPaths.environments, body2);
63081
63988
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63082
63989
  return { data: await call2(EnvironmentResponse, "POST", deployPaths.environments(tenantId, projectId), body2) };
@@ -63177,12 +64084,12 @@ var deployPreviewCommand = defineCommand({
63177
64084
  text: (d) => "release" in d && d.release !== null ? [releaseLine(d.release)] : [],
63178
64085
  handler: async (inv) => {
63179
64086
  const branch = textFlag(inv, "branch");
63180
- const target = textFlag(inv, "target");
64087
+ const target2 = textFlag(inv, "target");
63181
64088
  const commit = textFlag(inv, "commit");
63182
64089
  const run3 = textFlag(inv, "run");
63183
- if (branch === void 0 || target === void 0 || commit === void 0 || run3 === void 0) throw new UsageError("--branch, --target, --commit and --run are required", { hint: "overlane deploy preview --branch feature/x --target site --commit <sha> --run <run>" });
64090
+ if (branch === void 0 || target2 === void 0 || commit === void 0 || run3 === void 0) throw new UsageError("--branch, --target, --commit and --run are required", { hint: "overlane deploy preview --branch feature/x --target site --commit <sha> --run <run>" });
63184
64091
  const artifact = textFlag(inv, "artifact");
63185
- const body2 = { requestId: requestId4(), branch, targetId: target, commitSha: commit, runId: run3, ...artifact === void 0 ? {} : { artifact } };
64092
+ const body2 = { requestId: requestId4(), branch, targetId: target2, commitSha: commit, runId: run3, ...artifact === void 0 ? {} : { artifact } };
63186
64093
  if (inv.dryRun) return planned2(inv, "deploy", "POST", deployPaths.previews, body2);
63187
64094
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63188
64095
  const result = await call2(DeployResponse, "POST", deployPaths.previews(tenantId, projectId), body2);
@@ -63500,9 +64407,9 @@ var SERVICE_FLAG3 = { name: "service", type: "string", value: "URL", description
63500
64407
  var TEAM_FLAG3 = { name: "team", type: "string", value: "TENANT_ID", description: "The team (tenant) to act on (default: the one your token belongs to)" };
63501
64408
  var common5 = { needsAuth: true, needsNetwork: true };
63502
64409
  async function authed3(inv) {
63503
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
63504
- const token = await requireToken(inv.deps, target.storeKey);
63505
- return { target, call: serviceCall(inv.deps, target.service, token.token) };
64410
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
64411
+ const token = await requireToken(inv.deps, target2.storeKey);
64412
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
63506
64413
  }
63507
64414
  async function tenantOf3(call2, inv) {
63508
64415
  return textFlag(inv, "team") ?? (await call2(WhoAmI, "GET", apiPaths.whoami)).tenantId;
@@ -63513,9 +64420,9 @@ function idArg(inv, what) {
63513
64420
  return id2;
63514
64421
  }
63515
64422
  async function plan2(inv, method, path, body2, note) {
63516
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
64423
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
63517
64424
  const tenant = textFlag(inv, "team") ?? ":your-team";
63518
- return planOutcome(inv.deps, dryRunPlan({ service: target.service, requests: [{ method, path: path(tenant), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
64425
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method, path: path(tenant), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
63519
64426
  }
63520
64427
  var requestId6 = () => `cli-${randomUUID6()}`;
63521
64428
  var memorySearchCommand = defineCommand({
@@ -63766,12 +64673,12 @@ var common6 = { needsAuth: true, needsNetwork: true };
63766
64673
  var requestId7 = () => `cli-${randomUUID7()}`;
63767
64674
  var PRIORITIES = ["P0", "P1", "P2", "P3"];
63768
64675
  var STATUSES = ["backlog", "todo", "in_progress", "in_review", "cancelled"];
63769
- async function send(deps, label, schema, method, path, body2, dryRun) {
64676
+ async function send(deps, label2, schema, method, path, body2, dryRun) {
63770
64677
  if (dryRun) {
63771
- const project2 = await offlineProject(deps, label);
64678
+ const project2 = await offlineProject(deps, label2);
63772
64679
  return planOutcome(deps, dryRunPlan({ service: project2.serviceUrl, requests: [{ method, path: path(project2.binding.tenantId, project2.binding.projectId), ...body2 === void 0 ? {} : { body: body2 } }] }));
63773
64680
  }
63774
- const project = await connectedProject(deps, label);
64681
+ const project = await connectedProject(deps, label2);
63775
64682
  const value = await serviceCall(deps, project.serviceUrl, project.token)(schema, method, path(project.binding.tenantId, project.binding.projectId), body2);
63776
64683
  return { data: value, value };
63777
64684
  }
@@ -64271,8 +65178,8 @@ var reviewRunCommand = defineCommand({
64271
65178
  const diff = await diffOf(inv);
64272
65179
  if (diff !== void 0) {
64273
65180
  const title = textFlag(inv, "title");
64274
- const label = textFlag(inv, "label");
64275
- const source = { kind: "patch", diff: diff.text, ...title === void 0 ? {} : { title }, ...label === void 0 ? {} : { label } };
65181
+ const label2 = textFlag(inv, "label");
65182
+ const source = { kind: "patch", diff: diff.text, ...title === void 0 ? {} : { title }, ...label2 === void 0 ? {} : { label: label2 } };
64276
65183
  const patchBody = { requestId: requestId8(), trigger: "manual", source };
64277
65184
  if (inv.dryRun) return planned4(inv, "POST", reviewPaths.runs, { ...patchBody, source: { ...source, diff: `<${diff.text.length} characters from ${diff.from}>` } }, "runs the deterministic checks on the diff, then an optional model pass; nothing is posted");
64278
65185
  const sent = await connected4(inv);
@@ -64701,11 +65608,11 @@ function closestCommands(specs, typed, limit = 3) {
64701
65608
  let best = Number.POSITIVE_INFINITY;
64702
65609
  for (const [index, candidate] of candidates.entries()) {
64703
65610
  if (candidate.length === 0) continue;
64704
- const target = index === 0 ? path : spec.path[0] ?? path;
64705
- if (index === 1 && candidate === target) continue;
64706
- const prefix = target.startsWith(candidate) && candidate.length >= 2;
64707
- const distance = prefix ? 0.5 : editDistance(candidate, target);
64708
- const allowed = Math.max(2, Math.floor(target.length / 3));
65611
+ const target2 = index === 0 ? path : spec.path[0] ?? path;
65612
+ if (index === 1 && candidate === target2) continue;
65613
+ const prefix = target2.startsWith(candidate) && candidate.length >= 2;
65614
+ const distance = prefix ? 0.5 : editDistance(candidate, target2);
65615
+ const allowed = Math.max(2, Math.floor(target2.length / 3));
64709
65616
  if (distance <= allowed) best = Math.min(best, distance + (index === 1 ? 0.75 : 0));
64710
65617
  }
64711
65618
  if (Number.isFinite(best)) scored.push({ spec, score: best });
@@ -64942,6 +65849,69 @@ async function disconnect(deps, options) {
64942
65849
  // ../cli/src/doctor.ts
64943
65850
  import { resolve as resolve12 } from "node:path";
64944
65851
  init_zod();
65852
+
65853
+ // ../cli/src/rules.ts
65854
+ init_src();
65855
+ import { existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "node:fs";
65856
+ import { dirname as dirname8, join as join21 } from "node:path";
65857
+ function readIfRegular(root, relative4, unsafe) {
65858
+ const full = join21(root, relative4);
65859
+ if (!existsSync5(full) && !isLink(full)) return null;
65860
+ const stat = lstatSync2(full);
65861
+ if (stat.isSymbolicLink() || !stat.isFile()) {
65862
+ unsafe.push(relative4);
65863
+ return null;
65864
+ }
65865
+ return readFileSync12(full, "utf8");
65866
+ }
65867
+ function isLink(path) {
65868
+ try {
65869
+ return lstatSync2(path).isSymbolicLink();
65870
+ } catch {
65871
+ return false;
65872
+ }
65873
+ }
65874
+ function readRules(root) {
65875
+ const unsafe = [];
65876
+ const source = readIfRegular(root, RULES_SOURCE_PATH, unsafe);
65877
+ const files = {};
65878
+ for (const target2 of RULES_TARGETS) files[target2.path] = readIfRegular(root, target2.path, unsafe);
65879
+ return { source, files, unsafe };
65880
+ }
65881
+ function rulesStatus(root) {
65882
+ const read = readRules(root);
65883
+ const report2 = checkRules(read.source, { files: read.files });
65884
+ if (read.unsafe.length > 0 && report2.configured) {
65885
+ const targets = report2.targets.map((t) => read.unsafe.includes(t.path) ? { ...t, state: "damaged", detail: `${t.path} is a symbolic link or not a regular file; replace it with a regular file`, syncable: false } : t);
65886
+ return { report: { ...report2, drifted: true, targets }, unsafe: read.unsafe };
65887
+ }
65888
+ return { report: report2, unsafe: read.unsafe };
65889
+ }
65890
+ function planFromDisk(root, force) {
65891
+ const read = readRules(root);
65892
+ if (read.source === null) return { read, plan: { ok: false, problem: `${RULES_SOURCE_PATH} does not exist; create it with the rules every agent should follow` } };
65893
+ const plan3 = planRulesSync(read.source, { files: read.files }, { force });
65894
+ if (plan3.ok && read.unsafe.length > 0) {
65895
+ const steps = plan3.plan.steps.map((s) => read.unsafe.includes(s.path) ? { ...s, action: "refuse", next: null, reason: `${s.path} is a symbolic link or not a regular file; replace it with a regular file` } : s);
65896
+ return { read, plan: { ok: true, plan: { steps, blocked: steps.some((s) => s.action === "refuse") } } };
65897
+ }
65898
+ return { read, plan: plan3 };
65899
+ }
65900
+ function applyPlan2(root, plan3) {
65901
+ const written = [];
65902
+ for (const step2 of plan3.steps) {
65903
+ if (step2.next === null) continue;
65904
+ const full = join21(root, step2.path);
65905
+ mkdirSync8(dirname8(full), { recursive: true });
65906
+ const temp = `${full}.overlane-tmp`;
65907
+ writeFileSync7(temp, step2.next, { encoding: "utf8", mode: 420 });
65908
+ renameSync5(temp, full);
65909
+ written.push(step2.path);
65910
+ }
65911
+ return written;
65912
+ }
65913
+
65914
+ // ../cli/src/doctor.ts
64945
65915
  var DoctorResult = external_exports.object({
64946
65916
  project: external_exports.object({ provider: external_exports.enum(["overlane", "github", "gitlab"]).optional(), owner: external_exports.string().optional(), name: external_exports.string(), root: external_exports.string() }),
64947
65917
  binding: external_exports.enum(["ok", "missing", "invalid"]),
@@ -64949,6 +65919,8 @@ var DoctorResult = external_exports.object({
64949
65919
  credentials: external_exports.string().optional(),
64950
65920
  reachability: external_exports.enum(["reachable", "unreachable", "rejected", "not checked"]),
64951
65921
  agents: external_exports.array(external_exports.object({ agent: external_exports.string(), installed: external_exports.boolean(), state: external_exports.string(), detail: external_exports.string(), action: external_exports.string().nullable(), completionGate: external_exports.string() })),
65922
+ /** Present when the project has a rules source (.overlane/rules.md): whether each agent file still matches it (MS-C1.3). */
65923
+ rules: external_exports.object({ drifted: external_exports.boolean(), revision: external_exports.string().nullable(), targets: external_exports.array(external_exports.object({ path: external_exports.string(), state: external_exports.string() })), nextCommand: external_exports.string().nullable() }).optional(),
64952
65924
  ready: external_exports.boolean()
64953
65925
  });
64954
65926
  function commandIn(action) {
@@ -64986,9 +65958,15 @@ async function doctor(deps, options) {
64986
65958
  reports.push(await assessAgent(deps, { root, marketplaceRoot: marketplaceRoot2, info: info2, binding, hasToken, service }));
64987
65959
  }
64988
65960
  say(deps);
64989
- for (const line2 of renderReports(reports)) say(deps, line2);
65961
+ for (const line3 of renderReports(reports)) say(deps, line3);
65962
+ const rules = rulesStatus(root).report;
65963
+ if (rules.configured) {
65964
+ say(deps, rules.sourceProblem !== null ? `Rules: source problem - ${rules.sourceProblem}` : rules.drifted ? `Rules: drifted - ${rules.targets.filter((t) => t.state !== "in_sync").map((t) => `${t.path} (${t.state})`).join(", ")}; next: ${rules.nextCommand ?? "overlane rules check"}` : `Rules: in sync at revision ${rules.revision}`);
65965
+ }
64990
65966
  const installedStates = reports.filter((report2, index) => infos[index]?.installed === true).map((report2) => report2.state);
64991
- const ready = installedStates.length > 0 && installedStates.every((state) => state === "ready");
65967
+ const agentsReady = installedStates.length > 0 && installedStates.every((state) => state === "ready");
65968
+ const rulesDrifted = rules.configured && rules.drifted;
65969
+ const ready = agentsReady && !rulesDrifted;
64992
65970
  const data = {
64993
65971
  project: projectSummary(project),
64994
65972
  binding: binding.kind,
@@ -65003,9 +65981,13 @@ async function doctor(deps, options) {
65003
65981
  action: report2.action,
65004
65982
  completionGate: completionGate(report2.agent)
65005
65983
  })),
65984
+ ...rules.configured ? { rules: { drifted: rules.drifted, revision: rules.revision, targets: rules.targets.map((t) => ({ path: t.path, state: t.state })), nextCommand: rules.nextCommand } } : {},
65006
65985
  ready
65007
65986
  };
65008
65987
  if (ready) return { data };
65988
+ if (agentsReady && rulesDrifted) {
65989
+ return { data, error: new CliError(`not ready: the rules files have drifted from the source - ${rules.sourceProblem ?? rules.targets.find((t) => t.state !== "in_sync")?.detail ?? ""}`, { code: "rules_drift", exit: EXIT.conflict, hint: rules.nextCommand ?? "overlane rules check", quiet: true }) };
65990
+ }
65009
65991
  const pending = reports.find((report2) => report2.action !== null);
65010
65992
  return {
65011
65993
  data,
@@ -65021,22 +66003,22 @@ async function doctor(deps, options) {
65021
66003
  // ../cli/src/kit/setup.ts
65022
66004
  init_src();
65023
66005
  import { randomUUID as randomUUID9 } from "node:crypto";
65024
- import { existsSync as existsSync6, readdirSync as readdirSync2 } from "node:fs";
65025
- import { join as join22 } from "node:path";
66006
+ import { existsSync as existsSync7, readdirSync as readdirSync2 } from "node:fs";
66007
+ import { join as join23 } from "node:path";
65026
66008
 
65027
66009
  // ../cli/src/kit/ci.ts
65028
- import { existsSync as existsSync5, readdirSync } from "node:fs";
65029
- import { join as join21 } from "node:path";
66010
+ import { existsSync as existsSync6, readdirSync } from "node:fs";
66011
+ import { join as join22 } from "node:path";
65030
66012
  var ACTION_PINS = {
65031
66013
  checkout: "actions/checkout@v7.0.1",
65032
66014
  setupNode: "actions/setup-node@v7.0.0",
65033
66015
  pnpm: "pnpm/action-setup@v6.1.0",
65034
66016
  setupPython: "actions/setup-python@v7.0.0"
65035
66017
  };
65036
- var CI_FILE = join21(".github", "workflows", "ci.yml");
66018
+ var CI_FILE = join22(".github", "workflows", "ci.yml");
65037
66019
  function hasCi(root) {
65038
66020
  try {
65039
- return readdirSync(join21(root, ".github", "workflows")).some((name2) => /\.ya?ml$/.test(name2));
66021
+ return readdirSync(join22(root, ".github", "workflows")).some((name2) => /\.ya?ml$/.test(name2));
65040
66022
  } catch {
65041
66023
  return false;
65042
66024
  }
@@ -65048,12 +66030,12 @@ function ciWorkflow(root, detected) {
65048
66030
  for (const run3 of detected.typecheck ?? []) commands.push({ name: "Typecheck", run: run3 });
65049
66031
  for (const run3 of detected.test) commands.push({ name: "Test", run: run3 });
65050
66032
  if (!commands.some((command) => command.name !== "Install")) return null;
65051
- const node2 = existsSync5(join21(root, "package.json"));
65052
- const python = existsSync5(join21(root, "pyproject.toml"));
66033
+ const node2 = existsSync6(join22(root, "package.json"));
66034
+ const python = existsSync6(join22(root, "pyproject.toml"));
65053
66035
  const steps = [` - uses: ${ACTION_PINS.checkout}`];
65054
66036
  if (node2) {
65055
66037
  if (detected.install?.startsWith("pnpm")) steps.push(` - uses: ${ACTION_PINS.pnpm}`);
65056
- const version2 = existsSync5(join21(root, ".nvmrc")) ? "node-version-file: .nvmrc" : "node-version: 22";
66038
+ const version2 = existsSync6(join22(root, ".nvmrc")) ? "node-version-file: .nvmrc" : "node-version: 22";
65057
66039
  steps.push(` - uses: ${ACTION_PINS.setupNode}`, " with:", ` ${version2}`);
65058
66040
  }
65059
66041
  if (python) {
@@ -65095,7 +66077,7 @@ var INSTRUCTION_FILES = ["CLAUDE.md", "AGENTS.md"];
65095
66077
  var runPrompt = (projectId) => `Run one bounded step with the run-step skill for project ${projectId}.`;
65096
66078
  function hasSpecKit(root) {
65097
66079
  try {
65098
- return readdirSync2(join22(root, "specs"), { withFileTypes: true }).some((entry) => entry.isDirectory() && existsSync6(join22(root, "specs", entry.name, "tasks.md")));
66080
+ return readdirSync2(join23(root, "specs"), { withFileTypes: true }).some((entry) => entry.isDirectory() && existsSync7(join23(root, "specs", entry.name, "tasks.md")));
65099
66081
  } catch {
65100
66082
  return false;
65101
66083
  }
@@ -65132,7 +66114,7 @@ async function gitOk(deps, root, ...args2) {
65132
66114
  }
65133
66115
  async function dirty(deps, root, paths) {
65134
66116
  const status = await deps.exec("git", ["status", "--porcelain", "--", ...paths], { cwd: root });
65135
- return status.stdout.split("\n").filter((line2) => line2.length > 3).map((line2) => line2.slice(3));
66117
+ return status.stdout.split("\n").filter((line3) => line3.length > 3).map((line3) => line3.slice(3));
65136
66118
  }
65137
66119
  function shellQuote(value) {
65138
66120
  return `'${value.replaceAll("'", "'\\''")}'`;
@@ -65152,8 +66134,8 @@ async function connectKit(deps, options) {
65152
66134
  const project = await detectProject(deps, { name: options.name, provider: options.provider });
65153
66135
  const { root } = project;
65154
66136
  if (await headCommit(deps, root) === null) throw new CliError("the repository has no commit yet; make an initial commit first");
65155
- const planDir = join22(root, PLAN_DIR);
65156
- const planExists = existsSync6(join22(planDir, "project.md"));
66137
+ const planDir = join23(root, PLAN_DIR);
66138
+ const planExists = existsSync7(join23(planDir, "project.md"));
65157
66139
  const ciExists = hasCi(root);
65158
66140
  let plan3;
65159
66141
  let planSource;
@@ -65177,7 +66159,7 @@ async function connectKit(deps, options) {
65177
66159
  }
65178
66160
  const detected = await detectCommands(root);
65179
66161
  const workflow = ciExists ? null : ciWorkflow(root, detected);
65180
- const blocks = INSTRUCTION_FILES.map((file2) => ({ file: file2, before: readText(join22(root, file2)) })).map((entry) => ({ ...entry, after: withBlock2(entry.before) }));
66162
+ const blocks = INSTRUCTION_FILES.map((file2) => ({ file: file2, before: readText(join23(root, file2)) })).map((entry) => ({ ...entry, after: withBlock2(entry.before) }));
65181
66163
  const blockChanges = blocks.filter((entry) => entry.after !== entry.before);
65182
66164
  const firstMilestone = plan3.milestones[0]?.id ?? DEFAULT_RUN_POLICY.buildAheadLimit;
65183
66165
  const branchExists = (await gitOut(deps, root, "rev-parse", "--verify", "--quiet", `refs/heads/${KIT_BRANCH}`)).code === 0;
@@ -65232,18 +66214,18 @@ async function connectKit(deps, options) {
65232
66214
  await gitOk(deps, root, "switch", "-c", KIT_BRANCH);
65233
66215
  if (!planExists) await writePlan(root, plan3);
65234
66216
  const written = [];
65235
- if (workflow !== null && writeIfDifferent(join22(root, CI_FILE), workflow)) written.push(CI_FILE);
65236
- for (const entry of blockChanges) if (writeIfDifferent(join22(root, entry.file), entry.after)) written.push(entry.file);
65237
- const agentConfig = [join22(".claude", "settings.json"), join22(".codex", "config.toml")];
66217
+ if (workflow !== null && writeIfDifferent(join23(root, CI_FILE), workflow)) written.push(CI_FILE);
66218
+ for (const entry of blockChanges) if (writeIfDifferent(join23(root, entry.file), entry.after)) written.push(entry.file);
66219
+ const agentConfig = [join23(".claude", "settings.json"), join23(".codex", "config.toml")];
65238
66220
  const preDirty = new Set(await dirty(deps, root, agentConfig));
65239
- const preExisting = new Set(agentConfig.filter((file2) => existsSync6(join22(root, file2))));
65240
- const connected5 = await connect(deps, { ...options, yes: true });
65241
- if (connected5.error !== void 0 || readBindingFile(root).kind !== "ok") {
66221
+ const preExisting = new Set(agentConfig.filter((file2) => existsSync7(join23(root, file2))));
66222
+ const connected9 = await connect(deps, { ...options, yes: true });
66223
+ if (connected9.error !== void 0 || readBindingFile(root).kind !== "ok") {
65242
66224
  throw new CliError(`connect did not finish; the kit files are on ${KIT_BRANCH}, uncommitted. Fix the cause and run \`overlane connect\`, then commit`, { code: "connect_incomplete", hint: "overlane connect --yes" });
65243
66225
  }
65244
- const connectData = connected5.data !== void 0 && !("dryRun" in connected5.data) ? connected5.data : void 0;
66226
+ const connectData = connected9.data !== void 0 && !("dryRun" in connected9.data) ? connected9.data : void 0;
65245
66227
  for (const file2 of agentConfig) {
65246
- if (!existsSync6(join22(root, file2)) || preDirty.has(file2) || preExisting.has(file2) && (await dirty(deps, root, [file2])).length === 0) continue;
66228
+ if (!existsSync7(join23(root, file2)) || preDirty.has(file2) || preExisting.has(file2) && (await dirty(deps, root, [file2])).length === 0) continue;
65247
66229
  if ((await gitOut(deps, root, "check-ignore", "-q", "--", file2)).code === 0) continue;
65248
66230
  written.push(file2);
65249
66231
  }
@@ -65264,7 +66246,7 @@ async function connectKit(deps, options) {
65264
66246
  say(deps, `Plan synced: ${synced.created.length} created, ${synced.updated.length} updated, ${synced.unchanged.length} unchanged, ${synced.deferred.length} deferred.`);
65265
66247
  const pr = await openPullRequest(deps, root, project.name);
65266
66248
  say(deps);
65267
- for (const line2 of schedulerSetup(binding.binding.projectId)) say(deps, line2);
66249
+ for (const line3 of schedulerSetup(binding.binding.projectId)) say(deps, line3);
65268
66250
  return {
65269
66251
  data: {
65270
66252
  mode: options.mode,
@@ -65289,14 +66271,14 @@ async function openPullRequest(deps, root, _name) {
65289
66271
  const auth = version2.code === 0 ? await deps.exec("gh", ["auth", "status"], { cwd: root }) : version2;
65290
66272
  if (version2.code !== 0 || auth.code !== 0) {
65291
66273
  say(deps, `No authenticated gh found. The work is committed on branch ${KIT_BRANCH}. To open the pull request, run:`);
65292
- for (const line2 of manual) say(deps, ` ${line2}`);
66274
+ for (const line3 of manual) say(deps, ` ${line3}`);
65293
66275
  return { url: null, manual };
65294
66276
  }
65295
66277
  const push = await deps.exec("git", ["push", "-u", "origin", KIT_BRANCH], { cwd: root });
65296
66278
  const pr = push.code === 0 ? await deps.exec("gh", ["pr", "create", "--head", KIT_BRANCH, "--title", PR_TITLE, "--body", PR_BODY], { cwd: root }) : push;
65297
66279
  if (push.code !== 0 || pr.code !== 0) {
65298
66280
  warn(deps, `warning: could not open the pull request (${(pr.stderr || pr.stdout).trim()}). Branch ${KIT_BRANCH} is committed. Run:`);
65299
- for (const line2 of manual) say(deps, ` ${line2}`);
66281
+ for (const line3 of manual) say(deps, ` ${line3}`);
65300
66282
  return { url: null, manual };
65301
66283
  }
65302
66284
  say(deps, `Opened pull request: ${pr.stdout.trim()}`);
@@ -65414,7 +66396,7 @@ async function listReleases(deps) {
65414
66396
  compatSummary(record3),
65415
66397
  record3.pinnedBy.length === 0 ? "-" : record3.pinnedBy.map((id2) => id2 === ctx.binding.projectId ? `${id2} (this)` : id2).join(",")
65416
66398
  ]);
65417
- for (const line2 of table([header, ...rows])) say(deps, line2);
66399
+ for (const line3 of table([header, ...rows])) say(deps, line3);
65418
66400
  return { data };
65419
66401
  }
65420
66402
  async function releaseStatus(deps) {
@@ -65430,9 +66412,9 @@ async function releaseStatus(deps) {
65430
66412
  say(deps, `installed: ${installed === null ? "none recorded" : `${installed.version} ${installed.digest}`}`);
65431
66413
  say(deps, `binding: ${ctx.binding.releaseDigest}`);
65432
66414
  say(deps, `project pin: ${pin ?? `unknown${note}`}`);
65433
- const target = pin ?? ctx.binding.releaseDigest;
65434
- const inSync = installed?.digest === target;
65435
- say(deps, inSync ? "State: in sync." : `State: update available - run \`overlane release update\` (installed ${installed === null ? "none" : shortDigest(installed.digest)}, pinned ${shortDigest(target)}).`);
66415
+ const target2 = pin ?? ctx.binding.releaseDigest;
66416
+ const inSync = installed?.digest === target2;
66417
+ say(deps, inSync ? "State: in sync." : `State: update available - run \`overlane release update\` (installed ${installed === null ? "none" : shortDigest(installed.digest)}, pinned ${shortDigest(target2)}).`);
65436
66418
  const taskRunning = readTaskState(ctx.root) !== null;
65437
66419
  if (taskRunning) say(deps, "A task is running here; an update waits until it is released, paused or submitted.");
65438
66420
  const approvals = readApprovals(deps);
@@ -65472,8 +66454,8 @@ async function updateRelease(deps, input2) {
65472
66454
  }
65473
66455
  const ctx = await context3(deps);
65474
66456
  const pin = pinnedDigest(await ctx.client.list(), ctx.binding);
65475
- const target = input2.to ?? pin;
65476
- return installRelease(deps, ctx, target, { to: input2.to, approve: input2.approve, dryRun: input2.dryRun }, target === pin);
66457
+ const target2 = input2.to ?? pin;
66458
+ return installRelease(deps, ctx, target2, { to: input2.to, approve: input2.approve, dryRun: input2.dryRun }, target2 === pin);
65477
66459
  }
65478
66460
  async function rollbackRelease(deps, input2) {
65479
66461
  if (input2.dryRun) {
@@ -65766,16 +66748,16 @@ var runCommands = [runStartCommand, runNextCommand, runClaimCommand, runDecideCo
65766
66748
  init_src();
65767
66749
  init_zod();
65768
66750
  import { randomUUID as randomUUID10 } from "node:crypto";
65769
- import { mkdirSync as mkdirSync8, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "node:fs";
65770
- import { dirname as dirname8, join as join23 } from "node:path";
65771
- var TASK_POLICY_PATH = join23(".harness", "task-policy.json");
66751
+ import { mkdirSync as mkdirSync9, renameSync as renameSync6, writeFileSync as writeFileSync8 } from "node:fs";
66752
+ import { dirname as dirname9, join as join24 } from "node:path";
66753
+ var TASK_POLICY_PATH = join24(".harness", "task-policy.json");
65772
66754
  function writePolicyFile(root, policy, nowMs) {
65773
- const path = join23(root, TASK_POLICY_PATH);
65774
- mkdirSync8(dirname8(path), { recursive: true });
66755
+ const path = join24(root, TASK_POLICY_PATH);
66756
+ mkdirSync9(dirname9(path), { recursive: true });
65775
66757
  const temp = `${path}.${process.pid}.tmp`;
65776
- writeFileSync7(temp, `${JSON.stringify({ enforcement: policy.enforcement, ...policy.reason === void 0 ? {} : { reason: policy.reason }, checkedAt: new Date(nowMs).toISOString() }, null, 2)}
66758
+ writeFileSync8(temp, `${JSON.stringify({ enforcement: policy.enforcement, ...policy.reason === void 0 ? {} : { reason: policy.reason }, checkedAt: new Date(nowMs).toISOString() }, null, 2)}
65777
66759
  `);
65778
- renameSync5(temp, path);
66760
+ renameSync6(temp, path);
65779
66761
  }
65780
66762
  var TaskNewResult = withDryRun(
65781
66763
  external_exports.object({ taskId: external_exports.string(), version: external_exports.number(), state: external_exports.string(), holder: external_exports.string(), priority: TaskPriority, rank: external_exports.number(), next: external_exports.string() })
@@ -66507,6 +67489,875 @@ var taskHandoffCommand = defineCommand({
66507
67489
  });
66508
67490
  var taskCommands = [taskListCommand, taskStartCommand, taskStatusCommand, taskCheckpointCommand, taskResumeCommand, taskSubmitCommand, taskEvidenceCommand, taskHandoffCommand];
66509
67491
 
67492
+ // ../cli/src/commands/claims.ts
67493
+ init_src();
67494
+ init_zod();
67495
+ import { randomUUID as randomUUID11 } from "node:crypto";
67496
+ var common15 = { needsAuth: true, needsNetwork: true };
67497
+ var requestId10 = () => `cli-${randomUUID11()}`;
67498
+ async function connected5(inv) {
67499
+ const project = await connectedProject(inv.deps, "claim");
67500
+ return { project, call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67501
+ }
67502
+ async function planned5(inv, method, path, body2, note) {
67503
+ const project = await offlineProject(inv.deps, "claim");
67504
+ return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId, project.binding.projectId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
67505
+ }
67506
+ async function git3(deps, args2) {
67507
+ return deps.exec("git", args2, { cwd: deps.paths.cwd });
67508
+ }
67509
+ async function whereAmI(deps) {
67510
+ const head = await git3(deps, ["rev-parse", "HEAD"]);
67511
+ const branch = await git3(deps, ["symbolic-ref", "--short", "-q", "HEAD"]);
67512
+ const sha = head.stdout.trim();
67513
+ return { ...branch.code === 0 && branch.stdout.trim() !== "" ? { branch: branch.stdout.trim() } : {}, ...head.code === 0 && /^[0-9a-f]{40,64}$/.test(sha) ? { headSha: sha } : {} };
67514
+ }
67515
+ var SCOPE_FLAGS = [
67516
+ { name: "path", type: "string", multiple: true, value: "PATH", description: "A file or directory to claim (a directory claims everything under it); repeat for more" },
67517
+ { name: "glob", type: "string", multiple: true, value: "GLOB", description: "A glob such as 'src/billing/**'; repeat for more" },
67518
+ { name: "module", type: "string", multiple: true, value: "NAME", description: "A module, resolved through the code map where indexed; repeat for more" },
67519
+ { name: "paths", type: "string", value: "A,B", description: "For a module the code map does not know: the paths it covers (comma separated)" }
67520
+ ];
67521
+ function scopesOf(inv) {
67522
+ const paths = (textFlag(inv, "paths") ?? "").split(",").map((p) => p.trim()).filter((p) => p !== "");
67523
+ const scopes = [
67524
+ ...listFlag(inv, "path").map((value) => ({ kind: "path", value })),
67525
+ ...listFlag(inv, "glob").map((value) => ({ kind: "glob", value })),
67526
+ ...listFlag(inv, "module").map((value) => ({ kind: "module", value, ...paths.length === 0 ? {} : { paths } }))
67527
+ ];
67528
+ if (scopes.length === 0) throw new UsageError("a claim names its scope", { hint: "overlane claim acquire --task TASK --path src/billing/" });
67529
+ return scopes;
67530
+ }
67531
+ function leaseFlag(inv) {
67532
+ const text = textFlag(inv, "lease-seconds");
67533
+ if (text === void 0) return void 0;
67534
+ if (!/^\d+$/.test(text) || Number(text) < 60) throw new UsageError("--lease-seconds is a whole number of at least 60");
67535
+ return Number(text);
67536
+ }
67537
+ var claimAcquireCommand = defineCommand({
67538
+ path: ["claim", "acquire"],
67539
+ summary: "Claim the files or modules a task will change; an overlapping claim by another agent is queued or refused",
67540
+ description: "Claims scope before changing it. Paths and globs are repository-relative; a module is resolved through the code map where the project is indexed, else the --paths you give, else the claim is refused without guessing. Under the project's policy an overlapping claim by another agent is queued behind it (serialise, the default), refused (exit 5) or allowed with a warning. Records your branch and head so the pre-merge check can compare them. The lease ends if you stop renewing it.",
67541
+ flags: [
67542
+ { name: "task", type: "string", required: true, value: "TASK", description: "The task this work belongs to" },
67543
+ ...SCOPE_FLAGS,
67544
+ { name: "mode", type: "string", enum: ["exclusive", "shared"], value: "MODE", description: "exclusive (default) or shared read access" },
67545
+ { name: "agent", type: "string", value: "NAME", description: "A label such as codex or claude" },
67546
+ { name: "lease-seconds", type: "string", value: "N", description: "Lease length, 60 to 14400; the project default otherwise" }
67547
+ ],
67548
+ examples: ["overlane claim acquire --task task-12 --path apps/api-worker/src/billing/", "overlane claim acquire --task task-12 --module billing --glob 'docs/billing/**'"],
67549
+ effect: "write",
67550
+ ...common15,
67551
+ keywords: ["claim", "lease", "lock files", "avoid merge conflict", "two agents same file", "reserve scope", "serialise agents"],
67552
+ result: withDryRun(ClaimOutcome),
67553
+ text: (d) => "decision" in d ? [`${d.decision.toUpperCase()}${d.claim === null ? "" : ` claim ${d.claim.id} lease until ${d.claim.leaseExpiresAt}`}`, d.message, ...d.overlaps.map((o) => ` overlaps ${o.claimId} (task ${o.taskId}, ${o.agent ?? o.holder}): ${o.patterns.slice(0, 3).join(", ")}`), ...d.nextCommand === null ? [] : [`next: ${d.nextCommand}`]] : [],
67554
+ handler: async (inv) => {
67555
+ const task = textFlag(inv, "task");
67556
+ if (task === void 0) throw new UsageError("claim acquire needs --task", { hint: "overlane claim acquire --task TASK --path src/" });
67557
+ const here = await whereAmI(inv.deps);
67558
+ const lease = leaseFlag(inv);
67559
+ const body2 = {
67560
+ requestId: requestId10(),
67561
+ taskId: task,
67562
+ scopes: scopesOf(inv),
67563
+ mode: textFlag(inv, "mode") ?? "exclusive",
67564
+ ...textFlag(inv, "agent") === void 0 ? {} : { agent: textFlag(inv, "agent") },
67565
+ ...lease === void 0 ? {} : { leaseSeconds: lease },
67566
+ ...here
67567
+ };
67568
+ if (inv.dryRun) return planned5(inv, "POST", claimPaths.claims, body2, "claims the scope; nothing changes in your files");
67569
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67570
+ const outcome = await call2(ClaimOutcome, "POST", claimPaths.claims(tenantId, projectId), body2);
67571
+ if (outcome.decision === "refused") return { data: outcome, exit: EXIT.conflict, error: new CliError(outcome.message, { code: "scope_claimed", exit: EXIT.conflict, ...outcome.nextCommand === null ? {} : { hint: outcome.nextCommand }, quiet: true }) };
67572
+ return { data: outcome };
67573
+ }
67574
+ });
67575
+ var claimListCommand = defineCommand({
67576
+ path: ["claim", "list"],
67577
+ summary: "List the project's claims: who holds which scope, who waits, and when each lease ends",
67578
+ description: "Lists claims newest first with their status (active, queued, released, expired), scope patterns, lease end and the project's overlap policy.",
67579
+ flags: [
67580
+ { name: "task", type: "string", value: "TASK", description: "Only this task's claims" },
67581
+ { name: "status", type: "string", enum: ["active", "queued", "released", "expired"], value: "STATUS", description: "Only claims in this state" }
67582
+ ],
67583
+ examples: ["overlane claim list", "overlane claim list --status active --json"],
67584
+ effect: "read",
67585
+ ...common15,
67586
+ keywords: ["who is working on", "claims", "queued", "lease", "scope held"],
67587
+ result: ClaimListResponse,
67588
+ text: (d) => [`policy: ${d.policy}`, ...d.claims.map((c) => `${c.id} ${c.status.padEnd(8)} task ${c.taskId} ${c.agent ?? c.holder} ${c.patterns.slice(0, 3).join(", ")}${c.status === "active" ? ` until ${c.leaseExpiresAt}` : ""}`)],
67589
+ handler: async (inv) => {
67590
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67591
+ const params = new URLSearchParams();
67592
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
67593
+ if (textFlag(inv, "status") !== void 0) params.set("status", textFlag(inv, "status"));
67594
+ const query = params.toString();
67595
+ return { data: await call2(ClaimListResponse, "GET", `${claimPaths.claims(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
67596
+ }
67597
+ });
67598
+ var claimRenewCommand = defineCommand({
67599
+ path: ["claim", "renew"],
67600
+ summary: "Extend a claim's lease and report the branch head you are on",
67601
+ description: "Keeps a claim alive while the work continues. Also records the current branch and head so the pre-merge check compares the right commits. A claim that reached its maximum lifetime cannot be renewed; acquire it again.",
67602
+ flags: [
67603
+ { name: "claim", type: "string", required: true, value: "ID", description: "The claim to renew" },
67604
+ { name: "lease-seconds", type: "string", value: "N", description: "New lease length from now" }
67605
+ ],
67606
+ examples: ["overlane claim renew --claim clm_abc123"],
67607
+ effect: "write",
67608
+ ...common15,
67609
+ keywords: ["heartbeat", "keep lease", "extend claim"],
67610
+ result: withDryRun(Claim),
67611
+ text: (d) => "id" in d ? [`claim ${d.id} ${d.status}, lease until ${d.leaseExpiresAt}`] : [],
67612
+ handler: async (inv) => {
67613
+ const id2 = textFlag(inv, "claim");
67614
+ if (id2 === void 0) throw new UsageError("claim renew needs --claim", { hint: "overlane claim list" });
67615
+ const lease = leaseFlag(inv);
67616
+ const here = await whereAmI(inv.deps);
67617
+ const body2 = { ...lease === void 0 ? {} : { leaseSeconds: lease }, ...here };
67618
+ if (inv.dryRun) return planned5(inv, "POST", (t, p) => claimPaths.renew(t, p, id2), body2);
67619
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67620
+ return { data: await call2(Claim, "POST", claimPaths.renew(tenantId, projectId, id2), body2) };
67621
+ }
67622
+ });
67623
+ var claimReleaseCommand = defineCommand({
67624
+ path: ["claim", "release"],
67625
+ summary: "Release a claim so a queued agent can take the scope",
67626
+ description: "Releases the scope when the task is done or abandoned. The next queued claim on overlapping scope becomes active. Only the holder can release before the lease ends.",
67627
+ flags: [
67628
+ { name: "claim", type: "string", required: true, value: "ID", description: "The claim to release" },
67629
+ { name: "done", type: "boolean", description: "Record the release as the task being done" }
67630
+ ],
67631
+ examples: ["overlane claim release --claim clm_abc123 --done"],
67632
+ effect: "write",
67633
+ ...common15,
67634
+ keywords: ["release claim", "done with files", "free scope"],
67635
+ result: withDryRun(Claim),
67636
+ text: (d) => "id" in d ? [`claim ${d.id} ${d.status}${d.releaseReason === void 0 ? "" : ` (${d.releaseReason})`}`] : [],
67637
+ handler: async (inv) => {
67638
+ const id2 = textFlag(inv, "claim");
67639
+ if (id2 === void 0) throw new UsageError("claim release needs --claim", { hint: "overlane claim list" });
67640
+ const body2 = { reason: inv.values["done"] === true ? "done" : "released" };
67641
+ if (inv.dryRun) return planned5(inv, "POST", (t, p) => claimPaths.release(t, p, id2), body2);
67642
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67643
+ return { data: await call2(Claim, "POST", claimPaths.release(tenantId, projectId, id2), body2) };
67644
+ }
67645
+ });
67646
+ var claimReadinessCommand = defineCommand({
67647
+ path: ["claim", "readiness"],
67648
+ summary: "Say whether this claim's branch is clear to merge as far as claims and the conflict check can tell",
67649
+ description: "Combines the claim's state and the recorded `git merge-tree` checks of its branch head against every other open branch. Not ready (exit 5) when the claim is queued, a check is missing for the current heads, a check failed or a conflict is recorded.",
67650
+ flags: [{ name: "claim", type: "string", required: true, value: "ID", description: "The claim to evaluate" }],
67651
+ examples: ["overlane claim readiness --claim clm_abc123"],
67652
+ effect: "read",
67653
+ ...common15,
67654
+ keywords: ["ready to merge", "merge conflict check", "pre-merge", "can i merge"],
67655
+ result: MergeReadiness,
67656
+ text: (d) => [d.ready ? `READY (${d.checkedPairs} pair${d.checkedPairs === 1 ? "" : "s"} checked)` : "NOT READY", ...d.blockers.map((b) => ` ${b.code}: ${b.message}${b.nextCommand === null ? "" : ` -> ${b.nextCommand}`}`)],
67657
+ handler: async (inv) => {
67658
+ const id2 = textFlag(inv, "claim");
67659
+ if (id2 === void 0) throw new UsageError("claim readiness needs --claim", { hint: "overlane claim list" });
67660
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67661
+ const data = await call2(MergeReadiness, "GET", claimPaths.readiness(tenantId, projectId, id2));
67662
+ return data.ready ? { data } : { data, exit: EXIT.conflict, error: new CliError(`not ready to merge: ${data.blockers[0]?.message ?? "see blockers"}`, { code: data.blockers[0]?.code ?? "not_ready", exit: EXIT.conflict, ...data.blockers[0]?.nextCommand ? { hint: data.blockers[0].nextCommand } : {}, quiet: true }) };
67663
+ }
67664
+ });
67665
+ var CheckedPair = external_exports.object({ a: external_exports.object({ ref: external_exports.string(), sha: external_exports.string() }), b: external_exports.object({ ref: external_exports.string(), sha: external_exports.string() }), result: MergeTreeResult, recorded: external_exports.boolean() }).strict();
67666
+ var ConflictsResult = external_exports.object({ checks: external_exports.array(CheckedPair), conflicts: external_exports.number().int().nonnegative(), errors: external_exports.number().int().nonnegative() }).strict();
67667
+ async function resolveRef2(deps, ref) {
67668
+ const parsed = await git3(deps, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
67669
+ const sha = parsed.stdout.trim();
67670
+ return parsed.code === 0 && /^[0-9a-f]{40,64}$/.test(sha) ? { ref, sha } : { ref, error: `${ref} is not a commit in this checkout` };
67671
+ }
67672
+ async function runMergeTree(deps, a, b) {
67673
+ const out2 = await git3(deps, mergeTreeArgs(a, b));
67674
+ const result = parseMergeTree(out2.stdout, out2.code, out2.stderr);
67675
+ if (result.status === "error" && /unknown option|usage: git merge-tree/i.test(out2.stderr)) return { status: "error", files: [], message: "this git does not support `merge-tree --write-tree` (needs 2.38 or later)" };
67676
+ return result;
67677
+ }
67678
+ var claimConflictsCommand = defineCommand({
67679
+ path: ["claim", "conflicts"],
67680
+ summary: "Check two branch heads for a merge conflict with git merge-tree, before either pull request merges",
67681
+ description: "Runs `git merge-tree --write-tree` between two commits in this checkout, which merges them in memory and touches neither branch nor the working tree, and records the result so `claim readiness` can use it. With --all it checks every pair of open branches that has no check for its current heads. Exit 5 when a conflict is found. Needs git 2.38 or later; both commits must be present locally (fetch first).",
67682
+ flags: [
67683
+ { name: "a", type: "string", value: "REF", description: "First branch, tag or commit" },
67684
+ { name: "b", type: "string", value: "REF", description: "Second branch, tag or commit" },
67685
+ { name: "all", type: "boolean", description: "Check every open pair that has no check for its current heads" },
67686
+ { name: "no-record", type: "boolean", description: "Print the result without recording it (works offline)" }
67687
+ ],
67688
+ examples: ["overlane claim conflicts --a feat/billing --b feat/review", "overlane claim conflicts --all"],
67689
+ effect: "write",
67690
+ needsAuth: false,
67691
+ needsNetwork: false,
67692
+ keywords: ["merge conflict", "merge-tree", "will these branches conflict", "pre-merge check", "conflict between branches"],
67693
+ result: withDryRun(ConflictsResult),
67694
+ text: (d) => "checks" in d ? [...d.checks.map((c) => `${c.result.status.toUpperCase().padEnd(8)} ${c.a.ref} x ${c.b.ref}${c.result.files.length > 0 ? ` ${c.result.files.map((f) => f.path).slice(0, 5).join(", ")}` : ""}${c.result.message === void 0 ? "" : ` ${c.result.message}`}${c.recorded ? "" : " (not recorded)"}`), `${d.conflicts} conflict${d.conflicts === 1 ? "" : "s"}, ${d.errors} error${d.errors === 1 ? "" : "s"}`] : [],
67695
+ handler: async (inv) => {
67696
+ const a = textFlag(inv, "a");
67697
+ const b = textFlag(inv, "b");
67698
+ const all = inv.values["all"] === true;
67699
+ if (!all && (a === void 0 || b === void 0)) throw new UsageError("give --a and --b, or --all", { hint: "overlane claim conflicts --a feat/one --b feat/two" });
67700
+ if (inv.dryRun) {
67701
+ return planOutcome(inv.deps, dryRunPlan({ commands: [`git ${mergeTreeArgs(a ?? "<branch a>", b ?? "<branch b>").join(" ")}`], notes: ["git merge-tree --write-tree writes tree objects into this repository's object store and changes no branch or file; the result is recorded with the service."] }));
67702
+ }
67703
+ const record3 = inv.values["no-record"] !== true;
67704
+ const session = record3 || all ? await connected5(inv) : null;
67705
+ const pairs = [];
67706
+ const errors = [];
67707
+ const unresolved = (ref, message) => ({ a: { ref, sha: "0".repeat(40) }, b: { ref, sha: "0".repeat(40) }, result: { status: "error", files: [], message }, recorded: false });
67708
+ if (all) {
67709
+ const PendingList = external_exports.object({ pairs: external_exports.array(external_exports.object({ a: external_exports.object({ claimId: external_exports.string(), ref: external_exports.string(), headSha: external_exports.string() }), b: external_exports.object({ claimId: external_exports.string(), ref: external_exports.string(), headSha: external_exports.string() }) }).passthrough()) }).passthrough();
67710
+ const pending = await session.call(PendingList, "GET", claimPaths.pending(session.tenantId, session.projectId));
67711
+ for (const pair of pending.pairs) pairs.push({ a: { ref: pair.a.ref, sha: pair.a.headSha, claimId: pair.a.claimId }, b: { ref: pair.b.ref, sha: pair.b.headSha, claimId: pair.b.claimId } });
67712
+ } else {
67713
+ const left = await resolveRef2(inv.deps, a);
67714
+ const right = await resolveRef2(inv.deps, b);
67715
+ if ("error" in left) errors.push(unresolved(left.ref, left.error));
67716
+ else if ("error" in right) errors.push(unresolved(right.ref, right.error));
67717
+ else pairs.push({ a: left, b: right });
67718
+ }
67719
+ const checks = [...errors];
67720
+ for (const pair of pairs) {
67721
+ const result = await runMergeTree(inv.deps, pair.a.sha, pair.b.sha);
67722
+ let recorded = false;
67723
+ if (record3 && session !== null) {
67724
+ await session.call(MergeCheckRecord, "POST", claimPaths.checks(session.tenantId, session.projectId), { requestId: requestId10(), a: pair.a, b: pair.b, result });
67725
+ recorded = true;
67726
+ }
67727
+ checks.push({ a: { ref: pair.a.ref, sha: pair.a.sha }, b: { ref: pair.b.ref, sha: pair.b.sha }, result, recorded });
67728
+ }
67729
+ const conflicts = checks.filter((c) => c.result.status === "conflict").length;
67730
+ const failures = checks.filter((c) => c.result.status === "error").length;
67731
+ const data = { checks, conflicts, errors: failures };
67732
+ if (conflicts > 0) return { data, exit: EXIT.conflict, error: new CliError(`${conflicts} merge conflict${conflicts === 1 ? "" : "s"} found between open branches`, { code: "merge_conflict", exit: EXIT.conflict, hint: "overlane claim list", quiet: true }) };
67733
+ if (failures > 0) return { data, exit: EXIT.failure, error: new CliError(checks.find((c) => c.result.status === "error")?.result.message ?? "the merge check failed", { code: "merge_check_failed", exit: EXIT.failure, quiet: true }) };
67734
+ return { data };
67735
+ }
67736
+ });
67737
+ var claimCommands = [claimAcquireCommand, claimListCommand, claimRenewCommand, claimReleaseCommand, claimReadinessCommand, claimConflictsCommand];
67738
+
67739
+ // ../cli/src/commands/budget.ts
67740
+ init_src();
67741
+ init_zod();
67742
+ import { randomUUID as randomUUID12 } from "node:crypto";
67743
+ var common16 = { needsAuth: true, needsNetwork: true };
67744
+ var requestId11 = () => `cli-${randomUUID12()}`;
67745
+ async function connected6(inv) {
67746
+ const project = await connectedProject(inv.deps, "budget");
67747
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67748
+ }
67749
+ async function planned6(inv, method, path, body2, note) {
67750
+ const project = await offlineProject(inv.deps, "budget");
67751
+ return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
67752
+ }
67753
+ var usd2 = (cents) => `$${Math.trunc(cents / 100)}.${String(cents % 100).padStart(2, "0")}`;
67754
+ function target(inv) {
67755
+ const scope = textFlag(inv, "scope");
67756
+ const id2 = textFlag(inv, "id");
67757
+ if (scope === void 0) throw new UsageError("this command needs --scope", { hint: "overlane budget set --scope task --id TASK --usd 5" });
67758
+ if (scope === "team" && id2 !== void 0) throw new UsageError("a team budget has no --id");
67759
+ if (scope !== "team" && id2 === void 0) throw new UsageError(`a ${scope} budget needs --id`, { hint: `overlane budget set --scope ${scope} --id <${scope} id> --usd 5` });
67760
+ return { scope, ...id2 === void 0 ? {} : { scopeId: id2 } };
67761
+ }
67762
+ var label = (scope, scopeId) => scope === "team" ? "team" : `${scope} ${scopeId}`;
67763
+ var Show = external_exports.object({ tenantId: external_exports.string(), limits: external_exports.array(BudgetLimit), summary: BudgetSummary }).strict();
67764
+ var SetResult2 = external_exports.object({ tenantId: external_exports.string(), limit: BudgetLimit, change: external_exports.enum(["first", "lower", "same", "raise", "remove"]), previousCents: external_exports.number().int().nullable() }).strict();
67765
+ var budgetShowCommand = defineCommand({
67766
+ path: ["budget", "show"],
67767
+ summary: "Show budgets per task, agent, project and team, with what is spent and what is left",
67768
+ description: "Lists every limit with the spend counted against it this billing period (a task's budget counts its whole life). Spending caps set with `overlane billing caps` appear here too, read through the same check.",
67769
+ examples: ["overlane budget show", "overlane budget show --json"],
67770
+ effect: "read",
67771
+ ...common16,
67772
+ keywords: ["budget", "how much left", "spend per task", "cap", "limit"],
67773
+ result: Show,
67774
+ text: (d) => d.summary.lines.length === 0 ? ["No budgets set: nothing stops a run except plan allowances. Set one: overlane budget set --scope team --usd 50"] : d.summary.lines.map((l) => `${label(l.scope, l.scopeId).padEnd(28)} ${usd2(l.spentCents)} of ${usd2(l.limitCents)} (${usd2(Math.max(l.remainingCents, 0))} left)${l.source === "billing_cap" ? " billing cap" : ""}`),
67775
+ handler: async (inv) => {
67776
+ const { call: call2, tenantId } = await connected6(inv);
67777
+ return { data: await call2(Show, "GET", budgetPaths.root(tenantId)) };
67778
+ }
67779
+ });
67780
+ var budgetSetCommand = defineCommand({
67781
+ path: ["budget", "set"],
67782
+ summary: "Set a budget for a task, an agent, a project or the team",
67783
+ description: "Sets the most that may be spent at that scope. A first limit or a lower one takes effect at once. Raising a limit is spend above the budget: an agent needs an independent agent's approval (set OVERLANE_APPROVAL to the approval ID the refusal names), a person does not.",
67784
+ flags: [
67785
+ { name: "scope", type: "string", required: true, enum: BUDGET_SCOPES, value: "SCOPE", description: "task, agent, project or team" },
67786
+ { name: "id", type: "string", value: "ID", description: "The task, agent token or project (not for team)" },
67787
+ { name: "usd", type: "string", required: true, value: "AMOUNT", description: "Dollars, for example 5 or 12.50" }
67788
+ ],
67789
+ examples: ["overlane budget set --scope task --id task-12 --usd 5", "overlane budget set --scope team --usd 200 --dry-run"],
67790
+ effect: "write",
67791
+ ...common16,
67792
+ keywords: ["set budget", "limit spend", "cap a run", "stop at"],
67793
+ result: withDryRun(SetResult2),
67794
+ text: (d) => "limit" in d ? [`${label(d.limit.scope, d.limit.scopeId)}: ${usd2(d.limit.limitCents)} (${d.change}${d.previousCents === null ? "" : `, was ${usd2(d.previousCents)}`})`] : [],
67795
+ handler: async (inv) => {
67796
+ const usdText = textFlag(inv, "usd");
67797
+ if (usdText === void 0) throw new UsageError("set needs --usd", { hint: "overlane budget set --scope team --usd 50" });
67798
+ const limitCents = parseUsdToCents(usdText);
67799
+ if (limitCents < 1) throw new UsageError("--usd must be more than zero");
67800
+ const body2 = { requestId: requestId11(), ...target(inv), limitCents };
67801
+ if (inv.dryRun) return planned6(inv, "PUT", budgetPaths.root, body2, "raising an existing limit needs an independent agent's approval (class spend) when an agent runs it");
67802
+ const { call: call2, tenantId } = await connected6(inv);
67803
+ return { data: await call2(SetResult2, "PUT", budgetPaths.root(tenantId), body2) };
67804
+ }
67805
+ });
67806
+ var budgetCheckCommand = defineCommand({
67807
+ path: ["budget", "check"],
67808
+ summary: "Ask before spending: exits 6 and records a stop when the call would reach a budget",
67809
+ description: "Asks whether a call estimated at --usd may go ahead for this task, agent and project. The estimate is counted: spent plus the estimate at or over a limit refuses, and the answer names the tightest limit. A refusal exits 6, stores a record (see `budget stops`) and prints the next command. With --record an allowed estimate is counted as spent so the next check sees it. An agent token is always checked as itself.",
67810
+ flags: [
67811
+ { name: "usd", type: "string", required: true, value: "AMOUNT", description: "The call's estimated cost in dollars, for example 0.40" },
67812
+ { name: "task", type: "string", value: "TASK", description: "The task this call belongs to" },
67813
+ { name: "agent", type: "string", value: "ID", description: "The agent token, for a person checking for an agent" },
67814
+ { name: "kind", type: "string", value: "KIND", description: "What the call is, for the record (inference, ci, review)" },
67815
+ { name: "meter", type: "string", enum: ["reviews", "ciMinutes", "docsCalls", "hostedInference"], value: "METER", description: "Also check the plan allowance for this meter" },
67816
+ { name: "record", type: "boolean", description: "Count the estimate as spent when allowed" }
67817
+ ],
67818
+ examples: ["overlane budget check --usd 0.40 --task task-12", "overlane budget check --usd 0.05 --task task-12 --record --kind inference"],
67819
+ effect: "write",
67820
+ ...common16,
67821
+ keywords: ["can i spend", "budget gate", "stop at cap", "exit 6", "run cost"],
67822
+ result: withDryRun(BudgetDecision),
67823
+ text: (d) => "allowed" in d ? [`${d.allowed ? "ALLOWED" : "STOPPED"} ${d.message}`, ...d.nextCommand === null ? [] : [`next: ${d.nextCommand}`], ...d.stopId === null ? [] : [`record: ${d.stopId}`]] : [],
67824
+ handler: async (inv) => {
67825
+ const usdText = textFlag(inv, "usd");
67826
+ if (usdText === void 0) throw new UsageError("check needs --usd", { hint: "overlane budget check --usd 0.40 --task task-12" });
67827
+ const body2 = {
67828
+ requestId: requestId11(),
67829
+ estimateCents: parseUsdToCents(usdText),
67830
+ ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") },
67831
+ ...textFlag(inv, "agent") === void 0 ? {} : { agentId: textFlag(inv, "agent") },
67832
+ ...textFlag(inv, "kind") === void 0 ? {} : { kind: textFlag(inv, "kind") },
67833
+ ...textFlag(inv, "meter") === void 0 ? {} : { meter: textFlag(inv, "meter") },
67834
+ commit: boolFlag(inv, "record")
67835
+ };
67836
+ if (inv.dryRun) return planned6(inv, "POST", budgetPaths.check, body2, "asks the service; a refusal stores a stop record");
67837
+ const { call: call2, tenantId, projectId } = await connected6(inv);
67838
+ const decision = await call2(BudgetDecision, "POST", budgetPaths.check(tenantId), { ...body2, projectId });
67839
+ return decision.allowed ? { data: decision } : { data: decision, exit: EXIT.blocked, error: new CliError(decision.message, { code: decision.reason ?? "budget", exit: EXIT.blocked, ...decision.nextCommand === null ? {} : { hint: decision.nextCommand } }) };
67840
+ }
67841
+ });
67842
+ var budgetStopsCommand = defineCommand({
67843
+ path: ["budget", "stops"],
67844
+ summary: "List the runs a budget stopped, newest first",
67845
+ description: "Every refusal is a stored record: when, which limit, what was spent, what the call was estimated at, and who asked. Read-only for every role.",
67846
+ flags: [{ name: "limit", type: "string", value: "N", description: "How many to list, default 50" }],
67847
+ examples: ["overlane budget stops", "overlane budget stops --limit 10 --json"],
67848
+ effect: "read",
67849
+ ...common16,
67850
+ keywords: ["stopped runs", "budget stops", "what hit the cap", "refusals"],
67851
+ result: BudgetStopList,
67852
+ text: (d) => d.stops.length === 0 ? ["No stops recorded."] : d.stops.map((s) => `${s.at} ${s.reason} ${s.scope === null ? "" : label(s.scope, s.scopeId ?? "")} ${s.spentCents === null ? "" : `${usd2(s.spentCents)} of ${usd2(s.limitCents ?? 0)}`} call ${usd2(s.estimateCents)} ${s.id}`),
67853
+ handler: async (inv) => {
67854
+ const { call: call2, tenantId } = await connected6(inv);
67855
+ const limit = textFlag(inv, "limit");
67856
+ if (limit !== void 0 && !/^\d+$/.test(limit)) throw new UsageError("--limit is a whole number");
67857
+ return { data: await call2(BudgetStopList, "GET", `${budgetPaths.stops(tenantId)}${limit === void 0 ? "" : `?limit=${limit}`}`) };
67858
+ }
67859
+ });
67860
+ var budgetCommands = [budgetShowCommand, budgetSetCommand, budgetCheckCommand, budgetStopsCommand];
67861
+
67862
+ // ../cli/src/commands/rules.ts
67863
+ init_src();
67864
+ init_zod();
67865
+ var common17 = { needsAuth: false, needsNetwork: false };
67866
+ var Action2 = external_exports.enum(["create", "append", "replace", "none", "refuse", "force_replace"]);
67867
+ var SyncResult2 = external_exports.object({
67868
+ revision: external_exports.string().nullable(),
67869
+ steps: external_exports.array(external_exports.object({ id: external_exports.string(), path: external_exports.string(), action: Action2, reason: external_exports.string(), replaces: external_exports.object({ lines: external_exports.number().int(), firstLines: external_exports.array(external_exports.string()) }).optional(), written: external_exports.boolean() }).passthrough()),
67870
+ written: external_exports.array(external_exports.string()),
67871
+ blocked: external_exports.boolean()
67872
+ }).strict();
67873
+ var rulesSyncCommand = defineCommand({
67874
+ path: ["rules", "sync"],
67875
+ summary: "Write the project's rules source to AGENTS.md, CLAUDE.md, Cursor, Gemini and the Gemini settings",
67876
+ description: `Reads ${RULES_SOURCE_PATH} and writes it into one managed region in each agent's file. Missing files are created, a hand-written file gets the region appended with every existing line kept, and an out-of-date region is updated. A region someone edited by hand is never overwritten silently: sync refuses with exit 5 and writes nothing, until the change is moved into the source or --overwrite-edits is given, which first prints the lines it replaces. The Gemini settings only gain the missing names in context.fileName. A symbolic link in the way is refused.`,
67877
+ flags: [{ name: "overwrite-edits", type: "boolean", description: "Replace a region that was edited by hand (prints what it replaces first)" }],
67878
+ examples: ["overlane rules sync", "overlane rules sync --dry-run", "overlane rules sync --overwrite-edits"],
67879
+ effect: "write",
67880
+ ...common17,
67881
+ keywords: ["rules", "AGENTS.md", "CLAUDE.md", "cursor rules", "gemini", "one source of rules", "sync instructions", "agent instructions"],
67882
+ result: withDryRun(SyncResult2),
67883
+ text: (d) => "steps" in d ? d.steps.map((s) => `${s.action.toUpperCase().padEnd(13)} ${s.path} ${s.reason}`) : [],
67884
+ handler: async (inv) => {
67885
+ const project = await detectProject(inv.deps);
67886
+ const root = project.root;
67887
+ const force = boolFlag(inv, "overwrite-edits");
67888
+ const { read, plan: plan3 } = planFromDisk(root, force);
67889
+ if (!plan3.ok && inv.dryRun) return planOutcome(inv.deps, dryRunPlan({ writes: RULES_TARGETS.map((t) => ({ path: t.path, action: "write", description: `would be written from ${RULES_SOURCE_PATH}` })), notes: [`Would stop: ${plan3.problem}`] }));
67890
+ if (!plan3.ok) throw new CliError(plan3.problem, { code: "rules_source_invalid", exit: EXIT.conflict, hint: `create ${RULES_SOURCE_PATH} with the rules every agent should follow, then run overlane rules sync` });
67891
+ if (inv.dryRun) {
67892
+ return planOutcome(
67893
+ inv.deps,
67894
+ dryRunPlan({
67895
+ writes: plan3.plan.steps.filter((s) => s.next !== null).map((s) => ({ path: s.path, action: s.action === "append" ? "append" : "write", description: s.reason })),
67896
+ notes: plan3.plan.steps.filter((s) => s.action === "refuse").map((s) => `Would refuse: ${s.reason}`)
67897
+ })
67898
+ );
67899
+ }
67900
+ const revision = read.source === null ? null : rulesRevision(read.source);
67901
+ for (const step2 of plan3.plan.steps) {
67902
+ if (step2.action === "force_replace" && step2.replaces !== void 0) {
67903
+ warn(inv.deps, `Replacing ${step2.replaces.lines} lines of ${step2.path}; the lines that differ from the source:`);
67904
+ for (const line3 of step2.replaces.firstLines) warn(inv.deps, ` - ${line3}`);
67905
+ }
67906
+ }
67907
+ const written = plan3.plan.blocked ? [] : applyPlan2(root, plan3.plan);
67908
+ const data = { revision, steps: plan3.plan.steps.map((s) => ({ id: s.id, path: s.path, action: s.action, reason: s.reason, ...s.replaces === void 0 ? {} : { replaces: s.replaces }, written: written.includes(s.path) })), written, blocked: plan3.plan.blocked };
67909
+ if (plan3.plan.blocked) {
67910
+ const refused = plan3.plan.steps.find((s) => s.action === "refuse");
67911
+ return { data, exit: EXIT.conflict, error: new CliError(`nothing was written: ${refused?.reason ?? "a rules file needs attention"}`, { code: "rules_drift", exit: EXIT.conflict, hint: "move the change into .overlane/rules.md, or run overlane rules sync --overwrite-edits", quiet: true }) };
67912
+ }
67913
+ return { data };
67914
+ }
67915
+ });
67916
+ var rulesCheckCommand = defineCommand({
67917
+ path: ["rules", "check"],
67918
+ summary: "Report drift between the rules source and each agent's file; exits 6 on drift, for CI",
67919
+ description: `Compares ${RULES_SOURCE_PATH} with AGENTS.md, CLAUDE.md, the Cursor rule, GEMINI.md and the Gemini settings, and changes nothing. A target is in sync, missing, unmanaged (no managed region yet), stale (the source changed), edited (a hand edit inside the region, with the first differing line) or damaged. Exit 0 when everything is in sync or the project has no rules source; exit 6 on any drift. Run it in CI and see it in overlane doctor.`,
67920
+ examples: ["overlane rules check", "overlane rules check --json"],
67921
+ effect: "read",
67922
+ ...common17,
67923
+ keywords: ["rules drift", "instructions out of date", "hand edit AGENTS.md", "ci check rules", "doctor rules"],
67924
+ result: RulesCheckReport,
67925
+ text: (d) => !d.configured ? [`No rules source: ${d.nextCommand ?? ""}`] : d.sourceProblem !== null ? [`Rules source problem: ${d.sourceProblem}`] : [`Rules revision ${d.revision}`, ...d.targets.map((t) => `${t.state.toUpperCase().padEnd(10)} ${t.path} ${t.detail}`), ...d.nextCommand === null ? ["All in sync."] : [`next: ${d.nextCommand}`]],
67926
+ handler: async (inv) => {
67927
+ const project = await detectProject(inv.deps);
67928
+ const { report: report2 } = rulesStatus(project.root);
67929
+ if (!report2.drifted) return { data: report2 };
67930
+ const first = report2.targets.find((t) => t.state !== "in_sync");
67931
+ return { data: report2, exit: EXIT.blocked, error: new CliError(report2.sourceProblem ?? first?.detail ?? "the rules files have drifted from the source", { code: "rules_drift", exit: EXIT.blocked, ...report2.nextCommand === null ? {} : { hint: report2.nextCommand }, quiet: true }) };
67932
+ }
67933
+ });
67934
+ var rulesCommands = [rulesSyncCommand, rulesCheckCommand];
67935
+
67936
+ // ../cli/src/commands/pr.ts
67937
+ init_src();
67938
+ init_zod();
67939
+ import { randomUUID as randomUUID13 } from "node:crypto";
67940
+ var common18 = { needsAuth: true, needsNetwork: true };
67941
+ var requestId12 = () => `cli-${randomUUID13()}`;
67942
+ async function connected7(inv) {
67943
+ const project = await connectedProject(inv.deps, "pr");
67944
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67945
+ }
67946
+ async function planned7(inv, method, path, body2, note) {
67947
+ const project = await offlineProject(inv.deps, "pr");
67948
+ return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId, project.binding.projectId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
67949
+ }
67950
+ var git4 = (deps, args2) => deps.exec("git", args2, { cwd: deps.paths.cwd });
67951
+ var MAX_PATCH_FILES = 40;
67952
+ var MAX_PATCH_CHARS = 8e3;
67953
+ async function snapshotOf(inv, head, base4) {
67954
+ const sha = await git4(inv.deps, ["rev-parse", "--verify", "--quiet", `${head}^{commit}`]);
67955
+ if (sha.code !== 0) throw new UsageError(`${head} is not a commit in this checkout`, { hint: "git fetch, then retry" });
67956
+ const stat = await git4(inv.deps, ["diff", "--numstat", "-z", "--no-renames", `${base4}...${head}`]);
67957
+ if (stat.code !== 0) throw new UsageError(`could not compare ${base4} with ${head}: ${stat.stderr.trim().split("\n")[0] ?? "git failed"}`, { hint: "pass --base with a branch that exists, for example --base origin/main" });
67958
+ const files = [];
67959
+ for (const entry of stat.stdout.split("\0")) {
67960
+ const match = /^(\d+|-)\t(\d+|-)\t(.+)$/s.exec(entry);
67961
+ if (match === null) continue;
67962
+ files.push({ path: match[3], status: "modified", additions: match[1] === "-" ? 0 : Number(match[1]), deletions: match[2] === "-" ? 0 : Number(match[2]) });
67963
+ }
67964
+ for (const file2 of files.slice(0, MAX_PATCH_FILES)) {
67965
+ const patch = await git4(inv.deps, ["diff", "--unified=2", `${base4}...${head}`, "--", file2.path]);
67966
+ if (patch.code === 0 && patch.stdout !== "") file2.patch = patch.stdout.slice(0, MAX_PATCH_CHARS);
67967
+ }
67968
+ const project = await detectProject(inv.deps);
67969
+ const repository = textFlag(inv, "repo") ?? (project.repository === void 0 ? `overlane/${project.name.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 80) || "project"}` : `${project.repository.owner}/${project.repository.name}`);
67970
+ return { provider: project.provider === "github" ? "github" : "overlane", repository, headSha: sha.stdout.trim(), baseRef: base4, files };
67971
+ }
67972
+ function numberOf(inv) {
67973
+ const text = textFlag(inv, "number");
67974
+ if (text === void 0 || !/^[1-9]\d*$/.test(text)) throw new UsageError("give the pull request number with --number", { hint: "overlane pr route --number 7" });
67975
+ return Number(text);
67976
+ }
67977
+ function checkFlag(inv, name2) {
67978
+ const value = textFlag(inv, name2);
67979
+ return value ?? "missing";
67980
+ }
67981
+ var CHECK_FLAGS = [
67982
+ { name: "ci", type: "string", enum: CHECK_STATES, value: "STATE", description: "CI result: passed, failed, pending or missing (default missing, which blocks)" },
67983
+ { name: "secret-scan", type: "string", enum: CHECK_STATES, value: "STATE", description: "Secret scan result (default missing, which blocks)" },
67984
+ { name: "guardrails", type: "string", enum: CHECK_STATES, value: "STATE", description: "Guardrails result (default missing, which blocks)" },
67985
+ { name: "conflict", type: "string", enum: CHECK_STATES, value: "STATE", description: "Merge conflict check result, passed meaning no conflict (default missing, which blocks)" },
67986
+ { name: "critical", type: "string", value: "N", description: "Unresolved critical review findings (default 0)" },
67987
+ { name: "review-done", type: "boolean", description: "The review run finished for this head" }
67988
+ ];
67989
+ var LANE_TEXT = {
67990
+ blocked: "BLOCKED",
67991
+ fast: "FAST PATH",
67992
+ standard: "STANDARD",
67993
+ approver: "APPROVER"
67994
+ };
67995
+ var line2 = (s) => `${LANE_TEXT[s.route.lane] ?? s.route.lane} ${s.repository}#${s.number} risk ${s.route.risk}${s.approved ? " APPROVED" : ""}`;
67996
+ var prRouteCommand = defineCommand({
67997
+ path: ["pr", "route"],
67998
+ summary: "Route a pull request by risk: blocked, fast path, standard or approver",
67999
+ description: "Reads the change between --base and --head in this checkout, takes the check results you pass, and asks the service for a lane. Deterministic checks come first: any check that is not passed blocks every lane (a check you do not report counts as missing). Sensitive paths (auth, billing, migrations, workflows, secrets, infrastructure) force the approver lane. Only then, and only if the team switched the review_risk decision on, a decision model labels the risk; it can raise a lane, never lower one, and never approves. A blocked pull request exits 6 and names the first blocker's next command. Pass --author-token when an agent opened the pull request, so its own approval is refused.",
68000
+ flags: [
68001
+ { name: "number", type: "string", required: true, value: "N", description: "The pull request number" },
68002
+ { name: "head", type: "string", value: "REF", description: "The head commit or branch (default HEAD)" },
68003
+ { name: "base", type: "string", value: "REF", description: "The base to compare with (default origin/main, else main)" },
68004
+ { name: "repo", type: "string", value: "OWNER/NAME", description: "The repository name the pull request belongs to (default: the origin remote)" },
68005
+ { name: "title", type: "string", value: "TEXT", description: "The pull request title" },
68006
+ { name: "author", type: "string", value: "NAME", description: "The pull request author's name" },
68007
+ { name: "author-token", type: "string", value: "TOKEN_ID", description: "The agent token that opened it, so its lineage is known to the independence check" },
68008
+ { name: "task", type: "string", value: "TASK", description: "The task this pull request belongs to, for batching" },
68009
+ { name: "scope", type: "string", multiple: true, value: "GLOB", description: "A claimed scope pattern, for batching; repeat for more" },
68010
+ { name: "fork", type: "boolean", description: "The head comes from a fork" },
68011
+ ...CHECK_FLAGS
68012
+ ],
68013
+ examples: ["overlane pr route --number 7 --ci passed --secret-scan passed --guardrails passed --conflict passed --review-done", "overlane pr route --number 7 --base origin/main --task task-12 --dry-run"],
68014
+ effect: "write",
68015
+ ...common18,
68016
+ keywords: ["route pull request", "fast path", "risk lane", "who approves this pr", "triage pull requests", "pr risk"],
68017
+ result: withDryRun(StoredRoute),
68018
+ text: (d) => "route" in d ? [line2(d), ...d.route.reasons.map((r) => ` - ${r}`), `model: ${d.route.model.status} - ${d.route.model.note}`, ...d.route.nextCommand === null ? [] : [`next: ${d.route.nextCommand}`]] : [],
68019
+ handler: async (inv) => {
68020
+ const number4 = numberOf(inv);
68021
+ const head = textFlag(inv, "head") ?? "HEAD";
68022
+ let base4 = textFlag(inv, "base");
68023
+ if (base4 === void 0) base4 = (await git4(inv.deps, ["rev-parse", "--verify", "--quiet", "origin/main"])).code === 0 ? "origin/main" : "main";
68024
+ const critical = textFlag(inv, "critical");
68025
+ if (critical !== void 0 && !/^\d+$/.test(critical)) throw new UsageError("--critical is a whole number");
68026
+ const snapshot = await snapshotOf(inv, head, base4);
68027
+ const body2 = {
68028
+ requestId: requestId12(),
68029
+ pull: { provider: snapshot.provider, repository: snapshot.repository, number: number4, headSha: snapshot.headSha, title: textFlag(inv, "title") ?? `Pull request ${number4}`, body: "", author: textFlag(inv, "author") ?? "unknown", fork: boolFlag(inv, "fork"), files: snapshot.files },
68030
+ checks: { ci: checkFlag(inv, "ci"), secretScan: checkFlag(inv, "secret-scan"), guardrails: checkFlag(inv, "guardrails"), conflict: checkFlag(inv, "conflict"), criticalFindings: critical === void 0 ? 0 : Number(critical), reviewDone: boolFlag(inv, "review-done") },
68031
+ baseRef: snapshot.baseRef,
68032
+ ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") },
68033
+ ...textFlag(inv, "author-token") === void 0 ? {} : { authorAgentTokenId: textFlag(inv, "author-token") },
68034
+ ...listFlag(inv, "scope").length === 0 ? {} : { scope: listFlag(inv, "scope") }
68035
+ };
68036
+ if (inv.dryRun) {
68037
+ const preview = { ...body2, pull: { ...body2.pull, files: body2.pull.files.map((f) => ({ path: f.path, additions: f.additions, deletions: f.deletions })) } };
68038
+ return planned7(inv, "POST", prRoutingPaths.route, preview, "asks for a lane; the decision model is consulted only after the checks and only when switched on");
68039
+ }
68040
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68041
+ const stored = await call2(StoredRoute, "POST", prRoutingPaths.route(tenantId, projectId), body2);
68042
+ if (stored.route.lane === "blocked") {
68043
+ const first = stored.route.blockers[0];
68044
+ return { data: stored, exit: EXIT.blocked, error: new CliError(`blocked: ${first?.message ?? "a check is not clean"}`, { code: first?.code ?? "checks_not_clean", exit: EXIT.blocked, ...first?.nextCommand ? { hint: first.nextCommand } : {}, quiet: true }) };
68045
+ }
68046
+ return { data: stored };
68047
+ }
68048
+ });
68049
+ var Approved = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), approvalId: external_exports.string(), replay: external_exports.boolean(), via: external_exports.string(), route: StoredRoute }).strict();
68050
+ var prApproveCommand = defineCommand({
68051
+ path: ["pr", "approve"],
68052
+ summary: "Approve a routed pull request as an independent agent with evidence, or as a person",
68053
+ description: "Records an approval for the head you name. An agent's approval needs --evidence (what it checked) and --reason, and is refused when the approver is the author or shares the author's token lineage, run or lease. A person's approval needs neither. The approver lane accepts only the configured approver (a person on the project's list or holding an approver role). Standard and approver lanes wait for the review run. A decision model's label is never an approval. An approval is for one head: after a push, route again and approve the new head.",
68054
+ flags: [
68055
+ { name: "number", type: "string", required: true, value: "N", description: "The pull request number" },
68056
+ { name: "head", type: "string", value: "REF", description: "The head commit being approved (default HEAD)" },
68057
+ { name: "repo", type: "string", value: "OWNER/NAME", description: "The repository name (default: the origin remote)" },
68058
+ { name: "evidence", type: "string", multiple: true, value: "TEXT", description: "What you checked; repeat for more (required for an agent)" },
68059
+ { name: "reason", type: "string", value: "TEXT", description: "Why you approve (required for an agent)" }
68060
+ ],
68061
+ examples: ['overlane pr approve --number 7 --evidence "read the diff" --evidence "ran the tests" --reason "inside the task scope"'],
68062
+ effect: "write",
68063
+ ...common18,
68064
+ keywords: ["approve pull request", "fast path approval", "independent review", "sign off pr"],
68065
+ result: withDryRun(Approved),
68066
+ text: (d) => "approvalId" in d ? [`${d.route.approved ? "APPROVED" : "recorded"} ${d.route.repository}#${d.route.number} via ${d.via} ${d.approvalId}`] : [],
68067
+ handler: async (inv) => {
68068
+ const number4 = numberOf(inv);
68069
+ const head = textFlag(inv, "head") ?? "HEAD";
68070
+ const sha = await git4(inv.deps, ["rev-parse", "--verify", "--quiet", `${head}^{commit}`]);
68071
+ if (sha.code !== 0) throw new UsageError(`${head} is not a commit in this checkout`);
68072
+ const project = await detectProject(inv.deps);
68073
+ const repository = textFlag(inv, "repo") ?? (project.repository === void 0 ? `overlane/${project.name.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 80) || "project"}` : `${project.repository.owner}/${project.repository.name}`);
68074
+ const evidence = listFlag(inv, "evidence");
68075
+ const body2 = { requestId: requestId12(), repository, number: number4, headSha: sha.stdout.trim(), approval: { evidence, reason: textFlag(inv, "reason") ?? "" } };
68076
+ if (inv.dryRun) return planned7(inv, "POST", prRoutingPaths.approve, body2, "the service records the approval only if the independence and lane rules hold");
68077
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68078
+ return { data: await call2(Approved, "POST", prRoutingPaths.approve(tenantId, projectId), body2) };
68079
+ }
68080
+ });
68081
+ var Batches = external_exports.object({
68082
+ tenantId: external_exports.string(),
68083
+ projectId: external_exports.string(),
68084
+ maxSize: external_exports.number().int(),
68085
+ batches: external_exports.array(external_exports.object({ id: external_exports.string(), lane: external_exports.string(), keys: external_exports.array(external_exports.string()), reasons: external_exports.array(external_exports.string()) }).strict()),
68086
+ unbatched: external_exports.array(external_exports.string())
68087
+ }).strict();
68088
+ var prBatchCommand = defineCommand({
68089
+ path: ["pr", "batch"],
68090
+ summary: "Group related routed pull requests so one reviewer looks once",
68091
+ description: "Lists batches of routed pull requests in the same lane that share a task, overlap in claimed scope, or are stacked on one another, capped at the project's batch size. A blocked pull request never joins a batch. A batch groups attention and approves nothing: each pull request is approved at its own head.",
68092
+ examples: ["overlane pr batch", "overlane pr batch --json"],
68093
+ effect: "read",
68094
+ ...common18,
68095
+ keywords: ["batch pull requests", "group prs", "review together", "stacked prs"],
68096
+ result: Batches,
68097
+ text: (d) => [...d.batches.map((b) => `${b.lane.toUpperCase().padEnd(9)} ${b.keys.join(", ")} (${b.reasons.join(", ")})`), ...d.batches.length === 0 ? ["No related pull requests to batch."] : [], ...d.unbatched.length === 0 ? [] : [`alone: ${d.unbatched.join(", ")}`]],
68098
+ handler: async (inv) => {
68099
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68100
+ return { data: await call2(Batches, "GET", prRoutingPaths.batch(tenantId, projectId)) };
68101
+ }
68102
+ });
68103
+ var PolicyResult = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), policy: RoutingPolicy }).strict();
68104
+ var prPolicyCommand = defineCommand({
68105
+ path: ["pr", "policy"],
68106
+ summary: "Show or set who approves the approver lane and how large a batch may be",
68107
+ description: "With no flags, prints the project's routing policy. With flags, changes it: --approver names a person (a user ID) who may approve high-risk pull requests, --approver-role the roles that may (owner, admin), --allow-agent-approver lets an independent agent with evidence approve that lane too (off by default), --max-batch the largest batch. Changing it is a policy change.",
68108
+ flags: [
68109
+ { name: "approver", type: "string", multiple: true, value: "USER_ID", description: "A person who may approve the approver lane; repeat for more" },
68110
+ { name: "approver-role", type: "string", multiple: true, enum: ROUTE_APPROVER_ROLES, value: "ROLE", description: "A role that may approve the approver lane; repeat for more" },
68111
+ { name: "allow-agent-approver", type: "boolean", description: "Let an independent agent with evidence approve the approver lane" },
68112
+ { name: "max-batch", type: "string", value: "N", description: "The largest batch, 2 to 20" }
68113
+ ],
68114
+ examples: ["overlane pr policy", "overlane pr policy --approver user_abc --approver-role owner"],
68115
+ effect: "write",
68116
+ ...common18,
68117
+ keywords: ["who approves high risk", "approver", "routing policy", "batch size"],
68118
+ result: withDryRun(PolicyResult),
68119
+ text: (d) => "policy" in d ? [`approvers: ${d.policy.approverUsers.join(", ") || "(none listed)"}; roles: ${d.policy.approverRoles.join(", ") || "(none)"}; agents may approve the approver lane: ${d.policy.allowAgentApprover ? "yes" : "no"}; max batch ${d.policy.maxBatchSize}`] : [],
68120
+ handler: async (inv) => {
68121
+ const users = listFlag(inv, "approver");
68122
+ const roles = listFlag(inv, "approver-role");
68123
+ const maxBatch = textFlag(inv, "max-batch");
68124
+ if (maxBatch !== void 0 && (!/^\d+$/.test(maxBatch) || Number(maxBatch) < 2 || Number(maxBatch) > 20)) throw new UsageError("--max-batch is a whole number from 2 to 20");
68125
+ const changing = users.length > 0 || roles.length > 0 || maxBatch !== void 0 || inv.values["allow-agent-approver"] === true;
68126
+ const patch = { ...users.length > 0 ? { approverUsers: users } : {}, ...roles.length > 0 ? { approverRoles: roles } : {}, ...inv.values["allow-agent-approver"] === true ? { allowAgentApprover: true } : {}, ...maxBatch === void 0 ? {} : { maxBatchSize: Number(maxBatch) } };
68127
+ if (inv.dryRun) return planned7(inv, changing ? "PUT" : "GET", prRoutingPaths.policy, changing ? patch : void 0);
68128
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68129
+ return { data: await call2(PolicyResult, changing ? "PUT" : "GET", prRoutingPaths.policy(tenantId, projectId), changing ? patch : void 0) };
68130
+ }
68131
+ });
68132
+ var prCommands = [prRouteCommand, prApproveCommand, prBatchCommand, prPolicyCommand];
68133
+
68134
+ // ../cli/src/commands/egress.ts
68135
+ init_src();
68136
+ init_zod();
68137
+ import { randomUUID as randomUUID14 } from "node:crypto";
68138
+ var common19 = { needsAuth: true, needsNetwork: true };
68139
+ var requestId13 = () => `cli-${randomUUID14()}`;
68140
+ async function connected8(inv) {
68141
+ const project = await connectedProject(inv.deps, "egress");
68142
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
68143
+ }
68144
+ async function planned8(inv, method, path, body2, note) {
68145
+ const project = await offlineProject(inv.deps, "egress");
68146
+ return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId, project.binding.projectId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
68147
+ }
68148
+ function ruleOf(text) {
68149
+ const [host, ports] = text.split(":");
68150
+ const parsedPorts = ports === void 0 ? [443] : ports.split(",").map((p) => /^\d{1,5}$/.test(p) ? Number(p) : Number.NaN);
68151
+ if (parsedPorts.some((p) => !Number.isInteger(p) || p < 1 || p > 65535)) throw new UsageError(`${text}: ports are whole numbers from 1 to 65535, for example registry.npmjs.org:443,8443`);
68152
+ const checked2 = normaliseHost(host.replace(/^\*\./, "x."));
68153
+ if (!checked2.ok || checked2.kind !== "name") throw new UsageError(`${host} is not a host name; an address cannot be a rule`, { hint: "overlane egress policy set --allow registry.npmjs.org" });
68154
+ return { host: host.toLowerCase(), ports: parsedPorts };
68155
+ }
68156
+ var PolicyShow = EgressPolicyView;
68157
+ var hosts = (policy) => policy.rules.map((r) => `${r.host}${r.ports.length === 1 && r.ports[0] === 443 ? "" : `:${r.ports.join(",")}`}`);
68158
+ var egressPolicyShowCommand = defineCommand({
68159
+ path: ["egress", "policy", "show"],
68160
+ summary: "Show the egress allowlist: which hosts native CI and agent sandboxes may reach",
68161
+ description: "Prints the mode (enforce denies what no rule allows; report records and allows) and every rule. The default is deny: with no rule nothing is reachable. Raw IP addresses and cloud metadata services are denied in both modes.",
68162
+ examples: ["overlane egress policy show", "overlane egress policy show --json"],
68163
+ effect: "read",
68164
+ ...common19,
68165
+ keywords: ["egress", "allowlist", "which hosts can a job reach", "network policy", "sandbox network"],
68166
+ result: PolicyShow,
68167
+ text: (d) => [`mode: ${d.mode} version ${d.version}`, ...d.rules.length === 0 ? ["No rules: nothing is reachable (default deny)."] : hosts(d).map((h) => `allow ${h}`)],
68168
+ handler: async (inv) => {
68169
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68170
+ return { data: await call2(PolicyShow, "GET", egressPaths.policy(tenantId, projectId)) };
68171
+ }
68172
+ });
68173
+ var egressPolicySetCommand = defineCommand({
68174
+ path: ["egress", "policy", "set"],
68175
+ summary: "Set the egress allowlist and its mode",
68176
+ description: "Replaces the rules with the --allow hosts, or with --add keeps the current rules and adds to them. A rule is a host name such as registry.npmjs.org, optionally with ports (registry.npmjs.org:443,8443), or a wildcard such as *.example.com, which matches any subdomain and never the apex. A wildcard over a top-level or shared-hosting domain is refused. Use --mode report first to learn what a job needs: it records and allows, then switch to enforce. Changing the allowlist is a policy change.",
68177
+ flags: [
68178
+ { name: "allow", type: "string", multiple: true, value: "HOST[:PORTS]", description: "A host the sandbox may reach; repeat for more" },
68179
+ { name: "add", type: "boolean", description: "Keep the current rules and add these" },
68180
+ { name: "mode", type: "string", enum: ["enforce", "report"], value: "MODE", description: "enforce (default) or report" }
68181
+ ],
68182
+ examples: ["overlane egress policy set --allow registry.npmjs.org --allow api.github.com", "overlane egress policy set --add --allow '*.example.com' --mode report"],
68183
+ effect: "write",
68184
+ ...common19,
68185
+ keywords: ["allow host", "egress rule", "add to allowlist", "open a host"],
68186
+ result: withDryRun(PolicyShow),
68187
+ text: (d) => "rules" in d ? [`mode: ${d.mode} version ${d.version}`, ...hosts(d).map((h) => `allow ${h}`)] : [],
68188
+ handler: async (inv) => {
68189
+ const rules = listFlag(inv, "allow").map(ruleOf);
68190
+ const mode = textFlag(inv, "mode");
68191
+ const add = boolFlag(inv, "add");
68192
+ if (rules.length === 0 && mode === void 0) throw new UsageError("give --allow HOST, or --mode", { hint: "overlane egress policy set --allow registry.npmjs.org" });
68193
+ if (inv.dryRun) return planned8(inv, "PUT", egressPaths.policy, { mode: mode ?? "enforce", rules, ...add ? { merge: "adds to the current rules" } : {} }, "changing the allowlist is a policy change");
68194
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68195
+ const current2 = await call2(PolicyShow, "GET", egressPaths.policy(tenantId, projectId));
68196
+ const merged = add || rules.length === 0 ? [...current2.rules, ...rules] : rules;
68197
+ const seen = /* @__PURE__ */ new Set();
68198
+ const unique = merged.filter((rule) => {
68199
+ const key = `${rule.host}:${rule.ports.join(",")}`;
68200
+ return seen.has(key) ? false : (seen.add(key), true);
68201
+ });
68202
+ return { data: await call2(PolicyShow, "PUT", egressPaths.policy(tenantId, projectId), { mode: mode ?? current2.mode, rules: unique, expectedVersion: current2.version }) };
68203
+ }
68204
+ });
68205
+ var Check = EgressDecision.extend({ eventId: external_exports.string() }).passthrough();
68206
+ var egressCheckCommand = defineCommand({
68207
+ path: ["egress", "check"],
68208
+ summary: "Ask whether a host may be reached; exits 6 when the allowlist denies it",
68209
+ description: "Evaluates a host and port against the project's policy and records the answer. Raw IP addresses in any notation, cloud metadata services and internal names are denied whatever the rules say. In report mode a host with no rule is allowed and marked reported.",
68210
+ flags: [
68211
+ { name: "host", type: "string", required: true, value: "HOST", description: "The host name to check" },
68212
+ { name: "port", type: "string", value: "N", description: "The port (default 443)" },
68213
+ { name: "task", type: "string", value: "TASK", description: "The task asking, for the record" }
68214
+ ],
68215
+ examples: ["overlane egress check --host registry.npmjs.org", "overlane egress check --host 169.254.169.254"],
68216
+ effect: "write",
68217
+ ...common19,
68218
+ keywords: ["can the sandbox reach", "is this host allowed", "egress check"],
68219
+ result: withDryRun(Check),
68220
+ text: (d) => "outcome" in d ? [`${d.outcome.toUpperCase()} ${d.host}:${d.port} ${d.message}`] : [],
68221
+ handler: async (inv) => {
68222
+ const host = textFlag(inv, "host");
68223
+ if (host === void 0) throw new UsageError("check needs --host", { hint: "overlane egress check --host registry.npmjs.org" });
68224
+ const port = textFlag(inv, "port");
68225
+ if (port !== void 0 && !/^\d{1,5}$/.test(port)) throw new UsageError("--port is a whole number");
68226
+ const body2 = { host, ...port === void 0 ? {} : { port: Number(port) }, ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") } };
68227
+ if (inv.dryRun) return planned8(inv, "POST", egressPaths.evaluate, body2, "records the answer; nothing is connected to");
68228
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68229
+ const decision = await call2(Check, "POST", egressPaths.evaluate(tenantId, projectId), body2);
68230
+ return decision.allowed ? { data: decision } : { data: decision, exit: EXIT.blocked, error: new CliError(decision.message, { code: `egress_${decision.reason}`, exit: EXIT.blocked, hint: "overlane egress policy set --add --allow HOST", quiet: true }) };
68231
+ }
68232
+ });
68233
+ var Events = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), events: external_exports.array(EgressEvent) }).strict();
68234
+ var egressEventsCommand = defineCommand({
68235
+ path: ["egress", "events"],
68236
+ summary: "List recent egress decisions: host, port and outcome, never a path or a body",
68237
+ description: "Newest first. Each row is a host and port, whether it was allowed, denied or reported, why, and which task and run asked.",
68238
+ flags: [
68239
+ { name: "outcome", type: "string", enum: ["allowed", "denied", "reported"], value: "OUTCOME", description: "Only this outcome" },
68240
+ { name: "task", type: "string", value: "TASK", description: "Only this task" }
68241
+ ],
68242
+ examples: ["overlane egress events --outcome denied"],
68243
+ effect: "read",
68244
+ ...common19,
68245
+ keywords: ["what did the sandbox try to reach", "denied requests", "egress log"],
68246
+ result: Events,
68247
+ text: (d) => d.events.length === 0 ? ["No egress decisions recorded."] : d.events.map((e2) => `${e2.at} ${e2.outcome.padEnd(8)} ${e2.host}:${e2.port} ${e2.reason}${e2.taskId === null ? "" : ` task ${e2.taskId}`}`),
68248
+ handler: async (inv) => {
68249
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68250
+ const params = new URLSearchParams();
68251
+ if (textFlag(inv, "outcome") !== void 0) params.set("outcome", textFlag(inv, "outcome"));
68252
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
68253
+ const query = params.toString();
68254
+ return { data: await call2(Events, "GET", `${egressPaths.events(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
68255
+ }
68256
+ });
68257
+ var GrantResult = TaskGrant.extend({ replay: external_exports.boolean().optional(), reasons: external_exports.array(external_exports.string()).optional() }).strict();
68258
+ var Grants = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), grants: external_exports.array(TaskGrant) }).strict();
68259
+ var egressGrantCommand = defineCommand({
68260
+ path: ["egress", "grant"],
68261
+ summary: "Grant one task a credential for named hosts; the value never reaches the agent",
68262
+ description: "Creates a grant bound to this task, a host list and a lifetime (default 15 minutes). The grant names a secret; the egress proxy attaches its value to requests for the granted hosts, and the value is never returned to you, written to a log or put in the workspace. The hosts must already be on the allowlist. A grant for one task, named hosts and at most an hour is an ordinary action. A wider grant (--scope project or team), a longer one, or a wildcard of hosts is class secrets: an agent needs an independent agent's approval (set OVERLANE_APPROVAL to the ID the refusal names), a person does not.",
68263
+ flags: [
68264
+ { name: "task", type: "string", value: "TASK", description: "The task the grant belongs to (required for a task grant)" },
68265
+ { name: "host", type: "string", multiple: true, required: true, value: "HOST", description: "A host the credential may be attached for; repeat for more" },
68266
+ { name: "secret", type: "string", required: true, value: "NAME", description: "The secret's name in the project's secret store, such as NPM_TOKEN" },
68267
+ { name: "minutes", type: "string", value: "N", description: "Lifetime in minutes, 1 to 1440 (default 15)" },
68268
+ { name: "scope", type: "string", enum: ["task", "project", "team"], value: "SCOPE", description: "task (default), project or team; the wider two are class secrets" },
68269
+ { name: "header", type: "string", value: "NAME", description: "Attach as this header instead of Authorization: Bearer" }
68270
+ ],
68271
+ examples: ["overlane egress grant --task task-12 --host api.github.com --secret GITHUB_TOKEN", "overlane egress grant --task task-12 --host registry.npmjs.org --secret NPM_TOKEN --minutes 30"],
68272
+ effect: "write",
68273
+ ...common19,
68274
+ keywords: ["task credential", "scoped secret", "give the agent a token", "credential broker", "grant access to secret"],
68275
+ result: withDryRun(GrantResult),
68276
+ text: (d) => "secretName" in d ? [`grant ${d.id} ${d.scope}${d.taskId === null ? "" : ` ${d.taskId}`} ${d.secretName} for ${d.hosts.join(", ")} until ${d.expiresAt}`, ...d.class === "secrets" ? ["class: secrets (approved)"] : [], "The value is attached by the egress proxy and is never shown."] : [],
68277
+ handler: async (inv) => {
68278
+ const scope = textFlag(inv, "scope") ?? "task";
68279
+ const task = textFlag(inv, "task");
68280
+ if (scope === "task" && task === void 0) throw new UsageError("a task grant names its task", { hint: "overlane egress grant --task TASK --host api.github.com --secret GITHUB_TOKEN" });
68281
+ const minutes = textFlag(inv, "minutes");
68282
+ if (minutes !== void 0 && (!/^\d+$/.test(minutes) || Number(minutes) < 1 || Number(minutes) > 1440)) throw new UsageError("--minutes is a whole number from 1 to 1440");
68283
+ const header = textFlag(inv, "header");
68284
+ const body2 = {
68285
+ requestId: requestId13(),
68286
+ scope,
68287
+ ...scope === "task" ? { taskId: task } : {},
68288
+ hosts: listFlag(inv, "host"),
68289
+ secretName: textFlag(inv, "secret"),
68290
+ ...header === void 0 ? {} : { attach: { kind: "header", name: header.toLowerCase() } },
68291
+ ...minutes === void 0 ? {} : { ttlSeconds: Number(minutes) * 60 }
68292
+ };
68293
+ if (inv.dryRun) return planned8(inv, "POST", egressPaths.grants, body2, "the secret's value is not part of the request or the answer");
68294
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68295
+ return { data: await call2(GrantResult, "POST", egressPaths.grants(tenantId, projectId), body2) };
68296
+ }
68297
+ });
68298
+ var egressGrantsCommand = defineCommand({
68299
+ path: ["egress", "grants"],
68300
+ summary: "List the project's credential grants and whether each is still active",
68301
+ description: "Lists grants newest first: the task, hosts, the secret's name, the lifetime and how often the proxy used it. No value is ever shown.",
68302
+ flags: [
68303
+ { name: "task", type: "string", value: "TASK", description: "Only this task's grants" },
68304
+ { name: "status", type: "string", enum: ["active", "expired", "revoked"], value: "STATUS", description: "Only grants in this state" }
68305
+ ],
68306
+ examples: ["overlane egress grants --status active"],
68307
+ effect: "read",
68308
+ ...common19,
68309
+ keywords: ["list grants", "which secrets can the task use", "active credentials"],
68310
+ result: Grants,
68311
+ text: (d) => d.grants.length === 0 ? ["No grants."] : d.grants.map((g) => `${g.id} ${g.status.padEnd(7)} ${g.scope}${g.taskId === null ? "" : ` ${g.taskId}`} ${g.secretName} -> ${g.hosts.join(", ")} used ${g.uses}x until ${g.expiresAt}`),
68312
+ handler: async (inv) => {
68313
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68314
+ const params = new URLSearchParams();
68315
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
68316
+ if (textFlag(inv, "status") !== void 0) params.set("status", textFlag(inv, "status"));
68317
+ const query = params.toString();
68318
+ return { data: await call2(Grants, "GET", `${egressPaths.grants(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
68319
+ }
68320
+ });
68321
+ var egressRevokeCommand = defineCommand({
68322
+ path: ["egress", "revoke"],
68323
+ summary: "Revoke a credential grant at once",
68324
+ description: "Ends a grant: the proxy stops attaching its credential on the next request. Revoking is always allowed and needs no approval.",
68325
+ flags: [{ name: "grant", type: "string", required: true, value: "ID", description: "The grant to revoke" }],
68326
+ examples: ["overlane egress revoke --grant grt_abc123"],
68327
+ effect: "write",
68328
+ ...common19,
68329
+ keywords: ["revoke grant", "take back credential", "end task access"],
68330
+ result: withDryRun(TaskGrant),
68331
+ text: (d) => "secretName" in d ? [`grant ${d.id} is ${d.status}`] : [],
68332
+ handler: async (inv) => {
68333
+ const id2 = textFlag(inv, "grant");
68334
+ if (id2 === void 0) throw new UsageError("revoke needs --grant", { hint: "overlane egress grants" });
68335
+ if (inv.dryRun) return planned8(inv, "POST", (t, p) => egressPaths.revoke(t, p, id2), {});
68336
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68337
+ return { data: await call2(TaskGrant, "POST", egressPaths.revoke(tenantId, projectId, id2), {}) };
68338
+ }
68339
+ });
68340
+ var egressScanCommand = defineCommand({
68341
+ path: ["egress", "scan"],
68342
+ summary: "Check this workspace's file list for files that usually hold a credential",
68343
+ description: "Sends the names of the files git tracks or would add (never their contents) and reports the ones that usually hold a credential: .env files, .npmrc, private keys, cloud credential files. Exits 6 when any is found. Scanning a job's logs for a granted credential's value happens inside the runner, not here.",
68344
+ examples: ["overlane egress scan"],
68345
+ effect: "read",
68346
+ ...common19,
68347
+ keywords: ["credentials in workspace", "leak check", "secret files", "scan for credentials"],
68348
+ result: LeakScanResult,
68349
+ text: (d) => d.clean ? ["No credential files found."] : d.findings.map((f) => `${f.kind} ${f.place}${f.line === null ? "" : `:${f.line}`} ${f.rule}`),
68350
+ handler: async (inv) => {
68351
+ const listed = await inv.deps.exec("git", ["ls-files", "-co", "--exclude-standard", "-z"], { cwd: inv.deps.paths.cwd });
68352
+ if (listed.code !== 0) throw new UsageError("could not list the workspace files; run this inside a git repository");
68353
+ const files = listed.stdout.split("\0").filter((name2) => name2 !== "").slice(0, 2e4);
68354
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68355
+ const result = await call2(LeakScanResult, "POST", egressPaths.leakScan(tenantId, projectId), { files });
68356
+ return result.clean ? { data: result } : { data: result, exit: EXIT.blocked, error: new CliError(`${result.findings.length} file${result.findings.length === 1 ? "" : "s"} that usually hold a credential`, { code: "credential_files", exit: EXIT.blocked, hint: "keep them out of the workspace and the repository; grant the task a credential instead: overlane egress grant --help", quiet: true }) };
68357
+ }
68358
+ });
68359
+ var egressCommands = [egressPolicyShowCommand, egressPolicySetCommand, egressCheckCommand, egressEventsCommand, egressGrantCommand, egressGrantsCommand, egressRevokeCommand, egressScanCommand];
68360
+
66510
68361
  // ../cli/src/commands/index.ts
66511
68362
  function coreCommands() {
66512
68363
  return [
@@ -66535,6 +68386,11 @@ function coreCommands() {
66535
68386
  ...reviewCommands,
66536
68387
  ...guardCommands,
66537
68388
  ...billingCommands,
68389
+ ...claimCommands,
68390
+ ...budgetCommands,
68391
+ ...rulesCommands,
68392
+ ...prCommands,
68393
+ ...egressCommands,
66538
68394
  ...repoCommands,
66539
68395
  ...ciCommands,
66540
68396
  ...deployCommands
@@ -66690,10 +68546,10 @@ async function runCli(argv, deps, options) {
66690
68546
  const parsed = spec.style === "passthrough" ? { values: {}, positionals: [] } : parseFor(spec, rest);
66691
68547
  const safety = spec.style === "passthrough" ? safetyFlags(rest) : { dryRun: parsed.values["dry-run"] === true, force: parsed.values["force"] === true };
66692
68548
  if (spec.effect === "destructive" && !safety.dryRun && !safety.force && !deps.isTTY) {
66693
- const line2 = ["overlane", ...globals.tokens, "--force"].map(quote2).join(" ");
68549
+ const line3 = ["overlane", ...globals.tokens, "--force"].map(quote2).join(" ");
66694
68550
  throw new UsageError(`overlane ${commandName(spec)} is destructive and there is no terminal to confirm on; pass --force to proceed, or --dry-run to preview`, {
66695
68551
  code: "force_required",
66696
- hint: line2
68552
+ hint: line3
66697
68553
  });
66698
68554
  }
66699
68555
  const invocation = {
@@ -66758,37 +68614,37 @@ init_src2();
66758
68614
 
66759
68615
  // src/data-dir.ts
66760
68616
  import { homedir } from "node:os";
66761
- import { join as join28 } from "node:path";
68617
+ import { join as join29 } from "node:path";
66762
68618
  function dataDir(env = process.env, home = homedir()) {
66763
68619
  const override = env["MVRX_DATA_DIR"];
66764
68620
  if (override !== void 0 && override !== "") return override;
66765
- const base4 = env["XDG_DATA_HOME"] ?? (process.platform === "win32" ? env["LOCALAPPDATA"] : void 0) ?? (process.platform === "darwin" ? join28(home, "Library", "Application Support") : join28(home, ".local", "share"));
66766
- return join28(base4, "mvrx-harness");
68621
+ const base4 = env["XDG_DATA_HOME"] ?? (process.platform === "win32" ? env["LOCALAPPDATA"] : void 0) ?? (process.platform === "darwin" ? join29(home, "Library", "Application Support") : join29(home, ".local", "share"));
68622
+ return join29(base4, "mvrx-harness");
66767
68623
  }
66768
68624
  function marketplaceRoot(env = process.env, home = homedir()) {
66769
- return join28(dataDir(env, home), "marketplace");
68625
+ return join29(dataDir(env, home), "marketplace");
66770
68626
  }
66771
68627
 
66772
68628
  // src/plugin.ts
66773
- import { readFileSync as readFileSync13 } from "node:fs";
66774
- import { join as join30 } from "node:path";
68629
+ import { readFileSync as readFileSync14 } from "node:fs";
68630
+ import { join as join31 } from "node:path";
66775
68631
  import { parseArgs as parseArgs3 } from "node:util";
66776
68632
 
66777
68633
  // src/marketplace.ts
66778
68634
  init_src();
66779
68635
  import { createHash as createHash7 } from "node:crypto";
66780
- import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync9, readdirSync as readdirSync3, readFileSync as readFileSync12, renameSync as renameSync6, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync8 } from "node:fs";
66781
- import { dirname as dirname10, join as join29, relative as relative2, sep } from "node:path";
68636
+ import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync10, readdirSync as readdirSync3, readFileSync as readFileSync13, renameSync as renameSync7, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync9 } from "node:fs";
68637
+ import { dirname as dirname11, join as join30, relative as relative2, sep } from "node:path";
66782
68638
  var CLIENT_DIRS = ["claude", "codex"];
66783
68639
  var EMBEDDED_MANIFEST = "release-manifest.json";
66784
- var INSTALLED_MANIFEST = join29(".release", "manifest.json");
68640
+ var INSTALLED_MANIFEST = join30(".release", "manifest.json");
66785
68641
  var MarketplaceError = class extends Error {
66786
68642
  };
66787
68643
  function loadEmbedded(packageRoot2) {
66788
- const dir = join29(packageRoot2, "plugins");
66789
- const path = join29(dir, EMBEDDED_MANIFEST);
66790
- if (!existsSync7(path)) throw new MarketplaceError(`this package has no embedded plugin release (${path} is missing)`);
66791
- const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync12(path, "utf8")));
68644
+ const dir = join30(packageRoot2, "plugins");
68645
+ const path = join30(dir, EMBEDDED_MANIFEST);
68646
+ if (!existsSync8(path)) throw new MarketplaceError(`this package has no embedded plugin release (${path} is missing)`);
68647
+ const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync13(path, "utf8")));
66792
68648
  if (!parsed.success) throw new MarketplaceError(`the embedded release manifest is invalid: ${parsed.error.message}`);
66793
68649
  return { dir, manifest: parsed.data };
66794
68650
  }
@@ -66796,10 +68652,10 @@ function sha2562(data) {
66796
68652
  return `sha256:${createHash7("sha256").update(data).digest("hex")}`;
66797
68653
  }
66798
68654
  function listFiles(root, dir = root) {
66799
- if (!existsSync7(dir)) return [];
68655
+ if (!existsSync8(dir)) return [];
66800
68656
  const out2 = [];
66801
68657
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
66802
- const full = join29(dir, entry.name);
68658
+ const full = join30(dir, entry.name);
66803
68659
  if (entry.isDirectory()) out2.push(...listFiles(root, full));
66804
68660
  else out2.push(relative2(root, full).split(sep).join("/"));
66805
68661
  }
@@ -66811,23 +68667,23 @@ async function verifyTree(treeRoot, manifest) {
66811
68667
  if (recomputed !== manifest.digest) problems.push(`manifest digest ${manifest.digest} does not match its file list (${recomputed})`);
66812
68668
  const listed = new Set(manifest.files.map((file2) => file2.path));
66813
68669
  for (const file2 of manifest.files) {
66814
- const path = join29(treeRoot, file2.path);
66815
- if (!existsSync7(path)) {
68670
+ const path = join30(treeRoot, file2.path);
68671
+ if (!existsSync8(path)) {
66816
68672
  problems.push(`missing: ${file2.path}`);
66817
68673
  continue;
66818
68674
  }
66819
- if (sha2562(readFileSync12(path)) !== file2.digest) problems.push(`modified: ${file2.path}`);
68675
+ if (sha2562(readFileSync13(path)) !== file2.digest) problems.push(`modified: ${file2.path}`);
66820
68676
  }
66821
68677
  for (const client of CLIENT_DIRS) {
66822
- for (const found of listFiles(join29(treeRoot, client))) {
68678
+ for (const found of listFiles(join30(treeRoot, client))) {
66823
68679
  if (!listed.has(`${client}/${found}`)) problems.push(`unlisted: ${client}/${found}`);
66824
68680
  }
66825
68681
  }
66826
68682
  return problems;
66827
68683
  }
66828
- function readInstalledManifest(target) {
68684
+ function readInstalledManifest(target2) {
66829
68685
  try {
66830
- const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync12(join29(target, INSTALLED_MANIFEST), "utf8")));
68686
+ const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync13(join30(target2, INSTALLED_MANIFEST), "utf8")));
66831
68687
  return parsed.success ? parsed.data : null;
66832
68688
  } catch {
66833
68689
  return null;
@@ -66845,30 +68701,30 @@ function isNewer(a, b) {
66845
68701
  }
66846
68702
  return false;
66847
68703
  }
66848
- async function syncMarketplace(embedded, target) {
68704
+ async function syncMarketplace(embedded, target2) {
66849
68705
  const before = await verifyTree(embedded.dir, embedded.manifest);
66850
68706
  if (before.length > 0) throw new MarketplaceError(`the embedded plugin files do not match the release manifest; refusing to install:
66851
68707
  ${before.join("\n ")}`);
66852
- const staging = `${target}.new-${process.pid}`;
66853
- const previous = `${target}.old-${process.pid}`;
68708
+ const staging = `${target2}.new-${process.pid}`;
68709
+ const previous = `${target2}.old-${process.pid}`;
66854
68710
  rmSync4(staging, { recursive: true, force: true });
66855
- mkdirSync9(staging, { recursive: true });
68711
+ mkdirSync10(staging, { recursive: true });
66856
68712
  try {
66857
68713
  for (const client of CLIENT_DIRS) {
66858
- if (existsSync7(join29(embedded.dir, client))) cpSync(join29(embedded.dir, client), join29(staging, client), { recursive: true });
68714
+ if (existsSync8(join30(embedded.dir, client))) cpSync(join30(embedded.dir, client), join30(staging, client), { recursive: true });
66859
68715
  }
66860
- mkdirSync9(join29(staging, ".release"), { recursive: true });
66861
- writeFileSync8(join29(staging, INSTALLED_MANIFEST), `${JSON.stringify(embedded.manifest, null, 2)}
68716
+ mkdirSync10(join30(staging, ".release"), { recursive: true });
68717
+ writeFileSync9(join30(staging, INSTALLED_MANIFEST), `${JSON.stringify(embedded.manifest, null, 2)}
66862
68718
  `);
66863
68719
  const after = await verifyTree(staging, embedded.manifest);
66864
68720
  if (after.length > 0) throw new MarketplaceError(`the copied plugin files do not match the release manifest:
66865
68721
  ${after.join("\n ")}`);
66866
- mkdirSync9(dirname10(target), { recursive: true });
66867
- if (existsSync7(target)) renameSync6(target, previous);
68722
+ mkdirSync10(dirname11(target2), { recursive: true });
68723
+ if (existsSync8(target2)) renameSync7(target2, previous);
66868
68724
  try {
66869
- renameSync6(staging, target);
68725
+ renameSync7(staging, target2);
66870
68726
  } catch (error62) {
66871
- if (existsSync7(previous)) renameSync6(previous, target);
68727
+ if (existsSync8(previous)) renameSync7(previous, target2);
66872
68728
  throw error62;
66873
68729
  }
66874
68730
  rmSync4(previous, { recursive: true, force: true });
@@ -66876,11 +68732,11 @@ async function syncMarketplace(embedded, target) {
66876
68732
  rmSync4(staging, { recursive: true, force: true });
66877
68733
  }
66878
68734
  }
66879
- async function ensureMarketplace(embedded, target) {
66880
- const installed = readInstalledManifest(target);
66881
- const present = existsSync7(target) && statSync2(target).isDirectory();
68735
+ async function ensureMarketplace(embedded, target2) {
68736
+ const installed = readInstalledManifest(target2);
68737
+ const present = existsSync8(target2) && statSync2(target2).isDirectory();
66882
68738
  if (installed !== null && present && !isNewer(embedded.manifest.version, installed.version)) return false;
66883
- await syncMarketplace(embedded, target);
68739
+ await syncMarketplace(embedded, target2);
66884
68740
  return true;
66885
68741
  }
66886
68742
 
@@ -66890,8 +68746,8 @@ var PLUGIN_USAGE = ` overlane plugin install [--scope project|user] [--client c
66890
68746
  var PluginUsageError = class extends Error {
66891
68747
  };
66892
68748
  function readNames(treeRoot, client) {
66893
- const file2 = client === "claude" ? join30(treeRoot, "claude", ".claude-plugin", "marketplace.json") : join30(treeRoot, "codex", ".agents", "plugins", "marketplace.json");
66894
- const parsed = JSON.parse(readFileSync13(file2, "utf8"));
68749
+ const file2 = client === "claude" ? join31(treeRoot, "claude", ".claude-plugin", "marketplace.json") : join31(treeRoot, "codex", ".agents", "plugins", "marketplace.json");
68750
+ const parsed = JSON.parse(readFileSync14(file2, "utf8"));
66895
68751
  const record3 = parsed;
66896
68752
  const plugin = record3.plugins?.[0]?.name;
66897
68753
  if (typeof record3.name !== "string" || typeof plugin !== "string") throw new MarketplaceError(`${file2} names no marketplace or plugin`);
@@ -66905,7 +68761,7 @@ async function step(deps, program, args2, cwd) {
66905
68761
  `);
66906
68762
  const result = await deps.exec(program, args2, { cwd });
66907
68763
  const text = `${result.stdout}${result.stderr}`.trim();
66908
- if (text !== "") deps.stdout(`${text.split("\n").map((line2) => ` ${line2}`).join("\n")}
68764
+ if (text !== "") deps.stdout(`${text.split("\n").map((line3) => ` ${line3}`).join("\n")}
66909
68765
  `);
66910
68766
  return result.code === 0;
66911
68767
  }
@@ -66937,7 +68793,7 @@ ${PLUGIN_USAGE}`);
66937
68793
  const key = `${names.plugin}@${names.marketplace}`;
66938
68794
  if (!await available(deps, client)) {
66939
68795
  if (explicit !== void 0) {
66940
- deps.stderr(`overlane: \`${client}\` was not found on PATH; the marketplace is at ${join30(root, client)}
68796
+ deps.stderr(`overlane: \`${client}\` was not found on PATH; the marketplace is at ${join31(root, client)}
66941
68797
  `);
66942
68798
  failed = true;
66943
68799
  } else {
@@ -66949,7 +68805,7 @@ ${PLUGIN_USAGE}`);
66949
68805
  ranAny = true;
66950
68806
  deps.stdout(`${client}:
66951
68807
  `);
66952
- const dir = join30(root, client);
68808
+ const dir = join31(root, client);
66953
68809
  const steps = client === "claude" ? [
66954
68810
  ["claude", ["plugin", "marketplace", "add", dir, "--scope", scope]],
66955
68811
  ["claude", ["plugin", "marketplace", "update", names.marketplace]],
@@ -67012,7 +68868,7 @@ async function indexRun(sub, rest, env) {
67012
68868
  const args2 = [sub, ...rest.filter((token) => token !== "--dry-run" && token !== "--force")];
67013
68869
  const out2 = [];
67014
68870
  const err2 = [];
67015
- const code2 = await runCli2(args2, env, (line2) => out2.push(line2), (line2) => err2.push(line2));
68871
+ const code2 = await runCli2(args2, env, (line3) => out2.push(line3), (line3) => err2.push(line3));
67016
68872
  if (code2 === 0) return { data: { ok: true, message: out2.join("\n") } };
67017
68873
  const message = err2.join("\n").split("\n")[0] ?? "the indexer failed";
67018
68874
  if (code2 === 2) throw new UsageError(message, { hint: `overlane index ${sub} --help` });
@@ -67117,7 +68973,7 @@ var packageCommands = [indexBuildCommand, indexUploadCommand, shimCommand, plugi
67117
68973
  // src/deps.ts
67118
68974
  import { execFile as execFile4, spawn } from "node:child_process";
67119
68975
  import { homedir as homedir3 } from "node:os";
67120
- import { join as join33 } from "node:path";
68976
+ import { join as join34 } from "node:path";
67121
68977
  import { createInterface } from "node:readline/promises";
67122
68978
  function exec(command, args2, options) {
67123
68979
  return new Promise((resolveResult) => {
@@ -67163,8 +69019,8 @@ async function confirm(question) {
67163
69019
  }
67164
69020
  }
67165
69021
  function configDir2(env) {
67166
- const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join33(homedir3(), ".config");
67167
- return join33(base4, "mvrx-harness");
69022
+ const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join34(homedir3(), ".config");
69023
+ return join34(base4, "mvrx-harness");
67168
69024
  }
67169
69025
  function createDeps2(env = process.env) {
67170
69026
  return {