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 +2324 -498
- package/package.json +1 -1
- package/plugins/claude/plugins/mvrx-harness/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-context.mjs +34 -34
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-guard.mjs +28 -28
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-intake.mjs +35 -35
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-quota.mjs +33 -33
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-statusline.mjs +24 -24
- package/plugins/claude/plugins/mvrx-harness/scripts/harness-stop.mjs +28 -28
- package/plugins/codex/plugins/mvrx-harness/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-context.mjs +34 -34
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-guard.mjs +28 -28
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-intake.mjs +35 -35
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-quota.mjs +33 -33
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-statusline.mjs +24 -24
- package/plugins/codex/plugins/mvrx-harness/scripts/harness-stop.mjs +28 -28
- package/plugins/release-manifest.json +20 -20
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 = (
|
|
31
|
+
var __export = (target2, all) => {
|
|
32
32
|
for (var name2 in all)
|
|
33
|
-
__defProp(
|
|
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,
|
|
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(
|
|
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(
|
|
205
|
-
Object.defineProperty(
|
|
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(
|
|
220
|
-
Object.defineProperty(
|
|
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(
|
|
231
|
-
if (key in
|
|
232
|
-
assignProp(
|
|
230
|
+
function putProp(target2, key, value) {
|
|
231
|
+
if (key in target2)
|
|
232
|
+
assignProp(target2, key, value);
|
|
233
233
|
else
|
|
234
|
-
|
|
234
|
+
target2[key] = value;
|
|
235
235
|
}
|
|
236
|
-
function mirrorShape(
|
|
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(
|
|
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(
|
|
248
|
+
putProp(target2, key, wrap ? wrap(desc.value, key) : desc.value);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
function mirrorProps(
|
|
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(
|
|
257
|
+
deferProp(target2, key, () => source[key]);
|
|
258
258
|
else
|
|
259
|
-
putProp(
|
|
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
|
|
368
|
+
let target2;
|
|
369
369
|
return new Proxy({}, {
|
|
370
370
|
get(_, prop, receiver) {
|
|
371
|
-
|
|
372
|
-
return Reflect.get(
|
|
371
|
+
target2 ?? (target2 = getter());
|
|
372
|
+
return Reflect.get(target2, prop, receiver);
|
|
373
373
|
},
|
|
374
374
|
set(_, prop, value, receiver) {
|
|
375
|
-
|
|
376
|
-
return Reflect.set(
|
|
375
|
+
target2 ?? (target2 = getter());
|
|
376
|
+
return Reflect.set(target2, prop, value, receiver);
|
|
377
377
|
},
|
|
378
378
|
has(_, prop) {
|
|
379
|
-
|
|
380
|
-
return Reflect.has(
|
|
379
|
+
target2 ?? (target2 = getter());
|
|
380
|
+
return Reflect.has(target2, prop);
|
|
381
381
|
},
|
|
382
382
|
deleteProperty(_, prop) {
|
|
383
|
-
|
|
384
|
-
return Reflect.deleteProperty(
|
|
383
|
+
target2 ?? (target2 = getter());
|
|
384
|
+
return Reflect.deleteProperty(target2, prop);
|
|
385
385
|
},
|
|
386
386
|
ownKeys(_) {
|
|
387
|
-
|
|
388
|
-
return Reflect.ownKeys(
|
|
387
|
+
target2 ?? (target2 = getter());
|
|
388
|
+
return Reflect.ownKeys(target2);
|
|
389
389
|
},
|
|
390
390
|
getOwnPropertyDescriptor(_, prop) {
|
|
391
|
-
|
|
392
|
-
return Reflect.getOwnPropertyDescriptor(
|
|
391
|
+
target2 ?? (target2 = getter());
|
|
392
|
+
return Reflect.getOwnPropertyDescriptor(target2, prop);
|
|
393
393
|
},
|
|
394
394
|
defineProperty(_, prop, descriptor) {
|
|
395
|
-
|
|
396
|
-
return Reflect.defineProperty(
|
|
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
|
|
952
|
-
if (!initialized.has(
|
|
953
|
-
initialized.add(
|
|
954
|
-
members(
|
|
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
|
|
2087
|
-
this.content.push(
|
|
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(
|
|
12939
|
-
const targetAny =
|
|
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,
|
|
13143
|
+
function numericOperand(value, label2) {
|
|
13144
13144
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
13145
|
-
throw new ZodCompileUnsupportedError(`${
|
|
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(
|
|
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(
|
|
15573
|
+
assignProp(target2, key, source[key]);
|
|
15574
15574
|
}
|
|
15575
15575
|
}
|
|
15576
15576
|
}
|
|
15577
|
-
return
|
|
15577
|
+
return target2;
|
|
15578
15578
|
}
|
|
15579
15579
|
function initializeContext(params) {
|
|
15580
|
-
let
|
|
15581
|
-
if (
|
|
15582
|
-
|
|
15583
|
-
if (
|
|
15584
|
-
|
|
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(
|
|
18168
|
-
return pipe(this,
|
|
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,
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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,
|
|
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(
|
|
24426
|
-
if (/^[&*!%@`]/.test(text)) throw new YamlFail(
|
|
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,
|
|
24435
|
+
function indentOf(raw, line3) {
|
|
24436
24436
|
let n = 0;
|
|
24437
24437
|
while (raw[n] === " ") n += 1;
|
|
24438
|
-
if (raw[n] === " ") throw new YamlFail(
|
|
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
|
|
24553
|
-
if (
|
|
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(
|
|
24706
|
+
constructor(line3, message) {
|
|
24707
24707
|
super(message);
|
|
24708
|
-
this.line =
|
|
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(
|
|
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 =
|
|
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
|
|
26955
|
-
str +=
|
|
26956
|
-
${indent}${
|
|
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,
|
|
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,
|
|
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
|
|
27036
|
-
str +=
|
|
27037
|
-
${indentStep}${indent}${
|
|
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:
|
|
28781
|
-
error62.message += ` at line ${
|
|
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[
|
|
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 (
|
|
28792
|
-
let prev = src.substring(lc.lineStarts[
|
|
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 ===
|
|
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
|
|
29750
|
-
let match =
|
|
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 =
|
|
29764
|
-
while (match =
|
|
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 =
|
|
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
|
|
30906
|
-
if (
|
|
31782
|
+
let line3 = this.getLine();
|
|
31783
|
+
if (line3 === null)
|
|
30907
31784
|
return this.setNext("stream");
|
|
30908
|
-
if (
|
|
31785
|
+
if (line3[0] === cst.BOM) {
|
|
30909
31786
|
yield* this.pushCount(1);
|
|
30910
|
-
|
|
31787
|
+
line3 = line3.substring(1);
|
|
30911
31788
|
}
|
|
30912
|
-
if (
|
|
30913
|
-
let dirEnd =
|
|
30914
|
-
let cs =
|
|
31789
|
+
if (line3[0] === "%") {
|
|
31790
|
+
let dirEnd = line3.length;
|
|
31791
|
+
let cs = line3.indexOf("#");
|
|
30915
31792
|
while (cs !== -1) {
|
|
30916
|
-
const ch =
|
|
31793
|
+
const ch = line3[cs - 1];
|
|
30917
31794
|
if (ch === " " || ch === " ") {
|
|
30918
31795
|
dirEnd = cs - 1;
|
|
30919
31796
|
break;
|
|
30920
31797
|
} else {
|
|
30921
|
-
cs =
|
|
31798
|
+
cs = line3.indexOf("#", cs + 1);
|
|
30922
31799
|
}
|
|
30923
31800
|
}
|
|
30924
31801
|
while (true) {
|
|
30925
|
-
const ch =
|
|
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(
|
|
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(
|
|
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
|
|
30980
|
-
if (
|
|
31856
|
+
const line3 = this.getLine();
|
|
31857
|
+
if (line3 === null)
|
|
30981
31858
|
return this.setNext("doc");
|
|
30982
31859
|
let n = yield* this.pushIndicators();
|
|
30983
|
-
switch (
|
|
31860
|
+
switch (line3[n]) {
|
|
30984
31861
|
case "#":
|
|
30985
|
-
yield* this.pushCount(
|
|
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(
|
|
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
|
|
31031
|
-
if (
|
|
31907
|
+
const line3 = this.getLine();
|
|
31908
|
+
if (line3 === null)
|
|
31032
31909
|
return this.setNext("flow");
|
|
31033
|
-
if (indent !== -1 && indent < this.indentNext &&
|
|
31034
|
-
const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (
|
|
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 (
|
|
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 (
|
|
31925
|
+
switch (line3[n]) {
|
|
31049
31926
|
case void 0:
|
|
31050
31927
|
return "flow";
|
|
31051
31928
|
case "#":
|
|
31052
|
-
yield* this.pushCount(
|
|
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(
|
|
32339
|
+
function arrayPushArray(target2, source) {
|
|
31463
32340
|
if (source.length < 1e5)
|
|
31464
|
-
Array.prototype.push.apply(
|
|
32341
|
+
Array.prototype.push.apply(target2, source);
|
|
31465
32342
|
else
|
|
31466
32343
|
for (let i2 = 0; i2 < source.length; ++i2)
|
|
31467
|
-
|
|
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
|
|
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 :
|
|
33301
|
+
return override !== void 0 && override !== "" ? override : join25(packageRoot(env), "assets");
|
|
32425
33302
|
}
|
|
32426
33303
|
function assetFile(name2) {
|
|
32427
|
-
return
|
|
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.
|
|
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(
|
|
32796
|
-
return Object.prototype.hasOwnProperty.call(
|
|
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
|
|
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 (
|
|
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:
|
|
35892
|
-
return checked([
|
|
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:
|
|
35904
|
-
return checked([
|
|
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((
|
|
37115
|
-
if (!isSymbolDefined(
|
|
37116
|
-
wasmImports[
|
|
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 = (
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
40612
|
-
chunk.push(
|
|
40613
|
-
if (
|
|
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
|
|
40659
|
-
if (
|
|
40660
|
-
if (
|
|
40661
|
-
if (
|
|
40662
|
-
callees.set(entry.id, (callees.get(entry.id) ?? /* @__PURE__ */ new Set()).add(
|
|
40663
|
-
callers.set(
|
|
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
|
|
40683
|
-
for (const
|
|
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
|
|
40743
|
-
const b = index.symbols.get(
|
|
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
|
|
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} -> ${
|
|
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 ${
|
|
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
|
|
40867
|
-
if (!fits(["", taskFree ? "Central files:" : "Related files:", ...related,
|
|
40868
|
-
related.push(
|
|
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
|
|
41787
|
+
import { dirname as dirname10, join as join27 } from "node:path";
|
|
40911
41788
|
import { promisify as promisify2 } from "node:util";
|
|
40912
|
-
async function
|
|
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(
|
|
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
|
|
41811
|
+
const commitSha = await git5(options.root, ["rev-parse", "HEAD"]);
|
|
40935
41812
|
let branch;
|
|
40936
41813
|
try {
|
|
40937
|
-
branch = await
|
|
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 ??
|
|
40947
|
-
await mkdir3(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
41051
|
-
for (const name2 of await readdir4(
|
|
41052
|
-
await writeFile4(
|
|
41053
|
-
await writeFile4(
|
|
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(
|
|
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
|
|
42002
|
+
let target2 = {};
|
|
41126
42003
|
for (var name2 in all) {
|
|
41127
|
-
__defProp3(
|
|
42004
|
+
__defProp3(target2, name2, {
|
|
41128
42005
|
get: all[name2],
|
|
41129
42006
|
enumerable: true
|
|
41130
42007
|
});
|
|
41131
42008
|
}
|
|
41132
42009
|
if (symbols2) {
|
|
41133
|
-
__defProp3(
|
|
42010
|
+
__defProp3(target2, Symbol.toStringTag, { value: "Module" });
|
|
41134
42011
|
}
|
|
41135
|
-
return
|
|
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,
|
|
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
|
-
}) :
|
|
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(
|
|
44819
|
-
return JSONRPCMessageSchema.parse(JSON.parse(
|
|
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
|
|
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(
|
|
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
|
|
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 ?
|
|
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(
|
|
48088
|
+
constructor(label2) {
|
|
47212
48089
|
super();
|
|
47213
|
-
this.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(
|
|
48098
|
+
constructor(label2) {
|
|
47222
48099
|
super();
|
|
47223
|
-
this.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(
|
|
47589
|
-
return this._leafNode(new Label2(
|
|
48465
|
+
label(label2) {
|
|
48466
|
+
return this._leafNode(new Label2(label2));
|
|
47590
48467
|
}
|
|
47591
|
-
break(
|
|
47592
|
-
return this._leafNode(new Break(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
49856
|
-
|
|
49857
|
-
|
|
49858
|
-
|
|
49859
|
-
|
|
49860
|
-
|
|
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
|
-
|
|
49864
|
-
|
|
49865
|
-
|
|
49866
|
-
|
|
49867
|
-
|
|
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
|
-
|
|
49871
|
-
if (relative4.query !== void 0)
|
|
49872
|
-
else
|
|
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] === "/")
|
|
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)
|
|
49877
|
-
else if (!base4.path)
|
|
49878
|
-
else
|
|
49879
|
-
|
|
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
|
-
|
|
50758
|
+
target2.query = relative4.query;
|
|
49882
50759
|
}
|
|
49883
|
-
|
|
49884
|
-
|
|
49885
|
-
|
|
50760
|
+
target2.userinfo = base4.userinfo;
|
|
50761
|
+
target2.host = base4.host;
|
|
50762
|
+
target2.port = base4.port;
|
|
49886
50763
|
}
|
|
49887
|
-
|
|
50764
|
+
target2.scheme = base4.scheme;
|
|
49888
50765
|
}
|
|
49889
|
-
|
|
49890
|
-
return
|
|
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
|
|
55903
|
-
import { join as
|
|
55904
|
-
function
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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
|
|
56851
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
55975
56852
|
import { homedir as homedir2 } from "node:os";
|
|
55976
|
-
import { join as
|
|
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) ??
|
|
55979
|
-
return
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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([
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
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,
|
|
56395
|
-
const start2 = starts[
|
|
57271
|
+
function offsetOf(starts, text, line3, character) {
|
|
57272
|
+
const start2 = starts[line3];
|
|
56396
57273
|
if (start2 === void 0) return null;
|
|
56397
|
-
const next = starts[
|
|
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
|
|
56403
|
-
return [
|
|
57279
|
+
const line3 = starts.length - 1;
|
|
57280
|
+
return [line3, offset - (starts[line3] ?? 0)];
|
|
56404
57281
|
}
|
|
56405
|
-
function lineEnd(starts, text,
|
|
56406
|
-
const next = starts[
|
|
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 =
|
|
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
|
-
|
|
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
|
|
56486
|
-
const list = byFile.get(
|
|
56487
|
-
if (!list.some((r) => r.join(",") ===
|
|
56488
|
-
byFile.set(
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
57117
|
-
deps.stdout(`${
|
|
57993
|
+
function say(deps, line3 = "") {
|
|
57994
|
+
deps.stdout(`${line3}
|
|
57118
57995
|
`);
|
|
57119
57996
|
}
|
|
57120
|
-
function warn(deps,
|
|
57121
|
-
deps.stderr(`${redact(
|
|
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
|
|
57144
|
-
return { agent, installed: true, detail:
|
|
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
|
|
57336
|
-
const match = HEADER.exec(
|
|
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(
|
|
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(
|
|
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
|
|
58711
|
+
const label2 = AGENT_LABEL[agent];
|
|
57835
58712
|
if (!input2.info.installed) {
|
|
57836
|
-
return report(agent, "incompatible", input2.info.detail, `install ${
|
|
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 ${
|
|
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
|
|
58999
|
+
for (const line3 of codexEdit.block.split("\n")) if (line3.length > 0) say(deps, ` + ${line3}`);
|
|
58123
59000
|
}
|
|
58124
|
-
for (const
|
|
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((
|
|
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
|
|
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(
|
|
59393
|
+
function setAt(target2, key, value) {
|
|
58517
59394
|
const [head, ...rest] = key;
|
|
58518
|
-
if (head === void 0) return
|
|
58519
|
-
const out2 = { ...
|
|
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
|
|
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,
|
|
59634
|
+
function diffFiles(installed, target2) {
|
|
58758
59635
|
const before = new Map(installed.map((file2) => [file2.path, file2]));
|
|
58759
|
-
const after = new Set(
|
|
59636
|
+
const after = new Set(target2.map((file2) => file2.path));
|
|
58760
59637
|
const added = [];
|
|
58761
59638
|
const changed = [];
|
|
58762
|
-
for (const file2 of
|
|
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,
|
|
58800
|
-
const wanted = new Map(
|
|
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,
|
|
59685
|
+
function mismatchedFiles(marketplaceRoot2, target2) {
|
|
58809
59686
|
const bad = [];
|
|
58810
|
-
for (const file2 of
|
|
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((
|
|
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(
|
|
58967
|
-
return `overlane release update${explicitTo ? ` --to ${
|
|
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
|
|
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((
|
|
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((
|
|
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
|
|
59270
|
-
if (/^\s*```/.test(
|
|
59271
|
-
if (!fenced &&
|
|
59272
|
-
current2 =
|
|
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(
|
|
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
|
|
59286
|
-
if (
|
|
59287
|
-
const match = /^- (.*)$/.exec(
|
|
59288
|
-
if (!match) throw new KitError(`${where2}: expected a "- " bullet, found "${
|
|
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
|
|
59392
|
-
if (/^\s*```/.test(
|
|
60268
|
+
for (const line3 of body2.split("\n")) {
|
|
60269
|
+
if (/^\s*```/.test(line3)) fenced = !fenced;
|
|
59393
60270
|
if (fenced) continue;
|
|
59394
|
-
const heading = /^## ([^:]+): (.+)$/.exec(
|
|
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(
|
|
60279
|
+
if (/^### Acceptance\s*$/.test(line3)) {
|
|
59403
60280
|
inAcceptance = true;
|
|
59404
60281
|
continue;
|
|
59405
60282
|
}
|
|
59406
|
-
if (
|
|
59407
|
-
const depends = /^Depends on: (.+)$/.exec(
|
|
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 &&
|
|
59410
|
-
else throw new KitError(`${where2}: unexpected line under ${current2.id}: "${
|
|
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
|
|
59564
|
-
const kind = classifyCommand(
|
|
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(
|
|
59567
|
-
const normalized = alias && kind !== "install" ? `${alias[1]} run ${alias[2]}` :
|
|
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(
|
|
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
|
|
59676
|
-
const heading = /^##\s+Phase\s+(\d+)\s*[:\u2013\u2014-]\s*(.*?)\s*$/i.exec(
|
|
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(
|
|
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(
|
|
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
|
|
59709
|
-
const heading = /^###\s+User Story\s+(\d+)\b/i.exec(
|
|
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(
|
|
60593
|
+
if (/^#{1,3}\s/.test(line3) || /^---\s*$/.test(line3)) {
|
|
59717
60594
|
collecting = false;
|
|
59718
|
-
if (!/^---/.test(
|
|
60595
|
+
if (!/^---/.test(line3)) story = void 0;
|
|
59719
60596
|
continue;
|
|
59720
60597
|
}
|
|
59721
60598
|
if (!story) continue;
|
|
59722
|
-
if (/\*\*Acceptance Scenarios\*\*/i.test(
|
|
60599
|
+
if (/\*\*Acceptance Scenarios\*\*/i.test(line3)) {
|
|
59723
60600
|
collecting = true;
|
|
59724
60601
|
continue;
|
|
59725
60602
|
}
|
|
59726
|
-
const item = /^\s*\d+\.\s+(.+?)\s*$/.exec(
|
|
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
|
|
59748
|
-
const bullet = /^- (.+?)\s*$/.exec(
|
|
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,
|
|
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 ${
|
|
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,
|
|
59894
|
-
return boundProject(deps, await gitRoot(deps,
|
|
60770
|
+
async function offlineProject(deps, label2) {
|
|
60771
|
+
return boundProject(deps, await gitRoot(deps, label2));
|
|
59895
60772
|
}
|
|
59896
|
-
async function connectedProject(deps,
|
|
59897
|
-
const project = await offlineProject(deps,
|
|
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 = (
|
|
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 ${
|
|
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
|
|
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 [
|
|
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((
|
|
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((
|
|
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
|
|
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 ${
|
|
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 ${
|
|
60581
|
-
say(deps, ` ${resumeCommand(
|
|
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:
|
|
61465
|
+
to: target2,
|
|
60589
61466
|
commitSha: written.copy.commitSha,
|
|
60590
61467
|
route: written.route.route,
|
|
60591
61468
|
localOnly: written.route.localOnly,
|
|
60592
|
-
resumeCommand: resumeCommand(
|
|
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
|
|
61130
|
-
const inSync = installed === null ? "unknown" : installed.digest ===
|
|
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
|
|
61192
|
-
const token = await requireToken(inv.deps,
|
|
61193
|
-
return { target, call: serviceCall(inv.deps,
|
|
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
|
|
61200
|
-
return planOutcome(inv.deps, dryRunPlan({ service:
|
|
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
|
|
61559
|
-
const token = await requireToken(inv.deps,
|
|
61560
|
-
return { target, call: serviceCall(inv.deps,
|
|
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
|
|
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:
|
|
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 ${
|
|
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,
|
|
61595
|
-
const how = await saveTokens(inv.deps,
|
|
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 ${
|
|
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:
|
|
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
|
|
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:
|
|
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 ${
|
|
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,
|
|
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(`${
|
|
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,
|
|
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:
|
|
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
|
|
61662
|
-
const found = await findToken(inv.deps,
|
|
61663
|
-
if (found === null) throw new AuthRequiredError(`no credentials for ${
|
|
61664
|
-
const stored = found.source === "environment" ? null : await loadTokens(inv.deps,
|
|
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
|
|
61707
|
-
return planOutcome(inv.deps, dryRunPlan({ service:
|
|
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
|
|
61753
|
-
if (
|
|
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
|
|
61765
|
-
return planOutcome(inv.deps, dryRunPlan({ service:
|
|
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
|
|
61812
|
-
return planOutcome(inv.deps, dryRunPlan({ service:
|
|
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
|
|
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(), ...
|
|
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,
|
|
62100
|
-
const project = await connectedProject(inv.deps,
|
|
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,
|
|
62104
|
-
const project = await offlineProject(inv.deps,
|
|
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((
|
|
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
|
|
63078
|
-
if (
|
|
63079
|
-
const body2 = { name: name2, targetId:
|
|
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
|
|
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 ||
|
|
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:
|
|
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
|
|
63504
|
-
const token = await requireToken(inv.deps,
|
|
63505
|
-
return { target, call: serviceCall(inv.deps,
|
|
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
|
|
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:
|
|
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,
|
|
64646
|
+
async function send(deps, label2, schema, method, path, body2, dryRun) {
|
|
63770
64647
|
if (dryRun) {
|
|
63771
|
-
const project2 = await offlineProject(deps,
|
|
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,
|
|
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
|
|
64275
|
-
const source = { kind: "patch", diff: diff.text, ...title === void 0 ? {} : { title }, ...
|
|
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
|
|
64705
|
-
if (index === 1 && candidate ===
|
|
64706
|
-
const prefix =
|
|
64707
|
-
const distance = prefix ? 0.5 : editDistance(candidate,
|
|
64708
|
-
const allowed = Math.max(2, Math.floor(
|
|
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
|
|
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
|
|
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
|
|
65025
|
-
import { join as
|
|
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
|
|
65029
|
-
import { join as
|
|
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 =
|
|
65988
|
+
var CI_FILE = join22(".github", "workflows", "ci.yml");
|
|
65037
65989
|
function hasCi(root) {
|
|
65038
65990
|
try {
|
|
65039
|
-
return readdirSync(
|
|
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 =
|
|
65052
|
-
const python =
|
|
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 =
|
|
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(
|
|
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((
|
|
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 =
|
|
65156
|
-
const planExists =
|
|
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(
|
|
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(
|
|
65236
|
-
for (const entry of blockChanges) if (writeIfDifferent(
|
|
65237
|
-
const agentConfig = [
|
|
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) =>
|
|
65240
|
-
const
|
|
65241
|
-
if (
|
|
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 =
|
|
66196
|
+
const connectData = connected9.data !== void 0 && !("dryRun" in connected9.data) ? connected9.data : void 0;
|
|
65245
66197
|
for (const file2 of agentConfig) {
|
|
65246
|
-
if (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
65434
|
-
const inSync = installed?.digest ===
|
|
65435
|
-
say(deps, inSync ? "State: in sync." : `State: update available - run \`overlane release update\` (installed ${installed === null ? "none" : shortDigest(installed.digest)}, pinned ${shortDigest(
|
|
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
|
|
65476
|
-
return installRelease(deps, ctx,
|
|
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
|
|
65770
|
-
import { dirname as
|
|
65771
|
-
var TASK_POLICY_PATH =
|
|
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 =
|
|
65774
|
-
|
|
66725
|
+
const path = join24(root, TASK_POLICY_PATH);
|
|
66726
|
+
mkdirSync9(dirname9(path), { recursive: true });
|
|
65775
66727
|
const temp = `${path}.${process.pid}.tmp`;
|
|
65776
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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" ?
|
|
66766
|
-
return
|
|
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
|
|
68595
|
+
return join29(dataDir(env, home), "marketplace");
|
|
66770
68596
|
}
|
|
66771
68597
|
|
|
66772
68598
|
// src/plugin.ts
|
|
66773
|
-
import { readFileSync as
|
|
66774
|
-
import { join as
|
|
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
|
|
66781
|
-
import { dirname as
|
|
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 =
|
|
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 =
|
|
66789
|
-
const path =
|
|
66790
|
-
if (!
|
|
66791
|
-
const parsed = ReleaseManifest.safeParse(JSON.parse(
|
|
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 (!
|
|
68625
|
+
if (!existsSync8(dir)) return [];
|
|
66800
68626
|
const out2 = [];
|
|
66801
68627
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
66802
|
-
const full =
|
|
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 =
|
|
66815
|
-
if (!
|
|
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(
|
|
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(
|
|
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(
|
|
68654
|
+
function readInstalledManifest(target2) {
|
|
66829
68655
|
try {
|
|
66830
|
-
const parsed = ReleaseManifest.safeParse(JSON.parse(
|
|
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,
|
|
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 = `${
|
|
66853
|
-
const previous = `${
|
|
68678
|
+
const staging = `${target2}.new-${process.pid}`;
|
|
68679
|
+
const previous = `${target2}.old-${process.pid}`;
|
|
66854
68680
|
rmSync4(staging, { recursive: true, force: true });
|
|
66855
|
-
|
|
68681
|
+
mkdirSync10(staging, { recursive: true });
|
|
66856
68682
|
try {
|
|
66857
68683
|
for (const client of CLIENT_DIRS) {
|
|
66858
|
-
if (
|
|
68684
|
+
if (existsSync8(join30(embedded.dir, client))) cpSync(join30(embedded.dir, client), join30(staging, client), { recursive: true });
|
|
66859
68685
|
}
|
|
66860
|
-
|
|
66861
|
-
|
|
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
|
-
|
|
66867
|
-
if (
|
|
68692
|
+
mkdirSync10(dirname11(target2), { recursive: true });
|
|
68693
|
+
if (existsSync8(target2)) renameSync7(target2, previous);
|
|
66868
68694
|
try {
|
|
66869
|
-
|
|
68695
|
+
renameSync7(staging, target2);
|
|
66870
68696
|
} catch (error62) {
|
|
66871
|
-
if (
|
|
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,
|
|
66880
|
-
const installed = readInstalledManifest(
|
|
66881
|
-
const present =
|
|
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,
|
|
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" ?
|
|
66894
|
-
const parsed = JSON.parse(
|
|
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((
|
|
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 ${
|
|
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 =
|
|
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, (
|
|
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
|
|
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) ??
|
|
67167
|
-
return
|
|
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 {
|