@osfactory/har 1.2.0 → 1.4.0
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/index.js +1995 -546
- package/dist/templates/agent-skills/factory-line.md +22 -10
- package/dist/templates/agent-skills/har-wt.md +2 -1
- package/dist/templates/agent-skills/skills.manifest.json +25 -9
- package/dist/templates/lines/example-line/.github/workflows/check-manifest.yml +24 -0
- package/dist/templates/lines/example-line/AGENTS.md +48 -0
- package/dist/templates/lines/example-line/README.md +72 -0
- package/dist/templates/lines/example-line/docs/AUTHORING.md +79 -0
- package/dist/templates/lines/example-line/line.json +72 -0
- package/dist/templates/lines/example-line/line.manifest.json +37 -0
- package/dist/templates/lines/example-line/scripts/check-manifest.mjs +103 -0
- package/dist/templates/lines/example-line/stages/example-gate.sh +59 -0
- package/dist/templates/profiles/cli/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/profiles/default/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/profiles/ios/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/shared-docs/readme/session-lifecycle.md +3 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -593,8 +593,8 @@ var init_parseUtil = __esm({
|
|
|
593
593
|
init_errors();
|
|
594
594
|
init_en();
|
|
595
595
|
makeIssue = (params) => {
|
|
596
|
-
const { data, path:
|
|
597
|
-
const fullPath = [...
|
|
596
|
+
const { data, path: path97, errorMaps, issueData } = params;
|
|
597
|
+
const fullPath = [...path97, ...issueData.path || []];
|
|
598
598
|
const fullIssue = {
|
|
599
599
|
...issueData,
|
|
600
600
|
path: fullPath
|
|
@@ -902,11 +902,11 @@ var init_types = __esm({
|
|
|
902
902
|
init_parseUtil();
|
|
903
903
|
init_util();
|
|
904
904
|
ParseInputLazyPath = class {
|
|
905
|
-
constructor(parent, value,
|
|
905
|
+
constructor(parent, value, path97, key) {
|
|
906
906
|
this._cachedPath = [];
|
|
907
907
|
this.parent = parent;
|
|
908
908
|
this.data = value;
|
|
909
|
-
this._path =
|
|
909
|
+
this._path = path97;
|
|
910
910
|
this._key = key;
|
|
911
911
|
}
|
|
912
912
|
get path() {
|
|
@@ -4796,7 +4796,9 @@ var init_schema = __esm({
|
|
|
4796
4796
|
toolCallId: external_exports.string().optional(),
|
|
4797
4797
|
correlationId: external_exports.string().optional(),
|
|
4798
4798
|
workUnitId: WorkUnitIdSchema.optional(),
|
|
4799
|
-
attemptId: WorkAttemptIdSchema.optional()
|
|
4799
|
+
attemptId: WorkAttemptIdSchema.optional(),
|
|
4800
|
+
/** One occupancy of a slot (#316) — records are scoped to it, not to slot id. */
|
|
4801
|
+
occupancyKey: external_exports.string().min(1).optional()
|
|
4800
4802
|
}).passthrough();
|
|
4801
4803
|
AgentTrajectoryRecordSchema = AgentTrajectoryRecordV1Schema;
|
|
4802
4804
|
USAGE_HARVEST_VERSION = 2;
|
|
@@ -4811,6 +4813,8 @@ var init_schema = __esm({
|
|
|
4811
4813
|
suffix: external_exports.string().optional(),
|
|
4812
4814
|
workUnitId: WorkUnitIdSchema.optional(),
|
|
4813
4815
|
attemptId: WorkAttemptIdSchema.optional(),
|
|
4816
|
+
/** One occupancy of a slot (#316) — usage is scoped to it, not to slot id. */
|
|
4817
|
+
occupancyKey: external_exports.string().min(1).optional(),
|
|
4814
4818
|
tokensInput: external_exports.number().nonnegative().default(0),
|
|
4815
4819
|
tokensOutput: external_exports.number().nonnegative().default(0),
|
|
4816
4820
|
tokensCacheRead: external_exports.number().nonnegative().default(0),
|
|
@@ -28632,6 +28636,149 @@ var init_usage_pricing = __esm({
|
|
|
28632
28636
|
}
|
|
28633
28637
|
});
|
|
28634
28638
|
|
|
28639
|
+
// packages/schemas/src/line.ts
|
|
28640
|
+
var LINE_CONTRACT_VERSION, LINE_BUNDLE_KIND, LINE_MANIFEST_FILES, LineIdSchema, StationIdSchema, LineSkillSchema, LineMcpSchema, LineWorkSchema, LineWaveCellSchema, LineStationSchema, LineGateStageSchema, LineGateSchema, LineExtraStageSchema, LineHandoffSchema, LineTravelerSchema, LineProgramSchema, LineManifestFileSchema, LineManifestSchema, LineLedgerEntrySchema, LineLedgerSchema;
|
|
28641
|
+
var init_line = __esm({
|
|
28642
|
+
"packages/schemas/src/line.ts"() {
|
|
28643
|
+
"use strict";
|
|
28644
|
+
init_zod();
|
|
28645
|
+
init_schema();
|
|
28646
|
+
LINE_CONTRACT_VERSION = 1;
|
|
28647
|
+
LINE_BUNDLE_KIND = "line";
|
|
28648
|
+
LINE_MANIFEST_FILES = ["line.manifest.json", "template.manifest.json"];
|
|
28649
|
+
LineIdSchema = external_exports.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/, "Line id must be a lowercase slug (letters, digits, ._-)");
|
|
28650
|
+
StationIdSchema = external_exports.string().min(1).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "Station id must be stable and shell-friendly");
|
|
28651
|
+
LineSkillSchema = external_exports.object({
|
|
28652
|
+
id: external_exports.string().min(1),
|
|
28653
|
+
role: external_exports.enum(["orchestrator", "station"]).default("station"),
|
|
28654
|
+
/** Install hint only — HAR never vendors third-party skill packs. */
|
|
28655
|
+
install: external_exports.string().min(1).optional()
|
|
28656
|
+
});
|
|
28657
|
+
LineMcpSchema = external_exports.object({
|
|
28658
|
+
name: external_exports.string().min(1),
|
|
28659
|
+
why: external_exports.string().min(1).optional(),
|
|
28660
|
+
required: external_exports.boolean().default(false)
|
|
28661
|
+
});
|
|
28662
|
+
LineWorkSchema = external_exports.object({
|
|
28663
|
+
/** `github`, `linear`, `none`, or a free string. */
|
|
28664
|
+
source: external_exports.string().min(1).default("none"),
|
|
28665
|
+
ids: external_exports.array(external_exports.string().min(1)).default([]),
|
|
28666
|
+
url: external_exports.string().min(1).optional(),
|
|
28667
|
+
optional: external_exports.boolean().default(false)
|
|
28668
|
+
});
|
|
28669
|
+
LineWaveCellSchema = external_exports.object({
|
|
28670
|
+
workId: external_exports.string().min(1).optional(),
|
|
28671
|
+
title: external_exports.string().min(1).optional()
|
|
28672
|
+
});
|
|
28673
|
+
LineStationSchema = external_exports.object({
|
|
28674
|
+
id: StationIdSchema,
|
|
28675
|
+
title: external_exports.string().min(1),
|
|
28676
|
+
description: external_exports.string().min(1).optional(),
|
|
28677
|
+
work: LineWorkSchema.optional(),
|
|
28678
|
+
/** Each inner array is one wave; cells inside a wave run in parallel slots. */
|
|
28679
|
+
waves: external_exports.array(external_exports.array(LineWaveCellSchema)).optional(),
|
|
28680
|
+
skills: external_exports.array(external_exports.string().min(1)).optional(),
|
|
28681
|
+
mcp: external_exports.array(external_exports.string().min(1)).optional()
|
|
28682
|
+
});
|
|
28683
|
+
LineGateStageSchema = external_exports.object({
|
|
28684
|
+
id: external_exports.string().min(1),
|
|
28685
|
+
/** Required from this station onward — this is the ratchet tag. */
|
|
28686
|
+
fromStation: StationIdSchema,
|
|
28687
|
+
tier: external_exports.enum(["quick", "full"]).default("full")
|
|
28688
|
+
});
|
|
28689
|
+
LineGateSchema = external_exports.object({
|
|
28690
|
+
/** Must be true: a factory line never drops an earlier station's stages. */
|
|
28691
|
+
cumulative: external_exports.literal(true),
|
|
28692
|
+
/** When set, the gate is skipped unless this env var is "1". */
|
|
28693
|
+
optInEnv: external_exports.string().min(1).nullable().default(null),
|
|
28694
|
+
stages: external_exports.array(LineGateStageSchema).default([])
|
|
28695
|
+
});
|
|
28696
|
+
LineExtraStageSchema = external_exports.object({
|
|
28697
|
+
id: external_exports.string().min(1),
|
|
28698
|
+
kind: HarnessStageKindSchema.default("test"),
|
|
28699
|
+
tier: external_exports.enum(["quick", "full"]).default("full"),
|
|
28700
|
+
description: external_exports.string().min(1).optional()
|
|
28701
|
+
});
|
|
28702
|
+
LineHandoffSchema = external_exports.object({
|
|
28703
|
+
/** Must be false: agents hand off, they do not merge or release. */
|
|
28704
|
+
autonomousShip: external_exports.literal(false),
|
|
28705
|
+
waitFor: external_exports.string().min(1).optional()
|
|
28706
|
+
});
|
|
28707
|
+
LineTravelerSchema = external_exports.object({
|
|
28708
|
+
kind: external_exports.string().min(1),
|
|
28709
|
+
ref: external_exports.string().min(1).optional(),
|
|
28710
|
+
note: external_exports.string().min(1).optional()
|
|
28711
|
+
});
|
|
28712
|
+
LineProgramSchema = external_exports.object({
|
|
28713
|
+
contractVersion: external_exports.literal(LINE_CONTRACT_VERSION),
|
|
28714
|
+
id: LineIdSchema,
|
|
28715
|
+
title: external_exports.string().min(1),
|
|
28716
|
+
description: external_exports.string().min(1).optional(),
|
|
28717
|
+
skills: external_exports.array(LineSkillSchema).default([]),
|
|
28718
|
+
mcp: external_exports.array(LineMcpSchema).default([]),
|
|
28719
|
+
plugins: external_exports.array(external_exports.string().min(1)).default([]),
|
|
28720
|
+
stations: external_exports.array(LineStationSchema).min(1),
|
|
28721
|
+
gate: LineGateSchema,
|
|
28722
|
+
extraStages: external_exports.array(LineExtraStageSchema).default([]),
|
|
28723
|
+
handoff: LineHandoffSchema,
|
|
28724
|
+
traveler: LineTravelerSchema.optional(),
|
|
28725
|
+
prototypeNotes: external_exports.array(external_exports.string().min(1)).default([])
|
|
28726
|
+
}).superRefine((data, ctx) => {
|
|
28727
|
+
const stationIds = new Set(data.stations.map((s) => s.id));
|
|
28728
|
+
if (stationIds.size !== data.stations.length) {
|
|
28729
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "station ids must be unique" });
|
|
28730
|
+
}
|
|
28731
|
+
for (const stage of data.gate.stages) {
|
|
28732
|
+
if (!stationIds.has(stage.fromStation)) {
|
|
28733
|
+
ctx.addIssue({
|
|
28734
|
+
code: external_exports.ZodIssueCode.custom,
|
|
28735
|
+
message: `gate.stages[].fromStation "${stage.fromStation}" is not a station id`
|
|
28736
|
+
});
|
|
28737
|
+
}
|
|
28738
|
+
}
|
|
28739
|
+
});
|
|
28740
|
+
LineManifestFileSchema = external_exports.object({
|
|
28741
|
+
src: external_exports.string().min(1),
|
|
28742
|
+
dest: external_exports.string().min(1),
|
|
28743
|
+
executable: external_exports.boolean().optional()
|
|
28744
|
+
});
|
|
28745
|
+
LineManifestSchema = external_exports.object({
|
|
28746
|
+
kind: external_exports.literal(LINE_BUNDLE_KIND),
|
|
28747
|
+
id: LineIdSchema,
|
|
28748
|
+
title: external_exports.string().min(1).optional(),
|
|
28749
|
+
/** Bundle-relative path to the program JSON. */
|
|
28750
|
+
program: external_exports.string().min(1).default("line.json"),
|
|
28751
|
+
files: external_exports.array(LineManifestFileSchema).default([]),
|
|
28752
|
+
/** Stages to REGISTER in .har/stages.json — never added to verificationStages. */
|
|
28753
|
+
stages: external_exports.array(HarnessStageSchema).default([]),
|
|
28754
|
+
nextSteps: external_exports.array(external_exports.string().min(1)).default([]),
|
|
28755
|
+
docsPath: external_exports.string().min(1).optional()
|
|
28756
|
+
}).passthrough().superRefine((data, ctx) => {
|
|
28757
|
+
const raw = data;
|
|
28758
|
+
if (raw.verificationStages !== void 0) {
|
|
28759
|
+
ctx.addIssue({
|
|
28760
|
+
code: external_exports.ZodIssueCode.custom,
|
|
28761
|
+
message: "Line manifests must not declare verificationStages \u2014 line gate stages are opt-in and run via `har line gate`."
|
|
28762
|
+
});
|
|
28763
|
+
}
|
|
28764
|
+
});
|
|
28765
|
+
LineLedgerEntrySchema = external_exports.object({
|
|
28766
|
+
id: LineIdSchema,
|
|
28767
|
+
source: external_exports.enum(["bundled", "local", "path", "npm", "git"]),
|
|
28768
|
+
spec: external_exports.string().min(1),
|
|
28769
|
+
version: external_exports.string().optional(),
|
|
28770
|
+
/** Stages this line registered (registered only — not on verify). */
|
|
28771
|
+
stageIds: external_exports.array(external_exports.string().min(1)).default([]),
|
|
28772
|
+
programPath: external_exports.string().min(1),
|
|
28773
|
+
installedAt: external_exports.string()
|
|
28774
|
+
});
|
|
28775
|
+
LineLedgerSchema = external_exports.object({
|
|
28776
|
+
version: external_exports.literal("1"),
|
|
28777
|
+
lines: external_exports.array(LineLedgerEntrySchema).default([])
|
|
28778
|
+
});
|
|
28779
|
+
}
|
|
28780
|
+
});
|
|
28781
|
+
|
|
28635
28782
|
// src/harness/schema.ts
|
|
28636
28783
|
var init_schema2 = __esm({
|
|
28637
28784
|
"src/harness/schema.ts"() {
|
|
@@ -28640,6 +28787,7 @@ var init_schema2 = __esm({
|
|
|
28640
28787
|
init_harness_env();
|
|
28641
28788
|
init_usage_authority();
|
|
28642
28789
|
init_usage_pricing();
|
|
28790
|
+
init_line();
|
|
28643
28791
|
}
|
|
28644
28792
|
});
|
|
28645
28793
|
|
|
@@ -29043,7 +29191,7 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
|
29043
29191
|
const stream = options.stream ?? false;
|
|
29044
29192
|
const echoStdout = stream && (options.streamStdout ?? true);
|
|
29045
29193
|
const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
|
|
29046
|
-
return new Promise((
|
|
29194
|
+
return new Promise((resolve66) => {
|
|
29047
29195
|
const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
|
|
29048
29196
|
...spawnOptions,
|
|
29049
29197
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -29058,14 +29206,14 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
|
29058
29206
|
stderr += d;
|
|
29059
29207
|
if (stream) process.stderr.write(d);
|
|
29060
29208
|
});
|
|
29061
|
-
proc.on("close", (code) =>
|
|
29209
|
+
proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
|
|
29062
29210
|
});
|
|
29063
29211
|
}
|
|
29064
29212
|
function runShellCommand(command2, options = {}) {
|
|
29065
29213
|
const stream = options.stream ?? false;
|
|
29066
29214
|
const echoStdout = stream && (options.streamStdout ?? true);
|
|
29067
29215
|
const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
|
|
29068
|
-
return new Promise((
|
|
29216
|
+
return new Promise((resolve66) => {
|
|
29069
29217
|
const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
|
|
29070
29218
|
...spawnOptions,
|
|
29071
29219
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -29080,7 +29228,7 @@ function runShellCommand(command2, options = {}) {
|
|
|
29080
29228
|
stderr += d;
|
|
29081
29229
|
if (stream) process.stderr.write(d);
|
|
29082
29230
|
});
|
|
29083
|
-
proc.on("close", (code) =>
|
|
29231
|
+
proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
|
|
29084
29232
|
});
|
|
29085
29233
|
}
|
|
29086
29234
|
var import_child_process;
|
|
@@ -30071,7 +30219,7 @@ var init_slot_registry = __esm({
|
|
|
30071
30219
|
|
|
30072
30220
|
// src/runtime/exec.ts
|
|
30073
30221
|
function spawnResult(cmd, args, opts, shell) {
|
|
30074
|
-
return new Promise((
|
|
30222
|
+
return new Promise((resolve66) => {
|
|
30075
30223
|
const stream = opts.stream ?? false;
|
|
30076
30224
|
const proc = (0, import_child_process3.spawn)(cmd, args, {
|
|
30077
30225
|
cwd: opts.cwd,
|
|
@@ -30083,8 +30231,8 @@ function spawnResult(cmd, args, opts, shell) {
|
|
|
30083
30231
|
let stderr = "";
|
|
30084
30232
|
proc.stdout?.on("data", (c) => stdout += c.toString());
|
|
30085
30233
|
proc.stderr?.on("data", (c) => stderr += c.toString());
|
|
30086
|
-
proc.on("error", (err) =>
|
|
30087
|
-
proc.on("close", (code) =>
|
|
30234
|
+
proc.on("error", (err) => resolve66({ code: 127, stdout, stderr: stderr || String(err) }));
|
|
30235
|
+
proc.on("close", (code) => resolve66({ code: code ?? 1, stdout, stderr }));
|
|
30088
30236
|
});
|
|
30089
30237
|
}
|
|
30090
30238
|
var import_child_process3, fs13, path13, realSystemOps, defaultExec, stderrLog, realSleep;
|
|
@@ -30147,7 +30295,7 @@ var init_exec = __esm({
|
|
|
30147
30295
|
process.stderr.write(`==> ${message}
|
|
30148
30296
|
`);
|
|
30149
30297
|
};
|
|
30150
|
-
realSleep = (seconds) => new Promise((
|
|
30298
|
+
realSleep = (seconds) => new Promise((resolve66) => setTimeout(resolve66, seconds * 1e3));
|
|
30151
30299
|
}
|
|
30152
30300
|
});
|
|
30153
30301
|
|
|
@@ -32608,6 +32756,13 @@ function findValidation(checkoutDir, treeHash) {
|
|
|
32608
32756
|
return void 0;
|
|
32609
32757
|
}
|
|
32610
32758
|
}
|
|
32759
|
+
function findPassingFullValidation(input) {
|
|
32760
|
+
if (!isCheckoutRoot(input.checkoutDir)) return void 0;
|
|
32761
|
+
const { treeHash } = computeWorktreeSnapshot(input.checkoutDir);
|
|
32762
|
+
const record2 = findValidation(input.checkoutDir, treeHash) ?? (input.harnessRoot ? findValidation(input.harnessRoot, treeHash) : void 0);
|
|
32763
|
+
if (record2?.status === "pass" && record2.full) return record2;
|
|
32764
|
+
return void 0;
|
|
32765
|
+
}
|
|
32611
32766
|
function attachCommit(checkoutDir, treeHash, commitSha) {
|
|
32612
32767
|
const record2 = findValidation(checkoutDir, treeHash);
|
|
32613
32768
|
if (!record2) return void 0;
|
|
@@ -34738,16 +34893,16 @@ var init_local_executor = __esm({
|
|
|
34738
34893
|
const walk = (dir, prefix) => {
|
|
34739
34894
|
for (const entry of fs35.readdirSync(dir, { withFileTypes: true })) {
|
|
34740
34895
|
const full = path36.join(dir, entry.name);
|
|
34741
|
-
const
|
|
34896
|
+
const relative6 = path36.join(prefix, entry.name);
|
|
34742
34897
|
if (entry.isDirectory()) {
|
|
34743
|
-
walk(full,
|
|
34898
|
+
walk(full, relative6);
|
|
34744
34899
|
continue;
|
|
34745
34900
|
}
|
|
34746
|
-
if (filter2?.stageId && !
|
|
34901
|
+
if (filter2?.stageId && !relative6.includes(filter2.stageId)) continue;
|
|
34747
34902
|
const stat = fs35.statSync(full);
|
|
34748
34903
|
entries.push({
|
|
34749
34904
|
path: full,
|
|
34750
|
-
relativePath:
|
|
34905
|
+
relativePath: relative6,
|
|
34751
34906
|
sizeBytes: stat.size,
|
|
34752
34907
|
modifiedAt: stat.mtime.toISOString()
|
|
34753
34908
|
});
|
|
@@ -36663,7 +36818,7 @@ async function attempt(url) {
|
|
|
36663
36818
|
async function readControlJson(url) {
|
|
36664
36819
|
const first = await attempt(url);
|
|
36665
36820
|
if (first.read.ok || !first.retryable) return first.read;
|
|
36666
|
-
await new Promise((
|
|
36821
|
+
await new Promise((resolve66) => setTimeout(resolve66, RETRY_DELAY_MS));
|
|
36667
36822
|
return (await attempt(url)).read;
|
|
36668
36823
|
}
|
|
36669
36824
|
async function readControlPages(options) {
|
|
@@ -37148,7 +37303,7 @@ async function postForResponse(url, headers, payload) {
|
|
|
37148
37303
|
} catch (err) {
|
|
37149
37304
|
lastErr = err;
|
|
37150
37305
|
if (attempt2 < MAX_FETCH_ATTEMPTS) {
|
|
37151
|
-
await new Promise((
|
|
37306
|
+
await new Promise((resolve66) => setTimeout(resolve66, attempt2 * 100));
|
|
37152
37307
|
}
|
|
37153
37308
|
}
|
|
37154
37309
|
}
|
|
@@ -37388,7 +37543,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
|
|
|
37388
37543
|
const deadline = Date.now() + timeoutMs;
|
|
37389
37544
|
while (Date.now() < deadline) {
|
|
37390
37545
|
if (await isControlApiReachable(apiUrl)) return true;
|
|
37391
|
-
await new Promise((
|
|
37546
|
+
await new Promise((resolve66) => setTimeout(resolve66, intervalMs));
|
|
37392
37547
|
}
|
|
37393
37548
|
return false;
|
|
37394
37549
|
}
|
|
@@ -39037,15 +39192,15 @@ var require_route = __commonJS({
|
|
|
39037
39192
|
};
|
|
39038
39193
|
}
|
|
39039
39194
|
function wrapConversion(toModel, graph) {
|
|
39040
|
-
const
|
|
39195
|
+
const path97 = [graph[toModel].parent, toModel];
|
|
39041
39196
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
39042
39197
|
let cur = graph[toModel].parent;
|
|
39043
39198
|
while (graph[cur].parent) {
|
|
39044
|
-
|
|
39199
|
+
path97.unshift(graph[cur].parent);
|
|
39045
39200
|
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
39046
39201
|
cur = graph[cur].parent;
|
|
39047
39202
|
}
|
|
39048
|
-
fn.conversion =
|
|
39203
|
+
fn.conversion = path97;
|
|
39049
39204
|
return fn;
|
|
39050
39205
|
}
|
|
39051
39206
|
module2.exports = function(fromModel) {
|
|
@@ -45539,26 +45694,26 @@ var require_lib2 = __commonJS({
|
|
|
45539
45694
|
return matches;
|
|
45540
45695
|
};
|
|
45541
45696
|
exports2.analyse = analyse;
|
|
45542
|
-
var detectFile = (filepath, opts = {}) => new Promise((
|
|
45697
|
+
var detectFile = (filepath, opts = {}) => new Promise((resolve66, reject) => {
|
|
45543
45698
|
let fd;
|
|
45544
|
-
const
|
|
45699
|
+
const fs79 = (0, node_1.default)();
|
|
45545
45700
|
const handler = (err, buffer) => {
|
|
45546
45701
|
if (fd) {
|
|
45547
|
-
|
|
45702
|
+
fs79.closeSync(fd);
|
|
45548
45703
|
}
|
|
45549
45704
|
if (err) {
|
|
45550
45705
|
reject(err);
|
|
45551
45706
|
} else if (buffer) {
|
|
45552
|
-
|
|
45707
|
+
resolve66((0, exports2.detect)(buffer));
|
|
45553
45708
|
} else {
|
|
45554
45709
|
reject(new Error("No error and no buffer received"));
|
|
45555
45710
|
}
|
|
45556
45711
|
};
|
|
45557
45712
|
const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
|
|
45558
45713
|
if (sampleSize > 0) {
|
|
45559
|
-
fd =
|
|
45714
|
+
fd = fs79.openSync(filepath, "r");
|
|
45560
45715
|
let sample = Buffer.allocUnsafe(sampleSize);
|
|
45561
|
-
|
|
45716
|
+
fs79.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
|
|
45562
45717
|
if (err) {
|
|
45563
45718
|
handler(err, null);
|
|
45564
45719
|
} else {
|
|
@@ -45570,22 +45725,22 @@ var require_lib2 = __commonJS({
|
|
|
45570
45725
|
});
|
|
45571
45726
|
return;
|
|
45572
45727
|
}
|
|
45573
|
-
|
|
45728
|
+
fs79.readFile(filepath, handler);
|
|
45574
45729
|
});
|
|
45575
45730
|
exports2.detectFile = detectFile;
|
|
45576
45731
|
var detectFileSync = (filepath, opts = {}) => {
|
|
45577
|
-
const
|
|
45732
|
+
const fs79 = (0, node_1.default)();
|
|
45578
45733
|
if (opts && opts.sampleSize) {
|
|
45579
|
-
const fd =
|
|
45734
|
+
const fd = fs79.openSync(filepath, "r");
|
|
45580
45735
|
let sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
45581
|
-
const bytesRead =
|
|
45736
|
+
const bytesRead = fs79.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
|
|
45582
45737
|
if (bytesRead < opts.sampleSize) {
|
|
45583
45738
|
sample = sample.subarray(0, bytesRead);
|
|
45584
45739
|
}
|
|
45585
|
-
|
|
45740
|
+
fs79.closeSync(fd);
|
|
45586
45741
|
return (0, exports2.detect)(sample);
|
|
45587
45742
|
}
|
|
45588
|
-
return (0, exports2.detect)(
|
|
45743
|
+
return (0, exports2.detect)(fs79.readFileSync(filepath));
|
|
45589
45744
|
};
|
|
45590
45745
|
exports2.detectFileSync = detectFileSync;
|
|
45591
45746
|
exports2.default = {
|
|
@@ -50098,7 +50253,7 @@ var require_Observable = __commonJS({
|
|
|
50098
50253
|
Observable2.prototype.forEach = function(next, promiseCtor) {
|
|
50099
50254
|
var _this = this;
|
|
50100
50255
|
promiseCtor = getPromiseCtor(promiseCtor);
|
|
50101
|
-
return new promiseCtor(function(
|
|
50256
|
+
return new promiseCtor(function(resolve66, reject) {
|
|
50102
50257
|
var subscriber = new Subscriber_1.SafeSubscriber({
|
|
50103
50258
|
next: function(value) {
|
|
50104
50259
|
try {
|
|
@@ -50109,7 +50264,7 @@ var require_Observable = __commonJS({
|
|
|
50109
50264
|
}
|
|
50110
50265
|
},
|
|
50111
50266
|
error: reject,
|
|
50112
|
-
complete:
|
|
50267
|
+
complete: resolve66
|
|
50113
50268
|
});
|
|
50114
50269
|
_this.subscribe(subscriber);
|
|
50115
50270
|
});
|
|
@@ -50131,14 +50286,14 @@ var require_Observable = __commonJS({
|
|
|
50131
50286
|
Observable2.prototype.toPromise = function(promiseCtor) {
|
|
50132
50287
|
var _this = this;
|
|
50133
50288
|
promiseCtor = getPromiseCtor(promiseCtor);
|
|
50134
|
-
return new promiseCtor(function(
|
|
50289
|
+
return new promiseCtor(function(resolve66, reject) {
|
|
50135
50290
|
var value;
|
|
50136
50291
|
_this.subscribe(function(x) {
|
|
50137
50292
|
return value = x;
|
|
50138
50293
|
}, function(err) {
|
|
50139
50294
|
return reject(err);
|
|
50140
50295
|
}, function() {
|
|
50141
|
-
return
|
|
50296
|
+
return resolve66(value);
|
|
50142
50297
|
});
|
|
50143
50298
|
});
|
|
50144
50299
|
};
|
|
@@ -52234,11 +52389,11 @@ var require_innerFrom = __commonJS({
|
|
|
52234
52389
|
"use strict";
|
|
52235
52390
|
var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P2, generator) {
|
|
52236
52391
|
function adopt(value) {
|
|
52237
|
-
return value instanceof P2 ? value : new P2(function(
|
|
52238
|
-
|
|
52392
|
+
return value instanceof P2 ? value : new P2(function(resolve66) {
|
|
52393
|
+
resolve66(value);
|
|
52239
52394
|
});
|
|
52240
52395
|
}
|
|
52241
|
-
return new (P2 || (P2 = Promise))(function(
|
|
52396
|
+
return new (P2 || (P2 = Promise))(function(resolve66, reject) {
|
|
52242
52397
|
function fulfilled(value) {
|
|
52243
52398
|
try {
|
|
52244
52399
|
step(generator.next(value));
|
|
@@ -52254,7 +52409,7 @@ var require_innerFrom = __commonJS({
|
|
|
52254
52409
|
}
|
|
52255
52410
|
}
|
|
52256
52411
|
function step(result) {
|
|
52257
|
-
result.done ?
|
|
52412
|
+
result.done ? resolve66(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
52258
52413
|
}
|
|
52259
52414
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
52260
52415
|
});
|
|
@@ -52336,14 +52491,14 @@ var require_innerFrom = __commonJS({
|
|
|
52336
52491
|
}, i);
|
|
52337
52492
|
function verb(n) {
|
|
52338
52493
|
i[n] = o[n] && function(v2) {
|
|
52339
|
-
return new Promise(function(
|
|
52340
|
-
v2 = o[n](v2), settle(
|
|
52494
|
+
return new Promise(function(resolve66, reject) {
|
|
52495
|
+
v2 = o[n](v2), settle(resolve66, reject, v2.done, v2.value);
|
|
52341
52496
|
});
|
|
52342
52497
|
};
|
|
52343
52498
|
}
|
|
52344
|
-
function settle(
|
|
52499
|
+
function settle(resolve66, reject, d, v2) {
|
|
52345
52500
|
Promise.resolve(v2).then(function(v3) {
|
|
52346
|
-
|
|
52501
|
+
resolve66({ value: v3, done: d });
|
|
52347
52502
|
}, reject);
|
|
52348
52503
|
}
|
|
52349
52504
|
};
|
|
@@ -52962,7 +53117,7 @@ var require_lastValueFrom = __commonJS({
|
|
|
52962
53117
|
var EmptyError_1 = require_EmptyError();
|
|
52963
53118
|
function lastValueFrom2(source, config2) {
|
|
52964
53119
|
var hasConfig = typeof config2 === "object";
|
|
52965
|
-
return new Promise(function(
|
|
53120
|
+
return new Promise(function(resolve66, reject) {
|
|
52966
53121
|
var _hasValue = false;
|
|
52967
53122
|
var _value;
|
|
52968
53123
|
source.subscribe({
|
|
@@ -52973,9 +53128,9 @@ var require_lastValueFrom = __commonJS({
|
|
|
52973
53128
|
error: reject,
|
|
52974
53129
|
complete: function() {
|
|
52975
53130
|
if (_hasValue) {
|
|
52976
|
-
|
|
53131
|
+
resolve66(_value);
|
|
52977
53132
|
} else if (hasConfig) {
|
|
52978
|
-
|
|
53133
|
+
resolve66(config2.defaultValue);
|
|
52979
53134
|
} else {
|
|
52980
53135
|
reject(new EmptyError_1.EmptyError());
|
|
52981
53136
|
}
|
|
@@ -52997,16 +53152,16 @@ var require_firstValueFrom = __commonJS({
|
|
|
52997
53152
|
var Subscriber_1 = require_Subscriber();
|
|
52998
53153
|
function firstValueFrom(source, config2) {
|
|
52999
53154
|
var hasConfig = typeof config2 === "object";
|
|
53000
|
-
return new Promise(function(
|
|
53155
|
+
return new Promise(function(resolve66, reject) {
|
|
53001
53156
|
var subscriber = new Subscriber_1.SafeSubscriber({
|
|
53002
53157
|
next: function(value) {
|
|
53003
|
-
|
|
53158
|
+
resolve66(value);
|
|
53004
53159
|
subscriber.unsubscribe();
|
|
53005
53160
|
},
|
|
53006
53161
|
error: reject,
|
|
53007
53162
|
complete: function() {
|
|
53008
53163
|
if (hasConfig) {
|
|
53009
|
-
|
|
53164
|
+
resolve66(config2.defaultValue);
|
|
53010
53165
|
} else {
|
|
53011
53166
|
reject(new EmptyError_1.EmptyError());
|
|
53012
53167
|
}
|
|
@@ -58798,14 +58953,14 @@ var require_run_async = __commonJS({
|
|
|
58798
58953
|
return function() {
|
|
58799
58954
|
var args = arguments;
|
|
58800
58955
|
var originalThis = this;
|
|
58801
|
-
var promise = new Promise(function(
|
|
58956
|
+
var promise = new Promise(function(resolve66, reject) {
|
|
58802
58957
|
var resolved = false;
|
|
58803
58958
|
const wrappedResolve = function(value) {
|
|
58804
58959
|
if (resolved) {
|
|
58805
58960
|
console.warn("Run-async promise already resolved.");
|
|
58806
58961
|
}
|
|
58807
58962
|
resolved = true;
|
|
58808
|
-
|
|
58963
|
+
resolve66(value);
|
|
58809
58964
|
};
|
|
58810
58965
|
var rejected = false;
|
|
58811
58966
|
const wrappedReject = function(value) {
|
|
@@ -58913,32 +59068,32 @@ __export(otel_hooks_exports, {
|
|
|
58913
59068
|
});
|
|
58914
59069
|
function getOtelHooksHome() {
|
|
58915
59070
|
if (process.env.HAR_OTEL_HOOKS_HOME) {
|
|
58916
|
-
return
|
|
59071
|
+
return path76.resolve(process.env.HAR_OTEL_HOOKS_HOME);
|
|
58917
59072
|
}
|
|
58918
|
-
return
|
|
59073
|
+
return path76.join(os22.homedir(), ".har", "otel-hooks");
|
|
58919
59074
|
}
|
|
58920
59075
|
function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
|
|
58921
|
-
return
|
|
59076
|
+
return path76.join(hooksHome, "otel_config.json");
|
|
58922
59077
|
}
|
|
58923
59078
|
function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
|
|
58924
|
-
return
|
|
59079
|
+
return path76.join(hooksHome, "run-otel-hook.sh");
|
|
58925
59080
|
}
|
|
58926
59081
|
function getOtelHooksStateDir(hooksHome = getOtelHooksHome()) {
|
|
58927
|
-
return
|
|
59082
|
+
return path76.join(hooksHome, "state");
|
|
58928
59083
|
}
|
|
58929
59084
|
function localOtelHookBin(hooksHome) {
|
|
58930
|
-
return
|
|
59085
|
+
return path76.join(hooksHome, "node_modules", ".bin", "otel-hook");
|
|
58931
59086
|
}
|
|
58932
59087
|
function installedPackageVersion(hooksHome) {
|
|
58933
59088
|
try {
|
|
58934
|
-
const pkgPath =
|
|
59089
|
+
const pkgPath = path76.join(
|
|
58935
59090
|
hooksHome,
|
|
58936
59091
|
"node_modules",
|
|
58937
59092
|
"@osfactory",
|
|
58938
59093
|
"otel-hook",
|
|
58939
59094
|
"package.json"
|
|
58940
59095
|
);
|
|
58941
|
-
const pkg = JSON.parse(
|
|
59096
|
+
const pkg = JSON.parse(fs68.readFileSync(pkgPath, "utf8"));
|
|
58942
59097
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
58943
59098
|
} catch {
|
|
58944
59099
|
return null;
|
|
@@ -58989,15 +59144,15 @@ function buildOtelHooksConfig(options) {
|
|
|
58989
59144
|
};
|
|
58990
59145
|
}
|
|
58991
59146
|
function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
|
|
58992
|
-
|
|
59147
|
+
fs68.mkdirSync(hooksHome, { recursive: true });
|
|
58993
59148
|
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
58994
|
-
|
|
59149
|
+
fs68.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
|
|
58995
59150
|
`);
|
|
58996
59151
|
return configPath;
|
|
58997
59152
|
}
|
|
58998
59153
|
function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
|
|
58999
|
-
|
|
59000
|
-
|
|
59154
|
+
fs68.mkdirSync(hooksHome, { recursive: true });
|
|
59155
|
+
fs68.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
|
|
59001
59156
|
const wrapperPath = getOtelHooksWrapperPath(hooksHome);
|
|
59002
59157
|
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
59003
59158
|
const stateDir = getOtelHooksStateDir(hooksHome);
|
|
@@ -59009,9 +59164,9 @@ exec "${otelHookCommand}" run \\
|
|
|
59009
59164
|
--state-dir "${stateDir}" \\
|
|
59010
59165
|
"$@"
|
|
59011
59166
|
`;
|
|
59012
|
-
|
|
59167
|
+
fs68.writeFileSync(wrapperPath, script, { mode: 493 });
|
|
59013
59168
|
try {
|
|
59014
|
-
|
|
59169
|
+
fs68.chmodSync(wrapperPath, 493);
|
|
59015
59170
|
} catch {
|
|
59016
59171
|
}
|
|
59017
59172
|
return wrapperPath;
|
|
@@ -59024,7 +59179,7 @@ function commandExists3(command2) {
|
|
|
59024
59179
|
}
|
|
59025
59180
|
function resolveOtelHookCommand(hooksHome) {
|
|
59026
59181
|
const local = localOtelHookBin(hooksHome);
|
|
59027
|
-
if (
|
|
59182
|
+
if (fs68.existsSync(local)) return local;
|
|
59028
59183
|
return null;
|
|
59029
59184
|
}
|
|
59030
59185
|
function resolveHooksInstaller(hooksHome) {
|
|
@@ -59039,7 +59194,7 @@ function resolveHooksInstaller(hooksHome) {
|
|
|
59039
59194
|
return candidates.find((candidate) => commandExists3(candidate.command)) ?? null;
|
|
59040
59195
|
}
|
|
59041
59196
|
function tryInstallPackage(hooksHome) {
|
|
59042
|
-
|
|
59197
|
+
fs68.mkdirSync(hooksHome, { recursive: true });
|
|
59043
59198
|
const installer = resolveHooksInstaller(hooksHome);
|
|
59044
59199
|
if (!installer) {
|
|
59045
59200
|
return {
|
|
@@ -59052,7 +59207,7 @@ function tryInstallPackage(hooksHome) {
|
|
|
59052
59207
|
encoding: "utf8",
|
|
59053
59208
|
timeout: 18e4
|
|
59054
59209
|
});
|
|
59055
|
-
if (result.status === 0 &&
|
|
59210
|
+
if (result.status === 0 && fs68.existsSync(localOtelHookBin(hooksHome))) {
|
|
59056
59211
|
return { ok: true, detail: described };
|
|
59057
59212
|
}
|
|
59058
59213
|
return {
|
|
@@ -59078,14 +59233,14 @@ function uninstallLegacyPythonHooks(hooksHome) {
|
|
|
59078
59233
|
}
|
|
59079
59234
|
function removeLegacyPythonArtifacts(hooksHome) {
|
|
59080
59235
|
const legacyPaths = [
|
|
59081
|
-
|
|
59082
|
-
|
|
59083
|
-
|
|
59236
|
+
path76.join(hooksHome, ".venv"),
|
|
59237
|
+
path76.join(hooksHome, "otel_config.yaml"),
|
|
59238
|
+
path76.join(hooksHome, "venv")
|
|
59084
59239
|
];
|
|
59085
59240
|
for (const target of legacyPaths) {
|
|
59086
59241
|
try {
|
|
59087
|
-
if (
|
|
59088
|
-
|
|
59242
|
+
if (fs68.existsSync(target)) {
|
|
59243
|
+
fs68.rmSync(target, { recursive: true, force: true });
|
|
59089
59244
|
}
|
|
59090
59245
|
} catch {
|
|
59091
59246
|
}
|
|
@@ -59126,10 +59281,10 @@ function runSetupProvider(providerId, otelHookCommand, wrapperPath) {
|
|
|
59126
59281
|
};
|
|
59127
59282
|
}
|
|
59128
59283
|
function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
59129
|
-
if (!
|
|
59284
|
+
if (!fs68.existsSync(filePath)) return false;
|
|
59130
59285
|
let raw;
|
|
59131
59286
|
try {
|
|
59132
|
-
raw =
|
|
59287
|
+
raw = fs68.readFileSync(filePath, "utf8");
|
|
59133
59288
|
} catch {
|
|
59134
59289
|
return false;
|
|
59135
59290
|
}
|
|
@@ -59176,15 +59331,15 @@ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
|
59176
59331
|
return changed;
|
|
59177
59332
|
};
|
|
59178
59333
|
if (!walk(parsed)) return false;
|
|
59179
|
-
|
|
59334
|
+
fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
59180
59335
|
`);
|
|
59181
59336
|
return true;
|
|
59182
59337
|
}
|
|
59183
59338
|
function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
|
|
59184
|
-
if (!
|
|
59339
|
+
if (!fs68.existsSync(filePath)) return false;
|
|
59185
59340
|
let raw;
|
|
59186
59341
|
try {
|
|
59187
|
-
raw =
|
|
59342
|
+
raw = fs68.readFileSync(filePath, "utf8");
|
|
59188
59343
|
} catch {
|
|
59189
59344
|
return false;
|
|
59190
59345
|
}
|
|
@@ -59219,21 +59374,21 @@ function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
|
|
|
59219
59374
|
}
|
|
59220
59375
|
}
|
|
59221
59376
|
if (!changed) return false;
|
|
59222
|
-
|
|
59377
|
+
fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
59223
59378
|
`);
|
|
59224
59379
|
return true;
|
|
59225
59380
|
}
|
|
59226
59381
|
function rewriteKnownHookRegistrations(wrapperPath) {
|
|
59227
59382
|
const home = os22.homedir();
|
|
59228
59383
|
const candidates = [
|
|
59229
|
-
|
|
59230
|
-
|
|
59231
|
-
|
|
59384
|
+
path76.join(home, ".cursor", "hooks.json"),
|
|
59385
|
+
path76.join(home, ".claude", "settings.json"),
|
|
59386
|
+
path76.join(home, ".codex", "hooks.json")
|
|
59232
59387
|
];
|
|
59233
59388
|
for (const filePath of candidates) {
|
|
59234
59389
|
try {
|
|
59235
59390
|
rewriteHookCommandsToWrapper(filePath, wrapperPath);
|
|
59236
|
-
if (filePath.endsWith(`${
|
|
59391
|
+
if (filePath.endsWith(`${path76.sep}.cursor${path76.sep}hooks.json`)) {
|
|
59237
59392
|
pruneLegacyCursorHookEvents(filePath, wrapperPath);
|
|
59238
59393
|
}
|
|
59239
59394
|
} catch {
|
|
@@ -59249,7 +59404,7 @@ function ensureOtelHooks(options) {
|
|
|
59249
59404
|
const hooksHome = getOtelHooksHome();
|
|
59250
59405
|
const errors = [];
|
|
59251
59406
|
const providersSetup = [];
|
|
59252
|
-
|
|
59407
|
+
fs68.mkdirSync(hooksHome, { recursive: true });
|
|
59253
59408
|
let otelHookCommand = resolveOtelHookCommand(hooksHome);
|
|
59254
59409
|
const needsInstall = !otelHookCommand || installedPackageVersion(hooksHome) !== pinnedVersion();
|
|
59255
59410
|
if (needsInstall) {
|
|
@@ -59343,14 +59498,14 @@ function ensureOtelHooks(options) {
|
|
|
59343
59498
|
function disableOtelHooksExport() {
|
|
59344
59499
|
return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
|
|
59345
59500
|
}
|
|
59346
|
-
var import_child_process19,
|
|
59501
|
+
var import_child_process19, fs68, os22, path76, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
|
|
59347
59502
|
var init_otel_hooks = __esm({
|
|
59348
59503
|
"src/core/otel-hooks.ts"() {
|
|
59349
59504
|
"use strict";
|
|
59350
59505
|
import_child_process19 = require("child_process");
|
|
59351
|
-
|
|
59506
|
+
fs68 = __toESM(require("fs"));
|
|
59352
59507
|
os22 = __toESM(require("os"));
|
|
59353
|
-
|
|
59508
|
+
path76 = __toESM(require("path"));
|
|
59354
59509
|
init_control_config();
|
|
59355
59510
|
init_telemetry_config();
|
|
59356
59511
|
OTEL_HOOKS_PACKAGE = "@osfactory/otel-hook@0.2.0";
|
|
@@ -62325,7 +62480,7 @@ var require_compile = __commonJS({
|
|
|
62325
62480
|
const schOrFunc = root.refs[ref];
|
|
62326
62481
|
if (schOrFunc)
|
|
62327
62482
|
return schOrFunc;
|
|
62328
|
-
let _sch =
|
|
62483
|
+
let _sch = resolve66.call(this, root, ref);
|
|
62329
62484
|
if (_sch === void 0) {
|
|
62330
62485
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
62331
62486
|
const { schemaId } = this.opts;
|
|
@@ -62352,7 +62507,7 @@ var require_compile = __commonJS({
|
|
|
62352
62507
|
function sameSchemaEnv(s1, s2) {
|
|
62353
62508
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
62354
62509
|
}
|
|
62355
|
-
function
|
|
62510
|
+
function resolve66(root, ref) {
|
|
62356
62511
|
let sch;
|
|
62357
62512
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
62358
62513
|
ref = sch;
|
|
@@ -62570,8 +62725,8 @@ var require_utils2 = __commonJS({
|
|
|
62570
62725
|
}
|
|
62571
62726
|
return ind;
|
|
62572
62727
|
}
|
|
62573
|
-
function removeDotSegments(
|
|
62574
|
-
let input =
|
|
62728
|
+
function removeDotSegments(path97) {
|
|
62729
|
+
let input = path97;
|
|
62575
62730
|
const output = [];
|
|
62576
62731
|
let nextSlash = -1;
|
|
62577
62732
|
let len = 0;
|
|
@@ -62823,8 +62978,8 @@ var require_schemes = __commonJS({
|
|
|
62823
62978
|
wsComponent.secure = void 0;
|
|
62824
62979
|
}
|
|
62825
62980
|
if (wsComponent.resourceName) {
|
|
62826
|
-
const [
|
|
62827
|
-
wsComponent.path =
|
|
62981
|
+
const [path97, query] = wsComponent.resourceName.split("?");
|
|
62982
|
+
wsComponent.path = path97 && path97 !== "/" ? path97 : void 0;
|
|
62828
62983
|
wsComponent.query = query;
|
|
62829
62984
|
wsComponent.resourceName = void 0;
|
|
62830
62985
|
}
|
|
@@ -62983,55 +63138,55 @@ var require_fast_uri = __commonJS({
|
|
|
62983
63138
|
}
|
|
62984
63139
|
return uri;
|
|
62985
63140
|
}
|
|
62986
|
-
function
|
|
63141
|
+
function resolve66(baseURI, relativeURI, options) {
|
|
62987
63142
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
62988
63143
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
62989
63144
|
schemelessOptions.skipEscape = true;
|
|
62990
63145
|
return serialize(resolved, schemelessOptions);
|
|
62991
63146
|
}
|
|
62992
|
-
function resolveComponent(base,
|
|
63147
|
+
function resolveComponent(base, relative6, options, skipNormalization) {
|
|
62993
63148
|
const target = {};
|
|
62994
63149
|
if (!skipNormalization) {
|
|
62995
63150
|
base = parse4(serialize(base, options), options);
|
|
62996
|
-
|
|
63151
|
+
relative6 = parse4(serialize(relative6, options), options);
|
|
62997
63152
|
}
|
|
62998
63153
|
options = options || {};
|
|
62999
|
-
if (!options.tolerant &&
|
|
63000
|
-
target.scheme =
|
|
63001
|
-
target.userinfo =
|
|
63002
|
-
target.host =
|
|
63003
|
-
target.port =
|
|
63004
|
-
target.path = removeDotSegments(
|
|
63005
|
-
target.query =
|
|
63154
|
+
if (!options.tolerant && relative6.scheme) {
|
|
63155
|
+
target.scheme = relative6.scheme;
|
|
63156
|
+
target.userinfo = relative6.userinfo;
|
|
63157
|
+
target.host = relative6.host;
|
|
63158
|
+
target.port = relative6.port;
|
|
63159
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
63160
|
+
target.query = relative6.query;
|
|
63006
63161
|
} else {
|
|
63007
|
-
if (
|
|
63008
|
-
target.userinfo =
|
|
63009
|
-
target.host =
|
|
63010
|
-
target.port =
|
|
63011
|
-
target.path = removeDotSegments(
|
|
63012
|
-
target.query =
|
|
63162
|
+
if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
|
|
63163
|
+
target.userinfo = relative6.userinfo;
|
|
63164
|
+
target.host = relative6.host;
|
|
63165
|
+
target.port = relative6.port;
|
|
63166
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
63167
|
+
target.query = relative6.query;
|
|
63013
63168
|
} else {
|
|
63014
|
-
if (!
|
|
63169
|
+
if (!relative6.path) {
|
|
63015
63170
|
target.path = base.path;
|
|
63016
|
-
if (
|
|
63017
|
-
target.query =
|
|
63171
|
+
if (relative6.query !== void 0) {
|
|
63172
|
+
target.query = relative6.query;
|
|
63018
63173
|
} else {
|
|
63019
63174
|
target.query = base.query;
|
|
63020
63175
|
}
|
|
63021
63176
|
} else {
|
|
63022
|
-
if (
|
|
63023
|
-
target.path = removeDotSegments(
|
|
63177
|
+
if (relative6.path[0] === "/") {
|
|
63178
|
+
target.path = removeDotSegments(relative6.path);
|
|
63024
63179
|
} else {
|
|
63025
63180
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
63026
|
-
target.path = "/" +
|
|
63181
|
+
target.path = "/" + relative6.path;
|
|
63027
63182
|
} else if (!base.path) {
|
|
63028
|
-
target.path =
|
|
63183
|
+
target.path = relative6.path;
|
|
63029
63184
|
} else {
|
|
63030
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
63185
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
|
|
63031
63186
|
}
|
|
63032
63187
|
target.path = removeDotSegments(target.path);
|
|
63033
63188
|
}
|
|
63034
|
-
target.query =
|
|
63189
|
+
target.query = relative6.query;
|
|
63035
63190
|
}
|
|
63036
63191
|
target.userinfo = base.userinfo;
|
|
63037
63192
|
target.host = base.host;
|
|
@@ -63039,7 +63194,7 @@ var require_fast_uri = __commonJS({
|
|
|
63039
63194
|
}
|
|
63040
63195
|
target.scheme = base.scheme;
|
|
63041
63196
|
}
|
|
63042
|
-
target.fragment =
|
|
63197
|
+
target.fragment = relative6.fragment;
|
|
63043
63198
|
return target;
|
|
63044
63199
|
}
|
|
63045
63200
|
function equal(uriA, uriB, options) {
|
|
@@ -63241,7 +63396,7 @@ var require_fast_uri = __commonJS({
|
|
|
63241
63396
|
var fastUri = {
|
|
63242
63397
|
SCHEMES,
|
|
63243
63398
|
normalize: normalize3,
|
|
63244
|
-
resolve:
|
|
63399
|
+
resolve: resolve66,
|
|
63245
63400
|
resolveComponent,
|
|
63246
63401
|
equal,
|
|
63247
63402
|
serialize,
|
|
@@ -66217,12 +66372,12 @@ var require_dist = __commonJS({
|
|
|
66217
66372
|
throw new Error(`Unknown format "${name}"`);
|
|
66218
66373
|
return f;
|
|
66219
66374
|
};
|
|
66220
|
-
function addFormats(ajv, list,
|
|
66375
|
+
function addFormats(ajv, list, fs79, exportName) {
|
|
66221
66376
|
var _a2;
|
|
66222
66377
|
var _b2;
|
|
66223
66378
|
(_a2 = (_b2 = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
66224
66379
|
for (const f of list)
|
|
66225
|
-
ajv.addFormat(f,
|
|
66380
|
+
ajv.addFormat(f, fs79[f]);
|
|
66226
66381
|
}
|
|
66227
66382
|
module2.exports = exports2 = formatsPlugin;
|
|
66228
66383
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -67506,11 +67661,11 @@ var parser = new YargsParser({
|
|
|
67506
67661
|
resolve: import_path2.resolve,
|
|
67507
67662
|
// TODO: figure out a way to combine ESM and CJS coverage, such that
|
|
67508
67663
|
// we can exercise all the lines below:
|
|
67509
|
-
require: (
|
|
67664
|
+
require: (path97) => {
|
|
67510
67665
|
if (typeof require !== "undefined") {
|
|
67511
|
-
return require(
|
|
67512
|
-
} else if (
|
|
67513
|
-
return JSON.parse((0, import_fs2.readFileSync)(
|
|
67666
|
+
return require(path97);
|
|
67667
|
+
} else if (path97.match(/\.json$/)) {
|
|
67668
|
+
return JSON.parse((0, import_fs2.readFileSync)(path97, "utf8"));
|
|
67514
67669
|
} else {
|
|
67515
67670
|
throw Error("only .json config files are supported in ESM");
|
|
67516
67671
|
}
|
|
@@ -70036,12 +70191,12 @@ var YargsInstance = class {
|
|
|
70036
70191
|
async getCompletion(args, done) {
|
|
70037
70192
|
argsert("<array> [function]", [args, done], arguments.length);
|
|
70038
70193
|
if (!done) {
|
|
70039
|
-
return new Promise((
|
|
70194
|
+
return new Promise((resolve66, reject) => {
|
|
70040
70195
|
__classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
|
|
70041
70196
|
if (err)
|
|
70042
70197
|
reject(err);
|
|
70043
70198
|
else
|
|
70044
|
-
|
|
70199
|
+
resolve66(completions);
|
|
70045
70200
|
});
|
|
70046
70201
|
});
|
|
70047
70202
|
} else {
|
|
@@ -71291,16 +71446,16 @@ async function handleAgentSkills(options) {
|
|
|
71291
71446
|
}
|
|
71292
71447
|
async function askYesNo(question) {
|
|
71293
71448
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
71294
|
-
return new Promise((
|
|
71449
|
+
return new Promise((resolve66) => {
|
|
71295
71450
|
process.stderr.write(`${question} `);
|
|
71296
71451
|
rl.once("line", (answer) => {
|
|
71297
71452
|
rl.close();
|
|
71298
71453
|
const trimmed = answer.trim();
|
|
71299
71454
|
if (trimmed === "") {
|
|
71300
|
-
|
|
71455
|
+
resolve66(true);
|
|
71301
71456
|
return;
|
|
71302
71457
|
}
|
|
71303
|
-
|
|
71458
|
+
resolve66(/^y(es)?$/i.test(trimmed));
|
|
71304
71459
|
});
|
|
71305
71460
|
});
|
|
71306
71461
|
}
|
|
@@ -71380,8 +71535,8 @@ function handleRemove(argv) {
|
|
|
71380
71535
|
}
|
|
71381
71536
|
|
|
71382
71537
|
// src/cli/commands/env.ts
|
|
71383
|
-
var
|
|
71384
|
-
var
|
|
71538
|
+
var fs71 = __toESM(require("fs"));
|
|
71539
|
+
var path80 = __toESM(require("path"));
|
|
71385
71540
|
|
|
71386
71541
|
// src/cli/finish-command.ts
|
|
71387
71542
|
init_sync_context();
|
|
@@ -71391,8 +71546,8 @@ async function finishCommand(code) {
|
|
|
71391
71546
|
}
|
|
71392
71547
|
|
|
71393
71548
|
// src/core/harness.ts
|
|
71394
|
-
var
|
|
71395
|
-
var
|
|
71549
|
+
var fs65 = __toESM(require("fs"));
|
|
71550
|
+
var path72 = __toESM(require("path"));
|
|
71396
71551
|
|
|
71397
71552
|
// src/harness/generator.ts
|
|
71398
71553
|
var fs48 = __toESM(require("fs"));
|
|
@@ -72901,13 +73056,13 @@ var PromisePolyfill = class extends Promise {
|
|
|
72901
73056
|
// Available starting from Node 22
|
|
72902
73057
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
|
|
72903
73058
|
static withResolver() {
|
|
72904
|
-
let
|
|
73059
|
+
let resolve66;
|
|
72905
73060
|
let reject;
|
|
72906
73061
|
const promise = new Promise((res, rej) => {
|
|
72907
|
-
|
|
73062
|
+
resolve66 = res;
|
|
72908
73063
|
reject = rej;
|
|
72909
73064
|
});
|
|
72910
|
-
return { promise, resolve:
|
|
73065
|
+
return { promise, resolve: resolve66, reject };
|
|
72911
73066
|
}
|
|
72912
73067
|
};
|
|
72913
73068
|
|
|
@@ -72941,7 +73096,7 @@ function createPrompt(view) {
|
|
|
72941
73096
|
output
|
|
72942
73097
|
});
|
|
72943
73098
|
const screen = new ScreenManager(rl);
|
|
72944
|
-
const { promise, resolve:
|
|
73099
|
+
const { promise, resolve: resolve66, reject } = PromisePolyfill.withResolver();
|
|
72945
73100
|
const cancel = () => reject(new CancelPromptError());
|
|
72946
73101
|
if (signal) {
|
|
72947
73102
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
@@ -72968,7 +73123,7 @@ function createPrompt(view) {
|
|
|
72968
73123
|
cycle(() => {
|
|
72969
73124
|
try {
|
|
72970
73125
|
const nextView = view(config2, (value) => {
|
|
72971
|
-
setImmediate(() =>
|
|
73126
|
+
setImmediate(() => resolve66(value));
|
|
72972
73127
|
});
|
|
72973
73128
|
if (nextView === void 0) {
|
|
72974
73129
|
const callerFilename = callSites[1]?.getFileName();
|
|
@@ -74277,9 +74432,9 @@ var import_rxjs = __toESM(require_cjs(), 1);
|
|
|
74277
74432
|
var import_run_async = __toESM(require_run_async(), 1);
|
|
74278
74433
|
var import_mute_stream2 = __toESM(require_lib(), 1);
|
|
74279
74434
|
var _ = {
|
|
74280
|
-
set: (obj,
|
|
74435
|
+
set: (obj, path97 = "", value) => {
|
|
74281
74436
|
let pointer = obj;
|
|
74282
|
-
|
|
74437
|
+
path97.split(".").forEach((key, index, arr) => {
|
|
74283
74438
|
if (key === "__proto__" || key === "constructor")
|
|
74284
74439
|
return;
|
|
74285
74440
|
if (index === arr.length - 1) {
|
|
@@ -74290,8 +74445,8 @@ var _ = {
|
|
|
74290
74445
|
pointer = pointer[key];
|
|
74291
74446
|
});
|
|
74292
74447
|
},
|
|
74293
|
-
get: (obj,
|
|
74294
|
-
const travel = (regexp) => String.prototype.split.call(
|
|
74448
|
+
get: (obj, path97 = "", defaultValue) => {
|
|
74449
|
+
const travel = (regexp) => String.prototype.split.call(path97, regexp).filter(Boolean).reduce(
|
|
74295
74450
|
// @ts-expect-error implicit any on res[key]
|
|
74296
74451
|
(res, key) => res == null ? res : res[key],
|
|
74297
74452
|
obj
|
|
@@ -74409,7 +74564,7 @@ var PromptsRunner = class {
|
|
|
74409
74564
|
throw new Error(`Prompt for type ${question.type} not found`);
|
|
74410
74565
|
}
|
|
74411
74566
|
let cleanupSignal;
|
|
74412
|
-
const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((
|
|
74567
|
+
const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((resolve66, reject) => {
|
|
74413
74568
|
const { signal: signal2 } = opt;
|
|
74414
74569
|
if (signal2.aborted) {
|
|
74415
74570
|
reject(new AbortPromptError({ cause: signal2.reason }));
|
|
@@ -74446,7 +74601,7 @@ var PromptsRunner = class {
|
|
|
74446
74601
|
signal2.removeEventListener("abort", abort);
|
|
74447
74602
|
cleanupSignal = void 0;
|
|
74448
74603
|
};
|
|
74449
|
-
activePrompt.run().then(
|
|
74604
|
+
activePrompt.run().then(resolve66, reject).finally(cleanup);
|
|
74450
74605
|
}) : prompt2;
|
|
74451
74606
|
let cleanupModuleSignal;
|
|
74452
74607
|
const { signal: moduleSignal } = this.opt;
|
|
@@ -75418,15 +75573,21 @@ function harnessExists(repoPath) {
|
|
|
75418
75573
|
return fs53.existsSync(`${repoPath}/.har/stages.json`);
|
|
75419
75574
|
}
|
|
75420
75575
|
|
|
75421
|
-
// src/harness/
|
|
75576
|
+
// src/harness/bundle-resolve.ts
|
|
75422
75577
|
var import_child_process16 = require("child_process");
|
|
75423
75578
|
var fs54 = __toESM(require("fs"));
|
|
75424
75579
|
var os20 = __toESM(require("os"));
|
|
75425
75580
|
var path60 = __toESM(require("path"));
|
|
75426
75581
|
init_paths();
|
|
75427
|
-
|
|
75428
|
-
|
|
75429
|
-
|
|
75582
|
+
function findManifestPath(dir, config2) {
|
|
75583
|
+
for (const name of config2.manifestFiles) {
|
|
75584
|
+
const candidate = path60.join(dir, name);
|
|
75585
|
+
if (fs54.existsSync(candidate)) return candidate;
|
|
75586
|
+
}
|
|
75587
|
+
return null;
|
|
75588
|
+
}
|
|
75589
|
+
function hasManifest(dir, config2) {
|
|
75590
|
+
return findManifestPath(dir, config2) !== null;
|
|
75430
75591
|
}
|
|
75431
75592
|
function readPackageVersion(dir) {
|
|
75432
75593
|
const pkgPath = path60.join(dir, "package.json");
|
|
@@ -75438,9 +75599,9 @@ function readPackageVersion(dir) {
|
|
|
75438
75599
|
return void 0;
|
|
75439
75600
|
}
|
|
75440
75601
|
}
|
|
75441
|
-
function readManifestId(dir) {
|
|
75442
|
-
const manifestPath =
|
|
75443
|
-
if (!
|
|
75602
|
+
function readManifestId(dir, config2) {
|
|
75603
|
+
const manifestPath = findManifestPath(dir, config2);
|
|
75604
|
+
if (!manifestPath) return void 0;
|
|
75444
75605
|
try {
|
|
75445
75606
|
const raw = JSON.parse(fs54.readFileSync(manifestPath, "utf8"));
|
|
75446
75607
|
return typeof raw.id === "string" ? raw.id : void 0;
|
|
@@ -75459,47 +75620,48 @@ function looksLikeNpm(spec) {
|
|
|
75459
75620
|
if (spec.includes("/") && !spec.startsWith("@")) return false;
|
|
75460
75621
|
return /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@.+)?$/i.test(spec);
|
|
75461
75622
|
}
|
|
75462
|
-
function resolveBundledDir(pluginId) {
|
|
75463
|
-
const dir = path60.join(resolveTemplatesDir(), "plugins", pluginId);
|
|
75464
|
-
return hasManifest(dir) ? dir : null;
|
|
75465
|
-
}
|
|
75466
75623
|
function expandHome(p) {
|
|
75467
75624
|
if (p.startsWith("~/")) return path60.join(os20.homedir(), p.slice(2));
|
|
75468
75625
|
if (p === "~") return os20.homedir();
|
|
75469
75626
|
return p;
|
|
75470
75627
|
}
|
|
75471
|
-
function
|
|
75472
|
-
|
|
75473
|
-
|
|
75628
|
+
function makeTempDir(prefix) {
|
|
75629
|
+
return fs54.mkdtempSync(path60.join(os20.tmpdir(), prefix));
|
|
75630
|
+
}
|
|
75631
|
+
function manifestNames(config2) {
|
|
75632
|
+
return config2.manifestFiles.join(" or ");
|
|
75633
|
+
}
|
|
75634
|
+
function resolveTemplatesBundleDir(id, config2) {
|
|
75635
|
+
const dir = path60.join(resolveTemplatesDir(), config2.templatesSubdir, id);
|
|
75636
|
+
return hasManifest(dir, config2) ? dir : null;
|
|
75637
|
+
}
|
|
75638
|
+
function resolveLocalDir(id, repoPath, config2) {
|
|
75639
|
+
const dir = path60.resolve(repoPath, config2.localDir, id);
|
|
75640
|
+
return hasManifest(dir, config2) ? dir : null;
|
|
75474
75641
|
}
|
|
75475
|
-
function
|
|
75642
|
+
function isLocalBundleDir(dir, repoPath, config2) {
|
|
75643
|
+
return path60.dirname(dir) === path60.resolve(repoPath, config2.localDir);
|
|
75644
|
+
}
|
|
75645
|
+
function resolvePathSpec(spec, repoPath, config2) {
|
|
75476
75646
|
const raw = spec.startsWith("file:") ? spec.slice("file:".length) : spec;
|
|
75477
75647
|
const dir = path60.resolve(expandHome(raw));
|
|
75478
75648
|
if (!fs54.existsSync(dir) || !fs54.statSync(dir).isDirectory()) {
|
|
75479
|
-
throw new Error(
|
|
75649
|
+
throw new Error(`${config2.label} path not found: ${spec}`);
|
|
75480
75650
|
}
|
|
75481
|
-
if (!hasManifest(dir)) {
|
|
75482
|
-
throw new Error(`No
|
|
75651
|
+
if (!hasManifest(dir, config2)) {
|
|
75652
|
+
throw new Error(`No ${manifestNames(config2)} in ${dir}`);
|
|
75483
75653
|
}
|
|
75484
|
-
const id = readManifestId(dir) ?? path60.basename(dir);
|
|
75485
75654
|
return {
|
|
75486
|
-
id,
|
|
75487
|
-
kind:
|
|
75655
|
+
id: readManifestId(dir, config2) ?? path60.basename(dir),
|
|
75656
|
+
kind: isLocalBundleDir(dir, repoPath, config2) ? "local" : "path",
|
|
75488
75657
|
dir,
|
|
75489
75658
|
spec,
|
|
75490
75659
|
version: readPackageVersion(dir),
|
|
75491
75660
|
cleanupDirs: []
|
|
75492
75661
|
};
|
|
75493
75662
|
}
|
|
75494
|
-
function
|
|
75495
|
-
const
|
|
75496
|
-
return hasManifest(dir) ? dir : null;
|
|
75497
|
-
}
|
|
75498
|
-
function makeTempDir(prefix) {
|
|
75499
|
-
return fs54.mkdtempSync(path60.join(os20.tmpdir(), prefix));
|
|
75500
|
-
}
|
|
75501
|
-
function resolveNpmSpec(spec) {
|
|
75502
|
-
const tmp = makeTempDir("har-plugin-npm-");
|
|
75663
|
+
function resolveNpmSpec(spec, config2) {
|
|
75664
|
+
const tmp = makeTempDir(`har-${config2.label}-npm-`);
|
|
75503
75665
|
const cleanupDirs = [tmp];
|
|
75504
75666
|
try {
|
|
75505
75667
|
(0, import_child_process16.execFileSync)("npm", ["pack", spec, "--pack-destination", tmp], {
|
|
@@ -75517,20 +75679,18 @@ function resolveNpmSpec(spec) {
|
|
|
75517
75679
|
encoding: "utf8",
|
|
75518
75680
|
stdio: ["ignore", "pipe", "pipe"]
|
|
75519
75681
|
});
|
|
75520
|
-
|
|
75521
|
-
|
|
75522
|
-
|
|
75523
|
-
const nested = fs54.readdirSync(extractDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(extractDir, e.name)).find((d) => hasManifest(d));
|
|
75682
|
+
let dir = path60.join(extractDir, "package");
|
|
75683
|
+
if (!hasManifest(dir, config2)) {
|
|
75684
|
+
const nested = fs54.readdirSync(extractDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(extractDir, e.name)).find((d) => hasManifest(d, config2));
|
|
75524
75685
|
if (!nested) {
|
|
75525
75686
|
throw new Error(
|
|
75526
|
-
`npm package ${spec} has no
|
|
75687
|
+
`npm package ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
|
|
75527
75688
|
);
|
|
75528
75689
|
}
|
|
75529
75690
|
dir = nested;
|
|
75530
75691
|
}
|
|
75531
|
-
const id = readManifestId(dir) ?? spec.replace(/^@/, "").replace(/[/:].*/g, "");
|
|
75532
75692
|
return {
|
|
75533
|
-
id,
|
|
75693
|
+
id: readManifestId(dir, config2) ?? spec.replace(/^@/, "").replace(/[/:].*/g, ""),
|
|
75534
75694
|
kind: "npm",
|
|
75535
75695
|
dir,
|
|
75536
75696
|
spec,
|
|
@@ -75542,11 +75702,11 @@ function resolveNpmSpec(spec) {
|
|
|
75542
75702
|
fs54.rmSync(d, { recursive: true, force: true });
|
|
75543
75703
|
}
|
|
75544
75704
|
const message = err instanceof Error ? err.message : String(err);
|
|
75545
|
-
throw new Error(`Failed to install
|
|
75705
|
+
throw new Error(`Failed to install ${config2.label} from npm (${spec}): ${message}`);
|
|
75546
75706
|
}
|
|
75547
75707
|
}
|
|
75548
|
-
function resolveGitSpec(spec) {
|
|
75549
|
-
const tmp = makeTempDir(
|
|
75708
|
+
function resolveGitSpec(spec, config2) {
|
|
75709
|
+
const tmp = makeTempDir(`har-${config2.label}-git-`);
|
|
75550
75710
|
const cleanupDirs = [tmp];
|
|
75551
75711
|
const cloneDir = path60.join(tmp, "repo");
|
|
75552
75712
|
try {
|
|
@@ -75566,19 +75726,20 @@ function resolveGitSpec(spec) {
|
|
|
75566
75726
|
timeout: 18e4
|
|
75567
75727
|
});
|
|
75568
75728
|
let dir = cloneDir;
|
|
75569
|
-
if (!hasManifest(dir)) {
|
|
75570
|
-
const
|
|
75571
|
-
if (fs54.existsSync(
|
|
75572
|
-
const child = fs54.readdirSync(
|
|
75729
|
+
if (!hasManifest(dir, config2)) {
|
|
75730
|
+
const nestedRoot = path60.join(cloneDir, config2.gitNestedDir);
|
|
75731
|
+
if (fs54.existsSync(nestedRoot)) {
|
|
75732
|
+
const child = fs54.readdirSync(nestedRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(nestedRoot, e.name)).find((d) => hasManifest(d, config2));
|
|
75573
75733
|
if (child) dir = child;
|
|
75574
75734
|
}
|
|
75575
75735
|
}
|
|
75576
|
-
if (!hasManifest(dir)) {
|
|
75577
|
-
throw new Error(
|
|
75736
|
+
if (!hasManifest(dir, config2)) {
|
|
75737
|
+
throw new Error(
|
|
75738
|
+
`Git repo ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
|
|
75739
|
+
);
|
|
75578
75740
|
}
|
|
75579
|
-
const id = readManifestId(dir) ?? path60.basename(url.replace(/\.git$/, ""));
|
|
75580
75741
|
return {
|
|
75581
|
-
id,
|
|
75742
|
+
id: readManifestId(dir, config2) ?? path60.basename(url.replace(/\.git$/, "")),
|
|
75582
75743
|
kind: "git",
|
|
75583
75744
|
dir,
|
|
75584
75745
|
spec,
|
|
@@ -75590,21 +75751,21 @@ function resolveGitSpec(spec) {
|
|
|
75590
75751
|
fs54.rmSync(d, { recursive: true, force: true });
|
|
75591
75752
|
}
|
|
75592
75753
|
const message = err instanceof Error ? err.message : String(err);
|
|
75593
|
-
throw new Error(`Failed to install
|
|
75754
|
+
throw new Error(`Failed to install ${config2.label} from git (${spec}): ${message}`);
|
|
75594
75755
|
}
|
|
75595
75756
|
}
|
|
75596
|
-
function
|
|
75757
|
+
function resolveBundleSource(spec, repoPath, config2) {
|
|
75597
75758
|
const trimmed = spec.trim();
|
|
75598
75759
|
if (!trimmed) {
|
|
75599
|
-
throw new Error(
|
|
75760
|
+
throw new Error(`${config2.label} spec is empty`);
|
|
75600
75761
|
}
|
|
75601
75762
|
if (looksLikePath(trimmed)) {
|
|
75602
|
-
return resolvePathSpec(trimmed, repoPath);
|
|
75763
|
+
return resolvePathSpec(trimmed, repoPath, config2);
|
|
75603
75764
|
}
|
|
75604
75765
|
if (looksLikeGit(trimmed)) {
|
|
75605
|
-
return resolveGitSpec(trimmed);
|
|
75766
|
+
return resolveGitSpec(trimmed, config2);
|
|
75606
75767
|
}
|
|
75607
|
-
const bundled =
|
|
75768
|
+
const bundled = resolveTemplatesBundleDir(trimmed, config2);
|
|
75608
75769
|
if (bundled) {
|
|
75609
75770
|
return {
|
|
75610
75771
|
id: trimmed,
|
|
@@ -75615,10 +75776,10 @@ function resolvePluginSource(spec, repoPath = ".") {
|
|
|
75615
75776
|
cleanupDirs: []
|
|
75616
75777
|
};
|
|
75617
75778
|
}
|
|
75618
|
-
const local = resolveLocalDir(trimmed, repoPath);
|
|
75779
|
+
const local = resolveLocalDir(trimmed, repoPath, config2);
|
|
75619
75780
|
if (local) {
|
|
75620
75781
|
return {
|
|
75621
|
-
id: readManifestId(local) ?? trimmed,
|
|
75782
|
+
id: readManifestId(local, config2) ?? trimmed,
|
|
75622
75783
|
kind: "local",
|
|
75623
75784
|
dir: local,
|
|
75624
75785
|
spec: trimmed,
|
|
@@ -75626,23 +75787,54 @@ function resolvePluginSource(spec, repoPath = ".") {
|
|
|
75626
75787
|
cleanupDirs: []
|
|
75627
75788
|
};
|
|
75628
75789
|
}
|
|
75629
|
-
|
|
75630
|
-
return resolveNpmSpec(trimmed);
|
|
75631
|
-
}
|
|
75790
|
+
const hint = config2.unknownHint?.(repoPath) ?? "";
|
|
75632
75791
|
if (looksLikeNpm(trimmed)) {
|
|
75633
75792
|
try {
|
|
75634
|
-
return resolveNpmSpec(trimmed);
|
|
75793
|
+
return resolveNpmSpec(trimmed, config2);
|
|
75635
75794
|
} catch (npmErr) {
|
|
75795
|
+
if (trimmed.includes("@") || trimmed.includes("/")) throw npmErr;
|
|
75636
75796
|
const npmMessage = npmErr instanceof Error ? npmErr.message : String(npmErr);
|
|
75637
75797
|
throw new Error(
|
|
75638
|
-
`Unknown
|
|
75798
|
+
`Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path, npm package, or git URL. (${npmMessage})`
|
|
75639
75799
|
);
|
|
75640
75800
|
}
|
|
75641
75801
|
}
|
|
75642
75802
|
throw new Error(
|
|
75643
|
-
`Unknown
|
|
75803
|
+
`Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path (./bundle), npm package (@org/pkg), or git URL (github:org/repo).`
|
|
75644
75804
|
);
|
|
75645
75805
|
}
|
|
75806
|
+
function listTemplateBundleIds(config2) {
|
|
75807
|
+
const root = path60.join(resolveTemplatesDir(), config2.templatesSubdir);
|
|
75808
|
+
if (!fs54.existsSync(root)) return [];
|
|
75809
|
+
return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
|
|
75810
|
+
}
|
|
75811
|
+
function listLocalBundleIds(repoPath, config2) {
|
|
75812
|
+
const root = path60.resolve(repoPath, config2.localDir);
|
|
75813
|
+
if (!fs54.existsSync(root)) return [];
|
|
75814
|
+
return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
|
|
75815
|
+
}
|
|
75816
|
+
function cleanupResolvedBundle(source) {
|
|
75817
|
+
for (const dir of source.cleanupDirs) {
|
|
75818
|
+
fs54.rmSync(dir, { recursive: true, force: true });
|
|
75819
|
+
}
|
|
75820
|
+
}
|
|
75821
|
+
|
|
75822
|
+
// src/harness/plugin-resolve.ts
|
|
75823
|
+
var LOCAL_PLUGINS_DIR = ".har/plugins";
|
|
75824
|
+
var PLUGIN_BUNDLE_CONFIG = {
|
|
75825
|
+
// `line.manifest.json` is accepted here on purpose: resolving a line bundle
|
|
75826
|
+
// lets `readPluginManifestFromDir` refuse it with a pointer at `har line add`
|
|
75827
|
+
// instead of a bare "not a HAR plugin bundle".
|
|
75828
|
+
manifestFiles: ["template.manifest.json", "line.manifest.json"],
|
|
75829
|
+
templatesSubdir: "plugins",
|
|
75830
|
+
localDir: LOCAL_PLUGINS_DIR,
|
|
75831
|
+
gitNestedDir: "plugins",
|
|
75832
|
+
label: "plugin",
|
|
75833
|
+
unknownHint: (repoPath) => knownPluginsHint(repoPath)
|
|
75834
|
+
};
|
|
75835
|
+
function resolvePluginSource(spec, repoPath = ".") {
|
|
75836
|
+
return resolveBundleSource(spec, repoPath, PLUGIN_BUNDLE_CONFIG);
|
|
75837
|
+
}
|
|
75646
75838
|
function knownPluginsHint(repoPath) {
|
|
75647
75839
|
const bundled = listBundledPluginIds();
|
|
75648
75840
|
const local = listLocalPluginIds(repoPath);
|
|
@@ -75655,19 +75847,13 @@ function knownPluginsHint(repoPath) {
|
|
|
75655
75847
|
return parts.join(" ");
|
|
75656
75848
|
}
|
|
75657
75849
|
function listLocalPluginIds(repoPath = ".") {
|
|
75658
|
-
|
|
75659
|
-
if (!fs54.existsSync(root)) return [];
|
|
75660
|
-
return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name))).sort();
|
|
75850
|
+
return listLocalBundleIds(repoPath, PLUGIN_BUNDLE_CONFIG);
|
|
75661
75851
|
}
|
|
75662
75852
|
function listBundledPluginIds() {
|
|
75663
|
-
|
|
75664
|
-
if (!fs54.existsSync(root)) return [];
|
|
75665
|
-
return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name))).sort();
|
|
75853
|
+
return listTemplateBundleIds(PLUGIN_BUNDLE_CONFIG);
|
|
75666
75854
|
}
|
|
75667
75855
|
function cleanupResolvedPlugin(source) {
|
|
75668
|
-
|
|
75669
|
-
fs54.rmSync(dir, { recursive: true, force: true });
|
|
75670
|
-
}
|
|
75856
|
+
cleanupResolvedBundle(source);
|
|
75671
75857
|
}
|
|
75672
75858
|
|
|
75673
75859
|
// src/harness/plugin-prompt.ts
|
|
@@ -75774,13 +75960,17 @@ function resolveBundledPluginDir(pluginId) {
|
|
|
75774
75960
|
return dir;
|
|
75775
75961
|
}
|
|
75776
75962
|
function readPluginManifestFromDir(pluginDir, expectedId) {
|
|
75777
|
-
const manifestPath =
|
|
75778
|
-
if (!
|
|
75963
|
+
const manifestPath = findManifestPath(pluginDir, PLUGIN_BUNDLE_CONFIG);
|
|
75964
|
+
if (!manifestPath) {
|
|
75779
75965
|
throw new Error(`No template.manifest.json in ${pluginDir}`);
|
|
75780
75966
|
}
|
|
75781
|
-
const
|
|
75782
|
-
|
|
75783
|
-
|
|
75967
|
+
const raw = JSON.parse(fs56.readFileSync(manifestPath, "utf8"));
|
|
75968
|
+
if (raw.kind === LINE_BUNDLE_KIND || path62.basename(manifestPath) === "line.manifest.json") {
|
|
75969
|
+
throw new Error(
|
|
75970
|
+
`${path62.basename(manifestPath)} declares "kind": "line" \u2014 this is a factory line bundle, not a verification plugin. Install it with: har line add <spec>`
|
|
75971
|
+
);
|
|
75972
|
+
}
|
|
75973
|
+
const parsed = PluginManifestSchema.safeParse(raw);
|
|
75784
75974
|
if (!parsed.success) {
|
|
75785
75975
|
throw new Error(`Invalid plugin manifest: ${parsed.error.message}`);
|
|
75786
75976
|
}
|
|
@@ -76682,8 +76872,8 @@ init_manifest();
|
|
|
76682
76872
|
init_stages();
|
|
76683
76873
|
|
|
76684
76874
|
// src/harness/doctor.ts
|
|
76685
|
-
var
|
|
76686
|
-
var
|
|
76875
|
+
var fs63 = __toESM(require("fs"));
|
|
76876
|
+
var path70 = __toESM(require("path"));
|
|
76687
76877
|
init_slot_registry();
|
|
76688
76878
|
|
|
76689
76879
|
// src/harness/eject.ts
|
|
@@ -76787,20 +76977,408 @@ function adoptHarness(repoPath) {
|
|
|
76787
76977
|
// src/harness/doctor.ts
|
|
76788
76978
|
init_env();
|
|
76789
76979
|
init_manifest();
|
|
76980
|
+
|
|
76981
|
+
// src/harness/line-ledger.ts
|
|
76982
|
+
var fs60 = __toESM(require("fs"));
|
|
76983
|
+
var path66 = __toESM(require("path"));
|
|
76984
|
+
init_file_ops();
|
|
76985
|
+
init_manifest();
|
|
76986
|
+
init_schema2();
|
|
76987
|
+
function getLineLedgerPath(repoPath) {
|
|
76988
|
+
return path66.join(path66.resolve(repoPath), DEFAULT_HAR_DIR, "lines.json");
|
|
76989
|
+
}
|
|
76990
|
+
function readLineLedger(repoPath) {
|
|
76991
|
+
const ledgerPath = getLineLedgerPath(repoPath);
|
|
76992
|
+
if (!fs60.existsSync(ledgerPath)) return null;
|
|
76993
|
+
try {
|
|
76994
|
+
const parsed = LineLedgerSchema.safeParse(JSON.parse(fs60.readFileSync(ledgerPath, "utf8")));
|
|
76995
|
+
return parsed.success ? parsed.data : null;
|
|
76996
|
+
} catch {
|
|
76997
|
+
return null;
|
|
76998
|
+
}
|
|
76999
|
+
}
|
|
77000
|
+
function writeLineLedger(repoPath, ledger) {
|
|
77001
|
+
const ledgerPath = getLineLedgerPath(repoPath);
|
|
77002
|
+
const harnessDir = path66.dirname(ledgerPath);
|
|
77003
|
+
if (!fs60.existsSync(harnessDir)) {
|
|
77004
|
+
fs60.mkdirSync(harnessDir, { recursive: true });
|
|
77005
|
+
}
|
|
77006
|
+
writeFileSafe(ledgerPath, `${JSON.stringify(ledger, null, 2)}
|
|
77007
|
+
`);
|
|
77008
|
+
}
|
|
77009
|
+
function upsertLineLedgerEntry(repoPath, entry) {
|
|
77010
|
+
const existing = readLineLedger(repoPath) ?? { version: "1", lines: [] };
|
|
77011
|
+
const lines = existing.lines.filter((l) => l.id !== entry.id);
|
|
77012
|
+
lines.push(entry);
|
|
77013
|
+
lines.sort((a, b2) => a.id.localeCompare(b2.id));
|
|
77014
|
+
const ledger = { version: "1", lines };
|
|
77015
|
+
writeLineLedger(repoPath, ledger);
|
|
77016
|
+
return ledger;
|
|
77017
|
+
}
|
|
77018
|
+
|
|
77019
|
+
// src/harness/lines.ts
|
|
77020
|
+
var fs61 = __toESM(require("fs"));
|
|
77021
|
+
var path68 = __toESM(require("path"));
|
|
77022
|
+
init_logging();
|
|
77023
|
+
|
|
77024
|
+
// src/harness/line-prompt.ts
|
|
77025
|
+
var path67 = __toESM(require("path"));
|
|
77026
|
+
init_file_ops();
|
|
77027
|
+
init_manifest();
|
|
77028
|
+
function lineAdaptationPromptFile(lineId) {
|
|
77029
|
+
return `ADAPT-PROMPT-line-${lineId}.md`;
|
|
77030
|
+
}
|
|
77031
|
+
function buildLineAdaptationPrompt(repoPath, program, result) {
|
|
77032
|
+
const stations = program.stations.map((s, i) => `${i + 1}. \`${s.id}\` \u2014 ${s.title}${s.work?.ids?.length ? ` (work: ${s.work.ids.join(", ")})` : ""}`).join("\n");
|
|
77033
|
+
const gateRows = program.gate.stages.map((s) => `| \`${s.id}\` | \`${s.fromStation}\` | ${s.tier} |`).join("\n");
|
|
77034
|
+
const registered = result.stageIds.length > 0 ? result.stageIds.map((id) => `- \`${id}\` \u2014 registered, **not** in \`verificationStages\``).join("\n") : "- _(this line registered no extra stages)_";
|
|
77035
|
+
const declaredSkills = program.skills.length > 0 ? program.skills.map((s) => `- \`${s.id}\` (${s.role})${s.install ? ` \u2014 install: \`${s.install}\`` : ""}`).join("\n") : "- _(none declared)_";
|
|
77036
|
+
const declaredMcp = program.mcp.length > 0 ? program.mcp.map((m) => `- \`${m.name}\`${m.required ? " (required)" : " (optional)"}${m.why ? ` \u2014 ${m.why}` : ""}`).join("\n") : "- _(none declared)_";
|
|
77037
|
+
const warnings = result.warnings.length > 0 ? result.warnings.map((w2) => `- \u26A0 ${w2}`).join("\n") : "- _(none)_";
|
|
77038
|
+
const optIn = program.gate.optInEnv ? `This line's gate is opt-in: stages run only when \`${program.gate.optInEnv}=1\`.` : "This line has no opt-in env var \u2014 `har line gate` runs its stages on demand.";
|
|
77039
|
+
return `# Adapt the factory line: ${result.lineId}
|
|
77040
|
+
|
|
77041
|
+
Installed from \`${result.source}\` into \`${path67.basename(path67.resolve(repoPath))}\`.
|
|
77042
|
+
Program: \`${result.programPath}\`
|
|
77043
|
+
|
|
77044
|
+
## What just happened
|
|
77045
|
+
|
|
77046
|
+
A **factory line** was installed. A line is a *program* \u2014 an ordered set of
|
|
77047
|
+
stations plus a cumulative gate. It is not a verification plugin:
|
|
77048
|
+
|
|
77049
|
+
${registered}
|
|
77050
|
+
|
|
77051
|
+
\`verificationStages\` was **not** modified. Default \`har env verify --full\`
|
|
77052
|
+
takes exactly as long as it did before this install. ${optIn}
|
|
77053
|
+
|
|
77054
|
+
## Stations
|
|
77055
|
+
|
|
77056
|
+
${stations}
|
|
77057
|
+
|
|
77058
|
+
## Cumulative gate (the ratchet)
|
|
77059
|
+
|
|
77060
|
+
A gate stage tagged \`fromStation: X\` is required at X **and every later
|
|
77061
|
+
station**. Adding a station must never drop an earlier station's stages.
|
|
77062
|
+
|
|
77063
|
+
| Stage | From station | Tier |
|
|
77064
|
+
|---|---|---|
|
|
77065
|
+
${gateRows || "| _(none)_ | | |"}
|
|
77066
|
+
|
|
77067
|
+
Run it with:
|
|
77068
|
+
|
|
77069
|
+
\`\`\`bash
|
|
77070
|
+
har line gate <station> --line ${result.lineId}
|
|
77071
|
+
\`\`\`
|
|
77072
|
+
|
|
77073
|
+
Never with \`har env verify --full\` \u2014 that is the whole point of the split.
|
|
77074
|
+
|
|
77075
|
+
## Declared dependencies (declarations, not installs)
|
|
77076
|
+
|
|
77077
|
+
Skills:
|
|
77078
|
+
|
|
77079
|
+
${declaredSkills}
|
|
77080
|
+
|
|
77081
|
+
MCP servers:
|
|
77082
|
+
|
|
77083
|
+
${declaredMcp}
|
|
77084
|
+
|
|
77085
|
+
HAR does not vendor skill packs or MCP servers. Check they are present, install
|
|
77086
|
+
them the way your agent normally does, and skip tracker steps for any MCP marked
|
|
77087
|
+
optional that is missing.
|
|
77088
|
+
|
|
77089
|
+
## Warnings from install
|
|
77090
|
+
|
|
77091
|
+
${warnings}
|
|
77092
|
+
|
|
77093
|
+
## Your job now
|
|
77094
|
+
|
|
77095
|
+
1. Read \`${result.programPath}\` and make the stations describe *this* repo's
|
|
77096
|
+
work \u2014 station titles, \`work.source\`/\`work.ids\`, waves.
|
|
77097
|
+
2. Make each registered stage script real (replace TODO blocks). They live under
|
|
77098
|
+
\`.har/stages/\`.
|
|
77099
|
+
3. Leave \`gate.cumulative: true\` and \`handoff.autonomousShip: false\` alone \u2014
|
|
77100
|
+
both are contract, not preference.
|
|
77101
|
+
4. Do **not** add line stages to \`verificationStages\`. If a check should gate
|
|
77102
|
+
every verify, it belongs in a verification plugin instead
|
|
77103
|
+
(\`har env add-plugin\`), not on this line.
|
|
77104
|
+
5. Verify the harness still passes as before: \`har env verify <slot> --full\`.
|
|
77105
|
+
`;
|
|
77106
|
+
}
|
|
77107
|
+
function writeLineAdaptationPrompt(repoPath, lineId, content) {
|
|
77108
|
+
const filePath = path67.join(getHarnessDir(repoPath), lineAdaptationPromptFile(lineId));
|
|
77109
|
+
writeFileSafe(filePath, content);
|
|
77110
|
+
return filePath;
|
|
77111
|
+
}
|
|
77112
|
+
|
|
77113
|
+
// src/harness/line-resolve.ts
|
|
77114
|
+
init_schema2();
|
|
77115
|
+
var LOCAL_LINES_DIR = ".har/lines";
|
|
77116
|
+
var LINE_BUNDLE_CONFIG = {
|
|
77117
|
+
manifestFiles: LINE_MANIFEST_FILES,
|
|
77118
|
+
templatesSubdir: "lines",
|
|
77119
|
+
localDir: LOCAL_LINES_DIR,
|
|
77120
|
+
gitNestedDir: "lines",
|
|
77121
|
+
label: "line",
|
|
77122
|
+
unknownHint: (repoPath) => knownLinesHint(repoPath)
|
|
77123
|
+
};
|
|
77124
|
+
function resolveLineSource(spec, repoPath = ".") {
|
|
77125
|
+
return resolveBundleSource(spec, repoPath, LINE_BUNDLE_CONFIG);
|
|
77126
|
+
}
|
|
77127
|
+
function knownLinesHint(repoPath) {
|
|
77128
|
+
const bundled = listBundledLineIds();
|
|
77129
|
+
const local = listLocalLineIds(repoPath);
|
|
77130
|
+
const parts = [`Bundled: ${bundled.join(", ") || "(none)"}.`];
|
|
77131
|
+
if (local.length > 0) {
|
|
77132
|
+
parts.push(`Local (${LOCAL_LINES_DIR}/): ${local.join(", ")}.`);
|
|
77133
|
+
} else {
|
|
77134
|
+
parts.push("Scaffold a project-owned one with: har line create <id>.");
|
|
77135
|
+
}
|
|
77136
|
+
return parts.join(" ");
|
|
77137
|
+
}
|
|
77138
|
+
function listLocalLineIds(repoPath = ".") {
|
|
77139
|
+
return listLocalBundleIds(repoPath, LINE_BUNDLE_CONFIG);
|
|
77140
|
+
}
|
|
77141
|
+
function listBundledLineIds() {
|
|
77142
|
+
return listTemplateBundleIds(LINE_BUNDLE_CONFIG);
|
|
77143
|
+
}
|
|
77144
|
+
function cleanupResolvedLine(source) {
|
|
77145
|
+
cleanupResolvedBundle(source);
|
|
77146
|
+
}
|
|
77147
|
+
|
|
77148
|
+
// src/harness/lines.ts
|
|
77149
|
+
init_schema2();
|
|
77150
|
+
init_schema2();
|
|
77151
|
+
init_stages();
|
|
77152
|
+
function lineDirRel(lineId) {
|
|
77153
|
+
return `${LOCAL_LINES_DIR}/${lineId}`;
|
|
77154
|
+
}
|
|
77155
|
+
function lineProgramPathRel(lineId) {
|
|
77156
|
+
return `${lineDirRel(lineId)}/line.json`;
|
|
77157
|
+
}
|
|
77158
|
+
function readLineManifestFromDir(bundleDir, expectedId) {
|
|
77159
|
+
const manifestPath = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
|
|
77160
|
+
if (!manifestPath) {
|
|
77161
|
+
throw new Error(`No ${LINE_MANIFEST_FILES.join(" or ")} in ${bundleDir}`);
|
|
77162
|
+
}
|
|
77163
|
+
const raw = JSON.parse(fs61.readFileSync(manifestPath, "utf8"));
|
|
77164
|
+
if (raw.kind !== LINE_BUNDLE_KIND) {
|
|
77165
|
+
throw new Error(
|
|
77166
|
+
`${path68.basename(manifestPath)} is not a factory line bundle (missing "kind": "line"). Verification plugins install with: har env add-plugin <spec>`
|
|
77167
|
+
);
|
|
77168
|
+
}
|
|
77169
|
+
const parsed = LineManifestSchema.safeParse(raw);
|
|
77170
|
+
if (!parsed.success) {
|
|
77171
|
+
throw new Error(`Invalid line manifest: ${parsed.error.message}`);
|
|
77172
|
+
}
|
|
77173
|
+
if (expectedId && parsed.data.id !== expectedId) {
|
|
77174
|
+
throw new Error(`Line manifest id mismatch: expected ${expectedId}, got ${parsed.data.id}`);
|
|
77175
|
+
}
|
|
77176
|
+
return parsed.data;
|
|
77177
|
+
}
|
|
77178
|
+
function readLineProgramFromDir(bundleDir, manifest) {
|
|
77179
|
+
const programPath = path68.join(bundleDir, manifest.program);
|
|
77180
|
+
if (!fs61.existsSync(programPath)) {
|
|
77181
|
+
throw new Error(`Line program missing: ${manifest.program} (referenced by line.manifest.json)`);
|
|
77182
|
+
}
|
|
77183
|
+
const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
|
|
77184
|
+
if (!parsed.success) {
|
|
77185
|
+
throw new Error(`Invalid line program (${manifest.program}): ${parsed.error.message}`);
|
|
77186
|
+
}
|
|
77187
|
+
if (parsed.data.id !== manifest.id) {
|
|
77188
|
+
throw new Error(
|
|
77189
|
+
`Line program id "${parsed.data.id}" does not match manifest id "${manifest.id}"`
|
|
77190
|
+
);
|
|
77191
|
+
}
|
|
77192
|
+
return parsed.data;
|
|
77193
|
+
}
|
|
77194
|
+
function readInstalledLineProgram(repoPath, lineId) {
|
|
77195
|
+
const programPath = path68.join(path68.resolve(repoPath), lineProgramPathRel(lineId));
|
|
77196
|
+
if (!fs61.existsSync(programPath)) return null;
|
|
77197
|
+
const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
|
|
77198
|
+
return parsed.success ? parsed.data : null;
|
|
77199
|
+
}
|
|
77200
|
+
function ensureParentDir3(filePath) {
|
|
77201
|
+
fs61.mkdirSync(path68.dirname(filePath), { recursive: true });
|
|
77202
|
+
}
|
|
77203
|
+
function copyBundleFile(bundleDir, file, repoPath, force) {
|
|
77204
|
+
const srcPath = path68.join(bundleDir, file.src);
|
|
77205
|
+
const destPath = path68.join(repoPath, file.dest);
|
|
77206
|
+
if (!fs61.existsSync(srcPath)) {
|
|
77207
|
+
throw new Error(`Line file missing: ${file.src}`);
|
|
77208
|
+
}
|
|
77209
|
+
if (path68.resolve(srcPath) === path68.resolve(destPath)) {
|
|
77210
|
+
return file.dest;
|
|
77211
|
+
}
|
|
77212
|
+
if (fs61.existsSync(destPath) && !force) {
|
|
77213
|
+
throw new Error(
|
|
77214
|
+
`File already exists: ${file.dest}. Use --force to overwrite or remove it first.`
|
|
77215
|
+
);
|
|
77216
|
+
}
|
|
77217
|
+
ensureParentDir3(destPath);
|
|
77218
|
+
fs61.copyFileSync(srcPath, destPath);
|
|
77219
|
+
if (file.executable) {
|
|
77220
|
+
fs61.chmodSync(destPath, 493);
|
|
77221
|
+
}
|
|
77222
|
+
return file.dest;
|
|
77223
|
+
}
|
|
77224
|
+
function registerStagesOnly(repoPath, stages, force) {
|
|
77225
|
+
if (stages.length === 0) return;
|
|
77226
|
+
const registry2 = readStageRegistry(repoPath);
|
|
77227
|
+
const before = JSON.stringify(registry2.verificationStages ?? null);
|
|
77228
|
+
let nextStages = [...registry2.stages];
|
|
77229
|
+
for (const stage of stages) {
|
|
77230
|
+
const existing = nextStages.find((s) => s.id === stage.id);
|
|
77231
|
+
if (existing && !force) {
|
|
77232
|
+
throw new Error(
|
|
77233
|
+
`Stage "${stage.id}" already registered in .har/stages.json. Use --force to replace.`
|
|
77234
|
+
);
|
|
77235
|
+
}
|
|
77236
|
+
nextStages = existing ? nextStages.map((s) => s.id === stage.id ? stage : s) : [...nextStages, stage];
|
|
77237
|
+
}
|
|
77238
|
+
const updated = { ...registry2, stages: nextStages };
|
|
77239
|
+
const after = JSON.stringify(updated.verificationStages ?? null);
|
|
77240
|
+
if (before !== after) {
|
|
77241
|
+
throw new Error(
|
|
77242
|
+
"Refusing to write: applying a line changed verificationStages. Line gate stages are opt-in and run via `har line gate`."
|
|
77243
|
+
);
|
|
77244
|
+
}
|
|
77245
|
+
writeStageRegistry(repoPath, updated);
|
|
77246
|
+
}
|
|
77247
|
+
function assertGateStagesResolvable(program, registeredIds, repoPath) {
|
|
77248
|
+
const warnings = [];
|
|
77249
|
+
const registry2 = readStageRegistry(repoPath);
|
|
77250
|
+
const known = /* @__PURE__ */ new Set([...registry2.stages.map((s) => s.id), ...registeredIds]);
|
|
77251
|
+
for (const stage of program.gate.stages) {
|
|
77252
|
+
if (!known.has(stage.id)) {
|
|
77253
|
+
warnings.push(
|
|
77254
|
+
`gate stage "${stage.id}" (from ${stage.fromStation}) is not registered in .har/stages.json \u2014 install the plugin that provides it, or add it to the line bundle.`
|
|
77255
|
+
);
|
|
77256
|
+
}
|
|
77257
|
+
}
|
|
77258
|
+
return warnings;
|
|
77259
|
+
}
|
|
77260
|
+
function applyLineFromDir(repoPath, bundleDir, options, meta) {
|
|
77261
|
+
const resolved = path68.resolve(repoPath);
|
|
77262
|
+
const force = options.force ?? false;
|
|
77263
|
+
const warnings = [];
|
|
77264
|
+
const filesWritten = [];
|
|
77265
|
+
if (!harnessExists(resolved)) {
|
|
77266
|
+
throw new Error('No .har/ harness found. Run "har onboard" first.');
|
|
77267
|
+
}
|
|
77268
|
+
const manifest = readLineManifestFromDir(bundleDir);
|
|
77269
|
+
const program = readLineProgramFromDir(bundleDir, manifest);
|
|
77270
|
+
const stages = manifest.stages;
|
|
77271
|
+
const stageIds = stages.map((s) => s.id);
|
|
77272
|
+
const verificationBefore = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
|
|
77273
|
+
for (const file of manifest.files) {
|
|
77274
|
+
filesWritten.push(copyBundleFile(bundleDir, file, resolved, force));
|
|
77275
|
+
}
|
|
77276
|
+
const programDest = path68.join(resolved, lineProgramPathRel(manifest.id));
|
|
77277
|
+
const programSrc = path68.join(bundleDir, manifest.program);
|
|
77278
|
+
if (path68.resolve(programSrc) !== path68.resolve(programDest)) {
|
|
77279
|
+
if (fs61.existsSync(programDest) && !force) {
|
|
77280
|
+
throw new Error(
|
|
77281
|
+
`Line already installed: ${lineProgramPathRel(manifest.id)}. Use --force to replace.`
|
|
77282
|
+
);
|
|
77283
|
+
}
|
|
77284
|
+
ensureParentDir3(programDest);
|
|
77285
|
+
fs61.copyFileSync(programSrc, programDest);
|
|
77286
|
+
}
|
|
77287
|
+
filesWritten.push(lineProgramPathRel(manifest.id));
|
|
77288
|
+
const manifestDest = path68.join(resolved, lineDirRel(manifest.id), "line.manifest.json");
|
|
77289
|
+
const manifestSrc = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
|
|
77290
|
+
if (path68.resolve(manifestSrc) !== path68.resolve(manifestDest)) {
|
|
77291
|
+
ensureParentDir3(manifestDest);
|
|
77292
|
+
fs61.copyFileSync(manifestSrc, manifestDest);
|
|
77293
|
+
filesWritten.push(`${lineDirRel(manifest.id)}/line.manifest.json`);
|
|
77294
|
+
}
|
|
77295
|
+
registerStagesOnly(resolved, stages, force);
|
|
77296
|
+
const verificationAfter = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
|
|
77297
|
+
if (verificationBefore !== verificationAfter) {
|
|
77298
|
+
throw new Error(
|
|
77299
|
+
"Line apply changed verificationStages \u2014 this is a bug. Line gate stages must stay off `har env verify --full`."
|
|
77300
|
+
);
|
|
77301
|
+
}
|
|
77302
|
+
warnings.push(...assertGateStagesResolvable(program, stageIds, resolved));
|
|
77303
|
+
upsertLineLedgerEntry(resolved, {
|
|
77304
|
+
id: manifest.id,
|
|
77305
|
+
source: meta.source,
|
|
77306
|
+
spec: meta.spec,
|
|
77307
|
+
version: meta.version,
|
|
77308
|
+
stageIds,
|
|
77309
|
+
programPath: lineProgramPathRel(manifest.id),
|
|
77310
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77311
|
+
});
|
|
77312
|
+
const firstGatedStation = program.stations.find(
|
|
77313
|
+
(station) => program.gate.stages.some((stage) => stage.fromStation === station.id)
|
|
77314
|
+
)?.id ?? program.stations[0].id;
|
|
77315
|
+
const nextSteps = manifest.nextSteps.length > 0 ? manifest.nextSteps : [
|
|
77316
|
+
`har line status ${manifest.id}`,
|
|
77317
|
+
`har line gate ${firstGatedStation} --line ${manifest.id}`,
|
|
77318
|
+
`Adapt the program: ${lineProgramPathRel(manifest.id)}`
|
|
77319
|
+
];
|
|
77320
|
+
const partial2 = {
|
|
77321
|
+
lineId: manifest.id,
|
|
77322
|
+
title: manifest.title ?? program.title,
|
|
77323
|
+
stageIds,
|
|
77324
|
+
filesWritten,
|
|
77325
|
+
warnings,
|
|
77326
|
+
nextSteps,
|
|
77327
|
+
programPath: lineProgramPathRel(manifest.id),
|
|
77328
|
+
docsPath: manifest.docsPath,
|
|
77329
|
+
source: meta.source,
|
|
77330
|
+
stationIds: program.stations.map((s) => s.id),
|
|
77331
|
+
firstGatedStationId: firstGatedStation
|
|
77332
|
+
};
|
|
77333
|
+
const promptAbsPath = writeLineAdaptationPrompt(
|
|
77334
|
+
resolved,
|
|
77335
|
+
manifest.id,
|
|
77336
|
+
buildLineAdaptationPrompt(resolved, program, { ...partial2, adaptPromptPath: "" })
|
|
77337
|
+
);
|
|
77338
|
+
const adaptPromptPath = path68.relative(resolved, promptAbsPath);
|
|
77339
|
+
success(`Applied line: ${manifest.id}`);
|
|
77340
|
+
info(`Stations: ${partial2.stationIds.join(" \u2192 ")}`);
|
|
77341
|
+
info(
|
|
77342
|
+
stageIds.length > 0 ? `Registered stage(s) (NOT on verify): ${stageIds.join(", ")}` : "No extra stages registered"
|
|
77343
|
+
);
|
|
77344
|
+
for (const file of filesWritten) {
|
|
77345
|
+
info(` + ${file}`);
|
|
77346
|
+
}
|
|
77347
|
+
info(` + ${adaptPromptPath} (adaptation prompt for your coding agent)`);
|
|
77348
|
+
for (const warning of warnings) {
|
|
77349
|
+
warn(` \u26A0 ${warning}`);
|
|
77350
|
+
}
|
|
77351
|
+
return { ...partial2, adaptPromptPath };
|
|
77352
|
+
}
|
|
77353
|
+
function applyLine(repoPath, lineSpec, options = {}) {
|
|
77354
|
+
const spec = options.spec ?? lineSpec;
|
|
77355
|
+
const source = resolveLineSource(spec, repoPath);
|
|
77356
|
+
try {
|
|
77357
|
+
return applyLineFromDir(repoPath, source.dir, options, {
|
|
77358
|
+
source: source.kind,
|
|
77359
|
+
spec: source.spec,
|
|
77360
|
+
version: source.version
|
|
77361
|
+
});
|
|
77362
|
+
} finally {
|
|
77363
|
+
cleanupResolvedLine(source);
|
|
77364
|
+
}
|
|
77365
|
+
}
|
|
77366
|
+
|
|
77367
|
+
// src/harness/doctor.ts
|
|
76790
77368
|
init_stages();
|
|
76791
77369
|
init_hooks2();
|
|
76792
77370
|
|
|
76793
77371
|
// src/harness/migrations.ts
|
|
76794
|
-
var
|
|
76795
|
-
var
|
|
77372
|
+
var fs62 = __toESM(require("fs"));
|
|
77373
|
+
var path69 = __toESM(require("path"));
|
|
76796
77374
|
init_schema2();
|
|
76797
77375
|
init_manifest();
|
|
76798
77376
|
init_profiles();
|
|
76799
77377
|
init_stages();
|
|
76800
77378
|
init_manifest();
|
|
76801
77379
|
var MIGRATE_DIR = "migrate";
|
|
76802
|
-
var MIGRATE_BACKUP_DIR =
|
|
76803
|
-
var MIGRATE_PLAN_FILE =
|
|
77380
|
+
var MIGRATE_BACKUP_DIR = path69.join(MIGRATE_DIR, "backup");
|
|
77381
|
+
var MIGRATE_PLAN_FILE = path69.join(MIGRATE_DIR, "plan.json");
|
|
76804
77382
|
var LEGACY_MACHINERY_FILES = [
|
|
76805
77383
|
"agent-slot.sh",
|
|
76806
77384
|
"provision-toolchain.sh",
|
|
@@ -76841,25 +77419,25 @@ function harnessEnvIsPre10(content) {
|
|
|
76841
77419
|
return /^\s*(har_infra_enabled|har_pg|har_node_[a-z_]+|har_pkg_exec)\s*\(\)/m.test(content) || /^export\s+HARNESS_[A-Z0-9_]+_PORT_(DEFAULT|SCAN_START|SCAN_END)=/m.test(content);
|
|
76842
77420
|
}
|
|
76843
77421
|
function scriptIsVendored(harnessDir, script) {
|
|
76844
|
-
const file =
|
|
76845
|
-
if (!
|
|
76846
|
-
const content =
|
|
77422
|
+
const file = path69.join(harnessDir, script);
|
|
77423
|
+
if (!fs62.existsSync(file)) return false;
|
|
77424
|
+
const content = fs62.readFileSync(file, "utf8");
|
|
76847
77425
|
return !content.includes("exec har env") && !content.includes("runtime/har.cjs");
|
|
76848
77426
|
}
|
|
76849
77427
|
function isPre10Harness(repoPath) {
|
|
76850
|
-
const resolved =
|
|
77428
|
+
const resolved = path69.resolve(repoPath);
|
|
76851
77429
|
const harnessDir = getHarnessDir(resolved);
|
|
76852
|
-
if (!
|
|
77430
|
+
if (!fs62.existsSync(harnessDir)) return false;
|
|
76853
77431
|
const manifest = readManifest(resolved);
|
|
76854
77432
|
if (manifest?.ejected) return false;
|
|
76855
77433
|
for (const file of LEGACY_MACHINERY_FILES) {
|
|
76856
|
-
if (
|
|
77434
|
+
if (fs62.existsSync(path69.join(harnessDir, file))) return true;
|
|
76857
77435
|
}
|
|
76858
77436
|
for (const script of MANAGED_SHIM_FILES) {
|
|
76859
77437
|
if (scriptIsVendored(harnessDir, script)) return true;
|
|
76860
77438
|
}
|
|
76861
|
-
const envPath =
|
|
76862
|
-
if (
|
|
77439
|
+
const envPath = path69.join(harnessDir, "harness.env");
|
|
77440
|
+
if (fs62.existsSync(envPath) && harnessEnvIsPre10(fs62.readFileSync(envPath, "utf8"))) {
|
|
76863
77441
|
return true;
|
|
76864
77442
|
}
|
|
76865
77443
|
return false;
|
|
@@ -76992,10 +77570,10 @@ var SCRIPT_RESIDUE_TARGET = {
|
|
|
76992
77570
|
"agent-cli.sh": "review"
|
|
76993
77571
|
};
|
|
76994
77572
|
function backupRel(file) {
|
|
76995
|
-
return
|
|
77573
|
+
return path69.join(MIGRATE_BACKUP_DIR, file);
|
|
76996
77574
|
}
|
|
76997
77575
|
function buildPre10Plan(repoPath) {
|
|
76998
|
-
const resolved =
|
|
77576
|
+
const resolved = path69.resolve(repoPath);
|
|
76999
77577
|
const harnessDir = getHarnessDir(resolved);
|
|
77000
77578
|
const manifest = readManifest(resolved);
|
|
77001
77579
|
const profile = manifest?.profile ?? "default";
|
|
@@ -77004,7 +77582,7 @@ function buildPre10Plan(repoPath) {
|
|
|
77004
77582
|
const deleteLifecycleScripts = [];
|
|
77005
77583
|
const residue = [];
|
|
77006
77584
|
for (const script of MANAGED_SHIM_FILES) {
|
|
77007
|
-
if (!
|
|
77585
|
+
if (!fs62.existsSync(path69.join(harnessDir, script))) continue;
|
|
77008
77586
|
deleteLifecycleScripts.push(script);
|
|
77009
77587
|
if (scriptIsVendored(harnessDir, script)) {
|
|
77010
77588
|
const target = SCRIPT_RESIDUE_TARGET[script] ?? "review";
|
|
@@ -77018,31 +77596,31 @@ function buildPre10Plan(repoPath) {
|
|
|
77018
77596
|
}
|
|
77019
77597
|
const survivingScripts = [];
|
|
77020
77598
|
for (const dir of ["stages", "hooks"]) {
|
|
77021
|
-
const full =
|
|
77022
|
-
if (!
|
|
77023
|
-
for (const entry of
|
|
77024
|
-
if (entry.endsWith(".sh") || entry.endsWith(".mjs")) survivingScripts.push(
|
|
77599
|
+
const full = path69.join(harnessDir, dir);
|
|
77600
|
+
if (!fs62.existsSync(full)) continue;
|
|
77601
|
+
for (const entry of fs62.readdirSync(full)) {
|
|
77602
|
+
if (entry.endsWith(".sh") || entry.endsWith(".mjs")) survivingScripts.push(path69.join(dir, entry));
|
|
77025
77603
|
}
|
|
77026
77604
|
}
|
|
77027
|
-
for (const entry of
|
|
77605
|
+
for (const entry of fs62.readdirSync(harnessDir)) {
|
|
77028
77606
|
if (!entry.endsWith(".sh")) continue;
|
|
77029
77607
|
if (MANAGED_SHIM_FILES.includes(entry)) continue;
|
|
77030
77608
|
if (LEGACY_MACHINERY_FILES.includes(entry)) continue;
|
|
77031
|
-
if (!
|
|
77609
|
+
if (!fs62.statSync(path69.join(harnessDir, entry)).isFile()) continue;
|
|
77032
77610
|
survivingScripts.push(entry);
|
|
77033
77611
|
}
|
|
77034
77612
|
const referencedBy = (machineryFile) => {
|
|
77035
|
-
const base =
|
|
77613
|
+
const base = path69.basename(machineryFile).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
77036
77614
|
const loadRe = new RegExp(
|
|
77037
77615
|
`(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
|
|
77038
77616
|
);
|
|
77039
77617
|
return survivingScripts.filter(
|
|
77040
|
-
(script) =>
|
|
77618
|
+
(script) => fs62.readFileSync(path69.join(harnessDir, script), "utf8").split("\n").some((line) => !/^\s*#/.test(line) && loadRe.test(line))
|
|
77041
77619
|
);
|
|
77042
77620
|
};
|
|
77043
77621
|
const retireDocs = [];
|
|
77044
77622
|
for (const doc of RETIRED_DOC_FILES) {
|
|
77045
|
-
if (!
|
|
77623
|
+
if (!fs62.existsSync(path69.join(harnessDir, doc))) continue;
|
|
77046
77624
|
retireDocs.push(doc);
|
|
77047
77625
|
residue.push({
|
|
77048
77626
|
source: doc,
|
|
@@ -77055,8 +77633,8 @@ function buildPre10Plan(repoPath) {
|
|
|
77055
77633
|
const editedMachinery = [];
|
|
77056
77634
|
const retainMachinery = [];
|
|
77057
77635
|
for (const file of LEGACY_MACHINERY_FILES) {
|
|
77058
|
-
const full =
|
|
77059
|
-
if (!
|
|
77636
|
+
const full = path69.join(harnessDir, file);
|
|
77637
|
+
if (!fs62.existsSync(full)) continue;
|
|
77060
77638
|
const refs = referencedBy(file);
|
|
77061
77639
|
if (refs.length > 0) {
|
|
77062
77640
|
retainMachinery.push(file);
|
|
@@ -77070,7 +77648,7 @@ function buildPre10Plan(repoPath) {
|
|
|
77070
77648
|
}
|
|
77071
77649
|
deleteMachinery.push(file);
|
|
77072
77650
|
const recorded = fileBaseline[file];
|
|
77073
|
-
const installed = computeFileChecksum(
|
|
77651
|
+
const installed = computeFileChecksum(fs62.readFileSync(full, "utf8"));
|
|
77074
77652
|
if (recorded !== void 0 && installed !== recorded) {
|
|
77075
77653
|
editedMachinery.push(file);
|
|
77076
77654
|
residue.push({
|
|
@@ -77082,9 +77660,9 @@ function buildPre10Plan(repoPath) {
|
|
|
77082
77660
|
}
|
|
77083
77661
|
}
|
|
77084
77662
|
let env3 = null;
|
|
77085
|
-
const envPath =
|
|
77086
|
-
if (
|
|
77087
|
-
const content =
|
|
77663
|
+
const envPath = path69.join(harnessDir, "harness.env");
|
|
77664
|
+
if (fs62.existsSync(envPath)) {
|
|
77665
|
+
const content = fs62.readFileSync(envPath, "utf8");
|
|
77088
77666
|
if (harnessEnvIsPre10(content) || migrateHarnessEnvContent(content).commentedKeys.length > 0) {
|
|
77089
77667
|
env3 = migrateHarnessEnvContent(content);
|
|
77090
77668
|
for (const fn of env3.customFunctions) {
|
|
@@ -77109,7 +77687,7 @@ function buildPre10Plan(repoPath) {
|
|
|
77109
77687
|
for (const file of composed.keys()) {
|
|
77110
77688
|
if (MANAGED_SHIM_FILES.includes(file)) continue;
|
|
77111
77689
|
if (file === "gitignore.template") continue;
|
|
77112
|
-
if (!
|
|
77690
|
+
if (!fs62.existsSync(path69.join(harnessDir, file))) installMissing.push(file);
|
|
77113
77691
|
}
|
|
77114
77692
|
let phantomVerificationIds = [];
|
|
77115
77693
|
try {
|
|
@@ -77140,25 +77718,25 @@ function buildPre10Plan(repoPath) {
|
|
|
77140
77718
|
};
|
|
77141
77719
|
}
|
|
77142
77720
|
function applyPre10Plan(repoPath, plan) {
|
|
77143
|
-
const resolved =
|
|
77721
|
+
const resolved = path69.resolve(repoPath);
|
|
77144
77722
|
const harnessDir = getHarnessDir(resolved);
|
|
77145
77723
|
const effective = plan ?? buildPre10Plan(resolved);
|
|
77146
|
-
const backupDir =
|
|
77147
|
-
|
|
77724
|
+
const backupDir = path69.join(harnessDir, MIGRATE_BACKUP_DIR);
|
|
77725
|
+
fs62.mkdirSync(backupDir, { recursive: true });
|
|
77148
77726
|
const backup = (file) => {
|
|
77149
|
-
const src =
|
|
77150
|
-
if (!
|
|
77151
|
-
const dest =
|
|
77152
|
-
|
|
77153
|
-
|
|
77727
|
+
const src = path69.join(harnessDir, file);
|
|
77728
|
+
if (!fs62.existsSync(src)) return;
|
|
77729
|
+
const dest = path69.join(backupDir, file);
|
|
77730
|
+
fs62.mkdirSync(path69.dirname(dest), { recursive: true });
|
|
77731
|
+
fs62.copyFileSync(src, dest);
|
|
77154
77732
|
};
|
|
77155
77733
|
const written = [];
|
|
77156
77734
|
const deleted = [];
|
|
77157
|
-
const projectName =
|
|
77735
|
+
const projectName = path69.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
77158
77736
|
const composed = composeProfileTemplateMap(effective.profile);
|
|
77159
77737
|
for (const script of effective.deleteLifecycleScripts) {
|
|
77160
77738
|
backup(script);
|
|
77161
|
-
|
|
77739
|
+
fs62.rmSync(path69.join(harnessDir, script), { force: true });
|
|
77162
77740
|
deleted.push(script);
|
|
77163
77741
|
}
|
|
77164
77742
|
if (stripLifecycleShimCommands(resolved)) {
|
|
@@ -77166,26 +77744,26 @@ function applyPre10Plan(repoPath, plan) {
|
|
|
77166
77744
|
}
|
|
77167
77745
|
for (const file of effective.deleteMachinery) {
|
|
77168
77746
|
backup(file);
|
|
77169
|
-
|
|
77747
|
+
fs62.rmSync(path69.join(harnessDir, file), { force: true });
|
|
77170
77748
|
deleted.push(file);
|
|
77171
77749
|
}
|
|
77172
77750
|
for (const doc of effective.retireDocs ?? []) {
|
|
77173
77751
|
backup(doc);
|
|
77174
77752
|
}
|
|
77175
|
-
const libDir =
|
|
77176
|
-
if (
|
|
77177
|
-
|
|
77753
|
+
const libDir = path69.join(harnessDir, "lib");
|
|
77754
|
+
if (fs62.existsSync(libDir) && fs62.readdirSync(libDir).length === 0) {
|
|
77755
|
+
fs62.rmdirSync(libDir);
|
|
77178
77756
|
deleted.push("lib/");
|
|
77179
77757
|
}
|
|
77180
77758
|
for (const file of effective.installMissing ?? []) {
|
|
77181
77759
|
const source = composed.get(file);
|
|
77182
77760
|
if (!source) continue;
|
|
77183
|
-
const dest =
|
|
77184
|
-
if (
|
|
77761
|
+
const dest = path69.join(harnessDir, file);
|
|
77762
|
+
if (fs62.existsSync(dest)) continue;
|
|
77185
77763
|
const rendered = substituteTemplateTokens(readComposedTemplateContent(source), projectName);
|
|
77186
|
-
|
|
77187
|
-
|
|
77188
|
-
if (/\.(sh|mjs)$/.test(file))
|
|
77764
|
+
fs62.mkdirSync(path69.dirname(dest), { recursive: true });
|
|
77765
|
+
fs62.writeFileSync(dest, rendered);
|
|
77766
|
+
if (/\.(sh|mjs)$/.test(file)) fs62.chmodSync(dest, 493);
|
|
77189
77767
|
written.push(file);
|
|
77190
77768
|
}
|
|
77191
77769
|
if (effective.phantomVerificationIds.length > 0) {
|
|
@@ -77200,7 +77778,7 @@ function applyPre10Plan(repoPath, plan) {
|
|
|
77200
77778
|
}
|
|
77201
77779
|
if (effective.env) {
|
|
77202
77780
|
backup("harness.env");
|
|
77203
|
-
|
|
77781
|
+
fs62.writeFileSync(path69.join(harnessDir, "harness.env"), effective.env.content);
|
|
77204
77782
|
written.push("harness.env");
|
|
77205
77783
|
}
|
|
77206
77784
|
const manifest = readManifest(resolved);
|
|
@@ -77215,8 +77793,8 @@ function applyPre10Plan(repoPath, plan) {
|
|
|
77215
77793
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77216
77794
|
});
|
|
77217
77795
|
}
|
|
77218
|
-
|
|
77219
|
-
|
|
77796
|
+
fs62.writeFileSync(
|
|
77797
|
+
path69.join(harnessDir, MIGRATE_PLAN_FILE),
|
|
77220
77798
|
JSON.stringify({ ...effective, appliedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
|
|
77221
77799
|
);
|
|
77222
77800
|
return { plan: effective, backupDir, written, deleted };
|
|
@@ -77236,33 +77814,33 @@ function pendingMigrations(repoPath) {
|
|
|
77236
77814
|
);
|
|
77237
77815
|
}
|
|
77238
77816
|
function writeMigrationPlan(repoPath, plan) {
|
|
77239
|
-
const harnessDir = getHarnessDir(
|
|
77240
|
-
const planPath =
|
|
77241
|
-
|
|
77242
|
-
|
|
77817
|
+
const harnessDir = getHarnessDir(path69.resolve(repoPath));
|
|
77818
|
+
const planPath = path69.join(harnessDir, MIGRATE_PLAN_FILE);
|
|
77819
|
+
fs62.mkdirSync(path69.dirname(planPath), { recursive: true });
|
|
77820
|
+
fs62.writeFileSync(planPath, JSON.stringify(plan, null, 2) + "\n");
|
|
77243
77821
|
return planPath;
|
|
77244
77822
|
}
|
|
77245
77823
|
function removeMigrationArtifacts(repoPath) {
|
|
77246
|
-
const harnessDir = getHarnessDir(
|
|
77247
|
-
|
|
77248
|
-
|
|
77824
|
+
const harnessDir = getHarnessDir(path69.resolve(repoPath));
|
|
77825
|
+
fs62.rmSync(path69.join(harnessDir, MIGRATE_DIR), { recursive: true, force: true });
|
|
77826
|
+
fs62.rmSync(path69.join(harnessDir, "MIGRATE-PROMPT.md"), { force: true });
|
|
77249
77827
|
}
|
|
77250
77828
|
|
|
77251
77829
|
// src/harness/doctor.ts
|
|
77252
77830
|
function listHarnessShellScripts(harnessDir) {
|
|
77253
77831
|
const out = [];
|
|
77254
77832
|
const push = (dir, prefix) => {
|
|
77255
|
-
if (!
|
|
77256
|
-
for (const entry of
|
|
77833
|
+
if (!fs63.existsSync(dir)) return;
|
|
77834
|
+
for (const entry of fs63.readdirSync(dir).sort()) {
|
|
77257
77835
|
if (!entry.endsWith(".sh")) continue;
|
|
77258
|
-
const full =
|
|
77259
|
-
if (!
|
|
77836
|
+
const full = path70.join(dir, entry);
|
|
77837
|
+
if (!fs63.statSync(full).isFile()) continue;
|
|
77260
77838
|
out.push(prefix ? `${prefix}/${entry}` : entry);
|
|
77261
77839
|
}
|
|
77262
77840
|
};
|
|
77263
77841
|
push(harnessDir, "");
|
|
77264
|
-
push(
|
|
77265
|
-
push(
|
|
77842
|
+
push(path70.join(harnessDir, "stages"), "stages");
|
|
77843
|
+
push(path70.join(harnessDir, "hooks"), "hooks");
|
|
77266
77844
|
return out;
|
|
77267
77845
|
}
|
|
77268
77846
|
var CHECK_LABELS = {
|
|
@@ -77275,14 +77853,15 @@ var CHECK_LABELS = {
|
|
|
77275
77853
|
"slot-registry": "slot registry worktrees",
|
|
77276
77854
|
hooks: "lifecycle hooks (.har/hooks)",
|
|
77277
77855
|
"retired-machinery": "no references to retired runtime machinery",
|
|
77278
|
-
"ejected-runtime": "ejected runtime (user-owned)"
|
|
77856
|
+
"ejected-runtime": "ejected runtime (user-owned)",
|
|
77857
|
+
"factory-lines": "factory lines stay off verify"
|
|
77279
77858
|
};
|
|
77280
77859
|
var PRE_1_0_FUNCTION_RE = /^\s*(har_infra_enabled|har_pg|har_node_[a-z_]+|har_pkg_exec)\s*\(\)/m;
|
|
77281
77860
|
var LEGACY_TRIPLET_RE = /^export\s+HARNESS_[A-Z0-9_]+_PORT_(DEFAULT|SCAN_START|SCAN_END)=/m;
|
|
77282
77861
|
function detectContract(harnessDir) {
|
|
77283
|
-
const envPath =
|
|
77284
|
-
if (!
|
|
77285
|
-
const content =
|
|
77862
|
+
const envPath = path70.join(harnessDir, "harness.env");
|
|
77863
|
+
if (!fs63.existsSync(envPath)) return "none";
|
|
77864
|
+
const content = fs63.readFileSync(envPath, "utf8");
|
|
77286
77865
|
if (PRE_1_0_FUNCTION_RE.test(content) || LEGACY_TRIPLET_RE.test(content)) {
|
|
77287
77866
|
return "pre-1.0";
|
|
77288
77867
|
}
|
|
@@ -77292,18 +77871,18 @@ function resolveStageFile(harnessDir, stage) {
|
|
|
77292
77871
|
if (PACKAGE_RUNTIME_KINDS.has(stage.kind) && (!stage.command && !stage.script || stagePointsAtLifecycleShim(stage))) {
|
|
77293
77872
|
return null;
|
|
77294
77873
|
}
|
|
77295
|
-
if (stage.script) return
|
|
77874
|
+
if (stage.script) return path70.join(harnessDir, stage.script);
|
|
77296
77875
|
if (stage.command) {
|
|
77297
77876
|
const first = stage.command.trim().split(/\s+/)[0];
|
|
77298
77877
|
if (first.startsWith("./.har/")) {
|
|
77299
|
-
return
|
|
77878
|
+
return path70.join(harnessDir, first.replace(/^\.\/\.har\//, ""));
|
|
77300
77879
|
}
|
|
77301
77880
|
if (first.startsWith(".har/")) {
|
|
77302
|
-
return
|
|
77881
|
+
return path70.join(harnessDir, first.replace(/^\.har\//, ""));
|
|
77303
77882
|
}
|
|
77304
77883
|
return null;
|
|
77305
77884
|
}
|
|
77306
|
-
return
|
|
77885
|
+
return path70.join(harnessDir, "stages", `${stage.id}.sh`);
|
|
77307
77886
|
}
|
|
77308
77887
|
function checkStatus(findings, id) {
|
|
77309
77888
|
const mine = findings.filter((f) => f.check === id);
|
|
@@ -77315,7 +77894,7 @@ function runDoctor(repoPath) {
|
|
|
77315
77894
|
const harnessDir = getHarnessDir(repoPath);
|
|
77316
77895
|
const findings = [];
|
|
77317
77896
|
const skipped = /* @__PURE__ */ new Set();
|
|
77318
|
-
if (!
|
|
77897
|
+
if (!fs63.existsSync(harnessDir)) {
|
|
77319
77898
|
return {
|
|
77320
77899
|
ok: false,
|
|
77321
77900
|
contract: "none",
|
|
@@ -77362,9 +77941,9 @@ function runDoctor(repoPath) {
|
|
|
77362
77941
|
}
|
|
77363
77942
|
let registry2 = null;
|
|
77364
77943
|
try {
|
|
77365
|
-
const stagesPath =
|
|
77366
|
-
if (
|
|
77367
|
-
const raw = JSON.parse(
|
|
77944
|
+
const stagesPath = path70.join(harnessDir, "stages.json");
|
|
77945
|
+
if (fs63.existsSync(stagesPath)) {
|
|
77946
|
+
const raw = JSON.parse(fs63.readFileSync(stagesPath, "utf8"));
|
|
77368
77947
|
if (typeof raw !== "object" || raw === null || !Array.isArray(raw.stages)) {
|
|
77369
77948
|
throw new Error("stages.json has no `stages` array \u2014 the registry is structurally invalid");
|
|
77370
77949
|
}
|
|
@@ -77395,8 +77974,8 @@ function runDoctor(repoPath) {
|
|
|
77395
77974
|
for (const stage of registry2.stages) {
|
|
77396
77975
|
const file = resolveStageFile(harnessDir, stage);
|
|
77397
77976
|
if (!file) continue;
|
|
77398
|
-
const rel =
|
|
77399
|
-
if (!
|
|
77977
|
+
const rel = path70.relative(harnessDir, file);
|
|
77978
|
+
if (!fs63.existsSync(file)) {
|
|
77400
77979
|
findings.push({
|
|
77401
77980
|
check: "stage-files",
|
|
77402
77981
|
severity: contractSeverity,
|
|
@@ -77406,7 +77985,7 @@ function runDoctor(repoPath) {
|
|
|
77406
77985
|
});
|
|
77407
77986
|
continue;
|
|
77408
77987
|
}
|
|
77409
|
-
if (rel.endsWith(".sh") && !(
|
|
77988
|
+
if (rel.endsWith(".sh") && !(fs63.statSync(file).mode & 73)) {
|
|
77410
77989
|
findings.push({
|
|
77411
77990
|
check: "stage-files",
|
|
77412
77991
|
severity: "warning",
|
|
@@ -77429,13 +78008,13 @@ function runDoctor(repoPath) {
|
|
|
77429
78008
|
continue;
|
|
77430
78009
|
}
|
|
77431
78010
|
const file = resolveStageFile(harnessDir, stage);
|
|
77432
|
-
if (file && !
|
|
78011
|
+
if (file && !fs63.existsSync(file)) {
|
|
77433
78012
|
findings.push({
|
|
77434
78013
|
check: "lifecycle-stages",
|
|
77435
78014
|
severity: contractSeverity,
|
|
77436
|
-
file:
|
|
78015
|
+
file: path70.relative(harnessDir, file),
|
|
77437
78016
|
message: `Lifecycle stage \`${stage.id}\` (${kind}) points at a missing file`,
|
|
77438
|
-
remedy: `Restore .har/${
|
|
78017
|
+
remedy: `Restore .har/${path70.relative(harnessDir, file)} or fix the \`${stage.id}\` entry in stages.json`
|
|
77439
78018
|
});
|
|
77440
78019
|
}
|
|
77441
78020
|
}
|
|
@@ -77480,9 +78059,9 @@ function runDoctor(repoPath) {
|
|
|
77480
78059
|
if (!manifest?.ejected) {
|
|
77481
78060
|
skipped.add("ejected-runtime");
|
|
77482
78061
|
} else {
|
|
77483
|
-
const runtimeRel =
|
|
77484
|
-
const runtimePath =
|
|
77485
|
-
if (!
|
|
78062
|
+
const runtimeRel = path70.join(EJECTED_RUNTIME_DIR, EJECTED_RUNTIME_BUNDLE);
|
|
78063
|
+
const runtimePath = path70.join(harnessDir, runtimeRel);
|
|
78064
|
+
if (!fs63.existsSync(runtimePath)) {
|
|
77486
78065
|
findings.push({
|
|
77487
78066
|
check: "ejected-runtime",
|
|
77488
78067
|
severity: "error",
|
|
@@ -77495,7 +78074,7 @@ function runDoctor(repoPath) {
|
|
|
77495
78074
|
for (const entry of listSlotRegistryEntries(repoPath)) {
|
|
77496
78075
|
if (entry.status === "completed") continue;
|
|
77497
78076
|
const dir = entry.worktreePath ?? entry.workDir;
|
|
77498
|
-
if (dir && !
|
|
78077
|
+
if (dir && !fs63.existsSync(dir)) {
|
|
77499
78078
|
findings.push({
|
|
77500
78079
|
check: "slot-registry",
|
|
77501
78080
|
severity: "warning",
|
|
@@ -77505,11 +78084,11 @@ function runDoctor(repoPath) {
|
|
|
77505
78084
|
});
|
|
77506
78085
|
}
|
|
77507
78086
|
}
|
|
77508
|
-
const hooksDir =
|
|
77509
|
-
if (
|
|
77510
|
-
for (const file of
|
|
77511
|
-
const full =
|
|
77512
|
-
if (!
|
|
78087
|
+
const hooksDir = path70.join(harnessDir, "hooks");
|
|
78088
|
+
if (fs63.existsSync(hooksDir)) {
|
|
78089
|
+
for (const file of fs63.readdirSync(hooksDir).sort()) {
|
|
78090
|
+
const full = path70.join(hooksDir, file);
|
|
78091
|
+
if (!fs63.statSync(full).isFile() || !file.endsWith(".sh")) continue;
|
|
77513
78092
|
if (!LIFECYCLE_HOOKS.includes(file.replace(/\.sh$/, ""))) {
|
|
77514
78093
|
findings.push({
|
|
77515
78094
|
check: "hooks",
|
|
@@ -77520,7 +78099,7 @@ function runDoctor(repoPath) {
|
|
|
77520
78099
|
});
|
|
77521
78100
|
continue;
|
|
77522
78101
|
}
|
|
77523
|
-
if (!(
|
|
78102
|
+
if (!(fs63.statSync(full).mode & 73)) {
|
|
77524
78103
|
findings.push({
|
|
77525
78104
|
check: "hooks",
|
|
77526
78105
|
severity: "warning",
|
|
@@ -77532,15 +78111,15 @@ function runDoctor(repoPath) {
|
|
|
77532
78111
|
}
|
|
77533
78112
|
}
|
|
77534
78113
|
for (const rel of listHarnessShellScripts(harnessDir)) {
|
|
77535
|
-
const full =
|
|
78114
|
+
const full = path70.join(harnessDir, rel);
|
|
77536
78115
|
let content;
|
|
77537
78116
|
try {
|
|
77538
|
-
content =
|
|
78117
|
+
content = fs63.readFileSync(full, "utf8");
|
|
77539
78118
|
} catch {
|
|
77540
78119
|
continue;
|
|
77541
78120
|
}
|
|
77542
78121
|
for (const machinery of LEGACY_MACHINERY_FILES) {
|
|
77543
|
-
if (
|
|
78122
|
+
if (fs63.existsSync(path70.join(harnessDir, machinery))) continue;
|
|
77544
78123
|
const base = machinery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
77545
78124
|
const loadRe = new RegExp(
|
|
77546
78125
|
`(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
|
|
@@ -77559,6 +78138,46 @@ function runDoctor(repoPath) {
|
|
|
77559
78138
|
break;
|
|
77560
78139
|
}
|
|
77561
78140
|
}
|
|
78141
|
+
const lineLedger = readLineLedger(repoPath);
|
|
78142
|
+
if (lineLedger) {
|
|
78143
|
+
const verificationStages = registry2?.verificationStages ?? [];
|
|
78144
|
+
const registeredIds = new Set((registry2?.stages ?? []).map((stage) => stage.id));
|
|
78145
|
+
for (const line of lineLedger.lines) {
|
|
78146
|
+
for (const stageId of line.stageIds) {
|
|
78147
|
+
if (verificationStages.includes(stageId)) {
|
|
78148
|
+
findings.push({
|
|
78149
|
+
check: "factory-lines",
|
|
78150
|
+
severity: "error",
|
|
78151
|
+
file: "stages.json",
|
|
78152
|
+
message: `line "${line.id}" stage "${stageId}" is listed in verificationStages`,
|
|
78153
|
+
remedy: "Remove it from verificationStages \u2014 line gate stages run via `har line gate <station>`. If it must gate every verify, ship it as a verification plugin instead."
|
|
78154
|
+
});
|
|
78155
|
+
}
|
|
78156
|
+
}
|
|
78157
|
+
const program = readInstalledLineProgram(repoPath, line.id);
|
|
78158
|
+
if (!program) {
|
|
78159
|
+
findings.push({
|
|
78160
|
+
check: "factory-lines",
|
|
78161
|
+
severity: "warning",
|
|
78162
|
+
file: line.programPath,
|
|
78163
|
+
message: `line "${line.id}" is in .har/lines.json but its program is missing or invalid`,
|
|
78164
|
+
remedy: `Reinstall it (\`har line add ${line.id}\`) or drop the entry from .har/lines.json`
|
|
78165
|
+
});
|
|
78166
|
+
continue;
|
|
78167
|
+
}
|
|
78168
|
+
for (const gateStage of program.gate.stages) {
|
|
78169
|
+
if (!registeredIds.has(gateStage.id)) {
|
|
78170
|
+
findings.push({
|
|
78171
|
+
check: "factory-lines",
|
|
78172
|
+
severity: "warning",
|
|
78173
|
+
file: line.programPath,
|
|
78174
|
+
message: `line "${line.id}" gate stage "${gateStage.id}" (from ${gateStage.fromStation}) is not registered in stages.json`,
|
|
78175
|
+
remedy: "Install the plugin that provides it, add it to the line bundle, or drop the gate tag"
|
|
78176
|
+
});
|
|
78177
|
+
}
|
|
78178
|
+
}
|
|
78179
|
+
}
|
|
78180
|
+
}
|
|
77562
78181
|
const checks = Object.keys(CHECK_LABELS).map(
|
|
77563
78182
|
(id) => skipped.has(id) ? { id, label: CHECK_LABELS[id], status: "skip" } : checkStatus(findings, id)
|
|
77564
78183
|
);
|
|
@@ -77604,8 +78223,8 @@ function summarizeDoctorReport(report) {
|
|
|
77604
78223
|
}
|
|
77605
78224
|
|
|
77606
78225
|
// src/harness/migrate-prompt.ts
|
|
77607
|
-
var
|
|
77608
|
-
var
|
|
78226
|
+
var fs64 = __toESM(require("fs"));
|
|
78227
|
+
var path71 = __toESM(require("path"));
|
|
77609
78228
|
init_paths();
|
|
77610
78229
|
init_file_ops();
|
|
77611
78230
|
init_manifest();
|
|
@@ -77719,13 +78338,13 @@ function loadTemplate2() {
|
|
|
77719
78338
|
if (!filePath) {
|
|
77720
78339
|
throw new Error("Migration prompt template not found: migration-prompt.md. Run npm run build.");
|
|
77721
78340
|
}
|
|
77722
|
-
return
|
|
78341
|
+
return fs64.readFileSync(filePath, "utf8");
|
|
77723
78342
|
}
|
|
77724
78343
|
function buildMigrationPrompt(plan, applied) {
|
|
77725
78344
|
return loadTemplate2().replace("{{MIGRATION_SECTION}}", buildMigrationSection(plan, applied));
|
|
77726
78345
|
}
|
|
77727
78346
|
function writeMigrationPrompt(repoPath, content) {
|
|
77728
|
-
const filePath =
|
|
78347
|
+
const filePath = path71.join(getHarnessDir(repoPath), MIGRATE_PROMPT_FILE);
|
|
77729
78348
|
writeFileSafe(filePath, content);
|
|
77730
78349
|
return filePath;
|
|
77731
78350
|
}
|
|
@@ -77733,11 +78352,11 @@ function writeMigrationPrompt(repoPath, content) {
|
|
|
77733
78352
|
// src/core/harness.ts
|
|
77734
78353
|
function listHarnessScripts(repoPath) {
|
|
77735
78354
|
const harnessDir = getHarnessDir(repoPath);
|
|
77736
|
-
if (!
|
|
77737
|
-
return
|
|
78355
|
+
if (!fs65.existsSync(harnessDir)) return [];
|
|
78356
|
+
return fs65.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
|
|
77738
78357
|
}
|
|
77739
78358
|
function describeProject(repoPath) {
|
|
77740
|
-
const resolved =
|
|
78359
|
+
const resolved = path72.resolve(repoPath);
|
|
77741
78360
|
const manifest = readManifest(resolved);
|
|
77742
78361
|
const present = harnessExists(resolved);
|
|
77743
78362
|
return {
|
|
@@ -77757,8 +78376,8 @@ function describeProject(repoPath) {
|
|
|
77757
78376
|
};
|
|
77758
78377
|
}
|
|
77759
78378
|
async function initHarness(options) {
|
|
77760
|
-
const repoPath =
|
|
77761
|
-
if (!
|
|
78379
|
+
const repoPath = path72.resolve(options.repoPath);
|
|
78380
|
+
if (!fs65.existsSync(repoPath)) {
|
|
77762
78381
|
throw new Error(`Path not found: ${repoPath}`);
|
|
77763
78382
|
}
|
|
77764
78383
|
const scaffold = scaffoldHarnessBoilerplate(repoPath, {
|
|
@@ -77779,9 +78398,9 @@ async function initHarness(options) {
|
|
|
77779
78398
|
};
|
|
77780
78399
|
}
|
|
77781
78400
|
async function maintainHarness(options) {
|
|
77782
|
-
const repoPath =
|
|
78401
|
+
const repoPath = path72.resolve(options.repoPath);
|
|
77783
78402
|
const harnessDir = getHarnessDir(repoPath);
|
|
77784
|
-
if (!
|
|
78403
|
+
if (!fs65.existsSync(harnessDir)) {
|
|
77785
78404
|
throw new Error('No .har/ found. Run "har onboard" first.');
|
|
77786
78405
|
}
|
|
77787
78406
|
let migration;
|
|
@@ -77858,8 +78477,8 @@ function addPlugin(repoPath, pluginId, options = {}) {
|
|
|
77858
78477
|
}
|
|
77859
78478
|
|
|
77860
78479
|
// src/harness/adaptation-prompt.ts
|
|
77861
|
-
var
|
|
77862
|
-
var
|
|
78480
|
+
var fs66 = __toESM(require("fs"));
|
|
78481
|
+
var path73 = __toESM(require("path"));
|
|
77863
78482
|
var readline5 = __toESM(require("readline"));
|
|
77864
78483
|
|
|
77865
78484
|
// src/utils/clipboard.ts
|
|
@@ -77950,7 +78569,7 @@ function loadTemplate3(name) {
|
|
|
77950
78569
|
if (!filePath) {
|
|
77951
78570
|
throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
|
|
77952
78571
|
}
|
|
77953
|
-
return
|
|
78572
|
+
return fs66.readFileSync(filePath, "utf8");
|
|
77954
78573
|
}
|
|
77955
78574
|
function applyProfilePlaceholders(content, profile) {
|
|
77956
78575
|
return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
|
|
@@ -77972,7 +78591,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
|
|
|
77972
78591
|
}
|
|
77973
78592
|
function writeAdaptationPrompt(repoPath, content) {
|
|
77974
78593
|
const harnessDir = getHarnessDir(repoPath);
|
|
77975
|
-
const filePath =
|
|
78594
|
+
const filePath = path73.join(harnessDir, ADAPTATION_PROMPT_FILE);
|
|
77976
78595
|
writeFileSafe(filePath, content);
|
|
77977
78596
|
return filePath;
|
|
77978
78597
|
}
|
|
@@ -77991,12 +78610,12 @@ function printAdaptationPrompt(content) {
|
|
|
77991
78610
|
}
|
|
77992
78611
|
function askYesNo2(question) {
|
|
77993
78612
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
77994
|
-
return new Promise((
|
|
78613
|
+
return new Promise((resolve66) => {
|
|
77995
78614
|
process.stderr.write(`${question} `);
|
|
77996
78615
|
rl.once("line", (answer) => {
|
|
77997
78616
|
rl.close();
|
|
77998
78617
|
const trimmed = answer.trim();
|
|
77999
|
-
|
|
78618
|
+
resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
|
|
78000
78619
|
});
|
|
78001
78620
|
});
|
|
78002
78621
|
}
|
|
@@ -78028,28 +78647,28 @@ async function offerAdaptationPromptClipboard(content, options = {}) {
|
|
|
78028
78647
|
}
|
|
78029
78648
|
|
|
78030
78649
|
// src/harness/cursor-rule.ts
|
|
78031
|
-
var
|
|
78032
|
-
var
|
|
78650
|
+
var fs67 = __toESM(require("fs"));
|
|
78651
|
+
var path74 = __toESM(require("path"));
|
|
78033
78652
|
var readline6 = __toESM(require("readline"));
|
|
78034
78653
|
init_file_ops();
|
|
78035
78654
|
init_logging();
|
|
78036
78655
|
init_paths();
|
|
78037
78656
|
var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
|
|
78038
78657
|
function getCursorRulePath(repoPath) {
|
|
78039
|
-
return
|
|
78658
|
+
return path74.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
|
|
78040
78659
|
}
|
|
78041
78660
|
function isCursorWorkspace(repoPath) {
|
|
78042
|
-
return
|
|
78661
|
+
return fs67.existsSync(path74.join(repoPath, ".cursor"));
|
|
78043
78662
|
}
|
|
78044
78663
|
function cursorRuleExists(repoPath) {
|
|
78045
|
-
return
|
|
78664
|
+
return fs67.existsSync(getCursorRulePath(repoPath));
|
|
78046
78665
|
}
|
|
78047
78666
|
function scaffoldCursorRule(repoPath) {
|
|
78048
78667
|
const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
|
|
78049
78668
|
if (!templatePath) {
|
|
78050
78669
|
throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
|
|
78051
78670
|
}
|
|
78052
|
-
const content =
|
|
78671
|
+
const content = fs67.readFileSync(templatePath, "utf8");
|
|
78053
78672
|
writeFileSafe(getCursorRulePath(repoPath), content);
|
|
78054
78673
|
}
|
|
78055
78674
|
async function handleCursorRule(options) {
|
|
@@ -78087,16 +78706,16 @@ async function promptScaffoldCursorRule(exists) {
|
|
|
78087
78706
|
}
|
|
78088
78707
|
async function askYesNo3(question) {
|
|
78089
78708
|
const rl = readline6.createInterface({ input: process.stdin, output: process.stderr });
|
|
78090
|
-
return new Promise((
|
|
78709
|
+
return new Promise((resolve66) => {
|
|
78091
78710
|
process.stderr.write(`${question} `);
|
|
78092
78711
|
rl.once("line", (answer) => {
|
|
78093
78712
|
rl.close();
|
|
78094
78713
|
const trimmed = answer.trim();
|
|
78095
78714
|
if (trimmed === "") {
|
|
78096
|
-
|
|
78715
|
+
resolve66(true);
|
|
78097
78716
|
return;
|
|
78098
78717
|
}
|
|
78099
|
-
|
|
78718
|
+
resolve66(/^y(es)?$/i.test(trimmed));
|
|
78100
78719
|
});
|
|
78101
78720
|
});
|
|
78102
78721
|
}
|
|
@@ -78140,7 +78759,7 @@ init_control_config();
|
|
|
78140
78759
|
|
|
78141
78760
|
// src/core/control-lifecycle.ts
|
|
78142
78761
|
var import_child_process18 = require("child_process");
|
|
78143
|
-
var
|
|
78762
|
+
var path75 = __toESM(require("path"));
|
|
78144
78763
|
|
|
78145
78764
|
// src/core/control-image.ts
|
|
78146
78765
|
init_package_version();
|
|
@@ -78167,10 +78786,10 @@ var CONTROL_DATA_VOLUME = "har_control_data";
|
|
|
78167
78786
|
var CONTROL_CONTAINER_PORT = 3847;
|
|
78168
78787
|
var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
|
|
78169
78788
|
function resolveControlDir() {
|
|
78170
|
-
return
|
|
78789
|
+
return path75.resolve(__dirname, "..", "control");
|
|
78171
78790
|
}
|
|
78172
78791
|
function resolveControlBuildContext() {
|
|
78173
|
-
return
|
|
78792
|
+
return path75.resolve(resolveControlDir(), "..");
|
|
78174
78793
|
}
|
|
78175
78794
|
function buildDockerRunArgs(options) {
|
|
78176
78795
|
const name = options.containerName ?? CONTROL_CONTAINER_NAME;
|
|
@@ -78226,7 +78845,7 @@ async function startMissionControl(options) {
|
|
|
78226
78845
|
"-t",
|
|
78227
78846
|
imageRef,
|
|
78228
78847
|
"-f",
|
|
78229
|
-
|
|
78848
|
+
path75.join(resolveControlDir(), "Dockerfile"),
|
|
78230
78849
|
resolveControlBuildContext()
|
|
78231
78850
|
]);
|
|
78232
78851
|
if (buildCode !== 0) {
|
|
@@ -78373,7 +78992,7 @@ async function ensureTelemetryInfrastructure(options) {
|
|
|
78373
78992
|
// src/core/run-service.ts
|
|
78374
78993
|
init_telemetry_config();
|
|
78375
78994
|
init_telemetry_env();
|
|
78376
|
-
var
|
|
78995
|
+
var path77 = __toESM(require("path"));
|
|
78377
78996
|
init_local_executor();
|
|
78378
78997
|
function extractShellOutput(result) {
|
|
78379
78998
|
const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
|
|
@@ -78596,12 +79215,12 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78596
79215
|
suffix: session.suffix,
|
|
78597
79216
|
createdAt: session.createdAt
|
|
78598
79217
|
});
|
|
78599
|
-
const envFile =
|
|
79218
|
+
const envFile = path77.join(session.workDir, `.env.agent.${options.agentId}`);
|
|
78600
79219
|
try {
|
|
78601
79220
|
appendTelemetryEnvToFile(envFile, {
|
|
78602
79221
|
sessionKey,
|
|
78603
79222
|
agentId: options.agentId,
|
|
78604
|
-
repoPath:
|
|
79223
|
+
repoPath: path77.resolve(options.repoPath),
|
|
78605
79224
|
workDir: session.workDir,
|
|
78606
79225
|
branch: session.branch,
|
|
78607
79226
|
suffix: session.suffix,
|
|
@@ -78722,9 +79341,9 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78722
79341
|
return envResult;
|
|
78723
79342
|
}
|
|
78724
79343
|
/**
|
|
78725
|
-
* Finish a session:
|
|
78726
|
-
*
|
|
78727
|
-
* can push it and open a PR.
|
|
79344
|
+
* Finish a session: reuse the last matching passing full validation (or
|
|
79345
|
+
* re-run verify when asked), then teardown. The session branch is kept so
|
|
79346
|
+
* the user can push it and open a PR.
|
|
78728
79347
|
*/
|
|
78729
79348
|
async completeEnvironment(options) {
|
|
78730
79349
|
const harnessRoot = resolveHarnessRoot(options.repoPath);
|
|
@@ -78736,9 +79355,11 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78736
79355
|
stderr: `No active session for agent ${options.agentId}. Run launch first.`
|
|
78737
79356
|
};
|
|
78738
79357
|
}
|
|
79358
|
+
const runVerify = shouldReverifyOnComplete(options);
|
|
78739
79359
|
let verification;
|
|
78740
79360
|
let validation2;
|
|
78741
|
-
|
|
79361
|
+
let reusedValidation = false;
|
|
79362
|
+
if (runVerify) {
|
|
78742
79363
|
const verify = await this.runVerification({
|
|
78743
79364
|
repoPath: options.repoPath,
|
|
78744
79365
|
agentId: options.agentId,
|
|
@@ -78751,11 +79372,27 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78751
79372
|
if (verify.code !== 0 || verification?.status !== "pass") {
|
|
78752
79373
|
return {
|
|
78753
79374
|
...verify,
|
|
78754
|
-
stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun
|
|
79375
|
+
stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun `complete --verify`, or use teardown to free the slot without claiming completion."
|
|
79376
|
+
};
|
|
79377
|
+
}
|
|
79378
|
+
} else {
|
|
79379
|
+
const checkoutDir = resolveValidationCheckoutDir({
|
|
79380
|
+
worktreePath: session.worktreePath,
|
|
79381
|
+
workDir: session.workDir,
|
|
79382
|
+
harnessRoot
|
|
79383
|
+
});
|
|
79384
|
+
validation2 = findPassingFullValidation({ checkoutDir, harnessRoot });
|
|
79385
|
+
if (!validation2) {
|
|
79386
|
+
return {
|
|
79387
|
+
code: 1,
|
|
79388
|
+
stdout: "",
|
|
79389
|
+
stderr: `No passing full validation matches the current worktree. The tree may have changed since the last \`har env verify --full\`. Re-run verification with: har env complete ${options.agentId} --verify
|
|
79390
|
+
Or free the slot without claiming completion: har env teardown ${options.agentId}`
|
|
78755
79391
|
};
|
|
78756
79392
|
}
|
|
79393
|
+
reusedValidation = true;
|
|
78757
79394
|
}
|
|
78758
|
-
if (session.workUnitId && session.attemptId &&
|
|
79395
|
+
if (session.workUnitId && session.attemptId && runVerify && !validation2) {
|
|
78759
79396
|
return {
|
|
78760
79397
|
code: 1,
|
|
78761
79398
|
stdout: "",
|
|
@@ -78779,11 +79416,13 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78779
79416
|
trigger: options.trigger
|
|
78780
79417
|
});
|
|
78781
79418
|
if (teardown.code !== 0) return { ...teardown, verification };
|
|
79419
|
+
const reusedNote = reusedValidation && validation2 ? `Reused passing full validation ${validation2.validationId} for tree ${validation2.treeHash}.
|
|
79420
|
+
` : "";
|
|
78782
79421
|
const branchNote = session.branch ? `Branch kept: ${session.branch} \u2014 push it with: git push -u origin ${session.branch}
|
|
78783
79422
|
` : "";
|
|
78784
79423
|
return {
|
|
78785
79424
|
...teardown,
|
|
78786
|
-
stdout: teardown.stdout + branchNote,
|
|
79425
|
+
stdout: teardown.stdout + reusedNote + branchNote,
|
|
78787
79426
|
branch: session.branch,
|
|
78788
79427
|
workDir: session.workDir,
|
|
78789
79428
|
worktreePath: session.worktreePath,
|
|
@@ -78822,6 +79461,10 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
78822
79461
|
return toEnvironmentRunResult(result);
|
|
78823
79462
|
}
|
|
78824
79463
|
};
|
|
79464
|
+
function shouldReverifyOnComplete(options) {
|
|
79465
|
+
if (options.verify === true) return true;
|
|
79466
|
+
return options.skipVerify === false;
|
|
79467
|
+
}
|
|
78825
79468
|
var defaultRunService = new RunService();
|
|
78826
79469
|
function createRunService(executor = localScriptExecutor) {
|
|
78827
79470
|
return new RunService(executor);
|
|
@@ -78843,9 +79486,9 @@ init_work_units();
|
|
|
78843
79486
|
init_manifest();
|
|
78844
79487
|
|
|
78845
79488
|
// src/core/cleanup-service.ts
|
|
78846
|
-
var
|
|
79489
|
+
var fs69 = __toESM(require("fs"));
|
|
78847
79490
|
var os23 = __toESM(require("os"));
|
|
78848
|
-
var
|
|
79491
|
+
var path78 = __toESM(require("path"));
|
|
78849
79492
|
var import_child_process20 = require("child_process");
|
|
78850
79493
|
init_control_sync();
|
|
78851
79494
|
init_control_repo_path();
|
|
@@ -78854,9 +79497,9 @@ init_manifest();
|
|
|
78854
79497
|
var DEFAULT_STALE_DAYS = 7;
|
|
78855
79498
|
function getWorktreesRoot() {
|
|
78856
79499
|
if (process.env.HAR_WORKTREES_ROOT) {
|
|
78857
|
-
return
|
|
79500
|
+
return path78.resolve(process.env.HAR_WORKTREES_ROOT);
|
|
78858
79501
|
}
|
|
78859
|
-
return
|
|
79502
|
+
return path78.join(process.env.HOME || os23.homedir(), "worktrees");
|
|
78860
79503
|
}
|
|
78861
79504
|
function runGit3(cwd, args) {
|
|
78862
79505
|
try {
|
|
@@ -78870,7 +79513,7 @@ function runGit3(cwd, args) {
|
|
|
78870
79513
|
}
|
|
78871
79514
|
}
|
|
78872
79515
|
function basenameRepo(repoPath) {
|
|
78873
|
-
return
|
|
79516
|
+
return path78.basename(canonicalizeControlRepoPath(repoPath));
|
|
78874
79517
|
}
|
|
78875
79518
|
function ageDaysFrom(iso) {
|
|
78876
79519
|
if (!iso) return void 0;
|
|
@@ -78882,12 +79525,12 @@ function matchesKeep(candidate, keep) {
|
|
|
78882
79525
|
if (!keep?.length) return false;
|
|
78883
79526
|
for (const pin of keep) {
|
|
78884
79527
|
if (pin.worktreePath && candidate.worktreePath) {
|
|
78885
|
-
if (
|
|
79528
|
+
if (path78.resolve(pin.worktreePath) === path78.resolve(candidate.worktreePath)) return true;
|
|
78886
79529
|
}
|
|
78887
79530
|
if (pin.repoPath && pin.agentId !== void 0 && candidate.agentId !== void 0) {
|
|
78888
79531
|
const repo = canonicalizeControlRepoPath(pin.repoPath);
|
|
78889
|
-
const candidateRepo =
|
|
78890
|
-
if ((repo === candidateRepo ||
|
|
79532
|
+
const candidateRepo = path78.resolve(candidate.repoPath);
|
|
79533
|
+
if ((repo === candidateRepo || path78.resolve(pin.repoPath) === candidateRepo) && pin.agentId === candidate.agentId) {
|
|
78891
79534
|
return true;
|
|
78892
79535
|
}
|
|
78893
79536
|
}
|
|
@@ -78942,20 +79585,20 @@ function classifySlotCandidate(repoPath, projectName, slot, options) {
|
|
|
78942
79585
|
return candidate;
|
|
78943
79586
|
}
|
|
78944
79587
|
function resolveRepoForWorktree(worktreePath) {
|
|
78945
|
-
if (!
|
|
79588
|
+
if (!fs69.existsSync(worktreePath)) return void 0;
|
|
78946
79589
|
const commonDir = runGit3(worktreePath, "rev-parse --git-common-dir");
|
|
78947
79590
|
if (!commonDir) return void 0;
|
|
78948
|
-
const gitRoot =
|
|
79591
|
+
const gitRoot = path78.resolve(worktreePath, commonDir, "..");
|
|
78949
79592
|
if (!readManifest(gitRoot)) return void 0;
|
|
78950
79593
|
return canonicalizeControlRepoPath(gitRoot);
|
|
78951
79594
|
}
|
|
78952
79595
|
function discoverOrphanWorktrees(knownPaths) {
|
|
78953
79596
|
const worktreesRoot = getWorktreesRoot();
|
|
78954
|
-
if (!
|
|
79597
|
+
if (!fs69.existsSync(worktreesRoot)) return [];
|
|
78955
79598
|
const orphans = [];
|
|
78956
|
-
for (const entry of
|
|
79599
|
+
for (const entry of fs69.readdirSync(worktreesRoot, { withFileTypes: true })) {
|
|
78957
79600
|
if (!entry.isDirectory() || !entry.name.includes("-har-agent-")) continue;
|
|
78958
|
-
const worktreePath =
|
|
79601
|
+
const worktreePath = path78.resolve(path78.join(worktreesRoot, entry.name));
|
|
78959
79602
|
if (knownPaths.has(worktreePath)) continue;
|
|
78960
79603
|
const repoPath = resolveRepoForWorktree(worktreePath) ?? worktreesRoot;
|
|
78961
79604
|
const porcelain = runGit3(worktreePath, "status --porcelain");
|
|
@@ -78980,14 +79623,14 @@ async function discoverCleanupCandidates(options = {}) {
|
|
|
78980
79623
|
const candidates = [];
|
|
78981
79624
|
const knownWorktreePaths = /* @__PURE__ */ new Set();
|
|
78982
79625
|
for (const repoPath of repoPaths) {
|
|
78983
|
-
if (!
|
|
79626
|
+
if (!fs69.existsSync(repoPath) || !readManifest(repoPath)) continue;
|
|
78984
79627
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
78985
79628
|
const canonicalRepo = canonicalizeControlRepoPath(harnessRoot);
|
|
78986
79629
|
const status = collectEnvironmentStatus(harnessRoot);
|
|
78987
79630
|
const projectName = basenameRepo(harnessRoot);
|
|
78988
79631
|
for (const slot of status.slots) {
|
|
78989
|
-
if (slot.worktreePath) knownWorktreePaths.add(
|
|
78990
|
-
if (slot.workDir) knownWorktreePaths.add(
|
|
79632
|
+
if (slot.worktreePath) knownWorktreePaths.add(path78.resolve(slot.worktreePath));
|
|
79633
|
+
if (slot.workDir) knownWorktreePaths.add(path78.resolve(slot.workDir));
|
|
78991
79634
|
const candidate = classifySlotCandidate(canonicalRepo, projectName, slot, {
|
|
78992
79635
|
staleDays: options.staleDays,
|
|
78993
79636
|
keep: options.keep
|
|
@@ -79047,8 +79690,8 @@ async function askYesNo4(question) {
|
|
|
79047
79690
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
79048
79691
|
const readline8 = await import("readline");
|
|
79049
79692
|
const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
|
|
79050
|
-
const answer = await new Promise((
|
|
79051
|
-
rl.question(question,
|
|
79693
|
+
const answer = await new Promise((resolve66) => {
|
|
79694
|
+
rl.question(question, resolve66);
|
|
79052
79695
|
});
|
|
79053
79696
|
rl.close();
|
|
79054
79697
|
return /^[Yy]$/.test(answer.trim());
|
|
@@ -79093,7 +79736,7 @@ async function executeCleanupCandidates(candidates, options = {}) {
|
|
|
79093
79736
|
continue;
|
|
79094
79737
|
}
|
|
79095
79738
|
if (candidate.kind === "orphan_worktree" && candidate.worktreePath) {
|
|
79096
|
-
if (
|
|
79739
|
+
if (fs69.existsSync(candidate.worktreePath)) {
|
|
79097
79740
|
const repoRoot = candidate.repoPath !== getWorktreesRoot() ? candidate.repoPath : resolveRepoForWorktree(candidate.worktreePath);
|
|
79098
79741
|
if (repoRoot) {
|
|
79099
79742
|
try {
|
|
@@ -79104,8 +79747,8 @@ async function executeCleanupCandidates(candidates, options = {}) {
|
|
|
79104
79747
|
} catch {
|
|
79105
79748
|
}
|
|
79106
79749
|
}
|
|
79107
|
-
if (
|
|
79108
|
-
|
|
79750
|
+
if (fs69.existsSync(candidate.worktreePath)) {
|
|
79751
|
+
fs69.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
79109
79752
|
}
|
|
79110
79753
|
}
|
|
79111
79754
|
outcomes.push({ candidate, ok: true });
|
|
@@ -79131,7 +79774,7 @@ function parseCleanupKeepPins(values) {
|
|
|
79131
79774
|
}
|
|
79132
79775
|
if (raw.startsWith("/") || raw.startsWith("~")) {
|
|
79133
79776
|
return {
|
|
79134
|
-
worktreePath: raw.startsWith("~") ?
|
|
79777
|
+
worktreePath: raw.startsWith("~") ? path78.join(process.env.HOME || os23.homedir(), raw.slice(2)) : path78.resolve(raw)
|
|
79135
79778
|
};
|
|
79136
79779
|
}
|
|
79137
79780
|
return { repoPath: raw };
|
|
@@ -79145,12 +79788,12 @@ init_stages();
|
|
|
79145
79788
|
init_logging();
|
|
79146
79789
|
function askYesNo5(question) {
|
|
79147
79790
|
const rl = readline7.createInterface({ input: process.stdin, output: process.stderr });
|
|
79148
|
-
return new Promise((
|
|
79791
|
+
return new Promise((resolve66) => {
|
|
79149
79792
|
process.stderr.write(`${question} `);
|
|
79150
79793
|
rl.once("line", (answer) => {
|
|
79151
79794
|
rl.close();
|
|
79152
79795
|
const trimmed = answer.trim();
|
|
79153
|
-
|
|
79796
|
+
resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
|
|
79154
79797
|
});
|
|
79155
79798
|
});
|
|
79156
79799
|
}
|
|
@@ -79251,21 +79894,21 @@ function warnIfDockerUnavailable(status = detectDockerStatus()) {
|
|
|
79251
79894
|
}
|
|
79252
79895
|
|
|
79253
79896
|
// src/core/onboarding-preferences.ts
|
|
79254
|
-
var
|
|
79897
|
+
var fs70 = __toESM(require("fs"));
|
|
79255
79898
|
var os24 = __toESM(require("os"));
|
|
79256
|
-
var
|
|
79899
|
+
var path79 = __toESM(require("path"));
|
|
79257
79900
|
init_schema2();
|
|
79258
79901
|
function getOnboardingPreferencesPath() {
|
|
79259
79902
|
if (process.env.HAR_PREFERENCES_PATH) {
|
|
79260
|
-
return
|
|
79903
|
+
return path79.resolve(process.env.HAR_PREFERENCES_PATH);
|
|
79261
79904
|
}
|
|
79262
|
-
return
|
|
79905
|
+
return path79.join(os24.homedir(), ".har", "preferences.json");
|
|
79263
79906
|
}
|
|
79264
79907
|
function readOnboardingPreferences() {
|
|
79265
79908
|
const preferencePath = getOnboardingPreferencesPath();
|
|
79266
79909
|
try {
|
|
79267
|
-
if (!
|
|
79268
|
-
return OnboardingPreferencesSchema.parse(JSON.parse(
|
|
79910
|
+
if (!fs70.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
|
|
79911
|
+
return OnboardingPreferencesSchema.parse(JSON.parse(fs70.readFileSync(preferencePath, "utf8")));
|
|
79269
79912
|
} catch {
|
|
79270
79913
|
return OnboardingPreferencesSchema.parse({});
|
|
79271
79914
|
}
|
|
@@ -79290,8 +79933,8 @@ function writeOnboardingPreferences(overrides) {
|
|
|
79290
79933
|
};
|
|
79291
79934
|
const parsed = OnboardingPreferencesSchema.parse(preference);
|
|
79292
79935
|
const preferencePath = getOnboardingPreferencesPath();
|
|
79293
|
-
|
|
79294
|
-
|
|
79936
|
+
fs70.mkdirSync(path79.dirname(preferencePath), { recursive: true });
|
|
79937
|
+
fs70.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
|
|
79295
79938
|
`);
|
|
79296
79939
|
return parsed;
|
|
79297
79940
|
}
|
|
@@ -79311,12 +79954,12 @@ Typical agent workflow:
|
|
|
79311
79954
|
har env launch <id> # new session from the main checkout's HEAD
|
|
79312
79955
|
# \u2026edit only under the printed work dir\u2026
|
|
79313
79956
|
har env verify <id> --full
|
|
79314
|
-
har env complete <id> # done:
|
|
79957
|
+
har env complete <id> # done: reuse last full validation + free the slot (branch kept for PR)
|
|
79315
79958
|
|
|
79316
79959
|
Slot lifecycle (do not confuse these):
|
|
79317
79960
|
launch start a NEW session (worktree from $REPO_ROOT HEAD)
|
|
79318
79961
|
recover resume a failed/partial launch (keeps the existing worktree)
|
|
79319
|
-
complete finish successfully: full
|
|
79962
|
+
complete finish successfully: reuse last matching full validation, then teardown (frees the slot)
|
|
79320
79963
|
teardown abandon / free the slot without a completion validation
|
|
79321
79964
|
|
|
79322
79965
|
Occupied slots always block a new launch:
|
|
@@ -79332,7 +79975,7 @@ var HAR_ENV_EPILOG = `Environment lifecycle:
|
|
|
79332
79975
|
launch <id> fresh session worktree from the main checkout's current HEAD
|
|
79333
79976
|
recover <id> alias for launch --resume (failed/starting only)
|
|
79334
79977
|
verify <id> run checks (--full before declaring done)
|
|
79335
|
-
complete <id> full
|
|
79978
|
+
complete <id> reuse last full validation + teardown; --verify to re-run; keeps branch (frees slot)
|
|
79336
79979
|
teardown <id> free the slot without recording a completion validation
|
|
79337
79980
|
|
|
79338
79981
|
Occupied slots:
|
|
@@ -79560,11 +80203,15 @@ var envCommand = {
|
|
|
79560
80203
|
handleTeardown
|
|
79561
80204
|
).command(
|
|
79562
80205
|
"complete <id>",
|
|
79563
|
-
"Finish a session:
|
|
79564
|
-
(y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("
|
|
80206
|
+
"Finish a session: reuse the last matching full validation, teardown, keep the branch for a PR",
|
|
80207
|
+
(y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("verify", {
|
|
79565
80208
|
type: "boolean",
|
|
79566
80209
|
default: false,
|
|
79567
|
-
describe: "
|
|
80210
|
+
describe: "Re-run full verification before teardown (use when the tree may have changed since the last passing --full)"
|
|
80211
|
+
}).option("skip-verify", {
|
|
80212
|
+
type: "boolean",
|
|
80213
|
+
hidden: true,
|
|
80214
|
+
describe: "Deprecated no-op: complete already skips re-verification by default"
|
|
79568
80215
|
}),
|
|
79569
80216
|
handleComplete
|
|
79570
80217
|
).command(
|
|
@@ -79677,7 +80324,7 @@ var envCommand = {
|
|
|
79677
80324
|
}
|
|
79678
80325
|
};
|
|
79679
80326
|
async function handleInit(argv) {
|
|
79680
|
-
const repoPath =
|
|
80327
|
+
const repoPath = path80.resolve(argv.repo);
|
|
79681
80328
|
const onboarding = resolveOnboardingOptions(argv);
|
|
79682
80329
|
header("har env init");
|
|
79683
80330
|
info(`Repository: ${repoPath}`);
|
|
@@ -79724,7 +80371,7 @@ async function handleInit(argv) {
|
|
|
79724
80371
|
}
|
|
79725
80372
|
}
|
|
79726
80373
|
async function handleMaintain(argv) {
|
|
79727
|
-
const repoPath =
|
|
80374
|
+
const repoPath = path80.resolve(argv.repo);
|
|
79728
80375
|
const onboarding = resolveOnboardingOptions(argv);
|
|
79729
80376
|
header("har env maintain");
|
|
79730
80377
|
info(`Repository: ${repoPath}`);
|
|
@@ -79879,7 +80526,7 @@ async function applyAgentIntegrations(options) {
|
|
|
79879
80526
|
}
|
|
79880
80527
|
}
|
|
79881
80528
|
async function handleAddPlugin(argv) {
|
|
79882
|
-
const repoPath =
|
|
80529
|
+
const repoPath = path80.resolve(argv.repo);
|
|
79883
80530
|
const available = listPluginIds();
|
|
79884
80531
|
if (argv.list) {
|
|
79885
80532
|
for (const id of available) {
|
|
@@ -79915,7 +80562,7 @@ async function handleAddPlugin(argv) {
|
|
|
79915
80562
|
console.error(` Docs: ${result.docsPath}`);
|
|
79916
80563
|
console.error("");
|
|
79917
80564
|
info(`Adaptation prompt saved to ${result.adaptPromptPath}`);
|
|
79918
|
-
const promptContent =
|
|
80565
|
+
const promptContent = fs71.readFileSync(path80.join(repoPath, result.adaptPromptPath), "utf8");
|
|
79919
80566
|
await offerAdaptationPromptClipboard(promptContent, { fileLabel: result.adaptPromptPath });
|
|
79920
80567
|
} catch (err) {
|
|
79921
80568
|
error(err.message);
|
|
@@ -79957,7 +80604,7 @@ async function handleAddStage(argv) {
|
|
|
79957
80604
|
});
|
|
79958
80605
|
}
|
|
79959
80606
|
async function handlePreflight(argv) {
|
|
79960
|
-
const repo =
|
|
80607
|
+
const repo = path80.resolve(argv.repo);
|
|
79961
80608
|
const agentId = validateAgentId(argv.id, repo);
|
|
79962
80609
|
const result = await preflightEnvironment({
|
|
79963
80610
|
repoPath: repo,
|
|
@@ -79973,7 +80620,7 @@ async function handlePreflight(argv) {
|
|
|
79973
80620
|
return finishCommand(result.code);
|
|
79974
80621
|
}
|
|
79975
80622
|
async function handleLaunch(argv) {
|
|
79976
|
-
const repo =
|
|
80623
|
+
const repo = path80.resolve(argv.repo);
|
|
79977
80624
|
const agentId = validateAgentId(argv.id, repo);
|
|
79978
80625
|
const result = await launchEnvironment({
|
|
79979
80626
|
repoPath: repo,
|
|
@@ -80015,7 +80662,7 @@ async function handleWorkLink(argv) {
|
|
|
80015
80662
|
error("Provide --link or both --source and --url");
|
|
80016
80663
|
return finishCommand(1);
|
|
80017
80664
|
}
|
|
80018
|
-
const harnessRoot = resolveHarnessRoot(
|
|
80665
|
+
const harnessRoot = resolveHarnessRoot(path80.resolve(argv.repo));
|
|
80019
80666
|
const links = argv.link ? [parseWorkLinkSpec(argv.link)] : [{ source: argv.source, url: argv.url, label: argv.label }];
|
|
80020
80667
|
try {
|
|
80021
80668
|
const record2 = addWorkUnitLinks(harnessRoot, argv.workId, links);
|
|
@@ -80041,7 +80688,7 @@ async function handleRecover(argv) {
|
|
|
80041
80688
|
});
|
|
80042
80689
|
}
|
|
80043
80690
|
async function handleVerify(argv) {
|
|
80044
|
-
const repo =
|
|
80691
|
+
const repo = path80.resolve(argv.repo);
|
|
80045
80692
|
const agentId = validateAgentId(argv.id, repo);
|
|
80046
80693
|
const result = await runVerification({
|
|
80047
80694
|
repoPath: repo,
|
|
@@ -80070,7 +80717,7 @@ async function handleVerify(argv) {
|
|
|
80070
80717
|
return finishCommand(result.code);
|
|
80071
80718
|
}
|
|
80072
80719
|
async function handleTeardown(argv) {
|
|
80073
|
-
const repo =
|
|
80720
|
+
const repo = path80.resolve(argv.repo);
|
|
80074
80721
|
const agentId = validateAgentId(argv.id, repo);
|
|
80075
80722
|
const result = await teardownEnvironment({
|
|
80076
80723
|
repoPath: repo,
|
|
@@ -80081,11 +80728,12 @@ async function handleTeardown(argv) {
|
|
|
80081
80728
|
return finishCommand(result.code);
|
|
80082
80729
|
}
|
|
80083
80730
|
async function handleComplete(argv) {
|
|
80084
|
-
const repo =
|
|
80731
|
+
const repo = path80.resolve(argv.repo);
|
|
80085
80732
|
const agentId = validateAgentId(argv.id, repo);
|
|
80086
80733
|
const result = await completeEnvironment({
|
|
80087
80734
|
repoPath: repo,
|
|
80088
80735
|
agentId,
|
|
80736
|
+
verify: argv.verify,
|
|
80089
80737
|
skipVerify: argv.skipVerify,
|
|
80090
80738
|
capture: false
|
|
80091
80739
|
});
|
|
@@ -80102,7 +80750,7 @@ async function handleComplete(argv) {
|
|
|
80102
80750
|
return finishCommand(result.code);
|
|
80103
80751
|
}
|
|
80104
80752
|
async function handleSetupInfra(argv) {
|
|
80105
|
-
const repo =
|
|
80753
|
+
const repo = path80.resolve(argv.repo);
|
|
80106
80754
|
const result = await runStage({
|
|
80107
80755
|
repoPath: repo,
|
|
80108
80756
|
kind: "setup",
|
|
@@ -80112,7 +80760,7 @@ async function handleSetupInfra(argv) {
|
|
|
80112
80760
|
return finishCommand(result.code ?? (result.status === "pass" ? 0 : 1));
|
|
80113
80761
|
}
|
|
80114
80762
|
async function handleAgent(argv) {
|
|
80115
|
-
const repo =
|
|
80763
|
+
const repo = path80.resolve(argv.repo);
|
|
80116
80764
|
const agentId = validateAgentId(argv.id, repo);
|
|
80117
80765
|
const result = await runAgentOp({
|
|
80118
80766
|
repoPath: repo,
|
|
@@ -80123,7 +80771,7 @@ async function handleAgent(argv) {
|
|
|
80123
80771
|
return finishCommand(result.code);
|
|
80124
80772
|
}
|
|
80125
80773
|
async function handleDoctor(argv) {
|
|
80126
|
-
const repoPath =
|
|
80774
|
+
const repoPath = path80.resolve(argv.repo);
|
|
80127
80775
|
const report = runDoctor(repoPath);
|
|
80128
80776
|
if (argv.json) {
|
|
80129
80777
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -80147,12 +80795,12 @@ async function confirm(question) {
|
|
|
80147
80795
|
if (!process.stdin.isTTY) return false;
|
|
80148
80796
|
const readline8 = await import("readline");
|
|
80149
80797
|
const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
|
|
80150
|
-
const answer = await new Promise((
|
|
80798
|
+
const answer = await new Promise((resolve66) => rl.question(question, resolve66));
|
|
80151
80799
|
rl.close();
|
|
80152
80800
|
return /^y(es)?$/i.test(answer.trim());
|
|
80153
80801
|
}
|
|
80154
80802
|
async function handleEject(argv) {
|
|
80155
|
-
const repoPath = resolveHarnessRoot(
|
|
80803
|
+
const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
|
|
80156
80804
|
header("har env eject");
|
|
80157
80805
|
warn("This vendors the complete HAR runtime into .har/runtime/ \u2014 from then on");
|
|
80158
80806
|
warn("YOU OWN that directory:");
|
|
@@ -80183,7 +80831,7 @@ async function handleEject(argv) {
|
|
|
80183
80831
|
return finishCommand(0);
|
|
80184
80832
|
}
|
|
80185
80833
|
async function handleAdopt(argv) {
|
|
80186
|
-
const repoPath = resolveHarnessRoot(
|
|
80834
|
+
const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
|
|
80187
80835
|
try {
|
|
80188
80836
|
const result = adoptHarness(repoPath);
|
|
80189
80837
|
success("Returned to the packaged runtime \u2014 .har/runtime/ removed, eject flag cleared.");
|
|
@@ -80198,7 +80846,7 @@ async function handleAdopt(argv) {
|
|
|
80198
80846
|
return finishCommand(0);
|
|
80199
80847
|
}
|
|
80200
80848
|
async function handleStatus(argv) {
|
|
80201
|
-
const repoPath =
|
|
80849
|
+
const repoPath = path80.resolve(argv.repo);
|
|
80202
80850
|
const result = await getEnvironmentStatus({
|
|
80203
80851
|
repoPath,
|
|
80204
80852
|
capture: false
|
|
@@ -80211,7 +80859,7 @@ async function handleStatus(argv) {
|
|
|
80211
80859
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
80212
80860
|
}
|
|
80213
80861
|
async function handleLogs(argv) {
|
|
80214
|
-
const repo =
|
|
80862
|
+
const repo = path80.resolve(argv.repo);
|
|
80215
80863
|
const agentId = validateAgentId(argv.id, repo);
|
|
80216
80864
|
const result = await getEnvironmentLogs({
|
|
80217
80865
|
repoPath: repo,
|
|
@@ -80223,7 +80871,7 @@ async function handleLogs(argv) {
|
|
|
80223
80871
|
return finishCommand(result.code);
|
|
80224
80872
|
}
|
|
80225
80873
|
async function handleRunStage(argv) {
|
|
80226
|
-
const repo =
|
|
80874
|
+
const repo = path80.resolve(argv.repo);
|
|
80227
80875
|
const agentId = validateAgentId(argv.id, repo);
|
|
80228
80876
|
if (!argv.stage) {
|
|
80229
80877
|
error("Missing stage id. Usage: har env run-stage <id> <stage> [args..]");
|
|
@@ -80249,7 +80897,7 @@ async function handleRunStage(argv) {
|
|
|
80249
80897
|
}
|
|
80250
80898
|
async function handleArtifacts(argv) {
|
|
80251
80899
|
const artifacts = listArtifacts({
|
|
80252
|
-
repoPath:
|
|
80900
|
+
repoPath: path80.resolve(argv.repo),
|
|
80253
80901
|
stageId: argv.stage
|
|
80254
80902
|
});
|
|
80255
80903
|
if (argv.json) {
|
|
@@ -80268,7 +80916,7 @@ async function handleArtifacts(argv) {
|
|
|
80268
80916
|
async function handleCleanup(argv) {
|
|
80269
80917
|
const keepValues = argv.keep?.split(",").map((value) => value.trim()).filter(Boolean);
|
|
80270
80918
|
const keep = parseCleanupKeepPins(keepValues);
|
|
80271
|
-
const repoPaths = argv.repo ? [
|
|
80919
|
+
const repoPaths = argv.repo ? [path80.resolve(argv.repo)] : void 0;
|
|
80272
80920
|
const plan = await discoverCleanupCandidates({
|
|
80273
80921
|
cwd: process.cwd(),
|
|
80274
80922
|
repoPaths,
|
|
@@ -80317,7 +80965,7 @@ async function handleCleanup(argv) {
|
|
|
80317
80965
|
return finishCommand(failed > 0 ? 1 : 0);
|
|
80318
80966
|
}
|
|
80319
80967
|
async function handleRunsList(argv) {
|
|
80320
|
-
const repoPath =
|
|
80968
|
+
const repoPath = path80.resolve(argv.repo);
|
|
80321
80969
|
const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
|
|
80322
80970
|
if (argv.json) {
|
|
80323
80971
|
process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
|
|
@@ -80334,7 +80982,7 @@ async function handleRunsList(argv) {
|
|
|
80334
80982
|
}
|
|
80335
80983
|
}
|
|
80336
80984
|
async function handleRunsGet(argv) {
|
|
80337
|
-
const run2 = getRun(
|
|
80985
|
+
const run2 = getRun(path80.resolve(argv.repo), argv.runId);
|
|
80338
80986
|
if (!run2) {
|
|
80339
80987
|
error(`Run not found: ${argv.runId}`);
|
|
80340
80988
|
return finishCommand(1);
|
|
@@ -80451,7 +81099,7 @@ function printNextSteps() {
|
|
|
80451
81099
|
}
|
|
80452
81100
|
|
|
80453
81101
|
// src/cli/commands/control.ts
|
|
80454
|
-
var
|
|
81102
|
+
var path85 = __toESM(require("path"));
|
|
80455
81103
|
init_control_config();
|
|
80456
81104
|
init_portal_targets();
|
|
80457
81105
|
|
|
@@ -80459,8 +81107,8 @@ init_portal_targets();
|
|
|
80459
81107
|
init_control_config();
|
|
80460
81108
|
|
|
80461
81109
|
// src/core/portal-connect.ts
|
|
80462
|
-
var
|
|
80463
|
-
var
|
|
81110
|
+
var fs72 = __toESM(require("fs"));
|
|
81111
|
+
var path81 = __toESM(require("path"));
|
|
80464
81112
|
var import_crypto2 = require("crypto");
|
|
80465
81113
|
init_control_repo_path();
|
|
80466
81114
|
init_control_registry();
|
|
@@ -80506,7 +81154,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
|
|
|
80506
81154
|
const options = typeof openUrlOrOptions === "function" ? { openUrl: openUrlOrOptions } : openUrlOrOptions;
|
|
80507
81155
|
const openUrl = options.openUrl ?? openBrowser;
|
|
80508
81156
|
const state = (0, import_crypto.randomUUID)();
|
|
80509
|
-
return new Promise((
|
|
81157
|
+
return new Promise((resolve66, reject) => {
|
|
80510
81158
|
let timer;
|
|
80511
81159
|
const server = http.createServer((req, res) => {
|
|
80512
81160
|
const requestUrl = new URL(req.url ?? "", "http://127.0.0.1");
|
|
@@ -80534,7 +81182,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
|
|
|
80534
81182
|
res.end("<h1>Logged in</h1><p>You can close this tab and return to the terminal.</p>");
|
|
80535
81183
|
cleanup();
|
|
80536
81184
|
const targets = parseCliTargetsParam(requestUrl.searchParams.get("targets"));
|
|
80537
|
-
|
|
81185
|
+
resolve66({
|
|
80538
81186
|
portalUrl,
|
|
80539
81187
|
token,
|
|
80540
81188
|
workspace: workspaceSlug ?? workspace,
|
|
@@ -80629,8 +81277,8 @@ function remainingConnections(connections, usedAliases) {
|
|
|
80629
81277
|
// src/core/portal-connect.ts
|
|
80630
81278
|
init_portal_targets();
|
|
80631
81279
|
function attachRepoIfPresent(alias, repoPath) {
|
|
80632
|
-
const resolved =
|
|
80633
|
-
if (!
|
|
81280
|
+
const resolved = path81.resolve(repoPath ?? ".");
|
|
81281
|
+
if (!fs72.existsSync(resolved)) return null;
|
|
80634
81282
|
const canonical = canonicalizeControlRepoPath(resolved);
|
|
80635
81283
|
attachRepoPortalTarget(canonical, alias);
|
|
80636
81284
|
recordRepoForControlSync(canonical);
|
|
@@ -80697,8 +81345,8 @@ async function runPortalConnect(input) {
|
|
|
80697
81345
|
return { record: record2, attachedRepo, extraAttached, mappedInBrowser: true };
|
|
80698
81346
|
}
|
|
80699
81347
|
function listRegisteredReposForConnect(repoPath) {
|
|
80700
|
-
const resolved = repoPath ?
|
|
80701
|
-
const current = resolved &&
|
|
81348
|
+
const resolved = repoPath ? path81.resolve(repoPath) : null;
|
|
81349
|
+
const current = resolved && fs72.existsSync(resolved) ? canonicalizeControlRepoPath(resolved) : null;
|
|
80702
81350
|
return [.../* @__PURE__ */ new Set([...current ? [current] : [], ...listRegisteredRepos()])];
|
|
80703
81351
|
}
|
|
80704
81352
|
|
|
@@ -81014,21 +81662,21 @@ init_control_port();
|
|
|
81014
81662
|
init_control_sync();
|
|
81015
81663
|
|
|
81016
81664
|
// src/core/control-sync-selection.ts
|
|
81017
|
-
var
|
|
81665
|
+
var fs73 = __toESM(require("fs"));
|
|
81018
81666
|
var os25 = __toESM(require("os"));
|
|
81019
|
-
var
|
|
81667
|
+
var path82 = __toESM(require("path"));
|
|
81020
81668
|
init_control_repo_path();
|
|
81021
81669
|
function getSelectionPath() {
|
|
81022
81670
|
if (process.env.HAR_CONTROL_SYNC_SELECTION_PATH) {
|
|
81023
|
-
return
|
|
81671
|
+
return path82.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
|
|
81024
81672
|
}
|
|
81025
|
-
return
|
|
81673
|
+
return path82.join(os25.homedir(), ".har", "sync-selection.json");
|
|
81026
81674
|
}
|
|
81027
81675
|
function readSyncSelection() {
|
|
81028
81676
|
const selectionPath = getSelectionPath();
|
|
81029
81677
|
try {
|
|
81030
|
-
if (!
|
|
81031
|
-
const parsed = JSON.parse(
|
|
81678
|
+
if (!fs73.existsSync(selectionPath)) return null;
|
|
81679
|
+
const parsed = JSON.parse(fs73.readFileSync(selectionPath, "utf8"));
|
|
81032
81680
|
if (!Array.isArray(parsed.repos)) return null;
|
|
81033
81681
|
return parsed.repos.map((repoPath) => canonicalizeControlRepoPath(repoPath));
|
|
81034
81682
|
} catch {
|
|
@@ -81038,8 +81686,8 @@ function readSyncSelection() {
|
|
|
81038
81686
|
function writeSyncSelection(repos) {
|
|
81039
81687
|
const selectionPath = getSelectionPath();
|
|
81040
81688
|
const canonical = Array.from(new Set(repos.map((repoPath) => canonicalizeControlRepoPath(repoPath))));
|
|
81041
|
-
|
|
81042
|
-
|
|
81689
|
+
fs73.mkdirSync(path82.dirname(selectionPath), { recursive: true });
|
|
81690
|
+
fs73.writeFileSync(selectionPath, JSON.stringify({ repos: canonical }, null, 2) + "\n");
|
|
81043
81691
|
}
|
|
81044
81692
|
function resolveSyncSelection(input) {
|
|
81045
81693
|
const { discovered, stored, forceSelect, isTTY } = input;
|
|
@@ -81066,8 +81714,8 @@ function resolveSyncSelection(input) {
|
|
|
81066
81714
|
}
|
|
81067
81715
|
|
|
81068
81716
|
// src/core/control-unregister.ts
|
|
81069
|
-
var
|
|
81070
|
-
var
|
|
81717
|
+
var fs74 = __toESM(require("fs"));
|
|
81718
|
+
var path83 = __toESM(require("path"));
|
|
81071
81719
|
init_control_repo_path();
|
|
81072
81720
|
init_control_registry();
|
|
81073
81721
|
init_control_config();
|
|
@@ -81077,9 +81725,9 @@ function listUnregisterWorktreeCandidates(repoPath) {
|
|
|
81077
81725
|
const harnessRoot = canonicalizeControlRepoPath(repoPath);
|
|
81078
81726
|
const byPath = /* @__PURE__ */ new Map();
|
|
81079
81727
|
const add = (partial2) => {
|
|
81080
|
-
const resolved =
|
|
81728
|
+
const resolved = path83.resolve(partial2.worktreePath);
|
|
81081
81729
|
if (!resolved || resolved === harnessRoot) return;
|
|
81082
|
-
const exists = partial2.exists ??
|
|
81730
|
+
const exists = partial2.exists ?? fs74.existsSync(resolved);
|
|
81083
81731
|
const prev = byPath.get(resolved);
|
|
81084
81732
|
if (prev) {
|
|
81085
81733
|
byPath.set(resolved, {
|
|
@@ -81130,8 +81778,8 @@ async function askYesNo6(question) {
|
|
|
81130
81778
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
81131
81779
|
const readline8 = await import("readline");
|
|
81132
81780
|
const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
|
|
81133
|
-
const answer = await new Promise((
|
|
81134
|
-
rl.question(question,
|
|
81781
|
+
const answer = await new Promise((resolve66) => {
|
|
81782
|
+
rl.question(question, resolve66);
|
|
81135
81783
|
});
|
|
81136
81784
|
rl.close();
|
|
81137
81785
|
return /^[Yy]$/.test(answer.trim());
|
|
@@ -81193,28 +81841,28 @@ async function teardownWorktrees(repoPath, candidates, dryRun) {
|
|
|
81193
81841
|
trigger: "cli"
|
|
81194
81842
|
});
|
|
81195
81843
|
if (result.code !== 0) {
|
|
81196
|
-
if (
|
|
81197
|
-
|
|
81844
|
+
if (fs74.existsSync(candidate.worktreePath)) {
|
|
81845
|
+
fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
81198
81846
|
}
|
|
81199
81847
|
}
|
|
81200
81848
|
outcomes.push({
|
|
81201
81849
|
path: candidate.worktreePath,
|
|
81202
81850
|
agentId: candidate.agentId,
|
|
81203
|
-
deleted: !
|
|
81204
|
-
error: result.code !== 0 &&
|
|
81851
|
+
deleted: !fs74.existsSync(candidate.worktreePath),
|
|
81852
|
+
error: result.code !== 0 && fs74.existsSync(candidate.worktreePath) ? result.stderr : void 0
|
|
81205
81853
|
});
|
|
81206
81854
|
} catch (err) {
|
|
81207
81855
|
const message = err instanceof Error ? err.message : String(err);
|
|
81208
81856
|
try {
|
|
81209
|
-
if (
|
|
81210
|
-
|
|
81857
|
+
if (fs74.existsSync(candidate.worktreePath)) {
|
|
81858
|
+
fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
81211
81859
|
}
|
|
81212
81860
|
} catch {
|
|
81213
81861
|
}
|
|
81214
81862
|
outcomes.push({
|
|
81215
81863
|
path: candidate.worktreePath,
|
|
81216
81864
|
agentId: candidate.agentId,
|
|
81217
|
-
deleted: !
|
|
81865
|
+
deleted: !fs74.existsSync(candidate.worktreePath),
|
|
81218
81866
|
error: message
|
|
81219
81867
|
});
|
|
81220
81868
|
}
|
|
@@ -81277,16 +81925,16 @@ init_control_config();
|
|
|
81277
81925
|
init_control_registry();
|
|
81278
81926
|
|
|
81279
81927
|
// src/core/harness-scrub.ts
|
|
81280
|
-
var
|
|
81281
|
-
var
|
|
81928
|
+
var fs75 = __toESM(require("fs"));
|
|
81929
|
+
var path84 = __toESM(require("path"));
|
|
81282
81930
|
var HARNESS_SCRUB_DIRS = ["runs", "validations", "state", "slots"];
|
|
81283
81931
|
function scrubLocalHarnessDirs(repoPath) {
|
|
81284
81932
|
const results = [];
|
|
81285
|
-
const resolved =
|
|
81286
|
-
if (!
|
|
81933
|
+
const resolved = path84.resolve(repoPath);
|
|
81934
|
+
if (!fs75.existsSync(resolved)) {
|
|
81287
81935
|
for (const directory of HARNESS_SCRUB_DIRS) {
|
|
81288
81936
|
results.push({
|
|
81289
|
-
path:
|
|
81937
|
+
path: path84.join(resolved, ".har", directory),
|
|
81290
81938
|
directory,
|
|
81291
81939
|
deleted: false,
|
|
81292
81940
|
error: "repository path does not exist"
|
|
@@ -81295,24 +81943,24 @@ function scrubLocalHarnessDirs(repoPath) {
|
|
|
81295
81943
|
return results;
|
|
81296
81944
|
}
|
|
81297
81945
|
for (const directory of HARNESS_SCRUB_DIRS) {
|
|
81298
|
-
const target =
|
|
81299
|
-
if (!
|
|
81946
|
+
const target = path84.join(resolved, ".har", directory);
|
|
81947
|
+
if (!fs75.existsSync(target)) {
|
|
81300
81948
|
results.push({ path: target, directory, deleted: true });
|
|
81301
81949
|
continue;
|
|
81302
81950
|
}
|
|
81303
81951
|
try {
|
|
81304
|
-
|
|
81952
|
+
fs75.rmSync(target, { recursive: true, force: true });
|
|
81305
81953
|
results.push({
|
|
81306
81954
|
path: target,
|
|
81307
81955
|
directory,
|
|
81308
|
-
deleted: !
|
|
81309
|
-
error:
|
|
81956
|
+
deleted: !fs75.existsSync(target),
|
|
81957
|
+
error: fs75.existsSync(target) ? "failed to remove directory" : void 0
|
|
81310
81958
|
});
|
|
81311
81959
|
} catch (err) {
|
|
81312
81960
|
results.push({
|
|
81313
81961
|
path: target,
|
|
81314
81962
|
directory,
|
|
81315
|
-
deleted: !
|
|
81963
|
+
deleted: !fs75.existsSync(target),
|
|
81316
81964
|
error: err instanceof Error ? err.message : String(err)
|
|
81317
81965
|
});
|
|
81318
81966
|
}
|
|
@@ -81323,7 +81971,7 @@ function scrubLocalHarnessForRepos(repoPaths) {
|
|
|
81323
81971
|
const results = [];
|
|
81324
81972
|
const seen = /* @__PURE__ */ new Set();
|
|
81325
81973
|
for (const repoPath of repoPaths) {
|
|
81326
|
-
const key =
|
|
81974
|
+
const key = path84.resolve(repoPath);
|
|
81327
81975
|
if (seen.has(key)) continue;
|
|
81328
81976
|
seen.add(key);
|
|
81329
81977
|
results.push(...scrubLocalHarnessDirs(key));
|
|
@@ -81336,8 +81984,8 @@ async function askYesNo7(question) {
|
|
|
81336
81984
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
81337
81985
|
const readline8 = await import("readline");
|
|
81338
81986
|
const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
|
|
81339
|
-
const answer = await new Promise((
|
|
81340
|
-
rl.question(question,
|
|
81987
|
+
const answer = await new Promise((resolve66) => {
|
|
81988
|
+
rl.question(question, resolve66);
|
|
81341
81989
|
});
|
|
81342
81990
|
rl.close();
|
|
81343
81991
|
return /^[Yy]$/.test(answer.trim());
|
|
@@ -81616,7 +82264,7 @@ async function handleDown() {
|
|
|
81616
82264
|
return finishCommand(code);
|
|
81617
82265
|
}
|
|
81618
82266
|
async function handleRegister(argv) {
|
|
81619
|
-
const repoPath =
|
|
82267
|
+
const repoPath = path85.resolve(argv.repo);
|
|
81620
82268
|
header("har control register");
|
|
81621
82269
|
info(`Repository: ${repoPath}`);
|
|
81622
82270
|
try {
|
|
@@ -81714,7 +82362,7 @@ async function handleReset(argv) {
|
|
|
81714
82362
|
}
|
|
81715
82363
|
}
|
|
81716
82364
|
async function handleUnregister(argv) {
|
|
81717
|
-
const repoPath =
|
|
82365
|
+
const repoPath = path85.resolve(argv.repo);
|
|
81718
82366
|
const worktrees = listUnregisterWorktreeCandidates(repoPath);
|
|
81719
82367
|
try {
|
|
81720
82368
|
let deleteWorktrees = argv.deleteWorktrees;
|
|
@@ -81995,12 +82643,12 @@ async function handleTrajectory(argv) {
|
|
|
81995
82643
|
}
|
|
81996
82644
|
|
|
81997
82645
|
// src/cli/commands/hooks.ts
|
|
81998
|
-
var
|
|
82646
|
+
var path87 = __toESM(require("path"));
|
|
81999
82647
|
init_hooks();
|
|
82000
82648
|
|
|
82001
82649
|
// src/core/claude-hooks.ts
|
|
82002
|
-
var
|
|
82003
|
-
var
|
|
82650
|
+
var fs76 = __toESM(require("fs"));
|
|
82651
|
+
var path86 = __toESM(require("path"));
|
|
82004
82652
|
init_paths();
|
|
82005
82653
|
var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
|
|
82006
82654
|
var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
|
|
@@ -82012,8 +82660,8 @@ function isHarGuardEntry(entry) {
|
|
|
82012
82660
|
);
|
|
82013
82661
|
}
|
|
82014
82662
|
function readSettings(settingsPath) {
|
|
82015
|
-
if (!
|
|
82016
|
-
const raw =
|
|
82663
|
+
if (!fs76.existsSync(settingsPath)) return {};
|
|
82664
|
+
const raw = fs76.readFileSync(settingsPath, "utf8").trim();
|
|
82017
82665
|
if (raw === "") return {};
|
|
82018
82666
|
const parsed = JSON.parse(raw);
|
|
82019
82667
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -82022,19 +82670,19 @@ function readSettings(settingsPath) {
|
|
|
82022
82670
|
return parsed;
|
|
82023
82671
|
}
|
|
82024
82672
|
function writeSettings(settingsPath, settings) {
|
|
82025
|
-
|
|
82026
|
-
|
|
82673
|
+
fs76.mkdirSync(path86.dirname(settingsPath), { recursive: true });
|
|
82674
|
+
fs76.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
82027
82675
|
}
|
|
82028
82676
|
function installClaudeGuard(repoPath) {
|
|
82029
82677
|
const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
|
|
82030
82678
|
if (!templatePath) {
|
|
82031
82679
|
throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
|
|
82032
82680
|
}
|
|
82033
|
-
const guardScript =
|
|
82034
|
-
|
|
82035
|
-
|
|
82036
|
-
|
|
82037
|
-
const settingsPath =
|
|
82681
|
+
const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
82682
|
+
fs76.mkdirSync(path86.dirname(guardScript), { recursive: true });
|
|
82683
|
+
fs76.copyFileSync(templatePath, guardScript);
|
|
82684
|
+
fs76.chmodSync(guardScript, 493);
|
|
82685
|
+
const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
82038
82686
|
const settings = readSettings(settingsPath);
|
|
82039
82687
|
const hooks = settings.hooks ?? {};
|
|
82040
82688
|
const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
|
|
@@ -82048,15 +82696,15 @@ function installClaudeGuard(repoPath) {
|
|
|
82048
82696
|
}
|
|
82049
82697
|
function uninstallClaudeGuard(repoPath) {
|
|
82050
82698
|
let removed = false;
|
|
82051
|
-
const guardScript =
|
|
82052
|
-
if (
|
|
82053
|
-
|
|
82699
|
+
const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
82700
|
+
if (fs76.existsSync(guardScript)) {
|
|
82701
|
+
fs76.rmSync(guardScript);
|
|
82054
82702
|
removed = true;
|
|
82055
|
-
const parent =
|
|
82056
|
-
if (
|
|
82703
|
+
const parent = path86.dirname(guardScript);
|
|
82704
|
+
if (fs76.existsSync(parent) && fs76.readdirSync(parent).length === 0) fs76.rmdirSync(parent);
|
|
82057
82705
|
}
|
|
82058
|
-
const settingsPath =
|
|
82059
|
-
if (
|
|
82706
|
+
const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
82707
|
+
if (fs76.existsSync(settingsPath)) {
|
|
82060
82708
|
const settings = readSettings(settingsPath);
|
|
82061
82709
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
82062
82710
|
if (preToolUse) {
|
|
@@ -82081,8 +82729,8 @@ function uninstallClaudeGuard(repoPath) {
|
|
|
82081
82729
|
return { removed };
|
|
82082
82730
|
}
|
|
82083
82731
|
function claudeGuardInstalled(repoPath) {
|
|
82084
|
-
const settingsPath =
|
|
82085
|
-
if (!
|
|
82732
|
+
const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
82733
|
+
if (!fs76.existsSync(settingsPath)) return false;
|
|
82086
82734
|
try {
|
|
82087
82735
|
const settings = readSettings(settingsPath);
|
|
82088
82736
|
return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
|
|
@@ -82099,7 +82747,7 @@ function repoOption(y) {
|
|
|
82099
82747
|
function handleInstall2(argv) {
|
|
82100
82748
|
try {
|
|
82101
82749
|
if (argv.claude) {
|
|
82102
|
-
const result2 = installClaudeGuard(
|
|
82750
|
+
const result2 = installClaudeGuard(path87.resolve(argv.repo));
|
|
82103
82751
|
success("Claude Code worktree guard installed.");
|
|
82104
82752
|
info(`Guard script: ${result2.guardScript}`);
|
|
82105
82753
|
info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
|
|
@@ -82107,7 +82755,7 @@ function handleInstall2(argv) {
|
|
|
82107
82755
|
info("Commit both files so the guard travels with the repo.");
|
|
82108
82756
|
return;
|
|
82109
82757
|
}
|
|
82110
|
-
const result = installHooks({ repoPath:
|
|
82758
|
+
const result = installHooks({ repoPath: path87.resolve(argv.repo), force: argv.force });
|
|
82111
82759
|
success(`Commit gate installed in ${result.hooksDir}`);
|
|
82112
82760
|
info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
|
|
82113
82761
|
info("Commits of unverified change batches will be blocked in agent worktrees.");
|
|
@@ -82120,7 +82768,7 @@ function handleInstall2(argv) {
|
|
|
82120
82768
|
function handleUninstall(argv) {
|
|
82121
82769
|
try {
|
|
82122
82770
|
if (argv.claude) {
|
|
82123
|
-
const result2 = uninstallClaudeGuard(
|
|
82771
|
+
const result2 = uninstallClaudeGuard(path87.resolve(argv.repo));
|
|
82124
82772
|
if (result2.removed) {
|
|
82125
82773
|
success("Claude Code worktree guard removed.");
|
|
82126
82774
|
} else {
|
|
@@ -82128,7 +82776,7 @@ function handleUninstall(argv) {
|
|
|
82128
82776
|
}
|
|
82129
82777
|
return;
|
|
82130
82778
|
}
|
|
82131
|
-
const result = uninstallHooks(
|
|
82779
|
+
const result = uninstallHooks(path87.resolve(argv.repo));
|
|
82132
82780
|
if (result.removed) {
|
|
82133
82781
|
success(`Commit gate removed from ${result.hooksDir}`);
|
|
82134
82782
|
} else {
|
|
@@ -82141,7 +82789,7 @@ function handleUninstall(argv) {
|
|
|
82141
82789
|
}
|
|
82142
82790
|
function handleStatus2(argv) {
|
|
82143
82791
|
try {
|
|
82144
|
-
const status = getHooksStatus(
|
|
82792
|
+
const status = getHooksStatus(path87.resolve(argv.repo));
|
|
82145
82793
|
if (argv.json) {
|
|
82146
82794
|
process.stdout.write(`${JSON.stringify(status, null, 2)}
|
|
82147
82795
|
`);
|
|
@@ -82159,7 +82807,7 @@ function handleStatus2(argv) {
|
|
|
82159
82807
|
);
|
|
82160
82808
|
info(`Effective: ${status.effectiveMode} (in this checkout)`);
|
|
82161
82809
|
info(
|
|
82162
|
-
`Claude guard: ${claudeGuardInstalled(
|
|
82810
|
+
`Claude guard: ${claudeGuardInstalled(path87.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
|
|
82163
82811
|
);
|
|
82164
82812
|
} catch (err) {
|
|
82165
82813
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -82420,10 +83068,10 @@ function assignProp(target, prop, value) {
|
|
|
82420
83068
|
configurable: true
|
|
82421
83069
|
});
|
|
82422
83070
|
}
|
|
82423
|
-
function getElementAtPath(obj,
|
|
82424
|
-
if (!
|
|
83071
|
+
function getElementAtPath(obj, path97) {
|
|
83072
|
+
if (!path97)
|
|
82425
83073
|
return obj;
|
|
82426
|
-
return
|
|
83074
|
+
return path97.reduce((acc, key) => acc?.[key], obj);
|
|
82427
83075
|
}
|
|
82428
83076
|
function promiseAllObject(promisesObj) {
|
|
82429
83077
|
const keys = Object.keys(promisesObj);
|
|
@@ -82743,11 +83391,11 @@ function aborted(x, startIndex = 0) {
|
|
|
82743
83391
|
}
|
|
82744
83392
|
return false;
|
|
82745
83393
|
}
|
|
82746
|
-
function prefixIssues(
|
|
83394
|
+
function prefixIssues(path97, issues) {
|
|
82747
83395
|
return issues.map((iss) => {
|
|
82748
83396
|
var _a2;
|
|
82749
83397
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
82750
|
-
iss.path.unshift(
|
|
83398
|
+
iss.path.unshift(path97);
|
|
82751
83399
|
return iss;
|
|
82752
83400
|
});
|
|
82753
83401
|
}
|
|
@@ -88070,7 +88718,7 @@ var Protocol = class {
|
|
|
88070
88718
|
return;
|
|
88071
88719
|
}
|
|
88072
88720
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
88073
|
-
await new Promise((
|
|
88721
|
+
await new Promise((resolve66) => setTimeout(resolve66, pollInterval));
|
|
88074
88722
|
options?.signal?.throwIfAborted();
|
|
88075
88723
|
}
|
|
88076
88724
|
} catch (error3) {
|
|
@@ -88087,7 +88735,7 @@ var Protocol = class {
|
|
|
88087
88735
|
*/
|
|
88088
88736
|
request(request, resultSchema, options) {
|
|
88089
88737
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
88090
|
-
return new Promise((
|
|
88738
|
+
return new Promise((resolve66, reject) => {
|
|
88091
88739
|
const earlyReject = (error3) => {
|
|
88092
88740
|
reject(error3);
|
|
88093
88741
|
};
|
|
@@ -88165,7 +88813,7 @@ var Protocol = class {
|
|
|
88165
88813
|
if (!parseResult.success) {
|
|
88166
88814
|
reject(parseResult.error);
|
|
88167
88815
|
} else {
|
|
88168
|
-
|
|
88816
|
+
resolve66(parseResult.data);
|
|
88169
88817
|
}
|
|
88170
88818
|
} catch (error3) {
|
|
88171
88819
|
reject(error3);
|
|
@@ -88426,12 +89074,12 @@ var Protocol = class {
|
|
|
88426
89074
|
}
|
|
88427
89075
|
} catch {
|
|
88428
89076
|
}
|
|
88429
|
-
return new Promise((
|
|
89077
|
+
return new Promise((resolve66, reject) => {
|
|
88430
89078
|
if (signal.aborted) {
|
|
88431
89079
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
88432
89080
|
return;
|
|
88433
89081
|
}
|
|
88434
|
-
const timeoutId = setTimeout(
|
|
89082
|
+
const timeoutId = setTimeout(resolve66, interval);
|
|
88435
89083
|
signal.addEventListener("abort", () => {
|
|
88436
89084
|
clearTimeout(timeoutId);
|
|
88437
89085
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -89221,7 +89869,7 @@ var Server = class extends Protocol {
|
|
|
89221
89869
|
};
|
|
89222
89870
|
|
|
89223
89871
|
// src/mcp/server.ts
|
|
89224
|
-
var
|
|
89872
|
+
var path90 = __toESM(require("path"));
|
|
89225
89873
|
|
|
89226
89874
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
89227
89875
|
var import_node_process3 = __toESM(require("node:process"), 1);
|
|
@@ -89304,12 +89952,12 @@ var StdioServerTransport = class {
|
|
|
89304
89952
|
this.onclose?.();
|
|
89305
89953
|
}
|
|
89306
89954
|
send(message) {
|
|
89307
|
-
return new Promise((
|
|
89955
|
+
return new Promise((resolve66) => {
|
|
89308
89956
|
const json = serializeMessage(message);
|
|
89309
89957
|
if (this._stdout.write(json)) {
|
|
89310
|
-
|
|
89958
|
+
resolve66();
|
|
89311
89959
|
} else {
|
|
89312
|
-
this._stdout.once("drain",
|
|
89960
|
+
this._stdout.once("drain", resolve66);
|
|
89313
89961
|
}
|
|
89314
89962
|
});
|
|
89315
89963
|
}
|
|
@@ -89319,6 +89967,387 @@ var StdioServerTransport = class {
|
|
|
89319
89967
|
init_package_version();
|
|
89320
89968
|
init_telemetry_config();
|
|
89321
89969
|
init_runs();
|
|
89970
|
+
|
|
89971
|
+
// src/core/lines.ts
|
|
89972
|
+
var path89 = __toESM(require("path"));
|
|
89973
|
+
|
|
89974
|
+
// src/harness/line-create.ts
|
|
89975
|
+
var fs77 = __toESM(require("fs"));
|
|
89976
|
+
var path88 = __toESM(require("path"));
|
|
89977
|
+
init_paths();
|
|
89978
|
+
init_schema2();
|
|
89979
|
+
var LINE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
89980
|
+
function renderSkeleton(templatePath, tokens) {
|
|
89981
|
+
let content = fs77.readFileSync(templatePath, "utf8");
|
|
89982
|
+
for (const [token, value] of Object.entries(tokens)) {
|
|
89983
|
+
content = content.replace(new RegExp(token, "g"), value);
|
|
89984
|
+
}
|
|
89985
|
+
return content;
|
|
89986
|
+
}
|
|
89987
|
+
function createLine(repoPath, options) {
|
|
89988
|
+
const resolved = path88.resolve(repoPath);
|
|
89989
|
+
if (!harnessExists(resolved)) {
|
|
89990
|
+
throw new Error('No .har/ harness found. Run "har onboard" first.');
|
|
89991
|
+
}
|
|
89992
|
+
const id = options.id.trim();
|
|
89993
|
+
if (!LINE_ID_PATTERN.test(id)) {
|
|
89994
|
+
throw new Error(
|
|
89995
|
+
`Invalid line id "${id}". Use lowercase letters, digits, dots, dashes (e.g. "onboarding-line").`
|
|
89996
|
+
);
|
|
89997
|
+
}
|
|
89998
|
+
const lineDir = path88.join(resolved, LOCAL_LINES_DIR, id);
|
|
89999
|
+
const lineDirRel2 = `${LOCAL_LINES_DIR}/${id}`;
|
|
90000
|
+
if (fs77.existsSync(lineDir) && !options.force) {
|
|
90001
|
+
throw new Error(`Line already exists: ${lineDirRel2}/. Use --force to overwrite.`);
|
|
90002
|
+
}
|
|
90003
|
+
const stationIds = options.stations?.length ? options.stations : ["S1", "S2"];
|
|
90004
|
+
const title = options.title ?? `${id} factory line`;
|
|
90005
|
+
const gateStageId = `${id}-gate`;
|
|
90006
|
+
const withGateStage = options.gateStage ?? true;
|
|
90007
|
+
const program = LineProgramSchema.parse({
|
|
90008
|
+
contractVersion: LINE_CONTRACT_VERSION,
|
|
90009
|
+
id,
|
|
90010
|
+
title,
|
|
90011
|
+
description: options.description ?? 'Describe the program: what it produces, and what "done" means at the last station.',
|
|
90012
|
+
skills: [{ id: "factory-line", role: "orchestrator", install: "repo:.claude/skills/factory-line" }],
|
|
90013
|
+
mcp: [],
|
|
90014
|
+
plugins: [],
|
|
90015
|
+
stations: stationIds.map((stationId, i) => ({
|
|
90016
|
+
id: stationId,
|
|
90017
|
+
title: `Station ${i + 1}`,
|
|
90018
|
+
description: "What this station produces.",
|
|
90019
|
+
work: { source: "none", ids: [], optional: true }
|
|
90020
|
+
})),
|
|
90021
|
+
gate: {
|
|
90022
|
+
cumulative: true,
|
|
90023
|
+
optInEnv: options.optInEnv ?? null,
|
|
90024
|
+
stages: withGateStage ? [{ id: gateStageId, fromStation: stationIds[0], tier: "full" }] : []
|
|
90025
|
+
},
|
|
90026
|
+
extraStages: withGateStage ? [
|
|
90027
|
+
{
|
|
90028
|
+
id: gateStageId,
|
|
90029
|
+
kind: "test",
|
|
90030
|
+
tier: "full",
|
|
90031
|
+
description: `Line-owned gate for ${id} \u2014 registered, never on verify`
|
|
90032
|
+
}
|
|
90033
|
+
] : [],
|
|
90034
|
+
handoff: {
|
|
90035
|
+
autonomousShip: false,
|
|
90036
|
+
waitFor: "human review before merge or release"
|
|
90037
|
+
},
|
|
90038
|
+
prototypeNotes: []
|
|
90039
|
+
});
|
|
90040
|
+
const manifest = LineManifestSchema.parse({
|
|
90041
|
+
kind: LINE_BUNDLE_KIND,
|
|
90042
|
+
id,
|
|
90043
|
+
title,
|
|
90044
|
+
program: "line.json",
|
|
90045
|
+
files: withGateStage ? [{ src: `stages/${gateStageId}.sh`, dest: `.har/stages/${gateStageId}.sh`, executable: true }] : [],
|
|
90046
|
+
stages: withGateStage ? [
|
|
90047
|
+
{
|
|
90048
|
+
id: gateStageId,
|
|
90049
|
+
kind: "test",
|
|
90050
|
+
description: `Line-owned gate for ${id} \u2014 registered, never on verify`,
|
|
90051
|
+
script: `stages/${gateStageId}.sh`,
|
|
90052
|
+
requiresAgentId: true,
|
|
90053
|
+
artifacts: [
|
|
90054
|
+
{
|
|
90055
|
+
path: `.har/artifacts/${gateStageId}`,
|
|
90056
|
+
kind: "directory",
|
|
90057
|
+
description: `Artifacts for the ${gateStageId} stage`
|
|
90058
|
+
}
|
|
90059
|
+
],
|
|
90060
|
+
tier: "full"
|
|
90061
|
+
}
|
|
90062
|
+
] : [],
|
|
90063
|
+
nextSteps: [
|
|
90064
|
+
`Edit ${lineDirRel2}/line.json \u2014 name the stations and bind tracker work`,
|
|
90065
|
+
...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
|
|
90066
|
+
`har line status ${id}`,
|
|
90067
|
+
`har line gate ${stationIds[0]} --line ${id}`
|
|
90068
|
+
],
|
|
90069
|
+
docsPath: `${lineDirRel2}/README.md`
|
|
90070
|
+
});
|
|
90071
|
+
const filesWritten = [];
|
|
90072
|
+
const write = (rel, content, executable = false) => {
|
|
90073
|
+
const abs = path88.join(lineDir, rel);
|
|
90074
|
+
fs77.mkdirSync(path88.dirname(abs), { recursive: true });
|
|
90075
|
+
fs77.writeFileSync(abs, content);
|
|
90076
|
+
if (executable) fs77.chmodSync(abs, 493);
|
|
90077
|
+
filesWritten.push(`${lineDirRel2}/${rel}`);
|
|
90078
|
+
};
|
|
90079
|
+
write("line.manifest.json", `${JSON.stringify(manifest, null, 2)}
|
|
90080
|
+
`);
|
|
90081
|
+
write("line.json", `${JSON.stringify(program, null, 2)}
|
|
90082
|
+
`);
|
|
90083
|
+
if (withGateStage) {
|
|
90084
|
+
const stageScript = renderSkeleton(
|
|
90085
|
+
path88.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
|
|
90086
|
+
{
|
|
90087
|
+
__STAGE_ID__: gateStageId,
|
|
90088
|
+
__STAGE_KIND__: "test",
|
|
90089
|
+
__STAGE_DESCRIPTION__: `Line-owned gate for ${id} (not on verify)`
|
|
90090
|
+
}
|
|
90091
|
+
);
|
|
90092
|
+
write(`stages/${gateStageId}.sh`, stageScript, true);
|
|
90093
|
+
}
|
|
90094
|
+
write("README.md", renderLineReadme(id, title, stationIds, gateStageId, withGateStage));
|
|
90095
|
+
return {
|
|
90096
|
+
lineId: id,
|
|
90097
|
+
lineDir,
|
|
90098
|
+
filesWritten,
|
|
90099
|
+
nextSteps: [
|
|
90100
|
+
`Edit ${lineDirRel2}/line.json \u2014 stations, work binding, gate tags`,
|
|
90101
|
+
...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
|
|
90102
|
+
`Install it: har line add ${id} (re-run with --force after changes)`,
|
|
90103
|
+
`Installing writes .har/ADAPT-PROMPT-line-${id}.md \u2014 a ready adaptation prompt for your coding agent`,
|
|
90104
|
+
"Publishing later (npm/git) needs zero format changes \u2014 see the README"
|
|
90105
|
+
]
|
|
90106
|
+
};
|
|
90107
|
+
}
|
|
90108
|
+
function renderLineReadme(id, title, stationIds, gateStageId, withGateStage) {
|
|
90109
|
+
return `# ${title}
|
|
90110
|
+
|
|
90111
|
+
A HAR **factory line**: an ordered program of stations plus a cumulative gate.
|
|
90112
|
+
|
|
90113
|
+
A line is not a verification plugin. Installing it registers stages but never
|
|
90114
|
+
adds them to \`verificationStages\` \u2014 default \`har env verify --full\` stays
|
|
90115
|
+
exactly as fast as it was. Line gate stages run on demand:
|
|
90116
|
+
|
|
90117
|
+
\`\`\`bash
|
|
90118
|
+
har line gate ${stationIds[0]} --line ${id}
|
|
90119
|
+
\`\`\`
|
|
90120
|
+
|
|
90121
|
+
If a check should gate *every* verify, it belongs in a verification plugin
|
|
90122
|
+
(\`har plugin create\` / \`har env add-plugin\`), not here.
|
|
90123
|
+
|
|
90124
|
+
## Layout
|
|
90125
|
+
|
|
90126
|
+
\`\`\`text
|
|
90127
|
+
${id}/
|
|
90128
|
+
\u251C\u2500\u2500 line.manifest.json # kind: line \u2014 what \`har line add\` applies
|
|
90129
|
+
\u251C\u2500\u2500 line.json # the program: stations, gate, handoff
|
|
90130
|
+
${withGateStage ? `\u251C\u2500\u2500 stages/${gateStageId}.sh # extra gate stage \u2014 registered, NOT on verify
|
|
90131
|
+
` : ""}\u2514\u2500\u2500 README.md
|
|
90132
|
+
\`\`\`
|
|
90133
|
+
|
|
90134
|
+
## Stations
|
|
90135
|
+
|
|
90136
|
+
${stationIds.map((s, i) => `${i + 1}. \`${s}\``).join("\n")}
|
|
90137
|
+
|
|
90138
|
+
## The ratchet
|
|
90139
|
+
|
|
90140
|
+
\`gate.stages[].fromStation\` tags a stage as required at that station **and
|
|
90141
|
+
every later one**. Adding a station must never drop an earlier station's
|
|
90142
|
+
stages \u2014 that is what \`gate.cumulative: true\` means, and it is contract.
|
|
90143
|
+
|
|
90144
|
+
## Install channels
|
|
90145
|
+
|
|
90146
|
+
\`\`\`bash
|
|
90147
|
+
har line add ${id} # this repo (.har/lines/${id})
|
|
90148
|
+
har line add ./${id} # local path
|
|
90149
|
+
har line add github:acme/${id} # git
|
|
90150
|
+
har line add @acme/${id} # npm
|
|
90151
|
+
\`\`\`
|
|
90152
|
+
|
|
90153
|
+
Publishing needs no format change: add a \`package.json\` and push.
|
|
90154
|
+
`;
|
|
90155
|
+
}
|
|
90156
|
+
|
|
90157
|
+
// src/core/lines.ts
|
|
90158
|
+
init_stages();
|
|
90159
|
+
init_slot_registry();
|
|
90160
|
+
init_runs();
|
|
90161
|
+
function listInstalledLineIds(repoPath) {
|
|
90162
|
+
const ledger = readLineLedger(repoPath);
|
|
90163
|
+
const fromLedger = ledger?.lines.map((l) => l.id) ?? [];
|
|
90164
|
+
const onDisk = listLocalLineIds(repoPath).filter(
|
|
90165
|
+
(id) => readInstalledLineProgram(repoPath, id) !== null
|
|
90166
|
+
);
|
|
90167
|
+
return [.../* @__PURE__ */ new Set([...fromLedger, ...onDisk])].sort();
|
|
90168
|
+
}
|
|
90169
|
+
function cumulativeGateStages(program, stationId) {
|
|
90170
|
+
const stationIndex = program.stations.findIndex((s) => s.id === stationId);
|
|
90171
|
+
if (stationIndex < 0) {
|
|
90172
|
+
throw new Error(
|
|
90173
|
+
`Unknown station "${stationId}". Stations: ${program.stations.map((s) => s.id).join(", ")}`
|
|
90174
|
+
);
|
|
90175
|
+
}
|
|
90176
|
+
const indexOf = new Map(program.stations.map((s, i) => [s.id, i]));
|
|
90177
|
+
return program.gate.stages.filter((stage) => {
|
|
90178
|
+
const from2 = indexOf.get(stage.fromStation);
|
|
90179
|
+
return from2 !== void 0 && from2 <= stationIndex;
|
|
90180
|
+
});
|
|
90181
|
+
}
|
|
90182
|
+
function latestRunStatusByStage(repoPath, stageIds) {
|
|
90183
|
+
const statuses = /* @__PURE__ */ new Map();
|
|
90184
|
+
for (const stageId of stageIds) {
|
|
90185
|
+
const [latest] = listRuns(repoPath, { stageId, limit: 1 });
|
|
90186
|
+
if (latest) statuses.set(stageId, latest.status);
|
|
90187
|
+
}
|
|
90188
|
+
return statuses;
|
|
90189
|
+
}
|
|
90190
|
+
function getLineStatus(repoPath, lineId) {
|
|
90191
|
+
const resolved = path89.resolve(repoPath);
|
|
90192
|
+
const program = readInstalledLineProgram(resolved, lineId);
|
|
90193
|
+
if (!program) {
|
|
90194
|
+
throw new Error(
|
|
90195
|
+
`Line not installed: ${lineId}. Install one with "har line add <spec>" or scaffold with "har line create <id>".`
|
|
90196
|
+
);
|
|
90197
|
+
}
|
|
90198
|
+
const ledger = readLineLedger(resolved);
|
|
90199
|
+
const entry = ledger?.lines.find((l) => l.id === lineId);
|
|
90200
|
+
const registry2 = readStageRegistry(resolved);
|
|
90201
|
+
const registeredIds = new Set(registry2.stages.map((s) => s.id));
|
|
90202
|
+
const verificationStages = registry2.verificationStages ?? [];
|
|
90203
|
+
const allGateStageIds = [...new Set(program.gate.stages.map((s) => s.id))];
|
|
90204
|
+
const runStatuses = latestRunStatusByStage(resolved, allGateStageIds);
|
|
90205
|
+
const stations = program.stations.map((station, index) => {
|
|
90206
|
+
const required2 = cumulativeGateStages(program, station.id);
|
|
90207
|
+
const requiredStageIds = [...new Set(required2.map((s) => s.id))];
|
|
90208
|
+
const missingStageIds = requiredStageIds.filter((id) => !registeredIds.has(id));
|
|
90209
|
+
const passedStageIds = requiredStageIds.filter((id) => runStatuses.get(id) === "pass");
|
|
90210
|
+
const neverRunStageIds = requiredStageIds.filter((id) => !runStatuses.has(id));
|
|
90211
|
+
return {
|
|
90212
|
+
id: station.id,
|
|
90213
|
+
title: station.title,
|
|
90214
|
+
index,
|
|
90215
|
+
requiredStageIds,
|
|
90216
|
+
passedStageIds,
|
|
90217
|
+
neverRunStageIds,
|
|
90218
|
+
missingStageIds,
|
|
90219
|
+
green: missingStageIds.length === 0 && passedStageIds.length === requiredStageIds.length,
|
|
90220
|
+
work: station.work
|
|
90221
|
+
};
|
|
90222
|
+
});
|
|
90223
|
+
let currentStationId;
|
|
90224
|
+
for (const station of stations) {
|
|
90225
|
+
if (station.green) currentStationId = station.id;
|
|
90226
|
+
else break;
|
|
90227
|
+
}
|
|
90228
|
+
const nextStationId = stations.find((s) => !s.green)?.id;
|
|
90229
|
+
const lineStageIds = entry?.stageIds ?? [];
|
|
90230
|
+
const verifyLeaks = lineStageIds.filter((id) => verificationStages.includes(id));
|
|
90231
|
+
const warnings = [];
|
|
90232
|
+
if (verifyLeaks.length > 0) {
|
|
90233
|
+
warnings.push(
|
|
90234
|
+
`Line stage(s) ${verifyLeaks.join(", ")} are listed in verificationStages \u2014 line gate stages must stay off \`har env verify --full\`. Remove them from .har/stages.json.`
|
|
90235
|
+
);
|
|
90236
|
+
}
|
|
90237
|
+
for (const station of stations) {
|
|
90238
|
+
for (const missing of station.missingStageIds) {
|
|
90239
|
+
warnings.push(`Gate stage "${missing}" (station ${station.id}) is not registered in .har/stages.json`);
|
|
90240
|
+
}
|
|
90241
|
+
}
|
|
90242
|
+
const slotsInFlight = listSlotRegistryEntries(resolved).map((slot) => ({
|
|
90243
|
+
agentId: slot.agentId,
|
|
90244
|
+
branch: slot.branch,
|
|
90245
|
+
workUnitId: slot.workUnitId,
|
|
90246
|
+
workDir: slot.workDir
|
|
90247
|
+
}));
|
|
90248
|
+
return {
|
|
90249
|
+
lineId,
|
|
90250
|
+
title: program.title,
|
|
90251
|
+
programPath: entry?.programPath ?? `.har/lines/${lineId}/line.json`,
|
|
90252
|
+
installed: true,
|
|
90253
|
+
source: entry?.source,
|
|
90254
|
+
stations,
|
|
90255
|
+
currentStationId,
|
|
90256
|
+
nextStationId,
|
|
90257
|
+
optInEnv: program.gate.optInEnv,
|
|
90258
|
+
registeredStageIds: lineStageIds,
|
|
90259
|
+
verifyLeaks,
|
|
90260
|
+
slotsInFlight,
|
|
90261
|
+
handoffAutonomousShip: false,
|
|
90262
|
+
warnings: [...new Set(warnings)]
|
|
90263
|
+
};
|
|
90264
|
+
}
|
|
90265
|
+
function getAllLineStatuses(repoPath) {
|
|
90266
|
+
return listInstalledLineIds(repoPath).map((id) => getLineStatus(repoPath, id));
|
|
90267
|
+
}
|
|
90268
|
+
function resolveLineId(repoPath, lineId) {
|
|
90269
|
+
if (lineId) return lineId;
|
|
90270
|
+
const ids = listInstalledLineIds(repoPath);
|
|
90271
|
+
if (ids.length === 0) {
|
|
90272
|
+
throw new Error('No factory line installed. Run "har line create <id>" or "har line add <spec>".');
|
|
90273
|
+
}
|
|
90274
|
+
if (ids.length > 1) {
|
|
90275
|
+
throw new Error(`Multiple lines installed (${ids.join(", ")}). Pass --line <id>.`);
|
|
90276
|
+
}
|
|
90277
|
+
return ids[0];
|
|
90278
|
+
}
|
|
90279
|
+
async function runLineGate(options) {
|
|
90280
|
+
const repoPath = path89.resolve(options.repoPath);
|
|
90281
|
+
const lineId = resolveLineId(repoPath, options.lineId);
|
|
90282
|
+
const program = readInstalledLineProgram(repoPath, lineId);
|
|
90283
|
+
if (!program) {
|
|
90284
|
+
throw new Error(`Line not installed: ${lineId}`);
|
|
90285
|
+
}
|
|
90286
|
+
const required2 = cumulativeGateStages(program, options.station);
|
|
90287
|
+
if (program.gate.optInEnv && !options.force && process.env[program.gate.optInEnv] !== "1") {
|
|
90288
|
+
return {
|
|
90289
|
+
lineId,
|
|
90290
|
+
station: options.station,
|
|
90291
|
+
stages: required2.map((stage) => ({
|
|
90292
|
+
stageId: stage.id,
|
|
90293
|
+
fromStation: stage.fromStation,
|
|
90294
|
+
status: "skipped",
|
|
90295
|
+
reason: `${program.gate.optInEnv} is not "1"`
|
|
90296
|
+
})),
|
|
90297
|
+
pass: true,
|
|
90298
|
+
skipped: true,
|
|
90299
|
+
skipReason: `Gate is opt-in: set ${program.gate.optInEnv}=1 (or pass --force) to run it.`
|
|
90300
|
+
};
|
|
90301
|
+
}
|
|
90302
|
+
const registry2 = readStageRegistry(repoPath);
|
|
90303
|
+
const registeredIds = new Set(registry2.stages.map((s) => s.id));
|
|
90304
|
+
if (options.agentId === void 0) {
|
|
90305
|
+
const needSlot = required2.filter((stage) => registry2.stages.find((s) => s.id === stage.id)?.requiresAgentId).map((stage) => stage.id);
|
|
90306
|
+
if (needSlot.length > 0) {
|
|
90307
|
+
throw new Error(
|
|
90308
|
+
`Gate stage(s) ${needSlot.join(", ")} run in an agent slot \u2014 pass --agent <id> (har line gate ${options.station} --line ${lineId} --agent 1).`
|
|
90309
|
+
);
|
|
90310
|
+
}
|
|
90311
|
+
}
|
|
90312
|
+
const results = [];
|
|
90313
|
+
let pass = true;
|
|
90314
|
+
for (const stage of required2) {
|
|
90315
|
+
if (!registeredIds.has(stage.id)) {
|
|
90316
|
+
results.push({
|
|
90317
|
+
stageId: stage.id,
|
|
90318
|
+
fromStation: stage.fromStation,
|
|
90319
|
+
status: "skipped",
|
|
90320
|
+
reason: "stage is not registered in .har/stages.json"
|
|
90321
|
+
});
|
|
90322
|
+
pass = false;
|
|
90323
|
+
continue;
|
|
90324
|
+
}
|
|
90325
|
+
const result = await runStage({
|
|
90326
|
+
repoPath,
|
|
90327
|
+
stageId: stage.id,
|
|
90328
|
+
agentId: options.agentId,
|
|
90329
|
+
trigger: "cli"
|
|
90330
|
+
});
|
|
90331
|
+
results.push({
|
|
90332
|
+
stageId: stage.id,
|
|
90333
|
+
fromStation: stage.fromStation,
|
|
90334
|
+
status: result.status,
|
|
90335
|
+
durationMs: result.durationMs
|
|
90336
|
+
});
|
|
90337
|
+
if (result.status !== "pass") {
|
|
90338
|
+
pass = false;
|
|
90339
|
+
}
|
|
90340
|
+
}
|
|
90341
|
+
return { lineId, station: options.station, stages: results, pass, skipped: false };
|
|
90342
|
+
}
|
|
90343
|
+
function createLineBundle(repoPath, options) {
|
|
90344
|
+
return createLine(repoPath, options);
|
|
90345
|
+
}
|
|
90346
|
+
function addLine(repoPath, spec, options = {}) {
|
|
90347
|
+
return applyLine(repoPath, spec, options);
|
|
90348
|
+
}
|
|
90349
|
+
|
|
90350
|
+
// src/mcp/server.ts
|
|
89322
90351
|
init_work_units();
|
|
89323
90352
|
init_manifest();
|
|
89324
90353
|
|
|
@@ -89450,7 +90479,12 @@ var PreflightEnvironmentOutputSchema = ShellRunOutputSchema.extend({
|
|
|
89450
90479
|
var CompleteEnvironmentInputSchema = external_exports.object({
|
|
89451
90480
|
repo: external_exports.string().default("."),
|
|
89452
90481
|
agentId: agentIdSchema,
|
|
89453
|
-
|
|
90482
|
+
verify: external_exports.boolean().optional().describe(
|
|
90483
|
+
"Re-run full verification before teardown when the tree may have changed since the last passing --full"
|
|
90484
|
+
),
|
|
90485
|
+
skipVerify: external_exports.boolean().optional().describe(
|
|
90486
|
+
"Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run; skipVerify=false still forces a re-run."
|
|
90487
|
+
)
|
|
89454
90488
|
});
|
|
89455
90489
|
var CompleteEnvironmentOutputSchema = ShellRunOutputSchema.extend({
|
|
89456
90490
|
branch: external_exports.string().optional().describe("Session branch kept for the user to push / open a PR"),
|
|
@@ -89532,6 +90566,32 @@ var ControlUpOutputSchema = external_exports.object({
|
|
|
89532
90566
|
failed: external_exports.number().int(),
|
|
89533
90567
|
apiReady: external_exports.boolean()
|
|
89534
90568
|
});
|
|
90569
|
+
var CreateLineInputSchema = external_exports.object({
|
|
90570
|
+
repo: external_exports.string().default("."),
|
|
90571
|
+
id: external_exports.string().min(1).describe("Line id (lowercase slug, e.g. onboarding-line)"),
|
|
90572
|
+
title: external_exports.string().min(1).optional().describe("Human-readable line title"),
|
|
90573
|
+
description: external_exports.string().min(1).max(2e3).optional(),
|
|
90574
|
+
stations: external_exports.array(external_exports.string().min(1)).optional().describe("Station ids in order (default: S1, S2)"),
|
|
90575
|
+
gateStage: external_exports.boolean().default(true).describe("Scaffold one registered-but-off-verify gate stage"),
|
|
90576
|
+
optInEnv: external_exports.string().min(1).optional().describe('Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'),
|
|
90577
|
+
force: external_exports.boolean().default(false)
|
|
90578
|
+
});
|
|
90579
|
+
var AddLineInputSchema = external_exports.object({
|
|
90580
|
+
repo: external_exports.string().default("."),
|
|
90581
|
+
line: external_exports.string().min(1).describe("Line id, local path (./line), npm package (@org/pkg), or git URL (github:org/repo)"),
|
|
90582
|
+
force: external_exports.boolean().default(false).describe("Overwrite existing line files and stage entries")
|
|
90583
|
+
});
|
|
90584
|
+
var LineStatusInputSchema = external_exports.object({
|
|
90585
|
+
repo: external_exports.string().default("."),
|
|
90586
|
+
line: external_exports.string().min(1).optional().describe("Line id (default: every installed line)")
|
|
90587
|
+
});
|
|
90588
|
+
var RunLineGateInputSchema = external_exports.object({
|
|
90589
|
+
repo: external_exports.string().default("."),
|
|
90590
|
+
station: external_exports.string().min(1).describe("Station id whose cumulative gate should run"),
|
|
90591
|
+
line: external_exports.string().min(1).optional().describe("Line id when more than one is installed"),
|
|
90592
|
+
agentId: external_exports.number().int().optional().describe("Agent slot to run the stages in"),
|
|
90593
|
+
force: external_exports.boolean().default(false).describe("Run even when the program declares an opt-in env var that is not set")
|
|
90594
|
+
});
|
|
89535
90595
|
|
|
89536
90596
|
// src/mcp/server.ts
|
|
89537
90597
|
var HAR_MCP_TOOLS = [
|
|
@@ -89732,14 +90792,18 @@ var HAR_MCP_TOOLS = [
|
|
|
89732
90792
|
},
|
|
89733
90793
|
{
|
|
89734
90794
|
name: "har_complete_environment",
|
|
89735
|
-
description: "Finish a session when the work is done:
|
|
90795
|
+
description: "Finish a session when the work is done: reuses the last passing full validation for the current worktree (or re-runs verify when verify=true), tears the slot down, and KEEPS the session branch so the user can push it and open a PR.",
|
|
89736
90796
|
inputSchema: objectJsonSchema(
|
|
89737
90797
|
{
|
|
89738
90798
|
repo: repoJsonProperty,
|
|
89739
90799
|
agentId: agentIdJsonProperty,
|
|
90800
|
+
verify: {
|
|
90801
|
+
type: "boolean",
|
|
90802
|
+
description: "Re-run full verification before teardown when the tree may have changed since the last passing --full"
|
|
90803
|
+
},
|
|
89740
90804
|
skipVerify: {
|
|
89741
90805
|
type: "boolean",
|
|
89742
|
-
description: "
|
|
90806
|
+
description: "Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run."
|
|
89743
90807
|
}
|
|
89744
90808
|
},
|
|
89745
90809
|
["agentId"]
|
|
@@ -89780,6 +90844,73 @@ var HAR_MCP_TOOLS = [
|
|
|
89780
90844
|
repo: repoJsonProperty,
|
|
89781
90845
|
detach: { type: "boolean", description: "Run the container in detached mode (default true)" }
|
|
89782
90846
|
})
|
|
90847
|
+
},
|
|
90848
|
+
{
|
|
90849
|
+
name: "har_line_create",
|
|
90850
|
+
description: "Scaffold a project-owned factory line at .har/lines/<id>/ (manifest, program, optional gate stage, README). A line is a multi-station program, not a verification plugin.",
|
|
90851
|
+
inputSchema: objectJsonSchema(
|
|
90852
|
+
{
|
|
90853
|
+
repo: repoJsonProperty,
|
|
90854
|
+
id: { type: "string", description: "Line id (lowercase slug, e.g. onboarding-line)" },
|
|
90855
|
+
title: { type: "string", description: "Human-readable line title" },
|
|
90856
|
+
description: { type: "string", description: "One-paragraph description of the program" },
|
|
90857
|
+
stations: {
|
|
90858
|
+
type: "array",
|
|
90859
|
+
items: { type: "string" },
|
|
90860
|
+
description: "Station ids in order (default: S1, S2)"
|
|
90861
|
+
},
|
|
90862
|
+
gateStage: {
|
|
90863
|
+
type: "boolean",
|
|
90864
|
+
description: "Scaffold one registered-but-off-verify gate stage (default true)"
|
|
90865
|
+
},
|
|
90866
|
+
optInEnv: {
|
|
90867
|
+
type: "string",
|
|
90868
|
+
description: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
|
|
90869
|
+
},
|
|
90870
|
+
force: { type: "boolean", description: "Overwrite an existing line" }
|
|
90871
|
+
},
|
|
90872
|
+
["id"]
|
|
90873
|
+
)
|
|
90874
|
+
},
|
|
90875
|
+
{
|
|
90876
|
+
name: "har_add_line",
|
|
90877
|
+
description: "Install a factory line bundle (local id, path, npm package, or git URL). Registers the line's stages but NEVER adds them to verificationStages \u2014 default verify stays unchanged. Verification plugins use har_add_plugin instead.",
|
|
90878
|
+
inputSchema: objectJsonSchema(
|
|
90879
|
+
{
|
|
90880
|
+
repo: repoJsonProperty,
|
|
90881
|
+
line: {
|
|
90882
|
+
type: "string",
|
|
90883
|
+
description: "Line id, local path (./line), npm package (@org/pkg), or git URL"
|
|
90884
|
+
},
|
|
90885
|
+
force: { type: "boolean", description: "Overwrite existing line files and stage entries" }
|
|
90886
|
+
},
|
|
90887
|
+
["line"]
|
|
90888
|
+
)
|
|
90889
|
+
},
|
|
90890
|
+
{
|
|
90891
|
+
name: "har_line_status",
|
|
90892
|
+
description: "Stations, cumulative gate progress derived from .har/runs/ records, and slots in flight for installed factory lines. Pure read \u2014 writes no run records.",
|
|
90893
|
+
inputSchema: objectJsonSchema({
|
|
90894
|
+
repo: repoJsonProperty,
|
|
90895
|
+
line: { type: "string", description: "Line id (default: every installed line)" }
|
|
90896
|
+
})
|
|
90897
|
+
},
|
|
90898
|
+
{
|
|
90899
|
+
name: "har_run_line_gate",
|
|
90900
|
+
description: "Run one station's cumulative gate: every gate stage tagged at that station or earlier. Runs through the normal stage runner and writes run records; does not call verify and does not widen the verify plan.",
|
|
90901
|
+
inputSchema: objectJsonSchema(
|
|
90902
|
+
{
|
|
90903
|
+
repo: repoJsonProperty,
|
|
90904
|
+
station: { type: "string", description: "Station id whose cumulative gate should run" },
|
|
90905
|
+
line: { type: "string", description: "Line id when more than one is installed" },
|
|
90906
|
+
agentId: agentIdJsonProperty,
|
|
90907
|
+
force: {
|
|
90908
|
+
type: "boolean",
|
|
90909
|
+
description: "Run even when the program declares an opt-in env var that is not set"
|
|
90910
|
+
}
|
|
90911
|
+
},
|
|
90912
|
+
["station"]
|
|
90913
|
+
)
|
|
89783
90914
|
}
|
|
89784
90915
|
];
|
|
89785
90916
|
function jsonContent(data) {
|
|
@@ -90009,6 +91140,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
90009
91140
|
const result = await completeEnvironment({
|
|
90010
91141
|
repoPath: repo,
|
|
90011
91142
|
agentId,
|
|
91143
|
+
verify: input.verify,
|
|
90012
91144
|
skipVerify: input.skipVerify,
|
|
90013
91145
|
capture: true,
|
|
90014
91146
|
trigger: "mcp"
|
|
@@ -90045,7 +91177,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
90045
91177
|
const input = ControlUpInputSchema.parse({ ...args, repo });
|
|
90046
91178
|
const result = await startControlAndSync({
|
|
90047
91179
|
detach: input.detach,
|
|
90048
|
-
cwd:
|
|
91180
|
+
cwd: path90.resolve(input.repo)
|
|
90049
91181
|
});
|
|
90050
91182
|
if (result.code !== 0) {
|
|
90051
91183
|
return {
|
|
@@ -90067,6 +91199,59 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
90067
91199
|
})
|
|
90068
91200
|
);
|
|
90069
91201
|
}
|
|
91202
|
+
case "har_line_create": {
|
|
91203
|
+
const input = CreateLineInputSchema.parse({ ...args, repo });
|
|
91204
|
+
const result = createLineBundle(repo, {
|
|
91205
|
+
id: input.id,
|
|
91206
|
+
title: input.title,
|
|
91207
|
+
description: input.description,
|
|
91208
|
+
stations: input.stations,
|
|
91209
|
+
gateStage: input.gateStage,
|
|
91210
|
+
optInEnv: input.optInEnv,
|
|
91211
|
+
force: input.force
|
|
91212
|
+
});
|
|
91213
|
+
return jsonContent({
|
|
91214
|
+
lineId: result.lineId,
|
|
91215
|
+
filesWritten: result.filesWritten,
|
|
91216
|
+
nextSteps: result.nextSteps
|
|
91217
|
+
});
|
|
91218
|
+
}
|
|
91219
|
+
case "har_add_line": {
|
|
91220
|
+
const input = AddLineInputSchema.parse({ ...args, repo });
|
|
91221
|
+
const result = addLine(repo, input.line, { force: input.force, spec: input.line });
|
|
91222
|
+
return jsonContent({
|
|
91223
|
+
lineId: result.lineId,
|
|
91224
|
+
title: result.title,
|
|
91225
|
+
stationIds: result.stationIds,
|
|
91226
|
+
firstGatedStationId: result.firstGatedStationId,
|
|
91227
|
+
stageIds: result.stageIds,
|
|
91228
|
+
filesWritten: result.filesWritten,
|
|
91229
|
+
warnings: result.warnings,
|
|
91230
|
+
nextSteps: result.nextSteps,
|
|
91231
|
+
programPath: result.programPath,
|
|
91232
|
+
docsPath: result.docsPath,
|
|
91233
|
+
source: result.source,
|
|
91234
|
+
adaptPromptPath: result.adaptPromptPath,
|
|
91235
|
+
verificationStagesUnchanged: true
|
|
91236
|
+
});
|
|
91237
|
+
}
|
|
91238
|
+
case "har_line_status": {
|
|
91239
|
+
const input = LineStatusInputSchema.parse({ ...args, repo });
|
|
91240
|
+
const statuses = input.line ? [getLineStatus(repo, input.line)] : getAllLineStatuses(repo);
|
|
91241
|
+
return jsonContent(input.line ? statuses[0] : { lines: statuses });
|
|
91242
|
+
}
|
|
91243
|
+
case "har_run_line_gate": {
|
|
91244
|
+
const input = RunLineGateInputSchema.parse({ ...args, repo });
|
|
91245
|
+
const agentId = input.agentId === void 0 ? void 0 : validateAgentId(input.agentId, repo);
|
|
91246
|
+
const result = await runLineGate({
|
|
91247
|
+
repoPath: repo,
|
|
91248
|
+
lineId: input.line,
|
|
91249
|
+
station: input.station,
|
|
91250
|
+
agentId,
|
|
91251
|
+
force: input.force
|
|
91252
|
+
});
|
|
91253
|
+
return jsonContent(result);
|
|
91254
|
+
}
|
|
90070
91255
|
default:
|
|
90071
91256
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
90072
91257
|
}
|
|
@@ -90109,11 +91294,11 @@ var mcpCommand = {
|
|
|
90109
91294
|
};
|
|
90110
91295
|
|
|
90111
91296
|
// src/cli/commands/onboard.ts
|
|
90112
|
-
var
|
|
91297
|
+
var path92 = __toESM(require("path"));
|
|
90113
91298
|
init_stages();
|
|
90114
91299
|
|
|
90115
91300
|
// src/core/onboarding.ts
|
|
90116
|
-
var
|
|
91301
|
+
var path91 = __toESM(require("path"));
|
|
90117
91302
|
init_control_config();
|
|
90118
91303
|
init_control_sync();
|
|
90119
91304
|
init_otel_hooks();
|
|
@@ -90150,7 +91335,7 @@ var ONBOARDING_GUIDE_STEPS = [
|
|
|
90150
91335
|
body: [
|
|
90151
91336
|
"Quick: har env verify <id>",
|
|
90152
91337
|
"Done: har env verify <id> --full then har env complete <id>",
|
|
90153
|
-
"`complete`
|
|
91338
|
+
"`complete` reuses the last matching full validation, frees the slot, and keeps the session branch for a PR. Pass --verify if the tree changed."
|
|
90154
91339
|
].join("\n")
|
|
90155
91340
|
},
|
|
90156
91341
|
{
|
|
@@ -90257,7 +91442,7 @@ async function defaultEnsureControl(options) {
|
|
|
90257
91442
|
return { started: true, apiUrl: result.apiUrl };
|
|
90258
91443
|
}
|
|
90259
91444
|
async function runOnboarding(options, deps = {}) {
|
|
90260
|
-
const repoPath =
|
|
91445
|
+
const repoPath = path91.resolve(options.repoPath);
|
|
90261
91446
|
const applyTelemetry = deps.applyTelemetry ?? applyOnboardingTelemetry;
|
|
90262
91447
|
const ensureControl = deps.ensureControl ?? defaultEnsureControl;
|
|
90263
91448
|
const init = deps.initHarness ?? initHarness;
|
|
@@ -90455,7 +91640,7 @@ async function promptChoices(args, docker) {
|
|
|
90455
91640
|
};
|
|
90456
91641
|
}
|
|
90457
91642
|
const pluginChoices = listPluginChoices();
|
|
90458
|
-
const repoPath =
|
|
91643
|
+
const repoPath = path92.resolve(args.repo);
|
|
90459
91644
|
const alreadyPresent = harnessExists(repoPath);
|
|
90460
91645
|
const needProfile = !alreadyPresent && !args.skipInit && args.profile === void 0;
|
|
90461
91646
|
const needAgentSlots = shouldConfigureSlots && flaggedSlots === void 0;
|
|
@@ -90576,7 +91761,7 @@ function printSummary(result) {
|
|
|
90576
91761
|
console.error("");
|
|
90577
91762
|
}
|
|
90578
91763
|
async function handleOnboard(argv) {
|
|
90579
|
-
const repoPath =
|
|
91764
|
+
const repoPath = path92.resolve(argv.repo);
|
|
90580
91765
|
header("har onboard");
|
|
90581
91766
|
info(`Repository: ${repoPath}`);
|
|
90582
91767
|
try {
|
|
@@ -90693,24 +91878,288 @@ var onboardCommand = {
|
|
|
90693
91878
|
handler: (argv) => handleOnboard(argv)
|
|
90694
91879
|
};
|
|
90695
91880
|
|
|
91881
|
+
// src/cli/commands/line.ts
|
|
91882
|
+
var path93 = __toESM(require("path"));
|
|
91883
|
+
init_logging();
|
|
91884
|
+
function handleLineCreate(argv) {
|
|
91885
|
+
if (!argv.id) {
|
|
91886
|
+
error('Missing line id. Usage: har line create <id> [--stations S1,S2] [--title "..."]');
|
|
91887
|
+
return finishCommand(1);
|
|
91888
|
+
}
|
|
91889
|
+
const repoPath = path93.resolve(argv.repo);
|
|
91890
|
+
header("har line create");
|
|
91891
|
+
info(`Repository: ${repoPath}`);
|
|
91892
|
+
info(`Line: ${argv.id}`);
|
|
91893
|
+
try {
|
|
91894
|
+
const result = createLineBundle(repoPath, {
|
|
91895
|
+
id: argv.id,
|
|
91896
|
+
title: argv.title,
|
|
91897
|
+
description: argv.description,
|
|
91898
|
+
stations: argv.stations,
|
|
91899
|
+
gateStage: argv.gateStage,
|
|
91900
|
+
optInEnv: argv.optInEnv,
|
|
91901
|
+
force: argv.force
|
|
91902
|
+
});
|
|
91903
|
+
divider();
|
|
91904
|
+
success(`Factory line scaffolded: .har/lines/${result.lineId}/`);
|
|
91905
|
+
for (const file of result.filesWritten) {
|
|
91906
|
+
info(` + ${file}`);
|
|
91907
|
+
}
|
|
91908
|
+
console.error("");
|
|
91909
|
+
console.error(" Next steps:");
|
|
91910
|
+
for (const step of result.nextSteps) {
|
|
91911
|
+
console.error(` ${step}`);
|
|
91912
|
+
}
|
|
91913
|
+
console.error("");
|
|
91914
|
+
console.error(` Docs: .har/lines/${result.lineId}/README.md`);
|
|
91915
|
+
console.error("");
|
|
91916
|
+
} catch (err) {
|
|
91917
|
+
error(err.message);
|
|
91918
|
+
return finishCommand(1);
|
|
91919
|
+
}
|
|
91920
|
+
}
|
|
91921
|
+
function handleLineAdd(argv) {
|
|
91922
|
+
if (!argv.spec) {
|
|
91923
|
+
error("Missing line spec. Usage: har line add <id|path|npm|git>");
|
|
91924
|
+
return finishCommand(1);
|
|
91925
|
+
}
|
|
91926
|
+
const repoPath = path93.resolve(argv.repo);
|
|
91927
|
+
header("har line add");
|
|
91928
|
+
info(`Repository: ${repoPath}`);
|
|
91929
|
+
info(`Line: ${argv.spec}`);
|
|
91930
|
+
try {
|
|
91931
|
+
const result = addLine(repoPath, argv.spec, { force: argv.force, spec: argv.spec });
|
|
91932
|
+
divider();
|
|
91933
|
+
console.error("");
|
|
91934
|
+
console.error(" verificationStages unchanged \u2014 line gate stages are opt-in.");
|
|
91935
|
+
console.error(
|
|
91936
|
+
` Run the gate with: har line gate ${result.firstGatedStationId} --line ${result.lineId}`
|
|
91937
|
+
);
|
|
91938
|
+
console.error("");
|
|
91939
|
+
console.error(" Next steps:");
|
|
91940
|
+
for (const step of result.nextSteps) {
|
|
91941
|
+
console.error(` ${step}`);
|
|
91942
|
+
}
|
|
91943
|
+
console.error("");
|
|
91944
|
+
} catch (err) {
|
|
91945
|
+
error(err.message);
|
|
91946
|
+
return finishCommand(1);
|
|
91947
|
+
}
|
|
91948
|
+
}
|
|
91949
|
+
function renderStatus(status) {
|
|
91950
|
+
header(`Line: ${status.lineId} \u2014 ${status.title}`);
|
|
91951
|
+
info(`Program: ${status.programPath}${status.source ? ` (source: ${status.source})` : ""}`);
|
|
91952
|
+
info(
|
|
91953
|
+
status.optInEnv ? `Gate: opt-in via ${status.optInEnv}=1` : "Gate: on demand (har line gate <station>)"
|
|
91954
|
+
);
|
|
91955
|
+
info(
|
|
91956
|
+
status.registeredStageIds.length > 0 ? `Registered stages (off verify): ${status.registeredStageIds.join(", ")}` : "Registered stages (off verify): none"
|
|
91957
|
+
);
|
|
91958
|
+
console.error("");
|
|
91959
|
+
for (const station of status.stations) {
|
|
91960
|
+
const marker = station.green ? "\u2713" : station.id === status.nextStationId ? "\u25B6" : "\xB7";
|
|
91961
|
+
const required2 = station.requiredStageIds.length > 0 ? `${station.passedStageIds.length}/${station.requiredStageIds.length} gate stages green` : "no gate stages";
|
|
91962
|
+
console.error(` ${marker} ${station.id} ${station.title} \u2014 ${required2}`);
|
|
91963
|
+
if (station.missingStageIds.length > 0) {
|
|
91964
|
+
console.error(` missing (not registered): ${station.missingStageIds.join(", ")}`);
|
|
91965
|
+
}
|
|
91966
|
+
const pending = station.requiredStageIds.filter((id) => !station.passedStageIds.includes(id));
|
|
91967
|
+
if (pending.length > 0 && station.missingStageIds.length === 0) {
|
|
91968
|
+
console.error(` pending: ${pending.join(", ")}`);
|
|
91969
|
+
}
|
|
91970
|
+
}
|
|
91971
|
+
console.error("");
|
|
91972
|
+
if (status.slotsInFlight.length > 0) {
|
|
91973
|
+
console.error(" Slots in flight:");
|
|
91974
|
+
for (const slot of status.slotsInFlight) {
|
|
91975
|
+
const work = slot.workUnitId ? ` work=${slot.workUnitId}` : "";
|
|
91976
|
+
console.error(` agent ${slot.agentId}: ${slot.branch ?? slot.workDir}${work}`);
|
|
91977
|
+
}
|
|
91978
|
+
console.error("");
|
|
91979
|
+
}
|
|
91980
|
+
for (const warning of status.warnings) {
|
|
91981
|
+
warn(` \u26A0 ${warning}`);
|
|
91982
|
+
}
|
|
91983
|
+
console.error(
|
|
91984
|
+
status.nextStationId ? ` Next station: ${status.nextStationId}` : " All stations green \u2014 hand off for human review (autonomousShip: false)."
|
|
91985
|
+
);
|
|
91986
|
+
console.error("");
|
|
91987
|
+
}
|
|
91988
|
+
function handleLineStatus(argv) {
|
|
91989
|
+
const repoPath = path93.resolve(argv.repo);
|
|
91990
|
+
try {
|
|
91991
|
+
const statuses = argv.id ? [getLineStatus(repoPath, argv.id)] : getAllLineStatuses(repoPath);
|
|
91992
|
+
if (argv.json) {
|
|
91993
|
+
console.log(JSON.stringify(argv.id ? statuses[0] : statuses, null, 2));
|
|
91994
|
+
return;
|
|
91995
|
+
}
|
|
91996
|
+
if (statuses.length === 0) {
|
|
91997
|
+
info("No factory line installed.");
|
|
91998
|
+
info("Scaffold one: har line create <id> \u2022 Install one: har line add <spec>");
|
|
91999
|
+
return;
|
|
92000
|
+
}
|
|
92001
|
+
for (const status of statuses) {
|
|
92002
|
+
renderStatus(status);
|
|
92003
|
+
}
|
|
92004
|
+
} catch (err) {
|
|
92005
|
+
error(err.message);
|
|
92006
|
+
return finishCommand(1);
|
|
92007
|
+
}
|
|
92008
|
+
}
|
|
92009
|
+
async function handleLineGate(argv) {
|
|
92010
|
+
if (!argv.station) {
|
|
92011
|
+
error("Missing station. Usage: har line gate <station> [--line <id>] [--agent <slot>]");
|
|
92012
|
+
return finishCommand(1);
|
|
92013
|
+
}
|
|
92014
|
+
const repoPath = path93.resolve(argv.repo);
|
|
92015
|
+
try {
|
|
92016
|
+
const result = await runLineGate({
|
|
92017
|
+
repoPath,
|
|
92018
|
+
lineId: argv.line,
|
|
92019
|
+
station: argv.station,
|
|
92020
|
+
agentId: argv.agent,
|
|
92021
|
+
force: argv.force
|
|
92022
|
+
});
|
|
92023
|
+
if (argv.json) {
|
|
92024
|
+
console.log(JSON.stringify(result, null, 2));
|
|
92025
|
+
return finishCommand(result.pass ? 0 : 1);
|
|
92026
|
+
}
|
|
92027
|
+
header(`har line gate ${result.station} (${result.lineId})`);
|
|
92028
|
+
if (result.skipped) {
|
|
92029
|
+
info(result.skipReason ?? "Gate skipped.");
|
|
92030
|
+
for (const stage of result.stages) {
|
|
92031
|
+
info(` \xB7 ${stage.stageId} (from ${stage.fromStation}) \u2014 skipped`);
|
|
92032
|
+
}
|
|
92033
|
+
return;
|
|
92034
|
+
}
|
|
92035
|
+
for (const stage of result.stages) {
|
|
92036
|
+
const mark = stage.status === "pass" ? "\u2713" : stage.status === "skipped" ? "\xB7" : "\u2717";
|
|
92037
|
+
const detail = stage.reason ? ` \u2014 ${stage.reason}` : "";
|
|
92038
|
+
const ms = stage.durationMs !== void 0 ? ` (${Math.round(stage.durationMs / 100) / 10}s)` : "";
|
|
92039
|
+
console.error(` ${mark} ${stage.stageId} from ${stage.fromStation} ${stage.status}${ms}${detail}`);
|
|
92040
|
+
}
|
|
92041
|
+
divider();
|
|
92042
|
+
if (result.pass) {
|
|
92043
|
+
success(`Gate passed for station ${result.station}`);
|
|
92044
|
+
} else {
|
|
92045
|
+
error(`Gate failed for station ${result.station}`);
|
|
92046
|
+
}
|
|
92047
|
+
return finishCommand(result.pass ? 0 : 1);
|
|
92048
|
+
} catch (err) {
|
|
92049
|
+
error(err.message);
|
|
92050
|
+
return finishCommand(1);
|
|
92051
|
+
}
|
|
92052
|
+
}
|
|
92053
|
+
function handleLineList(argv) {
|
|
92054
|
+
const repoPath = path93.resolve(argv.repo);
|
|
92055
|
+
const installed = new Set(listInstalledLineIds(repoPath));
|
|
92056
|
+
for (const id of listBundledLineIds()) {
|
|
92057
|
+
console.log(`${id} (bundled)`);
|
|
92058
|
+
}
|
|
92059
|
+
for (const id of listLocalLineIds(repoPath)) {
|
|
92060
|
+
console.log(`${id} (local: .har/lines/${id})${installed.has(id) ? " [installed]" : ""}`);
|
|
92061
|
+
}
|
|
92062
|
+
}
|
|
92063
|
+
function parseStations(value) {
|
|
92064
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
92065
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
92066
|
+
}
|
|
92067
|
+
var lineCommand = {
|
|
92068
|
+
command: "line <subcommand>",
|
|
92069
|
+
describe: "Factory lines: multi-station programs with a cumulative gate that never joins verificationStages",
|
|
92070
|
+
builder: (yargs) => yargs.command(
|
|
92071
|
+
"create [id]",
|
|
92072
|
+
"Scaffold a project-owned line at .har/lines/<id>/ (manifest, program, gate stage, README)",
|
|
92073
|
+
(y) => y.positional("id", { type: "string", describe: "Line id (lowercase slug, e.g. onboarding-line)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("title", { type: "string", describe: "Human-readable line title" }).option("description", { type: "string", describe: "One-paragraph description of the program" }).option("stations", {
|
|
92074
|
+
type: "string",
|
|
92075
|
+
describe: "Comma-separated station ids in order (default: S1,S2)"
|
|
92076
|
+
}).option("gate-stage", {
|
|
92077
|
+
type: "boolean",
|
|
92078
|
+
default: true,
|
|
92079
|
+
describe: "Scaffold one registered-but-off-verify gate stage"
|
|
92080
|
+
}).option("opt-in-env", {
|
|
92081
|
+
type: "string",
|
|
92082
|
+
describe: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
|
|
92083
|
+
}).option("force", { type: "boolean", default: false, describe: "Overwrite an existing line" }),
|
|
92084
|
+
(argv) => handleLineCreate({
|
|
92085
|
+
id: argv.id,
|
|
92086
|
+
repo: argv.repo,
|
|
92087
|
+
title: argv.title,
|
|
92088
|
+
description: argv.description,
|
|
92089
|
+
stations: parseStations(argv.stations),
|
|
92090
|
+
gateStage: argv["gate-stage"],
|
|
92091
|
+
optInEnv: argv["opt-in-env"],
|
|
92092
|
+
force: argv.force
|
|
92093
|
+
})
|
|
92094
|
+
).command(
|
|
92095
|
+
"add [spec]",
|
|
92096
|
+
"Install a line bundle (local id, path, npm package, or git URL) \u2014 never touches verificationStages",
|
|
92097
|
+
(y) => y.positional("spec", {
|
|
92098
|
+
type: "string",
|
|
92099
|
+
describe: "Line id, path (./line), npm package (@org/pkg), or git URL (github:org/repo)"
|
|
92100
|
+
}).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("force", {
|
|
92101
|
+
type: "boolean",
|
|
92102
|
+
default: false,
|
|
92103
|
+
describe: "Overwrite existing line files and stage entries"
|
|
92104
|
+
}),
|
|
92105
|
+
(argv) => handleLineAdd({
|
|
92106
|
+
spec: argv.spec,
|
|
92107
|
+
repo: argv.repo,
|
|
92108
|
+
force: argv.force
|
|
92109
|
+
})
|
|
92110
|
+
).command(
|
|
92111
|
+
"status [id]",
|
|
92112
|
+
"Show stations, cumulative gate progress from run records, and slots in flight",
|
|
92113
|
+
(y) => y.positional("id", { type: "string", describe: "Line id (default: all installed lines)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("json", { type: "boolean", default: false, describe: "Emit structured JSON" }),
|
|
92114
|
+
(argv) => handleLineStatus({
|
|
92115
|
+
id: argv.id,
|
|
92116
|
+
repo: argv.repo,
|
|
92117
|
+
json: argv.json
|
|
92118
|
+
})
|
|
92119
|
+
).command(
|
|
92120
|
+
"gate [station]",
|
|
92121
|
+
"Run the cumulative gate for a station (does NOT run verify and does not widen it)",
|
|
92122
|
+
(y) => y.positional("station", { type: "string", describe: "Station id (e.g. S1)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("line", { type: "string", describe: "Line id when more than one is installed" }).option("agent", { type: "number", describe: "Agent slot to run the stages in" }).option("force", {
|
|
92123
|
+
type: "boolean",
|
|
92124
|
+
default: false,
|
|
92125
|
+
describe: "Run even when the program declares an opt-in env var that is not set"
|
|
92126
|
+
}).option("json", { type: "boolean", default: false, describe: "Emit structured JSON" }),
|
|
92127
|
+
(argv) => handleLineGate({
|
|
92128
|
+
station: argv.station,
|
|
92129
|
+
repo: argv.repo,
|
|
92130
|
+
line: argv.line,
|
|
92131
|
+
agent: argv.agent,
|
|
92132
|
+
force: argv.force,
|
|
92133
|
+
json: argv.json
|
|
92134
|
+
})
|
|
92135
|
+
).command(
|
|
92136
|
+
"list",
|
|
92137
|
+
"List lines available to this repository (bundled and local)",
|
|
92138
|
+
(y) => y.option("repo", { type: "string", default: ".", describe: "Path to the repository" }),
|
|
92139
|
+
(argv) => handleLineList({ repo: argv.repo })
|
|
92140
|
+
).demandCommand(1, "Please specify a subcommand: create, add, status, gate, list"),
|
|
92141
|
+
handler: () => {
|
|
92142
|
+
}
|
|
92143
|
+
};
|
|
92144
|
+
|
|
90696
92145
|
// src/cli/commands/plugin.ts
|
|
90697
|
-
var
|
|
92146
|
+
var path95 = __toESM(require("path"));
|
|
90698
92147
|
|
|
90699
92148
|
// src/harness/plugin-create.ts
|
|
90700
|
-
var
|
|
90701
|
-
var
|
|
92149
|
+
var fs78 = __toESM(require("fs"));
|
|
92150
|
+
var path94 = __toESM(require("path"));
|
|
90702
92151
|
init_paths();
|
|
90703
92152
|
init_schema2();
|
|
90704
92153
|
var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
90705
|
-
function
|
|
90706
|
-
let content =
|
|
92154
|
+
function renderSkeleton2(templatePath, tokens) {
|
|
92155
|
+
let content = fs78.readFileSync(templatePath, "utf8");
|
|
90707
92156
|
for (const [token, value] of Object.entries(tokens)) {
|
|
90708
92157
|
content = content.replace(new RegExp(token, "g"), value);
|
|
90709
92158
|
}
|
|
90710
92159
|
return content;
|
|
90711
92160
|
}
|
|
90712
92161
|
function createLocalPlugin(repoPath, options) {
|
|
90713
|
-
const resolved =
|
|
92162
|
+
const resolved = path94.resolve(repoPath);
|
|
90714
92163
|
if (!harnessExists(resolved)) {
|
|
90715
92164
|
throw new Error('No .har/ harness found. Run "har onboard" first.');
|
|
90716
92165
|
}
|
|
@@ -90726,9 +92175,9 @@ function createLocalPlugin(repoPath, options) {
|
|
|
90726
92175
|
`Invalid stage kind "${kind}". Available: ${HarnessStageKindSchema.options.join(", ")}`
|
|
90727
92176
|
);
|
|
90728
92177
|
}
|
|
90729
|
-
const pluginDir =
|
|
92178
|
+
const pluginDir = path94.join(resolved, LOCAL_PLUGINS_DIR, id);
|
|
90730
92179
|
const pluginDirRel = `${LOCAL_PLUGINS_DIR}/${id}`;
|
|
90731
|
-
if (
|
|
92180
|
+
if (fs78.existsSync(pluginDir) && !options.force) {
|
|
90732
92181
|
throw new Error(`Plugin already exists: ${pluginDirRel}/. Use --force to overwrite.`);
|
|
90733
92182
|
}
|
|
90734
92183
|
const description = options.description ?? `Custom ${kind} stage (local plugin: ${id})`;
|
|
@@ -90770,16 +92219,16 @@ function createLocalPlugin(repoPath, options) {
|
|
|
90770
92219
|
});
|
|
90771
92220
|
const filesWritten = [];
|
|
90772
92221
|
const write = (rel, content, executable = false) => {
|
|
90773
|
-
const abs =
|
|
90774
|
-
|
|
90775
|
-
|
|
90776
|
-
if (executable)
|
|
92222
|
+
const abs = path94.join(pluginDir, rel);
|
|
92223
|
+
fs78.mkdirSync(path94.dirname(abs), { recursive: true });
|
|
92224
|
+
fs78.writeFileSync(abs, content);
|
|
92225
|
+
if (executable) fs78.chmodSync(abs, 493);
|
|
90777
92226
|
filesWritten.push(`${pluginDirRel}/${rel}`);
|
|
90778
92227
|
};
|
|
90779
92228
|
write("template.manifest.json", `${JSON.stringify(manifest, null, 2)}
|
|
90780
92229
|
`);
|
|
90781
|
-
const stageScript =
|
|
90782
|
-
|
|
92230
|
+
const stageScript = renderSkeleton2(
|
|
92231
|
+
path94.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
|
|
90783
92232
|
{
|
|
90784
92233
|
__STAGE_ID__: id,
|
|
90785
92234
|
__STAGE_KIND__: kind,
|
|
@@ -90787,8 +92236,8 @@ function createLocalPlugin(repoPath, options) {
|
|
|
90787
92236
|
}
|
|
90788
92237
|
);
|
|
90789
92238
|
write(`stages/${id}.sh`, stageScript, true);
|
|
90790
|
-
const readme =
|
|
90791
|
-
|
|
92239
|
+
const readme = renderSkeleton2(
|
|
92240
|
+
path94.join(resolveTemplatesDir(), "plugins", "local-plugin-skeleton", "README.md"),
|
|
90792
92241
|
{ __PLUGIN_ID__: id }
|
|
90793
92242
|
);
|
|
90794
92243
|
write("README.md", readme);
|
|
@@ -90820,7 +92269,7 @@ async function handlePluginCreate(argv) {
|
|
|
90820
92269
|
error('Missing plugin id. Usage: har plugin create <id> [--kind test] [--description "..."]');
|
|
90821
92270
|
return finishCommand(1);
|
|
90822
92271
|
}
|
|
90823
|
-
const repoPath =
|
|
92272
|
+
const repoPath = path95.resolve(argv.repo);
|
|
90824
92273
|
header("har plugin create");
|
|
90825
92274
|
info(`Repository: ${repoPath}`);
|
|
90826
92275
|
info(`Plugin: ${argv.id}`);
|
|
@@ -90851,7 +92300,7 @@ async function handlePluginCreate(argv) {
|
|
|
90851
92300
|
}
|
|
90852
92301
|
}
|
|
90853
92302
|
function handlePluginList(argv) {
|
|
90854
|
-
const repoPath =
|
|
92303
|
+
const repoPath = path95.resolve(argv.repo);
|
|
90855
92304
|
for (const id of listPluginIds()) {
|
|
90856
92305
|
console.log(`${id} (bundled)`);
|
|
90857
92306
|
}
|
|
@@ -91018,7 +92467,7 @@ var preferencesCommand = {
|
|
|
91018
92467
|
};
|
|
91019
92468
|
|
|
91020
92469
|
// src/cli/commands/telemetry.ts
|
|
91021
|
-
var
|
|
92470
|
+
var path96 = __toESM(require("path"));
|
|
91022
92471
|
init_control_config();
|
|
91023
92472
|
init_control_sync();
|
|
91024
92473
|
init_manifest();
|
|
@@ -91111,7 +92560,7 @@ async function handleOff() {
|
|
|
91111
92560
|
info("Mission Control will not auto-start. Existing usage rows are kept.");
|
|
91112
92561
|
}
|
|
91113
92562
|
async function handleWriteEnv(argv) {
|
|
91114
|
-
const repoPath =
|
|
92563
|
+
const repoPath = path96.resolve(argv.repo);
|
|
91115
92564
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
91116
92565
|
const session = readSlotRegistry(harnessRoot, argv.agentId);
|
|
91117
92566
|
const workDir = argv.workDir ?? session?.workDir ?? repoPath;
|
|
@@ -91123,7 +92572,7 @@ async function handleWriteEnv(argv) {
|
|
|
91123
92572
|
suffix,
|
|
91124
92573
|
createdAt: session?.createdAt
|
|
91125
92574
|
});
|
|
91126
|
-
const envFile = argv.envFile ??
|
|
92575
|
+
const envFile = argv.envFile ?? path96.join(workDir, `.env.agent.${argv.agentId}`);
|
|
91127
92576
|
const attrs = {
|
|
91128
92577
|
sessionKey,
|
|
91129
92578
|
agentId: argv.agentId,
|
|
@@ -91139,7 +92588,7 @@ async function handleWriteEnv(argv) {
|
|
|
91139
92588
|
success(`Wrote session attribution to ${envFile}`);
|
|
91140
92589
|
}
|
|
91141
92590
|
function handlePrintEnv(argv) {
|
|
91142
|
-
const repoPath =
|
|
92591
|
+
const repoPath = path96.resolve(argv.repo);
|
|
91143
92592
|
const workDir = argv.workDir ?? repoPath;
|
|
91144
92593
|
const sessionKey = buildSessionKey({
|
|
91145
92594
|
branch: argv.branch,
|
|
@@ -91262,7 +92711,7 @@ var telemetryCommand = {
|
|
|
91262
92711
|
// src/cli/index.ts
|
|
91263
92712
|
async function runCli() {
|
|
91264
92713
|
ensureDefaultTelemetryPreference();
|
|
91265
|
-
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(onboardCommand).command(envCommand).command(agentsCommand).command(controlCommand).command(hqCommand).command(hooksCommand).command(mcpCommand).command(pluginCommand).command(preferencesCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har onboard or har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
|
|
92714
|
+
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(onboardCommand).command(envCommand).command(agentsCommand).command(controlCommand).command(hqCommand).command(hooksCommand).command(mcpCommand).command(pluginCommand).command(lineCommand).command(preferencesCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har onboard or har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
|
|
91266
92715
|
await syncDirtyRepos();
|
|
91267
92716
|
}
|
|
91268
92717
|
|