borgmcp 2.14.1 → 2.15.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/agent-runtime.d.ts +5 -0
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +14 -0
- package/dist/agent-runtime.js.map +1 -1
- package/dist/assimilate-cmd.d.ts +3 -0
- package/dist/assimilate-cmd.d.ts.map +1 -1
- package/dist/assimilate-cmd.js +34 -8
- package/dist/assimilate-cmd.js.map +1 -1
- package/dist/assimilate-deps.d.ts.map +1 -1
- package/dist/assimilate-deps.js +17 -3
- package/dist/assimilate-deps.js.map +1 -1
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +2 -1
- package/dist/claude.js.map +1 -1
- package/dist/cleanup-cmd.d.ts.map +1 -1
- package/dist/cleanup-cmd.js +2 -2
- package/dist/cleanup-cmd.js.map +1 -1
- package/dist/cli-tool-approval.d.ts.map +1 -1
- package/dist/cli-tool-approval.js +3 -2
- package/dist/cli-tool-approval.js.map +1 -1
- package/dist/codex-remote.d.ts.map +1 -1
- package/dist/codex-remote.js +4 -3
- package/dist/codex-remote.js.map +1 -1
- package/dist/config-utils.d.ts +16 -1
- package/dist/config-utils.d.ts.map +1 -1
- package/dist/config-utils.js +176 -17
- package/dist/config-utils.js.map +1 -1
- package/dist/credential-paths.d.ts.map +1 -1
- package/dist/credential-paths.js +2 -3
- package/dist/credential-paths.js.map +1 -1
- package/dist/cubes.js +2 -2
- package/dist/cubes.js.map +1 -1
- package/dist/foreign-path-reminder.d.ts +12 -0
- package/dist/foreign-path-reminder.d.ts.map +1 -0
- package/dist/foreign-path-reminder.js +122 -0
- package/dist/foreign-path-reminder.js.map +1 -0
- package/dist/launch-access.d.ts +22 -0
- package/dist/launch-access.d.ts.map +1 -0
- package/dist/launch-access.js +27 -0
- package/dist/launch-access.js.map +1 -0
- package/dist/launch-all-deps.d.ts.map +1 -1
- package/dist/launch-all-deps.js +2 -2
- package/dist/launch-all-deps.js.map +1 -1
- package/dist/lifecycle-log-guard.js +2 -2
- package/dist/lifecycle-log-guard.js.map +1 -1
- package/dist/local-server-cursor.js +2 -2
- package/dist/local-server-cursor.js.map +1 -1
- package/dist/opencode-plugin.js +2 -2
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/private-root.d.ts +17 -0
- package/dist/private-root.d.ts.map +1 -1
- package/dist/private-root.js +62 -2
- package/dist/private-root.js.map +1 -1
- package/dist/remote-client.d.ts.map +1 -1
- package/dist/remote-client.js +66 -7
- package/dist/remote-client.js.map +1 -1
- package/dist/seat-store.d.ts.map +1 -1
- package/dist/seat-store.js +4 -3
- package/dist/seat-store.js.map +1 -1
- package/dist/seats.d.ts.map +1 -1
- package/dist/seats.js +2 -2
- package/dist/seats.js.map +1 -1
- package/dist/self-path.d.ts +2 -0
- package/dist/self-path.d.ts.map +1 -1
- package/dist/self-path.js +4 -0
- package/dist/self-path.js.map +1 -1
- package/dist/server-trust.d.ts.map +1 -1
- package/dist/server-trust.js +3 -3
- package/dist/server-trust.js.map +1 -1
- package/dist/setup-confirm.d.ts +1 -1
- package/dist/setup-confirm.js +1 -1
- package/dist/stream-owner.js +2 -2
- package/dist/stream-owner.js.map +1 -1
- package/docs/EXTRACTION_PROVENANCE.md +3 -3
- package/docs/LOCAL_SERVER.md +23 -0
- package/docs/RELEASING.md +128 -1
- package/package.json +2 -1
- package/src/agent-runtime.ts +17 -0
- package/src/assimilate-cmd.ts +50 -8
- package/src/assimilate-deps.ts +21 -3
- package/src/claude.ts +2 -0
- package/src/cleanup-cmd.ts +2 -2
- package/src/cli-tool-approval.ts +3 -2
- package/src/codex-remote.ts +4 -2
- package/src/config-utils.ts +186 -18
- package/src/credential-paths.ts +2 -3
- package/src/cubes.ts +2 -2
- package/src/foreign-path-reminder.ts +141 -0
- package/src/launch-access.ts +40 -0
- package/src/launch-all-deps.ts +2 -2
- package/src/lifecycle-log-guard.ts +2 -2
- package/src/local-server-cursor.ts +2 -2
- package/src/opencode-plugin.ts +2 -2
- package/src/private-root.ts +62 -2
- package/src/remote-client.ts +75 -5
- package/src/seat-store.ts +4 -3
- package/src/seats.ts +2 -2
- package/src/self-path.ts +5 -0
- package/src/server-trust.ts +3 -3
- package/src/setup-confirm.ts +1 -1
- package/src/stream-owner.ts +2 -2
package/src/private-root.ts
CHANGED
|
@@ -1,8 +1,68 @@
|
|
|
1
|
+
import { lstatSync, realpathSync } from 'node:fs';
|
|
1
2
|
import { chmod, lstat, mkdir } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
|
-
import { isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Optional alternate home root for isolated client runs. The value is the
|
|
8
|
+
* replacement home directory, not the `.config/borgmcp` directory itself, so
|
|
9
|
+
* every client-owned path (credentials, seats, worktrees, and agent config)
|
|
10
|
+
* stays under one root.
|
|
11
|
+
*/
|
|
12
|
+
export const BORG_STATE_ROOT_ENV = 'BORG_STATE_ROOT';
|
|
13
|
+
|
|
14
|
+
function invalidStateRoot(): Error {
|
|
15
|
+
return new Error(`${BORG_STATE_ROOT_ENV} must be an absolute canonical path`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Return whether a path and every existing ancestor are free of symlinks. */
|
|
19
|
+
export function isCanonicalPath(root: string): boolean {
|
|
20
|
+
if (!isAbsolute(root) || resolve(root) !== root) return false;
|
|
21
|
+
let candidate = root;
|
|
22
|
+
while (true) {
|
|
23
|
+
try {
|
|
24
|
+
const metadata = lstatSync(candidate);
|
|
25
|
+
return !metadata.isSymbolicLink() && realpathSync(candidate) === candidate;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
28
|
+
const parent = dirname(candidate);
|
|
29
|
+
if (parent === candidate) return false;
|
|
30
|
+
candidate = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function configuredStateRoot(env: NodeJS.ProcessEnv = process.env): string | null {
|
|
36
|
+
const configured = env[BORG_STATE_ROOT_ENV];
|
|
37
|
+
if (configured === undefined) return null;
|
|
38
|
+
if (configured.length === 0 || !isCanonicalPath(configured)) {
|
|
39
|
+
throw invalidStateRoot();
|
|
40
|
+
}
|
|
41
|
+
return configured;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolve the effective home root used by all Borg-owned local state. */
|
|
45
|
+
export function borgHomeRoot(env: NodeJS.ProcessEnv = process.env): string {
|
|
46
|
+
return configuredStateRoot(env) ?? realpathSync(homedir());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const borgConfigRoot = (): string => join(borgHomeRoot(), '.config', 'borgmcp');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Environment used when a native agent CLI registers Borg. The CLI must write
|
|
53
|
+
* its own config under the same effective root that config-utils reads; the
|
|
54
|
+
* eventual MCP child receives BORG_STATE_ROOT separately via its registration.
|
|
55
|
+
*/
|
|
56
|
+
export function borgAgentConfigEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
57
|
+
if (env[BORG_STATE_ROOT_ENV] === undefined) return { ...env };
|
|
58
|
+
const root = borgHomeRoot(env);
|
|
59
|
+
return {
|
|
60
|
+
...env,
|
|
61
|
+
HOME: root,
|
|
62
|
+
CODEX_HOME: join(root, '.codex'),
|
|
63
|
+
XDG_CONFIG_HOME: join(root, '.config'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
6
66
|
|
|
7
67
|
/** Ensure Borg's local state root exists with owner-only directory permissions. */
|
|
8
68
|
export async function ensurePrivateBorgConfigRoot(root = borgConfigRoot()): Promise<void> {
|
package/src/remote-client.ts
CHANGED
|
@@ -80,6 +80,11 @@ export interface RemoteConnection {
|
|
|
80
80
|
// retryAfter can't wedge the call.
|
|
81
81
|
const RATE_LIMIT_MAX_RETRIES = 3;
|
|
82
82
|
const RATE_LIMIT_MAX_WAIT_MS = 60_000; // cap a single Retry-After honor
|
|
83
|
+
const UNREAD_CURSOR_MAX_TRANSPORT_RETRIES = 1;
|
|
84
|
+
// Replay is opt-in: most local requests are mutations or have ambiguous
|
|
85
|
+
// delivery, while unread-log reads carry an explicit cursor and are safe to
|
|
86
|
+
// repeat with the exact same request body.
|
|
87
|
+
type AuthedFetchRetryMode = 'unread-cursor';
|
|
83
88
|
export const LOCAL_SERVER_RESPONSE_LIMIT_BYTES = 32 * 1024 * 1024;
|
|
84
89
|
// A typed auth-error envelope is tiny; anything larger is hostile and the
|
|
85
90
|
// bounded read throws → the 401 fails closed to non-destructive CREDENTIAL_REJECTED.
|
|
@@ -179,6 +184,25 @@ export async function retryOn429(
|
|
|
179
184
|
return response;
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
function isConnectionReset(error: unknown): boolean {
|
|
188
|
+
let candidate: unknown = error;
|
|
189
|
+
for (let depth = 0; depth < 2; depth += 1) {
|
|
190
|
+
if (candidate === null || typeof candidate !== 'object') return false;
|
|
191
|
+
const typed = candidate as { code?: unknown; cause?: unknown };
|
|
192
|
+
if (typed.code === 'ECONNRESET') return true;
|
|
193
|
+
candidate = typed.cause;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function unreadLogTransportFailure(cause: unknown): BorgServerUnreachableError {
|
|
199
|
+
return new BorgServerUnreachableError(
|
|
200
|
+
'Borg could not complete the unread log read after one automatic retry. ' +
|
|
201
|
+
'The request may have reached the server; repeat `borg_read-log unread_only=true` until caught up.',
|
|
202
|
+
{ cause },
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
182
206
|
async function localAuthorityContext(
|
|
183
207
|
sessionToken: string,
|
|
184
208
|
apiUrl: string,
|
|
@@ -271,6 +295,7 @@ async function localServerRequest<T>(
|
|
|
271
295
|
path: string,
|
|
272
296
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH',
|
|
273
297
|
payload?: Record<string, unknown>,
|
|
298
|
+
options: { retryMode?: AuthedFetchRetryMode } = {},
|
|
274
299
|
): Promise<T | null> {
|
|
275
300
|
return decodeLocalProtocolResponse<T>((signal) => authedFetch(path, {
|
|
276
301
|
method,
|
|
@@ -286,6 +311,7 @@ async function localServerRequest<T>(
|
|
|
286
311
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
287
312
|
body: JSON.stringify(createProtocolEnvelope(randomUUID(), payload)),
|
|
288
313
|
}),
|
|
314
|
+
retryMode: options.retryMode,
|
|
289
315
|
}), true);
|
|
290
316
|
}
|
|
291
317
|
|
|
@@ -492,7 +518,11 @@ function localCursorBinding(active: ActiveCube) {
|
|
|
492
518
|
|
|
493
519
|
async function localReadLogPage(
|
|
494
520
|
active: ActiveCube,
|
|
495
|
-
opts: {
|
|
521
|
+
opts: {
|
|
522
|
+
cursor?: LocalServerCursor | null;
|
|
523
|
+
limit?: number;
|
|
524
|
+
retryMode?: AuthedFetchRetryMode;
|
|
525
|
+
} = {},
|
|
496
526
|
): Promise<any> {
|
|
497
527
|
const payload = await localServerRequest<any>(
|
|
498
528
|
active,
|
|
@@ -502,6 +532,7 @@ async function localReadLogPage(
|
|
|
502
532
|
cursor: opts.cursor ?? null,
|
|
503
533
|
...(opts.limit === undefined ? {} : { limit: opts.limit }),
|
|
504
534
|
},
|
|
535
|
+
{ retryMode: opts.retryMode },
|
|
505
536
|
);
|
|
506
537
|
if (!payload) throw new Error('Local Borg server returned an empty log response');
|
|
507
538
|
return payload;
|
|
@@ -627,6 +658,7 @@ async function authedFetch(
|
|
|
627
658
|
authToken?: string;
|
|
628
659
|
serverTrustIdentity?: string;
|
|
629
660
|
localSessionCredentialRef?: string;
|
|
661
|
+
retryMode?: AuthedFetchRetryMode;
|
|
630
662
|
} = {}
|
|
631
663
|
): Promise<Response> {
|
|
632
664
|
const {
|
|
@@ -635,6 +667,7 @@ async function authedFetch(
|
|
|
635
667
|
authToken,
|
|
636
668
|
serverTrustIdentity: suppliedTrustIdentity,
|
|
637
669
|
localSessionCredentialRef,
|
|
670
|
+
retryMode,
|
|
638
671
|
headers,
|
|
639
672
|
...rest
|
|
640
673
|
} = init;
|
|
@@ -697,7 +730,35 @@ async function authedFetch(
|
|
|
697
730
|
return res;
|
|
698
731
|
};
|
|
699
732
|
|
|
700
|
-
|
|
733
|
+
let transportRetriesRemaining = retryMode === 'unread-cursor'
|
|
734
|
+
? UNREAD_CURSOR_MAX_TRANSPORT_RETRIES
|
|
735
|
+
: 0;
|
|
736
|
+
const requestWithRetry = async (): Promise<Response> => {
|
|
737
|
+
try {
|
|
738
|
+
return await buildRequest(token);
|
|
739
|
+
} catch (error) {
|
|
740
|
+
if (retryMode !== 'unread-cursor' || !isConnectionReset(error)) throw error;
|
|
741
|
+
if (transportRetriesRemaining === 0) throw unreadLogTransportFailure(error);
|
|
742
|
+
transportRetriesRemaining -= 1;
|
|
743
|
+
debugLog('↻ retrying unread log read after ECONNRESET');
|
|
744
|
+
try {
|
|
745
|
+
return await buildRequest(token);
|
|
746
|
+
} catch (retryError) {
|
|
747
|
+
if (isConnectionReset(retryError)) throw unreadLogTransportFailure(retryError);
|
|
748
|
+
throw retryError;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
let response = await requestWithRetry();
|
|
754
|
+
let rateLimitRetryExhausted = false;
|
|
755
|
+
if (retryMode === 'unread-cursor') {
|
|
756
|
+
response = await retryOn429(response, requestWithRetry, {
|
|
757
|
+
sleep,
|
|
758
|
+
log: debugLog,
|
|
759
|
+
});
|
|
760
|
+
rateLimitRetryExhausted = response.status === 429;
|
|
761
|
+
}
|
|
701
762
|
|
|
702
763
|
if (response.status === 401) {
|
|
703
764
|
// Reached only after pinned-TLS trust is verified (localAuthorityContext
|
|
@@ -805,11 +866,14 @@ async function authedFetch(
|
|
|
805
866
|
if (localSessionCredentialRef !== undefined) markSeatRejected(localSessionCredentialRef);
|
|
806
867
|
throw new CubeDeletedError();
|
|
807
868
|
}
|
|
869
|
+
const retryGuidance = rateLimitRetryExhausted
|
|
870
|
+
? ' Repeat `borg_read-log unread_only=true` until caught up.'
|
|
871
|
+
: '';
|
|
808
872
|
throw new BorgServerHttpError(
|
|
809
873
|
response.status,
|
|
810
874
|
serverMessage
|
|
811
|
-
? `Borg server request failed (HTTP ${response.status}): ${serverMessage}`
|
|
812
|
-
: `Borg server request failed (HTTP ${response.status})`,
|
|
875
|
+
? `Borg server request failed (HTTP ${response.status}): ${serverMessage}${retryGuidance}`
|
|
876
|
+
: `Borg server request failed (HTTP ${response.status})${retryGuidance}`,
|
|
813
877
|
code,
|
|
814
878
|
);
|
|
815
879
|
}
|
|
@@ -954,7 +1018,13 @@ export async function readLog(
|
|
|
954
1018
|
let cursor: LocalServerCursor | null = null;
|
|
955
1019
|
if (opts.unreadOnly) cursor = await getLocalServerCursor(localCursorBinding(local));
|
|
956
1020
|
if (opts.since !== undefined) cursor = await resolveLocalLogCursor(local, opts.since);
|
|
957
|
-
const page = await localReadLogPage(local, {
|
|
1021
|
+
const page = await localReadLogPage(local, {
|
|
1022
|
+
cursor,
|
|
1023
|
+
limit: opts.limit,
|
|
1024
|
+
// Keep the cursor payload stable across a lost response; do not re-read or
|
|
1025
|
+
// advance local state until one response has been decoded successfully.
|
|
1026
|
+
...(opts.unreadOnly && opts.since === undefined ? { retryMode: 'unread-cursor' as const } : {}),
|
|
1027
|
+
});
|
|
958
1028
|
if (opts.unreadOnly && page.cursor) {
|
|
959
1029
|
await advanceLocalServerCursor(localCursorBinding(local), page.cursor);
|
|
960
1030
|
}
|
package/src/seat-store.ts
CHANGED
|
@@ -32,9 +32,10 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { constants } from 'node:fs';
|
|
35
|
-
import { open, link, lstat, mkdir, readFile,
|
|
35
|
+
import { open, link, lstat, mkdir, readFile, rename, stat, unlink } from 'node:fs/promises';
|
|
36
36
|
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
37
37
|
import { randomBytes } from 'node:crypto';
|
|
38
|
+
import { isCanonicalPath } from './private-root.js';
|
|
38
39
|
|
|
39
40
|
const LOCK_WAIT_MS = 10;
|
|
40
41
|
const LOCK_ATTEMPTS = 500;
|
|
@@ -58,7 +59,7 @@ async function assertSecureRoot(
|
|
|
58
59
|
root: string,
|
|
59
60
|
rootMode: SecureStoreOptions['rootMode'] = 'private',
|
|
60
61
|
): Promise<void> {
|
|
61
|
-
if (!
|
|
62
|
+
if (!isCanonicalPath(root)) {
|
|
62
63
|
throw new Error(`Borg credential store path ${root} is not canonical`);
|
|
63
64
|
}
|
|
64
65
|
let metadata: Awaited<ReturnType<typeof lstat>>;
|
|
@@ -83,7 +84,7 @@ async function assertSecureRoot(
|
|
|
83
84
|
if (uid !== null && metadata.uid !== uid) {
|
|
84
85
|
throw new Error(`Borg credential store root ${root} is not owned by the current user`);
|
|
85
86
|
}
|
|
86
|
-
if (
|
|
87
|
+
if (!isCanonicalPath(root)) {
|
|
87
88
|
throw new Error(`Borg credential store root ${root} is not canonical or contains a symlink`);
|
|
88
89
|
}
|
|
89
90
|
}
|
package/src/seats.ts
CHANGED
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
* hydration reader; every other observation is digest-only.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import os from 'os';
|
|
21
20
|
import path from 'path';
|
|
22
21
|
import { createHash } from 'node:crypto';
|
|
23
22
|
import { readStoreFile, withStore } from './seat-store.js';
|
|
24
23
|
import { LegacySessionCredentialCollisionError } from './server-errors.js';
|
|
24
|
+
import { borgConfigRoot } from './private-root.js';
|
|
25
25
|
|
|
26
|
-
export const SEATS_FILE = path.join(
|
|
26
|
+
export const SEATS_FILE = path.join(borgConfigRoot(), 'seats.json');
|
|
27
27
|
const SEATS_VERSION = 1 as const;
|
|
28
28
|
const REF_RE = /^borg-server-session:[a-f0-9]{64}$/;
|
|
29
29
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
package/src/self-path.ts
CHANGED
|
@@ -49,3 +49,8 @@ export function resolveClearRewakePath(): string {
|
|
|
49
49
|
export function resolveLogAuditPath(): string {
|
|
50
50
|
return resolveSelfBinPath('log-audit.js');
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** Absolute path to the optional pre-tool foreign-path reminder hook. */
|
|
54
|
+
export function resolveForeignPathReminderPath(): string {
|
|
55
|
+
return resolveSelfBinPath('foreign-path-reminder.js');
|
|
56
|
+
}
|
package/src/server-trust.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { constants } from 'node:fs';
|
|
|
4
4
|
import { access, lstat, mkdir, open, rename, rm, unlink, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { request as httpsRequest } from 'node:https';
|
|
6
6
|
import { connect as tlsConnect } from 'node:tls';
|
|
7
|
-
import { homedir } from 'node:os';
|
|
8
7
|
import { join, resolve } from 'node:path';
|
|
9
8
|
import { Readable } from 'node:stream';
|
|
10
9
|
import { BorgServerTrustError } from './server-errors.js';
|
|
@@ -27,6 +26,7 @@ import type {
|
|
|
27
26
|
EnrollmentArtifactBinding,
|
|
28
27
|
EnrollmentTrustPointer,
|
|
29
28
|
} from './enrollment-types.js';
|
|
29
|
+
import { borgHomeRoot } from './private-root.js';
|
|
30
30
|
|
|
31
31
|
// CR5 TLS LATTICE: OpenSSL/Node TLS certificate-verification error codes. A raw
|
|
32
32
|
// CA / cert-chain / SAN failure from the pinned transport is a potential MITM and
|
|
@@ -102,12 +102,12 @@ function invalidateOriginTrustCache(origin: string): void {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function serverDataDirectory(): string {
|
|
105
|
-
return resolve(process.env.BORG_SERVER_DATA_DIR ?? join(
|
|
105
|
+
return resolve(process.env.BORG_SERVER_DATA_DIR ?? join(borgHomeRoot(), '.borg', 'server'));
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function remoteTrustDirectory(origin: string): string {
|
|
109
109
|
const key = createHash('sha256').update(origin).digest('hex');
|
|
110
|
-
return join(
|
|
110
|
+
return join(borgHomeRoot(), '.borg', 'server-trust', key);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function trustGenerationsDirectory(origin: string): string {
|
package/src/setup-confirm.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Step-1 of the setup wizard writes the user's GLOBAL agent config
|
|
5
5
|
* (registers the borg MCP server + hooks). Before gh#818 this happened
|
|
6
6
|
* silently on first run. This module adds informed-consent disclosure:
|
|
7
|
-
* it lists WHICH files will be written (well-known
|
|
7
|
+
* it lists WHICH files will be written (well-known home-relative paths)
|
|
8
8
|
* and asks to continue before the first mutation.
|
|
9
9
|
*
|
|
10
10
|
* Pure / dep-injected (mirrors `resolveCliChoice` + `setup-action.ts`) so
|
package/src/stream-owner.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
3
|
import path from 'node:path';
|
|
4
|
+
import { borgConfigRoot } from './private-root.js';
|
|
5
5
|
|
|
6
6
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7
|
-
const STREAM_LOCKS_DIR = path.join(
|
|
7
|
+
const STREAM_LOCKS_DIR = path.join(borgConfigRoot(), 'stream-locks');
|
|
8
8
|
const OWNER_FILE = 'owner.json';
|
|
9
9
|
const TAKEOVER_FILE = 'takeover.json';
|
|
10
10
|
const SCHEMA_VERSION = 1;
|