@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/cli.js
CHANGED
|
@@ -9,166 +9,11 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/
|
|
13
|
-
import { realpathSync
|
|
14
|
-
import { createRequire } from "module";
|
|
12
|
+
// src/install-kind.ts
|
|
13
|
+
import { realpathSync } from "fs";
|
|
15
14
|
import { homedir } from "os";
|
|
16
15
|
import { dirname, join, sep } from "path";
|
|
17
16
|
import { fileURLToPath } from "url";
|
|
18
|
-
function nodeResolveSelf(specifier) {
|
|
19
|
-
const here = fileURLToPath(import.meta.url);
|
|
20
|
-
if (typeof import.meta.resolve === "function") {
|
|
21
|
-
try {
|
|
22
|
-
return fileURLToPath(import.meta.resolve(specifier));
|
|
23
|
-
} catch {
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
return createRequire(here).resolve(specifier);
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
return walkNodeModules(specifier, here);
|
|
31
|
-
}
|
|
32
|
-
function walkNodeModules(pkg2, fromPath) {
|
|
33
|
-
let dir2 = dirname(fromPath);
|
|
34
|
-
for (; ; ) {
|
|
35
|
-
const manifest = join(dir2, "node_modules", pkg2, "package.json");
|
|
36
|
-
if (nodeIsFile(manifest)) {
|
|
37
|
-
try {
|
|
38
|
-
return realpathSync(manifest);
|
|
39
|
-
} catch {
|
|
40
|
-
return manifest;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const parent = dirname(dir2);
|
|
44
|
-
if (parent === dir2) return null;
|
|
45
|
-
dir2 = parent;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function nodeResolve(specifier, fromPath) {
|
|
49
|
-
try {
|
|
50
|
-
return createRequire(fromPath).resolve(specifier);
|
|
51
|
-
} catch {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function nodeIsFile(path) {
|
|
56
|
-
try {
|
|
57
|
-
return statSync(path).isFile();
|
|
58
|
-
} catch {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function detectPreferMusl(platform = process.platform) {
|
|
63
|
-
if (platform !== "linux") return false;
|
|
64
|
-
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
65
|
-
const header = report?.header;
|
|
66
|
-
return report != null && header?.glibcVersionRuntime === void 0;
|
|
67
|
-
}
|
|
68
|
-
function defaultResolveEnv() {
|
|
69
|
-
return {
|
|
70
|
-
platform: process.platform,
|
|
71
|
-
arch: process.arch,
|
|
72
|
-
preferMusl: detectPreferMusl(),
|
|
73
|
-
// Both resolutions start from this module, which tsup bundles into
|
|
74
|
-
// `dist/cli.js` and `dist/postinstall.js` — both at the package's `dist/`
|
|
75
|
-
// root — so one environment serves the runtime checks and the install
|
|
76
|
-
// verifier alike.
|
|
77
|
-
resolveSelf: nodeResolveSelf,
|
|
78
|
-
resolve: nodeResolve,
|
|
79
|
-
isFile: nodeIsFile
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function claudePlatformPackages(platform, arch, preferMusl) {
|
|
83
|
-
if (platform === "android") return [`${CLAUDE_SDK}-linux-${arch}-android`];
|
|
84
|
-
if (platform === "linux") {
|
|
85
|
-
return preferMusl ? [`${CLAUDE_SDK}-linux-${arch}-musl`, `${CLAUDE_SDK}-linux-${arch}`] : [`${CLAUDE_SDK}-linux-${arch}`, `${CLAUDE_SDK}-linux-${arch}-musl`];
|
|
86
|
-
}
|
|
87
|
-
return [`${CLAUDE_SDK}-${platform}-${arch}`];
|
|
88
|
-
}
|
|
89
|
-
function resolveClaude(env) {
|
|
90
|
-
const candidates = claudePlatformPackages(env.platform, env.arch, env.preferMusl);
|
|
91
|
-
const base = {
|
|
92
|
-
runtime: "claude-code",
|
|
93
|
-
packageName: candidates[0] ?? null,
|
|
94
|
-
platform: env.platform,
|
|
95
|
-
arch: env.arch
|
|
96
|
-
};
|
|
97
|
-
const sdkEntry = env.resolveSelf(CLAUDE_SDK);
|
|
98
|
-
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
99
|
-
let sawPackage = null;
|
|
100
|
-
for (const pkg2 of candidates) {
|
|
101
|
-
const manifest = env.resolve(`${pkg2}/package.json`, sdkEntry);
|
|
102
|
-
if (!manifest) continue;
|
|
103
|
-
sawPackage ??= pkg2;
|
|
104
|
-
const exe = join(dirname(manifest), env.platform === "win32" ? "claude.exe" : "claude");
|
|
105
|
-
if (env.isFile(exe)) {
|
|
106
|
-
return { ...base, packageName: pkg2, status: "present", path: exe };
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return sawPackage ? { ...base, packageName: sawPackage, status: "executable_missing", path: null } : { ...base, status: "platform_package_missing", path: null };
|
|
110
|
-
}
|
|
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
|
-
function resolveCodex(env) {
|
|
130
|
-
const triple = codexTargetTriple(env.platform, env.arch);
|
|
131
|
-
const pkg2 = triple ? CODEX_PACKAGE_BY_TRIPLE[triple] : void 0;
|
|
132
|
-
const base = {
|
|
133
|
-
runtime: "codex",
|
|
134
|
-
packageName: pkg2 ?? null,
|
|
135
|
-
platform: env.platform,
|
|
136
|
-
arch: env.arch
|
|
137
|
-
};
|
|
138
|
-
if (!triple || !pkg2) return { ...base, status: "platform_package_missing", path: null };
|
|
139
|
-
const sdkEntry = env.resolveSelf(CODEX_SDK);
|
|
140
|
-
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
141
|
-
const cliManifest = env.resolve(`${CODEX_CLI}/package.json`, sdkEntry);
|
|
142
|
-
if (!cliManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
143
|
-
const platformManifest = env.resolve(`${pkg2}/package.json`, cliManifest);
|
|
144
|
-
if (!platformManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
145
|
-
const packageRoot = join(dirname(platformManifest), "vendor", triple);
|
|
146
|
-
const exeName = env.platform === "win32" ? "codex.exe" : "codex";
|
|
147
|
-
const current = join(packageRoot, "bin", exeName);
|
|
148
|
-
if (env.isFile(current) && env.isFile(join(packageRoot, "codex-package.json"))) {
|
|
149
|
-
return { ...base, status: "present", path: current };
|
|
150
|
-
}
|
|
151
|
-
const legacy = join(packageRoot, "codex", exeName);
|
|
152
|
-
if (env.isFile(legacy)) return { ...base, status: "present", path: legacy };
|
|
153
|
-
return { ...base, status: "executable_missing", path: null };
|
|
154
|
-
}
|
|
155
|
-
function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
156
|
-
try {
|
|
157
|
-
return runtime === "codex" ? resolveCodex(env) : resolveClaude(env);
|
|
158
|
-
} catch {
|
|
159
|
-
return {
|
|
160
|
-
runtime,
|
|
161
|
-
status: "platform_package_missing",
|
|
162
|
-
path: null,
|
|
163
|
-
packageName: null,
|
|
164
|
-
platform: env.platform,
|
|
165
|
-
arch: env.arch
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function missingNoun(status2) {
|
|
170
|
-
return status2 === "executable_missing" ? "executable" : "platform package";
|
|
171
|
-
}
|
|
172
17
|
function companionInstall(execPath = process.execPath, home = homedir()) {
|
|
173
18
|
const real = (path) => {
|
|
174
19
|
try {
|
|
@@ -180,47 +25,21 @@ function companionInstall(execPath = process.execPath, home = homedir()) {
|
|
|
180
25
|
const scriptNode = join(real(home), ".cabane", "node") + sep;
|
|
181
26
|
return real(execPath).startsWith(scriptNode) ? "script" : "npm";
|
|
182
27
|
}
|
|
183
|
-
function
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const label = HARNESS_LABEL[r.runtime];
|
|
191
|
-
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.`;
|
|
28
|
+
function repairRoute(packageRoot = ownRoot(), install = companionInstall()) {
|
|
29
|
+
let root = packageRoot;
|
|
30
|
+
try {
|
|
31
|
+
root = realpathSync(packageRoot);
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
return root.includes(`${sep}.cabane${sep}versions${sep}`) ? "managed" : install;
|
|
192
35
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
|
|
196
|
-
return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status2)} is missing. ${repairAction(install)} Then restart the companion.`;
|
|
36
|
+
function ownRoot() {
|
|
37
|
+
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
197
38
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD, INSTALL_SCRIPT_COMMAND;
|
|
202
|
-
var init_bundled_binaries = __esm({
|
|
203
|
-
"src/bundled-binaries.ts"() {
|
|
39
|
+
var INSTALL_SCRIPT_COMMAND;
|
|
40
|
+
var init_install_kind = __esm({
|
|
41
|
+
"src/install-kind.ts"() {
|
|
204
42
|
"use strict";
|
|
205
|
-
CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
206
|
-
CODEX_SDK = "@openai/codex-sdk";
|
|
207
|
-
CODEX_CLI = "@openai/codex";
|
|
208
|
-
CODEX_PACKAGE_BY_TRIPLE = {
|
|
209
|
-
"x86_64-unknown-linux-musl": `${CODEX_CLI}-linux-x64`,
|
|
210
|
-
"aarch64-unknown-linux-musl": `${CODEX_CLI}-linux-arm64`,
|
|
211
|
-
"x86_64-apple-darwin": `${CODEX_CLI}-darwin-x64`,
|
|
212
|
-
"aarch64-apple-darwin": `${CODEX_CLI}-darwin-arm64`,
|
|
213
|
-
"x86_64-pc-windows-msvc": `${CODEX_CLI}-win32-x64`,
|
|
214
|
-
"aarch64-pc-windows-msvc": `${CODEX_CLI}-win32-arm64`
|
|
215
|
-
};
|
|
216
|
-
HARNESS_LABEL = {
|
|
217
|
-
"claude-code": "Claude Code",
|
|
218
|
-
codex: "Codex"
|
|
219
|
-
};
|
|
220
|
-
HARNESS_INTENT_WORD = {
|
|
221
|
-
"claude-code": "connected",
|
|
222
|
-
codex: "enabled"
|
|
223
|
-
};
|
|
224
43
|
INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
|
|
225
44
|
}
|
|
226
45
|
});
|
|
@@ -358,16 +177,37 @@ var init_term = __esm({
|
|
|
358
177
|
// src/cabane-command.ts
|
|
359
178
|
import { accessSync, constants, realpathSync as realpathSync2 } from "fs";
|
|
360
179
|
import { homedir as homedir3 } from "os";
|
|
361
|
-
import { delimiter, join as join2, sep as sep2 } from "path";
|
|
180
|
+
import { basename, delimiter, dirname as dirname2, join as join2, sep as sep2 } from "path";
|
|
181
|
+
function invokedCommand(argv1) {
|
|
182
|
+
if (!argv1 || argv1.includes(VERSIONS_SEGMENT)) return null;
|
|
183
|
+
const name = basename(argv1);
|
|
184
|
+
if (name === "cabane" || name === "cabane-companion") return join2(dirname2(argv1), "cabane");
|
|
185
|
+
return npmBinFor(argv1);
|
|
186
|
+
}
|
|
187
|
+
function rememberInvokedCommand(env = process.env, argv1 = process.argv[1]) {
|
|
188
|
+
if (env[INVOKED_COMMAND_ENV]) return;
|
|
189
|
+
const invoked = invokedCommand(argv1);
|
|
190
|
+
if (invoked) env[INVOKED_COMMAND_ENV] = invoked;
|
|
191
|
+
}
|
|
362
192
|
function installedCommandPath(deps = {}) {
|
|
363
193
|
const install = deps.install ?? companionInstall();
|
|
364
194
|
const home = deps.home ?? homedir3();
|
|
365
195
|
if (install === "script") return join2(home, ".cabane", "bin", "cabane");
|
|
366
196
|
const entry = deps.cliEntry ?? companionCliEntry();
|
|
197
|
+
const own = npmBinFor(entry);
|
|
198
|
+
if (own) return own;
|
|
199
|
+
if (!entry.includes(VERSIONS_SEGMENT)) return null;
|
|
200
|
+
const isExecutable = deps.isExecutable ?? executable;
|
|
201
|
+
const invoked = "invoked" in deps ? deps.invoked : process.env[INVOKED_COMMAND_ENV];
|
|
202
|
+
if (invoked && isExecutable(invoked)) return invoked;
|
|
203
|
+
const besideNode = join2(dirname2(dirname2(deps.execPath ?? process.execPath)), "bin", "cabane");
|
|
204
|
+
return isExecutable(besideNode) ? besideNode : null;
|
|
205
|
+
}
|
|
206
|
+
function npmBinFor(path) {
|
|
367
207
|
const marker = `${sep2}lib${sep2}node_modules${sep2}@cabane${sep2}companion${sep2}`;
|
|
368
|
-
const at =
|
|
208
|
+
const at = path.indexOf(marker);
|
|
369
209
|
if (at < 0) return null;
|
|
370
|
-
return join2(
|
|
210
|
+
return join2(path.slice(0, at), "bin", "cabane");
|
|
371
211
|
}
|
|
372
212
|
function cabaneCommand(deps = {}) {
|
|
373
213
|
const own = installedCommandPath(deps);
|
|
@@ -383,6 +223,9 @@ function cabaneCommand(deps = {}) {
|
|
|
383
223
|
function cabane(rest, deps) {
|
|
384
224
|
return `${cabaneCommand(deps)} ${rest}`;
|
|
385
225
|
}
|
|
226
|
+
function mayNeedUpdate(deps) {
|
|
227
|
+
return `This companion may need an update: run ${cabane("update", deps)}`;
|
|
228
|
+
}
|
|
386
229
|
function firstOnPath(name, path, isExecutable = executable) {
|
|
387
230
|
for (const dir2 of (path ?? "").split(delimiter)) {
|
|
388
231
|
if (!dir2) continue;
|
|
@@ -406,12 +249,15 @@ function safeRealpath(path) {
|
|
|
406
249
|
return null;
|
|
407
250
|
}
|
|
408
251
|
}
|
|
252
|
+
var INVOKED_COMMAND_ENV, VERSIONS_SEGMENT;
|
|
409
253
|
var init_cabane_command = __esm({
|
|
410
254
|
"src/cabane-command.ts"() {
|
|
411
255
|
"use strict";
|
|
412
|
-
|
|
256
|
+
init_install_kind();
|
|
413
257
|
init_cli_entry();
|
|
414
258
|
init_term();
|
|
259
|
+
INVOKED_COMMAND_ENV = "CABANE_INVOKED_COMMAND";
|
|
260
|
+
VERSIONS_SEGMENT = `${sep2}.cabane${sep2}versions${sep2}`;
|
|
415
261
|
}
|
|
416
262
|
});
|
|
417
263
|
|
|
@@ -641,7 +487,7 @@ import {
|
|
|
641
487
|
existsSync as existsSync2
|
|
642
488
|
} from "fs";
|
|
643
489
|
import { homedir as homedir4, userInfo } from "os";
|
|
644
|
-
import { dirname as
|
|
490
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
645
491
|
import { z as z2 } from "zod";
|
|
646
492
|
function realAccountHome() {
|
|
647
493
|
if (realHomeCache === void 0) {
|
|
@@ -835,7 +681,7 @@ function loadConfigTolerant() {
|
|
|
835
681
|
}
|
|
836
682
|
function saveConfig(cfg) {
|
|
837
683
|
const path = configPath();
|
|
838
|
-
mkdirSync(
|
|
684
|
+
mkdirSync(dirname3(path), { recursive: true });
|
|
839
685
|
try {
|
|
840
686
|
chmodSync(cabaneDir(), 448);
|
|
841
687
|
} catch {
|
|
@@ -1032,6 +878,226 @@ var init_config = __esm({
|
|
|
1032
878
|
}
|
|
1033
879
|
});
|
|
1034
880
|
|
|
881
|
+
// src/bundled-binaries.ts
|
|
882
|
+
import { realpathSync as realpathSync3, statSync } from "fs";
|
|
883
|
+
import { createRequire } from "module";
|
|
884
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
885
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
886
|
+
function nodeResolveSelf(specifier) {
|
|
887
|
+
const here = fileURLToPath3(import.meta.url);
|
|
888
|
+
if (typeof import.meta.resolve === "function") {
|
|
889
|
+
try {
|
|
890
|
+
return fileURLToPath3(import.meta.resolve(specifier));
|
|
891
|
+
} catch {
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
try {
|
|
895
|
+
return createRequire(here).resolve(specifier);
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
return walkNodeModules(specifier, here);
|
|
899
|
+
}
|
|
900
|
+
function walkNodeModules(pkg2, fromPath) {
|
|
901
|
+
let dir2 = dirname4(fromPath);
|
|
902
|
+
for (; ; ) {
|
|
903
|
+
const manifest = join4(dir2, "node_modules", pkg2, "package.json");
|
|
904
|
+
if (nodeIsFile(manifest)) {
|
|
905
|
+
try {
|
|
906
|
+
return realpathSync3(manifest);
|
|
907
|
+
} catch {
|
|
908
|
+
return manifest;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const parent = dirname4(dir2);
|
|
912
|
+
if (parent === dir2) return null;
|
|
913
|
+
dir2 = parent;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function nodeResolve(specifier, fromPath) {
|
|
917
|
+
try {
|
|
918
|
+
return createRequire(fromPath).resolve(specifier);
|
|
919
|
+
} catch {
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function nodeIsFile(path) {
|
|
924
|
+
try {
|
|
925
|
+
return statSync(path).isFile();
|
|
926
|
+
} catch {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function detectPreferMusl(platform = process.platform) {
|
|
931
|
+
if (platform !== "linux") return false;
|
|
932
|
+
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
933
|
+
const header = report?.header;
|
|
934
|
+
return report != null && header?.glibcVersionRuntime === void 0;
|
|
935
|
+
}
|
|
936
|
+
function defaultResolveEnv() {
|
|
937
|
+
return {
|
|
938
|
+
platform: process.platform,
|
|
939
|
+
arch: process.arch,
|
|
940
|
+
preferMusl: detectPreferMusl(),
|
|
941
|
+
// Both resolutions start from this module, which tsup bundles into
|
|
942
|
+
// `dist/cli.js` and `dist/postinstall.js` — both at the package's `dist/`
|
|
943
|
+
// root — so one environment serves the runtime checks and the install
|
|
944
|
+
// verifier alike.
|
|
945
|
+
resolveSelf: nodeResolveSelf,
|
|
946
|
+
resolve: nodeResolve,
|
|
947
|
+
isFile: nodeIsFile
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function claudePlatformPackages(platform, arch, preferMusl) {
|
|
951
|
+
if (platform === "android") return [`${CLAUDE_SDK}-linux-${arch}-android`];
|
|
952
|
+
if (platform === "linux") {
|
|
953
|
+
return preferMusl ? [`${CLAUDE_SDK}-linux-${arch}-musl`, `${CLAUDE_SDK}-linux-${arch}`] : [`${CLAUDE_SDK}-linux-${arch}`, `${CLAUDE_SDK}-linux-${arch}-musl`];
|
|
954
|
+
}
|
|
955
|
+
return [`${CLAUDE_SDK}-${platform}-${arch}`];
|
|
956
|
+
}
|
|
957
|
+
function resolveClaude(env) {
|
|
958
|
+
const candidates = claudePlatformPackages(env.platform, env.arch, env.preferMusl);
|
|
959
|
+
const base = {
|
|
960
|
+
runtime: "claude-code",
|
|
961
|
+
packageName: candidates[0] ?? null,
|
|
962
|
+
platform: env.platform,
|
|
963
|
+
arch: env.arch
|
|
964
|
+
};
|
|
965
|
+
const sdkEntry = env.resolveSelf(CLAUDE_SDK);
|
|
966
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
967
|
+
let sawPackage = null;
|
|
968
|
+
for (const pkg2 of candidates) {
|
|
969
|
+
const manifest = env.resolve(`${pkg2}/package.json`, sdkEntry);
|
|
970
|
+
if (!manifest) continue;
|
|
971
|
+
sawPackage ??= pkg2;
|
|
972
|
+
const exe = join4(dirname4(manifest), env.platform === "win32" ? "claude.exe" : "claude");
|
|
973
|
+
if (env.isFile(exe)) {
|
|
974
|
+
return { ...base, packageName: pkg2, status: "present", path: exe };
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return sawPackage ? { ...base, packageName: sawPackage, status: "executable_missing", path: null } : { ...base, status: "platform_package_missing", path: null };
|
|
978
|
+
}
|
|
979
|
+
function codexTargetTriple(platform, arch) {
|
|
980
|
+
if (platform === "linux" || platform === "android") {
|
|
981
|
+
if (arch === "x64") return "x86_64-unknown-linux-musl";
|
|
982
|
+
if (arch === "arm64") return "aarch64-unknown-linux-musl";
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
if (platform === "darwin") {
|
|
986
|
+
if (arch === "x64") return "x86_64-apple-darwin";
|
|
987
|
+
if (arch === "arm64") return "aarch64-apple-darwin";
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
if (platform === "win32") {
|
|
991
|
+
if (arch === "x64") return "x86_64-pc-windows-msvc";
|
|
992
|
+
if (arch === "arm64") return "aarch64-pc-windows-msvc";
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
function codexPlatformPackage(platform, arch) {
|
|
998
|
+
const triple = codexTargetTriple(platform, arch);
|
|
999
|
+
return triple ? CODEX_PACKAGE_BY_TRIPLE[triple] ?? null : null;
|
|
1000
|
+
}
|
|
1001
|
+
function resolveCodex(env) {
|
|
1002
|
+
const triple = codexTargetTriple(env.platform, env.arch);
|
|
1003
|
+
const pkg2 = triple ? CODEX_PACKAGE_BY_TRIPLE[triple] : void 0;
|
|
1004
|
+
const base = {
|
|
1005
|
+
runtime: "codex",
|
|
1006
|
+
packageName: pkg2 ?? null,
|
|
1007
|
+
platform: env.platform,
|
|
1008
|
+
arch: env.arch
|
|
1009
|
+
};
|
|
1010
|
+
if (!triple || !pkg2) return { ...base, status: "platform_package_missing", path: null };
|
|
1011
|
+
const sdkEntry = env.resolveSelf(CODEX_SDK);
|
|
1012
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
1013
|
+
const cliManifest = env.resolve(`${CODEX_CLI}/package.json`, sdkEntry);
|
|
1014
|
+
if (!cliManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
1015
|
+
const platformManifest = env.resolve(`${pkg2}/package.json`, cliManifest);
|
|
1016
|
+
if (!platformManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
1017
|
+
const packageRoot = join4(dirname4(platformManifest), "vendor", triple);
|
|
1018
|
+
const exeName = env.platform === "win32" ? "codex.exe" : "codex";
|
|
1019
|
+
const current = join4(packageRoot, "bin", exeName);
|
|
1020
|
+
if (env.isFile(current) && env.isFile(join4(packageRoot, "codex-package.json"))) {
|
|
1021
|
+
return { ...base, status: "present", path: current };
|
|
1022
|
+
}
|
|
1023
|
+
const legacy = join4(packageRoot, "codex", exeName);
|
|
1024
|
+
if (env.isFile(legacy)) return { ...base, status: "present", path: legacy };
|
|
1025
|
+
return { ...base, status: "executable_missing", path: null };
|
|
1026
|
+
}
|
|
1027
|
+
function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
1028
|
+
try {
|
|
1029
|
+
return runtime === "codex" ? resolveCodex(env) : resolveClaude(env);
|
|
1030
|
+
} catch {
|
|
1031
|
+
return {
|
|
1032
|
+
runtime,
|
|
1033
|
+
status: "platform_package_missing",
|
|
1034
|
+
path: null,
|
|
1035
|
+
packageName: null,
|
|
1036
|
+
platform: env.platform,
|
|
1037
|
+
arch: env.arch
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
function missingNoun(status2) {
|
|
1042
|
+
return status2 === "executable_missing" ? "executable" : "platform package";
|
|
1043
|
+
}
|
|
1044
|
+
function repairCommands(route = repairRoute(), spell = cabane) {
|
|
1045
|
+
if (route === "managed") return [spell("stop"), spell("update")];
|
|
1046
|
+
if (route === "script") return [spell("stop"), INSTALL_SCRIPT_COMMAND];
|
|
1047
|
+
return ["npm i -g @cabane/companion --include=optional"];
|
|
1048
|
+
}
|
|
1049
|
+
function repairAction(route = repairRoute(), spell = cabane) {
|
|
1050
|
+
const [first, ...rest] = repairCommands(route, spell);
|
|
1051
|
+
if (rest.length === 0) return `Reinstall with \`${first}\`.`;
|
|
1052
|
+
return `Run \`${first}\`, then \`${rest.join("`, then `")}\`. ${STOP_WARNING}`;
|
|
1053
|
+
}
|
|
1054
|
+
function repairSteps(route = repairRoute()) {
|
|
1055
|
+
return repairCommands(route).join(", then ");
|
|
1056
|
+
}
|
|
1057
|
+
function repairCost(route = repairRoute()) {
|
|
1058
|
+
return repairCommands(route).length > 1 ? STOP_WARNING : "";
|
|
1059
|
+
}
|
|
1060
|
+
function startupWarning(r) {
|
|
1061
|
+
const label = HARNESS_LABEL[r.runtime];
|
|
1062
|
+
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.`;
|
|
1063
|
+
}
|
|
1064
|
+
function harnessIssueNote(runtime, status2, route = repairRoute(), spell = cabane) {
|
|
1065
|
+
const intent = HARNESS_INTENT_WORD[runtime];
|
|
1066
|
+
const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
|
|
1067
|
+
return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status2)} is missing. ${repairAction(route, spell)} Then restart the companion.`;
|
|
1068
|
+
}
|
|
1069
|
+
function turnIncompleteCopy(runtime) {
|
|
1070
|
+
return `**This device's ${HARNESS_LABEL[runtime]} runtime is incomplete.** ${repairAction()} Restart it, then try again.`;
|
|
1071
|
+
}
|
|
1072
|
+
var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD, STOP_WARNING;
|
|
1073
|
+
var init_bundled_binaries = __esm({
|
|
1074
|
+
"src/bundled-binaries.ts"() {
|
|
1075
|
+
"use strict";
|
|
1076
|
+
init_cabane_command();
|
|
1077
|
+
init_install_kind();
|
|
1078
|
+
CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
1079
|
+
CODEX_SDK = "@openai/codex-sdk";
|
|
1080
|
+
CODEX_CLI = "@openai/codex";
|
|
1081
|
+
CODEX_PACKAGE_BY_TRIPLE = {
|
|
1082
|
+
"x86_64-unknown-linux-musl": `${CODEX_CLI}-linux-x64`,
|
|
1083
|
+
"aarch64-unknown-linux-musl": `${CODEX_CLI}-linux-arm64`,
|
|
1084
|
+
"x86_64-apple-darwin": `${CODEX_CLI}-darwin-x64`,
|
|
1085
|
+
"aarch64-apple-darwin": `${CODEX_CLI}-darwin-arm64`,
|
|
1086
|
+
"x86_64-pc-windows-msvc": `${CODEX_CLI}-win32-x64`,
|
|
1087
|
+
"aarch64-pc-windows-msvc": `${CODEX_CLI}-win32-arm64`
|
|
1088
|
+
};
|
|
1089
|
+
HARNESS_LABEL = {
|
|
1090
|
+
"claude-code": "Claude Code",
|
|
1091
|
+
codex: "Codex"
|
|
1092
|
+
};
|
|
1093
|
+
HARNESS_INTENT_WORD = {
|
|
1094
|
+
"claude-code": "connected",
|
|
1095
|
+
codex: "enabled"
|
|
1096
|
+
};
|
|
1097
|
+
STOP_WARNING = "Stopping ends any turn in progress.";
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1035
1101
|
// packages/agent-runtime/src/failure.ts
|
|
1036
1102
|
import { z as z4 } from "zod";
|
|
1037
1103
|
function encodeFailureReason(failure) {
|
|
@@ -1302,6 +1368,7 @@ var init_api = __esm({
|
|
|
1302
1368
|
"src/api.ts"() {
|
|
1303
1369
|
"use strict";
|
|
1304
1370
|
init_api_error_shape();
|
|
1371
|
+
init_cabane_command();
|
|
1305
1372
|
init_errors();
|
|
1306
1373
|
DEVICE_HEADER = "x-cabane-device";
|
|
1307
1374
|
RETRY_BACKOFF_MS = [250, 750];
|
|
@@ -1436,7 +1503,7 @@ var init_api = __esm({
|
|
|
1436
1503
|
}
|
|
1437
1504
|
this.opts.log?.warn(
|
|
1438
1505
|
{ kind: entry.kind, turnId: entry.turnId, seq: entry.seq, ...apiErrorLogFields(err) },
|
|
1439
|
-
|
|
1506
|
+
`Cabane rejected a saved update; it could not be delivered. ${mayNeedUpdate()}`
|
|
1440
1507
|
);
|
|
1441
1508
|
outbox.remove(entry.turnId, entry.seq);
|
|
1442
1509
|
progressed = true;
|
|
@@ -7503,10 +7570,10 @@ var init_turn_containment = __esm({
|
|
|
7503
7570
|
|
|
7504
7571
|
// src/secrets.ts
|
|
7505
7572
|
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
7506
|
-
import { join as
|
|
7573
|
+
import { join as join13 } from "path";
|
|
7507
7574
|
import { z as z14 } from "zod";
|
|
7508
7575
|
function secretsPath() {
|
|
7509
|
-
return
|
|
7576
|
+
return join13(cabaneDir(), "secrets.json");
|
|
7510
7577
|
}
|
|
7511
7578
|
function loadSecretStore() {
|
|
7512
7579
|
const path = secretsPath();
|
|
@@ -8556,10 +8623,10 @@ var init_build_options = __esm({
|
|
|
8556
8623
|
// src/codex-instructions.ts
|
|
8557
8624
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
8558
8625
|
import { tmpdir } from "os";
|
|
8559
|
-
import { join as
|
|
8626
|
+
import { join as join14 } from "path";
|
|
8560
8627
|
async function writeCodexInstructionsFile(contents) {
|
|
8561
|
-
const dir2 = await mkdtemp(
|
|
8562
|
-
const path =
|
|
8628
|
+
const dir2 = await mkdtemp(join14(tmpdir(), PREFIX));
|
|
8629
|
+
const path = join14(dir2, "instructions.md");
|
|
8563
8630
|
await writeFile(path, contents, { encoding: "utf8", mode: 384 });
|
|
8564
8631
|
return {
|
|
8565
8632
|
path,
|
|
@@ -8579,13 +8646,13 @@ var init_codex_instructions = __esm({
|
|
|
8579
8646
|
// src/codex-images.ts
|
|
8580
8647
|
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
8581
8648
|
import { tmpdir as tmpdir2 } from "os";
|
|
8582
|
-
import { join as
|
|
8649
|
+
import { join as join15 } from "path";
|
|
8583
8650
|
function createCodexImagePreparer(opts) {
|
|
8584
8651
|
const doFetch = opts.fetch ?? fetch;
|
|
8585
8652
|
const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
8586
8653
|
return async (urls, signal) => {
|
|
8587
8654
|
signal.throwIfAborted();
|
|
8588
|
-
const dir2 = await mkdtemp2(
|
|
8655
|
+
const dir2 = await mkdtemp2(join15(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
|
|
8589
8656
|
const cleanup = async () => {
|
|
8590
8657
|
try {
|
|
8591
8658
|
await rm2(dir2, { recursive: true, force: true });
|
|
@@ -8606,7 +8673,7 @@ function createCodexImagePreparer(opts) {
|
|
|
8606
8673
|
const ext = EXTENSIONS[type];
|
|
8607
8674
|
if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
|
|
8608
8675
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
8609
|
-
const path =
|
|
8676
|
+
const path = join15(dir2, `image-${index + 1}.${ext}`);
|
|
8610
8677
|
await writeFile2(path, bytes, { mode: 384 });
|
|
8611
8678
|
return path;
|
|
8612
8679
|
} catch (err) {
|
|
@@ -8752,15 +8819,15 @@ var init_prepare_failure = __esm({
|
|
|
8752
8819
|
|
|
8753
8820
|
// src/prepared.ts
|
|
8754
8821
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
|
|
8755
|
-
import { join as
|
|
8822
|
+
import { join as join16 } from "path";
|
|
8756
8823
|
function dirFor(workspaceId) {
|
|
8757
|
-
return
|
|
8824
|
+
return join16(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
8758
8825
|
}
|
|
8759
8826
|
function conversationDir(workspaceId, conversationId) {
|
|
8760
|
-
return
|
|
8827
|
+
return join16(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
8761
8828
|
}
|
|
8762
8829
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
8763
|
-
return
|
|
8830
|
+
return join16(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
8764
8831
|
}
|
|
8765
8832
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
8766
8833
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -8798,9 +8865,9 @@ var init_prepared = __esm({
|
|
|
8798
8865
|
|
|
8799
8866
|
// src/transcript-writer.ts
|
|
8800
8867
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
8801
|
-
import { basename, dirname as
|
|
8868
|
+
import { basename as basename2, dirname as dirname8, join as join17 } from "path";
|
|
8802
8869
|
function transcriptsDir() {
|
|
8803
|
-
return
|
|
8870
|
+
return join17(runnerStateDir(), "transcripts");
|
|
8804
8871
|
}
|
|
8805
8872
|
function fileName(meta) {
|
|
8806
8873
|
const d = new Date(meta.ts);
|
|
@@ -8826,7 +8893,7 @@ function pruneOld(dir2, retain) {
|
|
|
8826
8893
|
const drop = files.sort().slice(0, files.length - retain);
|
|
8827
8894
|
for (const f of drop) {
|
|
8828
8895
|
try {
|
|
8829
|
-
rmSync6(
|
|
8896
|
+
rmSync6(join17(dir2, f), { force: true });
|
|
8830
8897
|
} catch {
|
|
8831
8898
|
}
|
|
8832
8899
|
}
|
|
@@ -8844,7 +8911,7 @@ var init_transcript_writer = __esm({
|
|
|
8844
8911
|
onWarn;
|
|
8845
8912
|
constructor(dir2, meta, onWarn) {
|
|
8846
8913
|
this.onWarn = onWarn;
|
|
8847
|
-
this.path =
|
|
8914
|
+
this.path = join17(dir2, fileName(meta));
|
|
8848
8915
|
try {
|
|
8849
8916
|
mkdirSync8(dir2, { recursive: true });
|
|
8850
8917
|
try {
|
|
@@ -8873,10 +8940,10 @@ var init_transcript_writer = __esm({
|
|
|
8873
8940
|
preserveAnomaly() {
|
|
8874
8941
|
if (this.broken) return;
|
|
8875
8942
|
try {
|
|
8876
|
-
const dir2 =
|
|
8943
|
+
const dir2 = join17(dirname8(this.path), "anomalies");
|
|
8877
8944
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
8878
8945
|
chmodSync4(dir2, 448);
|
|
8879
|
-
const target =
|
|
8946
|
+
const target = join17(dir2, basename2(this.path));
|
|
8880
8947
|
copyFileSync(this.path, target);
|
|
8881
8948
|
chmodSync4(target, 384);
|
|
8882
8949
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -8906,6 +8973,7 @@ var TurnCommitter;
|
|
|
8906
8973
|
var init_turn_committer = __esm({
|
|
8907
8974
|
"src/turn-committer.ts"() {
|
|
8908
8975
|
"use strict";
|
|
8976
|
+
init_cabane_command();
|
|
8909
8977
|
init_src();
|
|
8910
8978
|
init_api_error_shape();
|
|
8911
8979
|
TurnCommitter = class {
|
|
@@ -8915,7 +8983,7 @@ var init_turn_committer = __esm({
|
|
|
8915
8983
|
this.onError = (err, hook) => {
|
|
8916
8984
|
deps.log.warn(
|
|
8917
8985
|
{ ...apiErrorLogFields(err), hook },
|
|
8918
|
-
`Couldn't save ${String(deps.log.bindings().agentName ?? "the agent")}'s ${hook === "text" ? "reply" : "reply or activity"} to Cabane.
|
|
8986
|
+
`Couldn't save ${String(deps.log.bindings().agentName ?? "the agent")}'s ${hook === "text" ? "reply" : "reply or activity"} to Cabane. ${mayNeedUpdate()}`
|
|
8919
8987
|
);
|
|
8920
8988
|
deps.onCommitFailed?.(err);
|
|
8921
8989
|
};
|
|
@@ -9057,7 +9125,7 @@ async function postIncompleteNotice(ctx2, harness, resolution) {
|
|
|
9057
9125
|
} catch (err) {
|
|
9058
9126
|
ctx2.log.warn(
|
|
9059
9127
|
{ ...apiErrorLogFields(err) },
|
|
9060
|
-
|
|
9128
|
+
`Couldn't save the explanation to Cabane. ${mayNeedUpdate()}`
|
|
9061
9129
|
);
|
|
9062
9130
|
return false;
|
|
9063
9131
|
}
|
|
@@ -9119,6 +9187,7 @@ var init_turn_runtime_integrity = __esm({
|
|
|
9119
9187
|
"src/turn-runtime-integrity.ts"() {
|
|
9120
9188
|
"use strict";
|
|
9121
9189
|
init_api_error_shape();
|
|
9190
|
+
init_cabane_command();
|
|
9122
9191
|
init_bundled_binaries();
|
|
9123
9192
|
}
|
|
9124
9193
|
});
|
|
@@ -9167,6 +9236,7 @@ var init_turn_execution = __esm({
|
|
|
9167
9236
|
"src/turn-execution.ts"() {
|
|
9168
9237
|
"use strict";
|
|
9169
9238
|
init_term();
|
|
9239
|
+
init_cabane_command();
|
|
9170
9240
|
init_boot_id();
|
|
9171
9241
|
init_src();
|
|
9172
9242
|
init_build_options();
|
|
@@ -9405,7 +9475,7 @@ var init_turn_execution = __esm({
|
|
|
9405
9475
|
} else {
|
|
9406
9476
|
turnLog.warn(
|
|
9407
9477
|
{ ...apiErrorLogFields(err), turnId },
|
|
9408
|
-
|
|
9478
|
+
`Couldn't save the turn's result to Cabane. ${mayNeedUpdate()}`
|
|
9409
9479
|
);
|
|
9410
9480
|
}
|
|
9411
9481
|
}
|
|
@@ -9883,7 +9953,7 @@ var init_turn_execution = __esm({
|
|
|
9883
9953
|
} catch (postErr) {
|
|
9884
9954
|
turnLog.warn(
|
|
9885
9955
|
{ ...apiErrorLogFields(postErr) },
|
|
9886
|
-
|
|
9956
|
+
`Couldn't save the explanation to Cabane. ${mayNeedUpdate()}`
|
|
9887
9957
|
);
|
|
9888
9958
|
const reason = `runtime_unavailable:${err.runtime}`;
|
|
9889
9959
|
throw this.concluded(reason, reason);
|
|
@@ -10092,7 +10162,7 @@ var init_turn_execution = __esm({
|
|
|
10092
10162
|
stateLength: event.state.length,
|
|
10093
10163
|
runtime: turnRuntime
|
|
10094
10164
|
},
|
|
10095
|
-
|
|
10165
|
+
`Couldn't save the agent's memory in Cabane. The next turn will start a new session. ${mayNeedUpdate()}`
|
|
10096
10166
|
);
|
|
10097
10167
|
} else {
|
|
10098
10168
|
turnLog.warn(
|
|
@@ -10256,7 +10326,7 @@ var init_turn_execution = __esm({
|
|
|
10256
10326
|
} else {
|
|
10257
10327
|
turnLog.warn(
|
|
10258
10328
|
{ ...apiErrorLogFields(err) },
|
|
10259
|
-
|
|
10329
|
+
`Couldn't save the turn's result to Cabane. ${mayNeedUpdate()}`
|
|
10260
10330
|
);
|
|
10261
10331
|
}
|
|
10262
10332
|
}
|
|
@@ -10310,7 +10380,7 @@ var init_turn_execution = __esm({
|
|
|
10310
10380
|
// src/dispatcher.ts
|
|
10311
10381
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10312
10382
|
import { existsSync as existsSync11, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
10313
|
-
import { join as
|
|
10383
|
+
import { join as join18 } from "path";
|
|
10314
10384
|
function checkoutState(cwd) {
|
|
10315
10385
|
if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
|
|
10316
10386
|
if (!existsSync11(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
|
|
@@ -10323,7 +10393,7 @@ function checkoutState(cwd) {
|
|
|
10323
10393
|
if (entries.length === 0) {
|
|
10324
10394
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10325
10395
|
}
|
|
10326
|
-
const gitPath =
|
|
10396
|
+
const gitPath = join18(cwd, ".git");
|
|
10327
10397
|
if (!existsSync11(gitPath)) return { ok: true, reason: "usable" };
|
|
10328
10398
|
let stat;
|
|
10329
10399
|
try {
|
|
@@ -10331,7 +10401,7 @@ function checkoutState(cwd) {
|
|
|
10331
10401
|
} catch (error) {
|
|
10332
10402
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10333
10403
|
}
|
|
10334
|
-
if (stat.isDirectory() && !existsSync11(
|
|
10404
|
+
if (stat.isDirectory() && !existsSync11(join18(gitPath, "HEAD")))
|
|
10335
10405
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10336
10406
|
return { ok: true, reason: "usable" };
|
|
10337
10407
|
}
|
|
@@ -10703,15 +10773,15 @@ import {
|
|
|
10703
10773
|
mkdirSync as mkdirSync11,
|
|
10704
10774
|
mkdtempSync,
|
|
10705
10775
|
readFileSync as readFileSync10,
|
|
10706
|
-
realpathSync as
|
|
10776
|
+
realpathSync as realpathSync5,
|
|
10707
10777
|
rmSync as rmSync10,
|
|
10708
10778
|
writeFileSync as writeFileSync7
|
|
10709
10779
|
} from "fs";
|
|
10710
10780
|
import { createRequire as createRequire2 } from "module";
|
|
10711
10781
|
import { isIP } from "net";
|
|
10712
10782
|
import { homedir as homedir6, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
|
|
10713
|
-
import { dirname as
|
|
10714
|
-
import { fileURLToPath as
|
|
10783
|
+
import { dirname as dirname10, join as join22, sep as sep4 } from "path";
|
|
10784
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
10715
10785
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
10716
10786
|
function accountHome() {
|
|
10717
10787
|
try {
|
|
@@ -10742,23 +10812,23 @@ async function resolvedAddresses(baseUrl) {
|
|
|
10742
10812
|
}
|
|
10743
10813
|
}
|
|
10744
10814
|
function companionPackageRoot(modulePath) {
|
|
10745
|
-
for (let dir2 =
|
|
10815
|
+
for (let dir2 = dirname10(modulePath); ; dir2 = dirname10(dir2)) {
|
|
10746
10816
|
try {
|
|
10747
|
-
const manifest = JSON.parse(readFileSync10(
|
|
10817
|
+
const manifest = JSON.parse(readFileSync10(join22(dir2, "package.json"), "utf8"));
|
|
10748
10818
|
if (manifest.name === "@cabane/companion") return dir2;
|
|
10749
10819
|
} catch {
|
|
10750
10820
|
}
|
|
10751
|
-
if (
|
|
10821
|
+
if (dirname10(dir2) === dir2) return null;
|
|
10752
10822
|
}
|
|
10753
10823
|
}
|
|
10754
10824
|
function harnessRuntimePaths() {
|
|
10755
|
-
const paths = [
|
|
10756
|
-
const root = companionPackageRoot(
|
|
10825
|
+
const paths = [dirname10(dirname10(process.execPath))];
|
|
10826
|
+
const root = companionPackageRoot(fileURLToPath6(import.meta.url));
|
|
10757
10827
|
if (root) paths.push(root);
|
|
10758
10828
|
try {
|
|
10759
10829
|
const require2 = createRequire2(import.meta.url);
|
|
10760
10830
|
paths.push(
|
|
10761
|
-
|
|
10831
|
+
dirname10(realpathSync5(require2.resolve("@anthropic-ai/sandbox-runtime/package.json")))
|
|
10762
10832
|
);
|
|
10763
10833
|
} catch {
|
|
10764
10834
|
}
|
|
@@ -10766,7 +10836,7 @@ function harnessRuntimePaths() {
|
|
|
10766
10836
|
const bundled = resolveBundledBinary(runtime).path;
|
|
10767
10837
|
if (bundled) {
|
|
10768
10838
|
try {
|
|
10769
|
-
paths.push(
|
|
10839
|
+
paths.push(dirname10(realpathSync5(bundled)));
|
|
10770
10840
|
} catch {
|
|
10771
10841
|
}
|
|
10772
10842
|
}
|
|
@@ -10777,9 +10847,9 @@ function ownAddresses() {
|
|
|
10777
10847
|
return Object.values(networkInterfaces()).flat().flatMap((i) => i ? [i.address] : []);
|
|
10778
10848
|
}
|
|
10779
10849
|
function storeWalkRoots(homes, cabaneDir2) {
|
|
10780
|
-
const companionHome =
|
|
10781
|
-
const parent =
|
|
10782
|
-
const beside = homes.includes(companionHome) ||
|
|
10850
|
+
const companionHome = dirname10(cabaneDir2);
|
|
10851
|
+
const parent = dirname10(companionHome);
|
|
10852
|
+
const beside = homes.includes(companionHome) || dirname10(parent) === parent ? [] : [parent];
|
|
10783
10853
|
const candidates = [.../* @__PURE__ */ new Set([...homes, ...beside])];
|
|
10784
10854
|
return candidates.filter(
|
|
10785
10855
|
(root) => !candidates.some((other) => other !== root && isAtOrUnder(root, other))
|
|
@@ -10817,19 +10887,19 @@ async function walkCompanionStores(roots, opts = {}) {
|
|
|
10817
10887
|
}
|
|
10818
10888
|
function companionStoreDenies(input) {
|
|
10819
10889
|
const { homes, cabaneDir: cabaneDir2, foundStores } = input;
|
|
10820
|
-
const parent =
|
|
10890
|
+
const parent = dirname10(dirname10(cabaneDir2));
|
|
10821
10891
|
const always = [
|
|
10822
10892
|
cabaneDir2,
|
|
10823
10893
|
...homes.flatMap((home) => [
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10894
|
+
join22(home, ".cabane"),
|
|
10895
|
+
join22(home, "*", ".cabane"),
|
|
10896
|
+
join22(home, "*", "*", ".cabane")
|
|
10827
10897
|
]),
|
|
10828
10898
|
// A glob anchored at the root has no directory to start from.
|
|
10829
|
-
...
|
|
10899
|
+
...dirname10(parent) === parent ? [] : [join22(parent, "*", ".cabane")]
|
|
10830
10900
|
];
|
|
10831
|
-
const companionHome =
|
|
10832
|
-
const otherLogins = foundStores.map((store) =>
|
|
10901
|
+
const companionHome = dirname10(cabaneDir2);
|
|
10902
|
+
const otherLogins = foundStores.map((store) => dirname10(store)).filter((home) => home !== companionHome).flatMap((home) => [join22(home, ".claude"), join22(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
|
|
10833
10903
|
return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
|
|
10834
10904
|
}
|
|
10835
10905
|
function companionStoreWatch(log) {
|
|
@@ -10846,7 +10916,7 @@ function isAtOrUnder(path, dir2) {
|
|
|
10846
10916
|
}
|
|
10847
10917
|
function expandHome(path, home) {
|
|
10848
10918
|
if (path === "~") return home;
|
|
10849
|
-
return path.startsWith("~/") ?
|
|
10919
|
+
return path.startsWith("~/") ? join22(home, path.slice(2)) : path;
|
|
10850
10920
|
}
|
|
10851
10921
|
function stripPort(pattern) {
|
|
10852
10922
|
return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
|
|
@@ -10864,8 +10934,8 @@ function patternsOverlap(a, b) {
|
|
|
10864
10934
|
}
|
|
10865
10935
|
function harnessSandboxConfig(input) {
|
|
10866
10936
|
const { policy } = input;
|
|
10867
|
-
const claudeDir = input.claudeConfigDir ??
|
|
10868
|
-
const codexDir = input.codexHome ??
|
|
10937
|
+
const claudeDir = input.claudeConfigDir ?? join22(input.home, ".claude");
|
|
10938
|
+
const codexDir = input.codexHome ?? join22(input.home, ".codex");
|
|
10869
10939
|
const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
|
|
10870
10940
|
const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
|
|
10871
10941
|
const cabane2 = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
|
|
@@ -10882,7 +10952,7 @@ function harnessSandboxConfig(input) {
|
|
|
10882
10952
|
foundStores: input.foundStores,
|
|
10883
10953
|
ownState: [claudeDir, codexDir]
|
|
10884
10954
|
}),
|
|
10885
|
-
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) =>
|
|
10955
|
+
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join22(home, p))),
|
|
10886
10956
|
...expand(policy.filesystem?.denyRead)
|
|
10887
10957
|
];
|
|
10888
10958
|
const strict = policy.filesystem?.mode === "strict";
|
|
@@ -10890,7 +10960,7 @@ function harnessSandboxConfig(input) {
|
|
|
10890
10960
|
const allowRead = strict ? [
|
|
10891
10961
|
...input.cwd ? [input.cwd] : [],
|
|
10892
10962
|
claudeDir,
|
|
10893
|
-
|
|
10963
|
+
join22(input.home, ".claude.json"),
|
|
10894
10964
|
codexDir,
|
|
10895
10965
|
...input.runtimePaths ?? [],
|
|
10896
10966
|
...expand(policy.filesystem?.allowRead)
|
|
@@ -10930,7 +11000,7 @@ function harnessSandboxConfig(input) {
|
|
|
10930
11000
|
...input.cwd ? [input.cwd] : [],
|
|
10931
11001
|
input.tmp,
|
|
10932
11002
|
claudeDir,
|
|
10933
|
-
|
|
11003
|
+
join22(input.home, ".claude.json"),
|
|
10934
11004
|
codexDir,
|
|
10935
11005
|
...expand(policy.filesystem?.allowWrite)
|
|
10936
11006
|
],
|
|
@@ -11032,9 +11102,9 @@ async function prepareHarnessSandbox(input) {
|
|
|
11032
11102
|
} finally {
|
|
11033
11103
|
for (const name of held) delete process.env[name];
|
|
11034
11104
|
}
|
|
11035
|
-
mkdirSync11(
|
|
11036
|
-
const dir2 = mkdtempSync(
|
|
11037
|
-
const script =
|
|
11105
|
+
mkdirSync11(join22(cabaneDir(), "sandbox"), { recursive: true });
|
|
11106
|
+
const dir2 = mkdtempSync(join22(cabaneDir(), "sandbox", "turn-"));
|
|
11107
|
+
const script = join22(dir2, "exec-sandboxed");
|
|
11038
11108
|
writeFileSync7(
|
|
11039
11109
|
script,
|
|
11040
11110
|
[
|
|
@@ -11050,7 +11120,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
11050
11120
|
return {
|
|
11051
11121
|
wrap: (command, args) => ({ command: script, args: [command, ...args] }),
|
|
11052
11122
|
executableFor: (real) => {
|
|
11053
|
-
const shim =
|
|
11123
|
+
const shim = join22(dir2, `exec-${++shims}`);
|
|
11054
11124
|
writeFileSync7(shim, `#!/bin/bash
|
|
11055
11125
|
exec ${shellQuote(script)} ${shellQuote(real)} "$@"
|
|
11056
11126
|
`);
|
|
@@ -11143,7 +11213,7 @@ import {
|
|
|
11143
11213
|
rmSync as rmSync11,
|
|
11144
11214
|
writeFileSync as writeFileSync8
|
|
11145
11215
|
} from "fs";
|
|
11146
|
-
import { join as
|
|
11216
|
+
import { join as join23 } from "path";
|
|
11147
11217
|
var MAX_ENTRIES, MAX_AGE_MS, Outbox;
|
|
11148
11218
|
var init_outbox = __esm({
|
|
11149
11219
|
"src/outbox.ts"() {
|
|
@@ -11161,10 +11231,10 @@ var init_outbox = __esm({
|
|
|
11161
11231
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
11162
11232
|
// cases route writes at the right tmpdir.
|
|
11163
11233
|
dir() {
|
|
11164
|
-
return
|
|
11234
|
+
return join23(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
11165
11235
|
}
|
|
11166
11236
|
fileFor(turnId, seq) {
|
|
11167
|
-
return
|
|
11237
|
+
return join23(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
11168
11238
|
}
|
|
11169
11239
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
11170
11240
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -11206,7 +11276,7 @@ var init_outbox = __esm({
|
|
|
11206
11276
|
const entries = [];
|
|
11207
11277
|
for (const name of names) {
|
|
11208
11278
|
if (!name.endsWith(".json")) continue;
|
|
11209
|
-
const full =
|
|
11279
|
+
const full = join23(dir2, name);
|
|
11210
11280
|
try {
|
|
11211
11281
|
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
11212
11282
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -11346,7 +11416,7 @@ var init_image_manifest = __esm({
|
|
|
11346
11416
|
|
|
11347
11417
|
// src/runner/exec-shim.ts
|
|
11348
11418
|
import { chmodSync as chmodSync8, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
11349
|
-
import { join as
|
|
11419
|
+
import { join as join32 } from "path";
|
|
11350
11420
|
function shellQuote2(value) {
|
|
11351
11421
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11352
11422
|
}
|
|
@@ -11356,10 +11426,10 @@ function createShimDir() {
|
|
|
11356
11426
|
return {
|
|
11357
11427
|
write(argv) {
|
|
11358
11428
|
if (!dir2) {
|
|
11359
|
-
mkdirSync20(
|
|
11360
|
-
dir2 = mkdtempSync2(
|
|
11429
|
+
mkdirSync20(join32(cabaneDir(), "runner"), { recursive: true });
|
|
11430
|
+
dir2 = mkdtempSync2(join32(cabaneDir(), "runner", "turn-"));
|
|
11361
11431
|
}
|
|
11362
|
-
const path =
|
|
11432
|
+
const path = join32(dir2, `exec-${++count}`);
|
|
11363
11433
|
writeFileSync13(path, `#!/bin/sh
|
|
11364
11434
|
exec ${argv.map(shellQuote2).join(" ")} "$@"
|
|
11365
11435
|
`);
|
|
@@ -11716,13 +11786,13 @@ init_config();
|
|
|
11716
11786
|
import { createHash } from "crypto";
|
|
11717
11787
|
import { chmodSync as chmodSync2, existsSync as existsSync3, rmSync as rmSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
11718
11788
|
import { createServer, connect } from "net";
|
|
11719
|
-
import { join as
|
|
11789
|
+
import { join as join6 } from "path";
|
|
11720
11790
|
|
|
11721
11791
|
// src/harness-copy.ts
|
|
11722
11792
|
init_bundled_binaries();
|
|
11723
11793
|
init_cabane_command();
|
|
11724
11794
|
import { homedir as homedir5 } from "os";
|
|
11725
|
-
import { join as
|
|
11795
|
+
import { join as join5 } from "path";
|
|
11726
11796
|
|
|
11727
11797
|
// src/harness-status.ts
|
|
11728
11798
|
init_bundled_binaries();
|
|
@@ -11883,7 +11953,12 @@ function runtimeIssuesFor(gates) {
|
|
|
11883
11953
|
for (const name of ["claude-code", "codex"]) {
|
|
11884
11954
|
const gate = name === "codex" ? gates.codex : gates.claudeCode;
|
|
11885
11955
|
if (!gate.intended || gate.bundled === "present") continue;
|
|
11886
|
-
issues.push({
|
|
11956
|
+
issues.push({
|
|
11957
|
+
name,
|
|
11958
|
+
reason: gate.bundled,
|
|
11959
|
+
install: gates.install,
|
|
11960
|
+
...gates.managed ? { managed: true } : {}
|
|
11961
|
+
});
|
|
11887
11962
|
}
|
|
11888
11963
|
return issues;
|
|
11889
11964
|
}
|
|
@@ -12152,16 +12227,16 @@ function failureText(runtime, reason) {
|
|
|
12152
12227
|
case "timeout":
|
|
12153
12228
|
return `Couldn't check whether ${label} is signed in: the check didn't answer in time. Try again.`;
|
|
12154
12229
|
case "spawn":
|
|
12155
|
-
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${
|
|
12230
|
+
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())}`;
|
|
12156
12231
|
case "config":
|
|
12157
|
-
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${
|
|
12232
|
+
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.`;
|
|
12158
12233
|
case "unreadable":
|
|
12159
12234
|
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.`;
|
|
12160
12235
|
}
|
|
12161
12236
|
}
|
|
12162
12237
|
function bundleMissingText(runtime) {
|
|
12163
12238
|
const label = HARNESS_LABELS[runtime];
|
|
12164
|
-
return `This Cabane install is missing its bundled ${label}. Repair it with ${
|
|
12239
|
+
return `This Cabane install is missing its bundled ${label}. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
12165
12240
|
}
|
|
12166
12241
|
function unusableReason(runtime, check) {
|
|
12167
12242
|
if (check.status === "bundle_missing") return bundleMissingText(runtime);
|
|
@@ -12235,6 +12310,9 @@ function connectLines(outcome, opts = {}) {
|
|
|
12235
12310
|
]
|
|
12236
12311
|
};
|
|
12237
12312
|
}
|
|
12313
|
+
function spaced(sentence) {
|
|
12314
|
+
return sentence ? ` ${sentence}` : "";
|
|
12315
|
+
}
|
|
12238
12316
|
|
|
12239
12317
|
// src/control-socket.ts
|
|
12240
12318
|
var CONTROL_TIMEOUT_MS = 1e3;
|
|
@@ -12245,11 +12323,11 @@ function controlSocketPath() {
|
|
|
12245
12323
|
if (process.platform === "win32") {
|
|
12246
12324
|
return `\\\\.\\pipe\\cabane-companion-${key}`;
|
|
12247
12325
|
}
|
|
12248
|
-
const besideRuntime =
|
|
12326
|
+
const besideRuntime = join6(dir2, "companion.sock");
|
|
12249
12327
|
if (Buffer.byteLength(besideRuntime) <= MAX_UNIX_SOCKET_PATH_BYTES) return besideRuntime;
|
|
12250
12328
|
const filename = `cabane-companion-${key}.sock`;
|
|
12251
|
-
const xdgPath = process.env.XDG_RUNTIME_DIR && existsSync3(process.env.XDG_RUNTIME_DIR) ?
|
|
12252
|
-
return xdgPath && Buffer.byteLength(xdgPath) <= MAX_UNIX_SOCKET_PATH_BYTES ? xdgPath :
|
|
12329
|
+
const xdgPath = process.env.XDG_RUNTIME_DIR && existsSync3(process.env.XDG_RUNTIME_DIR) ? join6(process.env.XDG_RUNTIME_DIR, filename) : null;
|
|
12330
|
+
return xdgPath && Buffer.byteLength(xdgPath) <= MAX_UNIX_SOCKET_PATH_BYTES ? xdgPath : join6("/tmp", filename);
|
|
12253
12331
|
}
|
|
12254
12332
|
async function startControlServer(handlers) {
|
|
12255
12333
|
const path = controlSocketPath();
|
|
@@ -12424,10 +12502,10 @@ import {
|
|
|
12424
12502
|
openSync,
|
|
12425
12503
|
closeSync
|
|
12426
12504
|
} from "fs";
|
|
12427
|
-
import { join as
|
|
12505
|
+
import { join as join7 } from "path";
|
|
12428
12506
|
var PROBE_TIMEOUT_MS2 = 1e3;
|
|
12429
12507
|
function runtimePath() {
|
|
12430
|
-
return
|
|
12508
|
+
return join7(cabaneDir(), "runtime.json");
|
|
12431
12509
|
}
|
|
12432
12510
|
function serialize(state) {
|
|
12433
12511
|
return JSON.stringify(state, null, 2) + "\n";
|
|
@@ -12642,10 +12720,10 @@ import {
|
|
|
12642
12720
|
rmSync as rmSync4,
|
|
12643
12721
|
writeFileSync as writeFileSync3
|
|
12644
12722
|
} from "fs";
|
|
12645
|
-
import { dirname as
|
|
12723
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
12646
12724
|
import { z as z3 } from "zod";
|
|
12647
12725
|
function credentialsPath() {
|
|
12648
|
-
return
|
|
12726
|
+
return join8(cabaneDir(), "credentials.json");
|
|
12649
12727
|
}
|
|
12650
12728
|
var credentialStoreSchema = z3.record(z3.string(), z3.string());
|
|
12651
12729
|
function load() {
|
|
@@ -12667,7 +12745,7 @@ function load() {
|
|
|
12667
12745
|
}
|
|
12668
12746
|
function save(map) {
|
|
12669
12747
|
const path = credentialsPath();
|
|
12670
|
-
mkdirSync4(
|
|
12748
|
+
mkdirSync4(dirname5(path), { recursive: true });
|
|
12671
12749
|
try {
|
|
12672
12750
|
chmodSync3(cabaneDir(), 448);
|
|
12673
12751
|
} catch {
|
|
@@ -12962,11 +13040,11 @@ import {
|
|
|
12962
13040
|
renameSync as renameSync3,
|
|
12963
13041
|
statSync as statSync2
|
|
12964
13042
|
} from "fs";
|
|
12965
|
-
import { dirname as
|
|
13043
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
12966
13044
|
import { stripVTControlCharacters } from "util";
|
|
12967
13045
|
import pino from "pino";
|
|
12968
13046
|
function companionLogPath() {
|
|
12969
|
-
return
|
|
13047
|
+
return join9(cabaneDir(), "companion.log");
|
|
12970
13048
|
}
|
|
12971
13049
|
function humanText(value) {
|
|
12972
13050
|
return stripVTControlCharacters(String(value ?? "")).replace(
|
|
@@ -13080,7 +13158,7 @@ function configureLogger(log, config) {
|
|
|
13080
13158
|
}
|
|
13081
13159
|
function createLogger(destinations = {}, options = {}) {
|
|
13082
13160
|
const path = companionLogPath();
|
|
13083
|
-
if (!destinations.file) mkdirSync5(
|
|
13161
|
+
if (!destinations.file) mkdirSync5(dirname6(path), { recursive: true });
|
|
13084
13162
|
const file = destinations.file ?? new RotatingLog(path);
|
|
13085
13163
|
const state = { format: options.logFormat ?? "human" };
|
|
13086
13164
|
const streams = [];
|
|
@@ -13133,15 +13211,15 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
13133
13211
|
|
|
13134
13212
|
// src/dashboard/server.ts
|
|
13135
13213
|
init_errors();
|
|
13136
|
-
import { dirname as
|
|
13137
|
-
import { fileURLToPath as
|
|
13214
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
13215
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13138
13216
|
import { serve } from "@hono/node-server";
|
|
13139
13217
|
import { Hono } from "hono";
|
|
13140
13218
|
|
|
13141
13219
|
// src/dashboard/routes.ts
|
|
13142
13220
|
import { openSync as openSync3, readSync, closeSync as closeSync2, fstatSync, existsSync as existsSync6 } from "fs";
|
|
13143
13221
|
import { readFile } from "fs/promises";
|
|
13144
|
-
import { extname, join as
|
|
13222
|
+
import { extname, join as join10, normalize } from "path";
|
|
13145
13223
|
import { streamSSE } from "hono/streaming";
|
|
13146
13224
|
|
|
13147
13225
|
// src/state.ts
|
|
@@ -13390,13 +13468,13 @@ var CONTENT_TYPES = {
|
|
|
13390
13468
|
function registerRoutes(app, deps) {
|
|
13391
13469
|
const { supervisor, hub, staticDir } = deps;
|
|
13392
13470
|
app.get("/", async (c) => {
|
|
13393
|
-
const html = await readFile(
|
|
13471
|
+
const html = await readFile(join10(staticDir, "index.html"), "utf8");
|
|
13394
13472
|
return c.html(html);
|
|
13395
13473
|
});
|
|
13396
13474
|
app.get("/static/:file", async (c) => {
|
|
13397
13475
|
const file = c.req.param("file");
|
|
13398
13476
|
const safe2 = normalize(file).replace(/^(\.\.[/\\])+/, "");
|
|
13399
|
-
const full =
|
|
13477
|
+
const full = join10(staticDir, safe2);
|
|
13400
13478
|
if (!full.startsWith(staticDir) || !existsSync6(full)) return c.notFound();
|
|
13401
13479
|
const body = await readFile(full);
|
|
13402
13480
|
const type = CONTENT_TYPES[extname(full).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -13608,7 +13686,7 @@ function isAddrInUse(err) {
|
|
|
13608
13686
|
return Boolean(err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE");
|
|
13609
13687
|
}
|
|
13610
13688
|
function resolveStaticDir() {
|
|
13611
|
-
return
|
|
13689
|
+
return join11(dirname7(fileURLToPath4(import.meta.url)), "static");
|
|
13612
13690
|
}
|
|
13613
13691
|
|
|
13614
13692
|
// src/supervisor.ts
|
|
@@ -13617,6 +13695,7 @@ import { hostname as hostname2 } from "os";
|
|
|
13617
13695
|
|
|
13618
13696
|
// src/log-copy.ts
|
|
13619
13697
|
init_failure();
|
|
13698
|
+
init_bundled_binaries();
|
|
13620
13699
|
function turnFailureCopy(reason, runtime) {
|
|
13621
13700
|
const failure = decodeFailureReason(reason);
|
|
13622
13701
|
if (failure?.kind === "auth_expired") {
|
|
@@ -13635,7 +13714,7 @@ function turnFailureCopy(reason, runtime) {
|
|
|
13635
13714
|
if (reason?.startsWith("missing_secret:"))
|
|
13636
13715
|
return `A required secret is missing: ${humanText(reason.slice(15))}. Add it to ~/.cabane/secrets.json, then reply in Cabane to try again.`;
|
|
13637
13716
|
if (reason?.startsWith("runtime_incomplete:"))
|
|
13638
|
-
return
|
|
13717
|
+
return `The agent installation is incomplete. ${repairAction()} Then restart the companion.`;
|
|
13639
13718
|
if (/^(runtime|codex|opencode)_unavailable:/.test(reason ?? ""))
|
|
13640
13719
|
return "The agent is unavailable on this machine. Enable it here or choose another connector in Cabane.";
|
|
13641
13720
|
if (reason?.startsWith("model_unavailable:"))
|
|
@@ -13775,13 +13854,13 @@ init_config();
|
|
|
13775
13854
|
// src/dispatch-dedupe.ts
|
|
13776
13855
|
init_config();
|
|
13777
13856
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync7 } from "fs";
|
|
13778
|
-
import { join as
|
|
13857
|
+
import { join as join12 } from "path";
|
|
13779
13858
|
var MAX_IDS = 256;
|
|
13780
13859
|
function dir(log) {
|
|
13781
|
-
return
|
|
13860
|
+
return join12(cabaneDir(), log);
|
|
13782
13861
|
}
|
|
13783
13862
|
function pathFor(log, workspaceId) {
|
|
13784
|
-
return
|
|
13863
|
+
return join12(dir(log), encodeURIComponent(workspaceId));
|
|
13785
13864
|
}
|
|
13786
13865
|
function readIds(log, workspaceId) {
|
|
13787
13866
|
const path = pathFor(log, workspaceId);
|
|
@@ -13818,10 +13897,10 @@ function markCompleted(workspaceId, eventId) {
|
|
|
13818
13897
|
}
|
|
13819
13898
|
var MAX_RESUME_ATTEMPTS = 3;
|
|
13820
13899
|
function resumeDir() {
|
|
13821
|
-
return
|
|
13900
|
+
return join12(cabaneDir(), "resume-attempts");
|
|
13822
13901
|
}
|
|
13823
13902
|
function resumePathFor(workspaceId) {
|
|
13824
|
-
return
|
|
13903
|
+
return join12(resumeDir(), encodeURIComponent(workspaceId));
|
|
13825
13904
|
}
|
|
13826
13905
|
function readResumeCounts(workspaceId) {
|
|
13827
13906
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -13857,10 +13936,10 @@ function bumpResumeAttempt(workspaceId, eventId) {
|
|
|
13857
13936
|
return next;
|
|
13858
13937
|
}
|
|
13859
13938
|
function turnDir() {
|
|
13860
|
-
return
|
|
13939
|
+
return join12(cabaneDir(), "turns");
|
|
13861
13940
|
}
|
|
13862
13941
|
function turnPathFor(workspaceId) {
|
|
13863
|
-
return
|
|
13942
|
+
return join12(turnDir(), encodeURIComponent(workspaceId));
|
|
13864
13943
|
}
|
|
13865
13944
|
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;
|
|
13866
13945
|
function readTurnIds(workspaceId) {
|
|
@@ -13912,6 +13991,7 @@ function noResume() {
|
|
|
13912
13991
|
|
|
13913
13992
|
// src/supervisor.ts
|
|
13914
13993
|
init_bundled_binaries();
|
|
13994
|
+
init_install_kind();
|
|
13915
13995
|
|
|
13916
13996
|
// src/connector-health.ts
|
|
13917
13997
|
init_src();
|
|
@@ -14093,7 +14173,7 @@ import {
|
|
|
14093
14173
|
statSync as statSync4,
|
|
14094
14174
|
writeFileSync as writeFileSync6
|
|
14095
14175
|
} from "fs";
|
|
14096
|
-
import { join as
|
|
14176
|
+
import { join as join21 } from "path";
|
|
14097
14177
|
import { Writable } from "stream";
|
|
14098
14178
|
|
|
14099
14179
|
// src/providers/docker-cli.ts
|
|
@@ -14166,7 +14246,7 @@ import {
|
|
|
14166
14246
|
renameSync as renameSync5,
|
|
14167
14247
|
rmSync as rmSync8
|
|
14168
14248
|
} from "fs";
|
|
14169
|
-
import { join as
|
|
14249
|
+
import { join as join19 } from "path";
|
|
14170
14250
|
function routes(t) {
|
|
14171
14251
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
14172
14252
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -14212,7 +14292,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
14212
14292
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
14213
14293
|
let entry = {};
|
|
14214
14294
|
try {
|
|
14215
|
-
entry = JSON.parse(readFileSync8(
|
|
14295
|
+
entry = JSON.parse(readFileSync8(join19(from, name), "utf8"));
|
|
14216
14296
|
} catch {
|
|
14217
14297
|
}
|
|
14218
14298
|
return { name, entry };
|
|
@@ -14225,7 +14305,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
14225
14305
|
let published = 0;
|
|
14226
14306
|
for (const { name } of staged) {
|
|
14227
14307
|
try {
|
|
14228
|
-
move(
|
|
14308
|
+
move(join19(from, name), join19(to, name));
|
|
14229
14309
|
} catch {
|
|
14230
14310
|
return { published, complete: false };
|
|
14231
14311
|
}
|
|
@@ -14237,9 +14317,9 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
14237
14317
|
|
|
14238
14318
|
// src/providers/runner-base.ts
|
|
14239
14319
|
import { createHash as createHash3 } from "crypto";
|
|
14240
|
-
import { existsSync as existsSync13, realpathSync as
|
|
14241
|
-
import { dirname as
|
|
14242
|
-
import { fileURLToPath as
|
|
14320
|
+
import { existsSync as existsSync13, realpathSync as realpathSync4 } from "fs";
|
|
14321
|
+
import { dirname as dirname9, join as join20, sep as sep3 } from "path";
|
|
14322
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
14243
14323
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
14244
14324
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
14245
14325
|
RUN apt-get update \\
|
|
@@ -14283,17 +14363,17 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
14283
14363
|
}
|
|
14284
14364
|
return true;
|
|
14285
14365
|
}
|
|
14286
|
-
function ownInstall(moduleFile =
|
|
14287
|
-
let dir2 =
|
|
14366
|
+
function ownInstall(moduleFile = fileURLToPath5(import.meta.url)) {
|
|
14367
|
+
let dir2 = dirname9(moduleFile);
|
|
14288
14368
|
for (let i = 0; i < 4; i++) {
|
|
14289
|
-
if (existsSync13(
|
|
14290
|
-
dir2 =
|
|
14369
|
+
if (existsSync13(join20(dir2, "dist", "cli.js")) && existsSync13(join20(dir2, "package.json"))) break;
|
|
14370
|
+
dir2 = dirname9(dir2);
|
|
14291
14371
|
}
|
|
14292
|
-
if (!existsSync13(
|
|
14293
|
-
const sdk =
|
|
14372
|
+
if (!existsSync13(join20(dir2, "dist", "cli.js"))) return null;
|
|
14373
|
+
const sdk = join20(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
14294
14374
|
if (!existsSync13(sdk)) return null;
|
|
14295
|
-
const root =
|
|
14296
|
-
return
|
|
14375
|
+
const root = realpathSync4(dir2);
|
|
14376
|
+
return realpathSync4(sdk).startsWith(root + sep3) ? root : null;
|
|
14297
14377
|
}
|
|
14298
14378
|
function companionSource(input) {
|
|
14299
14379
|
if (input.configured) {
|
|
@@ -14429,39 +14509,39 @@ function networkCreateError(stderr) {
|
|
|
14429
14509
|
}
|
|
14430
14510
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
14431
14511
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
14432
|
-
return
|
|
14512
|
+
return join21(deviceDir, "attempts", attemptId, "session.tar");
|
|
14433
14513
|
}
|
|
14434
14514
|
function pruneOtherAttempts(deviceDir, pair2, keep) {
|
|
14435
|
-
const root =
|
|
14515
|
+
const root = join21(deviceDir, "attempts");
|
|
14436
14516
|
if (!existsSync14(root)) return;
|
|
14437
14517
|
for (const id of readdirSync4(root)) {
|
|
14438
14518
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
14439
14519
|
let pairOf = "";
|
|
14440
14520
|
try {
|
|
14441
|
-
pairOf = readFileSync9(
|
|
14521
|
+
pairOf = readFileSync9(join21(root, id, "pair"), "utf8");
|
|
14442
14522
|
} catch {
|
|
14443
14523
|
continue;
|
|
14444
14524
|
}
|
|
14445
14525
|
if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
|
|
14446
|
-
rmSync9(
|
|
14526
|
+
rmSync9(join21(root, id), { recursive: true, force: true });
|
|
14447
14527
|
}
|
|
14448
14528
|
}
|
|
14449
14529
|
}
|
|
14450
14530
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
14451
14531
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
14452
|
-
const root =
|
|
14532
|
+
const root = join21(deviceDir, "attempts");
|
|
14453
14533
|
if (!existsSync14(root)) return [];
|
|
14454
14534
|
const removed = [];
|
|
14455
14535
|
for (const id of readdirSync4(root)) {
|
|
14456
14536
|
if (!UUID_RE.test(id)) continue;
|
|
14457
14537
|
let kept;
|
|
14458
14538
|
try {
|
|
14459
|
-
kept = statSync4(
|
|
14539
|
+
kept = statSync4(join21(root, id)).mtimeMs;
|
|
14460
14540
|
} catch {
|
|
14461
14541
|
continue;
|
|
14462
14542
|
}
|
|
14463
14543
|
if (now - kept < maxAgeMs) continue;
|
|
14464
|
-
rmSync9(
|
|
14544
|
+
rmSync9(join21(root, id), { recursive: true, force: true });
|
|
14465
14545
|
removed.push(id);
|
|
14466
14546
|
}
|
|
14467
14547
|
return removed;
|
|
@@ -14662,11 +14742,11 @@ function createDockerProvider(opts) {
|
|
|
14662
14742
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
14663
14743
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
14664
14744
|
};
|
|
14665
|
-
const socketRoot = opts.socketRoot ??
|
|
14745
|
+
const socketRoot = opts.socketRoot ?? join21(
|
|
14666
14746
|
process.env.XDG_RUNTIME_DIR && existsSync14(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
14667
14747
|
`cabane-runners-${opts.deviceKey}`
|
|
14668
14748
|
);
|
|
14669
|
-
const socketDir = (name) =>
|
|
14749
|
+
const socketDir = (name) => join21(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
14670
14750
|
const create = async (name, attempt) => {
|
|
14671
14751
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
14672
14752
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -14808,7 +14888,7 @@ function createDockerProvider(opts) {
|
|
|
14808
14888
|
}
|
|
14809
14889
|
};
|
|
14810
14890
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
14811
|
-
const pendingPath = (name) =>
|
|
14891
|
+
const pendingPath = (name) => join21(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
14812
14892
|
const readPending = (name) => {
|
|
14813
14893
|
try {
|
|
14814
14894
|
const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
|
|
@@ -14822,12 +14902,12 @@ function createDockerProvider(opts) {
|
|
|
14822
14902
|
rmSync9(pendingPath(name), { force: true });
|
|
14823
14903
|
return;
|
|
14824
14904
|
}
|
|
14825
|
-
mkdirSync10(
|
|
14905
|
+
mkdirSync10(join21(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
14826
14906
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
14827
14907
|
};
|
|
14828
14908
|
const keepSession = async (name, attempt, attemptId) => {
|
|
14829
14909
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
14830
|
-
mkdirSync10(
|
|
14910
|
+
mkdirSync10(join21(archive, ".."), { recursive: true });
|
|
14831
14911
|
const partial = `${archive}.${process.pid}.partial`;
|
|
14832
14912
|
const file = new CappedFile(partial, sessionCap);
|
|
14833
14913
|
const r = await execIn(
|
|
@@ -14844,7 +14924,7 @@ function createDockerProvider(opts) {
|
|
|
14844
14924
|
);
|
|
14845
14925
|
if (r.code === 0 && !file.over) {
|
|
14846
14926
|
renameSync6(partial, archive);
|
|
14847
|
-
writeFileSync6(
|
|
14927
|
+
writeFileSync6(join21(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
14848
14928
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
14849
14929
|
await writeMarker(name, attemptId);
|
|
14850
14930
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -14858,7 +14938,7 @@ function createDockerProvider(opts) {
|
|
|
14858
14938
|
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"
|
|
14859
14939
|
);
|
|
14860
14940
|
};
|
|
14861
|
-
const stagingDir = (name) =>
|
|
14941
|
+
const stagingDir = (name) => join21(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
14862
14942
|
const ensureRunning = async (name) => {
|
|
14863
14943
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
14864
14944
|
if (seen.code !== 0) return false;
|
|
@@ -14866,7 +14946,7 @@ function createDockerProvider(opts) {
|
|
|
14866
14946
|
return (await docker.run(["start", name])).code === 0;
|
|
14867
14947
|
};
|
|
14868
14948
|
const dropArchive = (attemptId) => {
|
|
14869
|
-
rmSync9(
|
|
14949
|
+
rmSync9(join21(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
14870
14950
|
recursive: true,
|
|
14871
14951
|
force: true
|
|
14872
14952
|
});
|
|
@@ -14935,7 +15015,7 @@ function createDockerProvider(opts) {
|
|
|
14935
15015
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
14936
15016
|
await must(["start", name], "starting the container");
|
|
14937
15017
|
const git = await serveGitCredentials({
|
|
14938
|
-
socketPath:
|
|
15018
|
+
socketPath: join21(socketDir(name), "git.sock"),
|
|
14939
15019
|
hosts: opts.gitHosts,
|
|
14940
15020
|
fill: opts.gitFill,
|
|
14941
15021
|
log
|
|
@@ -15084,15 +15164,15 @@ function createDockerProvider(opts) {
|
|
|
15084
15164
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
15085
15165
|
if (transcripts) {
|
|
15086
15166
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
15087
|
-
if (kept.length) mkdirSync10(
|
|
15088
|
-
for (const f of kept) writeFileSync6(
|
|
15167
|
+
if (kept.length) mkdirSync10(join21(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
15168
|
+
for (const f of kept) writeFileSync6(join21(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
15089
15169
|
await removeIn(
|
|
15090
15170
|
name,
|
|
15091
15171
|
`${CONTAINER_STATE}/transcripts`,
|
|
15092
15172
|
kept.map((f) => f.name)
|
|
15093
15173
|
);
|
|
15094
15174
|
}
|
|
15095
|
-
const outboxRel =
|
|
15175
|
+
const outboxRel = join21("outbox", encodeURIComponent(attempt.agentId));
|
|
15096
15176
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
15097
15177
|
let unpublished = false;
|
|
15098
15178
|
if (outbox) {
|
|
@@ -15118,7 +15198,7 @@ function createDockerProvider(opts) {
|
|
|
15118
15198
|
const staged = stagingDir(name);
|
|
15119
15199
|
mkdirSync10(staged, { recursive: true });
|
|
15120
15200
|
writeFileSync6(
|
|
15121
|
-
|
|
15201
|
+
join21(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
15122
15202
|
`${JSON.stringify(entry)}
|
|
15123
15203
|
`
|
|
15124
15204
|
);
|
|
@@ -15131,7 +15211,7 @@ function createDockerProvider(opts) {
|
|
|
15131
15211
|
if (outbox.complete) {
|
|
15132
15212
|
const published = publishStaged(
|
|
15133
15213
|
stagingDir(name),
|
|
15134
|
-
|
|
15214
|
+
join21(opts.hostStateDir, outboxRel),
|
|
15135
15215
|
opts.moveFile
|
|
15136
15216
|
);
|
|
15137
15217
|
queued = published.published;
|
|
@@ -15546,19 +15626,19 @@ init_transcript_writer();
|
|
|
15546
15626
|
// src/updater.ts
|
|
15547
15627
|
import { spawn as spawn8 } from "child_process";
|
|
15548
15628
|
import { copyFileSync as copyFileSync3, existsSync as existsSync17, mkdirSync as mkdirSync16, renameSync as renameSync11, rmSync as rmSync14 } from "fs";
|
|
15549
|
-
import { dirname as
|
|
15629
|
+
import { dirname as dirname14, join as join27 } from "path";
|
|
15550
15630
|
|
|
15551
15631
|
// src/boot-rule.ts
|
|
15552
15632
|
import {
|
|
15553
15633
|
mkdirSync as mkdirSync13,
|
|
15554
15634
|
readFileSync as readFileSync12,
|
|
15555
|
-
realpathSync as
|
|
15635
|
+
realpathSync as realpathSync6,
|
|
15556
15636
|
renameSync as renameSync8,
|
|
15557
15637
|
rmSync as rmSync12,
|
|
15558
15638
|
symlinkSync,
|
|
15559
15639
|
writeFileSync as writeFileSync9
|
|
15560
15640
|
} from "fs";
|
|
15561
|
-
import { dirname as
|
|
15641
|
+
import { dirname as dirname11, join as join24 } from "path";
|
|
15562
15642
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
15563
15643
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
15564
15644
|
function superseded(pending, ...running) {
|
|
@@ -15587,37 +15667,37 @@ import {
|
|
|
15587
15667
|
mkdirSync as mkdirSync14,
|
|
15588
15668
|
readFileSync as readFileSync13,
|
|
15589
15669
|
readdirSync as readdirSync6,
|
|
15590
|
-
realpathSync as
|
|
15670
|
+
realpathSync as realpathSync7,
|
|
15591
15671
|
renameSync as renameSync9,
|
|
15592
15672
|
rmSync as rmSync13,
|
|
15593
15673
|
symlinkSync as symlinkSync2
|
|
15594
15674
|
} from "fs";
|
|
15595
|
-
import { dirname as
|
|
15596
|
-
import { fileURLToPath as
|
|
15675
|
+
import { dirname as dirname12, join as join25 } from "path";
|
|
15676
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
15597
15677
|
function versionsDir() {
|
|
15598
|
-
return
|
|
15678
|
+
return join25(cabaneDir(), "versions");
|
|
15599
15679
|
}
|
|
15600
15680
|
function currentLink() {
|
|
15601
|
-
return
|
|
15681
|
+
return join25(cabaneDir(), "current");
|
|
15602
15682
|
}
|
|
15603
15683
|
function bootGuardPath() {
|
|
15604
|
-
return
|
|
15684
|
+
return join25(cabaneDir(), "boot-guard.mjs");
|
|
15605
15685
|
}
|
|
15606
15686
|
function pinnedNodeDir() {
|
|
15607
|
-
return
|
|
15687
|
+
return join25(cabaneDir(), "node");
|
|
15608
15688
|
}
|
|
15609
15689
|
function pinnedNodePath() {
|
|
15610
|
-
return
|
|
15690
|
+
return join25(pinnedNodeDir(), "bin", "node");
|
|
15611
15691
|
}
|
|
15612
15692
|
function versionPackageRoot(version) {
|
|
15613
|
-
return
|
|
15693
|
+
return join25(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
15614
15694
|
}
|
|
15615
15695
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
15616
|
-
return
|
|
15696
|
+
return dirname12(dirname12(fileURLToPath7(moduleUrl)));
|
|
15617
15697
|
}
|
|
15618
15698
|
function packageVersion(root) {
|
|
15619
15699
|
try {
|
|
15620
|
-
const pkg2 = JSON.parse(readFileSync13(
|
|
15700
|
+
const pkg2 = JSON.parse(readFileSync13(join25(root, "package.json"), "utf8"));
|
|
15621
15701
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
15622
15702
|
} catch {
|
|
15623
15703
|
return null;
|
|
@@ -15625,15 +15705,15 @@ function packageVersion(root) {
|
|
|
15625
15705
|
}
|
|
15626
15706
|
function currentRoot() {
|
|
15627
15707
|
try {
|
|
15628
|
-
const root =
|
|
15629
|
-
return existsSync16(
|
|
15708
|
+
const root = realpathSync7(currentLink());
|
|
15709
|
+
return existsSync16(join25(root, "dist", "cli.js")) ? root : null;
|
|
15630
15710
|
} catch {
|
|
15631
15711
|
return null;
|
|
15632
15712
|
}
|
|
15633
15713
|
}
|
|
15634
15714
|
function pointCurrentAt(root) {
|
|
15635
15715
|
const link = currentLink();
|
|
15636
|
-
mkdirSync14(
|
|
15716
|
+
mkdirSync14(dirname12(link), { recursive: true });
|
|
15637
15717
|
const fresh = `${link}.${process.pid}.${Date.now()}`;
|
|
15638
15718
|
symlinkSync2(root, fresh);
|
|
15639
15719
|
try {
|
|
@@ -15649,7 +15729,7 @@ function pruneVersions(keep) {
|
|
|
15649
15729
|
const removed = [];
|
|
15650
15730
|
for (const name of readdirSync6(dir2)) {
|
|
15651
15731
|
if (keep.includes(name)) continue;
|
|
15652
|
-
rmSync13(
|
|
15732
|
+
rmSync13(join25(dir2, name), { recursive: true, force: true });
|
|
15653
15733
|
removed.push(name);
|
|
15654
15734
|
}
|
|
15655
15735
|
return removed;
|
|
@@ -15669,7 +15749,7 @@ function compareVersions(a, b) {
|
|
|
15669
15749
|
// src/update-state.ts
|
|
15670
15750
|
init_config();
|
|
15671
15751
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync14, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
15672
|
-
import { dirname as
|
|
15752
|
+
import { dirname as dirname13, join as join26 } from "path";
|
|
15673
15753
|
import { z as z18 } from "zod";
|
|
15674
15754
|
var resultSchema = z18.object({
|
|
15675
15755
|
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -15693,7 +15773,7 @@ var stateSchema = z18.object({
|
|
|
15693
15773
|
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
15694
15774
|
});
|
|
15695
15775
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
15696
|
-
return
|
|
15776
|
+
return join26(dir2, "update.json");
|
|
15697
15777
|
}
|
|
15698
15778
|
function readUpdateState(path = updateStatePath()) {
|
|
15699
15779
|
try {
|
|
@@ -15704,7 +15784,7 @@ function readUpdateState(path = updateStatePath()) {
|
|
|
15704
15784
|
}
|
|
15705
15785
|
}
|
|
15706
15786
|
function writeUpdateState(state, path = updateStatePath()) {
|
|
15707
|
-
mkdirSync15(
|
|
15787
|
+
mkdirSync15(dirname13(path), { recursive: true });
|
|
15708
15788
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
15709
15789
|
writeFileSync10(tmp, `${JSON.stringify(state, null, 2)}
|
|
15710
15790
|
`, { mode: 384 });
|
|
@@ -15742,10 +15822,10 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
15742
15822
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
15743
15823
|
}
|
|
15744
15824
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
15745
|
-
const built =
|
|
15825
|
+
const built = join27(root, "dist", "boot-guard.js");
|
|
15746
15826
|
if (!existsSync17(built)) return false;
|
|
15747
15827
|
const target = bootGuardPath();
|
|
15748
|
-
mkdirSync16(
|
|
15828
|
+
mkdirSync16(dirname14(target), { recursive: true });
|
|
15749
15829
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
15750
15830
|
copyFileSync3(built, tmp);
|
|
15751
15831
|
renameSync11(tmp, target);
|
|
@@ -15782,17 +15862,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
15782
15862
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
15783
15863
|
const run = deps.run ?? runProcess;
|
|
15784
15864
|
const node = deps.execPath ?? process.execPath;
|
|
15785
|
-
const target =
|
|
15865
|
+
const target = join27(versionsDir(), version);
|
|
15786
15866
|
const placed = versionPackageRoot(version);
|
|
15787
15867
|
if (existsSync17(placed)) {
|
|
15788
15868
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
15789
15869
|
return { ok: true, root: placed };
|
|
15790
15870
|
}
|
|
15791
|
-
const npm =
|
|
15871
|
+
const npm = join27(dirname14(node), "npm");
|
|
15792
15872
|
if (!existsSync17(npm))
|
|
15793
|
-
return { ok: false, reason: `there is no npm beside Node at ${
|
|
15873
|
+
return { ok: false, reason: `there is no npm beside Node at ${dirname14(node)}` };
|
|
15794
15874
|
mkdirSync16(versionsDir(), { recursive: true });
|
|
15795
|
-
const staging =
|
|
15875
|
+
const staging = join27(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
15796
15876
|
rmSync14(staging, { recursive: true, force: true });
|
|
15797
15877
|
const installed = await run(
|
|
15798
15878
|
npm,
|
|
@@ -15809,7 +15889,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
15809
15889
|
],
|
|
15810
15890
|
{
|
|
15811
15891
|
// npm's install scripts run on this Node, not whichever `node` is first on PATH.
|
|
15812
|
-
env: { ...process.env, PATH: `${
|
|
15892
|
+
env: { ...process.env, PATH: `${dirname14(node)}:${process.env.PATH ?? ""}` },
|
|
15813
15893
|
timeoutMs: 15 * 6e4
|
|
15814
15894
|
}
|
|
15815
15895
|
);
|
|
@@ -15821,7 +15901,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
15821
15901
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
15822
15902
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
15823
15903
|
}
|
|
15824
|
-
const root =
|
|
15904
|
+
const root = join27(staging, "node_modules", "@cabane", "companion");
|
|
15825
15905
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
15826
15906
|
if (verdict) {
|
|
15827
15907
|
rmSync14(staging, { recursive: true, force: true });
|
|
@@ -15833,7 +15913,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
15833
15913
|
}
|
|
15834
15914
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
15835
15915
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
15836
|
-
const answered = await deps.run(deps.execPath, [
|
|
15916
|
+
const answered = await deps.run(deps.execPath, [join27(root, "dist", "bin.js"), "--version"], {
|
|
15837
15917
|
timeoutMs: 6e4,
|
|
15838
15918
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
15839
15919
|
});
|
|
@@ -15841,7 +15921,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
15841
15921
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
15842
15922
|
}
|
|
15843
15923
|
if (harnesses.length === 0) return null;
|
|
15844
|
-
const probed = await deps.run(deps.execPath, [
|
|
15924
|
+
const probed = await deps.run(deps.execPath, [join27(root, "dist", "postinstall.js"), "--json"], {
|
|
15845
15925
|
timeoutMs: 6e4
|
|
15846
15926
|
});
|
|
15847
15927
|
let resolutions = {};
|
|
@@ -16419,7 +16499,7 @@ var CompanionSupervisor = class {
|
|
|
16419
16499
|
this.versionSkewWarned = true;
|
|
16420
16500
|
this.log.warn(
|
|
16421
16501
|
{ companionVersion: COMPANION_VERSION, serverVersion },
|
|
16422
|
-
|
|
16502
|
+
`This companion and Cabane are on different versions. If turns fail, update with ${cabane("update")}`
|
|
16423
16503
|
);
|
|
16424
16504
|
}
|
|
16425
16505
|
// Pull assignments and reconcile the live runner set against them. Re-entrancy
|
|
@@ -16648,7 +16728,8 @@ var CompanionSupervisor = class {
|
|
|
16648
16728
|
bundled: this.bundledStatus("claude-code")
|
|
16649
16729
|
},
|
|
16650
16730
|
codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") },
|
|
16651
|
-
install: companionInstall()
|
|
16731
|
+
install: companionInstall(),
|
|
16732
|
+
managed: repairRoute() === "managed"
|
|
16652
16733
|
});
|
|
16653
16734
|
}
|
|
16654
16735
|
// A missing binary is reported ONCE, and again only when the answer CHANGES,
|
|
@@ -17607,9 +17688,9 @@ function handleUncaught(log, err, origin) {
|
|
|
17607
17688
|
// src/crash-marker.ts
|
|
17608
17689
|
init_config();
|
|
17609
17690
|
import { existsSync as existsSync18, mkdirSync as mkdirSync17, readFileSync as readFileSync15, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "fs";
|
|
17610
|
-
import { join as
|
|
17691
|
+
import { join as join28 } from "path";
|
|
17611
17692
|
function crashMarkerPath() {
|
|
17612
|
-
return
|
|
17693
|
+
return join28(cabaneDir(), "last-error.json");
|
|
17613
17694
|
}
|
|
17614
17695
|
function recordCrash(rec2) {
|
|
17615
17696
|
try {
|
|
@@ -17800,7 +17881,7 @@ import {
|
|
|
17800
17881
|
writeFileSync as writeFileSync12
|
|
17801
17882
|
} from "fs";
|
|
17802
17883
|
import { homedir as homedir7, userInfo as userInfo3 } from "os";
|
|
17803
|
-
import { dirname as
|
|
17884
|
+
import { dirname as dirname15, join as join29, sep as sep5 } from "path";
|
|
17804
17885
|
function servicePlatform(platform = process.platform) {
|
|
17805
17886
|
if (platform === "linux") return "systemd";
|
|
17806
17887
|
if (platform === "darwin") return "launchd";
|
|
@@ -17818,15 +17899,15 @@ function serviceNode(execPath = process.execPath) {
|
|
|
17818
17899
|
return existsSync19(pinnedNodePath()) ? pinnedNodePath() : execPath;
|
|
17819
17900
|
}
|
|
17820
17901
|
function servicePaths(platform, env = process.env, home = homedir7(), node = serviceNode()) {
|
|
17821
|
-
const unitFile = platform === "systemd" ?
|
|
17822
|
-
env.XDG_CONFIG_HOME ||
|
|
17902
|
+
const unitFile = platform === "systemd" ? join29(
|
|
17903
|
+
env.XDG_CONFIG_HOME || join29(home, ".config"),
|
|
17823
17904
|
"systemd",
|
|
17824
17905
|
"user",
|
|
17825
17906
|
`${serviceName(env)}.service`
|
|
17826
|
-
) :
|
|
17907
|
+
) : join29(home, "Library", "LaunchAgents", `${launchdLabel(env)}.plist`);
|
|
17827
17908
|
return {
|
|
17828
17909
|
unitFile,
|
|
17829
|
-
envFile:
|
|
17910
|
+
envFile: join29(cabaneDir(), "service.env"),
|
|
17830
17911
|
node,
|
|
17831
17912
|
entry: bootGuardPath()
|
|
17832
17913
|
};
|
|
@@ -17893,7 +17974,7 @@ function renderLaunchdPlist(paths, env = process.env, servicePath) {
|
|
|
17893
17974
|
" <key>ThrottleInterval</key><integer>5</integer>",
|
|
17894
17975
|
// The companion writes its own log; the console copy would only grow a second one.
|
|
17895
17976
|
" <key>StandardOutPath</key><string>/dev/null</string>",
|
|
17896
|
-
` <key>StandardErrorPath</key><string>${xml(
|
|
17977
|
+
` <key>StandardErrorPath</key><string>${xml(join29(cabaneDir(), "service-error.log"))}</string>`,
|
|
17897
17978
|
"</dict>",
|
|
17898
17979
|
"</plist>",
|
|
17899
17980
|
""
|
|
@@ -17948,11 +18029,11 @@ async function installService(deps = {}) {
|
|
|
17948
18029
|
rewriteScriptWrappers();
|
|
17949
18030
|
const paths = servicePaths(platform, env, deps.home, node);
|
|
17950
18031
|
const servicePath = await loginShellPath(run, env);
|
|
17951
|
-
mkdirSync18(
|
|
18032
|
+
mkdirSync18(dirname15(paths.envFile), { recursive: true });
|
|
17952
18033
|
const existing = existsSync19(paths.envFile) ? readFileSync16(paths.envFile, "utf8") : "";
|
|
17953
18034
|
writeFileSync12(paths.envFile, withServicePath(existing, servicePath), { mode: 384 });
|
|
17954
18035
|
chmodSync7(paths.envFile, 384);
|
|
17955
|
-
mkdirSync18(
|
|
18036
|
+
mkdirSync18(dirname15(paths.unitFile), { recursive: true });
|
|
17956
18037
|
const outcome = await writeAndStartUnit(platform, paths, env, servicePath, run);
|
|
17957
18038
|
if (!outcome.ok) await removeUnit(platform, paths, env, run);
|
|
17958
18039
|
return outcome;
|
|
@@ -18033,15 +18114,15 @@ async function seedManagedInstall(deps) {
|
|
|
18033
18114
|
}
|
|
18034
18115
|
const target = versionPackageRoot(version);
|
|
18035
18116
|
if (!existsSync19(target)) {
|
|
18036
|
-
const staging =
|
|
18117
|
+
const staging = join29(versionsDir(), `.${version}.seed-${process.pid}`);
|
|
18037
18118
|
rmSync16(staging, { recursive: true, force: true });
|
|
18038
18119
|
try {
|
|
18039
|
-
cpSync(own,
|
|
18120
|
+
cpSync(own, join29(staging, "node_modules", "@cabane", "companion"), {
|
|
18040
18121
|
recursive: true,
|
|
18041
18122
|
verbatimSymlinks: true
|
|
18042
18123
|
});
|
|
18043
|
-
rmSync16(
|
|
18044
|
-
renameSync12(staging,
|
|
18124
|
+
rmSync16(join29(versionsDir(), version), { recursive: true, force: true });
|
|
18125
|
+
renameSync12(staging, join29(versionsDir(), version));
|
|
18045
18126
|
} catch (err) {
|
|
18046
18127
|
rmSync16(staging, { recursive: true, force: true });
|
|
18047
18128
|
return {
|
|
@@ -18052,7 +18133,7 @@ async function seedManagedInstall(deps) {
|
|
|
18052
18133
|
}
|
|
18053
18134
|
const unrunnable = await verifyInstall(target, version, [], deps);
|
|
18054
18135
|
if (unrunnable) {
|
|
18055
|
-
rmSync16(
|
|
18136
|
+
rmSync16(join29(versionsDir(), version), { recursive: true, force: true });
|
|
18056
18137
|
return { ok: false, reason: `The copy of Cabane for the service can't run: ${unrunnable}.` };
|
|
18057
18138
|
}
|
|
18058
18139
|
pointCurrentAt(target);
|
|
@@ -18134,9 +18215,9 @@ function userName() {
|
|
|
18134
18215
|
return process.env.USER ?? "";
|
|
18135
18216
|
}
|
|
18136
18217
|
}
|
|
18137
|
-
function rewriteScriptWrappers(dir2 =
|
|
18218
|
+
function rewriteScriptWrappers(dir2 = join29(cabaneDir(), "bin")) {
|
|
18138
18219
|
for (const name of ["cabane", "cabane-companion"]) {
|
|
18139
|
-
const path =
|
|
18220
|
+
const path = join29(dir2, name);
|
|
18140
18221
|
let text;
|
|
18141
18222
|
try {
|
|
18142
18223
|
text = readFileSync16(path, "utf8");
|
|
@@ -19129,10 +19210,11 @@ async function logs(opts = {}) {
|
|
|
19129
19210
|
|
|
19130
19211
|
// src/commands/status.ts
|
|
19131
19212
|
init_bundled_binaries();
|
|
19213
|
+
init_install_kind();
|
|
19132
19214
|
init_cabane_command();
|
|
19133
|
-
import { readdirSync as readdirSync7, realpathSync as
|
|
19215
|
+
import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync20 } from "fs";
|
|
19134
19216
|
import { homedir as homedir8 } from "os";
|
|
19135
|
-
import { dirname as
|
|
19217
|
+
import { dirname as dirname16, join as join30 } from "path";
|
|
19136
19218
|
init_config();
|
|
19137
19219
|
init_sandbox_policy();
|
|
19138
19220
|
init_secrets();
|
|
@@ -19198,10 +19280,10 @@ function versionLine() {
|
|
|
19198
19280
|
const install = companionInstall();
|
|
19199
19281
|
const own = installedCommandPath({ install });
|
|
19200
19282
|
if (install === "script") {
|
|
19201
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(
|
|
19283
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join30(homedir8(), ".cabane"))})`;
|
|
19202
19284
|
}
|
|
19203
19285
|
if (own)
|
|
19204
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(
|
|
19286
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname16(dirname16(own)))})`;
|
|
19205
19287
|
return `Cabane ${COMPANION_VERSION}`;
|
|
19206
19288
|
}
|
|
19207
19289
|
function otherInstallLines(running) {
|
|
@@ -19218,13 +19300,13 @@ function otherInstallLines(running) {
|
|
|
19218
19300
|
function otherInstall(install, own) {
|
|
19219
19301
|
const real = (p) => {
|
|
19220
19302
|
try {
|
|
19221
|
-
return
|
|
19303
|
+
return realpathSync8(p);
|
|
19222
19304
|
} catch {
|
|
19223
19305
|
return null;
|
|
19224
19306
|
}
|
|
19225
19307
|
};
|
|
19226
19308
|
if (install === "npm") {
|
|
19227
|
-
const script =
|
|
19309
|
+
const script = join30(
|
|
19228
19310
|
homedir8(),
|
|
19229
19311
|
".cabane",
|
|
19230
19312
|
"node",
|
|
@@ -19233,12 +19315,12 @@ function otherInstall(install, own) {
|
|
|
19233
19315
|
"@cabane",
|
|
19234
19316
|
"companion"
|
|
19235
19317
|
);
|
|
19236
|
-
return existsSync20(script) ?
|
|
19318
|
+
return existsSync20(script) ? join30(homedir8(), ".cabane") : null;
|
|
19237
19319
|
}
|
|
19238
19320
|
const found = firstOnPath("cabane-companion", process.env.PATH);
|
|
19239
19321
|
if (!found) return null;
|
|
19240
|
-
const ownAlias = own ? real(
|
|
19241
|
-
return real(found) === ownAlias ? null :
|
|
19322
|
+
const ownAlias = own ? real(join30(dirname16(own), "cabane-companion")) : null;
|
|
19323
|
+
return real(found) === ownAlias ? null : dirname16(found);
|
|
19242
19324
|
}
|
|
19243
19325
|
async function liveState() {
|
|
19244
19326
|
const running = readLiveRuntimeState();
|
|
@@ -19298,7 +19380,7 @@ function signInText(runtime, status2, version) {
|
|
|
19298
19380
|
case "signed_out":
|
|
19299
19381
|
return `signed out \u2014 run ${loginCommand(runtime)}`;
|
|
19300
19382
|
case "bundle_missing":
|
|
19301
|
-
return `this install is missing it \u2014 repair with ${
|
|
19383
|
+
return `this install is missing it \u2014 repair with ${repairSteps()}${repairCost() ? `. ${repairCost()}` : ""}`;
|
|
19302
19384
|
case "check_failed":
|
|
19303
19385
|
return "connected, but its sign-in couldn't be checked";
|
|
19304
19386
|
default:
|
|
@@ -19357,6 +19439,8 @@ init_config();
|
|
|
19357
19439
|
init_term();
|
|
19358
19440
|
init_version2();
|
|
19359
19441
|
init_cabane_command();
|
|
19442
|
+
init_bundled_binaries();
|
|
19443
|
+
init_install_kind();
|
|
19360
19444
|
var UPDATE_TIMEOUT_MS = 20 * 6e4;
|
|
19361
19445
|
async function update(opts = {}) {
|
|
19362
19446
|
const running = readLiveRuntimeState();
|
|
@@ -19374,13 +19458,13 @@ async function update(opts = {}) {
|
|
|
19374
19458
|
write(`${INDENT}${r.ok ? tick(r.message) : r.message}`);
|
|
19375
19459
|
return;
|
|
19376
19460
|
}
|
|
19377
|
-
const
|
|
19378
|
-
if (!
|
|
19461
|
+
const cfg = loadConfig();
|
|
19462
|
+
if (!cfg) {
|
|
19379
19463
|
write(`${INDENT}This computer isn't set up yet. Run ${cabane("start")} first.`);
|
|
19380
19464
|
process.exitCode = 1;
|
|
19381
19465
|
return;
|
|
19382
19466
|
}
|
|
19383
|
-
saveConfig({ ...
|
|
19467
|
+
saveConfig({ ...cfg, update: { auto } });
|
|
19384
19468
|
write(
|
|
19385
19469
|
INDENT + tick(
|
|
19386
19470
|
auto ? "Cabane will keep itself up to date. Updates install between turns when they can, and roll back if they fail." : "Cabane won't update itself."
|
|
@@ -19388,6 +19472,29 @@ async function update(opts = {}) {
|
|
|
19388
19472
|
);
|
|
19389
19473
|
return;
|
|
19390
19474
|
}
|
|
19475
|
+
const harnesses = connectedHarnesses();
|
|
19476
|
+
const latest = readUpdateState().server?.latest;
|
|
19477
|
+
if (!latest || compareVersions(latest, COMPANION_VERSION) <= 0) {
|
|
19478
|
+
const repair = await repairOwnCopy({ running: Boolean(running) });
|
|
19479
|
+
if (repair.kind === "running") {
|
|
19480
|
+
write(
|
|
19481
|
+
`${INDENT}This copy of Cabane ${COMPANION_VERSION} is incomplete: ${repair.reason}. Run ${cabane("stop")}, then ${cabane("update")} again to repair it. Stopping ends any turn in progress.`
|
|
19482
|
+
);
|
|
19483
|
+
process.exitCode = 1;
|
|
19484
|
+
return;
|
|
19485
|
+
}
|
|
19486
|
+
if (repair.kind === "failed") {
|
|
19487
|
+
write(`${INDENT}Couldn't repair Cabane ${COMPANION_VERSION}: ${repair.reason}.`);
|
|
19488
|
+
process.exitCode = 1;
|
|
19489
|
+
return;
|
|
19490
|
+
}
|
|
19491
|
+
if (repair.kind === "repaired") {
|
|
19492
|
+
write(
|
|
19493
|
+
INDENT + tick(`Repaired Cabane ${COMPANION_VERSION}. Start it with ${cabane("start")}`)
|
|
19494
|
+
);
|
|
19495
|
+
return;
|
|
19496
|
+
}
|
|
19497
|
+
}
|
|
19391
19498
|
if (running?.socket) {
|
|
19392
19499
|
const r = await controlRequest(
|
|
19393
19500
|
running.socket,
|
|
@@ -19398,7 +19505,6 @@ async function update(opts = {}) {
|
|
|
19398
19505
|
if (!r.ok) process.exitCode = 1;
|
|
19399
19506
|
return;
|
|
19400
19507
|
}
|
|
19401
|
-
const latest = readUpdateState().server?.latest;
|
|
19402
19508
|
if (!latest) {
|
|
19403
19509
|
write(
|
|
19404
19510
|
`${INDENT}Cabane hasn't said which version is latest yet. Run ${cabane("start")}, and it updates from there.`
|
|
@@ -19410,11 +19516,6 @@ async function update(opts = {}) {
|
|
|
19410
19516
|
write(INDENT + tick(`Cabane ${COMPANION_VERSION} is up to date.`));
|
|
19411
19517
|
return;
|
|
19412
19518
|
}
|
|
19413
|
-
const cfg = loadConfig();
|
|
19414
|
-
const harnesses = cfg ? [
|
|
19415
|
-
...isClaudeCodeConnected(cfg) ? ["claude-code"] : [],
|
|
19416
|
-
...isCodexEnabled(cfg) ? ["codex"] : []
|
|
19417
|
-
] : [];
|
|
19418
19519
|
write(
|
|
19419
19520
|
`${INDENT}Installing Cabane ${latest}. It brings its own copies of the agents, so this can take a few minutes.`
|
|
19420
19521
|
);
|
|
@@ -19454,6 +19555,31 @@ async function update(opts = {}) {
|
|
|
19454
19555
|
pointCurrentAt(outcome.root);
|
|
19455
19556
|
write(INDENT + tick(`Updated to ${latest}. Start it with ${cabane("start")}`));
|
|
19456
19557
|
}
|
|
19558
|
+
function bundledHere(platform, arch) {
|
|
19559
|
+
return ["claude-code", ...codexPlatformPackage(platform, arch) ? ["codex"] : []];
|
|
19560
|
+
}
|
|
19561
|
+
function connectedHarnesses() {
|
|
19562
|
+
const cfg = loadConfig();
|
|
19563
|
+
if (!cfg) return [];
|
|
19564
|
+
return [
|
|
19565
|
+
...isClaudeCodeConnected(cfg) ? ["claude-code"] : [],
|
|
19566
|
+
...isCodexEnabled(cfg) ? ["codex"] : []
|
|
19567
|
+
];
|
|
19568
|
+
}
|
|
19569
|
+
async function repairOwnCopy(deps) {
|
|
19570
|
+
if ((deps.route ?? repairRoute()) !== "managed") return { kind: "healthy" };
|
|
19571
|
+
const harnesses = deps.harnesses ?? bundledHere(deps.platform ?? process.platform, deps.arch ?? process.arch);
|
|
19572
|
+
const root = deps.root ?? ownPackageRoot();
|
|
19573
|
+
const version = deps.version ?? COMPANION_VERSION;
|
|
19574
|
+
const verify = deps.verify ?? ((r, v, h) => verifyInstall(r, v, h, { run: runProcess, execPath: process.execPath }));
|
|
19575
|
+
const broken = await verify(root, version, harnesses);
|
|
19576
|
+
if (!broken) return { kind: "healthy" };
|
|
19577
|
+
if (deps.running) return { kind: "running", reason: broken };
|
|
19578
|
+
const outcome = await (deps.stage ?? stageVersion)(version, harnesses);
|
|
19579
|
+
if (!outcome.ok) return { kind: "failed", reason: outcome.reason };
|
|
19580
|
+
(deps.point ?? pointCurrentAt)(outcome.root);
|
|
19581
|
+
return { kind: "repaired" };
|
|
19582
|
+
}
|
|
19457
19583
|
|
|
19458
19584
|
// src/commands/transcript.ts
|
|
19459
19585
|
init_cabane_command();
|
|
@@ -19461,7 +19587,7 @@ init_errors();
|
|
|
19461
19587
|
init_src();
|
|
19462
19588
|
init_transcript_writer();
|
|
19463
19589
|
import { existsSync as existsSync21, readFileSync as readFileSync17, readdirSync as readdirSync8 } from "fs";
|
|
19464
|
-
import { isAbsolute, join as
|
|
19590
|
+
import { isAbsolute, join as join31 } from "path";
|
|
19465
19591
|
async function transcript(opts = {}) {
|
|
19466
19592
|
const dir2 = transcriptsDir();
|
|
19467
19593
|
if (opts.follow) {
|
|
@@ -19478,7 +19604,7 @@ async function transcript(opts = {}) {
|
|
|
19478
19604
|
process.stdout.write(emptyMessage(dir2));
|
|
19479
19605
|
return;
|
|
19480
19606
|
}
|
|
19481
|
-
process.stdout.write(renderFile(
|
|
19607
|
+
process.stdout.write(renderFile(join31(dir2, newest)) + "\n");
|
|
19482
19608
|
return;
|
|
19483
19609
|
}
|
|
19484
19610
|
printList(dir2);
|
|
@@ -19561,7 +19687,7 @@ function isComplete(content) {
|
|
|
19561
19687
|
async function followTranscripts(dir2) {
|
|
19562
19688
|
const follower = new TranscriptFollower({
|
|
19563
19689
|
listFiles: () => listFiles(dir2),
|
|
19564
|
-
read: (f) => readFileSync17(
|
|
19690
|
+
read: (f) => readFileSync17(join31(dir2, f), "utf8"),
|
|
19565
19691
|
write: (s) => process.stdout.write(s),
|
|
19566
19692
|
// CSI: cursor up `n` lines, then erase from cursor to end of screen.
|
|
19567
19693
|
clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
|
|
@@ -19601,7 +19727,7 @@ function printList(dir2) {
|
|
|
19601
19727
|
|
|
19602
19728
|
`);
|
|
19603
19729
|
for (const f of files.slice(0, 20)) {
|
|
19604
|
-
const { meta, outcome } = peek(
|
|
19730
|
+
const { meta, outcome } = peek(join31(dir2, f));
|
|
19605
19731
|
const when = fmtTime(rec(meta)?.ts);
|
|
19606
19732
|
const ws = str2(rec(meta)?.workspaceSlug);
|
|
19607
19733
|
const o = rec(outcome);
|
|
@@ -19639,10 +19765,10 @@ function resolveTarget(dir2, target) {
|
|
|
19639
19765
|
if (existsSync21(target)) return target;
|
|
19640
19766
|
throw new CompanionError(`no transcript at ${target}.`);
|
|
19641
19767
|
}
|
|
19642
|
-
const exact =
|
|
19768
|
+
const exact = join31(dir2, target);
|
|
19643
19769
|
if (existsSync21(exact)) return exact;
|
|
19644
19770
|
const matches = listFiles(dir2).filter((f) => f.includes(target));
|
|
19645
|
-
if (matches.length === 1) return
|
|
19771
|
+
if (matches.length === 1) return join31(dir2, matches[0]);
|
|
19646
19772
|
if (matches.length === 0) {
|
|
19647
19773
|
throw new CompanionError(
|
|
19648
19774
|
`no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`
|
|
@@ -19806,6 +19932,8 @@ They are written per dispatch while \`${cabane("start")}\` is running.
|
|
|
19806
19932
|
// src/cli.ts
|
|
19807
19933
|
init_errors();
|
|
19808
19934
|
init_version2();
|
|
19935
|
+
init_cabane_command();
|
|
19936
|
+
rememberInvokedCommand();
|
|
19809
19937
|
var program = new Command();
|
|
19810
19938
|
program.name("cabane").description("Run your Cabane agents on this computer, with Claude Code, Codex or OpenCode.").version(COMPANION_VERSION);
|
|
19811
19939
|
program.command("pair").description(
|