borgmcp 2.0.3 → 2.0.4

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.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/dist/assimilate-cmd.d.ts +0 -2
  3. package/dist/assimilate-cmd.d.ts.map +1 -1
  4. package/dist/assimilate-cmd.js +51 -43
  5. package/dist/assimilate-cmd.js.map +1 -1
  6. package/dist/assimilate-deps.d.ts.map +1 -1
  7. package/dist/assimilate-deps.js +0 -1
  8. package/dist/assimilate-deps.js.map +1 -1
  9. package/dist/cleanup-cmd.d.ts +1 -1
  10. package/dist/cleanup-cmd.d.ts.map +1 -1
  11. package/dist/cleanup-cmd.js +3 -1
  12. package/dist/cleanup-cmd.js.map +1 -1
  13. package/dist/cli-help.d.ts.map +1 -1
  14. package/dist/cli-help.js +2 -1
  15. package/dist/cli-help.js.map +1 -1
  16. package/dist/cubes.d.ts +0 -2
  17. package/dist/cubes.d.ts.map +1 -1
  18. package/dist/cubes.js +0 -2
  19. package/dist/cubes.js.map +1 -1
  20. package/dist/launch-all-cmd.d.ts.map +1 -1
  21. package/dist/launch-all-cmd.js +13 -7
  22. package/dist/launch-all-cmd.js.map +1 -1
  23. package/dist/log-stream.d.ts +0 -2
  24. package/dist/log-stream.d.ts.map +1 -1
  25. package/dist/log-stream.js +3 -30
  26. package/dist/log-stream.js.map +1 -1
  27. package/dist/remote-client.d.ts.map +1 -1
  28. package/dist/remote-client.js +8 -23
  29. package/dist/remote-client.js.map +1 -1
  30. package/dist/seat-probe.d.ts +4 -4
  31. package/dist/seat-probe.d.ts.map +1 -1
  32. package/dist/seat-probe.js +2 -0
  33. package/dist/seat-probe.js.map +1 -1
  34. package/dist/seats.d.ts +0 -74
  35. package/dist/seats.d.ts.map +1 -1
  36. package/dist/seats.js +31 -131
  37. package/dist/seats.js.map +1 -1
  38. package/dist/server-errors.d.ts +6 -1
  39. package/dist/server-errors.d.ts.map +1 -1
  40. package/dist/server-errors.js +9 -0
  41. package/dist/server-errors.js.map +1 -1
  42. package/dist/server-facade.d.ts +1 -1
  43. package/dist/server-facade.d.ts.map +1 -1
  44. package/dist/server-facade.js +2 -2
  45. package/dist/server-facade.js.map +1 -1
  46. package/dist/server-handshake.d.ts +0 -2
  47. package/dist/server-handshake.d.ts.map +1 -1
  48. package/dist/server-handshake.js +0 -5
  49. package/dist/server-handshake.js.map +1 -1
  50. package/docs/EXTRACTION_PROVENANCE.md +5 -5
  51. package/docs/LOCAL_SERVER.md +7 -3
  52. package/docs/RELEASING.md +10 -1
  53. package/package.json +1 -1
  54. package/src/assimilate-cmd.ts +54 -51
  55. package/src/assimilate-deps.ts +0 -1
  56. package/src/cleanup-cmd.ts +5 -2
  57. package/src/cli-help.ts +2 -1
  58. package/src/cubes.ts +0 -4
  59. package/src/launch-all-cmd.ts +15 -7
  60. package/src/log-stream.ts +2 -30
  61. package/src/remote-client.ts +10 -22
  62. package/src/seat-probe.ts +5 -3
  63. package/src/seats.ts +35 -186
  64. package/src/server-errors.ts +9 -2
  65. package/src/server-facade.ts +2 -2
  66. package/src/server-handshake.ts +1 -10
  67. package/dist/session-continuity.d.ts +0 -33
  68. package/dist/session-continuity.d.ts.map +0 -1
  69. package/dist/session-continuity.js +0 -220
  70. package/dist/session-continuity.js.map +0 -1
  71. package/src/session-continuity.ts +0 -280
