@cabane/companion 0.6.28 → 0.6.30
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 +83 -11
- package/dist/runtime.js +83 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7200,6 +7200,9 @@ var TurnCommitter = class {
|
|
|
7200
7200
|
|
|
7201
7201
|
// src/workspace-readiness.ts
|
|
7202
7202
|
var CLASSIC_REQUIRED = ["read", "list", "search", "write", "edit"];
|
|
7203
|
+
var INITIALIZE_RETRY_DELAYS_MS = [250, 750, 1500];
|
|
7204
|
+
var INITIALIZE_ATTEMPT_TIMEOUT_MS = 4e3;
|
|
7205
|
+
var INITIALIZE_RETRY_BUDGET_MS = 1e4;
|
|
7203
7206
|
async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
7204
7207
|
const base = {
|
|
7205
7208
|
ok: false,
|
|
@@ -7225,7 +7228,7 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
7225
7228
|
"x-cabane-active-conversation": req.cabane.activeConversationId
|
|
7226
7229
|
};
|
|
7227
7230
|
try {
|
|
7228
|
-
const initialized = await
|
|
7231
|
+
const initialized = await initializeWithRetry(fetchImpl, req.cabane.mcpUrl, headers, {
|
|
7229
7232
|
jsonrpc: "2.0",
|
|
7230
7233
|
id: 1,
|
|
7231
7234
|
method: "initialize",
|
|
@@ -7237,6 +7240,8 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
7237
7240
|
});
|
|
7238
7241
|
if (initialized.status === 401 || initialized.status === 403)
|
|
7239
7242
|
return fail(base, "authentication_failed", `initialize returned HTTP ${initialized.status}`);
|
|
7243
|
+
if (initialized.transient)
|
|
7244
|
+
return fail(base, "workspace_endpoint_unreachable", initialized.detail);
|
|
7240
7245
|
if (!initialized.ok) return fail(base, "initialization_failed", initialized.detail);
|
|
7241
7246
|
base.initialized = true;
|
|
7242
7247
|
base.authenticated = true;
|
|
@@ -7271,11 +7276,47 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
7271
7276
|
} catch (error) {
|
|
7272
7277
|
return fail(
|
|
7273
7278
|
base,
|
|
7274
|
-
"
|
|
7279
|
+
"workspace_endpoint_unreachable",
|
|
7275
7280
|
error instanceof Error ? error.message : String(error)
|
|
7276
7281
|
);
|
|
7277
7282
|
}
|
|
7278
7283
|
}
|
|
7284
|
+
async function initializeWithRetry(fetchImpl, url, headers, body) {
|
|
7285
|
+
let lastFailure = null;
|
|
7286
|
+
const deadline = Date.now() + INITIALIZE_RETRY_BUDGET_MS;
|
|
7287
|
+
for (let attempt = 0; attempt <= INITIALIZE_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
7288
|
+
try {
|
|
7289
|
+
const remainingMs = deadline - Date.now();
|
|
7290
|
+
if (remainingMs <= 0) break;
|
|
7291
|
+
const result = await rpc(
|
|
7292
|
+
fetchImpl,
|
|
7293
|
+
url,
|
|
7294
|
+
headers,
|
|
7295
|
+
body,
|
|
7296
|
+
Math.min(INITIALIZE_ATTEMPT_TIMEOUT_MS, remainingMs)
|
|
7297
|
+
);
|
|
7298
|
+
if (result.status === 401 || result.status === 403 || result.ok) return result;
|
|
7299
|
+
if (result.status < 500) return result;
|
|
7300
|
+
lastFailure = { ...result, transient: true };
|
|
7301
|
+
} catch (error) {
|
|
7302
|
+
lastFailure = {
|
|
7303
|
+
ok: false,
|
|
7304
|
+
status: 0,
|
|
7305
|
+
sessionId: null,
|
|
7306
|
+
value: null,
|
|
7307
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
7308
|
+
transient: true
|
|
7309
|
+
};
|
|
7310
|
+
}
|
|
7311
|
+
const retryDelayMs = INITIALIZE_RETRY_DELAYS_MS[attempt];
|
|
7312
|
+
if (retryDelayMs === void 0 || Date.now() + retryDelayMs >= deadline) break;
|
|
7313
|
+
await delay(retryDelayMs);
|
|
7314
|
+
}
|
|
7315
|
+
return lastFailure;
|
|
7316
|
+
}
|
|
7317
|
+
function delay(ms) {
|
|
7318
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7319
|
+
}
|
|
7279
7320
|
function fail(proof, capability, detail) {
|
|
7280
7321
|
proof.failedCapability = capability;
|
|
7281
7322
|
proof.detail = detail.slice(0, 300);
|
|
@@ -7289,8 +7330,13 @@ function safeEndpoint(value) {
|
|
|
7289
7330
|
return null;
|
|
7290
7331
|
}
|
|
7291
7332
|
}
|
|
7292
|
-
async function rpc(fetchImpl, url, headers, body) {
|
|
7293
|
-
const response = await fetchImpl(url, {
|
|
7333
|
+
async function rpc(fetchImpl, url, headers, body, timeoutMs) {
|
|
7334
|
+
const response = await fetchImpl(url, {
|
|
7335
|
+
method: "POST",
|
|
7336
|
+
headers,
|
|
7337
|
+
body: JSON.stringify(body),
|
|
7338
|
+
...timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}
|
|
7339
|
+
});
|
|
7294
7340
|
const text = await response.text();
|
|
7295
7341
|
const value = parseRpcBody(text);
|
|
7296
7342
|
return {
|
|
@@ -7927,7 +7973,8 @@ ${reason}`,
|
|
|
7927
7973
|
);
|
|
7928
7974
|
}
|
|
7929
7975
|
if (!proof.ok) {
|
|
7930
|
-
const
|
|
7976
|
+
const recovery = proof.failedCapability === "workspace_endpoint_unreachable" ? "the Cabane workspace endpoint was unreachable; retry this dispatch" : "restart the connector after restoring the Cabane workspace tool mount";
|
|
7977
|
+
const reason = `workspace_tools_missing: ${proof.failedCapability}; checkout=${effectiveCwd}; runtime=${adapter.name}; recovery=${recovery}`;
|
|
7931
7978
|
closeTurnReceipt(false, reason);
|
|
7932
7979
|
try {
|
|
7933
7980
|
await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
|
|
@@ -8760,6 +8807,7 @@ var SseSubscriber = class {
|
|
|
8760
8807
|
try {
|
|
8761
8808
|
await this.connect();
|
|
8762
8809
|
backoff = 500;
|
|
8810
|
+
if (!this.aborted) await sleep3(backoff);
|
|
8763
8811
|
} catch (err) {
|
|
8764
8812
|
if (this.aborted) return;
|
|
8765
8813
|
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
@@ -8785,8 +8833,8 @@ var SseSubscriber = class {
|
|
|
8785
8833
|
}
|
|
8786
8834
|
async connect() {
|
|
8787
8835
|
const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
8788
|
-
const query = this.lastEventId ? "" : "?replayPending=1";
|
|
8789
|
-
const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
8836
|
+
const query = this.opts.path ? "" : this.lastEventId ? "" : "?replayPending=1";
|
|
8837
|
+
const url = this.opts.path ? `${base}${this.opts.path}` : `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
8790
8838
|
this.controller = new AbortController();
|
|
8791
8839
|
const headers = {
|
|
8792
8840
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -8878,6 +8926,7 @@ var CompanionSupervisor = class {
|
|
|
8878
8926
|
reexecFn;
|
|
8879
8927
|
dispatcherFactory;
|
|
8880
8928
|
deviceApi = null;
|
|
8929
|
+
deviceSub = null;
|
|
8881
8930
|
heartbeatTimer = null;
|
|
8882
8931
|
inFlightHeartbeat = null;
|
|
8883
8932
|
pollTimer = null;
|
|
@@ -8931,6 +8980,24 @@ var CompanionSupervisor = class {
|
|
|
8931
8980
|
baseUrl: this.config.baseUrl,
|
|
8932
8981
|
deviceToken: this.config.deviceToken
|
|
8933
8982
|
});
|
|
8983
|
+
this.deviceSub = new SseSubscriber({
|
|
8984
|
+
baseUrl: this.config.baseUrl,
|
|
8985
|
+
workspaceId: "device-control",
|
|
8986
|
+
path: "/api/companion/events",
|
|
8987
|
+
token: this.config.deviceToken,
|
|
8988
|
+
log: this.log,
|
|
8989
|
+
lastEventId: null,
|
|
8990
|
+
onMessage: async (ev) => {
|
|
8991
|
+
if (ev.event === "assignments_changed") {
|
|
8992
|
+
if (this.refreshing && this.inFlightRefresh) await this.inFlightRefresh;
|
|
8993
|
+
await this.refreshAssignments();
|
|
8994
|
+
}
|
|
8995
|
+
},
|
|
8996
|
+
onAuthFailure: () => {
|
|
8997
|
+
this.log.error("companion: device control stream authentication failed");
|
|
8998
|
+
}
|
|
8999
|
+
});
|
|
9000
|
+
this.deviceSub.start();
|
|
8934
9001
|
await this.refreshAssignments();
|
|
8935
9002
|
this.kickHeartbeat();
|
|
8936
9003
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
@@ -8943,7 +9010,10 @@ var CompanionSupervisor = class {
|
|
|
8943
9010
|
// at least the loops keeping the event loop alive; the SSE promises let a
|
|
8944
9011
|
// clean teardown resolve.
|
|
8945
9012
|
finishedPromises() {
|
|
8946
|
-
return [
|
|
9013
|
+
return [
|
|
9014
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9015
|
+
...[...this.workspaces.values()].flatMap((w) => w.sub ? [w.sub.finished] : [])
|
|
9016
|
+
];
|
|
8947
9017
|
}
|
|
8948
9018
|
// ---- device-level loops ----
|
|
8949
9019
|
kickHeartbeat() {
|
|
@@ -9627,13 +9697,15 @@ var CompanionSupervisor = class {
|
|
|
9627
9697
|
this.stopped = true;
|
|
9628
9698
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
9629
9699
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
9700
|
+
this.deviceSub?.stop();
|
|
9630
9701
|
for (const wr of this.workspaces.values()) {
|
|
9631
9702
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
9632
9703
|
if (wr.sub) wr.sub.stop();
|
|
9633
9704
|
}
|
|
9634
|
-
await Promise.all(
|
|
9635
|
-
|
|
9636
|
-
|
|
9705
|
+
await Promise.all([
|
|
9706
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9707
|
+
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
9708
|
+
]);
|
|
9637
9709
|
}
|
|
9638
9710
|
async drainForRestart(graceMs) {
|
|
9639
9711
|
this.restartPending = true;
|
package/dist/runtime.js
CHANGED
|
@@ -6312,6 +6312,9 @@ var TurnCommitter = class {
|
|
|
6312
6312
|
|
|
6313
6313
|
// src/workspace-readiness.ts
|
|
6314
6314
|
var CLASSIC_REQUIRED = ["read", "list", "search", "write", "edit"];
|
|
6315
|
+
var INITIALIZE_RETRY_DELAYS_MS = [250, 750, 1500];
|
|
6316
|
+
var INITIALIZE_ATTEMPT_TIMEOUT_MS = 4e3;
|
|
6317
|
+
var INITIALIZE_RETRY_BUDGET_MS = 1e4;
|
|
6315
6318
|
async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
6316
6319
|
const base = {
|
|
6317
6320
|
ok: false,
|
|
@@ -6337,7 +6340,7 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
6337
6340
|
"x-cabane-active-conversation": req.cabane.activeConversationId
|
|
6338
6341
|
};
|
|
6339
6342
|
try {
|
|
6340
|
-
const initialized = await
|
|
6343
|
+
const initialized = await initializeWithRetry(fetchImpl, req.cabane.mcpUrl, headers, {
|
|
6341
6344
|
jsonrpc: "2.0",
|
|
6342
6345
|
id: 1,
|
|
6343
6346
|
method: "initialize",
|
|
@@ -6349,6 +6352,8 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
6349
6352
|
});
|
|
6350
6353
|
if (initialized.status === 401 || initialized.status === 403)
|
|
6351
6354
|
return fail(base, "authentication_failed", `initialize returned HTTP ${initialized.status}`);
|
|
6355
|
+
if (initialized.transient)
|
|
6356
|
+
return fail(base, "workspace_endpoint_unreachable", initialized.detail);
|
|
6352
6357
|
if (!initialized.ok) return fail(base, "initialization_failed", initialized.detail);
|
|
6353
6358
|
base.initialized = true;
|
|
6354
6359
|
base.authenticated = true;
|
|
@@ -6383,11 +6388,47 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
|
|
|
6383
6388
|
} catch (error) {
|
|
6384
6389
|
return fail(
|
|
6385
6390
|
base,
|
|
6386
|
-
"
|
|
6391
|
+
"workspace_endpoint_unreachable",
|
|
6387
6392
|
error instanceof Error ? error.message : String(error)
|
|
6388
6393
|
);
|
|
6389
6394
|
}
|
|
6390
6395
|
}
|
|
6396
|
+
async function initializeWithRetry(fetchImpl, url, headers, body) {
|
|
6397
|
+
let lastFailure = null;
|
|
6398
|
+
const deadline = Date.now() + INITIALIZE_RETRY_BUDGET_MS;
|
|
6399
|
+
for (let attempt = 0; attempt <= INITIALIZE_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
6400
|
+
try {
|
|
6401
|
+
const remainingMs = deadline - Date.now();
|
|
6402
|
+
if (remainingMs <= 0) break;
|
|
6403
|
+
const result = await rpc(
|
|
6404
|
+
fetchImpl,
|
|
6405
|
+
url,
|
|
6406
|
+
headers,
|
|
6407
|
+
body,
|
|
6408
|
+
Math.min(INITIALIZE_ATTEMPT_TIMEOUT_MS, remainingMs)
|
|
6409
|
+
);
|
|
6410
|
+
if (result.status === 401 || result.status === 403 || result.ok) return result;
|
|
6411
|
+
if (result.status < 500) return result;
|
|
6412
|
+
lastFailure = { ...result, transient: true };
|
|
6413
|
+
} catch (error) {
|
|
6414
|
+
lastFailure = {
|
|
6415
|
+
ok: false,
|
|
6416
|
+
status: 0,
|
|
6417
|
+
sessionId: null,
|
|
6418
|
+
value: null,
|
|
6419
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
6420
|
+
transient: true
|
|
6421
|
+
};
|
|
6422
|
+
}
|
|
6423
|
+
const retryDelayMs = INITIALIZE_RETRY_DELAYS_MS[attempt];
|
|
6424
|
+
if (retryDelayMs === void 0 || Date.now() + retryDelayMs >= deadline) break;
|
|
6425
|
+
await delay(retryDelayMs);
|
|
6426
|
+
}
|
|
6427
|
+
return lastFailure;
|
|
6428
|
+
}
|
|
6429
|
+
function delay(ms) {
|
|
6430
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6431
|
+
}
|
|
6391
6432
|
function fail(proof, capability, detail) {
|
|
6392
6433
|
proof.failedCapability = capability;
|
|
6393
6434
|
proof.detail = detail.slice(0, 300);
|
|
@@ -6401,8 +6442,13 @@ function safeEndpoint(value) {
|
|
|
6401
6442
|
return null;
|
|
6402
6443
|
}
|
|
6403
6444
|
}
|
|
6404
|
-
async function rpc(fetchImpl, url, headers, body) {
|
|
6405
|
-
const response = await fetchImpl(url, {
|
|
6445
|
+
async function rpc(fetchImpl, url, headers, body, timeoutMs) {
|
|
6446
|
+
const response = await fetchImpl(url, {
|
|
6447
|
+
method: "POST",
|
|
6448
|
+
headers,
|
|
6449
|
+
body: JSON.stringify(body),
|
|
6450
|
+
...timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}
|
|
6451
|
+
});
|
|
6406
6452
|
const text = await response.text();
|
|
6407
6453
|
const value = parseRpcBody(text);
|
|
6408
6454
|
return {
|
|
@@ -7039,7 +7085,8 @@ ${reason}`,
|
|
|
7039
7085
|
);
|
|
7040
7086
|
}
|
|
7041
7087
|
if (!proof.ok) {
|
|
7042
|
-
const
|
|
7088
|
+
const recovery = proof.failedCapability === "workspace_endpoint_unreachable" ? "the Cabane workspace endpoint was unreachable; retry this dispatch" : "restart the connector after restoring the Cabane workspace tool mount";
|
|
7089
|
+
const reason = `workspace_tools_missing: ${proof.failedCapability}; checkout=${effectiveCwd}; runtime=${adapter.name}; recovery=${recovery}`;
|
|
7043
7090
|
closeTurnReceipt(false, reason);
|
|
7044
7091
|
try {
|
|
7045
7092
|
await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
|
|
@@ -7872,6 +7919,7 @@ var SseSubscriber = class {
|
|
|
7872
7919
|
try {
|
|
7873
7920
|
await this.connect();
|
|
7874
7921
|
backoff = 500;
|
|
7922
|
+
if (!this.aborted) await sleep2(backoff);
|
|
7875
7923
|
} catch (err) {
|
|
7876
7924
|
if (this.aborted) return;
|
|
7877
7925
|
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
@@ -7897,8 +7945,8 @@ var SseSubscriber = class {
|
|
|
7897
7945
|
}
|
|
7898
7946
|
async connect() {
|
|
7899
7947
|
const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
7900
|
-
const query = this.lastEventId ? "" : "?replayPending=1";
|
|
7901
|
-
const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
7948
|
+
const query = this.opts.path ? "" : this.lastEventId ? "" : "?replayPending=1";
|
|
7949
|
+
const url = this.opts.path ? `${base}${this.opts.path}` : `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
7902
7950
|
this.controller = new AbortController();
|
|
7903
7951
|
const headers = {
|
|
7904
7952
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -7990,6 +8038,7 @@ var CompanionSupervisor = class {
|
|
|
7990
8038
|
reexecFn;
|
|
7991
8039
|
dispatcherFactory;
|
|
7992
8040
|
deviceApi = null;
|
|
8041
|
+
deviceSub = null;
|
|
7993
8042
|
heartbeatTimer = null;
|
|
7994
8043
|
inFlightHeartbeat = null;
|
|
7995
8044
|
pollTimer = null;
|
|
@@ -8043,6 +8092,24 @@ var CompanionSupervisor = class {
|
|
|
8043
8092
|
baseUrl: this.config.baseUrl,
|
|
8044
8093
|
deviceToken: this.config.deviceToken
|
|
8045
8094
|
});
|
|
8095
|
+
this.deviceSub = new SseSubscriber({
|
|
8096
|
+
baseUrl: this.config.baseUrl,
|
|
8097
|
+
workspaceId: "device-control",
|
|
8098
|
+
path: "/api/companion/events",
|
|
8099
|
+
token: this.config.deviceToken,
|
|
8100
|
+
log: this.log,
|
|
8101
|
+
lastEventId: null,
|
|
8102
|
+
onMessage: async (ev) => {
|
|
8103
|
+
if (ev.event === "assignments_changed") {
|
|
8104
|
+
if (this.refreshing && this.inFlightRefresh) await this.inFlightRefresh;
|
|
8105
|
+
await this.refreshAssignments();
|
|
8106
|
+
}
|
|
8107
|
+
},
|
|
8108
|
+
onAuthFailure: () => {
|
|
8109
|
+
this.log.error("companion: device control stream authentication failed");
|
|
8110
|
+
}
|
|
8111
|
+
});
|
|
8112
|
+
this.deviceSub.start();
|
|
8046
8113
|
await this.refreshAssignments();
|
|
8047
8114
|
this.kickHeartbeat();
|
|
8048
8115
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
@@ -8055,7 +8122,10 @@ var CompanionSupervisor = class {
|
|
|
8055
8122
|
// at least the loops keeping the event loop alive; the SSE promises let a
|
|
8056
8123
|
// clean teardown resolve.
|
|
8057
8124
|
finishedPromises() {
|
|
8058
|
-
return [
|
|
8125
|
+
return [
|
|
8126
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
8127
|
+
...[...this.workspaces.values()].flatMap((w) => w.sub ? [w.sub.finished] : [])
|
|
8128
|
+
];
|
|
8059
8129
|
}
|
|
8060
8130
|
// ---- device-level loops ----
|
|
8061
8131
|
kickHeartbeat() {
|
|
@@ -8739,13 +8809,15 @@ var CompanionSupervisor = class {
|
|
|
8739
8809
|
this.stopped = true;
|
|
8740
8810
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
8741
8811
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
8812
|
+
this.deviceSub?.stop();
|
|
8742
8813
|
for (const wr of this.workspaces.values()) {
|
|
8743
8814
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
8744
8815
|
if (wr.sub) wr.sub.stop();
|
|
8745
8816
|
}
|
|
8746
|
-
await Promise.all(
|
|
8747
|
-
|
|
8748
|
-
|
|
8817
|
+
await Promise.all([
|
|
8818
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
8819
|
+
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
8820
|
+
]);
|
|
8749
8821
|
}
|
|
8750
8822
|
async drainForRestart(graceMs) {
|
|
8751
8823
|
this.restartPending = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
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",
|