@osfactory/har 0.17.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +983 -679
- package/dist/templates/har-boilerplate/agent-slot.sh +3 -9
- package/dist/templates/har-boilerplate/launch.sh +1 -8
- package/dist/templates/har-boilerplate-cli/agent-cli.sh +0 -2
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +3 -9
- package/dist/templates/har-boilerplate-cli/launch.sh +1 -8
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +2 -5
- package/dist/templates/har-boilerplate-ios/launch.sh +1 -6
- 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 path50 = url.path;
|
|
769
|
+
if (path50.length === 0) {
|
|
770
770
|
return;
|
|
771
771
|
}
|
|
772
|
-
if (url.scheme === "file" &&
|
|
772
|
+
if (url.scheme === "file" && path50.length === 1 && isNormalizedWindowsDriveLetter(path50[0])) {
|
|
773
773
|
return;
|
|
774
774
|
}
|
|
775
|
-
|
|
775
|
+
path50.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(resolve35, 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
|
+
resolve35(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(resolve35, 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
|
+
resolve35(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
|
+
resolve35(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
|
+
resolve35(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
|
+
resolve35(response);
|
|
2981
2981
|
});
|
|
2982
2982
|
raw.on("end", function() {
|
|
2983
2983
|
if (!response) {
|
|
2984
2984
|
response = new Response3(body, response_options);
|
|
2985
|
-
|
|
2985
|
+
resolve35(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
|
+
resolve35(response);
|
|
2994
2994
|
return;
|
|
2995
2995
|
}
|
|
2996
2996
|
response = new Response3(body, response_options);
|
|
2997
|
-
|
|
2997
|
+
resolve35(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(path50, { 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: path50, size, lastModified: mtimeMs });
|
|
6362
6362
|
if (!filename) {
|
|
6363
6363
|
filename = file.name;
|
|
6364
6364
|
}
|
|
@@ -6367,13 +6367,13 @@ function createFileFromPath(path48, { 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(path50, filenameOrOptions, options = {}) {
|
|
6371
|
+
const stats = (0, import_fs5.statSync)(path50);
|
|
6372
|
+
return createFileFromPath(path50, stats, filenameOrOptions, options);
|
|
6373
6373
|
}
|
|
6374
|
-
async function fileFromPath2(
|
|
6375
|
-
const stats = await import_fs5.promises.stat(
|
|
6376
|
-
return createFileFromPath(
|
|
6374
|
+
async function fileFromPath2(path50, filenameOrOptions, options) {
|
|
6375
|
+
const stats = await import_fs5.promises.stat(path50);
|
|
6376
|
+
return createFileFromPath(path50, 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({
|
|
@@ -6433,6 +6433,412 @@ var init_fileFromPath = __esm({
|
|
|
6433
6433
|
}
|
|
6434
6434
|
});
|
|
6435
6435
|
|
|
6436
|
+
// src/core/control-config.ts
|
|
6437
|
+
function getControlApiUrl() {
|
|
6438
|
+
return process.env.HAR_CONTROL_API_URL ?? DEFAULT_CONTROL_API_URL;
|
|
6439
|
+
}
|
|
6440
|
+
function isControlEnabled() {
|
|
6441
|
+
return process.env.HAR_CONTROL_DISABLED !== "true";
|
|
6442
|
+
}
|
|
6443
|
+
var DEFAULT_CONTROL_API_URL;
|
|
6444
|
+
var init_control_config = __esm({
|
|
6445
|
+
"src/core/control-config.ts"() {
|
|
6446
|
+
"use strict";
|
|
6447
|
+
DEFAULT_CONTROL_API_URL = "http://localhost:3847";
|
|
6448
|
+
}
|
|
6449
|
+
});
|
|
6450
|
+
|
|
6451
|
+
// src/core/telemetry-config.ts
|
|
6452
|
+
function getPreferencePath() {
|
|
6453
|
+
if (process.env.HAR_TELEMETRY_CONFIG_PATH) {
|
|
6454
|
+
return path35.resolve(process.env.HAR_TELEMETRY_CONFIG_PATH);
|
|
6455
|
+
}
|
|
6456
|
+
return path35.join(os9.homedir(), ".har", "telemetry.json");
|
|
6457
|
+
}
|
|
6458
|
+
function parseEnvOverride(raw) {
|
|
6459
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
6460
|
+
const normalized = raw.trim().toLowerCase();
|
|
6461
|
+
if (["0", "false", "off", "no", "disabled"].includes(normalized)) return false;
|
|
6462
|
+
if (["1", "true", "on", "yes", "enabled"].includes(normalized)) return true;
|
|
6463
|
+
return void 0;
|
|
6464
|
+
}
|
|
6465
|
+
function normalizeSignals(enabled, raw) {
|
|
6466
|
+
if (!enabled) return { ...DEFAULT_SIGNALS_OFF };
|
|
6467
|
+
return {
|
|
6468
|
+
metrics: raw?.metrics !== false,
|
|
6469
|
+
logs: raw?.logs !== false,
|
|
6470
|
+
prompts: raw?.prompts === true,
|
|
6471
|
+
traces: raw?.traces !== false
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6474
|
+
function readTelemetryPreference() {
|
|
6475
|
+
const preferencePath = getPreferencePath();
|
|
6476
|
+
try {
|
|
6477
|
+
if (!fs35.existsSync(preferencePath)) {
|
|
6478
|
+
return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
|
|
6479
|
+
}
|
|
6480
|
+
const parsed = JSON.parse(fs35.readFileSync(preferencePath, "utf8"));
|
|
6481
|
+
const enabled = parsed.enabled !== false;
|
|
6482
|
+
return {
|
|
6483
|
+
enabled,
|
|
6484
|
+
signals: normalizeSignals(enabled, parsed.signals),
|
|
6485
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : void 0
|
|
6486
|
+
};
|
|
6487
|
+
} catch {
|
|
6488
|
+
return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
function writeTelemetryPreference(enabled, signals) {
|
|
6492
|
+
const current = readTelemetryPreference();
|
|
6493
|
+
const nextSignals = normalizeSignals(enabled, {
|
|
6494
|
+
...current.signals,
|
|
6495
|
+
...signals
|
|
6496
|
+
});
|
|
6497
|
+
const preference = {
|
|
6498
|
+
enabled,
|
|
6499
|
+
signals: enabled ? nextSignals : { ...DEFAULT_SIGNALS_OFF },
|
|
6500
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6501
|
+
};
|
|
6502
|
+
const preferencePath = getPreferencePath();
|
|
6503
|
+
fs35.mkdirSync(path35.dirname(preferencePath), { recursive: true });
|
|
6504
|
+
fs35.writeFileSync(preferencePath, JSON.stringify(preference, null, 2) + "\n");
|
|
6505
|
+
return preference;
|
|
6506
|
+
}
|
|
6507
|
+
function isTelemetryEnabled() {
|
|
6508
|
+
const envOverride = parseEnvOverride(process.env.HAR_TELEMETRY);
|
|
6509
|
+
if (envOverride !== void 0) return envOverride;
|
|
6510
|
+
return readTelemetryPreference().enabled;
|
|
6511
|
+
}
|
|
6512
|
+
function getTelemetrySignals() {
|
|
6513
|
+
if (!isTelemetryEnabled()) return { ...DEFAULT_SIGNALS_OFF };
|
|
6514
|
+
return readTelemetryPreference().signals;
|
|
6515
|
+
}
|
|
6516
|
+
function getTelemetryPreferencePath() {
|
|
6517
|
+
return getPreferencePath();
|
|
6518
|
+
}
|
|
6519
|
+
var fs35, os9, path35, DEFAULT_SIGNALS_ON, DEFAULT_SIGNALS_OFF, TELEMETRY_SIGNALS;
|
|
6520
|
+
var init_telemetry_config = __esm({
|
|
6521
|
+
"src/core/telemetry-config.ts"() {
|
|
6522
|
+
"use strict";
|
|
6523
|
+
fs35 = __toESM(require("fs"));
|
|
6524
|
+
os9 = __toESM(require("os"));
|
|
6525
|
+
path35 = __toESM(require("path"));
|
|
6526
|
+
DEFAULT_SIGNALS_ON = {
|
|
6527
|
+
metrics: true,
|
|
6528
|
+
logs: true,
|
|
6529
|
+
prompts: false,
|
|
6530
|
+
/** Hooks are traces-first; usage metrics are derived from span gen_ai.usage.* in Mission Control. */
|
|
6531
|
+
traces: true
|
|
6532
|
+
};
|
|
6533
|
+
DEFAULT_SIGNALS_OFF = {
|
|
6534
|
+
metrics: false,
|
|
6535
|
+
logs: false,
|
|
6536
|
+
prompts: false,
|
|
6537
|
+
traces: false
|
|
6538
|
+
};
|
|
6539
|
+
TELEMETRY_SIGNALS = [
|
|
6540
|
+
"Traces (default): Cursor / Claude / Codex activity via opentelemetry-hooks \u2192 Mission Control",
|
|
6541
|
+
"Logs/events (default): hook lifecycle logs without prompt bodies",
|
|
6542
|
+
"Metrics (default): token usage derived from span gen_ai.usage.* attributes",
|
|
6543
|
+
"Prompts (opt-in): user prompt text via IDE_OTEL_CAPTURE_TEXT \u2014 har telemetry on --prompts (also fills Mission Control purpose)",
|
|
6544
|
+
"Fallback: har control sync harvests local Claude/Codex session files when hooks telemetry is missing"
|
|
6545
|
+
];
|
|
6546
|
+
}
|
|
6547
|
+
});
|
|
6548
|
+
|
|
6549
|
+
// src/core/otel-hooks.ts
|
|
6550
|
+
var otel_hooks_exports = {};
|
|
6551
|
+
__export(otel_hooks_exports, {
|
|
6552
|
+
OTEL_HOOKS_AGENTS: () => OTEL_HOOKS_AGENTS,
|
|
6553
|
+
OTEL_HOOKS_PACKAGE: () => OTEL_HOOKS_PACKAGE,
|
|
6554
|
+
buildOtelHooksConfig: () => buildOtelHooksConfig,
|
|
6555
|
+
disableOtelHooksExport: () => disableOtelHooksExport,
|
|
6556
|
+
ensureOtelHooks: () => ensureOtelHooks,
|
|
6557
|
+
getOtelHooksConfigPath: () => getOtelHooksConfigPath,
|
|
6558
|
+
getOtelHooksHome: () => getOtelHooksHome,
|
|
6559
|
+
getOtelHooksWrapperPath: () => getOtelHooksWrapperPath,
|
|
6560
|
+
rewriteHookCommandsToWrapper: () => rewriteHookCommandsToWrapper,
|
|
6561
|
+
writeOtelHooksConfig: () => writeOtelHooksConfig,
|
|
6562
|
+
writeOtelHooksWrapper: () => writeOtelHooksWrapper
|
|
6563
|
+
});
|
|
6564
|
+
function getOtelHooksHome() {
|
|
6565
|
+
if (process.env.HAR_OTEL_HOOKS_HOME) {
|
|
6566
|
+
return path41.resolve(process.env.HAR_OTEL_HOOKS_HOME);
|
|
6567
|
+
}
|
|
6568
|
+
return path41.join(os12.homedir(), ".har", "otel-hooks");
|
|
6569
|
+
}
|
|
6570
|
+
function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
|
|
6571
|
+
return path41.join(hooksHome, "otel_config.json");
|
|
6572
|
+
}
|
|
6573
|
+
function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
|
|
6574
|
+
return path41.join(hooksHome, "run-otel-hook.sh");
|
|
6575
|
+
}
|
|
6576
|
+
function buildOtelHooksConfig(options) {
|
|
6577
|
+
const enabled = options?.enabled ?? isTelemetryEnabled();
|
|
6578
|
+
const signals = getTelemetrySignals();
|
|
6579
|
+
const apiUrl = (options?.apiUrl ?? getControlApiUrl()).replace(/\/$/, "");
|
|
6580
|
+
return {
|
|
6581
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: enabled ? `${apiUrl}/api/otel/v1/traces` : null,
|
|
6582
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
6583
|
+
OTEL_SERVICE_NAME: "har-ide-agent",
|
|
6584
|
+
IDE_OTEL_BATCH_ON_STOP: "true",
|
|
6585
|
+
IDE_OTEL_ENABLE_LOGS: enabled && signals.logs ? "true" : "false",
|
|
6586
|
+
IDE_OTEL_LOG_ALL_EVENTS: enabled && signals.logs ? "true" : "false",
|
|
6587
|
+
IDE_OTEL_CAPTURE_TEXT: enabled && signals.prompts ? "true" : "false",
|
|
6588
|
+
IDE_OTEL_MASK_PROMPTS: "false",
|
|
6589
|
+
OTEL_RESOURCE_ATTRIBUTES: options?.resourceAttributes ?? null
|
|
6590
|
+
};
|
|
6591
|
+
}
|
|
6592
|
+
function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
|
|
6593
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6594
|
+
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
6595
|
+
const payload = {
|
|
6596
|
+
_comment: "Managed by har telemetry \u2014 do not edit by hand; use: har telemetry on|off",
|
|
6597
|
+
...config2
|
|
6598
|
+
};
|
|
6599
|
+
fs39.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}
|
|
6600
|
+
`);
|
|
6601
|
+
return configPath;
|
|
6602
|
+
}
|
|
6603
|
+
function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
|
|
6604
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6605
|
+
const wrapperPath = getOtelHooksWrapperPath(hooksHome);
|
|
6606
|
+
const script = `#!/usr/bin/env bash
|
|
6607
|
+
# Managed by har telemetry \u2014 invokes opentelemetry-hooks with HAR config home.
|
|
6608
|
+
set -euo pipefail
|
|
6609
|
+
export IDE_OTEL_HOOK_HOME="${hooksHome}"
|
|
6610
|
+
exec "${otelHookCommand}" "$@"
|
|
6611
|
+
`;
|
|
6612
|
+
fs39.writeFileSync(wrapperPath, script, { mode: 493 });
|
|
6613
|
+
try {
|
|
6614
|
+
fs39.chmodSync(wrapperPath, 493);
|
|
6615
|
+
} catch {
|
|
6616
|
+
}
|
|
6617
|
+
return wrapperPath;
|
|
6618
|
+
}
|
|
6619
|
+
function commandExists(command2) {
|
|
6620
|
+
const result = (0, import_child_process9.spawnSync)("sh", ["-c", `command -v ${JSON.stringify(command2)}`], {
|
|
6621
|
+
encoding: "utf8"
|
|
6622
|
+
});
|
|
6623
|
+
return result.status === 0 && Boolean(result.stdout?.trim());
|
|
6624
|
+
}
|
|
6625
|
+
function resolveOtelHookCommand() {
|
|
6626
|
+
if (commandExists("otel-hook")) {
|
|
6627
|
+
const which = (0, import_child_process9.spawnSync)("sh", ["-c", "command -v otel-hook"], { encoding: "utf8" });
|
|
6628
|
+
return which.stdout?.trim() || "otel-hook";
|
|
6629
|
+
}
|
|
6630
|
+
return null;
|
|
6631
|
+
}
|
|
6632
|
+
function tryInstallPackage() {
|
|
6633
|
+
if (commandExists("uv")) {
|
|
6634
|
+
const result = (0, import_child_process9.spawnSync)(
|
|
6635
|
+
"uv",
|
|
6636
|
+
["tool", "install", "--force", OTEL_HOOKS_PACKAGE],
|
|
6637
|
+
{ encoding: "utf8", timeout: 18e4 }
|
|
6638
|
+
);
|
|
6639
|
+
if (result.status === 0) {
|
|
6640
|
+
return { ok: true, detail: `uv tool install ${OTEL_HOOKS_PACKAGE}` };
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
if (commandExists("pipx")) {
|
|
6644
|
+
const result = (0, import_child_process9.spawnSync)("pipx", ["install", "--force", OTEL_HOOKS_PACKAGE], {
|
|
6645
|
+
encoding: "utf8",
|
|
6646
|
+
timeout: 18e4
|
|
6647
|
+
});
|
|
6648
|
+
if (result.status === 0) {
|
|
6649
|
+
return { ok: true, detail: `pipx install ${OTEL_HOOKS_PACKAGE}` };
|
|
6650
|
+
}
|
|
6651
|
+
return {
|
|
6652
|
+
ok: false,
|
|
6653
|
+
detail: `pipx install failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
|
|
6654
|
+
};
|
|
6655
|
+
}
|
|
6656
|
+
if (commandExists("pip3") || commandExists("pip")) {
|
|
6657
|
+
const pip = commandExists("pip3") ? "pip3" : "pip";
|
|
6658
|
+
const result = (0, import_child_process9.spawnSync)(
|
|
6659
|
+
pip,
|
|
6660
|
+
["install", "--user", OTEL_HOOKS_PACKAGE],
|
|
6661
|
+
{ encoding: "utf8", timeout: 18e4 }
|
|
6662
|
+
);
|
|
6663
|
+
if (result.status === 0) {
|
|
6664
|
+
return { ok: true, detail: `${pip} install --user ${OTEL_HOOKS_PACKAGE}` };
|
|
6665
|
+
}
|
|
6666
|
+
return {
|
|
6667
|
+
ok: false,
|
|
6668
|
+
detail: `${pip} install failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
|
|
6669
|
+
};
|
|
6670
|
+
}
|
|
6671
|
+
return {
|
|
6672
|
+
ok: false,
|
|
6673
|
+
detail: "Neither uv, pipx, nor pip3/pip found; install Python tooling to use opentelemetry-hooks"
|
|
6674
|
+
};
|
|
6675
|
+
}
|
|
6676
|
+
function runSetupAgent(agent, otelHookCommand, hooksHome) {
|
|
6677
|
+
const result = (0, import_child_process9.spawnSync)(otelHookCommand, ["setup", "--agent", agent], {
|
|
6678
|
+
encoding: "utf8",
|
|
6679
|
+
env: {
|
|
6680
|
+
...process.env,
|
|
6681
|
+
IDE_OTEL_HOOK_HOME: hooksHome
|
|
6682
|
+
},
|
|
6683
|
+
timeout: 6e4
|
|
6684
|
+
});
|
|
6685
|
+
if (result.status === 0) {
|
|
6686
|
+
return { ok: true, detail: `setup --agent ${agent}` };
|
|
6687
|
+
}
|
|
6688
|
+
return {
|
|
6689
|
+
ok: false,
|
|
6690
|
+
detail: `setup --agent ${agent} failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
|
|
6691
|
+
};
|
|
6692
|
+
}
|
|
6693
|
+
function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
6694
|
+
if (!fs39.existsSync(filePath)) return false;
|
|
6695
|
+
let raw;
|
|
6696
|
+
try {
|
|
6697
|
+
raw = fs39.readFileSync(filePath, "utf8");
|
|
6698
|
+
} catch {
|
|
6699
|
+
return false;
|
|
6700
|
+
}
|
|
6701
|
+
let parsed;
|
|
6702
|
+
try {
|
|
6703
|
+
parsed = JSON.parse(raw);
|
|
6704
|
+
} catch {
|
|
6705
|
+
return false;
|
|
6706
|
+
}
|
|
6707
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
6708
|
+
const quoted = JSON.stringify(wrapperPath);
|
|
6709
|
+
const rewriteCommand = (command2) => {
|
|
6710
|
+
if (!command2.includes("otel-hook") && !command2.includes("otel_hook.py")) return command2;
|
|
6711
|
+
if (command2.includes(wrapperPath)) return command2;
|
|
6712
|
+
return command2.replace(
|
|
6713
|
+
/(?:^|[;&|]\s*)(?:env\s+[^=\s]+=\S+\s+)*(?:python3?\s+)?(?:\S*otel[_-]hook\S*)/,
|
|
6714
|
+
(match) => match.replace(/(?:python3?\s+)?(?:\S*otel[_-]hook\S*)/, quoted.slice(1, -1))
|
|
6715
|
+
);
|
|
6716
|
+
};
|
|
6717
|
+
const walk = (node) => {
|
|
6718
|
+
let changed = false;
|
|
6719
|
+
if (Array.isArray(node)) {
|
|
6720
|
+
for (const item of node) changed = walk(item) || changed;
|
|
6721
|
+
return changed;
|
|
6722
|
+
}
|
|
6723
|
+
if (!node || typeof node !== "object") return false;
|
|
6724
|
+
const obj = node;
|
|
6725
|
+
if (typeof obj.command === "string") {
|
|
6726
|
+
const next = rewriteCommand(obj.command);
|
|
6727
|
+
if (next !== obj.command) {
|
|
6728
|
+
obj.command = next;
|
|
6729
|
+
changed = true;
|
|
6730
|
+
}
|
|
6731
|
+
}
|
|
6732
|
+
for (const value of Object.values(obj)) changed = walk(value) || changed;
|
|
6733
|
+
return changed;
|
|
6734
|
+
};
|
|
6735
|
+
if (!walk(parsed)) return false;
|
|
6736
|
+
fs39.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
6737
|
+
`);
|
|
6738
|
+
return true;
|
|
6739
|
+
}
|
|
6740
|
+
function rewriteKnownHookRegistrations(wrapperPath) {
|
|
6741
|
+
const home = os12.homedir();
|
|
6742
|
+
const candidates = [
|
|
6743
|
+
path41.join(home, ".cursor", "hooks.json"),
|
|
6744
|
+
path41.join(home, ".claude", "settings.json"),
|
|
6745
|
+
path41.join(home, ".codex", "hooks.json")
|
|
6746
|
+
];
|
|
6747
|
+
for (const filePath of candidates) {
|
|
6748
|
+
try {
|
|
6749
|
+
rewriteHookCommandsToWrapper(filePath, wrapperPath);
|
|
6750
|
+
} catch {
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
}
|
|
6754
|
+
function ensureOtelHooks(options) {
|
|
6755
|
+
const hooksHome = getOtelHooksHome();
|
|
6756
|
+
const errors = [];
|
|
6757
|
+
const agentsSetup = [];
|
|
6758
|
+
fs39.mkdirSync(hooksHome, { recursive: true });
|
|
6759
|
+
let otelHookCommand = resolveOtelHookCommand();
|
|
6760
|
+
if (!otelHookCommand) {
|
|
6761
|
+
const installed = tryInstallPackage();
|
|
6762
|
+
if (!installed.ok) {
|
|
6763
|
+
errors.push(installed.detail);
|
|
6764
|
+
const configPath2 = writeOtelHooksConfig(
|
|
6765
|
+
buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
|
|
6766
|
+
hooksHome
|
|
6767
|
+
);
|
|
6768
|
+
return {
|
|
6769
|
+
ok: false,
|
|
6770
|
+
hooksHome,
|
|
6771
|
+
configPath: configPath2,
|
|
6772
|
+
wrapperPath: getOtelHooksWrapperPath(hooksHome),
|
|
6773
|
+
otelHookCommand: null,
|
|
6774
|
+
warning: installed.detail,
|
|
6775
|
+
agentsSetup,
|
|
6776
|
+
errors
|
|
6777
|
+
};
|
|
6778
|
+
}
|
|
6779
|
+
otelHookCommand = resolveOtelHookCommand();
|
|
6780
|
+
if (!otelHookCommand) {
|
|
6781
|
+
errors.push("opentelemetry-hooks installed but otel-hook is not on PATH");
|
|
6782
|
+
const configPath2 = writeOtelHooksConfig(
|
|
6783
|
+
buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
|
|
6784
|
+
hooksHome
|
|
6785
|
+
);
|
|
6786
|
+
return {
|
|
6787
|
+
ok: false,
|
|
6788
|
+
hooksHome,
|
|
6789
|
+
configPath: configPath2,
|
|
6790
|
+
wrapperPath: getOtelHooksWrapperPath(hooksHome),
|
|
6791
|
+
otelHookCommand: null,
|
|
6792
|
+
warning: errors[errors.length - 1],
|
|
6793
|
+
agentsSetup,
|
|
6794
|
+
errors
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
const configPath = writeOtelHooksConfig(
|
|
6799
|
+
buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
|
|
6800
|
+
hooksHome
|
|
6801
|
+
);
|
|
6802
|
+
const wrapperPath = writeOtelHooksWrapper(otelHookCommand, hooksHome);
|
|
6803
|
+
if (options?.setupAgents !== false && isTelemetryEnabled()) {
|
|
6804
|
+
for (const agent of OTEL_HOOKS_AGENTS) {
|
|
6805
|
+
const setup = runSetupAgent(agent, otelHookCommand, hooksHome);
|
|
6806
|
+
if (setup.ok) agentsSetup.push(agent);
|
|
6807
|
+
else errors.push(setup.detail);
|
|
6808
|
+
}
|
|
6809
|
+
rewriteKnownHookRegistrations(wrapperPath);
|
|
6810
|
+
}
|
|
6811
|
+
const ok = errors.length === 0;
|
|
6812
|
+
return {
|
|
6813
|
+
ok,
|
|
6814
|
+
hooksHome,
|
|
6815
|
+
configPath,
|
|
6816
|
+
wrapperPath,
|
|
6817
|
+
otelHookCommand,
|
|
6818
|
+
message: ok ? `opentelemetry-hooks ready (${agentsSetup.join(", ") || "config only"}) \u2192 ${configPath}` : void 0,
|
|
6819
|
+
warning: errors.length ? errors.join("; ") : void 0,
|
|
6820
|
+
agentsSetup,
|
|
6821
|
+
errors
|
|
6822
|
+
};
|
|
6823
|
+
}
|
|
6824
|
+
function disableOtelHooksExport() {
|
|
6825
|
+
return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
|
|
6826
|
+
}
|
|
6827
|
+
var import_child_process9, fs39, os12, path41, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_AGENTS;
|
|
6828
|
+
var init_otel_hooks = __esm({
|
|
6829
|
+
"src/core/otel-hooks.ts"() {
|
|
6830
|
+
"use strict";
|
|
6831
|
+
import_child_process9 = require("child_process");
|
|
6832
|
+
fs39 = __toESM(require("fs"));
|
|
6833
|
+
os12 = __toESM(require("os"));
|
|
6834
|
+
path41 = __toESM(require("path"));
|
|
6835
|
+
init_control_config();
|
|
6836
|
+
init_telemetry_config();
|
|
6837
|
+
OTEL_HOOKS_PACKAGE = "opentelemetry-hooks==0.14.0";
|
|
6838
|
+
OTEL_HOOKS_AGENTS = ["cursor", "claude", "codex"];
|
|
6839
|
+
}
|
|
6840
|
+
});
|
|
6841
|
+
|
|
6436
6842
|
// node_modules/ajv/dist/compile/codegen/code.js
|
|
6437
6843
|
var require_code = __commonJS({
|
|
6438
6844
|
"node_modules/ajv/dist/compile/codegen/code.js"(exports2) {
|
|
@@ -9384,7 +9790,7 @@ var require_compile = __commonJS({
|
|
|
9384
9790
|
const schOrFunc = root.refs[ref];
|
|
9385
9791
|
if (schOrFunc)
|
|
9386
9792
|
return schOrFunc;
|
|
9387
|
-
let _sch =
|
|
9793
|
+
let _sch = resolve35.call(this, root, ref);
|
|
9388
9794
|
if (_sch === void 0) {
|
|
9389
9795
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
9390
9796
|
const { schemaId } = this.opts;
|
|
@@ -9411,7 +9817,7 @@ var require_compile = __commonJS({
|
|
|
9411
9817
|
function sameSchemaEnv(s1, s2) {
|
|
9412
9818
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
9413
9819
|
}
|
|
9414
|
-
function
|
|
9820
|
+
function resolve35(root, ref) {
|
|
9415
9821
|
let sch;
|
|
9416
9822
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
9417
9823
|
ref = sch;
|
|
@@ -9629,8 +10035,8 @@ var require_utils2 = __commonJS({
|
|
|
9629
10035
|
}
|
|
9630
10036
|
return ind;
|
|
9631
10037
|
}
|
|
9632
|
-
function removeDotSegments(
|
|
9633
|
-
let input =
|
|
10038
|
+
function removeDotSegments(path50) {
|
|
10039
|
+
let input = path50;
|
|
9634
10040
|
const output = [];
|
|
9635
10041
|
let nextSlash = -1;
|
|
9636
10042
|
let len = 0;
|
|
@@ -9882,8 +10288,8 @@ var require_schemes = __commonJS({
|
|
|
9882
10288
|
wsComponent.secure = void 0;
|
|
9883
10289
|
}
|
|
9884
10290
|
if (wsComponent.resourceName) {
|
|
9885
|
-
const [
|
|
9886
|
-
wsComponent.path =
|
|
10291
|
+
const [path50, query] = wsComponent.resourceName.split("?");
|
|
10292
|
+
wsComponent.path = path50 && path50 !== "/" ? path50 : void 0;
|
|
9887
10293
|
wsComponent.query = query;
|
|
9888
10294
|
wsComponent.resourceName = void 0;
|
|
9889
10295
|
}
|
|
@@ -10042,55 +10448,55 @@ var require_fast_uri = __commonJS({
|
|
|
10042
10448
|
}
|
|
10043
10449
|
return uri;
|
|
10044
10450
|
}
|
|
10045
|
-
function
|
|
10451
|
+
function resolve35(baseURI, relativeURI, options) {
|
|
10046
10452
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
10047
10453
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
10048
10454
|
schemelessOptions.skipEscape = true;
|
|
10049
10455
|
return serialize(resolved, schemelessOptions);
|
|
10050
10456
|
}
|
|
10051
|
-
function resolveComponent(base,
|
|
10457
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
10052
10458
|
const target = {};
|
|
10053
10459
|
if (!skipNormalization) {
|
|
10054
10460
|
base = parse4(serialize(base, options), options);
|
|
10055
|
-
|
|
10461
|
+
relative3 = parse4(serialize(relative3, options), options);
|
|
10056
10462
|
}
|
|
10057
10463
|
options = options || {};
|
|
10058
|
-
if (!options.tolerant &&
|
|
10059
|
-
target.scheme =
|
|
10060
|
-
target.userinfo =
|
|
10061
|
-
target.host =
|
|
10062
|
-
target.port =
|
|
10063
|
-
target.path = removeDotSegments(
|
|
10064
|
-
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;
|
|
10065
10471
|
} else {
|
|
10066
|
-
if (
|
|
10067
|
-
target.userinfo =
|
|
10068
|
-
target.host =
|
|
10069
|
-
target.port =
|
|
10070
|
-
target.path = removeDotSegments(
|
|
10071
|
-
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;
|
|
10072
10478
|
} else {
|
|
10073
|
-
if (!
|
|
10479
|
+
if (!relative3.path) {
|
|
10074
10480
|
target.path = base.path;
|
|
10075
|
-
if (
|
|
10076
|
-
target.query =
|
|
10481
|
+
if (relative3.query !== void 0) {
|
|
10482
|
+
target.query = relative3.query;
|
|
10077
10483
|
} else {
|
|
10078
10484
|
target.query = base.query;
|
|
10079
10485
|
}
|
|
10080
10486
|
} else {
|
|
10081
|
-
if (
|
|
10082
|
-
target.path = removeDotSegments(
|
|
10487
|
+
if (relative3.path[0] === "/") {
|
|
10488
|
+
target.path = removeDotSegments(relative3.path);
|
|
10083
10489
|
} else {
|
|
10084
10490
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
10085
|
-
target.path = "/" +
|
|
10491
|
+
target.path = "/" + relative3.path;
|
|
10086
10492
|
} else if (!base.path) {
|
|
10087
|
-
target.path =
|
|
10493
|
+
target.path = relative3.path;
|
|
10088
10494
|
} else {
|
|
10089
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
10495
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
10090
10496
|
}
|
|
10091
10497
|
target.path = removeDotSegments(target.path);
|
|
10092
10498
|
}
|
|
10093
|
-
target.query =
|
|
10499
|
+
target.query = relative3.query;
|
|
10094
10500
|
}
|
|
10095
10501
|
target.userinfo = base.userinfo;
|
|
10096
10502
|
target.host = base.host;
|
|
@@ -10098,7 +10504,7 @@ var require_fast_uri = __commonJS({
|
|
|
10098
10504
|
}
|
|
10099
10505
|
target.scheme = base.scheme;
|
|
10100
10506
|
}
|
|
10101
|
-
target.fragment =
|
|
10507
|
+
target.fragment = relative3.fragment;
|
|
10102
10508
|
return target;
|
|
10103
10509
|
}
|
|
10104
10510
|
function equal(uriA, uriB, options) {
|
|
@@ -10300,7 +10706,7 @@ var require_fast_uri = __commonJS({
|
|
|
10300
10706
|
var fastUri = {
|
|
10301
10707
|
SCHEMES,
|
|
10302
10708
|
normalize: normalize3,
|
|
10303
|
-
resolve:
|
|
10709
|
+
resolve: resolve35,
|
|
10304
10710
|
resolveComponent,
|
|
10305
10711
|
equal,
|
|
10306
10712
|
serialize,
|
|
@@ -13276,12 +13682,12 @@ var require_dist = __commonJS({
|
|
|
13276
13682
|
throw new Error(`Unknown format "${name}"`);
|
|
13277
13683
|
return f2;
|
|
13278
13684
|
};
|
|
13279
|
-
function addFormats(ajv, list,
|
|
13685
|
+
function addFormats(ajv, list, fs42, exportName) {
|
|
13280
13686
|
var _a3;
|
|
13281
13687
|
var _b2;
|
|
13282
13688
|
(_a3 = (_b2 = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
13283
13689
|
for (const f2 of list)
|
|
13284
|
-
ajv.addFormat(f2,
|
|
13690
|
+
ajv.addFormat(f2, fs42[f2]);
|
|
13285
13691
|
}
|
|
13286
13692
|
module2.exports = exports2 = formatsPlugin;
|
|
13287
13693
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -14565,11 +14971,11 @@ var parser = new YargsParser({
|
|
|
14565
14971
|
resolve: import_path2.resolve,
|
|
14566
14972
|
// TODO: figure out a way to combine ESM and CJS coverage, such that
|
|
14567
14973
|
// we can exercise all the lines below:
|
|
14568
|
-
require: (
|
|
14974
|
+
require: (path50) => {
|
|
14569
14975
|
if (typeof require !== "undefined") {
|
|
14570
|
-
return require(
|
|
14571
|
-
} else if (
|
|
14572
|
-
return JSON.parse((0, import_fs2.readFileSync)(
|
|
14976
|
+
return require(path50);
|
|
14977
|
+
} else if (path50.match(/\.json$/)) {
|
|
14978
|
+
return JSON.parse((0, import_fs2.readFileSync)(path50, "utf8"));
|
|
14573
14979
|
} else {
|
|
14574
14980
|
throw Error("only .json config files are supported in ESM");
|
|
14575
14981
|
}
|
|
@@ -15912,13 +16318,13 @@ function usage(yargs, shim3) {
|
|
|
15912
16318
|
};
|
|
15913
16319
|
self.stringifiedValues = function stringifiedValues(values, separator) {
|
|
15914
16320
|
let string3 = "";
|
|
15915
|
-
const
|
|
16321
|
+
const sep6 = separator || ", ";
|
|
15916
16322
|
const array2 = [].concat(values);
|
|
15917
16323
|
if (!values || !array2.length)
|
|
15918
16324
|
return string3;
|
|
15919
16325
|
array2.forEach((value) => {
|
|
15920
16326
|
if (string3.length)
|
|
15921
|
-
string3 +=
|
|
16327
|
+
string3 += sep6;
|
|
15922
16328
|
string3 += JSON.stringify(value);
|
|
15923
16329
|
});
|
|
15924
16330
|
return string3;
|
|
@@ -17095,12 +17501,12 @@ var YargsInstance = class {
|
|
|
17095
17501
|
async getCompletion(args, done) {
|
|
17096
17502
|
argsert("<array> [function]", [args, done], arguments.length);
|
|
17097
17503
|
if (!done) {
|
|
17098
|
-
return new Promise((
|
|
17504
|
+
return new Promise((resolve35, reject) => {
|
|
17099
17505
|
__classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
|
|
17100
17506
|
if (err)
|
|
17101
17507
|
reject(err);
|
|
17102
17508
|
else
|
|
17103
|
-
|
|
17509
|
+
resolve35(completions);
|
|
17104
17510
|
});
|
|
17105
17511
|
});
|
|
17106
17512
|
} else {
|
|
@@ -19250,8 +19656,8 @@ function getErrorMap() {
|
|
|
19250
19656
|
|
|
19251
19657
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
19252
19658
|
var makeIssue = (params) => {
|
|
19253
|
-
const { data, path:
|
|
19254
|
-
const fullPath = [...
|
|
19659
|
+
const { data, path: path50, errorMaps, issueData } = params;
|
|
19660
|
+
const fullPath = [...path50, ...issueData.path || []];
|
|
19255
19661
|
const fullIssue = {
|
|
19256
19662
|
...issueData,
|
|
19257
19663
|
path: fullPath
|
|
@@ -19367,11 +19773,11 @@ var errorUtil;
|
|
|
19367
19773
|
|
|
19368
19774
|
// node_modules/zod/v3/types.js
|
|
19369
19775
|
var ParseInputLazyPath = class {
|
|
19370
|
-
constructor(parent, value,
|
|
19776
|
+
constructor(parent, value, path50, key) {
|
|
19371
19777
|
this._cachedPath = [];
|
|
19372
19778
|
this.parent = parent;
|
|
19373
19779
|
this.data = value;
|
|
19374
|
-
this._path =
|
|
19780
|
+
this._path = path50;
|
|
19375
19781
|
this._key = key;
|
|
19376
19782
|
}
|
|
19377
19783
|
get path() {
|
|
@@ -22992,7 +23398,6 @@ var SlotRegistryEntrySchema = external_exports.object({
|
|
|
22992
23398
|
/** Random per-session chars; absent in root mode. */
|
|
22993
23399
|
suffix: external_exports.string().optional(),
|
|
22994
23400
|
createdAt: external_exports.string(),
|
|
22995
|
-
purpose: external_exports.string().optional(),
|
|
22996
23401
|
ports: external_exports.record(external_exports.number()).optional(),
|
|
22997
23402
|
previewUrls: external_exports.record(external_exports.string()).optional(),
|
|
22998
23403
|
status: external_exports.enum(["starting", "active", "failed", "completed"]).default("active"),
|
|
@@ -23038,6 +23443,7 @@ var AgentSlotStatusSchema = external_exports.object({
|
|
|
23038
23443
|
baseBranch: external_exports.string().optional(),
|
|
23039
23444
|
baseCommit: external_exports.string().optional(),
|
|
23040
23445
|
sessionCreatedAt: external_exports.string().optional(),
|
|
23446
|
+
/** Derived from first captured user prompt (OTEL hooks); not set at launch. */
|
|
23041
23447
|
purpose: external_exports.string().optional(),
|
|
23042
23448
|
sessionStatus: external_exports.enum(["starting", "active", "failed", "completed"]).optional(),
|
|
23043
23449
|
lastError: external_exports.string().optional(),
|
|
@@ -23106,7 +23512,7 @@ var ValidationRecordSchema = external_exports.object({
|
|
|
23106
23512
|
var SyncValidationsInputSchema = external_exports.object({
|
|
23107
23513
|
validations: external_exports.array(ValidationRecordSchema)
|
|
23108
23514
|
});
|
|
23109
|
-
var AgentToolSchema = external_exports.enum(["claude_code", "codex"]);
|
|
23515
|
+
var AgentToolSchema = external_exports.enum(["claude_code", "codex", "cursor"]);
|
|
23110
23516
|
var UsageSourceSchema = external_exports.enum(["otel", "harvest"]);
|
|
23111
23517
|
var AgentSessionUsageSchema = external_exports.object({
|
|
23112
23518
|
sessionKey: external_exports.string().min(1),
|
|
@@ -23410,16 +23816,16 @@ async function handleAgentSkills(options) {
|
|
|
23410
23816
|
}
|
|
23411
23817
|
async function askYesNo(question) {
|
|
23412
23818
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
23413
|
-
return new Promise((
|
|
23819
|
+
return new Promise((resolve35) => {
|
|
23414
23820
|
process.stderr.write(`${question} `);
|
|
23415
23821
|
rl.once("line", (answer) => {
|
|
23416
23822
|
rl.close();
|
|
23417
23823
|
const trimmed = answer.trim();
|
|
23418
23824
|
if (trimmed === "") {
|
|
23419
|
-
|
|
23825
|
+
resolve35(true);
|
|
23420
23826
|
return;
|
|
23421
23827
|
}
|
|
23422
|
-
|
|
23828
|
+
resolve35(/^y(es)?$/i.test(trimmed));
|
|
23423
23829
|
});
|
|
23424
23830
|
});
|
|
23425
23831
|
}
|
|
@@ -23498,7 +23904,7 @@ function handleRemove(argv) {
|
|
|
23498
23904
|
}
|
|
23499
23905
|
|
|
23500
23906
|
// src/cli/commands/env.ts
|
|
23501
|
-
var
|
|
23907
|
+
var path43 = __toESM(require("path"));
|
|
23502
23908
|
|
|
23503
23909
|
// src/core/harness.ts
|
|
23504
23910
|
var fs20 = __toESM(require("fs"));
|
|
@@ -24277,13 +24683,13 @@ var MultipartBody = class {
|
|
|
24277
24683
|
// node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
|
|
24278
24684
|
var import_web = require("node:stream/web");
|
|
24279
24685
|
var fileFromPathWarned = false;
|
|
24280
|
-
async function fileFromPath3(
|
|
24686
|
+
async function fileFromPath3(path50, ...args) {
|
|
24281
24687
|
const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
|
|
24282
24688
|
if (!fileFromPathWarned) {
|
|
24283
|
-
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(
|
|
24689
|
+
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path50)}) instead`);
|
|
24284
24690
|
fileFromPathWarned = true;
|
|
24285
24691
|
}
|
|
24286
|
-
return await _fileFromPath(
|
|
24692
|
+
return await _fileFromPath(path50, ...args);
|
|
24287
24693
|
}
|
|
24288
24694
|
var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
24289
24695
|
var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
@@ -24779,8 +25185,8 @@ async function defaultParseResponse(props) {
|
|
|
24779
25185
|
}
|
|
24780
25186
|
var APIPromise = class _APIPromise extends Promise {
|
|
24781
25187
|
constructor(responsePromise, parseResponse = defaultParseResponse) {
|
|
24782
|
-
super((
|
|
24783
|
-
|
|
25188
|
+
super((resolve35) => {
|
|
25189
|
+
resolve35(null);
|
|
24784
25190
|
});
|
|
24785
25191
|
this.responsePromise = responsePromise;
|
|
24786
25192
|
this.parseResponse = parseResponse;
|
|
@@ -24880,29 +25286,29 @@ var APIClient = class {
|
|
|
24880
25286
|
defaultIdempotencyKey() {
|
|
24881
25287
|
return `stainless-node-retry-${uuid4()}`;
|
|
24882
25288
|
}
|
|
24883
|
-
get(
|
|
24884
|
-
return this.methodRequest("get",
|
|
25289
|
+
get(path50, opts) {
|
|
25290
|
+
return this.methodRequest("get", path50, opts);
|
|
24885
25291
|
}
|
|
24886
|
-
post(
|
|
24887
|
-
return this.methodRequest("post",
|
|
25292
|
+
post(path50, opts) {
|
|
25293
|
+
return this.methodRequest("post", path50, opts);
|
|
24888
25294
|
}
|
|
24889
|
-
patch(
|
|
24890
|
-
return this.methodRequest("patch",
|
|
25295
|
+
patch(path50, opts) {
|
|
25296
|
+
return this.methodRequest("patch", path50, opts);
|
|
24891
25297
|
}
|
|
24892
|
-
put(
|
|
24893
|
-
return this.methodRequest("put",
|
|
25298
|
+
put(path50, opts) {
|
|
25299
|
+
return this.methodRequest("put", path50, opts);
|
|
24894
25300
|
}
|
|
24895
|
-
delete(
|
|
24896
|
-
return this.methodRequest("delete",
|
|
25301
|
+
delete(path50, opts) {
|
|
25302
|
+
return this.methodRequest("delete", path50, opts);
|
|
24897
25303
|
}
|
|
24898
|
-
methodRequest(method,
|
|
25304
|
+
methodRequest(method, path50, opts) {
|
|
24899
25305
|
return this.request(Promise.resolve(opts).then(async (opts2) => {
|
|
24900
25306
|
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;
|
|
24901
|
-
return { method, path:
|
|
25307
|
+
return { method, path: path50, ...opts2, body };
|
|
24902
25308
|
}));
|
|
24903
25309
|
}
|
|
24904
|
-
getAPIList(
|
|
24905
|
-
return this.requestAPIList(Page, { method: "get", path:
|
|
25310
|
+
getAPIList(path50, Page, opts) {
|
|
25311
|
+
return this.requestAPIList(Page, { method: "get", path: path50, ...opts });
|
|
24906
25312
|
}
|
|
24907
25313
|
calculateContentLength(body) {
|
|
24908
25314
|
if (typeof body === "string") {
|
|
@@ -24920,10 +25326,10 @@ var APIClient = class {
|
|
|
24920
25326
|
return null;
|
|
24921
25327
|
}
|
|
24922
25328
|
buildRequest(options) {
|
|
24923
|
-
const { method, path:
|
|
25329
|
+
const { method, path: path50, query, headers = {} } = options;
|
|
24924
25330
|
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;
|
|
24925
25331
|
const contentLength = this.calculateContentLength(body);
|
|
24926
|
-
const url = this.buildURL(
|
|
25332
|
+
const url = this.buildURL(path50, query);
|
|
24927
25333
|
if ("timeout" in options)
|
|
24928
25334
|
validatePositiveInteger("timeout", options.timeout);
|
|
24929
25335
|
const timeout = options.timeout ?? this.timeout;
|
|
@@ -25032,8 +25438,8 @@ var APIClient = class {
|
|
|
25032
25438
|
const request = this.makeRequest(options, null);
|
|
25033
25439
|
return new PagePromise(this, request, Page);
|
|
25034
25440
|
}
|
|
25035
|
-
buildURL(
|
|
25036
|
-
const url = isAbsoluteURL(
|
|
25441
|
+
buildURL(path50, query) {
|
|
25442
|
+
const url = isAbsoluteURL(path50) ? new URL(path50) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path50.startsWith("/") ? path50.slice(1) : path50));
|
|
25037
25443
|
const defaultQuery = this.defaultQuery();
|
|
25038
25444
|
if (!isEmptyObj(defaultQuery)) {
|
|
25039
25445
|
query = { ...defaultQuery, ...query };
|
|
@@ -25318,7 +25724,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
|
|
|
25318
25724
|
var isAbsoluteURL = (url) => {
|
|
25319
25725
|
return startsWithSchemeRegexp.test(url);
|
|
25320
25726
|
};
|
|
25321
|
-
var sleep = (ms) => new Promise((
|
|
25727
|
+
var sleep = (ms) => new Promise((resolve35) => setTimeout(resolve35, ms));
|
|
25322
25728
|
var validatePositiveInteger = (name, n2) => {
|
|
25323
25729
|
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
25324
25730
|
throw new AnthropicError(`${name} must be an integer`);
|
|
@@ -25809,12 +26215,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
25809
26215
|
}
|
|
25810
26216
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
25811
26217
|
});
|
|
25812
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((
|
|
25813
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise,
|
|
26218
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve35, reject) => {
|
|
26219
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve35, "f");
|
|
25814
26220
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
|
|
25815
26221
|
}), "f");
|
|
25816
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((
|
|
25817
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise,
|
|
26222
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve35, reject) => {
|
|
26223
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve35, "f");
|
|
25818
26224
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
|
|
25819
26225
|
}), "f");
|
|
25820
26226
|
__classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -25943,11 +26349,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
25943
26349
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
25944
26350
|
*/
|
|
25945
26351
|
emitted(event) {
|
|
25946
|
-
return new Promise((
|
|
26352
|
+
return new Promise((resolve35, reject) => {
|
|
25947
26353
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
|
|
25948
26354
|
if (event !== "error")
|
|
25949
26355
|
this.once("error", reject);
|
|
25950
|
-
this.once(event,
|
|
26356
|
+
this.once(event, resolve35);
|
|
25951
26357
|
});
|
|
25952
26358
|
}
|
|
25953
26359
|
async done() {
|
|
@@ -26174,7 +26580,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26174
26580
|
if (done) {
|
|
26175
26581
|
return { value: void 0, done: true };
|
|
26176
26582
|
}
|
|
26177
|
-
return new Promise((
|
|
26583
|
+
return new Promise((resolve35, reject) => readQueue.push({ resolve: resolve35, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
26178
26584
|
}
|
|
26179
26585
|
const chunk = pushQueue.shift();
|
|
26180
26586
|
return { value: chunk, done: false };
|
|
@@ -26322,12 +26728,12 @@ var MessageStream = class _MessageStream {
|
|
|
26322
26728
|
}
|
|
26323
26729
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
26324
26730
|
});
|
|
26325
|
-
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((
|
|
26326
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise,
|
|
26731
|
+
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve35, reject) => {
|
|
26732
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve35, "f");
|
|
26327
26733
|
__classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
|
|
26328
26734
|
}), "f");
|
|
26329
|
-
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((
|
|
26330
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise,
|
|
26735
|
+
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve35, reject) => {
|
|
26736
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve35, "f");
|
|
26331
26737
|
__classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
|
|
26332
26738
|
}), "f");
|
|
26333
26739
|
__classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -26456,11 +26862,11 @@ var MessageStream = class _MessageStream {
|
|
|
26456
26862
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
26457
26863
|
*/
|
|
26458
26864
|
emitted(event) {
|
|
26459
|
-
return new Promise((
|
|
26865
|
+
return new Promise((resolve35, reject) => {
|
|
26460
26866
|
__classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
|
|
26461
26867
|
if (event !== "error")
|
|
26462
26868
|
this.once("error", reject);
|
|
26463
|
-
this.once(event,
|
|
26869
|
+
this.once(event, resolve35);
|
|
26464
26870
|
});
|
|
26465
26871
|
}
|
|
26466
26872
|
async done() {
|
|
@@ -26687,7 +27093,7 @@ var MessageStream = class _MessageStream {
|
|
|
26687
27093
|
if (done) {
|
|
26688
27094
|
return { value: void 0, done: true };
|
|
26689
27095
|
}
|
|
26690
|
-
return new Promise((
|
|
27096
|
+
return new Promise((resolve35, reject) => readQueue.push({ resolve: resolve35, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
26691
27097
|
}
|
|
26692
27098
|
const chunk = pushQueue.shift();
|
|
26693
27099
|
return { value: chunk, done: false };
|
|
@@ -26947,11 +27353,11 @@ async function promptApplyAgentMdProposal(repoPath) {
|
|
|
26947
27353
|
}
|
|
26948
27354
|
async function askYesNo2(question) {
|
|
26949
27355
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
26950
|
-
return new Promise((
|
|
27356
|
+
return new Promise((resolve35) => {
|
|
26951
27357
|
process.stderr.write(`${question} `);
|
|
26952
27358
|
rl.once("line", (answer) => {
|
|
26953
27359
|
rl.close();
|
|
26954
|
-
|
|
27360
|
+
resolve35(/^y(es)?$/i.test(answer.trim()));
|
|
26955
27361
|
});
|
|
26956
27362
|
});
|
|
26957
27363
|
}
|
|
@@ -27181,7 +27587,7 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
|
|
|
27181
27587
|
}
|
|
27182
27588
|
async function askUser(question, options) {
|
|
27183
27589
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
27184
|
-
return new Promise((
|
|
27590
|
+
return new Promise((resolve35) => {
|
|
27185
27591
|
process.stderr.write("\n");
|
|
27186
27592
|
process.stderr.write(`\u2753 ${question}
|
|
27187
27593
|
`);
|
|
@@ -27192,7 +27598,7 @@ async function askUser(question, options) {
|
|
|
27192
27598
|
process.stderr.write("> ");
|
|
27193
27599
|
rl.once("line", (answer) => {
|
|
27194
27600
|
rl.close();
|
|
27195
|
-
|
|
27601
|
+
resolve35(answer.trim());
|
|
27196
27602
|
});
|
|
27197
27603
|
});
|
|
27198
27604
|
}
|
|
@@ -27336,7 +27742,7 @@ function runScript(scriptPath, args = [], options = {}) {
|
|
|
27336
27742
|
function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
27337
27743
|
const stream = options.stream ?? false;
|
|
27338
27744
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27339
|
-
return new Promise((
|
|
27745
|
+
return new Promise((resolve35) => {
|
|
27340
27746
|
const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
|
|
27341
27747
|
...spawnOptions,
|
|
27342
27748
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27351,13 +27757,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
|
27351
27757
|
stderr += d2;
|
|
27352
27758
|
if (stream) process.stderr.write(d2);
|
|
27353
27759
|
});
|
|
27354
|
-
proc.on("close", (code) =>
|
|
27760
|
+
proc.on("close", (code) => resolve35({ stdout, stderr, code: code ?? 0 }));
|
|
27355
27761
|
});
|
|
27356
27762
|
}
|
|
27357
27763
|
function runShellCommand(command2, options = {}) {
|
|
27358
27764
|
const stream = options.stream ?? false;
|
|
27359
27765
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27360
|
-
return new Promise((
|
|
27766
|
+
return new Promise((resolve35) => {
|
|
27361
27767
|
const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
|
|
27362
27768
|
...spawnOptions,
|
|
27363
27769
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27372,7 +27778,7 @@ function runShellCommand(command2, options = {}) {
|
|
|
27372
27778
|
stderr += d2;
|
|
27373
27779
|
if (stream) process.stderr.write(d2);
|
|
27374
27780
|
});
|
|
27375
|
-
proc.on("close", (code) =>
|
|
27781
|
+
proc.on("close", (code) => resolve35({ stdout, stderr, code: code ?? 0 }));
|
|
27376
27782
|
});
|
|
27377
27783
|
}
|
|
27378
27784
|
|
|
@@ -28536,16 +28942,16 @@ async function promptScaffoldCursorRule(exists) {
|
|
|
28536
28942
|
}
|
|
28537
28943
|
async function askYesNo3(question) {
|
|
28538
28944
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
|
|
28539
|
-
return new Promise((
|
|
28945
|
+
return new Promise((resolve35) => {
|
|
28540
28946
|
process.stderr.write(`${question} `);
|
|
28541
28947
|
rl.once("line", (answer) => {
|
|
28542
28948
|
rl.close();
|
|
28543
28949
|
const trimmed = answer.trim();
|
|
28544
28950
|
if (trimmed === "") {
|
|
28545
|
-
|
|
28951
|
+
resolve35(true);
|
|
28546
28952
|
return;
|
|
28547
28953
|
}
|
|
28548
|
-
|
|
28954
|
+
resolve35(/^y(es)?$/i.test(trimmed));
|
|
28549
28955
|
});
|
|
28550
28956
|
});
|
|
28551
28957
|
}
|
|
@@ -28663,7 +29069,6 @@ function buildLaunchFlagArgs(flags) {
|
|
|
28663
29069
|
if (flags.confirmReplace) args.push("--replace");
|
|
28664
29070
|
if (flags.force) args.push("--force");
|
|
28665
29071
|
if (flags.resume) args.push("--resume");
|
|
28666
|
-
if (flags.purpose) args.push(`--purpose=${flags.purpose}`);
|
|
28667
29072
|
return args;
|
|
28668
29073
|
}
|
|
28669
29074
|
function resolveRepoPath(repoPath) {
|
|
@@ -28744,9 +29149,6 @@ function buildExecutionPlan(repoPath, stage, options) {
|
|
|
28744
29149
|
const cwd = stage.cwd ? path24.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
|
|
28745
29150
|
const extraArgs = options.args ?? [];
|
|
28746
29151
|
const launchFlagArgs = stage.kind === "launch" && options.launchFlags ? buildLaunchFlagArgs(options.launchFlags) : [];
|
|
28747
|
-
if (stage.kind === "launch" && options.launchFlags?.purpose) {
|
|
28748
|
-
mergedEnv.HAR_SESSION_PURPOSE = options.launchFlags.purpose;
|
|
28749
|
-
}
|
|
28750
29152
|
if (stage.command) {
|
|
28751
29153
|
let shellCommand = substituteAgentId(stage.command, options.agentId);
|
|
28752
29154
|
for (const extra of [...extraArgs, ...launchFlagArgs]) {
|
|
@@ -28820,16 +29222,16 @@ var LocalScriptExecutor = class {
|
|
|
28820
29222
|
const walk = (dir, prefix) => {
|
|
28821
29223
|
for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
|
|
28822
29224
|
const full = path24.join(dir, entry.name);
|
|
28823
|
-
const
|
|
29225
|
+
const relative3 = path24.join(prefix, entry.name);
|
|
28824
29226
|
if (entry.isDirectory()) {
|
|
28825
|
-
walk(full,
|
|
29227
|
+
walk(full, relative3);
|
|
28826
29228
|
continue;
|
|
28827
29229
|
}
|
|
28828
|
-
if (filter?.stageId && !
|
|
29230
|
+
if (filter?.stageId && !relative3.includes(filter.stageId)) continue;
|
|
28829
29231
|
const stat = fs25.statSync(full);
|
|
28830
29232
|
entries.push({
|
|
28831
29233
|
path: full,
|
|
28832
|
-
relativePath:
|
|
29234
|
+
relativePath: relative3,
|
|
28833
29235
|
sizeBytes: stat.size,
|
|
28834
29236
|
modifiedAt: stat.mtime.toISOString()
|
|
28835
29237
|
});
|
|
@@ -28842,44 +29244,86 @@ var LocalScriptExecutor = class {
|
|
|
28842
29244
|
var localScriptExecutor = new LocalScriptExecutor();
|
|
28843
29245
|
|
|
28844
29246
|
// src/core/control-sync.ts
|
|
28845
|
-
var
|
|
29247
|
+
var path39 = __toESM(require("path"));
|
|
29248
|
+
init_control_config();
|
|
28846
29249
|
|
|
28847
|
-
// src/core/control-
|
|
28848
|
-
var
|
|
28849
|
-
|
|
28850
|
-
|
|
29250
|
+
// src/core/control-registry.ts
|
|
29251
|
+
var fs27 = __toESM(require("fs"));
|
|
29252
|
+
var os4 = __toESM(require("os"));
|
|
29253
|
+
var path26 = __toESM(require("path"));
|
|
29254
|
+
|
|
29255
|
+
// src/core/control-repo-path.ts
|
|
29256
|
+
var fs26 = __toESM(require("fs"));
|
|
29257
|
+
var path25 = __toESM(require("path"));
|
|
29258
|
+
function tryGit(cwd, args) {
|
|
29259
|
+
if (!fs26.existsSync(cwd)) return void 0;
|
|
29260
|
+
const result = run(`git ${args}`, { cwd });
|
|
29261
|
+
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
28851
29262
|
}
|
|
28852
|
-
function
|
|
28853
|
-
|
|
29263
|
+
function resolveMainWorkingTree(cwd) {
|
|
29264
|
+
const toplevel = tryGit(cwd, "rev-parse --show-toplevel");
|
|
29265
|
+
if (!toplevel) return void 0;
|
|
29266
|
+
const commonDirRaw = tryGit(cwd, "rev-parse --git-common-dir");
|
|
29267
|
+
const commonDir = commonDirRaw ? path25.resolve(cwd, commonDirRaw) : void 0;
|
|
29268
|
+
if (!commonDir) return path25.resolve(toplevel);
|
|
29269
|
+
if (path25.basename(commonDir) === ".git") {
|
|
29270
|
+
return path25.dirname(commonDir);
|
|
29271
|
+
}
|
|
29272
|
+
const porcelain = tryGit(cwd, "worktree list --porcelain");
|
|
29273
|
+
if (porcelain) {
|
|
29274
|
+
for (const line of porcelain.split("\n")) {
|
|
29275
|
+
if (line.startsWith("worktree ")) {
|
|
29276
|
+
return path25.resolve(line.slice("worktree ".length));
|
|
29277
|
+
}
|
|
29278
|
+
}
|
|
29279
|
+
}
|
|
29280
|
+
return path25.resolve(toplevel);
|
|
29281
|
+
}
|
|
29282
|
+
function canonicalizeControlRepoPath(repoPath) {
|
|
29283
|
+
const resolved = path25.resolve(repoPath);
|
|
29284
|
+
const toplevel = tryGit(resolved, "rev-parse --show-toplevel");
|
|
29285
|
+
if (!toplevel) return resolved;
|
|
29286
|
+
const mainRoot = resolveMainWorkingTree(resolved);
|
|
29287
|
+
if (!mainRoot) return resolved;
|
|
29288
|
+
const top2 = path25.resolve(toplevel);
|
|
29289
|
+
const main = path25.resolve(mainRoot);
|
|
29290
|
+
if (top2 === main) return resolved;
|
|
29291
|
+
const rel = path25.relative(top2, resolved);
|
|
29292
|
+
if (!rel || rel === ".") return main;
|
|
29293
|
+
if (rel.startsWith(`..${path25.sep}`) || rel === ".." || path25.isAbsolute(rel)) {
|
|
29294
|
+
return resolved;
|
|
29295
|
+
}
|
|
29296
|
+
return path25.resolve(main, rel);
|
|
28854
29297
|
}
|
|
28855
29298
|
|
|
28856
29299
|
// src/core/control-registry.ts
|
|
28857
|
-
var fs26 = __toESM(require("fs"));
|
|
28858
|
-
var os4 = __toESM(require("os"));
|
|
28859
|
-
var path25 = __toESM(require("path"));
|
|
28860
29300
|
function getRegistryPath() {
|
|
28861
29301
|
if (process.env.HAR_CONTROL_REGISTRY_PATH) {
|
|
28862
|
-
return
|
|
29302
|
+
return path26.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
|
|
28863
29303
|
}
|
|
28864
|
-
return
|
|
29304
|
+
return path26.join(os4.homedir(), ".har", "repos.json");
|
|
28865
29305
|
}
|
|
28866
29306
|
function readRegistry() {
|
|
28867
29307
|
const registryPath = getRegistryPath();
|
|
28868
29308
|
try {
|
|
28869
|
-
if (!
|
|
28870
|
-
return JSON.parse(
|
|
29309
|
+
if (!fs27.existsSync(registryPath)) return { repos: [] };
|
|
29310
|
+
return JSON.parse(fs27.readFileSync(registryPath, "utf8"));
|
|
28871
29311
|
} catch {
|
|
28872
29312
|
return { repos: [] };
|
|
28873
29313
|
}
|
|
28874
29314
|
}
|
|
28875
29315
|
function writeRegistry(registry2) {
|
|
28876
29316
|
const registryPath = getRegistryPath();
|
|
28877
|
-
|
|
28878
|
-
|
|
29317
|
+
fs27.mkdirSync(path26.dirname(registryPath), { recursive: true });
|
|
29318
|
+
fs27.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
|
|
29319
|
+
}
|
|
29320
|
+
function registryChanged(before, after) {
|
|
29321
|
+
if (before.length !== after.length) return true;
|
|
29322
|
+
return before.some((repoPath, index) => repoPath !== after[index]);
|
|
28879
29323
|
}
|
|
28880
29324
|
function recordRepoForControlSync(repoPath) {
|
|
28881
29325
|
if (process.env.HAR_CONTROL_DISABLED === "true") return;
|
|
28882
|
-
const resolved =
|
|
29326
|
+
const resolved = canonicalizeControlRepoPath(repoPath);
|
|
28883
29327
|
if (!readManifest(resolved)) return;
|
|
28884
29328
|
const registry2 = readRegistry();
|
|
28885
29329
|
if (registry2.repos.includes(resolved)) return;
|
|
@@ -28888,25 +29332,34 @@ function recordRepoForControlSync(repoPath) {
|
|
|
28888
29332
|
}
|
|
28889
29333
|
function listRegisteredRepos() {
|
|
28890
29334
|
const registry2 = readRegistry();
|
|
28891
|
-
const kept =
|
|
28892
|
-
|
|
29335
|
+
const kept = [];
|
|
29336
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29337
|
+
for (const repoPath of registry2.repos) {
|
|
29338
|
+
if (!fs27.existsSync(repoPath)) continue;
|
|
29339
|
+
const canonical = canonicalizeControlRepoPath(repoPath);
|
|
29340
|
+
if (!fs27.existsSync(canonical) || !readManifest(canonical)) continue;
|
|
29341
|
+
if (seen.has(canonical)) continue;
|
|
29342
|
+
seen.add(canonical);
|
|
29343
|
+
kept.push(canonical);
|
|
29344
|
+
}
|
|
29345
|
+
if (registryChanged(registry2.repos, kept)) {
|
|
28893
29346
|
writeRegistry({ repos: kept });
|
|
28894
29347
|
}
|
|
28895
29348
|
return kept;
|
|
28896
29349
|
}
|
|
28897
29350
|
|
|
28898
29351
|
// src/core/slot-status.ts
|
|
28899
|
-
var
|
|
29352
|
+
var fs32 = __toESM(require("fs"));
|
|
28900
29353
|
var os7 = __toESM(require("os"));
|
|
28901
|
-
var
|
|
29354
|
+
var path32 = __toESM(require("path"));
|
|
28902
29355
|
var import_child_process7 = require("child_process");
|
|
28903
29356
|
|
|
28904
29357
|
// src/core/runs.ts
|
|
28905
29358
|
var crypto2 = __toESM(require("crypto"));
|
|
28906
29359
|
var import_child_process2 = require("child_process");
|
|
28907
|
-
var
|
|
29360
|
+
var fs28 = __toESM(require("fs"));
|
|
28908
29361
|
var os5 = __toESM(require("os"));
|
|
28909
|
-
var
|
|
29362
|
+
var path27 = __toESM(require("path"));
|
|
28910
29363
|
var RUNS_DIR = "runs";
|
|
28911
29364
|
function gitCommonDir(cwd) {
|
|
28912
29365
|
try {
|
|
@@ -28915,7 +29368,7 @@ function gitCommonDir(cwd) {
|
|
|
28915
29368
|
encoding: "utf8",
|
|
28916
29369
|
stdio: ["pipe", "pipe", "ignore"]
|
|
28917
29370
|
}).trim();
|
|
28918
|
-
return out ?
|
|
29371
|
+
return out ? path27.resolve(cwd, out) : void 0;
|
|
28919
29372
|
} catch {
|
|
28920
29373
|
return void 0;
|
|
28921
29374
|
}
|
|
@@ -28937,7 +29390,7 @@ function gitPrefix(cwd) {
|
|
|
28937
29390
|
}
|
|
28938
29391
|
}
|
|
28939
29392
|
function getRunsDir(harnessRoot) {
|
|
28940
|
-
return
|
|
29393
|
+
return path27.join(getHarnessDir(harnessRoot), RUNS_DIR);
|
|
28941
29394
|
}
|
|
28942
29395
|
function formatLocalDate(d2) {
|
|
28943
29396
|
const y2 = d2.getFullYear();
|
|
@@ -28957,24 +29410,24 @@ function buildRunRelativePath(stageId, agentId, startedAt, runId, runsDir) {
|
|
|
28957
29410
|
const timePart = formatLocalTime(started);
|
|
28958
29411
|
const agentPart = agentId !== void 0 ? `_agent-${agentId}` : "";
|
|
28959
29412
|
let filename = `${timePart}_${stageId}${agentPart}.json`;
|
|
28960
|
-
const fullPath =
|
|
28961
|
-
if (
|
|
29413
|
+
const fullPath = path27.join(runsDir, dateFolder, filename);
|
|
29414
|
+
if (fs28.existsSync(fullPath)) {
|
|
28962
29415
|
filename = `${timePart}_${stageId}${agentPart}-${runId.slice(0, 8)}.json`;
|
|
28963
29416
|
}
|
|
28964
|
-
return
|
|
29417
|
+
return path27.join(dateFolder, filename);
|
|
28965
29418
|
}
|
|
28966
29419
|
function resolveRunFilePath(harnessRoot, run2) {
|
|
28967
29420
|
if (run2.relativePath) {
|
|
28968
|
-
return
|
|
29421
|
+
return path27.join(getRunsDir(harnessRoot), run2.relativePath);
|
|
28969
29422
|
}
|
|
28970
|
-
return
|
|
29423
|
+
return path27.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
|
|
28971
29424
|
}
|
|
28972
29425
|
function collectRunFiles(runsDir) {
|
|
28973
|
-
if (!
|
|
29426
|
+
if (!fs28.existsSync(runsDir)) return [];
|
|
28974
29427
|
const files = [];
|
|
28975
29428
|
const walk = (dir) => {
|
|
28976
|
-
for (const entry of
|
|
28977
|
-
const full =
|
|
29429
|
+
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
29430
|
+
const full = path27.join(dir, entry.name);
|
|
28978
29431
|
if (entry.isDirectory()) {
|
|
28979
29432
|
walk(full);
|
|
28980
29433
|
} else if (entry.name.endsWith(".json")) {
|
|
@@ -28988,30 +29441,30 @@ function collectRunFiles(runsDir) {
|
|
|
28988
29441
|
function resolveAgentWorkDir(harnessRoot, agentId) {
|
|
28989
29442
|
if (agentId === void 0) return void 0;
|
|
28990
29443
|
const entry = readSlotRegistry(harnessRoot, agentId);
|
|
28991
|
-
if (entry?.workDir &&
|
|
29444
|
+
if (entry?.workDir && fs28.existsSync(entry.workDir)) return entry.workDir;
|
|
28992
29445
|
const env3 = readHarnessEnv(harnessRoot);
|
|
28993
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
28994
|
-
const worktreeDir =
|
|
29446
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path27.basename(harnessRoot);
|
|
29447
|
+
const worktreeDir = path27.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
|
|
28995
29448
|
const relPrefix = gitPrefix(harnessRoot);
|
|
28996
29449
|
const sessionEnvFiles = [];
|
|
28997
|
-
const worktreesRoot =
|
|
28998
|
-
if (
|
|
29450
|
+
const worktreesRoot = path27.join(os5.homedir(), "worktrees");
|
|
29451
|
+
if (fs28.existsSync(worktreesRoot)) {
|
|
28999
29452
|
const suffix = `-har-agent-${agentId}-`;
|
|
29000
|
-
for (const entry2 of
|
|
29453
|
+
for (const entry2 of fs28.readdirSync(worktreesRoot, { withFileTypes: true })) {
|
|
29001
29454
|
if (!entry2.isDirectory() || !entry2.name.includes(suffix)) continue;
|
|
29002
|
-
const sessionDir =
|
|
29455
|
+
const sessionDir = path27.join(worktreesRoot, entry2.name);
|
|
29003
29456
|
if (!sameGitCheckout(harnessRoot, sessionDir)) continue;
|
|
29004
|
-
sessionEnvFiles.push(
|
|
29457
|
+
sessionEnvFiles.push(path27.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
|
|
29005
29458
|
}
|
|
29006
29459
|
}
|
|
29007
29460
|
const candidates = [
|
|
29008
|
-
|
|
29009
|
-
|
|
29461
|
+
path27.join(worktreeDir, `.env.agent.${agentId}`),
|
|
29462
|
+
path27.join(harnessRoot, `.env.agent.${agentId}`),
|
|
29010
29463
|
...sessionEnvFiles.sort()
|
|
29011
29464
|
];
|
|
29012
29465
|
for (const envFile of candidates) {
|
|
29013
|
-
if (!
|
|
29014
|
-
const content =
|
|
29466
|
+
if (!fs28.existsSync(envFile)) continue;
|
|
29467
|
+
const content = fs28.readFileSync(envFile, "utf8");
|
|
29015
29468
|
const match = content.match(/^REPO_ROOT=(.+)$/m);
|
|
29016
29469
|
if (match) return match[1].trim();
|
|
29017
29470
|
}
|
|
@@ -29023,11 +29476,11 @@ function createRun(ctx, meta) {
|
|
|
29023
29476
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29024
29477
|
const runId = crypto2.randomUUID();
|
|
29025
29478
|
const relativePath = buildRunRelativePath(meta.stageId, meta.agentId, startedAt, runId, runsDir);
|
|
29026
|
-
const runFilePath =
|
|
29027
|
-
|
|
29479
|
+
const runFilePath = path27.join(runsDir, relativePath);
|
|
29480
|
+
fs28.mkdirSync(path27.dirname(runFilePath), { recursive: true });
|
|
29028
29481
|
const run2 = RunRecordSchema.parse({
|
|
29029
29482
|
runId,
|
|
29030
|
-
repoPath:
|
|
29483
|
+
repoPath: path27.resolve(ctx.repoPath),
|
|
29031
29484
|
harnessRoot,
|
|
29032
29485
|
stageId: meta.stageId,
|
|
29033
29486
|
kind: meta.kind,
|
|
@@ -29038,7 +29491,7 @@ function createRun(ctx, meta) {
|
|
|
29038
29491
|
relativePath,
|
|
29039
29492
|
trigger: ctx.trigger ?? "cli"
|
|
29040
29493
|
});
|
|
29041
|
-
|
|
29494
|
+
fs28.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
|
|
29042
29495
|
return run2;
|
|
29043
29496
|
}
|
|
29044
29497
|
function finishRun(repoPath, runId, update) {
|
|
@@ -29057,13 +29510,13 @@ function finishRun(repoPath, runId, update) {
|
|
|
29057
29510
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
29058
29511
|
});
|
|
29059
29512
|
const runPath = resolveRunFilePath(harnessRoot, finished);
|
|
29060
|
-
|
|
29513
|
+
fs28.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
|
|
29061
29514
|
return finished;
|
|
29062
29515
|
}
|
|
29063
29516
|
function findRunRecord(harnessRoot, runId) {
|
|
29064
29517
|
const runsDir = getRunsDir(harnessRoot);
|
|
29065
29518
|
for (const filePath of collectRunFiles(runsDir)) {
|
|
29066
|
-
const parsed = RunRecordSchema.safeParse(JSON.parse(
|
|
29519
|
+
const parsed = RunRecordSchema.safeParse(JSON.parse(fs28.readFileSync(filePath, "utf8")));
|
|
29067
29520
|
if (parsed.success && parsed.data.runId === runId) {
|
|
29068
29521
|
return parsed.data;
|
|
29069
29522
|
}
|
|
@@ -29075,10 +29528,10 @@ function getRun(repoPath, runId) {
|
|
|
29075
29528
|
}
|
|
29076
29529
|
function listRuns(repoPath, filter = {}) {
|
|
29077
29530
|
const runsDir = getRunsDir(resolveHarnessRoot(repoPath));
|
|
29078
|
-
if (!
|
|
29531
|
+
if (!fs28.existsSync(runsDir)) return [];
|
|
29079
29532
|
const runs = [];
|
|
29080
29533
|
for (const filePath of collectRunFiles(runsDir)) {
|
|
29081
|
-
const parsed = RunRecordSchema.safeParse(JSON.parse(
|
|
29534
|
+
const parsed = RunRecordSchema.safeParse(JSON.parse(fs28.readFileSync(filePath, "utf8")));
|
|
29082
29535
|
if (!parsed.success) continue;
|
|
29083
29536
|
if (filter.stageId && parsed.data.stageId !== filter.stageId) continue;
|
|
29084
29537
|
runs.push(parsed.data);
|
|
@@ -29091,17 +29544,18 @@ function listRuns(repoPath, filter = {}) {
|
|
|
29091
29544
|
}
|
|
29092
29545
|
|
|
29093
29546
|
// src/core/slot-preflight.ts
|
|
29094
|
-
var
|
|
29547
|
+
var path31 = __toESM(require("path"));
|
|
29095
29548
|
|
|
29096
29549
|
// src/core/control-port.ts
|
|
29097
29550
|
var import_child_process4 = require("child_process");
|
|
29098
|
-
var
|
|
29099
|
-
var
|
|
29551
|
+
var fs30 = __toESM(require("fs"));
|
|
29552
|
+
var path29 = __toESM(require("path"));
|
|
29553
|
+
init_control_config();
|
|
29100
29554
|
|
|
29101
29555
|
// src/core/slot-ports.ts
|
|
29102
29556
|
var import_child_process3 = require("child_process");
|
|
29103
|
-
var
|
|
29104
|
-
var
|
|
29557
|
+
var fs29 = __toESM(require("fs"));
|
|
29558
|
+
var path28 = __toESM(require("path"));
|
|
29105
29559
|
function portStep(env3) {
|
|
29106
29560
|
return Number(env3.HARNESS_PORT_STEP ?? 10);
|
|
29107
29561
|
}
|
|
@@ -29157,7 +29611,7 @@ function allocateAppPorts(repoPath, agentId) {
|
|
|
29157
29611
|
}
|
|
29158
29612
|
function harnessUsesPm2(repoPath) {
|
|
29159
29613
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29160
|
-
return
|
|
29614
|
+
return fs29.existsSync(path28.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
|
|
29161
29615
|
}
|
|
29162
29616
|
|
|
29163
29617
|
// src/core/control-port.ts
|
|
@@ -29212,13 +29666,13 @@ function controlDefaultPortWarnings(containers, defaultFrontendPort, allocatedFr
|
|
|
29212
29666
|
return [formatControlDefaultPortWarning(control.name, defaultFrontendPort, allocatedFrontend)];
|
|
29213
29667
|
}
|
|
29214
29668
|
function inspectControlUpReadiness(repoPath) {
|
|
29215
|
-
const resolved =
|
|
29669
|
+
const resolved = path29.resolve(repoPath);
|
|
29216
29670
|
const port = parseControlHostPort();
|
|
29217
29671
|
const containers = listDockerContainers();
|
|
29218
29672
|
const control = controlContainerOnPort(containers, port);
|
|
29219
29673
|
const warnings = [];
|
|
29220
|
-
const controlHarnessDir =
|
|
29221
|
-
const harnessSlot1Active =
|
|
29674
|
+
const controlHarnessDir = path29.join(resolved, "control", ".har");
|
|
29675
|
+
const harnessSlot1Active = fs30.existsSync(controlHarnessDir) && readSlotRegistry(path29.join(resolved, "control"), 1)?.status === "active";
|
|
29222
29676
|
if (control) {
|
|
29223
29677
|
return {
|
|
29224
29678
|
warnings,
|
|
@@ -29249,9 +29703,9 @@ function inspectControlUpReadiness(repoPath) {
|
|
|
29249
29703
|
}
|
|
29250
29704
|
|
|
29251
29705
|
// src/core/slot-launch-guard-occupied.ts
|
|
29252
|
-
var
|
|
29706
|
+
var fs31 = __toESM(require("fs"));
|
|
29253
29707
|
var os6 = __toESM(require("os"));
|
|
29254
|
-
var
|
|
29708
|
+
var path30 = __toESM(require("path"));
|
|
29255
29709
|
var import_child_process5 = require("child_process");
|
|
29256
29710
|
function runGit(cwd, args) {
|
|
29257
29711
|
try {
|
|
@@ -29270,7 +29724,7 @@ function worktreeDirty(worktreePath) {
|
|
|
29270
29724
|
}
|
|
29271
29725
|
function gitCommonDir2(cwd) {
|
|
29272
29726
|
const out = runGit(cwd, "rev-parse --git-common-dir");
|
|
29273
|
-
return out ?
|
|
29727
|
+
return out ? path30.resolve(cwd, out) : void 0;
|
|
29274
29728
|
}
|
|
29275
29729
|
function sameGitCheckout2(a2, b2) {
|
|
29276
29730
|
const left2 = gitCommonDir2(a2);
|
|
@@ -29279,20 +29733,20 @@ function sameGitCheckout2(a2, b2) {
|
|
|
29279
29733
|
}
|
|
29280
29734
|
function discoverWorktree(harnessRoot, agentId, projectName) {
|
|
29281
29735
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
29282
|
-
if (session?.worktreePath &&
|
|
29736
|
+
if (session?.worktreePath && fs31.existsSync(session.worktreePath)) {
|
|
29283
29737
|
return session.worktreePath;
|
|
29284
29738
|
}
|
|
29285
|
-
const legacy =
|
|
29286
|
-
if (
|
|
29287
|
-
const worktreesRoot =
|
|
29288
|
-
if (!
|
|
29739
|
+
const legacy = path30.join(os6.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
|
|
29740
|
+
if (fs31.existsSync(legacy) && sameGitCheckout2(harnessRoot, legacy)) return legacy;
|
|
29741
|
+
const worktreesRoot = path30.join(os6.homedir(), "worktrees");
|
|
29742
|
+
if (!fs31.existsSync(worktreesRoot)) return void 0;
|
|
29289
29743
|
const suffix = `-har-agent-${agentId}-`;
|
|
29290
29744
|
const relPrefix = runGit(harnessRoot, "rev-parse --show-prefix") ?? "";
|
|
29291
|
-
for (const name of
|
|
29745
|
+
for (const name of fs31.readdirSync(worktreesRoot)) {
|
|
29292
29746
|
if (!name.includes(suffix)) continue;
|
|
29293
|
-
const candidate =
|
|
29747
|
+
const candidate = path30.join(worktreesRoot, name);
|
|
29294
29748
|
if (!sameGitCheckout2(harnessRoot, candidate)) continue;
|
|
29295
|
-
if (!
|
|
29749
|
+
if (!fs31.existsSync(path30.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
|
|
29296
29750
|
return candidate;
|
|
29297
29751
|
}
|
|
29298
29752
|
return void 0;
|
|
@@ -29300,13 +29754,13 @@ function discoverWorktree(harnessRoot, agentId, projectName) {
|
|
|
29300
29754
|
function collectOccupiedSlot(repoPath, agentId) {
|
|
29301
29755
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29302
29756
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29303
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
29757
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path30.basename(harnessRoot);
|
|
29304
29758
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
29305
29759
|
const worktreePath = discoverWorktree(harnessRoot, agentId, projectName);
|
|
29306
29760
|
const workDir = session?.workDir;
|
|
29307
|
-
const envInWorkDir = workDir ?
|
|
29308
|
-
const envInRoot =
|
|
29309
|
-
const envInWorktree = worktreePath ?
|
|
29761
|
+
const envInWorkDir = workDir ? fs31.existsSync(path30.join(workDir, `.env.agent.${agentId}`)) : false;
|
|
29762
|
+
const envInRoot = fs31.existsSync(path30.join(harnessRoot, `.env.agent.${agentId}`));
|
|
29763
|
+
const envInWorktree = worktreePath ? fs31.existsSync(path30.join(worktreePath, `.env.agent.${agentId}`)) : false;
|
|
29310
29764
|
const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
|
|
29311
29765
|
if (!active) return void 0;
|
|
29312
29766
|
const dirty = worktreePath ? worktreeDirty(worktreePath) : void 0;
|
|
@@ -29319,7 +29773,6 @@ function collectOccupiedSlot(repoPath, agentId) {
|
|
|
29319
29773
|
sessionStatus: session?.status,
|
|
29320
29774
|
lastError: session?.lastError,
|
|
29321
29775
|
sessionCreatedAt: session?.createdAt,
|
|
29322
|
-
purpose: session?.purpose,
|
|
29323
29776
|
dirty,
|
|
29324
29777
|
harnessUsage: "none"
|
|
29325
29778
|
};
|
|
@@ -29330,7 +29783,6 @@ function formatOccupiedSlot(slot, resume) {
|
|
|
29330
29783
|
slot.worktreePath ? ` Worktree: ${slot.worktreePath}` : void 0,
|
|
29331
29784
|
slot.branch ? ` Branch: ${slot.branch}` : void 0,
|
|
29332
29785
|
slot.workDir ? ` Work dir: ${slot.workDir}` : void 0,
|
|
29333
|
-
slot.purpose ? ` Purpose: ${slot.purpose}` : void 0,
|
|
29334
29786
|
slot.sessionStatus ? ` Status: ${slot.sessionStatus}` : void 0,
|
|
29335
29787
|
slot.lastError ? ` Error: ${slot.lastError}` : void 0,
|
|
29336
29788
|
slot.sessionCreatedAt ? ` Since: ${slot.sessionCreatedAt}` : void 0,
|
|
@@ -29461,7 +29913,7 @@ function checkInfraPort(env3, varName, defaultPort, scanStart, scanEnd) {
|
|
|
29461
29913
|
function inspectSlotReadiness(repoPath, agentId, options = {}) {
|
|
29462
29914
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29463
29915
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29464
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
29916
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path31.basename(harnessRoot);
|
|
29465
29917
|
const usesPm2 = harnessUsesPm2(repoPath);
|
|
29466
29918
|
const blockers = [];
|
|
29467
29919
|
const remediations = [];
|
|
@@ -29663,7 +30115,7 @@ function readWorktreeBranch(worktreePath) {
|
|
|
29663
30115
|
}
|
|
29664
30116
|
function gitCommonDir3(cwd) {
|
|
29665
30117
|
const out = runGit2(cwd, "rev-parse --git-common-dir");
|
|
29666
|
-
return out ?
|
|
30118
|
+
return out ? path32.resolve(cwd, out) : void 0;
|
|
29667
30119
|
}
|
|
29668
30120
|
function sameGitCheckout3(a2, b2) {
|
|
29669
30121
|
const left2 = gitCommonDir3(a2);
|
|
@@ -29675,12 +30127,12 @@ function gitPrefix2(cwd) {
|
|
|
29675
30127
|
return out ?? "";
|
|
29676
30128
|
}
|
|
29677
30129
|
function discoverSessionWorktreePath(harnessRoot, agentId) {
|
|
29678
|
-
const worktreesRoot =
|
|
29679
|
-
if (!
|
|
30130
|
+
const worktreesRoot = path32.join(os7.homedir(), "worktrees");
|
|
30131
|
+
if (!fs32.existsSync(worktreesRoot)) return void 0;
|
|
29680
30132
|
const suffix = `-har-agent-${agentId}-`;
|
|
29681
30133
|
const relPrefix = gitPrefix2(harnessRoot);
|
|
29682
|
-
const matches =
|
|
29683
|
-
(candidate) => sameGitCheckout3(harnessRoot, candidate) &&
|
|
30134
|
+
const matches = fs32.readdirSync(worktreesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.includes(suffix)).map((entry) => path32.join(worktreesRoot, entry.name)).filter(
|
|
30135
|
+
(candidate) => sameGitCheckout3(harnessRoot, candidate) && fs32.existsSync(path32.join(candidate, relPrefix, `.env.agent.${agentId}`))
|
|
29684
30136
|
);
|
|
29685
30137
|
return matches.sort()[0];
|
|
29686
30138
|
}
|
|
@@ -29755,18 +30207,18 @@ function detectPm2Issue(projectName, agentId, session, procs) {
|
|
|
29755
30207
|
}
|
|
29756
30208
|
function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
|
|
29757
30209
|
const env3 = readHarnessEnv(harnessRoot);
|
|
29758
|
-
const projectName = env3.HARNESS_PROJECT_NAME ??
|
|
30210
|
+
const projectName = env3.HARNESS_PROJECT_NAME ?? path32.basename(harnessRoot);
|
|
29759
30211
|
const session = readSlotRegistry(harnessRoot, agentId);
|
|
29760
|
-
const legacyWorktreePath =
|
|
30212
|
+
const legacyWorktreePath = path32.join(
|
|
29761
30213
|
os7.homedir(),
|
|
29762
30214
|
"worktrees",
|
|
29763
30215
|
`${projectName}-agent-${agentId}`
|
|
29764
30216
|
);
|
|
29765
|
-
const worktreePath = session?.worktreePath &&
|
|
30217
|
+
const worktreePath = session?.worktreePath && fs32.existsSync(session.worktreePath) ? session.worktreePath : fs32.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
|
|
29766
30218
|
const workDir = resolveAgentWorkDir(harnessRoot, agentId);
|
|
29767
|
-
const envInWorkDir = workDir ?
|
|
29768
|
-
const envInRoot =
|
|
29769
|
-
const envInWorktree = worktreePath ?
|
|
30219
|
+
const envInWorkDir = workDir ? fs32.existsSync(path32.join(workDir, `.env.agent.${agentId}`)) : false;
|
|
30220
|
+
const envInRoot = fs32.existsSync(path32.join(harnessRoot, `.env.agent.${agentId}`));
|
|
30221
|
+
const envInWorktree = worktreePath ? fs32.existsSync(path32.join(worktreePath, `.env.agent.${agentId}`)) : false;
|
|
29770
30222
|
const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
|
|
29771
30223
|
const latest = latestRunForAgent(runs, agentId);
|
|
29772
30224
|
const verifyRun = runs.find((r2) => r2.agentId === agentId && r2.stageId === "verify");
|
|
@@ -29805,7 +30257,6 @@ function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
|
|
|
29805
30257
|
baseBranch: session?.baseBranch,
|
|
29806
30258
|
baseCommit: session?.baseCommit,
|
|
29807
30259
|
sessionCreatedAt: session?.createdAt,
|
|
29808
|
-
purpose: session?.purpose,
|
|
29809
30260
|
sessionStatus: session?.status,
|
|
29810
30261
|
lastError: session?.lastError,
|
|
29811
30262
|
resumeHint,
|
|
@@ -29819,7 +30270,7 @@ function collectEnvironmentStatus(repoPath) {
|
|
|
29819
30270
|
const slotIds = getAgentSlotIds(harnessRoot);
|
|
29820
30271
|
const pm2Procs = listPm2Processes2();
|
|
29821
30272
|
return {
|
|
29822
|
-
repoPath:
|
|
30273
|
+
repoPath: path32.resolve(repoPath),
|
|
29823
30274
|
harnessRoot,
|
|
29824
30275
|
gitRemote: readGitRemote(harnessRoot),
|
|
29825
30276
|
profile: manifest?.profile,
|
|
@@ -29830,13 +30281,13 @@ function collectEnvironmentStatus(repoPath) {
|
|
|
29830
30281
|
|
|
29831
30282
|
// src/core/validations.ts
|
|
29832
30283
|
var crypto3 = __toESM(require("crypto"));
|
|
29833
|
-
var
|
|
29834
|
-
var
|
|
30284
|
+
var fs34 = __toESM(require("fs"));
|
|
30285
|
+
var path34 = __toESM(require("path"));
|
|
29835
30286
|
|
|
29836
30287
|
// src/core/change-batch.ts
|
|
29837
|
-
var
|
|
30288
|
+
var fs33 = __toESM(require("fs"));
|
|
29838
30289
|
var os8 = __toESM(require("os"));
|
|
29839
|
-
var
|
|
30290
|
+
var path33 = __toESM(require("path"));
|
|
29840
30291
|
function git(checkoutDir, args, env3) {
|
|
29841
30292
|
const result = run(`git ${args}`, { cwd: checkoutDir, env: env3 });
|
|
29842
30293
|
if (result.code !== 0) {
|
|
@@ -29844,37 +30295,37 @@ function git(checkoutDir, args, env3) {
|
|
|
29844
30295
|
}
|
|
29845
30296
|
return result.stdout.trim();
|
|
29846
30297
|
}
|
|
29847
|
-
function
|
|
30298
|
+
function tryGit2(checkoutDir, args) {
|
|
29848
30299
|
const result = run(`git ${args}`, { cwd: checkoutDir });
|
|
29849
30300
|
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
29850
30301
|
}
|
|
29851
30302
|
function isCheckoutRoot(checkoutDir) {
|
|
29852
|
-
const toplevel =
|
|
30303
|
+
const toplevel = tryGit2(checkoutDir, "rev-parse --show-toplevel");
|
|
29853
30304
|
if (!toplevel) return false;
|
|
29854
30305
|
try {
|
|
29855
|
-
return
|
|
30306
|
+
return fs33.realpathSync(toplevel) === fs33.realpathSync(checkoutDir);
|
|
29856
30307
|
} catch {
|
|
29857
30308
|
return false;
|
|
29858
30309
|
}
|
|
29859
30310
|
}
|
|
29860
30311
|
function getHeadTree(checkoutDir) {
|
|
29861
|
-
return
|
|
30312
|
+
return tryGit2(checkoutDir, "rev-parse -q --verify HEAD^{tree}");
|
|
29862
30313
|
}
|
|
29863
30314
|
function getHeadSha(checkoutDir) {
|
|
29864
|
-
return
|
|
30315
|
+
return tryGit2(checkoutDir, "rev-parse -q --verify HEAD");
|
|
29865
30316
|
}
|
|
29866
30317
|
function getCurrentBranch(checkoutDir) {
|
|
29867
|
-
const branch =
|
|
30318
|
+
const branch = tryGit2(checkoutDir, "rev-parse --abbrev-ref HEAD");
|
|
29868
30319
|
return branch && branch !== "HEAD" ? branch : void 0;
|
|
29869
30320
|
}
|
|
29870
30321
|
function getEmptyTree(checkoutDir) {
|
|
29871
30322
|
return git(checkoutDir, "hash-object -t tree /dev/null");
|
|
29872
30323
|
}
|
|
29873
30324
|
function isMergeOrRebaseInProgress(checkoutDir) {
|
|
29874
|
-
if (
|
|
30325
|
+
if (tryGit2(checkoutDir, "rev-parse -q --verify MERGE_HEAD") !== void 0) return true;
|
|
29875
30326
|
for (const dir of ["rebase-merge", "rebase-apply"]) {
|
|
29876
|
-
const gitPath =
|
|
29877
|
-
if (gitPath &&
|
|
30327
|
+
const gitPath = tryGit2(checkoutDir, `rev-parse --git-path ${dir}`);
|
|
30328
|
+
if (gitPath && fs33.existsSync(path33.resolve(checkoutDir, gitPath))) return true;
|
|
29878
30329
|
}
|
|
29879
30330
|
return false;
|
|
29880
30331
|
}
|
|
@@ -29897,8 +30348,8 @@ function parseNameStatus(output) {
|
|
|
29897
30348
|
return files;
|
|
29898
30349
|
}
|
|
29899
30350
|
function computeWorktreeSnapshot(checkoutDir) {
|
|
29900
|
-
const tmpDir =
|
|
29901
|
-
const tmpIndex =
|
|
30351
|
+
const tmpDir = fs33.mkdtempSync(path33.join(os8.tmpdir(), "har-idx-"));
|
|
30352
|
+
const tmpIndex = path33.join(tmpDir, "index");
|
|
29902
30353
|
const env3 = { GIT_INDEX_FILE: tmpIndex };
|
|
29903
30354
|
try {
|
|
29904
30355
|
const headTree = getHeadTree(checkoutDir);
|
|
@@ -29921,35 +30372,35 @@ function computeWorktreeSnapshot(checkoutDir) {
|
|
|
29921
30372
|
changedFiles: parseNameStatus(diffOutput)
|
|
29922
30373
|
};
|
|
29923
30374
|
} finally {
|
|
29924
|
-
|
|
30375
|
+
fs33.rmSync(tmpDir, { recursive: true, force: true });
|
|
29925
30376
|
}
|
|
29926
30377
|
}
|
|
29927
30378
|
|
|
29928
30379
|
// src/core/validations.ts
|
|
29929
30380
|
var VALIDATIONS_DIR = "validations";
|
|
29930
30381
|
function getValidationsDir(checkoutDir) {
|
|
29931
|
-
return
|
|
30382
|
+
return path34.join(checkoutDir, ".har", VALIDATIONS_DIR);
|
|
29932
30383
|
}
|
|
29933
30384
|
function validationPath(checkoutDir, treeHash) {
|
|
29934
|
-
return
|
|
30385
|
+
return path34.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
|
|
29935
30386
|
}
|
|
29936
30387
|
function writeRecord(checkoutDir, record2) {
|
|
29937
30388
|
const dir = getValidationsDir(checkoutDir);
|
|
29938
|
-
|
|
29939
|
-
|
|
30389
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
30390
|
+
fs34.writeFileSync(
|
|
29940
30391
|
validationPath(checkoutDir, record2.treeHash),
|
|
29941
30392
|
`${JSON.stringify(record2, null, 2)}
|
|
29942
30393
|
`
|
|
29943
30394
|
);
|
|
29944
30395
|
}
|
|
29945
30396
|
function ensureValidationsIgnored(checkoutDir) {
|
|
29946
|
-
const harDir =
|
|
29947
|
-
if (!
|
|
29948
|
-
const gitignorePath =
|
|
29949
|
-
const content =
|
|
30397
|
+
const harDir = path34.join(checkoutDir, ".har");
|
|
30398
|
+
if (!fs34.existsSync(harDir)) return;
|
|
30399
|
+
const gitignorePath = path34.join(harDir, ".gitignore");
|
|
30400
|
+
const content = fs34.existsSync(gitignorePath) ? fs34.readFileSync(gitignorePath, "utf8") : "";
|
|
29950
30401
|
if (content.split("\n").some((line) => line.trim() === `${VALIDATIONS_DIR}/`)) return;
|
|
29951
30402
|
const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
|
|
29952
|
-
|
|
30403
|
+
fs34.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
|
|
29953
30404
|
`);
|
|
29954
30405
|
}
|
|
29955
30406
|
function recordValidation(input) {
|
|
@@ -29966,8 +30417,8 @@ function recordValidation(input) {
|
|
|
29966
30417
|
treeHash: snapshot.treeHash,
|
|
29967
30418
|
headSha: snapshot.headSha,
|
|
29968
30419
|
branch: snapshot.branch,
|
|
29969
|
-
workDir:
|
|
29970
|
-
harnessRoot:
|
|
30420
|
+
workDir: path34.resolve(input.checkoutDir),
|
|
30421
|
+
harnessRoot: path34.resolve(input.harnessRoot),
|
|
29971
30422
|
agentId: input.agentId,
|
|
29972
30423
|
status: input.status,
|
|
29973
30424
|
full: input.full,
|
|
@@ -29979,16 +30430,16 @@ function recordValidation(input) {
|
|
|
29979
30430
|
committedAt: existing?.committedAt
|
|
29980
30431
|
});
|
|
29981
30432
|
writeRecord(input.checkoutDir, record2);
|
|
29982
|
-
if (
|
|
30433
|
+
if (path34.resolve(input.harnessRoot) !== path34.resolve(input.checkoutDir)) {
|
|
29983
30434
|
writeRecord(input.harnessRoot, record2);
|
|
29984
30435
|
}
|
|
29985
30436
|
return record2;
|
|
29986
30437
|
}
|
|
29987
30438
|
function findValidation(checkoutDir, treeHash) {
|
|
29988
30439
|
const file = validationPath(checkoutDir, treeHash);
|
|
29989
|
-
if (!
|
|
30440
|
+
if (!fs34.existsSync(file)) return void 0;
|
|
29990
30441
|
try {
|
|
29991
|
-
return ValidationRecordSchema.parse(JSON.parse(
|
|
30442
|
+
return ValidationRecordSchema.parse(JSON.parse(fs34.readFileSync(file, "utf8")));
|
|
29992
30443
|
} catch {
|
|
29993
30444
|
return void 0;
|
|
29994
30445
|
}
|
|
@@ -30003,19 +30454,19 @@ function attachCommit(checkoutDir, treeHash, commitSha) {
|
|
|
30003
30454
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30004
30455
|
};
|
|
30005
30456
|
writeRecord(checkoutDir, updated);
|
|
30006
|
-
if (record2.harnessRoot &&
|
|
30457
|
+
if (record2.harnessRoot && path34.resolve(record2.harnessRoot) !== path34.resolve(checkoutDir) && fs34.existsSync(path34.join(record2.harnessRoot, ".har"))) {
|
|
30007
30458
|
writeRecord(record2.harnessRoot, updated);
|
|
30008
30459
|
}
|
|
30009
30460
|
return updated;
|
|
30010
30461
|
}
|
|
30011
30462
|
function listValidations(harnessRoot) {
|
|
30012
30463
|
const dir = getValidationsDir(harnessRoot);
|
|
30013
|
-
if (!
|
|
30464
|
+
if (!fs34.existsSync(dir)) return [];
|
|
30014
30465
|
const records = [];
|
|
30015
|
-
for (const entry of
|
|
30466
|
+
for (const entry of fs34.readdirSync(dir)) {
|
|
30016
30467
|
if (!entry.endsWith(".json")) continue;
|
|
30017
30468
|
try {
|
|
30018
|
-
records.push(ValidationRecordSchema.parse(JSON.parse(
|
|
30469
|
+
records.push(ValidationRecordSchema.parse(JSON.parse(fs34.readFileSync(path34.join(dir, entry), "utf8"))));
|
|
30019
30470
|
} catch {
|
|
30020
30471
|
}
|
|
30021
30472
|
}
|
|
@@ -30044,105 +30495,19 @@ function createRemoteExecutor(apiUrl, apiKey) {
|
|
|
30044
30495
|
return new RemoteExecutor(url, key);
|
|
30045
30496
|
}
|
|
30046
30497
|
|
|
30047
|
-
// src/core/
|
|
30048
|
-
|
|
30049
|
-
var os9 = __toESM(require("os"));
|
|
30050
|
-
var path34 = __toESM(require("path"));
|
|
30051
|
-
var DEFAULT_SIGNALS_ON = {
|
|
30052
|
-
metrics: true,
|
|
30053
|
-
logs: true,
|
|
30054
|
-
prompts: false,
|
|
30055
|
-
traces: false
|
|
30056
|
-
};
|
|
30057
|
-
var DEFAULT_SIGNALS_OFF = {
|
|
30058
|
-
metrics: false,
|
|
30059
|
-
logs: false,
|
|
30060
|
-
prompts: false,
|
|
30061
|
-
traces: false
|
|
30062
|
-
};
|
|
30063
|
-
function getPreferencePath() {
|
|
30064
|
-
if (process.env.HAR_TELEMETRY_CONFIG_PATH) {
|
|
30065
|
-
return path34.resolve(process.env.HAR_TELEMETRY_CONFIG_PATH);
|
|
30066
|
-
}
|
|
30067
|
-
return path34.join(os9.homedir(), ".har", "telemetry.json");
|
|
30068
|
-
}
|
|
30069
|
-
function parseEnvOverride(raw) {
|
|
30070
|
-
if (raw === void 0 || raw === "") return void 0;
|
|
30071
|
-
const normalized = raw.trim().toLowerCase();
|
|
30072
|
-
if (["0", "false", "off", "no", "disabled"].includes(normalized)) return false;
|
|
30073
|
-
if (["1", "true", "on", "yes", "enabled"].includes(normalized)) return true;
|
|
30074
|
-
return void 0;
|
|
30075
|
-
}
|
|
30076
|
-
function normalizeSignals(enabled, raw) {
|
|
30077
|
-
if (!enabled) return { ...DEFAULT_SIGNALS_OFF };
|
|
30078
|
-
return {
|
|
30079
|
-
metrics: raw?.metrics !== false,
|
|
30080
|
-
logs: raw?.logs !== false,
|
|
30081
|
-
prompts: raw?.prompts === true,
|
|
30082
|
-
traces: raw?.traces === true
|
|
30083
|
-
};
|
|
30084
|
-
}
|
|
30085
|
-
function readTelemetryPreference() {
|
|
30086
|
-
const preferencePath = getPreferencePath();
|
|
30087
|
-
try {
|
|
30088
|
-
if (!fs34.existsSync(preferencePath)) {
|
|
30089
|
-
return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
|
|
30090
|
-
}
|
|
30091
|
-
const parsed = JSON.parse(fs34.readFileSync(preferencePath, "utf8"));
|
|
30092
|
-
const enabled = parsed.enabled !== false;
|
|
30093
|
-
return {
|
|
30094
|
-
enabled,
|
|
30095
|
-
signals: normalizeSignals(enabled, parsed.signals),
|
|
30096
|
-
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : void 0
|
|
30097
|
-
};
|
|
30098
|
-
} catch {
|
|
30099
|
-
return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
|
|
30100
|
-
}
|
|
30101
|
-
}
|
|
30102
|
-
function writeTelemetryPreference(enabled, signals) {
|
|
30103
|
-
const current = readTelemetryPreference();
|
|
30104
|
-
const nextSignals = normalizeSignals(enabled, {
|
|
30105
|
-
...current.signals,
|
|
30106
|
-
...signals
|
|
30107
|
-
});
|
|
30108
|
-
const preference = {
|
|
30109
|
-
enabled,
|
|
30110
|
-
signals: enabled ? nextSignals : { ...DEFAULT_SIGNALS_OFF },
|
|
30111
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30112
|
-
};
|
|
30113
|
-
const preferencePath = getPreferencePath();
|
|
30114
|
-
fs34.mkdirSync(path34.dirname(preferencePath), { recursive: true });
|
|
30115
|
-
fs34.writeFileSync(preferencePath, JSON.stringify(preference, null, 2) + "\n");
|
|
30116
|
-
return preference;
|
|
30117
|
-
}
|
|
30118
|
-
function isTelemetryEnabled() {
|
|
30119
|
-
const envOverride = parseEnvOverride(process.env.HAR_TELEMETRY);
|
|
30120
|
-
if (envOverride !== void 0) return envOverride;
|
|
30121
|
-
return readTelemetryPreference().enabled;
|
|
30122
|
-
}
|
|
30123
|
-
function getTelemetrySignals() {
|
|
30124
|
-
if (!isTelemetryEnabled()) return { ...DEFAULT_SIGNALS_OFF };
|
|
30125
|
-
return readTelemetryPreference().signals;
|
|
30126
|
-
}
|
|
30127
|
-
function getTelemetryPreferencePath() {
|
|
30128
|
-
return getPreferencePath();
|
|
30129
|
-
}
|
|
30130
|
-
var TELEMETRY_SIGNALS = [
|
|
30131
|
-
"Metrics (default): tokens and estimated USD cost via OTEL metrics",
|
|
30132
|
-
"Logs/events (default): session events (tool calls, api_request) without prompt bodies",
|
|
30133
|
-
"Prompts (opt-in): user/assistant text via OTEL_LOG_USER_PROMPTS \u2014 har telemetry on --prompts",
|
|
30134
|
-
"Traces (opt-in): thin span ingest when CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1",
|
|
30135
|
-
"Fallback: har control sync harvests local Claude/Codex session files when OTEL is missing"
|
|
30136
|
-
];
|
|
30498
|
+
// src/core/control-sync.ts
|
|
30499
|
+
init_telemetry_config();
|
|
30137
30500
|
|
|
30138
30501
|
// src/core/usage-harvest/claude.ts
|
|
30139
|
-
var
|
|
30502
|
+
var fs37 = __toESM(require("fs"));
|
|
30140
30503
|
var os10 = __toESM(require("os"));
|
|
30141
|
-
var
|
|
30504
|
+
var path37 = __toESM(require("path"));
|
|
30505
|
+
init_telemetry_config();
|
|
30142
30506
|
|
|
30143
30507
|
// src/core/telemetry-env.ts
|
|
30144
|
-
var
|
|
30145
|
-
var
|
|
30508
|
+
var fs36 = __toESM(require("fs"));
|
|
30509
|
+
var path36 = __toESM(require("path"));
|
|
30510
|
+
init_control_config();
|
|
30146
30511
|
function escapeAttrValue(value) {
|
|
30147
30512
|
return value.replace(/[,=]/g, "_");
|
|
30148
30513
|
}
|
|
@@ -30155,7 +30520,6 @@ function buildOtelResourceAttributes(attrs) {
|
|
|
30155
30520
|
];
|
|
30156
30521
|
if (attrs.branch) parts.push(`har.branch=${escapeAttrValue(attrs.branch)}`);
|
|
30157
30522
|
if (attrs.suffix) parts.push(`har.suffix=${escapeAttrValue(attrs.suffix)}`);
|
|
30158
|
-
if (attrs.purpose) parts.push(`har.purpose=${escapeAttrValue(attrs.purpose)}`);
|
|
30159
30523
|
return parts.join(",");
|
|
30160
30524
|
}
|
|
30161
30525
|
function buildSessionKey(input) {
|
|
@@ -30163,54 +30527,27 @@ function buildSessionKey(input) {
|
|
|
30163
30527
|
const stamp = (input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()).replace(/[:.]/g, "-");
|
|
30164
30528
|
return `agent-${input.agentId}-${input.suffix ?? stamp}`;
|
|
30165
30529
|
}
|
|
30166
|
-
function buildTelemetryEnvBlock(attrs
|
|
30530
|
+
function buildTelemetryEnvBlock(attrs) {
|
|
30167
30531
|
const apiUrl = getControlApiUrl().replace(/\/$/, "");
|
|
30168
30532
|
const lines = [
|
|
30169
30533
|
"",
|
|
30170
30534
|
"# HAR session attribution (generated)",
|
|
30171
30535
|
`HAR_SESSION_KEY=${attrs.sessionKey}`,
|
|
30172
30536
|
`HAR_CONTROL_API_URL=${apiUrl}`,
|
|
30173
|
-
`OTEL_RESOURCE_ATTRIBUTES=${buildOtelResourceAttributes(attrs)}
|
|
30537
|
+
`OTEL_RESOURCE_ATTRIBUTES=${buildOtelResourceAttributes(attrs)}`,
|
|
30538
|
+
"# Agent telemetry: opentelemetry-hooks \u2192 Mission Control (har telemetry on|off)"
|
|
30174
30539
|
];
|
|
30175
|
-
const injectOtel = isTelemetryEnabled() && options?.otelReady !== false;
|
|
30176
|
-
if (injectOtel) {
|
|
30177
|
-
const signals = getTelemetrySignals();
|
|
30178
|
-
lines.push(
|
|
30179
|
-
"# HAR telemetry \u2192 Mission Control OTLP (disable: har telemetry off)",
|
|
30180
|
-
"CLAUDE_CODE_ENABLE_TELEMETRY=1",
|
|
30181
|
-
"OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
|
|
30182
|
-
`OTEL_EXPORTER_OTLP_ENDPOINT=${apiUrl}/api/otel`
|
|
30183
|
-
);
|
|
30184
|
-
if (signals.metrics) {
|
|
30185
|
-
lines.push("OTEL_METRICS_EXPORTER=otlp");
|
|
30186
|
-
}
|
|
30187
|
-
if (signals.logs) {
|
|
30188
|
-
lines.push("OTEL_LOGS_EXPORTER=otlp");
|
|
30189
|
-
}
|
|
30190
|
-
if (signals.prompts) {
|
|
30191
|
-
lines.push(
|
|
30192
|
-
"OTEL_LOG_USER_PROMPTS=1",
|
|
30193
|
-
"OTEL_LOG_ASSISTANT_RESPONSES=1"
|
|
30194
|
-
);
|
|
30195
|
-
}
|
|
30196
|
-
if (signals.traces) {
|
|
30197
|
-
lines.push(
|
|
30198
|
-
"OTEL_TRACES_EXPORTER=otlp",
|
|
30199
|
-
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1"
|
|
30200
|
-
);
|
|
30201
|
-
}
|
|
30202
|
-
}
|
|
30203
30540
|
return lines.join("\n") + "\n";
|
|
30204
30541
|
}
|
|
30205
30542
|
var TELEMETRY_MARKER_START = "# HAR session attribution (generated)";
|
|
30206
30543
|
var TELEMETRY_MARKER_END = "# end HAR telemetry";
|
|
30207
|
-
function appendTelemetryEnvToFile(envFilePath, attrs
|
|
30208
|
-
const block = buildTelemetryEnvBlock(attrs
|
|
30544
|
+
function appendTelemetryEnvToFile(envFilePath, attrs) {
|
|
30545
|
+
const block = buildTelemetryEnvBlock(attrs).trimEnd() + `
|
|
30209
30546
|
${TELEMETRY_MARKER_END}
|
|
30210
30547
|
`;
|
|
30211
30548
|
let existing = "";
|
|
30212
|
-
if (
|
|
30213
|
-
existing =
|
|
30549
|
+
if (fs36.existsSync(envFilePath)) {
|
|
30550
|
+
existing = fs36.readFileSync(envFilePath, "utf8");
|
|
30214
30551
|
const start = existing.indexOf(TELEMETRY_MARKER_START);
|
|
30215
30552
|
if (start >= 0) {
|
|
30216
30553
|
const end = existing.indexOf(TELEMETRY_MARKER_END, start);
|
|
@@ -30221,44 +30558,28 @@ ${TELEMETRY_MARKER_END}
|
|
|
30221
30558
|
}
|
|
30222
30559
|
}
|
|
30223
30560
|
}
|
|
30224
|
-
|
|
30561
|
+
fs36.mkdirSync(path36.dirname(envFilePath), { recursive: true });
|
|
30225
30562
|
const combined = (existing.replace(/\n+$/, "\n") + block).replace(/^\n+/, "");
|
|
30226
|
-
|
|
30227
|
-
}
|
|
30228
|
-
function buildCodexOtelSnippet(attrs) {
|
|
30229
|
-
const apiUrl = getControlApiUrl().replace(/\/$/, "");
|
|
30230
|
-
const resource = buildOtelResourceAttributes(attrs);
|
|
30231
|
-
return `# Merge into ~/.codex/config.toml (HAR does not overwrite your config)
|
|
30232
|
-
# Generated for session ${attrs.sessionKey}
|
|
30233
|
-
|
|
30234
|
-
[otel]
|
|
30235
|
-
exporter = "otlp-http"
|
|
30236
|
-
endpoint = "${apiUrl}/api/otel"
|
|
30237
|
-
metrics_exporter = "otlp-http"
|
|
30238
|
-
|
|
30239
|
-
# Optional: set resource attributes via environment when starting Codex:
|
|
30240
|
-
# export OTEL_RESOURCE_ATTRIBUTES="${resource}"
|
|
30241
|
-
# export HAR_SESSION_KEY="${attrs.sessionKey}"
|
|
30242
|
-
`;
|
|
30563
|
+
fs36.writeFileSync(envFilePath, combined.endsWith("\n") ? combined : combined + "\n");
|
|
30243
30564
|
}
|
|
30244
30565
|
|
|
30245
30566
|
// src/core/usage-harvest/claude.ts
|
|
30246
30567
|
function pathsMatch(candidate, targets) {
|
|
30247
|
-
const norm =
|
|
30568
|
+
const norm = path37.resolve(candidate);
|
|
30248
30569
|
return targets.some((t2) => {
|
|
30249
|
-
const target =
|
|
30250
|
-
return norm === target || norm.startsWith(target +
|
|
30570
|
+
const target = path37.resolve(t2);
|
|
30571
|
+
return norm === target || norm.startsWith(target + path37.sep) || target.startsWith(norm + path37.sep);
|
|
30251
30572
|
});
|
|
30252
30573
|
}
|
|
30253
30574
|
function claudeProjectsRoot() {
|
|
30254
|
-
return process.env.HAR_CLAUDE_PROJECTS_DIR ?
|
|
30575
|
+
return process.env.HAR_CLAUDE_PROJECTS_DIR ? path37.resolve(process.env.HAR_CLAUDE_PROJECTS_DIR) : path37.join(os10.homedir(), ".claude", "projects");
|
|
30255
30576
|
}
|
|
30256
30577
|
function encodeClaudeProjectDir(cwd) {
|
|
30257
30578
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
30258
30579
|
}
|
|
30259
30580
|
function readJsonlRecords(filePath) {
|
|
30260
|
-
if (!
|
|
30261
|
-
const lines =
|
|
30581
|
+
if (!fs37.existsSync(filePath)) return [];
|
|
30582
|
+
const lines = fs37.readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
30262
30583
|
const out = [];
|
|
30263
30584
|
for (const line of lines) {
|
|
30264
30585
|
const trimmed = line.trim();
|
|
@@ -30301,16 +30622,16 @@ function findMatchingClaudeTranscripts(slot) {
|
|
|
30301
30622
|
const targets = [slot.workDir, slot.worktreePath].filter(Boolean);
|
|
30302
30623
|
if (targets.length === 0) return [];
|
|
30303
30624
|
const root = claudeProjectsRoot();
|
|
30304
|
-
if (!
|
|
30625
|
+
if (!fs37.existsSync(root)) return [];
|
|
30305
30626
|
const matches = [];
|
|
30306
|
-
for (const entry of
|
|
30627
|
+
for (const entry of fs37.readdirSync(root, { withFileTypes: true })) {
|
|
30307
30628
|
if (!entry.isDirectory()) continue;
|
|
30308
|
-
const projectDir =
|
|
30629
|
+
const projectDir = path37.join(root, entry.name);
|
|
30309
30630
|
const encodedHit = targets.some((t2) => entry.name.includes(encodeClaudeProjectDir(t2).slice(0, 40)));
|
|
30310
|
-
for (const file of
|
|
30631
|
+
for (const file of fs37.readdirSync(projectDir)) {
|
|
30311
30632
|
if (!file.endsWith(".jsonl")) continue;
|
|
30312
|
-
const filePath =
|
|
30313
|
-
const stat =
|
|
30633
|
+
const filePath = path37.join(projectDir, file);
|
|
30634
|
+
const stat = fs37.statSync(filePath);
|
|
30314
30635
|
const records = readJsonlRecords(filePath);
|
|
30315
30636
|
let cwdHit = encodedHit;
|
|
30316
30637
|
for (const record2 of records) {
|
|
@@ -30421,30 +30742,30 @@ function harvestClaudeEvents(slot) {
|
|
|
30421
30742
|
}
|
|
30422
30743
|
|
|
30423
30744
|
// src/core/usage-harvest/codex.ts
|
|
30424
|
-
var
|
|
30745
|
+
var fs38 = __toESM(require("fs"));
|
|
30425
30746
|
var os11 = __toESM(require("os"));
|
|
30426
|
-
var
|
|
30747
|
+
var path38 = __toESM(require("path"));
|
|
30427
30748
|
function pathsMatch2(candidate, targets) {
|
|
30428
|
-
const norm =
|
|
30749
|
+
const norm = path38.resolve(candidate);
|
|
30429
30750
|
return targets.some((t2) => {
|
|
30430
|
-
const target =
|
|
30431
|
-
return norm === target || norm.startsWith(target +
|
|
30751
|
+
const target = path38.resolve(t2);
|
|
30752
|
+
return norm === target || norm.startsWith(target + path38.sep) || target.startsWith(norm + path38.sep);
|
|
30432
30753
|
});
|
|
30433
30754
|
}
|
|
30434
30755
|
function codexSessionsRoot() {
|
|
30435
|
-
return process.env.HAR_CODEX_SESSIONS_DIR ?
|
|
30756
|
+
return process.env.HAR_CODEX_SESSIONS_DIR ? path38.resolve(process.env.HAR_CODEX_SESSIONS_DIR) : path38.join(os11.homedir(), ".codex", "sessions");
|
|
30436
30757
|
}
|
|
30437
30758
|
function walkJsonlFiles(dir, out = []) {
|
|
30438
|
-
if (!
|
|
30439
|
-
for (const entry of
|
|
30440
|
-
const full =
|
|
30759
|
+
if (!fs38.existsSync(dir)) return out;
|
|
30760
|
+
for (const entry of fs38.readdirSync(dir, { withFileTypes: true })) {
|
|
30761
|
+
const full = path38.join(dir, entry.name);
|
|
30441
30762
|
if (entry.isDirectory()) walkJsonlFiles(full, out);
|
|
30442
30763
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push(full);
|
|
30443
30764
|
}
|
|
30444
30765
|
return out;
|
|
30445
30766
|
}
|
|
30446
30767
|
function readJsonl(filePath) {
|
|
30447
|
-
const lines =
|
|
30768
|
+
const lines = fs38.readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
30448
30769
|
const out = [];
|
|
30449
30770
|
for (const line of lines) {
|
|
30450
30771
|
const trimmed = line.trim();
|
|
@@ -30508,7 +30829,7 @@ function harvestCodexUsage(slot) {
|
|
|
30508
30829
|
const extracted = extractCodexTokens(records);
|
|
30509
30830
|
if (!extracted.cwd || !pathsMatch2(extracted.cwd, targets)) continue;
|
|
30510
30831
|
if (extracted.tokensInput + extracted.tokensOutput + extracted.tokensCacheRead === 0) continue;
|
|
30511
|
-
const mtime =
|
|
30832
|
+
const mtime = fs38.statSync(file).mtimeMs;
|
|
30512
30833
|
if (mtime >= bestMtime) {
|
|
30513
30834
|
bestMtime = mtime;
|
|
30514
30835
|
best = extracted;
|
|
@@ -30578,7 +30899,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
|
|
|
30578
30899
|
const deadline = Date.now() + timeoutMs;
|
|
30579
30900
|
while (Date.now() < deadline) {
|
|
30580
30901
|
if (await isControlApiReachable(apiUrl)) return true;
|
|
30581
|
-
await new Promise((
|
|
30902
|
+
await new Promise((resolve35) => setTimeout(resolve35, intervalMs));
|
|
30582
30903
|
}
|
|
30583
30904
|
return false;
|
|
30584
30905
|
}
|
|
@@ -30590,7 +30911,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30590
30911
|
}
|
|
30591
30912
|
const repoPaths = new Set(listRegisteredRepos());
|
|
30592
30913
|
if (options?.cwd) {
|
|
30593
|
-
const cwd =
|
|
30914
|
+
const cwd = canonicalizeControlRepoPath(options.cwd);
|
|
30594
30915
|
if (readManifest(cwd)) repoPaths.add(cwd);
|
|
30595
30916
|
}
|
|
30596
30917
|
try {
|
|
@@ -30598,7 +30919,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30598
30919
|
if (listResponse.ok) {
|
|
30599
30920
|
const repos = await listResponse.json();
|
|
30600
30921
|
for (const repo of repos) {
|
|
30601
|
-
const resolved =
|
|
30922
|
+
const resolved = canonicalizeControlRepoPath(repo.path);
|
|
30602
30923
|
if (readManifest(resolved)) repoPaths.add(resolved);
|
|
30603
30924
|
}
|
|
30604
30925
|
}
|
|
@@ -30617,7 +30938,7 @@ async function syncAllKnownReposWithControl(options) {
|
|
|
30617
30938
|
return { synced, failed };
|
|
30618
30939
|
}
|
|
30619
30940
|
async function registerRepoWithControl(options) {
|
|
30620
|
-
const repoPath =
|
|
30941
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
30621
30942
|
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
30622
30943
|
const manifest = readManifest(repoPath);
|
|
30623
30944
|
const stagesRegistry = readStageRegistry(repoPath);
|
|
@@ -30630,7 +30951,7 @@ async function registerRepoWithControl(options) {
|
|
|
30630
30951
|
return postJson(`${apiUrl}/api/repos`, body, options.dryRun);
|
|
30631
30952
|
}
|
|
30632
30953
|
async function syncRepoWithControl(options) {
|
|
30633
|
-
const repoPath =
|
|
30954
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
30634
30955
|
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
30635
30956
|
if (options.cloud) {
|
|
30636
30957
|
const remote = createRemoteExecutor();
|
|
@@ -30658,7 +30979,7 @@ async function syncRepoWithControl(options) {
|
|
|
30658
30979
|
const listResponse = await fetch(`${apiUrl}/api/repos`);
|
|
30659
30980
|
if (!listResponse.ok) throw new Error("Failed to list repos from Control API");
|
|
30660
30981
|
const repos = await listResponse.json();
|
|
30661
|
-
const existing = repos.find((r2) =>
|
|
30982
|
+
const existing = repos.find((r2) => path39.resolve(r2.path) === repoPath);
|
|
30662
30983
|
if (!existing) throw new Error(`Repo not registered: ${repoPath}`);
|
|
30663
30984
|
await syncRepoRunsAndSlots(apiUrl, existing.id, repoPath, options.dryRun);
|
|
30664
30985
|
return;
|
|
@@ -30749,7 +31070,12 @@ async function syncRepoWithControlAsync(repoPath) {
|
|
|
30749
31070
|
}
|
|
30750
31071
|
return;
|
|
30751
31072
|
}
|
|
30752
|
-
|
|
31073
|
+
const canonical = canonicalizeControlRepoPath(repoPath);
|
|
31074
|
+
await withTimeout(
|
|
31075
|
+
syncRepoWithControl({ repoPath: canonical, apiUrl }),
|
|
31076
|
+
SYNC_TIMEOUT_MS,
|
|
31077
|
+
"control sync"
|
|
31078
|
+
);
|
|
30753
31079
|
} catch (err) {
|
|
30754
31080
|
if (verbose) {
|
|
30755
31081
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -30775,9 +31101,12 @@ function checkLaunchGuard2(repoPath, agentId, options = {}) {
|
|
|
30775
31101
|
return { allowed: true, slot: occupied.slot, readiness };
|
|
30776
31102
|
}
|
|
30777
31103
|
|
|
31104
|
+
// src/core/telemetry-ensure.ts
|
|
31105
|
+
init_control_config();
|
|
31106
|
+
|
|
30778
31107
|
// src/core/control-lifecycle.ts
|
|
30779
31108
|
var import_child_process8 = require("child_process");
|
|
30780
|
-
var
|
|
31109
|
+
var path40 = __toESM(require("path"));
|
|
30781
31110
|
|
|
30782
31111
|
// src/core/control-image.ts
|
|
30783
31112
|
var DEFAULT_CONTROL_IMAGE = "theosfactory/har-control";
|
|
@@ -30795,15 +31124,16 @@ function shouldBuildControlLocally() {
|
|
|
30795
31124
|
}
|
|
30796
31125
|
|
|
30797
31126
|
// src/core/control-lifecycle.ts
|
|
31127
|
+
init_control_config();
|
|
30798
31128
|
var CONTROL_CONTAINER_NAME = "har-control";
|
|
30799
31129
|
var CONTROL_DATA_VOLUME = "har_control_data";
|
|
30800
31130
|
var CONTROL_CONTAINER_PORT = 3847;
|
|
30801
31131
|
var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
|
|
30802
31132
|
function resolveControlDir() {
|
|
30803
|
-
return
|
|
31133
|
+
return path40.resolve(__dirname, "..", "control");
|
|
30804
31134
|
}
|
|
30805
31135
|
function resolveControlBuildContext() {
|
|
30806
|
-
return
|
|
31136
|
+
return path40.resolve(resolveControlDir(), "..");
|
|
30807
31137
|
}
|
|
30808
31138
|
function buildDockerRunArgs(options) {
|
|
30809
31139
|
const name = options.containerName ?? CONTROL_CONTAINER_NAME;
|
|
@@ -30859,7 +31189,7 @@ async function startMissionControl(options) {
|
|
|
30859
31189
|
"-t",
|
|
30860
31190
|
imageRef,
|
|
30861
31191
|
"-f",
|
|
30862
|
-
|
|
31192
|
+
path40.join(resolveControlDir(), "Dockerfile"),
|
|
30863
31193
|
resolveControlBuildContext()
|
|
30864
31194
|
]);
|
|
30865
31195
|
if (buildCode !== 0) {
|
|
@@ -30903,6 +31233,7 @@ async function startControlAndSync(options) {
|
|
|
30903
31233
|
}
|
|
30904
31234
|
|
|
30905
31235
|
// src/core/telemetry-ensure.ts
|
|
31236
|
+
init_telemetry_config();
|
|
30906
31237
|
async function ensureTelemetryInfrastructure(options) {
|
|
30907
31238
|
const apiUrl = getControlApiUrl();
|
|
30908
31239
|
const telemetryEnabled = isTelemetryEnabled();
|
|
@@ -31001,7 +31332,8 @@ async function ensureTelemetryInfrastructure(options) {
|
|
|
31001
31332
|
}
|
|
31002
31333
|
|
|
31003
31334
|
// src/core/run-service.ts
|
|
31004
|
-
|
|
31335
|
+
init_telemetry_config();
|
|
31336
|
+
var path42 = __toESM(require("path"));
|
|
31005
31337
|
function extractShellOutput(result) {
|
|
31006
31338
|
const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
|
|
31007
31339
|
const stderr = result.logs?.find((log) => log.stream === "stderr")?.content ?? "";
|
|
@@ -31117,23 +31449,31 @@ var RunService = class {
|
|
|
31117
31449
|
worktreePath: slot.worktreePath,
|
|
31118
31450
|
branch: slot.branch,
|
|
31119
31451
|
dirty: slot.dirty,
|
|
31120
|
-
sessionCreatedAt: slot.sessionCreatedAt
|
|
31121
|
-
purpose: slot.purpose
|
|
31452
|
+
sessionCreatedAt: slot.sessionCreatedAt
|
|
31122
31453
|
} : void 0
|
|
31123
31454
|
};
|
|
31124
31455
|
}
|
|
31125
31456
|
let telemetryBanner = "";
|
|
31126
|
-
let otelReady = false;
|
|
31127
31457
|
if (isTelemetryEnabled() && process.env.NODE_ENV !== "test") {
|
|
31128
31458
|
const ensured = await ensureTelemetryInfrastructure({ startIfNeeded: true });
|
|
31129
|
-
otelReady = ensured.otelReady;
|
|
31130
31459
|
if (ensured.message) {
|
|
31131
31460
|
telemetryBanner += `${ensured.message}
|
|
31132
|
-
Usage from Claude
|
|
31461
|
+
Usage from Cursor / Claude / Codex (opentelemetry-hooks) will appear under Worktrees. Disable: har telemetry off
|
|
31133
31462
|
`;
|
|
31134
31463
|
}
|
|
31135
31464
|
if (ensured.warning) {
|
|
31136
31465
|
telemetryBanner += `${ensured.warning}
|
|
31466
|
+
`;
|
|
31467
|
+
}
|
|
31468
|
+
try {
|
|
31469
|
+
const { ensureOtelHooks: ensureOtelHooks2 } = await Promise.resolve().then(() => (init_otel_hooks(), otel_hooks_exports));
|
|
31470
|
+
const hooks = ensureOtelHooks2({ setupAgents: true });
|
|
31471
|
+
if (hooks.message) telemetryBanner += `${hooks.message}
|
|
31472
|
+
`;
|
|
31473
|
+
if (hooks.warning) telemetryBanner += `${hooks.warning}
|
|
31474
|
+
`;
|
|
31475
|
+
} catch (err) {
|
|
31476
|
+
telemetryBanner += `opentelemetry-hooks setup skipped: ${err instanceof Error ? err.message : String(err)}
|
|
31137
31477
|
`;
|
|
31138
31478
|
}
|
|
31139
31479
|
}
|
|
@@ -31147,8 +31487,7 @@ Usage from Claude Code / Codex will appear under Worktrees. Disable: har telemet
|
|
|
31147
31487
|
claude: options.claude,
|
|
31148
31488
|
confirmReplace: options.confirmReplace,
|
|
31149
31489
|
force: options.force,
|
|
31150
|
-
resume: options.resume
|
|
31151
|
-
purpose: options.purpose
|
|
31490
|
+
resume: options.resume
|
|
31152
31491
|
},
|
|
31153
31492
|
trigger: "cli"
|
|
31154
31493
|
});
|
|
@@ -31167,21 +31506,16 @@ Usage from Claude Code / Codex will appear under Worktrees. Disable: har telemet
|
|
|
31167
31506
|
suffix: session.suffix,
|
|
31168
31507
|
createdAt: session.createdAt
|
|
31169
31508
|
});
|
|
31170
|
-
const envFile =
|
|
31509
|
+
const envFile = path42.join(session.workDir, `.env.agent.${options.agentId}`);
|
|
31171
31510
|
try {
|
|
31172
|
-
appendTelemetryEnvToFile(
|
|
31173
|
-
|
|
31174
|
-
|
|
31175
|
-
|
|
31176
|
-
|
|
31177
|
-
|
|
31178
|
-
|
|
31179
|
-
|
|
31180
|
-
suffix: session.suffix,
|
|
31181
|
-
purpose: session.purpose
|
|
31182
|
-
},
|
|
31183
|
-
{ otelReady: otelReady && isTelemetryEnabled() }
|
|
31184
|
-
);
|
|
31511
|
+
appendTelemetryEnvToFile(envFile, {
|
|
31512
|
+
sessionKey,
|
|
31513
|
+
agentId: options.agentId,
|
|
31514
|
+
repoPath: path42.resolve(options.repoPath),
|
|
31515
|
+
workDir: session.workDir,
|
|
31516
|
+
branch: session.branch,
|
|
31517
|
+
suffix: session.suffix
|
|
31518
|
+
});
|
|
31185
31519
|
} catch {
|
|
31186
31520
|
}
|
|
31187
31521
|
}
|
|
@@ -31467,9 +31801,6 @@ var envCommand = {
|
|
|
31467
31801
|
type: "boolean",
|
|
31468
31802
|
default: false,
|
|
31469
31803
|
describe: "Resume a failed or partial launch without creating a new worktree"
|
|
31470
|
-
}).option("purpose", {
|
|
31471
|
-
type: "string",
|
|
31472
|
-
describe: "Optional session label stored in the slot registry (shown in status and occupied-slot warnings)"
|
|
31473
31804
|
}),
|
|
31474
31805
|
handleLaunch
|
|
31475
31806
|
).command(
|
|
@@ -31543,7 +31874,7 @@ var envCommand = {
|
|
|
31543
31874
|
}
|
|
31544
31875
|
};
|
|
31545
31876
|
async function handleInit(argv) {
|
|
31546
|
-
const repoPath =
|
|
31877
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31547
31878
|
header("har env init");
|
|
31548
31879
|
info(`Repository: ${repoPath}`);
|
|
31549
31880
|
try {
|
|
@@ -31601,7 +31932,7 @@ async function handleInit(argv) {
|
|
|
31601
31932
|
}
|
|
31602
31933
|
}
|
|
31603
31934
|
async function handleMaintain(argv) {
|
|
31604
|
-
const repoPath =
|
|
31935
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31605
31936
|
header("har env maintain");
|
|
31606
31937
|
info(`Repository: ${repoPath}`);
|
|
31607
31938
|
try {
|
|
@@ -31699,7 +32030,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
|
|
|
31699
32030
|
if (autoYes) {
|
|
31700
32031
|
const proposal = readAgentMdProposal(repoPath);
|
|
31701
32032
|
if (proposal) {
|
|
31702
|
-
writeFileSafe(
|
|
32033
|
+
writeFileSafe(path43.join(repoPath, "AGENT.md"), proposal.content);
|
|
31703
32034
|
clearAgentMdProposal(repoPath);
|
|
31704
32035
|
info("Applied AGENT.md proposal (--yes)");
|
|
31705
32036
|
}
|
|
@@ -31708,7 +32039,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
|
|
|
31708
32039
|
await promptApplyAgentMdProposal(repoPath);
|
|
31709
32040
|
}
|
|
31710
32041
|
async function handleAddStage(argv) {
|
|
31711
|
-
const repoPath =
|
|
32042
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31712
32043
|
const available = listStageTemplateIds();
|
|
31713
32044
|
if (argv.list) {
|
|
31714
32045
|
for (const id of available) {
|
|
@@ -31785,7 +32116,7 @@ async function handleAddStage(argv) {
|
|
|
31785
32116
|
}
|
|
31786
32117
|
}
|
|
31787
32118
|
async function handlePreflight(argv) {
|
|
31788
|
-
const repo =
|
|
32119
|
+
const repo = path43.resolve(argv.repo);
|
|
31789
32120
|
const agentId = validateAgentId(argv.id, repo);
|
|
31790
32121
|
const result = await preflightEnvironment({
|
|
31791
32122
|
repoPath: repo,
|
|
@@ -31803,7 +32134,7 @@ async function handlePreflight(argv) {
|
|
|
31803
32134
|
process.exit(result.code);
|
|
31804
32135
|
}
|
|
31805
32136
|
async function handleLaunch(argv) {
|
|
31806
|
-
const repo =
|
|
32137
|
+
const repo = path43.resolve(argv.repo);
|
|
31807
32138
|
const agentId = validateAgentId(argv.id, repo);
|
|
31808
32139
|
let confirmReplace = argv.replace;
|
|
31809
32140
|
let force = argv.force;
|
|
@@ -31815,8 +32146,8 @@ async function handleLaunch(argv) {
|
|
|
31815
32146
|
console.error(guard.reason ?? "");
|
|
31816
32147
|
const readline5 = await import("readline");
|
|
31817
32148
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
31818
|
-
const answer = await new Promise((
|
|
31819
|
-
rl.question("Replace this slot? [y/N] ",
|
|
32149
|
+
const answer = await new Promise((resolve35) => {
|
|
32150
|
+
rl.question("Replace this slot? [y/N] ", resolve35);
|
|
31820
32151
|
});
|
|
31821
32152
|
rl.close();
|
|
31822
32153
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -31832,8 +32163,8 @@ async function handleLaunch(argv) {
|
|
|
31832
32163
|
warn("Worktree has uncommitted changes.");
|
|
31833
32164
|
const readline5 = await import("readline");
|
|
31834
32165
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
31835
|
-
const answer = await new Promise((
|
|
31836
|
-
rl.question("Discard uncommitted changes? [y/N] ",
|
|
32166
|
+
const answer = await new Promise((resolve35) => {
|
|
32167
|
+
rl.question("Discard uncommitted changes? [y/N] ", resolve35);
|
|
31837
32168
|
});
|
|
31838
32169
|
rl.close();
|
|
31839
32170
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -31855,7 +32186,6 @@ async function handleLaunch(argv) {
|
|
|
31855
32186
|
confirmReplace,
|
|
31856
32187
|
force,
|
|
31857
32188
|
resume: argv.resume,
|
|
31858
|
-
purpose: argv.purpose,
|
|
31859
32189
|
capture: false
|
|
31860
32190
|
});
|
|
31861
32191
|
if (result.blocked) {
|
|
@@ -31888,7 +32218,7 @@ async function handleRecover(argv) {
|
|
|
31888
32218
|
});
|
|
31889
32219
|
}
|
|
31890
32220
|
async function handleVerify(argv) {
|
|
31891
|
-
const repo =
|
|
32221
|
+
const repo = path43.resolve(argv.repo);
|
|
31892
32222
|
const agentId = validateAgentId(argv.id, repo);
|
|
31893
32223
|
const result = await runVerification({
|
|
31894
32224
|
repoPath: repo,
|
|
@@ -31900,7 +32230,7 @@ async function handleVerify(argv) {
|
|
|
31900
32230
|
process.exit(result.code);
|
|
31901
32231
|
}
|
|
31902
32232
|
async function handleTeardown(argv) {
|
|
31903
|
-
const repo =
|
|
32233
|
+
const repo = path43.resolve(argv.repo);
|
|
31904
32234
|
const agentId = validateAgentId(argv.id, repo);
|
|
31905
32235
|
const result = await teardownEnvironment({
|
|
31906
32236
|
repoPath: repo,
|
|
@@ -31911,7 +32241,7 @@ async function handleTeardown(argv) {
|
|
|
31911
32241
|
process.exit(result.code);
|
|
31912
32242
|
}
|
|
31913
32243
|
async function handleComplete(argv) {
|
|
31914
|
-
const repo =
|
|
32244
|
+
const repo = path43.resolve(argv.repo);
|
|
31915
32245
|
const agentId = validateAgentId(argv.id, repo);
|
|
31916
32246
|
const result = await completeEnvironment({
|
|
31917
32247
|
repoPath: repo,
|
|
@@ -31932,7 +32262,7 @@ async function handleComplete(argv) {
|
|
|
31932
32262
|
process.exit(result.code);
|
|
31933
32263
|
}
|
|
31934
32264
|
async function handleStatus(argv) {
|
|
31935
|
-
const repoPath =
|
|
32265
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31936
32266
|
if (argv.json) {
|
|
31937
32267
|
const status = collectEnvironmentStatus(repoPath);
|
|
31938
32268
|
const output = EnvironmentStatusSchema.parse(status);
|
|
@@ -31945,7 +32275,7 @@ async function handleStatus(argv) {
|
|
|
31945
32275
|
});
|
|
31946
32276
|
}
|
|
31947
32277
|
async function handleRunsList(argv) {
|
|
31948
|
-
const repoPath =
|
|
32278
|
+
const repoPath = path43.resolve(argv.repo);
|
|
31949
32279
|
const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
|
|
31950
32280
|
if (argv.json) {
|
|
31951
32281
|
process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
|
|
@@ -31962,7 +32292,7 @@ async function handleRunsList(argv) {
|
|
|
31962
32292
|
}
|
|
31963
32293
|
}
|
|
31964
32294
|
async function handleRunsGet(argv) {
|
|
31965
|
-
const run2 = getRun(
|
|
32295
|
+
const run2 = getRun(path43.resolve(argv.repo), argv.runId);
|
|
31966
32296
|
if (!run2) {
|
|
31967
32297
|
error(`Run not found: ${argv.runId}`);
|
|
31968
32298
|
process.exit(1);
|
|
@@ -32041,7 +32371,8 @@ function printNextSteps(auto2) {
|
|
|
32041
32371
|
}
|
|
32042
32372
|
|
|
32043
32373
|
// src/cli/commands/control.ts
|
|
32044
|
-
var
|
|
32374
|
+
var path44 = __toESM(require("path"));
|
|
32375
|
+
init_control_config();
|
|
32045
32376
|
var controlCommand = {
|
|
32046
32377
|
command: "control <subcommand>",
|
|
32047
32378
|
describe: "Mission Control dashboard (local)",
|
|
@@ -32144,7 +32475,7 @@ async function handleDown() {
|
|
|
32144
32475
|
process.exit(code);
|
|
32145
32476
|
}
|
|
32146
32477
|
async function handleRegister(argv) {
|
|
32147
|
-
const repoPath =
|
|
32478
|
+
const repoPath = path44.resolve(argv.repo);
|
|
32148
32479
|
header("har control register");
|
|
32149
32480
|
info(`Repository: ${repoPath}`);
|
|
32150
32481
|
try {
|
|
@@ -32164,7 +32495,7 @@ async function handleRegister(argv) {
|
|
|
32164
32495
|
}
|
|
32165
32496
|
}
|
|
32166
32497
|
async function handleSync(argv) {
|
|
32167
|
-
const repoPath =
|
|
32498
|
+
const repoPath = path44.resolve(argv.repo);
|
|
32168
32499
|
try {
|
|
32169
32500
|
await syncRepoWithControl({
|
|
32170
32501
|
repoPath,
|
|
@@ -32203,7 +32534,7 @@ async function handleWatch(argv) {
|
|
|
32203
32534
|
};
|
|
32204
32535
|
const tick = async () => {
|
|
32205
32536
|
if (argv.repo) {
|
|
32206
|
-
await syncOne(
|
|
32537
|
+
await syncOne(path44.resolve(argv.repo));
|
|
32207
32538
|
return;
|
|
32208
32539
|
}
|
|
32209
32540
|
try {
|
|
@@ -32234,12 +32565,12 @@ async function handleLogin(argv) {
|
|
|
32234
32565
|
}
|
|
32235
32566
|
|
|
32236
32567
|
// src/cli/commands/hooks.ts
|
|
32237
|
-
var
|
|
32568
|
+
var path47 = __toESM(require("path"));
|
|
32238
32569
|
|
|
32239
32570
|
// src/core/hooks.ts
|
|
32240
|
-
var
|
|
32241
|
-
var
|
|
32242
|
-
var
|
|
32571
|
+
var fs40 = __toESM(require("fs"));
|
|
32572
|
+
var os13 = __toESM(require("os"));
|
|
32573
|
+
var path45 = __toESM(require("path"));
|
|
32243
32574
|
var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
|
|
32244
32575
|
var MARKER_END = "# <<< har commit gate <<<";
|
|
32245
32576
|
var PRE_COMMIT_BLOCK = [
|
|
@@ -32254,27 +32585,27 @@ var POST_COMMIT_BLOCK = [
|
|
|
32254
32585
|
'"$(git rev-parse --git-path hooks)/har-post-commit" || true',
|
|
32255
32586
|
MARKER_END
|
|
32256
32587
|
].join("\n");
|
|
32257
|
-
function
|
|
32588
|
+
function tryGit3(cwd, args) {
|
|
32258
32589
|
const result = run(`git ${args}`, { cwd });
|
|
32259
32590
|
return result.code === 0 ? result.stdout.trim() : void 0;
|
|
32260
32591
|
}
|
|
32261
32592
|
function resolveCheckoutRoot(cwd) {
|
|
32262
|
-
return
|
|
32593
|
+
return tryGit3(cwd, "rev-parse --show-toplevel");
|
|
32263
32594
|
}
|
|
32264
32595
|
function resolveHooksDir(checkoutDir) {
|
|
32265
|
-
const hooksPath =
|
|
32596
|
+
const hooksPath = tryGit3(checkoutDir, "rev-parse --git-path hooks");
|
|
32266
32597
|
if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
|
|
32267
|
-
return
|
|
32598
|
+
return path45.resolve(checkoutDir, hooksPath);
|
|
32268
32599
|
}
|
|
32269
32600
|
function getConfiguredHooksPath(checkoutDir) {
|
|
32270
|
-
return
|
|
32601
|
+
return tryGit3(checkoutDir, "config core.hooksPath") || void 0;
|
|
32271
32602
|
}
|
|
32272
32603
|
function defaultHarInvocation() {
|
|
32273
32604
|
const entry = process.argv[1];
|
|
32274
32605
|
if (entry && entry.endsWith(".js")) {
|
|
32275
|
-
return `"${process.execPath}" "${
|
|
32606
|
+
return `"${process.execPath}" "${path45.resolve(entry)}"`;
|
|
32276
32607
|
}
|
|
32277
|
-
if (entry) return `"${
|
|
32608
|
+
if (entry) return `"${path45.resolve(entry)}"`;
|
|
32278
32609
|
return "har";
|
|
32279
32610
|
}
|
|
32280
32611
|
function buildHookScript(harInvocation, subcommand, failOpenNotice) {
|
|
@@ -32295,40 +32626,40 @@ exit 0
|
|
|
32295
32626
|
`;
|
|
32296
32627
|
}
|
|
32297
32628
|
function upsertMarkedBlock(filePath, block) {
|
|
32298
|
-
if (!
|
|
32299
|
-
|
|
32629
|
+
if (!fs40.existsSync(filePath)) {
|
|
32630
|
+
fs40.writeFileSync(filePath, `#!/bin/sh
|
|
32300
32631
|
${block}
|
|
32301
32632
|
`, { mode: 493 });
|
|
32302
32633
|
return "created";
|
|
32303
32634
|
}
|
|
32304
|
-
const content =
|
|
32635
|
+
const content = fs40.readFileSync(filePath, "utf8");
|
|
32305
32636
|
if (content.includes(MARKER_START)) {
|
|
32306
32637
|
const pattern = new RegExp(
|
|
32307
32638
|
`${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
|
|
32308
32639
|
);
|
|
32309
|
-
|
|
32310
|
-
|
|
32640
|
+
fs40.writeFileSync(filePath, content.replace(pattern, block));
|
|
32641
|
+
fs40.chmodSync(filePath, 493);
|
|
32311
32642
|
return "updated";
|
|
32312
32643
|
}
|
|
32313
32644
|
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
32314
|
-
|
|
32645
|
+
fs40.writeFileSync(filePath, `${content}${suffix}
|
|
32315
32646
|
${block}
|
|
32316
32647
|
`);
|
|
32317
|
-
|
|
32648
|
+
fs40.chmodSync(filePath, 493);
|
|
32318
32649
|
return "appended";
|
|
32319
32650
|
}
|
|
32320
32651
|
function removeMarkedBlock(filePath) {
|
|
32321
|
-
if (!
|
|
32322
|
-
const content =
|
|
32652
|
+
if (!fs40.existsSync(filePath)) return false;
|
|
32653
|
+
const content = fs40.readFileSync(filePath, "utf8");
|
|
32323
32654
|
if (!content.includes(MARKER_START)) return false;
|
|
32324
32655
|
const pattern = new RegExp(
|
|
32325
32656
|
`\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
|
|
32326
32657
|
);
|
|
32327
32658
|
const stripped = content.replace(pattern, "\n");
|
|
32328
32659
|
if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
|
|
32329
|
-
|
|
32660
|
+
fs40.rmSync(filePath);
|
|
32330
32661
|
} else {
|
|
32331
|
-
|
|
32662
|
+
fs40.writeFileSync(filePath, stripped);
|
|
32332
32663
|
}
|
|
32333
32664
|
return true;
|
|
32334
32665
|
}
|
|
@@ -32350,20 +32681,20 @@ Or re-run with --force to write into that directory anyway.`
|
|
|
32350
32681
|
);
|
|
32351
32682
|
}
|
|
32352
32683
|
const hooksDir = resolveHooksDir(checkout);
|
|
32353
|
-
|
|
32684
|
+
fs40.mkdirSync(hooksDir, { recursive: true });
|
|
32354
32685
|
const invocation = options.harInvocation ?? defaultHarInvocation();
|
|
32355
|
-
|
|
32356
|
-
|
|
32686
|
+
fs40.writeFileSync(
|
|
32687
|
+
path45.join(hooksDir, "har-pre-commit"),
|
|
32357
32688
|
buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
|
|
32358
32689
|
{ mode: 493 }
|
|
32359
32690
|
);
|
|
32360
|
-
|
|
32361
|
-
|
|
32691
|
+
fs40.writeFileSync(
|
|
32692
|
+
path45.join(hooksDir, "har-post-commit"),
|
|
32362
32693
|
buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
|
|
32363
32694
|
{ mode: 493 }
|
|
32364
32695
|
);
|
|
32365
|
-
const preCommit = upsertMarkedBlock(
|
|
32366
|
-
const postCommit = upsertMarkedBlock(
|
|
32696
|
+
const preCommit = upsertMarkedBlock(path45.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
|
|
32697
|
+
const postCommit = upsertMarkedBlock(path45.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
|
|
32367
32698
|
ensureValidationsIgnored(checkout);
|
|
32368
32699
|
return { hooksDir, preCommit, postCommit };
|
|
32369
32700
|
}
|
|
@@ -32372,12 +32703,12 @@ function uninstallHooks(repoPath) {
|
|
|
32372
32703
|
if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
|
|
32373
32704
|
const hooksDir = resolveHooksDir(checkout);
|
|
32374
32705
|
let removed = false;
|
|
32375
|
-
removed = removeMarkedBlock(
|
|
32376
|
-
removed = removeMarkedBlock(
|
|
32706
|
+
removed = removeMarkedBlock(path45.join(hooksDir, "pre-commit")) || removed;
|
|
32707
|
+
removed = removeMarkedBlock(path45.join(hooksDir, "post-commit")) || removed;
|
|
32377
32708
|
for (const file of ["har-pre-commit", "har-post-commit"]) {
|
|
32378
|
-
const full =
|
|
32379
|
-
if (
|
|
32380
|
-
|
|
32709
|
+
const full = path45.join(hooksDir, file);
|
|
32710
|
+
if (fs40.existsSync(full)) {
|
|
32711
|
+
fs40.rmSync(full);
|
|
32381
32712
|
removed = true;
|
|
32382
32713
|
}
|
|
32383
32714
|
}
|
|
@@ -32394,9 +32725,9 @@ function getCommitGateConfig(checkoutDir) {
|
|
|
32394
32725
|
function isAgentWorktree(checkoutDir) {
|
|
32395
32726
|
const branch = getCurrentBranch(checkoutDir);
|
|
32396
32727
|
if (branch && /^har-agent-\d+$/.test(branch)) return true;
|
|
32397
|
-
const worktreesRoot =
|
|
32398
|
-
const resolved =
|
|
32399
|
-
return resolved.startsWith(`${worktreesRoot}${
|
|
32728
|
+
const worktreesRoot = path45.join(os13.homedir(), "worktrees");
|
|
32729
|
+
const resolved = path45.resolve(checkoutDir);
|
|
32730
|
+
return resolved.startsWith(`${worktreesRoot}${path45.sep}`) && /-agent-\d+$/.test(path45.basename(resolved));
|
|
32400
32731
|
}
|
|
32401
32732
|
function resolveEffectiveMode(checkoutDir, gate) {
|
|
32402
32733
|
if (!gate.enabled) return "off";
|
|
@@ -32409,15 +32740,15 @@ function getHooksStatus(repoPath) {
|
|
|
32409
32740
|
const hooksDir = resolveHooksDir(checkout);
|
|
32410
32741
|
const gate = getCommitGateConfig(checkout);
|
|
32411
32742
|
const hasBlock = (name) => {
|
|
32412
|
-
const file =
|
|
32413
|
-
return
|
|
32743
|
+
const file = path45.join(hooksDir, name);
|
|
32744
|
+
return fs40.existsSync(file) && fs40.readFileSync(file, "utf8").includes(MARKER_START);
|
|
32414
32745
|
};
|
|
32415
32746
|
return {
|
|
32416
32747
|
checkout,
|
|
32417
32748
|
hooksDir,
|
|
32418
32749
|
configuredHooksPath: getConfiguredHooksPath(checkout),
|
|
32419
|
-
preCommitInstalled: hasBlock("pre-commit") &&
|
|
32420
|
-
postCommitInstalled: hasBlock("post-commit") &&
|
|
32750
|
+
preCommitInstalled: hasBlock("pre-commit") && fs40.existsSync(path45.join(hooksDir, "har-pre-commit")),
|
|
32751
|
+
postCommitInstalled: hasBlock("post-commit") && fs40.existsSync(path45.join(hooksDir, "har-post-commit")),
|
|
32421
32752
|
gate,
|
|
32422
32753
|
effectiveMode: resolveEffectiveMode(checkout, gate)
|
|
32423
32754
|
};
|
|
@@ -32428,7 +32759,7 @@ function checkCommitGate(cwd) {
|
|
|
32428
32759
|
}
|
|
32429
32760
|
const checkout = resolveCheckoutRoot(cwd);
|
|
32430
32761
|
if (!checkout) return { exitCode: 0, messages: [] };
|
|
32431
|
-
if (!
|
|
32762
|
+
if (!fs40.existsSync(path45.join(checkout, ".har", "stages.json"))) {
|
|
32432
32763
|
return { exitCode: 0, messages: [] };
|
|
32433
32764
|
}
|
|
32434
32765
|
const gate = getCommitGateConfig(checkout);
|
|
@@ -32485,7 +32816,7 @@ async function recordCommitAssociation(cwd) {
|
|
|
32485
32816
|
const checkout = resolveCheckoutRoot(cwd);
|
|
32486
32817
|
if (!checkout) return { attached: false };
|
|
32487
32818
|
const commitSha = getHeadSha(checkout);
|
|
32488
|
-
const tree =
|
|
32819
|
+
const tree = tryGit3(checkout, "rev-parse HEAD^{tree}");
|
|
32489
32820
|
if (!commitSha || !tree) return { attached: false };
|
|
32490
32821
|
const updated = attachCommit(checkout, tree, commitSha);
|
|
32491
32822
|
if (updated) {
|
|
@@ -32499,8 +32830,8 @@ async function recordCommitAssociation(cwd) {
|
|
|
32499
32830
|
}
|
|
32500
32831
|
|
|
32501
32832
|
// src/core/claude-hooks.ts
|
|
32502
|
-
var
|
|
32503
|
-
var
|
|
32833
|
+
var fs41 = __toESM(require("fs"));
|
|
32834
|
+
var path46 = __toESM(require("path"));
|
|
32504
32835
|
var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
|
|
32505
32836
|
var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
|
|
32506
32837
|
var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
|
|
@@ -32511,8 +32842,8 @@ function isHarGuardEntry(entry) {
|
|
|
32511
32842
|
);
|
|
32512
32843
|
}
|
|
32513
32844
|
function readSettings(settingsPath) {
|
|
32514
|
-
if (!
|
|
32515
|
-
const raw =
|
|
32845
|
+
if (!fs41.existsSync(settingsPath)) return {};
|
|
32846
|
+
const raw = fs41.readFileSync(settingsPath, "utf8").trim();
|
|
32516
32847
|
if (raw === "") return {};
|
|
32517
32848
|
const parsed = JSON.parse(raw);
|
|
32518
32849
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -32521,19 +32852,19 @@ function readSettings(settingsPath) {
|
|
|
32521
32852
|
return parsed;
|
|
32522
32853
|
}
|
|
32523
32854
|
function writeSettings(settingsPath, settings) {
|
|
32524
|
-
|
|
32525
|
-
|
|
32855
|
+
fs41.mkdirSync(path46.dirname(settingsPath), { recursive: true });
|
|
32856
|
+
fs41.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
32526
32857
|
}
|
|
32527
32858
|
function installClaudeGuard(repoPath) {
|
|
32528
32859
|
const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
|
|
32529
32860
|
if (!templatePath) {
|
|
32530
32861
|
throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
|
|
32531
32862
|
}
|
|
32532
|
-
const guardScript =
|
|
32533
|
-
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
const settingsPath =
|
|
32863
|
+
const guardScript = path46.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
32864
|
+
fs41.mkdirSync(path46.dirname(guardScript), { recursive: true });
|
|
32865
|
+
fs41.copyFileSync(templatePath, guardScript);
|
|
32866
|
+
fs41.chmodSync(guardScript, 493);
|
|
32867
|
+
const settingsPath = path46.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
32537
32868
|
const settings = readSettings(settingsPath);
|
|
32538
32869
|
const hooks = settings.hooks ?? {};
|
|
32539
32870
|
const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
|
|
@@ -32547,15 +32878,15 @@ function installClaudeGuard(repoPath) {
|
|
|
32547
32878
|
}
|
|
32548
32879
|
function uninstallClaudeGuard(repoPath) {
|
|
32549
32880
|
let removed = false;
|
|
32550
|
-
const guardScript =
|
|
32551
|
-
if (
|
|
32552
|
-
|
|
32881
|
+
const guardScript = path46.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
32882
|
+
if (fs41.existsSync(guardScript)) {
|
|
32883
|
+
fs41.rmSync(guardScript);
|
|
32553
32884
|
removed = true;
|
|
32554
|
-
const parent =
|
|
32555
|
-
if (
|
|
32885
|
+
const parent = path46.dirname(guardScript);
|
|
32886
|
+
if (fs41.existsSync(parent) && fs41.readdirSync(parent).length === 0) fs41.rmdirSync(parent);
|
|
32556
32887
|
}
|
|
32557
|
-
const settingsPath =
|
|
32558
|
-
if (
|
|
32888
|
+
const settingsPath = path46.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
32889
|
+
if (fs41.existsSync(settingsPath)) {
|
|
32559
32890
|
const settings = readSettings(settingsPath);
|
|
32560
32891
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
32561
32892
|
if (preToolUse) {
|
|
@@ -32580,8 +32911,8 @@ function uninstallClaudeGuard(repoPath) {
|
|
|
32580
32911
|
return { removed };
|
|
32581
32912
|
}
|
|
32582
32913
|
function claudeGuardInstalled(repoPath) {
|
|
32583
|
-
const settingsPath =
|
|
32584
|
-
if (!
|
|
32914
|
+
const settingsPath = path46.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
32915
|
+
if (!fs41.existsSync(settingsPath)) return false;
|
|
32585
32916
|
try {
|
|
32586
32917
|
const settings = readSettings(settingsPath);
|
|
32587
32918
|
return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
|
|
@@ -32597,7 +32928,7 @@ function repoOption(y2) {
|
|
|
32597
32928
|
function handleInstall2(argv) {
|
|
32598
32929
|
try {
|
|
32599
32930
|
if (argv.claude) {
|
|
32600
|
-
const result2 = installClaudeGuard(
|
|
32931
|
+
const result2 = installClaudeGuard(path47.resolve(argv.repo));
|
|
32601
32932
|
success("Claude Code worktree guard installed.");
|
|
32602
32933
|
info(`Guard script: ${result2.guardScript}`);
|
|
32603
32934
|
info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
|
|
@@ -32605,7 +32936,7 @@ function handleInstall2(argv) {
|
|
|
32605
32936
|
info("Commit both files so the guard travels with the repo.");
|
|
32606
32937
|
return;
|
|
32607
32938
|
}
|
|
32608
|
-
const result = installHooks({ repoPath:
|
|
32939
|
+
const result = installHooks({ repoPath: path47.resolve(argv.repo), force: argv.force });
|
|
32609
32940
|
success(`Commit gate installed in ${result.hooksDir}`);
|
|
32610
32941
|
info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
|
|
32611
32942
|
info("Commits of unverified change batches will be blocked in agent worktrees.");
|
|
@@ -32618,7 +32949,7 @@ function handleInstall2(argv) {
|
|
|
32618
32949
|
function handleUninstall(argv) {
|
|
32619
32950
|
try {
|
|
32620
32951
|
if (argv.claude) {
|
|
32621
|
-
const result2 = uninstallClaudeGuard(
|
|
32952
|
+
const result2 = uninstallClaudeGuard(path47.resolve(argv.repo));
|
|
32622
32953
|
if (result2.removed) {
|
|
32623
32954
|
success("Claude Code worktree guard removed.");
|
|
32624
32955
|
} else {
|
|
@@ -32626,7 +32957,7 @@ function handleUninstall(argv) {
|
|
|
32626
32957
|
}
|
|
32627
32958
|
return;
|
|
32628
32959
|
}
|
|
32629
|
-
const result = uninstallHooks(
|
|
32960
|
+
const result = uninstallHooks(path47.resolve(argv.repo));
|
|
32630
32961
|
if (result.removed) {
|
|
32631
32962
|
success(`Commit gate removed from ${result.hooksDir}`);
|
|
32632
32963
|
} else {
|
|
@@ -32639,7 +32970,7 @@ function handleUninstall(argv) {
|
|
|
32639
32970
|
}
|
|
32640
32971
|
function handleStatus2(argv) {
|
|
32641
32972
|
try {
|
|
32642
|
-
const status = getHooksStatus(
|
|
32973
|
+
const status = getHooksStatus(path47.resolve(argv.repo));
|
|
32643
32974
|
if (argv.json) {
|
|
32644
32975
|
process.stdout.write(`${JSON.stringify(status, null, 2)}
|
|
32645
32976
|
`);
|
|
@@ -32657,7 +32988,7 @@ function handleStatus2(argv) {
|
|
|
32657
32988
|
);
|
|
32658
32989
|
info(`Effective: ${status.effectiveMode} (in this checkout)`);
|
|
32659
32990
|
info(
|
|
32660
|
-
`Claude guard: ${claudeGuardInstalled(
|
|
32991
|
+
`Claude guard: ${claudeGuardInstalled(path47.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
|
|
32661
32992
|
);
|
|
32662
32993
|
} catch (err) {
|
|
32663
32994
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -32918,10 +33249,10 @@ function assignProp(target, prop, value) {
|
|
|
32918
33249
|
configurable: true
|
|
32919
33250
|
});
|
|
32920
33251
|
}
|
|
32921
|
-
function getElementAtPath(obj,
|
|
32922
|
-
if (!
|
|
33252
|
+
function getElementAtPath(obj, path50) {
|
|
33253
|
+
if (!path50)
|
|
32923
33254
|
return obj;
|
|
32924
|
-
return
|
|
33255
|
+
return path50.reduce((acc, key) => acc?.[key], obj);
|
|
32925
33256
|
}
|
|
32926
33257
|
function promiseAllObject(promisesObj) {
|
|
32927
33258
|
const keys = Object.keys(promisesObj);
|
|
@@ -33241,11 +33572,11 @@ function aborted(x2, startIndex = 0) {
|
|
|
33241
33572
|
}
|
|
33242
33573
|
return false;
|
|
33243
33574
|
}
|
|
33244
|
-
function prefixIssues(
|
|
33575
|
+
function prefixIssues(path50, issues) {
|
|
33245
33576
|
return issues.map((iss) => {
|
|
33246
33577
|
var _a3;
|
|
33247
33578
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
33248
|
-
iss.path.unshift(
|
|
33579
|
+
iss.path.unshift(path50);
|
|
33249
33580
|
return iss;
|
|
33250
33581
|
});
|
|
33251
33582
|
}
|
|
@@ -38568,7 +38899,7 @@ var Protocol = class {
|
|
|
38568
38899
|
return;
|
|
38569
38900
|
}
|
|
38570
38901
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
38571
|
-
await new Promise((
|
|
38902
|
+
await new Promise((resolve35) => setTimeout(resolve35, pollInterval));
|
|
38572
38903
|
options?.signal?.throwIfAborted();
|
|
38573
38904
|
}
|
|
38574
38905
|
} catch (error3) {
|
|
@@ -38585,7 +38916,7 @@ var Protocol = class {
|
|
|
38585
38916
|
*/
|
|
38586
38917
|
request(request, resultSchema, options) {
|
|
38587
38918
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
38588
|
-
return new Promise((
|
|
38919
|
+
return new Promise((resolve35, reject) => {
|
|
38589
38920
|
const earlyReject = (error3) => {
|
|
38590
38921
|
reject(error3);
|
|
38591
38922
|
};
|
|
@@ -38663,7 +38994,7 @@ var Protocol = class {
|
|
|
38663
38994
|
if (!parseResult.success) {
|
|
38664
38995
|
reject(parseResult.error);
|
|
38665
38996
|
} else {
|
|
38666
|
-
|
|
38997
|
+
resolve35(parseResult.data);
|
|
38667
38998
|
}
|
|
38668
38999
|
} catch (error3) {
|
|
38669
39000
|
reject(error3);
|
|
@@ -38924,12 +39255,12 @@ var Protocol = class {
|
|
|
38924
39255
|
}
|
|
38925
39256
|
} catch {
|
|
38926
39257
|
}
|
|
38927
|
-
return new Promise((
|
|
39258
|
+
return new Promise((resolve35, reject) => {
|
|
38928
39259
|
if (signal.aborted) {
|
|
38929
39260
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
38930
39261
|
return;
|
|
38931
39262
|
}
|
|
38932
|
-
const timeoutId = setTimeout(
|
|
39263
|
+
const timeoutId = setTimeout(resolve35, interval);
|
|
38933
39264
|
signal.addEventListener("abort", () => {
|
|
38934
39265
|
clearTimeout(timeoutId);
|
|
38935
39266
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -39719,7 +40050,7 @@ var Server = class extends Protocol {
|
|
|
39719
40050
|
};
|
|
39720
40051
|
|
|
39721
40052
|
// src/mcp/server.ts
|
|
39722
|
-
var
|
|
40053
|
+
var path48 = __toESM(require("path"));
|
|
39723
40054
|
|
|
39724
40055
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
39725
40056
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
@@ -39802,12 +40133,12 @@ var StdioServerTransport = class {
|
|
|
39802
40133
|
this.onclose?.();
|
|
39803
40134
|
}
|
|
39804
40135
|
send(message) {
|
|
39805
|
-
return new Promise((
|
|
40136
|
+
return new Promise((resolve35) => {
|
|
39806
40137
|
const json = serializeMessage(message);
|
|
39807
40138
|
if (this._stdout.write(json)) {
|
|
39808
|
-
|
|
40139
|
+
resolve35();
|
|
39809
40140
|
} else {
|
|
39810
|
-
this._stdout.once("drain",
|
|
40141
|
+
this._stdout.once("drain", resolve35);
|
|
39811
40142
|
}
|
|
39812
40143
|
});
|
|
39813
40144
|
}
|
|
@@ -39890,10 +40221,7 @@ var LaunchEnvironmentInputSchema = external_exports.object({
|
|
|
39890
40221
|
force: external_exports.boolean().default(false).describe(
|
|
39891
40222
|
"Discard uncommitted changes in a dirty occupied worktree. Requires confirmReplace=true and explicit user approval \u2014 never set autonomously."
|
|
39892
40223
|
),
|
|
39893
|
-
resume: external_exports.boolean().default(false).describe("Resume a failed or partial launch without creating a new worktree.")
|
|
39894
|
-
purpose: external_exports.string().optional().describe(
|
|
39895
|
-
"Optional session label stored in the slot registry (shown in status and occupied-slot warnings)."
|
|
39896
|
-
)
|
|
40224
|
+
resume: external_exports.boolean().default(false).describe("Resume a failed or partial launch without creating a new worktree.")
|
|
39897
40225
|
});
|
|
39898
40226
|
var LaunchEnvironmentOutputSchema = ShellRunOutputSchema.extend({
|
|
39899
40227
|
previewUrls: external_exports.record(external_exports.string()).optional(),
|
|
@@ -39907,8 +40235,7 @@ var LaunchEnvironmentOutputSchema = ShellRunOutputSchema.extend({
|
|
|
39907
40235
|
worktreePath: external_exports.string().optional(),
|
|
39908
40236
|
branch: external_exports.string().optional(),
|
|
39909
40237
|
dirty: external_exports.boolean().optional(),
|
|
39910
|
-
sessionCreatedAt: external_exports.string().optional()
|
|
39911
|
-
purpose: external_exports.string().optional()
|
|
40238
|
+
sessionCreatedAt: external_exports.string().optional()
|
|
39912
40239
|
}).optional()
|
|
39913
40240
|
});
|
|
39914
40241
|
var PreflightEnvironmentInputSchema = external_exports.object({
|
|
@@ -40033,10 +40360,6 @@ var HAR_MCP_TOOLS = [
|
|
|
40033
40360
|
resume: {
|
|
40034
40361
|
type: "boolean",
|
|
40035
40362
|
description: "Resume a failed or partial launch (status failed/starting) without --replace. Preserves worktree and env."
|
|
40036
|
-
},
|
|
40037
|
-
purpose: {
|
|
40038
|
-
type: "string",
|
|
40039
|
-
description: "Optional session label stored in the slot registry (shown in status and occupied-slot warnings)."
|
|
40040
40363
|
}
|
|
40041
40364
|
},
|
|
40042
40365
|
["agentId"]
|
|
@@ -40220,7 +40543,6 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
40220
40543
|
confirmReplace: input.confirmReplace,
|
|
40221
40544
|
force: input.force,
|
|
40222
40545
|
resume: input.resume,
|
|
40223
|
-
purpose: input.purpose,
|
|
40224
40546
|
capture: true
|
|
40225
40547
|
});
|
|
40226
40548
|
const parsed = LaunchEnvironmentOutputSchema.parse(result);
|
|
@@ -40360,7 +40682,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
40360
40682
|
const input = ControlUpInputSchema.parse({ ...args, repo });
|
|
40361
40683
|
const result = await startControlAndSync({
|
|
40362
40684
|
detach: input.detach,
|
|
40363
|
-
cwd:
|
|
40685
|
+
cwd: path48.resolve(input.repo)
|
|
40364
40686
|
});
|
|
40365
40687
|
if (result.code !== 0) {
|
|
40366
40688
|
return {
|
|
@@ -40423,8 +40745,10 @@ var mcpCommand = {
|
|
|
40423
40745
|
};
|
|
40424
40746
|
|
|
40425
40747
|
// src/cli/commands/telemetry.ts
|
|
40426
|
-
var
|
|
40427
|
-
|
|
40748
|
+
var path49 = __toESM(require("path"));
|
|
40749
|
+
init_control_config();
|
|
40750
|
+
init_otel_hooks();
|
|
40751
|
+
init_telemetry_config();
|
|
40428
40752
|
function printSignals() {
|
|
40429
40753
|
info("Collected when enabled (local Mission Control only):");
|
|
40430
40754
|
for (const signal of TELEMETRY_SIGNALS) {
|
|
@@ -40448,6 +40772,8 @@ async function handleStatus3(argv) {
|
|
|
40448
40772
|
controlApiUrl: apiUrl,
|
|
40449
40773
|
controlReachable: reachable,
|
|
40450
40774
|
otelEndpoint: `${apiUrl.replace(/\/$/, "")}/api/otel`,
|
|
40775
|
+
otelHooksHome: getOtelHooksHome(),
|
|
40776
|
+
otelHooksConfig: getOtelHooksConfigPath(),
|
|
40451
40777
|
signalDescriptions: [...TELEMETRY_SIGNALS]
|
|
40452
40778
|
};
|
|
40453
40779
|
if (argv.json) {
|
|
@@ -40463,25 +40789,29 @@ async function handleStatus3(argv) {
|
|
|
40463
40789
|
);
|
|
40464
40790
|
info(`Mission Control: ${apiUrl} (${reachable ? "reachable" : "not reachable"})`);
|
|
40465
40791
|
info(`OTLP endpoint: ${payload.otelEndpoint}`);
|
|
40792
|
+
info(`Hooks home: ${payload.otelHooksHome}`);
|
|
40466
40793
|
printSignals();
|
|
40467
40794
|
}
|
|
40468
40795
|
async function handleOn(argv) {
|
|
40469
40796
|
header("har telemetry on");
|
|
40470
40797
|
const preference = writeTelemetryPreference(true, {
|
|
40471
40798
|
prompts: argv.prompts === true ? true : void 0,
|
|
40472
|
-
traces:
|
|
40799
|
+
traces: true
|
|
40473
40800
|
});
|
|
40474
|
-
success("Telemetry enabled
|
|
40801
|
+
success("Telemetry enabled. Cursor / Claude / Codex export via opentelemetry-hooks \u2192 Mission Control.");
|
|
40475
40802
|
info(
|
|
40476
40803
|
`Signals: metrics=${preference.signals.metrics} logs=${preference.signals.logs} prompts=${preference.signals.prompts} traces=${preference.signals.traces}`
|
|
40477
40804
|
);
|
|
40478
40805
|
if (preference.signals.prompts) {
|
|
40479
|
-
warn("Prompt
|
|
40806
|
+
warn("Prompt text will leave the agent machine for local Mission Control (also used as session purpose).");
|
|
40480
40807
|
}
|
|
40481
40808
|
printSignals();
|
|
40482
40809
|
const result = await ensureTelemetryInfrastructure({ startIfNeeded: true });
|
|
40483
40810
|
if (result.message) success(result.message);
|
|
40484
40811
|
if (result.warning) warn(result.warning);
|
|
40812
|
+
const hooks = ensureOtelHooks({ setupAgents: true });
|
|
40813
|
+
if (hooks.message) success(hooks.message);
|
|
40814
|
+
if (hooks.warning) warn(hooks.warning);
|
|
40485
40815
|
if (result.otelReady) {
|
|
40486
40816
|
info(`OTLP ingest: ${result.apiUrl.replace(/\/$/, "")}/api/otel`);
|
|
40487
40817
|
info("Usage appears under Mission Control \u2192 Worktrees / Usage. Disable: har telemetry off");
|
|
@@ -40490,44 +40820,48 @@ async function handleOn(argv) {
|
|
|
40490
40820
|
async function handleOff() {
|
|
40491
40821
|
header("har telemetry off");
|
|
40492
40822
|
writeTelemetryPreference(false);
|
|
40823
|
+
try {
|
|
40824
|
+
disableOtelHooksExport();
|
|
40825
|
+
} catch (err) {
|
|
40826
|
+
warn(`Could not refresh hooks config: ${err instanceof Error ? err.message : String(err)}`);
|
|
40827
|
+
}
|
|
40493
40828
|
success("Telemetry disabled.");
|
|
40494
|
-
info("
|
|
40495
|
-
info("
|
|
40829
|
+
info("Hooks stay registered but OTLP export is cleared in ~/.har/otel-hooks/otel_config.json.");
|
|
40830
|
+
info("Mission Control will not auto-start. Existing usage rows are kept.");
|
|
40496
40831
|
}
|
|
40497
40832
|
async function handleWriteEnv(argv) {
|
|
40498
|
-
const repoPath =
|
|
40833
|
+
const repoPath = path49.resolve(argv.repo);
|
|
40499
40834
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
40500
40835
|
const session = readSlotRegistry(harnessRoot, argv.agentId);
|
|
40501
40836
|
const workDir = argv.workDir ?? session?.workDir ?? repoPath;
|
|
40502
40837
|
const branch = argv.branch ?? session?.branch;
|
|
40503
40838
|
const suffix = argv.suffix ?? session?.suffix;
|
|
40504
|
-
const purpose = argv.purpose ?? session?.purpose;
|
|
40505
40839
|
const sessionKey = argv.sessionKey ?? buildSessionKey({
|
|
40506
40840
|
branch,
|
|
40507
40841
|
agentId: argv.agentId,
|
|
40508
40842
|
suffix,
|
|
40509
40843
|
createdAt: session?.createdAt
|
|
40510
40844
|
});
|
|
40511
|
-
const envFile = argv.envFile ??
|
|
40512
|
-
let otelReady = argv.otelReady;
|
|
40513
|
-
if (otelReady === void 0 && isTelemetryEnabled()) {
|
|
40514
|
-
const ensured = await ensureTelemetryInfrastructure({ startIfNeeded: false });
|
|
40515
|
-
otelReady = ensured.otelReady;
|
|
40516
|
-
}
|
|
40845
|
+
const envFile = argv.envFile ?? path49.join(workDir, `.env.agent.${argv.agentId}`);
|
|
40517
40846
|
const attrs = {
|
|
40518
40847
|
sessionKey,
|
|
40519
40848
|
agentId: argv.agentId,
|
|
40520
40849
|
repoPath,
|
|
40521
40850
|
workDir,
|
|
40522
40851
|
branch,
|
|
40523
|
-
suffix
|
|
40524
|
-
purpose
|
|
40852
|
+
suffix
|
|
40525
40853
|
};
|
|
40526
|
-
appendTelemetryEnvToFile(envFile, attrs
|
|
40527
|
-
|
|
40854
|
+
appendTelemetryEnvToFile(envFile, attrs);
|
|
40855
|
+
if (isTelemetryEnabled()) {
|
|
40856
|
+
ensureOtelHooks({
|
|
40857
|
+
setupAgents: false,
|
|
40858
|
+
resourceAttributes: buildOtelResourceAttributes(attrs)
|
|
40859
|
+
});
|
|
40860
|
+
}
|
|
40861
|
+
success(`Wrote session attribution to ${envFile}`);
|
|
40528
40862
|
}
|
|
40529
40863
|
function handlePrintEnv(argv) {
|
|
40530
|
-
const repoPath =
|
|
40864
|
+
const repoPath = path49.resolve(argv.repo);
|
|
40531
40865
|
const workDir = argv.workDir ?? repoPath;
|
|
40532
40866
|
const sessionKey = buildSessionKey({
|
|
40533
40867
|
branch: argv.branch,
|
|
@@ -40535,55 +40869,38 @@ function handlePrintEnv(argv) {
|
|
|
40535
40869
|
suffix: argv.suffix
|
|
40536
40870
|
});
|
|
40537
40871
|
process.stdout.write(
|
|
40538
|
-
buildTelemetryEnvBlock(
|
|
40539
|
-
|
|
40540
|
-
|
|
40541
|
-
|
|
40542
|
-
|
|
40543
|
-
|
|
40544
|
-
|
|
40545
|
-
|
|
40546
|
-
},
|
|
40547
|
-
{ otelReady: argv.otelReady && isTelemetryEnabled() }
|
|
40548
|
-
)
|
|
40872
|
+
buildTelemetryEnvBlock({
|
|
40873
|
+
sessionKey,
|
|
40874
|
+
agentId: argv.agentId,
|
|
40875
|
+
repoPath,
|
|
40876
|
+
workDir,
|
|
40877
|
+
branch: argv.branch,
|
|
40878
|
+
suffix: argv.suffix
|
|
40879
|
+
})
|
|
40549
40880
|
);
|
|
40550
40881
|
}
|
|
40551
|
-
function
|
|
40552
|
-
|
|
40553
|
-
|
|
40554
|
-
|
|
40555
|
-
|
|
40556
|
-
|
|
40557
|
-
const
|
|
40558
|
-
|
|
40559
|
-
|
|
40560
|
-
|
|
40561
|
-
|
|
40562
|
-
|
|
40563
|
-
|
|
40564
|
-
|
|
40565
|
-
|
|
40566
|
-
|
|
40567
|
-
|
|
40568
|
-
workDir,
|
|
40569
|
-
branch,
|
|
40570
|
-
suffix,
|
|
40571
|
-
purpose: session?.purpose
|
|
40572
|
-
});
|
|
40573
|
-
if (argv.write) {
|
|
40574
|
-
const outDir = path47.join(harnessRoot, ".har", "telemetry");
|
|
40575
|
-
fs40.mkdirSync(outDir, { recursive: true });
|
|
40576
|
-
const outPath = path47.join(outDir, "codex.otel.toml.snippet");
|
|
40577
|
-
fs40.writeFileSync(outPath, snippet);
|
|
40578
|
-
success(`Wrote ${outPath}`);
|
|
40579
|
-
info("Merge the [otel] table into ~/.codex/config.toml manually.");
|
|
40580
|
-
return;
|
|
40882
|
+
async function handleInstallHooks() {
|
|
40883
|
+
header("har telemetry install-hooks");
|
|
40884
|
+
if (!isTelemetryEnabled()) {
|
|
40885
|
+
warn("Telemetry is off \u2014 enabling preference so hooks can export.");
|
|
40886
|
+
writeTelemetryPreference(true, { traces: true });
|
|
40887
|
+
}
|
|
40888
|
+
const result = await ensureTelemetryInfrastructure({ startIfNeeded: true });
|
|
40889
|
+
if (result.message) success(result.message);
|
|
40890
|
+
if (result.warning) warn(result.warning);
|
|
40891
|
+
const hooks = ensureOtelHooks({ setupAgents: true });
|
|
40892
|
+
if (hooks.ok) {
|
|
40893
|
+
success(hooks.message ?? "opentelemetry-hooks installed and agents registered");
|
|
40894
|
+
info(`Config: ${hooks.configPath}`);
|
|
40895
|
+
info(`Wrapper: ${hooks.wrapperPath}`);
|
|
40896
|
+
} else {
|
|
40897
|
+
error(hooks.warning ?? "Failed to install opentelemetry-hooks");
|
|
40898
|
+
process.exitCode = 1;
|
|
40581
40899
|
}
|
|
40582
|
-
process.stdout.write(snippet);
|
|
40583
40900
|
}
|
|
40584
40901
|
var telemetryCommand = {
|
|
40585
40902
|
command: "telemetry <subcommand>",
|
|
40586
|
-
describe: "Agent usage telemetry (Claude
|
|
40903
|
+
describe: "Agent usage telemetry (Cursor / Claude / Codex via opentelemetry-hooks \u2192 Mission Control)",
|
|
40587
40904
|
builder: (yargs) => yargs.command(
|
|
40588
40905
|
"status",
|
|
40589
40906
|
"Show telemetry preference and Mission Control reachability",
|
|
@@ -40593,34 +40910,40 @@ var telemetryCommand = {
|
|
|
40593
40910
|
}
|
|
40594
40911
|
).command(
|
|
40595
40912
|
"on",
|
|
40596
|
-
"Enable telemetry
|
|
40913
|
+
"Enable telemetry, ensure Mission Control, install/configure opentelemetry-hooks",
|
|
40597
40914
|
(y2) => y2.option("prompts", {
|
|
40598
40915
|
type: "boolean",
|
|
40599
40916
|
default: false,
|
|
40600
|
-
describe: "Opt in to shipping user
|
|
40601
|
-
}).option("traces", {
|
|
40602
|
-
type: "boolean",
|
|
40603
|
-
default: false,
|
|
40604
|
-
describe: "Opt in to thin OTEL traces ingest (Claude beta)"
|
|
40917
|
+
describe: "Opt in to shipping user prompt text (also fills Mission Control purpose)"
|
|
40605
40918
|
}),
|
|
40606
40919
|
(argv) => {
|
|
40607
40920
|
void handleOn({
|
|
40608
|
-
prompts: argv.prompts
|
|
40609
|
-
traces: argv.traces
|
|
40921
|
+
prompts: argv.prompts
|
|
40610
40922
|
});
|
|
40611
40923
|
}
|
|
40612
40924
|
).command(
|
|
40613
40925
|
"off",
|
|
40614
|
-
"Disable telemetry (
|
|
40926
|
+
"Disable telemetry (clear hooks OTLP endpoint, no MC auto-start)",
|
|
40615
40927
|
() => {
|
|
40616
40928
|
},
|
|
40617
40929
|
() => {
|
|
40618
40930
|
void handleOff();
|
|
40619
40931
|
}
|
|
40932
|
+
).command(
|
|
40933
|
+
"install-hooks",
|
|
40934
|
+
"Install opentelemetry-hooks and register Cursor / Claude / Codex",
|
|
40935
|
+
() => {
|
|
40936
|
+
},
|
|
40937
|
+
() => {
|
|
40938
|
+
void handleInstallHooks().catch((err) => {
|
|
40939
|
+
error(err instanceof Error ? err.message : String(err));
|
|
40940
|
+
process.exitCode = 1;
|
|
40941
|
+
});
|
|
40942
|
+
}
|
|
40620
40943
|
).command(
|
|
40621
40944
|
"write-env",
|
|
40622
|
-
"Append session
|
|
40623
|
-
(y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("env-file", { type: "string" }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("
|
|
40945
|
+
"Append HAR session attribution to .env.agent.<id> and refresh hooks config",
|
|
40946
|
+
(y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("env-file", { type: "string" }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("session-key", { type: "string" }),
|
|
40624
40947
|
(argv) => handleWriteEnv({
|
|
40625
40948
|
agentId: argv["agent-id"],
|
|
40626
40949
|
repo: argv.repo,
|
|
@@ -40628,42 +40951,23 @@ var telemetryCommand = {
|
|
|
40628
40951
|
workDir: argv["work-dir"],
|
|
40629
40952
|
branch: argv.branch,
|
|
40630
40953
|
suffix: argv.suffix,
|
|
40631
|
-
|
|
40632
|
-
sessionKey: argv["session-key"],
|
|
40633
|
-
otelReady: argv["otel-ready"]
|
|
40954
|
+
sessionKey: argv["session-key"]
|
|
40634
40955
|
}).catch((err) => {
|
|
40635
40956
|
error(err instanceof Error ? err.message : String(err));
|
|
40636
40957
|
process.exitCode = 1;
|
|
40637
40958
|
})
|
|
40638
40959
|
).command(
|
|
40639
40960
|
"print-env",
|
|
40640
|
-
"Print
|
|
40641
|
-
(y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" })
|
|
40961
|
+
"Print session attribution env block to stdout",
|
|
40962
|
+
(y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }),
|
|
40642
40963
|
(argv) => handlePrintEnv({
|
|
40643
40964
|
agentId: argv["agent-id"],
|
|
40644
40965
|
repo: argv.repo,
|
|
40645
40966
|
workDir: argv["work-dir"],
|
|
40646
40967
|
branch: argv.branch,
|
|
40647
|
-
suffix: argv.suffix
|
|
40648
|
-
otelReady: argv["otel-ready"]
|
|
40649
|
-
})
|
|
40650
|
-
).command(
|
|
40651
|
-
"codex-snippet",
|
|
40652
|
-
"Print or write a Codex [otel] config snippet",
|
|
40653
|
-
(y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("write", {
|
|
40654
|
-
type: "boolean",
|
|
40655
|
-
default: false,
|
|
40656
|
-
describe: "Write to .har/telemetry/codex.otel.toml.snippet"
|
|
40657
|
-
}),
|
|
40658
|
-
(argv) => handleCodexSnippet({
|
|
40659
|
-
agentId: argv["agent-id"],
|
|
40660
|
-
repo: argv.repo,
|
|
40661
|
-
workDir: argv["work-dir"],
|
|
40662
|
-
branch: argv.branch,
|
|
40663
|
-
suffix: argv.suffix,
|
|
40664
|
-
write: argv.write
|
|
40968
|
+
suffix: argv.suffix
|
|
40665
40969
|
})
|
|
40666
|
-
).demandCommand(1, "Please specify a subcommand: status, on, off, write-env, print-env
|
|
40970
|
+
).demandCommand(1, "Please specify a subcommand: status, on, off, install-hooks, write-env, print-env"),
|
|
40667
40971
|
handler: () => {
|
|
40668
40972
|
}
|
|
40669
40973
|
};
|