@cabane/companion 0.6.236 → 0.6.238
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 +8 -8
- package/dist/bin.js +5 -1
- package/dist/cli.js +129 -294
- package/dist/launch.js +3 -4
- package/dist/postinstall.js +338 -16
- package/package.json +1 -3
- package/dist/pairing-config.js +0 -566
- package/dist/runtime.js +0 -9642
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,13 @@ import { realpathSync } from "fs";
|
|
|
14
14
|
import { homedir } from "os";
|
|
15
15
|
import { dirname, join, sep } from "path";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
|
+
function installScriptCommand(address) {
|
|
18
|
+
return `curl -fsSL ${address.replace(/\/+$/, "")}/install.sh | bash`;
|
|
19
|
+
}
|
|
20
|
+
function installLineWords(install = companionInstall()) {
|
|
21
|
+
const line = install === "script" ? "installer line" : "npm line";
|
|
22
|
+
return `the ${line} from Your settings \u2192 Devices \u2192 Add a device in your Cabane`;
|
|
23
|
+
}
|
|
17
24
|
function companionInstall(execPath = process.execPath, home = homedir()) {
|
|
18
25
|
const real = (path) => {
|
|
19
26
|
try {
|
|
@@ -36,11 +43,9 @@ function repairRoute(packageRoot = ownRoot(), install = companionInstall()) {
|
|
|
36
43
|
function ownRoot() {
|
|
37
44
|
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
38
45
|
}
|
|
39
|
-
var INSTALL_SCRIPT_COMMAND;
|
|
40
46
|
var init_install_kind = __esm({
|
|
41
47
|
"src/install-kind.ts"() {
|
|
42
48
|
"use strict";
|
|
43
|
-
INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
|
|
44
49
|
}
|
|
45
50
|
});
|
|
46
51
|
|
|
@@ -222,7 +227,7 @@ var init_cabane_command = __esm({
|
|
|
222
227
|
});
|
|
223
228
|
|
|
224
229
|
// src/errors.ts
|
|
225
|
-
var CompanionError, ApiError, EdgeBlockError, ConfigError, PrepareHookError;
|
|
230
|
+
var CompanionError, StandaloneError, ApiError, EdgeBlockError, ConfigError, PrepareHookError;
|
|
226
231
|
var init_errors = __esm({
|
|
227
232
|
"src/errors.ts"() {
|
|
228
233
|
"use strict";
|
|
@@ -232,6 +237,12 @@ var init_errors = __esm({
|
|
|
232
237
|
this.name = "CompanionError";
|
|
233
238
|
}
|
|
234
239
|
};
|
|
240
|
+
StandaloneError = class extends CompanionError {
|
|
241
|
+
constructor(message2) {
|
|
242
|
+
super(message2);
|
|
243
|
+
this.name = "StandaloneError";
|
|
244
|
+
}
|
|
245
|
+
};
|
|
235
246
|
ApiError = class extends CompanionError {
|
|
236
247
|
constructor(status2, message2, body) {
|
|
237
248
|
super(message2);
|
|
@@ -603,31 +614,13 @@ function loadConfigTolerant() {
|
|
|
603
614
|
const strict = companionConfigSchema.safeParse(parsed);
|
|
604
615
|
if (strict.success) {
|
|
605
616
|
const {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
logLevel,
|
|
612
|
-
logFormat,
|
|
613
|
-
harnesses: harnesses2,
|
|
614
|
-
opencode: opencode2
|
|
617
|
+
baseUrl: _baseUrl,
|
|
618
|
+
deviceToken: _deviceToken,
|
|
619
|
+
deviceId: _deviceId,
|
|
620
|
+
deviceLabel: _deviceLabel,
|
|
621
|
+
...local2
|
|
615
622
|
} = strict.data;
|
|
616
|
-
return {
|
|
617
|
-
local: {
|
|
618
|
-
...agents2 !== void 0 ? { agents: agents2 } : {},
|
|
619
|
-
...prepareHook2 !== void 0 ? { prepareHook: prepareHook2 } : {},
|
|
620
|
-
...root2 !== void 0 ? { root: root2 } : {},
|
|
621
|
-
...dashboardPort !== void 0 ? { dashboardPort } : {},
|
|
622
|
-
...autoOpen !== void 0 ? { autoOpen } : {},
|
|
623
|
-
...logLevel !== void 0 ? { logLevel } : {},
|
|
624
|
-
...logFormat !== void 0 ? { logFormat } : {},
|
|
625
|
-
...harnesses2 !== void 0 ? { harnesses: harnesses2 } : {},
|
|
626
|
-
...opencode2 !== void 0 ? { opencode: opencode2 } : {}
|
|
627
|
-
},
|
|
628
|
-
note: null,
|
|
629
|
-
hadPriorConfig: true
|
|
630
|
-
};
|
|
623
|
+
return { local: local2, note: null, hadPriorConfig: true };
|
|
631
624
|
}
|
|
632
625
|
const obj = parsed && typeof parsed === "object" ? parsed : {};
|
|
633
626
|
const local = {};
|
|
@@ -647,6 +640,11 @@ function loadConfigTolerant() {
|
|
|
647
640
|
if (harnesses.success && harnesses.data !== void 0) local.harnesses = harnesses.data;
|
|
648
641
|
const opencode = companionConfigSchema.shape.opencode.safeParse(obj.opencode);
|
|
649
642
|
if (opencode.success && opencode.data !== void 0) local.opencode = opencode.data;
|
|
643
|
+
const update2 = companionConfigSchema.shape.update.safeParse(obj.update);
|
|
644
|
+
if (update2.success && update2.data !== void 0) local.update = update2.data;
|
|
645
|
+
if (typeof obj.serviceDeclined === "boolean") local.serviceDeclined = obj.serviceDeclined;
|
|
646
|
+
const runner = companionConfigSchema.shape.runner.safeParse(obj.runner);
|
|
647
|
+
if (runner.success && runner.data !== void 0) local.runner = runner.data;
|
|
650
648
|
return {
|
|
651
649
|
local,
|
|
652
650
|
note: `the existing ${path} was from an older or incompatible companion; re-pairing rewrote it.`,
|
|
@@ -726,7 +724,7 @@ var init_config = __esm({
|
|
|
726
724
|
// the rest of the config; `pair` always writes one.
|
|
727
725
|
deviceToken: z2.string().optional(),
|
|
728
726
|
// Identity hints, learned from the device flow and refreshed on the first
|
|
729
|
-
// assignments pull. Cosmetic — used for `status
|
|
727
|
+
// assignments pull. Cosmetic — used for `status` display only.
|
|
730
728
|
deviceId: z2.string().optional(),
|
|
731
729
|
deviceLabel: z2.string().optional(),
|
|
732
730
|
// Optional per-agent machine-local overrides (cwd / prepareHook), keyed by
|
|
@@ -738,9 +736,8 @@ var init_config = __esm({
|
|
|
738
736
|
// silent roster: an agent nobody listed gets no hook and no error, and starts
|
|
739
737
|
// in the operator's `$HOME`.
|
|
740
738
|
prepareHook: prepareHookSchema.optional(),
|
|
741
|
-
//
|
|
742
|
-
//
|
|
743
|
-
// startup. `logLevel` is pino's level, live-editable.
|
|
739
|
+
// Nothing reads either dashboard setting any more; both stay parseable so an
|
|
740
|
+
// older config file still loads. `logLevel` is pino's level, live-editable.
|
|
744
741
|
dashboardPort: z2.number().int().min(1).max(65535).optional(),
|
|
745
742
|
autoOpen: z2.boolean().optional(),
|
|
746
743
|
logLevel: z2.enum(["warn", "info", "debug"]).optional(),
|
|
@@ -958,18 +955,32 @@ function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
|
|
|
958
955
|
function missingNoun(status2) {
|
|
959
956
|
return status2 === "executable_missing" ? "executable" : "platform package";
|
|
960
957
|
}
|
|
961
|
-
function repairCommands(route = repairRoute(), spell = cabane) {
|
|
958
|
+
function repairCommands(route = repairRoute(), spell = cabane, address = configuredAddress()) {
|
|
962
959
|
if (route === "managed") return [spell("stop"), spell("update")];
|
|
963
|
-
if (route === "script")
|
|
960
|
+
if (route === "script")
|
|
961
|
+
return [
|
|
962
|
+
spell("stop"),
|
|
963
|
+
address ? installScriptCommand(address) : { words: installLineWords("script") }
|
|
964
|
+
];
|
|
964
965
|
return ["npm i -g @cabane/companion --include=optional"];
|
|
965
966
|
}
|
|
966
|
-
function
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
967
|
+
function configuredAddress() {
|
|
968
|
+
try {
|
|
969
|
+
return loadConfig()?.baseUrl ?? null;
|
|
970
|
+
} catch {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function quoteStep(step, quote) {
|
|
975
|
+
return typeof step === "string" ? quote(step) : step.words;
|
|
976
|
+
}
|
|
977
|
+
function repairAction(route = repairRoute(), spell = cabane, address = configuredAddress()) {
|
|
978
|
+
const [first, ...rest] = repairCommands(route, spell, address);
|
|
979
|
+
if (rest.length === 0) return `Reinstall with ${quoteStep(first, backticks)}.`;
|
|
980
|
+
return `Run ${quoteStep(first, backticks)}, then ${rest.map((step) => quoteStep(step, backticks)).join(", then ")}. ${STOP_WARNING}`;
|
|
970
981
|
}
|
|
971
982
|
function repairSteps(route = repairRoute()) {
|
|
972
|
-
return repairCommands(route).join(", then ");
|
|
983
|
+
return repairCommands(route).map((step) => quoteStep(step, (command) => command)).join(", then ");
|
|
973
984
|
}
|
|
974
985
|
function repairCost(route = repairRoute()) {
|
|
975
986
|
return repairCommands(route).length > 1 ? STOP_WARNING : "";
|
|
@@ -981,11 +992,12 @@ function startupWarning(r) {
|
|
|
981
992
|
function turnIncompleteCopy(runtime) {
|
|
982
993
|
return `**This device's ${HARNESS_LABEL[runtime]} runtime is incomplete.** ${repairAction()} Restart it, then try again.`;
|
|
983
994
|
}
|
|
984
|
-
var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD, STOP_WARNING;
|
|
995
|
+
var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD, backticks, STOP_WARNING;
|
|
985
996
|
var init_bundled_binaries = __esm({
|
|
986
997
|
"src/bundled-binaries.ts"() {
|
|
987
998
|
"use strict";
|
|
988
999
|
init_cabane_command();
|
|
1000
|
+
init_config();
|
|
989
1001
|
init_install_kind();
|
|
990
1002
|
CLAUDE_SDK = "@anthropic-ai/claude-agent-sdk";
|
|
991
1003
|
CODEX_SDK = "@openai/codex-sdk";
|
|
@@ -1006,6 +1018,7 @@ var init_bundled_binaries = __esm({
|
|
|
1006
1018
|
"claude-code": "connected",
|
|
1007
1019
|
codex: "enabled"
|
|
1008
1020
|
};
|
|
1021
|
+
backticks = (command) => `\`${command}\``;
|
|
1009
1022
|
STOP_WARNING = "Stopping ends any turn in progress.";
|
|
1010
1023
|
}
|
|
1011
1024
|
});
|
|
@@ -7420,10 +7433,6 @@ var init_turn_committer = __esm({
|
|
|
7420
7433
|
get lastMessageSeq() {
|
|
7421
7434
|
return this.pump.lastMessageSeq;
|
|
7422
7435
|
}
|
|
7423
|
-
// The turn's final reply text, or null if none landed.
|
|
7424
|
-
get replyBody() {
|
|
7425
|
-
return this.pump.replyBody;
|
|
7426
|
-
}
|
|
7427
7436
|
get finalSource() {
|
|
7428
7437
|
return this.pump.finalSource;
|
|
7429
7438
|
}
|
|
@@ -7801,7 +7810,6 @@ var init_turn_execution = __esm({
|
|
|
7801
7810
|
);
|
|
7802
7811
|
}
|
|
7803
7812
|
const durationMs = Date.now() - startedAt;
|
|
7804
|
-
this.supervisor.notifyEnd({ id: payload.messageId, ok: false, durationMs, reason });
|
|
7805
7813
|
return { ok: false, durationMs, reason, runtime: this.turnContext?.runtime };
|
|
7806
7814
|
}
|
|
7807
7815
|
// The admitted turn holds the conversation's lease, so the clear must NAME the
|
|
@@ -7840,7 +7848,6 @@ var init_turn_execution = __esm({
|
|
|
7840
7848
|
}
|
|
7841
7849
|
}
|
|
7842
7850
|
const durationMs = Date.now() - startedAt;
|
|
7843
|
-
this.supervisor.notifyEnd({ id: payload.messageId, ok: false, durationMs, reason });
|
|
7844
7851
|
return { ok: false, durationMs, reason, runtime: this.turnContext?.runtime };
|
|
7845
7852
|
}
|
|
7846
7853
|
async fetchContext() {
|
|
@@ -7949,11 +7956,7 @@ var init_turn_execution = __esm({
|
|
|
7949
7956
|
turnLog.debug({ role: message2.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
|
|
7950
7957
|
throw this.concluded("unexpected_role", UNEXPECTED_ROLE_REASON);
|
|
7951
7958
|
}
|
|
7952
|
-
this.supervisor.notifyStart({
|
|
7953
|
-
id: this.dispatchId,
|
|
7954
|
-
conversationId: payload.conversationId,
|
|
7955
|
-
message: message2.body
|
|
7956
|
-
});
|
|
7959
|
+
this.supervisor.notifyStart({ id: this.dispatchId, conversationId: payload.conversationId });
|
|
7957
7960
|
}
|
|
7958
7961
|
async resolveSecrets() {
|
|
7959
7962
|
const { turnLog } = this;
|
|
@@ -8722,7 +8725,6 @@ var init_turn_execution = __esm({
|
|
|
8722
8725
|
const o = this.outcome;
|
|
8723
8726
|
const { startedAt, turnLog } = this;
|
|
8724
8727
|
const durationMs = Date.now() - startedAt;
|
|
8725
|
-
const finalReplyBody = this.committer.replyBody;
|
|
8726
8728
|
if (this.transcript) {
|
|
8727
8729
|
this.transcript.close({
|
|
8728
8730
|
ok: o.okResult,
|
|
@@ -8735,21 +8737,9 @@ var init_turn_execution = __esm({
|
|
|
8735
8737
|
}
|
|
8736
8738
|
if (o.okResult) {
|
|
8737
8739
|
turnLog.debug({ durationMs }, "dispatcher: turn end (ok)");
|
|
8738
|
-
this.supervisor.notifyEnd({
|
|
8739
|
-
id: this.dispatchId,
|
|
8740
|
-
ok: true,
|
|
8741
|
-
durationMs,
|
|
8742
|
-
...finalReplyBody ? { reply: finalReplyBody.trim() } : {}
|
|
8743
|
-
});
|
|
8744
8740
|
return { ok: true, durationMs };
|
|
8745
8741
|
}
|
|
8746
8742
|
turnLog.debug({ durationMs, reason: o.resultReason }, "dispatcher: turn end (not ok)");
|
|
8747
|
-
this.supervisor.notifyEnd({
|
|
8748
|
-
id: this.dispatchId,
|
|
8749
|
-
ok: false,
|
|
8750
|
-
durationMs,
|
|
8751
|
-
...o.resultReason ? { reason: o.resultReason } : {}
|
|
8752
|
-
});
|
|
8753
8743
|
return {
|
|
8754
8744
|
ok: false,
|
|
8755
8745
|
durationMs,
|
|
@@ -9255,8 +9245,7 @@ async function runTurn(env, deps) {
|
|
|
9255
9245
|
emit({ cabaneRunner: "session_write_notice" });
|
|
9256
9246
|
return true;
|
|
9257
9247
|
},
|
|
9258
|
-
notifyStart: (info) => emit({ cabaneRunner: "start", ...info })
|
|
9259
|
-
notifyEnd: (info) => emit({ cabaneRunner: "end", ...info })
|
|
9248
|
+
notifyStart: (info) => emit({ cabaneRunner: "start", ...info })
|
|
9260
9249
|
};
|
|
9261
9250
|
let outbox;
|
|
9262
9251
|
const opts = runnerTurnOptions(input, log2, emit, {
|
|
@@ -9959,6 +9948,7 @@ import { hostname } from "os";
|
|
|
9959
9948
|
|
|
9960
9949
|
// src/enrollment.ts
|
|
9961
9950
|
init_errors();
|
|
9951
|
+
init_install_kind();
|
|
9962
9952
|
var EnrollmentExpiredError = class extends CompanionError {
|
|
9963
9953
|
constructor() {
|
|
9964
9954
|
super("this pairing code expired before it was confirmed.");
|
|
@@ -10012,8 +10002,9 @@ async function postJson(baseUrl, path, body, signal) {
|
|
|
10012
10002
|
"Too many pairing requests from this network. Wait a few minutes and run cabane setup again."
|
|
10013
10003
|
);
|
|
10014
10004
|
if (res.status === 404 && path.endsWith("/code"))
|
|
10015
|
-
throw new
|
|
10016
|
-
`No pairing endpoint at ${baseUrl}
|
|
10005
|
+
throw new StandaloneError(
|
|
10006
|
+
`No pairing endpoint at ${baseUrl}: it isn't a Cabane's address, or that Cabane is too old for this companion.
|
|
10007
|
+
Run ${installLineWords()}.`
|
|
10017
10008
|
);
|
|
10018
10009
|
const msg = parsed && typeof parsed === "object" && "error" in parsed ? String(parsed.error) : `${res.status}`;
|
|
10019
10010
|
throw new ApiError(res.status, `${res.status} ${msg}`, parsed);
|
|
@@ -10198,6 +10189,7 @@ async function pairDevice(opts, deps = {}) {
|
|
|
10198
10189
|
process.exitCode = 130;
|
|
10199
10190
|
return null;
|
|
10200
10191
|
}
|
|
10192
|
+
if (!current) opts.onCode?.();
|
|
10201
10193
|
current = code;
|
|
10202
10194
|
const minutes = Math.max(1, Math.round(code.expiresIn / 60));
|
|
10203
10195
|
const codeLines = ["", ` ${code.userCode}`, ` ${code.verificationUri}`, ""];
|
|
@@ -10304,10 +10296,10 @@ init_cabane_command();
|
|
|
10304
10296
|
init_config();
|
|
10305
10297
|
init_pairing();
|
|
10306
10298
|
init_errors();
|
|
10307
|
-
|
|
10308
|
-
var DEFAULT_BASE_URL = "https://app.cabane.ai";
|
|
10299
|
+
init_install_kind();
|
|
10309
10300
|
function resolvePairBaseUrl(server) {
|
|
10310
|
-
const raw = (server ?? process.env.CABANE_BASE_URL ??
|
|
10301
|
+
const raw = (server ?? process.env.CABANE_BASE_URL ?? "").trim();
|
|
10302
|
+
if (!raw) throw new NoPairAddressError();
|
|
10311
10303
|
if (!isAllowedBaseUrl(raw)) {
|
|
10312
10304
|
throw new CompanionError(
|
|
10313
10305
|
`invalid server URL "${raw}": must be an https URL (loopback http is allowed for local dev). Pass it as \`${cabane("setup --server https://your-cabane")}\`.`
|
|
@@ -10315,6 +10307,20 @@ function resolvePairBaseUrl(server) {
|
|
|
10315
10307
|
}
|
|
10316
10308
|
return raw;
|
|
10317
10309
|
}
|
|
10310
|
+
var NoPairAddressError = class extends StandaloneError {
|
|
10311
|
+
constructor(install = companionInstall()) {
|
|
10312
|
+
const words = installLineWords(install);
|
|
10313
|
+
super(
|
|
10314
|
+
`Setup doesn't know which Cabane to pair this device with. Run the
|
|
10315
|
+
${words.slice("the ".length)}.`
|
|
10316
|
+
);
|
|
10317
|
+
this.name = "NoPairAddressError";
|
|
10318
|
+
}
|
|
10319
|
+
};
|
|
10320
|
+
function rememberPairAddress(baseUrl) {
|
|
10321
|
+
const { local } = loadConfigTolerant();
|
|
10322
|
+
saveConfig({ ...local, baseUrl });
|
|
10323
|
+
}
|
|
10318
10324
|
function writePairedConfig(paired) {
|
|
10319
10325
|
const { local, note: note2 } = loadConfigTolerant();
|
|
10320
10326
|
const config = {
|
|
@@ -10339,7 +10345,6 @@ function isDevicePaired() {
|
|
|
10339
10345
|
init_config();
|
|
10340
10346
|
init_cabane_command();
|
|
10341
10347
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10342
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10343
10348
|
|
|
10344
10349
|
// src/prereqs.ts
|
|
10345
10350
|
init_config();
|
|
@@ -10356,71 +10361,37 @@ function warnAboutHarnessReadiness(cfg, deps = {}) {
|
|
|
10356
10361
|
}
|
|
10357
10362
|
|
|
10358
10363
|
// src/state.ts
|
|
10359
|
-
import { EventEmitter } from "events";
|
|
10360
|
-
function dispatchToWire(r) {
|
|
10361
|
-
return {
|
|
10362
|
-
id: r.id,
|
|
10363
|
-
timestamp: r.timestamp,
|
|
10364
|
-
workspace_slug: r.workspaceSlug,
|
|
10365
|
-
...r.agentUsername ? { agent_username: r.agentUsername } : {},
|
|
10366
|
-
message: r.message,
|
|
10367
|
-
status: r.status,
|
|
10368
|
-
...r.durationMs !== void 0 ? { duration_ms: r.durationMs } : {},
|
|
10369
|
-
...r.error ? { error: r.error } : {},
|
|
10370
|
-
...r.reply ? { reply: r.reply } : {}
|
|
10371
|
-
};
|
|
10372
|
-
}
|
|
10373
|
-
var MAX_DISPATCHES = 50;
|
|
10374
10364
|
function today() {
|
|
10375
10365
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10376
10366
|
}
|
|
10377
10367
|
var CompanionStateHub = class {
|
|
10378
10368
|
constructor(opts) {
|
|
10379
10369
|
this.opts = opts;
|
|
10380
|
-
this.emitter.setMaxListeners(0);
|
|
10381
10370
|
}
|
|
10382
10371
|
opts;
|
|
10383
|
-
emitter = new EventEmitter();
|
|
10384
10372
|
workspaces = /* @__PURE__ */ new Map();
|
|
10385
|
-
dispatches = [];
|
|
10386
|
-
dashboardUrl = null;
|
|
10387
10373
|
deviceId = null;
|
|
10388
10374
|
deviceLabel = null;
|
|
10389
10375
|
deviceError = null;
|
|
10390
10376
|
// The latest harness snapshot the supervisor probed, or null before the
|
|
10391
10377
|
// first probe (see CompanionStatusJson.harnesses).
|
|
10392
10378
|
harnesses = null;
|
|
10393
|
-
// ---- subscription (SSE) ----
|
|
10394
|
-
on(listener) {
|
|
10395
|
-
this.emitter.on("event", listener);
|
|
10396
|
-
return () => this.emitter.off("event", listener);
|
|
10397
|
-
}
|
|
10398
|
-
emit(type, data) {
|
|
10399
|
-
this.emitter.emit("event", { type, data });
|
|
10400
|
-
}
|
|
10401
10379
|
// ---- device ----
|
|
10402
10380
|
setDevice(info) {
|
|
10403
10381
|
if (info.deviceId !== void 0) this.deviceId = info.deviceId;
|
|
10404
10382
|
if (info.deviceLabel !== void 0) this.deviceLabel = info.deviceLabel;
|
|
10405
|
-
this.emitStatus();
|
|
10406
10383
|
}
|
|
10407
10384
|
setDeviceError(message2) {
|
|
10408
|
-
if (this.deviceError === message2) return;
|
|
10409
10385
|
this.deviceError = message2;
|
|
10410
|
-
this.emitStatus();
|
|
10411
10386
|
}
|
|
10412
10387
|
clearDeviceError() {
|
|
10413
|
-
if (this.deviceError === null) return;
|
|
10414
10388
|
this.deviceError = null;
|
|
10415
|
-
this.emitStatus();
|
|
10416
10389
|
}
|
|
10417
10390
|
// ---- harnesses ----
|
|
10418
|
-
// Replace the harness snapshot
|
|
10419
|
-
// the Companion's Harnesses surface live). The supervisor calls this on each
|
|
10391
|
+
// Replace the harness snapshot. The supervisor calls this on each
|
|
10420
10392
|
// heartbeat-cadence probe and on an on-demand recheck.
|
|
10421
10393
|
setHarnesses(harnesses) {
|
|
10422
10394
|
this.harnesses = harnesses;
|
|
10423
|
-
this.emitStatus();
|
|
10424
10395
|
}
|
|
10425
10396
|
// ---- workspace lifecycle ----
|
|
10426
10397
|
registerWorkspace(ws) {
|
|
@@ -10441,29 +10412,21 @@ var CompanionStateHub = class {
|
|
|
10441
10412
|
countDate: today(),
|
|
10442
10413
|
agents: /* @__PURE__ */ new Map()
|
|
10443
10414
|
});
|
|
10444
|
-
this.emitStatus();
|
|
10445
10415
|
}
|
|
10446
10416
|
removeWorkspace(workspaceId) {
|
|
10447
|
-
const ws = this.workspaces.get(workspaceId);
|
|
10448
|
-
if (!ws) return;
|
|
10449
10417
|
this.workspaces.delete(workspaceId);
|
|
10450
|
-
this.emit("workspace:disconnected", { slug: ws.slug });
|
|
10451
|
-
this.emitStatus();
|
|
10452
10418
|
}
|
|
10453
10419
|
setConnected(workspaceId, connected) {
|
|
10454
10420
|
const ws = this.workspaces.get(workspaceId);
|
|
10455
10421
|
if (!ws || ws.connected === connected) return;
|
|
10456
10422
|
ws.connected = connected;
|
|
10457
10423
|
if (connected) ws.authFailed = false;
|
|
10458
|
-
this.emitStatus();
|
|
10459
10424
|
}
|
|
10460
10425
|
setAuthFailed(workspaceId) {
|
|
10461
10426
|
const ws = this.workspaces.get(workspaceId);
|
|
10462
10427
|
if (!ws) return;
|
|
10463
|
-
if (ws.authFailed && !ws.connected) return;
|
|
10464
10428
|
ws.authFailed = true;
|
|
10465
10429
|
ws.connected = false;
|
|
10466
|
-
this.emitStatus();
|
|
10467
10430
|
}
|
|
10468
10431
|
recordEvent(workspaceId) {
|
|
10469
10432
|
const ws = this.workspaces.get(workspaceId);
|
|
@@ -10476,22 +10439,15 @@ var CompanionStateHub = class {
|
|
|
10476
10439
|
const ws = this.workspaces.get(workspaceId);
|
|
10477
10440
|
if (!ws) return;
|
|
10478
10441
|
ws.agents.set(agent.agentId, agent);
|
|
10479
|
-
this.emitStatus();
|
|
10480
10442
|
}
|
|
10481
10443
|
removeAgent(workspaceId, agentId) {
|
|
10482
|
-
|
|
10483
|
-
if (!ws) return;
|
|
10484
|
-
if (ws.agents.delete(agentId)) this.emitStatus();
|
|
10444
|
+
this.workspaces.get(workspaceId)?.agents.delete(agentId);
|
|
10485
10445
|
}
|
|
10486
|
-
// ---- dispatch
|
|
10487
|
-
// A DispatchObserver bound to one
|
|
10488
|
-
//
|
|
10489
|
-
observerFor(workspaceId
|
|
10490
|
-
|
|
10491
|
-
return {
|
|
10492
|
-
onStart: (info) => this.dispatchStarted(workspaceId, slug, username(), info),
|
|
10493
|
-
onEnd: (info) => this.dispatchEnded(slug, username(), info)
|
|
10494
|
-
};
|
|
10446
|
+
// ---- dispatch count ----
|
|
10447
|
+
// A DispatchObserver bound to one workspace, handed to each of its agents'
|
|
10448
|
+
// Dispatchers by the supervisor: a turn counts toward the day when it starts.
|
|
10449
|
+
observerFor(workspaceId) {
|
|
10450
|
+
return { onStart: () => this.bumpCount(workspaceId) };
|
|
10495
10451
|
}
|
|
10496
10452
|
bumpCount(workspaceId) {
|
|
10497
10453
|
const ws = this.workspaces.get(workspaceId);
|
|
@@ -10503,52 +10459,6 @@ var CompanionStateHub = class {
|
|
|
10503
10459
|
}
|
|
10504
10460
|
ws.dispatchCountToday += 1;
|
|
10505
10461
|
}
|
|
10506
|
-
dispatchStarted(workspaceId, slug, agentUsername, info) {
|
|
10507
|
-
this.bumpCount(workspaceId);
|
|
10508
|
-
const record = {
|
|
10509
|
-
id: info.id,
|
|
10510
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10511
|
-
workspaceSlug: slug,
|
|
10512
|
-
...agentUsername ? { agentUsername } : {},
|
|
10513
|
-
message: info.message,
|
|
10514
|
-
status: "running"
|
|
10515
|
-
};
|
|
10516
|
-
this.upsertDispatch(record);
|
|
10517
|
-
this.emit("dispatch:started", dispatchToWire(record));
|
|
10518
|
-
this.emitStatus();
|
|
10519
|
-
}
|
|
10520
|
-
dispatchEnded(slug, agentUsername, info) {
|
|
10521
|
-
const existing = this.dispatches.find((d) => d.id === info.id);
|
|
10522
|
-
const record = {
|
|
10523
|
-
id: info.id,
|
|
10524
|
-
timestamp: existing?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10525
|
-
workspaceSlug: existing?.workspaceSlug ?? slug,
|
|
10526
|
-
...existing?.agentUsername ?? agentUsername ? { agentUsername: existing?.agentUsername ?? agentUsername } : {},
|
|
10527
|
-
message: existing?.message ?? "",
|
|
10528
|
-
status: info.ok ? "replied" : "error",
|
|
10529
|
-
durationMs: info.durationMs,
|
|
10530
|
-
...info.reason ? { error: info.reason } : {},
|
|
10531
|
-
...info.reply ? { reply: info.reply } : {}
|
|
10532
|
-
};
|
|
10533
|
-
this.upsertDispatch(record);
|
|
10534
|
-
this.emit(info.ok ? "dispatch:completed" : "dispatch:error", dispatchToWire(record));
|
|
10535
|
-
}
|
|
10536
|
-
upsertDispatch(record) {
|
|
10537
|
-
const idx = this.dispatches.findIndex((d) => d.id === record.id);
|
|
10538
|
-
if (idx >= 0) {
|
|
10539
|
-
this.dispatches[idx] = record;
|
|
10540
|
-
return;
|
|
10541
|
-
}
|
|
10542
|
-
this.dispatches.unshift(record);
|
|
10543
|
-
if (this.dispatches.length > MAX_DISPATCHES) this.dispatches.length = MAX_DISPATCHES;
|
|
10544
|
-
}
|
|
10545
|
-
// ---- snapshots for the HTTP routes ----
|
|
10546
|
-
setDashboardUrl(url) {
|
|
10547
|
-
this.dashboardUrl = url;
|
|
10548
|
-
}
|
|
10549
|
-
recentDispatches(limit = 20) {
|
|
10550
|
-
return this.dispatches.slice(0, Math.max(0, limit));
|
|
10551
|
-
}
|
|
10552
10462
|
statusJson() {
|
|
10553
10463
|
const list = [...this.workspaces.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
10554
10464
|
const lastOverall = list.reduce((acc, ws) => {
|
|
@@ -10578,15 +10488,11 @@ var CompanionStateHub = class {
|
|
|
10578
10488
|
}))
|
|
10579
10489
|
})),
|
|
10580
10490
|
last_event_at_overall: lastOverall,
|
|
10581
|
-
dashboard_url: this.dashboardUrl,
|
|
10582
10491
|
companion_version: this.opts.companionVersion,
|
|
10583
10492
|
instance_id: this.opts.instanceId ?? null,
|
|
10584
10493
|
harnesses: this.harnesses
|
|
10585
10494
|
};
|
|
10586
10495
|
}
|
|
10587
|
-
emitStatus() {
|
|
10588
|
-
this.emit("status:changed", this.statusJson());
|
|
10589
|
-
}
|
|
10590
10496
|
};
|
|
10591
10497
|
|
|
10592
10498
|
// src/supervisor.ts
|
|
@@ -10652,7 +10558,7 @@ function turnFailureCopy(reason, runtime) {
|
|
|
10652
10558
|
const stopsFirst = commands.length > 1;
|
|
10653
10559
|
if (!stopsFirst) commands.push("cabane stop");
|
|
10654
10560
|
commands.push("cabane start");
|
|
10655
|
-
const list = commands.map((command) => `\`${command}\``);
|
|
10561
|
+
const list = commands.map((step) => quoteStep(step, (command) => `\`${command}\``));
|
|
10656
10562
|
return `${harnessLabel(runtime ?? reason.slice("runtime_incomplete:".length))}'s installation on this device is incomplete. Run ${list.slice(0, -1).join(", ")} and ${list.at(-1)}. ${stopsFirst ? `${STOP_WARNING} ` : ""}Reply in Cabane to try again.`;
|
|
10657
10563
|
}
|
|
10658
10564
|
const unavailable = /^(codex|opencode)_unavailable:/.exec(reason ?? "")?.[1] ?? (reason?.startsWith("runtime_unavailable:") ? reason.slice("runtime_unavailable:".length).trim() : void 0);
|
|
@@ -11355,7 +11261,6 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
11355
11261
|
duties.releaseAbort(turnId, controller);
|
|
11356
11262
|
const durationMs2 = Date.now() - startedAt;
|
|
11357
11263
|
await settleUnstarted(opts, payload, turnId, minted.errorReason, log2);
|
|
11358
|
-
duties.notifyEnd({ id: payload.messageId, ok: false, durationMs: durationMs2, reason: minted.reason });
|
|
11359
11264
|
return { ok: false, durationMs: durationMs2, reason: minted.reason };
|
|
11360
11265
|
}
|
|
11361
11266
|
let result;
|
|
@@ -11386,7 +11291,6 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
11386
11291
|
reason: "provider_failed"
|
|
11387
11292
|
});
|
|
11388
11293
|
const durationMs2 = Date.now() - startedAt;
|
|
11389
|
-
duties.notifyEnd({ id: payload.messageId, ok: false, durationMs: durationMs2, reason: reason2 });
|
|
11390
11294
|
return { ok: false, durationMs: durationMs2, reason: reason2 };
|
|
11391
11295
|
}
|
|
11392
11296
|
try {
|
|
@@ -11450,17 +11354,8 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
11450
11354
|
}
|
|
11451
11355
|
return;
|
|
11452
11356
|
case "start":
|
|
11453
|
-
duties.notifyStart({
|
|
11454
|
-
id: event.id,
|
|
11455
|
-
conversationId: event.conversationId,
|
|
11456
|
-
message: event.message
|
|
11457
|
-
});
|
|
11357
|
+
duties.notifyStart({ id: event.id, conversationId: event.conversationId });
|
|
11458
11358
|
return;
|
|
11459
|
-
case "end": {
|
|
11460
|
-
const { cabaneRunner: _kind, ...info } = event;
|
|
11461
|
-
duties.notifyEnd(info);
|
|
11462
|
-
return;
|
|
11463
|
-
}
|
|
11464
11359
|
case "session_write_notice":
|
|
11465
11360
|
duties.markSessionWriteNotice();
|
|
11466
11361
|
return;
|
|
@@ -11564,7 +11459,6 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
11564
11459
|
if (thrown !== void 0) throw new Error(thrown);
|
|
11565
11460
|
if (stopped) log2.info({ turnId, code }, "runner: stopped before it could report");
|
|
11566
11461
|
else log2.warn({ turnId, code }, "runner: exited without reporting a result");
|
|
11567
|
-
duties.notifyEnd({ id: payload.messageId, ok: false, durationMs, reason });
|
|
11568
11462
|
return { ok: false, durationMs, reason };
|
|
11569
11463
|
}
|
|
11570
11464
|
|
|
@@ -11591,12 +11485,6 @@ var Dispatcher = class {
|
|
|
11591
11485
|
} catch {
|
|
11592
11486
|
}
|
|
11593
11487
|
}
|
|
11594
|
-
notifyEnd(info) {
|
|
11595
|
-
try {
|
|
11596
|
-
this.opts.observer?.onEnd(info);
|
|
11597
|
-
} catch {
|
|
11598
|
-
}
|
|
11599
|
-
}
|
|
11600
11488
|
// The supervisor supplies a stable turnId on both fresh and resumed dispatch.
|
|
11601
11489
|
// resumed is separate: only replay seeds sequence/speech recovery from the
|
|
11602
11490
|
// prior span. A direct caller omitting turnId gets a fresh id.
|
|
@@ -11624,8 +11512,7 @@ var Dispatcher = class {
|
|
|
11624
11512
|
markSessionWriteNotice: () => {
|
|
11625
11513
|
this.sessionWriteNotified.add(key);
|
|
11626
11514
|
},
|
|
11627
|
-
notifyStart: (info) => this.notifyStart(info)
|
|
11628
|
-
notifyEnd: (info) => this.notifyEnd(info)
|
|
11515
|
+
notifyStart: (info) => this.notifyStart(info)
|
|
11629
11516
|
},
|
|
11630
11517
|
payload,
|
|
11631
11518
|
{
|
|
@@ -12724,7 +12611,6 @@ var CompanionSupervisor = class {
|
|
|
12724
12611
|
runHarnessCheck;
|
|
12725
12612
|
probeOpencode;
|
|
12726
12613
|
exitFn;
|
|
12727
|
-
reexecFn;
|
|
12728
12614
|
dispatcherFactory;
|
|
12729
12615
|
runnerCommand;
|
|
12730
12616
|
runnerEnv;
|
|
@@ -12735,8 +12621,8 @@ var CompanionSupervisor = class {
|
|
|
12735
12621
|
host = hostReport();
|
|
12736
12622
|
deviceApi = null;
|
|
12737
12623
|
deviceSub = null;
|
|
12738
|
-
// Whether the device stream is open, which is what every workspace's link
|
|
12739
|
-
// the
|
|
12624
|
+
// Whether the device stream is open, which is what every workspace's link in
|
|
12625
|
+
// the status snapshot reports.
|
|
12740
12626
|
deviceConnected = false;
|
|
12741
12627
|
// Set when the device stream ended on a 403: the device was deactivated, not
|
|
12742
12628
|
// unpaired. The next accepted heartbeat proves it was reactivated, and
|
|
@@ -12797,7 +12683,6 @@ var CompanionSupervisor = class {
|
|
|
12797
12683
|
this.probeOpencode = opts.probeOpencode;
|
|
12798
12684
|
this.harnessVersions = opts.harnessVersions ?? emptyHarnessVersions();
|
|
12799
12685
|
this.exitFn = opts.exit ?? ((code) => process.exit(code));
|
|
12800
|
-
this.reexecFn = opts.reexec ?? defaultReexec;
|
|
12801
12686
|
this.restartForUpdate = opts.restartForUpdate ?? (async () => {
|
|
12802
12687
|
await this.shutdown();
|
|
12803
12688
|
this.exitFn(0);
|
|
@@ -13431,7 +13316,7 @@ var CompanionSupervisor = class {
|
|
|
13431
13316
|
locks: this.runnerLocks,
|
|
13432
13317
|
gzipBodies: this.gzipBodies
|
|
13433
13318
|
},
|
|
13434
|
-
observer: this.hub.observerFor(ctx2.workspaceId
|
|
13319
|
+
observer: this.hub.observerFor(ctx2.workspaceId),
|
|
13435
13320
|
aborts: ctx2.aborts
|
|
13436
13321
|
});
|
|
13437
13322
|
}
|
|
@@ -13885,36 +13770,9 @@ var CompanionSupervisor = class {
|
|
|
13885
13770
|
};
|
|
13886
13771
|
return { kick, cancel: cancel2 };
|
|
13887
13772
|
}
|
|
13888
|
-
// ---- dashboard-facing operations ----
|
|
13889
|
-
// Re-read `~/.cabane/config.json` (the operator may have edited the `agents`
|
|
13890
|
-
// overrides or dashboard prefs), apply the log level live, and re-pull
|
|
13891
|
-
// assignments so any structural change is reflected. A device-token change
|
|
13892
|
-
// means re-pairing, which `pair` + a restart handle.
|
|
13893
|
-
reloadConfig() {
|
|
13894
|
-
const next = loadConfig();
|
|
13895
|
-
if (!next) return;
|
|
13896
|
-
this.config = next;
|
|
13897
|
-
configureLogger(this.log, next);
|
|
13898
|
-
this.rebuildDispatchers();
|
|
13899
|
-
void this.refreshAssignments();
|
|
13900
|
-
}
|
|
13901
|
-
// The dashboard "refresh assignments" control — an immediate pull.
|
|
13902
|
-
refresh() {
|
|
13903
|
-
void this.refreshAssignments();
|
|
13904
|
-
}
|
|
13905
|
-
applySettings(patch) {
|
|
13906
|
-
const next = {
|
|
13907
|
-
...this.config,
|
|
13908
|
-
...patch.logLevel ? { logLevel: patch.logLevel } : {},
|
|
13909
|
-
...patch.autoOpen !== void 0 ? { autoOpen: patch.autoOpen } : {}
|
|
13910
|
-
};
|
|
13911
|
-
this.config = next;
|
|
13912
|
-
saveConfig(next);
|
|
13913
|
-
configureLogger(this.log, next);
|
|
13914
|
-
}
|
|
13915
13773
|
// ---- harnesses ----
|
|
13916
13774
|
// Look at every agent, in this process: the one whose environment turns run
|
|
13917
|
-
// in. Cached for the heartbeat and the
|
|
13775
|
+
// in. Cached for the heartbeat and the status snapshot; coalesced, so a re-detect
|
|
13918
13776
|
// during the timer's own run waits for that one. Never throws.
|
|
13919
13777
|
async detect() {
|
|
13920
13778
|
if (this.detecting) return this.detecting;
|
|
@@ -13942,8 +13800,8 @@ var CompanionSupervisor = class {
|
|
|
13942
13800
|
});
|
|
13943
13801
|
return this.detecting;
|
|
13944
13802
|
}
|
|
13945
|
-
// A re-detect asked for over the control socket (`setup`, `connect`)
|
|
13946
|
-
//
|
|
13803
|
+
// A re-detect asked for over the control socket (`setup`, `connect`): read
|
|
13804
|
+
// the config file again first — this process owns it, so an
|
|
13947
13805
|
// edit made on disk (the allowlist, the OpenCode address) is adopted here
|
|
13948
13806
|
// rather than overwritten by its next save — then detect now, and beat at
|
|
13949
13807
|
// once so Cabane shows it promptly.
|
|
@@ -14079,9 +13937,6 @@ var CompanionSupervisor = class {
|
|
|
14079
13937
|
await this.shutdown();
|
|
14080
13938
|
this.exitFn(0);
|
|
14081
13939
|
}
|
|
14082
|
-
requestRestart() {
|
|
14083
|
-
this.reexecFn();
|
|
14084
|
-
}
|
|
14085
13940
|
};
|
|
14086
13941
|
async function waitForBoundedHeartbeat(heartbeat) {
|
|
14087
13942
|
let timer = null;
|
|
@@ -14095,21 +13950,6 @@ async function waitForBoundedHeartbeat(heartbeat) {
|
|
|
14095
13950
|
if (timer) clearTimeout(timer);
|
|
14096
13951
|
}
|
|
14097
13952
|
}
|
|
14098
|
-
function defaultReexec() {
|
|
14099
|
-
clearRuntimeState();
|
|
14100
|
-
void import("child_process").then(({ spawn: spawn8 }) => {
|
|
14101
|
-
try {
|
|
14102
|
-
const child = spawn8(process.execPath, process.argv.slice(1), {
|
|
14103
|
-
stdio: "inherit",
|
|
14104
|
-
detached: false
|
|
14105
|
-
});
|
|
14106
|
-
child.on("spawn", () => process.exit(0));
|
|
14107
|
-
child.on("error", () => {
|
|
14108
|
-
});
|
|
14109
|
-
} catch {
|
|
14110
|
-
}
|
|
14111
|
-
});
|
|
14112
|
-
}
|
|
14113
13953
|
|
|
14114
13954
|
// src/safety.ts
|
|
14115
13955
|
var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
|
|
@@ -14281,15 +14121,6 @@ function strayCompanionLines(strays, retry) {
|
|
|
14281
14121
|
}
|
|
14282
14122
|
|
|
14283
14123
|
// src/runtime.ts
|
|
14284
|
-
init_errors();
|
|
14285
|
-
function embeddedRunner(host) {
|
|
14286
|
-
if (!host.electron) return null;
|
|
14287
|
-
const cli = fileURLToPath5(new URL("./cli.js", host.moduleUrl));
|
|
14288
|
-
return {
|
|
14289
|
-
runnerCommand: [host.execPath, cli, "run-turn"],
|
|
14290
|
-
runnerEnv: { ...host.env, ELECTRON_RUN_AS_NODE: "1" }
|
|
14291
|
-
};
|
|
14292
|
-
}
|
|
14293
14124
|
async function createCompanionRuntime(opts = {}) {
|
|
14294
14125
|
const log2 = getLogger();
|
|
14295
14126
|
installProcessSafetyNet(log2);
|
|
@@ -14329,7 +14160,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14329
14160
|
const clearRuntimeOnExit = () => clearRuntimeStateIfOurs(instanceId);
|
|
14330
14161
|
process.on("exit", clearRuntimeOnExit);
|
|
14331
14162
|
const hub = new CompanionStateHub({
|
|
14332
|
-
//
|
|
14163
|
+
// `status` names the Cabane it is paired with.
|
|
14333
14164
|
baseUrl: cfg.baseUrl,
|
|
14334
14165
|
companionVersion: COMPANION_VERSION,
|
|
14335
14166
|
// Surfaced on `/api/status` so `stop`/`status` can confirm the
|
|
@@ -14340,7 +14171,6 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14340
14171
|
};
|
|
14341
14172
|
let supervisor;
|
|
14342
14173
|
let control = null;
|
|
14343
|
-
let dashboard = null;
|
|
14344
14174
|
let stopped = false;
|
|
14345
14175
|
const stop2 = async () => {
|
|
14346
14176
|
if (stopped) return;
|
|
@@ -14349,8 +14179,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14349
14179
|
process.removeListener("exit", clearRuntimeOnExit);
|
|
14350
14180
|
await Promise.allSettled([
|
|
14351
14181
|
...supervisor ? [supervisor.shutdown()] : [],
|
|
14352
|
-
...control ? [control.close()] : []
|
|
14353
|
-
...dashboard ? [dashboard.close()] : []
|
|
14182
|
+
...control ? [control.close()] : []
|
|
14354
14183
|
]);
|
|
14355
14184
|
};
|
|
14356
14185
|
try {
|
|
@@ -14359,12 +14188,6 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14359
14188
|
log: log2,
|
|
14360
14189
|
hub,
|
|
14361
14190
|
...opts.checkHarness ? { checkHarness: opts.checkHarness } : {},
|
|
14362
|
-
...embeddedRunner({
|
|
14363
|
-
electron: Boolean(process.versions.electron),
|
|
14364
|
-
execPath: process.execPath,
|
|
14365
|
-
moduleUrl: import.meta.url,
|
|
14366
|
-
env: process.env
|
|
14367
|
-
}) ?? {},
|
|
14368
14191
|
restartForUpdate: () => restartForUpdate()
|
|
14369
14192
|
});
|
|
14370
14193
|
const active2 = supervisor;
|
|
@@ -14379,21 +14202,10 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14379
14202
|
update: (auto) => active2.update(auto),
|
|
14380
14203
|
stop: () => void stop2().then(() => active2.requestStop())
|
|
14381
14204
|
});
|
|
14382
|
-
if (opts.startDashboard) {
|
|
14383
|
-
const preferredPort = cfg.dashboardPort;
|
|
14384
|
-
dashboard = await opts.startDashboard({
|
|
14385
|
-
supervisor,
|
|
14386
|
-
hub,
|
|
14387
|
-
logPath: companionLogPath(),
|
|
14388
|
-
...preferredPort !== void 0 ? { port: preferredPort } : {}
|
|
14389
|
-
});
|
|
14390
|
-
hub.setDashboardUrl(dashboard.url);
|
|
14391
|
-
}
|
|
14392
14205
|
if (stopped) throw new Error("The companion was stopped during startup.");
|
|
14393
14206
|
writeRuntimeState({
|
|
14394
14207
|
pid: process.pid,
|
|
14395
14208
|
socket: control.path,
|
|
14396
|
-
...dashboard ? { url: dashboard.url, port: dashboard.port } : {},
|
|
14397
14209
|
startedAt,
|
|
14398
14210
|
// The daemon launcher sets this env on the detached child, so the
|
|
14399
14211
|
// marker records whether this companion is backgrounded (foreground start
|
|
@@ -14408,8 +14220,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14408
14220
|
process.removeListener("exit", clearRuntimeOnExit);
|
|
14409
14221
|
await Promise.allSettled([
|
|
14410
14222
|
...supervisor ? [supervisor.shutdown()] : [],
|
|
14411
|
-
...control ? [control.close()] : []
|
|
14412
|
-
...dashboard ? [dashboard.close()] : []
|
|
14223
|
+
...control ? [control.close()] : []
|
|
14413
14224
|
]);
|
|
14414
14225
|
recordCrash({
|
|
14415
14226
|
reason: err instanceof Error ? err.message : String(err),
|
|
@@ -14433,7 +14244,6 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
14433
14244
|
return {
|
|
14434
14245
|
ok: true,
|
|
14435
14246
|
runtime: {
|
|
14436
|
-
...dashboard ? { url: dashboard.url, port: dashboard.port } : {},
|
|
14437
14247
|
socketPath: control.path,
|
|
14438
14248
|
config: cfg,
|
|
14439
14249
|
stop: stop2,
|
|
@@ -14822,6 +14632,7 @@ function userName() {
|
|
|
14822
14632
|
return process.env.USER ?? "";
|
|
14823
14633
|
}
|
|
14824
14634
|
}
|
|
14635
|
+
var SCRIPT_WRAPPER_COMMENT = /^# Written by \S+\/install\.sh\. /m;
|
|
14825
14636
|
function rewriteScriptWrappers(dir2 = join27(cabaneDir(), "bin")) {
|
|
14826
14637
|
for (const name of ["cabane", "cabane-companion"]) {
|
|
14827
14638
|
const path = join27(dir2, name);
|
|
@@ -14831,7 +14642,7 @@ function rewriteScriptWrappers(dir2 = join27(cabaneDir(), "bin")) {
|
|
|
14831
14642
|
} catch {
|
|
14832
14643
|
continue;
|
|
14833
14644
|
}
|
|
14834
|
-
if (!
|
|
14645
|
+
if (!SCRIPT_WRAPPER_COMMENT.test(text)) continue;
|
|
14835
14646
|
if (!text.includes("/dist/cli.js")) continue;
|
|
14836
14647
|
writeAtomic(path, text.replace("/dist/cli.js", "/dist/bin.js"));
|
|
14837
14648
|
chmodSync5(path, 493);
|
|
@@ -15168,6 +14979,9 @@ async function runAttached(runtime) {
|
|
|
15168
14979
|
});
|
|
15169
14980
|
}
|
|
15170
14981
|
|
|
14982
|
+
// src/commands/setup.ts
|
|
14983
|
+
init_errors();
|
|
14984
|
+
|
|
15171
14985
|
// src/prompts.ts
|
|
15172
14986
|
init_term();
|
|
15173
14987
|
import * as clack from "@clack/prompts";
|
|
@@ -15188,7 +15002,8 @@ function terminalUi() {
|
|
|
15188
15002
|
},
|
|
15189
15003
|
spinner: () => railSpinner(),
|
|
15190
15004
|
outro: (lines2) => clack.outro(lines2.join("\n ")),
|
|
15191
|
-
|
|
15005
|
+
// Clack draws only the first line on the rail's end; the rest align under its text.
|
|
15006
|
+
cancel: (message2) => clack.cancel(message2.split("\n").join("\n "))
|
|
15192
15007
|
};
|
|
15193
15008
|
}
|
|
15194
15009
|
var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
@@ -15244,7 +15059,10 @@ function plainUi() {
|
|
|
15244
15059
|
outro: (lines2) => {
|
|
15245
15060
|
for (const line of lines2) write(`${INDENT}${line}`);
|
|
15246
15061
|
},
|
|
15247
|
-
|
|
15062
|
+
// A closing message can run to more than one line; each is indented like the first.
|
|
15063
|
+
cancel: (message2) => {
|
|
15064
|
+
for (const line of message2.split("\n")) write(`${INDENT}${line}`);
|
|
15065
|
+
}
|
|
15248
15066
|
};
|
|
15249
15067
|
}
|
|
15250
15068
|
|
|
@@ -15256,9 +15074,13 @@ async function setup(opts = {}, deps = {}) {
|
|
|
15256
15074
|
try {
|
|
15257
15075
|
await setupScript(opts, deps);
|
|
15258
15076
|
} catch (err) {
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15077
|
+
if (err instanceof StandaloneError) {
|
|
15078
|
+
write(err.message);
|
|
15079
|
+
} else {
|
|
15080
|
+
for (const line of pathLines(reloadLine())) write(line);
|
|
15081
|
+
write(err instanceof Error ? err.message : String(err));
|
|
15082
|
+
write(`Check the server connection, then run ${cabane("setup")} again.`);
|
|
15083
|
+
}
|
|
15262
15084
|
process.exitCode = 1;
|
|
15263
15085
|
} finally {
|
|
15264
15086
|
setConsoleLogging(true);
|
|
@@ -15286,8 +15108,21 @@ async function setupScript(opts, deps) {
|
|
|
15286
15108
|
}
|
|
15287
15109
|
let justPaired = false;
|
|
15288
15110
|
if (!paired) {
|
|
15289
|
-
|
|
15290
|
-
|
|
15111
|
+
let baseUrl;
|
|
15112
|
+
try {
|
|
15113
|
+
baseUrl = resolvePairBaseUrl(opts.server ?? cfg?.baseUrl);
|
|
15114
|
+
} catch (err) {
|
|
15115
|
+
if (!(err instanceof NoPairAddressError)) throw err;
|
|
15116
|
+
ui.cancel(err.message);
|
|
15117
|
+
process.exitCode = 1;
|
|
15118
|
+
return;
|
|
15119
|
+
}
|
|
15120
|
+
const device = await (deps.pair ?? pairDevice)({
|
|
15121
|
+
baseUrl,
|
|
15122
|
+
interactive,
|
|
15123
|
+
ui,
|
|
15124
|
+
onCode: () => rememberPairAddress(baseUrl)
|
|
15125
|
+
});
|
|
15291
15126
|
if (!device) return;
|
|
15292
15127
|
writePairedConfig(device);
|
|
15293
15128
|
justPaired = true;
|
|
@@ -16378,7 +16213,7 @@ init_cabane_command();
|
|
|
16378
16213
|
rememberInvokedCommand();
|
|
16379
16214
|
var program = new Command();
|
|
16380
16215
|
program.name("cabane").description("Run your Cabane agents on this device, with Claude Code, Codex or OpenCode.").version(COMPANION_VERSION);
|
|
16381
|
-
program.command("setup").argument("[code]", "not taken: run cabane setup and enter the code it shows in Cabane").description("set up this device: find your agents, pair it with Cabane, and keep it running.").allowExcessArguments(false).option("--server <url>", "the Cabane
|
|
16216
|
+
program.command("setup").argument("[code]", "not taken: run cabane setup and enter the code it shows in Cabane").description("set up this device: find your agents, pair it with Cabane, and keep it running.").allowExcessArguments(false).option("--server <url>", "the address of the Cabane to pair with").addOption(
|
|
16382
16217
|
new Option("--log-level <level>", "info (default), warn, or debug").choices([
|
|
16383
16218
|
"info",
|
|
16384
16219
|
"warn",
|