borgmcp 0.9.57 → 0.9.59

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 (51) hide show
  1. package/README.md +104 -176
  2. package/dist/assimilate-cmd.d.ts.map +1 -1
  3. package/dist/assimilate-cmd.js +23 -5
  4. package/dist/assimilate-cmd.js.map +1 -1
  5. package/dist/auth-env.d.ts +52 -0
  6. package/dist/auth-env.d.ts.map +1 -0
  7. package/dist/auth-env.js +107 -0
  8. package/dist/auth-env.js.map +1 -0
  9. package/dist/auth.d.ts +33 -13
  10. package/dist/auth.d.ts.map +1 -1
  11. package/dist/auth.js +100 -4
  12. package/dist/auth.js.map +1 -1
  13. package/dist/claude.js +28 -4
  14. package/dist/claude.js.map +1 -1
  15. package/dist/cli-help.d.ts.map +1 -1
  16. package/dist/cli-help.js +6 -3
  17. package/dist/cli-help.js.map +1 -1
  18. package/dist/cli-platform.js +1 -1
  19. package/dist/cli-platform.js.map +1 -1
  20. package/dist/codex-launch.d.ts +1 -0
  21. package/dist/codex-launch.d.ts.map +1 -1
  22. package/dist/codex-launch.js +4 -2
  23. package/dist/codex-launch.js.map +1 -1
  24. package/dist/config.d.ts +13 -10
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +140 -60
  27. package/dist/config.js.map +1 -1
  28. package/dist/device-auth.d.ts +75 -0
  29. package/dist/device-auth.d.ts.map +1 -0
  30. package/dist/device-auth.js +167 -0
  31. package/dist/device-auth.js.map +1 -0
  32. package/dist/index.js +4 -3
  33. package/dist/index.js.map +1 -1
  34. package/dist/regen-format.d.ts.map +1 -1
  35. package/dist/regen-format.js +3 -1
  36. package/dist/regen-format.js.map +1 -1
  37. package/dist/roster-render.d.ts +1 -0
  38. package/dist/roster-render.d.ts.map +1 -1
  39. package/dist/roster-render.js +4 -1
  40. package/dist/roster-render.js.map +1 -1
  41. package/dist/setup.js +10 -1
  42. package/dist/setup.js.map +1 -1
  43. package/dist/token-crypto.d.ts +50 -0
  44. package/dist/token-crypto.d.ts.map +1 -0
  45. package/dist/token-crypto.js +91 -0
  46. package/dist/token-crypto.js.map +1 -0
  47. package/dist/token-store.d.ts +126 -0
  48. package/dist/token-store.d.ts.map +1 -0
  49. package/dist/token-store.js +222 -0
  50. package/dist/token-store.js.map +1 -0
  51. package/package.json +1 -9
