overlane 0.1.133 → 0.1.134

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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");
@@ -24381,28 +24381,28 @@ function stripComment(text) {
24381
24381
  }
24382
24382
  return text;
24383
24383
  }
24384
- function parseScalar(raw, line2) {
24384
+ function parseScalar(raw, line3) {
24385
24385
  const text = raw.trim();
24386
24386
  if (text === "" || text === "~" || text === "null") return null;
24387
24387
  if (text === "true") return true;
24388
24388
  if (text === "false") return false;
24389
24389
  if (/^-?\d+$/.test(text)) return Number(text);
24390
24390
  if (text.startsWith('"')) {
24391
- if (!text.endsWith('"') || text.length < 2) throw new YamlFail(line2, "unterminated double-quoted string");
24391
+ if (!text.endsWith('"') || text.length < 2) throw new YamlFail(line3, "unterminated double-quoted string");
24392
24392
  try {
24393
24393
  return JSON.parse(text);
24394
24394
  } catch {
24395
- throw new YamlFail(line2, "bad escape in double-quoted string");
24395
+ throw new YamlFail(line3, "bad escape in double-quoted string");
24396
24396
  }
24397
24397
  }
24398
24398
  if (text.startsWith("'")) {
24399
- if (!text.endsWith("'") || text.length < 2) throw new YamlFail(line2, "unterminated single-quoted string");
24399
+ if (!text.endsWith("'") || text.length < 2) throw new YamlFail(line3, "unterminated single-quoted string");
24400
24400
  return text.slice(1, -1).replaceAll("''", "'");
24401
24401
  }
24402
24402
  if (text === "{}") return {};
24403
24403
  if (text === "[]") return [];
24404
24404
  if (text.startsWith("[")) {
24405
- if (!text.endsWith("]")) throw new YamlFail(line2, "unterminated list; write the list on one line or use '- ' items");
24405
+ if (!text.endsWith("]")) throw new YamlFail(line3, "unterminated list; write the list on one line or use '- ' items");
24406
24406
  const inner = text.slice(1, -1);
24407
24407
  const items = [];
24408
24408
  let current2 = "";
@@ -24415,15 +24415,15 @@ function parseScalar(raw, line2) {
24415
24415
  quote3 = ch;
24416
24416
  current2 += ch;
24417
24417
  } else if (ch === ",") {
24418
- items.push(parseScalar(current2, line2));
24418
+ items.push(parseScalar(current2, line3));
24419
24419
  current2 = "";
24420
24420
  } else current2 += ch;
24421
24421
  }
24422
- if (current2.trim() !== "" || items.length > 0) items.push(parseScalar(current2, line2));
24422
+ if (current2.trim() !== "" || items.length > 0) items.push(parseScalar(current2, line3));
24423
24423
  return items;
24424
24424
  }
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`);
24425
+ if (text.startsWith("{")) throw new YamlFail(line3, "flow maps are not supported; use an indented block");
24426
+ if (/^[&*!%@`]/.test(text)) throw new YamlFail(line3, `'${text[0]}' (anchors, aliases and tags) is not supported`);
24427
24427
  return text;
24428
24428
  }