@@ -1,280 +0,0 @@
1
- import { createHash, randomBytes } from 'node:crypto';
2
- import { getServerCredential } from './config.js';
3
- import { getActiveCube, type ActiveCube } from './cubes.js';
4
- import { DroneEvictedError } from './drone-lifecycle.js';
5
- import { sendBorgServerAttach } from './server-handshake.js';
6
- import { BorgServerError, BorgServerTrustError } from './server-errors.js';
7
- import { loadBorgServerTrust } from './server-trust.js';
8
- import {
9
- getActiveSeat,
10
- prepareSeatReplacement,
11
- promoteSeatReplacement,
12
- refreshActiveSeatSession,
13
- scrubSeatReplacement,
14
- seatRef,
15
- type SeatRecord,
16
- } from './seats.js';
17
-
18
- export const SESSION_RENEWAL_LEAD_MS = 5 * 60_000;
19
-
20
- interface AttachOutcome {
21
- result: 'created' | 'reused';
22
- droneId: string;
23
- sessionId: string;
24
- expiresAt: string;
25
- }
26
-
27
- interface SessionContinuityDeps {
28
- now(): number;
29
- randomCredential(): string;
30
- getSeat(ref: string, binding: { origin: string; trustIdentity: string; cubeId: string }): Promise<SeatRecord | null>;
31
- getActive(): Promise<ActiveCube | null>;
32
- getParentCredential(origin: string, trustIdentity: string): Promise<string | null>;
33
- sendAttach(record: SeatRecord, bearer: string, parentCredential: string): Promise<AttachOutcome>;
34
- prepareReplacement: typeof prepareSeatReplacement;
35
- refreshSession: typeof refreshActiveSeatSession;
36
- promoteReplacement: typeof promoteSeatReplacement;
37
- scrubReplacement: typeof scrubSeatReplacement;
38
- }
39
-
40
- const defaultDeps: SessionContinuityDeps = {
41
- now: Date.now,
42
- randomCredential: () => randomBytes(32).toString('base64url'),
43
- getSeat: getActiveSeat,
44
- getActive: getActiveCube,
45
- getParentCredential: getServerCredential,
46
- async sendAttach(record, bearer, parentCredential) {
47
- const trust = await loadBorgServerTrust(record.origin);
48
- if (trust.identity !== record.trustIdentity) {
49
- throw new BorgServerTrustError('Borg server trust identity changed; refusing session renewal');
50
- }
51
- const attached = await sendBorgServerAttach(
52
- record.origin,
53
- record.trustIdentity,
54
- parentCredential,
55
- {
56
- cubeId: record.cubeId,
57
- roleId: record.roleId,
58
- operation: record.operation,
59
- priorDroneId: record.droneId,
60
- },
61
- bearer,
62
- { fetchImpl: trust.fetchImpl },
63
- );
64
- return {
65
- result: attached.result,
66
- droneId: attached.drone.id,
67
- sessionId: attached.session.sessionId,
68
- expiresAt: attached.session.expiresAt,
69
- };
70
- },
71
- prepareReplacement: prepareSeatReplacement,
72
- refreshSession: refreshActiveSeatSession,
73
- promoteReplacement: promoteSeatReplacement,
74
- scrubReplacement: scrubSeatReplacement,
75
- };
76
-
77
- const inflight = new Map<string, Promise<ActiveCube>>();
78
- const digestOf = (value: string) => createHash('sha256').update(value).digest('hex');
79
-
80
- function sameSeat(active: ActiveCube, candidate: ActiveCube | null): candidate is ActiveCube {
81
- return candidate !== null &&
82
- candidate.apiUrl === active.apiUrl &&
83
- candidate.serverTrustIdentity === active.serverTrustIdentity &&
84
- candidate.cubeId === active.cubeId &&
85
- candidate.droneId === active.droneId &&
86
- candidate.localSessionCredentialRef === active.localSessionCredentialRef;
87
- }
88
-
89
- function seatBinding(record: SeatRecord) {
90
- return {
91
- origin: record.origin,
92
- trustIdentity: record.trustIdentity,
93
- cubeId: record.cubeId,
94
- };
95
- }
96
-
97
- function validActiveRecord(active: ActiveCube, record: SeatRecord | null): record is SeatRecord {
98
- return record !== null &&
99
- record.state === 'active' &&
100
- record.origin === active.apiUrl &&
101
- record.trustIdentity === active.serverTrustIdentity &&
102
- record.cubeId === active.cubeId &&
103
- record.droneId === active.droneId &&
104
- seatRef(record) === active.localSessionCredentialRef;
105
- }
106
-
107
- function deterministicAttachFailure(error: unknown): boolean {
108
- return error instanceof BorgServerError ||
109
- error instanceof BorgServerTrustError ||
110
- error instanceof DroneEvictedError;
111
- }
112
-
113
- async function reloadSameSeat(
114
- active: ActiveCube,
115
- deps: SessionContinuityDeps,
116
- requireFreshCredential = false,
117
- ): Promise<ActiveCube> {
118
- const current = await deps.getActive();
119
- if (!sameSeat(active, current) || (requireFreshCredential && current.sessionToken === active.sessionToken)) {
120
- throw new Error('Session renewal no longer targets the same saved seat');
121
- }
122
- return current;
123
- }
124
-
125
- async function performRenewal(
126
- active: ActiveCube,
127
- mode: 'proactive' | 'expired',
128
- deps: SessionContinuityDeps,
129
- ): Promise<ActiveCube> {
130
- if (!active.serverTrustIdentity || !active.localSessionCredentialRef) {
131
- throw new Error('Selected Borg server authority state is missing or unreadable');
132
- }
133
- const binding = {
134
- origin: active.apiUrl,
135
- trustIdentity: active.serverTrustIdentity,
136
- cubeId: active.cubeId,
137
- };
138
- const record = await deps.getSeat(active.localSessionCredentialRef, binding);
139
- if (!validActiveRecord(active, record)) {
140
- throw new Error('Session renewal no longer targets the same saved seat');
141
- }
142
- const activeDigest = digestOf(active.sessionToken);
143
- if (digestOf(record.credential) !== activeDigest) {
144
- return reloadSameSeat(active, deps, true);
145
- }
146
- const expiresIn = Date.parse(record.expiresAt!) - deps.now();
147
- if (mode === 'proactive' && expiresIn > SESSION_RENEWAL_LEAD_MS && record.replacement === undefined) {
148
- return active;
149
- }
150
- const parentCredential = await deps.getParentCredential(record.origin, record.trustIdentity);
151
- if (!parentCredential) throw new Error('Selected Borg server parent credential is missing or unreadable');
152
- const recordBinding = seatBinding(record);
153
-
154
- let restoreRequired = mode === 'expired' || expiresIn <= 0 || record.replacement !== undefined;
155
- if (!restoreRequired) {
156
- let attached: AttachOutcome | null = null;
157
- try {
158
- attached = await deps.sendAttach(record, record.credential, parentCredential);
159
- } catch (error) {
160
- if (error instanceof BorgServerError && error.code === 'AUTH_EXPIRED') {
161
- restoreRequired = true;
162
- } else {
163
- throw error;
164
- }
165
- }
166
- if (!restoreRequired) {
167
- if (!attached) throw new Error('Session renewal returned no result');
168
- const renewedExpiry = Date.parse(attached.expiresAt);
169
- if (
170
- attached.result !== 'reused' ||
171
- attached.droneId !== record.droneId ||
172
- attached.sessionId !== record.sessionId ||
173
- !Number.isFinite(renewedExpiry) ||
174
- renewedExpiry <= Date.parse(record.expiresAt!)
175
- ) {
176
- throw new Error('Session renewal did not preserve the same saved seat');
177
- }
178
- const refreshed = await deps.refreshSession({
179
- ref: active.localSessionCredentialRef,
180
- binding: recordBinding,
181
- expectedDroneId: record.droneId!,
182
- expectedActiveDigest: activeDigest,
183
- expectedSessionId: record.sessionId!,
184
- expiresAt: attached.expiresAt,
185
- });
186
- if (!refreshed) return reloadSameSeat(active, deps);
187
- return reloadSameSeat(active, deps);
188
- }
189
- }
190
-
191
- const replacement = await deps.prepareReplacement({
192
- ref: active.localSessionCredentialRef,
193
- binding: recordBinding,
194
- expectedDroneId: record.droneId!,
195
- expectedActiveDigest: activeDigest,
196
- replacementCredential: deps.randomCredential(),
197
- });
198
- if (!replacement.ok) return reloadSameSeat(active, deps, true);
199
-
200
- let attached: AttachOutcome;
201
- try {
202
- attached = await deps.sendAttach(record, replacement.credential, parentCredential);
203
- } catch (error) {
204
- if (deterministicAttachFailure(error)) {
205
- await deps.scrubReplacement({
206
- ref: active.localSessionCredentialRef,
207
- binding: recordBinding,
208
- expectedActiveDigest: activeDigest,
209
- expectedReplacementDigest: replacement.digest,
210
- });
211
- }
212
- throw error;
213
- }
214
- const restoredExpiry = Date.parse(attached.expiresAt);
215
- if (
216
- (attached.result !== 'created' && attached.result !== 'reused') ||
217
- attached.droneId !== record.droneId ||
218
- attached.sessionId === record.sessionId ||
219
- !Number.isFinite(restoredExpiry) ||
220
- restoredExpiry <= deps.now()
221
- ) {
222
- await deps.scrubReplacement({
223
- ref: active.localSessionCredentialRef,
224
- binding: recordBinding,
225
- expectedActiveDigest: activeDigest,
226
- expectedReplacementDigest: replacement.digest,
227
- });
228
- throw new Error('Session renewal did not preserve the same saved seat');
229
- }
230
- const promoted = await deps.promoteReplacement({
231
- ref: active.localSessionCredentialRef,
232
- binding: recordBinding,
233
- expectedDroneId: record.droneId!,
234
- expectedActiveDigest: activeDigest,
235
- expectedReplacementDigest: replacement.digest,
236
- sessionId: attached.sessionId,
237
- expiresAt: attached.expiresAt,
238
- });
239
- if (promoted !== 'promoted') return reloadSameSeat(active, deps, true);
240
- return reloadSameSeat(active, deps, true);
241
- }
242
-
243
- function renewLocalSession(
244
- active: ActiveCube,
245
- mode: 'proactive' | 'expired',
246
- deps: SessionContinuityDeps,
247
- ): Promise<ActiveCube> {
248
- const key = active.localSessionCredentialRef;
249
- if (!key) return Promise.reject(new Error('Selected Borg server authority state is missing or unreadable'));
250
- const existing = inflight.get(key);
251
- if (existing) return existing;
252
- const renewal = performRenewal(active, mode, deps);
253
- inflight.set(key, renewal);
254
- void renewal.finally(() => {
255
- if (inflight.get(key) === renewal) inflight.delete(key);
256
- }).catch(() => {});
257
- return renewal;
258
- }
259
-
260
- export function ensureLocalSessionFresh(active: ActiveCube): Promise<ActiveCube> {
261
- return renewLocalSession(active, 'proactive', defaultDeps);
262
- }
263
-
264
- export function recoverExpiredLocalSession(active: ActiveCube): Promise<ActiveCube> {
265
- return renewLocalSession(active, 'expired', defaultDeps);
266
- }
267
-
268
- /** @internal */
269
- export function __renewLocalSessionForTest(
270
- active: ActiveCube,
271
- mode: 'proactive' | 'expired',
272
- deps: SessionContinuityDeps,
273
- ): Promise<ActiveCube> {
274
- return renewLocalSession(active, mode, deps);
275
- }
276
-
277
- /** @internal */
278
- export function __resetSessionContinuityForTest(): void {
279
- inflight.clear();
280
- }