@cabane/companion 0.6.46 → 0.6.47
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/dist/cli.js +47 -13
- package/dist/runtime.js +36 -8
- package/dist/static/app.js +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -823,6 +823,7 @@ var HARNESS_LABELS = {
|
|
|
823
823
|
};
|
|
824
824
|
var LABELS = HARNESS_LABELS;
|
|
825
825
|
var OFFER_ORDER = ["claude-code", "codex", "opencode"];
|
|
826
|
+
var DEFAULT_OPENCODE_SERVER_URL = "http://127.0.0.1:4096";
|
|
826
827
|
function parseHarnessRuntime(raw) {
|
|
827
828
|
const key = raw.trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
828
829
|
if (key === "claude-code" || key === "claudecode" || key === "claude") return "claude-code";
|
|
@@ -941,6 +942,15 @@ function deriveOpencode(signals, manifestHas) {
|
|
|
941
942
|
enable: null
|
|
942
943
|
};
|
|
943
944
|
}
|
|
945
|
+
if (signals.opencodeReachable && signals.opencodeDetectedUrl) {
|
|
946
|
+
return {
|
|
947
|
+
...base,
|
|
948
|
+
state: "detected_not_exposed",
|
|
949
|
+
version: signals.opencodeVersion,
|
|
950
|
+
detail: signals.opencodeDetectedUrl,
|
|
951
|
+
enable: "opencode"
|
|
952
|
+
};
|
|
953
|
+
}
|
|
944
954
|
return {
|
|
945
955
|
...base,
|
|
946
956
|
state: "not_detected",
|
|
@@ -950,17 +960,32 @@ function deriveOpencode(signals, manifestHas) {
|
|
|
950
960
|
};
|
|
951
961
|
}
|
|
952
962
|
var PROBE_TIMEOUT_MS = 4e3;
|
|
963
|
+
var DEFAULT_OPENCODE_PROBE_TIMEOUT_MS = 1e3;
|
|
964
|
+
function probeDefaultOpencodeVersion(probeOpencode = (url) => probeOpencodeVersion(url)) {
|
|
965
|
+
return withTimeout(
|
|
966
|
+
probeOpencode(DEFAULT_OPENCODE_SERVER_URL),
|
|
967
|
+
null,
|
|
968
|
+
DEFAULT_OPENCODE_PROBE_TIMEOUT_MS
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
async function resolveOpencodeServerUrl(configuredServerUrl, requestedServerUrl, probeDefault = probeDefaultOpencodeVersion) {
|
|
972
|
+
const requested = requestedServerUrl?.trim();
|
|
973
|
+
if (requested) return requested;
|
|
974
|
+
if (configuredServerUrl) return configuredServerUrl;
|
|
975
|
+
return await probeDefault() !== null ? DEFAULT_OPENCODE_SERVER_URL : null;
|
|
976
|
+
}
|
|
953
977
|
async function probeHarnessSignals(cfg, deps = {}) {
|
|
954
978
|
const probeClaudePresence = deps.probeClaudePresence ?? claudeOnPath;
|
|
955
979
|
const probeClaudeVersion = deps.probeClaudeVersion ?? (() => probeCliVersion("claude"));
|
|
956
980
|
const probeCodexVersion = deps.probeCodexVersion ?? (() => probeCliVersion("codex"));
|
|
957
981
|
const probeOpencode = deps.probeOpencode ?? ((url) => probeOpencodeVersion(url));
|
|
958
|
-
const
|
|
982
|
+
const configuredServerUrl = cfg.opencode?.serverUrl;
|
|
983
|
+
const opencodeProbeUrl = configuredServerUrl ?? DEFAULT_OPENCODE_SERVER_URL;
|
|
959
984
|
const [claudeOnPathResult, claudeVersion, codexVersion, opencodeVersion] = await Promise.all([
|
|
960
985
|
withTimeout(probeClaudePresence(), false),
|
|
961
986
|
withTimeout(probeClaudeVersion(), null),
|
|
962
987
|
withTimeout(probeCodexVersion(), null),
|
|
963
|
-
|
|
988
|
+
configuredServerUrl ? withTimeout(probeOpencode(opencodeProbeUrl), null) : probeDefaultOpencodeVersion(probeOpencode)
|
|
964
989
|
]);
|
|
965
990
|
return {
|
|
966
991
|
claudeOnPath: claudeOnPathResult,
|
|
@@ -973,13 +998,14 @@ async function probeHarnessSignals(cfg, deps = {}) {
|
|
|
973
998
|
codexOnPath: codexVersion !== null,
|
|
974
999
|
codexVersion,
|
|
975
1000
|
codexEnabled: isCodexEnabled(cfg),
|
|
976
|
-
opencodeConfigured: !!
|
|
1001
|
+
opencodeConfigured: !!configuredServerUrl,
|
|
977
1002
|
// A version came back ⟺ the serve answered its health endpoint (CT584).
|
|
978
1003
|
opencodeReachable: opencodeVersion !== null,
|
|
979
|
-
opencodeVersion
|
|
1004
|
+
opencodeVersion,
|
|
1005
|
+
opencodeDetectedUrl: opencodeVersion !== null ? opencodeProbeUrl : null
|
|
980
1006
|
};
|
|
981
1007
|
}
|
|
982
|
-
function withTimeout(promise, fallback) {
|
|
1008
|
+
function withTimeout(promise, fallback, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
983
1009
|
return new Promise((resolve) => {
|
|
984
1010
|
let settled = false;
|
|
985
1011
|
const done = (v) => {
|
|
@@ -988,7 +1014,7 @@ function withTimeout(promise, fallback) {
|
|
|
988
1014
|
resolve(v);
|
|
989
1015
|
}
|
|
990
1016
|
};
|
|
991
|
-
const timer = setTimeout(() => done(fallback),
|
|
1017
|
+
const timer = setTimeout(() => done(fallback), timeoutMs);
|
|
992
1018
|
timer.unref?.();
|
|
993
1019
|
promise.then(
|
|
994
1020
|
(v) => {
|
|
@@ -1316,13 +1342,14 @@ async function connect2(raw, opts = {}) {
|
|
|
1316
1342
|
if (runtime === "claude-code") next = { ...cfg, claudeCode: { enabled: true } };
|
|
1317
1343
|
else if (runtime === "codex") next = { ...cfg, codex: { enabled: true } };
|
|
1318
1344
|
else {
|
|
1319
|
-
const
|
|
1345
|
+
const requestedServerUrl = opts.serverUrl?.trim();
|
|
1346
|
+
const serverUrl = await resolveOpencodeServerUrl(void 0, requestedServerUrl);
|
|
1320
1347
|
if (!serverUrl) {
|
|
1321
1348
|
throw new CompanionError(
|
|
1322
1349
|
"opencode is addressed by URL \u2014 pass it: `cabane-companion connect opencode --url http://127.0.0.1:4096`."
|
|
1323
1350
|
);
|
|
1324
1351
|
}
|
|
1325
|
-
if (await probeOpencodeVersion(serverUrl) === null) {
|
|
1352
|
+
if (requestedServerUrl && await probeOpencodeVersion(serverUrl) === null) {
|
|
1326
1353
|
throw new CompanionError(
|
|
1327
1354
|
`Couldn\u2019t reach an opencode server at ${serverUrl}. Start \`opencode serve\` and check the URL.`
|
|
1328
1355
|
);
|
|
@@ -10056,11 +10083,13 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
10056
10083
|
});
|
|
10057
10084
|
await supervisor.start();
|
|
10058
10085
|
const connectHarness = async (runtime, serverUrl) => {
|
|
10059
|
-
const
|
|
10086
|
+
const currentConfig = supervisor.currentConfig();
|
|
10087
|
+
const resolvedServerUrl = runtime === "opencode" ? await resolveOpencodeServerUrl(currentConfig.opencode?.serverUrl, serverUrl) : null;
|
|
10088
|
+
const candidate = runtime === "opencode" ? { ...currentConfig, opencode: { serverUrl: resolvedServerUrl ?? "" } } : currentConfig;
|
|
10060
10089
|
const verdict = await shakeOutHarness(runtime, candidate);
|
|
10061
10090
|
if (verdict === "absent") return { ok: false, error: absentLine(runtime) };
|
|
10062
10091
|
const result = await supervisor.enableHarness(
|
|
10063
|
-
runtime === "opencode" ? { runtime: "opencode", serverUrl:
|
|
10092
|
+
runtime === "opencode" ? { runtime: "opencode", serverUrl: resolvedServerUrl ?? "" } : { runtime }
|
|
10064
10093
|
);
|
|
10065
10094
|
if (!result.ok) return { ok: false, error: result.error };
|
|
10066
10095
|
return { ok: true, message: connectedLine(runtime, verdict) };
|
|
@@ -10330,17 +10359,22 @@ function reportAlreadyRunning(pid) {
|
|
|
10330
10359
|
write("Connect a harness to the running companion: cabane-companion connect claude-code");
|
|
10331
10360
|
}
|
|
10332
10361
|
async function collectHarnessChoices(interactive) {
|
|
10333
|
-
const opencodeUrl = "http://127.0.0.1:4096";
|
|
10334
10362
|
const [claudePresent, claudeVersion, codexVersion, opencodeVersion] = await Promise.all([
|
|
10335
10363
|
claudeOnPath().catch(() => false),
|
|
10336
10364
|
probeCliVersion("claude").catch(() => null),
|
|
10337
10365
|
probeCliVersion("codex").catch(() => null),
|
|
10338
|
-
|
|
10366
|
+
probeDefaultOpencodeVersion()
|
|
10339
10367
|
]);
|
|
10340
10368
|
const found = [
|
|
10341
10369
|
...claudePresent ? [{ runtime: "claude-code", version: claudeVersion }] : [],
|
|
10342
10370
|
...codexVersion ? [{ runtime: "codex", version: codexVersion }] : [],
|
|
10343
|
-
...opencodeVersion ? [
|
|
10371
|
+
...opencodeVersion ? [
|
|
10372
|
+
{
|
|
10373
|
+
runtime: "opencode",
|
|
10374
|
+
version: opencodeVersion,
|
|
10375
|
+
serverUrl: DEFAULT_OPENCODE_SERVER_URL
|
|
10376
|
+
}
|
|
10377
|
+
] : []
|
|
10344
10378
|
].sort((a, b) => OFFER_ORDER.indexOf(a.runtime) - OFFER_ORDER.indexOf(b.runtime));
|
|
10345
10379
|
if (found.length === 0) {
|
|
10346
10380
|
write(
|
package/dist/runtime.js
CHANGED
|
@@ -1300,6 +1300,7 @@ var HARNESS_LABELS = {
|
|
|
1300
1300
|
opencode: "OpenCode"
|
|
1301
1301
|
};
|
|
1302
1302
|
var LABELS = HARNESS_LABELS;
|
|
1303
|
+
var DEFAULT_OPENCODE_SERVER_URL = "http://127.0.0.1:4096";
|
|
1303
1304
|
function deriveHarnessSnapshot(signals) {
|
|
1304
1305
|
const advertised = new Set(
|
|
1305
1306
|
buildCompanionManifest({
|
|
@@ -1411,6 +1412,15 @@ function deriveOpencode(signals, manifestHas) {
|
|
|
1411
1412
|
enable: null
|
|
1412
1413
|
};
|
|
1413
1414
|
}
|
|
1415
|
+
if (signals.opencodeReachable && signals.opencodeDetectedUrl) {
|
|
1416
|
+
return {
|
|
1417
|
+
...base,
|
|
1418
|
+
state: "detected_not_exposed",
|
|
1419
|
+
version: signals.opencodeVersion,
|
|
1420
|
+
detail: signals.opencodeDetectedUrl,
|
|
1421
|
+
enable: "opencode"
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1414
1424
|
return {
|
|
1415
1425
|
...base,
|
|
1416
1426
|
state: "not_detected",
|
|
@@ -1420,17 +1430,32 @@ function deriveOpencode(signals, manifestHas) {
|
|
|
1420
1430
|
};
|
|
1421
1431
|
}
|
|
1422
1432
|
var PROBE_TIMEOUT_MS = 4e3;
|
|
1433
|
+
var DEFAULT_OPENCODE_PROBE_TIMEOUT_MS = 1e3;
|
|
1434
|
+
function probeDefaultOpencodeVersion(probeOpencode = (url) => probeOpencodeVersion(url)) {
|
|
1435
|
+
return withTimeout(
|
|
1436
|
+
probeOpencode(DEFAULT_OPENCODE_SERVER_URL),
|
|
1437
|
+
null,
|
|
1438
|
+
DEFAULT_OPENCODE_PROBE_TIMEOUT_MS
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
async function resolveOpencodeServerUrl(configuredServerUrl, requestedServerUrl, probeDefault = probeDefaultOpencodeVersion) {
|
|
1442
|
+
const requested = requestedServerUrl?.trim();
|
|
1443
|
+
if (requested) return requested;
|
|
1444
|
+
if (configuredServerUrl) return configuredServerUrl;
|
|
1445
|
+
return await probeDefault() !== null ? DEFAULT_OPENCODE_SERVER_URL : null;
|
|
1446
|
+
}
|
|
1423
1447
|
async function probeHarnessSignals(cfg, deps = {}) {
|
|
1424
1448
|
const probeClaudePresence = deps.probeClaudePresence ?? claudeOnPath;
|
|
1425
1449
|
const probeClaudeVersion = deps.probeClaudeVersion ?? (() => probeCliVersion("claude"));
|
|
1426
1450
|
const probeCodexVersion = deps.probeCodexVersion ?? (() => probeCliVersion("codex"));
|
|
1427
1451
|
const probeOpencode = deps.probeOpencode ?? ((url) => probeOpencodeVersion(url));
|
|
1428
|
-
const
|
|
1452
|
+
const configuredServerUrl = cfg.opencode?.serverUrl;
|
|
1453
|
+
const opencodeProbeUrl = configuredServerUrl ?? DEFAULT_OPENCODE_SERVER_URL;
|
|
1429
1454
|
const [claudeOnPathResult, claudeVersion, codexVersion, opencodeVersion] = await Promise.all([
|
|
1430
1455
|
withTimeout(probeClaudePresence(), false),
|
|
1431
1456
|
withTimeout(probeClaudeVersion(), null),
|
|
1432
1457
|
withTimeout(probeCodexVersion(), null),
|
|
1433
|
-
|
|
1458
|
+
configuredServerUrl ? withTimeout(probeOpencode(opencodeProbeUrl), null) : probeDefaultOpencodeVersion(probeOpencode)
|
|
1434
1459
|
]);
|
|
1435
1460
|
return {
|
|
1436
1461
|
claudeOnPath: claudeOnPathResult,
|
|
@@ -1443,13 +1468,14 @@ async function probeHarnessSignals(cfg, deps = {}) {
|
|
|
1443
1468
|
codexOnPath: codexVersion !== null,
|
|
1444
1469
|
codexVersion,
|
|
1445
1470
|
codexEnabled: isCodexEnabled(cfg),
|
|
1446
|
-
opencodeConfigured: !!
|
|
1471
|
+
opencodeConfigured: !!configuredServerUrl,
|
|
1447
1472
|
// A version came back ⟺ the serve answered its health endpoint (CT584).
|
|
1448
1473
|
opencodeReachable: opencodeVersion !== null,
|
|
1449
|
-
opencodeVersion
|
|
1474
|
+
opencodeVersion,
|
|
1475
|
+
opencodeDetectedUrl: opencodeVersion !== null ? opencodeProbeUrl : null
|
|
1450
1476
|
};
|
|
1451
1477
|
}
|
|
1452
|
-
function withTimeout(promise, fallback) {
|
|
1478
|
+
function withTimeout(promise, fallback, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
1453
1479
|
return new Promise((resolve) => {
|
|
1454
1480
|
let settled = false;
|
|
1455
1481
|
const done = (v) => {
|
|
@@ -1458,7 +1484,7 @@ function withTimeout(promise, fallback) {
|
|
|
1458
1484
|
resolve(v);
|
|
1459
1485
|
}
|
|
1460
1486
|
};
|
|
1461
|
-
const timer = setTimeout(() => done(fallback),
|
|
1487
|
+
const timer = setTimeout(() => done(fallback), timeoutMs);
|
|
1462
1488
|
timer.unref?.();
|
|
1463
1489
|
promise.then(
|
|
1464
1490
|
(v) => {
|
|
@@ -9565,11 +9591,13 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
9565
9591
|
});
|
|
9566
9592
|
await supervisor.start();
|
|
9567
9593
|
const connectHarness = async (runtime, serverUrl) => {
|
|
9568
|
-
const
|
|
9594
|
+
const currentConfig = supervisor.currentConfig();
|
|
9595
|
+
const resolvedServerUrl = runtime === "opencode" ? await resolveOpencodeServerUrl(currentConfig.opencode?.serverUrl, serverUrl) : null;
|
|
9596
|
+
const candidate = runtime === "opencode" ? { ...currentConfig, opencode: { serverUrl: resolvedServerUrl ?? "" } } : currentConfig;
|
|
9569
9597
|
const verdict = await shakeOutHarness(runtime, candidate);
|
|
9570
9598
|
if (verdict === "absent") return { ok: false, error: absentLine(runtime) };
|
|
9571
9599
|
const result = await supervisor.enableHarness(
|
|
9572
|
-
runtime === "opencode" ? { runtime: "opencode", serverUrl:
|
|
9600
|
+
runtime === "opencode" ? { runtime: "opencode", serverUrl: resolvedServerUrl ?? "" } : { runtime }
|
|
9573
9601
|
);
|
|
9574
9602
|
if (!result.ok) return { ok: false, error: result.error };
|
|
9575
9603
|
return { ok: true, message: connectedLine(runtime, verdict) };
|
package/dist/static/app.js
CHANGED
|
@@ -158,6 +158,7 @@ function renderHarnesses() {
|
|
|
158
158
|
const input = el('input');
|
|
159
159
|
input.type = 'text';
|
|
160
160
|
input.placeholder = 'opencode serve URL (e.g. http://127.0.0.1:4096)';
|
|
161
|
+
if (h.state === 'detected_not_exposed') input.value = h.detail;
|
|
161
162
|
const btn = el('button', 'primary small', 'Add opencode');
|
|
162
163
|
const submit = () => {
|
|
163
164
|
const url = input.value.trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect a coding agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
|
|
6
6
|
"license": "UNLICENSED",
|