@osfactory/har 0.49.2 → 0.50.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 +82 -46
- package/dist/templates/adaptation-prompt-init.md +5 -0
- package/dist/templates/adaptation-prompt-maintain.md +1 -1
- package/dist/templates/har-boilerplate/agent-cli.sh +7 -6
- package/dist/templates/har-boilerplate/agent-slot.sh +1 -1
- package/dist/templates/har-boilerplate/harness.env +117 -0
- package/dist/templates/har-boilerplate/launch.sh +3 -3
- package/dist/templates/har-boilerplate/provision-toolchain.sh +136 -15
- package/dist/templates/har-boilerplate/setup-infra.sh +1 -1
- package/dist/templates/har-boilerplate/teardown.sh +1 -1
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +1 -1
- package/dist/templates/har-boilerplate-cli/harness.env +117 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +3 -2
- package/dist/templates/har-boilerplate-cli/provision-toolchain.sh +136 -15
- package/dist/templates/har-boilerplate-cli/setup-infra.sh +1 -1
- package/dist/templates/har-boilerplate-ios/harness.env +117 -0
- package/dist/templates/har-boilerplate-ios/provision-toolchain.sh +136 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29790,10 +29790,32 @@ var init_slot_launch_guard_occupied = __esm({
|
|
|
29790
29790
|
}
|
|
29791
29791
|
});
|
|
29792
29792
|
|
|
29793
|
+
// src/utils/package-runner.ts
|
|
29794
|
+
function commandExists(command2) {
|
|
29795
|
+
const result = (0, import_child_process6.spawnSync)("sh", ["-c", `command -v ${JSON.stringify(command2)}`], {
|
|
29796
|
+
encoding: "utf8"
|
|
29797
|
+
});
|
|
29798
|
+
return result.status === 0 && Boolean(result.stdout?.trim());
|
|
29799
|
+
}
|
|
29800
|
+
function packageRunner() {
|
|
29801
|
+
if (cached !== void 0) return cached;
|
|
29802
|
+
const available = RUNNERS.find((runner) => commandExists(runner.split(" ")[0]));
|
|
29803
|
+
cached = available ?? RUNNERS[0];
|
|
29804
|
+
return cached;
|
|
29805
|
+
}
|
|
29806
|
+
var import_child_process6, RUNNERS, cached;
|
|
29807
|
+
var init_package_runner = __esm({
|
|
29808
|
+
"src/utils/package-runner.ts"() {
|
|
29809
|
+
"use strict";
|
|
29810
|
+
import_child_process6 = require("child_process");
|
|
29811
|
+
RUNNERS = ["npx --yes", "bunx", "pnpm dlx", "yarn dlx"];
|
|
29812
|
+
}
|
|
29813
|
+
});
|
|
29814
|
+
|
|
29793
29815
|
// src/core/slot-preflight.ts
|
|
29794
29816
|
function listPm2Processes() {
|
|
29795
29817
|
try {
|
|
29796
|
-
const raw = (0,
|
|
29818
|
+
const raw = (0, import_child_process7.execSync)(`${packageRunner()} pm2 jlist`, {
|
|
29797
29819
|
encoding: "utf8",
|
|
29798
29820
|
stdio: ["pipe", "pipe", "ignore"],
|
|
29799
29821
|
timeout: 3e3
|
|
@@ -29874,7 +29896,7 @@ function inspectSlotReadiness(repoPath, agentId, options = {}) {
|
|
|
29874
29896
|
remediation: "Stop the other harness session (./.har/teardown.sh in that repo) or use a different agent slot.",
|
|
29875
29897
|
details: { processes: foreign.processes }
|
|
29876
29898
|
});
|
|
29877
|
-
remediations.push(
|
|
29899
|
+
remediations.push(`Inspect with: ${packageRunner()} pm2 jlist | grep agent-${agentId}`);
|
|
29878
29900
|
}
|
|
29879
29901
|
if (usesPm2 && pm2Procs) {
|
|
29880
29902
|
const slotPrefix = `har-${projectName}-agent-${agentId}-`;
|
|
@@ -30008,7 +30030,7 @@ function formatPreflightReport(agentId, readiness) {
|
|
|
30008
30030
|
}
|
|
30009
30031
|
return lines2.join("\n");
|
|
30010
30032
|
}
|
|
30011
|
-
var path16,
|
|
30033
|
+
var path16, import_child_process7;
|
|
30012
30034
|
var init_slot_preflight = __esm({
|
|
30013
30035
|
"src/core/slot-preflight.ts"() {
|
|
30014
30036
|
"use strict";
|
|
@@ -30019,14 +30041,15 @@ var init_slot_preflight = __esm({
|
|
|
30019
30041
|
init_slot_launch_guard_occupied();
|
|
30020
30042
|
init_slot_ports();
|
|
30021
30043
|
init_slot_registry();
|
|
30022
|
-
|
|
30044
|
+
import_child_process7 = require("child_process");
|
|
30045
|
+
init_package_runner();
|
|
30023
30046
|
}
|
|
30024
30047
|
});
|
|
30025
30048
|
|
|
30026
30049
|
// src/core/slot-status.ts
|
|
30027
30050
|
function readGitRemote(repoPath) {
|
|
30028
30051
|
try {
|
|
30029
|
-
const remote = (0,
|
|
30052
|
+
const remote = (0, import_child_process8.execSync)("git remote get-url origin", {
|
|
30030
30053
|
cwd: repoPath,
|
|
30031
30054
|
encoding: "utf8",
|
|
30032
30055
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -30038,7 +30061,7 @@ function readGitRemote(repoPath) {
|
|
|
30038
30061
|
}
|
|
30039
30062
|
function runGit2(cwd, args) {
|
|
30040
30063
|
try {
|
|
30041
|
-
return (0,
|
|
30064
|
+
return (0, import_child_process8.execSync)(`git ${args}`, {
|
|
30042
30065
|
cwd,
|
|
30043
30066
|
encoding: "utf8",
|
|
30044
30067
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -30114,7 +30137,7 @@ function lastBuildPass(run2) {
|
|
|
30114
30137
|
}
|
|
30115
30138
|
function listPm2Processes2() {
|
|
30116
30139
|
try {
|
|
30117
|
-
const raw = (0,
|
|
30140
|
+
const raw = (0, import_child_process8.execSync)(`${packageRunner()} pm2 jlist`, {
|
|
30118
30141
|
encoding: "utf8",
|
|
30119
30142
|
stdio: ["pipe", "pipe", "ignore"],
|
|
30120
30143
|
timeout: 3e3
|
|
@@ -30217,14 +30240,14 @@ function collectEnvironmentStatus(repoPath) {
|
|
|
30217
30240
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30218
30241
|
};
|
|
30219
30242
|
}
|
|
30220
|
-
var fs16, os6, path17,
|
|
30243
|
+
var fs16, os6, path17, import_child_process8, BYPASS_WARNING_MS;
|
|
30221
30244
|
var init_slot_status = __esm({
|
|
30222
30245
|
"src/core/slot-status.ts"() {
|
|
30223
30246
|
"use strict";
|
|
30224
30247
|
fs16 = __toESM(require("fs"));
|
|
30225
30248
|
os6 = __toESM(require("os"));
|
|
30226
30249
|
path17 = __toESM(require("path"));
|
|
30227
|
-
|
|
30250
|
+
import_child_process8 = require("child_process");
|
|
30228
30251
|
init_env();
|
|
30229
30252
|
init_manifest();
|
|
30230
30253
|
init_stages();
|
|
@@ -30232,6 +30255,7 @@ var init_slot_status = __esm({
|
|
|
30232
30255
|
init_runs();
|
|
30233
30256
|
init_slot_registry();
|
|
30234
30257
|
init_slot_preflight();
|
|
30258
|
+
init_package_runner();
|
|
30235
30259
|
BYPASS_WARNING_MS = 15 * 60 * 1e3;
|
|
30236
30260
|
}
|
|
30237
30261
|
});
|
|
@@ -53490,8 +53514,8 @@ exec "${otelHookCommand}" run \\
|
|
|
53490
53514
|
}
|
|
53491
53515
|
return wrapperPath;
|
|
53492
53516
|
}
|
|
53493
|
-
function
|
|
53494
|
-
const result = (0,
|
|
53517
|
+
function commandExists3(command2) {
|
|
53518
|
+
const result = (0, import_child_process11.spawnSync)("sh", ["-c", `command -v ${JSON.stringify(command2)}`], {
|
|
53495
53519
|
encoding: "utf8"
|
|
53496
53520
|
});
|
|
53497
53521
|
return result.status === 0 && Boolean(result.stdout?.trim());
|
|
@@ -53501,25 +53525,37 @@ function resolveOtelHookCommand(hooksHome) {
|
|
|
53501
53525
|
if (fs41.existsSync(local)) return local;
|
|
53502
53526
|
return null;
|
|
53503
53527
|
}
|
|
53528
|
+
function resolveHooksInstaller(hooksHome) {
|
|
53529
|
+
const candidates = [
|
|
53530
|
+
{
|
|
53531
|
+
command: "npm",
|
|
53532
|
+
args: ["install", "--prefix", hooksHome, "--no-fund", "--no-audit", OTEL_HOOKS_PACKAGE]
|
|
53533
|
+
},
|
|
53534
|
+
{ command: "bun", args: ["add", "--cwd", hooksHome, OTEL_HOOKS_PACKAGE] },
|
|
53535
|
+
{ command: "pnpm", args: ["add", "--dir", hooksHome, OTEL_HOOKS_PACKAGE] }
|
|
53536
|
+
];
|
|
53537
|
+
return candidates.find((candidate) => commandExists3(candidate.command)) ?? null;
|
|
53538
|
+
}
|
|
53504
53539
|
function tryInstallPackage(hooksHome) {
|
|
53505
53540
|
fs41.mkdirSync(hooksHome, { recursive: true });
|
|
53506
|
-
|
|
53541
|
+
const installer = resolveHooksInstaller(hooksHome);
|
|
53542
|
+
if (!installer) {
|
|
53507
53543
|
return {
|
|
53508
53544
|
ok: false,
|
|
53509
|
-
detail: "
|
|
53545
|
+
detail: "no package manager found; install Node.js or bun to use @osfactory/otel-hook"
|
|
53510
53546
|
};
|
|
53511
53547
|
}
|
|
53512
|
-
const
|
|
53513
|
-
|
|
53514
|
-
|
|
53515
|
-
|
|
53516
|
-
);
|
|
53548
|
+
const described = `${installer.command} ${installer.args.join(" ")}`;
|
|
53549
|
+
const result = (0, import_child_process11.spawnSync)(installer.command, installer.args, {
|
|
53550
|
+
encoding: "utf8",
|
|
53551
|
+
timeout: 18e4
|
|
53552
|
+
});
|
|
53517
53553
|
if (result.status === 0 && fs41.existsSync(localOtelHookBin(hooksHome))) {
|
|
53518
|
-
return { ok: true, detail:
|
|
53554
|
+
return { ok: true, detail: described };
|
|
53519
53555
|
}
|
|
53520
53556
|
return {
|
|
53521
53557
|
ok: false,
|
|
53522
|
-
detail:
|
|
53558
|
+
detail: `${described} failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
|
|
53523
53559
|
};
|
|
53524
53560
|
}
|
|
53525
53561
|
function uninstallLegacyPythonHooks(hooksHome) {
|
|
@@ -53530,9 +53566,9 @@ function uninstallLegacyPythonHooks(hooksHome) {
|
|
|
53530
53566
|
["pip3", ["uninstall", "-y", "opentelemetry-hooks"]]
|
|
53531
53567
|
];
|
|
53532
53568
|
for (const [cmd, args] of attempts) {
|
|
53533
|
-
if (!
|
|
53569
|
+
if (!commandExists3(cmd)) continue;
|
|
53534
53570
|
try {
|
|
53535
|
-
(0,
|
|
53571
|
+
(0, import_child_process11.spawnSync)(cmd, args, { encoding: "utf8", timeout: 6e4 });
|
|
53536
53572
|
} catch {
|
|
53537
53573
|
}
|
|
53538
53574
|
}
|
|
@@ -53564,7 +53600,7 @@ function normalizeLegacyProviderFlagsInCommand(command2) {
|
|
|
53564
53600
|
}
|
|
53565
53601
|
function runSetupProvider(providerId, otelHookCommand, wrapperPath) {
|
|
53566
53602
|
const hookCommand = `${wrapperPath} --provider ${providerId}`;
|
|
53567
|
-
const result = (0,
|
|
53603
|
+
const result = (0, import_child_process11.spawnSync)(
|
|
53568
53604
|
otelHookCommand,
|
|
53569
53605
|
[
|
|
53570
53606
|
"setup",
|
|
@@ -53805,11 +53841,11 @@ function ensureOtelHooks(options) {
|
|
|
53805
53841
|
function disableOtelHooksExport() {
|
|
53806
53842
|
return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
|
|
53807
53843
|
}
|
|
53808
|
-
var
|
|
53844
|
+
var import_child_process11, fs41, os15, path47, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
|
|
53809
53845
|
var init_otel_hooks = __esm({
|
|
53810
53846
|
"src/core/otel-hooks.ts"() {
|
|
53811
53847
|
"use strict";
|
|
53812
|
-
|
|
53848
|
+
import_child_process11 = require("child_process");
|
|
53813
53849
|
fs41 = __toESM(require("fs"));
|
|
53814
53850
|
os15 = __toESM(require("os"));
|
|
53815
53851
|
path47 = __toESM(require("path"));
|
|
@@ -70178,13 +70214,13 @@ var path44 = __toESM(require("path"));
|
|
|
70178
70214
|
var readline5 = __toESM(require("readline"));
|
|
70179
70215
|
|
|
70180
70216
|
// src/utils/clipboard.ts
|
|
70181
|
-
var
|
|
70182
|
-
function
|
|
70183
|
-
const probe = process.platform === "win32" ? (0,
|
|
70217
|
+
var import_child_process9 = require("child_process");
|
|
70218
|
+
function commandExists2(command2) {
|
|
70219
|
+
const probe = process.platform === "win32" ? (0, import_child_process9.spawnSync)("where", [command2], { encoding: "utf8", stdio: ["ignore", "ignore", "ignore"] }) : (0, import_child_process9.spawnSync)("which", [command2], { encoding: "utf8", stdio: ["ignore", "ignore", "ignore"] });
|
|
70184
70220
|
return probe.status === 0;
|
|
70185
70221
|
}
|
|
70186
70222
|
function tryPipeCommand(command2, args, text) {
|
|
70187
|
-
const result = (0,
|
|
70223
|
+
const result = (0, import_child_process9.spawnSync)(command2, args, {
|
|
70188
70224
|
input: text,
|
|
70189
70225
|
encoding: "utf8",
|
|
70190
70226
|
stdio: ["pipe", "ignore", "ignore"]
|
|
@@ -70199,12 +70235,12 @@ function writeOsc52Clipboard(text, stream = process.stderr) {
|
|
|
70199
70235
|
return true;
|
|
70200
70236
|
}
|
|
70201
70237
|
function copyViaPlatformTools(text) {
|
|
70202
|
-
if (process.platform === "darwin" &&
|
|
70238
|
+
if (process.platform === "darwin" && commandExists2("pbcopy")) {
|
|
70203
70239
|
if (tryPipeCommand("pbcopy", [], text)) return { ok: true, method: "pbcopy" };
|
|
70204
70240
|
}
|
|
70205
70241
|
if (process.platform === "win32") {
|
|
70206
|
-
if (
|
|
70207
|
-
const result = (0,
|
|
70242
|
+
if (commandExists2("powershell")) {
|
|
70243
|
+
const result = (0, import_child_process9.spawnSync)(
|
|
70208
70244
|
"powershell",
|
|
70209
70245
|
[
|
|
70210
70246
|
"-NoProfile",
|
|
@@ -70220,18 +70256,18 @@ function copyViaPlatformTools(text) {
|
|
|
70220
70256
|
);
|
|
70221
70257
|
if (result.status === 0 && !result.error) return { ok: true, method: "powershell" };
|
|
70222
70258
|
}
|
|
70223
|
-
if (
|
|
70259
|
+
if (commandExists2("clip") && tryPipeCommand("clip", [], text)) {
|
|
70224
70260
|
return { ok: true, method: "clip" };
|
|
70225
70261
|
}
|
|
70226
70262
|
}
|
|
70227
70263
|
if (process.platform === "linux" || process.platform === "freebsd" || process.platform === "openbsd") {
|
|
70228
|
-
if (
|
|
70264
|
+
if (commandExists2("wl-copy") && tryPipeCommand("wl-copy", [], text)) {
|
|
70229
70265
|
return { ok: true, method: "wl-copy" };
|
|
70230
70266
|
}
|
|
70231
|
-
if (
|
|
70267
|
+
if (commandExists2("xclip") && tryPipeCommand("xclip", ["-selection", "clipboard"], text)) {
|
|
70232
70268
|
return { ok: true, method: "xclip" };
|
|
70233
70269
|
}
|
|
70234
|
-
if (
|
|
70270
|
+
if (commandExists2("xsel") && tryPipeCommand("xsel", ["--clipboard", "--input"], text)) {
|
|
70235
70271
|
return { ok: true, method: "xsel" };
|
|
70236
70272
|
}
|
|
70237
70273
|
}
|
|
@@ -70450,7 +70486,7 @@ init_validation();
|
|
|
70450
70486
|
init_control_config();
|
|
70451
70487
|
|
|
70452
70488
|
// src/core/control-lifecycle.ts
|
|
70453
|
-
var
|
|
70489
|
+
var import_child_process10 = require("child_process");
|
|
70454
70490
|
var path46 = __toESM(require("path"));
|
|
70455
70491
|
|
|
70456
70492
|
// src/core/control-image.ts
|
|
@@ -70503,7 +70539,7 @@ function buildDockerRunArgs(options) {
|
|
|
70503
70539
|
return args;
|
|
70504
70540
|
}
|
|
70505
70541
|
function runDocker(args) {
|
|
70506
|
-
const result = (0,
|
|
70542
|
+
const result = (0, import_child_process10.spawnSync)("docker", args, {
|
|
70507
70543
|
stdio: "inherit",
|
|
70508
70544
|
env: process.env
|
|
70509
70545
|
});
|
|
@@ -70513,7 +70549,7 @@ function runDocker(args) {
|
|
|
70513
70549
|
return result.status ?? 1;
|
|
70514
70550
|
}
|
|
70515
70551
|
function controlContainerExists(name = CONTROL_CONTAINER_NAME) {
|
|
70516
|
-
const result = (0,
|
|
70552
|
+
const result = (0, import_child_process10.spawnSync)("docker", ["ps", "-aq", "-f", `name=^${name}$`], {
|
|
70517
70553
|
encoding: "utf8",
|
|
70518
70554
|
stdio: ["pipe", "pipe", "ignore"]
|
|
70519
70555
|
});
|
|
@@ -72353,13 +72389,13 @@ init_portal_credentials();
|
|
|
72353
72389
|
// src/core/portal-login.ts
|
|
72354
72390
|
var http = __toESM(require("http"));
|
|
72355
72391
|
var import_crypto = require("crypto");
|
|
72356
|
-
var
|
|
72392
|
+
var import_child_process12 = require("child_process");
|
|
72357
72393
|
var CALLBACK_PATH = "/callback";
|
|
72358
72394
|
var LOGIN_TIMEOUT_MS = 3 * 6e4;
|
|
72359
72395
|
function openBrowser(url) {
|
|
72360
72396
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
72361
72397
|
try {
|
|
72362
|
-
const child = (0,
|
|
72398
|
+
const child = (0, import_child_process12.spawn)(cmd, args, { stdio: "ignore", detached: true });
|
|
72363
72399
|
child.on("error", () => {
|
|
72364
72400
|
});
|
|
72365
72401
|
child.unref();
|
|
@@ -73519,7 +73555,7 @@ __export(util_exports, {
|
|
|
73519
73555
|
assertNever: () => assertNever,
|
|
73520
73556
|
assertNotEqual: () => assertNotEqual,
|
|
73521
73557
|
assignProp: () => assignProp,
|
|
73522
|
-
cached: () =>
|
|
73558
|
+
cached: () => cached2,
|
|
73523
73559
|
captureStackTrace: () => captureStackTrace,
|
|
73524
73560
|
cleanEnum: () => cleanEnum,
|
|
73525
73561
|
cleanRegex: () => cleanRegex,
|
|
@@ -73584,7 +73620,7 @@ function jsonStringifyReplacer(_2, value) {
|
|
|
73584
73620
|
return value.toString();
|
|
73585
73621
|
return value;
|
|
73586
73622
|
}
|
|
73587
|
-
function
|
|
73623
|
+
function cached2(getter) {
|
|
73588
73624
|
const set = false;
|
|
73589
73625
|
return {
|
|
73590
73626
|
get value() {
|
|
@@ -73673,7 +73709,7 @@ var captureStackTrace = Error.captureStackTrace ? Error.captureStackTrace : (...
|
|
|
73673
73709
|
function isObject(data) {
|
|
73674
73710
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
73675
73711
|
}
|
|
73676
|
-
var allowsEval =
|
|
73712
|
+
var allowsEval = cached2(() => {
|
|
73677
73713
|
if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
|
|
73678
73714
|
return false;
|
|
73679
73715
|
}
|
|
@@ -75168,7 +75204,7 @@ function handleOptionalObjectResult(result, final, key, input) {
|
|
|
75168
75204
|
}
|
|
75169
75205
|
var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
75170
75206
|
$ZodType.init(inst, def);
|
|
75171
|
-
const _normalized =
|
|
75207
|
+
const _normalized = cached2(() => {
|
|
75172
75208
|
const keys = Object.keys(def.shape);
|
|
75173
75209
|
for (const k of keys) {
|
|
75174
75210
|
if (!(def.shape[k] instanceof $ZodType)) {
|
|
@@ -75403,7 +75439,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
75403
75439
|
}
|
|
75404
75440
|
return propValues;
|
|
75405
75441
|
});
|
|
75406
|
-
const disc =
|
|
75442
|
+
const disc = cached2(() => {
|
|
75407
75443
|
const opts = def.options;
|
|
75408
75444
|
const map = /* @__PURE__ */ new Map();
|
|
75409
75445
|
for (const o of opts) {
|
|
@@ -69,6 +69,11 @@ how this project is built and tested. Use toolchain variables from `.env.agent.<
|
|
|
69
69
|
The stock verify section is keyed by `HARNESS_ECOSYSTEM`; it is a starting point,
|
|
70
70
|
not the repo's final contract. Replace conventions that do not match this project.
|
|
71
71
|
|
|
72
|
+
`${NPM_BIN}` may resolve to bun, pnpm, or yarn as well as npm, so keep Node steps
|
|
73
|
+
package-manager agnostic: always `${NPM_BIN:-npm} run <script>` (never bare
|
|
74
|
+
`${NPM_BIN} test`, which runs bun's own test runner), and never npm-only flags
|
|
75
|
+
such as `--prefix` — use a subshell like `(cd docs && ${NPM_BIN:-npm} run build)`.
|
|
76
|
+
|
|
72
77
|
**Tier contract:**
|
|
73
78
|
|
|
74
79
|
| Mode | Command | Intent |
|
|
@@ -25,7 +25,7 @@ Prefer targeted edits over full rewrites. Key files to review:
|
|
|
25
25
|
Keep this accurate — it is the harness index. Update whenever scripts, stages, or workflow change.
|
|
26
26
|
|
|
27
27
|
### `.har/harness.env`, `verify.sh`, `provision-toolchain.sh`, `ecosystem.agent.template.cjs`, `CLAUDE.agent.md`
|
|
28
|
-
Align commands and instructions with the current stack. Verify steps must use toolchain paths from `.env.agent.<id>` (`PYTHON_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) — never hardcoded venv or interpreter paths. Replace stock ecosystem conventions that do not match the repository; do not leave npm/pytest/go/cargo/maven/gradle examples in place by accident.
|
|
28
|
+
Align commands and instructions with the current stack. Verify steps must use toolchain paths from `.env.agent.<id>` (`PYTHON_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) — never hardcoded venv or interpreter paths. `NPM_BIN` may be bun, pnpm, or yarn, so Node steps must use `${NPM_BIN:-npm} run <script>` and avoid npm-only flags such as `--prefix`. Replace stock ecosystem conventions that do not match the repository; do not leave npm/pytest/go/cargo/maven/gradle examples in place by accident.
|
|
29
29
|
|
|
30
30
|
### `.har/env.template`, `setup-infra.sh`, `docker-compose.agent.yml`
|
|
31
31
|
Update only if infra changed.
|
|
@@ -28,7 +28,7 @@ case "$COMMAND" in
|
|
|
28
28
|
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT" || true)"
|
|
29
29
|
WORKTREE_DIR="$(existing_slot_worktree "$AGENT_ID")"
|
|
30
30
|
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
31
|
-
PM2_RAW=$(
|
|
31
|
+
PM2_RAW=$($(har_pkg_exec) pm2 jlist 2>/dev/null || true)
|
|
32
32
|
PM2_FOUND=false
|
|
33
33
|
PM2_FOREIGN=false
|
|
34
34
|
PM2_LEGACY=false
|
|
@@ -123,22 +123,23 @@ process.stdin.on('end', () => {
|
|
|
123
123
|
logs)
|
|
124
124
|
SERVICE="${3:-}"
|
|
125
125
|
if [ -n "$SERVICE" ]; then
|
|
126
|
-
|
|
126
|
+
$(har_pkg_exec) pm2 logs "${PM2_SLOT_PREFIX}-${SERVICE}" --lines 100
|
|
127
127
|
else
|
|
128
|
-
|
|
128
|
+
$(har_pkg_exec) pm2 logs --name "${PM2_SLOT_PREFIX}" --lines 100
|
|
129
129
|
fi
|
|
130
130
|
;;
|
|
131
131
|
|
|
132
132
|
restart)
|
|
133
133
|
SERVICE="${3:-}"
|
|
134
|
+
PKG_EXEC="$(har_pkg_exec)"
|
|
134
135
|
if [ -n "$SERVICE" ]; then
|
|
135
|
-
|
|
136
|
+
$(har_pkg_exec) pm2 restart "${PM2_SLOT_PREFIX}-${SERVICE}"
|
|
136
137
|
else
|
|
137
|
-
|
|
138
|
+
$(har_pkg_exec) pm2 jlist 2>/dev/null | node -e "
|
|
138
139
|
const prefix = '${PM2_SLOT_PREFIX}-';
|
|
139
140
|
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|
|
140
141
|
const names = d.filter(x => x.name && x.name.startsWith(prefix)).map(x => x.name);
|
|
141
|
-
names.forEach(n => require('child_process').execSync('
|
|
142
|
+
names.forEach(n => require('child_process').execSync('${PKG_EXEC} pm2 restart ' + n, {stdio:'inherit'}));
|
|
142
143
|
if (names.length === 0) console.log('No processes found for ${PM2_SLOT_PREFIX}');
|
|
143
144
|
" 2>/dev/null || true
|
|
144
145
|
fi
|
|
@@ -184,7 +184,7 @@ har_check_foreign_pm2() {
|
|
|
184
184
|
local agent_id="$1"
|
|
185
185
|
local pm2_raw
|
|
186
186
|
har_harness_uses_pm2 || return 0
|
|
187
|
-
pm2_raw="$(
|
|
187
|
+
pm2_raw="$($(har_pkg_exec) pm2 jlist 2>/dev/null || true)"
|
|
188
188
|
[ -n "$pm2_raw" ] || return 0
|
|
189
189
|
set +e
|
|
190
190
|
echo "$pm2_raw" | node -e "
|
|
@@ -51,6 +51,10 @@ export HARNESS_AGENT_SLOT_MAX=5
|
|
|
51
51
|
# Ecosystem: auto (detect from manifests) | node | python | go | rust | java | ruby | none
|
|
52
52
|
export HARNESS_ECOSYSTEM="auto"
|
|
53
53
|
# export HARNESS_INSTALL_CMD="make deps"
|
|
54
|
+
# Node package manager: auto-detected from package.json "packageManager" and the
|
|
55
|
+
# lockfile (bun.lock → bun, pnpm-lock.yaml → pnpm, yarn.lock → yarn, else npm),
|
|
56
|
+
# falling back to whichever is installed. Set to pin one: npm | bun | pnpm | yarn
|
|
57
|
+
# export HARNESS_NODE_PACKAGE_MANAGER="bun"
|
|
54
58
|
|
|
55
59
|
# Shared infrastructure — space-separated docker compose service names from
|
|
56
60
|
# docker-compose.agent.yml. Started ONCE by setup-infra.sh and shared by all
|
|
@@ -66,6 +70,119 @@ har_infra_enabled() {
|
|
|
66
70
|
esac
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
# ── Node package manager helpers ──────────────────────────────────────────────
|
|
74
|
+
# Mirrored verbatim in harness.env and provision-toolchain.sh so a sourcing
|
|
75
|
+
# script and the provisioning subprocess resolve the same tool.
|
|
76
|
+
|
|
77
|
+
# Package managers HAR can drive, in fallback preference order.
|
|
78
|
+
HAR_NODE_PACKAGE_MANAGERS="npm bun pnpm yarn"
|
|
79
|
+
|
|
80
|
+
# Package manager the repo declares: an explicit HARNESS_NODE_PACKAGE_MANAGER
|
|
81
|
+
# wins, then package.json "packageManager", then the lockfile. Empty when the
|
|
82
|
+
# repo says nothing.
|
|
83
|
+
har_node_declared_package_manager() {
|
|
84
|
+
local dir="${1:-.}"
|
|
85
|
+
|
|
86
|
+
if [ -n "${HARNESS_NODE_PACKAGE_MANAGER:-}" ]; then
|
|
87
|
+
echo "$HARNESS_NODE_PACKAGE_MANAGER"
|
|
88
|
+
return
|
|
89
|
+
fi
|
|
90
|
+
if [ -f "$dir/package.json" ]; then
|
|
91
|
+
local field
|
|
92
|
+
field="$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*"\([a-z]*\)@.*/\1/p' "$dir/package.json" | head -1)"
|
|
93
|
+
if [ -n "$field" ]; then
|
|
94
|
+
echo "$field"
|
|
95
|
+
return
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
if [ -f "$dir/bun.lock" ] || [ -f "$dir/bun.lockb" ]; then echo bun; return; fi
|
|
99
|
+
if [ -f "$dir/pnpm-lock.yaml" ]; then echo pnpm; return; fi
|
|
100
|
+
if [ -f "$dir/yarn.lock" ]; then echo yarn; return; fi
|
|
101
|
+
if [ -f "$dir/package-lock.json" ]; then echo npm; return; fi
|
|
102
|
+
echo ""
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Package manager to actually run. A manager the repo declares but this machine
|
|
106
|
+
# lacks falls back to one that is installed, so a repo pinned to npm still
|
|
107
|
+
# provisions on a bun-only machine (and the reverse).
|
|
108
|
+
har_node_package_manager() {
|
|
109
|
+
local dir="${1:-.}"
|
|
110
|
+
local declared
|
|
111
|
+
declared="$(har_node_declared_package_manager "$dir")"
|
|
112
|
+
|
|
113
|
+
if [ -n "$declared" ] && command -v "$declared" >/dev/null 2>&1; then
|
|
114
|
+
echo "$declared"
|
|
115
|
+
return
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
local candidate
|
|
119
|
+
for candidate in $HAR_NODE_PACKAGE_MANAGERS; do
|
|
120
|
+
if command -v "$candidate" >/dev/null 2>&1; then
|
|
121
|
+
echo "$candidate"
|
|
122
|
+
return
|
|
123
|
+
fi
|
|
124
|
+
done
|
|
125
|
+
|
|
126
|
+
echo "${declared:-npm}"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Lockfile a manager writes, so a substitute install can clean up after itself.
|
|
130
|
+
har_node_lockfile() {
|
|
131
|
+
case "$1" in
|
|
132
|
+
bun) echo "bun.lock" ;;
|
|
133
|
+
npm) echo "package-lock.json" ;;
|
|
134
|
+
pnpm) echo "pnpm-lock.yaml" ;;
|
|
135
|
+
yarn) echo "yarn.lock" ;;
|
|
136
|
+
*) echo "" ;;
|
|
137
|
+
esac
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Install dependencies in a directory. When a substitute manager stands in for
|
|
141
|
+
# the one the repo declares, any lockfile it creates is removed afterwards:
|
|
142
|
+
# provisioning must not migrate the repo to a different package manager.
|
|
143
|
+
# (bun writes bun.lock even under --no-save, so the flags alone are not enough.)
|
|
144
|
+
har_node_install() {
|
|
145
|
+
local dir="$1"
|
|
146
|
+
local manager="$2"
|
|
147
|
+
local declared="${3:-}"
|
|
148
|
+
local substituting=false
|
|
149
|
+
local lockfile=""
|
|
150
|
+
local had_lockfile=false
|
|
151
|
+
|
|
152
|
+
if [ -n "$declared" ] && [ "$declared" != "$manager" ]; then
|
|
153
|
+
substituting=true
|
|
154
|
+
lockfile="$(har_node_lockfile "$manager")"
|
|
155
|
+
if [ -n "$lockfile" ] && [ -e "$dir/$lockfile" ]; then
|
|
156
|
+
had_lockfile=true
|
|
157
|
+
fi
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
local code=0
|
|
161
|
+
(cd "$dir" && "$manager" install --silent) || code=$?
|
|
162
|
+
|
|
163
|
+
if [ "$substituting" = true ] && [ "$had_lockfile" = false ] && [ -n "$lockfile" ]; then
|
|
164
|
+
rm -f "$dir/$lockfile"
|
|
165
|
+
[ "$manager" = "bun" ] && rm -f "$dir/bun.lockb"
|
|
166
|
+
fi
|
|
167
|
+
return $code
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Package runner (npx equivalent) for one-off CLIs such as pm2, including the
|
|
171
|
+
# flag that keeps it non-interactive. Takes a manager name, or none to resolve
|
|
172
|
+
# from the environment and PATH.
|
|
173
|
+
har_pkg_exec() {
|
|
174
|
+
case "${1:-${HARNESS_NODE_PACKAGE_MANAGER:-}}" in
|
|
175
|
+
bun) echo "bunx"; return ;;
|
|
176
|
+
pnpm) echo "pnpm dlx"; return ;;
|
|
177
|
+
yarn) echo "yarn dlx"; return ;;
|
|
178
|
+
npm) echo "npx --yes"; return ;;
|
|
179
|
+
esac
|
|
180
|
+
if command -v npx >/dev/null 2>&1; then echo "npx --yes"; return; fi
|
|
181
|
+
if command -v bunx >/dev/null 2>&1; then echo "bunx"; return; fi
|
|
182
|
+
if command -v pnpm >/dev/null 2>&1; then echo "pnpm dlx"; return; fi
|
|
183
|
+
echo "npx --yes"
|
|
184
|
+
}
|
|
185
|
+
|
|
69
186
|
# Postgres client — uses host tools when installed, otherwise docker exec into
|
|
70
187
|
# the shared db container. Usage: har_pg psql -d postgres -c "SELECT 1"
|
|
71
188
|
# har_pg createdb -T template_x agent_1
|
|
@@ -249,13 +249,13 @@ DEBUG_PORT="$DEBUG_PORT" \
|
|
|
249
249
|
|
|
250
250
|
PM2_REGEX="$(har_pm2_delete_regex "$AGENT_ID")"
|
|
251
251
|
# Stop existing processes for this agent (project-scoped — never touch other harnesses).
|
|
252
|
-
|
|
252
|
+
$(har_pkg_exec) pm2 delete "$PM2_REGEX" 2>/dev/null || true
|
|
253
253
|
|
|
254
254
|
# Start PM2 processes
|
|
255
255
|
log "Starting PM2 processes..."
|
|
256
256
|
cd "$WORK_DIR"
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
$(har_pkg_exec) pm2 start "$ECOSYSTEM_FILE"
|
|
258
|
+
$(har_pkg_exec) pm2 save --force >/dev/null 2>&1 || true
|
|
259
259
|
|
|
260
260
|
# Health check
|
|
261
261
|
if [ -n "${HARNESS_HEALTH_CHECK_PATH:-}" ]; then
|