24429
24429
  function parseYaml(source) {
@@ -24432,10 +24432,10 @@ function parseYaml(source) {
24432
24432
  let i2 = 0;
24433
24433
  let pending = null;
24434
24434
  const keyOf = (path) => JSON.stringify(path);
24435
- function indentOf(raw, line2) {
24435
+ function indentOf(raw, line3) {
24436
24436
  let n = 0;
24437
24437
  while (raw[n] === " ") n += 1;
24438
- if (raw[n] === " ") throw new YamlFail(line2, "tabs are not allowed for indentation; use spaces");
24438
+ if (raw[n] === " ") throw new YamlFail(line3, "tabs are not allowed for indentation; use spaces");
24439
24439
  return n;
24440
24440
  }
24441
24441
  function peek() {
@@ -24549,8 +24549,8 @@ function parseYaml(source) {
24549
24549
  value,
24550
24550
  lineOf(path) {
24551
24551
  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;
24552
+ const line3 = n === 0 ? 1 : lineMap.get(keyOf(path.slice(0, n)));
24553
+ if (line3 !== void 0) return line3;
24554
24554
  }
24555
24555
  return 1;
24556
24556
  }
@@ -24703,9 +24703,9 @@ var init_ci = __esm({
24703
24703
  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
24704
  }).strict();
24705
24705
  YamlFail = class extends Error {
24706
- constructor(line2, message) {
24706
+ constructor(line3, message) {
24707
24707
  super(message);
24708
- this.line = line2;
24708
+ this.line = line3;
24709
24709
  }
24710
24710
  line;
24711
24711
  };
@@ -25037,6 +25037,878 @@ var init_decision = __esm({
25037
25037
  }
25038
25038
  });
25039
25039
 
25040
+ // ../contracts/src/claims.ts
25041
+ function mergeTreeArgs(a, b) {
25042
+ return ["merge-tree", "--write-tree", "--name-only", "-z", a, b];
25043
+ }
25044
+ function parseMergeTree(stdout, exitCode, stderr = "") {
25045
+ const parts2 = stdout.split("\0");
25046
+ const oid = parts2[0]?.trim() ?? "";
25047
+ if (!/^[0-9a-f]{40,64}$/.test(oid) || exitCode !== 0 && exitCode !== 1) {
25048
+ const hint = stderr.trim() || stdout.trim() || `git exited ${exitCode}`;
25049
+ return { status: "error", files: [], message: hint.slice(0, 1e3) };
25050
+ }
25051
+ const files = [];
25052
+ let index = 1;
25053
+ const names = [];
25054
+ while (index < parts2.length && parts2[index] !== "") {
25055
+ names.push(parts2[index]);
25056
+ index += 1;
25057
+ }
25058
+ index += 1;
25059
+ const kinds = /* @__PURE__ */ new Map();
25060
+ while (index < parts2.length) {
25061
+ const count = Number(parts2[index]);
25062
+ if (!Number.isInteger(count) || count < 0) break;
25063
+ const paths = parts2.slice(index + 1, index + 1 + count);
25064
+ const type = parts2[index + 1 + count] ?? "";
25065
+ const kind = /^CONFLICT \((.+)\)$/.exec(type)?.[1];
25066
+ if (kind !== void 0) {
25067
+ for (const path of paths) if (!kinds.has(path)) kinds.set(path, kind);
25068
+ }
25069
+ index += count + 3;
25070
+ }
25071
+ for (const name2 of names) files.push({ path: name2, kind: kinds.get(name2) ?? "content" });
25072
+ if (exitCode === 0) return files.length === 0 ? { status: "clean", treeOid: oid, files: [] } : { status: "error", files: [], message: "git reported a clean exit with conflicted files" };
25073
+ 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) };
25074
+ }
25075
+ 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;
25076
+ var init_claims = __esm({
25077
+ "../contracts/src/claims.ts"() {
25078
+ "use strict";
25079
+ init_zod();
25080
+ init_common();
25081
+ CLAIM_SCOPE_KINDS = ["path", "glob", "module"];
25082
+ ClaimScopeKind = external_exports.enum(CLAIM_SCOPE_KINDS);
25083
+ MAX_CLAIM_SCOPES = 32;
25084
+ MAX_CLAIM_PATTERNS = 128;
25085
+ MAX_PATTERN_LENGTH = 300;
25086
+ ClaimScopeInput = external_exports.object({
25087
+ kind: ClaimScopeKind,
25088
+ value: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
25089
+ /** For a module that is not in the code map: the paths or globs the caller says it covers. */
25090
+ paths: external_exports.array(external_exports.string().min(1).max(MAX_PATTERN_LENGTH)).max(MAX_CLAIM_PATTERNS).optional()
25091
+ }).strict();
25092
+ CLAIM_POLICIES = ["serialise", "refuse", "allow"];
25093
+ ClaimPolicy = external_exports.enum(CLAIM_POLICIES);
25094
+ CLAIM_MODES = ["exclusive", "shared"];
25095
+ ClaimMode = external_exports.enum(CLAIM_MODES);
25096
+ CLAIM_STATUSES = ["active", "queued", "released", "expired"];
25097
+ ClaimStatus = external_exports.enum(CLAIM_STATUSES);
25098
+ CLAIM_LIMITS = {
25099
+ defaultLeaseSeconds: 1800,
25100
+ maxLeaseSeconds: 4 * 3600,
25101
+ /** A claim never lives longer than this from its first grant, however often it is renewed. */
25102
+ maxLifetimeSeconds: 24 * 3600,
25103
+ /** A queued claim that has not been promoted by then expires. */
25104
+ queueSeconds: 4 * 3600,
25105
+ maxActivePerHolder: 20
25106
+ };
25107
+ ClaimSettings = external_exports.object({
25108
+ tenantId: TenantId,
25109
+ projectId: ProjectId,
25110
+ policy: ClaimPolicy,
25111
+ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds),
25112
+ updatedBy: external_exports.string().nullable(),
25113
+ updatedAt: Timestamp.nullable()
25114
+ }).strict();
25115
+ DEFAULT_CLAIM_SETTINGS = { policy: "serialise", leaseSeconds: CLAIM_LIMITS.defaultLeaseSeconds };
25116
+ PutClaimSettingsRequest = external_exports.object({ policy: ClaimPolicy, leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional() }).strict();
25117
+ Ref2 = external_exports.string().min(1).max(200);
25118
+ Sha = external_exports.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full lowercase commit SHA");
25119
+ Claim = external_exports.object({
25120
+ tenantId: TenantId,
25121
+ projectId: ProjectId,
25122
+ id: external_exports.string().min(1).max(80),
25123
+ taskId: external_exports.string().min(1).max(128),
25124
+ /** The principal that holds the claim: an agent token ID or a user ID. */
25125
+ holder: external_exports.string().min(1).max(200),
25126
+ /** A free label such as `codex` or `claude`, for people reading the list. */
25127
+ agent: external_exports.string().min(1).max(80).optional(),
25128
+ mode: ClaimMode,
25129
+ status: ClaimStatus,
25130
+ scopes: external_exports.array(ClaimScopeInput).min(1).max(MAX_CLAIM_SCOPES),
25131
+ patterns: external_exports.array(external_exports.string().min(1).max(MAX_PATTERN_LENGTH)).min(1).max(MAX_CLAIM_PATTERNS),
25132
+ resolvedFrom: external_exports.enum(["given", "code-map"]),
25133
+ /** The branch and head the work is on, once it has one; the pre-merge check compares heads. */
25134
+ branch: Ref2.optional(),
25135
+ headSha: Sha.optional(),
25136
+ /** Arrival order within the project; serialised claims are promoted in this order. */
25137
+ sequence: external_exports.number().int().positive(),
25138
+ leaseExpiresAt: Timestamp,
25139
+ /** The ceiling for renewals. */
25140
+ maxExpiresAt: Timestamp,
25141
+ /** The claims this one waits behind while it is queued. */
25142
+ waitingOn: external_exports.array(external_exports.string()).default([]),
25143
+ createdAt: Timestamp,
25144
+ updatedAt: Timestamp,
25145
+ releasedAt: Timestamp.optional(),
25146
+ releaseReason: external_exports.enum(["done", "released", "expired", "queue_timeout", "forced", "refused"]).optional()
25147
+ }).strict();
25148
+ AcquireClaimRequest = external_exports.object({
25149
+ requestId: external_exports.string().min(1).max(128),
25150
+ taskId: external_exports.string().min(1).max(128),
25151
+ scopes: external_exports.array(ClaimScopeInput).min(1).max(MAX_CLAIM_SCOPES),
25152
+ mode: ClaimMode.default("exclusive"),
25153
+ agent: external_exports.string().min(1).max(80).optional(),
25154
+ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional(),
25155
+ branch: Ref2.optional(),
25156
+ headSha: Sha.optional()
25157
+ }).strict();
25158
+ RenewClaimRequest = external_exports.object({ leaseSeconds: external_exports.number().int().min(60).max(CLAIM_LIMITS.maxLeaseSeconds).optional(), branch: Ref2.optional(), headSha: Sha.optional() }).strict();
25159
+ ReleaseClaimRequest = external_exports.object({ reason: external_exports.enum(["done", "released"]).default("released") }).strict();
25160
+ CLAIM_DECISIONS = ["granted", "queued", "refused"];
25161
+ 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();
25162
+ ClaimOutcome = external_exports.object({
25163
+ tenantId: TenantId,
25164
+ projectId: ProjectId,
25165
+ decision: external_exports.enum(CLAIM_DECISIONS),
25166
+ claim: Claim.nullable(),
25167
+ /** Claims that overlap this one: what it waits behind, what refused it, or what it shares scope with under `allow`. */
25168
+ overlaps: external_exports.array(Holder),
25169
+ /** Position in the queue, 1 for next; 0 when granted or refused. */
25170
+ position: external_exports.number().int().nonnegative(),
25171
+ policy: ClaimPolicy,
25172
+ message: external_exports.string().min(1),
25173
+ nextCommand: external_exports.string().nullable(),
25174
+ replay: external_exports.boolean().default(false)
25175
+ }).strict();
25176
+ ClaimListResponse = external_exports.object({ tenantId: TenantId, projectId: ProjectId, policy: ClaimPolicy, claims: external_exports.array(Claim) }).strict();
25177
+ MERGE_CHECK_STATUSES = ["clean", "conflict", "error"];
25178
+ MergeCheckStatus = external_exports.enum(MERGE_CHECK_STATUSES);
25179
+ ConflictedFile = external_exports.object({ path: external_exports.string().min(1).max(1e3), kind: external_exports.string().min(1).max(80) }).strict();
25180
+ MergeTreeResult = external_exports.object({
25181
+ status: MergeCheckStatus,
25182
+ /** The tree git wrote for a clean merge or with conflict markers. Absent on an error. */
25183
+ treeOid: external_exports.string().regex(/^[0-9a-f]{40,64}$/).optional(),
25184
+ files: external_exports.array(ConflictedFile).max(500),
25185
+ message: external_exports.string().max(1e3).optional()
25186
+ }).strict();
25187
+ RecordMergeCheckRequest = external_exports.object({
25188
+ requestId: external_exports.string().min(1).max(128),
25189
+ a: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict(),
25190
+ b: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().max(80).optional() }).strict(),
25191
+ result: MergeTreeResult
25192
+ }).strict();
25193
+ 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();
25194
+ MergeCheckRecord = external_exports.object({
25195
+ tenantId: TenantId,
25196
+ projectId: ProjectId,
25197
+ id: external_exports.string().min(1).max(80),
25198
+ a: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().optional() }).strict(),
25199
+ b: external_exports.object({ ref: Ref2, sha: Sha, claimId: external_exports.string().optional() }).strict(),
25200
+ result: MergeTreeResult,
25201
+ source: external_exports.enum(["runner", "client"]),
25202
+ reportedBy: external_exports.string().min(1),
25203
+ checkedAt: Timestamp
25204
+ }).strict();
25205
+ READINESS_BLOCKERS = ["claim_queued", "claim_inactive", "conflict", "check_required", "check_error", "no_branch"];
25206
+ 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();
25207
+ 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();
25208
+ claimPaths = {
25209
+ claims: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/claims`,
25210
+ claim: (tenantId, projectId, id2) => `${claimPaths.claims(tenantId, projectId)}/${encodeURIComponent(id2)}`,
25211
+ renew: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/renew`,
25212
+ release: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/release`,
25213
+ readiness: (tenantId, projectId, id2) => `${claimPaths.claim(tenantId, projectId, id2)}/readiness`,
25214
+ settings: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/settings`,
25215
+ checks: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks`,
25216
+ pending: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks/pending`,
25217
+ runCheck: (tenantId, projectId) => `${claimPaths.claims(tenantId, projectId)}/merge-checks/run`
25218
+ };
25219
+ }
25220
+ });
25221
+
25222
+ // ../contracts/src/budgets.ts
25223
+ 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;
25224
+ var init_budgets = __esm({
25225
+ "../contracts/src/budgets.ts"() {
25226
+ "use strict";
25227
+ init_zod();
25228
+ init_common();
25229
+ BUDGET_SCOPES = ["task", "agent", "project", "team"];
25230
+ BudgetScope = external_exports.enum(BUDGET_SCOPES);
25231
+ MAX_BUDGET_CENTS = 1e8;
25232
+ BUDGET_EXIT_CODE = 6;
25233
+ ScopeId = external_exports.string().min(1).max(128);
25234
+ BudgetLimit = external_exports.object({
25235
+ tenantId: TenantId,
25236
+ scope: BudgetScope,
25237
+ scopeId: ScopeId,
25238
+ /** Task limits count the task's whole life; the others count the current billing period. */
25239
+ limitCents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS),
25240
+ updatedBy: external_exports.string().min(1),
25241
+ updatedAt: Timestamp
25242
+ }).strict();
25243
+ PutBudgetRequest = external_exports.object({
25244
+ requestId: external_exports.string().min(1).max(128),
25245
+ scope: BudgetScope,
25246
+ /** Omitted for the team. */
25247
+ scopeId: ScopeId.optional(),
25248
+ limitCents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS)
25249
+ }).strict().superRefine((request, ctx) => {
25250
+ if (request.scope !== "team" && request.scopeId === void 0) ctx.addIssue({ code: "custom", path: ["scopeId"], message: `a ${request.scope} budget names its ${request.scope}` });
25251
+ });
25252
+ DeleteBudgetRequest = external_exports.object({ requestId: external_exports.string().min(1).max(128), scope: BudgetScope, scopeId: ScopeId.optional() }).strict().superRefine((request, ctx) => {
25253
+ if (request.scope !== "team" && request.scopeId === void 0) ctx.addIssue({ code: "custom", path: ["scopeId"], message: `a ${request.scope} budget names its ${request.scope}` });
25254
+ });
25255
+ BudgetSubject = external_exports.object({
25256
+ projectId: ProjectId.optional(),
25257
+ taskId: external_exports.string().min(1).max(128).optional(),
25258
+ agentId: external_exports.string().min(1).max(128).optional()
25259
+ }).strict();
25260
+ BUDGET_SOURCES = ["budget", "billing_cap"];
25261
+ BudgetLine = external_exports.object({
25262
+ scope: BudgetScope,
25263
+ scopeId: ScopeId,
25264
+ /** `budget` is a control-plane limit; `billing_cap` is the MS-B5 spending cap read through the same check. */
25265
+ source: external_exports.enum(BUDGET_SOURCES),
25266
+ limitCents: external_exports.number().int().nonnegative(),
25267
+ spentCents: external_exports.number().int().nonnegative()
25268
+ }).strict();
25269
+ BudgetBindingSchema = external_exports.object({
25270
+ scope: BudgetScope,
25271
+ scopeId: ScopeId,
25272
+ source: external_exports.enum(BUDGET_SOURCES),
25273
+ limitCents: external_exports.number().int().nonnegative(),
25274
+ spentCents: external_exports.number().int().nonnegative(),
25275
+ roomCents: external_exports.number().int()
25276
+ }).strict();
25277
+ BUDGET_STOP_REASONS = ["budget", "billing_cap", "read_only", "plan_allowance"];
25278
+ BudgetStopReason = external_exports.enum(BUDGET_STOP_REASONS);
25279
+ BudgetCheckRequest = external_exports.object({
25280
+ requestId: external_exports.string().min(1).max(128),
25281
+ estimateCents: external_exports.number().int().nonnegative().max(MAX_BUDGET_CENTS),
25282
+ projectId: ProjectId.optional(),
25283
+ taskId: external_exports.string().min(1).max(128).optional(),
25284
+ /** For a person calling on an agent's behalf. An agent token is always checked as itself. */
25285
+ agentId: external_exports.string().min(1).max(128).optional(),
25286
+ /** What the call is, for the record (`inference`, `ci`, `review`). Free text, never a prompt. */
25287
+ kind: external_exports.string().min(1).max(64).optional(),
25288
+ /** When true and the call is allowed, the estimate is recorded as spent so the next check counts it. */
25289
+ commit: external_exports.boolean().default(false),
25290
+ /** A billing meter, to also check the plan allowance (reviews, CI minutes, hosted inference). */
25291
+ meter: external_exports.enum(["reviews", "ciMinutes", "docsCalls", "hostedInference"]).optional()
25292
+ }).strict();
25293
+ BudgetDecision = external_exports.object({
25294
+ tenantId: TenantId,
25295
+ allowed: external_exports.boolean(),
25296
+ exitCode: external_exports.union([external_exports.literal(0), external_exports.literal(BUDGET_EXIT_CODE)]),
25297
+ reason: BudgetStopReason.nullable(),
25298
+ message: external_exports.string().min(1),
25299
+ nextCommand: external_exports.string().nullable(),
25300
+ estimateCents: external_exports.number().int().nonnegative(),
25301
+ binding: BudgetBindingSchema.nullable(),
25302
+ checked: external_exports.array(BudgetBindingSchema),
25303
+ /** The ID of the stored stop record when refused. */
25304
+ stopId: external_exports.string().nullable(),
25305
+ /** True when this answer is a replay of an earlier request with the same ID. */
25306
+ replay: external_exports.boolean(),
25307
+ recorded: external_exports.boolean()
25308
+ }).strict();
25309
+ BudgetStop = external_exports.object({
25310
+ tenantId: TenantId,
25311
+ id: external_exports.string().min(1),
25312
+ reason: BudgetStopReason,
25313
+ scope: BudgetScope.nullable(),
25314
+ scopeId: external_exports.string().nullable(),
25315
+ limitCents: external_exports.number().int().nonnegative().nullable(),
25316
+ spentCents: external_exports.number().int().nonnegative().nullable(),
25317
+ estimateCents: external_exports.number().int().nonnegative(),
25318
+ projectId: external_exports.string().nullable(),
25319
+ taskId: external_exports.string().nullable(),
25320
+ agentId: external_exports.string().nullable(),
25321
+ kind: external_exports.string().nullable(),
25322
+ message: external_exports.string().min(1),
25323
+ requestedBy: external_exports.string().min(1),
25324
+ at: Timestamp
25325
+ }).strict();
25326
+ RecordSpendRequest = external_exports.object({
25327
+ requestId: external_exports.string().min(1).max(128),
25328
+ cents: external_exports.number().int().positive().max(MAX_BUDGET_CENTS),
25329
+ projectId: ProjectId.optional(),
25330
+ taskId: external_exports.string().min(1).max(128).optional(),
25331
+ agentId: external_exports.string().min(1).max(128).optional(),
25332
+ kind: external_exports.string().min(1).max(64).optional()
25333
+ }).strict();
25334
+ BudgetUsageLine = external_exports.object({
25335
+ scope: BudgetScope,
25336
+ scopeId: ScopeId,
25337
+ source: external_exports.enum(BUDGET_SOURCES),
25338
+ limitCents: external_exports.number().int().nonnegative(),
25339
+ spentCents: external_exports.number().int().nonnegative(),
25340
+ remainingCents: external_exports.number().int()
25341
+ }).strict();
25342
+ BudgetSummary = external_exports.object({ tenantId: TenantId, periodStart: Timestamp, lines: external_exports.array(BudgetUsageLine) }).strict();
25343
+ BudgetStopList = external_exports.object({ tenantId: TenantId, stops: external_exports.array(BudgetStop) }).strict();
25344
+ budgetPaths = {
25345
+ root: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/budgets`,
25346
+ check: (tenantId) => `${budgetPaths.root(tenantId)}/check`,
25347
+ spend: (tenantId) => `${budgetPaths.root(tenantId)}/spend`,
25348
+ stops: (tenantId) => `${budgetPaths.root(tenantId)}/stops`
25349
+ };
25350
+ }
25351
+ });
25352
+
25353
+ // ../contracts/src/rules-sync.ts
25354
+ function rulesRevision(source) {
25355
+ return sha256Hex(normaliseText(source)).slice(0, 12);
25356
+ }
25357
+ function validateRulesSource(source) {
25358
+ const text = normaliseText(source);
25359
+ if (text.trim() === "") return { ok: false, problem: `${RULES_SOURCE_PATH} is empty` };
25360
+ if (new TextEncoder().encode(text).length > MAX_RULES_BYTES) return { ok: false, problem: `${RULES_SOURCE_PATH} is larger than ${MAX_RULES_BYTES / 1024} KB` };
25361
+ if (text.includes(MARKER)) return { ok: false, problem: `${RULES_SOURCE_PATH} contains the text "${MARKER}", which is reserved for the managed region markers` };
25362
+ return { ok: true, text: text.endsWith("\n") ? text : `${text}
25363
+ `, revision: rulesRevision(text) };
25364
+ }
25365
+ function regionBody(source) {
25366
+ return `<!-- Generated from ${RULES_SOURCE_PATH}. Edit that file, then run \`overlane rules sync\`. A hand edit in this region is reported as drift. -->
25367
+
25368
+ ${source}`;
25369
+ }
25370
+ function renderRegion(source) {
25371
+ const check2 = validateRulesSource(source);
25372
+ if (!check2.ok) throw new Error(check2.problem);
25373
+ const body2 = regionBody(check2.text);
25374
+ return `<!-- ${MARKER}:begin rev=${check2.revision} digest=${digestOf(body2)} -->
25375
+ ${body2}<!-- ${MARKER}:end -->
25376
+ `;
25377
+ }
25378
+ function scanRegion(fileText) {
25379
+ const lines = normaliseText(fileText).split("\n");
25380
+ const regions = [];
25381
+ let open2 = null;
25382
+ for (let i2 = 0; i2 < lines.length; i2 += 1) {
25383
+ const line3 = lines[i2];
25384
+ const begin = BEGIN.exec(line3);
25385
+ if (begin !== null) {
25386
+ 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}` };
25387
+ open2 = { index: i2, revision: begin[1], digest: begin[2] };
25388
+ } else if (line3.trim().startsWith(`<!-- ${MARKER}:begin`)) {
25389
+ return { kind: "damaged", problem: `line ${i2 + 1} looks like a begin marker but is not well formed` };
25390
+ } else if (line3 === END_LINE) {
25391
+ if (open2 === null) return { kind: "damaged", problem: `an end marker on line ${i2 + 1} with no begin marker` };
25392
+ regions.push({ startLine: open2.index, endLine: i2, revision: open2.revision, digest: open2.digest, body: `${lines.slice(open2.index + 1, i2).join("\n")}
25393
+ ` });
25394
+ open2 = null;
25395
+ }
25396
+ }
25397
+ if (open2 !== null) return { kind: "damaged", problem: `the begin marker on line ${open2.index + 1} has no end marker` };
25398
+ if (regions.length > 1) return { kind: "damaged", problem: `${regions.length} managed regions in one file` };
25399
+ const only = regions[0];
25400
+ return only === void 0 ? { kind: "none" } : { kind: "one", region: only };
25401
+ }
25402
+ function firstDifference(found, expected) {
25403
+ const a = found.split("\n");
25404
+ const b = expected.split("\n");
25405
+ const length = Math.max(a.length, b.length);
25406
+ for (let i2 = 0; i2 < length; i2 += 1) {
25407
+ if ((a[i2] ?? "") !== (b[i2] ?? "")) return { line: i2 + 1, found: a[i2] ?? "", expected: b[i2] ?? "" };
25408
+ }
25409
+ return { line: 1, found: "", expected: "" };
25410
+ }
25411
+ function describeLine(text) {
25412
+ const clean2 = text.trim();
25413
+ return clean2.length > 80 ? `${clean2.slice(0, 77)}...` : clean2;
25414
+ }
25415
+ function inspectRegionTarget(target2, fileText, source) {
25416
+ const base4 = { id: target2.id, path: target2.path };
25417
+ if (fileText === null) return { ...base4, state: "missing", detail: `${target2.path} does not exist; sync creates it`, syncable: true };
25418
+ const scan = scanRegion(fileText);
25419
+ if (scan.kind === "damaged") return { ...base4, state: "damaged", detail: `${target2.path}: ${scan.problem}; fix the markers by hand, then run sync`, syncable: false };
25420
+ 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 };
25421
+ const { region } = scan;
25422
+ if (digestOf(region.body) !== region.digest) {
25423
+ const expected = regionBody(source.text);
25424
+ const diff = firstDifference(region.body, region.revision === source.revision ? expected : region.body);
25425
+ const where2 = region.revision === source.revision ? `line ${diff.line} of the region` : "the region";
25426
+ return {
25427
+ ...base4,
25428
+ state: "edited",
25429
+ 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`,
25430
+ ...region.revision === source.revision ? { firstDifference: diff } : {},
25431
+ syncable: false
25432
+ };
25433
+ }
25434
+ 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 };
25435
+ return { ...base4, state: "in_sync", detail: `${target2.path} matches revision ${source.revision}`, syncable: true };
25436
+ }
25437
+ function planRegionSync(target2, fileText, source, force) {
25438
+ const report2 = inspectRegionTarget(target2, fileText, source);
25439
+ const region = renderRegion(source.text);
25440
+ const base4 = { id: target2.id, path: target2.path };
25441
+ switch (report2.state) {
25442
+ case "in_sync":
25443
+ return { ...base4, action: "none", next: null, reason: report2.detail };
25444
+ case "missing":
25445
+ return { ...base4, action: "create", next: `${target2.preamble}${region}`, reason: "created" };
25446
+ case "unmanaged": {
25447
+ const text = normaliseText(fileText);
25448
+ const joined = text === "" ? "" : text.endsWith("\n\n") ? text : text.endsWith("\n") ? `${text}
25449
+ ` : `${text}
25450
+
25451
+ `;
25452
+ return { ...base4, action: "append", next: `${joined}${region}`, reason: "appended a managed region at the end; every existing line is kept" };
25453
+ }
25454
+ case "stale":
25455
+ case "edited": {
25456
+ if (report2.state === "edited" && !force) return { ...base4, action: "refuse", next: null, reason: report2.detail };
25457
+ const scan = scanRegion(fileText);
25458
+ if (scan.kind !== "one") return { ...base4, action: "refuse", next: null, reason: report2.detail };
25459
+ const lines = normaliseText(fileText).split("\n");
25460
+ const before = lines.slice(0, scan.region.startLine).join("\n");
25461
+ const after = lines.slice(scan.region.endLine + 1).join("\n");
25462
+ const next = `${before === "" ? "" : `${before}
25463
+ `}${region}${after === "" ? "" : after}`;
25464
+ const replaced = scan.region.body.split("\n").filter((l, i2, all) => !(i2 === all.length - 1 && l === ""));
25465
+ const written = new Set(regionBody(source.text).split("\n"));
25466
+ const going = replaced.filter((l) => l.trim() !== "" && !written.has(l));
25467
+ return {
25468
+ ...base4,
25469
+ action: report2.state === "edited" ? "force_replace" : "replace",
25470
+ next: next.endsWith("\n") ? next : `${next}
25471
+ `,
25472
+ ...report2.state === "edited" ? { replaces: { lines: replaced.length, firstLines: (going.length > 0 ? going : replaced).slice(0, 5) } } : {},
25473
+ reason: report2.state === "edited" ? "replaced a hand-edited region because --force was given" : "updated to the current revision"
25474
+ };
25475
+ }
25476
+ case "damaged":
25477
+ return { ...base4, action: "refuse", next: null, reason: report2.detail };
25478
+ }
25479
+ }
25480
+ function parseSettings(text) {
25481
+ if (text === null || text.trim() === "") return { ok: true, value: {} };
25482
+ try {
25483
+ const value = JSON.parse(text);
25484
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return { ok: false, problem: "is not a JSON object" };
25485
+ return { ok: true, value };
25486
+ } catch {
25487
+ return { ok: false, problem: "is not valid JSON (comments are not supported here)" };
25488
+ }
25489
+ }
25490
+ function contextFileNames(settings) {
25491
+ const context4 = settings["context"];
25492
+ if (context4 === null || typeof context4 !== "object" || Array.isArray(context4)) return { names: [], shape: "none" };
25493
+ const fileName = context4["fileName"];
25494
+ if (typeof fileName === "string") return { names: [fileName], shape: "string" };
25495
+ if (Array.isArray(fileName) && fileName.every((n) => typeof n === "string")) return { names: fileName, shape: "array" };
25496
+ return { names: [], shape: fileName === void 0 ? "none" : "other" };
25497
+ }
25498
+ function inspectGeminiSettings(target2, fileText) {
25499
+ const base4 = { id: target2.id, path: target2.path };
25500
+ if (fileText === null) return { ...base4, state: "missing", detail: `${target2.path} does not exist; sync creates it`, syncable: true };
25501
+ const parsed = parseSettings(fileText);
25502
+ if (!parsed.ok) return { ...base4, state: "damaged", detail: `${target2.path} ${parsed.problem}; fix it by hand, then run sync`, syncable: false };
25503
+ const { names, shape: shape2 } = contextFileNames(parsed.value);
25504
+ 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 };
25505
+ const missing = GEMINI_CONTEXT_FILES.filter((name2) => !names.includes(name2));
25506
+ if (missing.length === 0) return { ...base4, state: "in_sync", detail: `${target2.path} lists ${GEMINI_CONTEXT_FILES.join(" and ")}`, syncable: true };
25507
+ 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 };
25508
+ }
25509
+ function planGeminiSettingsSync(target2, fileText) {
25510
+ const report2 = inspectGeminiSettings(target2, fileText);
25511
+ const base4 = { id: target2.id, path: target2.path };
25512
+ if (report2.state === "in_sync") return { ...base4, action: "none", next: null, reason: report2.detail };
25513
+ if (report2.state === "damaged") return { ...base4, action: "refuse", next: null, reason: report2.detail };
25514
+ const parsed = parseSettings(fileText);
25515
+ if (!parsed.ok) return { ...base4, action: "refuse", next: null, reason: report2.detail };
25516
+ const settings = { ...parsed.value };
25517
+ const existing = contextFileNames(settings).names;
25518
+ const context4 = settings["context"] !== null && typeof settings["context"] === "object" && !Array.isArray(settings["context"]) ? { ...settings["context"] } : {};
25519
+ const names = [...existing];
25520
+ for (const name2 of GEMINI_CONTEXT_FILES) if (!names.includes(name2)) names.push(name2);
25521
+ context4["fileName"] = names;
25522
+ settings["context"] = context4;
25523
+ return { ...base4, action: fileText === null ? "create" : "append", next: `${JSON.stringify(settings, null, 2)}
25524
+ `, reason: fileText === null ? "created" : "added the missing names to context.fileName; nothing else changed" };
25525
+ }
25526
+ function checkRules(source, input2) {
25527
+ if (source === null) return { configured: false, revision: null, sourceProblem: null, drifted: false, targets: [], nextCommand: `create ${RULES_SOURCE_PATH}, then run overlane rules sync` };
25528
+ const check2 = validateRulesSource(source);
25529
+ if (!check2.ok) return { configured: true, revision: null, sourceProblem: check2.problem, drifted: true, targets: [], nextCommand: `fix ${RULES_SOURCE_PATH}` };
25530
+ const targets = RULES_TARGETS.map((target2) => target2.kind === "region" ? inspectRegionTarget(target2, input2.files[target2.path] ?? null, check2) : inspectGeminiSettings(target2, input2.files[target2.path] ?? null));
25531
+ const drifted = targets.some((t) => t.state !== "in_sync");
25532
+ const needsForce = targets.some((t) => t.state === "edited");
25533
+ return { configured: true, revision: check2.revision, sourceProblem: null, drifted, targets, nextCommand: !drifted ? null : needsForce ? "overlane rules sync --overwrite-edits" : "overlane rules sync" };
25534
+ }
25535
+ function planRulesSync(source, input2, options = {}) {
25536
+ const check2 = validateRulesSource(source);
25537
+ if (!check2.ok) return { ok: false, problem: check2.problem };
25538
+ 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));
25539
+ return { ok: true, plan: { steps, blocked: steps.some((s) => s.action === "refuse") } };
25540
+ }
25541
+ 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;
25542
+ var init_rules_sync = __esm({
25543
+ "../contracts/src/rules-sync.ts"() {
25544
+ "use strict";
25545
+ init_zod();
25546
+ init_audit();
25547
+ RULES_SOURCE_PATH = ".overlane/rules.md";
25548
+ MAX_RULES_BYTES = 64 * 1024;
25549
+ MARKER = "overlane-rules";
25550
+ RULES_TARGET_IDS = ["agents-md", "claude-md", "cursor", "gemini-md", "gemini-settings"];
25551
+ RulesTargetId = external_exports.enum(RULES_TARGET_IDS);
25552
+ CURSOR_PREAMBLE = "---\ndescription: Project rules, generated from .overlane/rules.md\nalwaysApply: true\n---\n\n";
25553
+ RULES_TARGETS = [
25554
+ { id: "agents-md", path: "AGENTS.md", kind: "region", reader: "Codex, Cursor, Gemini CLI and other agents that read AGENTS.md", preamble: "" },
25555
+ { id: "claude-md", path: "CLAUDE.md", kind: "region", reader: "Claude Code", preamble: "" },
25556
+ { id: "cursor", path: ".cursor/rules/overlane.mdc", kind: "region", reader: "Cursor", preamble: CURSOR_PREAMBLE },
25557
+ { id: "gemini-md", path: "GEMINI.md", kind: "region", reader: "Gemini CLI", preamble: "" },
25558
+ { id: "gemini-settings", path: ".gemini/settings.json", kind: "gemini-settings", reader: "Gemini CLI settings (context.fileName)", preamble: "" }
25559
+ ];
25560
+ GEMINI_CONTEXT_FILES = ["GEMINI.md", "AGENTS.md"];
25561
+ normaliseText = (text) => text.replace(/\r\n?/g, "\n");
25562
+ digestOf = (body2) => sha256Hex(body2).slice(0, 16);
25563
+ BEGIN = new RegExp(`^<!-- ${MARKER}:begin rev=([0-9a-f]{12}) digest=([0-9a-f]{16}) -->$`);
25564
+ END_LINE = `<!-- ${MARKER}:end -->`;
25565
+ TARGET_STATES = ["in_sync", "missing", "unmanaged", "stale", "edited", "damaged"];
25566
+ TargetState = external_exports.enum(TARGET_STATES);
25567
+ RulesCheckReport = external_exports.object({
25568
+ configured: external_exports.boolean(),
25569
+ revision: external_exports.string().nullable(),
25570
+ sourceProblem: external_exports.string().nullable(),
25571
+ drifted: external_exports.boolean(),
25572
+ targets: external_exports.array(
25573
+ external_exports.object({
25574
+ id: RulesTargetId,
25575
+ path: external_exports.string(),
25576
+ state: TargetState,
25577
+ detail: external_exports.string(),
25578
+ syncable: external_exports.boolean(),
25579
+ firstDifference: external_exports.object({ line: external_exports.number().int(), found: external_exports.string(), expected: external_exports.string() }).strict().optional()
25580
+ }).strict()
25581
+ ),
25582
+ nextCommand: external_exports.string().nullable()
25583
+ }).strict();
25584
+ }
25585
+ });
25586
+
25587
+ // ../contracts/src/pr-routing.ts
25588
+ 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;
25589
+ var init_pr_routing = __esm({
25590
+ "../contracts/src/pr-routing.ts"() {
25591
+ "use strict";
25592
+ init_zod();
25593
+ init_autonomy();
25594
+ init_claims();
25595
+ init_common();
25596
+ init_review();
25597
+ ROUTE_LANES = ["blocked", "fast", "standard", "approver"];
25598
+ RouteLane = external_exports.enum(ROUTE_LANES);
25599
+ ROUTE_RISKS = ["low", "medium", "high"];
25600
+ RouteRisk = external_exports.enum(ROUTE_RISKS);
25601
+ CHECK_STATES = ["passed", "failed", "pending", "missing"];
25602
+ CheckState = external_exports.enum(CHECK_STATES);
25603
+ PrChecks = external_exports.object({
25604
+ ci: CheckState.default("missing"),
25605
+ secretScan: CheckState.default("missing"),
25606
+ guardrails: CheckState.default("missing"),
25607
+ /** The pre-merge conflict check (MS-C1.1): passed means no conflict. */
25608
+ conflict: CheckState.default("missing"),
25609
+ /** Unresolved critical review findings. */
25610
+ criticalFindings: external_exports.number().int().nonnegative().default(0),
25611
+ /** The Overlane review run finished for this head. */
25612
+ reviewDone: external_exports.boolean().default(false)
25613
+ }).strict();
25614
+ NO_CHECKS = { ci: "missing", secretScan: "missing", guardrails: "missing", conflict: "missing", criticalFindings: 0, reviewDone: false };
25615
+ SEG = "(?:^|/)";
25616
+ END = "(?:/|\\.|[-_]|$)";
25617
+ 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$`)];
25618
+ WORKFLOWS = [/^\.github\/(?:workflows|actions)\//, /^\.gitlab-ci/, new RegExp(`${SEG}\\.circleci/`), new RegExp(`${SEG}azure-pipelines`), new RegExp(`${SEG}jenkinsfile`), /^\.buildkite\//];
25619
+ MIGRATIONS = [new RegExp(`${SEG}(?:migrations?|db/migrate)/`), /\.sql$/, new RegExp(`${SEG}schema\\.(?:sql|prisma)$`), new RegExp(`${SEG}drizzle/`)];
25620
+ BILLING2 = [new RegExp(`${SEG}(?:billing|payments?|stripe|invoices?|checkout|subscriptions?)${END}`)];
25621
+ AUTH = [new RegExp(`${SEG}(?:auth|authn|authz|oauth|identity|permissions?|rbac|acl|sessions?|login|sso|saml|tokens?)${END}`)];
25622
+ 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)/`)];
25623
+ DOCS = [/^docs\//, /^apps\/docs\//, /\.(?:md|mdx|txt|rst)$/, new RegExp(`${SEG}readme`)];
25624
+ TESTS = [new RegExp(`${SEG}(?:tests?|__tests__|spec|e2e)/`), /\.(?:test|spec)\.[cm]?[jt]sx?$/];
25625
+ RiskSignal = external_exports.object({ code: external_exports.string().min(1), message: external_exports.string().min(1) }).strict();
25626
+ DeterministicRisk = external_exports.object({
25627
+ level: RouteRisk,
25628
+ linesChanged: external_exports.number().int().nonnegative(),
25629
+ filesChanged: external_exports.number().int().nonnegative(),
25630
+ sensitive: external_exports.array(external_exports.object({ path: external_exports.string(), category: external_exports.string() }).strict()),
25631
+ kind: external_exports.enum(["docs", "tests", "code", "mixed", "empty"]),
25632
+ signals: external_exports.array(RiskSignal)
25633
+ }).strict();
25634
+ Blocker = external_exports.object({ code: external_exports.string().min(1), message: external_exports.string().min(1), nextCommand: external_exports.string().nullable() }).strict();
25635
+ MODEL_STATUSES = ["off", "skipped", "undecided", "failed", "used"];
25636
+ ModelStatus = external_exports.enum(MODEL_STATUSES);
25637
+ APPROVAL_REQUIREMENTS = ["none", "independent", "approver"];
25638
+ RouteDecision = external_exports.object({
25639
+ lane: RouteLane,
25640
+ /** Risk after the model, if it raised it; equals `deterministic.level` otherwise. */
25641
+ risk: RouteRisk,
25642
+ deterministic: DeterministicRisk,
25643
+ blockers: external_exports.array(Blocker),
25644
+ model: external_exports.object({ status: ModelStatus, level: RouteRisk.nullable(), raised: external_exports.boolean(), note: external_exports.string() }).strict(),
25645
+ approval: external_exports.object({ requirement: external_exports.enum(APPROVAL_REQUIREMENTS), reviewRequired: external_exports.boolean() }).strict(),
25646
+ reasons: external_exports.array(external_exports.string()),
25647
+ nextCommand: external_exports.string().nullable(),
25648
+ /** True when the model may still be asked: the checks are clean and the lane can still rise. */
25649
+ modelEligible: external_exports.boolean()
25650
+ }).strict();
25651
+ ROUTE_APPROVER_ROLES = ["owner", "admin"];
25652
+ RoutingPolicy = external_exports.object({
25653
+ approverUsers: external_exports.array(external_exports.string().min(1).max(200)).max(50).default([]),
25654
+ approverRoles: external_exports.array(external_exports.enum(ROUTE_APPROVER_ROLES)).max(2).default(["owner", "admin"]),
25655
+ /** Off by default: the approver lane is a person's. Turning it on lets an independent agent with evidence approve it too. */
25656
+ allowAgentApprover: external_exports.boolean().default(false),
25657
+ maxBatchSize: external_exports.number().int().min(2).max(20).default(5)
25658
+ }).strict();
25659
+ PutRoutingPolicyRequest = RoutingPolicy.partial().strict();
25660
+ ROUTE_APPROVAL_SOURCES = ["agent", "person", "model"];
25661
+ RouteApprovalInput = external_exports.object({
25662
+ /** 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. */
25663
+ source: external_exports.enum(ROUTE_APPROVAL_SOURCES).optional(),
25664
+ /** What was checked, for an agent's approval. */
25665
+ evidence: external_exports.array(external_exports.string().min(1).max(400)).max(20).default([]),
25666
+ reason: external_exports.string().max(400).default("")
25667
+ }).strict();
25668
+ RoutePrRequest = external_exports.object({
25669
+ requestId: external_exports.string().min(1).max(128),
25670
+ pull: PullRequestSnapshot,
25671
+ checks: PrChecks.default({ ...NO_CHECKS }),
25672
+ taskId: external_exports.string().min(1).max(128).optional(),
25673
+ branch: external_exports.string().min(1).max(250).optional(),
25674
+ baseRef: external_exports.string().min(1).max(250).optional(),
25675
+ /** The claimed scope patterns, when the task holds a claim (MS-C1.1). */
25676
+ scope: external_exports.array(external_exports.string().min(1).max(300)).max(128).optional(),
25677
+ /** The agent token that opened the pull request, so its lineage is known to the independence check. */
25678
+ authorAgentTokenId: external_exports.string().min(1).max(200).optional(),
25679
+ authorUserId: external_exports.string().min(1).max(200).optional()
25680
+ }).strict();
25681
+ StoredRoute = external_exports.object({
25682
+ tenantId: TenantId,
25683
+ projectId: ProjectId,
25684
+ repository: external_exports.string().min(1),
25685
+ number: external_exports.number().int().positive(),
25686
+ headSha: external_exports.string().min(1),
25687
+ title: external_exports.string(),
25688
+ author: external_exports.string(),
25689
+ taskId: external_exports.string().nullable(),
25690
+ branch: external_exports.string().nullable(),
25691
+ baseRef: external_exports.string().nullable(),
25692
+ route: RouteDecision,
25693
+ checks: PrChecks,
25694
+ 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()),
25695
+ /** The lane's requirement is met by an accepted approval at this head and the checks are still clean. */
25696
+ approved: external_exports.boolean(),
25697
+ routedAt: Timestamp
25698
+ }).strict();
25699
+ RouteApproveRequest = external_exports.object({
25700
+ requestId: external_exports.string().min(1).max(128),
25701
+ repository: external_exports.string().min(1).max(200),
25702
+ number: external_exports.number().int().positive(),
25703
+ headSha: external_exports.string().min(1).max(64),
25704
+ approval: RouteApprovalInput
25705
+ }).strict();
25706
+ prRoutingPaths = {
25707
+ routes: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/pr-routes`,
25708
+ route: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/route`,
25709
+ approve: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/approve`,
25710
+ batch: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/batch`,
25711
+ policy: (tenantId, projectId) => `${prRoutingPaths.routes(tenantId, projectId)}/policy`
25712
+ };
25713
+ }
25714
+ });
25715
+
25716
+ // ../contracts/src/egress.ts
25717
+ function normaliseHost(raw) {
25718
+ let host = raw.trim().toLowerCase();
25719
+ if (host === "") return { ok: false, reason: "invalid_host" };
25720
+ if (/[^\u0000-\u007f]/.test(host)) return { ok: false, reason: "non_ascii_host" };
25721
+ if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
25722
+ if (host.includes(":")) return /^[0-9a-f:.]+$/.test(host) && host.length >= 2 ? { ok: true, host, kind: "ip" } : { ok: false, reason: "invalid_host" };
25723
+ if (host.endsWith(".")) host = host.slice(0, -1);
25724
+ if (host === "" || host.length > 253 || host.endsWith(".") || /[\s/\\@#?%*]/.test(host)) return { ok: false, reason: "invalid_host" };
25725
+ const labels = host.split(".");
25726
+ const last = labels[labels.length - 1];
25727
+ if (/^(?:0x[0-9a-f]*|\d+)$/.test(last)) return /^[0-9a-fx.]+$/.test(host) ? { ok: true, host, kind: "ip" } : { ok: false, reason: "invalid_host" };
25728
+ if (!labels.every((label2) => LABEL.test(label2))) return { ok: false, reason: "invalid_host" };
25729
+ return { ok: true, host, kind: "name" };
25730
+ }
25731
+ function isMetadataHost(host) {
25732
+ return METADATA_NAMES.has(host) || METADATA_ADDRESSES.has(host);
25733
+ }
25734
+ function isInternalName(host) {
25735
+ return host === "localhost" || INTERNAL_SUFFIXES.some((suffix) => host.endsWith(suffix)) || !host.includes(".");
25736
+ }
25737
+ function isSharedHostingWildcard(pattern) {
25738
+ if (!pattern.startsWith("*.")) return false;
25739
+ const name2 = pattern.slice(2);
25740
+ return SHARED_HOSTING_SUFFIXES.some((suffix) => name2 === suffix || name2.endsWith(`.${suffix}`));
25741
+ }
25742
+ 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;
25743
+ var init_egress = __esm({
25744
+ "../contracts/src/egress.ts"() {
25745
+ "use strict";
25746
+ init_zod();
25747
+ init_common();
25748
+ LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
25749
+ METADATA_NAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata", "instance-data", "metadata.azure.com", "metadata.tencentyun.com"]);
25750
+ 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"]);
25751
+ INTERNAL_SUFFIXES = [".internal", ".local", ".localhost", ".localdomain", ".lan", ".home.arpa", ".corp", ".intranet", ".cluster.local"];
25752
+ SHARED_HOSTING_SUFFIXES = [
25753
+ "co.uk",
25754
+ "org.uk",
25755
+ "com.au",
25756
+ "co.jp",
25757
+ "co.nz",
25758
+ "co.in",
25759
+ "com.br",
25760
+ "github.io",
25761
+ "githubusercontent.com",
25762
+ "gitlab.io",
25763
+ "herokuapp.com",
25764
+ "vercel.app",
25765
+ "netlify.app",
25766
+ "workers.dev",
25767
+ "pages.dev",
25768
+ "web.app",
25769
+ "firebaseapp.com",
25770
+ "appspot.com",
25771
+ "azurewebsites.net",
25772
+ "cloudfront.net",
25773
+ "amazonaws.com",
25774
+ "blob.core.windows.net",
25775
+ "r2.dev",
25776
+ "ngrok.io",
25777
+ "ngrok-free.app",
25778
+ "trycloudflare.com",
25779
+ "onrender.com",
25780
+ "fly.dev",
25781
+ "glitch.me",
25782
+ "repl.co"
25783
+ ];
25784
+ HOST_PATTERN = external_exports.string().min(3).max(253).refine((value) => {
25785
+ const text = value.trim().toLowerCase();
25786
+ const name2 = text.startsWith("*.") ? text.slice(2) : text;
25787
+ const check2 = normaliseHost(name2);
25788
+ return check2.ok && check2.kind === "name" && name2.includes(".") && !isMetadataHost(name2) && !isInternalName(name2) && !isSharedHostingWildcard(text);
25789
+ }, "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)");
25790
+ MAX_EGRESS_RULES = 100;
25791
+ EgressRule = external_exports.object({
25792
+ /** An exact host, or `*.example.com`, which matches any subdomain at any depth and never the apex `example.com`. */
25793
+ host: HOST_PATTERN.transform((value) => value.trim().toLowerCase()),
25794
+ /** Allowed destination ports. Default 443. */
25795
+ ports: external_exports.array(external_exports.number().int().min(1).max(65535)).min(1).max(8).default([443]),
25796
+ note: external_exports.string().max(200).optional()
25797
+ }).strict();
25798
+ EGRESS_MODES = ["enforce", "report"];
25799
+ EgressPolicy = external_exports.object({
25800
+ /** `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. */
25801
+ mode: external_exports.enum(EGRESS_MODES).default("enforce"),
25802
+ rules: external_exports.array(EgressRule).max(MAX_EGRESS_RULES).default([])
25803
+ }).strict();
25804
+ EgressPolicyView = EgressPolicy.extend({ tenantId: TenantId, projectId: ProjectId, version: external_exports.number().int().nonnegative(), updatedAt: Timestamp.nullable() }).strict();
25805
+ PutEgressPolicyRequest = EgressPolicy.extend({ expectedVersion: external_exports.number().int().nonnegative().optional() }).strict();
25806
+ EGRESS_REASONS = ["rule_match", "no_rule", "port_not_allowed", "report_only", "invalid_host", "raw_ip", "metadata", "internal_name", "private_address"];
25807
+ EgressReason = external_exports.enum(EGRESS_REASONS);
25808
+ EgressDecision = external_exports.object({
25809
+ outcome: external_exports.enum(["allowed", "denied", "reported"]),
25810
+ /** False only when the request must not go out. `reported` is allowed. */
25811
+ allowed: external_exports.boolean(),
25812
+ reason: EgressReason,
25813
+ host: external_exports.string(),
25814
+ port: external_exports.number().int(),
25815
+ rule: external_exports.string().nullable(),
25816
+ message: external_exports.string()
25817
+ }).strict();
25818
+ EgressEvent = external_exports.object({
25819
+ tenantId: TenantId,
25820
+ projectId: ProjectId,
25821
+ id: external_exports.string().min(1),
25822
+ at: Timestamp,
25823
+ source: external_exports.enum(["runner", "api"]),
25824
+ taskId: external_exports.string().nullable(),
25825
+ runId: external_exports.string().nullable(),
25826
+ host: external_exports.string(),
25827
+ port: external_exports.number().int(),
25828
+ outcome: external_exports.enum(["allowed", "denied", "reported"]),
25829
+ reason: EgressReason,
25830
+ rule: external_exports.string().nullable(),
25831
+ grantId: external_exports.string().nullable()
25832
+ }).strict();
25833
+ EvaluateEgressRequest = external_exports.object({
25834
+ host: external_exports.string().min(1).max(300),
25835
+ port: external_exports.number().int().min(1).max(65535).default(443),
25836
+ taskId: external_exports.string().min(1).max(128).optional(),
25837
+ runId: external_exports.string().min(1).max(128).optional(),
25838
+ /** What the name resolved to, when the caller looked it up. */
25839
+ resolved: external_exports.array(external_exports.string().min(1).max(64)).max(16).optional()
25840
+ }).strict();
25841
+ GRANT_SCOPES = ["task", "project", "team"];
25842
+ GrantScope = external_exports.enum(GRANT_SCOPES);
25843
+ MAX_GRANT_SECONDS = 24 * 3600;
25844
+ MAX_GRANT_HOSTS = 10;
25845
+ 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"]);
25846
+ GrantAttach = external_exports.discriminatedUnion("kind", [
25847
+ external_exports.object({ kind: external_exports.literal("bearer") }).strict(),
25848
+ 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()
25849
+ ]);
25850
+ SECRET_NAME = /^[A-Z][A-Z0-9_]{1,63}$/;
25851
+ GrantRequest = external_exports.object({
25852
+ requestId: external_exports.string().min(1).max(128),
25853
+ scope: GrantScope.default("task"),
25854
+ /** Required for a task grant. */
25855
+ taskId: external_exports.string().min(1).max(128).optional(),
25856
+ hosts: external_exports.array(HOST_PATTERN.transform((v) => v.trim().toLowerCase())).min(1).max(MAX_GRANT_HOSTS),
25857
+ /** The secret's name in the project's secret store. The value is never in this request or its answer. */
25858
+ secretName: external_exports.string().regex(SECRET_NAME, "an upper-case secret name such as NPM_TOKEN"),
25859
+ attach: GrantAttach.default({ kind: "bearer" }),
25860
+ ttlSeconds: external_exports.number().int().min(60).max(MAX_GRANT_SECONDS).default(900)
25861
+ }).strict().superRefine((request, ctx) => {
25862
+ if (request.scope === "task" && request.taskId === void 0) ctx.addIssue({ code: "custom", path: ["taskId"], message: "a task grant names its task" });
25863
+ if (request.scope !== "task" && request.taskId !== void 0) ctx.addIssue({ code: "custom", path: ["taskId"], message: "only a task grant names a task" });
25864
+ });
25865
+ GRANT_STATUSES = ["active", "expired", "revoked"];
25866
+ TaskGrant = external_exports.object({
25867
+ tenantId: TenantId,
25868
+ projectId: ProjectId,
25869
+ id: external_exports.string().min(1),
25870
+ scope: GrantScope,
25871
+ taskId: external_exports.string().nullable(),
25872
+ hosts: external_exports.array(external_exports.string()),
25873
+ secretName: external_exports.string(),
25874
+ attach: GrantAttach,
25875
+ status: external_exports.enum(GRANT_STATUSES),
25876
+ createdBy: external_exports.string(),
25877
+ createdAt: Timestamp,
25878
+ expiresAt: Timestamp,
25879
+ revokedAt: Timestamp.nullable(),
25880
+ uses: external_exports.number().int().nonnegative(),
25881
+ class: external_exports.enum(["grant", "secrets"])
25882
+ }).strict();
25883
+ ResolveRefusal = external_exports.enum(["no_grant", "expired", "revoked", "host_not_granted", "task_mismatch", "egress_denied", "secret_unavailable", "port_not_allowed"]);
25884
+ RevokeGrantRequest = external_exports.object({ reason: external_exports.string().max(200).optional() }).strict();
25885
+ LeakScanRequest = external_exports.object({
25886
+ 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([]),
25887
+ /** The workspace's file listing. */
25888
+ files: external_exports.array(external_exports.string().min(1).max(500)).max(2e4).default([])
25889
+ }).strict();
25890
+ LeakFilesRequest = LeakScanRequest.pick({ files: true }).strict();
25891
+ LeakFinding = external_exports.object({
25892
+ kind: external_exports.enum(["minted_value", "credential_pattern", "credential_file"]),
25893
+ rule: external_exports.string(),
25894
+ place: external_exports.string(),
25895
+ line: external_exports.number().int().nullable(),
25896
+ grantId: external_exports.string().nullable()
25897
+ }).strict();
25898
+ LeakScanResult = external_exports.object({ tenantId: TenantId, projectId: ProjectId, clean: external_exports.boolean(), checkedGrants: external_exports.number().int().nonnegative(), findings: external_exports.array(LeakFinding) }).strict();
25899
+ egressPaths = {
25900
+ base: (tenantId, projectId) => `/v1/tenants/${encodeURIComponent(tenantId)}/projects/${encodeURIComponent(projectId)}/egress`,
25901
+ policy: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/policy`,
25902
+ evaluate: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/evaluate`,
25903
+ events: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/events`,
25904
+ grants: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/grants`,
25905
+ grant: (tenantId, projectId, id2) => `${egressPaths.grants(tenantId, projectId)}/${encodeURIComponent(id2)}`,
25906
+ revoke: (tenantId, projectId, id2) => `${egressPaths.grant(tenantId, projectId, id2)}/revoke`,
25907
+ leakScan: (tenantId, projectId) => `${egressPaths.base(tenantId, projectId)}/leak-scan`
25908
+ };
25909
+ }
25910
+ });
25911
+
25040
25912
  // ../contracts/src/index.ts
25041
25913
  var init_src = __esm({
25042
25914
  "../contracts/src/index.ts"() {
@@ -25078,6 +25950,11 @@ var init_src = __esm({
25078
25950
  init_ci();
25079
25951
  init_deploy();
25080
25952
  init_decision();
25953
+ init_claims();
25954
+ init_budgets();
25955
+ init_rules_sync();
25956
+ init_pr_routing();
25957
+ init_egress();
25081
25958
  }
25082
25959
  });
25083
25960
 
@@ -25348,13 +26225,13 @@ var require_directives = __commonJS({
25348
26225
  * @param onError - May be called even if the action was successful
25349
26226
  * @returns `true` on success
25350
26227
  */
25351
- add(line2, onError) {
26228
+ add(line3, onError) {
25352
26229
  if (this.atNextDocument) {
25353
26230
  this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" };
25354
26231
  this.tags = Object.assign({}, _Directives.defaultTags);
25355
26232
  this.atNextDocument = false;
25356
26233
  }
25357
- const parts2 = line2.trim().split(/[ \t]+/);
26234
+ const parts2 = line3.trim().split(/[ \t]+/);
25358
26235
  const name2 = parts2.shift();
25359
26236
  switch (name2) {
25360
26237
  case "%TAG": {
@@ -26951,9 +27828,9 @@ var require_stringifyCollection = __commonJS({
26951
27828
  } else {
26952
27829
  str = lines[0];
26953
27830
  for (let i2 = 1; i2 < lines.length; ++i2) {
26954
- const line2 = lines[i2];
26955
- str += line2 ? `
26956
- ${indent}${line2}` : "\n";
27831
+ const line3 = lines[i2];
27832
+ str += line3 ? `
27833
+ ${indent}${line3}` : "\n";
26957
27834
  }
26958
27835
  }
26959
27836
  if (comment) {
@@ -27011,7 +27888,7 @@ ${indent}${line2}` : "\n";
27011
27888
  str += ",";
27012
27889
  } else if (ctx.options.trailingComma) {
27013
27890
  if (ctx.options.lineWidth > 0) {
27014
- reqNewline || (reqNewline = lines.reduce((sum, line2) => sum + line2.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
27891
+ reqNewline || (reqNewline = lines.reduce((sum, line3) => sum + line3.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
27015
27892
  }
27016
27893
  if (reqNewline) {
27017
27894
  str += ",";
@@ -27027,14 +27904,14 @@ ${indent}${line2}` : "\n";
27027
27904
  return start2 + end;
27028
27905
  } else {
27029
27906
  if (!reqNewline) {
27030
- const len = lines.reduce((sum, line2) => sum + line2.length + 2, 2);
27907
+ const len = lines.reduce((sum, line3) => sum + line3.length + 2, 2);
27031
27908
  reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
27032
27909
  }
27033
27910
  if (reqNewline) {
27034
27911
  let str = start2;
27035
- for (const line2 of lines)
27036
- str += line2 ? `
27037
- ${indentStep}${indent}${line2}` : "\n";
27912
+ for (const line3 of lines)
27913
+ str += line3 ? `
27914
+ ${indentStep}${indent}${line3}` : "\n";
27038
27915
  return `${str}
27039
27916
  ${indent}${end}`;
27040
27917
  } else {
@@ -28777,10 +29654,10 @@ var require_errors = __commonJS({
28777
29654
  if (error62.pos[0] === -1)
28778
29655
  return;
28779
29656
  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}`;
29657
+ const { line: line3, col } = error62.linePos[0];
29658
+ error62.message += ` at line ${line3}, column ${col}`;
28782
29659
  let ci = col - 1;
28783
- let lineStr = src.substring(lc.lineStarts[line2 - 1], lc.lineStarts[line2]).replace(/[\n\r]+$/, "");
29660
+ let lineStr = src.substring(lc.lineStarts[line3 - 1], lc.lineStarts[line3]).replace(/[\n\r]+$/, "");
28784
29661
  if (ci >= 60 && lineStr.length > 80) {
28785
29662
  const trimStart = Math.min(ci - 39, lineStr.length - 79);
28786
29663
  lineStr = "\u2026" + lineStr.substring(trimStart);
@@ -28788,8 +29665,8 @@ var require_errors = __commonJS({
28788
29665
  }
28789
29666
  if (lineStr.length > 80)
28790
29667
  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]);
29668
+ if (line3 > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
29669
+ let prev = src.substring(lc.lineStarts[line3 - 2], lc.lineStarts[line3 - 1]);
28793
29670
  if (prev.length > 80)
28794
29671
  prev = prev.substring(0, 79) + "\u2026\n";
28795
29672
  lineStr = prev + lineStr;
@@ -28797,7 +29674,7 @@ var require_errors = __commonJS({
28797
29674
  if (/[^ ]/.test(lineStr)) {
28798
29675
  let count = 1;
28799
29676
  const end = error62.linePos[1];
28800
- if (end?.line === line2 && end.col > col) {
29677
+ if (end?.line === line3 && end.col > col) {
28801
29678
  count = Math.max(1, Math.min(end.col - col, 80 - ci));
28802
29679
  }
28803
29680
  const pointer = " ".repeat(ci) + "^".repeat(count);
@@ -29746,8 +30623,8 @@ var require_resolve_flow_scalar = __commonJS({
29746
30623
  return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
29747
30624
  }
29748
30625
  function unfoldLines(source) {
29749
- const line2 = /(.*?)\r?\n/sy;
29750
- let match = line2.exec(source);
30626
+ const line3 = /(.*?)\r?\n/sy;
30627
+ let match = line3.exec(source);
29751
30628
  if (!match)
29752
30629
  return source;
29753
30630
  let trimEnd, trimBoth;
@@ -29760,8 +30637,8 @@ var require_resolve_flow_scalar = __commonJS({
29760
30637
  }
29761
30638
  let res = match[1].replace(trimEnd, "");
29762
30639
  let sep3 = " ";
29763
- let pos = line2.lastIndex;
29764
- while (match = line2.exec(source)) {
30640
+ let pos = line3.lastIndex;
30641
+ while (match = line3.exec(source)) {
29765
30642
  const lm = match[1].replace(trimBoth, "");
29766
30643
  if (lm === "") {
29767
30644
  if (sep3 === "\n")
@@ -29772,7 +30649,7 @@ var require_resolve_flow_scalar = __commonJS({
29772
30649
  res += sep3 + lm;
29773
30650
  sep3 = " ";
29774
30651
  }
29775
- pos = line2.lastIndex;
30652
+ pos = line3.lastIndex;
29776
30653
  }
29777
30654
  const last = /[ \t]*(.*)/sy;
29778
30655
  last.lastIndex = pos;
@@ -30902,40 +31779,40 @@ var require_lexer = __commonJS({
30902
31779
  }
30903
31780
  }
30904
31781
  *parseStream() {
30905
- let line2 = this.getLine();
30906
- if (line2 === null)
31782
+ let line3 = this.getLine();
31783
+ if (line3 === null)
30907
31784
  return this.setNext("stream");
30908
- if (line2[0] === cst.BOM) {
31785
+ if (line3[0] === cst.BOM) {
30909
31786
  yield* this.pushCount(1);
30910
- line2 = line2.substring(1);
31787
+ line3 = line3.substring(1);
30911
31788
  }
30912
- if (line2[0] === "%") {
30913
- let dirEnd = line2.length;
30914
- let cs = line2.indexOf("#");
31789
+ if (line3[0] === "%") {
31790
+ let dirEnd = line3.length;
31791
+ let cs = line3.indexOf("#");
30915
31792
  while (cs !== -1) {
30916
- const ch = line2[cs - 1];
31793
+ const ch = line3[cs - 1];
30917
31794
  if (ch === " " || ch === " ") {
30918
31795
  dirEnd = cs - 1;
30919
31796
  break;
30920
31797
  } else {
30921
- cs = line2.indexOf("#", cs + 1);
31798
+ cs = line3.indexOf("#", cs + 1);
30922
31799
  }
30923
31800
  }
30924
31801
  while (true) {
30925
- const ch = line2[dirEnd - 1];
31802
+ const ch = line3[dirEnd - 1];
30926
31803
  if (ch === " " || ch === " ")
30927
31804
  dirEnd -= 1;
30928
31805
  else
30929
31806
  break;
30930
31807
  }
30931
31808
  const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true));
30932
- yield* this.pushCount(line2.length - n);
31809
+ yield* this.pushCount(line3.length - n);
30933
31810
  this.pushNewline();
30934
31811
  return "stream";
30935
31812
  }
30936
31813
  if (this.atLineEnd()) {
30937
31814
  const sp = yield* this.pushSpaces(true);
30938
- yield* this.pushCount(line2.length - sp);
31815
+ yield* this.pushCount(line3.length - sp);
30939
31816
  yield* this.pushNewline();
30940
31817
  return "stream";
30941
31818
  }
@@ -30976,13 +31853,13 @@ var require_lexer = __commonJS({
30976
31853
  }
30977
31854
  *parseDocument() {
30978
31855
  yield* this.pushSpaces(true);
30979
- const line2 = this.getLine();
30980
- if (line2 === null)
31856
+ const line3 = this.getLine();
31857
+ if (line3 === null)
30981
31858
  return this.setNext("doc");
30982
31859
  let n = yield* this.pushIndicators();
30983
- switch (line2[n]) {
31860
+ switch (line3[n]) {
30984
31861
  case "#":
30985
- yield* this.pushCount(line2.length - n);
31862
+ yield* this.pushCount(line3.length - n);
30986
31863
  // fallthrough
30987
31864
  case void 0:
30988
31865
  yield* this.pushNewline();
@@ -31007,7 +31884,7 @@ var require_lexer = __commonJS({
31007
31884
  case ">":
31008
31885
  n += yield* this.parseBlockScalarHeader();
31009
31886
  n += yield* this.pushSpaces(true);
31010
- yield* this.pushCount(line2.length - n);
31887
+ yield* this.pushCount(line3.length - n);
31011
31888
  yield* this.pushNewline();
31012
31889
  return yield* this.parseBlockScalar();
31013
31890
  default:
@@ -31027,11 +31904,11 @@ var require_lexer = __commonJS({
31027
31904
  }
31028
31905
  sp += yield* this.pushSpaces(true);
31029
31906
  } while (nl + sp > 0);
31030
- const line2 = this.getLine();
31031
- if (line2 === null)
31907
+ const line3 = this.getLine();
31908
+ if (line3 === null)
31032
31909
  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] === "}");
31910
+ if (indent !== -1 && indent < this.indentNext && line3[0] !== "#" || indent === 0 && (line3.startsWith("---") || line3.startsWith("...")) && isEmpty(line3[3])) {
31911
+ const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line3[0] === "]" || line3[0] === "}");
31035
31912
  if (!atFlowEndMarker) {
31036
31913
  this.flowLevel = 0;
31037
31914
  yield cst.FLOW_END;
@@ -31039,17 +31916,17 @@ var require_lexer = __commonJS({
31039
31916
  }
31040
31917
  }
31041
31918
  let n = 0;
31042
- while (line2[n] === ",") {
31919
+ while (line3[n] === ",") {
31043
31920
  n += yield* this.pushCount(1);
31044
31921
  n += yield* this.pushSpaces(true);
31045
31922
  this.flowKey = false;
31046
31923
  }
31047
31924
  n += yield* this.pushIndicators();
31048
- switch (line2[n]) {
31925
+ switch (line3[n]) {
31049
31926
  case void 0:
31050
31927
  return "flow";
31051
31928
  case "#":
31052
- yield* this.pushCount(line2.length - n);
31929
+ yield* this.pushCount(line3.length - n);
31053
31930
  return "flow";
31054
31931
  case "{":
31055
31932
  case "[":
@@ -31459,12 +32336,12 @@ var require_parser = __commonJS({
31459
32336
  }
31460
32337
  return prev.splice(i2, prev.length);
31461
32338
  }
31462
- function arrayPushArray(target, source) {
32339
+ function arrayPushArray(target2, source) {
31463
32340
  if (source.length < 1e5)
31464
- Array.prototype.push.apply(target, source);
32341
+ Array.prototype.push.apply(target2, source);
31465
32342
  else
31466
32343
  for (let i2 = 0; i2 < source.length; ++i2)
31467
- target.push(source[i2]);
32344
+ target2.push(source[i2]);
31468
32345
  }
31469
32346
  function fixFlowSeqItems(fc) {
31470
32347
  if (fc.start.type === "flow-seq-start") {
@@ -32412,7 +33289,7 @@ var require_dist = __commonJS({
32412
33289
  });
32413
33290
 
32414
33291
  // src/assets.ts
32415
- import { basename as basename5, join as join24, resolve as resolve13 } from "node:path";
33292
+ import { basename as basename5, join as join25, resolve as resolve13 } from "node:path";
32416
33293
  import { fileURLToPath } from "node:url";
32417
33294
  function packageRoot(env = process.env) {
32418
33295
  const override = env["MVRX_PACKAGE_ROOT"];
@@ -32421,16 +33298,16 @@ function packageRoot(env = process.env) {
32421
33298
  }
32422
33299
  function assetDir(env = process.env) {
32423
33300
  const override = env["MVRX_ASSET_DIR"];
32424
- return override !== void 0 && override !== "" ? override : join24(packageRoot(env), "assets");
33301
+ return override !== void 0 && override !== "" ? override : join25(packageRoot(env), "assets");
32425
33302
  }
32426
33303
  function assetFile(name2) {
32427
- return join24(assetDir(), basename5(name2));
33304
+ return join25(assetDir(), basename5(name2));
32428
33305
  }
32429
33306
  var VERSION;
32430
33307
  var init_assets = __esm({
32431
33308
  "src/assets.ts"() {
32432
33309
  "use strict";
32433
- VERSION = true ? "0.1.133" : "0.0.0-dev";
33310
+ VERSION = true ? "0.1.134" : "0.0.0-dev";
32434
33311
  }
32435
33312
  });
32436
33313
 
@@ -32792,8 +33669,8 @@ var init_scalar = __esm({
32792
33669
  });
32793
33670
 
32794
33671
  // ../../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;
33672
+ function unsafeIsSetExplicit(target2, localName) {
33673
+ return Object.prototype.hasOwnProperty.call(target2, localName) && target2[localName] !== void 0;
32797
33674
  }
32798
33675
  function setOwn(obj, key, value) {
32799
33676
  if (key === "__proto__") {
@@ -34236,10 +35113,10 @@ function newField(proto, parentOrFile, reg, oneof, mapEntries) {
34236
35113
  enumerable: true,
34237
35114
  configurable: true
34238
35115
  });
34239
- const label = proto.label;
35116
+ const label2 = proto.label;
34240
35117
  const type = proto.type;
34241
35118
  const jstype = (_c = proto.options) === null || _c === void 0 ? void 0 : _c.jstype;
34242
- if (label === LABEL_REPEATED) {
35119
+ if (label2 === LABEL_REPEATED) {
34243
35120
  const mapEntry = type == TYPE_MESSAGE ? mapEntries === null || mapEntries === void 0 ? void 0 : mapEntries.get(trimLeadingDot(proto.typeName)) : void 0;
34244
35121
  if (mapEntry) {
34245
35122
  field2.fieldKind = "map";
@@ -35888,8 +36765,8 @@ var init_scip_symbol = __esm({
35888
36765
  function occurrenceRange(occurrence) {
35889
36766
  const typed = occurrence.typedRange;
35890
36767
  if (typed.case === "singleLineRange") {
35891
- const { line: line2, startCharacter, endCharacter } = typed.value;
35892
- return checked([line2, startCharacter, line2, endCharacter]);
36768
+ const { line: line3, startCharacter, endCharacter } = typed.value;
36769
+ return checked([line3, startCharacter, line3, endCharacter]);
35893
36770
  }
35894
36771
  if (typed.case === "multiLineRange") {
35895
36772
  const { startLine, startCharacter, endLine, endCharacter } = typed.value;
@@ -35900,8 +36777,8 @@ function occurrenceRange(occurrence) {
35900
36777
  function occurrenceEnclosingRange(occurrence) {
35901
36778
  const typed = occurrence.typedEnclosingRange;
35902
36779
  if (typed.case === "singleLineEnclosingRange") {
35903
- const { line: line2, startCharacter, endCharacter } = typed.value;
35904
- return checked([line2, startCharacter, line2, endCharacter]);
36780
+ const { line: line3, startCharacter, endCharacter } = typed.value;
36781
+ return checked([line3, startCharacter, line3, endCharacter]);
35905
36782
  }
35906
36783
  if (typed.case === "multiLineEnclosingRange") {
35907
36784
  const { startLine, startCharacter, endLine, endCharacter } = typed.value;
@@ -37111,9 +37988,9 @@ async function Module2(moduleArg = {}) {
37111
37988
  }, "loadWebAssemblyModule");
37112
37989
  var mergeLibSymbols = /* @__PURE__ */ __name((exports, libName2) => {
37113
37990
  for (var [sym, exp] of Object.entries(exports)) {
37114
- const setImport = /* @__PURE__ */ __name((target) => {
37115
- if (!isSymbolDefined(target)) {
37116
- wasmImports[target] = exp;
37991
+ const setImport = /* @__PURE__ */ __name((target2) => {
37992
+ if (!isSymbolDefined(target2)) {
37993
+ wasmImports[target2] = exp;
37117
37994
  }
37118
37995
  }, "setImport");
37119
37996
  setImport(sym);
@@ -37999,7 +38876,7 @@ var __defProp2, __name, Edit, SIZE_OF_SHORT, SIZE_OF_INT, SIZE_OF_CURSOR, SIZE_O
37999
38876
  var init_web_tree_sitter = __esm({
38000
38877
  "../../node_modules/.pnpm/web-tree-sitter@0.27.0/node_modules/web-tree-sitter/web-tree-sitter.js"() {
38001
38878
  __defProp2 = Object.defineProperty;
38002
- __name = (target, value) => __defProp2(target, "name", { value, configurable: true });
38879
+ __name = (target2, value) => __defProp2(target2, "name", { value, configurable: true });
38003
38880
  Edit = class {
38004
38881
  static {
38005
38882
  __name(this, "Edit");
@@ -40359,7 +41236,7 @@ var init_treesitter = __esm({
40359
41236
  import { execFile } from "node:child_process";
40360
41237
  import { createHash as createHash6 } from "node:crypto";
40361
41238
  import { lstat, readFile as readFile4 } from "node:fs/promises";
40362
- import { join as join25 } from "node:path";
41239
+ import { join as join26 } from "node:path";
40363
41240
  import { promisify } from "node:util";
40364
41241
  function shardKeyOf(shard) {
40365
41242
  return `sha256:${sha256Hex2(Buffer.from(`${shard.path}\0${shard.contentHash}\0${shard.precision}`, "utf8"))}`;
@@ -40386,7 +41263,7 @@ async function mapLimit(items, limit, task) {
40386
41263
  return results;
40387
41264
  }
40388
41265
  async function hashRegularFile(repoRoot, path) {
40389
- const absolute = join25(repoRoot, path);
41266
+ const absolute = join26(repoRoot, path);
40390
41267
  try {
40391
41268
  const stats = await lstat(absolute);
40392
41269
  if (!stats.isFile()) return null;
@@ -40424,7 +41301,7 @@ async function buildSnapshot(options) {
40424
41301
  const needParse = [...hashes.keys()].filter((path) => !byPath.has(path));
40425
41302
  const parsed = await mapLimit(needParse, 8, async (path) => {
40426
41303
  if (languageForPath(path) === null) return { path, shard: null };
40427
- const bytes = await readFile4(join25(options.repoRoot, path));
41304
+ const bytes = await readFile4(join26(options.repoRoot, path));
40428
41305
  if (bytes.byteLength > MAX_PARSE_BYTES) return { path, shard: null };
40429
41306
  const hash2 = `sha256:${sha256Hex2(bytes)}`;
40430
41307
  if (hash2 !== hashes.get(path)) throw new Error(`${path} changed while the snapshot was being built`);
@@ -40608,9 +41485,9 @@ function headerOf(content, symbol2) {
40608
41485
  const start2 = symbol2.range[0];
40609
41486
  const chunk = [];
40610
41487
  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;
41488
+ const line3 = lines[i2] ?? "";
41489
+ chunk.push(line3);
41490
+ if (line3.includes("{")) break;
40614
41491
  }
40615
41492
  return chunk.join(" ");
40616
41493
  }
@@ -40655,12 +41532,12 @@ async function buildRepoIndex(files) {
40655
41532
  const id2 = match[0];
40656
41533
  if (KEYWORDS.has(id2) || seen.has(id2)) continue;
40657
41534
  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));
41535
+ for (const target2 of byName.get(id2) ?? []) {
41536
+ if (target2.id === entry.id || target2.kind === "property" || target2.kind === "parameter") continue;
41537
+ if (target2.path !== entry.path && !reaches(visible, target2.path)) continue;
41538
+ if (target2.id === entry.parent) continue;
41539
+ callees.set(entry.id, (callees.get(entry.id) ?? /* @__PURE__ */ new Set()).add(target2.id));
41540
+ callers.set(target2.id, (callers.get(target2.id) ?? /* @__PURE__ */ new Set()).add(entry.id));
40664
41541
  }
40665
41542
  }
40666
41543
  }
@@ -40679,8 +41556,8 @@ async function buildRepoIndex(files) {
40679
41556
  const implementsIds = (impl === void 0 ? [] : impl.split(/,(?![^<]*>)/).map((part) => resolve16(entry, part.trim()))).filter((value) => value !== void 0).map((value) => value.id);
40680
41557
  if (extendsIds.length === 0 && implementsIds.length === 0) continue;
40681
41558
  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] });
41559
+ for (const target2 of extendsIds) heirs.set(target2, { extendedBy: [...heirs.get(target2)?.extendedBy ?? [], id2], implementedBy: heirs.get(target2)?.implementedBy ?? [] });
41560
+ for (const target2 of implementsIds) heirs.set(target2, { extendedBy: heirs.get(target2)?.extendedBy ?? [], implementedBy: [...heirs.get(target2)?.implementedBy ?? [], id2] });
40684
41561
  }
40685
41562
  const words = /* @__PURE__ */ new Map();
40686
41563
  for (const entry of symbols2.values()) for (const word of new Set(wordsOf(entry.name))) words.set(word, (words.get(word) ?? 0) + 1);
@@ -40739,8 +41616,8 @@ function pageRank(index, personal) {
40739
41616
  for (const [from, targets] of index.callees) {
40740
41617
  const a = index.symbols.get(from)?.path;
40741
41618
  if (a === void 0) continue;
40742
- for (const target of targets) {
40743
- const b = index.symbols.get(target)?.path;
41619
+ for (const target2 of targets) {
41620
+ const b = index.symbols.get(target2)?.path;
40744
41621
  if (b !== void 0) link(a, b, 1);
40745
41622
  }
40746
41623
  }
@@ -40770,14 +41647,14 @@ function callerLines(index, seed) {
40770
41647
  for (let depth = 1; depth <= 3 && frontier.length > 0 && lines.length < 12; depth += 1) {
40771
41648
  const next = [];
40772
41649
  for (const id2 of frontier) {
40773
- const target = index.symbols.get(id2);
41650
+ const target2 = index.symbols.get(id2);
40774
41651
  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
41652
  for (const caller of callers.slice(0, 8)) {
40776
41653
  seen.add(caller.id);
40777
41654
  next.push(caller.id);
40778
- if (lines.length < 12) lines.push(`${" ".repeat(depth)}${caller.qualified} ${caller.path}:${caller.line} -> ${target.qualified}`);
41655
+ if (lines.length < 12) lines.push(`${" ".repeat(depth)}${caller.qualified} ${caller.path}:${caller.line} -> ${target2.qualified}`);
40779
41656
  }
40780
- if (callers.length > 8) lines.push(`${" ".repeat(depth)}(+${callers.length - 8} more callers of ${target.qualified})`);
41657
+ if (callers.length > 8) lines.push(`${" ".repeat(depth)}(+${callers.length - 8} more callers of ${target2.qualified})`);
40781
41658
  }
40782
41659
  frontier = next;
40783
41660
  }
@@ -40863,9 +41740,9 @@ function buildCodeMap(index, task, options = DEFAULT_MAP_OPTIONS) {
40863
41740
  const top = ordered[0]?.[1] ?? 0;
40864
41741
  for (const [path, mass] of ordered) {
40865
41742
  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);
41743
+ const line3 = outline(index, path);
41744
+ if (!fits(["", taskFree ? "Central files:" : "Related files:", ...related, line3])) break;
41745
+ related.push(line3);
40869
41746
  }
40870
41747
  if (related.length > 0) lines.push("", taskFree ? "Central files:" : "Related files:", ...related);
40871
41748
  const text = `${lines.join("\n")}
@@ -40907,9 +41784,9 @@ var init_context_map = __esm({
40907
41784
  // ../index-builder/src/context-map-build.ts
40908
41785
  import { execFile as execFile2 } from "node:child_process";
40909
41786
  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";
41787
+ import { dirname as dirname10, join as join27 } from "node:path";
40911
41788
  import { promisify as promisify2 } from "node:util";
40912
- async function git3(root, args2) {
41789
+ async function git5(root, args2) {
40913
41790
  const { stdout } = await execFileAsync2("git", args2, { cwd: root, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
40914
41791
  return stdout.trim();
40915
41792
  }
@@ -40920,7 +41797,7 @@ async function buildDigestForCheckout(options) {
40920
41797
  for (const path of tracked) {
40921
41798
  if (languageForPath(path) === null) continue;
40922
41799
  try {
40923
- const bytes = await readFile5(join26(options.root, path));
41800
+ const bytes = await readFile5(join27(options.root, path));
40924
41801
  if (bytes.byteLength > MAX_PARSE_BYTES) {
40925
41802
  uncovered += 1;
40926
41803
  continue;
@@ -40931,10 +41808,10 @@ async function buildDigestForCheckout(options) {
40931
41808
  }
40932
41809
  }
40933
41810
  const index = await buildRepoIndex(files);
40934
- const commitSha = await git3(options.root, ["rev-parse", "HEAD"]);
41811
+ const commitSha = await git5(options.root, ["rev-parse", "HEAD"]);
40935
41812
  let branch;
40936
41813
  try {
40937
- branch = await git3(options.root, ["symbolic-ref", "--short", "-q", "HEAD"]) || void 0;
41814
+ branch = await git5(options.root, ["symbolic-ref", "--short", "-q", "HEAD"]) || void 0;
40938
41815
  } catch {
40939
41816
  branch = void 0;
40940
41817
  }
@@ -40943,8 +41820,8 @@ async function buildDigestForCheckout(options) {
40943
41820
  );
40944
41821
  }
40945
41822
  async function writeDigest(root, digest, out2) {
40946
- const path = out2 ?? join26(root, CODE_MAP_DIGEST_PATH);
40947
- await mkdir3(dirname9(path), { recursive: true });
41823
+ const path = out2 ?? join27(root, CODE_MAP_DIGEST_PATH);
41824
+ await mkdir3(dirname10(path), { recursive: true });
40948
41825
  await writeFile3(path, `${JSON.stringify(digest)}
40949
41826
  `);
40950
41827
  return path;
@@ -40964,10 +41841,10 @@ var init_context_map_build = __esm({
40964
41841
  // ../index-builder/src/run.ts
40965
41842
  import { execFile as execFile3 } from "node:child_process";
40966
41843
  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";
41844
+ import { join as join28, resolve as resolve14 } from "node:path";
40968
41845
  import { parseArgs as parseArgs2 } from "node:util";
40969
41846
  import { promisify as promisify3 } from "node:util";
40970
- async function git4(root, args2) {
41847
+ async function git6(root, args2) {
40971
41848
  const { stdout } = await execFileAsync3("git", args2, { cwd: root, encoding: "utf8" });
40972
41849
  return stdout.trim();
40973
41850
  }
@@ -40978,7 +41855,7 @@ function parseRepository(value) {
40978
41855
  }
40979
41856
  async function repositoryFromRemote(root) {
40980
41857
  try {
40981
- const url2 = await git4(root, ["remote", "get-url", "origin"]);
41858
+ const url2 = await git6(root, ["remote", "get-url", "origin"]);
40982
41859
  return /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/.exec(url2)?.slice(1, 3).join("/");
40983
41860
  } catch {
40984
41861
  return void 0;
@@ -40990,7 +41867,7 @@ async function snapshotFromArgs(values, env) {
40990
41867
  if (projectId !== void 0 && !ProjectId.safeParse(projectId).success) throw new UsageError2(`--project must be a project identifier, got "${projectId}"`);
40991
41868
  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
41869
  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"]);
41870
+ const commit = (typeof values["commit"] === "string" ? values["commit"] : void 0) ?? env["GITHUB_SHA"] ?? await git6(root, ["rev-parse", "HEAD"]);
40994
41871
  const branch = typeof values["branch"] === "string" ? values["branch"] : void 0;
40995
41872
  const tenant = (typeof values["tenant"] === "string" ? values["tenant"] : void 0) ?? env["HARNESS_TENANT"];
40996
41873
  if (!tenant) throw new UsageError2("missing --tenant (or HARNESS_TENANT)");
@@ -41047,13 +41924,13 @@ ${USAGE}`);
41047
41924
  }
41048
41925
  }
41049
41926
  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));
41927
+ await mkdir4(join28(dir, "shards"), { recursive: true });
41928
+ for (const name2 of await readdir4(join28(dir, "shards"))) await rm2(join28(dir, "shards", name2));
41929
+ await writeFile4(join28(dir, "manifest.json"), JSON.stringify(built.manifest, null, 2));
41930
+ await writeFile4(join28(dir, "uncovered.json"), JSON.stringify(built.uncovered, null, 2));
41054
41931
  for (const shard of built.shards) {
41055
41932
  const name2 = shardKeyOf(shard).slice("sha256:".length);
41056
- await writeFile4(join27(dir, "shards", `${name2}.json`), JSON.stringify(shard));
41933
+ await writeFile4(join28(dir, "shards", `${name2}.json`), JSON.stringify(shard));
41057
41934
  }
41058
41935
  }
41059
41936
  var execFileAsync3, USAGE, UsageError2, OPTIONS;
@@ -41122,17 +41999,17 @@ var init_chunk_Br0eD_fh = __esm({
41122
41999
  __hasOwnProp2 = Object.prototype.hasOwnProperty;
41123
42000
  __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
41124
42001
  __exportAll = (all, symbols2) => {
41125
- let target = {};
42002
+ let target2 = {};
41126
42003
  for (var name2 in all) {
41127
- __defProp3(target, name2, {
42004
+ __defProp3(target2, name2, {
41128
42005
  get: all[name2],
41129
42006
  enumerable: true
41130
42007
  });
41131
42008
  }
41132
42009
  if (symbols2) {
41133
- __defProp3(target, Symbol.toStringTag, { value: "Module" });
42010
+ __defProp3(target2, Symbol.toStringTag, { value: "Module" });
41134
42011
  }
41135
- return target;
42012
+ return target2;
41136
42013
  };
41137
42014
  __copyProps2 = (to, from, except, desc) => {
41138
42015
  if (from && typeof from === "object" || typeof from === "function") {
@@ -41148,10 +42025,10 @@ var init_chunk_Br0eD_fh = __esm({
41148
42025
  }
41149
42026
  return to;
41150
42027
  };
41151
- __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", {
42028
+ __toESM2 = (mod, isNodeMode, target2) => (target2 = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(isNodeMode || !mod || !mod.__esModule ? __defProp3(target2, "default", {
41152
42029
  value: mod,
41153
42030
  enumerable: true
41154
- }) : target, mod));
42031
+ }) : target2, mod));
41155
42032
  }
41156
42033
  });
41157
42034
 
@@ -44815,8 +45692,8 @@ function manualInputRequiredValue(decoded) {
44815
45692
  ...decoded.requestState !== void 0 && { requestState: decoded.requestState }
44816
45693
  };
44817
45694
  }
44818
- function deserializeMessage(line2) {
44819
- return JSONRPCMessageSchema.parse(JSON.parse(line2));
45695
+ function deserializeMessage(line3) {
45696
+ return JSONRPCMessageSchema.parse(JSON.parse(line3));
44820
45697
  }
44821
45698
  function serializeMessage(message) {
44822
45699
  return JSON.stringify(message) + "\n";
@@ -46755,10 +47632,10 @@ var init_src_CX2iR2pK = __esm({
46755
47632
  while (this._buffer) {
46756
47633
  const index = this._buffer.indexOf("\n");
46757
47634
  if (index === -1) return null;
46758
- const line2 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
47635
+ const line3 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
46759
47636
  this._buffer = this._buffer.subarray(index + 1);
46760
47637
  try {
46761
- return deserializeMessage(line2);
47638
+ return deserializeMessage(line3);
46762
47639
  } catch (error62) {
46763
47640
  if (error62 instanceof SyntaxError) continue;
46764
47641
  throw error62;
@@ -46981,7 +47858,7 @@ var init_ajvProvider_CEoC_sr = __esm({
46981
47858
  }
46982
47859
  };
46983
47860
  exports.ValueScopeName = ValueScopeName;
46984
- const line2 = (0, code_1._)`\n`;
47861
+ const line3 = (0, code_1._)`\n`;
46985
47862
  var ValueScope = class extends Scope {
46986
47863
  constructor(opts) {
46987
47864
  super(opts);
@@ -46989,7 +47866,7 @@ var init_ajvProvider_CEoC_sr = __esm({
46989
47866
  this._scope = opts.scope;
46990
47867
  this.opts = {
46991
47868
  ...opts,
46992
- _n: opts.lines ? line2 : code_1.nil
47869
+ _n: opts.lines ? line3 : code_1.nil
46993
47870
  };
46994
47871
  }
46995
47872
  get() {
@@ -47208,9 +48085,9 @@ var init_ajvProvider_CEoC_sr = __esm({
47208
48085
  }
47209
48086
  };
47210
48087
  var Label2 = class extends Node2 {
47211
- constructor(label) {
48088
+ constructor(label2) {
47212
48089
  super();
47213
- this.label = label;
48090
+ this.label = label2;
47214
48091
  this.names = {};
47215
48092
  }
47216
48093
  render({ _n }) {
@@ -47218,9 +48095,9 @@ var init_ajvProvider_CEoC_sr = __esm({
47218
48095
  }
47219
48096
  };
47220
48097
  var Break = class extends Node2 {
47221
- constructor(label) {
48098
+ constructor(label2) {
47222
48099
  super();
47223
- this.label = label;
48100
+ this.label = label2;
47224
48101
  this.names = {};
47225
48102
  }
47226
48103
  render({ _n }) {
@@ -47585,11 +48462,11 @@ var init_ajvProvider_CEoC_sr = __esm({
47585
48462
  endFor() {
47586
48463
  return this._endBlockNode(For);
47587
48464
  }
47588
- label(label) {
47589
- return this._leafNode(new Label2(label));
48465
+ label(label2) {
48466
+ return this._leafNode(new Label2(label2));
47590
48467
  }
47591
- break(label) {
47592
- return this._leafNode(new Break(label));
48468
+ break(label2) {
48469
+ return this._leafNode(new Break(label2));
47593
48470
  }
47594
48471
  return(value) {
47595
48472
  const node2 = new Return();
@@ -49357,7 +50234,7 @@ var init_ajvProvider_CEoC_sr = __esm({
49357
50234
  }
49358
50235
  }
49359
50236
  exports.compileSchema = compileSchema;
49360
- function resolveRef2(root, baseId, ref) {
50237
+ function resolveRef3(root, baseId, ref) {
49361
50238
  var _a3;
49362
50239
  ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
49363
50240
  const schOrFunc = root.refs[ref];
@@ -49376,7 +50253,7 @@ var init_ajvProvider_CEoC_sr = __esm({
49376
50253
  if (_sch === void 0) return;
49377
50254
  return root.refs[ref] = inlineOrCompile.call(this, _sch);
49378
50255
  }
49379
- exports.resolveRef = resolveRef2;
50256
+ exports.resolveRef = resolveRef3;
49380
50257
  function inlineOrCompile(sch) {
49381
50258
  if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs)) return sch.schema;
49382
50259
  return sch.validate ? sch : compileSchema.call(this, sch);
@@ -49845,49 +50722,49 @@ var init_ajvProvider_CEoC_sr = __esm({
49845
50722
  return serialize(resolved, schemelessOptions);
49846
50723
  }
49847
50724
  function resolveComponent(base4, relative4, options, skipNormalization) {
49848
- const target = {};
50725
+ const target2 = {};
49849
50726
  if (!skipNormalization) {
49850
50727
  base4 = parse5(serialize(base4, options), options);
49851
50728
  relative4 = parse5(serialize(relative4, options), options);
49852
50729
  }
49853
50730
  options = options || {};
49854
50731
  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;
50732
+ target2.scheme = relative4.scheme;
50733
+ target2.userinfo = relative4.userinfo;
50734
+ target2.host = relative4.host;
50735
+ target2.port = relative4.port;
50736
+ target2.path = removeDotSegments(relative4.path || "");
50737
+ target2.query = relative4.query;
49861
50738
  } else {
49862
50739
  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;
50740
+ target2.userinfo = relative4.userinfo;
50741
+ target2.host = relative4.host;
50742
+ target2.port = relative4.port;
50743
+ target2.path = removeDotSegments(relative4.path || "");
50744
+ target2.query = relative4.query;
49868
50745
  } else {
49869
50746
  if (!relative4.path) {
49870
- target.path = base4.path;
49871
- if (relative4.query !== void 0) target.query = relative4.query;
49872
- else target.query = base4.query;
50747
+ target2.path = base4.path;
50748
+ if (relative4.query !== void 0) target2.query = relative4.query;
50749
+ else target2.query = base4.query;
49873
50750
  } else {
49874
- if (relative4.path[0] === "/") target.path = removeDotSegments(relative4.path);
50751
+ if (relative4.path[0] === "/") target2.path = removeDotSegments(relative4.path);
49875
50752
  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);
50753
+ if ((base4.userinfo !== void 0 || base4.host !== void 0 || base4.port !== void 0) && !base4.path) target2.path = "/" + relative4.path;
50754
+ else if (!base4.path) target2.path = relative4.path;
50755
+ else target2.path = base4.path.slice(0, base4.path.lastIndexOf("/") + 1) + relative4.path;
50756
+ target2.path = removeDotSegments(target2.path);
49880
50757
  }
49881
- target.query = relative4.query;
50758
+ target2.query = relative4.query;
49882
50759
  }
49883
- target.userinfo = base4.userinfo;
49884
- target.host = base4.host;
49885
- target.port = base4.port;
50760
+ target2.userinfo = base4.userinfo;
50761
+ target2.host = base4.host;
50762
+ target2.port = base4.port;
49886
50763
  }
49887
- target.scheme = base4.scheme;
50764
+ target2.scheme = base4.scheme;
49888
50765
  }
49889
- target.fragment = relative4.fragment;
49890
- return target;
50766
+ target2.fragment = relative4.fragment;
50767
+ return target2;
49891
50768
  }
49892
50769
  function equal(uriA, uriB, options) {
49893
50770
  if (typeof uriA === "string") {
@@ -55899,9 +56776,9 @@ var init_cloud = __esm({
55899
56776
 
55900
56777
  // ../code-shim/src/git.ts
55901
56778
  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) {
56779
+ import { existsSync as existsSync9 } from "node:fs";
56780
+ import { join as join32 } from "node:path";
56781
+ function git7(root, args2) {
55905
56782
  try {
55906
56783
  return execFileSync("git", args2, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024, timeout: 1e4 });
55907
56784
  } catch {
@@ -55909,12 +56786,12 @@ function git5(root, args2) {
55909
56786
  }
55910
56787
  }
55911
56788
  function headCommit2(root) {
55912
- const out2 = git5(root, ["rev-parse", "--verify", "HEAD"]);
56789
+ const out2 = git7(root, ["rev-parse", "--verify", "HEAD"]);
55913
56790
  const sha = out2?.trim();
55914
56791
  return sha !== void 0 && /^[0-9a-f]{40,64}$/.test(sha) ? sha : null;
55915
56792
  }
55916
56793
  function originRepository(root) {
55917
- const url2 = git5(root, ["remote", "get-url", "origin"])?.trim();
56794
+ const url2 = git7(root, ["remote", "get-url", "origin"])?.trim();
55918
56795
  if (url2 === void 0 || url2.length === 0) return null;
55919
56796
  const match = /[:/]([^/:]+)\/([^/]+?)(?:\.git)?\/?$/.exec(url2);
55920
56797
  const owner = match?.[1];
@@ -55960,9 +56837,9 @@ function parseStatus(raw, exists) {
55960
56837
  }
55961
56838
  function readWorkingCopyState(root) {
55962
56839
  const head = headCommit2(root);
55963
- const raw = git5(root, ["status", "--porcelain=v2", "-z", "--untracked-files=all"]);
56840
+ const raw = git7(root, ["status", "--porcelain=v2", "-z", "--untracked-files=all"]);
55964
56841
  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))) };
56842
+ return { head, ...parseStatus(raw, (path) => existsSync9(join32(root, path))) };
55966
56843
  }
55967
56844
  var init_git2 = __esm({
55968
56845
  "../code-shim/src/git.ts"() {
@@ -55971,18 +56848,18 @@ var init_git2 = __esm({
55971
56848
  });
55972
56849
 
55973
56850
  // ../code-shim/src/config.ts
55974
- import { readFileSync as readFileSync14 } from "node:fs";
56851
+ import { readFileSync as readFileSync15 } from "node:fs";
55975
56852
  import { homedir as homedir2 } from "node:os";
55976
- import { join as join32 } from "node:path";
56853
+ import { join as join33 } from "node:path";
55977
56854
  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");
56855
+ const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join33(homedir2(), ".config");
56856
+ return join33(base4, "mvrx-harness");
55980
56857
  }
55981
56858
  function findToken2(env, serviceUrl, now) {
55982
56859
  const fromEnv = env["HARNESS_TOKEN"];
55983
56860
  if (fromEnv !== void 0 && fromEnv.length > 0) return fromEnv;
55984
56861
  try {
55985
- const file2 = JSON.parse(readFileSync14(join32(configDir(env), "credentials.json"), "utf8"));
56862
+ const file2 = JSON.parse(readFileSync15(join33(configDir(env), "credentials.json"), "utf8"));
55986
56863
  const stored = file2[serviceUrl];
55987
56864
  if (stored === void 0 || typeof stored.accessToken !== "string" || stored.accessToken.length === 0) return null;
55988
56865
  if (typeof stored.expiresAt === "number" && stored.expiresAt <= now) return null;
@@ -55994,7 +56871,7 @@ function findToken2(env, serviceUrl, now) {
55994
56871
  function resolveCloudConfig(root, env, now) {
55995
56872
  let binding;
55996
56873
  try {
55997
- const parsed = ProjectBinding.safeParse(JSON.parse(readFileSync14(join32(root, ".harness", "project.json"), "utf8")));
56874
+ const parsed = ProjectBinding.safeParse(JSON.parse(readFileSync15(join33(root, ".harness", "project.json"), "utf8")));
55998
56875
  if (!parsed.success) return null;
55999
56876
  binding = parsed.data;
56000
56877
  } catch {
@@ -56016,7 +56893,7 @@ var init_config = __esm({
56016
56893
  });
56017
56894
 
56018
56895
  // ../code-shim/src/local.ts
56019
- import { readFileSync as readFileSync15, statSync as statSync3 } from "node:fs";
56896
+ import { readFileSync as readFileSync16, statSync as statSync3 } from "node:fs";
56020
56897
  import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve15, sep as sep2 } from "node:path";
56021
56898
  function resolveInside(root, path) {
56022
56899
  const absolute = resolve15(root, path);
@@ -56052,12 +56929,12 @@ function scanWord(content, name2, language) {
56052
56929
  const hashComments = language === "python";
56053
56930
  const slashComments = language !== "python";
56054
56931
  const out2 = [];
56055
- let line2 = 0;
56932
+ let line3 = 0;
56056
56933
  let lineStart = 0;
56057
56934
  let i2 = 0;
56058
56935
  const n = content.length;
56059
56936
  const newline = (at) => {
56060
- line2 += 1;
56937
+ line3 += 1;
56061
56938
  lineStart = at + 1;
56062
56939
  };
56063
56940
  while (i2 < n) {
@@ -56092,7 +56969,7 @@ function scanWord(content, name2, language) {
56092
56969
  } else if (/[A-Za-z_$]/.test(c)) {
56093
56970
  const start2 = i2;
56094
56971
  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]);
56972
+ if (content.slice(start2, i2) === name2) out2.push([line3, start2 - lineStart, line3, i2 - lineStart]);
56096
56973
  } else {
56097
56974
  i2 += 1;
56098
56975
  }
@@ -56136,7 +57013,7 @@ var init_local = __esm({
56136
57013
  let shard;
56137
57014
  try {
56138
57015
  this.parses += 1;
56139
- shard = await this.parse(path, readFileSync15(absolute, "utf8"));
57016
+ shard = await this.parse(path, readFileSync16(absolute, "utf8"));
56140
57017
  } catch {
56141
57018
  shard = null;
56142
57019
  }
@@ -56148,7 +57025,7 @@ var init_local = __esm({
56148
57025
  const absolute = resolveInside(this.root, path);
56149
57026
  if (absolute === null) return null;
56150
57027
  try {
56151
- return readFileSync15(absolute, "utf8");
57028
+ return readFileSync16(absolute, "utf8");
56152
57029
  } catch {
56153
57030
  return null;
56154
57031
  }
@@ -56158,7 +57035,7 @@ var init_local = __esm({
56158
57035
  const absolute = resolveInside(this.root, path);
56159
57036
  if (absolute === null) return void 0;
56160
57037
  try {
56161
- const lines = readFileSync15(absolute, "utf8").split("\n");
57038
+ const lines = readFileSync16(absolute, "utf8").split("\n");
56162
57039
  const start2 = range[0];
56163
57040
  if (start2 >= lines.length) return void 0;
56164
57041
  const end = Math.min(Math.max(range[2], start2), start2 + maxLines - 1, lines.length - 1);
@@ -56385,25 +57262,25 @@ var init_nav = __esm({
56385
57262
 
56386
57263
  // ../code-shim/src/edit.ts
56387
57264
  import { createHash as createHash8 } from "node:crypto";
56388
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
57265
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
56389
57266
  function lineStarts(text) {
56390
57267
  const starts = [0];
56391
57268
  for (let i2 = 0; i2 < text.length; i2 += 1) if (text[i2] === "\n") starts.push(i2 + 1);
56392
57269
  return starts;
56393
57270
  }
56394
- function offsetOf(starts, text, line2, character) {
56395
- const start2 = starts[line2];
57271
+ function offsetOf(starts, text, line3, character) {
57272
+ const start2 = starts[line3];
56396
57273
  if (start2 === void 0) return null;
56397
- const next = starts[line2 + 1] ?? text.length + 1;
57274
+ const next = starts[line3 + 1] ?? text.length + 1;
56398
57275
  return Math.min(start2 + character, next - 1);
56399
57276
  }
56400
57277
  function positionAt(text, offset) {
56401
57278
  const starts = lineStarts(text.slice(0, offset));
56402
- const line2 = starts.length - 1;
56403
- return [line2, offset - (starts[line2] ?? 0)];
57279
+ const line3 = starts.length - 1;
57280
+ return [line3, offset - (starts[line3] ?? 0)];
56404
57281
  }
56405
- function lineEnd(starts, text, line2) {
56406
- const next = starts[line2 + 1];
57282
+ function lineEnd(starts, text, line3) {
57283
+ const next = starts[line3 + 1];
56407
57284
  return next === void 0 ? text.length : next - 1;
56408
57285
  }
56409
57286
  async function locate(index, path, namePath) {
@@ -56426,7 +57303,7 @@ async function editSymbol(index, args2) {
56426
57303
  if (!found.ok) return found;
56427
57304
  const absolute = resolveInside(index.root, args2.path);
56428
57305
  if (absolute === null) return { ok: false, reason: `path ${args2.path} is outside the repository` };
56429
- const original = readFileSync16(absolute, "utf8");
57306
+ const original = readFileSync17(absolute, "utf8");
56430
57307
  const starts = lineStarts(original);
56431
57308
  const range = found.symbol.enclosingRange ?? found.symbol.range;
56432
57309
  const from = offsetOf(starts, original, range[0], range[1]);
@@ -56454,7 +57331,7 @@ ${args2.content.replace(/\n+$/, "")}`;
56454
57331
  const before = positionAt(next, changedStart);
56455
57332
  const after = positionAt(next, insertAt + inserted.length);
56456
57333
  const bytes = Buffer.from(next, "utf8");
56457
- writeFileSync9(absolute, bytes);
57334
+ writeFileSync10(absolute, bytes);
56458
57335
  return {
56459
57336
  ok: true,
56460
57337
  path: args2.path,
@@ -56482,10 +57359,10 @@ async function renameSymbol(deps, args2) {
56482
57359
  }
56483
57360
  const targets = [{ path: args2.path, range: found.symbol.range }, ...answer.references.map((hit) => ({ path: hit.path, range: hit.range }))];
56484
57361
  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);
57362
+ for (const target2 of targets) {
57363
+ const list = byFile.get(target2.path) ?? [];
57364
+ if (!list.some((r) => r.join(",") === target2.range.join(","))) list.push(target2.range);
57365
+ byFile.set(target2.path, list);
56489
57366
  }
56490
57367
  const plan3 = [];
56491
57368
  for (const [path, ranges] of byFile) {
@@ -56493,7 +57370,7 @@ async function renameSymbol(deps, args2) {
56493
57370
  if (absolute === null) return { ok: false, reason: `reference path ${path} is outside the repository` };
56494
57371
  let text;
56495
57372
  try {
56496
- text = readFileSync16(absolute, "utf8");
57373
+ text = readFileSync17(absolute, "utf8");
56497
57374
  } catch {
56498
57375
  return { ok: false, reason: `cannot read ${path}` };
56499
57376
  }
@@ -56515,7 +57392,7 @@ async function renameSymbol(deps, args2) {
56515
57392
  let next = item.text;
56516
57393
  for (const offset of offsets) next = next.slice(0, offset) + args2.new_name + next.slice(offset + oldName.length);
56517
57394
  const bytes = Buffer.from(next, "utf8");
56518
- writeFileSync9(item.absolute, bytes);
57395
+ writeFileSync10(item.absolute, bytes);
56519
57396
  filesChanged.push({ path: item.path, edits: offsets.length, sha256: sha2563(bytes) });
56520
57397
  edits += offsets.length;
56521
57398
  }
@@ -57113,12 +57990,12 @@ function toErrorBody(error62, command) {
57113
57990
  }
57114
57991
 
57115
57992
  // ../cli/src/deps.ts
57116
- function say(deps, line2 = "") {
57117
- deps.stdout(`${line2}
57993
+ function say(deps, line3 = "") {
57994
+ deps.stdout(`${line3}
57118
57995
  `);
57119
57996
  }
57120
- function warn(deps, line2) {
57121
- deps.stderr(`${redact(line2)}
57997
+ function warn(deps, line3) {
57998
+ deps.stderr(`${redact(line3)}
57122
57999
  `);
57123
58000
  }
57124
58001
 
@@ -57140,8 +58017,8 @@ function parseAgentList(value) {
57140
58017
  async function detectAgent(deps, agent, cwd) {
57141
58018
  const result = await deps.exec(agent, ["--version"], { cwd });
57142
58019
  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" };
58020
+ const line3 = result.stdout.trim().split("\n")[0] ?? "";
58021
+ return { agent, installed: true, detail: line3.length > 0 ? line3 : "version not reported" };
57145
58022
  }
57146
58023
  const reason = result.code === 127 ? `\`${agent}\` was not found on PATH` : `\`${agent} --version\` failed (exit ${result.code}): ${result.stderr.trim()}`;
57147
58024
  return { agent, installed: false, detail: reason };
@@ -57332,17 +58209,17 @@ var HEADER = /^\s*\[(?!\[)(.+?)\]\s*(?:#.*)?$/;
57332
58209
  function readTables(text) {
57333
58210
  const tables = [];
57334
58211
  let current2 = null;
57335
- for (const line2 of text.split("\n")) {
57336
- const match = HEADER.exec(line2);
58212
+ for (const line3 of text.split("\n")) {
58213
+ const match = HEADER.exec(line3);
57337
58214
  if (match !== null && match[1] !== void 0) {
57338
58215
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
57339
58216
  const path = parseKeyPath(match[1]);
57340
58217
  current2 = path === null ? null : { path, lines: [] };
57341
- } else if (/^\s*\[\[/.test(line2)) {
58218
+ } else if (/^\s*\[\[/.test(line3)) {
57342
58219
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
57343
58220
  current2 = null;
57344
58221
  } else if (current2 !== null) {
57345
- current2.lines.push(line2);
58222
+ current2.lines.push(line3);
57346
58223
  }
57347
58224
  }
57348
58225
  if (current2 !== null) tables.push({ path: current2.path, body: current2.lines.join("\n") });
@@ -57831,9 +58708,9 @@ function namesCommit(stdout, commit) {
57831
58708
  }
57832
58709
  async function assessAgent(deps, input2) {
57833
58710
  const { agent } = input2.info;
57834
- const label = AGENT_LABEL[agent];
58711
+ const label2 = AGENT_LABEL[agent];
57835
58712
  if (!input2.info.installed) {
57836
- return report(agent, "incompatible", input2.info.detail, `install ${label} (\`${agent}\`) and run \`overlane doctor\` again`);
58713
+ return report(agent, "incompatible", input2.info.detail, `install ${label2} (\`${agent}\`) and run \`overlane doctor\` again`);
57837
58714
  }
57838
58715
  if (input2.binding.kind !== "ok") {
57839
58716
  const why = input2.binding.kind === "missing" ? "no .harness/project.json" : `invalid .harness/project.json (${input2.binding.detail})`;
@@ -57843,7 +58720,7 @@ async function assessAgent(deps, input2) {
57843
58720
  if (input2.service === "rejected") return report(agent, "blocked", "the service rejected the stored token", "run `overlane connect` to sign in again");
57844
58721
  const enabled = agent === "claude" ? readClaudeState(input2.root).plugin : readCodexState(deps, input2.root).projectEnabled;
57845
58722
  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`");
58723
+ return report(agent, "blocked", `the Overlane plugin (id mvrx-harness) is not installed and enabled for ${label2} in this project`, "run `overlane connect`");
57847
58724
  }
57848
58725
  if (agent === "codex" && !codexHookTrusted(deps)) {
57849
58726
  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 +58996,9 @@ async function connect(deps, options) {
58119
58996
  $ ${formatCommand(step2)}`);
58120
58997
  if (codexEdit !== null) {
58121
58998
  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}`);
58999
+ for (const line3 of codexEdit.block.split("\n")) if (line3.length > 0) say(deps, ` + ${line3}`);
58123
59000
  }
58124
- for (const line2 of present) say(deps, ` skip: ${line2}`);
59001
+ for (const line3 of present) say(deps, ` skip: ${line3}`);
58125
59002
  say(deps, ` write ${BINDING_FILE}: project ID, tenant ID, service URL, release digest and the release-signing public key from the service`);
58126
59003
  say(deps, " write .harness/owned.json: the entries overlane added, so `overlane disconnect` removes only those");
58127
59004
  if (excludeNeeded) say(deps, ` append ${EXCLUDE_LINE} to ${await excludePath(deps, root)}`);
@@ -58149,7 +59026,7 @@ async function connect(deps, options) {
58149
59026
  writes,
58150
59027
  notes: [
58151
59028
  ...existingToken === null ? [`Sign-in: browser sign-in to ${serviceUrl} (OAuth with PKCE); set HARNESS_TOKEN to skip it.`] : [],
58152
- ...present.map((line2) => `skip: ${line2}`)
59029
+ ...present.map((line3) => `skip: ${line3}`)
58153
59030
  ]
58154
59031
  });
58155
59032
  return { data: plan3 };
@@ -58221,7 +59098,7 @@ async function connect(deps, options) {
58221
59098
  for (const info2 of infos) {
58222
59099
  reports.push(await assessAgent(deps, { root, marketplaceRoot: marketplaceRoot2, info: info2, binding: { kind: "ok", binding }, hasToken: true, service: "reachable" }));
58223
59100
  }
58224
- for (const line2 of renderReports(reports)) say(deps, ` ${line2}`);
59101
+ for (const line3 of renderReports(reports)) say(deps, ` ${line3}`);
58225
59102
  return {
58226
59103
  data: {
58227
59104
  project: projectSummary(project),
@@ -58513,10 +59390,10 @@ function deepMerge(existing, incoming) {
58513
59390
  }
58514
59391
  return incoming;
58515
59392
  }
58516
- function setAt(target, key, value) {
59393
+ function setAt(target2, key, value) {
58517
59394
  const [head, ...rest] = key;
58518
- if (head === void 0) return target;
58519
- const out2 = { ...target };
59395
+ if (head === void 0) return target2;
59396
+ const out2 = { ...target2 };
58520
59397
  if (rest.length === 0) {
58521
59398
  out2[head] = deepMerge(out2[head], value);
58522
59399
  return out2;
@@ -58708,7 +59585,7 @@ var clientInstallCommand = defineCommand({
58708
59585
  }
58709
59586
  const files = applyPlan(dir, ops);
58710
59587
  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);
59588
+ for (const line3 of NEXT[name2]) say(inv.deps, line3);
58712
59589
  return { data: { client: name2, surface, dir, files, next: NEXT[name2] } };
58713
59590
  }
58714
59591
  });
@@ -58754,12 +59631,12 @@ async function verifyRelease(record3, publicKey) {
58754
59631
  }
58755
59632
  return manifest;
58756
59633
  }
58757
- function diffFiles(installed, target) {
59634
+ function diffFiles(installed, target2) {
58758
59635
  const before = new Map(installed.map((file2) => [file2.path, file2]));
58759
- const after = new Set(target.map((file2) => file2.path));
59636
+ const after = new Set(target2.map((file2) => file2.path));
58760
59637
  const added = [];
58761
59638
  const changed = [];
58762
- for (const file2 of target) {
59639
+ for (const file2 of target2) {
58763
59640
  const old = before.get(file2.path);
58764
59641
  if (old === void 0) added.push(file2);
58765
59642
  else if (old.digest !== file2.digest) changed.push(file2);
@@ -58796,8 +59673,8 @@ function diskDigest(marketplaceRoot2, file2) {
58796
59673
  return null;
58797
59674
  }
58798
59675
  }
58799
- function editedManagedFiles(marketplaceRoot2, installed, target) {
58800
- const wanted = new Map(target.map((file2) => [file2.path, file2.digest]));
59676
+ function editedManagedFiles(marketplaceRoot2, installed, target2) {
59677
+ const wanted = new Map(target2.map((file2) => [file2.path, file2.digest]));
58801
59678
  const edited = [];
58802
59679
  for (const file2 of installed) {
58803
59680
  const actual = diskDigest(marketplaceRoot2, file2);
@@ -58805,9 +59682,9 @@ function editedManagedFiles(marketplaceRoot2, installed, target) {
58805
59682
  }
58806
59683
  return edited;
58807
59684
  }
58808
- function mismatchedFiles(marketplaceRoot2, target) {
59685
+ function mismatchedFiles(marketplaceRoot2, target2) {
58809
59686
  const bad = [];
58810
- for (const file2 of target) {
59687
+ for (const file2 of target2) {
58811
59688
  const actual = diskDigest(marketplaceRoot2, file2);
58812
59689
  if (actual !== null && actual !== file2.digest) bad.push(file2.path);
58813
59690
  }
@@ -58926,7 +59803,7 @@ async function verifySourceTree(deps, marketplaceRoot2, files, digest) {
58926
59803
  else if (actual !== file2.digest) problems.push(`${file2.path} differs`);
58927
59804
  }
58928
59805
  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);
59806
+ const tracked = (await git(deps, marketplaceRoot2, ["ls-tree", "-r", "--name-only", "HEAD"])).text.split("\n").filter((line3) => line3.length > 0);
58930
59807
  for (const path of tracked) {
58931
59808
  if (!listed.has(path) && !path.startsWith(`${SIDECAR_DIR}/`)) problems.push(`${path} is not in the release`);
58932
59809
  }
@@ -58963,8 +59840,8 @@ function showDiff(deps, diff) {
58963
59840
  for (const file2 of files) say(deps, ` ${sign} ${file2.path} (${file2.kind})`);
58964
59841
  }
58965
59842
  }
58966
- function approvalCommand(target, explicitTo) {
58967
- return `overlane release update${explicitTo ? ` --to ${target}` : ""} --approve ${target}`;
59843
+ function approvalCommand(target2, explicitTo) {
59844
+ return `overlane release update${explicitTo ? ` --to ${target2}` : ""} --approve ${target2}`;
58968
59845
  }
58969
59846
  async function installRelease(deps, ctx, targetDigest, options, isPin) {
58970
59847
  const { root, binding } = ctx;
@@ -59019,7 +59896,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59019
59896
  refusals.push(`approval_required: --approve ${options.approve} does not name this release; run: ${approvalCommand(manifest.digest, options.to !== void 0)}`);
59020
59897
  }
59021
59898
  if (options.dryRun) {
59022
- for (const line2 of refusals) say(deps, ` would refuse - ${line2}`);
59899
+ for (const line3 of refusals) say(deps, ` would refuse - ${line3}`);
59023
59900
  say(deps, refusals.length === 0 ? "Dry run: this update would install; nothing was changed." : "Dry run: nothing was changed.");
59024
59901
  const files = { added: diff.added.length, changed: diff.changed.length, removed: diff.removed.length };
59025
59902
  return {
@@ -59029,7 +59906,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59029
59906
  requests: [{ method: "GET", path: releasePaths.one(binding.tenantId, targetDigest) }],
59030
59907
  commands: ["claude", "codex"].flatMap((agent) => updateSteps(agent === "claude" ? "claude" : "codex").map((step2) => `${step2.command} ${step2.args.join(" ")}`)),
59031
59908
  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}`)
59909
+ notes: refusals.map((line3) => `would refuse - ${line3}`)
59033
59910
  }),
59034
59911
  release: { version: manifest.version, digest: manifest.digest, changeKinds: [...kinds], restart: manifest.restart, files },
59035
59912
  wouldInstall: refusals.length === 0,
@@ -59037,7 +59914,7 @@ async function installRelease(deps, ctx, targetDigest, options, isPin) {
59037
59914
  }
59038
59915
  };
59039
59916
  }
59040
- const taskRefusal = refusals.find((line2) => line2.startsWith("task_in_progress"));
59917
+ const taskRefusal = refusals.find((line3) => line3.startsWith("task_in_progress"));
59041
59918
  if (taskRefusal !== void 0) throw new ReleaseRefusal("task_in_progress", taskRefusal.slice("task_in_progress: ".length));
59042
59919
  const first = refusals[0];
59043
59920
  if (first !== void 0) {
@@ -59266,14 +60143,14 @@ function sectionsOf(body2, level = 2) {
59266
60143
  const sections = /* @__PURE__ */ new Map([["", []]]);
59267
60144
  let current2 = "";
59268
60145
  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();
60146
+ for (const line3 of body2.replace(/\r\n/g, "\n").split("\n")) {
60147
+ if (/^\s*```/.test(line3)) fenced = !fenced;
60148
+ if (!fenced && line3.startsWith(marker)) {
60149
+ current2 = line3.slice(marker.length).trim();
59273
60150
  if (!sections.has(current2)) sections.set(current2, []);
59274
60151
  continue;
59275
60152
  }
59276
- sections.get(current2)?.push(line2);
60153
+ sections.get(current2)?.push(line3);
59277
60154
  }
59278
60155
  return sections;
59279
60156
  }
@@ -59282,10 +60159,10 @@ function sectionText(lines) {
59282
60159
  }
59283
60160
  function bulletsOf(lines, where2) {
59284
60161
  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}"`);
60162
+ for (const line3 of lines ?? []) {
60163
+ if (line3.trim() === "") continue;
60164
+ const match = /^- (.*)$/.exec(line3);
60165
+ if (!match) throw new KitError(`${where2}: expected a "- " bullet, found "${line3}"`);
59289
60166
  items.push(match[1] ?? "");
59290
60167
  }
59291
60168
  return items;
@@ -59388,10 +60265,10 @@ function parseMilestones(text, where2) {
59388
60265
  let current2;
59389
60266
  let inAcceptance = false;
59390
60267
  let fenced = false;
59391
- for (const line2 of body2.split("\n")) {
59392
- if (/^\s*```/.test(line2)) fenced = !fenced;
60268
+ for (const line3 of body2.split("\n")) {
60269
+ if (/^\s*```/.test(line3)) fenced = !fenced;
59393
60270
  if (fenced) continue;
59394
- const heading = /^## ([^:]+): (.+)$/.exec(line2);
60271
+ const heading = /^## ([^:]+): (.+)$/.exec(line3);
59395
60272
  if (heading) {
59396
60273
  current2 = { id: (heading[1] ?? "").trim(), title: (heading[2] ?? "").trim(), acceptance: [], dependsOn: [] };
59397
60274
  milestones.push(current2);
@@ -59399,15 +60276,15 @@ function parseMilestones(text, where2) {
59399
60276
  continue;
59400
60277
  }
59401
60278
  if (!current2) continue;
59402
- if (/^### Acceptance\s*$/.test(line2)) {
60279
+ if (/^### Acceptance\s*$/.test(line3)) {
59403
60280
  inAcceptance = true;
59404
60281
  continue;
59405
60282
  }
59406
- if (line2.trim() === "") continue;
59407
- const depends = /^Depends on: (.+)$/.exec(line2);
60283
+ if (line3.trim() === "") continue;
60284
+ const depends = /^Depends on: (.+)$/.exec(line3);
59408
60285
  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}"`);
60286
+ else if (inAcceptance && line3.startsWith("- ")) current2.acceptance.push(line3.slice(2));
60287
+ else throw new KitError(`${where2}: unexpected line under ${current2.id}: "${line3}"`);
59411
60288
  }
59412
60289
  return milestones;
59413
60290
  }
@@ -59560,18 +60437,18 @@ async function detectCommands(repoDir) {
59560
60437
  if (typeof run3 !== "string") continue;
59561
60438
  const dir = stepDir ?? jobDir;
59562
60439
  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);
60440
+ for (const line3 of logical) {
60441
+ const kind = classifyCommand(line3);
59565
60442
  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;
60443
+ const alias = /^(pnpm|npm|yarn|bun)(?: run)? ([\w:-]+)$/.exec(line3);
60444
+ const normalized = alias && kind !== "install" ? `${alias[1]} run ${alias[2]}` : line3;
59568
60445
  const command = dir && dir !== "." ? `cd ${dir} && ${normalized}` : normalized;
59569
60446
  if (kind === "install") {
59570
60447
  if (install === void 0) install = command;
59571
60448
  if (!sources.includes(source)) sources.push(source);
59572
60449
  continue;
59573
60450
  }
59574
- const script = scriptBodies.get(line2);
60451
+ const script = scriptBodies.get(line3);
59575
60452
  if (script !== void 0 && dir === void 0 || dir === void 0 && found[kind].includes(command)) {
59576
60453
  if (!sources.includes(source)) sources.push(source);
59577
60454
  continue;
@@ -59672,8 +60549,8 @@ function parseTasksFile(text, where2) {
59672
60549
  }
59673
60550
  return state.current;
59674
60551
  };
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);
60552
+ for (const line3 of text.replace(BLOCK_PATTERN, "").replace(/\r\n/g, "\n").split("\n")) {
60553
+ const heading = /^##\s+Phase\s+(\d+)\s*[:\u2013\u2014-]\s*(.*?)\s*$/i.exec(line3);
59677
60554
  if (heading) {
59678
60555
  let title2 = heading[2] ?? "";
59679
60556
  const tag = /\s*\{#([^}\s]+)\}\s*$/.exec(title2);
@@ -59682,12 +60559,12 @@ function parseTasksFile(text, where2) {
59682
60559
  phases.push(state.current);
59683
60560
  continue;
59684
60561
  }
59685
- const checkpoint = /^\*\*Checkpoint\*\*:\s*(.+?)\s*$/.exec(line2);
60562
+ const checkpoint = /^\*\*Checkpoint\*\*:\s*(.+?)\s*$/.exec(line3);
59686
60563
  if (checkpoint && state.current) {
59687
60564
  state.current.checkpoint = checkpoint[1];
59688
60565
  continue;
59689
60566
  }
59690
- const task = /^\s*- \[[ xX]\]\s+([A-Za-z]*\d[\w.:-]*)((?:\s+\[(?:P|US\d+)\])*)\s+(\S.*?)\s*$/.exec(line2);
60567
+ const task = /^\s*- \[[ xX]\]\s+([A-Za-z]*\d[\w.:-]*)((?:\s+\[(?:P|US\d+)\])*)\s+(\S.*?)\s*$/.exec(line3);
59691
60568
  if (!task) continue;
59692
60569
  const markers = task[2] ?? "";
59693
60570
  let title = task[3] ?? "";
@@ -59705,25 +60582,25 @@ function parseStories(specText) {
59705
60582
  const stories = /* @__PURE__ */ new Map();
59706
60583
  let story;
59707
60584
  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);
60585
+ for (const line3 of specText.replace(/\r\n/g, "\n").split("\n")) {
60586
+ const heading = /^###\s+User Story\s+(\d+)\b/i.exec(line3);
59710
60587
  if (heading) {
59711
60588
  story = [];
59712
60589
  stories.set(Number(heading[1]), story);
59713
60590
  collecting = false;
59714
60591
  continue;
59715
60592
  }
59716
- if (/^#{1,3}\s/.test(line2) || /^---\s*$/.test(line2)) {
60593
+ if (/^#{1,3}\s/.test(line3) || /^---\s*$/.test(line3)) {
59717
60594
  collecting = false;
59718
- if (!/^---/.test(line2)) story = void 0;
60595
+ if (!/^---/.test(line3)) story = void 0;
59719
60596
  continue;
59720
60597
  }
59721
60598
  if (!story) continue;
59722
- if (/\*\*Acceptance Scenarios\*\*/i.test(line2)) {
60599
+ if (/\*\*Acceptance Scenarios\*\*/i.test(line3)) {
59723
60600
  collecting = true;
59724
60601
  continue;
59725
60602
  }
59726
- const item = /^\s*\d+\.\s+(.+?)\s*$/.exec(line2);
60603
+ const item = /^\s*\d+\.\s+(.+?)\s*$/.exec(line3);
59727
60604
  if (collecting && item) story.push((item[1] ?? "").replace(/\*\*/g, ""));
59728
60605
  }
59729
60606
  return stories;
@@ -59744,8 +60621,8 @@ function parseConstitution(text) {
59744
60621
  constraints.push(body2 && !/^\[.*\]$/.test(body2) ? `${key}: ${body2}` : key);
59745
60622
  }
59746
60623
  } else {
59747
- for (const line2 of principles) {
59748
- const bullet = /^- (.+?)\s*$/.exec(line2);
60624
+ for (const line3 of principles) {
60625
+ const bullet = /^- (.+?)\s*$/.exec(line3);
59749
60626
  if (bullet) constraints.push(bullet[1] ?? "");
59750
60627
  }
59751
60628
  }
@@ -59872,10 +60749,10 @@ async function importSpecKit(repoDir) {
59872
60749
  }
59873
60750
 
59874
60751
  // ../cli/src/session.ts
59875
- async function gitRoot(deps, label) {
60752
+ async function gitRoot(deps, label2) {
59876
60753
  const top = await deps.exec("git", ["rev-parse", "--show-toplevel"], { cwd: deps.paths.cwd });
59877
60754
  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`);
60755
+ if (top.code !== 0 || root.length === 0) throw new UsageError(`not inside a git repository; run overlane ${label2} from the connected project`);
59879
60756
  return root;
59880
60757
  }
59881
60758
  function boundProject(deps, root) {
@@ -59890,11 +60767,11 @@ function boundProject(deps, root) {
59890
60767
  const override = deps.env["HARNESS_SERVICE_URL"];
59891
60768
  return { root, binding: bound.binding, serviceUrl: override !== void 0 && override.length > 0 ? override : bound.binding.serviceUrl };
59892
60769
  }
59893
- async function offlineProject(deps, label) {
59894
- return boundProject(deps, await gitRoot(deps, label));
60770
+ async function offlineProject(deps, label2) {
60771
+ return boundProject(deps, await gitRoot(deps, label2));
59895
60772
  }
59896
- async function connectedProject(deps, label) {
59897
- const project = await offlineProject(deps, label);
60773
+ async function connectedProject(deps, label2) {
60774
+ const project = await offlineProject(deps, label2);
59898
60775
  const token = await requireToken(deps, project.binding.serviceUrl);
59899
60776
  return { ...project, token: token.token };
59900
60777
  }
@@ -59945,24 +60822,24 @@ function current(window3, updatedAt, now) {
59945
60822
  function usageWarnings(usage, now) {
59946
60823
  if (usage === null) return [];
59947
60824
  const out2 = [];
59948
- const check2 = (label, window3, threshold) => {
60825
+ const check2 = (label2, window3, threshold) => {
59949
60826
  if (window3 === void 0 || window3.usedPercentage < threshold || !current(window3, usage.updatedAt, now)) return;
59950
60827
  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.`);
60828
+ 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
60829
  };
59953
60830
  check2("5-hour", usage.fiveHour, FIVE_HOUR_WARN_PERCENT);
59954
60831
  check2("weekly", usage.sevenDay, WEEKLY_WARN_PERCENT);
59955
60832
  return out2;
59956
60833
  }
59957
60834
  function warnUsage(deps) {
59958
- for (const line2 of usageWarnings(readUsage(deps), deps.now())) warn(deps, `warning: ${line2}`);
60835
+ for (const line3 of usageWarnings(readUsage(deps), deps.now())) warn(deps, `warning: ${line3}`);
59959
60836
  }
59960
60837
  function currentUsagePercent(usage, now) {
59961
60838
  if (usage === null) return null;
59962
60839
  let best = null;
59963
- for (const [label, window3] of [["5-hour", usage.fiveHour], ["weekly", usage.sevenDay]]) {
60840
+ for (const [label2, window3] of [["5-hour", usage.fiveHour], ["weekly", usage.sevenDay]]) {
59964
60841
  if (window3 === void 0 || !current(window3, usage.updatedAt, now)) continue;
59965
- if (best === null || window3.usedPercentage > best.percent) best = { percent: window3.usedPercentage, label };
60842
+ if (best === null || window3.usedPercentage > best.percent) best = { percent: window3.usedPercentage, label: label2 };
59966
60843
  }
59967
60844
  return best;
59968
60845
  }
@@ -60077,9 +60954,9 @@ async function inspectWorkingCopy(deps, root) {
60077
60954
  const digest = await workingTreeDigest(deps, root);
60078
60955
  const status = await git2(deps, root, ["status", "--porcelain", "--untracked-files=all"]);
60079
60956
  if (digest === null || status === null) return null;
60080
- const clean2 = status.split("\n").every((line2) => line2.length === 0 || isHarnessPath(line2.slice(3)));
60957
+ const clean2 = status.split("\n").every((line3) => line3.length === 0 || isHarnessPath(line3.slice(3)));
60081
60958
  const remotes = await git2(deps, root, ["branch", "-r", "--contains", "HEAD"]) ?? "";
60082
- const pushed = remotes.split("\n").some((line2) => line2.trim().length > 0 && !line2.includes("->"));
60959
+ const pushed = remotes.split("\n").some((line3) => line3.trim().length > 0 && !line3.includes("->"));
60083
60960
  return { commitSha: head, workingTreeDigest: digest, clean: clean2, pushed };
60084
60961
  }
60085
60962
  function recoveryRoute(facts2) {
@@ -60568,28 +61445,28 @@ function resumeCommand(agent, taskId) {
60568
61445
  async function handoffTask(deps, input2) {
60569
61446
  const { to } = input2;
60570
61447
  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));
61448
+ const target2 = to ?? otherAgent(detectExecutorName(deps.env));
60572
61449
  const written = await writeCheckpoint(deps, {
60573
- summary: typeof input2.summary === "string" ? input2.summary : `Handed off to ${target}`,
61450
+ summary: typeof input2.summary === "string" ? input2.summary : `Handed off to ${target2}`,
60574
61451
  next: typeof input2.next === "string" ? input2.next : "Resume the task",
60575
61452
  pause: "handoff",
60576
61453
  dryRun: input2.dryRun
60577
61454
  });
60578
61455
  if (written.kind === "planned") return written.outcome;
60579
61456
  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)}`);
61457
+ say(deps, `To continue it in ${target2}, run this yourself from this working tree:`);
61458
+ say(deps, ` ${resumeCommand(target2, written.taskId)}`);
60582
61459
  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
61460
  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
61461
  warnUsage(deps);
60585
61462
  return {
60586
61463
  data: {
60587
61464
  taskId: written.taskId,
60588
- to: target,
61465
+ to: target2,
60589
61466
  commitSha: written.copy.commitSha,
60590
61467
  route: written.route.route,
60591
61468
  localOnly: written.route.localOnly,
60592
- resumeCommand: resumeCommand(target, written.taskId)
61469
+ resumeCommand: resumeCommand(target2, written.taskId)
60593
61470
  }
60594
61471
  };
60595
61472
  }
@@ -61126,8 +62003,8 @@ async function buildContext(deps, version2) {
61126
62003
  const answers = [releases, policy, next, taskView].filter((value) => value !== null);
61127
62004
  const serviceStatus = answers.some((value) => !isMissing2(value)) ? "reachable" : answers.every((value) => value === "unauthenticated") ? "unauthenticated" : "unreachable";
61128
62005
  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;
62006
+ const target2 = isMissing2(pinnedByService) ? binding.releaseDigest : pinnedByService;
62007
+ const inSync = installed === null ? "unknown" : installed.digest === target2;
61131
62008
  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
62009
  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
62010
  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 +62065,16 @@ var ECOSYSTEM_FLAG = { name: "ecosystem", type: "string", enum: DOCS_ECOSYSTEMS,
61188
62065
  var PROJECT_FLAG = { name: "project", type: "string", value: "PROJECT_ID", description: "The project whose lockfile pins choose the version" };
61189
62066
  var common = { needsAuth: true, needsNetwork: true };
61190
62067
  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) };
62068
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62069
+ const token = await requireToken(inv.deps, target2.storeKey);
62070
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
61194
62071
  }
61195
62072
  async function tenantOf(call2, inv) {
61196
62073
  return textFlag(inv, "team") ?? (await call2(WhoAmI, "GET", apiPaths.whoami)).tenantId;
61197
62074
  }
61198
62075
  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 } }] }));
62076
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62077
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method, path, ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
61201
62078
  }
61202
62079
  function libraryArg(inv) {
61203
62080
  const library = inv.positionals[0];
@@ -61555,9 +62432,9 @@ function shape(token) {
61555
62432
  return { tokenId: token.tokenId, label: token.label, scope: scopeText(token), tools: token.tools, expiresAt: token.expiresAt, ...token.revokedAt === void 0 ? {} : { revokedAt: token.revokedAt } };
61556
62433
  }
61557
62434
  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) };
62435
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62436
+ const token = await requireToken(inv.deps, target2.storeKey);
62437
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
61561
62438
  }
61562
62439
  async function tenantOf2(call2, inv) {
61563
62440
  const given = textFlag(inv, "team");
@@ -61575,28 +62452,28 @@ var authLoginCommand = defineCommand({
61575
62452
  keywords: ["sign in", "log in", "login", "authenticate", "browser", "device code", "credentials", "session", "expired", "reauthenticate"],
61576
62453
  result: AuthLoginResult,
61577
62454
  handler: async (inv) => {
61578
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
62455
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
61579
62456
  if (inv.dryRun) {
61580
62457
  return planOutcome(
61581
62458
  inv.deps,
61582
62459
  dryRunPlan({
61583
- service: target.service,
62460
+ service: target2.service,
61584
62461
  requests: [
61585
62462
  { method: "POST", path: "/auth/device", note: "ask for a device code" },
61586
62463
  { method: "POST", path: "/auth/device/token", note: "poll until the browser approval, then receive the session once" }
61587
62464
  ],
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" }],
62465
+ 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
62466
  notes: ["The browser is opened to approve the code. No credentials are needed for this step."]
61590
62467
  })
61591
62468
  );
61592
62469
  }
61593
62470
  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 });
62471
+ const login = await deviceLogin(inv.deps, target2.service, { openBrowser: inv.values["no-browser"] !== true });
62472
+ const how = await saveTokens(inv.deps, target2.storeKey, { accessToken: login.accessToken, expiresAt: login.expiresAt });
61596
62473
  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"}).`);
62474
+ say(inv.deps, `Signed in to ${target2.service} (stored in the ${how === "keychain" ? "OS keychain" : "0600 credentials file"}).`);
61598
62475
  return {
61599
- data: { signedIn: true, service: target.service, how, tenantId: login.tenantId, expiresAt: new Date(login.expiresAt).toISOString(), tokenEnvSet, next: "overlane context" }
62476
+ data: { signedIn: true, service: target2.service, how, tenantId: login.tenantId, expiresAt: new Date(login.expiresAt).toISOString(), tokenEnvSet, next: "overlane context" }
61600
62477
  };
61601
62478
  }
61602
62479
  });
@@ -61611,24 +62488,24 @@ var authLogoutCommand = defineCommand({
61611
62488
  keywords: ["sign out", "log out", "logout", "forget credentials", "remove token", "end session"],
61612
62489
  result: AuthLogoutResult,
61613
62490
  handler: async (inv) => {
61614
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
62491
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
61615
62492
  if (inv.dryRun) {
61616
62493
  return planOutcome(
61617
62494
  inv.deps,
61618
62495
  dryRunPlan({
61619
- service: target.service,
62496
+ service: target2.service,
61620
62497
  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" }]
62498
+ writes: [{ path: `${describeStore(inv.deps, "keychain")} (account ${target2.storeKey})`, action: "delete", description: "delete the stored session" }]
61622
62499
  })
61623
62500
  );
61624
62501
  }
61625
62502
  const tokenEnvSet = (inv.deps.env["HARNESS_TOKEN"] ?? "") !== "";
61626
- const loaded = await loadTokens(inv.deps, target.storeKey).catch(() => null);
62503
+ const loaded = await loadTokens(inv.deps, target2.storeKey).catch(() => null);
61627
62504
  let revoked = "not_needed";
61628
62505
  if (loaded !== null && loaded.tokens.accessToken.startsWith(SERVER_TOKEN_PREFIX)) {
61629
62506
  rememberSecret(loaded.tokens.accessToken);
61630
62507
  try {
61631
- const response = await inv.deps.fetch(`${target.service}/v1/auth/logout`, {
62508
+ const response = await inv.deps.fetch(`${target2.service}/v1/auth/logout`, {
61632
62509
  method: "POST",
61633
62510
  headers: { authorization: `Bearer ${loaded.tokens.accessToken}`, accept: "application/json" },
61634
62511
  signal: AbortSignal.timeout(LOGOUT_TIMEOUT_MS)
@@ -61638,11 +62515,11 @@ var authLogoutCommand = defineCommand({
61638
62515
  revoked = "unreachable";
61639
62516
  }
61640
62517
  }
61641
- await deleteTokens(inv.deps, target.storeKey);
62518
+ await deleteTokens(inv.deps, target2.storeKey);
61642
62519
  if (revoked === "unreachable") warn(inv.deps, "The service could not be reached, so the session may stay valid there until it expires.");
61643
62520
  if (tokenEnvSet) warn(inv.deps, "HARNESS_TOKEN is still set in this environment.");
61644
62521
  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" } };
62522
+ return { data: { signedOut: loaded !== null, revoked, service: target2.service, tokenEnvSet, next: "overlane auth login" } };
61646
62523
  }
61647
62524
  });
61648
62525
  var authTokenCommand = defineCommand({
@@ -61658,10 +62535,10 @@ var authTokenCommand = defineCommand({
61658
62535
  result: AuthTokenResult,
61659
62536
  text: (d) => [d.token],
61660
62537
  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);
62538
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62539
+ const found = await findToken(inv.deps, target2.storeKey);
62540
+ if (found === null) throw new AuthRequiredError(`no credentials for ${target2.service}; run \`overlane auth login\` or set HARNESS_TOKEN`, { hint: "overlane auth login" });
62541
+ const stored = found.source === "environment" ? null : await loadTokens(inv.deps, target2.storeKey).catch(() => null);
61665
62542
  const expiresAt = stored?.tokens.expiresAt;
61666
62543
  return { data: { token: found.token, how: found.source, ...expiresAt === void 0 ? {} : { expiresAt: new Date(expiresAt).toISOString() } } };
61667
62544
  }
@@ -61703,8 +62580,8 @@ var teamInviteCommand = defineCommand({
61703
62580
  const role = textFlag(inv, "role") ?? "member";
61704
62581
  const body2 = { email: email3, role };
61705
62582
  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 }] }));
62583
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62584
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/invitations`, body: body2 }] }));
61708
62585
  }
61709
62586
  const { call: call2 } = await authed2(inv);
61710
62587
  const tenantId = await tenantOf2(call2, inv);
@@ -61749,20 +62626,20 @@ var tokenCreateCommand = defineCommand({
61749
62626
  keywords: ["agent token", "api key", "create token", "mint token", "service account", "ci token", "bot credentials", "HARNESS_TOKEN"],
61750
62627
  result: TokenCreateResult,
61751
62628
  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" });
62629
+ const label2 = textFlag(inv, "label");
62630
+ if (label2 === void 0) throw new UsageError("--label is required", { hint: "overlane schema token create" });
61754
62631
  const days = Number(textFlag(inv, "ttl-days") ?? "7");
61755
62632
  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
62633
  const project = textFlag(inv, "project");
61757
62634
  const request = {
61758
- label,
62635
+ label: label2,
61759
62636
  scope: project === void 0 ? { kind: "team" } : { kind: "project", projectId: project },
61760
62637
  tools: listFlag(inv, "tool"),
61761
62638
  ttlSeconds: days * 86400
61762
62639
  };
61763
62640
  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 }] }));
62641
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62642
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "POST", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens`, body: request }] }));
61766
62643
  }
61767
62644
  const { call: call2 } = await authed2(inv);
61768
62645
  const tenantId = await tenantOf2(call2, inv);
@@ -61808,8 +62685,8 @@ var tokenRevokeCommand = defineCommand({
61808
62685
  const id2 = inv.positionals[0];
61809
62686
  if (id2 === void 0) throw new UsageError("a token ID is required", { hint: "overlane token list" });
61810
62687
  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}` }] }));
62688
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
62689
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method: "DELETE", path: `/v1/tenants/${textFlag(inv, "team") ?? ":your-team"}/tokens/${id2}` }] }));
61813
62690
  }
61814
62691
  const { call: call2 } = await authed2(inv);
61815
62692
  const tenantId = await tenantOf2(call2, inv);
@@ -62047,10 +62924,10 @@ var billingCapsSetCommand = defineCommand({
62047
62924
  result: withDryRun(Cap),
62048
62925
  text: (d) => "limitCents" in d ? [`cap ${d.scope}${d.scopeId === void 0 ? "" : ` ${d.scopeId}`}: ${usd(d.limitCents)} per period`] : [],
62049
62926
  handler: async (inv) => {
62050
- const target = capTarget(inv);
62927
+ const target2 = capTarget(inv);
62051
62928
  const usdText = textFlag(inv, "usd");
62052
62929
  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) };
62930
+ const body2 = { requestId: requestId3(), ...target2, limitCents: parseUsdToCents(usdText) };
62054
62931
  if (inv.dryRun) return planned(inv, "PUT", billingPaths.caps, body2);
62055
62932
  const { call: call2, tenantId } = await connected(inv);
62056
62933
  return { data: await call2(Cap, "PUT", billingPaths.caps(tenantId), body2) };
@@ -62096,12 +62973,12 @@ import { readFileSync as readFileSync8, statSync } from "node:fs";
62096
62973
  import { resolve as resolve8 } from "node:path";
62097
62974
  var common3 = { needsAuth: true, needsNetwork: true };
62098
62975
  var requestId4 = () => `cli-${randomUUID4()}`;
62099
- async function connected2(inv, label) {
62100
- const project = await connectedProject(inv.deps, label);
62976
+ async function connected2(inv, label2) {
62977
+ const project = await connectedProject(inv.deps, label2);
62101
62978
  return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
62102
62979
  }
62103
- async function planned2(inv, label, method, path, body2, note) {
62104
- const project = await offlineProject(inv.deps, label);
62980
+ async function planned2(inv, label2, method, path, body2, note) {
62981
+ const project = await offlineProject(inv.deps, label2);
62105
62982
  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
62983
  }
62107
62984
  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 +63899,7 @@ var deployTargetsCommand = defineCommand({
63022
63899
  ...common3,
63023
63900
  keywords: ["deploy targets", "environments", "where do we deploy", "staging production"],
63024
63901
  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}`)],
63902
+ 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
63903
  handler: async (inv) => {
63027
63904
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63028
63905
  return { data: await call2(TargetListResponse, "GET", deployPaths.targets(tenantId, projectId)) };
@@ -63074,9 +63951,9 @@ var deployEnvCreateCommand = defineCommand({
63074
63951
  text: (d) => "environment" in d ? [`Created ${d.environment.name}${d.environment.protected ? " (protected)" : ""}.`] : [],
63075
63952
  handler: async (inv) => {
63076
63953
  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 } : {} };
63954
+ const target2 = textFlag(inv, "target");
63955
+ if (target2 === void 0) throw new UsageError("--target is required", { hint: "overlane deploy targets" });
63956
+ const body2 = { name: name2, targetId: target2, ...boolFlag(inv, "protected") ? { protected: true } : {} };
63080
63957
  if (inv.dryRun) return planned2(inv, "deploy", "POST", deployPaths.environments, body2);
63081
63958
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63082
63959
  return { data: await call2(EnvironmentResponse, "POST", deployPaths.environments(tenantId, projectId), body2) };
@@ -63177,12 +64054,12 @@ var deployPreviewCommand = defineCommand({
63177
64054
  text: (d) => "release" in d && d.release !== null ? [releaseLine(d.release)] : [],
63178
64055
  handler: async (inv) => {
63179
64056
  const branch = textFlag(inv, "branch");
63180
- const target = textFlag(inv, "target");
64057
+ const target2 = textFlag(inv, "target");
63181
64058
  const commit = textFlag(inv, "commit");
63182
64059
  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>" });
64060
+ 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
64061
  const artifact = textFlag(inv, "artifact");
63185
- const body2 = { requestId: requestId4(), branch, targetId: target, commitSha: commit, runId: run3, ...artifact === void 0 ? {} : { artifact } };
64062
+ const body2 = { requestId: requestId4(), branch, targetId: target2, commitSha: commit, runId: run3, ...artifact === void 0 ? {} : { artifact } };
63186
64063
  if (inv.dryRun) return planned2(inv, "deploy", "POST", deployPaths.previews, body2);
63187
64064
  const { call: call2, tenantId, projectId } = await connected2(inv, "deploy");
63188
64065
  const result = await call2(DeployResponse, "POST", deployPaths.previews(tenantId, projectId), body2);
@@ -63500,9 +64377,9 @@ var SERVICE_FLAG3 = { name: "service", type: "string", value: "URL", description
63500
64377
  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
64378
  var common5 = { needsAuth: true, needsNetwork: true };
63502
64379
  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) };
64380
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
64381
+ const token = await requireToken(inv.deps, target2.storeKey);
64382
+ return { target: target2, call: serviceCall(inv.deps, target2.service, token.token) };
63506
64383
  }
63507
64384
  async function tenantOf3(call2, inv) {
63508
64385
  return textFlag(inv, "team") ?? (await call2(WhoAmI, "GET", apiPaths.whoami)).tenantId;
@@ -63513,9 +64390,9 @@ function idArg(inv, what) {
63513
64390
  return id2;
63514
64391
  }
63515
64392
  async function plan2(inv, method, path, body2, note) {
63516
- const target = await resolveTarget(inv.deps, textFlag(inv, "service"));
64393
+ const target2 = await resolveTarget(inv.deps, textFlag(inv, "service"));
63517
64394
  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 } }] }));
64395
+ return planOutcome(inv.deps, dryRunPlan({ service: target2.service, requests: [{ method, path: path(tenant), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
63519
64396
  }
63520
64397
  var requestId6 = () => `cli-${randomUUID6()}`;
63521
64398
  var memorySearchCommand = defineCommand({
@@ -63766,12 +64643,12 @@ var common6 = { needsAuth: true, needsNetwork: true };
63766
64643
  var requestId7 = () => `cli-${randomUUID7()}`;
63767
64644
  var PRIORITIES = ["P0", "P1", "P2", "P3"];
63768
64645
  var STATUSES = ["backlog", "todo", "in_progress", "in_review", "cancelled"];
63769
- async function send(deps, label, schema, method, path, body2, dryRun) {
64646
+ async function send(deps, label2, schema, method, path, body2, dryRun) {
63770
64647
  if (dryRun) {
63771
- const project2 = await offlineProject(deps, label);
64648
+ const project2 = await offlineProject(deps, label2);
63772
64649
  return planOutcome(deps, dryRunPlan({ service: project2.serviceUrl, requests: [{ method, path: path(project2.binding.tenantId, project2.binding.projectId), ...body2 === void 0 ? {} : { body: body2 } }] }));
63773
64650
  }
63774
- const project = await connectedProject(deps, label);
64651
+ const project = await connectedProject(deps, label2);
63775
64652
  const value = await serviceCall(deps, project.serviceUrl, project.token)(schema, method, path(project.binding.tenantId, project.binding.projectId), body2);
63776
64653
  return { data: value, value };
63777
64654
  }
@@ -64271,8 +65148,8 @@ var reviewRunCommand = defineCommand({
64271
65148
  const diff = await diffOf(inv);
64272
65149
  if (diff !== void 0) {
64273
65150
  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 } };
65151
+ const label2 = textFlag(inv, "label");
65152
+ const source = { kind: "patch", diff: diff.text, ...title === void 0 ? {} : { title }, ...label2 === void 0 ? {} : { label: label2 } };
64276
65153
  const patchBody = { requestId: requestId8(), trigger: "manual", source };
64277
65154
  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
65155
  const sent = await connected4(inv);
@@ -64701,11 +65578,11 @@ function closestCommands(specs, typed, limit = 3) {
64701
65578
  let best = Number.POSITIVE_INFINITY;
64702
65579
  for (const [index, candidate] of candidates.entries()) {
64703
65580
  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));
65581
+ const target2 = index === 0 ? path : spec.path[0] ?? path;
65582
+ if (index === 1 && candidate === target2) continue;
65583
+ const prefix = target2.startsWith(candidate) && candidate.length >= 2;
65584
+ const distance = prefix ? 0.5 : editDistance(candidate, target2);
65585
+ const allowed = Math.max(2, Math.floor(target2.length / 3));
64709
65586
  if (distance <= allowed) best = Math.min(best, distance + (index === 1 ? 0.75 : 0));
64710
65587
  }
64711
65588
  if (Number.isFinite(best)) scored.push({ spec, score: best });
@@ -64942,6 +65819,69 @@ async function disconnect(deps, options) {
64942
65819
  // ../cli/src/doctor.ts
64943
65820
  import { resolve as resolve12 } from "node:path";
64944
65821
  init_zod();
65822
+
65823
+ // ../cli/src/rules.ts
65824
+ init_src();
65825
+ import { existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "node:fs";
65826
+ import { dirname as dirname8, join as join21 } from "node:path";
65827
+ function readIfRegular(root, relative4, unsafe) {
65828
+ const full = join21(root, relative4);
65829
+ if (!existsSync5(full) && !isLink(full)) return null;
65830
+ const stat = lstatSync2(full);
65831
+ if (stat.isSymbolicLink() || !stat.isFile()) {
65832
+ unsafe.push(relative4);
65833
+ return null;
65834
+ }
65835
+ return readFileSync12(full, "utf8");
65836
+ }
65837
+ function isLink(path) {
65838
+ try {
65839
+ return lstatSync2(path).isSymbolicLink();
65840
+ } catch {
65841
+ return false;
65842
+ }
65843
+ }
65844
+ function readRules(root) {
65845
+ const unsafe = [];
65846
+ const source = readIfRegular(root, RULES_SOURCE_PATH, unsafe);
65847
+ const files = {};
65848
+ for (const target2 of RULES_TARGETS) files[target2.path] = readIfRegular(root, target2.path, unsafe);
65849
+ return { source, files, unsafe };
65850
+ }
65851
+ function rulesStatus(root) {
65852
+ const read = readRules(root);
65853
+ const report2 = checkRules(read.source, { files: read.files });
65854
+ if (read.unsafe.length > 0 && report2.configured) {
65855
+ 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);
65856
+ return { report: { ...report2, drifted: true, targets }, unsafe: read.unsafe };
65857
+ }
65858
+ return { report: report2, unsafe: read.unsafe };
65859
+ }
65860
+ function planFromDisk(root, force) {
65861
+ const read = readRules(root);
65862
+ 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` } };
65863
+ const plan3 = planRulesSync(read.source, { files: read.files }, { force });
65864
+ if (plan3.ok && read.unsafe.length > 0) {
65865
+ 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);
65866
+ return { read, plan: { ok: true, plan: { steps, blocked: steps.some((s) => s.action === "refuse") } } };
65867
+ }
65868
+ return { read, plan: plan3 };
65869
+ }
65870
+ function applyPlan2(root, plan3) {
65871
+ const written = [];
65872
+ for (const step2 of plan3.steps) {
65873
+ if (step2.next === null) continue;
65874
+ const full = join21(root, step2.path);
65875
+ mkdirSync8(dirname8(full), { recursive: true });
65876
+ const temp = `${full}.overlane-tmp`;
65877
+ writeFileSync7(temp, step2.next, { encoding: "utf8", mode: 420 });
65878
+ renameSync5(temp, full);
65879
+ written.push(step2.path);
65880
+ }
65881
+ return written;
65882
+ }
65883
+
65884
+ // ../cli/src/doctor.ts
64945
65885
  var DoctorResult = external_exports.object({
64946
65886
  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
65887
  binding: external_exports.enum(["ok", "missing", "invalid"]),
@@ -64949,6 +65889,8 @@ var DoctorResult = external_exports.object({
64949
65889
  credentials: external_exports.string().optional(),
64950
65890
  reachability: external_exports.enum(["reachable", "unreachable", "rejected", "not checked"]),
64951
65891
  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() })),
65892
+ /** Present when the project has a rules source (.overlane/rules.md): whether each agent file still matches it (MS-C1.3). */
65893
+ 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
65894
  ready: external_exports.boolean()
64953
65895
  });
64954
65896
  function commandIn(action) {
@@ -64986,9 +65928,15 @@ async function doctor(deps, options) {
64986
65928
  reports.push(await assessAgent(deps, { root, marketplaceRoot: marketplaceRoot2, info: info2, binding, hasToken, service }));
64987
65929
  }
64988
65930
  say(deps);
64989
- for (const line2 of renderReports(reports)) say(deps, line2);
65931
+ for (const line3 of renderReports(reports)) say(deps, line3);
65932
+ const rules = rulesStatus(root).report;
65933
+ if (rules.configured) {
65934
+ 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}`);
65935
+ }
64990
65936
  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");
65937
+ const agentsReady = installedStates.length > 0 && installedStates.every((state) => state === "ready");
65938
+ const rulesDrifted = rules.configured && rules.drifted;
65939
+ const ready = agentsReady && !rulesDrifted;
64992
65940
  const data = {
64993
65941
  project: projectSummary(project),
64994
65942
  binding: binding.kind,
@@ -65003,9 +65951,13 @@ async function doctor(deps, options) {
65003
65951
  action: report2.action,
65004
65952
  completionGate: completionGate(report2.agent)
65005
65953
  })),
65954
+ ...rules.configured ? { rules: { drifted: rules.drifted, revision: rules.revision, targets: rules.targets.map((t) => ({ path: t.path, state: t.state })), nextCommand: rules.nextCommand } } : {},
65006
65955
  ready
65007
65956
  };
65008
65957
  if (ready) return { data };
65958
+ if (agentsReady && rulesDrifted) {
65959
+ 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 }) };
65960
+ }
65009
65961
  const pending = reports.find((report2) => report2.action !== null);
65010
65962
  return {
65011
65963
  data,
@@ -65021,22 +65973,22 @@ async function doctor(deps, options) {
65021
65973
  // ../cli/src/kit/setup.ts
65022
65974
  init_src();
65023
65975
  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";
65976
+ import { existsSync as existsSync7, readdirSync as readdirSync2 } from "node:fs";
65977
+ import { join as join23 } from "node:path";
65026
65978
 
65027
65979
  // ../cli/src/kit/ci.ts
65028
- import { existsSync as existsSync5, readdirSync } from "node:fs";
65029
- import { join as join21 } from "node:path";
65980
+ import { existsSync as existsSync6, readdirSync } from "node:fs";
65981
+ import { join as join22 } from "node:path";
65030
65982
  var ACTION_PINS = {
65031
65983
  checkout: "actions/checkout@v7.0.1",
65032
65984
  setupNode: "actions/setup-node@v7.0.0",
65033
65985
  pnpm: "pnpm/action-setup@v6.1.0",
65034
65986
  setupPython: "actions/setup-python@v7.0.0"
65035
65987
  };
65036
- var CI_FILE = join21(".github", "workflows", "ci.yml");
65988
+ var CI_FILE = join22(".github", "workflows", "ci.yml");
65037
65989
  function hasCi(root) {
65038
65990
  try {
65039
- return readdirSync(join21(root, ".github", "workflows")).some((name2) => /\.ya?ml$/.test(name2));
65991
+ return readdirSync(join22(root, ".github", "workflows")).some((name2) => /\.ya?ml$/.test(name2));
65040
65992
  } catch {
65041
65993
  return false;
65042
65994
  }
@@ -65048,12 +66000,12 @@ function ciWorkflow(root, detected) {
65048
66000
  for (const run3 of detected.typecheck ?? []) commands.push({ name: "Typecheck", run: run3 });
65049
66001
  for (const run3 of detected.test) commands.push({ name: "Test", run: run3 });
65050
66002
  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"));
66003
+ const node2 = existsSync6(join22(root, "package.json"));
66004
+ const python = existsSync6(join22(root, "pyproject.toml"));
65053
66005
  const steps = [` - uses: ${ACTION_PINS.checkout}`];
65054
66006
  if (node2) {
65055
66007
  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";
66008
+ const version2 = existsSync6(join22(root, ".nvmrc")) ? "node-version-file: .nvmrc" : "node-version: 22";
65057
66009
  steps.push(` - uses: ${ACTION_PINS.setupNode}`, " with:", ` ${version2}`);
65058
66010
  }
65059
66011
  if (python) {
@@ -65095,7 +66047,7 @@ var INSTRUCTION_FILES = ["CLAUDE.md", "AGENTS.md"];
65095
66047
  var runPrompt = (projectId) => `Run one bounded step with the run-step skill for project ${projectId}.`;
65096
66048
  function hasSpecKit(root) {
65097
66049
  try {
65098
- return readdirSync2(join22(root, "specs"), { withFileTypes: true }).some((entry) => entry.isDirectory() && existsSync6(join22(root, "specs", entry.name, "tasks.md")));
66050
+ return readdirSync2(join23(root, "specs"), { withFileTypes: true }).some((entry) => entry.isDirectory() && existsSync7(join23(root, "specs", entry.name, "tasks.md")));
65099
66051
  } catch {
65100
66052
  return false;
65101
66053
  }
@@ -65132,7 +66084,7 @@ async function gitOk(deps, root, ...args2) {
65132
66084
  }
65133
66085
  async function dirty(deps, root, paths) {
65134
66086
  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));
66087
+ return status.stdout.split("\n").filter((line3) => line3.length > 3).map((line3) => line3.slice(3));
65136
66088
  }
65137
66089
  function shellQuote(value) {
65138
66090
  return `'${value.replaceAll("'", "'\\''")}'`;
@@ -65152,8 +66104,8 @@ async function connectKit(deps, options) {
65152
66104
  const project = await detectProject(deps, { name: options.name, provider: options.provider });
65153
66105
  const { root } = project;
65154
66106
  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"));
66107
+ const planDir = join23(root, PLAN_DIR);
66108
+ const planExists = existsSync7(join23(planDir, "project.md"));
65157
66109
  const ciExists = hasCi(root);
65158
66110
  let plan3;
65159
66111
  let planSource;
@@ -65177,7 +66129,7 @@ async function connectKit(deps, options) {
65177
66129
  }
65178
66130
  const detected = await detectCommands(root);
65179
66131
  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) }));
66132
+ const blocks = INSTRUCTION_FILES.map((file2) => ({ file: file2, before: readText(join23(root, file2)) })).map((entry) => ({ ...entry, after: withBlock2(entry.before) }));
65181
66133
  const blockChanges = blocks.filter((entry) => entry.after !== entry.before);
65182
66134
  const firstMilestone = plan3.milestones[0]?.id ?? DEFAULT_RUN_POLICY.buildAheadLimit;
65183
66135
  const branchExists = (await gitOut(deps, root, "rev-parse", "--verify", "--quiet", `refs/heads/${KIT_BRANCH}`)).code === 0;
@@ -65232,18 +66184,18 @@ async function connectKit(deps, options) {
65232
66184
  await gitOk(deps, root, "switch", "-c", KIT_BRANCH);
65233
66185
  if (!planExists) await writePlan(root, plan3);
65234
66186
  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")];
66187
+ if (workflow !== null && writeIfDifferent(join23(root, CI_FILE), workflow)) written.push(CI_FILE);
66188
+ for (const entry of blockChanges) if (writeIfDifferent(join23(root, entry.file), entry.after)) written.push(entry.file);
66189
+ const agentConfig = [join23(".claude", "settings.json"), join23(".codex", "config.toml")];
65238
66190
  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") {
66191
+ const preExisting = new Set(agentConfig.filter((file2) => existsSync7(join23(root, file2))));
66192
+ const connected9 = await connect(deps, { ...options, yes: true });
66193
+ if (connected9.error !== void 0 || readBindingFile(root).kind !== "ok") {
65242
66194
  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
66195
  }
65244
- const connectData = connected5.data !== void 0 && !("dryRun" in connected5.data) ? connected5.data : void 0;
66196
+ const connectData = connected9.data !== void 0 && !("dryRun" in connected9.data) ? connected9.data : void 0;
65245
66197
  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;
66198
+ if (!existsSync7(join23(root, file2)) || preDirty.has(file2) || preExisting.has(file2) && (await dirty(deps, root, [file2])).length === 0) continue;
65247
66199
  if ((await gitOut(deps, root, "check-ignore", "-q", "--", file2)).code === 0) continue;
65248
66200
  written.push(file2);
65249
66201
  }
@@ -65264,7 +66216,7 @@ async function connectKit(deps, options) {
65264
66216
  say(deps, `Plan synced: ${synced.created.length} created, ${synced.updated.length} updated, ${synced.unchanged.length} unchanged, ${synced.deferred.length} deferred.`);
65265
66217
  const pr = await openPullRequest(deps, root, project.name);
65266
66218
  say(deps);
65267
- for (const line2 of schedulerSetup(binding.binding.projectId)) say(deps, line2);
66219
+ for (const line3 of schedulerSetup(binding.binding.projectId)) say(deps, line3);
65268
66220
  return {
65269
66221
  data: {
65270
66222
  mode: options.mode,
@@ -65289,14 +66241,14 @@ async function openPullRequest(deps, root, _name) {
65289
66241
  const auth = version2.code === 0 ? await deps.exec("gh", ["auth", "status"], { cwd: root }) : version2;
65290
66242
  if (version2.code !== 0 || auth.code !== 0) {
65291
66243
  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}`);
66244
+ for (const line3 of manual) say(deps, ` ${line3}`);
65293
66245
  return { url: null, manual };
65294
66246
  }
65295
66247
  const push = await deps.exec("git", ["push", "-u", "origin", KIT_BRANCH], { cwd: root });
65296
66248
  const pr = push.code === 0 ? await deps.exec("gh", ["pr", "create", "--head", KIT_BRANCH, "--title", PR_TITLE, "--body", PR_BODY], { cwd: root }) : push;
65297
66249
  if (push.code !== 0 || pr.code !== 0) {
65298
66250
  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}`);
66251
+ for (const line3 of manual) say(deps, ` ${line3}`);
65300
66252
  return { url: null, manual };
65301
66253
  }
65302
66254
  say(deps, `Opened pull request: ${pr.stdout.trim()}`);
@@ -65414,7 +66366,7 @@ async function listReleases(deps) {
65414
66366
  compatSummary(record3),
65415
66367
  record3.pinnedBy.length === 0 ? "-" : record3.pinnedBy.map((id2) => id2 === ctx.binding.projectId ? `${id2} (this)` : id2).join(",")
65416
66368
  ]);
65417
- for (const line2 of table([header, ...rows])) say(deps, line2);
66369
+ for (const line3 of table([header, ...rows])) say(deps, line3);
65418
66370
  return { data };
65419
66371
  }
65420
66372
  async function releaseStatus(deps) {
@@ -65430,9 +66382,9 @@ async function releaseStatus(deps) {
65430
66382
  say(deps, `installed: ${installed === null ? "none recorded" : `${installed.version} ${installed.digest}`}`);
65431
66383
  say(deps, `binding: ${ctx.binding.releaseDigest}`);
65432
66384
  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)}).`);
66385
+ const target2 = pin ?? ctx.binding.releaseDigest;
66386
+ const inSync = installed?.digest === target2;
66387
+ say(deps, inSync ? "State: in sync." : `State: update available - run \`overlane release update\` (installed ${installed === null ? "none" : shortDigest(installed.digest)}, pinned ${shortDigest(target2)}).`);
65436
66388
  const taskRunning = readTaskState(ctx.root) !== null;
65437
66389
  if (taskRunning) say(deps, "A task is running here; an update waits until it is released, paused or submitted.");
65438
66390
  const approvals = readApprovals(deps);
@@ -65472,8 +66424,8 @@ async function updateRelease(deps, input2) {
65472
66424
  }
65473
66425
  const ctx = await context3(deps);
65474
66426
  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);
66427
+ const target2 = input2.to ?? pin;
66428
+ return installRelease(deps, ctx, target2, { to: input2.to, approve: input2.approve, dryRun: input2.dryRun }, target2 === pin);
65477
66429
  }
65478
66430
  async function rollbackRelease(deps, input2) {
65479
66431
  if (input2.dryRun) {
@@ -65766,16 +66718,16 @@ var runCommands = [runStartCommand, runNextCommand, runClaimCommand, runDecideCo
65766
66718
  init_src();
65767
66719
  init_zod();
65768
66720
  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");
66721
+ import { mkdirSync as mkdirSync9, renameSync as renameSync6, writeFileSync as writeFileSync8 } from "node:fs";
66722
+ import { dirname as dirname9, join as join24 } from "node:path";
66723
+ var TASK_POLICY_PATH = join24(".harness", "task-policy.json");
65772
66724
  function writePolicyFile(root, policy, nowMs) {
65773
- const path = join23(root, TASK_POLICY_PATH);
65774
- mkdirSync8(dirname8(path), { recursive: true });
66725
+ const path = join24(root, TASK_POLICY_PATH);
66726
+ mkdirSync9(dirname9(path), { recursive: true });
65775
66727
  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)}
66728
+ writeFileSync8(temp, `${JSON.stringify({ enforcement: policy.enforcement, ...policy.reason === void 0 ? {} : { reason: policy.reason }, checkedAt: new Date(nowMs).toISOString() }, null, 2)}
65777
66729
  `);
65778
- renameSync5(temp, path);
66730
+ renameSync6(temp, path);
65779
66731
  }
65780
66732
  var TaskNewResult = withDryRun(
65781
66733
  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 +67459,875 @@ var taskHandoffCommand = defineCommand({
66507
67459
  });
66508
67460
  var taskCommands = [taskListCommand, taskStartCommand, taskStatusCommand, taskCheckpointCommand, taskResumeCommand, taskSubmitCommand, taskEvidenceCommand, taskHandoffCommand];
66509
67461
 
67462
+ // ../cli/src/commands/claims.ts
67463
+ init_src();
67464
+ init_zod();
67465
+ import { randomUUID as randomUUID11 } from "node:crypto";
67466
+ var common15 = { needsAuth: true, needsNetwork: true };
67467
+ var requestId10 = () => `cli-${randomUUID11()}`;
67468
+ async function connected5(inv) {
67469
+ const project = await connectedProject(inv.deps, "claim");
67470
+ return { project, call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67471
+ }
67472
+ async function planned5(inv, method, path, body2, note) {
67473
+ const project = await offlineProject(inv.deps, "claim");
67474
+ 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 } }] }));
67475
+ }
67476
+ async function git3(deps, args2) {
67477
+ return deps.exec("git", args2, { cwd: deps.paths.cwd });
67478
+ }
67479
+ async function whereAmI(deps) {
67480
+ const head = await git3(deps, ["rev-parse", "HEAD"]);
67481
+ const branch = await git3(deps, ["symbolic-ref", "--short", "-q", "HEAD"]);
67482
+ const sha = head.stdout.trim();
67483
+ return { ...branch.code === 0 && branch.stdout.trim() !== "" ? { branch: branch.stdout.trim() } : {}, ...head.code === 0 && /^[0-9a-f]{40,64}$/.test(sha) ? { headSha: sha } : {} };
67484
+ }
67485
+ var SCOPE_FLAGS = [
67486
+ { name: "path", type: "string", multiple: true, value: "PATH", description: "A file or directory to claim (a directory claims everything under it); repeat for more" },
67487
+ { name: "glob", type: "string", multiple: true, value: "GLOB", description: "A glob such as 'src/billing/**'; repeat for more" },
67488
+ { name: "module", type: "string", multiple: true, value: "NAME", description: "A module, resolved through the code map where indexed; repeat for more" },
67489
+ { name: "paths", type: "string", value: "A,B", description: "For a module the code map does not know: the paths it covers (comma separated)" }
67490
+ ];
67491
+ function scopesOf(inv) {
67492
+ const paths = (textFlag(inv, "paths") ?? "").split(",").map((p) => p.trim()).filter((p) => p !== "");
67493
+ const scopes = [
67494
+ ...listFlag(inv, "path").map((value) => ({ kind: "path", value })),
67495
+ ...listFlag(inv, "glob").map((value) => ({ kind: "glob", value })),
67496
+ ...listFlag(inv, "module").map((value) => ({ kind: "module", value, ...paths.length === 0 ? {} : { paths } }))
67497
+ ];
67498
+ if (scopes.length === 0) throw new UsageError("a claim names its scope", { hint: "overlane claim acquire --task TASK --path src/billing/" });
67499
+ return scopes;
67500
+ }
67501
+ function leaseFlag(inv) {
67502
+ const text = textFlag(inv, "lease-seconds");
67503
+ if (text === void 0) return void 0;
67504
+ if (!/^\d+$/.test(text) || Number(text) < 60) throw new UsageError("--lease-seconds is a whole number of at least 60");
67505
+ return Number(text);
67506
+ }
67507
+ var claimAcquireCommand = defineCommand({
67508
+ path: ["claim", "acquire"],
67509
+ summary: "Claim the files or modules a task will change; an overlapping claim by another agent is queued or refused",
67510
+ 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.",
67511
+ flags: [
67512
+ { name: "task", type: "string", required: true, value: "TASK", description: "The task this work belongs to" },
67513
+ ...SCOPE_FLAGS,
67514
+ { name: "mode", type: "string", enum: ["exclusive", "shared"], value: "MODE", description: "exclusive (default) or shared read access" },
67515
+ { name: "agent", type: "string", value: "NAME", description: "A label such as codex or claude" },
67516
+ { name: "lease-seconds", type: "string", value: "N", description: "Lease length, 60 to 14400; the project default otherwise" }
67517
+ ],
67518
+ examples: ["overlane claim acquire --task task-12 --path apps/api-worker/src/billing/", "overlane claim acquire --task task-12 --module billing --glob 'docs/billing/**'"],
67519
+ effect: "write",
67520
+ ...common15,
67521
+ keywords: ["claim", "lease", "lock files", "avoid merge conflict", "two agents same file", "reserve scope", "serialise agents"],
67522
+ result: withDryRun(ClaimOutcome),
67523
+ 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}`]] : [],
67524
+ handler: async (inv) => {
67525
+ const task = textFlag(inv, "task");
67526
+ if (task === void 0) throw new UsageError("claim acquire needs --task", { hint: "overlane claim acquire --task TASK --path src/" });
67527
+ const here = await whereAmI(inv.deps);
67528
+ const lease = leaseFlag(inv);
67529
+ const body2 = {
67530
+ requestId: requestId10(),
67531
+ taskId: task,
67532
+ scopes: scopesOf(inv),
67533
+ mode: textFlag(inv, "mode") ?? "exclusive",
67534
+ ...textFlag(inv, "agent") === void 0 ? {} : { agent: textFlag(inv, "agent") },
67535
+ ...lease === void 0 ? {} : { leaseSeconds: lease },
67536
+ ...here
67537
+ };
67538
+ if (inv.dryRun) return planned5(inv, "POST", claimPaths.claims, body2, "claims the scope; nothing changes in your files");
67539
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67540
+ const outcome = await call2(ClaimOutcome, "POST", claimPaths.claims(tenantId, projectId), body2);
67541
+ 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 }) };
67542
+ return { data: outcome };
67543
+ }
67544
+ });
67545
+ var claimListCommand = defineCommand({
67546
+ path: ["claim", "list"],
67547
+ summary: "List the project's claims: who holds which scope, who waits, and when each lease ends",
67548
+ description: "Lists claims newest first with their status (active, queued, released, expired), scope patterns, lease end and the project's overlap policy.",
67549
+ flags: [
67550
+ { name: "task", type: "string", value: "TASK", description: "Only this task's claims" },
67551
+ { name: "status", type: "string", enum: ["active", "queued", "released", "expired"], value: "STATUS", description: "Only claims in this state" }
67552
+ ],
67553
+ examples: ["overlane claim list", "overlane claim list --status active --json"],
67554
+ effect: "read",
67555
+ ...common15,
67556
+ keywords: ["who is working on", "claims", "queued", "lease", "scope held"],
67557
+ result: ClaimListResponse,
67558
+ 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}` : ""}`)],
67559
+ handler: async (inv) => {
67560
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67561
+ const params = new URLSearchParams();
67562
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
67563
+ if (textFlag(inv, "status") !== void 0) params.set("status", textFlag(inv, "status"));
67564
+ const query = params.toString();
67565
+ return { data: await call2(ClaimListResponse, "GET", `${claimPaths.claims(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
67566
+ }
67567
+ });
67568
+ var claimRenewCommand = defineCommand({
67569
+ path: ["claim", "renew"],
67570
+ summary: "Extend a claim's lease and report the branch head you are on",
67571
+ 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.",
67572
+ flags: [
67573
+ { name: "claim", type: "string", required: true, value: "ID", description: "The claim to renew" },
67574
+ { name: "lease-seconds", type: "string", value: "N", description: "New lease length from now" }
67575
+ ],
67576
+ examples: ["overlane claim renew --claim clm_abc123"],
67577
+ effect: "write",
67578
+ ...common15,
67579
+ keywords: ["heartbeat", "keep lease", "extend claim"],
67580
+ result: withDryRun(Claim),
67581
+ text: (d) => "id" in d ? [`claim ${d.id} ${d.status}, lease until ${d.leaseExpiresAt}`] : [],
67582
+ handler: async (inv) => {
67583
+ const id2 = textFlag(inv, "claim");
67584
+ if (id2 === void 0) throw new UsageError("claim renew needs --claim", { hint: "overlane claim list" });
67585
+ const lease = leaseFlag(inv);
67586
+ const here = await whereAmI(inv.deps);
67587
+ const body2 = { ...lease === void 0 ? {} : { leaseSeconds: lease }, ...here };
67588
+ if (inv.dryRun) return planned5(inv, "POST", (t, p) => claimPaths.renew(t, p, id2), body2);
67589
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67590
+ return { data: await call2(Claim, "POST", claimPaths.renew(tenantId, projectId, id2), body2) };
67591
+ }
67592
+ });
67593
+ var claimReleaseCommand = defineCommand({
67594
+ path: ["claim", "release"],
67595
+ summary: "Release a claim so a queued agent can take the scope",
67596
+ 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.",
67597
+ flags: [
67598
+ { name: "claim", type: "string", required: true, value: "ID", description: "The claim to release" },
67599
+ { name: "done", type: "boolean", description: "Record the release as the task being done" }
67600
+ ],
67601
+ examples: ["overlane claim release --claim clm_abc123 --done"],
67602
+ effect: "write",
67603
+ ...common15,
67604
+ keywords: ["release claim", "done with files", "free scope"],
67605
+ result: withDryRun(Claim),
67606
+ text: (d) => "id" in d ? [`claim ${d.id} ${d.status}${d.releaseReason === void 0 ? "" : ` (${d.releaseReason})`}`] : [],
67607
+ handler: async (inv) => {
67608
+ const id2 = textFlag(inv, "claim");
67609
+ if (id2 === void 0) throw new UsageError("claim release needs --claim", { hint: "overlane claim list" });
67610
+ const body2 = { reason: inv.values["done"] === true ? "done" : "released" };
67611
+ if (inv.dryRun) return planned5(inv, "POST", (t, p) => claimPaths.release(t, p, id2), body2);
67612
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67613
+ return { data: await call2(Claim, "POST", claimPaths.release(tenantId, projectId, id2), body2) };
67614
+ }
67615
+ });
67616
+ var claimReadinessCommand = defineCommand({
67617
+ path: ["claim", "readiness"],
67618
+ summary: "Say whether this claim's branch is clear to merge as far as claims and the conflict check can tell",
67619
+ 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.",
67620
+ flags: [{ name: "claim", type: "string", required: true, value: "ID", description: "The claim to evaluate" }],
67621
+ examples: ["overlane claim readiness --claim clm_abc123"],
67622
+ effect: "read",
67623
+ ...common15,
67624
+ keywords: ["ready to merge", "merge conflict check", "pre-merge", "can i merge"],
67625
+ result: MergeReadiness,
67626
+ 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}`}`)],
67627
+ handler: async (inv) => {
67628
+ const id2 = textFlag(inv, "claim");
67629
+ if (id2 === void 0) throw new UsageError("claim readiness needs --claim", { hint: "overlane claim list" });
67630
+ const { call: call2, tenantId, projectId } = await connected5(inv);
67631
+ const data = await call2(MergeReadiness, "GET", claimPaths.readiness(tenantId, projectId, id2));
67632
+ 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 }) };
67633
+ }
67634
+ });
67635
+ 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();
67636
+ var ConflictsResult = external_exports.object({ checks: external_exports.array(CheckedPair), conflicts: external_exports.number().int().nonnegative(), errors: external_exports.number().int().nonnegative() }).strict();
67637
+ async function resolveRef2(deps, ref) {
67638
+ const parsed = await git3(deps, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
67639
+ const sha = parsed.stdout.trim();
67640
+ return parsed.code === 0 && /^[0-9a-f]{40,64}$/.test(sha) ? { ref, sha } : { ref, error: `${ref} is not a commit in this checkout` };
67641
+ }
67642
+ async function runMergeTree(deps, a, b) {
67643
+ const out2 = await git3(deps, mergeTreeArgs(a, b));
67644
+ const result = parseMergeTree(out2.stdout, out2.code, out2.stderr);
67645
+ 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)" };
67646
+ return result;
67647
+ }
67648
+ var claimConflictsCommand = defineCommand({
67649
+ path: ["claim", "conflicts"],
67650
+ summary: "Check two branch heads for a merge conflict with git merge-tree, before either pull request merges",
67651
+ 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).",
67652
+ flags: [
67653
+ { name: "a", type: "string", value: "REF", description: "First branch, tag or commit" },
67654
+ { name: "b", type: "string", value: "REF", description: "Second branch, tag or commit" },
67655
+ { name: "all", type: "boolean", description: "Check every open pair that has no check for its current heads" },
67656
+ { name: "no-record", type: "boolean", description: "Print the result without recording it (works offline)" }
67657
+ ],
67658
+ examples: ["overlane claim conflicts --a feat/billing --b feat/review", "overlane claim conflicts --all"],
67659
+ effect: "write",
67660
+ needsAuth: false,
67661
+ needsNetwork: false,
67662
+ keywords: ["merge conflict", "merge-tree", "will these branches conflict", "pre-merge check", "conflict between branches"],
67663
+ result: withDryRun(ConflictsResult),
67664
+ 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"}`] : [],
67665
+ handler: async (inv) => {
67666
+ const a = textFlag(inv, "a");
67667
+ const b = textFlag(inv, "b");
67668
+ const all = inv.values["all"] === true;
67669
+ 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" });
67670
+ if (inv.dryRun) {
67671
+ 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."] }));
67672
+ }
67673
+ const record3 = inv.values["no-record"] !== true;
67674
+ const session = record3 || all ? await connected5(inv) : null;
67675
+ const pairs = [];
67676
+ const errors = [];
67677
+ const unresolved = (ref, message) => ({ a: { ref, sha: "0".repeat(40) }, b: { ref, sha: "0".repeat(40) }, result: { status: "error", files: [], message }, recorded: false });
67678
+ if (all) {
67679
+ 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();
67680
+ const pending = await session.call(PendingList, "GET", claimPaths.pending(session.tenantId, session.projectId));
67681
+ 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 } });
67682
+ } else {
67683
+ const left = await resolveRef2(inv.deps, a);
67684
+ const right = await resolveRef2(inv.deps, b);
67685
+ if ("error" in left) errors.push(unresolved(left.ref, left.error));
67686
+ else if ("error" in right) errors.push(unresolved(right.ref, right.error));
67687
+ else pairs.push({ a: left, b: right });
67688
+ }
67689
+ const checks = [...errors];
67690
+ for (const pair of pairs) {
67691
+ const result = await runMergeTree(inv.deps, pair.a.sha, pair.b.sha);
67692
+ let recorded = false;
67693
+ if (record3 && session !== null) {
67694
+ await session.call(MergeCheckRecord, "POST", claimPaths.checks(session.tenantId, session.projectId), { requestId: requestId10(), a: pair.a, b: pair.b, result });
67695
+ recorded = true;
67696
+ }
67697
+ checks.push({ a: { ref: pair.a.ref, sha: pair.a.sha }, b: { ref: pair.b.ref, sha: pair.b.sha }, result, recorded });
67698
+ }
67699
+ const conflicts = checks.filter((c) => c.result.status === "conflict").length;
67700
+ const failures = checks.filter((c) => c.result.status === "error").length;
67701
+ const data = { checks, conflicts, errors: failures };
67702
+ 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 }) };
67703
+ 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 }) };
67704
+ return { data };
67705
+ }
67706
+ });
67707
+ var claimCommands = [claimAcquireCommand, claimListCommand, claimRenewCommand, claimReleaseCommand, claimReadinessCommand, claimConflictsCommand];
67708
+
67709
+ // ../cli/src/commands/budget.ts
67710
+ init_src();
67711
+ init_zod();
67712
+ import { randomUUID as randomUUID12 } from "node:crypto";
67713
+ var common16 = { needsAuth: true, needsNetwork: true };
67714
+ var requestId11 = () => `cli-${randomUUID12()}`;
67715
+ async function connected6(inv) {
67716
+ const project = await connectedProject(inv.deps, "budget");
67717
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67718
+ }
67719
+ async function planned6(inv, method, path, body2, note) {
67720
+ const project = await offlineProject(inv.deps, "budget");
67721
+ return planOutcome(inv.deps, dryRunPlan({ service: project.serviceUrl, requests: [{ method, path: path(project.binding.tenantId), ...body2 === void 0 ? {} : { body: body2 }, ...note === void 0 ? {} : { note } }] }));
67722
+ }
67723
+ var usd2 = (cents) => `$${Math.trunc(cents / 100)}.${String(cents % 100).padStart(2, "0")}`;
67724
+ function target(inv) {
67725
+ const scope = textFlag(inv, "scope");
67726
+ const id2 = textFlag(inv, "id");
67727
+ if (scope === void 0) throw new UsageError("this command needs --scope", { hint: "overlane budget set --scope task --id TASK --usd 5" });
67728
+ if (scope === "team" && id2 !== void 0) throw new UsageError("a team budget has no --id");
67729
+ 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` });
67730
+ return { scope, ...id2 === void 0 ? {} : { scopeId: id2 } };
67731
+ }
67732
+ var label = (scope, scopeId) => scope === "team" ? "team" : `${scope} ${scopeId}`;
67733
+ var Show = external_exports.object({ tenantId: external_exports.string(), limits: external_exports.array(BudgetLimit), summary: BudgetSummary }).strict();
67734
+ 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();
67735
+ var budgetShowCommand = defineCommand({
67736
+ path: ["budget", "show"],
67737
+ summary: "Show budgets per task, agent, project and team, with what is spent and what is left",
67738
+ 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.",
67739
+ examples: ["overlane budget show", "overlane budget show --json"],
67740
+ effect: "read",
67741
+ ...common16,
67742
+ keywords: ["budget", "how much left", "spend per task", "cap", "limit"],
67743
+ result: Show,
67744
+ 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" : ""}`),
67745
+ handler: async (inv) => {
67746
+ const { call: call2, tenantId } = await connected6(inv);
67747
+ return { data: await call2(Show, "GET", budgetPaths.root(tenantId)) };
67748
+ }
67749
+ });
67750
+ var budgetSetCommand = defineCommand({
67751
+ path: ["budget", "set"],
67752
+ summary: "Set a budget for a task, an agent, a project or the team",
67753
+ 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.",
67754
+ flags: [
67755
+ { name: "scope", type: "string", required: true, enum: BUDGET_SCOPES, value: "SCOPE", description: "task, agent, project or team" },
67756
+ { name: "id", type: "string", value: "ID", description: "The task, agent token or project (not for team)" },
67757
+ { name: "usd", type: "string", required: true, value: "AMOUNT", description: "Dollars, for example 5 or 12.50" }
67758
+ ],
67759
+ examples: ["overlane budget set --scope task --id task-12 --usd 5", "overlane budget set --scope team --usd 200 --dry-run"],
67760
+ effect: "write",
67761
+ ...common16,
67762
+ keywords: ["set budget", "limit spend", "cap a run", "stop at"],
67763
+ result: withDryRun(SetResult2),
67764
+ 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)}`})`] : [],
67765
+ handler: async (inv) => {
67766
+ const usdText = textFlag(inv, "usd");
67767
+ if (usdText === void 0) throw new UsageError("set needs --usd", { hint: "overlane budget set --scope team --usd 50" });
67768
+ const limitCents = parseUsdToCents(usdText);
67769
+ if (limitCents < 1) throw new UsageError("--usd must be more than zero");
67770
+ const body2 = { requestId: requestId11(), ...target(inv), limitCents };
67771
+ 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");
67772
+ const { call: call2, tenantId } = await connected6(inv);
67773
+ return { data: await call2(SetResult2, "PUT", budgetPaths.root(tenantId), body2) };
67774
+ }
67775
+ });
67776
+ var budgetCheckCommand = defineCommand({
67777
+ path: ["budget", "check"],
67778
+ summary: "Ask before spending: exits 6 and records a stop when the call would reach a budget",
67779
+ 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.",
67780
+ flags: [
67781
+ { name: "usd", type: "string", required: true, value: "AMOUNT", description: "The call's estimated cost in dollars, for example 0.40" },
67782
+ { name: "task", type: "string", value: "TASK", description: "The task this call belongs to" },
67783
+ { name: "agent", type: "string", value: "ID", description: "The agent token, for a person checking for an agent" },
67784
+ { name: "kind", type: "string", value: "KIND", description: "What the call is, for the record (inference, ci, review)" },
67785
+ { name: "meter", type: "string", enum: ["reviews", "ciMinutes", "docsCalls", "hostedInference"], value: "METER", description: "Also check the plan allowance for this meter" },
67786
+ { name: "record", type: "boolean", description: "Count the estimate as spent when allowed" }
67787
+ ],
67788
+ examples: ["overlane budget check --usd 0.40 --task task-12", "overlane budget check --usd 0.05 --task task-12 --record --kind inference"],
67789
+ effect: "write",
67790
+ ...common16,
67791
+ keywords: ["can i spend", "budget gate", "stop at cap", "exit 6", "run cost"],
67792
+ result: withDryRun(BudgetDecision),
67793
+ text: (d) => "allowed" in d ? [`${d.allowed ? "ALLOWED" : "STOPPED"} ${d.message}`, ...d.nextCommand === null ? [] : [`next: ${d.nextCommand}`], ...d.stopId === null ? [] : [`record: ${d.stopId}`]] : [],
67794
+ handler: async (inv) => {
67795
+ const usdText = textFlag(inv, "usd");
67796
+ if (usdText === void 0) throw new UsageError("check needs --usd", { hint: "overlane budget check --usd 0.40 --task task-12" });
67797
+ const body2 = {
67798
+ requestId: requestId11(),
67799
+ estimateCents: parseUsdToCents(usdText),
67800
+ ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") },
67801
+ ...textFlag(inv, "agent") === void 0 ? {} : { agentId: textFlag(inv, "agent") },
67802
+ ...textFlag(inv, "kind") === void 0 ? {} : { kind: textFlag(inv, "kind") },
67803
+ ...textFlag(inv, "meter") === void 0 ? {} : { meter: textFlag(inv, "meter") },
67804
+ commit: boolFlag(inv, "record")
67805
+ };
67806
+ if (inv.dryRun) return planned6(inv, "POST", budgetPaths.check, body2, "asks the service; a refusal stores a stop record");
67807
+ const { call: call2, tenantId, projectId } = await connected6(inv);
67808
+ const decision = await call2(BudgetDecision, "POST", budgetPaths.check(tenantId), { ...body2, projectId });
67809
+ 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 } }) };
67810
+ }
67811
+ });
67812
+ var budgetStopsCommand = defineCommand({
67813
+ path: ["budget", "stops"],
67814
+ summary: "List the runs a budget stopped, newest first",
67815
+ 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.",
67816
+ flags: [{ name: "limit", type: "string", value: "N", description: "How many to list, default 50" }],
67817
+ examples: ["overlane budget stops", "overlane budget stops --limit 10 --json"],
67818
+ effect: "read",
67819
+ ...common16,
67820
+ keywords: ["stopped runs", "budget stops", "what hit the cap", "refusals"],
67821
+ result: BudgetStopList,
67822
+ 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}`),
67823
+ handler: async (inv) => {
67824
+ const { call: call2, tenantId } = await connected6(inv);
67825
+ const limit = textFlag(inv, "limit");
67826
+ if (limit !== void 0 && !/^\d+$/.test(limit)) throw new UsageError("--limit is a whole number");
67827
+ return { data: await call2(BudgetStopList, "GET", `${budgetPaths.stops(tenantId)}${limit === void 0 ? "" : `?limit=${limit}`}`) };
67828
+ }
67829
+ });
67830
+ var budgetCommands = [budgetShowCommand, budgetSetCommand, budgetCheckCommand, budgetStopsCommand];
67831
+
67832
+ // ../cli/src/commands/rules.ts
67833
+ init_src();
67834
+ init_zod();
67835
+ var common17 = { needsAuth: false, needsNetwork: false };
67836
+ var Action2 = external_exports.enum(["create", "append", "replace", "none", "refuse", "force_replace"]);
67837
+ var SyncResult2 = external_exports.object({
67838
+ revision: external_exports.string().nullable(),
67839
+ 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()),
67840
+ written: external_exports.array(external_exports.string()),
67841
+ blocked: external_exports.boolean()
67842
+ }).strict();
67843
+ var rulesSyncCommand = defineCommand({
67844
+ path: ["rules", "sync"],
67845
+ summary: "Write the project's rules source to AGENTS.md, CLAUDE.md, Cursor, Gemini and the Gemini settings",
67846
+ 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.`,
67847
+ flags: [{ name: "overwrite-edits", type: "boolean", description: "Replace a region that was edited by hand (prints what it replaces first)" }],
67848
+ examples: ["overlane rules sync", "overlane rules sync --dry-run", "overlane rules sync --overwrite-edits"],
67849
+ effect: "write",
67850
+ ...common17,
67851
+ keywords: ["rules", "AGENTS.md", "CLAUDE.md", "cursor rules", "gemini", "one source of rules", "sync instructions", "agent instructions"],
67852
+ result: withDryRun(SyncResult2),
67853
+ text: (d) => "steps" in d ? d.steps.map((s) => `${s.action.toUpperCase().padEnd(13)} ${s.path} ${s.reason}`) : [],
67854
+ handler: async (inv) => {
67855
+ const project = await detectProject(inv.deps);
67856
+ const root = project.root;
67857
+ const force = boolFlag(inv, "overwrite-edits");
67858
+ const { read, plan: plan3 } = planFromDisk(root, force);
67859
+ 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}`] }));
67860
+ 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` });
67861
+ if (inv.dryRun) {
67862
+ return planOutcome(
67863
+ inv.deps,
67864
+ dryRunPlan({
67865
+ writes: plan3.plan.steps.filter((s) => s.next !== null).map((s) => ({ path: s.path, action: s.action === "append" ? "append" : "write", description: s.reason })),
67866
+ notes: plan3.plan.steps.filter((s) => s.action === "refuse").map((s) => `Would refuse: ${s.reason}`)
67867
+ })
67868
+ );
67869
+ }
67870
+ const revision = read.source === null ? null : rulesRevision(read.source);
67871
+ for (const step2 of plan3.plan.steps) {
67872
+ if (step2.action === "force_replace" && step2.replaces !== void 0) {
67873
+ warn(inv.deps, `Replacing ${step2.replaces.lines} lines of ${step2.path}; the lines that differ from the source:`);
67874
+ for (const line3 of step2.replaces.firstLines) warn(inv.deps, ` - ${line3}`);
67875
+ }
67876
+ }
67877
+ const written = plan3.plan.blocked ? [] : applyPlan2(root, plan3.plan);
67878
+ 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 };
67879
+ if (plan3.plan.blocked) {
67880
+ const refused = plan3.plan.steps.find((s) => s.action === "refuse");
67881
+ 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 }) };
67882
+ }
67883
+ return { data };
67884
+ }
67885
+ });
67886
+ var rulesCheckCommand = defineCommand({
67887
+ path: ["rules", "check"],
67888
+ summary: "Report drift between the rules source and each agent's file; exits 6 on drift, for CI",
67889
+ 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.`,
67890
+ examples: ["overlane rules check", "overlane rules check --json"],
67891
+ effect: "read",
67892
+ ...common17,
67893
+ keywords: ["rules drift", "instructions out of date", "hand edit AGENTS.md", "ci check rules", "doctor rules"],
67894
+ result: RulesCheckReport,
67895
+ 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}`]],
67896
+ handler: async (inv) => {
67897
+ const project = await detectProject(inv.deps);
67898
+ const { report: report2 } = rulesStatus(project.root);
67899
+ if (!report2.drifted) return { data: report2 };
67900
+ const first = report2.targets.find((t) => t.state !== "in_sync");
67901
+ 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 }) };
67902
+ }
67903
+ });
67904
+ var rulesCommands = [rulesSyncCommand, rulesCheckCommand];
67905
+
67906
+ // ../cli/src/commands/pr.ts
67907
+ init_src();
67908
+ init_zod();
67909
+ import { randomUUID as randomUUID13 } from "node:crypto";
67910
+ var common18 = { needsAuth: true, needsNetwork: true };
67911
+ var requestId12 = () => `cli-${randomUUID13()}`;
67912
+ async function connected7(inv) {
67913
+ const project = await connectedProject(inv.deps, "pr");
67914
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
67915
+ }
67916
+ async function planned7(inv, method, path, body2, note) {
67917
+ const project = await offlineProject(inv.deps, "pr");
67918
+ 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 } }] }));
67919
+ }
67920
+ var git4 = (deps, args2) => deps.exec("git", args2, { cwd: deps.paths.cwd });
67921
+ var MAX_PATCH_FILES = 40;
67922
+ var MAX_PATCH_CHARS = 8e3;
67923
+ async function snapshotOf(inv, head, base4) {
67924
+ const sha = await git4(inv.deps, ["rev-parse", "--verify", "--quiet", `${head}^{commit}`]);
67925
+ if (sha.code !== 0) throw new UsageError(`${head} is not a commit in this checkout`, { hint: "git fetch, then retry" });
67926
+ const stat = await git4(inv.deps, ["diff", "--numstat", "-z", "--no-renames", `${base4}...${head}`]);
67927
+ 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" });
67928
+ const files = [];
67929
+ for (const entry of stat.stdout.split("\0")) {
67930
+ const match = /^(\d+|-)\t(\d+|-)\t(.+)$/s.exec(entry);
67931
+ if (match === null) continue;
67932
+ files.push({ path: match[3], status: "modified", additions: match[1] === "-" ? 0 : Number(match[1]), deletions: match[2] === "-" ? 0 : Number(match[2]) });
67933
+ }
67934
+ for (const file2 of files.slice(0, MAX_PATCH_FILES)) {
67935
+ const patch = await git4(inv.deps, ["diff", "--unified=2", `${base4}...${head}`, "--", file2.path]);
67936
+ if (patch.code === 0 && patch.stdout !== "") file2.patch = patch.stdout.slice(0, MAX_PATCH_CHARS);
67937
+ }
67938
+ const project = await detectProject(inv.deps);
67939
+ 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}`);
67940
+ return { provider: project.provider === "github" ? "github" : "overlane", repository, headSha: sha.stdout.trim(), baseRef: base4, files };
67941
+ }
67942
+ function numberOf(inv) {
67943
+ const text = textFlag(inv, "number");
67944
+ 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" });
67945
+ return Number(text);
67946
+ }
67947
+ function checkFlag(inv, name2) {
67948
+ const value = textFlag(inv, name2);
67949
+ return value ?? "missing";
67950
+ }
67951
+ var CHECK_FLAGS = [
67952
+ { name: "ci", type: "string", enum: CHECK_STATES, value: "STATE", description: "CI result: passed, failed, pending or missing (default missing, which blocks)" },
67953
+ { name: "secret-scan", type: "string", enum: CHECK_STATES, value: "STATE", description: "Secret scan result (default missing, which blocks)" },
67954
+ { name: "guardrails", type: "string", enum: CHECK_STATES, value: "STATE", description: "Guardrails result (default missing, which blocks)" },
67955
+ { name: "conflict", type: "string", enum: CHECK_STATES, value: "STATE", description: "Merge conflict check result, passed meaning no conflict (default missing, which blocks)" },
67956
+ { name: "critical", type: "string", value: "N", description: "Unresolved critical review findings (default 0)" },
67957
+ { name: "review-done", type: "boolean", description: "The review run finished for this head" }
67958
+ ];
67959
+ var LANE_TEXT = {
67960
+ blocked: "BLOCKED",
67961
+ fast: "FAST PATH",
67962
+ standard: "STANDARD",
67963
+ approver: "APPROVER"
67964
+ };
67965
+ var line2 = (s) => `${LANE_TEXT[s.route.lane] ?? s.route.lane} ${s.repository}#${s.number} risk ${s.route.risk}${s.approved ? " APPROVED" : ""}`;
67966
+ var prRouteCommand = defineCommand({
67967
+ path: ["pr", "route"],
67968
+ summary: "Route a pull request by risk: blocked, fast path, standard or approver",
67969
+ 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.",
67970
+ flags: [
67971
+ { name: "number", type: "string", required: true, value: "N", description: "The pull request number" },
67972
+ { name: "head", type: "string", value: "REF", description: "The head commit or branch (default HEAD)" },
67973
+ { name: "base", type: "string", value: "REF", description: "The base to compare with (default origin/main, else main)" },
67974
+ { name: "repo", type: "string", value: "OWNER/NAME", description: "The repository name the pull request belongs to (default: the origin remote)" },
67975
+ { name: "title", type: "string", value: "TEXT", description: "The pull request title" },
67976
+ { name: "author", type: "string", value: "NAME", description: "The pull request author's name" },
67977
+ { name: "author-token", type: "string", value: "TOKEN_ID", description: "The agent token that opened it, so its lineage is known to the independence check" },
67978
+ { name: "task", type: "string", value: "TASK", description: "The task this pull request belongs to, for batching" },
67979
+ { name: "scope", type: "string", multiple: true, value: "GLOB", description: "A claimed scope pattern, for batching; repeat for more" },
67980
+ { name: "fork", type: "boolean", description: "The head comes from a fork" },
67981
+ ...CHECK_FLAGS
67982
+ ],
67983
+ 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"],
67984
+ effect: "write",
67985
+ ...common18,
67986
+ keywords: ["route pull request", "fast path", "risk lane", "who approves this pr", "triage pull requests", "pr risk"],
67987
+ result: withDryRun(StoredRoute),
67988
+ 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}`]] : [],
67989
+ handler: async (inv) => {
67990
+ const number4 = numberOf(inv);
67991
+ const head = textFlag(inv, "head") ?? "HEAD";
67992
+ let base4 = textFlag(inv, "base");
67993
+ if (base4 === void 0) base4 = (await git4(inv.deps, ["rev-parse", "--verify", "--quiet", "origin/main"])).code === 0 ? "origin/main" : "main";
67994
+ const critical = textFlag(inv, "critical");
67995
+ if (critical !== void 0 && !/^\d+$/.test(critical)) throw new UsageError("--critical is a whole number");
67996
+ const snapshot = await snapshotOf(inv, head, base4);
67997
+ const body2 = {
67998
+ requestId: requestId12(),
67999
+ 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 },
68000
+ 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") },
68001
+ baseRef: snapshot.baseRef,
68002
+ ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") },
68003
+ ...textFlag(inv, "author-token") === void 0 ? {} : { authorAgentTokenId: textFlag(inv, "author-token") },
68004
+ ...listFlag(inv, "scope").length === 0 ? {} : { scope: listFlag(inv, "scope") }
68005
+ };
68006
+ if (inv.dryRun) {
68007
+ const preview = { ...body2, pull: { ...body2.pull, files: body2.pull.files.map((f) => ({ path: f.path, additions: f.additions, deletions: f.deletions })) } };
68008
+ 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");
68009
+ }
68010
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68011
+ const stored = await call2(StoredRoute, "POST", prRoutingPaths.route(tenantId, projectId), body2);
68012
+ if (stored.route.lane === "blocked") {
68013
+ const first = stored.route.blockers[0];
68014
+ 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 }) };
68015
+ }
68016
+ return { data: stored };
68017
+ }
68018
+ });
68019
+ 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();
68020
+ var prApproveCommand = defineCommand({
68021
+ path: ["pr", "approve"],
68022
+ summary: "Approve a routed pull request as an independent agent with evidence, or as a person",
68023
+ 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.",
68024
+ flags: [
68025
+ { name: "number", type: "string", required: true, value: "N", description: "The pull request number" },
68026
+ { name: "head", type: "string", value: "REF", description: "The head commit being approved (default HEAD)" },
68027
+ { name: "repo", type: "string", value: "OWNER/NAME", description: "The repository name (default: the origin remote)" },
68028
+ { name: "evidence", type: "string", multiple: true, value: "TEXT", description: "What you checked; repeat for more (required for an agent)" },
68029
+ { name: "reason", type: "string", value: "TEXT", description: "Why you approve (required for an agent)" }
68030
+ ],
68031
+ examples: ['overlane pr approve --number 7 --evidence "read the diff" --evidence "ran the tests" --reason "inside the task scope"'],
68032
+ effect: "write",
68033
+ ...common18,
68034
+ keywords: ["approve pull request", "fast path approval", "independent review", "sign off pr"],
68035
+ result: withDryRun(Approved),
68036
+ text: (d) => "approvalId" in d ? [`${d.route.approved ? "APPROVED" : "recorded"} ${d.route.repository}#${d.route.number} via ${d.via} ${d.approvalId}`] : [],
68037
+ handler: async (inv) => {
68038
+ const number4 = numberOf(inv);
68039
+ const head = textFlag(inv, "head") ?? "HEAD";
68040
+ const sha = await git4(inv.deps, ["rev-parse", "--verify", "--quiet", `${head}^{commit}`]);
68041
+ if (sha.code !== 0) throw new UsageError(`${head} is not a commit in this checkout`);
68042
+ const project = await detectProject(inv.deps);
68043
+ 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}`);
68044
+ const evidence = listFlag(inv, "evidence");
68045
+ const body2 = { requestId: requestId12(), repository, number: number4, headSha: sha.stdout.trim(), approval: { evidence, reason: textFlag(inv, "reason") ?? "" } };
68046
+ if (inv.dryRun) return planned7(inv, "POST", prRoutingPaths.approve, body2, "the service records the approval only if the independence and lane rules hold");
68047
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68048
+ return { data: await call2(Approved, "POST", prRoutingPaths.approve(tenantId, projectId), body2) };
68049
+ }
68050
+ });
68051
+ var Batches = external_exports.object({
68052
+ tenantId: external_exports.string(),
68053
+ projectId: external_exports.string(),
68054
+ maxSize: external_exports.number().int(),
68055
+ 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()),
68056
+ unbatched: external_exports.array(external_exports.string())
68057
+ }).strict();
68058
+ var prBatchCommand = defineCommand({
68059
+ path: ["pr", "batch"],
68060
+ summary: "Group related routed pull requests so one reviewer looks once",
68061
+ 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.",
68062
+ examples: ["overlane pr batch", "overlane pr batch --json"],
68063
+ effect: "read",
68064
+ ...common18,
68065
+ keywords: ["batch pull requests", "group prs", "review together", "stacked prs"],
68066
+ result: Batches,
68067
+ 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(", ")}`]],
68068
+ handler: async (inv) => {
68069
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68070
+ return { data: await call2(Batches, "GET", prRoutingPaths.batch(tenantId, projectId)) };
68071
+ }
68072
+ });
68073
+ var PolicyResult = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), policy: RoutingPolicy }).strict();
68074
+ var prPolicyCommand = defineCommand({
68075
+ path: ["pr", "policy"],
68076
+ summary: "Show or set who approves the approver lane and how large a batch may be",
68077
+ 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.",
68078
+ flags: [
68079
+ { name: "approver", type: "string", multiple: true, value: "USER_ID", description: "A person who may approve the approver lane; repeat for more" },
68080
+ { 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" },
68081
+ { name: "allow-agent-approver", type: "boolean", description: "Let an independent agent with evidence approve the approver lane" },
68082
+ { name: "max-batch", type: "string", value: "N", description: "The largest batch, 2 to 20" }
68083
+ ],
68084
+ examples: ["overlane pr policy", "overlane pr policy --approver user_abc --approver-role owner"],
68085
+ effect: "write",
68086
+ ...common18,
68087
+ keywords: ["who approves high risk", "approver", "routing policy", "batch size"],
68088
+ result: withDryRun(PolicyResult),
68089
+ 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}`] : [],
68090
+ handler: async (inv) => {
68091
+ const users = listFlag(inv, "approver");
68092
+ const roles = listFlag(inv, "approver-role");
68093
+ const maxBatch = textFlag(inv, "max-batch");
68094
+ 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");
68095
+ const changing = users.length > 0 || roles.length > 0 || maxBatch !== void 0 || inv.values["allow-agent-approver"] === true;
68096
+ 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) } };
68097
+ if (inv.dryRun) return planned7(inv, changing ? "PUT" : "GET", prRoutingPaths.policy, changing ? patch : void 0);
68098
+ const { call: call2, tenantId, projectId } = await connected7(inv);
68099
+ return { data: await call2(PolicyResult, changing ? "PUT" : "GET", prRoutingPaths.policy(tenantId, projectId), changing ? patch : void 0) };
68100
+ }
68101
+ });
68102
+ var prCommands = [prRouteCommand, prApproveCommand, prBatchCommand, prPolicyCommand];
68103
+
68104
+ // ../cli/src/commands/egress.ts
68105
+ init_src();
68106
+ init_zod();
68107
+ import { randomUUID as randomUUID14 } from "node:crypto";
68108
+ var common19 = { needsAuth: true, needsNetwork: true };
68109
+ var requestId13 = () => `cli-${randomUUID14()}`;
68110
+ async function connected8(inv) {
68111
+ const project = await connectedProject(inv.deps, "egress");
68112
+ return { call: serviceCall(inv.deps, project.serviceUrl, project.token), tenantId: project.binding.tenantId, projectId: project.binding.projectId };
68113
+ }
68114
+ async function planned8(inv, method, path, body2, note) {
68115
+ const project = await offlineProject(inv.deps, "egress");
68116
+ 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 } }] }));
68117
+ }
68118
+ function ruleOf(text) {
68119
+ const [host, ports] = text.split(":");
68120
+ const parsedPorts = ports === void 0 ? [443] : ports.split(",").map((p) => /^\d{1,5}$/.test(p) ? Number(p) : Number.NaN);
68121
+ 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`);
68122
+ const checked2 = normaliseHost(host.replace(/^\*\./, "x."));
68123
+ 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" });
68124
+ return { host: host.toLowerCase(), ports: parsedPorts };
68125
+ }
68126
+ var PolicyShow = EgressPolicyView;
68127
+ var hosts = (policy) => policy.rules.map((r) => `${r.host}${r.ports.length === 1 && r.ports[0] === 443 ? "" : `:${r.ports.join(",")}`}`);
68128
+ var egressPolicyShowCommand = defineCommand({
68129
+ path: ["egress", "policy", "show"],
68130
+ summary: "Show the egress allowlist: which hosts native CI and agent sandboxes may reach",
68131
+ 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.",
68132
+ examples: ["overlane egress policy show", "overlane egress policy show --json"],
68133
+ effect: "read",
68134
+ ...common19,
68135
+ keywords: ["egress", "allowlist", "which hosts can a job reach", "network policy", "sandbox network"],
68136
+ result: PolicyShow,
68137
+ 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}`)],
68138
+ handler: async (inv) => {
68139
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68140
+ return { data: await call2(PolicyShow, "GET", egressPaths.policy(tenantId, projectId)) };
68141
+ }
68142
+ });
68143
+ var egressPolicySetCommand = defineCommand({
68144
+ path: ["egress", "policy", "set"],
68145
+ summary: "Set the egress allowlist and its mode",
68146
+ 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.",
68147
+ flags: [
68148
+ { name: "allow", type: "string", multiple: true, value: "HOST[:PORTS]", description: "A host the sandbox may reach; repeat for more" },
68149
+ { name: "add", type: "boolean", description: "Keep the current rules and add these" },
68150
+ { name: "mode", type: "string", enum: ["enforce", "report"], value: "MODE", description: "enforce (default) or report" }
68151
+ ],
68152
+ examples: ["overlane egress policy set --allow registry.npmjs.org --allow api.github.com", "overlane egress policy set --add --allow '*.example.com' --mode report"],
68153
+ effect: "write",
68154
+ ...common19,
68155
+ keywords: ["allow host", "egress rule", "add to allowlist", "open a host"],
68156
+ result: withDryRun(PolicyShow),
68157
+ text: (d) => "rules" in d ? [`mode: ${d.mode} version ${d.version}`, ...hosts(d).map((h) => `allow ${h}`)] : [],
68158
+ handler: async (inv) => {
68159
+ const rules = listFlag(inv, "allow").map(ruleOf);
68160
+ const mode = textFlag(inv, "mode");
68161
+ const add = boolFlag(inv, "add");
68162
+ if (rules.length === 0 && mode === void 0) throw new UsageError("give --allow HOST, or --mode", { hint: "overlane egress policy set --allow registry.npmjs.org" });
68163
+ 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");
68164
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68165
+ const current2 = await call2(PolicyShow, "GET", egressPaths.policy(tenantId, projectId));
68166
+ const merged = add || rules.length === 0 ? [...current2.rules, ...rules] : rules;
68167
+ const seen = /* @__PURE__ */ new Set();
68168
+ const unique = merged.filter((rule) => {
68169
+ const key = `${rule.host}:${rule.ports.join(",")}`;
68170
+ return seen.has(key) ? false : (seen.add(key), true);
68171
+ });
68172
+ return { data: await call2(PolicyShow, "PUT", egressPaths.policy(tenantId, projectId), { mode: mode ?? current2.mode, rules: unique, expectedVersion: current2.version }) };
68173
+ }
68174
+ });
68175
+ var Check = EgressDecision.extend({ eventId: external_exports.string() }).passthrough();
68176
+ var egressCheckCommand = defineCommand({
68177
+ path: ["egress", "check"],
68178
+ summary: "Ask whether a host may be reached; exits 6 when the allowlist denies it",
68179
+ 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.",
68180
+ flags: [
68181
+ { name: "host", type: "string", required: true, value: "HOST", description: "The host name to check" },
68182
+ { name: "port", type: "string", value: "N", description: "The port (default 443)" },
68183
+ { name: "task", type: "string", value: "TASK", description: "The task asking, for the record" }
68184
+ ],
68185
+ examples: ["overlane egress check --host registry.npmjs.org", "overlane egress check --host 169.254.169.254"],
68186
+ effect: "write",
68187
+ ...common19,
68188
+ keywords: ["can the sandbox reach", "is this host allowed", "egress check"],
68189
+ result: withDryRun(Check),
68190
+ text: (d) => "outcome" in d ? [`${d.outcome.toUpperCase()} ${d.host}:${d.port} ${d.message}`] : [],
68191
+ handler: async (inv) => {
68192
+ const host = textFlag(inv, "host");
68193
+ if (host === void 0) throw new UsageError("check needs --host", { hint: "overlane egress check --host registry.npmjs.org" });
68194
+ const port = textFlag(inv, "port");
68195
+ if (port !== void 0 && !/^\d{1,5}$/.test(port)) throw new UsageError("--port is a whole number");
68196
+ const body2 = { host, ...port === void 0 ? {} : { port: Number(port) }, ...textFlag(inv, "task") === void 0 ? {} : { taskId: textFlag(inv, "task") } };
68197
+ if (inv.dryRun) return planned8(inv, "POST", egressPaths.evaluate, body2, "records the answer; nothing is connected to");
68198
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68199
+ const decision = await call2(Check, "POST", egressPaths.evaluate(tenantId, projectId), body2);
68200
+ 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 }) };
68201
+ }
68202
+ });
68203
+ var Events = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), events: external_exports.array(EgressEvent) }).strict();
68204
+ var egressEventsCommand = defineCommand({
68205
+ path: ["egress", "events"],
68206
+ summary: "List recent egress decisions: host, port and outcome, never a path or a body",
68207
+ description: "Newest first. Each row is a host and port, whether it was allowed, denied or reported, why, and which task and run asked.",
68208
+ flags: [
68209
+ { name: "outcome", type: "string", enum: ["allowed", "denied", "reported"], value: "OUTCOME", description: "Only this outcome" },
68210
+ { name: "task", type: "string", value: "TASK", description: "Only this task" }
68211
+ ],
68212
+ examples: ["overlane egress events --outcome denied"],
68213
+ effect: "read",
68214
+ ...common19,
68215
+ keywords: ["what did the sandbox try to reach", "denied requests", "egress log"],
68216
+ result: Events,
68217
+ 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}`}`),
68218
+ handler: async (inv) => {
68219
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68220
+ const params = new URLSearchParams();
68221
+ if (textFlag(inv, "outcome") !== void 0) params.set("outcome", textFlag(inv, "outcome"));
68222
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
68223
+ const query = params.toString();
68224
+ return { data: await call2(Events, "GET", `${egressPaths.events(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
68225
+ }
68226
+ });
68227
+ var GrantResult = TaskGrant.extend({ replay: external_exports.boolean().optional(), reasons: external_exports.array(external_exports.string()).optional() }).strict();
68228
+ var Grants = external_exports.object({ tenantId: external_exports.string(), projectId: external_exports.string(), grants: external_exports.array(TaskGrant) }).strict();
68229
+ var egressGrantCommand = defineCommand({
68230
+ path: ["egress", "grant"],
68231
+ summary: "Grant one task a credential for named hosts; the value never reaches the agent",
68232
+ 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.",
68233
+ flags: [
68234
+ { name: "task", type: "string", value: "TASK", description: "The task the grant belongs to (required for a task grant)" },
68235
+ { name: "host", type: "string", multiple: true, required: true, value: "HOST", description: "A host the credential may be attached for; repeat for more" },
68236
+ { name: "secret", type: "string", required: true, value: "NAME", description: "The secret's name in the project's secret store, such as NPM_TOKEN" },
68237
+ { name: "minutes", type: "string", value: "N", description: "Lifetime in minutes, 1 to 1440 (default 15)" },
68238
+ { name: "scope", type: "string", enum: ["task", "project", "team"], value: "SCOPE", description: "task (default), project or team; the wider two are class secrets" },
68239
+ { name: "header", type: "string", value: "NAME", description: "Attach as this header instead of Authorization: Bearer" }
68240
+ ],
68241
+ 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"],
68242
+ effect: "write",
68243
+ ...common19,
68244
+ keywords: ["task credential", "scoped secret", "give the agent a token", "credential broker", "grant access to secret"],
68245
+ result: withDryRun(GrantResult),
68246
+ 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."] : [],
68247
+ handler: async (inv) => {
68248
+ const scope = textFlag(inv, "scope") ?? "task";
68249
+ const task = textFlag(inv, "task");
68250
+ 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" });
68251
+ const minutes = textFlag(inv, "minutes");
68252
+ 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");
68253
+ const header = textFlag(inv, "header");
68254
+ const body2 = {
68255
+ requestId: requestId13(),
68256
+ scope,
68257
+ ...scope === "task" ? { taskId: task } : {},
68258
+ hosts: listFlag(inv, "host"),
68259
+ secretName: textFlag(inv, "secret"),
68260
+ ...header === void 0 ? {} : { attach: { kind: "header", name: header.toLowerCase() } },
68261
+ ...minutes === void 0 ? {} : { ttlSeconds: Number(minutes) * 60 }
68262
+ };
68263
+ if (inv.dryRun) return planned8(inv, "POST", egressPaths.grants, body2, "the secret's value is not part of the request or the answer");
68264
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68265
+ return { data: await call2(GrantResult, "POST", egressPaths.grants(tenantId, projectId), body2) };
68266
+ }
68267
+ });
68268
+ var egressGrantsCommand = defineCommand({
68269
+ path: ["egress", "grants"],
68270
+ summary: "List the project's credential grants and whether each is still active",
68271
+ 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.",
68272
+ flags: [
68273
+ { name: "task", type: "string", value: "TASK", description: "Only this task's grants" },
68274
+ { name: "status", type: "string", enum: ["active", "expired", "revoked"], value: "STATUS", description: "Only grants in this state" }
68275
+ ],
68276
+ examples: ["overlane egress grants --status active"],
68277
+ effect: "read",
68278
+ ...common19,
68279
+ keywords: ["list grants", "which secrets can the task use", "active credentials"],
68280
+ result: Grants,
68281
+ 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}`),
68282
+ handler: async (inv) => {
68283
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68284
+ const params = new URLSearchParams();
68285
+ if (textFlag(inv, "task") !== void 0) params.set("task", textFlag(inv, "task"));
68286
+ if (textFlag(inv, "status") !== void 0) params.set("status", textFlag(inv, "status"));
68287
+ const query = params.toString();
68288
+ return { data: await call2(Grants, "GET", `${egressPaths.grants(tenantId, projectId)}${query === "" ? "" : `?${query}`}`) };
68289
+ }
68290
+ });
68291
+ var egressRevokeCommand = defineCommand({
68292
+ path: ["egress", "revoke"],
68293
+ summary: "Revoke a credential grant at once",
68294
+ description: "Ends a grant: the proxy stops attaching its credential on the next request. Revoking is always allowed and needs no approval.",
68295
+ flags: [{ name: "grant", type: "string", required: true, value: "ID", description: "The grant to revoke" }],
68296
+ examples: ["overlane egress revoke --grant grt_abc123"],
68297
+ effect: "write",
68298
+ ...common19,
68299
+ keywords: ["revoke grant", "take back credential", "end task access"],
68300
+ result: withDryRun(TaskGrant),
68301
+ text: (d) => "secretName" in d ? [`grant ${d.id} is ${d.status}`] : [],
68302
+ handler: async (inv) => {
68303
+ const id2 = textFlag(inv, "grant");
68304
+ if (id2 === void 0) throw new UsageError("revoke needs --grant", { hint: "overlane egress grants" });
68305
+ if (inv.dryRun) return planned8(inv, "POST", (t, p) => egressPaths.revoke(t, p, id2), {});
68306
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68307
+ return { data: await call2(TaskGrant, "POST", egressPaths.revoke(tenantId, projectId, id2), {}) };
68308
+ }
68309
+ });
68310
+ var egressScanCommand = defineCommand({
68311
+ path: ["egress", "scan"],
68312
+ summary: "Check this workspace's file list for files that usually hold a credential",
68313
+ 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.",
68314
+ examples: ["overlane egress scan"],
68315
+ effect: "read",
68316
+ ...common19,
68317
+ keywords: ["credentials in workspace", "leak check", "secret files", "scan for credentials"],
68318
+ result: LeakScanResult,
68319
+ text: (d) => d.clean ? ["No credential files found."] : d.findings.map((f) => `${f.kind} ${f.place}${f.line === null ? "" : `:${f.line}`} ${f.rule}`),
68320
+ handler: async (inv) => {
68321
+ const listed = await inv.deps.exec("git", ["ls-files", "-co", "--exclude-standard", "-z"], { cwd: inv.deps.paths.cwd });
68322
+ if (listed.code !== 0) throw new UsageError("could not list the workspace files; run this inside a git repository");
68323
+ const files = listed.stdout.split("\0").filter((name2) => name2 !== "").slice(0, 2e4);
68324
+ const { call: call2, tenantId, projectId } = await connected8(inv);
68325
+ const result = await call2(LeakScanResult, "POST", egressPaths.leakScan(tenantId, projectId), { files });
68326
+ 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 }) };
68327
+ }
68328
+ });
68329
+ var egressCommands = [egressPolicyShowCommand, egressPolicySetCommand, egressCheckCommand, egressEventsCommand, egressGrantCommand, egressGrantsCommand, egressRevokeCommand, egressScanCommand];
68330
+
66510
68331
  // ../cli/src/commands/index.ts
66511
68332
  function coreCommands() {
66512
68333
  return [
@@ -66535,6 +68356,11 @@ function coreCommands() {
66535
68356
  ...reviewCommands,
66536
68357
  ...guardCommands,
66537
68358
  ...billingCommands,
68359
+ ...claimCommands,
68360
+ ...budgetCommands,
68361
+ ...rulesCommands,
68362
+ ...prCommands,
68363
+ ...egressCommands,
66538
68364
  ...repoCommands,
66539
68365
  ...ciCommands,
66540
68366
  ...deployCommands
@@ -66690,10 +68516,10 @@ async function runCli(argv, deps, options) {
66690
68516
  const parsed = spec.style === "passthrough" ? { values: {}, positionals: [] } : parseFor(spec, rest);
66691
68517
  const safety = spec.style === "passthrough" ? safetyFlags(rest) : { dryRun: parsed.values["dry-run"] === true, force: parsed.values["force"] === true };
66692
68518
  if (spec.effect === "destructive" && !safety.dryRun && !safety.force && !deps.isTTY) {
66693
- const line2 = ["overlane", ...globals.tokens, "--force"].map(quote2).join(" ");
68519
+ const line3 = ["overlane", ...globals.tokens, "--force"].map(quote2).join(" ");
66694
68520
  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
68521
  code: "force_required",
66696
- hint: line2
68522
+ hint: line3
66697
68523
  });
66698
68524
  }
66699
68525
  const invocation = {
@@ -66758,37 +68584,37 @@ init_src2();
66758
68584
 
66759
68585
  // src/data-dir.ts
66760
68586
  import { homedir } from "node:os";
66761
- import { join as join28 } from "node:path";
68587
+ import { join as join29 } from "node:path";
66762
68588
  function dataDir(env = process.env, home = homedir()) {
66763
68589
  const override = env["MVRX_DATA_DIR"];
66764
68590
  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");
68591
+ 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"));
68592
+ return join29(base4, "mvrx-harness");
66767
68593
  }
66768
68594
  function marketplaceRoot(env = process.env, home = homedir()) {
66769
- return join28(dataDir(env, home), "marketplace");
68595
+ return join29(dataDir(env, home), "marketplace");
66770
68596
  }
66771
68597
 
66772
68598
  // src/plugin.ts
66773
- import { readFileSync as readFileSync13 } from "node:fs";
66774
- import { join as join30 } from "node:path";
68599
+ import { readFileSync as readFileSync14 } from "node:fs";
68600
+ import { join as join31 } from "node:path";
66775
68601
  import { parseArgs as parseArgs3 } from "node:util";
66776
68602
 
66777
68603
  // src/marketplace.ts
66778
68604
  init_src();
66779
68605
  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";
68606
+ 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";
68607
+ import { dirname as dirname11, join as join30, relative as relative2, sep } from "node:path";
66782
68608
  var CLIENT_DIRS = ["claude", "codex"];
66783
68609
  var EMBEDDED_MANIFEST = "release-manifest.json";
66784
- var INSTALLED_MANIFEST = join29(".release", "manifest.json");
68610
+ var INSTALLED_MANIFEST = join30(".release", "manifest.json");
66785
68611
  var MarketplaceError = class extends Error {
66786
68612
  };
66787
68613
  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")));
68614
+ const dir = join30(packageRoot2, "plugins");
68615
+ const path = join30(dir, EMBEDDED_MANIFEST);
68616
+ if (!existsSync8(path)) throw new MarketplaceError(`this package has no embedded plugin release (${path} is missing)`);
68617
+ const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync13(path, "utf8")));
66792
68618
  if (!parsed.success) throw new MarketplaceError(`the embedded release manifest is invalid: ${parsed.error.message}`);
66793
68619
  return { dir, manifest: parsed.data };
66794
68620
  }
@@ -66796,10 +68622,10 @@ function sha2562(data) {
66796
68622
  return `sha256:${createHash7("sha256").update(data).digest("hex")}`;
66797
68623
  }
66798
68624
  function listFiles(root, dir = root) {
66799
- if (!existsSync7(dir)) return [];
68625
+ if (!existsSync8(dir)) return [];
66800
68626
  const out2 = [];
66801
68627
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
66802
- const full = join29(dir, entry.name);
68628
+ const full = join30(dir, entry.name);
66803
68629
  if (entry.isDirectory()) out2.push(...listFiles(root, full));
66804
68630
  else out2.push(relative2(root, full).split(sep).join("/"));
66805
68631
  }
@@ -66811,23 +68637,23 @@ async function verifyTree(treeRoot, manifest) {
66811
68637
  if (recomputed !== manifest.digest) problems.push(`manifest digest ${manifest.digest} does not match its file list (${recomputed})`);
66812
68638
  const listed = new Set(manifest.files.map((file2) => file2.path));
66813
68639
  for (const file2 of manifest.files) {
66814
- const path = join29(treeRoot, file2.path);
66815
- if (!existsSync7(path)) {
68640
+ const path = join30(treeRoot, file2.path);
68641
+ if (!existsSync8(path)) {
66816
68642
  problems.push(`missing: ${file2.path}`);
66817
68643
  continue;
66818
68644
  }
66819
- if (sha2562(readFileSync12(path)) !== file2.digest) problems.push(`modified: ${file2.path}`);
68645
+ if (sha2562(readFileSync13(path)) !== file2.digest) problems.push(`modified: ${file2.path}`);
66820
68646
  }
66821
68647
  for (const client of CLIENT_DIRS) {
66822
- for (const found of listFiles(join29(treeRoot, client))) {
68648
+ for (const found of listFiles(join30(treeRoot, client))) {
66823
68649
  if (!listed.has(`${client}/${found}`)) problems.push(`unlisted: ${client}/${found}`);
66824
68650
  }
66825
68651
  }
66826
68652
  return problems;
66827
68653
  }
66828
- function readInstalledManifest(target) {
68654
+ function readInstalledManifest(target2) {
66829
68655
  try {
66830
- const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync12(join29(target, INSTALLED_MANIFEST), "utf8")));
68656
+ const parsed = ReleaseManifest.safeParse(JSON.parse(readFileSync13(join30(target2, INSTALLED_MANIFEST), "utf8")));
66831
68657
  return parsed.success ? parsed.data : null;
66832
68658
  } catch {
66833
68659
  return null;
@@ -66845,30 +68671,30 @@ function isNewer(a, b) {
66845
68671
  }
66846
68672
  return false;
66847
68673
  }
66848
- async function syncMarketplace(embedded, target) {
68674
+ async function syncMarketplace(embedded, target2) {
66849
68675
  const before = await verifyTree(embedded.dir, embedded.manifest);
66850
68676
  if (before.length > 0) throw new MarketplaceError(`the embedded plugin files do not match the release manifest; refusing to install:
66851
68677
  ${before.join("\n ")}`);
66852
- const staging = `${target}.new-${process.pid}`;
66853
- const previous = `${target}.old-${process.pid}`;
68678
+ const staging = `${target2}.new-${process.pid}`;
68679
+ const previous = `${target2}.old-${process.pid}`;
66854
68680
  rmSync4(staging, { recursive: true, force: true });
66855
- mkdirSync9(staging, { recursive: true });
68681
+ mkdirSync10(staging, { recursive: true });
66856
68682
  try {
66857
68683
  for (const client of CLIENT_DIRS) {
66858
- if (existsSync7(join29(embedded.dir, client))) cpSync(join29(embedded.dir, client), join29(staging, client), { recursive: true });
68684
+ if (existsSync8(join30(embedded.dir, client))) cpSync(join30(embedded.dir, client), join30(staging, client), { recursive: true });
66859
68685
  }
66860
- mkdirSync9(join29(staging, ".release"), { recursive: true });
66861
- writeFileSync8(join29(staging, INSTALLED_MANIFEST), `${JSON.stringify(embedded.manifest, null, 2)}
68686
+ mkdirSync10(join30(staging, ".release"), { recursive: true });
68687
+ writeFileSync9(join30(staging, INSTALLED_MANIFEST), `${JSON.stringify(embedded.manifest, null, 2)}
66862
68688
  `);
66863
68689
  const after = await verifyTree(staging, embedded.manifest);
66864
68690
  if (after.length > 0) throw new MarketplaceError(`the copied plugin files do not match the release manifest:
66865
68691
  ${after.join("\n ")}`);
66866
- mkdirSync9(dirname10(target), { recursive: true });
66867
- if (existsSync7(target)) renameSync6(target, previous);
68692
+ mkdirSync10(dirname11(target2), { recursive: true });
68693
+ if (existsSync8(target2)) renameSync7(target2, previous);
66868
68694
  try {
66869
- renameSync6(staging, target);
68695
+ renameSync7(staging, target2);
66870
68696
  } catch (error62) {
66871
- if (existsSync7(previous)) renameSync6(previous, target);
68697
+ if (existsSync8(previous)) renameSync7(previous, target2);
66872
68698
  throw error62;
66873
68699
  }
66874
68700
  rmSync4(previous, { recursive: true, force: true });
@@ -66876,11 +68702,11 @@ async function syncMarketplace(embedded, target) {
66876
68702
  rmSync4(staging, { recursive: true, force: true });
66877
68703
  }
66878
68704
  }
