@paigy/mcp 0.35.0 → 0.37.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/{chunk-YOGAG4YY.js → chunk-242XN7FD.js} +643 -489
- package/dist/chunk-PN7FDW26.js +315 -0
- package/dist/chunk-UG2FARVV.js +54 -0
- package/dist/chunk-V66NUNSQ.js +143 -0
- package/dist/{chunk-U6SLIORK.js → chunk-VXHTA4PB.js} +1974 -693
- package/dist/enable.js +3 -2
- package/dist/index.js +127 -354
- package/dist/listen.js +31 -34
- package/dist/onboard.js +62 -45
- package/dist/slot.js +7 -0
- package/dist/statusline.js +1 -1
- package/package.json +20 -18
- package/dist/chunk-KNMI3CJ3.js +0 -177
package/dist/listen.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
answerText
|
|
4
|
+
} from "./chunk-UG2FARVV.js";
|
|
2
5
|
import {
|
|
3
6
|
WAKE_EVENT,
|
|
4
7
|
wakeChannel
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-242XN7FD.js";
|
|
6
9
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
ackReply,
|
|
11
|
+
authToken,
|
|
12
|
+
decodeReply,
|
|
13
|
+
registerDelivery,
|
|
14
|
+
sweepLeased
|
|
15
|
+
} from "./chunk-VXHTA4PB.js";
|
|
10
16
|
|
|
11
17
|
// src/listen.ts
|
|
12
18
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -113,30 +119,6 @@ function uninstallService() {
|
|
|
113
119
|
|
|
114
120
|
// src/listen.ts
|
|
115
121
|
var emit = (obj) => void process.stdout.write(JSON.stringify(obj) + "\n");
|
|
116
|
-
function answerText(answer) {
|
|
117
|
-
switch (answer.kind) {
|
|
118
|
-
case "text":
|
|
119
|
-
return answer.text;
|
|
120
|
-
case "option":
|
|
121
|
-
return answer.label ?? answer.optionId;
|
|
122
|
-
case "multi":
|
|
123
|
-
case "ranked":
|
|
124
|
-
return (answer.labels ?? answer.optionIds).join(", ");
|
|
125
|
-
case "confirm":
|
|
126
|
-
return answer.approved ? "approved" : "declined";
|
|
127
|
-
case "clarify":
|
|
128
|
-
return answer.chunks.join(" ");
|
|
129
|
-
// A call log — PAIGY_TEXT is what the USER said, so the bot's prompts stay out
|
|
130
|
-
// (the full exchange is in PAIGY_WORK for a launcher that wants it).
|
|
131
|
-
case "turns":
|
|
132
|
-
return answer.turns.map((t) => t.reply).join(" ");
|
|
133
|
-
// An auto-answer derived from the user's past decisions — reads like a fast human.
|
|
134
|
-
case "precedent":
|
|
135
|
-
return answer.answer;
|
|
136
|
-
case "ignored":
|
|
137
|
-
return "";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
122
|
function launchEnv(work, reason) {
|
|
141
123
|
const env = { PAIGY_WORK: JSON.stringify(work), PAIGY_EVENT: reason };
|
|
142
124
|
const put = (key, value) => {
|
|
@@ -168,15 +150,30 @@ function launchEnv(work, reason) {
|
|
|
168
150
|
}
|
|
169
151
|
return env;
|
|
170
152
|
}
|
|
153
|
+
var SWEEP_PASSES = 10;
|
|
171
154
|
async function sweep(reason) {
|
|
172
155
|
try {
|
|
173
|
-
const work = await checkReplies2();
|
|
174
|
-
emit({ type: "sweep", reason, ...work });
|
|
175
156
|
const cmd = process.env.PAIGY_ON_WAKE;
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
157
|
+
const token = authToken() ?? "";
|
|
158
|
+
for (let pass = 0; pass < SWEEP_PASSES; pass += 1) {
|
|
159
|
+
const raw = await sweepLeased();
|
|
160
|
+
const leased = raw.replies;
|
|
161
|
+
const work = {
|
|
162
|
+
...raw,
|
|
163
|
+
work: raw.work ?? [],
|
|
164
|
+
replies: leased.map((r) => {
|
|
165
|
+
const { type: _type, ...reply } = decodeReply({ ...r, type: "reply" });
|
|
166
|
+
return reply;
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
emit({ type: "sweep", reason, ...work });
|
|
170
|
+
const inbound = work.replies.length > 0 || work.requests.length > 0;
|
|
171
|
+
const reengage = work.owedCallbacks.length > 0 && (reason === "boot" || reason.includes("callback"));
|
|
172
|
+
if (cmd && (inbound || reengage)) {
|
|
173
|
+
spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(work, reason) } });
|
|
174
|
+
}
|
|
175
|
+
if (cmd) for (const r of leased) await ackReply({ ...r, type: "reply" }, token);
|
|
176
|
+
if (leased.length === 0) return;
|
|
180
177
|
}
|
|
181
178
|
} catch (e) {
|
|
182
179
|
emit({ type: "error", reason, message: e.message });
|
package/dist/onboard.js
CHANGED
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolvePairing,
|
|
4
|
+
startPairing,
|
|
5
|
+
suggestedAgentName
|
|
6
|
+
} from "./chunk-V66NUNSQ.js";
|
|
2
7
|
import {
|
|
3
8
|
autoConfigureClients,
|
|
4
9
|
claudeInstallHint,
|
|
5
10
|
openBrowser
|
|
6
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PN7FDW26.js";
|
|
12
|
+
import "./chunk-242XN7FD.js";
|
|
7
13
|
import {
|
|
8
14
|
AGENT_NAME,
|
|
9
15
|
TOKEN_PATH,
|
|
10
16
|
hatch,
|
|
11
17
|
listSlots,
|
|
12
|
-
|
|
13
|
-
pollToken,
|
|
18
|
+
overrideToken,
|
|
14
19
|
readToken,
|
|
15
|
-
requestCode,
|
|
16
20
|
saveToken,
|
|
17
21
|
setIdentity,
|
|
18
|
-
sleep,
|
|
19
22
|
whoAmI
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-VXHTA4PB.js";
|
|
21
24
|
|
|
22
25
|
// src/onboard.ts
|
|
26
|
+
function reportRegistered() {
|
|
27
|
+
const registered = autoConfigureClients();
|
|
28
|
+
if (registered.length) {
|
|
29
|
+
console.log("Also registered the Paigy MCP server in:");
|
|
30
|
+
for (const r of registered) console.log(` - ${r}`);
|
|
31
|
+
console.log("(Restart those tools to pick it up.)\n");
|
|
32
|
+
}
|
|
33
|
+
const hint = claudeInstallHint();
|
|
34
|
+
if (hint) console.log(`${hint}
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
23
37
|
async function main() {
|
|
24
38
|
console.log(`
|
|
25
39
|
Paigy MCP onboarding (agent: "${AGENT_NAME}")
|
|
26
40
|
`);
|
|
41
|
+
const chosen = process.env.PAIGY_NAME ?? suggestedAgentName();
|
|
27
42
|
if (readToken()) {
|
|
28
43
|
let who = await whoAmI().catch(() => null);
|
|
29
44
|
if (who) {
|
|
@@ -50,68 +65,70 @@ Paigy MCP onboarding (agent: "${AGENT_NAME}")
|
|
|
50
65
|
console.log("The saved token is no longer valid \u2014 re-pairing.\n");
|
|
51
66
|
}
|
|
52
67
|
if (listSlots().includes("Desktop")) {
|
|
53
|
-
|
|
68
|
+
overrideToken(readToken("Desktop"));
|
|
54
69
|
try {
|
|
55
|
-
const minted = await hatch(
|
|
70
|
+
const minted = await hatch(chosen ?? "Agent", process.env.PAIGY_VOICE ?? null);
|
|
56
71
|
saveToken({ ok: true, access_token: minted.token, name: minted.name, device: null });
|
|
57
|
-
console.log(
|
|
72
|
+
console.log("Hatched instantly under this machine's credential \u2014 no code needed.");
|
|
58
73
|
console.log(` Name : ${minted.name}
|
|
59
74
|
`);
|
|
60
|
-
|
|
61
|
-
if (auto.length) {
|
|
62
|
-
console.log("Also registered the Paigy MCP server in:");
|
|
63
|
-
for (const r of auto) console.log(` - ${r}`);
|
|
64
|
-
console.log("(Restart those tools to pick it up.)\n");
|
|
65
|
-
}
|
|
75
|
+
reportRegistered();
|
|
66
76
|
return;
|
|
67
77
|
} catch {
|
|
68
78
|
console.log("(This machine's saved credential didn't work \u2014 falling back to a code.)\n");
|
|
69
79
|
} finally {
|
|
70
|
-
|
|
80
|
+
overrideToken(null);
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
|
-
const
|
|
83
|
+
const start = await startPairing(chosen);
|
|
74
84
|
console.log("Open this URL in your browser to approve the connection:\n");
|
|
75
|
-
console.log(` ${
|
|
85
|
+
console.log(` ${start.verificationUri}
|
|
76
86
|
`);
|
|
77
|
-
console.log(` (User code: ${
|
|
87
|
+
console.log(` (User code: ${start.userCode})
|
|
78
88
|
`);
|
|
79
|
-
openBrowser(
|
|
80
|
-
const intervalMs = (code.interval ?? 5) * 1e3;
|
|
81
|
-
const expiresAt = Date.now() + code.expires_in * 1e3;
|
|
89
|
+
openBrowser(start.verificationUri);
|
|
82
90
|
process.stdout.write("Waiting for approval");
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
91
|
+
const deadline = Date.now() + start.expiresIn * 1e3;
|
|
92
|
+
let shownSas = null;
|
|
93
|
+
while (Date.now() < deadline) {
|
|
94
|
+
const outcome = await resolvePairing(start.deviceCode, Math.max(1e3, deadline - Date.now()));
|
|
95
|
+
if (outcome.kind === "awaiting_confirm") {
|
|
96
|
+
if (shownSas !== outcome.sas) {
|
|
97
|
+
shownSas = outcome.sas;
|
|
90
98
|
console.log(`
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.log(` Device : ${token.device ?? "(none)"}
|
|
96
|
-
`);
|
|
97
|
-
const registered = autoConfigureClients();
|
|
98
|
-
if (registered.length) {
|
|
99
|
-
console.log("Also registered the Paigy MCP server in:");
|
|
100
|
-
for (const r of registered) console.log(` - ${r}`);
|
|
101
|
-
console.log("(Restart those tools to pick it up.)\n");
|
|
102
|
-
}
|
|
103
|
-
const hint = claudeInstallHint();
|
|
104
|
-
if (hint) console.log(`${hint}
|
|
100
|
+
This pairing is end-to-end encrypted. Verify this code matches the one on the phone:
|
|
101
|
+
|
|
102
|
+
${outcome.sas}
|
|
105
103
|
`);
|
|
106
|
-
|
|
104
|
+
process.stdout.write("Waiting for the phone to confirm");
|
|
107
105
|
}
|
|
108
|
-
|
|
106
|
+
process.stdout.write(".");
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (outcome.kind === "pending") {
|
|
110
|
+
process.stdout.write(".");
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (outcome.kind === "error") {
|
|
109
114
|
console.error(`
|
|
110
115
|
|
|
111
|
-
${
|
|
116
|
+
${outcome.message}
|
|
112
117
|
`);
|
|
113
118
|
process.exit(1);
|
|
114
119
|
}
|
|
120
|
+
console.log(`
|
|
121
|
+
|
|
122
|
+
Paired! Token saved to ${TOKEN_PATH} (mode 0600).`);
|
|
123
|
+
console.log("Treat this token like a password \u2014 it grants account access. Never log, echo, or commit it.");
|
|
124
|
+
console.log(` Name : ${outcome.token.name}`);
|
|
125
|
+
console.log(` Device : ${outcome.token.device ?? "(none)"}`);
|
|
126
|
+
if (outcome.sas) {
|
|
127
|
+
console.log(` E2EE : ${outcome.e2ee ? "verified and pinned" : "offered, identity key not yet confirmed on the phone"} (code ${outcome.sas})`);
|
|
128
|
+
}
|
|
129
|
+
console.log();
|
|
130
|
+
reportRegistered();
|
|
131
|
+
return;
|
|
115
132
|
}
|
|
116
133
|
console.error("\n\nDevice code expired without approval. Please re-run onboarding.\n");
|
|
117
134
|
process.exit(1);
|
package/dist/slot.js
ADDED
package/dist/statusline.js
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"mcp": "
|
|
9
|
-
"paigy-mcp": "
|
|
10
|
-
"paigy-mcp-onboard": "
|
|
11
|
-
"paigy-listen": "
|
|
12
|
-
"paigy-statusline": "
|
|
13
|
-
"paigy-enable-tools": "
|
|
8
|
+
"mcp": "dist/index.js",
|
|
9
|
+
"paigy-mcp": "dist/index.js",
|
|
10
|
+
"paigy-mcp-onboard": "dist/onboard.js",
|
|
11
|
+
"paigy-listen": "dist/listen.js",
|
|
12
|
+
"paigy-statusline": "dist/statusline.js",
|
|
13
|
+
"paigy-enable-tools": "dist/enable.js",
|
|
14
|
+
"paigy-slot": "dist/slot.js"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"dist"
|
|
@@ -23,6 +24,13 @@
|
|
|
23
24
|
"url": "git+https://github.com/mauurda/paigy.git",
|
|
24
25
|
"directory": "apps/mcp"
|
|
25
26
|
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepublishOnly": "pnpm --filter @paigy/schema build && pnpm --filter @paigy/crypto build && pnpm --filter @paigy/sdk build && pnpm build"
|
|
33
|
+
},
|
|
26
34
|
"dependencies": {
|
|
27
35
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
28
36
|
"@supabase/supabase-js": "^2.47.10",
|
|
@@ -32,19 +40,13 @@
|
|
|
32
40
|
"zod-to-json-schema": "^3.24.1"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
43
|
+
"@paigy/crypto": "workspace:*",
|
|
44
|
+
"@paigy/schema": "workspace:*",
|
|
45
|
+
"@paigy/sdk": "workspace:*",
|
|
35
46
|
"@types/node": "^22.0.0",
|
|
36
47
|
"@types/qrcode-generator": "^1.0.6",
|
|
37
48
|
"tsup": "^8.3.5",
|
|
38
49
|
"typescript": "^5.7.2",
|
|
39
|
-
"vitest": "^2.1.8"
|
|
40
|
-
"@paigy/crypto": "0.0.0",
|
|
41
|
-
"@paigy/schema": "0.0.0",
|
|
42
|
-
"@paigy/sdk": "0.2.0"
|
|
43
|
-
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsup",
|
|
46
|
-
"dev": "tsup --watch",
|
|
47
|
-
"typecheck": "tsc --noEmit",
|
|
48
|
-
"test": "vitest run"
|
|
50
|
+
"vitest": "^2.1.8"
|
|
49
51
|
}
|
|
50
|
-
}
|
|
52
|
+
}
|
package/dist/chunk-KNMI3CJ3.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AGENT_NAME
|
|
3
|
-
} from "./chunk-U6SLIORK.js";
|
|
4
|
-
|
|
5
|
-
// src/clients.ts
|
|
6
|
-
import { execFile } from "child_process";
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
-
import { homedir, platform } from "os";
|
|
9
|
-
import { join, dirname } from "path";
|
|
10
|
-
function openBrowser(url) {
|
|
11
|
-
const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "cmd" : "xdg-open";
|
|
12
|
-
const args = platform() === "win32" ? ["/c", "start", url] : [url];
|
|
13
|
-
execFile(cmd, args, () => {
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
function withPaigyMcpJson(existing, agent) {
|
|
17
|
-
let config = {};
|
|
18
|
-
if (existing) {
|
|
19
|
-
try {
|
|
20
|
-
config = JSON.parse(existing);
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
config.mcpServers = {
|
|
26
|
-
...config.mcpServers ?? {},
|
|
27
|
-
paigy: { command: "npx", args: ["-y", "@paigy/mcp@latest"], env: { PAIGY_AGENT: agent } }
|
|
28
|
-
};
|
|
29
|
-
return JSON.stringify(config, null, 2);
|
|
30
|
-
}
|
|
31
|
-
function withPaigyMcpToml(existing, agent) {
|
|
32
|
-
if (existing.includes("[mcp_servers.paigy]")) return null;
|
|
33
|
-
const entry = [
|
|
34
|
-
"",
|
|
35
|
-
"[mcp_servers.paigy]",
|
|
36
|
-
'command = "npx"',
|
|
37
|
-
'args = ["-y", "@paigy/mcp@latest"]',
|
|
38
|
-
`env = { PAIGY_AGENT = "${agent}" }`,
|
|
39
|
-
""
|
|
40
|
-
].join("\n");
|
|
41
|
-
return existing + entry;
|
|
42
|
-
}
|
|
43
|
-
function autoConfigureClients(skip = AGENT_NAME) {
|
|
44
|
-
const home = homedir();
|
|
45
|
-
const registered = [];
|
|
46
|
-
if (skip !== "antigravity") {
|
|
47
|
-
const antigravityConfigs = [
|
|
48
|
-
join(home, ".gemini", "config", "mcp_config.json"),
|
|
49
|
-
join(home, ".gemini", "antigravity-cli", "mcp_config.json")
|
|
50
|
-
];
|
|
51
|
-
for (const path of antigravityConfigs) {
|
|
52
|
-
try {
|
|
53
|
-
if (!existsSync(dirname(path))) continue;
|
|
54
|
-
const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
55
|
-
const updated = withPaigyMcpJson(existing, "antigravity");
|
|
56
|
-
if (updated === null) continue;
|
|
57
|
-
writeFileSync(path, updated);
|
|
58
|
-
registered.push(`Google Antigravity (${path})`);
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (skip !== "codex") {
|
|
64
|
-
const codexConfig = join(home, ".codex", "config.toml");
|
|
65
|
-
try {
|
|
66
|
-
if (existsSync(dirname(codexConfig))) {
|
|
67
|
-
const existing = existsSync(codexConfig) ? readFileSync(codexConfig, "utf8") : "";
|
|
68
|
-
const updated = withPaigyMcpToml(existing, "codex");
|
|
69
|
-
if (updated !== null) {
|
|
70
|
-
writeFileSync(codexConfig, updated);
|
|
71
|
-
registered.push(`OpenAI Codex (${codexConfig})`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return registered;
|
|
78
|
-
}
|
|
79
|
-
function claudeInstallHint(skip = AGENT_NAME) {
|
|
80
|
-
if (skip.startsWith("claude")) return null;
|
|
81
|
-
const claudeDir = join(homedir(), ".claude");
|
|
82
|
-
if (!existsSync(claudeDir)) return null;
|
|
83
|
-
try {
|
|
84
|
-
const registry = join(claudeDir, "plugins", "installed_plugins.json");
|
|
85
|
-
if (existsSync(registry) && readFileSync(registry, "utf8").includes('"paigy@')) return null;
|
|
86
|
-
} catch {
|
|
87
|
-
}
|
|
88
|
-
return "Claude Code detected \u2014 to add Paigy there, run /plugin marketplace add paigy-ai/mcp then /plugin install paigy (pairing carries over; no need to pair again). Note: If you run this inside a live session, type /reload-plugins afterward so the agent connects to the new tools.";
|
|
89
|
-
}
|
|
90
|
-
var ENABLE_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
|
|
91
|
-
var PAIGY_TOOL_IDS = [
|
|
92
|
-
"mcp__paigy__contact",
|
|
93
|
-
"mcp__paigy__notify_user",
|
|
94
|
-
"mcp__paigy__notify",
|
|
95
|
-
"mcp__paigy__await_reply",
|
|
96
|
-
"mcp__paigy__check_replies",
|
|
97
|
-
"mcp__paigy__set_task_state",
|
|
98
|
-
"mcp__paigy__schedule_callback",
|
|
99
|
-
"mcp__paigy__get_thread"
|
|
100
|
-
];
|
|
101
|
-
function withPaigyAllowlist(existing, tools) {
|
|
102
|
-
let config = {};
|
|
103
|
-
if (existing) {
|
|
104
|
-
try {
|
|
105
|
-
config = JSON.parse(existing);
|
|
106
|
-
} catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const permissions = config.permissions && typeof config.permissions === "object" ? config.permissions : {};
|
|
111
|
-
const allow = Array.isArray(permissions.allow) ? [...permissions.allow] : [];
|
|
112
|
-
for (const t of tools) if (!allow.includes(t)) allow.push(t);
|
|
113
|
-
config.permissions = { ...permissions, allow };
|
|
114
|
-
return JSON.stringify(config, null, 2);
|
|
115
|
-
}
|
|
116
|
-
function settingsPathFor(scope, cwd) {
|
|
117
|
-
return scope === "project" ? join(cwd, ".claude", "settings.json") : join(homedir(), ".claude", "settings.json");
|
|
118
|
-
}
|
|
119
|
-
function paigyToolsAllowlisted(scope = "user", cwd = process.cwd()) {
|
|
120
|
-
try {
|
|
121
|
-
const path = settingsPathFor(scope, cwd);
|
|
122
|
-
if (!path || !existsSync(path)) return false;
|
|
123
|
-
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
124
|
-
const allow = Array.isArray(config.permissions?.allow) ? config.permissions.allow : [];
|
|
125
|
-
return PAIGY_TOOL_IDS.every((t) => allow.includes(t));
|
|
126
|
-
} catch {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function enablePaigyTools(scope = "user", cwd = process.cwd()) {
|
|
131
|
-
if (scope === "user" && !existsSync(join(homedir(), ".claude"))) {
|
|
132
|
-
return {
|
|
133
|
-
ok: false,
|
|
134
|
-
reason: `No ~/.claude on this machine \u2014 the hosting tool uses its own permission allowlist. Add these ids there by hand: ${PAIGY_TOOL_IDS.join(", ")}.`
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
const path = settingsPathFor(scope, cwd);
|
|
138
|
-
let existing = null;
|
|
139
|
-
try {
|
|
140
|
-
existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
141
|
-
} catch {
|
|
142
|
-
existing = null;
|
|
143
|
-
}
|
|
144
|
-
let prevAllow = [];
|
|
145
|
-
if (existing) {
|
|
146
|
-
try {
|
|
147
|
-
const parsed = JSON.parse(existing);
|
|
148
|
-
if (Array.isArray(parsed.permissions?.allow)) prevAllow = parsed.permissions.allow;
|
|
149
|
-
} catch {
|
|
150
|
-
return { ok: false, reason: `${path} didn't parse as JSON \u2014 not overwriting it. Add the ids by hand.` };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const updated = withPaigyAllowlist(existing, PAIGY_TOOL_IDS);
|
|
154
|
-
if (updated === null) return { ok: false, reason: `${path} didn't parse as JSON \u2014 not overwriting it. Add the ids by hand.` };
|
|
155
|
-
try {
|
|
156
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
157
|
-
writeFileSync(path, updated + "\n");
|
|
158
|
-
} catch (e) {
|
|
159
|
-
return { ok: false, reason: `Couldn't write ${path}: ${e.message}` };
|
|
160
|
-
}
|
|
161
|
-
return {
|
|
162
|
-
ok: true,
|
|
163
|
-
path,
|
|
164
|
-
added: PAIGY_TOOL_IDS.filter((t) => !prevAllow.includes(t)),
|
|
165
|
-
already: PAIGY_TOOL_IDS.filter((t) => prevAllow.includes(t))
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export {
|
|
170
|
-
openBrowser,
|
|
171
|
-
autoConfigureClients,
|
|
172
|
-
claudeInstallHint,
|
|
173
|
-
ENABLE_COMMAND,
|
|
174
|
-
PAIGY_TOOL_IDS,
|
|
175
|
-
paigyToolsAllowlisted,
|
|
176
|
-
enablePaigyTools
|
|
177
|
-
};
|