@acprompt/join 0.1.8 → 0.1.9
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/auth/paths.d.ts.map +1 -1
- package/dist/auth/paths.js +6 -1
- package/dist/auth/paths.js.map +1 -1
- package/dist/detect/claude-code.d.ts +11 -0
- package/dist/detect/claude-code.d.ts.map +1 -1
- package/dist/detect/claude-code.js +24 -9
- package/dist/detect/claude-code.js.map +1 -1
- package/dist/install/claude-code.d.ts +16 -0
- package/dist/install/claude-code.d.ts.map +1 -1
- package/dist/install/claude-code.js +21 -2
- package/dist/install/claude-code.js.map +1 -1
- package/dist/install/letta.d.ts.map +1 -1
- package/dist/install/letta.js +3 -1
- package/dist/install/letta.js.map +1 -1
- package/dist/util/atomic-write.d.ts +8 -0
- package/dist/util/atomic-write.d.ts.map +1 -0
- package/dist/util/atomic-write.js +58 -0
- package/dist/util/atomic-write.js.map +1 -0
- package/package.json +1 -1
package/dist/auth/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"AAOA,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAWjD;AAED,yEAAyE;AACzE,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAevF"}
|
package/dist/auth/paths.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promises as fs } from "node:fs";
|
|
5
|
+
import { writeFileAtomic } from "../util/atomic-write.js";
|
|
5
6
|
export function acpromptDir() {
|
|
6
7
|
return path.join(os.homedir(), ".acprompt");
|
|
7
8
|
}
|
|
@@ -34,7 +35,11 @@ export async function ensureDir() {
|
|
|
34
35
|
export async function writeSecureJson(filePath, payload) {
|
|
35
36
|
await ensureDir();
|
|
36
37
|
const body = JSON.stringify(payload, null, 2);
|
|
37
|
-
|
|
38
|
+
// R33 — atomic (temp file + rename). A torn write here leaves a truncated
|
|
39
|
+
// config.json, which reads as "not logged in" and silently discards the
|
|
40
|
+
// stored agent identity. Mode is applied to the temp file, so the secret is
|
|
41
|
+
// never world-readable even for an instant.
|
|
42
|
+
await writeFileAtomic(filePath, body, { mode: 0o600 });
|
|
38
43
|
if (process.platform !== "win32") {
|
|
39
44
|
try {
|
|
40
45
|
await fs.chmod(filePath, 0o600);
|
package/dist/auth/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAgB;IACtE,MAAM,SAAS,EAAE,CAAC;IAClB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9C,0EAA0E;IAC1E,wEAAwE;IACxE,4EAA4E;IAC5E,4CAA4C;IAC5C,MAAM,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { DetectionContext, HarnessDetector } from "./types.js";
|
|
2
2
|
export declare function claudeDesktopConfigPath(ctx: DetectionContext): string;
|
|
3
|
+
/**
|
|
4
|
+
* The command we probe for the Claude Code CLI on every platform.
|
|
5
|
+
*
|
|
6
|
+
* R33 — Windows used to probe `claude.cmd` specifically. That only matches the
|
|
7
|
+
* npm-installed shim; the NATIVE Windows installer ships `claude.exe` (no .cmd),
|
|
8
|
+
* so the probe failed for a correctly installed CLI and `acprompt-join` told a
|
|
9
|
+
* native-install user "Claude Code CLI not on PATH". Probing the bare `claude`
|
|
10
|
+
* through the shell (see below) lets cmd.exe resolve either `claude.cmd` or
|
|
11
|
+
* `claude.exe` via PATHEXT. Exported so the regression is pinned by a test.
|
|
12
|
+
*/
|
|
13
|
+
export declare function claudeCliProbe(_platform: NodeJS.Platform): string;
|
|
3
14
|
export declare const claudeCodeDetector: HarnessDetector;
|
|
4
15
|
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/detect/claude-code.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,gBAAgB,EAEhB,eAAe,EAChB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAiBrE;
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/detect/claude-code.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,gBAAgB,EAEhB,eAAe,EAChB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAiBrE;AAWD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAEjE;AA6BD,eAAO,MAAM,kBAAkB,EAAE,eAkChC,CAAC"}
|
|
@@ -39,22 +39,37 @@ async function fileExists(p) {
|
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The command we probe for the Claude Code CLI on every platform.
|
|
44
|
+
*
|
|
45
|
+
* R33 — Windows used to probe `claude.cmd` specifically. That only matches the
|
|
46
|
+
* npm-installed shim; the NATIVE Windows installer ships `claude.exe` (no .cmd),
|
|
47
|
+
* so the probe failed for a correctly installed CLI and `acprompt-join` told a
|
|
48
|
+
* native-install user "Claude Code CLI not on PATH". Probing the bare `claude`
|
|
49
|
+
* through the shell (see below) lets cmd.exe resolve either `claude.cmd` or
|
|
50
|
+
* `claude.exe` via PATHEXT. Exported so the regression is pinned by a test.
|
|
51
|
+
*/
|
|
52
|
+
export function claudeCliProbe(_platform) {
|
|
53
|
+
return "claude";
|
|
54
|
+
}
|
|
42
55
|
async function claudeCliOnPath(ctx) {
|
|
43
56
|
// `claude --version` is the cheapest way to confirm the CLI exists.
|
|
44
57
|
// Swallow every error — if it's not installed, the CLI simply isn't there.
|
|
45
58
|
//
|
|
46
59
|
// Windows-specific: on Node 24 (CVE-2024-27980 hardening), spawning a
|
|
47
60
|
// `.cmd` file without `shell: true` is rejected unconditionally with
|
|
48
|
-
// EINVAL.
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
61
|
+
// EINVAL. We pass `shell: true` on win32 to stay compatible across Node
|
|
62
|
+
// 20/22/24. Safe against the F7-class cmd.exe argv-splitting trap because
|
|
63
|
+
// the only arg is the literal `--version`, containing no whitespace or
|
|
64
|
+
// shell metacharacters.
|
|
65
|
+
//
|
|
66
|
+
// R33 — `env: ctx.env` is deliberate, not cosmetic: without it the probe
|
|
67
|
+
// used the PROCESS environment regardless of the context, so a caller (or a
|
|
68
|
+
// test) that supplied a controlled PATH still hit the developer's real PATH.
|
|
69
|
+
const probe = claudeCliProbe(ctx.platform);
|
|
55
70
|
const opts = ctx.platform === "win32"
|
|
56
|
-
? { timeout: 3000, shell: true }
|
|
57
|
-
: { timeout: 3000 };
|
|
71
|
+
? { timeout: 3000, shell: true, env: ctx.env, windowsHide: true }
|
|
72
|
+
: { timeout: 3000, env: ctx.env };
|
|
58
73
|
try {
|
|
59
74
|
await execFileAsync(probe, ["--version"], opts);
|
|
60
75
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/detect/claude-code.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,yDAAyD;AACzD,mDAAmD;AACnD,6DAA6D;AAC7D,iFAAiF;AACjF,6DAA6D;AAC7D,6EAA6E;AAC7E,0EAA0E;AAC1E,2EAA2E;AAC3E,uDAAuD;AACvD,EAAE;AACF,wEAAwE;AACxE,2EAA2E;AAC3E,6EAA6E;AAE7E,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAOtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,UAAU,uBAAuB,CAAC,GAAqB;IAC3D,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,IAAI,CACd,GAAG,CAAC,IAAI,EACR,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,4BAA4B,CAC7B,CAAC;IACJ,CAAC;IACD,0BAA0B;IAC1B,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;AACtE,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,CAAS;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAqB;IAClD,oEAAoE;IACpE,2EAA2E;IAC3E,EAAE;IACF,sEAAsE;IACtE,qEAAqE;IACrE,
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/detect/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import { type HarnessInstaller } from "./shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pinned mcp-remote version.
|
|
4
|
+
*
|
|
5
|
+
* R33 — `npx -y mcp-remote` resolved to "whatever is latest at install time",
|
|
6
|
+
* so an upstream release could silently change behavior months after we wrote
|
|
7
|
+
* the config (the shape below depends on a specific documented-in-code behavior:
|
|
8
|
+
* `${VAR}` expansion of header values). Pinned so the config a user gets is
|
|
9
|
+
* reproducible; bump deliberately, then re-run scripts/probe-mcp-remote.mjs
|
|
10
|
+
* against the real endpoint before shipping.
|
|
11
|
+
*
|
|
12
|
+
* Verified 2026-10-31 (R33): 0.15.2 still expands `${VAR}` in header values —
|
|
13
|
+
* dist substituteEnvVars() is applied to every parsed header before the
|
|
14
|
+
* transport is built. If a bump ever removes that, every Claude Desktop config
|
|
15
|
+
* we wrote starts 401ing with a literal "${ACP_MCP_AUTH}" header.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MCP_REMOTE_SPEC = "mcp-remote@0.15.2";
|
|
2
18
|
/**
|
|
3
19
|
* Build the Claude-Desktop-compatible MCP server entry for ACPrompt. See
|
|
4
20
|
* the file-top comment for why this shape (and not `{type:"http",...}`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/install/claude-code.ts"],"names":[],"mappings":"AA8CA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/install/claude-code.ts"],"names":[],"mappings":"AA8CA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIvE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAc1B;AAyBD,eAAO,MAAM,mBAAmB,EAAE,gBA4CjC,CAAC"}
|
|
@@ -44,7 +44,24 @@ import pc from "picocolors";
|
|
|
44
44
|
import { promises as fs } from "node:fs";
|
|
45
45
|
import path from "node:path";
|
|
46
46
|
import { readMergeableJson } from "./shared.js";
|
|
47
|
+
import { writeJsonAtomic } from "../util/atomic-write.js";
|
|
47
48
|
import { claudeDesktopConfigPath } from "../detect/claude-code.js";
|
|
49
|
+
/**
|
|
50
|
+
* Pinned mcp-remote version.
|
|
51
|
+
*
|
|
52
|
+
* R33 — `npx -y mcp-remote` resolved to "whatever is latest at install time",
|
|
53
|
+
* so an upstream release could silently change behavior months after we wrote
|
|
54
|
+
* the config (the shape below depends on a specific documented-in-code behavior:
|
|
55
|
+
* `${VAR}` expansion of header values). Pinned so the config a user gets is
|
|
56
|
+
* reproducible; bump deliberately, then re-run scripts/probe-mcp-remote.mjs
|
|
57
|
+
* against the real endpoint before shipping.
|
|
58
|
+
*
|
|
59
|
+
* Verified 2026-10-31 (R33): 0.15.2 still expands `${VAR}` in header values —
|
|
60
|
+
* dist substituteEnvVars() is applied to every parsed header before the
|
|
61
|
+
* transport is built. If a bump ever removes that, every Claude Desktop config
|
|
62
|
+
* we wrote starts 401ing with a literal "${ACP_MCP_AUTH}" header.
|
|
63
|
+
*/
|
|
64
|
+
export const MCP_REMOTE_SPEC = "mcp-remote@0.15.2";
|
|
48
65
|
/**
|
|
49
66
|
* Build the Claude-Desktop-compatible MCP server entry for ACPrompt. See
|
|
50
67
|
* the file-top comment for why this shape (and not `{type:"http",...}`)
|
|
@@ -56,7 +73,7 @@ export function buildClaudeDesktopEntry(opts) {
|
|
|
56
73
|
command: "npx",
|
|
57
74
|
args: [
|
|
58
75
|
"-y",
|
|
59
|
-
|
|
76
|
+
MCP_REMOTE_SPEC,
|
|
60
77
|
opts.mcpUrl,
|
|
61
78
|
"--header",
|
|
62
79
|
"Authorization:${ACP_MCP_AUTH}",
|
|
@@ -81,7 +98,9 @@ async function mergeConfig(configPath, entry) {
|
|
|
81
98
|
const servers = (current.mcpServers ?? {});
|
|
82
99
|
servers["acprompt"] = entry;
|
|
83
100
|
current.mcpServers = servers;
|
|
84
|
-
|
|
101
|
+
// R33 — atomic: a torn write here destroys every other MCP server the
|
|
102
|
+
// user had configured. See src/util/atomic-write.ts.
|
|
103
|
+
await writeJsonAtomic(configPath, current);
|
|
85
104
|
}
|
|
86
105
|
export const claudeCodeInstaller = async (ic) => {
|
|
87
106
|
const wrote = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/install/claude-code.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,2BAA2B;AAC3B,8DAA8D;AAC9D,oEAAoE;AACpE,uEAAuE;AACvE,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,yDAAyD;AACzD,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,2EAA2E;AAC3E,kEAAkE;AAClE,mEAAmE;AACnE,qEAAqE;AACrE,0EAA0E;AAC1E,yDAAyD;AACzD,sEAAsE;AACtE,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,0EAA0E;AAC1E,uDAAuD;AACvD,wEAAwE;AACxE,uEAAuE;AACvE,wEAAwE;AACxE,uDAAuD;AACvD,EAAE;AACF,oBAAoB;AACpB,yEAAyE;AACzE,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,sCAAsC;AAEtC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAyB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAGvC;IACC,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI,EAAE;YACJ,IAAI;YACJ,
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/install/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"letta.d.ts","sourceRoot":"","sources":["../../src/install/letta.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"letta.d.ts","sourceRoot":"","sources":["../../src/install/letta.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIvE,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,eAAO,MAAM,cAAc,EAAE,gBAmB5B,CAAC"}
|
package/dist/install/letta.js
CHANGED
|
@@ -18,6 +18,7 @@ import pc from "picocolors";
|
|
|
18
18
|
import { promises as fs } from "node:fs";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import { readMergeableJson } from "./shared.js";
|
|
21
|
+
import { writeJsonAtomic } from "../util/atomic-write.js";
|
|
21
22
|
import { lettaMcpConfigPath } from "../detect/letta.js";
|
|
22
23
|
export async function writeLettaMcpConfig(configPath, mcpUrl, token) {
|
|
23
24
|
// See comment in claude-code.ts::mergeConfig — readMergeableJson is the
|
|
@@ -36,7 +37,8 @@ export async function writeLettaMcpConfig(configPath, mcpUrl, token) {
|
|
|
36
37
|
headers: { Authorization: `Bearer ${token}` },
|
|
37
38
|
};
|
|
38
39
|
current.mcpServers = servers;
|
|
39
|
-
|
|
40
|
+
// R33 — atomic write; see src/util/atomic-write.ts.
|
|
41
|
+
await writeJsonAtomic(configPath, current);
|
|
40
42
|
}
|
|
41
43
|
export const lettaInstaller = async (ic) => {
|
|
42
44
|
const configPath = ic.detection.configTargets?.[0] ??
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"letta.js","sourceRoot":"","sources":["../../src/install/letta.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,mEAAmE;AACnE,yCAAyC;AACzC,EAAE;AACF,MAAM;AACN,sBAAsB;AACtB,sBAAsB;AACtB,0CAA0C;AAC1C,qDAAqD;AACrD,2DAA2D;AAC3D,UAAU;AACV,QAAQ;AACR,MAAM;AAEN,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAyB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAkB,EAClB,MAAc,EACd,KAAa;IAEb,wEAAwE;IACxE,mEAAmE;IACnE,sEAAsE;IACtE,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAA4B,CAAC;IACtE,OAAO,CAAC,UAAU,CAAC,GAAG;QACpB,SAAS,EAAE,iBAAiB;QAC5B,GAAG,EAAE,MAAM;QACX,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;KAC9C,CAAC;IACF,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;IAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"letta.js","sourceRoot":"","sources":["../../src/install/letta.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,mEAAmE;AACnE,yCAAyC;AACzC,EAAE;AACF,MAAM;AACN,sBAAsB;AACtB,sBAAsB;AACtB,0CAA0C;AAC1C,qDAAqD;AACrD,2DAA2D;AAC3D,UAAU;AACV,QAAQ;AACR,MAAM;AAEN,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAyB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAkB,EAClB,MAAc,EACd,KAAa;IAEb,wEAAwE;IACxE,mEAAmE;IACnE,sEAAsE;IACtE,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAA4B,CAAC;IACtE,OAAO,CAAC,UAAU,CAAC,GAAG;QACpB,SAAS,EAAE,iBAAiB;QAC5B,GAAG,EAAE,MAAM;QACX,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;KAC9C,CAAC;IACF,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;IAC7B,oDAAoD;IACpD,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAqB,KAAK,EAAE,EAAE,EAAE,EAAE;IAC3D,MAAM,UAAU,GACd,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAC/B,kBAAkB,CAAC;YACjB,GAAG,EAAE,EAAE,CAAC,IAAI;YACZ,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;IAEL,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACf,MAAM,mBAAmB,CAAC,UAAU,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,sCAAsC,UAAU,GAAG,CAAC,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,GAAG,CAAC,gEAAgE,CAAC,CACzE,CAAC;IAEF,OAAO,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC9C,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface AtomicWriteOptions {
|
|
2
|
+
/** File mode for the temp file (e.g. 0o600 for secrets). */
|
|
3
|
+
mode?: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function writeFileAtomic(targetPath: string, data: string, opts?: AtomicWriteOptions): Promise<void>;
|
|
6
|
+
/** JSON convenience wrapper: pretty-print then write atomically. */
|
|
7
|
+
export declare function writeJsonAtomic(targetPath: string, value: unknown, opts?: AtomicWriteOptions): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=atomic-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../src/util/atomic-write.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,oEAAoE;AACpE,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Atomic file write — serialize to a sibling temp file, fsync, rename over
|
|
2
|
+
// the target.
|
|
3
|
+
//
|
|
4
|
+
// Why this exists: several installers merge our entry into a config file the
|
|
5
|
+
// user already owns (Claude Desktop's claude_desktop_config.json, Letta's mcp
|
|
6
|
+
// config, ~/.acprompt/config.json). A plain `fs.writeFile` truncates the target
|
|
7
|
+
// first and then streams the new bytes in, so a crash, a full disk, a killed
|
|
8
|
+
// process, or a Windows antivirus/OneDrive lock in the middle of that window
|
|
9
|
+
// leaves a HALF-WRITTEN file. For a merged config that means the user's other
|
|
10
|
+
// MCP servers are gone; for the token store it means auth silently breaks.
|
|
11
|
+
//
|
|
12
|
+
// rename(2) within one directory is atomic, so the target is always either the
|
|
13
|
+
// complete old file or the complete new one.
|
|
14
|
+
import { promises as fs } from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { randomBytes } from "node:crypto";
|
|
17
|
+
export async function writeFileAtomic(targetPath, data, opts = {}) {
|
|
18
|
+
const dir = path.dirname(targetPath);
|
|
19
|
+
const tmpPath = path.join(dir, `.${path.basename(targetPath)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
|
|
20
|
+
let handle;
|
|
21
|
+
try {
|
|
22
|
+
handle = await fs.open(tmpPath, "w", opts.mode ?? 0o644);
|
|
23
|
+
await handle.writeFile(data, "utf8");
|
|
24
|
+
// Flush to disk before the rename, otherwise a power loss can leave the
|
|
25
|
+
// renamed (empty) file behind on some filesystems.
|
|
26
|
+
await handle.sync();
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await handle?.close();
|
|
30
|
+
}
|
|
31
|
+
// Windows: rename over an existing file fails with EPERM/EBUSY while another
|
|
32
|
+
// process (editor, antivirus, sync client) holds a handle. Retry briefly —
|
|
33
|
+
// these are almost always short-lived — then fail loudly with the real cause.
|
|
34
|
+
const attempts = process.platform === "win32" ? 5 : 1;
|
|
35
|
+
let lastErr;
|
|
36
|
+
for (let i = 0; i < attempts; i++) {
|
|
37
|
+
try {
|
|
38
|
+
await fs.rename(tmpPath, targetPath);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
lastErr = err;
|
|
43
|
+
const code = err.code;
|
|
44
|
+
if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES")
|
|
45
|
+
break;
|
|
46
|
+
await new Promise((r) => setTimeout(r, 60 * (i + 1)));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Never leave the temp fragment behind on failure.
|
|
50
|
+
await fs.rm(tmpPath, { force: true }).catch(() => undefined);
|
|
51
|
+
throw new Error(`Could not write ${targetPath} atomically: ${lastErr?.message ?? "unknown error"}. ` +
|
|
52
|
+
`The existing file was left untouched. Close any program holding it open and retry.`);
|
|
53
|
+
}
|
|
54
|
+
/** JSON convenience wrapper: pretty-print then write atomically. */
|
|
55
|
+
export async function writeJsonAtomic(targetPath, value, opts = {}) {
|
|
56
|
+
await writeFileAtomic(targetPath, JSON.stringify(value, null, 2), opts);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=atomic-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/util/atomic-write.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,cAAc;AACd,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,+EAA+E;AAC/E,6CAA6C;AAE7C,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,IAAY,EACZ,OAA2B,EAAE;IAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,GAAG,EACH,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CACrF,CAAC;IAEF,IAAI,MAAuD,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC;QACzD,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrC,wEAAwE;QACxE,mDAAmD;QACnD,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,OAAgB,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,GAAG,CAAC;YACd,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;YACjD,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ;gBAAE,MAAM;YACrE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC7D,MAAM,IAAI,KAAK,CACb,mBAAmB,UAAU,gBAAiB,OAAiB,EAAE,OAAO,IAAI,eAAe,IAAI;QAC7F,oFAAoF,CACvF,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,KAAc,EACd,OAA2B,EAAE;IAE7B,MAAM,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAC1E,CAAC"}
|