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