@paigy/mcp 0.28.1 → 0.29.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.
@@ -1,7 +1,8 @@
1
1
  // ../../packages/sdk/dist/index.js
2
2
  import { createRequire as __sdkCreateRequire } from "module";
3
3
  import { z } from "zod";
4
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
4
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
5
+ import { randomUUID } from "crypto";
5
6
  import { homedir } from "os";
6
7
  import { join } from "path";
7
8
  var require2 = __sdkCreateRequire(import.meta.url);
@@ -3555,7 +3556,11 @@ function verifyPairingCommit(commitment, proto, role, x25519Pub, ed25519Pub, non
3555
3556
  return false;
3556
3557
  }
3557
3558
  }
3558
- var AGENT_NAME = process.env.PAIGY_AGENT ?? "mcp-agent";
3559
+ function sessionSlot(sessionId) {
3560
+ const id = sessionId ?? process.env.PAIGY_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? randomUUID();
3561
+ return `session:${id.slice(0, 8)}`;
3562
+ }
3563
+ var AGENT_NAME = process.env.PAIGY_AGENT || sessionSlot();
3559
3564
  var PROTO = "paigy-pair-v2|sas=24";
3560
3565
  var SAS_BITS = 24;
3561
3566
  var TOKEN_PATH = join(homedir(), ".paigy", "token.json");
@@ -3572,10 +3577,31 @@ function readTokenFile() {
3572
3577
  return {};
3573
3578
  }
3574
3579
  }