66879
- async function ensureMarketplace(embedded, target) {
66880
- const installed = readInstalledManifest(target);
66881
- const present = existsSync7(target) && statSync2(target).isDirectory();
68705
+ async function ensureMarketplace(embedded, target2) {
68706
+ const installed = readInstalledManifest(target2);
68707
+ const present = existsSync8(target2) && statSync2(target2).isDirectory();
66882
68708
  if (installed !== null && present && !isNewer(embedded.manifest.version, installed.version)) return false;
66883
- await syncMarketplace(embedded, target);
68709
+ await syncMarketplace(embedded, target2);
66884
68710
  return true;
66885
68711
  }
66886
68712
 
@@ -66890,8 +68716,8 @@ var PLUGIN_USAGE = ` overlane plugin install [--scope project|user] [--client c
66890
68716
  var PluginUsageError = class extends Error {
66891
68717
  };
66892
68718
  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"));
68719
+ const file2 = client === "claude" ? join31(treeRoot, "claude", ".claude-plugin", "marketplace.json") : join31(treeRoot, "codex", ".agents", "plugins", "marketplace.json");
68720
+ const parsed = JSON.parse(readFileSync14(file2, "utf8"));
66895
68721
  const record3 = parsed;
66896
68722
  const plugin = record3.plugins?.[0]?.name;
66897
68723
  if (typeof record3.name !== "string" || typeof plugin !== "string") throw new MarketplaceError(`${file2} names no marketplace or plugin`);
@@ -66905,7 +68731,7 @@ async function step(deps, program, args2, cwd) {
66905
68731
  `);
