borgmcp 2.14.2 → 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 +33 -7
- 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/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 +2 -2
- package/docs/LOCAL_SERVER.md +23 -0
- package/docs/RELEASING.md +123 -1
- package/package.json +2 -1
- package/src/agent-runtime.ts +17 -0
- package/src/assimilate-cmd.ts +49 -7
- 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/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/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;
|