patchcord 0.3.46 → 0.3.47
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/bin/patchcord.mjs +37 -18
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -24,6 +24,18 @@ function isSafeToken(t) {
|
|
|
24
24
|
return /^[A-Za-z0-9_\-=+/.]+$/.test(t) && t.length < 200;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function isSafeUrl(u) {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = new URL(u);
|
|
30
|
+
return parsed.protocol === "https:" || parsed.protocol === "http:";
|
|
31
|
+
} catch { return false; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isSafeId(s) {
|
|
35
|
+
return /^[A-Za-z0-9_\-]+$/.test(s) && s.length < 100;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
27
39
|
if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
28
40
|
console.log(`patchcord — agent messaging for AI coding agents
|
|
29
41
|
|
|
@@ -47,6 +59,16 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
47
59
|
const fullStatusline = flags.includes("--full");
|
|
48
60
|
const { readFileSync, writeFileSync } = await import("fs");
|
|
49
61
|
|
|
62
|
+
function safeReadJson(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
let content = readFileSync(filePath, "utf-8");
|
|
65
|
+
// Strip JSONC comments (Zed, Gemini use JSONC)
|
|
66
|
+
content = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
67
|
+
content = content.replace(/,\s*([}\]])/g, "$1");
|
|
68
|
+
return JSON.parse(content);
|
|
69
|
+
} catch { return null; }
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
console.log(`
|
|
51
73
|
___ ____ ___ ____ _ _ ____ ____ ____ ___
|
|
52
74
|
|__] |__| | | |__| | | | |__/ | \\
|
|
@@ -341,7 +363,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
341
363
|
const geminiPath = join(HOME, ".gemini", "settings.json");
|
|
342
364
|
if (existsSync(geminiPath)) {
|
|
343
365
|
try {
|
|
344
|
-
const existing =
|
|
366
|
+
const existing = safeReadJson(geminiPath) || {};
|
|
345
367
|
if (existing.mcpServers?.patchcord) {
|
|
346
368
|
console.log(`\n ${yellow}⚠ Gemini CLI already configured${r}`);
|
|
347
369
|
console.log(` ${dim}${geminiPath}${r}`);
|
|
@@ -377,7 +399,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
377
399
|
: join(HOME, ".config", "zed", "settings.json");
|
|
378
400
|
if (existsSync(zedPath)) {
|
|
379
401
|
try {
|
|
380
|
-
const existing =
|
|
402
|
+
const existing = safeReadJson(zedPath) || {};
|
|
381
403
|
if (existing.context_servers?.patchcord) {
|
|
382
404
|
console.log(`\n ${yellow}⚠ Zed already configured${r}`);
|
|
383
405
|
console.log(` ${dim}${zedPath}${r}`);
|
|
@@ -418,7 +440,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
418
440
|
console.log(` ${yellow}This overrides per-project config and causes conflicts.${r}`);
|
|
419
441
|
const cleanGlobal = (await ask(` ${dim}Remove patchcord from global config? (Y/n):${r} `)).trim().toLowerCase();
|
|
420
442
|
if (cleanGlobal !== "n" && cleanGlobal !== "no") {
|
|
421
|
-
const cleaned = globalContent.replace(/\[mcp_servers\.patchcord\][^\
|
|
443
|
+
const cleaned = globalContent.replace(/\[mcp_servers\.patchcord\]\n(?:(?!\[)[^\n]*\n?)*/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
422
444
|
writeFileSync(globalCodexConfig, cleaned + "\n");
|
|
423
445
|
console.log(` ${green}✓${r} Removed from global config`);
|
|
424
446
|
}
|
|
@@ -484,7 +506,14 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
484
506
|
const customUrl = (await ask(`\n${dim}Custom server URL? (y/N):${r} `)).trim().toLowerCase();
|
|
485
507
|
if (customUrl === "y" || customUrl === "yes") {
|
|
486
508
|
const url = (await ask("Server URL: ")).trim();
|
|
487
|
-
if (url)
|
|
509
|
+
if (url) {
|
|
510
|
+
if (!isSafeUrl(url)) {
|
|
511
|
+
console.error("Invalid URL. Must start with https:// or http://");
|
|
512
|
+
rl.close();
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
serverUrl = url;
|
|
516
|
+
}
|
|
488
517
|
}
|
|
489
518
|
|
|
490
519
|
rl.close();
|
|
@@ -564,12 +593,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
564
593
|
} else if (isGemini) {
|
|
565
594
|
// Gemini CLI: global only (~/.gemini/settings.json)
|
|
566
595
|
const geminiPath = join(HOME, ".gemini", "settings.json");
|
|
567
|
-
let geminiSettings = {};
|
|
568
|
-
if (existsSync(geminiPath)) {
|
|
569
|
-
try {
|
|
570
|
-
geminiSettings = JSON.parse(readFileSync(geminiPath, "utf-8"));
|
|
571
|
-
} catch {}
|
|
572
|
-
}
|
|
596
|
+
let geminiSettings = (existsSync(geminiPath) && safeReadJson(geminiPath)) || {};
|
|
573
597
|
if (!geminiSettings.mcpServers) geminiSettings.mcpServers = {};
|
|
574
598
|
geminiSettings.mcpServers.patchcord = {
|
|
575
599
|
httpUrl: `${serverUrl}/mcp`,
|
|
@@ -592,12 +616,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
592
616
|
const zedPath = process.platform === "darwin"
|
|
593
617
|
? join(HOME, "Library", "Application Support", "Zed", "settings.json")
|
|
594
618
|
: join(HOME, ".config", "zed", "settings.json");
|
|
595
|
-
let zedSettings = {};
|
|
596
|
-
if (existsSync(zedPath)) {
|
|
597
|
-
try {
|
|
598
|
-
zedSettings = JSON.parse(readFileSync(zedPath, "utf-8"));
|
|
599
|
-
} catch {}
|
|
600
|
-
}
|
|
619
|
+
let zedSettings = (existsSync(zedPath) && safeReadJson(zedPath)) || {};
|
|
601
620
|
if (!zedSettings.context_servers) zedSettings.context_servers = {};
|
|
602
621
|
zedSettings.context_servers.patchcord = {
|
|
603
622
|
url: `${serverUrl}/mcp`,
|
|
@@ -676,7 +695,7 @@ if (!cmd || cmd === "install" || cmd === "agent") {
|
|
|
676
695
|
const configPath = join(codexDir, "config.toml");
|
|
677
696
|
let existing = existsSync(configPath) ? readFileSync(configPath, "utf-8") : "";
|
|
678
697
|
// Remove old patchcord config block if present
|
|
679
|
-
existing = existing.replace(/\[mcp_servers\.patchcord\][^\
|
|
698
|
+
existing = existing.replace(/\[mcp_servers\.patchcord\]\n(?:(?!\[)[^\n]*\n?)*/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
680
699
|
existing = existing.trimEnd() + `\n\n[mcp_servers.patchcord]\nurl = "${serverUrl}/mcp/bearer"\nhttp_headers = { "Authorization" = "Bearer ${token}", "X-Patchcord-Machine" = "${hostname}" }\n`;
|
|
681
700
|
writeFileSync(configPath, existing);
|
|
682
701
|
// Clean up any PATCHCORD_TOKEN we previously wrote to .env
|
|
@@ -799,7 +818,7 @@ if (cmd === "skill") {
|
|
|
799
818
|
}
|
|
800
819
|
} catch {}
|
|
801
820
|
|
|
802
|
-
if (!namespace || !agentId) {
|
|
821
|
+
if (!namespace || !agentId || !isSafeId(namespace) || !isSafeId(agentId)) {
|
|
803
822
|
console.error("Cannot determine agent identity. Check your token.");
|
|
804
823
|
process.exit(1);
|
|
805
824
|
}
|