@cabane/companion 0.6.88 → 0.6.90
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 +17 -11
- package/dist/cli.js +776 -219
- package/dist/pairing-config.js +9 -6
- package/dist/postinstall.js +209 -0
- package/dist/runtime.js +765 -208
- package/package.json +2 -1
package/dist/pairing-config.js
CHANGED
|
@@ -182,9 +182,10 @@ var companionConfigSchema = z2.object({
|
|
|
182
182
|
// The change this task exists for: Claude Code used to be the one harness a
|
|
183
183
|
// device exposed with no configuration — a `claude --version` exit-0 was taken as
|
|
184
184
|
// consent — and it is now the third config-driven harness, opted into exactly
|
|
185
|
-
// like codex.
|
|
186
|
-
//
|
|
187
|
-
//
|
|
185
|
+
// like codex. CT1379: the binary that must be there is the Agent SDK's own
|
|
186
|
+
// bundled executable — the file a turn actually launches — not the user's
|
|
187
|
+
// `claude` on PATH, which the SDK never runs. The manifest advertises
|
|
188
|
+
// claude-code only when this block says so AND that binary resolves.
|
|
188
189
|
//
|
|
189
190
|
// The block's PRESENCE is also the migration marker (see `migrateConnectedHarnesses`).
|
|
190
191
|
// Absent means the config predates this task — a device whose user was never
|
|
@@ -204,9 +205,11 @@ var companionConfigSchema = z2.object({
|
|
|
204
205
|
// not a server address. The operator installs Codex + logs in (`codex login`, or
|
|
205
206
|
// `CODEX_API_KEY` — Cabane never sees the key) and sets `{ "codex": { "enabled":
|
|
206
207
|
// true } }`. Presence of the block enables it (an explicit `enabled: false`
|
|
207
|
-
// keeps the block but turns it off).
|
|
208
|
-
//
|
|
209
|
-
//
|
|
208
|
+
// keeps the block but turns it off). Absent → the device doesn't offer codex.
|
|
209
|
+
// CT1379: enabling is now half the answer, not all of it — the manifest and the
|
|
210
|
+
// adapter registry additionally require the SDK's vendored `codex` binary to
|
|
211
|
+
// resolve, because the enable flag alone advertised a runtime a device with a
|
|
212
|
+
// half-installed platform package could not start.
|
|
210
213
|
codex: z2.object({
|
|
211
214
|
enabled: z2.boolean().optional()
|
|
212
215
|
}).strict().optional()
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// src/bundled-binaries.ts
|
|
2
|
+
import { realpathSync, statSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
function nodeResolveSelf(specifier) {
|
|
7
|
+
const here = fileURLToPath(import.meta.url);
|
|
8
|
+
if (typeof import.meta.resolve === "function") {
|
|
9
|
+
try {
|
|
10
|
+
return fileURLToPath(import.meta.resolve(specifier));
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return createRequire(here).resolve(specifier);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
return walkNodeModules(specifier, here);
|
|
19
|
+
}
|
|
20
|
+
function walkNodeModules(pkg, fromPath) {
|
|
21
|
+
let dir = dirname(fromPath);
|
|
22
|
+
for (; ; ) {
|
|
23
|
+
const manifest = join(dir, "node_modules", pkg, "package.json");
|
|
24
|
+
if (nodeIsFile(manifest)) {
|
|
25
|
+
try {
|
|
26
|
+
return realpathSync(manifest);
|
|
27
|
+
} catch {
|
|
28
|
+
return manifest;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const parent = dirname(dir);
|
|
32
|
+
if (parent === dir) return null;
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function nodeResolve(specifier, fromPath) {
|
|
37
|
+
try {
|
|
38
|
+
return createRequire(fromPath).resolve(specifier);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function nodeIsFile(path) {
|
|
44
|
+
try {
|
|
45
|
+
return statSync(path).isFile();
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function detectPreferMusl(platform = process.platform) {
|
|
51
|
+
if (platform !== "linux") return false;
|
|
52
|
+
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
53
|
+
const header = report?.header;
|
|
54
|
+
return report != null && header?.glibcVersionRuntime === void 0;
|
|
55
|
+
}
|
|
56
|
+
function defaultResolveEnv() {
|
|
57
|
+
return {
|
|
58
|
+
platform: process.platform,
|
|
59
|
+
arch: process.arch,
|
|
60
|
+
preferMusl: detectPreferMusl(),
|
|
61
|
+
// Both resolutions start from this module, which tsup bundles into
|
|
62
|
+
// `dist/cli.js` and `dist/postinstall.js` — both at the package's `dist/`
|
|
63
|
+
// root — so one environment serves the runtime checks and the install
|
|
64
|
+
// verifier alike.
|
|
65
|
+
resolveSelf: nodeResolveSelf,
|
|
66
|
+
resolve: nodeResolve,
|
|
67
|
+
isFile: nodeIsFile
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
71
|
+
function claudePlatformPackages(platform, arch, preferMusl) {
|
|
72
|
+
if (platform === "android") return [`${CLAUDE_SDK}-linux-${arch}-android`];
|
|
73
|
+
if (platform === "linux") {
|
|
74
|
+
return preferMusl ? [`${CLAUDE_SDK}-linux-${arch}-musl`, `${CLAUDE_SDK}-linux-${arch}`] : [`${CLAUDE_SDK}-linux-${arch}`, `${CLAUDE_SDK}-linux-${arch}-musl`];
|
|
75
|
+
}
|
|
76
|
+
return [`${CLAUDE_SDK}-${platform}-${arch}`];
|
|
77
|
+
}
|
|
78
|
+
function resolveClaude(env) {
|
|
79
|
+
const candidates = claudePlatformPackages(env.platform, env.arch, env.preferMusl);
|
|
80
|
+
const base = {
|
|
81
|
+
runtime: "claude-code",
|
|
82
|
+
packageName: candidates[0] ?? null,
|
|
83
|
+
platform: env.platform,
|
|
84
|
+
arch: env.arch
|
|
85
|
+
};
|
|
86
|
+
const sdkEntry = env.resolveSelf(CLAUDE_SDK);
|
|
87
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
88
|
+
let sawPackage = null;
|
|
89
|
+
for (const pkg of candidates) {
|
|
90
|
+
const manifest = env.resolve(`${pkg}/package.json`, sdkEntry);
|
|
91
|
+
if (!manifest) continue;
|
|
92
|
+
sawPackage ??= pkg;
|
|
93
|
+
const exe = join(dirname(manifest), env.platform === "win32" ? "claude.exe" : "claude");
|
|
94
|
+
if (env.isFile(exe)) {
|
|
95
|
+
return { ...base, packageName: pkg, status: "present", path: exe };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return sawPackage ? { ...base, packageName: sawPackage, status: "executable_missing", path: null } : { ...base, status: "platform_package_missing", path: null };
|
|
99
|
+
}
|
|
100
|
+
var CODEX_SDK = "@openai/codex-sdk";
|
|
101
|
+
var CODEX_CLI = "@openai/codex";
|
|
102
|
+
function codexTargetTriple(platform, arch) {
|
|
103
|
+
if (platform === "linux" || platform === "android") {
|
|
104
|
+
if (arch === "x64") return "x86_64-unknown-linux-musl";
|
|
105
|
+
if (arch === "arm64") return "aarch64-unknown-linux-musl";
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (platform === "darwin") {
|
|
109
|
+
if (arch === "x64") return "x86_64-apple-darwin";
|
|
110
|
+
if (arch === "arm64") return "aarch64-apple-darwin";
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (platform === "win32") {
|
|
114
|
+
if (arch === "x64") return "x86_64-pc-windows-msvc";
|
|
115
|
+
if (arch === "arm64") return "aarch64-pc-windows-msvc";
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
var CODEX_PACKAGE_BY_TRIPLE = {
|
|
121
|
+
"x86_64-unknown-linux-musl": `${CODEX_CLI}-linux-x64`,
|
|
122
|
+
"aarch64-unknown-linux-musl": `${CODEX_CLI}-linux-arm64`,
|
|
123
|
+
"x86_64-apple-darwin": `${CODEX_CLI}-darwin-x64`,
|
|
124
|
+
"aarch64-apple-darwin": `${CODEX_CLI}-darwin-arm64`,
|
|
125
|
+
"x86_64-pc-windows-msvc": `${CODEX_CLI}-win32-x64`,
|
|
126
|
+
"aarch64-pc-windows-msvc": `${CODEX_CLI}-win32-arm64`
|
|
127
|
+
};
|
|
128
|
+
function resolveCodex(env) {
|
|
129
|
+
const triple = codexTargetTriple(env.platform, env.arch);
|
|
130
|
+
const pkg = triple ? CODEX_PACKAGE_BY_TRIPLE[triple] : void 0;
|
|
131
|
+
const base = {
|
|
132
|
+
runtime: "codex",
|
|
133
|
+
packageName: pkg ?? null,
|
|
134
|
+
platform: env.platform,
|
|
135
|
+
arch: env.arch
|
|
136
|
+
};
|
|
137
|
+
if (!triple || !pkg) return { ...base, status: "platform_package_missing", path: null };
|
|
138
|
+
const sdkEntry = env.resolveSelf(CODEX_SDK);
|
|
139
|
+
if (!sdkEntry) return { ...base, status: "platform_package_missing", path: null };
|
|
140
|
+
const cliManifest = env.resolve(`${CODEX_CLI}/package.json`, sdkEntry);
|
|
141
|
+
if (!cliManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
142
|
+
const platformManifest = env.resolve(`${pkg}/package.json`, cliManifest);
|
|
143
|
+
if (!platformManifest) return { ...base, status: "platform_package_missing", path: null };
|
|
144
|
+
const packageRoot = join(dirname(platformManifest), "vendor", triple);
|
|
145
|
+
const exeName = env.platform === "win32" ? "codex.exe" : "codex";
|
|
146
|
+
const current = join(packageRoot, "bin", exeName);
|
|
147
|
+
if (env.isFile(current) && env.isFile(join(packageRoot, "codex-package.json"))) {
|
|
148
|
+
return { ...base, status: "present", path: current };
|
|
149
|
+
}
|
|
150
|
+
const legacy = join(packageRoot, "codex", exeName);
|
|
151
|
+
if (env.isFile(legacy)) return { ...base, status: "present", path: legacy };
|
|
152
|
+
return { ...base, status: "executable_missing", path: null };
|
|
153
|
+
}
|
|
154
|
+
function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
155
|
+
try {
|
|
156
|
+
return runtime === "codex" ? resolveCodex(env) : resolveClaude(env);
|
|
157
|
+
} catch {
|
|
158
|
+
return {
|
|
159
|
+
runtime,
|
|
160
|
+
status: "platform_package_missing",
|
|
161
|
+
path: null,
|
|
162
|
+
packageName: null,
|
|
163
|
+
platform: env.platform,
|
|
164
|
+
arch: env.arch
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function resolveBothBundledBinaries(env = defaultResolveEnv()) {
|
|
169
|
+
return {
|
|
170
|
+
"claude-code": resolveBundledBinary("claude-code", env),
|
|
171
|
+
codex: resolveBundledBinary("codex", env)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
var HARNESS_LABEL = {
|
|
175
|
+
"claude-code": "Claude Code",
|
|
176
|
+
codex: "Codex"
|
|
177
|
+
};
|
|
178
|
+
function missingNoun(status) {
|
|
179
|
+
return status === "executable_missing" ? "executable" : "platform package";
|
|
180
|
+
}
|
|
181
|
+
function repairCommands() {
|
|
182
|
+
return ["npm i -g @cabane/companion --include=optional"];
|
|
183
|
+
}
|
|
184
|
+
function repairAction() {
|
|
185
|
+
return `Reinstall with \`${repairCommands()[0]}\`.`;
|
|
186
|
+
}
|
|
187
|
+
function installWarning(r) {
|
|
188
|
+
return `warning: @cabane/companion is incomplete: its bundled ${HARNESS_LABEL[r.runtime]} ${missingNoun(r.status)} for ${r.platform}-${r.arch} is missing. ${repairAction()}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/postinstall.ts
|
|
192
|
+
function main(argv) {
|
|
193
|
+
const resolutions = resolveBothBundledBinaries();
|
|
194
|
+
if (argv.includes("--json")) {
|
|
195
|
+
process.stdout.write(`${JSON.stringify(resolutions, null, 2)}
|
|
196
|
+
`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const missing = Object.values(resolutions).filter(
|
|
200
|
+
(r) => r.status !== "present"
|
|
201
|
+
);
|
|
202
|
+
for (const r of missing) process.stderr.write(`${installWarning(r)}
|
|
203
|
+
`);
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
main(process.argv.slice(2));
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
process.exitCode = 0;
|