@osfactory/har 0.18.0 → 0.19.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 +943 -446
- package/dist/templates/cursor-rule.mdc.template +8 -7
- package/dist/templates/har-boilerplate/README.md +10 -8
- package/dist/templates/har-boilerplate/agent-slot.sh +11 -0
- package/dist/templates/har-boilerplate-cli/README.md +10 -8
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +11 -0
- package/dist/templates/har-boilerplate-ios/README.md +7 -3
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -765,14 +765,14 @@ var require_url_state_machine = __commonJS({
|
|
|
765
765
|
return url.replace(/\u0009|\u000A|\u000D/g, "");
|
|
766
766
|
}
|
|
767
767
|
function shortenPath(url) {
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
768
|
+
const path51 = url.path;
|
|
769
|
+
if (path51.length === 0) {
|
|
770
770
|
return;
|
|
771
771
|
}
|
|
772
|
-
if (url.scheme === "file" &&
|
|
772
|
+
if (url.scheme === "file" && path51.length === 1 && isNormalizedWindowsDriveLetter(path51[0])) {
|
|
773
773
|
return;
|
|
774
774
|
}
|
|
775
|
-
|
|
775
|
+
path51.pop();
|
|
776
776
|
}
|
|
777
777
|
function includesCredentials(url) {
|
|
778
778
|
return url.username !== "" || url.password !== "";
|
|
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
|
|
|
2092
2092
|
let accum = [];
|
|
2093
2093
|
let accumBytes = 0;
|
|
2094
2094
|
let abort = false;
|
|
2095
|
-
return new Body.Promise(function(
|
|
2095
|
+
return new Body.Promise(function(resolve36, reject) {
|
|
2096
2096
|
let resTimeout;
|
|
2097
2097
|
if (_this4.timeout) {
|
|
2098
2098
|
resTimeout = setTimeout(function() {
|
|
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
|
|
|
2126
2126
|
}
|
|
2127
2127
|
clearTimeout(resTimeout);
|
|
2128
2128
|
try {
|
|
2129
|
-
|
|
2129
|
+
resolve36(Buffer.concat(accum, accumBytes));
|
|
2130
2130
|
} catch (err) {
|
|
2131
2131
|
reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
|
|
2132
2132
|
}
|
|
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
|
|
|
2801
2801
|
throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
|
|
2802
2802
|
}
|
|
2803
2803
|
Body.Promise = fetch3.Promise;
|
|
2804
|
-
return new fetch3.Promise(function(
|
|
2804
|
+
return new fetch3.Promise(function(resolve36, reject) {
|
|
2805
2805
|
const request = new Request3(url, opts);
|
|
2806
2806
|
const options = getNodeRequestOptions(request);
|
|
2807
2807
|
const send = (options.protocol === "https:" ? https : http).request;
|
|
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
|
|
|
2934
2934
|
requestOpts.body = void 0;
|
|
2935
2935
|
requestOpts.headers.delete("content-length");
|
|
2936
2936
|
}
|
|
2937
|
-
|
|
2937
|
+
resolve36(fetch3(new Request3(locationURL, requestOpts)));
|
|
2938
2938
|
finalize();
|
|
2939
2939
|
return;
|
|
2940
2940
|
}
|
|
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
|
|
|
2955
2955
|
const codings = headers.get("Content-Encoding");
|
|
2956
2956
|
if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
|
|
2957
2957
|
response = new Response3(body, response_options);
|
|
2958
|
-
|
|
2958
|
+
resolve36(response);
|
|
2959
2959
|
return;
|
|
2960
2960
|
}
|
|
2961
2961
|
const zlibOptions = {
|
|
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
|
|
|
2965
2965
|
if (codings == "gzip" || codings == "x-gzip") {
|
|
2966
2966
|
body = body.pipe(zlib.createGunzip(zlibOptions));
|
|
2967
2967
|
response = new Response3(body, response_options);
|
|
2968
|
-
|
|
2968
|
+
resolve36(response);
|
|
2969
2969
|
return;
|
|
2970
2970
|
}
|
|
2971
2971
|
if (codings == "deflate" || codings == "x-deflate") {
|
|
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
|
|
|
2977
2977
|
body = body.pipe(zlib.createInflateRaw());
|
|
2978
2978
|
}
|
|
2979
2979
|
response = new Response3(body, response_options);
|
|
2980
|
-
|
|
2980
|
+
resolve36(response);
|
|
2981
2981
|
});
|
|
2982
2982
|
raw.on("end", function() {
|
|
2983
2983
|
if (!response) {
|
|
2984
2984
|
response = new Response3(body, response_options);
|
|
2985
|
-
|
|
2985
|
+
resolve36(response);
|
|
2986
2986
|
}
|
|
2987
2987
|
});
|
|
2988
2988
|
return;
|
|
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
|
|
|
2990
2990
|
if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
|
|
2991
2991
|
body = body.pipe(zlib.createBrotliDecompress());
|
|
2992
2992
|
response = new Response3(body, response_options);
|
|
2993
|
-
|
|
2993
|
+
resolve36(response);
|
|
2994
2994
|
return;
|
|
2995
2995
|
}
|
|
2996
2996
|
response = new Response3(body, response_options);
|
|
2997
|
-
|
|
2997
|
+
resolve36(response);
|
|
2998
2998
|
});
|
|
2999
2999
|
writeToStream(req, request);
|
|
3000
3000
|
});
|
|
@@ -6351,14 +6351,14 @@ __export(fileFromPath_exports, {
|
|
|
6351
6351
|
fileFromPathSync: () => fileFromPathSync,
|
|
6352
6352
|
isFile: () => isFile
|
|
6353
6353
|
});
|
|
6354
|
-
function createFileFromPath(
|
|
6354
|
+
function createFileFromPath(path51, { mtimeMs, size }, filenameOrOptions, options = {}) {
|
|
6355
6355
|
let filename;
|
|
6356
6356
|
if (isPlainObject_default2(filenameOrOptions)) {
|
|
6357
6357
|
[options, filename] = [filenameOrOptions, void 0];
|
|
6358
6358
|
} else {
|
|
6359
6359
|
filename = filenameOrOptions;
|
|
6360
6360
|
}
|
|
6361
|
-
const file = new FileFromPath({ path:
|
|
6361
|
+
const file = new FileFromPath({ path: path51, size, lastModified: mtimeMs });
|
|
6362
6362
|
if (!filename) {
|
|
6363
6363
|
filename = file.name;
|
|
6364
6364
|
}
|
|
@@ -6367,13 +6367,13 @@ function createFileFromPath(path49, { mtimeMs, size }, filenameOrOptions, option
|
|
|
6367
6367
|
lastModified: file.lastModified
|
|
6368
6368
|
});
|
|
6369
6369
|
}
|
|
6370
|
-
function fileFromPathSync(
|
|
6371
|
-
const stats = (0, import_fs5.statSync)(
|
|
6372
|
-
return createFileFromPath(
|
|
6370
|
+
function fileFromPathSync(path51, filenameOrOptions, options = {}) {
|
|
6371
|
+
const stats = (0, import_fs5.statSync)(path51);
|
|
6372
|
+
return createFileFromPath(path51, stats, filenameOrOptions, options);
|
|
6373
6373
|
}
|
|
6374
|
-
async function fileFromPath2(
|
|
6375
|
-
const stats = await import_fs5.promises.stat(
|
|
6376
|
-
return createFileFromPath(
|
|
6374
|
+
async function fileFromPath2(path51, filenameOrOptions, options) {
|
|
6375
|
+
const stats = await import_fs5.promises.stat(path51);
|
|
6376
|
+
return createFileFromPath(path51, stats, filenameOrOptions, options);
|
|
6377
6377
|
}
|
|
6378
6378
|
var import_fs5, import_path5, import_node_domexception, __classPrivateFieldSet5, __classPrivateFieldGet6, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
|
|
6379
6379
|
var init_fileFromPath = __esm({
|
|
@@ -6451,9 +6451,9 @@ var init_control_config = __esm({
|
|
|
6451
6451
|
// src/core/telemetry-config.ts
|
|
6452
6452
|
function getPreferencePath() {
|
|
6453
6453
|
if (process.env.HAR_TELEMETRY_CONFIG_PATH) {
|
|
6454
|
-
return
|
|
6454
|
+
return path35.resolve(process.env.HAR_TELEMETRY_CONFIG_PATH);
|
|
6455
6455
|
}
|
|
6456
|
-
return
|
|
6456
|
+
return path35.join(os9.homedir(), ".har", "telemetry.json");
|
|
6457
6457
|
}
|
|
6458
6458
|
function parseEnvOverride(raw) {
|
|
6459
6459
|
if (raw === void 0 || raw === "") return void 0;
|
|
@@ -6474,10 +6474,10 @@ function normalizeSignals(enabled, raw) {
|
|
|
6474
6474
|
function readTelemetryPreference() {
|
|
6475
6475
|
const preferencePath = getPreferencePath();
|
|
6476
6476
|
try {
|
|
6477
|
-
if (!
|
|
6477
|
+
if (!fs35.existsSync(preferencePath)) {
|
|
6478
6478
|
return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
|
|
6479
6479
|
}
|
|
6480
|
-
const parsed = JSON.parse(
|
|
6480
|
+
const parsed = JSON.parse(fs35.readFileSync(preferencePath, "utf8"));
|
|
6481
6481
|
const enabled = parsed.enabled !== false;
|
|
6482
6482
|
return {
|
|
6483
6483
|
enabled,
|
|
@@ -6500,8 +6500,8 @@ function writeTelemetryPreference(enabled, signals) {
|
|
|
6500
6500
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6501
6501
|
};
|
|
6502
6502
|
const preferencePath = getPreferencePath();
|
|
6503
|
-
|
|
6504
|
-
|
|
6503
|
+
fs35.mkdirSync(path35.dirname(preferencePath), { recursive: true });
|
|
6504
|
+
fs35.writeFileSync(preferencePath, JSON.stringify(preference, null, 2) + "\n");
|
|
6505
6505
|
return preference;
|
|
6506
6506
|
}
|
|
6507
6507
|
function isTelemetryEnabled() {
|
|
@@ -6516,13 +6516,13 @@ function getTelemetrySignals() {
|
|
|
6516
6516
|
function getTelemetryPreferencePath() {
|
|
6517
6517
|
return getPreferencePath();
|
|
6518
6518
|
}
|
|
6519
|
-
var
|
|
6519
|
+
var fs35, os9, path35, DEFAULT_SIGNALS_ON, DEFAULT_SIGNALS_OFF, TELEMETRY_SIGNALS;
|
|
6520
6520
|
var init_telemetry_config = __esm({
|
|
6521
6521
|
"src/core/telemetry-config.ts"() {
|
|
6522
6522
|
"use strict";
|
|
6523
|
-
|
|
6523
|
+
fs35 = __toESM(require("fs"));
|
|
6524
6524
|
os9 = __toESM(require("os"));
|
|
6525
|
-
|
|
6525
|
+
path35 = __toESM(require("path"));
|
|
6526
6526
|
DEFAULT_SIGNALS_ON = {
|
|
6527
6527
|
metrics: true,
|
|
6528
6528
|
logs: true,
|
|
@@ -6563,15 +6563,15 @@ __export(otel_hooks_exports, {
|
|
|
6563
6563
|
});
|
|
6564
6564
|
function getOtelHooksHome() {
|
|
6565
6565
|
if (process.env.HAR_OTEL_HOOKS_HOME) {
|
|
6566
|
-
return
|
|
6566
|
+
return path41.resolve(process.env.HAR_OTEL_HOOKS_HOME);
|
|
6567
6567
|
}
|
|
6568
|
-
return
|
|
6568
|
+
return path41.join(os12.homedir(), ".har", "otel-hooks");
|
|
6569
6569
|
}
|
|
6570
6570
|
function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
|
|
6571
|
-
return
|
|
6571
|
+
return path41.join(hooksHome, "otel_config.json");
|
|
6572
6572
|
}
|
|
6573
6573
|
function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
|
|
6574
|
-
return
|
|
6574
|
+
return path41.join(hooksHome, "run-otel-hook.sh");
|
|
6575
6575
|
}
|
|
6576
6576
|
function buildOtelHooksConfig(options) {
|
|
6577
6577
|
const enabled = options?.enabled ?? isTelemetryEnabled();
|
|
@@ -6590,18 +6590,18 @@ function buildOtelHooksConfig(options) {
|
|
|
6590
6590
|
};
|
|
6591
6591
|
}
|
|
6592
6592
|
function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
|
|
6593
|
-
|
|
6593
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6594
6594
|
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
6595
6595
|
const payload = {
|
|
6596
6596
|
_comment: "Managed by har telemetry \u2014 do not edit by hand; use: har telemetry on|off",
|
|
6597
6597
|
...config2
|
|
6598
6598
|
};
|
|
6599
|
-
|
|
6599
|
+
fs39.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}
|
|
6600
6600
|
`);
|
|
6601
6601
|
return configPath;
|
|
6602
6602
|
}
|
|
6603
6603
|
function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
|
|
6604
|
-
|
|
6604
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6605
6605
|
const wrapperPath = getOtelHooksWrapperPath(hooksHome);
|
|
6606
6606
|
const script = `#!/usr/bin/env bash
|
|
6607
6607
|
# Managed by har telemetry \u2014 invokes opentelemetry-hooks with HAR config home.
|
|
@@ -6609,9 +6609,9 @@ set -euo pipefail
|
|
|
6609
6609
|
export IDE_OTEL_HOOK_HOME="${hooksHome}"
|
|
6610
6610
|
exec "${otelHookCommand}" "$@"
|
|
6611
6611
|
`;
|
|
6612
|
-
|
|
6612
|
+
fs39.writeFileSync(wrapperPath, script, { mode: 493 });
|
|
6613
6613
|
try {
|
|
6614
|
-
|
|
6614
|
+
fs39.chmodSync(wrapperPath, 493);
|
|
6615
6615
|
} catch {
|
|
6616
6616
|
}
|
|
6617
6617
|
return wrapperPath;
|
|
@@ -6691,10 +6691,10 @@ function runSetupAgent(agent, otelHookCommand, hooksHome) {
|
|
|
6691
6691
|
};
|
|
6692
6692
|
}
|
|
6693
6693
|
function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
6694
|
-
if (!
|
|
6694
|
+
if (!fs39.existsSync(filePath)) return false;
|
|
6695
6695
|
let raw;
|
|
6696
6696
|
try {
|
|
6697
|
-
raw =
|
|
6697
|
+
raw = fs39.readFileSync(filePath, "utf8");
|
|
6698
6698
|
} catch {
|
|
6699
6699
|
return false;
|
|
6700
6700
|
}
|
|
@@ -6733,16 +6733,16 @@ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
|
6733
6733
|
return changed;
|
|
6734
6734
|
};
|
|
6735
6735
|
if (!walk(parsed)) return false;
|
|
6736
|
-
|
|
6736
|
+
fs39.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
6737
6737
|
`);
|
|
6738
6738
|
return true;
|
|
6739
6739
|
}
|
|
6740
6740
|
function rewriteKnownHookRegistrations(wrapperPath) {
|
|
6741
6741
|
const home = os12.homedir();
|
|
6742
6742
|
const candidates = [
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6743
|
+
path41.join(home, ".cursor", "hooks.json"),
|
|
6744
|
+
path41.join(home, ".claude", "settings.json"),
|
|
6745
|
+
path41.join(home, ".codex", "hooks.json")
|
|
6746
6746
|
];
|
|
6747
6747
|
for (const filePath of candidates) {
|
|
6748
6748
|
try {
|
|
@@ -6755,7 +6755,7 @@ function ensureOtelHooks(options) {
|
|
|
6755
6755
|
const hooksHome = getOtelHooksHome();
|
|
6756
6756
|
const errors = [];
|
|
6757
6757
|
const agentsSetup = [];
|
|
6758
|
-
|
|
6758
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6759
6759
|
let otelHookCommand = resolveOtelHookCommand();
|
|
6760
6760
|
if (!otelHookCommand) {
|
|
6761
6761
|
const installed = tryInstallPackage();
|
|
@@ -6824,14 +6824,14 @@ function ensureOtelHooks(options) {
|
|
|
6824
6824
|
function disableOtelHooksExport() {
|
|
6825
6825
|
return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
|
|
6826
6826
|
}
|
|
6827
|
-
var import_child_process9,
|
|
6827
|
+
var import_child_process9, fs39, os12, path41, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_AGENTS;
|
|
6828
6828
|
var init_otel_hooks = __esm({
|
|
6829
6829
|
"src/core/otel-hooks.ts"() {
|
|
6830
6830
|
"use strict";
|
|
6831
6831
|
import_child_process9 = require("child_process");
|
|
6832
|
-
|
|
6832
|
+
fs39 = __toESM(require("fs"));
|
|
6833
6833
|
os12 = __toESM(require("os"));
|
|
6834
|
-
|
|
6834
|
+
path41 = __toESM(require("path"));
|
|
6835
6835
|
init_control_config();
|
|
6836
6836
|
init_telemetry_config();
|
|
6837
6837
|
OTEL_HOOKS_PACKAGE = "opentelemetry-hooks==0.14.0";
|
|
@@ -9790,7 +9790,7 @@ var require_compile = __commonJS({
|
|
|
9790
9790
|
const schOrFunc = root.refs[ref];
|
|
9791
9791
|
if (schOrFunc)
|
|
9792
9792
|
return schOrFunc;
|
|
9793
|
-
let _sch =
|
|
9793
|
+
let _sch = resolve36.call(this, root, ref);
|
|
9794
9794
|
if (_sch === void 0) {
|
|
9795
9795
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
9796
9796
|
const { schemaId } = this.opts;
|
|
@@ -9817,7 +9817,7 @@ var require_compile = __commonJS({
|
|
|
9817
9817
|
function sameSchemaEnv(s1, s2) {
|
|
9818
9818
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
9819
9819
|
}
|
|
9820
|
-
function
|
|
9820
|
+
function resolve36(root, ref) {
|
|
9821
9821
|
let sch;
|
|
9822
9822
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
9823
9823
|
ref = sch;
|
|
@@ -10035,8 +10035,8 @@ var require_utils2 = __commonJS({
|
|
|
10035
10035
|
}
|
|
10036
10036
|
return ind;
|
|
10037
10037
|
}
|
|
10038
|
-
function removeDotSegments(
|
|
10039
|
-
let input =
|
|
10038
|
+
function removeDotSegments(path51) {
|
|
10039
|
+
let input = path51;
|
|
10040
10040
|
const output = [];
|
|
10041
10041
|
let nextSlash = -1;
|
|
10042
10042
|
let len = 0;
|
|
@@ -10288,8 +10288,8 @@ var require_schemes = __commonJS({
|
|
|
10288
10288
|
wsComponent.secure = void 0;
|
|
10289
10289
|
}
|
|
10290
10290
|
if (wsComponent.resourceName) {
|
|
10291
|
-
const [
|
|
10292
|
-
wsComponent.path =
|
|
10291
|
+
const [path51, query] = wsComponent.resourceName.split("?");
|
|
10292
|
+
wsComponent.path = path51 && path51 !== "/" ? path51 : void 0;
|
|
10293
10293
|
wsComponent.query = query;
|
|
10294
10294
|
wsComponent.resourceName = void 0;
|
|
10295
10295
|
}
|
|
@@ -10448,55 +10448,55 @@ var require_fast_uri = __commonJS({
|
|
|
10448
10448
|
}
|
|
10449
10449
|
return uri;
|
|
10450
10450
|
}
|
|
10451
|
-
function
|
|
10451
|
+
function resolve36(baseURI, relativeURI, options) {
|
|
10452
10452
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
10453
10453
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
10454
10454
|
schemelessOptions.skipEscape = true;
|
|
10455
10455
|
return serialize(resolved, schemelessOptions);
|
|
10456
10456
|
}
|
|
10457
|
-
function resolveComponent(base,
|
|
10457
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
10458
10458
|
const target = {};
|
|
10459
10459
|
if (!skipNormalization) {
|
|
10460
10460
|
base = parse4(serialize(base, options), options);
|
|
10461
|
-
|
|
10461
|
+
relative3 = parse4(serialize(relative3, options), options);
|
|
10462
10462
|
}
|
|
10463
10463
|
options = options || {};
|
|
10464
|
-
if (!options.tolerant &&
|
|
10465
|
-
target.scheme =
|
|
10466
|
-
target.userinfo =
|
|
10467
|
-
target.host =
|
|
10468
|
-
target.port =
|
|
10469
|
-
target.path = removeDotSegments(
|
|
10470
|
-
target.query =
|
|
10464
|
+
if (!options.tolerant && relative3.scheme) {
|
|
10465
|
+
target.scheme = relative3.scheme;
|
|
10466
|
+
target.userinfo = relative3.userinfo;
|
|
10467
|
+
target.host = relative3.host;
|
|
10468
|
+
target.port = relative3.port;
|
|
10469
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
10470
|
+
target.query = relative3.query;
|
|
10471
10471
|
} else {
|
|
10472
|
-
if (
|
|
10473
|
-
target.userinfo =
|
|
10474
|
-
target.host =
|
|
10475
|
-
target.port =
|
|
10476
|
-
target.path = removeDotSegments(
|
|
10477
|
-
target.query =
|
|
10472
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
10473
|
+
target.userinfo = relative3.userinfo;
|
|
10474
|
+
target.host = relative3.host;
|
|
10475
|
+
target.port = relative3.port;
|
|
10476
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
10477
|
+
target.query = relative3.query;
|
|
10478
10478
|
} else {
|
|
10479
|
-
if (!
|
|
10479
|
+
if (!relative3.path) {
|
|
10480
10480
|
target.path = base.path;
|
|
10481
|
-
if (
|
|
10482
|
-
target.query =
|
|
10481
|
+
if (relative3.query !== void 0) {
|
|
10482
|
+
target.query = relative3.query;
|
|
10483
10483
|
} else {
|
|
10484
10484
|
target.query = base.query;
|
|
10485
10485
|
}
|
|
10486
10486
|
} else {
|
|
10487
|
-
if (
|
|
10488
|
-
target.path = removeDotSegments(
|
|
10487
|
+
if (relative3.path[0] === "/") {
|
|
10488
|
+
target.path = removeDotSegments(relative3.path);
|
|
10489
10489
|
} else {
|
|
10490
10490
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
10491
|
-
target.path = "/" +
|
|
10491
|
+
target.path = "/" + relative3.path;
|
|
10492
10492
|
} else if (!base.path) {
|
|
10493
|
-
target.path =
|
|
10493
|
+
target.path = relative3.path;
|
|
10494
10494
|
} else {
|
|
10495
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
10495
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
10496
10496
|
}
|
|
10497
10497
|
target.path = removeDotSegments(target.path);
|
|
10498
10498
|
}
|
|
10499
|
-
target.query =
|
|
10499
|
+
target.query = relative3.query;
|
|
10500
10500
|
}
|
|
10501
10501
|
target.userinfo = base.userinfo;
|
|
10502
10502
|
target.host = base.host;
|
|
@@ -10504,7 +10504,7 @@ var require_fast_uri = __commonJS({
|
|
|
10504
10504
|
}
|
|
10505
10505
|
target.scheme = base.scheme;
|
|
10506
10506
|
}
|
|
10507
|
-
target.fragment =
|
|
10507
|
+
target.fragment = relative3.fragment;
|
|
10508
10508
|
return target;
|
|
10509
10509
|
}
|
|
10510
10510
|
function equal(uriA, uriB, options) {
|
|
@@ -10706,7 +10706,7 @@ var require_fast_uri = __commonJS({
|
|
|
10706
10706
|
var fastUri = {
|
|
10707
10707
|
SCHEMES,
|
|
10708
10708
|
normalize: normalize3,
|
|
10709
|
-
resolve:
|
|
10709
|
+
resolve: resolve36,
|
|
10710
10710
|
resolveComponent,
|
|
10711
10711
|
equal,
|
|
10712
10712
|
serialize,
|
|
@@ -13682,12 +13682,12 @@ var require_dist = __commonJS({
|
|
|
13682
13682
|
throw new Error(`Unknown format "${name}"`);
|
|
13683
13683
|
return f2;
|
|
13684
13684
|
};
|
|
13685
|
-
function addFormats(ajv, list,
|
|
13685
|
+
function addFormats(ajv, list, fs43, exportName) {
|
|
13686
13686
|
var _a3;
|
|
13687
13687
|
var _b2;
|
|
13688
13688
|
(_a3 = (_b2 = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
13689
13689
|
for (const f2 of list)
|
|
13690
|
-
ajv.addFormat(f2,
|
|
13690
|
+
ajv.addFormat(f2, fs43[f2]);
|
|
13691
13691
|
}
|
|
13692
13692
|
module2.exports = exports2 = formatsPlugin;
|
|
13693
13693
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -14971,11 +14971,11 @@ var parser = new YargsParser({
|
|
|
14971
14971
|
resolve: import_path2.resolve,
|
|
14972
14972
|
// TODO: figure out a way to combine ESM and CJS coverage, such that
|
|
14973
14973
|
// we can exercise all the lines below:
|
|
14974
|
-
require: (
|
|
14974
|
+
require: (path51) => {
|
|
14975
14975
|
if (typeof require !== "undefined") {
|
|
14976
|
-
return require(
|
|
14977
|
-
} else if (
|
|
14978
|
-
return JSON.parse((0, import_fs2.readFileSync)(
|
|
14976
|
+
return require(path51);
|
|
14977
|
+
} else if (path51.match(/\.json$/)) {
|
|
14978
|
+
return JSON.parse((0, import_fs2.readFileSync)(path51, "utf8"));
|
|
14979
14979
|
} else {
|
|
14980
14980
|
throw Error("only .json config files are supported in ESM");
|
|
14981
14981
|
}
|
|
@@ -16318,13 +16318,13 @@ function usage(yargs, shim3) {
|
|
|
16318
16318
|
};
|
|
16319
16319
|
self.stringifiedValues = function stringifiedValues(values, separator) {
|
|
16320
16320
|
let string3 = "";
|
|
16321
|
-
const
|
|
16321
|
+
const sep6 = separator || ", ";
|
|
16322
16322
|
const array2 = [].concat(values);
|
|
16323
16323
|
if (!values || !array2.length)
|
|
16324
16324
|
return string3;
|
|
16325
16325
|
array2.forEach((value) => {
|
|
16326
16326
|
if (string3.length)
|
|
16327
|
-
string3 +=
|
|
16327
|
+
string3 += sep6;
|
|
16328
16328
|
string3 += JSON.stringify(value);
|
|
16329
16329
|
});
|
|
16330
16330
|
return string3;
|
|
@@ -17501,12 +17501,12 @@ var YargsInstance = class {
|
|
|
17501
17501
|
async getCompletion(args, done) {
|
|
17502
17502
|
argsert("<array> [function]", [args, done], arguments.length);
|
|
17503
17503
|
if (!done) {
|
|
17504
|
-
return new Promise((
|
|
17504
|
+
return new Promise((resolve36, reject) => {
|
|
17505
17505
|
__classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
|
|
17506
17506
|
if (err)
|
|
17507
17507
|
reject(err);
|
|
17508
17508
|
else
|
|
17509
|
-
|
|
17509
|
+
resolve36(completions);
|
|
17510
17510
|
});
|
|
17511
17511
|
});
|
|
17512
17512
|
} else {
|
|
@@ -19656,8 +19656,8 @@ function getErrorMap() {
|
|
|
19656
19656
|
|
|
19657
19657
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
19658
19658
|
var makeIssue = (params) => {
|
|
19659
|
-
const { data, path:
|
|
19660
|
-
const fullPath = [...
|
|
19659
|
+
const { data, path: path51, errorMaps, issueData } = params;
|
|
19660
|
+
const fullPath = [...path51, ...issueData.path || []];
|
|
19661
19661
|
const fullIssue = {
|
|
19662
19662
|
...issueData,
|
|
19663
19663
|
path: fullPath
|
|
@@ -19773,11 +19773,11 @@ var errorUtil;
|
|
|
19773
19773
|
|
|
19774
19774
|
// node_modules/zod/v3/types.js
|
|
19775
19775
|
var ParseInputLazyPath = class {
|
|
19776
|
-
constructor(parent, value,
|
|
19776
|
+
constructor(parent, value, path51, key) {
|
|
19777
19777
|
this._cachedPath = [];
|
|
19778
19778
|
this.parent = parent;
|
|
19779
19779
|
this.data = value;
|
|
19780
|
-
this._path =
|
|
19780
|
+
this._path = path51;
|
|
19781
19781
|
this._key = key;
|
|
19782
19782
|
}
|
|
19783
19783
|
get path() {
|
|
@@ -23477,7 +23477,26 @@ var RegisterRepoInputSchema = external_exports.object({
|
|
|
23477
23477
|
path: external_exports.string().min(1),
|
|
23478
23478
|
gitRemote: external_exports.string().optional(),
|
|
23479
23479
|
manifest: HarnessManifestSchema.optional(),
|
|
23480
|
-
stagesRegistry: HarnessStageRegistrySchema.optional()
|
|
23480
|
+
stagesRegistry: HarnessStageRegistrySchema.optional(),
|
|
23481
|
+
/** Clear a prior unregister blocklist entry and re-register. */
|
|
23482
|
+
force: external_exports.boolean().optional()
|
|
23483
|
+
});
|
|
23484
|
+
var UnregisterRepoInputSchema = external_exports.object({
|
|
23485
|
+
/** When true, attempt to remove session worktrees on disk (host paths). */
|
|
23486
|
+
deleteWorktrees: external_exports.boolean().optional().default(false)
|
|
23487
|
+
});
|
|
23488
|
+
var UnregisterWorktreeResultSchema = external_exports.object({
|
|
23489
|
+
path: external_exports.string(),
|
|
23490
|
+
agentId: external_exports.number().optional(),
|
|
23491
|
+
deleted: external_exports.boolean(),
|
|
23492
|
+
error: external_exports.string().optional()
|
|
23493
|
+
});
|
|
23494
|
+
var UnregisterRepoResultSchema = external_exports.object({
|
|
23495
|
+
ok: external_exports.literal(true),
|
|
23496
|
+
id: external_exports.string(),
|
|
23497
|
+
path: external_exports.string(),
|
|
23498
|
+
deleteWorktrees: external_exports.boolean(),
|
|
23499
|
+
worktrees: external_exports.array(UnregisterWorktreeResultSchema)
|
|
23481
23500
|
});
|
|
23482
23501
|
var SyncRunsInputSchema = external_exports.object({
|
|
23483
23502
|
runs: external_exports.array(RunRecordSchema)
|
|
@@ -23816,16 +23835,16 @@ async function handleAgentSkills(options) {
|
|
|
23816
23835
|
}
|
|
23817
23836
|
async function askYesNo(question) {
|
|
23818
23837
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
23819
|
-
return new Promise((
|
|
23838
|
+
return new Promise((resolve36) => {
|
|
23820
23839
|
process.stderr.write(`${question} `);
|
|
23821
23840
|
rl.once("line", (answer) => {
|
|
23822
23841
|
rl.close();
|
|
23823
23842
|
const trimmed = answer.trim();
|
|
23824
23843
|
if (trimmed === "") {
|
|
23825
|
-
|
|
23844
|
+
resolve36(true);
|
|
23826
23845
|
return;
|
|
23827
23846
|
}
|
|
23828
|
-
|
|
23847
|
+
resolve36(/^y(es)?$/i.test(trimmed));
|
|
23829
23848
|
});
|
|
23830
23849
|
});
|
|
23831
23850
|
}
|
|
@@ -23904,7 +23923,7 @@ function handleRemove(argv) {
|
|
|
23904
23923
|
}
|
|
23905
23924
|
|
|
23906
23925
|
// src/cli/commands/env.ts
|
|
23907
|
-
var
|
|
23926
|
+
var path43 = __toESM(require("path"));
|
|
23908
23927
|
|
|
23909
23928
|
// src/core/harness.ts
|
|
23910
23929
|
var fs20 = __toESM(require("fs"));
|
|
@@ -24683,13 +24702,13 @@ var MultipartBody = class {
|
|
|
24683
24702
|
// node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
|
|
24684
24703
|
var import_web = require("node:stream/web");
|
|
24685
24704
|
var fileFromPathWarned = false;
|
|
24686
|
-
async function fileFromPath3(
|
|
24705
|
+
async function fileFromPath3(path51, ...args) {
|
|
24687
24706
|
const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
|
|
24688
24707
|
if (!fileFromPathWarned) {
|
|
24689
|
-
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(
|
|
24708
|
+
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path51)}) instead`);
|
|
24690
24709
|
fileFromPathWarned = true;
|
|
24691
24710
|
}
|
|
24692
|
-
return await _fileFromPath(
|
|
24711
|
+
return await _fileFromPath(path51, ...args);
|
|
24693
24712
|
}
|
|
24694
24713
|
var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
24695
24714
|
var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
@@ -25185,8 +25204,8 @@ async function defaultParseResponse(props) {
|
|
|
25185
25204
|
}
|
|
25186
25205
|
var APIPromise = class _APIPromise extends Promise {
|
|
25187
25206
|
constructor(responsePromise, parseResponse = defaultParseResponse) {
|
|
25188
|
-
super((
|
|
25189
|
-
|
|
25207
|
+
super((resolve36) => {
|
|
25208
|
+
resolve36(null);
|
|
25190
25209
|
});
|
|
25191
25210
|
this.responsePromise = responsePromise;
|
|
25192
25211
|
this.parseResponse = parseResponse;
|
|
@@ -25286,29 +25305,29 @@ var APIClient = class {
|
|
|
25286
25305
|
defaultIdempotencyKey() {
|
|
25287
25306
|
return `stainless-node-retry-${uuid4()}`;
|
|
25288
25307
|
}
|
|
25289
|
-
get(
|
|
25290
|
-
return this.methodRequest("get",
|
|
25308
|
+
get(path51, opts) {
|
|
25309
|
+
return this.methodRequest("get", path51, opts);
|
|
25291
25310
|
}
|
|
25292
|
-
post(
|
|
25293
|
-
return this.methodRequest("post",
|
|
25311
|
+
post(path51, opts) {
|
|
25312
|
+
return this.methodRequest("post", path51, opts);
|
|
25294
25313
|
}
|
|
25295
|
-
patch(
|
|
25296
|
-
return this.methodRequest("patch",
|
|
25314
|
+
patch(path51, opts) {
|
|
25315
|
+
return this.methodRequest("patch", path51, opts);
|
|
25297
25316
|
}
|
|
25298
|
-
put(
|
|
25299
|
-
return this.methodRequest("put",
|
|
25317
|
+
put(path51, opts) {
|
|
25318
|
+
return this.methodRequest("put", path51, opts);
|
|
25300
25319
|
}
|
|
25301
|
-
delete(
|
|
25302
|
-
return this.methodRequest("delete",
|
|
25320
|
+
delete(path51, opts) {
|
|
25321
|
+
return this.methodRequest("delete", path51, opts);
|
|
25303
25322
|
}
|
|
25304
|
-
methodRequest(method,
|
|
25323
|
+
methodRequest(method, path51, opts) {
|
|
25305
25324
|
return this.request(Promise.resolve(opts).then(async (opts2) => {
|
|
25306
25325
|
const body = opts2 && isBlobLike(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
|
|
25307
|
-
return { method, path:
|
|
25326
|
+
return { method, path: path51, ...opts2, body };
|
|
25308
25327
|
}));
|
|
25309
25328
|
}
|
|
25310
|
-
getAPIList(
|
|
25311
|
-
return this.requestAPIList(Page, { method: "get", path:
|
|
25329
|
+
getAPIList(path51, Page, opts) {
|
|
25330
|
+
return this.requestAPIList(Page, { method: "get", path: path51, ...opts });
|
|
25312
25331
|
}
|
|
25313
25332
|
calculateContentLength(body) {
|
|
25314
25333
|
if (typeof body === "string") {
|
|
@@ -25326,10 +25345,10 @@ var APIClient = class {
|
|
|
25326
25345
|
return null;
|
|
25327
25346
|
}
|
|
25328
25347
|
buildRequest(options) {
|
|
25329
|
-
const { method, path:
|
|
25348
|
+
const { method, path: path51, query, headers = {} } = options;
|
|
25330
25349
|
const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
|
|
25331
25350
|
const contentLength = this.calculateContentLength(body);
|
|
25332
|
-
const url = this.buildURL(
|
|
25351
|
+
const url = this.buildURL(path51, query);
|
|
25333
25352
|
if ("timeout" in options)
|
|
25334
25353
|
validatePositiveInteger("timeout", options.timeout);
|
|
25335
25354
|
const timeout = options.timeout ?? this.timeout;
|
|
@@ -25438,8 +25457,8 @@ var APIClient = class {
|
|
|
25438
25457
|
const request = this.makeRequest(options, null);
|
|
25439
25458
|
return new PagePromise(this, request, Page);
|
|
25440
25459
|
}
|
|
25441
|
-
buildURL(
|
|
25442
|
-
const url = isAbsoluteURL(
|
|
25460
|
+
buildURL(path51, query) {
|
|
25461
|
+
const url = isAbsoluteURL(path51) ? new URL(path51) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path51.startsWith("/") ? path51.slice(1) : path51));
|
|
25443
25462
|
const defaultQuery = this.defaultQuery();
|
|
25444
25463
|
if (!isEmptyObj(defaultQuery)) {
|
|
25445
25464
|
query = { ...defaultQuery, ...query };
|
|
@@ -25724,7 +25743,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
|
|
|
25724
25743
|
var isAbsoluteURL = (url) => {
|
|
25725
25744
|
return startsWithSchemeRegexp.test(url);
|
|
25726
25745
|
};
|
|
25727
|
-
var sleep = (ms) => new Promise((
|
|
25746
|
+
var sleep = (ms) => new Promise((resolve36) => setTimeout(resolve36, ms));
|
|
25728
25747
|
var validatePositiveInteger = (name, n2) => {
|
|
25729
25748
|
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
25730
25749
|
throw new AnthropicError(`${name} must be an integer`);
|
|
@@ -26215,12 +26234,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26215
26234
|
}
|
|
26216
26235
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
26217
26236
|
});
|
|
26218
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((
|
|
26219
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise,
|
|
26237
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve36, reject) => {
|
|
26238
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve36, "f");
|
|
26220
26239
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
|
|
26221
26240
|
}), "f");
|
|
26222
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((
|
|
26223
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise,
|
|
26241
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve36, reject) => {
|
|
26242
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve36, "f");
|
|
26224
26243
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
|
|
26225
26244
|
}), "f");
|
|
26226
26245
|
__classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -26349,11 +26368,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26349
26368
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
26350
26369
|
*/
|
|
26351
26370
|
emitted(event) {
|
|
26352
|
-
return new Promise((
|
|
26371
|
+
return new Promise((resolve36, reject) => {
|
|
26353
26372
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
|
|
26354
26373
|
if (event !== "error")
|
|
26355
26374
|
this.once("error", reject);
|
|
26356
|
-
this.once(event,
|
|
26375
|
+
this.once(event, resolve36);
|
|
26357
26376
|
});
|
|
26358
26377
|
}
|
|
26359
26378
|
async done() {
|
|
@@ -26580,7 +26599,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26580
26599
|
if (done) {
|
|
26581
26600
|
return { value: void 0, done: true };
|
|
26582
26601
|
}
|
|
26583
|
-
return new Promise((
|
|
26602
|
+
return new Promise((resolve36, reject) => readQueue.push({ resolve: resolve36, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
26584
26603
|
}
|
|
26585
26604
|
const chunk = pushQueue.shift();
|
|
26586
26605
|
return { value: chunk, done: false };
|
|
@@ -26728,12 +26747,12 @@ var MessageStream = class _MessageStream {
|
|
|
26728
26747
|
}
|
|
26729
26748
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
26730
26749
|
});
|
|
26731
|
-
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((
|
|
26732
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise,
|
|
26750
|
+
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve36, reject) => {
|
|
26751
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve36, "f");
|
|
26733
26752
|
__classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
|
|
26734
26753
|
}), "f");
|
|
26735
|
-
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((
|
|
26736
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise,
|
|
26754
|
+
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve36, reject) => {
|
|
26755
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve36, "f");
|
|
26737
26756
|
__classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
|
|
26738
26757
|
}), "f");
|
|
26739
26758
|
__classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -26862,11 +26881,11 @@ var MessageStream = class _MessageStream {
|
|
|
26862
26881
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
26863
26882
|
*/
|
|
26864
26883
|
emitted(event) {
|
|
26865
|
-
return new Promise((
|
|
26884
|
+
return new Promise((resolve36, reject) => {
|
|
26866
26885
|
__classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
|
|
26867
26886
|
if (event !== "error")
|
|
26868
26887
|
this.once("error", reject);
|
|
26869
|
-
this.once(event,
|
|
26888
|
+
this.once(event, resolve36);
|
|
26870
26889
|
});
|
|
26871
26890
|
}
|
|
26872
26891
|
async done() {
|
|
@@ -27093,7 +27112,7 @@ var MessageStream = class _MessageStream {
|
|
|
27093
27112
|
if (done) {
|
|
27094
27113
|
return { value: void 0, done: true };
|
|
27095
27114
|
}
|
|
27096
|
-
return new Promise((
|
|
27115
|
+
return new Promise((resolve36, reject) => readQueue.push({ resolve: resolve36, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
27097
27116
|
}
|
|
27098
27117
|
const chunk = pushQueue.shift();
|
|
27099
27118
|
return { value: chunk, done: false };
|
|
@@ -27353,11 +27372,11 @@ async function promptApplyAgentMdProposal(repoPath) {
|
|
|
27353
27372
|
}
|
|
27354
27373
|
async function askYesNo2(question) {
|
|
27355
27374
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
27356
|
-
return new Promise((
|
|
27375
|
+
return new Promise((resolve36) => {
|
|
27357
27376
|
process.stderr.write(`${question} `);
|
|
27358
27377
|
rl.once("line", (answer) => {
|
|
27359
27378
|
rl.close();
|
|
27360
|
-
|
|
27379
|
+
resolve36(/^y(es)?$/i.test(answer.trim()));
|
|
27361
27380
|
});
|
|
27362
27381
|
});
|
|
27363
27382
|
}
|
|
@@ -27587,7 +27606,7 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
|
|
|
27587
27606
|
}
|
|
27588
27607
|
async function askUser(question, options) {
|
|
27589
27608
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
27590
|
-
return new Promise((
|
|
27609
|
+
return new Promise((resolve36) => {
|
|
27591
27610
|
process.stderr.write("\n");
|
|
27592
27611
|
process.stderr.write(`\u2753 ${question}
|
|
27593
27612
|
`);
|
|
@@ -27598,7 +27617,7 @@ async function askUser(question, options) {
|
|
|
27598
27617
|
process.stderr.write("> ");
|
|
27599
27618
|
rl.once("line", (answer) => {
|
|
27600
27619
|
rl.close();
|
|
27601
|
-
|
|
27620
|
+
resolve36(answer.trim());
|
|
27602
27621
|
});
|
|
27603
27622
|
});
|
|
27604
27623
|
}
|
|
@@ -27742,7 +27761,7 @@ function runScript(scriptPath, args = [], options = {}) {
|
|
|
27742
27761
|
function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
27743
27762
|
const stream = options.stream ?? false;
|
|
27744
27763
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27745
|
-
return new Promise((
|
|
27764
|
+
return new Promise((resolve36) => {
|
|
27746
27765
|
const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
|
|
27747
27766
|
...spawnOptions,
|
|
27748
27767
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27757,13 +27776,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
|
27757
27776
|
stderr += d2;
|
|
27758
27777
|
if (stream) process.stderr.write(d2);
|
|
27759
27778
|
});
|
|
27760
|
-
proc.on("close", (code) =>
|
|
27779
|
+
proc.on("close", (code) => resolve36({ stdout, stderr, code: code ?? 0 }));
|
|
27761
27780
|
});
|
|
27762
27781
|
}
|
|
27763
27782
|
function runShellCommand(command2, options = {}) {
|
|
27764
27783
|
const stream = options.stream ?? false;
|
|
27765
27784
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27766
|
-
return new Promise((
|
|
27785
|
+
return new Promise((resolve36) => {
|
|
27767
27786
|
const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
|
|
27768
27787
|
...spawnOptions,
|
|
27769
27788
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27778,7 +27797,7 @@ function runShellCommand(command2, options = {}) {
|
|
|
27778
27797
|
stderr += d2;
|
|
27779
27798
|
if (stream) process.stderr.write(d2);
|
|
27780
27799
|
});
|
|
27781
|
-
proc.on("close", (code) =>
|
|
27800
|
+
proc.on("close", (code) => resolve36({ stdout, stderr, code: code ?? 0 }));
|
|
27782
27801
|
});
|
|
27783
27802
|
}
|
|
27784
27803
|
|
|
@@ -28942,16 +28961,16 @@ async function promptScaffoldCursorRule(exists) {
|
|
|
28942
28961
|
}
|
|
28943
28962
|
async function askYesNo3(question) {
|
|
28944
28963
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
|
|
28945
|
-
return new Promise((
|
|
28964
|
+
return new Promise((resolve36) => {
|
|
28946
28965
|
process.stderr.write(`${question} `);
|
|
28947
28966
|
rl.once("line", (answer) => {
|
|
28948
28967
|
rl.close();
|
|
28949
28968
|
const trimmed = answer.trim();
|
|
28950
28969
|
if (trimmed === "") {
|
|
28951
|
-
|
|
28970
|
+
resolve36(true);
|
|
28952
28971
|
return;
|
|
28953
28972
|
}
|
|
28954
|
-
|
|
28973
|
+
resolve36(/^y(es)?$/i.test(trimmed));
|
|
28955
28974
|
});
|
|
28956
28975
|
});
|
|
28957
28976
|
}
|
|
@@ -29056,6 +29075,18 @@ function readSlotRegistry(repoPath, agentId) {
|
|
|
29056
29075
|
return void 0;
|
|
29057
29076
|
}
|
|
29058
29077
|
}
|
|
29078
|
+
function listSlotRegistryEntries(repoPath) {
|
|
29079
|
+
const dir = getSlotRegistryDir(repoPath);
|
|
29080
|
+
if (!fs24.existsSync(dir)) return [];
|
|
29081
|
+
const entries = [];
|
|
29082
|
+
for (const name of fs24.readdirSync(dir)) {
|
|
29083
|
+
const match = name.match(/^agent-(\d+)\.json$/);
|
|
29084
|
+
if (!match) continue;
|
|
29085
|
+
const entry = readSlotRegistry(repoPath, Number(match[1]));
|
|
29086
|
+
if (entry) entries.push(entry);
|
|
29087
|
+
}
|
|
29088
|
+
return entries;
|
|
29089
|
+
}
|
|
29059
29090
|
|
|
29060
29091
|
// src/core/local-executor.ts
|
|
29061
29092
|
function quoteShellArg(arg) {
|
|
@@ -29222,16 +29253,16 @@ var LocalScriptExecutor = class {
|
|
|
29222
29253
|
const walk = (dir, prefix) => {
|
|
29223
29254
|
for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
|
|
29224
29255
|
const full = path24.join(dir, entry.name);
|
|
29225
|
-
const
|
|
29256
|
+
const relative3 = path24.join(prefix, entry.name);
|
|
29226
29257
|
if (entry.isDirectory()) {
|
|
29227
|
-
walk(full,
|
|
29258
|
+
walk(full, relative3);
|
|
29228
29259
|
continue;
|
|
29229
29260
|
}
|
|
29230
|
-
if (filter?.stageId && !
|
|
29261
|
+
if (filter?.stageId && !relative3.includes(filter.stageId)) continue;
|
|
29231
29262
|
const stat = fs25.statSync(full);
|
|
29232
29263
|
entries.push({
|
|
29233
29264
|
path: full,
|
|
29234
|
-
relativePath:
|
|
29265
|
+
relativePath: relative3,
|
|
29235
29266
|
sizeBytes: stat.size,
|
|
29236
29267
|
modifiedAt: stat.mtime.toISOString()
|
|
29237
29268
|
});
|
|
@@ -29244,63 +29275,136 @@ var LocalScriptExecutor = class {
|
|
|
29244
29275
|
var localScriptExecutor = new LocalScriptExecutor();
|
|
29245
29276
|
|
|
29246
29277
|
// src/core/control-sync.ts
|
|
29247
|
-
var
|
|
29278
|
+
var path39 = __toESM(require("path"));
|
|
29248
29279
|
init_control_config();
|
|
29249
29280
|
|
|
29250
29281
|
// src/core/control-registry.ts
|
|
29251
|
-
var
|
|
29282
|
+
var fs27 = __toESM(require("fs"));
|
|
29252
29283
|
var os4 = __toESM(require("os"));
|
|
29284
|
+
var path26 = __toESM(require("path"));
|
|
29285
|
+
|
|
29286
|
+
// src/core/control-repo-path.ts
|
|
29287
|
+
var fs26 = __toESM(require("fs"));
|
|
29253
29288
|
var path25 = __toESM(require("path"));
|
|
29289
|
+
function tryGit(cwd, args) {
|
|
29290
|
+
if (!fs26.existsSync(cwd)) return void 0;
|
|
29291
|
+
const result = run(`git ${args}`, { cwd });
|
|
29292
|
+
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
29293
|
+
}
|
|
29294
|
+
function resolveMainWorkingTree(cwd) {
|
|
29295
|
+
const toplevel = tryGit(cwd, "rev-parse --show-toplevel");
|
|
29296
|
+
if (!toplevel) return void 0;
|
|
29297
|
+
const commonDirRaw = tryGit(cwd, "rev-parse --git-common-dir");
|
|
29298
|
+
const commonDir = commonDirRaw ? path25.resolve(cwd, commonDirRaw) : void 0;
|
|
29299
|
+
if (!commonDir) return path25.resolve(toplevel);
|
|
29300
|
+
if (path25.basename(commonDir) === ".git") {
|
|
29301
|
+
return path25.dirname(commonDir);
|
|
29302
|
+
}
|
|
29303
|
+
const porcelain = tryGit(cwd, "worktree list --porcelain");
|
|
29304
|
+
if (porcelain) {
|
|
29305
|
+
for (const line of porcelain.split("\n")) {
|
|
29306
|
+
if (line.startsWith("worktree ")) {
|
|
29307
|
+
return path25.resolve(line.slice("worktree ".length));
|
|
29308
|
+
}
|
|
29309
|
+
}
|
|
29310
|
+
}
|
|
29311
|
+
return path25.resolve(toplevel);
|
|
29312
|
+
}
|
|
29313
|
+
function canonicalizeControlRepoPath(repoPath) {
|
|
29314
|
+
const resolved = path25.resolve(repoPath);
|
|
29315
|
+
const toplevel = tryGit(resolved, "rev-parse --show-toplevel");
|
|
29316
|
+
if (!toplevel) return resolved;
|
|
29317
|
+
const mainRoot = resolveMainWorkingTree(resolved);
|
|
29318
|
+
if (!mainRoot) return resolved;
|
|
29319
|
+
const top2 = path25.resolve(toplevel);
|
|
29320
|
+
const main = path25.resolve(mainRoot);
|
|
29321
|
+
if (top2 === main) return resolved;
|
|
29322
|
+
const rel = path25.relative(top2, resolved);
|
|
29323
|
+
if (!rel || rel === ".") return main;
|
|
29324
|
+
if (rel.startsWith(`..${path25.sep}`) || rel === ".." || path25.isAbsolute(rel)) {
|
|
29325
|
+
return resolved;
|
|
29326
|
+
}
|
|
29327
|
+
return path25.resolve(main, rel);
|
|
29328
|
+
}
|
|
29329
|
+
|
|
29330
|
+
// src/core/control-registry.ts
|
|
29254
29331
|
function getRegistryPath() {
|
|
29255
29332
|
if (process.env.HAR_CONTROL_REGISTRY_PATH) {
|
|
29256
|
-
return
|
|
29333
|
+
return path26.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
|
|
29257
29334
|
}
|
|
29258
|
-
return
|
|
29335
|
+
return path26.join(os4.homedir(), ".har", "repos.json");
|
|
29259
29336
|
}
|
|
29260
29337
|
function readRegistry() {
|
|
29261
29338
|
const registryPath = getRegistryPath();
|
|
29262
29339
|
try {
|
|
29263
|
-
if (!
|
|
29264
|
-
return JSON.parse(
|
|
29340
|
+
if (!fs27.existsSync(registryPath)) return { repos: [] };
|
|
29341
|
+
return JSON.parse(fs27.readFileSync(registryPath, "utf8"));
|
|
29265
29342
|
} catch {
|
|
29266
29343
|
return { repos: [] };
|
|
29267
29344
|
}
|
|
29268
29345
|
}
|
|
29269
29346
|
function writeRegistry(registry2) {
|
|
29270
29347
|
const registryPath = getRegistryPath();
|
|
29271
|
-
|
|
29272
|
-
|
|
29348
|
+
fs27.mkdirSync(path26.dirname(registryPath), { recursive: true });
|
|
29349
|
+
fs27.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
|
|
29350
|
+
}
|
|
29351
|
+
function registryChanged(before, after) {
|
|
29352
|
+
if (before.length !== after.length) return true;
|
|
29353
|
+
return before.some((repoPath, index) => repoPath !== after[index]);
|
|
29273
29354
|
}
|
|
29274
29355
|
function recordRepoForControlSync(repoPath) {
|
|
29275
29356
|
if (process.env.HAR_CONTROL_DISABLED === "true") return;
|
|
29276
|
-
const resolved =
|
|
29357
|
+
const resolved = canonicalizeControlRepoPath(repoPath);
|
|
29277
29358
|
if (!readManifest(resolved)) return;
|
|
29278
29359
|
const registry2 = readRegistry();
|
|
29279
29360
|
if (registry2.repos.includes(resolved)) return;
|
|
29280
29361
|
registry2.repos.push(resolved);
|
|
29281
29362
|
writeRegistry(registry2);
|
|
29282
29363
|
}
|
|
29364
|
+
function removeRegisteredRepo(repoPath) {
|
|
29365
|
+
const resolved = canonicalizeControlRepoPath(repoPath);
|
|
29366
|
+
const registry2 = readRegistry();
|
|
29367
|
+
const next = registry2.repos.filter((entry) => {
|
|
29368
|
+
try {
|
|
29369
|
+
return canonicalizeControlRepoPath(entry) !== resolved;
|
|
29370
|
+
} catch {
|
|
29371
|
+
return path26.resolve(entry) !== resolved;
|
|
29372
|
+
}
|
|
29373
|
+
});
|
|
29374
|
+
if (next.length === registry2.repos.length) return false;
|
|
29375
|
+
writeRegistry({ repos: next });
|
|
29376
|
+
return true;
|
|
29377
|
+
}
|
|
29283
29378
|
function listRegisteredRepos() {
|
|
29284
29379
|
const registry2 = readRegistry();
|
|
29285
|
-
const kept =
|
|
29286
|
-
|
|
29380
|
+
const kept = [];
|
|
29381
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29382
|
+
for (const repoPath of registry2.repos) {
|
|
29383
|
+
if (!fs27.existsSync(repoPath)) continue;
|
|
29384
|
+
const canonical = canonicalizeControlRepoPath(repoPath);
|
|
29385
|
+
if (!fs27.existsSync(canonical) || !readManifest(canonical)) continue;
|
|
29386
|
+
if (seen.has(canonical)) continue;
|
|
29387
|
+
seen.add(canonical);
|
|
29388
|
+
kept.push(canonical);
|
|
29389
|
+
}
|
|
29390
|
+
if (registryChanged(registry2.repos, kept)) {
|
|
29287
29391
|
writeRegistry({ repos: kept });
|
|
29288
29392
|
}
|
|
29289
29393
|
return kept;
|
|
29290
29394
|
}
|
|
29291
29395
|
|
|
29292
29396
|
// src/core/slot-status.ts
|
|
29293
|
-
var
|
|
29397
|
+
var fs32 = __toESM(require("fs"));
|
|
29294
29398
|
var os7 = __toESM(require("os"));
|
|
29295
|
-
var
|
|
29399
|
+
var path32 = __toESM(require("path"));
|
|
29296
29400
|
var import_child_process7 = require("child_process");
|
|
29297
29401
|
|
|
29298
29402
|
// src/core/runs.ts
|
|
29299
29403
|
var crypto2 = __toESM(require("crypto"));
|
|
29300
29404
|
var import_child_process2 = require("child_process");
|
|
29301
|
-
var
|
|
29405
|
+
var fs28 = __toESM(require("fs"));
|
|
29302
29406
|
var os5 = __toESM(require("os"));
|
|
29303
|
-
var
|
|
29407
|
+
var path27 = __toESM(require("path"));
|
|
29304
29408
|
var RUNS_DIR = "runs";
|
|
29305
29409
|
function gitCommonDir(cwd) {
|
|
29306
29410
|
try {
|
|
@@ -29309,7 +29413,7 @@ function gitCommonDir(cwd) {
|
|
|
29309
29413
|
encoding: "utf8",
|
|
29310
29414
|
stdio: ["pipe", "pipe", "ignore"]
|
|
29311
29415
|
}).trim();
|
|
29312
|
-
return out ?
|
|
29416
|
+
return out ? path27.resolve(cwd, out) : void 0;
|
|
29313
29417
|
} catch {
|
|
29314
29418
|
return void 0;
|
|
29315
29419
|
}
|
|
@@ -29331,7 +29435,7 @@ function gitPrefix(cwd) {
|
|
|
29331
29435
|
}
|
|
29332
29436
|
}
|
|
29333
29437
|
function getRunsDir(harnessRoot) {
|
|
29334
|
-
return
|
|
29438
|
+
return path27.join(getHarnessDir(harnessRoot), RUNS_DIR);
|
|
29335
29439
|
}
|
|
29336
29440
|
function formatLocalDate(d2) {
|
|
29337
29441
|
const y2 = d2.getFullYear();
|
|
@@ -29351,24 +29455,24 @@ function buildRunRelativePath(stageId, agentId, startedAt, runId, runsDir) {
|
|
|
29351
29455
|
const timePart = formatLocalTime(started);
|
|
29352
29456
|
const agentPart = agentId !== void 0 ? `_agent-${agentId}` : "";
|
|
29353
29457
|
let filename = `${timePart}_${stageId}${agentPart}.json`;
|
|
29354
|
-
const fullPath =
|
|
29355
|
-
if (
|
|
29458
|
+
const fullPath = path27.join(runsDir, dateFolder, filename);
|
|
29459
|
+
if (fs28.existsSync(fullPath)) {
|
|
29356
29460
|
filename = `${timePart}_${stageId}${agentPart}-${runId.slice(0, 8)}.json`;
|
|
29357
29461
|
}
|
|
29358
|
-
return
|
|
29462
|
+
return path27.join(dateFolder, filename);
|
|
29359
29463
|
}
|
|
29360
29464
|
function resolveRunFilePath(harnessRoot, run2) {
|
|
29361
29465
|
if (run2.relativePath) {
|
|
29362
|
-
return
|
|
29466
|
+
return path27.join(getRunsDir(harnessRoot), run2.relativePath);
|
|
29363
29467
|
}
|
|
29364
|
-
return
|
|
29468
|
+
return path27.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
|
|
29365
29469
|
}
|
|
29366
29470
|
function collectRunFiles(runsDir) {
|
|
29367
|
-
if (!
|
|
29471
|
+
if (!fs28.existsSync(runsDir)) return [];
|
|
29368
29472
|
const files = [];
|
|
29369
29473
|
const walk = (dir) => {
|
|
29370
|
-
for (const entry of
|
|
29371
|
-
const full =
|
|
29474
|
+
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
29475
|
+
const full = path27.join(dir, entry.name);
|
|
29372
29476
|
if (entry.isDirectory()) {
|
|
29373
29477
|
walk(full);
|
|
29374
29478
|
} else if (entry.name.endsWith(".json")) {
|
|
@@ -29382,30 +29486,30 @@ function collectRunFiles(runsDir) {
|
|
|
29382
29486
|
function resolveAgentWorkDir(harnessRoot, agentId) {
|
|
29383
29487
|
if (agentId === void 0) return void 0;
|
|
29384
29488
|
const entry = readSlotRegistry(harnessRoot, agentId);
|
|
29385
|
-
if (entry?.workDir &&
|
|
29489
|
+
if (entry?.workDir && fs28.existsSync(entry.workDir)) return entry.workDir;
|
|
29386
29490
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29387
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
29388
|
-
const worktreeDir =
|
|
29491
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path27.basename(harnessRoot);
|
|
29492
|
+
const worktreeDir = path27.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
|
|
29389
29493
|
const relPrefix = gitPrefix(harnessRoot);
|
|
29390
29494
|
const sessionEnvFiles = [];
|
|
29391
|
-
const worktreesRoot =
|
|
29392
|
-
if (
|
|
29495
|
+
const worktreesRoot = path27.join(os5.homedir(), "worktrees");
|
|
29496
|
+
if (fs28.existsSync(worktreesRoot)) {
|
|
29393
29497
|
const suffix = `-har-agent-${agentId}-`;
|
|
29394
|
-
for (const entry2 of
|
|
29498
|
+
for (const entry2 of fs28.readdirSync(worktreesRoot, { withFileTypes: true })) {
|
|
29395
29499
|
if (!entry2.isDirectory() || !entry2.name.includes(suffix)) continue;
|
|
29396
|
-
const sessionDir =
|
|
29500
|
+
const sessionDir = path27.join(worktreesRoot, entry2.name);
|
|
29397
29501
|
if (!sameGitCheckout(harnessRoot, sessionDir)) continue;
|
|
29398
|
-
sessionEnvFiles.push(
|
|
29502
|
+
sessionEnvFiles.push(path27.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
|
|
29399
29503
|
}
|
|
29400
29504
|
}
|
|
29401
29505
|
const candidates = [
|
|
29402
|
-
|
|
29403
|
-
|
|
29506
|
+
path27.join(worktreeDir, `.env.agent.${agentId}`),
|
|
29507
|
+
path27.join(harnessRoot, `.env.agent.${agentId}`),
|
|
29404
29508
|
...sessionEnvFiles.sort()
|
|
29405
29509
|
];
|
|
29406
29510
|
for (const envFile of candidates) {
|
|
29407
|
-
if (!
|
|
29408
|
-
const content =
|
|
29511
|
+
if (!fs28.existsSync(envFile)) continue;
|
|
29512
|
+
const content = fs28.readFileSync(envFile, "utf8");
|
|
29409
29513
|
const match = content.match(/^REPO_ROOT=(.+)$/m);
|
|
29410
29514
|
if (match) return match[1].trim();
|
|
29411
29515
|
}
|
|
@@ -29417,11 +29521,11 @@ function createRun(ctx, meta) {
|
|
|
29417
29521
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29418
29522
|
const runId = crypto2.randomUUID();
|
|
29419
29523
|
const relativePath = buildRunRelativePath(meta.stageId, meta.agentId, startedAt, runId, runsDir);
|
|
29420
|
-
const runFilePath =
|
|
29421
|
-
|
|
29524
|
+
const runFilePath = path27.join(runsDir, relativePath);
|
|
29525
|
+
fs28.mkdirSync(path27.dirname(runFilePath), { recursive: true });
|
|
29422
29526
|
const run2 = RunRecordSchema.parse({
|
|
29423
29527
|
runId,
|
|
29424
|
-
repoPath:
|
|
29528
|
+
repoPath: path27.resolve(ctx.repoPath),
|
|
29425
29529
|
harnessRoot,
|
|
29426
29530
|
stageId: meta.stageId,
|
|
29427
29531
|
kind: meta.kind,
|
|
@@ -29432,7 +29536,7 @@ function createRun(ctx, meta) {
|
|
|
29432
29536
|
relativePath,
|
|
29433
29537
|
trigger: ctx.trigger ?? "cli"
|
|
29434
29538
|
});
|
|
29435
|
-
|
|
29539
|
+
fs28.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
|
|
29436
29540
|
return run2;
|
|
29437
29541
|
}
|
|
29438
29542
|
function finishRun(repoPath, runId, update) {
|
|
@@ -29451,13 +29555,13 @@ function finishRun(repoPath, runId, update) {
|
|
|
29451
29555
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
29452
29556
|
});
|
|
29453
29557
|
const runPath = resolveRunFilePath(harnessRoot, finished);
|
|
29454
|
-
|
|
29558
|
+
fs28.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
|
|
29455
29559
|
return finished;
|
|
29456
29560
|
}
|
|
29457
29561
|
function findRunRecord(harnessRoot, runId) {
|
|
29458
29562
|
const runsDir = getRunsDir(harnessRoot);
|
|
29459
29563
|
for (const filePath of collectRunFiles(runsDir)) {
|
|
29460
|
-
const parsed = RunRecordSchema.safeParse(JSON.parse(
|
|
29564
|
+
const parsed = RunRecordSchema.safeParse(JSON.parse(fs28.readFileSync(filePath, "utf8")));
|
|
29461
29565
|
if (parsed.success && parsed.data.runId === runId) {
|
|
29462
29566
|
return parsed.data;
|
|
29463
29567
|
}
|
|
@@ -29469,10 +29573,10 @@ function getRun(repoPath, runId) {
|
|
|
29469
29573
|
}
|
|
29470
29574
|
function listRuns(repoPath, filter = {}) {
|
|
29471
29575
|
const runsDir = getRunsDir(resolveHarnessRoot(repoPath));
|
|
29472
|
-
if (!
|
|
29576
|
+
if (!fs28.existsSync(runsDir)) return [];
|
|
29473
29577
|
const runs = [];
|
|
29474
29578
|
for (const filePath of collectRunFiles(runsDir)) {
|
|
29475
|
-
const parsed = RunRecordSchema.safeParse(JSON.parse(
|
|
29579
|
+
const parsed = RunRecordSchema.safeParse(JSON.parse(fs28.readFileSync(filePath, "utf8")));
|
|
29476
29580
|
if (!parsed.success) continue;
|
|
29477
29581
|
if (filter.stageId && parsed.data.stageId !== filter.stageId) continue;
|
|
29478
29582
|
runs.push(parsed.data);
|
|
@@ -29485,18 +29589,18 @@ function listRuns(repoPath, filter = {}) {
|
|
|
29485
29589
|
}
|
|
29486
29590
|
|
|
29487
29591
|
// src/core/slot-preflight.ts
|
|
29488
|
-
var
|
|
29592
|
+
var path31 = __toESM(require("path"));
|
|
29489
29593
|
|
|
29490
29594
|
// src/core/control-port.ts
|
|
29491
29595
|
var import_child_process4 = require("child_process");
|
|
29492
|
-
var
|
|
29493
|
-
var
|
|
29596
|
+
var fs30 = __toESM(require("fs"));
|
|
29597
|
+
var path29 = __toESM(require("path"));
|
|
29494
29598
|
init_control_config();
|
|
29495
29599
|
|
|
29496
29600
|
// src/core/slot-ports.ts
|
|
29497
29601
|
var import_child_process3 = require("child_process");
|
|
29498
|
-
var
|
|
29499
|
-
var
|
|
29602
|
+
var fs29 = __toESM(require("fs"));
|
|
29603
|
+
var path28 = __toESM(require("path"));
|
|
29500
29604
|
function portStep(env3) {
|
|
29501
29605
|
return Number(env3.HARNESS_PORT_STEP ?? 10);
|
|
29502
29606
|
}
|
|
@@ -29552,7 +29656,7 @@ function allocateAppPorts(repoPath, agentId) {
|
|
|
29552
29656
|
}
|
|
29553
29657
|
function harnessUsesPm2(repoPath) {
|
|
29554
29658
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29555
|
-
return
|
|
29659
|
+
return fs29.existsSync(path28.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
|
|
29556
29660
|
}
|
|
29557
29661
|
|
|
29558
29662
|
// src/core/control-port.ts
|
|
@@ -29607,13 +29711,13 @@ function controlDefaultPortWarnings(containers, defaultFrontendPort, allocatedFr
|
|
|
29607
29711
|
return [formatControlDefaultPortWarning(control.name, defaultFrontendPort, allocatedFrontend)];
|
|
29608
29712
|
}
|
|
29609
29713
|
function inspectControlUpReadiness(repoPath) {
|
|
29610
|
-
const resolved =
|
|
29714
|
+
const resolved = path29.resolve(repoPath);
|
|
29611
29715
|
const port = parseControlHostPort();
|
|
29612
29716
|
const containers = listDockerContainers();
|
|
29613
29717
|
const control = controlContainerOnPort(containers, port);
|
|
29614
29718
|
const warnings = [];
|
|
29615
|
-
const controlHarnessDir =
|
|
29616
|
-
const harnessSlot1Active =
|
|
29719
|
+
const controlHarnessDir = path29.join(resolved, "control", ".har");
|
|
29720
|
+
const harnessSlot1Active = fs30.existsSync(controlHarnessDir) && readSlotRegistry(path29.join(resolved, "control"), 1)?.status === "active";
|
|
29617
29721
|
if (control) {
|
|
29618
29722
|
return {
|
|
29619
29723
|
warnings,
|
|
@@ -29644,9 +29748,9 @@ function inspectControlUpReadiness(repoPath) {
|
|
|
29644
29748
|
}
|
|
29645
29749
|
|
|
29646
29750
|
// src/core/slot-launch-guard-occupied.ts
|
|
29647
|
-
var
|
|
29751
|
+
var fs31 = __toESM(require("fs"));
|
|
29648
29752
|
var os6 = __toESM(require("os"));
|
|
29649
|
-
var
|
|
29753
|
+
var path30 = __toESM(require("path"));
|
|
29650
29754
|
var import_child_process5 = require("child_process");
|
|
29651
29755
|
function runGit(cwd, args) {
|
|
29652
29756
|
try {
|
|
@@ -29665,7 +29769,7 @@ function worktreeDirty(worktreePath) {
|
|
|
29665
29769
|
}
|
|
29666
29770
|
function gitCommonDir2(cwd) {
|
|
29667
29771
|
const out = runGit(cwd, "rev-parse --git-common-dir");
|
|
29668
|
-
return out ?
|
|
29772
|
+
return out ? path30.resolve(cwd, out) : void 0;
|
|
29669
29773
|
}
|
|
29670
29774
|
function sameGitCheckout2(a2, b2) {
|
|
29671
29775
|
const left2 = gitCommonDir2(a2);
|
|
@@ -29674,20 +29778,20 @@ function sameGitCheckout2(a2, b2) {
|
|
|
29674
29778
|
}
|
|
29675
29779
|
function discoverWorktree(harnessRoot, agentId, projectName) {
|
|
29676
29780
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
29677
|
-
if (session?.worktreePath &&
|
|
29781
|
+
if (session?.worktreePath && fs31.existsSync(session.worktreePath)) {
|
|
29678
29782
|
return session.worktreePath;
|
|
29679
29783
|
}
|
|
29680
|
-
const legacy =
|
|
29681
|
-
if (
|
|
29682
|
-
const worktreesRoot =
|
|
29683
|
-
if (!
|
|
29784
|
+
const legacy = path30.join(os6.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
|
|
29785
|
+
if (fs31.existsSync(legacy) && sameGitCheckout2(harnessRoot, legacy)) return legacy;
|
|
29786
|
+
const worktreesRoot = path30.join(os6.homedir(), "worktrees");
|
|
29787
|
+
if (!fs31.existsSync(worktreesRoot)) return void 0;
|
|
29684
29788
|
const suffix = `-har-agent-${agentId}-`;
|
|
29685
29789
|
const relPrefix = runGit(harnessRoot, "rev-parse --show-prefix") ?? "";
|
|
29686
|
-
for (const name of
|
|
29790
|
+
for (const name of fs31.readdirSync(worktreesRoot)) {
|
|
29687
29791
|
if (!name.includes(suffix)) continue;
|
|
29688
|
-
const candidate =
|
|
29792
|
+
const candidate = path30.join(worktreesRoot, name);
|
|
29689
29793
|
if (!sameGitCheckout2(harnessRoot, candidate)) continue;
|
|
29690
|
-
if (!
|
|
29794
|
+
if (!fs31.existsSync(path30.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
|
|
29691
29795
|
return candidate;
|
|
29692
29796
|
}
|
|
29693
29797
|
return void 0;
|
|
@@ -29695,13 +29799,13 @@ function discoverWorktree(harnessRoot, agentId, projectName) {
|
|
|
29695
29799
|
function collectOccupiedSlot(repoPath, agentId) {
|
|
29696
29800
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29697
29801
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29698
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
29802
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path30.basename(harnessRoot);
|
|
29699
29803
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
29700
29804
|
const worktreePath = discoverWorktree(harnessRoot, agentId, projectName);
|
|
29701
29805
|
const workDir = session?.workDir;
|
|
29702
|
-
const envInWorkDir = workDir ?
|
|
29703
|
-
const envInRoot =
|
|
29704
|
-
const envInWorktree = worktreePath ?
|
|
29806
|
+
const envInWorkDir = workDir ? fs31.existsSync(path30.join(workDir, `.env.agent.${agentId}`)) : false;
|
|
29807
|
+
const envInRoot = fs31.existsSync(path30.join(harnessRoot, `.env.agent.${agentId}`));
|
|
29808
|
+
const envInWorktree = worktreePath ? fs31.existsSync(path30.join(worktreePath, `.env.agent.${agentId}`)) : false;
|
|
29705
29809
|
const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
|
|
29706
29810
|
if (!active) return void 0;
|
|
29707
29811
|
const dirty = worktreePath ? worktreeDirty(worktreePath) : void 0;
|
|
@@ -29718,7 +29822,17 @@ function collectOccupiedSlot(repoPath, agentId) {
|
|
|
29718
29822
|
harnessUsage: "none"
|
|
29719
29823
|
};
|
|
29720
29824
|
}
|
|
29721
|
-
function
|
|
29825
|
+
function describeLaunchBase(repoPath) {
|
|
29826
|
+
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29827
|
+
const branch = runGit(harnessRoot, "rev-parse --abbrev-ref HEAD") ?? "detached";
|
|
29828
|
+
const sha = runGit(harnessRoot, "rev-parse --short HEAD");
|
|
29829
|
+
return [
|
|
29830
|
+
`New session will be based on: ${branch}${sha ? ` @ ${sha}` : ""}`,
|
|
29831
|
+
` (HEAD of ${harnessRoot})`,
|
|
29832
|
+
" --replace does not choose this base \u2014 switch the main checkout first for a new unrelated task."
|
|
29833
|
+
];
|
|
29834
|
+
}
|
|
29835
|
+
function formatOccupiedSlot(slot, repoPath, resume) {
|
|
29722
29836
|
const lines = [
|
|
29723
29837
|
`Slot ${slot.agentId} is already in use.`,
|
|
29724
29838
|
slot.worktreePath ? ` Worktree: ${slot.worktreePath}` : void 0,
|
|
@@ -29728,6 +29842,8 @@ function formatOccupiedSlot(slot, resume) {
|
|
|
29728
29842
|
slot.lastError ? ` Error: ${slot.lastError}` : void 0,
|
|
29729
29843
|
slot.sessionCreatedAt ? ` Since: ${slot.sessionCreatedAt}` : void 0,
|
|
29730
29844
|
slot.dirty ? " Git: dirty (uncommitted changes \u2014 commit or use force to discard)" : " Git: clean",
|
|
29845
|
+
"",
|
|
29846
|
+
...describeLaunchBase(repoPath),
|
|
29731
29847
|
""
|
|
29732
29848
|
];
|
|
29733
29849
|
if (resume || slot.sessionStatus === "failed" || slot.sessionStatus === "starting") {
|
|
@@ -29740,6 +29856,7 @@ function formatOccupiedSlot(slot, resume) {
|
|
|
29740
29856
|
);
|
|
29741
29857
|
}
|
|
29742
29858
|
lines.push(
|
|
29859
|
+
"Prefer complete/teardown when the previous task is finished, then launch.",
|
|
29743
29860
|
"Replacing removes the worktree. The session branch is kept only if you committed.",
|
|
29744
29861
|
"Gitignored paths (state/, runs/, local clones) are NOT preserved.",
|
|
29745
29862
|
"",
|
|
@@ -29775,7 +29892,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29775
29892
|
allowed: false,
|
|
29776
29893
|
blocked: true,
|
|
29777
29894
|
slot,
|
|
29778
|
-
reason: formatOccupiedSlot(slot)
|
|
29895
|
+
reason: formatOccupiedSlot(slot, repoPath)
|
|
29779
29896
|
};
|
|
29780
29897
|
}
|
|
29781
29898
|
if (!options.confirmReplace) {
|
|
@@ -29783,7 +29900,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29783
29900
|
allowed: false,
|
|
29784
29901
|
blocked: true,
|
|
29785
29902
|
slot,
|
|
29786
|
-
reason: formatOccupiedSlot(slot)
|
|
29903
|
+
reason: formatOccupiedSlot(slot, repoPath)
|
|
29787
29904
|
};
|
|
29788
29905
|
}
|
|
29789
29906
|
if (slot.dirty && !options.force) {
|
|
@@ -29792,7 +29909,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29792
29909
|
blocked: true,
|
|
29793
29910
|
slot,
|
|
29794
29911
|
reason: [
|
|
29795
|
-
formatOccupiedSlot(slot),
|
|
29912
|
+
formatOccupiedSlot(slot, repoPath),
|
|
29796
29913
|
"",
|
|
29797
29914
|
"The occupied worktree has uncommitted changes.",
|
|
29798
29915
|
"Pass force=true / --force to discard them (only after explicit user approval)."
|
|
@@ -29854,7 +29971,7 @@ function checkInfraPort(env3, varName, defaultPort, scanStart, scanEnd) {
|
|
|
29854
29971
|
function inspectSlotReadiness(repoPath, agentId, options = {}) {
|
|
29855
29972
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29856
29973
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29857
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
29974
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path31.basename(harnessRoot);
|
|
29858
29975
|
const usesPm2 = harnessUsesPm2(repoPath);
|
|
29859
29976
|
const blockers = [];
|
|
29860
29977
|
const remediations = [];
|
|
@@ -30056,7 +30173,7 @@ function readWorktreeBranch(worktreePath) {
|
|
|
30056
30173
|
}
|
|
30057
30174
|
function gitCommonDir3(cwd) {
|
|
30058
30175
|
const out = runGit2(cwd, "rev-parse --git-common-dir");
|
|
30059
|
-
return out ?
|
|
30176
|
+
return out ? path32.resolve(cwd, out) : void 0;
|
|
30060
30177
|
}
|
|
30061
30178
|
function sameGitCheckout3(a2, b2) {
|
|
30062
30179
|
const left2 = gitCommonDir3(a2);
|
|
@@ -30068,12 +30185,12 @@ function gitPrefix2(cwd) {
|
|
|
30068
30185
|
return out ?? "";
|
|
30069
30186
|
}
|
|
30070
30187
|
function discoverSessionWorktreePath(harnessRoot, agentId) {
|
|
30071
|
-
const worktreesRoot =
|
|
30072
|
-
if (!
|
|
30188
|
+
const worktreesRoot = path32.join(os7.homedir(), "worktrees");
|
|
30189
|
+
if (!fs32.existsSync(worktreesRoot)) return void 0;
|
|
30073
30190
|
const suffix = `-har-agent-${agentId}-`;
|
|
30074
30191
|
const relPrefix = gitPrefix2(harnessRoot);
|
|
30075
|
-
const matches =
|
|
30076
|
-
(candidate) => sameGitCheckout3(harnessRoot, candidate) &&
|
|
30192
|
+
const matches = fs32.readdirSync(worktreesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.includes(suffix)).map((entry) => path32.join(worktreesRoot, entry.name)).filter(
|
|
30193
|
+
(candidate) => sameGitCheckout3(harnessRoot, candidate) && fs32.existsSync(path32.join(candidate, relPrefix, `.env.agent.${agentId}`))
|
|
30077
30194
|
);
|
|
30078
30195
|
return matches.sort()[0];
|
|
30079
30196
|
}
|
|
@@ -30148,18 +30265,18 @@ function detectPm2Issue(projectName, agentId, session, procs) {
|
|
|
30148
30265
|
}
|
|
30149
30266
|
function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
|
|
30150
30267
|
const env3 = readHarnessEnv(harnessRoot);
|
|
30151
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
30268
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path32.basename(harnessRoot);
|
|
30152
30269
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
30153
|
-
const legacyWorktreePath =
|
|
30270
|
+
const legacyWorktreePath = path32.join(
|
|
30154
30271
|
os7.homedir(),
|
|
30155
30272
|
"worktrees",
|
|
30156
30273
|
`${projectName}-agent-${agentId}`
|
|
30157
30274
|
);
|
|
30158
|
-
const worktreePath = session?.worktreePath &&
|
|
30275
|
+
const worktreePath = session?.worktreePath && fs32.existsSync(session.worktreePath) ? session.worktreePath : fs32.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
|
|
30159
30276
|
const workDir = resolveAgentWorkDir(harnessRoot, agentId);
|
|
30160
|
-
const envInWorkDir = workDir ?
|
|
30161
|
-
const envInRoot =
|
|
30162
|
-
const envInWorktree = worktreePath ?
|
|
30277
|
+
const envInWorkDir = workDir ? fs32.existsSync(path32.join(workDir, `.env.agent.${agentId}`)) : false;
|
|
30278
|
+
const envInRoot = fs32.existsSync(path32.join(harnessRoot, `.env.agent.${agentId}`));
|
|
30279
|
+
const envInWorktree = worktreePath ? fs32.existsSync(path32.join(worktreePath, `.env.agent.${agentId}`)) : false;
|
|
30163
30280
|
const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
|
|
30164
30281
|
const latest = latestRunForAgent(runs, agentId);
|
|
30165
30282
|
const verifyRun = runs.find((r2) => r2.agentId === agentId && r2.stageId === "verify");
|
|
@@ -30211,7 +30328,7 @@ function collectEnvironmentStatus(repoPath) {
|
|
|
30211
30328
|
const slotIds = getAgentSlotIds(harnessRoot);
|
|
30212
30329
|
const pm2Procs = listPm2Processes2();
|
|
30213
30330
|
return {
|
|
30214
|
-
repoPath:
|
|
30331
|
+
repoPath: path32.resolve(repoPath),
|
|
30215
30332
|
harnessRoot,
|
|
30216
30333
|
gitRemote: readGitRemote(harnessRoot),
|
|
30217
30334
|
profile: manifest?.profile,
|
|
@@ -30222,13 +30339,13 @@ function collectEnvironmentStatus(repoPath) {
|
|
|
30222
30339
|
|
|
30223
30340
|
// src/core/validations.ts
|
|
30224
30341
|
var crypto3 = __toESM(require("crypto"));
|
|
30225
|
-
var
|
|
30226
|
-
var
|
|
30342
|
+
var fs34 = __toESM(require("fs"));
|
|
30343
|
+
var path34 = __toESM(require("path"));
|
|
30227
30344
|
|
|
30228
30345
|
// src/core/change-batch.ts
|
|
30229
|
-
var
|
|
30346
|
+
var fs33 = __toESM(require("fs"));
|
|
30230
30347
|
var os8 = __toESM(require("os"));
|
|
30231
|
-
var
|
|
30348
|
+
var path33 = __toESM(require("path"));
|
|
30232
30349
|
function git(checkoutDir, args, env3) {
|
|
30233
30350
|
const result = run(`git ${args}`, { cwd: checkoutDir, env: env3 });
|
|
30234
30351
|
if (result.code !== 0) {
|
|
@@ -30236,37 +30353,37 @@ function git(checkoutDir, args, env3) {
|
|
|
30236
30353
|
}
|
|
30237
30354
|
return result.stdout.trim();
|
|
30238
30355
|
}
|
|
30239
|
-
function
|
|
30356
|
+
function tryGit2(checkoutDir, args) {
|
|
30240
30357
|
const result = run(`git ${args}`, { cwd: checkoutDir });
|
|
30241
30358
|
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
30242
30359
|
}
|
|
30243
30360
|
function isCheckoutRoot(checkoutDir) {
|
|
30244
|
-
const toplevel =
|
|
30361
|
+
const toplevel = tryGit2(checkoutDir, "rev-parse --show-toplevel");
|
|
30245
30362
|
if (!toplevel) return false;
|
|
30246
30363
|
try {
|
|
30247
|
-
return
|
|
30364
|
+
return fs33.realpathSync(toplevel) === fs33.realpathSync(checkoutDir);
|
|
30248
30365
|
} catch {
|
|
30249
30366
|
return false;
|
|
30250
30367
|
}
|
|
30251
30368
|
}
|
|
30252
30369
|
function getHeadTree(checkoutDir) {
|
|
30253
|
-
return
|
|
30370
|
+
return tryGit2(checkoutDir, "rev-parse -q --verify HEAD^{tree}");
|
|
30254
30371
|
}
|
|
30255
30372
|
function getHeadSha(checkoutDir) {
|
|
30256
|
-
return
|
|
30373
|
+
return tryGit2(checkoutDir, "rev-parse -q --verify HEAD");
|
|
30257
30374
|
}
|
|
30258
30375
|
function getCurrentBranch(checkoutDir) {
|
|
30259
|
-
const branch =
|
|
30376
|
+
const branch = tryGit2(checkoutDir, "rev-parse --abbrev-ref HEAD");
|
|
30260
30377
|
return branch && branch !== "HEAD" ? branch : void 0;
|
|
30261
30378
|
}
|
|
30262
30379
|
function getEmptyTree(checkoutDir) {
|
|
30263
30380
|
return git(checkoutDir, "hash-object -t tree /dev/null");
|
|
30264
30381
|
}
|
|
30265
30382
|
function isMergeOrRebaseInProgress(checkoutDir) {
|
|
30266
|
-
if (
|
|
30383
|
+
if (tryGit2(checkoutDir, "rev-parse -q --verify MERGE_HEAD") !== void 0) return true;
|
|
30267
30384
|
for (const dir of ["rebase-merge", "rebase-apply"]) {
|
|
30268
|
-
const gitPath =
|
|
30269
|
-
if (gitPath &&
|
|
30385
|
+
const gitPath = tryGit2(checkoutDir, `rev-parse --git-path ${dir}`);
|
|
30386
|
+
if (gitPath && fs33.existsSync(path33.resolve(checkoutDir, gitPath))) return true;
|
|
30270
30387
|
}
|
|
30271
30388
|
return false;
|
|
30272
30389
|
}
|
|
@@ -30289,8 +30406,8 @@ function parseNameStatus(output) {
|
|
|
30289
30406
|
return files;
|
|
30290
30407
|
}
|
|
30291
30408
|
function computeWorktreeSnapshot(checkoutDir) {
|
|
30292
|
-
const tmpDir =
|
|
30293
|
-
const tmpIndex =
|
|
30409
|
+
const tmpDir = fs33.mkdtempSync(path33.join(os8.tmpdir(), "har-idx-"));
|
|
30410
|
+
const tmpIndex = path33.join(tmpDir, "index");
|
|
30294
30411
|
const env3 = { GIT_INDEX_FILE: tmpIndex };
|
|
30295
30412
|
try {
|
|
30296
30413
|
const headTree = getHeadTree(checkoutDir);
|
|
@@ -30313,35 +30430,35 @@ function computeWorktreeSnapshot(checkoutDir) {
|
|
|
30313
30430
|
changedFiles: parseNameStatus(diffOutput)
|
|
30314
30431
|
};
|
|
30315
30432
|
} finally {
|
|
30316
|
-
|
|
30433
|
+
fs33.rmSync(tmpDir, { recursive: true, force: true });
|
|
30317
30434
|
}
|
|
30318
30435
|
}
|
|
30319
30436
|
|
|
30320
30437
|
// src/core/validations.ts
|
|
30321
30438
|
var VALIDATIONS_DIR = "validations";
|
|
30322
30439
|
function getValidationsDir(checkoutDir) {
|
|
30323
|
-
return
|
|
30440
|
+
return path34.join(checkoutDir, ".har", VALIDATIONS_DIR);
|
|
30324
30441
|
}
|
|
30325
30442
|
function validationPath(checkoutDir, treeHash) {
|
|
30326
|
-
return
|
|
30443
|
+
return path34.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
|
|
30327
30444
|
}
|
|
30328
30445
|
function writeRecord(checkoutDir, record2) {
|
|
30329
30446
|
const dir = getValidationsDir(checkoutDir);
|
|
30330
|
-
|
|
30331
|
-
|
|
30447
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
30448
|
+
fs34.writeFileSync(
|
|
30332
30449
|
validationPath(checkoutDir, record2.treeHash),
|
|
30333
30450
|
`${JSON.stringify(record2, null, 2)}
|
|
30334
30451
|
`
|
|
30335
30452
|
);
|
|
30336
30453
|
}
|
|
30337
30454
|
function ensureValidationsIgnored(checkoutDir) {
|
|
30338
|
-
const harDir =
|
|
30339
|
-
if (!
|
|
30340
|
-
const gitignorePath =
|
|
30341
|
-
const content =
|
|
30455
|
+
const harDir = path34.join(checkoutDir, ".har");
|
|
30456
|
+
if (!fs34.existsSync(harDir)) return;
|
|
30457
|
+
const gitignorePath = path34.join(harDir, ".gitignore");
|
|
30458
|
+
const content = fs34.existsSync(gitignorePath) ? fs34.readFileSync(gitignorePath, "utf8") : "";
|
|
30342
30459
|
if (content.split("\n").some((line) => line.trim() === `${VALIDATIONS_DIR}/`)) return;
|
|
30343
30460
|
const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
|
|
30344
|
-
|
|
30461
|
+
fs34.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
|
|
30345
30462
|
`);
|
|
30346
30463
|
}
|
|
30347
30464
|
function recordValidation(input) {
|
|
@@ -30358,8 +30475,8 @@ function recordValidation(input) {
|
|
|
30358
30475
|
treeHash: snapshot.treeHash,
|
|
30359
30476
|
headSha: snapshot.headSha,
|
|
30360
30477
|
branch: snapshot.branch,
|
|
30361
|
-
workDir:
|
|
30362
|
-
harnessRoot:
|
|
30478
|
+
workDir: path34.resolve(input.checkoutDir),
|
|
30479
|
+
harnessRoot: path34.resolve(input.harnessRoot),
|
|
30363
30480
|
agentId: input.agentId,
|
|
30364
30481
|
status: input.status,
|
|
30365
30482
|
full: input.full,
|
|
@@ -30371,16 +30488,16 @@ function recordValidation(input) {
|
|
|
30371
30488
|
committedAt: existing?.committedAt
|
|
30372
30489
|
});
|
|
30373
30490
|
writeRecord(input.checkoutDir, record2);
|
|
30374
|
-
if (
|
|
30491
|
+
if (path34.resolve(input.harnessRoot) !== path34.resolve(input.checkoutDir)) {
|
|
30375
30492
|
writeRecord(input.harnessRoot, record2);
|
|
30376
30493
|
}
|
|
30377
30494
|
return record2;
|
|
30378
30495
|
}
|
|
30379
30496
|
function findValidation(checkoutDir, treeHash) {
|
|
30380
30497
|
const file = validationPath(checkoutDir, treeHash);
|
|
30381
|
-
if (!
|
|
30498
|
+
if (!fs34.existsSync(file)) return void 0;
|
|
30382
30499
|
try {
|
|
30383
|
-
return ValidationRecordSchema.parse(JSON.parse(
|
|
30500
|
+
return ValidationRecordSchema.parse(JSON.parse(fs34.readFileSync(file, "utf8")));
|
|
30384
30501
|
} catch {
|
|
30385
30502
|
return void 0;
|
|
30386
30503
|
}
|
|
@@ -30395,19 +30512,19 @@ function attachCommit(checkoutDir, treeHash, commitSha) {
|
|
|
30395
30512
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30396
30513
|
};
|
|
30397
30514
|
writeRecord(checkoutDir, updated);
|
|
30398
|
-
if (record2.harnessRoot &&
|
|
30515
|
+
if (record2.harnessRoot && path34.resolve(record2.harnessRoot) !== path34.resolve(checkoutDir) && fs34.existsSync(path34.join(record2.harnessRoot, ".har"))) {
|
|
30399
30516
|
writeRecord(record2.harnessRoot, updated);
|
|
30400
30517
|
}
|
|
30401
30518
|
return updated;
|
|
30402
30519
|
}
|
|
30403
30520
|
function listValidations(harnessRoot) {
|
|
30404
30521
|
const dir = getValidationsDir(harnessRoot);
|
|
30405
|
-
if (!
|
|
30522
|
+
if (!fs34.existsSync(dir)) return [];
|
|
30406
30523
|
const records = [];
|
|
30407
|
-
for (const entry of
|
|
30524
|
+
for (const entry of fs34.readdirSync(dir)) {
|
|
30408
30525
|
if (!entry.endsWith(".json")) continue;
|
|
30409
30526
|
try {
|
|
30410
|
-
records.push(ValidationRecordSchema.parse(JSON.parse(
|
|
30527
|
+
records.push(ValidationRecordSchema.parse(JSON.parse(fs34.readFileSync(path34.join(dir, entry), "utf8"))));
|
|
30411
30528
|
} catch {
|
|
30412
30529
|
}
|
|
30413
30530
|
}
|
|
@@ -30440,14 +30557,14 @@ function createRemoteExecutor(apiUrl, apiKey) {
|
|
|
30440
30557
|
init_telemetry_config();
|
|
30441
30558
|
|
|
30442
30559
|
// src/core/usage-harvest/claude.ts
|
|
30443
|
-
var
|
|
30560
|
+
var fs37 = __toESM(require("fs"));
|
|
30444
30561
|
var os10 = __toESM(require("os"));
|
|
30445
|
-
var
|
|
30562
|
+
var path37 = __toESM(require("path"));
|
|
30446
30563
|
init_telemetry_config();
|
|
30447
30564
|
|
|
30448
30565
|
// src/core/telemetry-env.ts
|
|
30449
|
-
var
|
|
30450
|
-
var
|
|
30566
|
+
var fs36 = __toESM(require("fs"));
|
|
30567
|
+
var path36 = __toESM(require("path"));
|
|
30451
30568
|
init_control_config();
|
|
30452
30569
|
function escapeAttrValue(value) {
|
|
30453
30570
|
return value.replace(/[,=]/g, "_");
|
|
@@ -30487,8 +30604,8 @@ function appendTelemetryEnvToFile(envFilePath, attrs) {
|
|
|
30487
30604
|
${TELEMETRY_MARKER_END}
|
|
30488
30605
|
`;
|
|
30489
30606
|
let existing = "";
|
|
30490
|
-
if (
|
|
30491
|
-
existing =
|
|
30607
|
+
if (fs36.existsSync(envFilePath)) {
|
|
30608
|
+
existing = fs36.readFileSync(envFilePath, "utf8");
|
|
30492
30609
|
const start = existing.indexOf(TELEMETRY_MARKER_START);
|
|
30493
30610
|
if (start >= 0) {
|
|
30494
30611
|
const end = existing.indexOf(TELEMETRY_MARKER_END, start);
|
|
@@ -30499,28 +30616,28 @@ ${TELEMETRY_MARKER_END}
|
|
|
30499
30616
|
}
|
|
30500
30617
|
}
|
|
30501
30618
|
}
|
|
30502
|
-
|
|
30619
|
+
fs36.mkdirSync(path36.dirname(envFilePath), { recursive: true });
|
|
30503
30620
|
const combined = (existing.replace(/\n+$/, "\n") + block).replace(/^\n+/, "");
|
|
30504
|
-
|
|
30621
|
+
fs36.writeFileSync(envFilePath, combined.endsWith("\n") ? combined : combined + "\n");
|
|
30505
30622
|
}
|
|
30506
30623
|
|
|
30507
30624
|
// src/core/usage-harvest/claude.ts
|
|
30508
30625
|
function pathsMatch(candidate, targets) {
|
|
30509
|
-
const norm =
|
|
30626
|
+
const norm = path37.resolve(candidate);
|
|
30510
30627
|
return targets.some((t2) => {
|
|
30511
|
-
const target =
|
|
30512
|
-
return norm === target || norm.startsWith(target +
|
|
30628
|
+
const target = path37.resolve(t2);
|
|
30629
|
+
return norm === target || norm.startsWith(target + path37.sep) || target.startsWith(norm + path37.sep);
|
|
30513
30630
|
});
|
|
30514
30631
|
}
|
|
30515
30632
|
function claudeProjectsRoot() {
|
|
30516
|
-
return process.env.HAR_CLAUDE_PROJECTS_DIR ?
|
|
30633
|
+
return process.env.HAR_CLAUDE_PROJECTS_DIR ? path37.resolve(process.env.HAR_CLAUDE_PROJECTS_DIR) : path37.join(os10.homedir(), ".claude", "projects");
|
|
30517
30634
|
}
|
|
30518
30635
|
function encodeClaudeProjectDir(cwd) {
|
|
30519
30636
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
30520
30637
|
}
|
|
30521
30638
|
function readJsonlRecords(filePath) {
|
|
30522
|
-
if (!
|
|
30523
|
-
const lines =
|
|
30639
|
+
if (!fs37.existsSync(filePath)) return [];
|
|
30640
|
+
const lines = fs37.readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
30524
30641
|
const out = [];
|
|
30525
30642
|
for (const line of lines) {
|
|
30526
30643
|
const trimmed = line.trim();
|
|
@@ -30563,16 +30680,16 @@ function findMatchingClaudeTranscripts(slot) {
|
|
|
30563
30680
|
const targets = [slot.workDir, slot.worktreePath].filter(Boolean);
|
|
30564
30681
|
if (targets.length === 0) return [];
|
|
30565
30682
|
const root = claudeProjectsRoot();
|
|
30566
|
-
if (!
|
|
30683
|
+
if (!fs37.existsSync(root)) return [];
|
|
30567
30684
|
const matches = [];
|
|
30568
|
-
for (const entry of
|
|
30685
|
+
for (const entry of fs37.readdirSync(root, { withFileTypes: true })) {
|
|
30569
30686
|
if (!entry.isDirectory()) continue;
|
|
30570
|
-
const projectDir =
|
|
30687
|
+
const projectDir = path37.join(root, entry.name);
|
|
30571
30688
|
const encodedHit = targets.some((t2) => entry.name.includes(encodeClaudeProjectDir(t2).slice(0, 40)));
|
|
30572
|
-
for (const file of
|
|
30689
|
+
for (const file of fs37.readdirSync(projectDir)) {
|
|
30573
30690
|
if (!file.endsWith(".jsonl")) continue;
|
|
30574
|
-
const filePath =
|
|
30575
|
-
const stat =
|
|
30691
|
+
const filePath = path37.join(projectDir, file);
|
|
30692
|
+
const stat = fs37.statSync(filePath);
|
|
30576
30693
|
const records = readJsonlRecords(filePath);
|
|
30577
30694
|
let cwdHit = encodedHit;
|
|
30578
30695
|
for (const record2 of records) {
|
|
@@ -30683,30 +30800,30 @@ function harvestClaudeEvents(slot) {
|
|
|
30683
30800
|
}
|
|
30684
30801
|
|
|
30685
30802
|
// src/core/usage-harvest/codex.ts
|
|
30686
|
-
var
|
|
30803
|
+
var fs38 = __toESM(require("fs"));
|
|
30687
30804
|
var os11 = __toESM(require("os"));
|
|
30688
|
-
var
|
|
30805
|
+
var path38 = __toESM(require("path"));
|
|
30689
30806
|
function pathsMatch2(candidate, targets) {
|
|
30690
|
-
const norm =
|
|
30807
|
+
const norm = path38.resolve(candidate);
|
|
30691
30808
|
return targets.some((t2) => {
|
|
30692
|
-
const target =
|
|
30693
|
-
return norm === target || norm.startsWith(target +
|
|
30809
|
+
const target = path38.resolve(t2);
|
|
30810
|
+
return norm === target || norm.startsWith(target + path38.sep) || target.startsWith(norm + path38.sep);
|
|
30694
30811
|
});
|
|
30695
30812
|
}
|
|
30696
30813
|
function codexSessionsRoot() {
|
|
30697
|
-
return process.env.HAR_CODEX_SESSIONS_DIR ?
|
|
30814
|
+
return process.env.HAR_CODEX_SESSIONS_DIR ? path38.resolve(process.env.HAR_CODEX_SESSIONS_DIR) : path38.join(os11.homedir(), ".codex", "sessions");
|
|
30698
30815
|
}
|
|
30699
30816
|
function walkJsonlFiles(dir, out = []) {
|
|
30700
|
-
if (!
|
|
30701
|
-
for (const entry of
|
|
30702
|
-
const full =
|
|
30817
|
+
if (!fs38.existsSync(dir)) return out;
|
|
30818
|
+
for (const entry of fs38.readdirSync(dir, { withFileTypes: true })) {
|
|
30819
|
+
const full = path38.join(dir, entry.name);
|
|
30703
30820
|
if (entry.isDirectory()) walkJsonlFiles(full, out);
|
|
30704
30821
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push(full);
|
|
30705
30822
|
}
|
|
30706
30823
|
return out;
|
|
30707
30824
|
}
|
|
30708
30825
|
function readJsonl(filePath) {
|
|
30709
|
-
const lines =
|
|
30826
|
+
const lines = fs38.readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
30710
30827
|
const out = [];
|
|
30711
30828
|
for (const line of lines) {
|
|
30712
30829
|
const trimmed = line.trim();
|
|
@@ -30770,7 +30887,7 @@ function harvestCodexUsage(slot) {
|
|
|
30770
30887
|
const extracted = extractCodexTokens(records);
|
|
30771
30888
|
if (!extracted.cwd || !pathsMatch2(extracted.cwd, targets)) continue;
|
|
30772
30889
|
if (extracted.tokensInput + extracted.tokensOutput + extracted.tokensCacheRead === 0) continue;
|
|
30773
|
-
const mtime =
|
|
30890
|
+
const mtime = fs38.statSync(file).mtimeMs;
|
|
30774
30891
|
if (mtime >= bestMtime) {
|
|
30775
30892
|
bestMtime = mtime;
|
|
30776
30893
|
best = extracted;
|
|
@@ -30840,7 +30957,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
|
|
|
30840
30957
|
const deadline = Date.now() + timeoutMs;
|
|
30841
30958
|
while (Date.now() < deadline) {
|
|
30842
30959
|
if (await isControlApiReachable(apiUrl)) return true;
|
|
30843
|
-
await new Promise((
|
|
30960
|
+
await new Promise((resolve36) => setTimeout(resolve36, intervalMs));
|
|
30844
30961
|
}
|
|
30845
30962
|
return false;
|
|
30846
30963
|
}
|
|
@@ -30852,7 +30969,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30852
30969
|
}
|
|
30853
30970
|
const repoPaths = new Set(listRegisteredRepos());
|
|
30854
30971
|
if (options?.cwd) {
|
|
30855
|
-
const cwd =
|
|
30972
|
+
const cwd = canonicalizeControlRepoPath(options.cwd);
|
|
30856
30973
|
if (readManifest(cwd)) repoPaths.add(cwd);
|
|
30857
30974
|
}
|
|
30858
30975
|
try {
|
|
@@ -30860,7 +30977,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30860
30977
|
if (listResponse.ok) {
|
|
30861
30978
|
const repos = await listResponse.json();
|
|
30862
30979
|
for (const repo of repos) {
|
|
30863
|
-
const resolved =
|
|
30980
|
+
const resolved = canonicalizeControlRepoPath(repo.path);
|
|
30864
30981
|
if (readManifest(resolved)) repoPaths.add(resolved);
|
|
30865
30982
|
}
|
|
30866
30983
|
}
|
|
@@ -30879,7 +30996,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30879
30996
|
return { synced, failed };
|
|
30880
30997
|
}
|
|
30881
30998
|
async function registerRepoWithControl(options) {
|
|
30882
|
-
const repoPath =
|
|
30999
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
30883
31000
|
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
30884
31001
|
const manifest = readManifest(repoPath);
|
|
30885
31002
|
const stagesRegistry = readStageRegistry(repoPath);
|
|
@@ -30887,12 +31004,27 @@ async function registerRepoWithControl(options) {
|
|
|
30887
31004
|
path: repoPath,
|
|
30888
31005
|
gitRemote: options.gitRemote,
|
|
30889
31006
|
manifest: manifest ?? void 0,
|
|
30890
|
-
stagesRegistry: stagesRegistry ?? void 0
|
|
31007
|
+
stagesRegistry: stagesRegistry ?? void 0,
|
|
31008
|
+
force: options.force
|
|
30891
31009
|
};
|
|
30892
|
-
|
|
31010
|
+
if (options.dryRun) return null;
|
|
31011
|
+
const response = await fetch(`${apiUrl}/api/repos`, {
|
|
31012
|
+
method: "POST",
|
|
31013
|
+
headers: { "Content-Type": "application/json" },
|
|
31014
|
+
body: JSON.stringify(body)
|
|
31015
|
+
});
|
|
31016
|
+
if (response.status === 409) {
|
|
31017
|
+
removeRegisteredRepo(repoPath);
|
|
31018
|
+
return null;
|
|
31019
|
+
}
|
|
31020
|
+
if (!response.ok) {
|
|
31021
|
+
const text = await response.text().catch(() => "");
|
|
31022
|
+
throw new Error(`Control API ${response.status}: ${text || response.statusText}`);
|
|
31023
|
+
}
|
|
31024
|
+
return await response.json();
|
|
30893
31025
|
}
|
|
30894
31026
|
async function syncRepoWithControl(options) {
|
|
30895
|
-
const repoPath =
|
|
31027
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
30896
31028
|
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
30897
31029
|
if (options.cloud) {
|
|
30898
31030
|
const remote = createRemoteExecutor();
|
|
@@ -30920,8 +31052,10 @@ async function syncRepoWithControl(options) {
|
|
|
30920
31052
|
const listResponse = await fetch(`${apiUrl}/api/repos`);
|
|
30921
31053
|
if (!listResponse.ok) throw new Error("Failed to list repos from Control API");
|
|
30922
31054
|
const repos = await listResponse.json();
|
|
30923
|
-
const existing = repos.find((r2) =>
|
|
30924
|
-
if (!existing)
|
|
31055
|
+
const existing = repos.find((r2) => path39.resolve(r2.path) === repoPath);
|
|
31056
|
+
if (!existing) {
|
|
31057
|
+
return;
|
|
31058
|
+
}
|
|
30925
31059
|
await syncRepoRunsAndSlots(apiUrl, existing.id, repoPath, options.dryRun);
|
|
30926
31060
|
return;
|
|
30927
31061
|
}
|
|
@@ -31011,7 +31145,12 @@ async function syncRepoWithControlAsync(repoPath) {
|
|
|
31011
31145
|
}
|
|
31012
31146
|
return;
|
|
31013
31147
|
}
|
|
31014
|
-
|
|
31148
|
+
const canonical = canonicalizeControlRepoPath(repoPath);
|
|
31149
|
+
await withTimeout(
|
|
31150
|
+
syncRepoWithControl({ repoPath: canonical, apiUrl }),
|
|
31151
|
+
SYNC_TIMEOUT_MS,
|
|
31152
|
+
"control sync"
|
|
31153
|
+
);
|
|
31015
31154
|
} catch (err) {
|
|
31016
31155
|
if (verbose) {
|
|
31017
31156
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -31042,7 +31181,7 @@ init_control_config();
|
|
|
31042
31181
|
|
|
31043
31182
|
// src/core/control-lifecycle.ts
|
|
31044
31183
|
var import_child_process8 = require("child_process");
|
|
31045
|
-
var
|
|
31184
|
+
var path40 = __toESM(require("path"));
|
|
31046
31185
|
|
|
31047
31186
|
// src/core/control-image.ts
|
|
31048
31187
|
var DEFAULT_CONTROL_IMAGE = "theosfactory/har-control";
|
|
@@ -31066,10 +31205,10 @@ var CONTROL_DATA_VOLUME = "har_control_data";
|
|
|
31066
31205
|
var CONTROL_CONTAINER_PORT = 3847;
|
|
31067
31206
|
var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
|
|
31068
31207
|
function resolveControlDir() {
|
|
31069
|
-
return
|
|
31208
|
+
return path40.resolve(__dirname, "..", "control");
|
|
31070
31209
|
}
|
|
31071
31210
|
function resolveControlBuildContext() {
|
|
31072
|
-
return
|
|
31211
|
+
return path40.resolve(resolveControlDir(), "..");
|
|
31073
31212
|
}
|
|
31074
31213
|
function buildDockerRunArgs(options) {
|
|
31075
31214
|
const name = options.containerName ?? CONTROL_CONTAINER_NAME;
|
|
@@ -31125,7 +31264,7 @@ async function startMissionControl(options) {
|
|
|
31125
31264
|
"-t",
|
|
31126
31265
|
imageRef,
|
|
31127
31266
|
"-f",
|
|
31128
|
-
|
|
31267
|
+
path40.join(resolveControlDir(), "Dockerfile"),
|
|
31129
31268
|
resolveControlBuildContext()
|
|
31130
31269
|
]);
|
|
31131
31270
|
if (buildCode !== 0) {
|
|
@@ -31269,7 +31408,7 @@ async function ensureTelemetryInfrastructure(options) {
|
|
|
31269
31408
|
|
|
31270
31409
|
// src/core/run-service.ts
|
|
31271
31410
|
init_telemetry_config();
|
|
31272
|
-
var
|
|
31411
|
+
var path42 = __toESM(require("path"));
|
|
31273
31412
|
function extractShellOutput(result) {
|
|
31274
31413
|
const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
|
|
31275
31414
|
const stderr = result.logs?.find((log) => log.stream === "stderr")?.content ?? "";
|
|
@@ -31442,12 +31581,12 @@ Usage from Cursor / Claude / Codex (opentelemetry-hooks) will appear under Workt
|
|
|
31442
31581
|
suffix: session.suffix,
|
|
31443
31582
|
createdAt: session.createdAt
|
|
31444
31583
|
});
|
|
31445
|
-
const envFile =
|
|
31584
|
+
const envFile = path42.join(session.workDir, `.env.agent.${options.agentId}`);
|
|
31446
31585
|
try {
|
|
31447
31586
|
appendTelemetryEnvToFile(envFile, {
|
|
31448
31587
|
sessionKey,
|
|
31449
31588
|
agentId: options.agentId,
|
|
31450
|
-
repoPath:
|
|
31589
|
+
repoPath: path42.resolve(options.repoPath),
|
|
31451
31590
|
workDir: session.workDir,
|
|
31452
31591
|
branch: session.branch,
|
|
31453
31592
|
suffix: session.suffix
|
|
@@ -31612,6 +31751,9 @@ Usage from Cursor / Claude / Codex (opentelemetry-hooks) will appear under Workt
|
|
|
31612
31751
|
}
|
|
31613
31752
|
};
|
|
31614
31753
|
var defaultRunService = new RunService();
|
|
31754
|
+
function createRunService(executor = localScriptExecutor) {
|
|
31755
|
+
return new RunService(executor);
|
|
31756
|
+
}
|
|
31615
31757
|
var runStage = defaultRunService.runStage.bind(defaultRunService);
|
|
31616
31758
|
var listArtifacts = defaultRunService.listArtifacts.bind(defaultRunService);
|
|
31617
31759
|
var launchEnvironment = defaultRunService.launchEnvironment.bind(defaultRunService);
|
|
@@ -31622,10 +31764,59 @@ var completeEnvironment = defaultRunService.completeEnvironment.bind(defaultRunS
|
|
|
31622
31764
|
var getEnvironmentStatus = defaultRunService.getEnvironmentStatus.bind(defaultRunService);
|
|
31623
31765
|
var getEnvironmentLogs = defaultRunService.getEnvironmentLogs.bind(defaultRunService);
|
|
31624
31766
|
|
|
31767
|
+
// src/cli/help-text.ts
|
|
31768
|
+
var HAR_ROOT_EPILOG = `Typical agent workflow:
|
|
31769
|
+
har env status # see which slots are free / occupied
|
|
31770
|
+
har env launch <id> # new session from the main checkout's HEAD
|
|
31771
|
+
# \u2026edit only under the printed work dir\u2026
|
|
31772
|
+
har env verify <id> --full
|
|
31773
|
+
har env complete <id> # done: verify + free the slot (branch kept for PR)
|
|
31774
|
+
|
|
31775
|
+
Slot lifecycle (do not confuse these):
|
|
31776
|
+
launch start a NEW session (worktree from $REPO_ROOT HEAD)
|
|
31777
|
+
recover resume a failed/partial launch (keeps the existing worktree)
|
|
31778
|
+
complete finish successfully: full verify, then teardown (frees the slot)
|
|
31779
|
+
teardown abandon / free the slot without a completion validation
|
|
31780
|
+
|
|
31781
|
+
--replace only confirms destroying a previous session on that slot id
|
|
31782
|
+
it does NOT choose main \u2014 new session still uses current HEAD
|
|
31783
|
+
--force additionally discard dirty uncommitted work (user approval only)
|
|
31784
|
+
--resume recover failed/starting; never use --replace for that
|
|
31785
|
+
|
|
31786
|
+
For a new unrelated task: checkout main (or your intended base) on the main
|
|
31787
|
+
repo checkout, free the slot (complete/teardown), then launch.`;
|
|
31788
|
+
var HAR_ENV_EPILOG = `Environment lifecycle:
|
|
31789
|
+
preflight <id> dry-run: ports, PM2, Docker, occupied-slot gates
|
|
31790
|
+
launch <id> fresh session worktree from the main checkout's current HEAD
|
|
31791
|
+
recover <id> alias for launch --resume (failed/starting only)
|
|
31792
|
+
verify <id> run checks (--full before declaring done)
|
|
31793
|
+
complete <id> full verify + teardown; keeps branch for push/PR (frees slot)
|
|
31794
|
+
teardown <id> free the slot without recording a completion validation
|
|
31795
|
+
|
|
31796
|
+
Occupied slots:
|
|
31797
|
+
Prefer complete/teardown when the previous task is finished, then launch.
|
|
31798
|
+
Use --replace only to reuse the same slot id immediately (destroys old worktree).
|
|
31799
|
+
--replace does not select the base branch \u2014 HEAD of --repo does.
|
|
31800
|
+
Dirty previous worktree also needs --force after explicit user approval.`;
|
|
31801
|
+
var LAUNCH_COMMAND_DESCRIBE = "Start a fresh agent session (new worktree from the main checkout HEAD)";
|
|
31802
|
+
var LAUNCH_REPLACE_DESCRIBE = "Destroy the previous session on this slot and start another (does not choose main; still uses --repo HEAD)";
|
|
31803
|
+
var LAUNCH_FORCE_DESCRIBE = "With --replace: discard uncommitted changes in the old worktree (only after explicit user approval)";
|
|
31804
|
+
var LAUNCH_RESUME_DESCRIBE = "Continue a failed or starting launch without creating a new worktree (prefer over --replace)";
|
|
31805
|
+
var LAUNCH_EPILOG = `Every launch creates a NEW session from the current HEAD of --repo (the main
|
|
31806
|
+
checkout). The worktree path encodes that base branch \u2014 switch --repo to main
|
|
31807
|
+
before launch for a new unrelated task unless you intentionally stack on a
|
|
31808
|
+
feature branch.
|
|
31809
|
+
|
|
31810
|
+
Free slot: har env launch 1
|
|
31811
|
+
Occupied: har env complete 1 # or teardown \u2014 then launch
|
|
31812
|
+
har env launch 1 --replace # reuse slot id now
|
|
31813
|
+
har env launch 1 --replace --force # + discard dirty work
|
|
31814
|
+
Failed launch: har env recover 1 # or: har env launch 1 --resume`;
|
|
31815
|
+
|
|
31625
31816
|
// src/cli/commands/env.ts
|
|
31626
31817
|
var envCommand = {
|
|
31627
31818
|
command: "env <subcommand>",
|
|
31628
|
-
describe: "Manage agent environments",
|
|
31819
|
+
describe: "Manage agent environments (launch \u2192 verify \u2192 complete)",
|
|
31629
31820
|
builder: (yargs) => yargs.command(
|
|
31630
31821
|
"init",
|
|
31631
31822
|
"Copy harness boilerplate into .har/ (use --auto for built-in Claude adaptation)",
|
|
@@ -31720,24 +31911,28 @@ var envCommand = {
|
|
|
31720
31911
|
handleAddStage
|
|
31721
31912
|
).command(
|
|
31722
31913
|
"launch <id>",
|
|
31723
|
-
|
|
31724
|
-
(y2) => y2.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", {
|
|
31914
|
+
LAUNCH_COMMAND_DESCRIBE,
|
|
31915
|
+
(y2) => y2.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", {
|
|
31916
|
+
type: "string",
|
|
31917
|
+
default: ".",
|
|
31918
|
+
describe: "Main checkout whose HEAD becomes the new session base"
|
|
31919
|
+
}).option("worktree", {
|
|
31725
31920
|
type: "boolean",
|
|
31726
31921
|
default: true,
|
|
31727
31922
|
describe: "Use an isolated git worktree (default)"
|
|
31728
31923
|
}).option("claude", { type: "boolean", default: false }).option("replace", {
|
|
31729
31924
|
type: "boolean",
|
|
31730
31925
|
default: false,
|
|
31731
|
-
describe:
|
|
31926
|
+
describe: LAUNCH_REPLACE_DESCRIBE
|
|
31732
31927
|
}).option("force", {
|
|
31733
31928
|
type: "boolean",
|
|
31734
31929
|
default: false,
|
|
31735
|
-
describe:
|
|
31930
|
+
describe: LAUNCH_FORCE_DESCRIBE
|
|
31736
31931
|
}).option("resume", {
|
|
31737
31932
|
type: "boolean",
|
|
31738
31933
|
default: false,
|
|
31739
|
-
describe:
|
|
31740
|
-
}),
|
|
31934
|
+
describe: LAUNCH_RESUME_DESCRIBE
|
|
31935
|
+
}).epilog(LAUNCH_EPILOG),
|
|
31741
31936
|
handleLaunch
|
|
31742
31937
|
).command(
|
|
31743
31938
|
"recover <id>",
|
|
@@ -31805,12 +32000,15 @@ var envCommand = {
|
|
|
31805
32000
|
).demandCommand(1, "Use: runs list | runs get <runId>"),
|
|
31806
32001
|
() => {
|
|
31807
32002
|
}
|
|
31808
|
-
).demandCommand(
|
|
32003
|
+
).demandCommand(
|
|
32004
|
+
1,
|
|
32005
|
+
"Please specify a subcommand: init, maintain, add-stage, launch, recover, verify, complete, teardown, status, runs"
|
|
32006
|
+
).epilog(HAR_ENV_EPILOG),
|
|
31809
32007
|
handler: () => {
|
|
31810
32008
|
}
|
|
31811
32009
|
};
|
|
31812
32010
|
async function handleInit(argv) {
|
|
31813
|
-
const repoPath =
|
|
32011
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31814
32012
|
header("har env init");
|
|
31815
32013
|
info(`Repository: ${repoPath}`);
|
|
31816
32014
|
try {
|
|
@@ -31868,7 +32066,7 @@ async function handleInit(argv) {
|
|
|
31868
32066
|
}
|
|
31869
32067
|
}
|
|
31870
32068
|
async function handleMaintain(argv) {
|
|
31871
|
-
const repoPath =
|
|
32069
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31872
32070
|
header("har env maintain");
|
|
31873
32071
|
info(`Repository: ${repoPath}`);
|
|
31874
32072
|
try {
|
|
@@ -31966,7 +32164,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
|
|
|
31966
32164
|
if (autoYes) {
|
|
31967
32165
|
const proposal = readAgentMdProposal(repoPath);
|
|
31968
32166
|
if (proposal) {
|
|
31969
|
-
writeFileSafe(
|
|
32167
|
+
writeFileSafe(path43.join(repoPath, "AGENT.md"), proposal.content);
|
|
31970
32168
|
clearAgentMdProposal(repoPath);
|
|
31971
32169
|
info("Applied AGENT.md proposal (--yes)");
|
|
31972
32170
|
}
|
|
@@ -31975,7 +32173,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
|
|
|
31975
32173
|
await promptApplyAgentMdProposal(repoPath);
|
|
31976
32174
|
}
|
|
31977
32175
|
async function handleAddStage(argv) {
|
|
31978
|
-
const repoPath =
|
|
32176
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31979
32177
|
const available = listStageTemplateIds();
|
|
31980
32178
|
if (argv.list) {
|
|
31981
32179
|
for (const id of available) {
|
|
@@ -32052,7 +32250,7 @@ async function handleAddStage(argv) {
|
|
|
32052
32250
|
}
|
|
32053
32251
|
}
|
|
32054
32252
|
async function handlePreflight(argv) {
|
|
32055
|
-
const repo =
|
|
32253
|
+
const repo = path43.resolve(argv.repo);
|
|
32056
32254
|
const agentId = validateAgentId(argv.id, repo);
|
|
32057
32255
|
const result = await preflightEnvironment({
|
|
32058
32256
|
repoPath: repo,
|
|
@@ -32070,7 +32268,7 @@ async function handlePreflight(argv) {
|
|
|
32070
32268
|
process.exit(result.code);
|
|
32071
32269
|
}
|
|
32072
32270
|
async function handleLaunch(argv) {
|
|
32073
|
-
const repo =
|
|
32271
|
+
const repo = path43.resolve(argv.repo);
|
|
32074
32272
|
const agentId = validateAgentId(argv.id, repo);
|
|
32075
32273
|
let confirmReplace = argv.replace;
|
|
32076
32274
|
let force = argv.force;
|
|
@@ -32082,8 +32280,8 @@ async function handleLaunch(argv) {
|
|
|
32082
32280
|
console.error(guard.reason ?? "");
|
|
32083
32281
|
const readline5 = await import("readline");
|
|
32084
32282
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32085
|
-
const answer = await new Promise((
|
|
32086
|
-
rl.question("Replace this slot? [y/N] ",
|
|
32283
|
+
const answer = await new Promise((resolve36) => {
|
|
32284
|
+
rl.question("Replace this slot? [y/N] ", resolve36);
|
|
32087
32285
|
});
|
|
32088
32286
|
rl.close();
|
|
32089
32287
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -32099,8 +32297,8 @@ async function handleLaunch(argv) {
|
|
|
32099
32297
|
warn("Worktree has uncommitted changes.");
|
|
32100
32298
|
const readline5 = await import("readline");
|
|
32101
32299
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32102
|
-
const answer = await new Promise((
|
|
32103
|
-
rl.question("Discard uncommitted changes? [y/N] ",
|
|
32300
|
+
const answer = await new Promise((resolve36) => {
|
|
32301
|
+
rl.question("Discard uncommitted changes? [y/N] ", resolve36);
|
|
32104
32302
|
});
|
|
32105
32303
|
rl.close();
|
|
32106
32304
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -32154,7 +32352,7 @@ async function handleRecover(argv) {
|
|
|
32154
32352
|
});
|
|
32155
32353
|
}
|
|
32156
32354
|
async function handleVerify(argv) {
|
|
32157
|
-
const repo =
|
|
32355
|
+
const repo = path43.resolve(argv.repo);
|
|
32158
32356
|
const agentId = validateAgentId(argv.id, repo);
|
|
32159
32357
|
const result = await runVerification({
|
|
32160
32358
|
repoPath: repo,
|
|
@@ -32166,7 +32364,7 @@ async function handleVerify(argv) {
|
|
|
32166
32364
|
process.exit(result.code);
|
|
32167
32365
|
}
|
|
32168
32366
|
async function handleTeardown(argv) {
|
|
32169
|
-
const repo =
|
|
32367
|
+
const repo = path43.resolve(argv.repo);
|
|
32170
32368
|
const agentId = validateAgentId(argv.id, repo);
|
|
32171
32369
|
const result = await teardownEnvironment({
|
|
32172
32370
|
repoPath: repo,
|
|
@@ -32177,7 +32375,7 @@ async function handleTeardown(argv) {
|
|
|
32177
32375
|
process.exit(result.code);
|
|
32178
32376
|
}
|
|
32179
32377
|
async function handleComplete(argv) {
|
|
32180
|
-
const repo =
|
|
32378
|
+
const repo = path43.resolve(argv.repo);
|
|
32181
32379
|
const agentId = validateAgentId(argv.id, repo);
|
|
32182
32380
|
const result = await completeEnvironment({
|
|
32183
32381
|
repoPath: repo,
|
|
@@ -32198,7 +32396,7 @@ async function handleComplete(argv) {
|
|
|
32198
32396
|
process.exit(result.code);
|
|
32199
32397
|
}
|
|
32200
32398
|
async function handleStatus(argv) {
|
|
32201
|
-
const repoPath =
|
|
32399
|
+
const repoPath = path43.resolve(argv.repo);
|
|
32202
32400
|
if (argv.json) {
|
|
32203
32401
|
const status = collectEnvironmentStatus(repoPath);
|
|
32204
32402
|
const output = EnvironmentStatusSchema.parse(status);
|
|
@@ -32211,7 +32409,7 @@ async function handleStatus(argv) {
|
|
|
32211
32409
|
});
|
|
32212
32410
|
}
|
|
32213
32411
|
async function handleRunsList(argv) {
|
|
32214
|
-
const repoPath =
|
|
32412
|
+
const repoPath = path43.resolve(argv.repo);
|
|
32215
32413
|
const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
|
|
32216
32414
|
if (argv.json) {
|
|
32217
32415
|
process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
|
|
@@ -32228,7 +32426,7 @@ async function handleRunsList(argv) {
|
|
|
32228
32426
|
}
|
|
32229
32427
|
}
|
|
32230
32428
|
async function handleRunsGet(argv) {
|
|
32231
|
-
const run2 = getRun(
|
|
32429
|
+
const run2 = getRun(path43.resolve(argv.repo), argv.runId);
|
|
32232
32430
|
if (!run2) {
|
|
32233
32431
|
error(`Run not found: ${argv.runId}`);
|
|
32234
32432
|
process.exit(1);
|
|
@@ -32307,8 +32505,213 @@ function printNextSteps(auto2) {
|
|
|
32307
32505
|
}
|
|
32308
32506
|
|
|
32309
32507
|
// src/cli/commands/control.ts
|
|
32310
|
-
var
|
|
32508
|
+
var path45 = __toESM(require("path"));
|
|
32311
32509
|
init_control_config();
|
|
32510
|
+
|
|
32511
|
+
// src/core/control-unregister.ts
|
|
32512
|
+
var fs40 = __toESM(require("fs"));
|
|
32513
|
+
var path44 = __toESM(require("path"));
|
|
32514
|
+
init_control_config();
|
|
32515
|
+
function listUnregisterWorktreeCandidates(repoPath) {
|
|
32516
|
+
const harnessRoot = canonicalizeControlRepoPath(repoPath);
|
|
32517
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
32518
|
+
const add = (partial2) => {
|
|
32519
|
+
const resolved = path44.resolve(partial2.worktreePath);
|
|
32520
|
+
if (!resolved || resolved === harnessRoot) return;
|
|
32521
|
+
const exists = partial2.exists ?? fs40.existsSync(resolved);
|
|
32522
|
+
const prev = byPath.get(resolved);
|
|
32523
|
+
if (prev) {
|
|
32524
|
+
byPath.set(resolved, {
|
|
32525
|
+
...prev,
|
|
32526
|
+
dirty: prev.dirty || partial2.dirty,
|
|
32527
|
+
branch: prev.branch ?? partial2.branch,
|
|
32528
|
+
workDir: prev.workDir ?? partial2.workDir
|
|
32529
|
+
});
|
|
32530
|
+
return;
|
|
32531
|
+
}
|
|
32532
|
+
byPath.set(resolved, {
|
|
32533
|
+
agentId: partial2.agentId,
|
|
32534
|
+
worktreePath: resolved,
|
|
32535
|
+
workDir: partial2.workDir,
|
|
32536
|
+
branch: partial2.branch,
|
|
32537
|
+
dirty: partial2.dirty,
|
|
32538
|
+
exists
|
|
32539
|
+
});
|
|
32540
|
+
};
|
|
32541
|
+
for (const entry of listSlotRegistryEntries(harnessRoot)) {
|
|
32542
|
+
const worktreePath = entry.worktreePath ?? entry.workDir;
|
|
32543
|
+
if (!worktreePath) continue;
|
|
32544
|
+
add({
|
|
32545
|
+
agentId: entry.agentId,
|
|
32546
|
+
worktreePath,
|
|
32547
|
+
workDir: entry.workDir,
|
|
32548
|
+
branch: entry.branch
|
|
32549
|
+
});
|
|
32550
|
+
}
|
|
32551
|
+
try {
|
|
32552
|
+
const status = collectEnvironmentStatus(harnessRoot);
|
|
32553
|
+
for (const slot of status.slots) {
|
|
32554
|
+
const worktreePath = slot.worktreePath ?? slot.workDir;
|
|
32555
|
+
if (!worktreePath) continue;
|
|
32556
|
+
add({
|
|
32557
|
+
agentId: slot.agentId,
|
|
32558
|
+
worktreePath,
|
|
32559
|
+
workDir: slot.workDir,
|
|
32560
|
+
branch: slot.branch,
|
|
32561
|
+
dirty: slot.dirty === true
|
|
32562
|
+
});
|
|
32563
|
+
}
|
|
32564
|
+
} catch {
|
|
32565
|
+
}
|
|
32566
|
+
return [...byPath.values()].sort((a2, b2) => a2.agentId - b2.agentId);
|
|
32567
|
+
}
|
|
32568
|
+
async function askYesNo4(question) {
|
|
32569
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
32570
|
+
const readline5 = await import("readline");
|
|
32571
|
+
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32572
|
+
const answer = await new Promise((resolve36) => {
|
|
32573
|
+
rl.question(question, resolve36);
|
|
32574
|
+
});
|
|
32575
|
+
rl.close();
|
|
32576
|
+
return /^[Yy]$/.test(answer.trim());
|
|
32577
|
+
}
|
|
32578
|
+
async function confirmUnregister(options) {
|
|
32579
|
+
if (options.yes) {
|
|
32580
|
+
return { proceed: true, deleteWorktrees: false };
|
|
32581
|
+
}
|
|
32582
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
32583
|
+
throw new Error("Non-interactive terminal \u2014 pass --yes (and optionally --delete-worktrees)");
|
|
32584
|
+
}
|
|
32585
|
+
const proceed = await askYesNo4("Unregister this repository from Mission Control? [y/N] ");
|
|
32586
|
+
if (!proceed) return { proceed: false, deleteWorktrees: false };
|
|
32587
|
+
const existing = options.worktrees.filter((w2) => w2.exists);
|
|
32588
|
+
if (existing.length === 0) {
|
|
32589
|
+
return { proceed: true, deleteWorktrees: false };
|
|
32590
|
+
}
|
|
32591
|
+
process.stderr.write("\nSession worktrees:\n");
|
|
32592
|
+
for (const wt2 of existing) {
|
|
32593
|
+
const dirty = wt2.dirty ? " (dirty)" : "";
|
|
32594
|
+
process.stderr.write(` agent-${wt2.agentId}: ${wt2.worktreePath}${dirty}
|
|
32595
|
+
`);
|
|
32596
|
+
}
|
|
32597
|
+
const deleteWorktrees = await askYesNo4(
|
|
32598
|
+
`Also delete ${existing.length} session worktree(s) on disk? [y/N] `
|
|
32599
|
+
);
|
|
32600
|
+
return { proceed: true, deleteWorktrees };
|
|
32601
|
+
}
|
|
32602
|
+
async function deleteJson(url, body, dryRun) {
|
|
32603
|
+
if (dryRun) return null;
|
|
32604
|
+
const response = await fetch(url, {
|
|
32605
|
+
method: "DELETE",
|
|
32606
|
+
headers: { "Content-Type": "application/json" },
|
|
32607
|
+
body: JSON.stringify(body)
|
|
32608
|
+
});
|
|
32609
|
+
if (!response.ok) {
|
|
32610
|
+
const text = await response.text().catch(() => "");
|
|
32611
|
+
return { ok: false, status: response.status, text: text || response.statusText };
|
|
32612
|
+
}
|
|
32613
|
+
return { ok: true, data: await response.json() };
|
|
32614
|
+
}
|
|
32615
|
+
async function teardownWorktrees(repoPath, candidates, dryRun) {
|
|
32616
|
+
const outcomes = [];
|
|
32617
|
+
const service = createRunService();
|
|
32618
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32619
|
+
for (const candidate of candidates) {
|
|
32620
|
+
if (!candidate.exists) continue;
|
|
32621
|
+
if (seen.has(candidate.worktreePath)) continue;
|
|
32622
|
+
seen.add(candidate.worktreePath);
|
|
32623
|
+
if (dryRun) {
|
|
32624
|
+
outcomes.push({ path: candidate.worktreePath, agentId: candidate.agentId, deleted: false });
|
|
32625
|
+
continue;
|
|
32626
|
+
}
|
|
32627
|
+
try {
|
|
32628
|
+
const result = await service.teardownEnvironment({
|
|
32629
|
+
repoPath,
|
|
32630
|
+
agentId: candidate.agentId,
|
|
32631
|
+
capture: false,
|
|
32632
|
+
trigger: "cli"
|
|
32633
|
+
});
|
|
32634
|
+
if (result.code !== 0) {
|
|
32635
|
+
if (fs40.existsSync(candidate.worktreePath)) {
|
|
32636
|
+
fs40.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
32637
|
+
}
|
|
32638
|
+
}
|
|
32639
|
+
outcomes.push({
|
|
32640
|
+
path: candidate.worktreePath,
|
|
32641
|
+
agentId: candidate.agentId,
|
|
32642
|
+
deleted: !fs40.existsSync(candidate.worktreePath),
|
|
32643
|
+
error: result.code !== 0 && fs40.existsSync(candidate.worktreePath) ? result.stderr : void 0
|
|
32644
|
+
});
|
|
32645
|
+
} catch (err) {
|
|
32646
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32647
|
+
try {
|
|
32648
|
+
if (fs40.existsSync(candidate.worktreePath)) {
|
|
32649
|
+
fs40.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
32650
|
+
}
|
|
32651
|
+
} catch {
|
|
32652
|
+
}
|
|
32653
|
+
outcomes.push({
|
|
32654
|
+
path: candidate.worktreePath,
|
|
32655
|
+
agentId: candidate.agentId,
|
|
32656
|
+
deleted: !fs40.existsSync(candidate.worktreePath),
|
|
32657
|
+
error: message
|
|
32658
|
+
});
|
|
32659
|
+
}
|
|
32660
|
+
}
|
|
32661
|
+
return outcomes;
|
|
32662
|
+
}
|
|
32663
|
+
async function unregisterRepoWithControl(options) {
|
|
32664
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
32665
|
+
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
32666
|
+
const deleteWorktrees = options.deleteWorktrees === true;
|
|
32667
|
+
const candidates = listUnregisterWorktreeCandidates(repoPath);
|
|
32668
|
+
let id = null;
|
|
32669
|
+
let apiUnreachable = false;
|
|
32670
|
+
let apiWorktrees = [];
|
|
32671
|
+
if (!options.dryRun) {
|
|
32672
|
+
try {
|
|
32673
|
+
const listResponse = await fetch(`${apiUrl}/api/repos`, {
|
|
32674
|
+
signal: AbortSignal.timeout(5e3)
|
|
32675
|
+
});
|
|
32676
|
+
if (listResponse.ok) {
|
|
32677
|
+
const repos = await listResponse.json();
|
|
32678
|
+
id = repos.find((r2) => canonicalizeControlRepoPath(r2.path) === repoPath)?.id ?? null;
|
|
32679
|
+
} else {
|
|
32680
|
+
apiUnreachable = true;
|
|
32681
|
+
}
|
|
32682
|
+
} catch {
|
|
32683
|
+
apiUnreachable = true;
|
|
32684
|
+
}
|
|
32685
|
+
}
|
|
32686
|
+
if (id && !options.dryRun) {
|
|
32687
|
+
const deleted = await deleteJson(`${apiUrl}/api/repos/${id}`, { deleteWorktrees: false }, options.dryRun);
|
|
32688
|
+
if (deleted && !deleted.ok && deleted.status !== 404) {
|
|
32689
|
+
throw new Error(`Control API ${deleted.status}: ${deleted.text}`);
|
|
32690
|
+
}
|
|
32691
|
+
if (deleted?.ok) {
|
|
32692
|
+
apiWorktrees = deleted.data.worktrees ?? [];
|
|
32693
|
+
}
|
|
32694
|
+
} else if (!id && !options.dryRun && !apiUnreachable) {
|
|
32695
|
+
}
|
|
32696
|
+
const removedFromRegistry = options.dryRun ? false : removeRegisteredRepo(repoPath);
|
|
32697
|
+
let worktrees = apiWorktrees;
|
|
32698
|
+
if (deleteWorktrees) {
|
|
32699
|
+
worktrees = await teardownWorktrees(repoPath, candidates, options.dryRun);
|
|
32700
|
+
} else {
|
|
32701
|
+
worktrees = candidates.filter((c2) => c2.exists).map((c2) => ({ path: c2.worktreePath, agentId: c2.agentId, deleted: false }));
|
|
32702
|
+
}
|
|
32703
|
+
return {
|
|
32704
|
+
ok: true,
|
|
32705
|
+
id,
|
|
32706
|
+
path: repoPath,
|
|
32707
|
+
removedFromRegistry,
|
|
32708
|
+
deleteWorktrees,
|
|
32709
|
+
worktrees,
|
|
32710
|
+
apiUnreachable: apiUnreachable || void 0
|
|
32711
|
+
};
|
|
32712
|
+
}
|
|
32713
|
+
|
|
32714
|
+
// src/cli/commands/control.ts
|
|
32312
32715
|
var controlCommand = {
|
|
32313
32716
|
command: "control <subcommand>",
|
|
32314
32717
|
describe: "Mission Control dashboard (local)",
|
|
@@ -32330,8 +32733,26 @@ var controlCommand = {
|
|
|
32330
32733
|
).command(
|
|
32331
32734
|
"register",
|
|
32332
32735
|
"Register a repository with Mission Control and sync runs + slots",
|
|
32333
|
-
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }),
|
|
32736
|
+
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }).option("force", {
|
|
32737
|
+
type: "boolean",
|
|
32738
|
+
default: false,
|
|
32739
|
+
describe: "Re-register even if previously unregistered"
|
|
32740
|
+
}),
|
|
32334
32741
|
handleRegister
|
|
32742
|
+
).command(
|
|
32743
|
+
"unregister",
|
|
32744
|
+
"Remove a repository from Mission Control (optionally delete session worktrees)",
|
|
32745
|
+
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }).option("yes", {
|
|
32746
|
+
alias: "y",
|
|
32747
|
+
type: "boolean",
|
|
32748
|
+
default: false,
|
|
32749
|
+
describe: "Skip confirmation prompts"
|
|
32750
|
+
}).option("delete-worktrees", {
|
|
32751
|
+
type: "boolean",
|
|
32752
|
+
default: false,
|
|
32753
|
+
describe: "Delete session worktrees for this repo (prompted interactively when omitted)"
|
|
32754
|
+
}).option("json", { type: "boolean", default: false }),
|
|
32755
|
+
handleUnregister
|
|
32335
32756
|
).command(
|
|
32336
32757
|
"sync",
|
|
32337
32758
|
"Sync runs and slot status to Mission Control",
|
|
@@ -32351,7 +32772,10 @@ var controlCommand = {
|
|
|
32351
32772
|
"Configure HAR Cloud API credentials (hosted sync)",
|
|
32352
32773
|
(y2) => y2.option("api-key", { type: "string", describe: "HAR Cloud API key" }),
|
|
32353
32774
|
handleLogin
|
|
32354
|
-
).demandCommand(
|
|
32775
|
+
).demandCommand(
|
|
32776
|
+
1,
|
|
32777
|
+
"Please specify a subcommand: up, down, register, unregister, sync, watch, login"
|
|
32778
|
+
),
|
|
32355
32779
|
handler: () => {
|
|
32356
32780
|
}
|
|
32357
32781
|
};
|
|
@@ -32411,14 +32835,18 @@ async function handleDown() {
|
|
|
32411
32835
|
process.exit(code);
|
|
32412
32836
|
}
|
|
32413
32837
|
async function handleRegister(argv) {
|
|
32414
|
-
const repoPath =
|
|
32838
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32415
32839
|
header("har control register");
|
|
32416
32840
|
info(`Repository: ${repoPath}`);
|
|
32417
32841
|
try {
|
|
32842
|
+
if (!argv.dryRun) {
|
|
32843
|
+
recordRepoForControlSync(repoPath);
|
|
32844
|
+
}
|
|
32418
32845
|
await syncRepoWithControl({
|
|
32419
32846
|
repoPath,
|
|
32420
32847
|
apiUrl: argv.apiUrl,
|
|
32421
|
-
dryRun: argv.dryRun
|
|
32848
|
+
dryRun: argv.dryRun,
|
|
32849
|
+
force: true
|
|
32422
32850
|
});
|
|
32423
32851
|
if (argv.dryRun) {
|
|
32424
32852
|
info("Dry run \u2014 no API call made");
|
|
@@ -32430,8 +32858,77 @@ async function handleRegister(argv) {
|
|
|
32430
32858
|
process.exit(1);
|
|
32431
32859
|
}
|
|
32432
32860
|
}
|
|
32861
|
+
async function handleUnregister(argv) {
|
|
32862
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32863
|
+
const worktrees = listUnregisterWorktreeCandidates(repoPath);
|
|
32864
|
+
try {
|
|
32865
|
+
let deleteWorktrees = argv.deleteWorktrees;
|
|
32866
|
+
if (!argv.yes && !argv.dryRun) {
|
|
32867
|
+
const confirmation = await confirmUnregister({ yes: false, worktrees });
|
|
32868
|
+
if (!confirmation.proceed) {
|
|
32869
|
+
error("Aborted \u2014 repository left registered.");
|
|
32870
|
+
process.exit(2);
|
|
32871
|
+
}
|
|
32872
|
+
deleteWorktrees = argv.deleteWorktrees || confirmation.deleteWorktrees;
|
|
32873
|
+
} else if (argv.yes && !argv.deleteWorktrees && worktrees.some((w2) => w2.exists)) {
|
|
32874
|
+
deleteWorktrees = false;
|
|
32875
|
+
}
|
|
32876
|
+
const result = await unregisterRepoWithControl({
|
|
32877
|
+
repoPath,
|
|
32878
|
+
apiUrl: argv.apiUrl,
|
|
32879
|
+
dryRun: argv.dryRun,
|
|
32880
|
+
deleteWorktrees
|
|
32881
|
+
});
|
|
32882
|
+
if (argv.json) {
|
|
32883
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
32884
|
+
return;
|
|
32885
|
+
}
|
|
32886
|
+
header("har control unregister");
|
|
32887
|
+
info(`Repository: ${result.path}`);
|
|
32888
|
+
if (argv.dryRun) {
|
|
32889
|
+
info("Dry run \u2014 no changes made");
|
|
32890
|
+
if (worktrees.filter((w2) => w2.exists).length > 0) {
|
|
32891
|
+
info(
|
|
32892
|
+
`Would propose deleting ${worktrees.filter((w2) => w2.exists).length} session worktree(s)`
|
|
32893
|
+
);
|
|
32894
|
+
}
|
|
32895
|
+
return;
|
|
32896
|
+
}
|
|
32897
|
+
if (result.apiUnreachable) {
|
|
32898
|
+
warn("Mission Control API unreachable \u2014 removed from local registry only");
|
|
32899
|
+
} else if (result.id) {
|
|
32900
|
+
success("Removed from Mission Control");
|
|
32901
|
+
} else {
|
|
32902
|
+
info("Repository was not present in Mission Control");
|
|
32903
|
+
}
|
|
32904
|
+
if (result.removedFromRegistry) {
|
|
32905
|
+
success("Removed from local sync registry (~/.har/repos.json)");
|
|
32906
|
+
}
|
|
32907
|
+
if (result.deleteWorktrees) {
|
|
32908
|
+
const deleted = result.worktrees.filter((w2) => w2.deleted).length;
|
|
32909
|
+
const failed = result.worktrees.filter((w2) => !w2.deleted && w2.error);
|
|
32910
|
+
success(`Deleted ${deleted} session worktree(s)`);
|
|
32911
|
+
for (const failure of failed) {
|
|
32912
|
+
warn(`Failed to delete ${failure.path}: ${failure.error}`);
|
|
32913
|
+
}
|
|
32914
|
+
} else if (result.worktrees.length > 0) {
|
|
32915
|
+
info(
|
|
32916
|
+
`Left ${result.worktrees.length} session worktree(s) on disk (rerun with --delete-worktrees to remove)`
|
|
32917
|
+
);
|
|
32918
|
+
}
|
|
32919
|
+
} catch (err) {
|
|
32920
|
+
if (argv.json) {
|
|
32921
|
+
process.stdout.write(
|
|
32922
|
+
JSON.stringify({ ok: false, error: err.message }, null, 2) + "\n"
|
|
32923
|
+
);
|
|
32924
|
+
process.exit(1);
|
|
32925
|
+
}
|
|
32926
|
+
error(err.message);
|
|
32927
|
+
process.exit(1);
|
|
32928
|
+
}
|
|
32929
|
+
}
|
|
32433
32930
|
async function handleSync(argv) {
|
|
32434
|
-
const repoPath =
|
|
32931
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32435
32932
|
try {
|
|
32436
32933
|
await syncRepoWithControl({
|
|
32437
32934
|
repoPath,
|
|
@@ -32470,7 +32967,7 @@ async function handleWatch(argv) {
|
|
|
32470
32967
|
};
|
|
32471
32968
|
const tick = async () => {
|
|
32472
32969
|
if (argv.repo) {
|
|
32473
|
-
await syncOne(
|
|
32970
|
+
await syncOne(path45.resolve(argv.repo));
|
|
32474
32971
|
return;
|
|
32475
32972
|
}
|
|
32476
32973
|
try {
|
|
@@ -32501,12 +32998,12 @@ async function handleLogin(argv) {
|
|
|
32501
32998
|
}
|
|
32502
32999
|
|
|
32503
33000
|
// src/cli/commands/hooks.ts
|
|
32504
|
-
var
|
|
33001
|
+
var path48 = __toESM(require("path"));
|
|
32505
33002
|
|
|
32506
33003
|
// src/core/hooks.ts
|
|
32507
|
-
var
|
|
33004
|
+
var fs41 = __toESM(require("fs"));
|
|
32508
33005
|
var os13 = __toESM(require("os"));
|
|
32509
|
-
var
|
|
33006
|
+
var path46 = __toESM(require("path"));
|
|
32510
33007
|
var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
|
|
32511
33008
|
var MARKER_END = "# <<< har commit gate <<<";
|
|
32512
33009
|
var PRE_COMMIT_BLOCK = [
|
|
@@ -32521,27 +33018,27 @@ var POST_COMMIT_BLOCK = [
|
|
|
32521
33018
|
'"$(git rev-parse --git-path hooks)/har-post-commit" || true',
|
|
32522
33019
|
MARKER_END
|
|
32523
33020
|
].join("\n");
|
|
32524
|
-
function
|
|
33021
|
+
function tryGit3(cwd, args) {
|
|
32525
33022
|
const result = run(`git ${args}`, { cwd });
|
|
32526
33023
|
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
32527
33024
|
}
|
|
32528
33025
|
function resolveCheckoutRoot(cwd) {
|
|
32529
|
-
return
|
|
33026
|
+
return tryGit3(cwd, "rev-parse --show-toplevel");
|
|
32530
33027
|
}
|
|
32531
33028
|
function resolveHooksDir(checkoutDir) {
|
|
32532
|
-
const hooksPath =
|
|
33029
|
+
const hooksPath = tryGit3(checkoutDir, "rev-parse --git-path hooks");
|
|
32533
33030
|
if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
|
|
32534
|
-
return
|
|
33031
|
+
return path46.resolve(checkoutDir, hooksPath);
|
|
32535
33032
|
}
|
|
32536
33033
|
function getConfiguredHooksPath(checkoutDir) {
|
|
32537
|
-
return
|
|
33034
|
+
return tryGit3(checkoutDir, "config core.hooksPath") || void 0;
|
|
32538
33035
|
}
|
|
32539
33036
|
function defaultHarInvocation() {
|
|
32540
33037
|
const entry = process.argv[1];
|
|
32541
33038
|
if (entry && entry.endsWith(".js")) {
|
|
32542
|
-
return `"${process.execPath}" "${
|
|
33039
|
+
return `"${process.execPath}" "${path46.resolve(entry)}"`;
|
|
32543
33040
|
}
|
|
32544
|
-
if (entry) return `"${
|
|
33041
|
+
if (entry) return `"${path46.resolve(entry)}"`;
|
|
32545
33042
|
return "har";
|
|
32546
33043
|
}
|
|
32547
33044
|
function buildHookScript(harInvocation, subcommand, failOpenNotice) {
|
|
@@ -32562,40 +33059,40 @@ exit 0
|
|
|
32562
33059
|
`;
|
|
32563
33060
|
}
|
|
32564
33061
|
function upsertMarkedBlock(filePath, block) {
|
|
32565
|
-
if (!
|
|
32566
|
-
|
|
33062
|
+
if (!fs41.existsSync(filePath)) {
|
|
33063
|
+
fs41.writeFileSync(filePath, `#!/bin/sh
|
|
32567
33064
|
${block}
|
|
32568
33065
|
`, { mode: 493 });
|
|
32569
33066
|
return "created";
|
|
32570
33067
|
}
|
|
32571
|
-
const content =
|
|
33068
|
+
const content = fs41.readFileSync(filePath, "utf8");
|
|
32572
33069
|
if (content.includes(MARKER_START)) {
|
|
32573
33070
|
const pattern = new RegExp(
|
|
32574
33071
|
`${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
|
|
32575
33072
|
);
|
|
32576
|
-
|
|
32577
|
-
|
|
33073
|
+
fs41.writeFileSync(filePath, content.replace(pattern, block));
|
|
33074
|
+
fs41.chmodSync(filePath, 493);
|
|
32578
33075
|
return "updated";
|
|
32579
33076
|
}
|
|
32580
33077
|
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
32581
|
-
|
|
33078
|
+
fs41.writeFileSync(filePath, `${content}${suffix}
|
|
32582
33079
|
${block}
|
|
32583
33080
|
`);
|
|
32584
|
-
|
|
33081
|
+
fs41.chmodSync(filePath, 493);
|
|
32585
33082
|
return "appended";
|
|
32586
33083
|
}
|
|
32587
33084
|
function removeMarkedBlock(filePath) {
|
|
32588
|
-
if (!
|
|
32589
|
-
const content =
|
|
33085
|
+
if (!fs41.existsSync(filePath)) return false;
|
|
33086
|
+
const content = fs41.readFileSync(filePath, "utf8");
|
|
32590
33087
|
if (!content.includes(MARKER_START)) return false;
|
|
32591
33088
|
const pattern = new RegExp(
|
|
32592
33089
|
`\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
|
|
32593
33090
|
);
|
|
32594
33091
|
const stripped = content.replace(pattern, "\n");
|
|
32595
33092
|
if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
|
|
32596
|
-
|
|
33093
|
+
fs41.rmSync(filePath);
|
|
32597
33094
|
} else {
|
|
32598
|
-
|
|
33095
|
+
fs41.writeFileSync(filePath, stripped);
|
|
32599
33096
|
}
|
|
32600
33097
|
return true;
|
|
32601
33098
|
}
|
|
@@ -32617,20 +33114,20 @@ Or re-run with --force to write into that directory anyway.`
|
|
|
32617
33114
|
);
|
|
32618
33115
|
}
|
|
32619
33116
|
const hooksDir = resolveHooksDir(checkout);
|
|
32620
|
-
|
|
33117
|
+
fs41.mkdirSync(hooksDir, { recursive: true });
|
|
32621
33118
|
const invocation = options.harInvocation ?? defaultHarInvocation();
|
|
32622
|
-
|
|
32623
|
-
|
|
33119
|
+
fs41.writeFileSync(
|
|
33120
|
+
path46.join(hooksDir, "har-pre-commit"),
|
|
32624
33121
|
buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
|
|
32625
33122
|
{ mode: 493 }
|
|
32626
33123
|
);
|
|
32627
|
-
|
|
32628
|
-
|
|
33124
|
+
fs41.writeFileSync(
|
|
33125
|
+
path46.join(hooksDir, "har-post-commit"),
|
|
32629
33126
|
buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
|
|
32630
33127
|
{ mode: 493 }
|
|
32631
33128
|
);
|
|
32632
|
-
const preCommit = upsertMarkedBlock(
|
|
32633
|
-
const postCommit = upsertMarkedBlock(
|
|
33129
|
+
const preCommit = upsertMarkedBlock(path46.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
|
|
33130
|
+
const postCommit = upsertMarkedBlock(path46.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
|
|
32634
33131
|
ensureValidationsIgnored(checkout);
|
|
32635
33132
|
return { hooksDir, preCommit, postCommit };
|
|
32636
33133
|
}
|
|
@@ -32639,12 +33136,12 @@ function uninstallHooks(repoPath) {
|
|
|
32639
33136
|
if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
|
|
32640
33137
|
const hooksDir = resolveHooksDir(checkout);
|
|
32641
33138
|
let removed = false;
|
|
32642
|
-
removed = removeMarkedBlock(
|
|
32643
|
-
removed = removeMarkedBlock(
|
|
33139
|
+
removed = removeMarkedBlock(path46.join(hooksDir, "pre-commit")) || removed;
|
|
33140
|
+
removed = removeMarkedBlock(path46.join(hooksDir, "post-commit")) || removed;
|
|
32644
33141
|
for (const file of ["har-pre-commit", "har-post-commit"]) {
|
|
32645
|
-
const full =
|
|
32646
|
-
if (
|
|
32647
|
-
|
|
33142
|
+
const full = path46.join(hooksDir, file);
|
|
33143
|
+
if (fs41.existsSync(full)) {
|
|
33144
|
+
fs41.rmSync(full);
|
|
32648
33145
|
removed = true;
|
|
32649
33146
|
}
|
|
32650
33147
|
}
|
|
@@ -32661,9 +33158,9 @@ function getCommitGateConfig(checkoutDir) {
|
|
|
32661
33158
|
function isAgentWorktree(checkoutDir) {
|
|
32662
33159
|
const branch = getCurrentBranch(checkoutDir);
|
|
32663
33160
|
if (branch && /^har-agent-\d+$/.test(branch)) return true;
|
|
32664
|
-
const worktreesRoot =
|
|
32665
|
-
const resolved =
|
|
32666
|
-
return resolved.startsWith(`${worktreesRoot}${
|
|
33161
|
+
const worktreesRoot = path46.join(os13.homedir(), "worktrees");
|
|
33162
|
+
const resolved = path46.resolve(checkoutDir);
|
|
33163
|
+
return resolved.startsWith(`${worktreesRoot}${path46.sep}`) && /-agent-\d+$/.test(path46.basename(resolved));
|
|
32667
33164
|
}
|
|
32668
33165
|
function resolveEffectiveMode(checkoutDir, gate) {
|
|
32669
33166
|
if (!gate.enabled) return "off";
|
|
@@ -32676,15 +33173,15 @@ function getHooksStatus(repoPath) {
|
|
|
32676
33173
|
const hooksDir = resolveHooksDir(checkout);
|
|
32677
33174
|
const gate = getCommitGateConfig(checkout);
|
|
32678
33175
|
const hasBlock = (name) => {
|
|
32679
|
-
const file =
|
|
32680
|
-
return
|
|
33176
|
+
const file = path46.join(hooksDir, name);
|
|
33177
|
+
return fs41.existsSync(file) && fs41.readFileSync(file, "utf8").includes(MARKER_START);
|
|
32681
33178
|
};
|
|
32682
33179
|
return {
|
|
32683
33180
|
checkout,
|
|
32684
33181
|
hooksDir,
|
|
32685
33182
|
configuredHooksPath: getConfiguredHooksPath(checkout),
|
|
32686
|
-
preCommitInstalled: hasBlock("pre-commit") &&
|
|
32687
|
-
postCommitInstalled: hasBlock("post-commit") &&
|
|
33183
|
+
preCommitInstalled: hasBlock("pre-commit") && fs41.existsSync(path46.join(hooksDir, "har-pre-commit")),
|
|
33184
|
+
postCommitInstalled: hasBlock("post-commit") && fs41.existsSync(path46.join(hooksDir, "har-post-commit")),
|
|
32688
33185
|
gate,
|
|
32689
33186
|
effectiveMode: resolveEffectiveMode(checkout, gate)
|
|
32690
33187
|
};
|
|
@@ -32695,7 +33192,7 @@ function checkCommitGate(cwd) {
|
|
|
32695
33192
|
}
|
|
32696
33193
|
const checkout = resolveCheckoutRoot(cwd);
|
|
32697
33194
|
if (!checkout) return { exitCode: 0, messages: [] };
|
|
32698
|
-
if (!
|
|
33195
|
+
if (!fs41.existsSync(path46.join(checkout, ".har", "stages.json"))) {
|
|
32699
33196
|
return { exitCode: 0, messages: [] };
|
|
32700
33197
|
}
|
|
32701
33198
|
const gate = getCommitGateConfig(checkout);
|
|
@@ -32752,7 +33249,7 @@ async function recordCommitAssociation(cwd) {
|
|
|
32752
33249
|
const checkout = resolveCheckoutRoot(cwd);
|
|
32753
33250
|
if (!checkout) return { attached: false };
|
|
32754
33251
|
const commitSha = getHeadSha(checkout);
|
|
32755
|
-
const tree =
|
|
33252
|
+
const tree = tryGit3(checkout, "rev-parse HEAD^{tree}");
|
|
32756
33253
|
if (!commitSha || !tree) return { attached: false };
|
|
32757
33254
|
const updated = attachCommit(checkout, tree, commitSha);
|
|
32758
33255
|
if (updated) {
|
|
@@ -32766,8 +33263,8 @@ async function recordCommitAssociation(cwd) {
|
|
|
32766
33263
|
}
|
|
32767
33264
|
|
|
32768
33265
|
// src/core/claude-hooks.ts
|
|
32769
|
-
var
|
|
32770
|
-
var
|
|
33266
|
+
var fs42 = __toESM(require("fs"));
|
|
33267
|
+
var path47 = __toESM(require("path"));
|
|
32771
33268
|
var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
|
|
32772
33269
|
var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
|
|
32773
33270
|
var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
|
|
@@ -32778,8 +33275,8 @@ function isHarGuardEntry(entry) {
|
|
|
32778
33275
|
);
|
|
32779
33276
|
}
|
|
32780
33277
|
function readSettings(settingsPath) {
|
|
32781
|
-
if (!
|
|
32782
|
-
const raw =
|
|
33278
|
+
if (!fs42.existsSync(settingsPath)) return {};
|
|
33279
|
+
const raw = fs42.readFileSync(settingsPath, "utf8").trim();
|
|
32783
33280
|
if (raw === "") return {};
|
|
32784
33281
|
const parsed = JSON.parse(raw);
|
|
32785
33282
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -32788,19 +33285,19 @@ function readSettings(settingsPath) {
|
|
|
32788
33285
|
return parsed;
|
|
32789
33286
|
}
|
|
32790
33287
|
function writeSettings(settingsPath, settings) {
|
|
32791
|
-
|
|
32792
|
-
|
|
33288
|
+
fs42.mkdirSync(path47.dirname(settingsPath), { recursive: true });
|
|
33289
|
+
fs42.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
32793
33290
|
}
|
|
32794
33291
|
function installClaudeGuard(repoPath) {
|
|
32795
33292
|
const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
|
|
32796
33293
|
if (!templatePath) {
|
|
32797
33294
|
throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
|
|
32798
33295
|
}
|
|
32799
|
-
const guardScript =
|
|
32800
|
-
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
const settingsPath =
|
|
33296
|
+
const guardScript = path47.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
33297
|
+
fs42.mkdirSync(path47.dirname(guardScript), { recursive: true });
|
|
33298
|
+
fs42.copyFileSync(templatePath, guardScript);
|
|
33299
|
+
fs42.chmodSync(guardScript, 493);
|
|
33300
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
32804
33301
|
const settings = readSettings(settingsPath);
|
|
32805
33302
|
const hooks = settings.hooks ?? {};
|
|
32806
33303
|
const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
|
|
@@ -32814,15 +33311,15 @@ function installClaudeGuard(repoPath) {
|
|
|
32814
33311
|
}
|
|
32815
33312
|
function uninstallClaudeGuard(repoPath) {
|
|
32816
33313
|
let removed = false;
|
|
32817
|
-
const guardScript =
|
|
32818
|
-
if (
|
|
32819
|
-
|
|
33314
|
+
const guardScript = path47.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
33315
|
+
if (fs42.existsSync(guardScript)) {
|
|
33316
|
+
fs42.rmSync(guardScript);
|
|
32820
33317
|
removed = true;
|
|
32821
|
-
const parent =
|
|
32822
|
-
if (
|
|
33318
|
+
const parent = path47.dirname(guardScript);
|
|
33319
|
+
if (fs42.existsSync(parent) && fs42.readdirSync(parent).length === 0) fs42.rmdirSync(parent);
|
|
32823
33320
|
}
|
|
32824
|
-
const settingsPath =
|
|
32825
|
-
if (
|
|
33321
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
33322
|
+
if (fs42.existsSync(settingsPath)) {
|
|
32826
33323
|
const settings = readSettings(settingsPath);
|
|
32827
33324
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
32828
33325
|
if (preToolUse) {
|
|
@@ -32847,8 +33344,8 @@ function uninstallClaudeGuard(repoPath) {
|
|
|
32847
33344
|
return { removed };
|
|
32848
33345
|
}
|
|
32849
33346
|
function claudeGuardInstalled(repoPath) {
|
|
32850
|
-
const settingsPath =
|
|
32851
|
-
if (!
|
|
33347
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
33348
|
+
if (!fs42.existsSync(settingsPath)) return false;
|
|
32852
33349
|
try {
|
|
32853
33350
|
const settings = readSettings(settingsPath);
|
|
32854
33351
|
return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
|
|
@@ -32864,7 +33361,7 @@ function repoOption(y2) {
|
|
|
32864
33361
|
function handleInstall2(argv) {
|
|
32865
33362
|
try {
|
|
32866
33363
|
if (argv.claude) {
|
|
32867
|
-
const result2 = installClaudeGuard(
|
|
33364
|
+
const result2 = installClaudeGuard(path48.resolve(argv.repo));
|
|
32868
33365
|
success("Claude Code worktree guard installed.");
|
|
32869
33366
|
info(`Guard script: ${result2.guardScript}`);
|
|
32870
33367
|
info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
|
|
@@ -32872,7 +33369,7 @@ function handleInstall2(argv) {
|
|
|
32872
33369
|
info("Commit both files so the guard travels with the repo.");
|
|
32873
33370
|
return;
|
|
32874
33371
|
}
|
|
32875
|
-
const result = installHooks({ repoPath:
|
|
33372
|
+
const result = installHooks({ repoPath: path48.resolve(argv.repo), force: argv.force });
|
|
32876
33373
|
success(`Commit gate installed in ${result.hooksDir}`);
|
|
32877
33374
|
info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
|
|
32878
33375
|
info("Commits of unverified change batches will be blocked in agent worktrees.");
|
|
@@ -32885,7 +33382,7 @@ function handleInstall2(argv) {
|
|
|
32885
33382
|
function handleUninstall(argv) {
|
|
32886
33383
|
try {
|
|
32887
33384
|
if (argv.claude) {
|
|
32888
|
-
const result2 = uninstallClaudeGuard(
|
|
33385
|
+
const result2 = uninstallClaudeGuard(path48.resolve(argv.repo));
|
|
32889
33386
|
if (result2.removed) {
|
|
32890
33387
|
success("Claude Code worktree guard removed.");
|
|
32891
33388
|
} else {
|
|
@@ -32893,7 +33390,7 @@ function handleUninstall(argv) {
|
|
|
32893
33390
|
}
|
|
32894
33391
|
return;
|
|
32895
33392
|
}
|
|
32896
|
-
const result = uninstallHooks(
|
|
33393
|
+
const result = uninstallHooks(path48.resolve(argv.repo));
|
|
32897
33394
|
if (result.removed) {
|
|
32898
33395
|
success(`Commit gate removed from ${result.hooksDir}`);
|
|
32899
33396
|
} else {
|
|
@@ -32906,7 +33403,7 @@ function handleUninstall(argv) {
|
|
|
32906
33403
|
}
|
|
32907
33404
|
function handleStatus2(argv) {
|
|
32908
33405
|
try {
|
|
32909
|
-
const status = getHooksStatus(
|
|
33406
|
+
const status = getHooksStatus(path48.resolve(argv.repo));
|
|
32910
33407
|
if (argv.json) {
|
|
32911
33408
|
process.stdout.write(`${JSON.stringify(status, null, 2)}
|
|
32912
33409
|
`);
|
|
@@ -32924,7 +33421,7 @@ function handleStatus2(argv) {
|
|
|
32924
33421
|
);
|
|
32925
33422
|
info(`Effective: ${status.effectiveMode} (in this checkout)`);
|
|
32926
33423
|
info(
|
|
32927
|
-
`Claude guard: ${claudeGuardInstalled(
|
|
33424
|
+
`Claude guard: ${claudeGuardInstalled(path48.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
|
|
32928
33425
|
);
|
|
32929
33426
|
} catch (err) {
|
|
32930
33427
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -33185,10 +33682,10 @@ function assignProp(target, prop, value) {
|
|
|
33185
33682
|
configurable: true
|
|
33186
33683
|
});
|
|
33187
33684
|
}
|
|
33188
|
-
function getElementAtPath(obj,
|
|
33189
|
-
if (!
|
|
33685
|
+
function getElementAtPath(obj, path51) {
|
|
33686
|
+
if (!path51)
|
|
33190
33687
|
return obj;
|
|
33191
|
-
return
|
|
33688
|
+
return path51.reduce((acc, key) => acc?.[key], obj);
|
|
33192
33689
|
}
|
|
33193
33690
|
function promiseAllObject(promisesObj) {
|
|
33194
33691
|
const keys = Object.keys(promisesObj);
|
|
@@ -33508,11 +34005,11 @@ function aborted(x2, startIndex = 0) {
|
|
|
33508
34005
|
}
|
|
33509
34006
|
return false;
|
|
33510
34007
|
}
|
|
33511
|
-
function prefixIssues(
|
|
34008
|
+
function prefixIssues(path51, issues) {
|
|
33512
34009
|
return issues.map((iss) => {
|
|
33513
34010
|
var _a3;
|
|
33514
34011
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
33515
|
-
iss.path.unshift(
|
|
34012
|
+
iss.path.unshift(path51);
|
|
33516
34013
|
return iss;
|
|
33517
34014
|
});
|
|
33518
34015
|
}
|
|
@@ -38835,7 +39332,7 @@ var Protocol = class {
|
|
|
38835
39332
|
return;
|
|
38836
39333
|
}
|
|
38837
39334
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
38838
|
-
await new Promise((
|
|
39335
|
+
await new Promise((resolve36) => setTimeout(resolve36, pollInterval));
|
|
38839
39336
|
options?.signal?.throwIfAborted();
|
|
38840
39337
|
}
|
|
38841
39338
|
} catch (error3) {
|
|
@@ -38852,7 +39349,7 @@ var Protocol = class {
|
|
|
38852
39349
|
*/
|
|
38853
39350
|
request(request, resultSchema, options) {
|
|
38854
39351
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
38855
|
-
return new Promise((
|
|
39352
|
+
return new Promise((resolve36, reject) => {
|
|
38856
39353
|
const earlyReject = (error3) => {
|
|
38857
39354
|
reject(error3);
|
|
38858
39355
|
};
|
|
@@ -38930,7 +39427,7 @@ var Protocol = class {
|
|
|
38930
39427
|
if (!parseResult.success) {
|
|
38931
39428
|
reject(parseResult.error);
|
|
38932
39429
|
} else {
|
|
38933
|
-
|
|
39430
|
+
resolve36(parseResult.data);
|
|
38934
39431
|
}
|
|
38935
39432
|
} catch (error3) {
|
|
38936
39433
|
reject(error3);
|
|
@@ -39191,12 +39688,12 @@ var Protocol = class {
|
|
|
39191
39688
|
}
|
|
39192
39689
|
} catch {
|
|
39193
39690
|
}
|
|
39194
|
-
return new Promise((
|
|
39691
|
+
return new Promise((resolve36, reject) => {
|
|
39195
39692
|
if (signal.aborted) {
|
|
39196
39693
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
39197
39694
|
return;
|
|
39198
39695
|
}
|
|
39199
|
-
const timeoutId = setTimeout(
|
|
39696
|
+
const timeoutId = setTimeout(resolve36, interval);
|
|
39200
39697
|
signal.addEventListener("abort", () => {
|
|
39201
39698
|
clearTimeout(timeoutId);
|
|
39202
39699
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -39986,7 +40483,7 @@ var Server = class extends Protocol {
|
|
|
39986
40483
|
};
|
|
39987
40484
|
|
|
39988
40485
|
// src/mcp/server.ts
|
|
39989
|
-
var
|
|
40486
|
+
var path49 = __toESM(require("path"));
|
|
39990
40487
|
|
|
39991
40488
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
39992
40489
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
@@ -40069,12 +40566,12 @@ var StdioServerTransport = class {
|
|
|
40069
40566
|
this.onclose?.();
|
|
40070
40567
|
}
|
|
40071
40568
|
send(message) {
|
|
40072
|
-
return new Promise((
|
|
40569
|
+
return new Promise((resolve36) => {
|
|
40073
40570
|
const json = serializeMessage(message);
|
|
40074
40571
|
if (this._stdout.write(json)) {
|
|
40075
|
-
|
|
40572
|
+
resolve36();
|
|
40076
40573
|
} else {
|
|
40077
|
-
this._stdout.once("drain",
|
|
40574
|
+
this._stdout.once("drain", resolve36);
|
|
40078
40575
|
}
|
|
40079
40576
|
});
|
|
40080
40577
|
}
|
|
@@ -40152,7 +40649,7 @@ var LaunchEnvironmentInputSchema = external_exports.object({
|
|
|
40152
40649
|
worktree: external_exports.boolean().default(true),
|
|
40153
40650
|
claude: external_exports.boolean().default(false),
|
|
40154
40651
|
confirmReplace: external_exports.boolean().default(false).describe(
|
|
40155
|
-
"
|
|
40652
|
+
"Destroy the previous session on this slot and start another from main-checkout HEAD. Does not choose main. Prefer complete/teardown when the prior task is finished. Call har_get_status first; get explicit user approval."
|
|
40156
40653
|
),
|
|
40157
40654
|
force: external_exports.boolean().default(false).describe(
|
|
40158
40655
|
"Discard uncommitted changes in a dirty occupied worktree. Requires confirmReplace=true and explicit user approval \u2014 never set autonomously."
|
|
@@ -40278,7 +40775,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40278
40775
|
},
|
|
40279
40776
|
{
|
|
40280
40777
|
name: "har_launch_environment",
|
|
40281
|
-
description: "Start a FRESH agent session. Run
|
|
40778
|
+
description: "Start a FRESH agent session from the main checkout HEAD (switch that checkout to main first for a new unrelated task). Run BEFORE editing any file: returns workDir \u2014 ALL edits go there, never the main checkout. Prefer har_complete_environment / har_teardown_environment when a prior task is done. Occupied slots block until confirmReplace=true (does NOT choose main). force=true discards dirty work \u2014 never set without user approval. Failed launches: resume=true / har_recover_environment.",
|
|
40282
40779
|
inputSchema: objectJsonSchema(
|
|
40283
40780
|
{
|
|
40284
40781
|
repo: repoJsonProperty,
|
|
@@ -40287,7 +40784,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40287
40784
|
claude: { type: "boolean" },
|
|
40288
40785
|
confirmReplace: {
|
|
40289
40786
|
type: "boolean",
|
|
40290
|
-
description: "
|
|
40787
|
+
description: "Destroy the previous session on this slot and start another from main-checkout HEAD. Does not choose main. Prefer complete/teardown when the prior task is finished. Call har_get_status first; get explicit user approval."
|
|
40291
40788
|
},
|
|
40292
40789
|
force: {
|
|
40293
40790
|
type: "boolean",
|
|
@@ -40295,7 +40792,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40295
40792
|
},
|
|
40296
40793
|
resume: {
|
|
40297
40794
|
type: "boolean",
|
|
40298
|
-
description: "Resume a failed or partial launch (status failed/starting) without
|
|
40795
|
+
description: "Resume a failed or partial launch (status failed/starting) without confirmReplace. Preserves worktree and env."
|
|
40299
40796
|
}
|
|
40300
40797
|
},
|
|
40301
40798
|
["agentId"]
|
|
@@ -40618,7 +41115,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
40618
41115
|
const input = ControlUpInputSchema.parse({ ...args, repo });
|
|
40619
41116
|
const result = await startControlAndSync({
|
|
40620
41117
|
detach: input.detach,
|
|
40621
|
-
cwd:
|
|
41118
|
+
cwd: path49.resolve(input.repo)
|
|
40622
41119
|
});
|
|
40623
41120
|
if (result.code !== 0) {
|
|
40624
41121
|
return {
|
|
@@ -40681,7 +41178,7 @@ var mcpCommand = {
|
|
|
40681
41178
|
};
|
|
40682
41179
|
|
|
40683
41180
|
// src/cli/commands/telemetry.ts
|
|
40684
|
-
var
|
|
41181
|
+
var path50 = __toESM(require("path"));
|
|
40685
41182
|
init_control_config();
|
|
40686
41183
|
init_otel_hooks();
|
|
40687
41184
|
init_telemetry_config();
|
|
@@ -40766,7 +41263,7 @@ async function handleOff() {
|
|
|
40766
41263
|
info("Mission Control will not auto-start. Existing usage rows are kept.");
|
|
40767
41264
|
}
|
|
40768
41265
|
async function handleWriteEnv(argv) {
|
|
40769
|
-
const repoPath =
|
|
41266
|
+
const repoPath = path50.resolve(argv.repo);
|
|
40770
41267
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
40771
41268
|
const session = readSlotRegistry(harnessRoot, argv.agentId);
|
|
40772
41269
|
const workDir = argv.workDir ?? session?.workDir ?? repoPath;
|
|
@@ -40778,7 +41275,7 @@ async function handleWriteEnv(argv) {
|
|
|
40778
41275
|
suffix,
|
|
40779
41276
|
createdAt: session?.createdAt
|
|
40780
41277
|
});
|
|
40781
|
-
const envFile = argv.envFile ??
|
|
41278
|
+
const envFile = argv.envFile ?? path50.join(workDir, `.env.agent.${argv.agentId}`);
|
|
40782
41279
|
const attrs = {
|
|
40783
41280
|
sessionKey,
|
|
40784
41281
|
agentId: argv.agentId,
|
|
@@ -40797,7 +41294,7 @@ async function handleWriteEnv(argv) {
|
|
|
40797
41294
|
success(`Wrote session attribution to ${envFile}`);
|
|
40798
41295
|
}
|
|
40799
41296
|
function handlePrintEnv(argv) {
|
|
40800
|
-
const repoPath =
|
|
41297
|
+
const repoPath = path50.resolve(argv.repo);
|
|
40801
41298
|
const workDir = argv.workDir ?? repoPath;
|
|
40802
41299
|
const sessionKey = buildSessionKey({
|
|
40803
41300
|
branch: argv.branch,
|
|
@@ -40910,7 +41407,7 @@ var telemetryCommand = {
|
|
|
40910
41407
|
|
|
40911
41408
|
// src/cli/index.ts
|
|
40912
41409
|
async function runCli() {
|
|
40913
|
-
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(agentsCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har env
|
|
41410
|
+
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(agentsCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
|
|
40914
41411
|
}
|
|
40915
41412
|
|
|
40916
41413
|
// src/index.ts
|