codeam-cli 2.50.0 → 2.51.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/CHANGELOG.md +40 -0
- package/dist/index.js +625 -290
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -522,8 +522,8 @@ var _default = makeConfig();
|
|
|
522
522
|
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
523
523
|
|
|
524
524
|
// src/commands/pair-auto.ts
|
|
525
|
-
var
|
|
526
|
-
var
|
|
525
|
+
var fs43 = __toESM(require("fs"));
|
|
526
|
+
var os36 = __toESM(require("os"));
|
|
527
527
|
var path48 = __toESM(require("path"));
|
|
528
528
|
var import_crypto4 = require("crypto");
|
|
529
529
|
|
|
@@ -5397,7 +5397,7 @@ function readAnonId() {
|
|
|
5397
5397
|
}
|
|
5398
5398
|
function superProperties() {
|
|
5399
5399
|
return {
|
|
5400
|
-
cliVersion: true ? "2.
|
|
5400
|
+
cliVersion: true ? "2.51.1" : "0.0.0-dev",
|
|
5401
5401
|
nodeVersion: process.version,
|
|
5402
5402
|
platform: process.platform,
|
|
5403
5403
|
arch: process.arch,
|
|
@@ -5578,7 +5578,7 @@ var os4 = __toESM(require("os"));
|
|
|
5578
5578
|
// package.json
|
|
5579
5579
|
var package_default = {
|
|
5580
5580
|
name: "codeam-cli",
|
|
5581
|
-
version: "2.
|
|
5581
|
+
version: "2.51.1",
|
|
5582
5582
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
5583
5583
|
type: "commonjs",
|
|
5584
5584
|
main: "dist/index.js",
|
|
@@ -7728,8 +7728,8 @@ function closeAllTerminals() {
|
|
|
7728
7728
|
}
|
|
7729
7729
|
|
|
7730
7730
|
// src/commands/start/handlers.ts
|
|
7731
|
-
var
|
|
7732
|
-
var
|
|
7731
|
+
var fs42 = __toESM(require("fs"));
|
|
7732
|
+
var os35 = __toESM(require("os"));
|
|
7733
7733
|
var path47 = __toESM(require("path"));
|
|
7734
7734
|
var import_crypto3 = require("crypto");
|
|
7735
7735
|
var import_child_process20 = require("child_process");
|
|
@@ -11164,10 +11164,10 @@ function buildForPlatform(platform3) {
|
|
|
11164
11164
|
var import_node_crypto4 = require("crypto");
|
|
11165
11165
|
|
|
11166
11166
|
// src/agents/claude/resolver.ts
|
|
11167
|
-
function buildClaudeLaunch(extraArgs = [],
|
|
11168
|
-
const found =
|
|
11167
|
+
function buildClaudeLaunch(extraArgs = [], os42 = createOsStrategy()) {
|
|
11168
|
+
const found = os42.findInPath("claude") ?? os42.findInPath("claude-code");
|
|
11169
11169
|
if (!found) return null;
|
|
11170
|
-
return
|
|
11170
|
+
return os42.buildLaunch(found, extraArgs);
|
|
11171
11171
|
}
|
|
11172
11172
|
|
|
11173
11173
|
// src/agents/claude/installer.ts
|
|
@@ -11760,8 +11760,8 @@ var ClaudeRuntimeStrategy = class {
|
|
|
11760
11760
|
meta = getAgent("claude");
|
|
11761
11761
|
mode = "interactive";
|
|
11762
11762
|
os;
|
|
11763
|
-
constructor(
|
|
11764
|
-
this.os =
|
|
11763
|
+
constructor(os42) {
|
|
11764
|
+
this.os = os42;
|
|
11765
11765
|
}
|
|
11766
11766
|
/**
|
|
11767
11767
|
* Claude Code's react-ink TUI enables bracketed-paste mode at
|
|
@@ -12438,8 +12438,8 @@ function codexCredentialLocator() {
|
|
|
12438
12438
|
function codexLoginLauncher() {
|
|
12439
12439
|
return {
|
|
12440
12440
|
async ensureInstalled() {
|
|
12441
|
-
const
|
|
12442
|
-
return
|
|
12441
|
+
const os42 = createOsStrategy();
|
|
12442
|
+
return os42.findInPath("codex") !== null;
|
|
12443
12443
|
},
|
|
12444
12444
|
launch() {
|
|
12445
12445
|
return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
|
|
@@ -12462,8 +12462,8 @@ var CodexRuntimeStrategy = class {
|
|
|
12462
12462
|
meta = getAgent("codex");
|
|
12463
12463
|
mode = "interactive";
|
|
12464
12464
|
os;
|
|
12465
|
-
constructor(
|
|
12466
|
-
this.os =
|
|
12465
|
+
constructor(os42) {
|
|
12466
|
+
this.os = os42;
|
|
12467
12467
|
}
|
|
12468
12468
|
async prepareLaunch() {
|
|
12469
12469
|
let binary = this.os.findInPath("codex");
|
|
@@ -12562,12 +12562,12 @@ var CodexRuntimeStrategy = class {
|
|
|
12562
12562
|
});
|
|
12563
12563
|
}
|
|
12564
12564
|
};
|
|
12565
|
-
function resolveNpm(
|
|
12566
|
-
return
|
|
12565
|
+
function resolveNpm(os42) {
|
|
12566
|
+
return os42.id === "win32" ? "npm.cmd" : "npm";
|
|
12567
12567
|
}
|
|
12568
|
-
async function installCodexViaNpm(
|
|
12568
|
+
async function installCodexViaNpm(os42) {
|
|
12569
12569
|
return new Promise((resolve7, reject) => {
|
|
12570
|
-
const proc = (0, import_node_child_process4.spawn)(resolveNpm(
|
|
12570
|
+
const proc = (0, import_node_child_process4.spawn)(resolveNpm(os42), ["install", "-g", "@openai/codex"], {
|
|
12571
12571
|
stdio: "inherit"
|
|
12572
12572
|
});
|
|
12573
12573
|
proc.on("close", (code) => {
|
|
@@ -12584,16 +12584,16 @@ async function installCodexViaNpm(os41) {
|
|
|
12584
12584
|
});
|
|
12585
12585
|
});
|
|
12586
12586
|
}
|
|
12587
|
-
function augmentNpmGlobalBin(
|
|
12587
|
+
function augmentNpmGlobalBin(os42) {
|
|
12588
12588
|
try {
|
|
12589
|
-
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(
|
|
12589
|
+
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os42), ["prefix", "-g"], {
|
|
12590
12590
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12591
12591
|
});
|
|
12592
12592
|
if (result.status !== 0) return;
|
|
12593
12593
|
const prefix = result.stdout.toString().trim();
|
|
12594
12594
|
if (!prefix) return;
|
|
12595
|
-
const binDir =
|
|
12596
|
-
|
|
12595
|
+
const binDir = os42.id === "win32" ? prefix : path22.join(prefix, "bin");
|
|
12596
|
+
os42.augmentPath([binDir]);
|
|
12597
12597
|
} catch {
|
|
12598
12598
|
}
|
|
12599
12599
|
}
|
|
@@ -12677,9 +12677,9 @@ var import_node_child_process7 = require("child_process");
|
|
|
12677
12677
|
// src/agents/coderabbit/installer.ts
|
|
12678
12678
|
var import_node_child_process5 = require("child_process");
|
|
12679
12679
|
var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
|
|
12680
|
-
async function ensureCoderabbitInstalled(
|
|
12681
|
-
if (
|
|
12682
|
-
if (
|
|
12680
|
+
async function ensureCoderabbitInstalled(os42) {
|
|
12681
|
+
if (os42.findInPath("coderabbit")) return true;
|
|
12682
|
+
if (os42.id === "win32") {
|
|
12683
12683
|
console.error(
|
|
12684
12684
|
"\n \u2717 CodeRabbit on Windows requires WSL.\n Install the CLI inside your WSL distribution\n (curl -fsSL https://cli.coderabbit.ai/install.sh | sh)\n then re-run `codeam link coderabbit` from WSL.\n"
|
|
12685
12685
|
);
|
|
@@ -12694,8 +12694,8 @@ async function ensureCoderabbitInstalled(os41) {
|
|
|
12694
12694
|
proc.on("error", () => resolve7(false));
|
|
12695
12695
|
});
|
|
12696
12696
|
if (!ok) return false;
|
|
12697
|
-
|
|
12698
|
-
return
|
|
12697
|
+
os42.augmentPath([`${os42.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
|
|
12698
|
+
return os42.findInPath("coderabbit") !== null;
|
|
12699
12699
|
}
|
|
12700
12700
|
|
|
12701
12701
|
// src/agents/coderabbit/link.ts
|
|
@@ -12730,10 +12730,10 @@ function coderabbitCredentialLocator() {
|
|
|
12730
12730
|
validate: validateNonEmptyCredential
|
|
12731
12731
|
};
|
|
12732
12732
|
}
|
|
12733
|
-
function coderabbitLoginLauncher(
|
|
12733
|
+
function coderabbitLoginLauncher(os42) {
|
|
12734
12734
|
return {
|
|
12735
12735
|
async ensureInstalled() {
|
|
12736
|
-
return ensureCoderabbitInstalled(
|
|
12736
|
+
return ensureCoderabbitInstalled(os42);
|
|
12737
12737
|
},
|
|
12738
12738
|
launch() {
|
|
12739
12739
|
return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
|
|
@@ -12779,8 +12779,8 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
12779
12779
|
meta = getAgent("coderabbit");
|
|
12780
12780
|
mode = "batch";
|
|
12781
12781
|
os;
|
|
12782
|
-
constructor(
|
|
12783
|
-
this.os =
|
|
12782
|
+
constructor(os42) {
|
|
12783
|
+
this.os = os42;
|
|
12784
12784
|
}
|
|
12785
12785
|
getDefaultArgs() {
|
|
12786
12786
|
return ["review"];
|
|
@@ -12896,10 +12896,10 @@ function cursorCredentialLocator() {
|
|
|
12896
12896
|
validate: validateNonEmptyCredential
|
|
12897
12897
|
};
|
|
12898
12898
|
}
|
|
12899
|
-
function cursorLoginLauncher(
|
|
12899
|
+
function cursorLoginLauncher(os42) {
|
|
12900
12900
|
return {
|
|
12901
12901
|
async ensureInstalled() {
|
|
12902
|
-
if (
|
|
12902
|
+
if (os42.findInPath("cursor-agent")) return true;
|
|
12903
12903
|
console.error(
|
|
12904
12904
|
"\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
|
|
12905
12905
|
);
|
|
@@ -12961,8 +12961,8 @@ var CursorRuntimeStrategy = class {
|
|
|
12961
12961
|
meta = getAgent("cursor");
|
|
12962
12962
|
mode = "interactive";
|
|
12963
12963
|
os;
|
|
12964
|
-
constructor(
|
|
12965
|
-
this.os =
|
|
12964
|
+
constructor(os42) {
|
|
12965
|
+
this.os = os42;
|
|
12966
12966
|
}
|
|
12967
12967
|
async prepareLaunch() {
|
|
12968
12968
|
const binary = this.os.findInPath("cursor-agent");
|
|
@@ -13101,10 +13101,10 @@ function aiderCredentialLocator() {
|
|
|
13101
13101
|
validate: validateNonEmptyCredential
|
|
13102
13102
|
};
|
|
13103
13103
|
}
|
|
13104
|
-
function aiderLoginLauncher(
|
|
13104
|
+
function aiderLoginLauncher(os42) {
|
|
13105
13105
|
return {
|
|
13106
13106
|
async ensureInstalled() {
|
|
13107
|
-
if (
|
|
13107
|
+
if (os42.findInPath("aider")) return true;
|
|
13108
13108
|
console.error(
|
|
13109
13109
|
"\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
|
|
13110
13110
|
);
|
|
@@ -13114,7 +13114,7 @@ function aiderLoginLauncher(os41) {
|
|
|
13114
13114
|
console.error(
|
|
13115
13115
|
"\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
|
|
13116
13116
|
);
|
|
13117
|
-
return (0, import_node_child_process9.spawn)(
|
|
13117
|
+
return (0, import_node_child_process9.spawn)(os42.id === "win32" ? "cmd.exe" : "sh", os42.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
|
|
13118
13118
|
stdio: "ignore"
|
|
13119
13119
|
});
|
|
13120
13120
|
}
|
|
@@ -13186,8 +13186,8 @@ var AiderRuntimeStrategy = class {
|
|
|
13186
13186
|
meta = getAgent("aider");
|
|
13187
13187
|
mode = "interactive";
|
|
13188
13188
|
os;
|
|
13189
|
-
constructor(
|
|
13190
|
-
this.os =
|
|
13189
|
+
constructor(os42) {
|
|
13190
|
+
this.os = os42;
|
|
13191
13191
|
}
|
|
13192
13192
|
async prepareLaunch() {
|
|
13193
13193
|
const binary = this.os.findInPath("aider");
|
|
@@ -13316,8 +13316,8 @@ function geminiCredentialLocator() {
|
|
|
13316
13316
|
function geminiLoginLauncher() {
|
|
13317
13317
|
return {
|
|
13318
13318
|
async ensureInstalled() {
|
|
13319
|
-
const
|
|
13320
|
-
return
|
|
13319
|
+
const os42 = createOsStrategy();
|
|
13320
|
+
return os42.findInPath("gemini") !== null;
|
|
13321
13321
|
},
|
|
13322
13322
|
launch() {
|
|
13323
13323
|
return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
|
|
@@ -13350,8 +13350,8 @@ var GeminiRuntimeStrategy = class {
|
|
|
13350
13350
|
meta = getAgent("gemini");
|
|
13351
13351
|
mode = "interactive";
|
|
13352
13352
|
os;
|
|
13353
|
-
constructor(
|
|
13354
|
-
this.os =
|
|
13353
|
+
constructor(os42) {
|
|
13354
|
+
this.os = os42;
|
|
13355
13355
|
}
|
|
13356
13356
|
async prepareLaunch() {
|
|
13357
13357
|
const binary = this.os.findInPath("gemini");
|
|
@@ -13450,18 +13450,18 @@ var GeminiRuntimeStrategy = class {
|
|
|
13450
13450
|
|
|
13451
13451
|
// src/agents/registry.ts
|
|
13452
13452
|
var runtimeBuilders = {
|
|
13453
|
-
claude: (
|
|
13454
|
-
codex: (
|
|
13455
|
-
coderabbit: (
|
|
13456
|
-
cursor: (
|
|
13457
|
-
aider: (
|
|
13458
|
-
gemini: (
|
|
13453
|
+
claude: (os42) => new ClaudeRuntimeStrategy(os42),
|
|
13454
|
+
codex: (os42) => new CodexRuntimeStrategy(os42),
|
|
13455
|
+
coderabbit: (os42) => new CoderabbitRuntimeStrategy(os42),
|
|
13456
|
+
cursor: (os42) => new CursorRuntimeStrategy(os42),
|
|
13457
|
+
aider: (os42) => new AiderRuntimeStrategy(os42),
|
|
13458
|
+
gemini: (os42) => new GeminiRuntimeStrategy(os42)
|
|
13459
13459
|
};
|
|
13460
13460
|
var deployBuilders = {
|
|
13461
13461
|
claude: () => new ClaudeDeployStrategy(),
|
|
13462
13462
|
codex: () => new CodexDeployStrategy()
|
|
13463
13463
|
};
|
|
13464
|
-
function createAgentStrategy(agent,
|
|
13464
|
+
function createAgentStrategy(agent, os42 = createOsStrategy()) {
|
|
13465
13465
|
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
13466
13466
|
throw new Error(
|
|
13467
13467
|
`Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
@@ -13471,10 +13471,10 @@ function createAgentStrategy(agent, os41 = createOsStrategy()) {
|
|
|
13471
13471
|
if (!build) {
|
|
13472
13472
|
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
13473
13473
|
}
|
|
13474
|
-
return build(
|
|
13474
|
+
return build(os42);
|
|
13475
13475
|
}
|
|
13476
|
-
function createInteractiveAgentStrategy(agent,
|
|
13477
|
-
const s = createAgentStrategy(agent,
|
|
13476
|
+
function createInteractiveAgentStrategy(agent, os42 = createOsStrategy()) {
|
|
13477
|
+
const s = createAgentStrategy(agent, os42);
|
|
13478
13478
|
if (s.mode !== "interactive") {
|
|
13479
13479
|
throw new Error(
|
|
13480
13480
|
`Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
|
|
@@ -13877,19 +13877,39 @@ async function linkDryRunPreflight(ctx) {
|
|
|
13877
13877
|
}
|
|
13878
13878
|
|
|
13879
13879
|
// src/commands/host-agent.ts
|
|
13880
|
-
var
|
|
13881
|
-
var
|
|
13882
|
-
var
|
|
13880
|
+
var import_node_child_process15 = require("child_process");
|
|
13881
|
+
var os29 = __toESM(require("os"));
|
|
13882
|
+
var fs32 = __toESM(require("fs"));
|
|
13883
13883
|
var path36 = __toESM(require("path"));
|
|
13884
13884
|
|
|
13885
|
+
// src/util/restrict-to-owner.ts
|
|
13886
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
13887
|
+
var import_node_os3 = __toESM(require("os"));
|
|
13888
|
+
var import_node_child_process11 = require("child_process");
|
|
13889
|
+
function restrictToOwner(filePath) {
|
|
13890
|
+
try {
|
|
13891
|
+
if (process.platform === "win32") {
|
|
13892
|
+
const username = import_node_os3.default.userInfo().username;
|
|
13893
|
+
(0, import_node_child_process11.execFileSync)(
|
|
13894
|
+
"icacls",
|
|
13895
|
+
[filePath, "/inheritance:r", "/grant:r", `${username}:F`],
|
|
13896
|
+
{ stdio: "ignore" }
|
|
13897
|
+
);
|
|
13898
|
+
} else {
|
|
13899
|
+
import_node_fs5.default.chmodSync(filePath, 384);
|
|
13900
|
+
}
|
|
13901
|
+
} catch {
|
|
13902
|
+
}
|
|
13903
|
+
}
|
|
13904
|
+
|
|
13885
13905
|
// src/commands/host/host-client.ts
|
|
13886
|
-
var
|
|
13887
|
-
var
|
|
13906
|
+
var fs27 = __toESM(require("fs"));
|
|
13907
|
+
var os24 = __toESM(require("os"));
|
|
13888
13908
|
var path31 = __toESM(require("path"));
|
|
13889
13909
|
function sampleCpuTimes() {
|
|
13890
13910
|
let idle = 0;
|
|
13891
13911
|
let total = 0;
|
|
13892
|
-
for (const cpu of
|
|
13912
|
+
for (const cpu of os24.cpus()) {
|
|
13893
13913
|
const t2 = cpu.times;
|
|
13894
13914
|
idle += t2.idle;
|
|
13895
13915
|
total += t2.user + t2.nice + t2.sys + t2.idle + t2.irq;
|
|
@@ -13908,8 +13928,8 @@ var MetricsCollector = class {
|
|
|
13908
13928
|
const prev = this.prevCpu;
|
|
13909
13929
|
this.prevCpu = current;
|
|
13910
13930
|
if (!prev) {
|
|
13911
|
-
const cores =
|
|
13912
|
-
const proxy =
|
|
13931
|
+
const cores = os24.cpus().length || 1;
|
|
13932
|
+
const proxy = os24.loadavg()[0] / cores * 100;
|
|
13913
13933
|
return Math.min(100, Math.max(0, Math.round(proxy)));
|
|
13914
13934
|
}
|
|
13915
13935
|
const idleDelta = current.idle - prev.idle;
|
|
@@ -13922,8 +13942,8 @@ var MetricsCollector = class {
|
|
|
13922
13942
|
collect() {
|
|
13923
13943
|
return {
|
|
13924
13944
|
cpuPct: this.cpuPct(),
|
|
13925
|
-
ramUsedMb: Math.round((
|
|
13926
|
-
ramTotalMb: Math.round(
|
|
13945
|
+
ramUsedMb: Math.round((os24.totalmem() - os24.freemem()) / 1048576),
|
|
13946
|
+
ramTotalMb: Math.round(os24.totalmem() / 1048576),
|
|
13927
13947
|
latencyMs: this.lastLatencyMs
|
|
13928
13948
|
};
|
|
13929
13949
|
}
|
|
@@ -13932,19 +13952,19 @@ function apiBase() {
|
|
|
13932
13952
|
return process.env.CODEAM_API_URL ?? resolveApiBaseUrl();
|
|
13933
13953
|
}
|
|
13934
13954
|
function hostIdentityPath() {
|
|
13935
|
-
return path31.join(
|
|
13955
|
+
return path31.join(os24.homedir(), ".codeam", "host-agent.json");
|
|
13936
13956
|
}
|
|
13937
13957
|
function collectOsInfo() {
|
|
13938
13958
|
return {
|
|
13939
|
-
distro:
|
|
13940
|
-
arch:
|
|
13941
|
-
kernel:
|
|
13959
|
+
distro: os24.platform(),
|
|
13960
|
+
arch: os24.arch(),
|
|
13961
|
+
kernel: os24.release(),
|
|
13942
13962
|
nodeVersion: process.versions.node
|
|
13943
13963
|
};
|
|
13944
13964
|
}
|
|
13945
13965
|
function loadHostIdentity() {
|
|
13946
13966
|
try {
|
|
13947
|
-
const raw =
|
|
13967
|
+
const raw = fs27.readFileSync(hostIdentityPath(), "utf8");
|
|
13948
13968
|
const parsed = JSON.parse(raw);
|
|
13949
13969
|
if (typeof parsed === "object" && parsed !== null && typeof parsed.hostId === "string" && typeof parsed.hostToken === "string" && typeof parsed.controlPluginId === "string") {
|
|
13950
13970
|
const p2 = parsed;
|
|
@@ -13957,12 +13977,12 @@ function loadHostIdentity() {
|
|
|
13957
13977
|
}
|
|
13958
13978
|
function saveHostIdentity(identity) {
|
|
13959
13979
|
const file = hostIdentityPath();
|
|
13960
|
-
|
|
13961
|
-
|
|
13980
|
+
fs27.mkdirSync(path31.dirname(file), { recursive: true, mode: 448 });
|
|
13981
|
+
fs27.writeFileSync(file, JSON.stringify(identity, null, 2), {
|
|
13962
13982
|
encoding: "utf8",
|
|
13963
13983
|
mode: 384
|
|
13964
13984
|
});
|
|
13965
|
-
|
|
13985
|
+
fs27.chmodSync(file, 384);
|
|
13966
13986
|
}
|
|
13967
13987
|
var HostHttpError = class extends Error {
|
|
13968
13988
|
constructor(message, status2) {
|
|
@@ -13985,7 +14005,7 @@ function isHostAuthRejection(err) {
|
|
|
13985
14005
|
}
|
|
13986
14006
|
function deleteHostIdentity() {
|
|
13987
14007
|
try {
|
|
13988
|
-
|
|
14008
|
+
fs27.rmSync(hostIdentityPath(), { force: true });
|
|
13989
14009
|
} catch {
|
|
13990
14010
|
}
|
|
13991
14011
|
}
|
|
@@ -14107,17 +14127,17 @@ async function reportDeployProgress(auth, deployId, step, message, sessionId) {
|
|
|
14107
14127
|
}
|
|
14108
14128
|
|
|
14109
14129
|
// src/commands/host/workspace.ts
|
|
14110
|
-
var
|
|
14111
|
-
var
|
|
14130
|
+
var fs28 = __toESM(require("fs"));
|
|
14131
|
+
var os25 = __toESM(require("os"));
|
|
14112
14132
|
var path32 = __toESM(require("path"));
|
|
14113
|
-
var
|
|
14133
|
+
var import_node_child_process12 = require("child_process");
|
|
14114
14134
|
var import_node_util4 = require("util");
|
|
14115
|
-
var execFileP4 = (0, import_node_util4.promisify)(
|
|
14135
|
+
var execFileP4 = (0, import_node_util4.promisify)(import_node_child_process12.execFile);
|
|
14116
14136
|
function isAbsolutePathTarget(target) {
|
|
14117
14137
|
return path32.isAbsolute(target);
|
|
14118
14138
|
}
|
|
14119
14139
|
function selfHostedWorkspaceRoot() {
|
|
14120
|
-
return path32.join(
|
|
14140
|
+
return path32.join(os25.homedir(), ".codeam", "self-hosted");
|
|
14121
14141
|
}
|
|
14122
14142
|
function nonInteractiveGitEnv() {
|
|
14123
14143
|
return {
|
|
@@ -14171,12 +14191,9 @@ async function configureGitCredentials(dest, repoRef, cloneToken) {
|
|
|
14171
14191
|
const gh = githubOwnerRepo(repoRef.trim());
|
|
14172
14192
|
if (!gh || !cloneToken) return;
|
|
14173
14193
|
const credFile = path32.join(dest, ".git", "codeam-credentials");
|
|
14174
|
-
|
|
14194
|
+
fs28.writeFileSync(credFile, `https://x-access-token:${cloneToken}@github.com
|
|
14175
14195
|
`, { mode: 384 });
|
|
14176
|
-
|
|
14177
|
-
fs27.chmodSync(credFile, 384);
|
|
14178
|
-
} catch {
|
|
14179
|
-
}
|
|
14196
|
+
restrictToOwner(credFile);
|
|
14180
14197
|
const env = nonInteractiveGitEnv();
|
|
14181
14198
|
const git2 = (args2) => execFileP4("git", ["-C", dest, ...args2], { timeout: 3e4, env });
|
|
14182
14199
|
const credFilePosix = credFile.split(path32.sep).join("/");
|
|
@@ -14215,17 +14232,17 @@ function maskToken(text, cloneToken) {
|
|
|
14215
14232
|
}
|
|
14216
14233
|
async function prepareWorkspace(repoOrPath, deployId, cloneToken) {
|
|
14217
14234
|
if (isAbsolutePathTarget(repoOrPath)) {
|
|
14218
|
-
if (!
|
|
14235
|
+
if (!fs28.existsSync(repoOrPath)) {
|
|
14219
14236
|
throw new Error(`deploy target path does not exist: ${repoOrPath}`);
|
|
14220
14237
|
}
|
|
14221
14238
|
return repoOrPath;
|
|
14222
14239
|
}
|
|
14223
14240
|
const dest = path32.join(selfHostedWorkspaceRoot(), deployId);
|
|
14224
|
-
if (
|
|
14241
|
+
if (fs28.existsSync(path32.join(dest, ".git"))) {
|
|
14225
14242
|
if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken);
|
|
14226
14243
|
return dest;
|
|
14227
14244
|
}
|
|
14228
|
-
|
|
14245
|
+
fs28.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
|
|
14229
14246
|
const cloneUrl = repoCloneUrl(repoOrPath, cloneToken);
|
|
14230
14247
|
try {
|
|
14231
14248
|
await execFileP4("git", ["clone", "--depth", "1", cloneUrl, dest], {
|
|
@@ -14242,8 +14259,8 @@ async function prepareWorkspace(repoOrPath, deployId, cloneToken) {
|
|
|
14242
14259
|
}
|
|
14243
14260
|
|
|
14244
14261
|
// src/commands/host/agent-provisioning.ts
|
|
14245
|
-
var
|
|
14246
|
-
var
|
|
14262
|
+
var fs29 = __toESM(require("fs"));
|
|
14263
|
+
var os26 = __toESM(require("os"));
|
|
14247
14264
|
var path33 = __toESM(require("path"));
|
|
14248
14265
|
var PUBLIC_TO_INTERNAL_AGENT = {
|
|
14249
14266
|
claude_code: "claude",
|
|
@@ -14259,16 +14276,16 @@ function toInternalAgentId(publicAgentId) {
|
|
|
14259
14276
|
return PUBLIC_TO_INTERNAL_AGENT[publicAgentId] ?? null;
|
|
14260
14277
|
}
|
|
14261
14278
|
function ensureDir(dir) {
|
|
14262
|
-
|
|
14279
|
+
fs29.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
14263
14280
|
}
|
|
14264
14281
|
function writeFile0600(filePath, contents) {
|
|
14265
14282
|
ensureDir(path33.dirname(filePath));
|
|
14266
|
-
|
|
14267
|
-
|
|
14283
|
+
fs29.writeFileSync(filePath, contents, { encoding: "utf8", mode: 384 });
|
|
14284
|
+
restrictToOwner(filePath);
|
|
14268
14285
|
}
|
|
14269
14286
|
function rmIfExists(filePath) {
|
|
14270
14287
|
try {
|
|
14271
|
-
|
|
14288
|
+
fs29.rmSync(filePath, { force: true });
|
|
14272
14289
|
} catch {
|
|
14273
14290
|
}
|
|
14274
14291
|
}
|
|
@@ -14287,7 +14304,7 @@ var claudeProvisioner = {
|
|
|
14287
14304
|
rmIfExists(credentialsJson);
|
|
14288
14305
|
}
|
|
14289
14306
|
const claudeJson = path33.join(home, ".claude.json");
|
|
14290
|
-
if (!
|
|
14307
|
+
if (!fs29.existsSync(claudeJson)) {
|
|
14291
14308
|
writeFile0600(
|
|
14292
14309
|
claudeJson,
|
|
14293
14310
|
JSON.stringify({ hasCompletedOnboarding: true, customApiKeyResponses: { approved: [] } })
|
|
@@ -14360,7 +14377,7 @@ var UnsupportedAgentError = class extends Error {
|
|
|
14360
14377
|
this.agentId = agentId;
|
|
14361
14378
|
}
|
|
14362
14379
|
};
|
|
14363
|
-
function provisionAgentCredentials(publicAgentId, auth, homeDir2 =
|
|
14380
|
+
function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os26.homedir()) {
|
|
14364
14381
|
const internal = toInternalAgentId(publicAgentId);
|
|
14365
14382
|
if (!internal) throw new UnsupportedAgentError(publicAgentId);
|
|
14366
14383
|
const provisioner = PROVISIONERS[internal];
|
|
@@ -14369,12 +14386,12 @@ function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os25.homedir(
|
|
|
14369
14386
|
}
|
|
14370
14387
|
|
|
14371
14388
|
// src/commands/host/git-tooling.ts
|
|
14372
|
-
var
|
|
14373
|
-
var
|
|
14374
|
-
var
|
|
14389
|
+
var import_node_child_process13 = require("child_process");
|
|
14390
|
+
var fs30 = __toESM(require("fs"));
|
|
14391
|
+
var os27 = __toESM(require("os"));
|
|
14375
14392
|
var path34 = __toESM(require("path"));
|
|
14376
14393
|
function codeamBinDir() {
|
|
14377
|
-
return process.env.CODEAM_BIN_DIR ?? path34.join(
|
|
14394
|
+
return process.env.CODEAM_BIN_DIR ?? path34.join(os27.homedir(), ".codeam", "bin");
|
|
14378
14395
|
}
|
|
14379
14396
|
var FALLBACK_GH_VERSION = "2.62.0";
|
|
14380
14397
|
var RELEASE_API = "https://api.github.com/repos/cli/cli/releases/latest";
|
|
@@ -14406,7 +14423,7 @@ async function download(url, dest) {
|
|
|
14406
14423
|
const res = await fetch(url, { headers: { "User-Agent": "codeam-cli" } });
|
|
14407
14424
|
if (!res.ok || !res.body) return false;
|
|
14408
14425
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
14409
|
-
|
|
14426
|
+
fs30.writeFileSync(dest, buf);
|
|
14410
14427
|
return true;
|
|
14411
14428
|
} catch {
|
|
14412
14429
|
return false;
|
|
@@ -14429,7 +14446,7 @@ async function ensureGhCli(runner, token, deps = {}) {
|
|
|
14429
14446
|
const version3 = await resolveVersionFn(token);
|
|
14430
14447
|
const asset = `gh_${version3}_${osToken}_${arch2}`;
|
|
14431
14448
|
const url = `https://github.com/cli/cli/releases/download/v${version3}/${asset}.${ext}`;
|
|
14432
|
-
const tmpRoot =
|
|
14449
|
+
const tmpRoot = fs30.mkdtempSync(path34.join(os27.tmpdir(), "codeam-gh-"));
|
|
14433
14450
|
const archive = path34.join(tmpRoot, `${asset}.${ext}`);
|
|
14434
14451
|
if (!await downloadFn(url, archive)) {
|
|
14435
14452
|
log.warn("host-agent", "gh download failed \u2014 skipping (git pull/push still work via the credential helper)");
|
|
@@ -14441,15 +14458,15 @@ async function ensureGhCli(runner, token, deps = {}) {
|
|
|
14441
14458
|
return null;
|
|
14442
14459
|
}
|
|
14443
14460
|
const extractedBin = path34.join(tmpRoot, asset, "bin", binaryName);
|
|
14444
|
-
if (!
|
|
14461
|
+
if (!fs30.existsSync(extractedBin)) {
|
|
14445
14462
|
log.warn("host-agent", "gh binary not found in the extracted archive \u2014 skipping");
|
|
14446
14463
|
return null;
|
|
14447
14464
|
}
|
|
14448
14465
|
const binDir = codeamBinDir();
|
|
14449
|
-
|
|
14466
|
+
fs30.mkdirSync(binDir, { recursive: true });
|
|
14450
14467
|
const target = path34.join(binDir, binaryName);
|
|
14451
|
-
|
|
14452
|
-
|
|
14468
|
+
fs30.copyFileSync(extractedBin, target);
|
|
14469
|
+
fs30.chmodSync(target, 493);
|
|
14453
14470
|
log.info("host-agent", `gh installed to ${target} (v${version3})`);
|
|
14454
14471
|
return target;
|
|
14455
14472
|
} catch (e) {
|
|
@@ -14483,7 +14500,7 @@ var defaultGitToolingRunner = {
|
|
|
14483
14500
|
which(cmd) {
|
|
14484
14501
|
try {
|
|
14485
14502
|
const probe = process.platform === "win32" ? "where" : "which";
|
|
14486
|
-
(0,
|
|
14503
|
+
(0, import_node_child_process13.execFileSync)(probe, [cmd], { stdio: "ignore" });
|
|
14487
14504
|
return true;
|
|
14488
14505
|
} catch {
|
|
14489
14506
|
return false;
|
|
@@ -14491,7 +14508,7 @@ var defaultGitToolingRunner = {
|
|
|
14491
14508
|
},
|
|
14492
14509
|
run(cmd, args2, opts = {}) {
|
|
14493
14510
|
return new Promise((resolve7) => {
|
|
14494
|
-
const child = (0,
|
|
14511
|
+
const child = (0, import_node_child_process13.spawn)(cmd, args2, {
|
|
14495
14512
|
stdio: [opts.input !== void 0 ? "pipe" : "ignore", "ignore", "pipe"]
|
|
14496
14513
|
});
|
|
14497
14514
|
let stderr = "";
|
|
@@ -14578,6 +14595,14 @@ function mapStatsToSavings(stats, prev, inputPricePerMillion = DEFAULT_INPUT_PRI
|
|
|
14578
14595
|
};
|
|
14579
14596
|
return { next, delta };
|
|
14580
14597
|
}
|
|
14598
|
+
function defaultGetBudgetEnv() {
|
|
14599
|
+
const raw = process.env["HEADROOM_BUDGET"];
|
|
14600
|
+
if (!raw) return null;
|
|
14601
|
+
const budgetUsd = Number(raw);
|
|
14602
|
+
if (!isFinite(budgetUsd) || budgetUsd <= 0) return null;
|
|
14603
|
+
const budgetPeriod = process.env["HEADROOM_BUDGET_PERIOD"] ?? "daily";
|
|
14604
|
+
return { budgetUsd, budgetPeriod };
|
|
14605
|
+
}
|
|
14581
14606
|
var HeadroomStatsReporter = class {
|
|
14582
14607
|
constructor(deps) {
|
|
14583
14608
|
this.deps = deps;
|
|
@@ -14591,14 +14616,22 @@ var HeadroomStatsReporter = class {
|
|
|
14591
14616
|
}
|
|
14592
14617
|
async tick() {
|
|
14593
14618
|
try {
|
|
14619
|
+
const stats = await this.deps.fetchStats();
|
|
14594
14620
|
const { next, delta } = mapStatsToSavings(
|
|
14595
|
-
|
|
14621
|
+
stats,
|
|
14596
14622
|
this.prev,
|
|
14597
14623
|
this.deps.inputPricePerMillionUsd ?? DEFAULT_INPUT_PRICE_PER_MILLION
|
|
14598
14624
|
);
|
|
14599
14625
|
this.prev = next;
|
|
14600
14626
|
if (delta.compressionTokens > 0 || delta.compressionSavingsUsd > 0 || delta.rawTokensEst > 0 || delta.cacheReadTokens > 0 || delta.cacheSavingsUsd > 0) {
|
|
14601
|
-
|
|
14627
|
+
const getBudgetEnv = this.deps.getBudgetEnv ?? defaultGetBudgetEnv;
|
|
14628
|
+
const budgetEnv = getBudgetEnv();
|
|
14629
|
+
const budget = budgetEnv ? {
|
|
14630
|
+
periodSpendUsd: stats.cost?.cost_with_headroom_usd ?? 0,
|
|
14631
|
+
budgetUsd: budgetEnv.budgetUsd,
|
|
14632
|
+
budgetPeriod: budgetEnv.budgetPeriod
|
|
14633
|
+
} : void 0;
|
|
14634
|
+
await this.deps.postSavings(delta, budget);
|
|
14602
14635
|
}
|
|
14603
14636
|
} catch {
|
|
14604
14637
|
}
|
|
@@ -14611,24 +14644,32 @@ var HeadroomStatsReporter = class {
|
|
|
14611
14644
|
}
|
|
14612
14645
|
};
|
|
14613
14646
|
|
|
14647
|
+
// src/services/headroom/budget-args.ts
|
|
14648
|
+
function buildBudgetProxyArgs(env) {
|
|
14649
|
+
const budget = env["HEADROOM_BUDGET"];
|
|
14650
|
+
if (!budget) return [];
|
|
14651
|
+
const period = env["HEADROOM_BUDGET_PERIOD"] ?? "daily";
|
|
14652
|
+
return ["--budget", budget, "--budget-period", period];
|
|
14653
|
+
}
|
|
14654
|
+
|
|
14614
14655
|
// src/lib/updateNotifier.ts
|
|
14615
|
-
var
|
|
14616
|
-
var
|
|
14656
|
+
var fs31 = __toESM(require("fs"));
|
|
14657
|
+
var os28 = __toESM(require("os"));
|
|
14617
14658
|
var path35 = __toESM(require("path"));
|
|
14618
14659
|
var https6 = __toESM(require("https"));
|
|
14619
|
-
var
|
|
14660
|
+
var import_node_child_process14 = require("child_process");
|
|
14620
14661
|
var import_picocolors3 = __toESM(require("picocolors"));
|
|
14621
14662
|
var PKG_NAME = "codeam-cli";
|
|
14622
14663
|
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
14623
14664
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14624
14665
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
14625
14666
|
function cachePath() {
|
|
14626
|
-
const dir = path35.join(
|
|
14667
|
+
const dir = path35.join(os28.homedir(), ".codeam");
|
|
14627
14668
|
return path35.join(dir, "update-check.json");
|
|
14628
14669
|
}
|
|
14629
14670
|
function readCache() {
|
|
14630
14671
|
try {
|
|
14631
|
-
const raw =
|
|
14672
|
+
const raw = fs31.readFileSync(cachePath(), "utf8");
|
|
14632
14673
|
const parsed = JSON.parse(raw);
|
|
14633
14674
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
14634
14675
|
return parsed;
|
|
@@ -14639,10 +14680,10 @@ function readCache() {
|
|
|
14639
14680
|
function writeCache(cache) {
|
|
14640
14681
|
try {
|
|
14641
14682
|
const file = cachePath();
|
|
14642
|
-
|
|
14683
|
+
fs31.mkdirSync(path35.dirname(file), { recursive: true });
|
|
14643
14684
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
14644
|
-
|
|
14645
|
-
|
|
14685
|
+
fs31.writeFileSync(tmp, JSON.stringify(cache));
|
|
14686
|
+
fs31.renameSync(tmp, file);
|
|
14646
14687
|
} catch {
|
|
14647
14688
|
}
|
|
14648
14689
|
}
|
|
@@ -14710,14 +14751,14 @@ function notifyIfStale(currentVersion, latest) {
|
|
|
14710
14751
|
}
|
|
14711
14752
|
function isLinkedInstall() {
|
|
14712
14753
|
try {
|
|
14713
|
-
const root = (0,
|
|
14754
|
+
const root = (0, import_node_child_process14.execSync)("npm root -g", {
|
|
14714
14755
|
encoding: "utf8",
|
|
14715
14756
|
stdio: ["ignore", "pipe", "ignore"],
|
|
14716
14757
|
timeout: 2e3
|
|
14717
14758
|
}).trim();
|
|
14718
14759
|
if (!root) return false;
|
|
14719
14760
|
const pkgPath = path35.join(root, PKG_NAME);
|
|
14720
|
-
return
|
|
14761
|
+
return fs31.lstatSync(pkgPath).isSymbolicLink();
|
|
14721
14762
|
} catch {
|
|
14722
14763
|
return false;
|
|
14723
14764
|
}
|
|
@@ -14738,7 +14779,7 @@ function maybeAutoUpdate(currentVersion, latest) {
|
|
|
14738
14779
|
|
|
14739
14780
|
`
|
|
14740
14781
|
);
|
|
14741
|
-
const install = (0,
|
|
14782
|
+
const install = (0, import_node_child_process14.spawnSync)("npm", ["install", "-g", `${PKG_NAME}@latest`], {
|
|
14742
14783
|
stdio: "inherit",
|
|
14743
14784
|
env: process.env
|
|
14744
14785
|
});
|
|
@@ -14753,13 +14794,13 @@ function maybeAutoUpdate(currentVersion, latest) {
|
|
|
14753
14794
|
return;
|
|
14754
14795
|
}
|
|
14755
14796
|
try {
|
|
14756
|
-
|
|
14797
|
+
fs31.unlinkSync(cachePath());
|
|
14757
14798
|
} catch {
|
|
14758
14799
|
}
|
|
14759
14800
|
process.stderr.write(` ${import_picocolors3.default.green("\u2713")} Updated. Resuming session...
|
|
14760
14801
|
|
|
14761
14802
|
`);
|
|
14762
|
-
const child = (0,
|
|
14803
|
+
const child = (0, import_node_child_process14.spawnSync)("codeam", process.argv.slice(2), {
|
|
14763
14804
|
stdio: "inherit",
|
|
14764
14805
|
env: process.env
|
|
14765
14806
|
});
|
|
@@ -14769,7 +14810,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
14769
14810
|
if (process.env.NODE_ENV === "test") return;
|
|
14770
14811
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14771
14812
|
if (process.env.CI) return;
|
|
14772
|
-
const current = true ? "2.
|
|
14813
|
+
const current = true ? "2.51.1" : null;
|
|
14773
14814
|
if (!current) return;
|
|
14774
14815
|
const cache = readCache();
|
|
14775
14816
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -14786,7 +14827,7 @@ function checkForUpdates() {
|
|
|
14786
14827
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14787
14828
|
if (process.env.CI) return;
|
|
14788
14829
|
if (!process.stdout.isTTY) return;
|
|
14789
|
-
const current = true ? "2.
|
|
14830
|
+
const current = true ? "2.51.1" : null;
|
|
14790
14831
|
if (!current) return;
|
|
14791
14832
|
const cache = readCache();
|
|
14792
14833
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -14823,7 +14864,7 @@ function maybeStartHeadroomReporter(ctx) {
|
|
|
14823
14864
|
const res = await fetch("http://localhost:8787/stats");
|
|
14824
14865
|
return res.json();
|
|
14825
14866
|
},
|
|
14826
|
-
postSavings: async (delta) => {
|
|
14867
|
+
postSavings: async (delta, budget) => {
|
|
14827
14868
|
await fetch(ingestUrl, {
|
|
14828
14869
|
method: "POST",
|
|
14829
14870
|
headers: {
|
|
@@ -14834,7 +14875,12 @@ function maybeStartHeadroomReporter(ctx) {
|
|
|
14834
14875
|
sessionId: ctx.sessionId,
|
|
14835
14876
|
pluginId: ctx.pluginId,
|
|
14836
14877
|
agentId: process.env["HEADROOM_AGENT"] ?? "claude",
|
|
14837
|
-
savings: delta
|
|
14878
|
+
savings: delta,
|
|
14879
|
+
...budget ? {
|
|
14880
|
+
periodSpendUsd: budget.periodSpendUsd,
|
|
14881
|
+
budgetUsd: budget.budgetUsd,
|
|
14882
|
+
budgetPeriod: budget.budgetPeriod
|
|
14883
|
+
} : {}
|
|
14838
14884
|
})
|
|
14839
14885
|
});
|
|
14840
14886
|
}
|
|
@@ -14853,8 +14899,8 @@ function maybeResumeLocalHeadroomReporter(ctx) {
|
|
|
14853
14899
|
if (process.env["HEADROOM_ENABLED"] === "1") return null;
|
|
14854
14900
|
try {
|
|
14855
14901
|
const file = headroomConfigPath();
|
|
14856
|
-
if (!
|
|
14857
|
-
const cfg = JSON.parse(
|
|
14902
|
+
if (!fs32.existsSync(file)) return null;
|
|
14903
|
+
const cfg = JSON.parse(fs32.readFileSync(file, "utf8"));
|
|
14858
14904
|
if (!cfg?.enabled) return null;
|
|
14859
14905
|
const agent = cfg.agent ?? "claude";
|
|
14860
14906
|
const ingestUrl = `${resolveApiBaseUrl()}/api/sessions/${ctx.sessionId}/headroom-savings`;
|
|
@@ -14867,7 +14913,7 @@ function maybeResumeLocalHeadroomReporter(ctx) {
|
|
|
14867
14913
|
// Body MUST match HeadroomSavingsDto + PluginAuthGuard (sessionId +
|
|
14868
14914
|
// pluginId in the body) — same shape as the codespace reporter above and
|
|
14869
14915
|
// the on-demand `startReporter` in handlers.ts.
|
|
14870
|
-
postSavings: async (delta) => {
|
|
14916
|
+
postSavings: async (delta, budget) => {
|
|
14871
14917
|
await fetch(ingestUrl, {
|
|
14872
14918
|
method: "POST",
|
|
14873
14919
|
headers: {
|
|
@@ -14878,7 +14924,12 @@ function maybeResumeLocalHeadroomReporter(ctx) {
|
|
|
14878
14924
|
sessionId: ctx.sessionId,
|
|
14879
14925
|
pluginId: ctx.pluginId,
|
|
14880
14926
|
agentId: agent,
|
|
14881
|
-
savings: delta
|
|
14927
|
+
savings: delta,
|
|
14928
|
+
...budget ? {
|
|
14929
|
+
periodSpendUsd: budget.periodSpendUsd,
|
|
14930
|
+
budgetUsd: budget.budgetUsd,
|
|
14931
|
+
budgetPeriod: budget.budgetPeriod
|
|
14932
|
+
} : {}
|
|
14882
14933
|
})
|
|
14883
14934
|
});
|
|
14884
14935
|
}
|
|
@@ -14940,7 +14991,7 @@ var HEADROOM_MIN_FREE_DISK_BYTES = 2 * 1024 * 1024 * 1024;
|
|
|
14940
14991
|
var defaultHeadroomRunner = {
|
|
14941
14992
|
which(cmd) {
|
|
14942
14993
|
try {
|
|
14943
|
-
(0,
|
|
14994
|
+
(0, import_node_child_process15.execFileSync)("which", [cmd], { stdio: "ignore" });
|
|
14944
14995
|
return true;
|
|
14945
14996
|
} catch {
|
|
14946
14997
|
return false;
|
|
@@ -14949,7 +15000,7 @@ var defaultHeadroomRunner = {
|
|
|
14949
15000
|
run(cmd, args2, opts = {}) {
|
|
14950
15001
|
return new Promise((resolve7) => {
|
|
14951
15002
|
const spawnEnv = opts.env ?? process.env;
|
|
14952
|
-
const child = (0,
|
|
15003
|
+
const child = (0, import_node_child_process15.spawn)(cmd, args2, { stdio: ["ignore", "pipe", "pipe"], env: spawnEnv });
|
|
14953
15004
|
let stderrBuf = "";
|
|
14954
15005
|
let stdoutBuf = "";
|
|
14955
15006
|
let settled = false;
|
|
@@ -15113,15 +15164,16 @@ function isHeadroomSupportedAgent(agentId) {
|
|
|
15113
15164
|
return n.startsWith("claude") || n.startsWith("codex") || n.startsWith("copilot");
|
|
15114
15165
|
}
|
|
15115
15166
|
function headroomConfigPath() {
|
|
15116
|
-
return path36.join(
|
|
15167
|
+
return path36.join(os29.homedir(), ".codeam", "headroom-config.json");
|
|
15117
15168
|
}
|
|
15118
15169
|
function persistHeadroomConfig(config) {
|
|
15119
15170
|
try {
|
|
15120
15171
|
const file = headroomConfigPath();
|
|
15121
|
-
|
|
15172
|
+
fs32.mkdirSync(path36.dirname(file), { recursive: true, mode: 448 });
|
|
15122
15173
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
15123
|
-
|
|
15124
|
-
|
|
15174
|
+
fs32.writeFileSync(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 });
|
|
15175
|
+
fs32.renameSync(tmp, file);
|
|
15176
|
+
restrictToOwner(file);
|
|
15125
15177
|
} catch (err) {
|
|
15126
15178
|
log.warn(
|
|
15127
15179
|
"host-agent",
|
|
@@ -15130,7 +15182,7 @@ function persistHeadroomConfig(config) {
|
|
|
15130
15182
|
}
|
|
15131
15183
|
}
|
|
15132
15184
|
function agentSettingsPath(kind) {
|
|
15133
|
-
const home =
|
|
15185
|
+
const home = os29.homedir();
|
|
15134
15186
|
if (kind === "claude") return path36.join(home, ".claude", "settings.json");
|
|
15135
15187
|
if (kind === "codex") return path36.join(home, ".codex", "auth.json");
|
|
15136
15188
|
if (kind === "copilot") return path36.join(home, ".config", "github-copilot", "hosts.json");
|
|
@@ -15140,11 +15192,11 @@ function backupAgentHeadroomConfig(kind) {
|
|
|
15140
15192
|
const src = agentSettingsPath(kind);
|
|
15141
15193
|
if (!src) return;
|
|
15142
15194
|
try {
|
|
15143
|
-
if (!
|
|
15144
|
-
const dest = path36.join(
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15195
|
+
if (!fs32.existsSync(src)) return;
|
|
15196
|
+
const dest = path36.join(os29.homedir(), ".codeam", `headroom-backup-${kind}.json`);
|
|
15197
|
+
fs32.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
|
|
15198
|
+
fs32.copyFileSync(src, dest);
|
|
15199
|
+
fs32.chmodSync(dest, 384);
|
|
15148
15200
|
log.info("host-agent", `headroom config backup: ${src} \u2192 ${dest}`);
|
|
15149
15201
|
} catch (err) {
|
|
15150
15202
|
log.warn(
|
|
@@ -15156,12 +15208,12 @@ function backupAgentHeadroomConfig(kind) {
|
|
|
15156
15208
|
function restoreAgentHeadroomConfig(kind) {
|
|
15157
15209
|
const dest = agentSettingsPath(kind);
|
|
15158
15210
|
if (!dest) return false;
|
|
15159
|
-
const src = path36.join(
|
|
15160
|
-
if (!
|
|
15211
|
+
const src = path36.join(os29.homedir(), ".codeam", `headroom-backup-${kind}.json`);
|
|
15212
|
+
if (!fs32.existsSync(src)) return false;
|
|
15161
15213
|
try {
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15214
|
+
fs32.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
|
|
15215
|
+
fs32.copyFileSync(src, dest);
|
|
15216
|
+
fs32.chmodSync(dest, 384);
|
|
15165
15217
|
log.info("host-agent", `headroom config restored: ${src} \u2192 ${dest}`);
|
|
15166
15218
|
return true;
|
|
15167
15219
|
} catch (err) {
|
|
@@ -15174,16 +15226,21 @@ function restoreAgentHeadroomConfig(kind) {
|
|
|
15174
15226
|
}
|
|
15175
15227
|
function readHeadroomChildEnv() {
|
|
15176
15228
|
try {
|
|
15177
|
-
const raw =
|
|
15229
|
+
const raw = fs32.readFileSync(headroomConfigPath(), "utf8");
|
|
15178
15230
|
const parsed = JSON.parse(raw);
|
|
15179
15231
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
15180
15232
|
const o = parsed;
|
|
15181
15233
|
if (o.enabled === true && typeof o.agent === "string" && o.agent.length > 0 && typeof o.ingestUrl === "string" && o.ingestUrl.length > 0) {
|
|
15182
|
-
|
|
15234
|
+
const env = {
|
|
15183
15235
|
HEADROOM_ENABLED: "1",
|
|
15184
15236
|
HEADROOM_AGENT: o.agent,
|
|
15185
15237
|
HEADROOM_SAVINGS_INGEST_URL: o.ingestUrl
|
|
15186
15238
|
};
|
|
15239
|
+
if (o.budgetEnabled === true && typeof o.budgetUsd === "number") {
|
|
15240
|
+
env["HEADROOM_BUDGET"] = String(o.budgetUsd);
|
|
15241
|
+
env["HEADROOM_BUDGET_PERIOD"] = typeof o.budgetPeriod === "string" ? o.budgetPeriod : "daily";
|
|
15242
|
+
}
|
|
15243
|
+
return env;
|
|
15187
15244
|
}
|
|
15188
15245
|
return {};
|
|
15189
15246
|
} catch {
|
|
@@ -15210,21 +15267,21 @@ function bundledClaudeBinDir() {
|
|
|
15210
15267
|
const atAnthropic = path36.join(nm, "@anthropic-ai");
|
|
15211
15268
|
let entries;
|
|
15212
15269
|
try {
|
|
15213
|
-
entries =
|
|
15270
|
+
entries = fs32.readdirSync(atAnthropic);
|
|
15214
15271
|
} catch {
|
|
15215
15272
|
continue;
|
|
15216
15273
|
}
|
|
15217
15274
|
for (const entry of entries) {
|
|
15218
15275
|
if (!entry.startsWith("claude-agent-sdk-")) continue;
|
|
15219
15276
|
const bin = path36.join(atAnthropic, entry, "claude");
|
|
15220
|
-
if (
|
|
15277
|
+
if (fs32.existsSync(bin)) return path36.dirname(bin);
|
|
15221
15278
|
}
|
|
15222
15279
|
}
|
|
15223
15280
|
return null;
|
|
15224
15281
|
}
|
|
15225
15282
|
async function getFreeDiskBytes(dir) {
|
|
15226
15283
|
try {
|
|
15227
|
-
const s = await
|
|
15284
|
+
const s = await fs32.promises.statfs(dir);
|
|
15228
15285
|
return s.bsize * s.bavail;
|
|
15229
15286
|
} catch {
|
|
15230
15287
|
return null;
|
|
@@ -15439,11 +15496,16 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
|
|
|
15439
15496
|
}
|
|
15440
15497
|
onProgress("proxy");
|
|
15441
15498
|
try {
|
|
15442
|
-
const
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
|
|
15499
|
+
const proxyEnv = { ...process.env, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
|
|
15500
|
+
const proxy = (0, import_node_child_process15.spawn)(
|
|
15501
|
+
"headroom",
|
|
15502
|
+
["proxy", "--port", "8787", ...buildBudgetProxyArgs(proxyEnv)],
|
|
15503
|
+
{
|
|
15504
|
+
stdio: "ignore",
|
|
15505
|
+
detached: true,
|
|
15506
|
+
env: proxyEnv
|
|
15507
|
+
}
|
|
15508
|
+
);
|
|
15447
15509
|
proxy.once("error", (e) => {
|
|
15448
15510
|
log.warn(
|
|
15449
15511
|
"host-agent",
|
|
@@ -15460,18 +15522,18 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
|
|
|
15460
15522
|
onProgress("ready");
|
|
15461
15523
|
return true;
|
|
15462
15524
|
}
|
|
15463
|
-
var defaultSpawner = (env, cwd, args2 = []) => (0,
|
|
15525
|
+
var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process15.spawn)(process.execPath, [process.argv[1], "pair-auto", ...args2], {
|
|
15464
15526
|
cwd,
|
|
15465
15527
|
env: { ...process.env, ...env },
|
|
15466
15528
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15467
15529
|
detached: false
|
|
15468
15530
|
});
|
|
15469
15531
|
function currentCliVersion() {
|
|
15470
|
-
return true ? "2.
|
|
15532
|
+
return true ? "2.51.1" : null;
|
|
15471
15533
|
}
|
|
15472
15534
|
function runCmd(cmd, args2, timeoutMs) {
|
|
15473
15535
|
return new Promise((resolve7) => {
|
|
15474
|
-
(0,
|
|
15536
|
+
(0, import_node_child_process15.execFile)(cmd, args2, { timeout: timeoutMs }, (err, stdout, stderr) => {
|
|
15475
15537
|
const code = err && typeof err.code === "number" ? err.code : err ? null : 0;
|
|
15476
15538
|
resolve7({ code, stdout: stdout ?? "", stderr: stderr ?? "" });
|
|
15477
15539
|
});
|
|
@@ -15542,20 +15604,20 @@ var defaultOnUpdated = (version3) => {
|
|
|
15542
15604
|
};
|
|
15543
15605
|
var defaultDisableService = () => {
|
|
15544
15606
|
try {
|
|
15545
|
-
(0,
|
|
15607
|
+
(0, import_node_child_process15.execFileSync)("systemctl", ["disable", "--now", "codeam-host-agent"], { stdio: "ignore" });
|
|
15546
15608
|
} catch {
|
|
15547
15609
|
}
|
|
15548
15610
|
};
|
|
15549
15611
|
var defaultTeardownHeadroom = () => {
|
|
15550
15612
|
try {
|
|
15551
|
-
const kind = JSON.parse(
|
|
15613
|
+
const kind = JSON.parse(fs32.readFileSync(headroomConfigPath(), "utf8")).agent;
|
|
15552
15614
|
if (kind) {
|
|
15553
|
-
(0,
|
|
15615
|
+
(0, import_node_child_process15.execFileSync)("headroom", ["unwrap", kind], { stdio: "ignore", timeout: 15e3 });
|
|
15554
15616
|
}
|
|
15555
15617
|
} catch {
|
|
15556
15618
|
}
|
|
15557
15619
|
try {
|
|
15558
|
-
(0,
|
|
15620
|
+
(0, import_node_child_process15.execFileSync)("pkill", ["-TERM", "-f", "headroom.*proxy"], { stdio: "ignore" });
|
|
15559
15621
|
} catch {
|
|
15560
15622
|
}
|
|
15561
15623
|
persistHeadroomConfig({ enabled: false });
|
|
@@ -15862,9 +15924,9 @@ var HostAgentSupervisor = class {
|
|
|
15862
15924
|
API_TIMEOUT_MS: "3000000",
|
|
15863
15925
|
CODEAM_AUTO_TOKEN: payload.autoPairToken
|
|
15864
15926
|
};
|
|
15865
|
-
const houseConfigDir = path36.join(
|
|
15927
|
+
const houseConfigDir = path36.join(os29.homedir(), ".codeam", "house-claude");
|
|
15866
15928
|
try {
|
|
15867
|
-
|
|
15929
|
+
fs32.mkdirSync(houseConfigDir, { recursive: true, mode: 448 });
|
|
15868
15930
|
} catch {
|
|
15869
15931
|
}
|
|
15870
15932
|
childEnv.CLAUDE_CONFIG_DIR = houseConfigDir;
|
|
@@ -15882,7 +15944,7 @@ var HostAgentSupervisor = class {
|
|
|
15882
15944
|
report("installing", "installing agent CLI");
|
|
15883
15945
|
await this.runAgentInstall(payload.agentInstallScript);
|
|
15884
15946
|
}
|
|
15885
|
-
const home = process.env.HOME ||
|
|
15947
|
+
const home = process.env.HOME || os29.homedir();
|
|
15886
15948
|
childEnv.PATH = `${home}/.local/bin:${process.env.PATH ?? ""}`;
|
|
15887
15949
|
if (payload.cloneToken) {
|
|
15888
15950
|
try {
|
|
@@ -15905,7 +15967,7 @@ var HostAgentSupervisor = class {
|
|
|
15905
15967
|
}
|
|
15906
15968
|
if (payload.headroomEnabled && payload.headroomAgent && payload.headroomSavingsIngestUrl && isHeadroomSupportedAgent(payload.headroomAgent)) {
|
|
15907
15969
|
report("headroom", "setting up Headroom proxy");
|
|
15908
|
-
const freeBytes = await this.getFreeDisk(
|
|
15970
|
+
const freeBytes = await this.getFreeDisk(os29.homedir());
|
|
15909
15971
|
const alreadyInstalled = this.isHeadroomInstalled();
|
|
15910
15972
|
if (!alreadyInstalled && freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
|
|
15911
15973
|
const freeGb = (freeBytes / 1e9).toFixed(1);
|
|
@@ -16027,8 +16089,8 @@ var HostAgentSupervisor = class {
|
|
|
16027
16089
|
*/
|
|
16028
16090
|
runAgentInstall(script) {
|
|
16029
16091
|
return new Promise((resolve7) => {
|
|
16030
|
-
const home = process.env.HOME ||
|
|
16031
|
-
const child = (0,
|
|
16092
|
+
const home = process.env.HOME || os29.homedir();
|
|
16093
|
+
const child = (0, import_node_child_process15.spawn)("sh", ["-c", script], {
|
|
16032
16094
|
env: { ...process.env, HOME: home },
|
|
16033
16095
|
stdio: ["ignore", "pipe", "pipe"]
|
|
16034
16096
|
});
|
|
@@ -16831,8 +16893,8 @@ function activePreviewSessionIds() {
|
|
|
16831
16893
|
|
|
16832
16894
|
// src/beads/bd-adapter.ts
|
|
16833
16895
|
var import_child_process15 = require("child_process");
|
|
16834
|
-
var
|
|
16835
|
-
var
|
|
16896
|
+
var fs37 = __toESM(require("fs"));
|
|
16897
|
+
var os31 = __toESM(require("os"));
|
|
16836
16898
|
var path41 = __toESM(require("path"));
|
|
16837
16899
|
var BD_PACKAGE = "@beads/bd";
|
|
16838
16900
|
function resolveBundledBdBinary() {
|
|
@@ -16849,7 +16911,7 @@ function _defaultResolveBundled() {
|
|
|
16849
16911
|
const binaryName = process.platform === "win32" ? "bd.exe" : "bd";
|
|
16850
16912
|
const binaryPath = path41.join(binDir, binaryName);
|
|
16851
16913
|
try {
|
|
16852
|
-
|
|
16914
|
+
fs37.accessSync(binaryPath, fs37.constants.F_OK);
|
|
16853
16915
|
return binaryPath;
|
|
16854
16916
|
} catch {
|
|
16855
16917
|
return null;
|
|
@@ -16865,7 +16927,7 @@ function _defaultResolveOnPath() {
|
|
|
16865
16927
|
for (const candidate of candidates) {
|
|
16866
16928
|
const full = path41.join(dir, candidate);
|
|
16867
16929
|
try {
|
|
16868
|
-
|
|
16930
|
+
fs37.accessSync(full, fs37.constants.F_OK);
|
|
16869
16931
|
return full;
|
|
16870
16932
|
} catch {
|
|
16871
16933
|
}
|
|
@@ -16950,7 +17012,7 @@ var BdAdapter = class {
|
|
|
16950
17012
|
const env = { ...process.env };
|
|
16951
17013
|
if (!env.HOME) {
|
|
16952
17014
|
try {
|
|
16953
|
-
const home =
|
|
17015
|
+
const home = os31.homedir();
|
|
16954
17016
|
if (home) env.HOME = home;
|
|
16955
17017
|
} catch {
|
|
16956
17018
|
}
|
|
@@ -17039,8 +17101,8 @@ function coerceIssue(row, projectKey) {
|
|
|
17039
17101
|
|
|
17040
17102
|
// src/beads/provisioner.ts
|
|
17041
17103
|
var import_child_process19 = require("child_process");
|
|
17042
|
-
var
|
|
17043
|
-
var
|
|
17104
|
+
var fs40 = __toESM(require("fs"));
|
|
17105
|
+
var os33 = __toESM(require("os"));
|
|
17044
17106
|
var path44 = __toESM(require("path"));
|
|
17045
17107
|
|
|
17046
17108
|
// src/beads/install-bd.ts
|
|
@@ -17106,8 +17168,8 @@ async function installBd(platform3 = process.platform) {
|
|
|
17106
17168
|
|
|
17107
17169
|
// src/beads/install-dolt.ts
|
|
17108
17170
|
var import_child_process17 = require("child_process");
|
|
17109
|
-
var
|
|
17110
|
-
var
|
|
17171
|
+
var fs38 = __toESM(require("fs"));
|
|
17172
|
+
var os32 = __toESM(require("os"));
|
|
17111
17173
|
var path42 = __toESM(require("path"));
|
|
17112
17174
|
var DOLT_INSTALL_SH_URL = "https://github.com/dolthub/dolt/releases/latest/download/install.sh";
|
|
17113
17175
|
var DOLT_MSI_URL = "https://github.com/dolthub/dolt/releases/latest/download/dolt-windows-amd64.msi";
|
|
@@ -17148,11 +17210,11 @@ function resolveDoltInstallStrategy(platform3) {
|
|
|
17148
17210
|
}
|
|
17149
17211
|
var DOLT_RELEASE_BASE = "https://github.com/dolthub/dolt/releases/latest/download";
|
|
17150
17212
|
function doltPlatformTuple(platform3, arch2) {
|
|
17151
|
-
const
|
|
17213
|
+
const os42 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
|
|
17152
17214
|
const a = arch2 === "x64" ? "amd64" : arch2 === "arm64" ? "arm64" : null;
|
|
17153
17215
|
if (!a) return null;
|
|
17154
|
-
if (
|
|
17155
|
-
return `${
|
|
17216
|
+
if (os42 === "windows" && a !== "amd64") return null;
|
|
17217
|
+
return `${os42}-${a}`;
|
|
17156
17218
|
}
|
|
17157
17219
|
function resolveDoltTarballStrategy(targetDir, platform3, arch2) {
|
|
17158
17220
|
const tuple = doltPlatformTuple(platform3, arch2);
|
|
@@ -17197,14 +17259,14 @@ async function installDoltToDir(targetDir, platform3 = process.platform, arch2 =
|
|
|
17197
17259
|
return result;
|
|
17198
17260
|
}
|
|
17199
17261
|
var _doltPathSeam = {
|
|
17200
|
-
homedir: () =>
|
|
17262
|
+
homedir: () => os32.homedir(),
|
|
17201
17263
|
getPath: () => process.env.PATH ?? "",
|
|
17202
17264
|
setPath: (p2) => {
|
|
17203
17265
|
process.env.PATH = p2;
|
|
17204
17266
|
},
|
|
17205
17267
|
exists: (p2) => {
|
|
17206
17268
|
try {
|
|
17207
|
-
|
|
17269
|
+
fs38.accessSync(p2, fs38.constants.F_OK);
|
|
17208
17270
|
return true;
|
|
17209
17271
|
} catch {
|
|
17210
17272
|
return false;
|
|
@@ -17367,7 +17429,7 @@ async function ensureSharedServer(adapter, options = {}) {
|
|
|
17367
17429
|
// src/beads/project-key.ts
|
|
17368
17430
|
var import_child_process18 = require("child_process");
|
|
17369
17431
|
var crypto2 = __toESM(require("crypto"));
|
|
17370
|
-
var
|
|
17432
|
+
var fs39 = __toESM(require("fs"));
|
|
17371
17433
|
var path43 = __toESM(require("path"));
|
|
17372
17434
|
function normalizeOrigin(raw) {
|
|
17373
17435
|
const trimmed = raw.trim();
|
|
@@ -17400,7 +17462,7 @@ function findRepoRoot(cwd) {
|
|
|
17400
17462
|
if (seen.has(dir)) return null;
|
|
17401
17463
|
seen.add(dir);
|
|
17402
17464
|
try {
|
|
17403
|
-
const stat3 =
|
|
17465
|
+
const stat3 = fs39.statSync(path43.join(dir, ".git"), { throwIfNoEntry: false });
|
|
17404
17466
|
if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
|
|
17405
17467
|
} catch {
|
|
17406
17468
|
}
|
|
@@ -17415,7 +17477,7 @@ var _execSeam2 = {
|
|
|
17415
17477
|
const out2 = (0, import_child_process18.execFileSync)(file, args2, opts);
|
|
17416
17478
|
return typeof out2 === "string" ? out2 : out2.toString("utf8");
|
|
17417
17479
|
},
|
|
17418
|
-
realpath: (p2) =>
|
|
17480
|
+
realpath: (p2) => fs39.realpathSync(p2)
|
|
17419
17481
|
};
|
|
17420
17482
|
function readOrigin(cwd) {
|
|
17421
17483
|
try {
|
|
@@ -17486,17 +17548,17 @@ var _provisionSeam = {
|
|
|
17486
17548
|
};
|
|
17487
17549
|
var _linkSeam = {
|
|
17488
17550
|
platform: () => process.platform,
|
|
17489
|
-
homedir: () =>
|
|
17551
|
+
homedir: () => os33.homedir(),
|
|
17490
17552
|
isWritableDir: (dir) => {
|
|
17491
17553
|
try {
|
|
17492
|
-
|
|
17554
|
+
fs40.accessSync(dir, fs40.constants.W_OK);
|
|
17493
17555
|
return true;
|
|
17494
17556
|
} catch {
|
|
17495
17557
|
return false;
|
|
17496
17558
|
}
|
|
17497
17559
|
},
|
|
17498
17560
|
ensureDir: (dir) => {
|
|
17499
|
-
|
|
17561
|
+
fs40.mkdirSync(dir, { recursive: true });
|
|
17500
17562
|
},
|
|
17501
17563
|
/**
|
|
17502
17564
|
* A directory to symlink `bd` into so the AGENT's shell + Claude Code's
|
|
@@ -17536,7 +17598,7 @@ var _linkSeam = {
|
|
|
17536
17598
|
const entry = process.argv[1];
|
|
17537
17599
|
if (entry) {
|
|
17538
17600
|
try {
|
|
17539
|
-
candidates.push(path44.dirname(
|
|
17601
|
+
candidates.push(path44.dirname(fs40.realpathSync(entry)));
|
|
17540
17602
|
} catch {
|
|
17541
17603
|
candidates.push(path44.dirname(entry));
|
|
17542
17604
|
}
|
|
@@ -17550,13 +17612,13 @@ var _linkSeam = {
|
|
|
17550
17612
|
/** Current symlink target at `linkPath`, or null when absent / not a link. */
|
|
17551
17613
|
readlink: (linkPath) => {
|
|
17552
17614
|
try {
|
|
17553
|
-
return
|
|
17615
|
+
return fs40.readlinkSync(linkPath);
|
|
17554
17616
|
} catch {
|
|
17555
17617
|
return null;
|
|
17556
17618
|
}
|
|
17557
17619
|
},
|
|
17558
|
-
unlink: (linkPath) =>
|
|
17559
|
-
symlink: (target, linkPath) =>
|
|
17620
|
+
unlink: (linkPath) => fs40.unlinkSync(linkPath),
|
|
17621
|
+
symlink: (target, linkPath) => fs40.symlinkSync(target, linkPath)
|
|
17560
17622
|
};
|
|
17561
17623
|
function linkBdOntoPath(binaryPath) {
|
|
17562
17624
|
if (_linkSeam.platform() === "win32") return;
|
|
@@ -18032,15 +18094,15 @@ async function handleBeadsActionCommand(action, started) {
|
|
|
18032
18094
|
}
|
|
18033
18095
|
|
|
18034
18096
|
// src/beads/config-store.ts
|
|
18035
|
-
var
|
|
18036
|
-
var
|
|
18097
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
18098
|
+
var import_node_os4 = __toESM(require("os"));
|
|
18037
18099
|
var import_node_path4 = __toESM(require("path"));
|
|
18038
18100
|
function beadsConfigPath() {
|
|
18039
|
-
return import_node_path4.default.join(
|
|
18101
|
+
return import_node_path4.default.join(import_node_os4.default.homedir(), ".codeam", "beads-config.json");
|
|
18040
18102
|
}
|
|
18041
18103
|
function readBeadsEnabled() {
|
|
18042
18104
|
try {
|
|
18043
|
-
const raw =
|
|
18105
|
+
const raw = import_node_fs6.default.readFileSync(beadsConfigPath(), "utf8");
|
|
18044
18106
|
const cfg = JSON.parse(raw);
|
|
18045
18107
|
return cfg.enabled !== false;
|
|
18046
18108
|
} catch {
|
|
@@ -18049,10 +18111,11 @@ function readBeadsEnabled() {
|
|
|
18049
18111
|
}
|
|
18050
18112
|
function persistBeadsConfig(cfg) {
|
|
18051
18113
|
const file = beadsConfigPath();
|
|
18052
|
-
|
|
18114
|
+
import_node_fs6.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
18053
18115
|
const tmp = `${file}.tmp`;
|
|
18054
|
-
|
|
18055
|
-
|
|
18116
|
+
import_node_fs6.default.writeFileSync(tmp, JSON.stringify(cfg), { mode: 384 });
|
|
18117
|
+
import_node_fs6.default.renameSync(tmp, file);
|
|
18118
|
+
restrictToOwner(file);
|
|
18056
18119
|
}
|
|
18057
18120
|
|
|
18058
18121
|
// src/beads/wiring.ts
|
|
@@ -18191,7 +18254,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
|
18191
18254
|
function cleanupAttachmentTempFiles() {
|
|
18192
18255
|
for (const p2 of pendingAttachmentFiles) {
|
|
18193
18256
|
try {
|
|
18194
|
-
|
|
18257
|
+
fs42.unlinkSync(p2);
|
|
18195
18258
|
} catch {
|
|
18196
18259
|
}
|
|
18197
18260
|
}
|
|
@@ -18200,8 +18263,8 @@ function cleanupAttachmentTempFiles() {
|
|
|
18200
18263
|
function saveFilesTemp(files) {
|
|
18201
18264
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
18202
18265
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
18203
|
-
const tmpPath = path47.join(
|
|
18204
|
-
|
|
18266
|
+
const tmpPath = path47.join(os35.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
|
|
18267
|
+
fs42.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
18205
18268
|
pendingAttachmentFiles.add(tmpPath);
|
|
18206
18269
|
return tmpPath;
|
|
18207
18270
|
});
|
|
@@ -18221,7 +18284,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
18221
18284
|
setTimeout(() => {
|
|
18222
18285
|
for (const p2 of paths) {
|
|
18223
18286
|
try {
|
|
18224
|
-
|
|
18287
|
+
fs42.unlinkSync(p2);
|
|
18225
18288
|
} catch {
|
|
18226
18289
|
}
|
|
18227
18290
|
pendingAttachmentFiles.delete(p2);
|
|
@@ -18424,7 +18487,7 @@ var listFiles = async (ctx, cmd, parsed) => {
|
|
|
18424
18487
|
var envReadH = async (ctx, cmd) => {
|
|
18425
18488
|
const envPath = path47.join(process.cwd(), ".env");
|
|
18426
18489
|
try {
|
|
18427
|
-
const raw = await
|
|
18490
|
+
const raw = await fs42.promises.readFile(envPath, "utf8");
|
|
18428
18491
|
await ctx.relay.sendResult(cmd.id, "completed", {
|
|
18429
18492
|
exists: true,
|
|
18430
18493
|
vars: parseDotenv(raw)
|
|
@@ -18458,11 +18521,11 @@ var envWriteH = async (ctx, cmd, parsed) => {
|
|
|
18458
18521
|
const envPath = path47.join(process.cwd(), ".env");
|
|
18459
18522
|
const tmpPath = path47.join(process.cwd(), ".env.codeam.tmp");
|
|
18460
18523
|
try {
|
|
18461
|
-
await
|
|
18462
|
-
await
|
|
18524
|
+
await fs42.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
|
|
18525
|
+
await fs42.promises.rename(tmpPath, envPath);
|
|
18463
18526
|
await ctx.relay.sendResult(cmd.id, "completed", { ok: true, count: vars.length });
|
|
18464
18527
|
} catch (err) {
|
|
18465
|
-
await
|
|
18528
|
+
await fs42.promises.rm(tmpPath, { force: true }).catch(() => void 0);
|
|
18466
18529
|
await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
|
|
18467
18530
|
}
|
|
18468
18531
|
};
|
|
@@ -18480,7 +18543,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18480
18543
|
let configuredAgent = rawAgentId;
|
|
18481
18544
|
if (!configuredAgent) {
|
|
18482
18545
|
try {
|
|
18483
|
-
const raw = JSON.parse(
|
|
18546
|
+
const raw = JSON.parse(fs42.readFileSync(headroomConfigPath(), "utf8"));
|
|
18484
18547
|
configuredAgent = raw.agent ?? "";
|
|
18485
18548
|
} catch {
|
|
18486
18549
|
}
|
|
@@ -18514,7 +18577,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18514
18577
|
persist: persistHeadroomConfig,
|
|
18515
18578
|
readEnabled: () => {
|
|
18516
18579
|
try {
|
|
18517
|
-
const raw = JSON.parse(
|
|
18580
|
+
const raw = JSON.parse(fs42.readFileSync(headroomConfigPath(), "utf8"));
|
|
18518
18581
|
return raw.enabled === true;
|
|
18519
18582
|
} catch {
|
|
18520
18583
|
return false;
|
|
@@ -18527,7 +18590,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18527
18590
|
const res = await fetch("http://localhost:8787/stats");
|
|
18528
18591
|
return res.json();
|
|
18529
18592
|
},
|
|
18530
|
-
postSavings: async (delta) => {
|
|
18593
|
+
postSavings: async (delta, budget) => {
|
|
18531
18594
|
if (!opts.ingestUrl) return;
|
|
18532
18595
|
await fetch(opts.ingestUrl, {
|
|
18533
18596
|
method: "POST",
|
|
@@ -18546,7 +18609,12 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18546
18609
|
sessionId: ctx.sessionId,
|
|
18547
18610
|
pluginId: ctx.pluginId,
|
|
18548
18611
|
agentId: opts.agent,
|
|
18549
|
-
savings: delta
|
|
18612
|
+
savings: delta,
|
|
18613
|
+
...budget ? {
|
|
18614
|
+
periodSpendUsd: budget.periodSpendUsd,
|
|
18615
|
+
budgetUsd: budget.budgetUsd,
|
|
18616
|
+
budgetPeriod: budget.budgetPeriod
|
|
18617
|
+
} : {}
|
|
18550
18618
|
})
|
|
18551
18619
|
});
|
|
18552
18620
|
}
|
|
@@ -18587,6 +18655,89 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18587
18655
|
});
|
|
18588
18656
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
18589
18657
|
};
|
|
18658
|
+
var headroomBudgetH = async (ctx, cmd) => {
|
|
18659
|
+
const payload = cmd.payload;
|
|
18660
|
+
let rawAgentId = ctx.agentId || (typeof payload.agentId === "string" ? payload.agentId : "");
|
|
18661
|
+
if (rawAgentId === "claude_code") rawAgentId = "claude";
|
|
18662
|
+
let payloadAgentId = typeof payload.agentId === "string" ? payload.agentId : "";
|
|
18663
|
+
if (payloadAgentId === "claude_code") payloadAgentId = "claude";
|
|
18664
|
+
if (!rawAgentId || !isHeadroomSupportedAgent(rawAgentId)) {
|
|
18665
|
+
await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
|
|
18666
|
+
return;
|
|
18667
|
+
}
|
|
18668
|
+
if (!payloadAgentId || payloadAgentId !== rawAgentId) {
|
|
18669
|
+
await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
|
|
18670
|
+
return;
|
|
18671
|
+
}
|
|
18672
|
+
let headroomActive = false;
|
|
18673
|
+
try {
|
|
18674
|
+
const raw = JSON.parse(fs42.readFileSync(headroomConfigPath(), "utf8"));
|
|
18675
|
+
headroomActive = raw.enabled === true;
|
|
18676
|
+
} catch {
|
|
18677
|
+
}
|
|
18678
|
+
if (!headroomActive) {
|
|
18679
|
+
await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
|
|
18680
|
+
return;
|
|
18681
|
+
}
|
|
18682
|
+
let existingConfig = { enabled: true };
|
|
18683
|
+
try {
|
|
18684
|
+
existingConfig = JSON.parse(fs42.readFileSync(headroomConfigPath(), "utf8"));
|
|
18685
|
+
} catch {
|
|
18686
|
+
}
|
|
18687
|
+
if (payload.budgetEnabled && payload.budgetUsd != null) {
|
|
18688
|
+
persistHeadroomConfig({
|
|
18689
|
+
...existingConfig,
|
|
18690
|
+
enabled: existingConfig.enabled ?? true,
|
|
18691
|
+
budgetEnabled: true,
|
|
18692
|
+
budgetUsd: payload.budgetUsd,
|
|
18693
|
+
budgetPeriod: payload.budgetPeriod ?? "daily"
|
|
18694
|
+
});
|
|
18695
|
+
process.env["HEADROOM_BUDGET"] = String(payload.budgetUsd);
|
|
18696
|
+
process.env["HEADROOM_BUDGET_PERIOD"] = payload.budgetPeriod ?? "daily";
|
|
18697
|
+
} else {
|
|
18698
|
+
persistHeadroomConfig({
|
|
18699
|
+
...existingConfig,
|
|
18700
|
+
enabled: existingConfig.enabled ?? true,
|
|
18701
|
+
budgetEnabled: false,
|
|
18702
|
+
budgetUsd: void 0,
|
|
18703
|
+
budgetPeriod: void 0
|
|
18704
|
+
});
|
|
18705
|
+
delete process.env["HEADROOM_BUDGET"];
|
|
18706
|
+
delete process.env["HEADROOM_BUDGET_PERIOD"];
|
|
18707
|
+
}
|
|
18708
|
+
try {
|
|
18709
|
+
const killer = (0, import_child_process20.spawn)("pkill", ["-TERM", "-f", "headroom.*proxy"], {
|
|
18710
|
+
detached: true,
|
|
18711
|
+
stdio: "ignore"
|
|
18712
|
+
});
|
|
18713
|
+
killer.once("error", () => {
|
|
18714
|
+
});
|
|
18715
|
+
killer.unref();
|
|
18716
|
+
} catch {
|
|
18717
|
+
}
|
|
18718
|
+
try {
|
|
18719
|
+
const proxyEnv = { ...process.env, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
|
|
18720
|
+
const proxy = (0, import_child_process20.spawn)(
|
|
18721
|
+
"headroom",
|
|
18722
|
+
["proxy", "--port", "8787", ...buildBudgetProxyArgs(proxyEnv)],
|
|
18723
|
+
{
|
|
18724
|
+
stdio: "ignore",
|
|
18725
|
+
detached: true,
|
|
18726
|
+
env: proxyEnv
|
|
18727
|
+
}
|
|
18728
|
+
);
|
|
18729
|
+
proxy.once("error", (e) => {
|
|
18730
|
+
log.warn("headroom-budget", `proxy relaunch error (best-effort): ${e.message}`);
|
|
18731
|
+
});
|
|
18732
|
+
proxy.unref();
|
|
18733
|
+
} catch (e) {
|
|
18734
|
+
log.warn(
|
|
18735
|
+
"headroom-budget",
|
|
18736
|
+
`proxy relaunch failed (best-effort): ${e instanceof Error ? e.message : String(e)}`
|
|
18737
|
+
);
|
|
18738
|
+
}
|
|
18739
|
+
await ctx.relay.sendResult(cmd.id, "completed", { applied: true });
|
|
18740
|
+
};
|
|
18590
18741
|
var _beadsEmitChain = Promise.resolve();
|
|
18591
18742
|
var beadsConfigureH = async (ctx, cmd, parsed) => {
|
|
18592
18743
|
const action = parsed.action;
|
|
@@ -19083,7 +19234,7 @@ function normalizeDetectionForSpawn(detection, cwd) {
|
|
|
19083
19234
|
const binName = args2[0];
|
|
19084
19235
|
if (binName.startsWith("-")) return detection;
|
|
19085
19236
|
const binPath = path47.join(cwd, "node_modules", ".bin", binName);
|
|
19086
|
-
if (!
|
|
19237
|
+
if (!fs42.existsSync(binPath)) return detection;
|
|
19087
19238
|
return {
|
|
19088
19239
|
...detection,
|
|
19089
19240
|
command: binPath,
|
|
@@ -19632,6 +19783,7 @@ var handlers = {
|
|
|
19632
19783
|
env_read: envReadH,
|
|
19633
19784
|
env_write: envWriteH,
|
|
19634
19785
|
headroom_configure: headroomConfigureH,
|
|
19786
|
+
headroom_budget: headroomBudgetH,
|
|
19635
19787
|
beads_configure: beadsConfigureH
|
|
19636
19788
|
};
|
|
19637
19789
|
async function dispatchCommand(ctx, cmd) {
|
|
@@ -19902,10 +20054,10 @@ function readTokenFromArgs(args2) {
|
|
|
19902
20054
|
if (fileFlag) {
|
|
19903
20055
|
const path61 = fileFlag.slice("--token-file=".length);
|
|
19904
20056
|
try {
|
|
19905
|
-
const content =
|
|
20057
|
+
const content = fs43.readFileSync(path61, "utf8").trim();
|
|
19906
20058
|
if (content.length === 0) fail(`--token-file ${path61} is empty`);
|
|
19907
20059
|
try {
|
|
19908
|
-
|
|
20060
|
+
fs43.unlinkSync(path61);
|
|
19909
20061
|
} catch {
|
|
19910
20062
|
}
|
|
19911
20063
|
return content;
|
|
@@ -19931,7 +20083,7 @@ async function claimOnce(token, pluginId, pluginSecretHash) {
|
|
|
19931
20083
|
pluginId,
|
|
19932
20084
|
ideName: "codeam-cli (codespace)",
|
|
19933
20085
|
ideVersion: process.env.npm_package_version ?? "unknown",
|
|
19934
|
-
hostname:
|
|
20086
|
+
hostname: os36.hostname(),
|
|
19935
20087
|
codespaceName: process.env.CODESPACE_NAME ?? "",
|
|
19936
20088
|
// Current git branch of the codespace's working directory, so the
|
|
19937
20089
|
// backend can populate `PairedSession.branch` for the codespace pair.
|
|
@@ -19992,7 +20144,7 @@ async function claim(token, pluginId, pluginSecretHash) {
|
|
|
19992
20144
|
}
|
|
19993
20145
|
}
|
|
19994
20146
|
function pairAutoLockPath() {
|
|
19995
|
-
return path48.join(
|
|
20147
|
+
return path48.join(os36.homedir(), ".codeam", "pair-auto.lock");
|
|
19996
20148
|
}
|
|
19997
20149
|
function isLivePairAuto(pid) {
|
|
19998
20150
|
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
@@ -20002,7 +20154,7 @@ function isLivePairAuto(pid) {
|
|
|
20002
20154
|
if (e.code !== "EPERM") return false;
|
|
20003
20155
|
}
|
|
20004
20156
|
try {
|
|
20005
|
-
return
|
|
20157
|
+
return fs43.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
|
|
20006
20158
|
} catch {
|
|
20007
20159
|
return true;
|
|
20008
20160
|
}
|
|
@@ -20012,24 +20164,24 @@ function isLiveCodeam(pid) {
|
|
|
20012
20164
|
}
|
|
20013
20165
|
function daemonLockPath(sessionId) {
|
|
20014
20166
|
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
20015
|
-
return path48.join(
|
|
20167
|
+
return path48.join(os36.homedir(), ".codeam", `daemon-${safe}.lock`);
|
|
20016
20168
|
}
|
|
20017
20169
|
function acquireDaemonLock(sessionId) {
|
|
20018
20170
|
const lockPath = daemonLockPath(sessionId);
|
|
20019
20171
|
try {
|
|
20020
|
-
|
|
20172
|
+
fs43.mkdirSync(path48.dirname(lockPath), { recursive: true });
|
|
20021
20173
|
try {
|
|
20022
|
-
|
|
20174
|
+
fs43.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
20023
20175
|
} catch (e) {
|
|
20024
20176
|
if (e.code !== "EEXIST") throw e;
|
|
20025
|
-
const holder = Number(
|
|
20177
|
+
const holder = Number(fs43.readFileSync(lockPath, "utf8").trim());
|
|
20026
20178
|
if (holder && holder !== process.pid && isLiveCodeam(holder)) return false;
|
|
20027
|
-
|
|
20179
|
+
fs43.writeFileSync(lockPath, String(process.pid));
|
|
20028
20180
|
}
|
|
20029
20181
|
const release3 = () => {
|
|
20030
20182
|
try {
|
|
20031
|
-
if (
|
|
20032
|
-
|
|
20183
|
+
if (fs43.existsSync(lockPath) && Number(fs43.readFileSync(lockPath, "utf8").trim()) === process.pid) {
|
|
20184
|
+
fs43.unlinkSync(lockPath);
|
|
20033
20185
|
}
|
|
20034
20186
|
} catch {
|
|
20035
20187
|
}
|
|
@@ -20051,19 +20203,19 @@ function acquireDaemonLock(sessionId) {
|
|
|
20051
20203
|
function acquireSingletonLock() {
|
|
20052
20204
|
const lockPath = pairAutoLockPath();
|
|
20053
20205
|
try {
|
|
20054
|
-
|
|
20206
|
+
fs43.mkdirSync(path48.dirname(lockPath), { recursive: true });
|
|
20055
20207
|
try {
|
|
20056
|
-
|
|
20208
|
+
fs43.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
20057
20209
|
} catch (e) {
|
|
20058
20210
|
if (e.code !== "EEXIST") throw e;
|
|
20059
|
-
const holder = Number(
|
|
20211
|
+
const holder = Number(fs43.readFileSync(lockPath, "utf8").trim());
|
|
20060
20212
|
if (isLivePairAuto(holder)) return false;
|
|
20061
|
-
|
|
20213
|
+
fs43.writeFileSync(lockPath, String(process.pid));
|
|
20062
20214
|
}
|
|
20063
20215
|
process.once("exit", () => {
|
|
20064
20216
|
try {
|
|
20065
|
-
if (
|
|
20066
|
-
|
|
20217
|
+
if (fs43.existsSync(lockPath) && Number(fs43.readFileSync(lockPath, "utf8").trim()) === process.pid) {
|
|
20218
|
+
fs43.unlinkSync(lockPath);
|
|
20067
20219
|
}
|
|
20068
20220
|
} catch {
|
|
20069
20221
|
}
|
|
@@ -20145,7 +20297,7 @@ async function pairAuto(args2) {
|
|
|
20145
20297
|
}
|
|
20146
20298
|
|
|
20147
20299
|
// src/services/headroom/wrap-launch.ts
|
|
20148
|
-
var
|
|
20300
|
+
var import_node_child_process16 = require("child_process");
|
|
20149
20301
|
function wrapWithHeadroom(launch, opts) {
|
|
20150
20302
|
if (!opts.enabled || !opts.headroomPresent) return launch;
|
|
20151
20303
|
return {
|
|
@@ -20158,7 +20310,7 @@ var _present;
|
|
|
20158
20310
|
function headroomPresent() {
|
|
20159
20311
|
if (_present !== void 0) return Promise.resolve(_present);
|
|
20160
20312
|
return new Promise((resolve7) => {
|
|
20161
|
-
(0,
|
|
20313
|
+
(0, import_node_child_process16.execFile)("headroom", ["--version"], (err) => {
|
|
20162
20314
|
_present = !err;
|
|
20163
20315
|
resolve7(_present);
|
|
20164
20316
|
});
|
|
@@ -20590,9 +20742,9 @@ function requiresAcp(agent) {
|
|
|
20590
20742
|
var import_node_crypto7 = require("crypto");
|
|
20591
20743
|
|
|
20592
20744
|
// src/services/history.service.ts
|
|
20593
|
-
var
|
|
20745
|
+
var fs44 = __toESM(require("fs"));
|
|
20594
20746
|
var path50 = __toESM(require("path"));
|
|
20595
|
-
var
|
|
20747
|
+
var os37 = __toESM(require("os"));
|
|
20596
20748
|
var https7 = __toESM(require("https"));
|
|
20597
20749
|
var http6 = __toESM(require("http"));
|
|
20598
20750
|
var import_zod2 = require("zod");
|
|
@@ -20619,7 +20771,7 @@ function parseJsonl(filePath) {
|
|
|
20619
20771
|
const messages = [];
|
|
20620
20772
|
let raw;
|
|
20621
20773
|
try {
|
|
20622
|
-
raw =
|
|
20774
|
+
raw = fs44.readFileSync(filePath, "utf8");
|
|
20623
20775
|
} catch (err) {
|
|
20624
20776
|
if (err.code !== "ENOENT") {
|
|
20625
20777
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -20760,7 +20912,7 @@ var HistoryService = class _HistoryService {
|
|
|
20760
20912
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
20761
20913
|
}
|
|
20762
20914
|
get projectDir() {
|
|
20763
|
-
return this.runtime.resolveHistoryDir(this.cwd) ?? path50.join(
|
|
20915
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path50.join(os37.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
20764
20916
|
}
|
|
20765
20917
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
20766
20918
|
setCurrentConversationId(id) {
|
|
@@ -20809,9 +20961,9 @@ var HistoryService = class _HistoryService {
|
|
|
20809
20961
|
const dir = this.projectDir;
|
|
20810
20962
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
20811
20963
|
try {
|
|
20812
|
-
const files =
|
|
20964
|
+
const files = fs44.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
20813
20965
|
try {
|
|
20814
|
-
const stat3 =
|
|
20966
|
+
const stat3 = fs44.statSync(path50.join(dir, e.name));
|
|
20815
20967
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
20816
20968
|
} catch {
|
|
20817
20969
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -20852,13 +21004,13 @@ var HistoryService = class _HistoryService {
|
|
|
20852
21004
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
20853
21005
|
let entries;
|
|
20854
21006
|
try {
|
|
20855
|
-
entries =
|
|
21007
|
+
entries = fs44.readdirSync(dir, { withFileTypes: true });
|
|
20856
21008
|
} catch {
|
|
20857
21009
|
return null;
|
|
20858
21010
|
}
|
|
20859
21011
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
20860
21012
|
try {
|
|
20861
|
-
const stat3 =
|
|
21013
|
+
const stat3 = fs44.statSync(path50.join(dir, e.name));
|
|
20862
21014
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
20863
21015
|
} catch {
|
|
20864
21016
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -20872,7 +21024,7 @@ var HistoryService = class _HistoryService {
|
|
|
20872
21024
|
extractUsageFromFile(filePath) {
|
|
20873
21025
|
let raw;
|
|
20874
21026
|
try {
|
|
20875
|
-
raw =
|
|
21027
|
+
raw = fs44.readFileSync(filePath, "utf8");
|
|
20876
21028
|
} catch {
|
|
20877
21029
|
return null;
|
|
20878
21030
|
}
|
|
@@ -20917,9 +21069,9 @@ var HistoryService = class _HistoryService {
|
|
|
20917
21069
|
let totalCost = 0;
|
|
20918
21070
|
let files;
|
|
20919
21071
|
try {
|
|
20920
|
-
files =
|
|
21072
|
+
files = fs44.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
20921
21073
|
try {
|
|
20922
|
-
return
|
|
21074
|
+
return fs44.statSync(path50.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
20923
21075
|
} catch {
|
|
20924
21076
|
return false;
|
|
20925
21077
|
}
|
|
@@ -20930,7 +21082,7 @@ var HistoryService = class _HistoryService {
|
|
|
20930
21082
|
for (const file of files) {
|
|
20931
21083
|
let raw;
|
|
20932
21084
|
try {
|
|
20933
|
-
raw =
|
|
21085
|
+
raw = fs44.readFileSync(path50.join(projectDir, file), "utf8");
|
|
20934
21086
|
} catch {
|
|
20935
21087
|
continue;
|
|
20936
21088
|
}
|
|
@@ -21099,7 +21251,7 @@ var HistoryService = class _HistoryService {
|
|
|
21099
21251
|
if (!filePath) return false;
|
|
21100
21252
|
let mtimeMs;
|
|
21101
21253
|
try {
|
|
21102
|
-
mtimeMs =
|
|
21254
|
+
mtimeMs = fs44.statSync(filePath).mtimeMs;
|
|
21103
21255
|
} catch {
|
|
21104
21256
|
return false;
|
|
21105
21257
|
}
|
|
@@ -21167,10 +21319,10 @@ var HistoryService = class _HistoryService {
|
|
|
21167
21319
|
};
|
|
21168
21320
|
|
|
21169
21321
|
// src/agents/acp/client.ts
|
|
21170
|
-
var
|
|
21171
|
-
var
|
|
21322
|
+
var import_node_child_process17 = require("child_process");
|
|
21323
|
+
var fs45 = __toESM(require("fs/promises"));
|
|
21172
21324
|
var fsSync = __toESM(require("fs"));
|
|
21173
|
-
var
|
|
21325
|
+
var os38 = __toESM(require("os"));
|
|
21174
21326
|
var path51 = __toESM(require("path"));
|
|
21175
21327
|
var import_node_stream = require("stream");
|
|
21176
21328
|
|
|
@@ -23718,7 +23870,7 @@ var AcpClient = class {
|
|
|
23718
23870
|
"acpClient",
|
|
23719
23871
|
`spawn cmd=${adapter.command} args=[${adapter.args.join(",")}] cwd=${cwd}`
|
|
23720
23872
|
);
|
|
23721
|
-
const child = (0,
|
|
23873
|
+
const child = (0, import_node_child_process17.spawn)(adapter.command, adapter.args, {
|
|
23722
23874
|
cwd,
|
|
23723
23875
|
// extraEnv (e.g. CLAUDE_CODE_DISABLE_1M_CONTEXT=1 on an on-demand
|
|
23724
23876
|
// re-spawn) layers over process.env; PATH stays last so the augmented
|
|
@@ -24004,7 +24156,7 @@ var AcpClient = class {
|
|
|
24004
24156
|
},
|
|
24005
24157
|
readTextFile: async (params) => {
|
|
24006
24158
|
try {
|
|
24007
|
-
const content = await
|
|
24159
|
+
const content = await fs45.readFile(params.path, "utf8");
|
|
24008
24160
|
return applyLineRange(content, params.line ?? null, params.limit ?? null);
|
|
24009
24161
|
} catch (err) {
|
|
24010
24162
|
const code = err.code;
|
|
@@ -24024,7 +24176,7 @@ var AcpClient = class {
|
|
|
24024
24176
|
},
|
|
24025
24177
|
writeTextFile: async (params) => {
|
|
24026
24178
|
try {
|
|
24027
|
-
await
|
|
24179
|
+
await fs45.writeFile(params.path, params.content, "utf8");
|
|
24028
24180
|
return {};
|
|
24029
24181
|
} catch (err) {
|
|
24030
24182
|
const code = err.code;
|
|
@@ -24073,7 +24225,7 @@ function applyLineRange(content, line, limit) {
|
|
|
24073
24225
|
return { content: lines.slice(start2, end).join("\n") };
|
|
24074
24226
|
}
|
|
24075
24227
|
function knownAgentBinaryDirs() {
|
|
24076
|
-
const home =
|
|
24228
|
+
const home = os38.homedir();
|
|
24077
24229
|
const out2 = [];
|
|
24078
24230
|
out2.push("/tmp/codeam-node20/bin");
|
|
24079
24231
|
for (const root of [
|
|
@@ -24504,6 +24656,79 @@ function createOneMRecovery(deps) {
|
|
|
24504
24656
|
};
|
|
24505
24657
|
}
|
|
24506
24658
|
|
|
24659
|
+
// src/agents/acp/budgetRecovery.ts
|
|
24660
|
+
var BUDGET_EXCEEDED_RE = /budget exceeded for (\w+) period/i;
|
|
24661
|
+
function looksLikeBudgetExceeded(text) {
|
|
24662
|
+
return BUDGET_EXCEEDED_RE.test(text);
|
|
24663
|
+
}
|
|
24664
|
+
function extractBudgetPeriod(text) {
|
|
24665
|
+
const m = BUDGET_EXCEEDED_RE.exec(text);
|
|
24666
|
+
return m ? m[1] ?? "current" : "current";
|
|
24667
|
+
}
|
|
24668
|
+
var BUDGET_PAUSE_OPTION = "Pause budget this session";
|
|
24669
|
+
var BUDGET_RAISE_OPTION = "Raise budget";
|
|
24670
|
+
function createBudgetRecovery(deps) {
|
|
24671
|
+
let pending = null;
|
|
24672
|
+
const buildQuestion = (period) => `\u{1F4B8} **Headroom budget reached for the ${period} period.**
|
|
24673
|
+
|
|
24674
|
+
You have two options:
|
|
24675
|
+
\u2022 **Pause budget this session** \u2014 removes the spending cap for this session only (the proxy restarts without a budget limit).
|
|
24676
|
+
\u2022 **Raise budget** \u2014 open your agent budget settings to increase the cap.`;
|
|
24677
|
+
return {
|
|
24678
|
+
offer: async (commandId, blocks, detail) => {
|
|
24679
|
+
const period = extractBudgetPeriod(detail);
|
|
24680
|
+
pending = { blocks, period };
|
|
24681
|
+
const question = buildQuestion(period);
|
|
24682
|
+
const options = [BUDGET_PAUSE_OPTION, BUDGET_RAISE_OPTION];
|
|
24683
|
+
await deps.publishText(question);
|
|
24684
|
+
await deps.publishSelectPrompt(question, options);
|
|
24685
|
+
await deps.publishAwaitingAnswer(question, options);
|
|
24686
|
+
deps.appendAgentReply(question);
|
|
24687
|
+
deps.flushHistory();
|
|
24688
|
+
deps.log?.(`budget-exceeded recovery offered period=${period} id=${commandId.slice(0, 8)}`);
|
|
24689
|
+
await deps.sendResult(commandId, "failed", {
|
|
24690
|
+
error: `Headroom budget exceeded (${period}) \u2014 recovery offered`
|
|
24691
|
+
});
|
|
24692
|
+
},
|
|
24693
|
+
tryRecover: async (commandId, optionIndex) => {
|
|
24694
|
+
if (!pending) return false;
|
|
24695
|
+
const { blocks, period } = pending;
|
|
24696
|
+
pending = null;
|
|
24697
|
+
if (optionIndex === 0) {
|
|
24698
|
+
deps.log?.("budget recovery \u2192 pause: relaunching proxy without budget");
|
|
24699
|
+
try {
|
|
24700
|
+
await deps.relaunchProxyWithoutBudget();
|
|
24701
|
+
} catch (err) {
|
|
24702
|
+
deps.log?.(`budget recovery \u2192 relaunch failed (best-effort): ${String(err)}`);
|
|
24703
|
+
}
|
|
24704
|
+
await deps.sendResult(commandId, "completed", {
|
|
24705
|
+
action: "pause",
|
|
24706
|
+
period,
|
|
24707
|
+
note: "Headroom proxy relaunched without budget limit for this session. Send your message again."
|
|
24708
|
+
});
|
|
24709
|
+
const note = "\u2705 **Budget paused for this session.** The proxy is running without a spending cap \u2014 send your message again.";
|
|
24710
|
+
await deps.publishText(note);
|
|
24711
|
+
deps.appendAgentReply(note);
|
|
24712
|
+
deps.flushHistory();
|
|
24713
|
+
deps.log?.(`budget recovery \u2192 pause complete id=${commandId.slice(0, 8)}`);
|
|
24714
|
+
} else {
|
|
24715
|
+
deps.log?.(`budget recovery \u2192 raise: emitting deep-link for agent=${deps.agentId}`);
|
|
24716
|
+
await deps.publishRawChunk({
|
|
24717
|
+
type: "open_agent_budget_settings",
|
|
24718
|
+
agentId: deps.agentId,
|
|
24719
|
+
done: true
|
|
24720
|
+
});
|
|
24721
|
+
await deps.sendResult(commandId, "completed", {
|
|
24722
|
+
action: "raise",
|
|
24723
|
+
agentId: deps.agentId
|
|
24724
|
+
});
|
|
24725
|
+
deps.log?.(`budget recovery \u2192 raise emitted id=${commandId.slice(0, 8)}`);
|
|
24726
|
+
}
|
|
24727
|
+
return true;
|
|
24728
|
+
}
|
|
24729
|
+
};
|
|
24730
|
+
}
|
|
24731
|
+
|
|
24507
24732
|
// src/agents/acp/reconcileDelta.ts
|
|
24508
24733
|
function reconcileCumulative(existing, incoming) {
|
|
24509
24734
|
if (incoming.length === 0) return existing;
|
|
@@ -24524,15 +24749,15 @@ function commonPrefixLength(a, b) {
|
|
|
24524
24749
|
|
|
24525
24750
|
// src/agents/acp/onboarding.ts
|
|
24526
24751
|
var import_child_process22 = require("child_process");
|
|
24527
|
-
var
|
|
24528
|
-
var
|
|
24752
|
+
var fs46 = __toESM(require("fs"));
|
|
24753
|
+
var os39 = __toESM(require("os"));
|
|
24529
24754
|
var path52 = __toESM(require("path"));
|
|
24530
24755
|
var _onboardingSeam = {
|
|
24531
|
-
markerPath: (sessionId) => path52.join(
|
|
24532
|
-
exists: (p2) =>
|
|
24756
|
+
markerPath: (sessionId) => path52.join(os39.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
|
|
24757
|
+
exists: (p2) => fs46.existsSync(p2),
|
|
24533
24758
|
write: (p2) => {
|
|
24534
|
-
|
|
24535
|
-
|
|
24759
|
+
fs46.mkdirSync(path52.dirname(p2), { recursive: true });
|
|
24760
|
+
fs46.writeFileSync(p2, "");
|
|
24536
24761
|
},
|
|
24537
24762
|
disabled: () => {
|
|
24538
24763
|
const v = process.env.CODEAM_ONBOARDING_DISABLED;
|
|
@@ -24862,7 +25087,7 @@ var import_crypto5 = require("crypto");
|
|
|
24862
25087
|
|
|
24863
25088
|
// src/services/turn-files/git-changeset.ts
|
|
24864
25089
|
var import_child_process23 = require("child_process");
|
|
24865
|
-
var
|
|
25090
|
+
var fs47 = __toESM(require("fs/promises"));
|
|
24866
25091
|
var path53 = __toESM(require("path"));
|
|
24867
25092
|
async function collectRepoChangeset(opts) {
|
|
24868
25093
|
const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
|
|
@@ -24912,7 +25137,7 @@ function readUntrackedLineCount(absPath) {
|
|
|
24912
25137
|
}
|
|
24913
25138
|
async function defaultReadUntrackedLineCount(absPath) {
|
|
24914
25139
|
try {
|
|
24915
|
-
const content = await
|
|
25140
|
+
const content = await fs47.readFile(absPath, "utf8");
|
|
24916
25141
|
let count = 0;
|
|
24917
25142
|
let pos = -1;
|
|
24918
25143
|
while ((pos = content.indexOf("\n", pos + 1)) !== -1) {
|
|
@@ -25004,7 +25229,7 @@ function defaultRunGit(cwd, args2) {
|
|
|
25004
25229
|
});
|
|
25005
25230
|
}
|
|
25006
25231
|
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
25007
|
-
const
|
|
25232
|
+
const fs51 = await import("fs/promises");
|
|
25008
25233
|
const out2 = [];
|
|
25009
25234
|
await walk(workingDir, 0);
|
|
25010
25235
|
return out2;
|
|
@@ -25012,7 +25237,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
25012
25237
|
if (depth > maxDepth) return;
|
|
25013
25238
|
let entries = [];
|
|
25014
25239
|
try {
|
|
25015
|
-
const dirents = await
|
|
25240
|
+
const dirents = await fs51.readdir(dir, { withFileTypes: true });
|
|
25016
25241
|
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
25017
25242
|
} catch {
|
|
25018
25243
|
return;
|
|
@@ -25038,7 +25263,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
25038
25263
|
}
|
|
25039
25264
|
|
|
25040
25265
|
// src/services/turn-files/files-outbox.ts
|
|
25041
|
-
var
|
|
25266
|
+
var fs48 = __toESM(require("fs/promises"));
|
|
25042
25267
|
var path54 = __toESM(require("path"));
|
|
25043
25268
|
var import_os7 = require("os");
|
|
25044
25269
|
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
@@ -25080,8 +25305,8 @@ var FilesOutbox = class {
|
|
|
25080
25305
|
/** Persist the entry to disk and trigger a flush. Returns once the
|
|
25081
25306
|
* line is durable on disk (not once the POST succeeds). */
|
|
25082
25307
|
async enqueue(entry) {
|
|
25083
|
-
await
|
|
25084
|
-
await
|
|
25308
|
+
await fs48.mkdir(path54.dirname(this.filePath), { recursive: true });
|
|
25309
|
+
await fs48.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
25085
25310
|
this.backoffIndex = 0;
|
|
25086
25311
|
if (this.autoSchedule) this.scheduleFlush(0);
|
|
25087
25312
|
}
|
|
@@ -25170,7 +25395,7 @@ var FilesOutbox = class {
|
|
|
25170
25395
|
async readAll() {
|
|
25171
25396
|
let raw = "";
|
|
25172
25397
|
try {
|
|
25173
|
-
raw = await
|
|
25398
|
+
raw = await fs48.readFile(this.filePath, "utf8");
|
|
25174
25399
|
} catch {
|
|
25175
25400
|
return [];
|
|
25176
25401
|
}
|
|
@@ -25194,12 +25419,12 @@ var FilesOutbox = class {
|
|
|
25194
25419
|
async rewrite(entries) {
|
|
25195
25420
|
const tmpPath = `${this.filePath}.${process.pid}.tmp`;
|
|
25196
25421
|
if (entries.length === 0) {
|
|
25197
|
-
await
|
|
25422
|
+
await fs48.unlink(this.filePath).catch(() => void 0);
|
|
25198
25423
|
return;
|
|
25199
25424
|
}
|
|
25200
25425
|
const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
25201
|
-
await
|
|
25202
|
-
await
|
|
25426
|
+
await fs48.writeFile(tmpPath, payload, "utf8");
|
|
25427
|
+
await fs48.rename(tmpPath, this.filePath);
|
|
25203
25428
|
}
|
|
25204
25429
|
};
|
|
25205
25430
|
function applyJitter(ms) {
|
|
@@ -25938,10 +26163,26 @@ async function surfaceStartupFailure(opts) {
|
|
|
25938
26163
|
);
|
|
25939
26164
|
errRelay.start();
|
|
25940
26165
|
}
|
|
26166
|
+
function budgetBubbleMessage(agent, period) {
|
|
26167
|
+
return `\u{1F4B8} **Headroom budget reached for the ${period} period.**
|
|
26168
|
+
|
|
26169
|
+
The local Headroom proxy rejected the ${agent} request because the configured spending cap was hit. Choose an option below to continue.`;
|
|
26170
|
+
}
|
|
26171
|
+
function buildRelaunchProxyEnv(baseEnv) {
|
|
26172
|
+
const env = { ...baseEnv, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
|
|
26173
|
+
delete env["HEADROOM_BUDGET"];
|
|
26174
|
+
delete env["HEADROOM_BUDGET_PERIOD"];
|
|
26175
|
+
return env;
|
|
26176
|
+
}
|
|
25941
26177
|
function failureBubble(opts) {
|
|
25942
26178
|
if (looksLikeAuthFailure(opts.detail) || looksLikeAuthFailure(opts.recentStderr)) {
|
|
25943
26179
|
return AUTH_FAILURE_MESSAGE;
|
|
25944
26180
|
}
|
|
26181
|
+
const budgetHaystack = `${opts.detail}
|
|
26182
|
+
${opts.recentStderr}`;
|
|
26183
|
+
if (looksLikeBudgetExceeded(budgetHaystack)) {
|
|
26184
|
+
return budgetBubbleMessage(opts.agent, extractBudgetPeriod(budgetHaystack));
|
|
26185
|
+
}
|
|
25945
26186
|
if (looksLikeProviderOutage(opts.detail) || looksLikeProviderOutage(opts.recentStderr)) {
|
|
25946
26187
|
return providerOutageMessage(opts.agent);
|
|
25947
26188
|
}
|
|
@@ -25974,6 +26215,25 @@ async function reportCredentialInvalid(opts, fetchImpl = fetch) {
|
|
|
25974
26215
|
} catch {
|
|
25975
26216
|
}
|
|
25976
26217
|
}
|
|
26218
|
+
async function postBudgetReached(opts, fetchImpl = fetch) {
|
|
26219
|
+
const url = `${resolveApiBaseUrl()}/api/sessions/${encodeURIComponent(opts.sessionId)}/headroom-budget-reached`;
|
|
26220
|
+
try {
|
|
26221
|
+
await fetchImpl(url, {
|
|
26222
|
+
method: "POST",
|
|
26223
|
+
headers: {
|
|
26224
|
+
"Content-Type": "application/json",
|
|
26225
|
+
"X-Plugin-Auth-Token": opts.pluginAuthToken
|
|
26226
|
+
},
|
|
26227
|
+
body: JSON.stringify({
|
|
26228
|
+
sessionId: opts.sessionId,
|
|
26229
|
+
pluginId: opts.pluginId,
|
|
26230
|
+
agent: opts.agent,
|
|
26231
|
+
period: opts.period
|
|
26232
|
+
})
|
|
26233
|
+
});
|
|
26234
|
+
} catch {
|
|
26235
|
+
}
|
|
26236
|
+
}
|
|
25977
26237
|
async function handleGetConversation(args2) {
|
|
25978
26238
|
const { relay, commandId, jsonlHistory, acpSessionId } = args2;
|
|
25979
26239
|
try {
|
|
@@ -26139,6 +26399,58 @@ async function runAcpSession(opts) {
|
|
|
26139
26399
|
describeError,
|
|
26140
26400
|
log: (msg) => log.info("acpRunner", msg)
|
|
26141
26401
|
});
|
|
26402
|
+
let _budgetReachedPosted = false;
|
|
26403
|
+
const relaunchProxyWithoutBudget = async () => {
|
|
26404
|
+
const { spawn: spawn32 } = await import("child_process");
|
|
26405
|
+
try {
|
|
26406
|
+
const killer = spawn32("pkill", ["-TERM", "-f", "headroom.*proxy"], {
|
|
26407
|
+
detached: true,
|
|
26408
|
+
stdio: "ignore"
|
|
26409
|
+
});
|
|
26410
|
+
killer.once("error", () => {
|
|
26411
|
+
});
|
|
26412
|
+
killer.unref();
|
|
26413
|
+
} catch {
|
|
26414
|
+
}
|
|
26415
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
26416
|
+
const proxyEnv = buildRelaunchProxyEnv(process.env);
|
|
26417
|
+
try {
|
|
26418
|
+
const proxy = spawn32(
|
|
26419
|
+
"headroom",
|
|
26420
|
+
["proxy", "--port", "8787"],
|
|
26421
|
+
{ stdio: "ignore", detached: true, env: proxyEnv }
|
|
26422
|
+
);
|
|
26423
|
+
proxy.once("error", (e) => {
|
|
26424
|
+
log.warn("acpRunner", `budget recovery proxy relaunch error (best-effort): ${e.message}`);
|
|
26425
|
+
});
|
|
26426
|
+
proxy.unref();
|
|
26427
|
+
} catch (e) {
|
|
26428
|
+
log.warn(
|
|
26429
|
+
"acpRunner",
|
|
26430
|
+
`budget recovery proxy relaunch failed (best-effort): ${e instanceof Error ? e.message : String(e)}`
|
|
26431
|
+
);
|
|
26432
|
+
}
|
|
26433
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
26434
|
+
};
|
|
26435
|
+
const budgetRecovery = createBudgetRecovery({
|
|
26436
|
+
publishText: (text) => publisher.publishOutput({ type: "text", content: text, done: true }),
|
|
26437
|
+
publishSelectPrompt: (question, options) => publisher.publishOutput({
|
|
26438
|
+
type: "select_prompt",
|
|
26439
|
+
content: question,
|
|
26440
|
+
options,
|
|
26441
|
+
optionDescriptions: options.map(() => ""),
|
|
26442
|
+
currentIndex: 0,
|
|
26443
|
+
done: true
|
|
26444
|
+
}),
|
|
26445
|
+
publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto7.randomUUID)(), prompt, options }),
|
|
26446
|
+
publishRawChunk: (chunk) => publisher.publishOutput(chunk),
|
|
26447
|
+
sendResult: (commandId, status2, result) => relay.sendResult(commandId, status2, result),
|
|
26448
|
+
appendAgentReply: (text) => history.appendAgentReply(text),
|
|
26449
|
+
flushHistory: () => void history.flush(),
|
|
26450
|
+
relaunchProxyWithoutBudget,
|
|
26451
|
+
agentId: opts.agent,
|
|
26452
|
+
log: (msg) => log.info("acpRunner", msg)
|
|
26453
|
+
});
|
|
26142
26454
|
showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
|
|
26143
26455
|
let handshake;
|
|
26144
26456
|
try {
|
|
@@ -26248,7 +26560,11 @@ async function runAcpSession(opts) {
|
|
|
26248
26560
|
getBeads,
|
|
26249
26561
|
publisher,
|
|
26250
26562
|
recentStderr,
|
|
26251
|
-
oneMRecovery
|
|
26563
|
+
oneMRecovery,
|
|
26564
|
+
budgetRecovery,
|
|
26565
|
+
{ get: () => _budgetReachedPosted, set: (v) => {
|
|
26566
|
+
_budgetReachedPosted = v;
|
|
26567
|
+
} }
|
|
26252
26568
|
);
|
|
26253
26569
|
},
|
|
26254
26570
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
@@ -26272,7 +26588,7 @@ async function runAcpSession(opts) {
|
|
|
26272
26588
|
await new Promise(() => {
|
|
26273
26589
|
});
|
|
26274
26590
|
}
|
|
26275
|
-
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, oneMRecovery) {
|
|
26591
|
+
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, oneMRecovery, budgetRecovery, budgetReachedFlag) {
|
|
26276
26592
|
switch (cmd.type) {
|
|
26277
26593
|
case "beads_action": {
|
|
26278
26594
|
const beads = getBeads();
|
|
@@ -26356,6 +26672,24 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
26356
26672
|
const detail = describeError(err);
|
|
26357
26673
|
log.warn("acpRunner", `prompt failed: ${detail}`);
|
|
26358
26674
|
await cancelStuckTurn(client2);
|
|
26675
|
+
if (looksLikeBudgetExceeded(`${detail}
|
|
26676
|
+
${recentStderr.join("\n")}`)) {
|
|
26677
|
+
await streaming.closeAll();
|
|
26678
|
+
if (!budgetReachedFlag.get()) {
|
|
26679
|
+
budgetReachedFlag.set(true);
|
|
26680
|
+
void postBudgetReached({
|
|
26681
|
+
sessionId: opts.sessionId,
|
|
26682
|
+
pluginId: opts.pluginId,
|
|
26683
|
+
pluginAuthToken: opts.pluginAuthToken,
|
|
26684
|
+
agent: opts.agent,
|
|
26685
|
+
period: extractBudgetPeriod(`${detail}
|
|
26686
|
+
${recentStderr.join("\n")}`)
|
|
26687
|
+
});
|
|
26688
|
+
}
|
|
26689
|
+
await budgetRecovery.offer(cmd.id, blocks, `${detail}
|
|
26690
|
+
${recentStderr.join("\n")}`);
|
|
26691
|
+
return;
|
|
26692
|
+
}
|
|
26359
26693
|
if (shouldOfferOneMRecovery({ detail, recentStderr: recentStderr.join("\n"), finalText: "" })) {
|
|
26360
26694
|
await streaming.closeAll();
|
|
26361
26695
|
await oneMRecovery.offer(cmd.id, blocks);
|
|
@@ -26462,6 +26796,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
26462
26796
|
const index = typeof payload?.index === "number" ? payload.index : 0;
|
|
26463
26797
|
const offset = typeof payload?.from === "number" ? payload.from : 0;
|
|
26464
26798
|
const absoluteIndex = index + offset;
|
|
26799
|
+
if (await budgetRecovery.tryRecover(cmd.id, absoluteIndex)) return;
|
|
26465
26800
|
const result = streaming.resolveSelection(absoluteIndex);
|
|
26466
26801
|
switch (result.kind) {
|
|
26467
26802
|
case "resolved":
|
|
@@ -27718,15 +28053,15 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
27718
28053
|
}
|
|
27719
28054
|
|
|
27720
28055
|
// src/agents/claude/onboarding.ts
|
|
27721
|
-
var
|
|
27722
|
-
var
|
|
28056
|
+
var fs49 = __toESM(require("fs"));
|
|
28057
|
+
var os40 = __toESM(require("os"));
|
|
27723
28058
|
var path55 = __toESM(require("path"));
|
|
27724
28059
|
function ensureClaudeOnboarded() {
|
|
27725
28060
|
try {
|
|
27726
|
-
const file = path55.join(
|
|
28061
|
+
const file = path55.join(os40.homedir(), ".claude.json");
|
|
27727
28062
|
let config = {};
|
|
27728
28063
|
try {
|
|
27729
|
-
config = JSON.parse(
|
|
28064
|
+
config = JSON.parse(fs49.readFileSync(file, "utf8"));
|
|
27730
28065
|
} catch {
|
|
27731
28066
|
}
|
|
27732
28067
|
if (config.hasCompletedOnboarding === true && typeof config.theme === "string") {
|
|
@@ -27737,8 +28072,8 @@ function ensureClaudeOnboarded() {
|
|
|
27737
28072
|
if (typeof config.lastOnboardingVersion !== "string") {
|
|
27738
28073
|
config.lastOnboardingVersion = "2.1.177";
|
|
27739
28074
|
}
|
|
27740
|
-
|
|
27741
|
-
|
|
28075
|
+
fs49.mkdirSync(path55.dirname(file), { recursive: true });
|
|
28076
|
+
fs49.writeFileSync(file, JSON.stringify(config, null, 2));
|
|
27742
28077
|
log.info("claude", "pre-completed Claude onboarding (skip first-run theme picker)");
|
|
27743
28078
|
} catch (err) {
|
|
27744
28079
|
log.warn("claude", `ensureClaudeOnboarded failed (non-fatal): ${err.message}`);
|
|
@@ -30433,7 +30768,7 @@ async function invite() {
|
|
|
30433
30768
|
var import_node_dns = require("dns");
|
|
30434
30769
|
var import_node_util5 = require("util");
|
|
30435
30770
|
var import_node_crypto8 = require("crypto");
|
|
30436
|
-
var
|
|
30771
|
+
var fs50 = __toESM(require("fs"));
|
|
30437
30772
|
var path60 = __toESM(require("path"));
|
|
30438
30773
|
var import_picocolors14 = __toESM(require("picocolors"));
|
|
30439
30774
|
var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
|
|
@@ -30492,11 +30827,11 @@ async function checkHealth(apiBase2) {
|
|
|
30492
30827
|
function checkConfigDir() {
|
|
30493
30828
|
const dir = path60.join(require("os").homedir(), ".codeam");
|
|
30494
30829
|
try {
|
|
30495
|
-
|
|
30830
|
+
fs50.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
30496
30831
|
const probe = path60.join(dir, ".doctor-probe");
|
|
30497
|
-
|
|
30498
|
-
const read2 =
|
|
30499
|
-
|
|
30832
|
+
fs50.writeFileSync(probe, "ok", { mode: 384 });
|
|
30833
|
+
const read2 = fs50.readFileSync(probe, "utf8");
|
|
30834
|
+
fs50.unlinkSync(probe);
|
|
30500
30835
|
if (read2 !== "ok") throw new Error("write/read round-trip mismatch");
|
|
30501
30836
|
return {
|
|
30502
30837
|
id: "config-dir",
|
|
@@ -30536,9 +30871,9 @@ function checkSessions() {
|
|
|
30536
30871
|
}
|
|
30537
30872
|
}
|
|
30538
30873
|
function checkAgentBinaries() {
|
|
30539
|
-
const
|
|
30874
|
+
const os42 = createOsStrategy();
|
|
30540
30875
|
return getEnabledAgents().map((meta) => {
|
|
30541
|
-
const found =
|
|
30876
|
+
const found = os42.findInPath(meta.binaryName);
|
|
30542
30877
|
return {
|
|
30543
30878
|
id: `agent-${meta.id}`,
|
|
30544
30879
|
label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
|
|
@@ -30602,7 +30937,7 @@ function checkChokidar() {
|
|
|
30602
30937
|
}
|
|
30603
30938
|
async function doctor(args2 = []) {
|
|
30604
30939
|
const json = args2.includes("--json");
|
|
30605
|
-
const cliVersion = true ? "2.
|
|
30940
|
+
const cliVersion = true ? "2.51.1" : "0.0.0-dev";
|
|
30606
30941
|
const apiBase2 = resolveApiBaseUrl();
|
|
30607
30942
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
30608
30943
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -30801,7 +31136,7 @@ async function completion(args2) {
|
|
|
30801
31136
|
// src/commands/version.ts
|
|
30802
31137
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
30803
31138
|
function version2() {
|
|
30804
|
-
const v = true ? "2.
|
|
31139
|
+
const v = true ? "2.51.1" : "unknown";
|
|
30805
31140
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
30806
31141
|
}
|
|
30807
31142
|
|
|
@@ -30950,10 +31285,10 @@ var EXIT_CODE_NAMES = {
|
|
|
30950
31285
|
};
|
|
30951
31286
|
|
|
30952
31287
|
// src/index.ts
|
|
30953
|
-
var
|
|
31288
|
+
var os41 = __toESM(require("os"));
|
|
30954
31289
|
if (!process.env.HOME) {
|
|
30955
31290
|
try {
|
|
30956
|
-
const home =
|
|
31291
|
+
const home = os41.homedir();
|
|
30957
31292
|
if (home) process.env.HOME = home;
|
|
30958
31293
|
} catch {
|
|
30959
31294
|
}
|