@@ -0,0 +1,126 @@
1
+ /**
2
+ * gh#557 — token storage backends + selection.
3
+ *
4
+ * config.ts exposes the public token API (storeIdToken/getIdToken/...). This
5
+ * module supplies the interchangeable storage engines it sits on top of:
6
+ *
7
+ * - KeychainBackend — OS keychain via @napi-rs/keyring (the default;
8
+ * real platform at-rest encryption).
9
+ * - EncryptedFileBackend — ~/.borg/credentials, all accounts in one
10
+ * AES-256-GCM blob, file 0600 / dir 0700. Engages
11
+ * only when no keychain is available. Obfuscation-
12
+ * grade (see token-crypto.ts).
13
+ * - caller-managed — BORG_TOKEN / BORG_TOKEN_FILE: an externally
14
+ * supplied id_token, used read-only with no store
15
+ * (the caller owns its lifecycle/freshness).
16
+ *
17
+ * Every engine takes its side-effecting dependencies (keyring entry factory,
18
+ * fs, machine key) by injection so the logic is unit-tested without a real
19
+ * keychain or disk.
20
+ */
21
+ export type TokenBackendName = 'keychain' | 'encrypted-file';
22
+ /**
23
+ * Account-agnostic key/value store over a backing engine. `account` is one
24
+ * of config.ts's three slots (id-token, refresh-token, expiry).
25
+ */
26
+ export interface TokenBackend {
27
+ readonly name: TokenBackendName;
28
+ get(account: string): Promise<string | null>;
29
+ set(account: string, value: string): Promise<void>;
30
+ delete(account: string): Promise<void>;
31
+ }
32
+ /**
33
+ * The slice of @napi-rs/keyring's AsyncEntry this backend depends on. The
34
+ * return types mirror AsyncEntry exactly (deletePassword resolves to an
35
+ * implementation-defined value we ignore) so the real class is assignable.
36
+ */
37
+ export interface KeyringEntry {
38
+ setPassword(value: string): Promise<void>;
39
+ getPassword(): Promise<string | null | undefined>;
40
+ deletePassword(): Promise<unknown>;
41
+ }
42
+ export type KeyringEntryFactory = (account: string) => KeyringEntry;
43
+ /**
44
+ * Build the OS-keychain backend. Preserves config.ts's prior semantics:
45
+ * a missing entry reads as null, and delete is silent on a NoEntry error
46
+ * (idempotent clear) while other errors propagate (fail-loud).
47
+ */
48
+ export declare function makeKeychainBackend(entryFactory?: KeyringEntryFactory): TokenBackend;
49
+ /** The minimal fs surface the file backend needs (injected for tests). */
50
+ export interface FileStoreFs {
51
+ readFile(filePath: string): Promise<string>;
52
+ writeFile(filePath: string, data: string, mode: number): Promise<void>;
53
+ mkdir(dir: string, mode: number): Promise<void>;
54
+ /** Atomically move `from`→`to` (overwrites). Used for crash-safe writes. */
55
+ rename(from: string, to: string): Promise<void>;
56
+ /**
57
+ * Atomically create `lockPath` ONLY if it does not exist (O_EXCL). Resolves
58
+ * `true` if it was created (lock acquired) or `false` if it already existed
59
+ * (lock held by another process). This is the inter-process serialization
60
+ * primitive — no native dependency.
61
+ */
62
+ createExclusive(lockPath: string, content: string): Promise<boolean>;
63
+ /** Remove a file; silent if already gone (lock release + stale-lock steal). */
64
+ removeFile(filePath: string): Promise<void>;
65
+ /** Age in ms (now − mtime) of a file, or null if it does not exist. */
66
+ fileAgeMs(filePath: string): Promise<number | null>;
67
+ }
68
+ export interface EncryptedFileBackendDeps {
69
+ filePath: string;
70
+ key: Buffer;
71
+ fs: FileStoreFs;
72
+ /** Backoff between lock-acquire retries; defaults to a real setTimeout sleep. */
73
+ sleep?: (ms: number) => Promise<void>;
74
+ /** Monotonic clock for the acquire deadline; defaults to Date.now. */
75
+ now?: () => number;
76
+ /** Unique suffix for the write temp file; defaults to a random hex token. */
77
+ uniqueSuffix?: () => string;
78
+ }
79
+ /**
80
+ * Build the encrypted-file backend. All accounts live in one JSON object
81
+ * encrypted as a single AES-256-GCM envelope at `filePath`.
82
+ *
83
+ * A missing file reads as an empty map. A file that won't decrypt (wrong
84
+ * machine key after a hostname change, truncation, tampering) is ALSO
85
+ * treated as empty: the only consequence is the user re-runs `borg setup`,
86
+ * which is the right fail-safe for credential material — a hard crash on a
87
+ * corrupt dotfile would be worse UX than transparent re-auth.
88
+ *
89
+ * gh#570 — concurrency + atomicity. Multiple `borg` processes (e.g. sibling
90
+ * drone sessions on one host) can share `~/.borg/credentials`. Two fixes:
91
+ * - Anti-lost-update (load-bearing): `set`/`delete` serialize their whole
92
+ * read-modify-write cycle behind an O_EXCL lock file, so concurrent
93
+ * writers no longer each read a stale map and clobber each other.
94
+ * - Anti-corruption: every write goes to a unique temp file then `rename`s
95
+ * into place, so a reader (which is intentionally lock-FREE) always sees a
96
+ * complete old-or-new file, never a torn one.
97
+ */
98
+ export declare function makeEncryptedFileBackend(deps: EncryptedFileBackendDeps): TokenBackend;
99
+ /** User-facing BORG_TOKEN_STORE values (friendlier than the backend names). */
100
+ export type ForcedStore = 'keychain' | 'file';
101
+ export interface SelectTokenBackendDeps {
102
+ keyringAvailable: () => Promise<boolean>;
103
+ makeKeychain: () => TokenBackend;
104
+ makeFile: () => TokenBackend;
105
+ /** BORG_TOKEN_STORE override: skip probing and force a backend. */
106
+ forced?: ForcedStore;
107
+ }
108
+ /**
109
+ * Select the persistent backend: a forced choice (BORG_TOKEN_STORE=keychain|file)
110
+ * wins and skips the probe; otherwise probe the keychain and fall back to the
111
+ * encrypted file when it's unavailable.
112
+ */
113
+ export declare function selectTokenBackend(deps: SelectTokenBackendDeps): Promise<TokenBackend>;
114
+ export interface CallerManagedDeps {
115
+ env: NodeJS.ProcessEnv;
116
+ readFile: (filePath: string) => Promise<string>;
117
+ }
118
+ /**
119
+ * Resolve an externally-supplied id_token (no storage). BORG_TOKEN takes
120
+ * precedence; otherwise BORG_TOKEN_FILE is read from disk. Returns null when
121
+ * neither is configured. The value is trimmed (env vars and files commonly
122
+ * carry trailing newlines). The caller owns this token's freshness, so it
123
+ * bypasses the keychain AND the expiry check in config.ts.
124
+ */
125
+ export declare function readCallerManagedIdToken(deps: CallerManagedDeps): Promise<string | null>;
126
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * gh#557 — token storage backends + selection.
3
+ *
4
+ * config.ts exposes the public token API (storeIdToken/getIdToken/...). This
5
+ * module supplies the interchangeable storage engines it sits on top of:
6
+ *
7
+ * - KeychainBackend — OS keychain via @napi-rs/keyring (the default;
8
+ * real platform at-rest encryption).
9
+ * - EncryptedFileBackend — ~/.borg/credentials, all accounts in one
10
+ * AES-256-GCM blob, file 0600 / dir 0700. Engages
11
+ * only when no keychain is available. Obfuscation-
12
+ * grade (see token-crypto.ts).
13
+ * - caller-managed — BORG_TOKEN / BORG_TOKEN_FILE: an externally
14
+ * supplied id_token, used read-only with no store
15
+ * (the caller owns its lifecycle/freshness).
16
+ *
17
+ * Every engine takes its side-effecting dependencies (keyring entry factory,
18
+ * fs, machine key) by injection so the logic is unit-tested without a real
19
+ * keychain or disk.
20
+ */
21
+ import path from 'path';
22
+ import crypto from 'crypto';
23
+ import { AsyncEntry } from '@napi-rs/keyring';
24
+ import { decryptString, encryptString } from './token-crypto.js';
25
+ const SERVICE_NAME = 'borg-mcp';
26
+ const defaultEntryFactory = (account) => new AsyncEntry(SERVICE_NAME, account);
27
+ /**
28
+ * Build the OS-keychain backend. Preserves config.ts's prior semantics:
29
+ * a missing entry reads as null, and delete is silent on a NoEntry error
30
+ * (idempotent clear) while other errors propagate (fail-loud).
31
+ */
32
+ export function makeKeychainBackend(entryFactory = defaultEntryFactory) {
33
+ return {
34
+ name: 'keychain',
35
+ async get(account) {
36
+ return (await entryFactory(account).getPassword()) ?? null;
37
+ },
38
+ async set(account, value) {
39
+ await entryFactory(account).setPassword(value);
40
+ },
41
+ async delete(account) {
42
+ try {
43
+ await entryFactory(account).deletePassword();
44
+ }
45
+ catch (err) {
46
+ const msg = String(err?.message ?? '');
47
+ if (/no entry|not found|no matching/i.test(msg))
48
+ return;
49
+ throw err;
50
+ }
51
+ },
52
+ };
53
+ }
54
+ // gh#570 lock tuning. Token writes are tiny + infrequent, so a short retry
55
+ // cadence + a generous staleness window are right: the staleness window only
56
+ // has to exceed a real write (milliseconds), and a crashed lock-holder is
57
+ // reclaimed after it.
58
+ const LOCK_RETRY_DELAY_MS = 25;
59
+ const LOCK_MAX_WAIT_MS = 2000;
60
+ const LOCK_STALE_MS = 10_000;
61
+ function defaultSleep(ms) {
62
+ return new Promise((resolve) => setTimeout(resolve, ms));
63
+ }
64
+ function defaultUniqueSuffix() {
65
+ // PID + random keeps two concurrent writers' temp files distinct so the
66
+ // temp write itself never races. crypto is already a client dependency.
67
+ return `${process.pid}.${crypto.randomBytes(6).toString('hex')}`;
68
+ }
69
+ /**
70
+ * Build the encrypted-file backend. All accounts live in one JSON object
71
+ * encrypted as a single AES-256-GCM envelope at `filePath`.
72
+ *
73
+ * A missing file reads as an empty map. A file that won't decrypt (wrong
74
+ * machine key after a hostname change, truncation, tampering) is ALSO
75
+ * treated as empty: the only consequence is the user re-runs `borg setup`,
76
+ * which is the right fail-safe for credential material — a hard crash on a
77
+ * corrupt dotfile would be worse UX than transparent re-auth.
78
+ *
79
+ * gh#570 — concurrency + atomicity. Multiple `borg` processes (e.g. sibling
80
+ * drone sessions on one host) can share `~/.borg/credentials`. Two fixes:
81
+ * - Anti-lost-update (load-bearing): `set`/`delete` serialize their whole
82
+ * read-modify-write cycle behind an O_EXCL lock file, so concurrent
83
+ * writers no longer each read a stale map and clobber each other.
84
+ * - Anti-corruption: every write goes to a unique temp file then `rename`s
85
+ * into place, so a reader (which is intentionally lock-FREE) always sees a
86
+ * complete old-or-new file, never a torn one.
87
+ */
88
+ export function makeEncryptedFileBackend(deps) {
89
+ const { filePath, key, fs } = deps;
90
+ const sleep = deps.sleep ?? defaultSleep;
91
+ const now = deps.now ?? Date.now;
92
+ const uniqueSuffix = deps.uniqueSuffix ?? defaultUniqueSuffix;
93
+ const lockPath = `${filePath}.lock`;
94
+ async function readMap() {
95
+ let raw;
96
+ try {
97
+ raw = await fs.readFile(filePath);
98
+ }
99
+ catch {
100
+ return {}; // missing file → no stored tokens yet
101
+ }
102
+ try {
103
+ const json = decryptString(raw.trim(), key);
104
+ const parsed = JSON.parse(json);
105
+ return parsed && typeof parsed === 'object' ? parsed : {};
106
+ }
107
+ catch {
108
+ return {}; // undecryptable / corrupt → fail safe to re-auth
109
+ }
110
+ }
111
+ async function writeMap(map) {
112
+ await fs.mkdir(path.dirname(filePath), 0o700);
113
+ // Atomic write: encrypt → temp → rename. rename preserves the temp's 0600.
114
+ const tmpPath = `${filePath}.${uniqueSuffix()}.tmp`;
115
+ await fs.writeFile(tmpPath, encryptString(JSON.stringify(map), key), 0o600);
116
+ try {
117
+ await fs.rename(tmpPath, filePath);
118
+ }
119
+ catch (err) {
120
+ // rename failed (disk full / permission). Remove the orphaned temp so
121
+ // repeated failures don't accumulate .tmp files, then rethrow so the
122
+ // caller still fails loud. Cleanup is best-effort — a failed unlink must
123
+ // not mask the original error.
124
+ try {
125
+ await fs.removeFile(tmpPath);
126
+ }
127
+ catch {
128
+ /* ignore cleanup failure — the original rename error takes precedence */
129
+ }
130
+ throw err;
131
+ }
132
+ }
133
+ /**
134
+ * Run `fn` while holding the O_EXCL lock, serializing read-modify-write
135
+ * across processes. A lock left by a crashed holder is reclaimed once it is
136
+ * older than LOCK_STALE_MS. If the lock can't be acquired within
137
+ * LOCK_MAX_WAIT_MS, proceed best-effort (steal it): the worst case is the
138
+ * original benign lost-update, never a stuck auth.
139
+ */
140
+ async function withFileLock(fn) {
141
+ const deadline = now() + LOCK_MAX_WAIT_MS;
142
+ let held = false;
143
+ while (!held) {
144
+ held = await fs.createExclusive(lockPath, `${process.pid}@${now()}`);
145
+ if (held)
146
+ break;
147
+ const age = await fs.fileAgeMs(lockPath);
148
+ if (age !== null && age > LOCK_STALE_MS) {
149
+ await fs.removeFile(lockPath); // reclaim a stale (crashed-holder) lock
150
+ continue;
151
+ }
152
+ if (now() >= deadline) {
153
+ await fs.removeFile(lockPath); // last resort: steal + proceed best-effort
154
+ held = await fs.createExclusive(lockPath, `${process.pid}@${now()}`);
155
+ break;
156
+ }
157
+ await sleep(LOCK_RETRY_DELAY_MS);
158
+ }
159
+ try {
160
+ return await fn();
161
+ }
162
+ finally {
163
+ if (held)
164
+ await fs.removeFile(lockPath);
165
+ }
166
+ }
167
+ return {
168
+ name: 'encrypted-file',
169
+ async get(account) {
170
+ // Lock-free: temp+rename guarantees readMap sees a complete file.
171
+ const map = await readMap();
172
+ return Object.prototype.hasOwnProperty.call(map, account) ? map[account] : null;
173
+ },
174
+ set(account, value) {
175
+ return withFileLock(async () => {
176
+ const map = await readMap();
177
+ map[account] = value;
178
+ await writeMap(map);
179
+ });
180
+ },
181
+ delete(account) {
182
+ return withFileLock(async () => {
183
+ const map = await readMap();
184
+ if (Object.prototype.hasOwnProperty.call(map, account)) {
185
+ delete map[account];
186
+ await writeMap(map);
187
+ }
188
+ });
189
+ },
190
+ };
191
+ }
192
+ /**
193
+ * Select the persistent backend: a forced choice (BORG_TOKEN_STORE=keychain|file)
194
+ * wins and skips the probe; otherwise probe the keychain and fall back to the
195
+ * encrypted file when it's unavailable.
196
+ */
197
+ export async function selectTokenBackend(deps) {
198
+ if (deps.forced === 'keychain')
199
+ return deps.makeKeychain();
200
+ if (deps.forced === 'file')
201
+ return deps.makeFile();
202
+ return (await deps.keyringAvailable()) ? deps.makeKeychain() : deps.makeFile();
203
+ }
204
+ /**
205
+ * Resolve an externally-supplied id_token (no storage). BORG_TOKEN takes
206
+ * precedence; otherwise BORG_TOKEN_FILE is read from disk. Returns null when
207
+ * neither is configured. The value is trimmed (env vars and files commonly
208
+ * carry trailing newlines). The caller owns this token's freshness, so it
209
+ * bypasses the keychain AND the expiry check in config.ts.
210
+ */
211
+ export async function readCallerManagedIdToken(deps) {
212
+ const inline = deps.env.BORG_TOKEN?.trim();
213
+ if (inline)
214
+ return inline;
215
+ const file = deps.env.BORG_TOKEN_FILE?.trim();
216
+ if (file) {
217
+ const contents = await deps.readFile(file);
218
+ return contents.trim();
219
+ }
220
+ return null;
221
+ }
222
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,YAAY,GAAG,UAAU,CAAC;AA8BhC,MAAM,mBAAmB,GAAwB,CAAC,OAAO,EAAE,EAAE,CAC3D,IAAI,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAExC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,eAAoC,mBAAmB;IAEvD,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK;YACtB,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,IAAI,CAAC;gBACH,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,CAAC;YAC/C,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;gBACvC,IAAI,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC;oBAAE,OAAO;gBACxD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAsCD,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,sBAAsB;AACtB,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,mBAAmB;IAC1B,wEAAwE;IACxE,wEAAwE;IACxE,OAAO,GAAG,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAA8B;IACrE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,mBAAmB,CAAC;IAC9D,MAAM,QAAQ,GAAG,GAAG,QAAQ,OAAO,CAAC;IAEpC,KAAK,UAAU,OAAO;QACpB,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,sCAAsC;QACnD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,iDAAiD;QAC9D,CAAC;IACH,CAAC;IAED,KAAK,UAAU,QAAQ,CAAC,GAAe;QACrC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9C,2EAA2E;QAC3E,MAAM,OAAO,GAAG,GAAG,QAAQ,IAAI,YAAY,EAAE,MAAM,CAAC;QACpD,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5E,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,qEAAqE;YACrE,yEAAyE;YACzE,+BAA+B;YAC/B,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;YAC3E,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,UAAU,YAAY,CAAI,EAAoB;QACjD,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAC1C,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,OAAO,CAAC,IAAI,EAAE,CAAC;YACb,IAAI,GAAG,MAAM,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;YACrE,IAAI,IAAI;gBAAE,MAAM;YAChB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,GAAG,aAAa,EAAE,CAAC;gBACxC,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,wCAAwC;gBACvE,SAAS;YACX,CAAC;YACD,IAAI,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACtB,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,2CAA2C;gBAC1E,IAAI,GAAG,MAAM,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;gBACrE,MAAM;YACR,CAAC;YACD,MAAM,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,IAAI,IAAI;gBAAE,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,kEAAkE;YAClE,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClF,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,KAAK;YAChB,OAAO,YAAY,CAAC,KAAK,IAAI,EAAE;gBAC7B,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;gBAC5B,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;gBACrB,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,OAAO;YACZ,OAAO,YAAY,CAAC,KAAK,IAAI,EAAE;gBAC7B,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;gBAC5B,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;oBACvD,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;oBACpB,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAeD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAA4B;IAE5B,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACnD,OAAO,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;AACjF,CAAC;AASD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAAuB;IAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "0.9.57",
3
+ "version": "0.9.59",
4
4
  "description": "Coordinate AI coding agents in shared cubes. Works with Claude Code and Codex. Create projects, assign roles, and share a live activity log.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,15 +40,7 @@
40
40
  ],
41
41
  "author": "Theodor Storm <theodor@byteventures.se>",
42
42
  "license": "Apache-2.0",
43
- "repository": {
44
- "type": "git",
45
- "url": "https://github.com/theodorstorm/borg-mcp",
46
- "directory": "client"
47
- },
48
43
  "homepage": "https://borgmcp.ai",
49
- "bugs": {
50
- "url": "https://github.com/theodorstorm/borg-mcp/issues"
51
- },
52
44
  "dependencies": {
53
45
  "@modelcontextprotocol/sdk": "^1.0.4",
54
46
  "@napi-rs/keyring": "^1.3.0",