@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/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-YOGAG4YY.js";
8
+ } from "./chunk-242XN7FD.js";
6
9
  import {
7
- checkReplies2,
8
- registerDelivery
9
- } from "./chunk-U6SLIORK.js";
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 inbound = work.replies.length > 0 || work.requests.length > 0;
177
- const reengage = work.owedCallbacks.length > 0 && (reason === "boot" || reason.includes("callback"));
178
- if (cmd && (inbound || reengage)) {
179
- spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(work, reason) } });
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-KNMI3CJ3.js";
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
- overrideToken2,
13
- pollToken,
18
+ overrideToken,
14
19
  readToken,
15
- requestCode,
16
20
  saveToken,
17
21
  setIdentity,
18
- sleep,
19
22
  whoAmI
20
- } from "./chunk-U6SLIORK.js";
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
- overrideToken2(readToken("Desktop"));
68
+ overrideToken(readToken("Desktop"));
54
69
  try {
55
- const minted = await hatch(process.env.PAIGY_NAME ?? "Agent", null);
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(`Hatched instantly under this machine's credential \u2014 no code needed.`);
72
+ console.log("Hatched instantly under this machine's credential \u2014 no code needed.");
58
73
  console.log(` Name : ${minted.name}
59
74
  `);
60
- const auto = autoConfigureClients();
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
- overrideToken2(null);
80
+ overrideToken(null);
71
81
  }
72
82
  }
73
- const code = await requestCode();
83
+ const start = await startPairing(chosen);
74
84
  console.log("Open this URL in your browser to approve the connection:\n");
75
- console.log(` ${code.verification_uri_complete}
85
+ console.log(` ${start.verificationUri}
76
86
  `);
77
- console.log(` (User code: ${code.user_code})
87
+ console.log(` (User code: ${start.userCode})
78
88
  `);
79
- openBrowser(code.verification_uri_complete);
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
- while (Date.now() < expiresAt) {
84
- await sleep(intervalMs);
85
- process.stdout.write(".");
86
- try {
87
- const token = await pollToken(code.device_code);
88
- if (token !== null) {
89
- saveToken(token);
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
- Paired! Token saved to ${TOKEN_PATH} (mode 0600).`);
93
- console.log("Treat this token like a password \u2014 it grants account access. Never log, echo, or commit it.");
94
- console.log(` Name : ${token.name}`);
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
- return;
104
+ process.stdout.write("Waiting for the phone to confirm");
107
105
  }
108
- } catch (err) {
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
- ${err.message}
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_NAME
4
+ } from "./chunk-VXHTA4PB.js";
5
+
6
+ // src/slot.ts
7
+ process.stdout.write(AGENT_NAME);
@@ -6,7 +6,7 @@ import {
6
6
  BACKEND_URL,
7
7
  reach,
8
8
  readToken
9
- } from "./chunk-U6SLIORK.js";
9
+ } from "./chunk-VXHTA4PB.js";
10
10
 
11
11
  // src/statusline.ts
12
12
  import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.35.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": "./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"
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
+ }
@@ -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
- };