@cabane/companion 0.6.0 → 0.6.2
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 +63 -29
- package/dist/cli.js +662 -1823
- package/dist/pairing-config.js +29 -53
- package/dist/runtime.js +490 -1585
- package/dist/static/index.html +2 -3
- package/package.json +2 -2
package/dist/pairing-config.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "fs";
|
|
11
11
|
import { homedir, userInfo } from "os";
|
|
12
12
|
import { dirname, join } from "path";
|
|
13
|
-
import { z as
|
|
13
|
+
import { z as z2 } from "zod";
|
|
14
14
|
|
|
15
15
|
// src/errors.ts
|
|
16
16
|
var CompanionError = class extends Error {
|
|
@@ -37,9 +37,6 @@ var ConfigError = class extends CompanionError {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
// src/pairing.ts
|
|
40
|
-
import { z } from "zod";
|
|
41
|
-
var PAIRING_VERSION = 1;
|
|
42
|
-
var DEVICE_TOKEN_PREFIX = "cabdev_";
|
|
43
40
|
function isAllowedBaseUrl(raw) {
|
|
44
41
|
let url;
|
|
45
42
|
try {
|
|
@@ -54,45 +51,24 @@ function isAllowedBaseUrl(raw) {
|
|
|
54
51
|
}
|
|
55
52
|
return false;
|
|
56
53
|
}
|
|
57
|
-
var pairingSchema = z.object({
|
|
58
|
-
// Bumped if the wire shape changes incompatibly. We only accept v1.
|
|
59
|
-
v: z.literal(PAIRING_VERSION),
|
|
60
|
-
baseUrl: z.string().url().refine(isAllowedBaseUrl, {
|
|
61
|
-
message: "baseUrl must be https (loopback http is allowed for local dev only)"
|
|
62
|
-
}),
|
|
63
|
-
// The `cabdev_` device token plaintext — the companion's one durable credential.
|
|
64
|
-
deviceToken: z.string().min(1).startsWith(DEVICE_TOKEN_PREFIX, {
|
|
65
|
-
message: 'deviceToken must be a cabane device token (starts with "cabdev_")'
|
|
66
|
-
}),
|
|
67
|
-
// Optional identity hints the app may include for nicer local display. The
|
|
68
|
-
// companion also learns these from the first assignments pull, so they're not
|
|
69
|
-
// required.
|
|
70
|
-
deviceId: z.string().min(1).optional(),
|
|
71
|
-
deviceLabel: z.string().min(1).optional()
|
|
72
|
-
});
|
|
73
54
|
|
|
74
55
|
// src/prepare-hook.ts
|
|
75
56
|
import { spawn } from "child_process";
|
|
76
|
-
import { z
|
|
77
|
-
var prepareHookSchema =
|
|
78
|
-
command:
|
|
79
|
-
args:
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
var prepareHookSchema = z.object({
|
|
59
|
+
command: z.string().min(1),
|
|
60
|
+
args: z.array(z.string()).optional(),
|
|
80
61
|
// Extra env handed to the hook process itself (merged over process.env).
|
|
81
|
-
env:
|
|
62
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
82
63
|
// Wall-clock cap for the hook. Provisioning is slow (minutes), so the
|
|
83
64
|
// default is generous; a hook that hangs past this is killed and the turn
|
|
84
65
|
// fails with a clear timeout message rather than pinning the companion.
|
|
85
|
-
timeoutMs:
|
|
66
|
+
timeoutMs: z.number().int().positive().optional()
|
|
86
67
|
}).strict();
|
|
87
68
|
var DEFAULT_TIMEOUT_MS = 10 * 6e4;
|
|
88
|
-
var prepareResultSchema =
|
|
89
|
-
cwd:
|
|
90
|
-
env:
|
|
91
|
-
nativeWorkAssignment: z2.object({
|
|
92
|
-
itemId: z2.string(),
|
|
93
|
-
executionId: z2.string(),
|
|
94
|
-
activationEpoch: z2.number().int().nonnegative()
|
|
95
|
-
}).strict().optional()
|
|
69
|
+
var prepareResultSchema = z.object({
|
|
70
|
+
cwd: z.string().min(1),
|
|
71
|
+
env: z.record(z.string(), z.string()).optional()
|
|
96
72
|
});
|
|
97
73
|
|
|
98
74
|
// src/config.ts
|
|
@@ -122,8 +98,8 @@ function cabaneDir() {
|
|
|
122
98
|
function configPath() {
|
|
123
99
|
return join(cabaneDir(), "config.json");
|
|
124
100
|
}
|
|
125
|
-
var localAgentConfigSchema =
|
|
126
|
-
cwd:
|
|
101
|
+
var localAgentConfigSchema = z2.object({
|
|
102
|
+
cwd: z2.string().optional(),
|
|
127
103
|
prepareHook: prepareHookSchema.optional(),
|
|
128
104
|
// CT289: the Claude Code auto-memory escape hatch. Auto-memory is forced OFF
|
|
129
105
|
// by default on every companion (memory belongs in the Cabane workspace, and a
|
|
@@ -132,13 +108,13 @@ var localAgentConfigSchema = z3.object({
|
|
|
132
108
|
// auto-memory back to your own `~/.claude/settings.json` — Cabane then stops
|
|
133
109
|
// injecting the off switch and your normal Claude Code memory workflow applies
|
|
134
110
|
// (in coding mode, where the checkout's project settings are read).
|
|
135
|
-
claudeCode:
|
|
111
|
+
claudeCode: z2.object({ autoMemory: z2.boolean().optional() }).strict().optional()
|
|
136
112
|
}).strict();
|
|
137
|
-
var companionConfigSchema =
|
|
113
|
+
var companionConfigSchema = z2.object({
|
|
138
114
|
// The cabane instance this device is paired with. SJ515: https-enforced
|
|
139
115
|
// (loopback exempt) so a hand-edited config can't smuggle a plaintext-http
|
|
140
116
|
// base URL onto the MITM-able channel the device token + prompt ride.
|
|
141
|
-
baseUrl:
|
|
117
|
+
baseUrl: z2.string().url().refine(isAllowedBaseUrl, {
|
|
142
118
|
message: "baseUrl must be https (loopback http is allowed for local dev only)"
|
|
143
119
|
}),
|
|
144
120
|
// The `cabdev_` device token plaintext — the companion's one durable credential,
|
|
@@ -146,22 +122,22 @@ var companionConfigSchema = z3.object({
|
|
|
146
122
|
// heartbeat endpoints. Optional so `cabane-companion logout` can strip it (a
|
|
147
123
|
// "paired but logged out" state the supervisor refuses to run) while keeping
|
|
148
124
|
// the rest of the config; `pair` always writes one.
|
|
149
|
-
deviceToken:
|
|
150
|
-
// Identity hints, learned from the
|
|
125
|
+
deviceToken: z2.string().optional(),
|
|
126
|
+
// Identity hints, learned from the device flow and refreshed on the first
|
|
151
127
|
// assignments pull. Cosmetic — used for `status`/dashboard display only.
|
|
152
|
-
deviceId:
|
|
153
|
-
deviceLabel:
|
|
128
|
+
deviceId: z2.string().optional(),
|
|
129
|
+
deviceLabel: z2.string().optional(),
|
|
154
130
|
// Optional per-agent machine-local overrides (cwd / prepareHook), keyed by
|
|
155
131
|
// agentId / username / `slug/username`. Hand-added by the operator; the companion
|
|
156
132
|
// never writes this (it only persists credentials + the device, elsewhere).
|
|
157
|
-
agents:
|
|
133
|
+
agents: z2.record(z2.string(), localAgentConfigSchema).optional(),
|
|
158
134
|
// Dashboard settings (all optional). dashboardPort: preferred bind port (next
|
|
159
135
|
// free one if taken); autoOpen: whether `start` opens the browser (the
|
|
160
136
|
// `--no-open` flag / `COMPANION_NO_OPEN=1` override per-run); logLevel: pino
|
|
161
137
|
// level, live-editable from the dashboard settings panel.
|
|
162
|
-
dashboardPort:
|
|
163
|
-
autoOpen:
|
|
164
|
-
logLevel:
|
|
138
|
+
dashboardPort: z2.number().int().min(1).max(65535).optional(),
|
|
139
|
+
autoOpen: z2.boolean().optional(),
|
|
140
|
+
logLevel: z2.enum(["warn", "info", "debug"]).optional(),
|
|
165
141
|
// CT270: the opencode runtime, when the operator runs one on this machine. The
|
|
166
142
|
// operator installs opencode, starts `opencode serve` (auth via opencode's own
|
|
167
143
|
// `/connect` — Cabane never sees provider keys), and points the companion at it
|
|
@@ -169,8 +145,8 @@ var companionConfigSchema = z3.object({
|
|
|
169
145
|
// heartbeat manifest (so the server offers DeepSeek/opencode models here and
|
|
170
146
|
// routes those turns to this device) AND registers the opencode adapter in the
|
|
171
147
|
// dispatcher. Absent → the device is claude-code-only, exactly as before.
|
|
172
|
-
opencode:
|
|
173
|
-
serverUrl:
|
|
148
|
+
opencode: z2.object({
|
|
149
|
+
serverUrl: z2.string().url()
|
|
174
150
|
}).strict().optional(),
|
|
175
151
|
// CT481: the codex runtime, when the operator runs Codex on this machine. Unlike
|
|
176
152
|
// opencode (a long-lived `opencode serve` addressed by URL), Codex is a local CLI
|
|
@@ -181,8 +157,8 @@ var companionConfigSchema = z3.object({
|
|
|
181
157
|
// keeps the block but turns it off). Enabling makes the device advertise the
|
|
182
158
|
// `codex` runtime on its heartbeat manifest AND registers the codex adapter in
|
|
183
159
|
// the dispatcher. Absent → the device doesn't offer codex, exactly as before.
|
|
184
|
-
codex:
|
|
185
|
-
enabled:
|
|
160
|
+
codex: z2.object({
|
|
161
|
+
enabled: z2.boolean().optional()
|
|
186
162
|
}).strict().optional()
|
|
187
163
|
});
|
|
188
164
|
function loadConfig() {
|
|
@@ -251,7 +227,7 @@ function loadConfigTolerant() {
|
|
|
251
227
|
}
|
|
252
228
|
const obj = parsed && typeof parsed === "object" ? parsed : {};
|
|
253
229
|
const local = {};
|
|
254
|
-
const agents =
|
|
230
|
+
const agents = z2.record(z2.string(), localAgentConfigSchema).safeParse(obj.agents);
|
|
255
231
|
if (agents.success) local.agents = agents.data;
|
|
256
232
|
if (typeof obj.dashboardPort === "number") local.dashboardPort = obj.dashboardPort;
|
|
257
233
|
if (typeof obj.autoOpen === "boolean") local.autoOpen = obj.autoOpen;
|
|
@@ -316,7 +292,7 @@ async function postJson(baseUrl, path, body) {
|
|
|
316
292
|
if (res.status >= 400) {
|
|
317
293
|
if (res.status === 404 && path.endsWith("/code")) {
|
|
318
294
|
throw new CompanionError(
|
|
319
|
-
`this cabane server (${baseUrl}) doesn't support device-flow pairing yet. Update the server
|
|
295
|
+
`this cabane server (${baseUrl}) doesn't support device-flow pairing yet. Update the server to a version that supports \`cabane-companion pair\`.`
|
|
320
296
|
);
|
|
321
297
|
}
|
|
322
298
|
const msg = parsed && typeof parsed === "object" && "error" in parsed ? String(parsed.error) : `${res.status}`;
|