borgmcp 2.12.1 → 2.14.0
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/assimilate-cmd.d.ts.map +1 -1
- package/dist/assimilate-cmd.js +8 -4
- package/dist/assimilate-cmd.js.map +1 -1
- package/dist/claude.d.ts +23 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +46 -26
- package/dist/claude.js.map +1 -1
- package/dist/codex-app-wake.d.ts +3 -3
- package/dist/codex-app-wake.d.ts.map +1 -1
- package/dist/codex-app-wake.js +10 -8
- package/dist/codex-app-wake.js.map +1 -1
- package/dist/cube-activity-wake-copy.d.ts +3 -0
- package/dist/cube-activity-wake-copy.d.ts.map +1 -0
- package/dist/cube-activity-wake-copy.js +5 -0
- package/dist/cube-activity-wake-copy.js.map +1 -0
- package/dist/inbox-monitor.d.ts +1 -1
- package/dist/inbox-monitor.d.ts.map +1 -1
- package/dist/inbox-monitor.js +6 -3
- package/dist/inbox-monitor.js.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -27
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +2 -1
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +44 -12
- package/dist/log-stream.js.map +1 -1
- package/dist/opencode-drone.d.ts +12 -3
- package/dist/opencode-drone.d.ts.map +1 -1
- package/dist/opencode-drone.js +60 -5
- package/dist/opencode-drone.js.map +1 -1
- package/dist/opencode-wake-copy.d.ts.map +1 -1
- package/dist/opencode-wake-copy.js +3 -1
- package/dist/opencode-wake-copy.js.map +1 -1
- package/dist/remote-client.d.ts +5 -0
- package/dist/remote-client.d.ts.map +1 -1
- package/dist/remote-client.js +36 -11
- package/dist/remote-client.js.map +1 -1
- package/dist/roster-render.d.ts +2 -0
- package/dist/roster-render.d.ts.map +1 -1
- package/dist/roster-render.js +4 -4
- package/dist/roster-render.js.map +1 -1
- package/docs/EXTRACTION_PROVENANCE.md +8 -7
- package/docs/LOCAL_SERVER.md +3 -7
- package/docs/RELEASING.md +10 -6
- package/package.json +2 -2
- package/src/assimilate-cmd.ts +8 -4
- package/src/claude.ts +68 -27
- package/src/codex-app-wake.ts +15 -8
- package/src/cube-activity-wake-copy.ts +6 -0
- package/src/inbox-monitor.ts +7 -3
- package/src/index.ts +65 -26
- package/src/log-stream.ts +49 -16
- package/src/opencode-drone.ts +73 -5
- package/src/opencode-wake-copy.ts +4 -1
- package/src/remote-client.ts +41 -14
- package/src/roster-render.ts +6 -4
package/src/remote-client.ts
CHANGED
|
@@ -85,10 +85,27 @@ export const LOCAL_SERVER_RESPONSE_LIMIT_BYTES = 32 * 1024 * 1024;
|
|
|
85
85
|
// bounded read throws → the 401 fails closed to non-destructive CREDENTIAL_REJECTED.
|
|
86
86
|
const AUTH_ERROR_ENVELOPE_LIMIT_BYTES = 64 * 1024;
|
|
87
87
|
const ROLE_SECTION_CONFLICT_CODE = 'ROLE_SECTION_CONFLICT';
|
|
88
|
+
const CAPACITY_EXCEEDED_CODE = 'CAPACITY_EXCEEDED';
|
|
88
89
|
export const LOCAL_SERVER_REQUEST_TIMEOUT_MS = 5_000;
|
|
89
90
|
const LOCAL_SERVER_RESPONSE_LIMIT_MESSAGE =
|
|
90
91
|
'Local Borg server response exceeded the response limit';
|
|
91
92
|
|
|
93
|
+
function sanitizeServerMessage(message: string): string {
|
|
94
|
+
return message
|
|
95
|
+
.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, '')
|
|
96
|
+
.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '')
|
|
97
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]/g, '')
|
|
98
|
+
.replace(/\\u(?:000[0-9a-f]|001[0-9a-f]|007f|008[0-9a-f]|009[0-9a-f])/gi, '');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const SERVER_ADVISORY_MAX_CHARS = 512;
|
|
102
|
+
|
|
103
|
+
export function sanitizeServerAdvisory(value: unknown): string | undefined {
|
|
104
|
+
if (typeof value !== 'string') return undefined;
|
|
105
|
+
const sanitized = sanitizeServerMessage(value).trim();
|
|
106
|
+
return sanitized.length > 0 ? sanitized.slice(0, SERVER_ADVISORY_MAX_CHARS) : undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
/**
|
|
93
110
|
* Parse a `Retry-After` header (delta-seconds form, which the worker
|
|
94
111
|
* emits — mcp-server.ts:382/583) into milliseconds. Returns null when
|
|
@@ -725,11 +742,11 @@ async function authedFetch(
|
|
|
725
742
|
}
|
|
726
743
|
|
|
727
744
|
if (!response.ok) {
|
|
728
|
-
//
|
|
729
|
-
//
|
|
730
|
-
//
|
|
731
|
-
// branching; never surface the server-provided message or details.
|
|
745
|
+
// Decode only the bounded protocol error envelope. Its message is the
|
|
746
|
+
// server's operator-facing action guidance; details and unrecognized bodies
|
|
747
|
+
// remain excluded from the client error.
|
|
732
748
|
let code: ErrorCode | undefined;
|
|
749
|
+
let serverMessage: string | undefined;
|
|
733
750
|
let protocolMismatch = false;
|
|
734
751
|
try {
|
|
735
752
|
const body = await readBoundedResponseBody(
|
|
@@ -739,7 +756,11 @@ async function authedFetch(
|
|
|
739
756
|
);
|
|
740
757
|
const parsed = JSON.parse(body);
|
|
741
758
|
try {
|
|
742
|
-
|
|
759
|
+
const decoded = decodeProtocolErrorEnvelope(parsed);
|
|
760
|
+
code = decoded.error.code;
|
|
761
|
+
serverMessage = response.status === 404
|
|
762
|
+
? undefined
|
|
763
|
+
: sanitizeServerMessage(decoded.error.message);
|
|
743
764
|
} catch (error) {
|
|
744
765
|
if (
|
|
745
766
|
error instanceof ProtocolContractError &&
|
|
@@ -750,21 +771,25 @@ async function authedFetch(
|
|
|
750
771
|
if (
|
|
751
772
|
parsed !== null && typeof parsed === 'object' &&
|
|
752
773
|
parsed.error !== null && typeof parsed.error === 'object' &&
|
|
753
|
-
parsed.error.code === ROLE_SECTION_CONFLICT_CODE
|
|
774
|
+
(parsed.error.code === ROLE_SECTION_CONFLICT_CODE
|
|
775
|
+
|| parsed.error.code === CAPACITY_EXCEEDED_CODE)
|
|
754
776
|
) {
|
|
755
777
|
// The shared protocol intentionally omits this server-local code. Re-validate the whole
|
|
756
778
|
// envelope through the strict shared decoder with only the recognized
|
|
757
|
-
// code substituted
|
|
758
|
-
|
|
779
|
+
// code substituted. The original message remains in place so the
|
|
780
|
+
// shared decoder validates its length and diagnostic shape.
|
|
781
|
+
const decoded = decodeProtocolErrorEnvelope({
|
|
759
782
|
...parsed,
|
|
760
783
|
error: {
|
|
761
784
|
...parsed.error,
|
|
762
785
|
code: ErrorCode.INVALID_INPUT,
|
|
763
|
-
message: 'Role section conflict.',
|
|
764
786
|
...(Object.hasOwn(parsed.error, 'details') ? { details: 'Redacted.' } : {}),
|
|
765
787
|
},
|
|
766
788
|
});
|
|
767
|
-
code =
|
|
789
|
+
code = parsed.error.code as ErrorCode;
|
|
790
|
+
serverMessage = response.status === 404
|
|
791
|
+
? undefined
|
|
792
|
+
: sanitizeServerMessage(decoded.error.message);
|
|
768
793
|
}
|
|
769
794
|
}
|
|
770
795
|
} catch {
|
|
@@ -782,7 +807,9 @@ async function authedFetch(
|
|
|
782
807
|
}
|
|
783
808
|
throw new BorgServerHttpError(
|
|
784
809
|
response.status,
|
|
785
|
-
|
|
810
|
+
serverMessage
|
|
811
|
+
? `Borg server request failed (HTTP ${response.status}): ${serverMessage}`
|
|
812
|
+
: `Borg server request failed (HTTP ${response.status})`,
|
|
786
813
|
code,
|
|
787
814
|
);
|
|
788
815
|
}
|
|
@@ -1302,7 +1329,7 @@ export async function updateCube(
|
|
|
1302
1329
|
updates: { name?: string; cube_directive?: string; message_taxonomy?: MessageTaxonomy | null },
|
|
1303
1330
|
activeOverride?: ActiveCube,
|
|
1304
1331
|
connectionOverride?: RemoteConnection,
|
|
1305
|
-
): Promise<{ cube: any }> {
|
|
1332
|
+
): Promise<{ cube: any; advisory?: unknown }> {
|
|
1306
1333
|
assertUuidShape(cubeId, 'cube_id');
|
|
1307
1334
|
const active = activeOverride ?? await getActiveCube();
|
|
1308
1335
|
if (!active?.serverTrustIdentity) throw new Error('Selected Borg server authority state is missing or unreadable');
|
|
@@ -1438,7 +1465,7 @@ export async function updateRole(
|
|
|
1438
1465
|
targetCubeId?: string,
|
|
1439
1466
|
activeOverride?: ActiveCube,
|
|
1440
1467
|
connectionOverride?: RemoteConnection,
|
|
1441
|
-
): Promise<{ role: any }> {
|
|
1468
|
+
): Promise<{ role: any; advisory?: unknown }> {
|
|
1442
1469
|
assertUuidShape(roleId, 'role_id');
|
|
1443
1470
|
const active = activeOverride ?? await getActiveCube();
|
|
1444
1471
|
if (!active?.serverTrustIdentity) throw new Error('Selected Borg server authority state is missing or unreadable');
|
|
@@ -1514,7 +1541,7 @@ export async function patchRoleSection(
|
|
|
1514
1541
|
targetCubeId?: string,
|
|
1515
1542
|
activeOverride?: ActiveCube,
|
|
1516
1543
|
connectionOverride?: RemoteConnection,
|
|
1517
|
-
): Promise<{ role: any }> {
|
|
1544
|
+
): Promise<{ role: any; advisory?: unknown }> {
|
|
1518
1545
|
assertUuidShape(roleId, 'role_id');
|
|
1519
1546
|
const active = activeOverride ?? await getActiveCube();
|
|
1520
1547
|
if (!active?.serverTrustIdentity) throw new Error('Selected Borg server authority state is missing or unreadable');
|
package/src/roster-render.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface RosterDrone {
|
|
|
28
28
|
last_seen: string | Date;
|
|
29
29
|
/** Only present when the request carried `since`. */
|
|
30
30
|
seen_since?: boolean;
|
|
31
|
+
/** Additive server-side wake state; absent on pre-C3 servers. */
|
|
32
|
+
wake_state?: 'idle' | 'pending' | 'awake' | 'stale';
|
|
31
33
|
/**
|
|
32
34
|
* gh#370 — which AI agent is running this drone. Null for drones that
|
|
33
35
|
* joined before the column existed or via a launcher path that didn't
|
|
@@ -215,7 +217,8 @@ export function renderRoster(inputs: RenderRosterInputs): string {
|
|
|
215
217
|
const regenCountMarker =
|
|
216
218
|
typeof d.regen_count === 'number' ? ` · \`regen-count:${d.regen_count}\`` : '';
|
|
217
219
|
if (resolvedSince) {
|
|
218
|
-
// T2.1 awake/stale column.
|
|
220
|
+
// T2.1 awake/stale column. When available, wake_state is the server's
|
|
221
|
+
// richer state; older servers fall back to seen_since. `seen_since === true` → drone called a
|
|
219
222
|
// tool after the resolved timestamp; treat as awake. False or
|
|
220
223
|
// missing → stale. Missing should not happen when the server
|
|
221
224
|
// echoed a since, but defending against a shape mismatch is
|
|
@@ -229,10 +232,9 @@ export function renderRoster(inputs: RenderRosterInputs): string {
|
|
|
229
232
|
// probe call — pure redundancy. The per-row `last seen X ago`
|
|
230
233
|
// field carries the diagnostic detail for "how stale is this
|
|
231
234
|
// particular drone."
|
|
232
|
-
const
|
|
233
|
-
const marker = isAwake ? '`awake`' : '`stale`';
|
|
235
|
+
const marker = d.wake_state ?? (d.seen_since === true ? 'awake' : 'stale');
|
|
234
236
|
lines.push(
|
|
235
|
-
`- **${d.label}**${addr} (Role: ${roleName}) — last seen ${lastSeen} ·
|
|
237
|
+
`- **${d.label}**${addr} (Role: ${roleName}) — last seen ${lastSeen} · \`${marker}\`${regenCountMarker}${wakePathMarker}${wakePathClassMarker}`
|
|
236
238
|
);
|
|
237
239
|
} else {
|
|
238
240
|
lines.push(
|