3575
- function saveToken(token, agent2 = AGENT_NAME) {
3576
- const slots = { ...readTokenFile(), [agent2]: token };
3580
+ function withTokenLock(mutate) {
3581
+ const lock = TOKEN_PATH + ".lock";
3582
+ const spin = new Int32Array(new SharedArrayBuffer(4));
3577
3583
  mkdirSync(join(homedir(), ".paigy"), { recursive: true });
3578
- writeFileSync(TOKEN_PATH, JSON.stringify(slots, null, 2) + "\n", { mode: 384 });
3584
+ for (const deadline = Date.now() + 5e3; Date.now() < deadline; ) {
3585
+ try {
3586
+ closeSync(openSync(lock, "wx"));
3587
+ break;
3588
+ } catch {
3589
+ const held = statSync(lock, { throwIfNoEntry: false })?.mtimeMs ?? Date.now();
3590
+ if (Date.now() - held > 5e3) rmSync(lock, { force: true });
3591
+ else Atomics.wait(spin, 0, 0, 25);
3592
+ }
3593
+ }
3594
+ try {
3595
+ return mutate();
3596
+ } finally {
3597
+ rmSync(lock, { force: true });
3598
+ }
3599
+ }
3600
+ function saveToken(token, agent2 = AGENT_NAME) {
3601
+ withTokenLock(() => {
3602
+ const slots = { ...readTokenFile(), [agent2]: token };
3603
+ writeFileSync(TOKEN_PATH, JSON.stringify(slots, null, 2) + "\n", { mode: 384 });
3604
+ });
3579
3605
  }
3580
3606
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
3581
3607
  function readToken(agent2 = AGENT_NAME) {
@@ -3585,13 +3611,18 @@ function readToken(agent2 = AGENT_NAME) {
3585
3611
  function listSlots() {
3586
3612
  return Object.keys(readTokenFile());
3587
3613
  }
3614
+ function slotName(agent2) {
3615
+ return readTokenFile()[agent2]?.name ?? null;
3616
+ }
3588
3617
  function deleteToken(agent2 = AGENT_NAME) {
3589
- const slots = readTokenFile();
3590
- if (!(agent2 in slots)) return false;
3591
- delete slots[agent2];
3592
- if (Object.keys(slots).length === 0) rmSync(TOKEN_PATH);
3593
- else writeFileSync(TOKEN_PATH, JSON.stringify(slots, null, 2) + "\n", { mode: 384 });
3594
- return true;
3618
+ return withTokenLock(() => {
3619
+ const slots = readTokenFile();
3620
+ if (!(agent2 in slots)) return false;
3621
+ delete slots[agent2];
3622
+ if (Object.keys(slots).length === 0) rmSync(TOKEN_PATH);
3623
+ else writeFileSync(TOKEN_PATH, JSON.stringify(slots, null, 2) + "\n", { mode: 384 });
3624
+ return true;
3625
+ });
3595
3626
  }
3596
3627
  async function revokeToken(token) {
3597
3628
  const res = await reach(`${BACKEND_URL}/api/device/revoke`, {
@@ -3978,6 +4009,7 @@ export {
3978
4009
  sleep,
3979
4010
  readToken,
3980
4011
  listSlots,
4012
+ slotName,
3981
4013
  deleteToken,
3982
4014
  revokeToken,
3983
4015
  requestCode,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AGENT_NAME
3
- } from "./chunk-7P4WMXGB.js";
3
+ } from "./chunk-BJW6Z2OY.js";
4
4
 
5
5
  // src/clients.ts
6
6
  import { execFile } from "child_process";
package/dist/index.js CHANGED
@@ -7,12 +7,13 @@ import {
7
7
  autoConfigureClients,
8
8
  claudeInstallHint,
9
9
  enablePaigyTools
10
- } from "./chunk-EQAFMUQC.js";
10
+ } from "./chunk-JG2XL7Y3.js";
11
11
  import {
12
12
  clearSurface,
13
13
  writeSurface
14
14
  } from "./chunk-3SVNPJPN.js";
15
15
  import {
16
+ AGENT_NAME,
16
17
  NotifyRequestSchema,
17
18
  ScheduleCallbackSchema,
18
19
  SetTaskStateSchema,
@@ -42,9 +43,10 @@ import {
42
43
  searchThreads,
43
44
  setTaskState,
44
45
  sleep,
46
+ slotName,
45
47
  startE2ee,
46
48
  submitNotification
47
- } from "./chunk-7P4WMXGB.js";
49
+ } from "./chunk-BJW6Z2OY.js";
48
50
 
49
51
  // src/index.ts
50
52
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -233,7 +235,7 @@ var AwaitReplySchema = z.object({
233
235
  var JOIN_CAP_MS = 45e3;
234
236
  var PairSchema = z.object({
235
237
  device_code: z.string().optional().describe("Omit to start pairing (returns an approval link to show the user). Pass the device_code from that first call to finish, once the user has approved."),
236
- name: z.string().min(1).max(60).optional().describe("Hatch path only: the name you choose for this identity. Pick your own \u2014 something you'd introduce yourself as on a call."),
238
+ name: z.string().min(1).max(60).optional().describe("Hatch path only: the name you choose for this identity. Pick your own \u2014 ONE or TWO words, the way you'd introduce yourself on a call (it is spoken aloud and shown in lists). 'Piper', 'Blue Heron' \u2014 never a sentence or a task description."),
237
239
  voice: z.string().optional().describe("Hatch path only: your voice on calls \u2014 one of rachel, george, jessica, brian, lily.")
238
240
  });
239
241
  var GetThreadSchema = z.object({
@@ -340,6 +342,13 @@ function renderPairOutcome(outcome, device_code) {
340
342
  };
341
343
  }
342
344
  }
345
+ function unidentifiedResult() {
346
+ return { isError: true, content: [{ type: "text", text: JSON.stringify({
347
+ error: "no_identity",
348
+ slot: AGENT_NAME,
349
+ message: `This session has no Paigy identity yet, so nothing was sent \u2014 it will not speak as another session. This machine is already paired, so call \`pair\` (no arguments) to hatch this session its own identity instantly, then retry. To reuse an existing one instead, start the session with PAIGY_AGENT set to its slot (${listSlots().filter((s) => s !== "Desktop").join(", ") || "none yet"}).`
350
+ }) }] };
351
+ }
343
352
  function pairStartResult(start) {
344
353
  writeSurface("pairing code", start.userCode, start.expiresIn);
345
354
  const qr = qrcode(0, "M");
@@ -441,7 +450,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
441
450
  try {
442
451
  return await handleTool(request, extra?.signal);
443
452
  } catch (e) {
444
- if (e instanceof UnpairedError) return pairStartResult(await startPairing(suggestedAgentName()));
453
+ if (e instanceof UnpairedError) {
454
+ if (listSlots().includes("Desktop")) return unidentifiedResult();
455
+ return pairStartResult(await startPairing(suggestedAgentName()));
456
+ }
445
457
  throw e;
446
458
  }
447
459
  });
@@ -544,7 +556,8 @@ async function handleTool(request, signal) {
544
556
  branch: parsed.branch ?? git.branch
545
557
  };
546
558
  const result = await submitNotification(enriched);
547
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
559
+ const sentAs = slotName(AGENT_NAME);
560
+ return { content: [{ type: "text", text: JSON.stringify(sentAs ? { ...result, sentAs } : result) }] };
548
561
  }
549
562
  case "await_reply": {
550
563
  const { notificationId, maxWaitSeconds } = AwaitReplySchema.parse(request.params.arguments);
package/dist/listen.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  checkReplies,
8
8
  registerDelivery
9
- } from "./chunk-7P4WMXGB.js";
9
+ } from "./chunk-BJW6Z2OY.js";
10
10
 
11
11
  // src/listen.ts
12
12
  import { createClient } from "@supabase/supabase-js";
package/dist/onboard.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  autoConfigureClients,
4
4
  claudeInstallHint,
5
5
  openBrowser
6
- } from "./chunk-EQAFMUQC.js";
6
+ } from "./chunk-JG2XL7Y3.js";
7
7
  import {
8
8
  AGENT_NAME,
9
9
  TOKEN_PATH,
@@ -11,7 +11,7 @@ import {
11
11
  requestCode,
12
12
  saveToken,
13
13
  sleep
14
- } from "./chunk-7P4WMXGB.js";
14
+ } from "./chunk-BJW6Z2OY.js";
15
15
 
16
16
  // src/onboard.ts
17
17
  async function main() {
@@ -6,7 +6,7 @@ import {
6
6
  BACKEND_URL,
7
7
  reach,
8
8
  readToken
9
- } from "./chunk-7P4WMXGB.js";
9
+ } from "./chunk-BJW6Z2OY.js";
10
10
 
11
11
  // src/statusline.ts
12
12
  import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "Paigy MCP server — a voice inbox for your AI agents. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,8 +37,8 @@
37
37
  "typescript": "^5.7.2",
38
38
  "vitest": "^2.1.8",
39
39
  "@paigy/crypto": "0.0.0",
40
- "@paigy/sdk": "0.1.0",
41
- "@paigy/schema": "0.0.0"
40
+ "@paigy/schema": "0.0.0",
41
+ "@paigy/sdk": "0.2.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup",