@cabane/companion 0.6.159 → 0.6.160
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/README.md +2 -2
- package/dist/cli.js +537 -409
- package/dist/launch.js +10 -17
- package/dist/pairing-config.js +19 -17
- package/dist/postinstall.js +141 -28
- package/dist/runtime.js +386 -335
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -4,187 +4,13 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
4
4
|
// src/cabane-command.ts
|
|
5
5
|
import { accessSync, constants, realpathSync as realpathSync2 } from "fs";
|
|
6
6
|
import { homedir as homedir3 } from "os";
|
|
7
|
-
import { delimiter, join as join2, sep as sep2 } from "path";
|
|
7
|
+
import { basename, delimiter, dirname as dirname2, join as join2, sep as sep2 } from "path";
|
|
8
8
|
|
|
9
|
-
// src/
|
|
10
|
-
import { realpathSync
|
|
11
|
-
import { createRequire } from "module";
|
|
9
|
+
// src/install-kind.ts
|
|
10
|
+
import { realpathSync } from "fs";
|
|
12
11
|
import { homedir } from "os";
|
|
13
12
|
import { dirname, join, sep } from "path";
|
|
14
13
|
import { fileURLToPath } from "url";
|
|
15
|
-
function nodeResolveSelf(specifier) {
|
|
16
|
-
const here = fileURLToPath(import.meta.url);
|
|
17
|
-
if (typeof import.meta.resolve === "function") {
|
|
18
|
-
try {
|
|
19
|
-
return fileURLToPath(import.meta.resolve(specifier));
|
|
20
|
-
} catch {
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
return createRequire(here).resolve(specifier);
|
|
25
|
-
} catch {
|
|
26
|
-
}
|
|
27
|
-
return walkNodeModules(specifier, here);
|
|
28
|
-
}
|
|
29
|
-
function walkNodeModules(pkg2, fromPath) {
|
|
30
|
-
let dir2 = dirname(fromPath);
|
|
31
|
-
for (; ; ) {
|
|
32
|
-
const manifest = join(dir2, "node_modules", pkg2, "package.json");
|
|
33
|
-
if (nodeIsFile(manifest)) {
|
|
34
|
-
try {
|
|
35
|
-
return realpathSync(manifest);
|
|
36
|
-
} catch {
|
|
37
|
-
return manifest;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const parent = dirname(dir2);
|
|
41
|
-
if (parent === dir2) return null;
|
|
42
|
-
dir2 = parent;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function nodeResolve(specifier, fromPath) {
|
|
46
|
-
try {
|
|
47
|
-
return createRequire(fromPath).resolve(specifier);
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function nodeIsFile(path) {
|
|
53
|
-
try {
|
|
54
|
-
return statSync(path).isFile();
|
|
55
|
-
} catch {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function detectPreferMusl(platform = process.platform) {
|
|
60
|
-
if (platform !== "linux") return false;
|
|
61
|
-
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
62
|
-
const header = report?.header;
|
|
63
|
-
return report != null && header?.glibcVersionRuntime === void 0;
|
|
64
|
-
}
|
|
65
|
-
function defaultResolveEnv() {
|
|
66
|
-
return {
|
|
67
|
-
platform: process.platform,
|
|
68
|
-
arch: process.arch,
|
|
69
|
-
preferMusl: detectPreferMusl(),
|
|
70
|
-
// Both resolutions start from this module, which tsup bundles into
|
|
71
|
-
// `dist/cli.js` and `dist/postinstall.js` — both at the package's `dist/`
|
|
72
|
-
// root — so one environment serves the runtime checks and the install
|
|
73
|
-
// verifier alike.
|
|
74
|
-
resolveSelf: nodeResolveSelf,
|
|
75
|
-
resolve: nodeResolve,
|
|
76
|
-
isFile: nodeIsFile
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
var CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
80
|
-
function claudePlatformPackages(platform, arch, preferMusl) {
|
|
81
|
-
if (platform === "android") return [`${CLAUDE_SDK}-linux-${arch}-android`];
|
|
82
|
-
if (platform === "linux") {
|
|
83
|
-
return preferMusl ? [`${CLAUDE_SDK}-linux-${arch}-musl`, `${CLAUDE_SDK}-linux-${arch}`] : [`${CLAUDE_SDK}-linux-${arch}`, `${CLAUDE_SDK}-linux-${arch}-musl`];
|
|
84
|
-
}
|
|
85
|
-
return [`${CLAUDE_SDK}-${platform}-${arch}`];
|
|
86
|
-
}
|
|
87
|
-
function resolveClaude(env) {
|
|
88
|
-
const candidates = claudePlatformPackages(env.platform, env.arch, env.preferMusl);
|
|
89
|
-
const base = {
|
|
90
|
-
runtime: "claude-code",
|
|
91
|
-
packageName: candidates[0] ?? null,
|
|
92
|
-
platform: env.platform,
|
|
93
|
-
arch: env.arch
|
|
94
|
-
};
|
|
95
|
-
const sdkEntry = env.resolveSelf(CLAUDE_SDK);
|
|
96
|
-
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
97
|
-
let sawPackage = null;
|
|
98
|
-
for (const pkg2 of candidates) {
|
|
99
|
-
const manifest = env.resolve(`${pkg2}/package.json`, sdkEntry);
|
|
100
|
-
if (!manifest) continue;
|
|
101
|
-
sawPackage ??= pkg2;
|
|
102
|
-
const exe = join(dirname(manifest), env.platform === "win32" ? "claude.exe" : "claude");
|
|
103
|
-
if (env.isFile(exe)) {
|
|
104
|
-
return { ...base, packageName: pkg2, status: "present", path: exe };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return sawPackage ? { ...base, packageName: sawPackage, status: "executable_missing", path: null } : { ...base, status: "platform_package_missing", path: null };
|
|
108
|
-
}
|
|
109
|
-
var CODEX_SDK = "@openai/codex-sdk";
|
|
110
|
-
var CODEX_CLI = "@openai/codex";
|
|
111
|
-
function codexTargetTriple(platform, arch) {
|
|
112
|
-
if (platform === "linux" || platform === "android") {
|
|
113
|
-
if (arch === "x64") return "x86_64-unknown-linux-musl";
|
|
114
|
-
if (arch === "arm64") return "aarch64-unknown-linux-musl";
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
if (platform === "darwin") {
|
|
118
|
-
if (arch === "x64") return "x86_64-apple-darwin";
|
|
119
|
-
if (arch === "arm64") return "aarch64-apple-darwin";
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
if (platform === "win32") {
|
|
123
|
-
if (arch === "x64") return "x86_64-pc-windows-msvc";
|
|
124
|
-
if (arch === "arm64") return "aarch64-pc-windows-msvc";
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
var CODEX_PACKAGE_BY_TRIPLE = {
|
|
130
|
-
"x86_64-unknown-linux-musl": `${CODEX_CLI}-linux-x64`,
|
|
131
|
-
"aarch64-unknown-linux-musl": `${CODEX_CLI}-linux-arm64`,
|
|
132
|
-
"x86_64-apple-darwin": `${CODEX_CLI}-darwin-x64`,
|
|
133
|
-
"aarch64-apple-darwin": `${CODEX_CLI}-darwin-arm64`,
|
|
134
|
-
"x86_64-pc-windows-msvc": `${CODEX_CLI}-win32-x64`,
|
|
135
|
-
"aarch64-pc-windows-msvc": `${CODEX_CLI}-win32-arm64`
|
|
136
|
-
};
|
|
137
|
-
function resolveCodex(env) {
|
|
138
|
-
const triple = codexTargetTriple(env.platform, env.arch);
|
|
139
|
-
const pkg2 = triple ? CODEX_PACKAGE_BY_TRIPLE[triple] : void 0;
|
|
140
|
-
const base = {
|
|
141
|
-
runtime: "codex",
|
|
142
|
-
packageName: pkg2 ?? null,
|
|
143
|
-
platform: env.platform,
|
|
144
|
-
arch: env.arch
|
|
145
|
-
};
|
|
146
|
-
if (!triple || !pkg2) return { ...base, status: "platform_package_missing", path: null };
|
|
147
|
-
const sdkEntry = env.resolveSelf(CODEX_SDK);
|
|
148
|
-
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
149
|
-
const cliManifest = env.resolve(`${CODEX_CLI}/package.json`, sdkEntry);
|
|
150
|
-
if (!cliManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
151
|
-
const platformManifest = env.resolve(`${pkg2}/package.json`, cliManifest);
|
|
152
|
-
if (!platformManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
153
|
-
const packageRoot = join(dirname(platformManifest), "vendor", triple);
|
|
154
|
-
const exeName = env.platform === "win32" ? "codex.exe" : "codex";
|
|
155
|
-
const current = join(packageRoot, "bin", exeName);
|
|
156
|
-
if (env.isFile(current) && env.isFile(join(packageRoot, "codex-package.json"))) {
|
|
157
|
-
return { ...base, status: "present", path: current };
|
|
158
|
-
}
|
|
159
|
-
const legacy = join(packageRoot, "codex", exeName);
|
|
160
|
-
if (env.isFile(legacy)) return { ...base, status: "present", path: legacy };
|
|
161
|
-
return { ...base, status: "executable_missing", path: null };
|
|
162
|
-
}
|
|
163
|
-
function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
164
|
-
try {
|
|
165
|
-
return runtime === "codex" ? resolveCodex(env) : resolveClaude(env);
|
|
166
|
-
} catch {
|
|
167
|
-
return {
|
|
168
|
-
runtime,
|
|
169
|
-
status: "platform_package_missing",
|
|
170
|
-
path: null,
|
|
171
|
-
packageName: null,
|
|
172
|
-
platform: env.platform,
|
|
173
|
-
arch: env.arch
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
var HARNESS_LABEL = {
|
|
178
|
-
"claude-code": "Claude Code",
|
|
179
|
-
codex: "Codex"
|
|
180
|
-
};
|
|
181
|
-
var HARNESS_INTENT_WORD = {
|
|
182
|
-
"claude-code": "connected",
|
|
183
|
-
codex: "enabled"
|
|
184
|
-
};
|
|
185
|
-
function missingNoun(status) {
|
|
186
|
-
return status === "executable_missing" ? "executable" : "platform package";
|
|
187
|
-
}
|
|
188
14
|
var INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
|
|
189
15
|
function companionInstall(execPath = process.execPath, home = homedir()) {
|
|
190
16
|
const real = (path) => {
|
|
@@ -197,23 +23,16 @@ function companionInstall(execPath = process.execPath, home = homedir()) {
|
|
|
197
23
|
const scriptNode = join(real(home), ".cabane", "node") + sep;
|
|
198
24
|
return real(execPath).startsWith(scriptNode) ? "script" : "npm";
|
|
199
25
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const label = HARNESS_LABEL[r.runtime];
|
|
208
|
-
return `${label} is ${HARNESS_INTENT_WORD[r.runtime]}, but its installation is incomplete (${missingNoun(r.status)} is missing). It cannot run turns until repaired. ${repairAction()} Then restart the companion.`;
|
|
209
|
-
}
|
|
210
|
-
function harnessIssueNote(runtime, status, install = companionInstall()) {
|
|
211
|
-
const intent = HARNESS_INTENT_WORD[runtime];
|
|
212
|
-
const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
|
|
213
|
-
return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status)} is missing. ${repairAction(install)} Then restart the companion.`;
|
|
26
|
+
function repairRoute(packageRoot = ownRoot(), install = companionInstall()) {
|
|
27
|
+
let root = packageRoot;
|
|
28
|
+
try {
|
|
29
|
+
root = realpathSync(packageRoot);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
return root.includes(`${sep}.cabane${sep}versions${sep}`) ? "managed" : install;
|
|
214
33
|
}
|
|
215
|
-
function
|
|
216
|
-
return
|
|
34
|
+
function ownRoot() {
|
|
35
|
+
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
217
36
|
}
|
|
218
37
|
|
|
219
38
|
// src/cli-entry.ts
|
|
@@ -241,15 +60,27 @@ function tildePath(path, home = homedir2()) {
|
|
|
241
60
|
}
|
|
242
61
|
|
|
243
62
|
// src/cabane-command.ts
|
|
63
|
+
var INVOKED_COMMAND_ENV = "CABANE_INVOKED_COMMAND";
|
|
64
|
+
var VERSIONS_SEGMENT = `${sep2}.cabane${sep2}versions${sep2}`;
|
|
244
65
|
function installedCommandPath(deps = {}) {
|
|
245
66
|
const install = deps.install ?? companionInstall();
|
|
246
67
|
const home = deps.home ?? homedir3();
|
|
247
68
|
if (install === "script") return join2(home, ".cabane", "bin", "cabane");
|
|
248
69
|
const entry = deps.cliEntry ?? companionCliEntry();
|
|
70
|
+
const own = npmBinFor(entry);
|
|
71
|
+
if (own) return own;
|
|
72
|
+
if (!entry.includes(VERSIONS_SEGMENT)) return null;
|
|
73
|
+
const isExecutable = deps.isExecutable ?? executable;
|
|
74
|
+
const invoked = "invoked" in deps ? deps.invoked : process.env[INVOKED_COMMAND_ENV];
|
|
75
|
+
if (invoked && isExecutable(invoked)) return invoked;
|
|
76
|
+
const besideNode = join2(dirname2(dirname2(deps.execPath ?? process.execPath)), "bin", "cabane");
|
|
77
|
+
return isExecutable(besideNode) ? besideNode : null;
|
|
78
|
+
}
|
|
79
|
+
function npmBinFor(path) {
|
|
249
80
|
const marker = `${sep2}lib${sep2}node_modules${sep2}@cabane${sep2}companion${sep2}`;
|
|
250
|
-
const at =
|
|
81
|
+
const at = path.indexOf(marker);
|
|
251
82
|
if (at < 0) return null;
|
|
252
|
-
return join2(
|
|
83
|
+
return join2(path.slice(0, at), "bin", "cabane");
|
|
253
84
|
}
|
|
254
85
|
function cabaneCommand(deps = {}) {
|
|
255
86
|
const own = installedCommandPath(deps);
|
|
@@ -265,6 +96,9 @@ function cabaneCommand(deps = {}) {
|
|
|
265
96
|
function cabane(rest, deps) {
|
|
266
97
|
return `${cabaneCommand(deps)} ${rest}`;
|
|
267
98
|
}
|
|
99
|
+
function mayNeedUpdate(deps) {
|
|
100
|
+
return `This companion may need an update: run ${cabane("update", deps)}`;
|
|
101
|
+
}
|
|
268
102
|
function firstOnPath(name, path, isExecutable = executable) {
|
|
269
103
|
for (const dir2 of (path ?? "").split(delimiter)) {
|
|
270
104
|
if (!dir2) continue;
|
|
@@ -300,7 +134,7 @@ import {
|
|
|
300
134
|
existsSync as existsSync2
|
|
301
135
|
} from "fs";
|
|
302
136
|
import { homedir as homedir4, userInfo } from "os";
|
|
303
|
-
import { dirname as
|
|
137
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
304
138
|
import { z as z2 } from "zod";
|
|
305
139
|
|
|
306
140
|
// src/errors.ts
|
|
@@ -763,7 +597,7 @@ function loadConfig() {
|
|
|
763
597
|
}
|
|
764
598
|
function saveConfig(cfg) {
|
|
765
599
|
const path = configPath();
|
|
766
|
-
mkdirSync(
|
|
600
|
+
mkdirSync(dirname3(path), { recursive: true });
|
|
767
601
|
try {
|
|
768
602
|
chmodSync(cabaneDir(), 448);
|
|
769
603
|
} catch {
|
|
@@ -795,20 +629,228 @@ function requireConfig() {
|
|
|
795
629
|
}
|
|
796
630
|
|
|
797
631
|
// src/dashboard/server.ts
|
|
798
|
-
import { dirname as
|
|
799
|
-
import { fileURLToPath as
|
|
632
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
633
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
800
634
|
import { serve } from "@hono/node-server";
|
|
801
635
|
import { Hono } from "hono";
|
|
802
636
|
|
|
803
637
|
// src/dashboard/routes.ts
|
|
804
638
|
import { openSync as openSync2, readSync, closeSync, fstatSync, existsSync as existsSync3 } from "fs";
|
|
805
639
|
import { readFile } from "fs/promises";
|
|
806
|
-
import { extname, join as
|
|
640
|
+
import { extname, join as join7, normalize } from "path";
|
|
807
641
|
import { streamSSE } from "hono/streaming";
|
|
808
642
|
|
|
809
643
|
// src/harness-copy.ts
|
|
810
644
|
import { homedir as homedir5 } from "os";
|
|
811
|
-
import { join as
|
|
645
|
+
import { join as join5 } from "path";
|
|
646
|
+
|
|
647
|
+
// src/bundled-binaries.ts
|
|
648
|
+
import { realpathSync as realpathSync3, statSync } from "fs";
|
|
649
|
+
import { createRequire } from "module";
|
|
650
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
651
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
652
|
+
function nodeResolveSelf(specifier) {
|
|
653
|
+
const here = fileURLToPath3(import.meta.url);
|
|
654
|
+
if (typeof import.meta.resolve === "function") {
|
|
655
|
+
try {
|
|
656
|
+
return fileURLToPath3(import.meta.resolve(specifier));
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
return createRequire(here).resolve(specifier);
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
return walkNodeModules(specifier, here);
|
|
665
|
+
}
|
|
666
|
+
function walkNodeModules(pkg2, fromPath) {
|
|
667
|
+
let dir2 = dirname4(fromPath);
|
|
668
|
+
for (; ; ) {
|
|
669
|
+
const manifest = join4(dir2, "node_modules", pkg2, "package.json");
|
|
670
|
+
if (nodeIsFile(manifest)) {
|
|
671
|
+
try {
|
|
672
|
+
return realpathSync3(manifest);
|
|
673
|
+
} catch {
|
|
674
|
+
return manifest;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const parent = dirname4(dir2);
|
|
678
|
+
if (parent === dir2) return null;
|
|
679
|
+
dir2 = parent;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function nodeResolve(specifier, fromPath) {
|
|
683
|
+
try {
|
|
684
|
+
return createRequire(fromPath).resolve(specifier);
|
|
685
|
+
} catch {
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function nodeIsFile(path) {
|
|
690
|
+
try {
|
|
691
|
+
return statSync(path).isFile();
|
|
692
|
+
} catch {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function detectPreferMusl(platform = process.platform) {
|
|
697
|
+
if (platform !== "linux") return false;
|
|
698
|
+
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
699
|
+
const header = report?.header;
|
|
700
|
+
return report != null && header?.glibcVersionRuntime === void 0;
|
|
701
|
+
}
|
|
702
|
+
function defaultResolveEnv() {
|
|
703
|
+
return {
|
|
704
|
+
platform: process.platform,
|
|
705
|
+
arch: process.arch,
|
|
706
|
+
preferMusl: detectPreferMusl(),
|
|
707
|
+
// Both resolutions start from this module, which tsup bundles into
|
|
708
|
+
// `dist/cli.js` and `dist/postinstall.js` — both at the package's `dist/`
|
|
709
|
+
// root — so one environment serves the runtime checks and the install
|
|
710
|
+
// verifier alike.
|
|
711
|
+
resolveSelf: nodeResolveSelf,
|
|
712
|
+
resolve: nodeResolve,
|
|
713
|
+
isFile: nodeIsFile
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
var CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
717
|
+
function claudePlatformPackages(platform, arch, preferMusl) {
|
|
718
|
+
if (platform === "android") return [`${CLAUDE_SDK}-linux-${arch}-android`];
|
|
719
|
+
if (platform === "linux") {
|
|
720
|
+
return preferMusl ? [`${CLAUDE_SDK}-linux-${arch}-musl`, `${CLAUDE_SDK}-linux-${arch}`] : [`${CLAUDE_SDK}-linux-${arch}`, `${CLAUDE_SDK}-linux-${arch}-musl`];
|
|
721
|
+
}
|
|
722
|
+
return [`${CLAUDE_SDK}-${platform}-${arch}`];
|
|
723
|
+
}
|
|
724
|
+
function resolveClaude(env) {
|
|
725
|
+
const candidates = claudePlatformPackages(env.platform, env.arch, env.preferMusl);
|
|
726
|
+
const base = {
|
|
727
|
+
runtime: "claude-code",
|
|
728
|
+
packageName: candidates[0] ?? null,
|
|
729
|
+
platform: env.platform,
|
|
730
|
+
arch: env.arch
|
|
731
|
+
};
|
|
732
|
+
const sdkEntry = env.resolveSelf(CLAUDE_SDK);
|
|
733
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
734
|
+
let sawPackage = null;
|
|
735
|
+
for (const pkg2 of candidates) {
|
|
736
|
+
const manifest = env.resolve(`${pkg2}/package.json`, sdkEntry);
|
|
737
|
+
if (!manifest) continue;
|
|
738
|
+
sawPackage ??= pkg2;
|
|
739
|
+
const exe = join4(dirname4(manifest), env.platform === "win32" ? "claude.exe" : "claude");
|
|
740
|
+
if (env.isFile(exe)) {
|
|
741
|
+
return { ...base, packageName: pkg2, status: "present", path: exe };
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return sawPackage ? { ...base, packageName: sawPackage, status: "executable_missing", path: null } : { ...base, status: "platform_package_missing", path: null };
|
|
745
|
+
}
|
|
746
|
+
var CODEX_SDK = "@openai/codex-sdk";
|
|
747
|
+
var CODEX_CLI = "@openai/codex";
|
|
748
|
+
function codexTargetTriple(platform, arch) {
|
|
749
|
+
if (platform === "linux" || platform === "android") {
|
|
750
|
+
if (arch === "x64") return "x86_64-unknown-linux-musl";
|
|
751
|
+
if (arch === "arm64") return "aarch64-unknown-linux-musl";
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
if (platform === "darwin") {
|
|
755
|
+
if (arch === "x64") return "x86_64-apple-darwin";
|
|
756
|
+
if (arch === "arm64") return "aarch64-apple-darwin";
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
if (platform === "win32") {
|
|
760
|
+
if (arch === "x64") return "x86_64-pc-windows-msvc";
|
|
761
|
+
if (arch === "arm64") return "aarch64-pc-windows-msvc";
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
var CODEX_PACKAGE_BY_TRIPLE = {
|
|
767
|
+
"x86_64-unknown-linux-musl": `${CODEX_CLI}-linux-x64`,
|
|
768
|
+
"aarch64-unknown-linux-musl": `${CODEX_CLI}-linux-arm64`,
|
|
769
|
+
"x86_64-apple-darwin": `${CODEX_CLI}-darwin-x64`,
|
|
770
|
+
"aarch64-apple-darwin": `${CODEX_CLI}-darwin-arm64`,
|
|
771
|
+
"x86_64-pc-windows-msvc": `${CODEX_CLI}-win32-x64`,
|
|
772
|
+
"aarch64-pc-windows-msvc": `${CODEX_CLI}-win32-arm64`
|
|
773
|
+
};
|
|
774
|
+
function resolveCodex(env) {
|
|
775
|
+
const triple = codexTargetTriple(env.platform, env.arch);
|
|
776
|
+
const pkg2 = triple ? CODEX_PACKAGE_BY_TRIPLE[triple] : void 0;
|
|
777
|
+
const base = {
|
|
778
|
+
runtime: "codex",
|
|
779
|
+
packageName: pkg2 ?? null,
|
|
780
|
+
platform: env.platform,
|
|
781
|
+
arch: env.arch
|
|
782
|
+
};
|
|
783
|
+
if (!triple || !pkg2) return { ...base, status: "platform_package_missing", path: null };
|
|
784
|
+
const sdkEntry = env.resolveSelf(CODEX_SDK);
|
|
785
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
786
|
+
const cliManifest = env.resolve(`${CODEX_CLI}/package.json`, sdkEntry);
|
|
787
|
+
if (!cliManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
788
|
+
const platformManifest = env.resolve(`${pkg2}/package.json`, cliManifest);
|
|
789
|
+
if (!platformManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
790
|
+
const packageRoot = join4(dirname4(platformManifest), "vendor", triple);
|
|
791
|
+
const exeName = env.platform === "win32" ? "codex.exe" : "codex";
|
|
792
|
+
const current = join4(packageRoot, "bin", exeName);
|
|
793
|
+
if (env.isFile(current) && env.isFile(join4(packageRoot, "codex-package.json"))) {
|
|
794
|
+
return { ...base, status: "present", path: current };
|
|
795
|
+
}
|
|
796
|
+
const legacy = join4(packageRoot, "codex", exeName);
|
|
797
|
+
if (env.isFile(legacy)) return { ...base, status: "present", path: legacy };
|
|
798
|
+
return { ...base, status: "executable_missing", path: null };
|
|
799
|
+
}
|
|
800
|
+
function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
801
|
+
try {
|
|
802
|
+
return runtime === "codex" ? resolveCodex(env) : resolveClaude(env);
|
|
803
|
+
} catch {
|
|
804
|
+
return {
|
|
805
|
+
runtime,
|
|
806
|
+
status: "platform_package_missing",
|
|
807
|
+
path: null,
|
|
808
|
+
packageName: null,
|
|
809
|
+
platform: env.platform,
|
|
810
|
+
arch: env.arch
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
var HARNESS_LABEL = {
|
|
815
|
+
"claude-code": "Claude Code",
|
|
816
|
+
codex: "Codex"
|
|
817
|
+
};
|
|
818
|
+
var HARNESS_INTENT_WORD = {
|
|
819
|
+
"claude-code": "connected",
|
|
820
|
+
codex: "enabled"
|
|
821
|
+
};
|
|
822
|
+
function missingNoun(status) {
|
|
823
|
+
return status === "executable_missing" ? "executable" : "platform package";
|
|
824
|
+
}
|
|
825
|
+
function repairCommands(route = repairRoute(), spell = cabane) {
|
|
826
|
+
if (route === "managed") return [spell("stop"), spell("update")];
|
|
827
|
+
if (route === "script") return [spell("stop"), INSTALL_SCRIPT_COMMAND];
|
|
828
|
+
return ["npm i -g @cabane/companion --include=optional"];
|
|
829
|
+
}
|
|
830
|
+
function repairAction(route = repairRoute(), spell = cabane) {
|
|
831
|
+
const [first, ...rest] = repairCommands(route, spell);
|
|
832
|
+
if (rest.length === 0) return `Reinstall with \`${first}\`.`;
|
|
833
|
+
return `Run \`${first}\`, then \`${rest.join("`, then `")}\`. ${STOP_WARNING}`;
|
|
834
|
+
}
|
|
835
|
+
var STOP_WARNING = "Stopping ends any turn in progress.";
|
|
836
|
+
function repairSteps(route = repairRoute()) {
|
|
837
|
+
return repairCommands(route).join(", then ");
|
|
838
|
+
}
|
|
839
|
+
function repairCost(route = repairRoute()) {
|
|
840
|
+
return repairCommands(route).length > 1 ? STOP_WARNING : "";
|
|
841
|
+
}
|
|
842
|
+
function startupWarning(r) {
|
|
843
|
+
const label = HARNESS_LABEL[r.runtime];
|
|
844
|
+
return `${label} is ${HARNESS_INTENT_WORD[r.runtime]}, but its installation is incomplete (${missingNoun(r.status)} is missing). It cannot run turns until repaired. ${repairAction()} Then restart the companion.`;
|
|
845
|
+
}
|
|
846
|
+
function harnessIssueNote(runtime, status, route = repairRoute(), spell = cabane) {
|
|
847
|
+
const intent = HARNESS_INTENT_WORD[runtime];
|
|
848
|
+
const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
|
|
849
|
+
return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status)} is missing. ${repairAction(route, spell)} Then restart the companion.`;
|
|
850
|
+
}
|
|
851
|
+
function turnIncompleteCopy(runtime) {
|
|
852
|
+
return `**This device's ${HARNESS_LABEL[runtime]} runtime is incomplete.** ${repairAction()} Restart it, then try again.`;
|
|
853
|
+
}
|
|
812
854
|
|
|
813
855
|
// src/harness-usable.ts
|
|
814
856
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -964,7 +1006,12 @@ function runtimeIssuesFor(gates) {
|
|
|
964
1006
|
for (const name of ["claude-code", "codex"]) {
|
|
965
1007
|
const gate = name === "codex" ? gates.codex : gates.claudeCode;
|
|
966
1008
|
if (!gate.intended || gate.bundled === "present") continue;
|
|
967
|
-
issues.push({
|
|
1009
|
+
issues.push({
|
|
1010
|
+
name,
|
|
1011
|
+
reason: gate.bundled,
|
|
1012
|
+
install: gates.install,
|
|
1013
|
+
...gates.managed ? { managed: true } : {}
|
|
1014
|
+
});
|
|
968
1015
|
}
|
|
969
1016
|
return issues;
|
|
970
1017
|
}
|
|
@@ -1225,16 +1272,16 @@ function failureText(runtime, reason) {
|
|
|
1225
1272
|
case "timeout":
|
|
1226
1273
|
return `Couldn't check whether ${label} is signed in: the check didn't answer in time. Try again.`;
|
|
1227
1274
|
case "spawn":
|
|
1228
|
-
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${
|
|
1275
|
+
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
1229
1276
|
case "config":
|
|
1230
|
-
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${
|
|
1277
|
+
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${join5(homedir5(), ".codex", "config.toml").replace(homedir5(), "~")})` : ""}. Fix it, then try again.`;
|
|
1231
1278
|
case "unreadable":
|
|
1232
1279
|
return `Couldn't check whether ${label} is signed in: it answered in a way Cabane couldn't read. Try again, and if it keeps happening, update Cabane.`;
|
|
1233
1280
|
}
|
|
1234
1281
|
}
|
|
1235
1282
|
function bundleMissingText(runtime) {
|
|
1236
1283
|
const label = HARNESS_LABELS[runtime];
|
|
1237
|
-
return `This Cabane install is missing its bundled ${label}. Repair it with ${
|
|
1284
|
+
return `This Cabane install is missing its bundled ${label}. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
1238
1285
|
}
|
|
1239
1286
|
function unusableReason(runtime, check) {
|
|
1240
1287
|
if (check.status === "bundle_missing") return bundleMissingText(runtime);
|
|
@@ -1271,6 +1318,9 @@ function connectLines(outcome, opts = {}) {
|
|
|
1271
1318
|
]
|
|
1272
1319
|
};
|
|
1273
1320
|
}
|
|
1321
|
+
function spaced(sentence) {
|
|
1322
|
+
return sentence ? ` ${sentence}` : "";
|
|
1323
|
+
}
|
|
1274
1324
|
|
|
1275
1325
|
// src/logger.ts
|
|
1276
1326
|
import {
|
|
@@ -1280,11 +1330,11 @@ import {
|
|
|
1280
1330
|
renameSync as renameSync2,
|
|
1281
1331
|
statSync as statSync2
|
|
1282
1332
|
} from "fs";
|
|
1283
|
-
import { dirname as
|
|
1333
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
1284
1334
|
import { stripVTControlCharacters } from "util";
|
|
1285
1335
|
import pino from "pino";
|
|
1286
1336
|
function companionLogPath() {
|
|
1287
|
-
return
|
|
1337
|
+
return join6(cabaneDir(), "companion.log");
|
|
1288
1338
|
}
|
|
1289
1339
|
function humanText(value) {
|
|
1290
1340
|
return stripVTControlCharacters(String(value ?? "")).replace(
|
|
@@ -1392,7 +1442,7 @@ function configureLogger(log, config) {
|
|
|
1392
1442
|
}
|
|
1393
1443
|
function createLogger(destinations = {}, options = {}) {
|
|
1394
1444
|
const path = companionLogPath();
|
|
1395
|
-
if (!destinations.file) mkdirSync2(
|
|
1445
|
+
if (!destinations.file) mkdirSync2(dirname5(path), { recursive: true });
|
|
1396
1446
|
const file = destinations.file ?? new RotatingLog(path);
|
|
1397
1447
|
const state = { format: options.logFormat ?? "human" };
|
|
1398
1448
|
const streams = [];
|
|
@@ -1684,13 +1734,13 @@ var CONTENT_TYPES = {
|
|
|
1684
1734
|
function registerRoutes(app, deps) {
|
|
1685
1735
|
const { supervisor, hub, staticDir } = deps;
|
|
1686
1736
|
app.get("/", async (c) => {
|
|
1687
|
-
const html = await readFile(
|
|
1737
|
+
const html = await readFile(join7(staticDir, "index.html"), "utf8");
|
|
1688
1738
|
return c.html(html);
|
|
1689
1739
|
});
|
|
1690
1740
|
app.get("/static/:file", async (c) => {
|
|
1691
1741
|
const file = c.req.param("file");
|
|
1692
1742
|
const safe2 = normalize(file).replace(/^(\.\.[/\\])+/, "");
|
|
1693
|
-
const full =
|
|
1743
|
+
const full = join7(staticDir, safe2);
|
|
1694
1744
|
if (!full.startsWith(staticDir) || !existsSync3(full)) return c.notFound();
|
|
1695
1745
|
const body = await readFile(full);
|
|
1696
1746
|
const type = CONTENT_TYPES[extname(full).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -1902,14 +1952,14 @@ function isAddrInUse(err) {
|
|
|
1902
1952
|
return Boolean(err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE");
|
|
1903
1953
|
}
|
|
1904
1954
|
function resolveStaticDir() {
|
|
1905
|
-
return
|
|
1955
|
+
return join8(dirname6(fileURLToPath4(import.meta.url)), "static");
|
|
1906
1956
|
}
|
|
1907
1957
|
|
|
1908
1958
|
// src/control-socket.ts
|
|
1909
1959
|
import { createHash } from "crypto";
|
|
1910
1960
|
import { chmodSync as chmodSync2, existsSync as existsSync4, rmSync as rmSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
1911
1961
|
import { createServer, connect } from "net";
|
|
1912
|
-
import { join as
|
|
1962
|
+
import { join as join9 } from "path";
|
|
1913
1963
|
var CONTROL_TIMEOUT_MS = 1e3;
|
|
1914
1964
|
var MAX_UNIX_SOCKET_PATH_BYTES = 103;
|
|
1915
1965
|
function controlSocketPath() {
|
|
@@ -1918,11 +1968,11 @@ function controlSocketPath() {
|
|
|
1918
1968
|
if (process.platform === "win32") {
|
|
1919
1969
|
return `\\\\.\\pipe\\cabane-companion-${key}`;
|
|
1920
1970
|
}
|
|
1921
|
-
const besideRuntime =
|
|
1971
|
+
const besideRuntime = join9(dir2, "companion.sock");
|
|
1922
1972
|
if (Buffer.byteLength(besideRuntime) <= MAX_UNIX_SOCKET_PATH_BYTES) return besideRuntime;
|
|
1923
1973
|
const filename = `cabane-companion-${key}.sock`;
|
|
1924
|
-
const xdgPath = process.env.XDG_RUNTIME_DIR && existsSync4(process.env.XDG_RUNTIME_DIR) ?
|
|
1925
|
-
return xdgPath && Buffer.byteLength(xdgPath) <= MAX_UNIX_SOCKET_PATH_BYTES ? xdgPath :
|
|
1974
|
+
const xdgPath = process.env.XDG_RUNTIME_DIR && existsSync4(process.env.XDG_RUNTIME_DIR) ? join9(process.env.XDG_RUNTIME_DIR, filename) : null;
|
|
1975
|
+
return xdgPath && Buffer.byteLength(xdgPath) <= MAX_UNIX_SOCKET_PATH_BYTES ? xdgPath : join9("/tmp", filename);
|
|
1926
1976
|
}
|
|
1927
1977
|
async function startControlServer(handlers) {
|
|
1928
1978
|
const path = controlSocketPath();
|
|
@@ -2122,10 +2172,10 @@ import {
|
|
|
2122
2172
|
openSync as openSync3,
|
|
2123
2173
|
closeSync as closeSync2
|
|
2124
2174
|
} from "fs";
|
|
2125
|
-
import { join as
|
|
2175
|
+
import { join as join10 } from "path";
|
|
2126
2176
|
var PROBE_TIMEOUT_MS2 = 1e3;
|
|
2127
2177
|
function runtimePath() {
|
|
2128
|
-
return
|
|
2178
|
+
return join10(cabaneDir(), "runtime.json");
|
|
2129
2179
|
}
|
|
2130
2180
|
function serialize(state) {
|
|
2131
2181
|
return JSON.stringify(state, null, 2) + "\n";
|
|
@@ -2344,7 +2394,7 @@ function turnFailureCopy(reason, runtime) {
|
|
|
2344
2394
|
if (reason?.startsWith("missing_secret:"))
|
|
2345
2395
|
return `A required secret is missing: ${humanText(reason.slice(15))}. Add it to ~/.cabane/secrets.json, then reply in Cabane to try again.`;
|
|
2346
2396
|
if (reason?.startsWith("runtime_incomplete:"))
|
|
2347
|
-
return
|
|
2397
|
+
return `The agent installation is incomplete. ${repairAction()} Then restart the companion.`;
|
|
2348
2398
|
if (/^(runtime|codex|opencode)_unavailable:/.test(reason ?? ""))
|
|
2349
2399
|
return "The agent is unavailable on this machine. Enable it here or choose another connector in Cabane.";
|
|
2350
2400
|
if (reason?.startsWith("model_unavailable:"))
|
|
@@ -2619,7 +2669,7 @@ var CabaneApi = class {
|
|
|
2619
2669
|
}
|
|
2620
2670
|
this.opts.log?.warn(
|
|
2621
2671
|
{ kind: entry.kind, turnId: entry.turnId, seq: entry.seq, ...apiErrorLogFields(err) },
|
|
2622
|
-
|
|
2672
|
+
`Cabane rejected a saved update; it could not be delivered. ${mayNeedUpdate()}`
|
|
2623
2673
|
);
|
|
2624
2674
|
outbox.remove(entry.turnId, entry.seq);
|
|
2625
2675
|
progressed = true;
|
|
@@ -3031,10 +3081,10 @@ import {
|
|
|
3031
3081
|
rmSync as rmSync4,
|
|
3032
3082
|
writeFileSync as writeFileSync3
|
|
3033
3083
|
} from "fs";
|
|
3034
|
-
import { dirname as
|
|
3084
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
3035
3085
|
import { z as z4 } from "zod";
|
|
3036
3086
|
function credentialsPath() {
|
|
3037
|
-
return
|
|
3087
|
+
return join11(cabaneDir(), "credentials.json");
|
|
3038
3088
|
}
|
|
3039
3089
|
var credentialStoreSchema = z4.record(z4.string(), z4.string());
|
|
3040
3090
|
function load() {
|
|
@@ -3056,7 +3106,7 @@ function load() {
|
|
|
3056
3106
|
}
|
|
3057
3107
|
function save(map) {
|
|
3058
3108
|
const path = credentialsPath();
|
|
3059
|
-
mkdirSync5(
|
|
3109
|
+
mkdirSync5(dirname7(path), { recursive: true });
|
|
3060
3110
|
try {
|
|
3061
3111
|
chmodSync3(cabaneDir(), 448);
|
|
3062
3112
|
} catch {
|
|
@@ -3102,13 +3152,13 @@ function pruneCredentials(keepAgentIds) {
|
|
|
3102
3152
|
|
|
3103
3153
|
// src/dispatch-dedupe.ts
|
|
3104
3154
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync7 } from "fs";
|
|
3105
|
-
import { join as
|
|
3155
|
+
import { join as join12 } from "path";
|
|
3106
3156
|
var MAX_IDS = 256;
|
|
3107
3157
|
function dir(log) {
|
|
3108
|
-
return
|
|
3158
|
+
return join12(cabaneDir(), log);
|
|
3109
3159
|
}
|
|
3110
3160
|
function pathFor(log, workspaceId) {
|
|
3111
|
-
return
|
|
3161
|
+
return join12(dir(log), encodeURIComponent(workspaceId));
|
|
3112
3162
|
}
|
|
3113
3163
|
function readIds(log, workspaceId) {
|
|
3114
3164
|
const path = pathFor(log, workspaceId);
|
|
@@ -3145,10 +3195,10 @@ function markCompleted(workspaceId, eventId) {
|
|
|
3145
3195
|
}
|
|
3146
3196
|
var MAX_RESUME_ATTEMPTS = 3;
|
|
3147
3197
|
function resumeDir() {
|
|
3148
|
-
return
|
|
3198
|
+
return join12(cabaneDir(), "resume-attempts");
|
|
3149
3199
|
}
|
|
3150
3200
|
function resumePathFor(workspaceId) {
|
|
3151
|
-
return
|
|
3201
|
+
return join12(resumeDir(), encodeURIComponent(workspaceId));
|
|
3152
3202
|
}
|
|
3153
3203
|
function readResumeCounts(workspaceId) {
|
|
3154
3204
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -3184,10 +3234,10 @@ function bumpResumeAttempt(workspaceId, eventId) {
|
|
|
3184
3234
|
return next;
|
|
3185
3235
|
}
|
|
3186
3236
|
function turnDir() {
|
|
3187
|
-
return
|
|
3237
|
+
return join12(cabaneDir(), "turns");
|
|
3188
3238
|
}
|
|
3189
3239
|
function turnPathFor(workspaceId) {
|
|
3190
|
-
return
|
|
3240
|
+
return join12(turnDir(), encodeURIComponent(workspaceId));
|
|
3191
3241
|
}
|
|
3192
3242
|
var TURN_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3193
3243
|
function readTurnIds(workspaceId) {
|
|
@@ -8182,7 +8232,7 @@ var ConnectorHealthStore = class {
|
|
|
8182
8232
|
// src/dispatcher.ts
|
|
8183
8233
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8184
8234
|
import { existsSync as existsSync11, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
8185
|
-
import { join as
|
|
8235
|
+
import { join as join18 } from "path";
|
|
8186
8236
|
|
|
8187
8237
|
// src/runner/launch.ts
|
|
8188
8238
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -8641,11 +8691,11 @@ function createTurnContainment(turnId, deps = {}) {
|
|
|
8641
8691
|
|
|
8642
8692
|
// src/secrets.ts
|
|
8643
8693
|
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
8644
|
-
import { join as
|
|
8694
|
+
import { join as join13 } from "path";
|
|
8645
8695
|
import { z as z14 } from "zod";
|
|
8646
8696
|
var PLACEHOLDER_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
8647
8697
|
function secretsPath() {
|
|
8648
|
-
return
|
|
8698
|
+
return join13(cabaneDir(), "secrets.json");
|
|
8649
8699
|
}
|
|
8650
8700
|
var secretStoreSchema = z14.record(z14.string(), z14.string());
|
|
8651
8701
|
function loadSecretStore() {
|
|
@@ -9615,11 +9665,11 @@ function trimSlash2(s) {
|
|
|
9615
9665
|
// src/codex-instructions.ts
|
|
9616
9666
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
9617
9667
|
import { tmpdir } from "os";
|
|
9618
|
-
import { join as
|
|
9668
|
+
import { join as join14 } from "path";
|
|
9619
9669
|
var PREFIX = "cabane-codex-instructions-";
|
|
9620
9670
|
async function writeCodexInstructionsFile(contents) {
|
|
9621
|
-
const dir2 = await mkdtemp(
|
|
9622
|
-
const path =
|
|
9671
|
+
const dir2 = await mkdtemp(join14(tmpdir(), PREFIX));
|
|
9672
|
+
const path = join14(dir2, "instructions.md");
|
|
9623
9673
|
await writeFile(path, contents, { encoding: "utf8", mode: 384 });
|
|
9624
9674
|
return {
|
|
9625
9675
|
path,
|
|
@@ -9632,7 +9682,7 @@ async function writeCodexInstructionsFile(contents) {
|
|
|
9632
9682
|
// src/codex-images.ts
|
|
9633
9683
|
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
9634
9684
|
import { tmpdir as tmpdir2 } from "os";
|
|
9635
|
-
import { join as
|
|
9685
|
+
import { join as join15 } from "path";
|
|
9636
9686
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
9637
9687
|
var EXTENSIONS = {
|
|
9638
9688
|
"image/png": "png",
|
|
@@ -9645,7 +9695,7 @@ function createCodexImagePreparer(opts) {
|
|
|
9645
9695
|
const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
9646
9696
|
return async (urls, signal) => {
|
|
9647
9697
|
signal.throwIfAborted();
|
|
9648
|
-
const dir2 = await mkdtemp2(
|
|
9698
|
+
const dir2 = await mkdtemp2(join15(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
|
|
9649
9699
|
const cleanup = async () => {
|
|
9650
9700
|
try {
|
|
9651
9701
|
await rm2(dir2, { recursive: true, force: true });
|
|
@@ -9666,7 +9716,7 @@ function createCodexImagePreparer(opts) {
|
|
|
9666
9716
|
const ext = EXTENSIONS[type];
|
|
9667
9717
|
if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
|
|
9668
9718
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
9669
|
-
const path =
|
|
9719
|
+
const path = join15(dir2, `image-${index + 1}.${ext}`);
|
|
9670
9720
|
await writeFile2(path, bytes, { mode: 384 });
|
|
9671
9721
|
return path;
|
|
9672
9722
|
} catch (err) {
|
|
@@ -9788,15 +9838,15 @@ function reportFailure(config, body) {
|
|
|
9788
9838
|
|
|
9789
9839
|
// src/prepared.ts
|
|
9790
9840
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
|
|
9791
|
-
import { join as
|
|
9841
|
+
import { join as join16 } from "path";
|
|
9792
9842
|
function dirFor(workspaceId) {
|
|
9793
|
-
return
|
|
9843
|
+
return join16(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
9794
9844
|
}
|
|
9795
9845
|
function conversationDir(workspaceId, conversationId) {
|
|
9796
|
-
return
|
|
9846
|
+
return join16(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
9797
9847
|
}
|
|
9798
9848
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
9799
|
-
return
|
|
9849
|
+
return join16(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
9800
9850
|
}
|
|
9801
9851
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
9802
9852
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -9828,9 +9878,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
|
|
|
9828
9878
|
|
|
9829
9879
|
// src/transcript-writer.ts
|
|
9830
9880
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
9831
|
-
import { basename, dirname as
|
|
9881
|
+
import { basename as basename2, dirname as dirname8, join as join17 } from "path";
|
|
9832
9882
|
function transcriptsDir() {
|
|
9833
|
-
return
|
|
9883
|
+
return join17(runnerStateDir(), "transcripts");
|
|
9834
9884
|
}
|
|
9835
9885
|
var RETAIN = 200;
|
|
9836
9886
|
var ANOMALY_RETAIN = 50;
|
|
@@ -9840,7 +9890,7 @@ var TranscriptWriter = class {
|
|
|
9840
9890
|
onWarn;
|
|
9841
9891
|
constructor(dir2, meta, onWarn) {
|
|
9842
9892
|
this.onWarn = onWarn;
|
|
9843
|
-
this.path =
|
|
9893
|
+
this.path = join17(dir2, fileName(meta));
|
|
9844
9894
|
try {
|
|
9845
9895
|
mkdirSync8(dir2, { recursive: true });
|
|
9846
9896
|
try {
|
|
@@ -9869,10 +9919,10 @@ var TranscriptWriter = class {
|
|
|
9869
9919
|
preserveAnomaly() {
|
|
9870
9920
|
if (this.broken) return;
|
|
9871
9921
|
try {
|
|
9872
|
-
const dir2 =
|
|
9922
|
+
const dir2 = join17(dirname8(this.path), "anomalies");
|
|
9873
9923
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
9874
9924
|
chmodSync4(dir2, 448);
|
|
9875
|
-
const target =
|
|
9925
|
+
const target = join17(dir2, basename2(this.path));
|
|
9876
9926
|
copyFileSync(this.path, target);
|
|
9877
9927
|
chmodSync4(target, 384);
|
|
9878
9928
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9918,7 +9968,7 @@ function pruneOld(dir2, retain) {
|
|
|
9918
9968
|
const drop = files.sort().slice(0, files.length - retain);
|
|
9919
9969
|
for (const f of drop) {
|
|
9920
9970
|
try {
|
|
9921
|
-
rmSync6(
|
|
9971
|
+
rmSync6(join17(dir2, f), { force: true });
|
|
9922
9972
|
} catch {
|
|
9923
9973
|
}
|
|
9924
9974
|
}
|
|
@@ -9932,7 +9982,7 @@ var TurnCommitter = class {
|
|
|
9932
9982
|
this.onError = (err, hook) => {
|
|
9933
9983
|
deps.log.warn(
|
|
9934
9984
|
{ ...apiErrorLogFields(err), hook },
|
|
9935
|
-
`Couldn't save ${String(deps.log.bindings().agentName ?? "the agent")}'s ${hook === "text" ? "reply" : "reply or activity"} to Cabane.
|
|
9985
|
+
`Couldn't save ${String(deps.log.bindings().agentName ?? "the agent")}'s ${hook === "text" ? "reply" : "reply or activity"} to Cabane. ${mayNeedUpdate()}`
|
|
9936
9986
|
);
|
|
9937
9987
|
deps.onCommitFailed?.(err);
|
|
9938
9988
|
};
|
|
@@ -10072,7 +10122,7 @@ async function postIncompleteNotice(ctx2, harness, resolution) {
|
|
|
10072
10122
|
} catch (err) {
|
|
10073
10123
|
ctx2.log.warn(
|
|
10074
10124
|
{ ...apiErrorLogFields(err) },
|
|
10075
|
-
|
|
10125
|
+
`Couldn't save the explanation to Cabane. ${mayNeedUpdate()}`
|
|
10076
10126
|
);
|
|
10077
10127
|
return false;
|
|
10078
10128
|
}
|
|
@@ -10374,7 +10424,7 @@ var TurnExecution = class {
|
|
|
10374
10424
|
} else {
|
|
10375
10425
|
turnLog.warn(
|
|
10376
10426
|
{ ...apiErrorLogFields(err), turnId },
|
|
10377
|
-
|
|
10427
|
+
`Couldn't save the turn's result to Cabane. ${mayNeedUpdate()}`
|
|
10378
10428
|
);
|
|
10379
10429
|
}
|
|
10380
10430
|
}
|
|
@@ -10852,7 +10902,7 @@ var TurnExecution = class {
|
|
|
10852
10902
|
} catch (postErr) {
|
|
10853
10903
|
turnLog.warn(
|
|
10854
10904
|
{ ...apiErrorLogFields(postErr) },
|
|
10855
|
-
|
|
10905
|
+
`Couldn't save the explanation to Cabane. ${mayNeedUpdate()}`
|
|
10856
10906
|
);
|
|
10857
10907
|
const reason = `runtime_unavailable:${err.runtime}`;
|
|
10858
10908
|
throw this.concluded(reason, reason);
|
|
@@ -11061,7 +11111,7 @@ var TurnExecution = class {
|
|
|
11061
11111
|
stateLength: event.state.length,
|
|
11062
11112
|
runtime: turnRuntime
|
|
11063
11113
|
},
|
|
11064
|
-
|
|
11114
|
+
`Couldn't save the agent's memory in Cabane. The next turn will start a new session. ${mayNeedUpdate()}`
|
|
11065
11115
|
);
|
|
11066
11116
|
} else {
|
|
11067
11117
|
turnLog.warn(
|
|
@@ -11225,7 +11275,7 @@ var TurnExecution = class {
|
|
|
11225
11275
|
} else {
|
|
11226
11276
|
turnLog.warn(
|
|
11227
11277
|
{ ...apiErrorLogFields(err) },
|
|
11228
|
-
|
|
11278
|
+
`Couldn't save the turn's result to Cabane. ${mayNeedUpdate()}`
|
|
11229
11279
|
);
|
|
11230
11280
|
}
|
|
11231
11281
|
}
|
|
@@ -11298,7 +11348,7 @@ function checkoutState(cwd) {
|
|
|
11298
11348
|
if (entries.length === 0) {
|
|
11299
11349
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
11300
11350
|
}
|
|
11301
|
-
const gitPath =
|
|
11351
|
+
const gitPath = join18(cwd, ".git");
|
|
11302
11352
|
if (!existsSync11(gitPath)) return { ok: true, reason: "usable" };
|
|
11303
11353
|
let stat;
|
|
11304
11354
|
try {
|
|
@@ -11306,7 +11356,7 @@ function checkoutState(cwd) {
|
|
|
11306
11356
|
} catch (error) {
|
|
11307
11357
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
11308
11358
|
}
|
|
11309
|
-
if (stat.isDirectory() && !existsSync11(
|
|
11359
|
+
if (stat.isDirectory() && !existsSync11(join18(gitPath, "HEAD")))
|
|
11310
11360
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
11311
11361
|
return { ok: true, reason: "usable" };
|
|
11312
11362
|
}
|
|
@@ -11524,7 +11574,7 @@ import {
|
|
|
11524
11574
|
statSync as statSync4,
|
|
11525
11575
|
writeFileSync as writeFileSync6
|
|
11526
11576
|
} from "fs";
|
|
11527
|
-
import { join as
|
|
11577
|
+
import { join as join21 } from "path";
|
|
11528
11578
|
import { Writable } from "stream";
|
|
11529
11579
|
|
|
11530
11580
|
// src/providers/docker-cli.ts
|
|
@@ -11718,7 +11768,7 @@ import {
|
|
|
11718
11768
|
renameSync as renameSync5,
|
|
11719
11769
|
rmSync as rmSync8
|
|
11720
11770
|
} from "fs";
|
|
11721
|
-
import { join as
|
|
11771
|
+
import { join as join19 } from "path";
|
|
11722
11772
|
function routes(t) {
|
|
11723
11773
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
11724
11774
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -11764,7 +11814,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11764
11814
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
11765
11815
|
let entry = {};
|
|
11766
11816
|
try {
|
|
11767
|
-
entry = JSON.parse(readFileSync8(
|
|
11817
|
+
entry = JSON.parse(readFileSync8(join19(from, name), "utf8"));
|
|
11768
11818
|
} catch {
|
|
11769
11819
|
}
|
|
11770
11820
|
return { name, entry };
|
|
@@ -11777,7 +11827,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11777
11827
|
let published = 0;
|
|
11778
11828
|
for (const { name } of staged) {
|
|
11779
11829
|
try {
|
|
11780
|
-
move(
|
|
11830
|
+
move(join19(from, name), join19(to, name));
|
|
11781
11831
|
} catch {
|
|
11782
11832
|
return { published, complete: false };
|
|
11783
11833
|
}
|
|
@@ -11789,9 +11839,9 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11789
11839
|
|
|
11790
11840
|
// src/providers/runner-base.ts
|
|
11791
11841
|
import { createHash as createHash3 } from "crypto";
|
|
11792
|
-
import { existsSync as existsSync13, realpathSync as
|
|
11793
|
-
import { dirname as
|
|
11794
|
-
import { fileURLToPath as
|
|
11842
|
+
import { existsSync as existsSync13, realpathSync as realpathSync4 } from "fs";
|
|
11843
|
+
import { dirname as dirname9, join as join20, sep as sep3 } from "path";
|
|
11844
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11795
11845
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
11796
11846
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
11797
11847
|
RUN apt-get update \\
|
|
@@ -11835,17 +11885,17 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
11835
11885
|
}
|
|
11836
11886
|
return true;
|
|
11837
11887
|
}
|
|
11838
|
-
function ownInstall(moduleFile =
|
|
11839
|
-
let dir2 =
|
|
11888
|
+
function ownInstall(moduleFile = fileURLToPath5(import.meta.url)) {
|
|
11889
|
+
let dir2 = dirname9(moduleFile);
|
|
11840
11890
|
for (let i = 0; i < 4; i++) {
|
|
11841
|
-
if (existsSync13(
|
|
11842
|
-
dir2 =
|
|
11891
|
+
if (existsSync13(join20(dir2, "dist", "cli.js")) && existsSync13(join20(dir2, "package.json"))) break;
|
|
11892
|
+
dir2 = dirname9(dir2);
|
|
11843
11893
|
}
|
|
11844
|
-
if (!existsSync13(
|
|
11845
|
-
const sdk =
|
|
11894
|
+
if (!existsSync13(join20(dir2, "dist", "cli.js"))) return null;
|
|
11895
|
+
const sdk = join20(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
11846
11896
|
if (!existsSync13(sdk)) return null;
|
|
11847
|
-
const root =
|
|
11848
|
-
return
|
|
11897
|
+
const root = realpathSync4(dir2);
|
|
11898
|
+
return realpathSync4(sdk).startsWith(root + sep3) ? root : null;
|
|
11849
11899
|
}
|
|
11850
11900
|
function companionSource(input) {
|
|
11851
11901
|
if (input.configured) {
|
|
@@ -11981,39 +12031,39 @@ function networkCreateError(stderr) {
|
|
|
11981
12031
|
}
|
|
11982
12032
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11983
12033
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11984
|
-
return
|
|
12034
|
+
return join21(deviceDir, "attempts", attemptId, "session.tar");
|
|
11985
12035
|
}
|
|
11986
12036
|
function pruneOtherAttempts(deviceDir, pair, keep) {
|
|
11987
|
-
const root =
|
|
12037
|
+
const root = join21(deviceDir, "attempts");
|
|
11988
12038
|
if (!existsSync14(root)) return;
|
|
11989
12039
|
for (const id of readdirSync4(root)) {
|
|
11990
12040
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
11991
12041
|
let pairOf = "";
|
|
11992
12042
|
try {
|
|
11993
|
-
pairOf = readFileSync9(
|
|
12043
|
+
pairOf = readFileSync9(join21(root, id, "pair"), "utf8");
|
|
11994
12044
|
} catch {
|
|
11995
12045
|
continue;
|
|
11996
12046
|
}
|
|
11997
12047
|
if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
|
|
11998
|
-
rmSync9(
|
|
12048
|
+
rmSync9(join21(root, id), { recursive: true, force: true });
|
|
11999
12049
|
}
|
|
12000
12050
|
}
|
|
12001
12051
|
}
|
|
12002
12052
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
12003
12053
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
12004
|
-
const root =
|
|
12054
|
+
const root = join21(deviceDir, "attempts");
|
|
12005
12055
|
if (!existsSync14(root)) return [];
|
|
12006
12056
|
const removed = [];
|
|
12007
12057
|
for (const id of readdirSync4(root)) {
|
|
12008
12058
|
if (!UUID_RE.test(id)) continue;
|
|
12009
12059
|
let kept;
|
|
12010
12060
|
try {
|
|
12011
|
-
kept = statSync4(
|
|
12061
|
+
kept = statSync4(join21(root, id)).mtimeMs;
|
|
12012
12062
|
} catch {
|
|
12013
12063
|
continue;
|
|
12014
12064
|
}
|
|
12015
12065
|
if (now - kept < maxAgeMs) continue;
|
|
12016
|
-
rmSync9(
|
|
12066
|
+
rmSync9(join21(root, id), { recursive: true, force: true });
|
|
12017
12067
|
removed.push(id);
|
|
12018
12068
|
}
|
|
12019
12069
|
return removed;
|
|
@@ -12214,11 +12264,11 @@ function createDockerProvider(opts) {
|
|
|
12214
12264
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
12215
12265
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
12216
12266
|
};
|
|
12217
|
-
const socketRoot = opts.socketRoot ??
|
|
12267
|
+
const socketRoot = opts.socketRoot ?? join21(
|
|
12218
12268
|
process.env.XDG_RUNTIME_DIR && existsSync14(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
12219
12269
|
`cabane-runners-${opts.deviceKey}`
|
|
12220
12270
|
);
|
|
12221
|
-
const socketDir = (name) =>
|
|
12271
|
+
const socketDir = (name) => join21(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
12222
12272
|
const create = async (name, attempt) => {
|
|
12223
12273
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
12224
12274
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -12360,7 +12410,7 @@ function createDockerProvider(opts) {
|
|
|
12360
12410
|
}
|
|
12361
12411
|
};
|
|
12362
12412
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
12363
|
-
const pendingPath = (name) =>
|
|
12413
|
+
const pendingPath = (name) => join21(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
12364
12414
|
const readPending = (name) => {
|
|
12365
12415
|
try {
|
|
12366
12416
|
const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
|
|
@@ -12374,12 +12424,12 @@ function createDockerProvider(opts) {
|
|
|
12374
12424
|
rmSync9(pendingPath(name), { force: true });
|
|
12375
12425
|
return;
|
|
12376
12426
|
}
|
|
12377
|
-
mkdirSync10(
|
|
12427
|
+
mkdirSync10(join21(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
12378
12428
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
12379
12429
|
};
|
|
12380
12430
|
const keepSession = async (name, attempt, attemptId) => {
|
|
12381
12431
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
12382
|
-
mkdirSync10(
|
|
12432
|
+
mkdirSync10(join21(archive, ".."), { recursive: true });
|
|
12383
12433
|
const partial = `${archive}.${process.pid}.partial`;
|
|
12384
12434
|
const file = new CappedFile(partial, sessionCap);
|
|
12385
12435
|
const r = await execIn(
|
|
@@ -12396,7 +12446,7 @@ function createDockerProvider(opts) {
|
|
|
12396
12446
|
);
|
|
12397
12447
|
if (r.code === 0 && !file.over) {
|
|
12398
12448
|
renameSync6(partial, archive);
|
|
12399
|
-
writeFileSync6(
|
|
12449
|
+
writeFileSync6(join21(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
12400
12450
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
12401
12451
|
await writeMarker(name, attemptId);
|
|
12402
12452
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -12410,7 +12460,7 @@ function createDockerProvider(opts) {
|
|
|
12410
12460
|
file.over ? "runners: the session files are over the size cap and weren't kept; the next new container starts the session over" : "runners: couldn't keep the session files; the next new container starts the session over"
|
|
12411
12461
|
);
|
|
12412
12462
|
};
|
|
12413
|
-
const stagingDir = (name) =>
|
|
12463
|
+
const stagingDir = (name) => join21(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
12414
12464
|
const ensureRunning = async (name) => {
|
|
12415
12465
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
12416
12466
|
if (seen.code !== 0) return false;
|
|
@@ -12418,7 +12468,7 @@ function createDockerProvider(opts) {
|
|
|
12418
12468
|
return (await docker.run(["start", name])).code === 0;
|
|
12419
12469
|
};
|
|
12420
12470
|
const dropArchive = (attemptId) => {
|
|
12421
|
-
rmSync9(
|
|
12471
|
+
rmSync9(join21(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
12422
12472
|
recursive: true,
|
|
12423
12473
|
force: true
|
|
12424
12474
|
});
|
|
@@ -12487,7 +12537,7 @@ function createDockerProvider(opts) {
|
|
|
12487
12537
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
12488
12538
|
await must(["start", name], "starting the container");
|
|
12489
12539
|
const git = await serveGitCredentials({
|
|
12490
|
-
socketPath:
|
|
12540
|
+
socketPath: join21(socketDir(name), "git.sock"),
|
|
12491
12541
|
hosts: opts.gitHosts,
|
|
12492
12542
|
fill: opts.gitFill,
|
|
12493
12543
|
log
|
|
@@ -12636,15 +12686,15 @@ function createDockerProvider(opts) {
|
|
|
12636
12686
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
12637
12687
|
if (transcripts) {
|
|
12638
12688
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
12639
|
-
if (kept.length) mkdirSync10(
|
|
12640
|
-
for (const f of kept) writeFileSync6(
|
|
12689
|
+
if (kept.length) mkdirSync10(join21(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
12690
|
+
for (const f of kept) writeFileSync6(join21(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
12641
12691
|
await removeIn(
|
|
12642
12692
|
name,
|
|
12643
12693
|
`${CONTAINER_STATE}/transcripts`,
|
|
12644
12694
|
kept.map((f) => f.name)
|
|
12645
12695
|
);
|
|
12646
12696
|
}
|
|
12647
|
-
const outboxRel =
|
|
12697
|
+
const outboxRel = join21("outbox", encodeURIComponent(attempt.agentId));
|
|
12648
12698
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
12649
12699
|
let unpublished = false;
|
|
12650
12700
|
if (outbox) {
|
|
@@ -12670,7 +12720,7 @@ function createDockerProvider(opts) {
|
|
|
12670
12720
|
const staged = stagingDir(name);
|
|
12671
12721
|
mkdirSync10(staged, { recursive: true });
|
|
12672
12722
|
writeFileSync6(
|
|
12673
|
-
|
|
12723
|
+
join21(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
12674
12724
|
`${JSON.stringify(entry)}
|
|
12675
12725
|
`
|
|
12676
12726
|
);
|
|
@@ -12683,7 +12733,7 @@ function createDockerProvider(opts) {
|
|
|
12683
12733
|
if (outbox.complete) {
|
|
12684
12734
|
const published = publishStaged(
|
|
12685
12735
|
stagingDir(name),
|
|
12686
|
-
|
|
12736
|
+
join21(opts.hostStateDir, outboxRel),
|
|
12687
12737
|
opts.moveFile
|
|
12688
12738
|
);
|
|
12689
12739
|
queued = published.published;
|
|
@@ -12892,15 +12942,15 @@ import {
|
|
|
12892
12942
|
mkdirSync as mkdirSync11,
|
|
12893
12943
|
mkdtempSync,
|
|
12894
12944
|
readFileSync as readFileSync10,
|
|
12895
|
-
realpathSync as
|
|
12945
|
+
realpathSync as realpathSync5,
|
|
12896
12946
|
rmSync as rmSync10,
|
|
12897
12947
|
writeFileSync as writeFileSync7
|
|
12898
12948
|
} from "fs";
|
|
12899
12949
|
import { createRequire as createRequire2 } from "module";
|
|
12900
12950
|
import { isIP } from "net";
|
|
12901
12951
|
import { homedir as homedir6, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
|
|
12902
|
-
import { dirname as
|
|
12903
|
-
import { fileURLToPath as
|
|
12952
|
+
import { dirname as dirname10, join as join22, sep as sep4 } from "path";
|
|
12953
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
12904
12954
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
12905
12955
|
function accountHome() {
|
|
12906
12956
|
try {
|
|
@@ -12912,9 +12962,9 @@ function accountHome() {
|
|
|
12912
12962
|
var STORE_WALK_TIMEOUT_MS = 6e4;
|
|
12913
12963
|
var STORE_WALK_MAX_AGE_MS = 5 * 6e4;
|
|
12914
12964
|
function storeWalkRoots(homes, cabaneDir2) {
|
|
12915
|
-
const companionHome =
|
|
12916
|
-
const parent =
|
|
12917
|
-
const beside = homes.includes(companionHome) ||
|
|
12965
|
+
const companionHome = dirname10(cabaneDir2);
|
|
12966
|
+
const parent = dirname10(companionHome);
|
|
12967
|
+
const beside = homes.includes(companionHome) || dirname10(parent) === parent ? [] : [parent];
|
|
12918
12968
|
const candidates = [.../* @__PURE__ */ new Set([...homes, ...beside])];
|
|
12919
12969
|
return candidates.filter(
|
|
12920
12970
|
(root) => !candidates.some((other) => other !== root && isAtOrUnder(root, other))
|
|
@@ -13109,7 +13159,7 @@ import {
|
|
|
13109
13159
|
rmSync as rmSync11,
|
|
13110
13160
|
writeFileSync as writeFileSync8
|
|
13111
13161
|
} from "fs";
|
|
13112
|
-
import { join as
|
|
13162
|
+
import { join as join23 } from "path";
|
|
13113
13163
|
var MAX_ENTRIES = 2e3;
|
|
13114
13164
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
13115
13165
|
var Outbox = class {
|
|
@@ -13122,10 +13172,10 @@ var Outbox = class {
|
|
|
13122
13172
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
13123
13173
|
// cases route writes at the right tmpdir.
|
|
13124
13174
|
dir() {
|
|
13125
|
-
return
|
|
13175
|
+
return join23(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
13126
13176
|
}
|
|
13127
13177
|
fileFor(turnId, seq) {
|
|
13128
|
-
return
|
|
13178
|
+
return join23(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
13129
13179
|
}
|
|
13130
13180
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
13131
13181
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -13167,7 +13217,7 @@ var Outbox = class {
|
|
|
13167
13217
|
const entries = [];
|
|
13168
13218
|
for (const name of names) {
|
|
13169
13219
|
if (!name.endsWith(".json")) continue;
|
|
13170
|
-
const full =
|
|
13220
|
+
const full = join23(dir2, name);
|
|
13171
13221
|
try {
|
|
13172
13222
|
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
13173
13223
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -13391,19 +13441,19 @@ var SseSubscriber = class {
|
|
|
13391
13441
|
// src/updater.ts
|
|
13392
13442
|
import { spawn as spawn8 } from "child_process";
|
|
13393
13443
|
import { copyFileSync as copyFileSync3, existsSync as existsSync17, mkdirSync as mkdirSync16, renameSync as renameSync11, rmSync as rmSync14 } from "fs";
|
|
13394
|
-
import { dirname as
|
|
13444
|
+
import { dirname as dirname14, join as join27 } from "path";
|
|
13395
13445
|
|
|
13396
13446
|
// src/boot-rule.ts
|
|
13397
13447
|
import {
|
|
13398
13448
|
mkdirSync as mkdirSync13,
|
|
13399
13449
|
readFileSync as readFileSync12,
|
|
13400
|
-
realpathSync as
|
|
13450
|
+
realpathSync as realpathSync6,
|
|
13401
13451
|
renameSync as renameSync8,
|
|
13402
13452
|
rmSync as rmSync12,
|
|
13403
13453
|
symlinkSync,
|
|
13404
13454
|
writeFileSync as writeFileSync9
|
|
13405
13455
|
} from "fs";
|
|
13406
|
-
import { dirname as
|
|
13456
|
+
import { dirname as dirname11, join as join24 } from "path";
|
|
13407
13457
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
13408
13458
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
13409
13459
|
function superseded(pending, ...running) {
|
|
@@ -13431,31 +13481,31 @@ import {
|
|
|
13431
13481
|
mkdirSync as mkdirSync14,
|
|
13432
13482
|
readFileSync as readFileSync13,
|
|
13433
13483
|
readdirSync as readdirSync6,
|
|
13434
|
-
realpathSync as
|
|
13484
|
+
realpathSync as realpathSync7,
|
|
13435
13485
|
renameSync as renameSync9,
|
|
13436
13486
|
rmSync as rmSync13,
|
|
13437
13487
|
symlinkSync as symlinkSync2
|
|
13438
13488
|
} from "fs";
|
|
13439
|
-
import { dirname as
|
|
13440
|
-
import { fileURLToPath as
|
|
13489
|
+
import { dirname as dirname12, join as join25 } from "path";
|
|
13490
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
13441
13491
|
function versionsDir() {
|
|
13442
|
-
return
|
|
13492
|
+
return join25(cabaneDir(), "versions");
|
|
13443
13493
|
}
|
|
13444
13494
|
function currentLink() {
|
|
13445
|
-
return
|
|
13495
|
+
return join25(cabaneDir(), "current");
|
|
13446
13496
|
}
|
|
13447
13497
|
function bootGuardPath() {
|
|
13448
|
-
return
|
|
13498
|
+
return join25(cabaneDir(), "boot-guard.mjs");
|
|
13449
13499
|
}
|
|
13450
13500
|
function versionPackageRoot(version) {
|
|
13451
|
-
return
|
|
13501
|
+
return join25(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
13452
13502
|
}
|
|
13453
13503
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
13454
|
-
return
|
|
13504
|
+
return dirname12(dirname12(fileURLToPath7(moduleUrl)));
|
|
13455
13505
|
}
|
|
13456
13506
|
function packageVersion(root) {
|
|
13457
13507
|
try {
|
|
13458
|
-
const pkg2 = JSON.parse(readFileSync13(
|
|
13508
|
+
const pkg2 = JSON.parse(readFileSync13(join25(root, "package.json"), "utf8"));
|
|
13459
13509
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
13460
13510
|
} catch {
|
|
13461
13511
|
return null;
|
|
@@ -13463,15 +13513,15 @@ function packageVersion(root) {
|
|
|
13463
13513
|
}
|
|
13464
13514
|
function currentRoot() {
|
|
13465
13515
|
try {
|
|
13466
|
-
const root =
|
|
13467
|
-
return existsSync16(
|
|
13516
|
+
const root = realpathSync7(currentLink());
|
|
13517
|
+
return existsSync16(join25(root, "dist", "cli.js")) ? root : null;
|
|
13468
13518
|
} catch {
|
|
13469
13519
|
return null;
|
|
13470
13520
|
}
|
|
13471
13521
|
}
|
|
13472
13522
|
function pointCurrentAt(root) {
|
|
13473
13523
|
const link = currentLink();
|
|
13474
|
-
mkdirSync14(
|
|
13524
|
+
mkdirSync14(dirname12(link), { recursive: true });
|
|
13475
13525
|
const fresh = `${link}.${process.pid}.${Date.now()}`;
|
|
13476
13526
|
symlinkSync2(root, fresh);
|
|
13477
13527
|
try {
|
|
@@ -13487,7 +13537,7 @@ function pruneVersions(keep) {
|
|
|
13487
13537
|
const removed = [];
|
|
13488
13538
|
for (const name of readdirSync6(dir2)) {
|
|
13489
13539
|
if (keep.includes(name)) continue;
|
|
13490
|
-
rmSync13(
|
|
13540
|
+
rmSync13(join25(dir2, name), { recursive: true, force: true });
|
|
13491
13541
|
removed.push(name);
|
|
13492
13542
|
}
|
|
13493
13543
|
return removed;
|
|
@@ -13506,7 +13556,7 @@ function compareVersions(a, b) {
|
|
|
13506
13556
|
|
|
13507
13557
|
// src/update-state.ts
|
|
13508
13558
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync14, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
13509
|
-
import { dirname as
|
|
13559
|
+
import { dirname as dirname13, join as join26 } from "path";
|
|
13510
13560
|
import { z as z18 } from "zod";
|
|
13511
13561
|
var resultSchema = z18.object({
|
|
13512
13562
|
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -13530,7 +13580,7 @@ var stateSchema = z18.object({
|
|
|
13530
13580
|
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
13531
13581
|
});
|
|
13532
13582
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13533
|
-
return
|
|
13583
|
+
return join26(dir2, "update.json");
|
|
13534
13584
|
}
|
|
13535
13585
|
function readUpdateState(path = updateStatePath()) {
|
|
13536
13586
|
try {
|
|
@@ -13541,7 +13591,7 @@ function readUpdateState(path = updateStatePath()) {
|
|
|
13541
13591
|
}
|
|
13542
13592
|
}
|
|
13543
13593
|
function writeUpdateState(state, path = updateStatePath()) {
|
|
13544
|
-
mkdirSync15(
|
|
13594
|
+
mkdirSync15(dirname13(path), { recursive: true });
|
|
13545
13595
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
13546
13596
|
writeFileSync10(tmp, `${JSON.stringify(state, null, 2)}
|
|
13547
13597
|
`, { mode: 384 });
|
|
@@ -13579,10 +13629,10 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
13579
13629
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
13580
13630
|
}
|
|
13581
13631
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
13582
|
-
const built =
|
|
13632
|
+
const built = join27(root, "dist", "boot-guard.js");
|
|
13583
13633
|
if (!existsSync17(built)) return false;
|
|
13584
13634
|
const target = bootGuardPath();
|
|
13585
|
-
mkdirSync16(
|
|
13635
|
+
mkdirSync16(dirname14(target), { recursive: true });
|
|
13586
13636
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
13587
13637
|
copyFileSync3(built, tmp);
|
|
13588
13638
|
renameSync11(tmp, target);
|
|
@@ -13619,17 +13669,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
13619
13669
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
13620
13670
|
const run = deps.run ?? runProcess;
|
|
13621
13671
|
const node = deps.execPath ?? process.execPath;
|
|
13622
|
-
const target =
|
|
13672
|
+
const target = join27(versionsDir(), version);
|
|
13623
13673
|
const placed = versionPackageRoot(version);
|
|
13624
13674
|
if (existsSync17(placed)) {
|
|
13625
13675
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
13626
13676
|
return { ok: true, root: placed };
|
|
13627
13677
|
}
|
|
13628
|
-
const npm =
|
|
13678
|
+
const npm = join27(dirname14(node), "npm");
|
|
13629
13679
|
if (!existsSync17(npm))
|
|
13630
|
-
return { ok: false, reason: `there is no npm beside Node at ${
|
|
13680
|
+
return { ok: false, reason: `there is no npm beside Node at ${dirname14(node)}` };
|
|
13631
13681
|
mkdirSync16(versionsDir(), { recursive: true });
|
|
13632
|
-
const staging =
|
|
13682
|
+
const staging = join27(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
13633
13683
|
rmSync14(staging, { recursive: true, force: true });
|
|
13634
13684
|
const installed = await run(
|
|
13635
13685
|
npm,
|
|
@@ -13646,7 +13696,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13646
13696
|
],
|
|
13647
13697
|
{
|
|
13648
13698
|
// npm's install scripts run on this Node, not whichever `node` is first on PATH.
|
|
13649
|
-
env: { ...process.env, PATH: `${
|
|
13699
|
+
env: { ...process.env, PATH: `${dirname14(node)}:${process.env.PATH ?? ""}` },
|
|
13650
13700
|
timeoutMs: 15 * 6e4
|
|
13651
13701
|
}
|
|
13652
13702
|
);
|
|
@@ -13658,7 +13708,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13658
13708
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
13659
13709
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
13660
13710
|
}
|
|
13661
|
-
const root =
|
|
13711
|
+
const root = join27(staging, "node_modules", "@cabane", "companion");
|
|
13662
13712
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
13663
13713
|
if (verdict) {
|
|
13664
13714
|
rmSync14(staging, { recursive: true, force: true });
|
|
@@ -13670,7 +13720,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13670
13720
|
}
|
|
13671
13721
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
13672
13722
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
13673
|
-
const answered = await deps.run(deps.execPath, [
|
|
13723
|
+
const answered = await deps.run(deps.execPath, [join27(root, "dist", "bin.js"), "--version"], {
|
|
13674
13724
|
timeoutMs: 6e4,
|
|
13675
13725
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
13676
13726
|
});
|
|
@@ -13678,7 +13728,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
13678
13728
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
13679
13729
|
}
|
|
13680
13730
|
if (harnesses.length === 0) return null;
|
|
13681
|
-
const probed = await deps.run(deps.execPath, [
|
|
13731
|
+
const probed = await deps.run(deps.execPath, [join27(root, "dist", "postinstall.js"), "--json"], {
|
|
13682
13732
|
timeoutMs: 6e4
|
|
13683
13733
|
});
|
|
13684
13734
|
let resolutions = {};
|
|
@@ -14259,7 +14309,7 @@ var CompanionSupervisor = class {
|
|
|
14259
14309
|
this.versionSkewWarned = true;
|
|
14260
14310
|
this.log.warn(
|
|
14261
14311
|
{ companionVersion: COMPANION_VERSION, serverVersion },
|
|
14262
|
-
|
|
14312
|
+
`This companion and Cabane are on different versions. If turns fail, update with ${cabane("update")}`
|
|
14263
14313
|
);
|
|
14264
14314
|
}
|
|
14265
14315
|
// Pull assignments and reconcile the live runner set against them. Re-entrancy
|
|
@@ -14488,7 +14538,8 @@ var CompanionSupervisor = class {
|
|
|
14488
14538
|
bundled: this.bundledStatus("claude-code")
|
|
14489
14539
|
},
|
|
14490
14540
|
codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") },
|
|
14491
|
-
install: companionInstall()
|
|
14541
|
+
install: companionInstall(),
|
|
14542
|
+
managed: repairRoute() === "managed"
|
|
14492
14543
|
});
|
|
14493
14544
|
}
|
|
14494
14545
|
// A missing binary is reported ONCE, and again only when the answer CHANGES,
|
|
@@ -15443,9 +15494,9 @@ function handleUncaught(log, err, origin) {
|
|
|
15443
15494
|
|
|
15444
15495
|
// src/crash-marker.ts
|
|
15445
15496
|
import { existsSync as existsSync18, mkdirSync as mkdirSync17, readFileSync as readFileSync15, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "fs";
|
|
15446
|
-
import { join as
|
|
15497
|
+
import { join as join28 } from "path";
|
|
15447
15498
|
function crashMarkerPath() {
|
|
15448
|
-
return
|
|
15499
|
+
return join28(cabaneDir(), "last-error.json");
|
|
15449
15500
|
}
|
|
15450
15501
|
function recordCrash(rec) {
|
|
15451
15502
|
try {
|