borgmcp 3.15.1 → 3.16.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/README.md +2 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/codex-launch.d.ts.map +1 -1
- package/dist/codex-launch.js +3 -2
- package/dist/codex-launch.js.map +1 -1
- package/dist/docs-sections.d.ts.map +1 -1
- package/dist/docs-sections.js +8 -0
- package/dist/docs-sections.js.map +1 -1
- package/dist/document-render.d.ts +6 -0
- package/dist/document-render.d.ts.map +1 -0
- package/dist/document-render.js +34 -0
- package/dist/document-render.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/dist/launch-all-cmd.d.ts.map +1 -1
- package/dist/launch-all-cmd.js +12 -0
- package/dist/launch-all-cmd.js.map +1 -1
- package/dist/launch-all-deps.d.ts +2 -0
- package/dist/launch-all-deps.d.ts.map +1 -1
- package/dist/launch-all-deps.js +13 -0
- package/dist/launch-all-deps.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 +16 -3
- package/dist/log-stream.js.map +1 -1
- package/dist/regen-format.d.ts.map +1 -1
- package/dist/regen-format.js +6 -1
- package/dist/regen-format.js.map +1 -1
- package/dist/remote-client.d.ts +6 -1
- package/dist/remote-client.d.ts.map +1 -1
- package/dist/remote-client.js +41 -7
- package/dist/remote-client.js.map +1 -1
- package/dist/server-handshake.d.ts +4 -3
- package/dist/server-handshake.d.ts.map +1 -1
- package/dist/server-handshake.js.map +1 -1
- package/dist/tool-manifest.d.ts.map +1 -1
- package/dist/tool-manifest.js +65 -2
- package/dist/tool-manifest.js.map +1 -1
- package/dist/update-cmd.d.ts +5 -1
- package/dist/update-cmd.d.ts.map +1 -1
- package/dist/update-cmd.js +54 -14
- package/dist/update-cmd.js.map +1 -1
- package/docs/DOCUMENTS.md +49 -0
- package/docs/RELEASING.md +2 -2
- package/package.json +2 -2
- package/src/codex-launch.ts +3 -2
- package/src/docs-sections.ts +8 -0
- package/src/document-render.ts +41 -0
- package/src/index.ts +44 -1
- package/src/launch-all-cmd.ts +14 -0
- package/src/launch-all-deps.ts +15 -0
- package/src/log-stream.ts +21 -3
- package/src/regen-format.ts +6 -1
- package/src/remote-client.ts +108 -15
- package/src/server-handshake.ts +4 -3
- package/src/tool-manifest.ts +69 -2
- package/src/update-cmd.ts +67 -17
package/src/update-cmd.ts
CHANGED
|
@@ -63,7 +63,7 @@ export interface UpdateDeps {
|
|
|
63
63
|
options?: { ignoreScripts?: boolean },
|
|
64
64
|
): Promise<void>;
|
|
65
65
|
reenter(binPath: string, args: readonly string[]): Promise<number>;
|
|
66
|
-
serverJson(binPath: string, command: 'update' | 'status'): Promise<
|
|
66
|
+
serverJson(binPath: string, command: 'update' | 'status'): Promise<ServerJsonExecution>;
|
|
67
67
|
verifyRunningProtocol(origin: string): Promise<void>;
|
|
68
68
|
refreshAgentIntegrations(): Promise<void>;
|
|
69
69
|
confirm(message: string): Promise<'yes' | 'no' | 'eof' | 'interrupted'>;
|
|
@@ -73,6 +73,11 @@ export interface UpdateDeps {
|
|
|
73
73
|
calls?: string[];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
export interface ServerJsonExecution {
|
|
77
|
+
exitCode: number;
|
|
78
|
+
value: unknown;
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
interface ServerStatus {
|
|
77
82
|
state: 'running' | 'stopped';
|
|
78
83
|
installedController: string;
|
|
@@ -85,6 +90,10 @@ interface ServerStatus {
|
|
|
85
90
|
mode: 'foreground' | 'managed' | 'legacy' | 'stopped';
|
|
86
91
|
serviceAdapter: 'launchd' | 'systemd' | null;
|
|
87
92
|
serviceRecovery: { command: string[] } | null;
|
|
93
|
+
runtimeLock:
|
|
94
|
+
| { state: 'clear' }
|
|
95
|
+
| { state: 'stale'; recoveryAction: 'borg-mcp-server recover-stale-lock' }
|
|
96
|
+
| null;
|
|
88
97
|
dataIdentity: 'available' | 'unavailable';
|
|
89
98
|
nextAction: string | null;
|
|
90
99
|
}
|
|
@@ -375,6 +384,27 @@ function decodeManagedServiceRecovery(value: unknown): { command: string[] } | n
|
|
|
375
384
|
return { command: record.command as string[] };
|
|
376
385
|
}
|
|
377
386
|
|
|
387
|
+
function decodeStoppedRuntimeLock(value: unknown): Exclude<ServerStatus['runtimeLock'], null> | null {
|
|
388
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
389
|
+
const record = value as Record<string, unknown>;
|
|
390
|
+
if (record.state === 'clear') return { state: 'clear' };
|
|
391
|
+
if (
|
|
392
|
+
record.state !== 'stale' ||
|
|
393
|
+
!Number.isSafeInteger(record.pid) || (record.pid as number) <= 0 ||
|
|
394
|
+
record.process_state !== 'absent' ||
|
|
395
|
+
typeof record.runtime !== 'string' || record.runtime.length === 0 ||
|
|
396
|
+
(record.runtime_integrity !== null &&
|
|
397
|
+
(typeof record.runtime_integrity !== 'string' || !isCanonicalSha512Integrity(record.runtime_integrity))) ||
|
|
398
|
+
(record.build_identity !== null && typeof record.build_identity !== 'string') ||
|
|
399
|
+
(record.endpoint !== null && typeof record.endpoint !== 'string') ||
|
|
400
|
+
!['foreground', 'managed', 'legacy'].includes(record.mode as string) ||
|
|
401
|
+
record.recovery_action !== 'borg-mcp-server recover-stale-lock'
|
|
402
|
+
) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
return { state: 'stale', recoveryAction: record.recovery_action };
|
|
406
|
+
}
|
|
407
|
+
|
|
378
408
|
function decodeServerStatus(value: unknown): ServerStatus {
|
|
379
409
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
380
410
|
throw new Error('server returned invalid JSON status');
|
|
@@ -414,12 +444,24 @@ function decodeServerStatus(value: unknown): ServerStatus {
|
|
|
414
444
|
) {
|
|
415
445
|
throw new Error('server returned inconsistent JSON status');
|
|
416
446
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
447
|
+
let managedRecovery: { command: string[] } | null = null;
|
|
448
|
+
let runtimeLock: ServerStatus['runtimeLock'] = null;
|
|
449
|
+
if (record.status === 'stopped') {
|
|
450
|
+
runtimeLock = decodeStoppedRuntimeLock(record.runtime_lock);
|
|
451
|
+
if (runtimeLock === null) throw new Error('server returned invalid JSON status runtime lock');
|
|
452
|
+
if (!['active', 'inactive', 'absent'].includes(record.service_state as string)) {
|
|
453
|
+
throw new Error('server returned invalid managed-service recovery status');
|
|
454
|
+
}
|
|
455
|
+
managedRecovery = record.service_adapter !== null && record.service_state === 'inactive'
|
|
456
|
+
? decodeManagedServiceRecovery(record.service_recovery)
|
|
457
|
+
: null;
|
|
458
|
+
if (
|
|
459
|
+
(record.service_adapter !== null && record.service_state === 'inactive' && managedRecovery === null) ||
|
|
460
|
+
(record.service_state !== 'inactive' && record.service_recovery !== null) ||
|
|
461
|
+
(record.service_adapter === null && record.service_state !== 'absent')
|
|
462
|
+
) {
|
|
463
|
+
throw new Error('server returned invalid managed-service recovery status');
|
|
464
|
+
}
|
|
423
465
|
}
|
|
424
466
|
return {
|
|
425
467
|
state: record.status,
|
|
@@ -433,11 +475,21 @@ function decodeServerStatus(value: unknown): ServerStatus {
|
|
|
433
475
|
mode: record.mode as ServerStatus['mode'],
|
|
434
476
|
serviceAdapter: record.service_adapter,
|
|
435
477
|
serviceRecovery: managedRecovery,
|
|
478
|
+
runtimeLock,
|
|
436
479
|
dataIdentity: record.data_identity,
|
|
437
480
|
nextAction: record.next_action,
|
|
438
481
|
};
|
|
439
482
|
}
|
|
440
483
|
|
|
484
|
+
function decodeServerStatusExecution(execution: ServerJsonExecution): ServerStatus {
|
|
485
|
+
const status = decodeServerStatus(execution.value);
|
|
486
|
+
const stale = status.state === 'stopped' && status.runtimeLock?.state === 'stale';
|
|
487
|
+
if ((stale && execution.exitCode !== 1) || (!stale && execution.exitCode !== 0)) {
|
|
488
|
+
throw new Error(`server returned invalid JSON status exit pairing (${execution.exitCode})`);
|
|
489
|
+
}
|
|
490
|
+
return status;
|
|
491
|
+
}
|
|
492
|
+
|
|
441
493
|
function decodeServerUpdate(value: unknown): ServerUpdateResult {
|
|
442
494
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
443
495
|
throw new Error('server returned invalid JSON update result');
|
|
@@ -551,6 +603,9 @@ function renderServerFailureRecovery(
|
|
|
551
603
|
}
|
|
552
604
|
|
|
553
605
|
function renderStoppedServiceRecovery(status: ServerStatus): string {
|
|
606
|
+
if (status.runtimeLock?.state === 'stale') {
|
|
607
|
+
return `Local server runtime lock is stale.\nRecover it with: borg-mcp-server recover-stale-lock\n`;
|
|
608
|
+
}
|
|
554
609
|
if (status.serviceRecovery !== null) {
|
|
555
610
|
const command = status.serviceRecovery.command.map(shellEscape).join(' ');
|
|
556
611
|
return `Local server service is stopped.\nRestart it with: ${command}\n`;
|
|
@@ -754,13 +809,13 @@ export async function runUpdate(options: UpdateOptions, deps: UpdateDeps): Promi
|
|
|
754
809
|
const observeStatusAfterFailure = async (): Promise<void> => {
|
|
755
810
|
recoveryStatusAttempted = true;
|
|
756
811
|
try {
|
|
757
|
-
observedStatus =
|
|
812
|
+
observedStatus = decodeServerStatusExecution(await deps.serverJson(server.binPath, 'status'));
|
|
758
813
|
} catch {
|
|
759
814
|
observedStatus = null;
|
|
760
815
|
}
|
|
761
816
|
};
|
|
762
817
|
try {
|
|
763
|
-
let status =
|
|
818
|
+
let status = decodeServerStatusExecution(await deps.serverJson(server.binPath, 'status'));
|
|
764
819
|
observedStatus = status;
|
|
765
820
|
initialServerState = status.state;
|
|
766
821
|
if (status.installedController !== exactServerIdentity(pair.server.version)) {
|
|
@@ -775,7 +830,7 @@ export async function runUpdate(options: UpdateOptions, deps: UpdateDeps): Promi
|
|
|
775
830
|
updateAttempted = true;
|
|
776
831
|
failureStage = 'server runtime activation';
|
|
777
832
|
retryCommand = 'borg server update';
|
|
778
|
-
const update = decodeServerUpdate(await deps.serverJson(server.binPath, 'update'));
|
|
833
|
+
const update = decodeServerUpdate((await deps.serverJson(server.binPath, 'update')).value);
|
|
779
834
|
observedUpdate = update;
|
|
780
835
|
if (update.status === 'failed') {
|
|
781
836
|
await observeStatusAfterFailure();
|
|
@@ -794,7 +849,7 @@ export async function runUpdate(options: UpdateOptions, deps: UpdateDeps): Promi
|
|
|
794
849
|
failureStage = 'post-update server status check';
|
|
795
850
|
retryCommand = 'borg server status';
|
|
796
851
|
recoveryStatusAttempted = true;
|
|
797
|
-
status =
|
|
852
|
+
status = decodeServerStatusExecution(await deps.serverJson(server.binPath, 'status'));
|
|
798
853
|
observedStatus = status;
|
|
799
854
|
}
|
|
800
855
|
failureStage = 'final server state verification';
|
|
@@ -1221,12 +1276,7 @@ export function buildDefaultUpdateDeps(): UpdateDeps {
|
|
|
1221
1276
|
} catch {
|
|
1222
1277
|
throw new Error(`server ${command} returned invalid JSON${serverCommandStderr(result.stderr)}`);
|
|
1223
1278
|
}
|
|
1224
|
-
|
|
1225
|
-
throw new Error(
|
|
1226
|
-
`server ${command} exited ${result.code}${serverCommandStderr(result.stderr)}`,
|
|
1227
|
-
);
|
|
1228
|
-
}
|
|
1229
|
-
return parsed;
|
|
1279
|
+
return { exitCode: result.code, value: parsed };
|
|
1230
1280
|
},
|
|
1231
1281
|
verifyRunningProtocol: async (origin) => {
|
|
1232
1282
|
const trust = await loadBorgServerTrust(origin);
|