66906
68732
  const result = await deps.exec(program, args2, { cwd });
66907
68733
  const text = `${result.stdout}${result.stderr}`.trim();
66908
- if (text !== "") deps.stdout(`${text.split("\n").map((line2) => ` ${line2}`).join("\n")}
68734
+ if (text !== "") deps.stdout(`${text.split("\n").map((line3) => ` ${line3}`).join("\n")}
66909
68735
  `);
66910
68736
  return result.code === 0;
66911
68737
  }
@@ -66937,7 +68763,7 @@ ${PLUGIN_USAGE}`);
66937
68763
  const key = `${names.plugin}@${names.marketplace}`;
66938
68764
  if (!await available(deps, client)) {
66939
68765
  if (explicit !== void 0) {
66940
- deps.stderr(`overlane: \`${client}\` was not found on PATH; the marketplace is at ${join30(root, client)}
68766
+ deps.stderr(`overlane: \`${client}\` was not found on PATH; the marketplace is at ${join31(root, client)}
66941
68767
  `);
66942
68768
  failed = true;
66943
68769
  } else {
@@ -66949,7 +68775,7 @@ ${PLUGIN_USAGE}`);
66949
68775
  ranAny = true;
66950
68776
  deps.stdout(`${client}:
66951
68777
  `);
66952
- const dir = join30(root, client);
68778
+ const dir = join31(root, client);
66953
68779
  const steps = client === "claude" ? [
66954
68780
  ["claude", ["plugin", "marketplace", "add", dir, "--scope", scope]],
66955
68781
  ["claude", ["plugin", "marketplace", "update", names.marketplace]],
@@ -67012,7 +68838,7 @@ async function indexRun(sub, rest, env) {
67012
68838
  const args2 = [sub, ...rest.filter((token) => token !== "--dry-run" && token !== "--force")];
67013
68839
  const out2 = [];
67014
68840
  const err2 = [];
67015
- const code2 = await runCli2(args2, env, (line2) => out2.push(line2), (line2) => err2.push(line2));
68841
+ const code2 = await runCli2(args2, env, (line3) => out2.push(line3), (line3) => err2.push(line3));
67016
68842
  if (code2 === 0) return { data: { ok: true, message: out2.join("\n") } };
67017
68843
  const message = err2.join("\n").split("\n")[0] ?? "the indexer failed";
67018
68844
  if (code2 === 2) throw new UsageError(message, { hint: `overlane index ${sub} --help` });
@@ -67117,7 +68943,7 @@ var packageCommands = [indexBuildCommand, indexUploadCommand, shimCommand, plugi
67117
68943
  // src/deps.ts
67118
68944
  import { execFile as execFile4, spawn } from "node:child_process";
67119
68945
  import { homedir as homedir3 } from "node:os";
67120
- import { join as join33 } from "node:path";
68946
+ import { join as join34 } from "node:path";
67121
68947
  import { createInterface } from "node:readline/promises";
67122
68948
  function exec(command, args2, options) {
67123
68949
  return new Promise((resolveResult) => {
@@ -67163,8 +68989,8 @@ async function confirm(question) {
67163
68989
  }
67164
68990
  }
67165
68991
  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");
68992
+ const base4 = env["XDG_CONFIG_HOME"] ?? (process.platform === "win32" ? env["APPDATA"] : void 0) ?? join34(homedir3(), ".config");
68993
+ return join34(base4, "mvrx-harness");
67168
68994
  }
67169
68995
  function createDeps2(env = process.env) {
67170
68996
  return {