patchcord 0.3.70 → 0.3.72

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 (2) hide show
  1. package/bin/patchcord.mjs +62 -8
  2. package/package.json +1 -1
package/bin/patchcord.mjs CHANGED
@@ -321,8 +321,61 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
321
321
  }
322
322
  rl.close();
323
323
  } else {
324
+ // Check if patchcord is already configured — offer to update URL without re-auth
325
+ let existingToken = "";
326
+ let existingConfigFile = "";
327
+ const mcpJsonPath = join(cwd, ".mcp.json");
328
+ const codexTomlPath = join(cwd, ".codex", "config.toml");
329
+ if (existsSync(mcpJsonPath)) {
330
+ try {
331
+ const existing = JSON.parse(readFileSync(mcpJsonPath, "utf-8"));
332
+ const pt = existing?.mcpServers?.patchcord;
333
+ if (pt?.headers?.Authorization) {
334
+ existingToken = pt.headers.Authorization.replace(/^Bearer\s+/i, "");
335
+ existingConfigFile = mcpJsonPath;
336
+ }
337
+ } catch {}
338
+ }
339
+ if (!existingToken && existsSync(codexTomlPath)) {
340
+ try {
341
+ const content = readFileSync(codexTomlPath, "utf-8");
342
+ const match = content.match(/Bearer\s+([^\s"]+)/);
343
+ if (match) {
344
+ existingToken = match[1];
345
+ existingConfigFile = codexTomlPath;
346
+ }
347
+ } catch {}
348
+ }
349
+ if (existingToken) {
350
+ console.log(`\n ${dim}Existing patchcord token found in ${existingConfigFile}${r}`);
351
+ const { createInterface: createRLU } = await import("readline");
352
+ const rlU = createRLU({ input: process.stdin, output: process.stdout });
353
+ const askU = (q) => new Promise((resolve) => rlU.question(q, resolve));
354
+ const answer = (await askU(` ${bold}Update config and keep existing token? (Y/n):${r} `)).trim().toLowerCase();
355
+ rlU.close();
356
+ if (answer !== "n" && answer !== "no") {
357
+ token = existingToken;
358
+ // Validate existing token
359
+ const validateResp = run(`curl -sf --max-time 5 -H "Authorization: Bearer ${token}" "${serverUrl}/api/inbox?limit=0&count_only=1"`);
360
+ if (validateResp) {
361
+ try {
362
+ const data = JSON.parse(validateResp);
363
+ identity = `${data.agent_id}@${data.namespace_id}`;
364
+ clientType = data.client_type || "";
365
+ choice = CLIENT_TYPE_MAP[clientType] || "";
366
+ console.log(` ${green}✓${r} ${bold}${identity}${r} — token valid`);
367
+ } catch {}
368
+ }
369
+ if (!identity) {
370
+ console.log(` ${yellow}⚠${r} Token expired or invalid. Starting fresh setup.`);
371
+ token = "";
372
+ }
373
+ }
374
+ }
375
+
376
+ if (!token) {
324
377
  // Browser connect flow
325
- rl.close();
378
+ if (rl) rl.close();
326
379
 
327
380
  function canOpenBrowser() {
328
381
  if (process.env.SSH_CLIENT || process.env.SSH_TTY) return false;
@@ -475,7 +528,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
475
528
  }
476
529
  }
477
530
  }
478
- }
531
+ } // end connect flow
532
+ } // end if (!token)
479
533
 
480
534
  const isCodex = choice === "2";
481
535
  const isCursor = choice === "3";
@@ -555,7 +609,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
555
609
  let geminiSettings = (existsSync(geminiPath) && safeReadJson(geminiPath)) || {};
556
610
  if (!geminiSettings.mcpServers) geminiSettings.mcpServers = {};
557
611
  geminiSettings.mcpServers.patchcord = {
558
- httpUrl: `${serverUrl}/mcp`,
612
+ httpUrl: `${serverUrl}/mcp/bearer`,
559
613
  headers: {
560
614
  Authorization: `Bearer ${token}`,
561
615
  "X-Patchcord-Machine": hostname,
@@ -578,7 +632,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
578
632
  let zedSettings = (existsSync(zedPath) && safeReadJson(zedPath)) || {};
579
633
  if (!zedSettings.context_servers) zedSettings.context_servers = {};
580
634
  zedSettings.context_servers.patchcord = {
581
- url: `${serverUrl}/mcp`,
635
+ url: `${serverUrl}/mcp/bearer`,
582
636
  headers: {
583
637
  Authorization: `Bearer ${token}`,
584
638
  "X-Patchcord-Machine": hostname,
@@ -603,7 +657,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
603
657
  if (!ocConfig.mcp) ocConfig.mcp = {};
604
658
  ocConfig.mcp.patchcord = {
605
659
  type: "remote",
606
- url: `${serverUrl}/mcp`,
660
+ url: `${serverUrl}/mcp/bearer`,
607
661
  headers: {
608
662
  Authorization: `Bearer ${token}`,
609
663
  "X-Patchcord-Machine": hostname,
@@ -620,7 +674,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
620
674
  servers: {
621
675
  patchcord: {
622
676
  type: "http",
623
- url: `${serverUrl}/mcp`,
677
+ url: `${serverUrl}/mcp/bearer`,
624
678
  headers: {
625
679
  Authorization: `Bearer ${token}`,
626
680
  "X-Patchcord-Machine": hostname,
@@ -746,13 +800,13 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
746
800
  console.log(`\n ${green}✓${r} Codex configured: ${dim}${configPath}${r}`);
747
801
  console.log(` ${green}✓${r} Plugin installed: ${dim}@patchcord${r}, ${dim}@patchcord-wait${r}`);
748
802
  } else {
749
- // Claude Code: write .mcp.json (MCP server only channel plugin disabled)
803
+ // Claude Code: write .mcp.json (use /mcp/bearer to avoid OAuth discovery override)
750
804
  const mcpPath = join(cwd, ".mcp.json");
751
805
  const mcpConfig = {
752
806
  mcpServers: {
753
807
  patchcord: {
754
808
  type: "http",
755
- url: `${serverUrl}/mcp`,
809
+ url: `${serverUrl}/mcp/bearer`,
756
810
  headers: {
757
811
  Authorization: `Bearer ${token}`,
758
812
  "X-Patchcord-Machine": hostname,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.3.70",
3
+ "version": "0.3.72",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",