patchcord 0.5.31 → 0.5.32
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 +89 -26
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -67,7 +67,7 @@ Usage:
|
|
|
67
67
|
npx patchcord@latest --token <token> Self-hosted / CI setup
|
|
68
68
|
npx patchcord@latest --token <token> --server <url> Self-hosted with custom server
|
|
69
69
|
npx patchcord@latest --full Same + full statusline
|
|
70
|
-
npx patchcord@latest --rename <new-name>
|
|
70
|
+
npx patchcord@latest --rename <new-name> [--tool <slug>] Rename this agent (paste from dashboard)
|
|
71
71
|
npx patchcord@latest skill apply Fetch custom skill from web console`);
|
|
72
72
|
process.exit(0);
|
|
73
73
|
}
|
|
@@ -77,47 +77,110 @@ if (cmd === "plugin-path") {
|
|
|
77
77
|
process.exit(0);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// ── --rename <new> [--expect-token <prefix>]
|
|
81
|
-
// Renames the agent in
|
|
82
|
-
//
|
|
80
|
+
// ── --rename <new> [--tool <slug>] [--expect-token <prefix>] ────
|
|
81
|
+
// Renames the agent's bearer in its tool's per-project config. Supported
|
|
82
|
+
// tools: claude_code, codex, cursor, vscode, opencode. Dashboard generates
|
|
83
|
+
// the full pasted command including --tool (so we read the right file)
|
|
84
|
+
// and --expect-token (sha256 prefix, prevents wrong-machine paste).
|
|
83
85
|
{
|
|
84
86
|
const renameIdx = process.argv.indexOf("--rename");
|
|
85
87
|
if (renameIdx !== -1) {
|
|
86
88
|
const newName = (process.argv[renameIdx + 1] || "").trim().toLowerCase();
|
|
87
89
|
const expectIdx = process.argv.indexOf("--expect-token");
|
|
88
90
|
const expectPrefix = expectIdx !== -1 ? (process.argv[expectIdx + 1] || "").trim() : "";
|
|
91
|
+
const toolIdx = process.argv.indexOf("--tool");
|
|
92
|
+
const tool = toolIdx !== -1 ? (process.argv[toolIdx + 1] || "").trim().toLowerCase() : "";
|
|
89
93
|
|
|
90
94
|
if (!newName || !/^[a-z][a-z0-9-]{1,49}$/.test(newName)) {
|
|
91
|
-
console.error("Usage: npx patchcord@latest --rename <new-name> [--expect-token <prefix>]");
|
|
92
|
-
console.error(" <new-name
|
|
95
|
+
console.error("Usage: npx patchcord@latest --rename <new-name> [--tool <slug>] [--expect-token <prefix>]");
|
|
96
|
+
console.error(" <new-name>: lowercase letters, digits, dashes; 2-50 chars");
|
|
97
|
+
console.error(" <slug>: claude_code | codex | cursor | vscode | opencode");
|
|
93
98
|
process.exit(1);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
const { readFileSync } = await import("fs");
|
|
102
|
+
|
|
103
|
+
// Per-tool token readers. Each returns {token, baseUrl, configFile} or null.
|
|
104
|
+
const readMcpJsonShape = (path, key = "mcpServers") => {
|
|
105
|
+
if (!existsSync(path)) return null;
|
|
106
|
+
try {
|
|
107
|
+
const obj = JSON.parse(readFileSync(path, "utf-8"));
|
|
108
|
+
const pt = obj?.[key]?.patchcord;
|
|
109
|
+
if (!pt?.headers?.Authorization || !pt?.url) return null;
|
|
110
|
+
return {
|
|
111
|
+
token: pt.headers.Authorization.replace(/^Bearer\s+/i, ""),
|
|
112
|
+
baseUrl: pt.url.replace(/\/mcp(\/bearer)?$/, ""),
|
|
113
|
+
configFile: path,
|
|
114
|
+
};
|
|
115
|
+
} catch { return null; }
|
|
116
|
+
};
|
|
117
|
+
const readOpenCodeShape = (path) => {
|
|
118
|
+
if (!existsSync(path)) return null;
|
|
119
|
+
try {
|
|
120
|
+
const obj = JSON.parse(readFileSync(path, "utf-8"));
|
|
121
|
+
const pt = obj?.mcp?.patchcord;
|
|
122
|
+
if (!pt?.headers?.Authorization || !pt?.url) return null;
|
|
123
|
+
return {
|
|
124
|
+
token: pt.headers.Authorization.replace(/^Bearer\s+/i, ""),
|
|
125
|
+
baseUrl: pt.url.replace(/\/mcp(\/bearer)?$/, ""),
|
|
126
|
+
configFile: path,
|
|
127
|
+
};
|
|
128
|
+
} catch { return null; }
|
|
129
|
+
};
|
|
130
|
+
const readCodexTomlShape = (path) => {
|
|
131
|
+
if (!existsSync(path)) return null;
|
|
132
|
+
try {
|
|
133
|
+
const content = readFileSync(path, "utf-8");
|
|
134
|
+
const block = content.match(/\[mcp_servers\.patchcord[-\w]*\]([\s\S]*?)(?=\n\[|$)/);
|
|
135
|
+
if (!block) return null;
|
|
136
|
+
const urlMatch = block[1].match(/url\s*=\s*"([^"]+)"/);
|
|
137
|
+
const tokenMatch = block[1].match(/Bearer\s+([^\s"]+)/);
|
|
138
|
+
if (!urlMatch || !tokenMatch) return null;
|
|
139
|
+
return {
|
|
140
|
+
token: tokenMatch[1],
|
|
141
|
+
baseUrl: urlMatch[1].replace(/\/mcp(\/bearer)?$/, ""),
|
|
142
|
+
configFile: path,
|
|
143
|
+
};
|
|
144
|
+
} catch { return null; }
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const READERS = {
|
|
148
|
+
claude_code: (cwd) => readMcpJsonShape(join(cwd, ".mcp.json")),
|
|
149
|
+
cursor: (cwd) => readMcpJsonShape(join(cwd, ".cursor", "mcp.json")),
|
|
150
|
+
vscode: (cwd) => readMcpJsonShape(join(cwd, ".vscode", "mcp.json"), "servers"),
|
|
151
|
+
opencode: (cwd) => readOpenCodeShape(join(cwd, "opencode.json")),
|
|
152
|
+
codex: (cwd) => readCodexTomlShape(join(cwd, ".codex", "config.toml")),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (tool && !READERS[tool]) {
|
|
156
|
+
console.error(`✗ Rename not supported for --tool=${tool}.`);
|
|
157
|
+
console.error(` Supported: ${Object.keys(READERS).join(", ")}.`);
|
|
158
|
+
console.error(` This usually means the tool stores its config in a global location`);
|
|
159
|
+
console.error(` not yet wired up to --rename. Open an issue.`);
|
|
106
160
|
process.exit(1);
|
|
107
161
|
}
|
|
108
162
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
163
|
+
// Walk up from cwd looking for any supported config. If --tool given,
|
|
164
|
+
// only check that tool; otherwise try all in priority order.
|
|
165
|
+
const cwd = process.cwd();
|
|
166
|
+
const tryReaders = tool ? [READERS[tool]] : Object.values(READERS);
|
|
167
|
+
let found = null;
|
|
168
|
+
let dir = cwd;
|
|
169
|
+
while (dir && dir !== "/" && !found) {
|
|
170
|
+
for (const r of tryReaders) {
|
|
171
|
+
const result = r(dir);
|
|
172
|
+
if (result) { found = result; break; }
|
|
173
|
+
}
|
|
174
|
+
if (!found) dir = dirname(dir);
|
|
175
|
+
}
|
|
176
|
+
if (!found) {
|
|
177
|
+
const whichFile = tool
|
|
178
|
+
? `the ${tool} config file in or above ${cwd}`
|
|
179
|
+
: `any supported tool config (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, opencode.json, .codex/config.toml) above ${cwd}`;
|
|
180
|
+
console.error(`No patchcord bearer found in ${whichFile}. Run from the agent's project folder.`);
|
|
115
181
|
process.exit(1);
|
|
116
182
|
}
|
|
117
|
-
const
|
|
118
|
-
const auth = config?.mcpServers?.patchcord?.headers?.Authorization || "";
|
|
119
|
-
const baseUrl = mcpUrl.replace(/\/mcp(\/bearer)?$/, "");
|
|
120
|
-
const token = auth.replace(/^Bearer\s+/, "");
|
|
183
|
+
const { token, baseUrl, configFile: mcpJson } = found;
|
|
121
184
|
|
|
122
185
|
if (!baseUrl || !token || !isSafeToken(token) || !isSafeUrl(baseUrl)) {
|
|
123
186
|
console.error(`Cannot read patchcord URL/token from ${mcpJson}.`);
|