@osfactory/har 1.14.3 → 1.15.1
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 +101 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38722,6 +38722,26 @@ function substituteEnvTemplate(template, values) {
|
|
|
38722
38722
|
}
|
|
38723
38723
|
);
|
|
38724
38724
|
}
|
|
38725
|
+
function findUnsubstitutedTemplateVars(template) {
|
|
38726
|
+
const allowed = new Set(AGENT_ENV_TEMPLATE_VARS);
|
|
38727
|
+
const found = /* @__PURE__ */ new Map();
|
|
38728
|
+
template.split("\n").forEach((line, idx) => {
|
|
38729
|
+
if (/^\s*#/.test(line)) return;
|
|
38730
|
+
const re2 = /\$(?:\{([A-Za-z_][A-Za-z0-9_]*)([^}]*)(\}?)|([A-Za-z_][A-Za-z0-9_]*))/g;
|
|
38731
|
+
for (let m = re2.exec(line); m !== null; m = re2.exec(line)) {
|
|
38732
|
+
const braced = m[1] !== void 0;
|
|
38733
|
+
const name = braced ? m[1] : m[4];
|
|
38734
|
+
const plain = !braced || m[2] === "" && m[3] === "}";
|
|
38735
|
+
const reason = plain ? "not-substituted" : "parameter-syntax";
|
|
38736
|
+
if (plain && allowed.has(name)) continue;
|
|
38737
|
+
const key = `${reason}:${name}`;
|
|
38738
|
+
const entry = found.get(key) ?? { name, reason, lines: [] };
|
|
38739
|
+
if (!entry.lines.includes(idx + 1)) entry.lines.push(idx + 1);
|
|
38740
|
+
found.set(key, entry);
|
|
38741
|
+
}
|
|
38742
|
+
});
|
|
38743
|
+
return [...found.values()];
|
|
38744
|
+
}
|
|
38725
38745
|
function generateAgentEnvFile(options) {
|
|
38726
38746
|
const templatePath = options.templatePath ?? path24.join(options.harnessDir, "env.template");
|
|
38727
38747
|
if (!fs24.existsSync(templatePath)) return false;
|
|
@@ -40482,12 +40502,11 @@ function listPm2Processes() {
|
|
|
40482
40502
|
return void 0;
|
|
40483
40503
|
}
|
|
40484
40504
|
}
|
|
40485
|
-
function detectForeignPm2(
|
|
40505
|
+
function detectForeignPm2(_projectName, agentId, procs) {
|
|
40486
40506
|
if (!procs) return void 0;
|
|
40487
|
-
const slotPrefix = `har-${projectName}-agent-${agentId}-`;
|
|
40488
40507
|
const legacyPrefix = `agent-${agentId}-`;
|
|
40489
40508
|
const foreign = procs.filter(
|
|
40490
|
-
(p) => p.name &&
|
|
40509
|
+
(p) => p.name && p.name.startsWith(legacyPrefix) && !p.name.startsWith("har-")
|
|
40491
40510
|
);
|
|
40492
40511
|
if (foreign.length === 0) return void 0;
|
|
40493
40512
|
return {
|
|
@@ -40497,6 +40516,48 @@ function detectForeignPm2(projectName, agentId, procs) {
|
|
|
40497
40516
|
}))
|
|
40498
40517
|
};
|
|
40499
40518
|
}
|
|
40519
|
+
function freeAgentSlots(harnessRoot, projectName, agentId, procs) {
|
|
40520
|
+
let ids = [];
|
|
40521
|
+
let range;
|
|
40522
|
+
try {
|
|
40523
|
+
range = getAgentSlotRange(harnessRoot);
|
|
40524
|
+
ids = getAgentSlotIds(harnessRoot);
|
|
40525
|
+
} catch {
|
|
40526
|
+
return { free: [] };
|
|
40527
|
+
}
|
|
40528
|
+
const free = ids.filter((id) => {
|
|
40529
|
+
if (id === agentId) return false;
|
|
40530
|
+
const session = readSlotRegistry(harnessRoot, id);
|
|
40531
|
+
if (session !== void 0 && session.status !== "completed") return false;
|
|
40532
|
+
return detectForeignPm2(projectName, id, procs) === void 0;
|
|
40533
|
+
});
|
|
40534
|
+
return { free, range };
|
|
40535
|
+
}
|
|
40536
|
+
function foreignPm2Advice(agentId, processes, free, range) {
|
|
40537
|
+
const cwds = [
|
|
40538
|
+
...new Set(processes.map((p) => p.cwd).filter((cwd) => Boolean(cwd)))
|
|
40539
|
+
];
|
|
40540
|
+
const rangeLabel = range ? `agentSlots ${range.min}\u2013${range.max}` : "agentSlots in .har/stages.json";
|
|
40541
|
+
const higher = free.filter((id) => id > agentId);
|
|
40542
|
+
const suggested = higher[0] ?? free[0];
|
|
40543
|
+
const lines = [];
|
|
40544
|
+
if (suggested !== void 0) {
|
|
40545
|
+
const kind = higher.length > 0 ? "higher slot" : "slot";
|
|
40546
|
+
lines.push(
|
|
40547
|
+
`Use a free ${kind}: har env launch ${suggested} (free: ${free.join(", ")}; ${rangeLabel}).`
|
|
40548
|
+
);
|
|
40549
|
+
} else if (range) {
|
|
40550
|
+
lines.push(`No free slot in ${rangeLabel}. Raise agentSlots.max in .har/stages.json.`);
|
|
40551
|
+
} else {
|
|
40552
|
+
lines.push("Use a different agent slot (see agentSlots in .har/stages.json).");
|
|
40553
|
+
}
|
|
40554
|
+
lines.push(
|
|
40555
|
+
"These processes belong to another project and must not be deleted from this checkout."
|
|
40556
|
+
);
|
|
40557
|
+
const where = cwds.length === 0 ? "that project checkout (cwd unknown)" : cwds.join(", ");
|
|
40558
|
+
lines.push(`To stop that other session, run \`har env teardown ${agentId}\` in ${where}.`);
|
|
40559
|
+
return { lines, suggested };
|
|
40560
|
+
}
|
|
40500
40561
|
function infraEnabled2(env3, service) {
|
|
40501
40562
|
return ` ${env3.HARNESS_INFRA_SERVICES ?? ""} `.includes(` ${service} `);
|
|
40502
40563
|
}
|
|
@@ -40550,12 +40611,17 @@ function inspectSlotReadiness(repoPath, agentId, options = {}) {
|
|
|
40550
40611
|
const foreign = usesPm2 ? detectForeignPm2(projectName, agentId, pm2Procs) : void 0;
|
|
40551
40612
|
if (foreign) {
|
|
40552
40613
|
const names = foreign.processes.map((p) => p.name).join(", ");
|
|
40614
|
+
const slots = freeAgentSlots(harnessRoot, projectName, agentId, pm2Procs);
|
|
40615
|
+
const advice = foreignPm2Advice(agentId, foreign.processes, slots.free, slots.range);
|
|
40553
40616
|
blockers.push({
|
|
40554
40617
|
code: "foreign_pm2",
|
|
40555
40618
|
message: `Foreign PM2 processes match agent ${agentId}: ${names}`,
|
|
40556
|
-
remediation: "
|
|
40557
|
-
details: { processes: foreign.processes }
|
|
40619
|
+
remediation: advice.lines.join(" "),
|
|
40620
|
+
details: { processes: foreign.processes, adviceLines: advice.lines }
|
|
40558
40621
|
});
|
|
40622
|
+
if (advice.suggested !== void 0) {
|
|
40623
|
+
remediations.push(`har env launch ${advice.suggested}`);
|
|
40624
|
+
}
|
|
40559
40625
|
remediations.push(`Inspect with: ${packageRunner()} pm2 jlist | grep agent-${agentId}`);
|
|
40560
40626
|
}
|
|
40561
40627
|
if (usesPm2 && pm2Procs) {
|
|
@@ -40708,10 +40774,14 @@ function bashBlockerLines(agentId, b2) {
|
|
|
40708
40774
|
switch (b2.code) {
|
|
40709
40775
|
case "foreign_pm2": {
|
|
40710
40776
|
const procs = details.processes ?? [];
|
|
40777
|
+
const advice = Array.isArray(details.adviceLines) ? details.adviceLines : [
|
|
40778
|
+
"Use a different agent slot.",
|
|
40779
|
+
"These processes belong to another project and must not be deleted from this checkout."
|
|
40780
|
+
];
|
|
40711
40781
|
return [
|
|
40712
40782
|
`ERROR: foreign PM2 processes match agent ${agentId}:`,
|
|
40713
40783
|
...procs.map((p) => ` ${p.name} cwd=${p.cwd ?? "unknown"}`),
|
|
40714
|
-
|
|
40784
|
+
...advice.map((line) => ` ${line}`)
|
|
40715
40785
|
];
|
|
40716
40786
|
}
|
|
40717
40787
|
case "control_port_conflict":
|
|
@@ -40814,6 +40884,7 @@ var init_slot_preflight = __esm({
|
|
|
40814
40884
|
init_slot_launch_guard_occupied();
|
|
40815
40885
|
init_slot_ports();
|
|
40816
40886
|
init_capabilities();
|
|
40887
|
+
init_stages();
|
|
40817
40888
|
init_slot_registry();
|
|
40818
40889
|
init_worktree_untracked();
|
|
40819
40890
|
import_child_process11 = require("child_process");
|
|
@@ -42480,7 +42551,7 @@ function detectPm2Issue(projectName, agentId, session, procs) {
|
|
|
42480
42551
|
const legacyPrefix = `agent-${agentId}-`;
|
|
42481
42552
|
const owned = procs.filter((p) => p.name?.startsWith(slotPrefix));
|
|
42482
42553
|
const foreign = procs.filter(
|
|
42483
|
-
(p) => p.name &&
|
|
42554
|
+
(p) => p.name && p.name.startsWith(legacyPrefix) && !p.name.startsWith("har-")
|
|
42484
42555
|
);
|
|
42485
42556
|
if (foreign.length > 0) return "foreign_pm2";
|
|
42486
42557
|
if (owned.length > 0) {
|
|
@@ -84485,6 +84556,7 @@ function applyLine(repoPath, lineSpec, options = {}) {
|
|
|
84485
84556
|
// src/harness/doctor.ts
|
|
84486
84557
|
init_stages();
|
|
84487
84558
|
init_hooks2();
|
|
84559
|
+
init_agent_env();
|
|
84488
84560
|
|
|
84489
84561
|
// src/harness/migrations.ts
|
|
84490
84562
|
var fs64 = __toESM(require("fs"));
|
|
@@ -84969,6 +85041,7 @@ var CHECK_LABELS = {
|
|
|
84969
85041
|
"lifecycle-stages": "lifecycle stages (launch/verify/teardown)",
|
|
84970
85042
|
"verification-ids": "verificationStages ids",
|
|
84971
85043
|
"port-lanes": "infra port lanes",
|
|
85044
|
+
"env-template": "env.template placeholders",
|
|
84972
85045
|
"slot-registry": "slot registry worktrees",
|
|
84973
85046
|
hooks: "lifecycle hooks (.har/hooks)",
|
|
84974
85047
|
"retired-machinery": "no references to retired runtime machinery",
|
|
@@ -85196,6 +85269,27 @@ function runDoctor(repoPath) {
|
|
|
85196
85269
|
}
|
|
85197
85270
|
}
|
|
85198
85271
|
}
|
|
85272
|
+
const envTemplatePath = path73.join(harnessDir, "env.template");
|
|
85273
|
+
if (!fs65.existsSync(envTemplatePath)) {
|
|
85274
|
+
skipped.add("env-template");
|
|
85275
|
+
} else {
|
|
85276
|
+
const allowed = new Set(AGENT_ENV_TEMPLATE_VARS);
|
|
85277
|
+
const allowedVars = AGENT_ENV_TEMPLATE_VARS.join(", ");
|
|
85278
|
+
const template = fs65.readFileSync(envTemplatePath, "utf8");
|
|
85279
|
+
for (const { name, reason, lines } of findUnsubstitutedTemplateVars(template)) {
|
|
85280
|
+
const rest = lines.slice(1);
|
|
85281
|
+
const more = rest.length ? ` (also line${rest.length > 1 ? "s" : ""} ${rest.join(", ")})` : "";
|
|
85282
|
+
const literalDollar = "A literal `$` cannot be written in env.template: envsubst has no escape and keeps `\\$` as-is";
|
|
85283
|
+
findings.push({
|
|
85284
|
+
check: "env-template",
|
|
85285
|
+
severity: "warning",
|
|
85286
|
+
file: "env.template",
|
|
85287
|
+
line: lines[0],
|
|
85288
|
+
message: reason === "parameter-syntax" ? `\${${name}\u2026} uses shell parameter syntax, which launch never expands \u2014 the whole reference is written literally to .env.agent.<id>${more}` : `\${${name}} is not substituted at launch and will be written literally to .env.agent.<id>${more}`,
|
|
85289
|
+
remedy: reason === "parameter-syntax" && allowed.has(name) ? `Write a plain \${${name}}; :- / :? / # / % are not supported by envsubst` : `Use one of ${allowedVars} (plain \${NAME}, no modifiers), or hard-code the value (variables in harness.env are not substituted into env.template). ${literalDollar}`
|
|
85290
|
+
});
|
|
85291
|
+
}
|
|
85292
|
+
}
|
|
85199
85293
|
const manifest = readManifest(repoPath);
|
|
85200
85294
|
if (!manifest?.ejected) {
|
|
85201
85295
|
skipped.add("ejected-runtime");
|