claude-relay 2.2.3 → 2.2.4
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/cli.js +136 -18
- package/lib/config.js +8 -2
- package/lib/daemon.js +4 -0
- package/lib/ipc.js +7 -3
- package/lib/project.js +1 -1
- package/lib/sdk-bridge.js +2 -2
- package/lib/terminal.js +2 -1
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,6 +10,17 @@ var { loadConfig, saveConfig, configPath, socketPath, logPath, ensureConfigDir,
|
|
|
10
10
|
var { sendIPCCommand } = require("../lib/ipc");
|
|
11
11
|
var { generateAuthToken } = require("../lib/server");
|
|
12
12
|
|
|
13
|
+
function openUrl(url) {
|
|
14
|
+
try {
|
|
15
|
+
if (process.platform === "win32") {
|
|
16
|
+
spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true, windowsHide: true }).unref();
|
|
17
|
+
} else {
|
|
18
|
+
var cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
19
|
+
spawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
20
|
+
}
|
|
21
|
+
} catch (e) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
var args = process.argv.slice(2);
|
|
14
25
|
var port = 2633;
|
|
15
26
|
var useHttps = true;
|
|
@@ -388,10 +399,8 @@ function ensureCerts(ip) {
|
|
|
388
399
|
}
|
|
389
400
|
|
|
390
401
|
try {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
{ stdio: "pipe" }
|
|
394
|
-
);
|
|
402
|
+
var mkcertArgs = ["-key-file", keyPath, "-cert-file", certPath].concat(domains);
|
|
403
|
+
execFileSync("mkcert", mkcertArgs, { stdio: "pipe" });
|
|
395
404
|
} catch (err) {
|
|
396
405
|
return null;
|
|
397
406
|
}
|
|
@@ -1026,9 +1035,13 @@ function setup(callback) {
|
|
|
1026
1035
|
log(sym.bar);
|
|
1027
1036
|
|
|
1028
1037
|
promptPin(function (pin) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1038
|
+
if (process.platform === "darwin") {
|
|
1039
|
+
promptToggle("Keep awake", "Prevent system sleep while relay is running", false, function (keepAwake) {
|
|
1040
|
+
callback(pin, keepAwake);
|
|
1041
|
+
});
|
|
1042
|
+
} else {
|
|
1043
|
+
callback(pin, false);
|
|
1044
|
+
}
|
|
1032
1045
|
});
|
|
1033
1046
|
});
|
|
1034
1047
|
});
|
|
@@ -1098,6 +1111,7 @@ async function forkDaemon(pin, keepAwake, extraProjects) {
|
|
|
1098
1111
|
|
|
1099
1112
|
var child = spawn(process.execPath, [daemonScript], {
|
|
1100
1113
|
detached: true,
|
|
1114
|
+
windowsHide: true,
|
|
1101
1115
|
stdio: ["ignore", logFd, logFd],
|
|
1102
1116
|
env: Object.assign({}, process.env, {
|
|
1103
1117
|
CLAUDE_RELAY_CONFIG: configPath(),
|
|
@@ -1127,6 +1141,96 @@ async function forkDaemon(pin, keepAwake, extraProjects) {
|
|
|
1127
1141
|
showServerStarted(config, ip);
|
|
1128
1142
|
}
|
|
1129
1143
|
|
|
1144
|
+
// ==============================
|
|
1145
|
+
// Restart daemon with TLS enabled
|
|
1146
|
+
// ==============================
|
|
1147
|
+
async function restartDaemonWithTLS(config, callback) {
|
|
1148
|
+
var ip = getLocalIP();
|
|
1149
|
+
var certPaths = ensureCerts(ip);
|
|
1150
|
+
if (!certPaths) {
|
|
1151
|
+
callback(config);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Shut down old daemon
|
|
1156
|
+
stopDaemonWatcher();
|
|
1157
|
+
try {
|
|
1158
|
+
await sendIPCCommand(socketPath(), { cmd: "shutdown" });
|
|
1159
|
+
} catch (e) {}
|
|
1160
|
+
|
|
1161
|
+
// Wait for port to be released
|
|
1162
|
+
var waited = 0;
|
|
1163
|
+
while (waited < 5000) {
|
|
1164
|
+
await new Promise(function (resolve) { setTimeout(resolve, 300); });
|
|
1165
|
+
waited += 300;
|
|
1166
|
+
var free = await isPortFree(config.port);
|
|
1167
|
+
if (free) break;
|
|
1168
|
+
}
|
|
1169
|
+
clearStaleConfig();
|
|
1170
|
+
|
|
1171
|
+
// Re-fork with TLS
|
|
1172
|
+
var newConfig = {
|
|
1173
|
+
pid: null,
|
|
1174
|
+
port: config.port,
|
|
1175
|
+
pinHash: config.pinHash || null,
|
|
1176
|
+
tls: true,
|
|
1177
|
+
debug: config.debug || false,
|
|
1178
|
+
keepAwake: config.keepAwake || false,
|
|
1179
|
+
projects: config.projects || [],
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
ensureConfigDir();
|
|
1183
|
+
saveConfig(newConfig);
|
|
1184
|
+
|
|
1185
|
+
var daemonScript = path.join(__dirname, "..", "lib", "daemon.js");
|
|
1186
|
+
var logFile = logPath();
|
|
1187
|
+
var logFd = fs.openSync(logFile, "a");
|
|
1188
|
+
|
|
1189
|
+
var child = spawn(process.execPath, [daemonScript], {
|
|
1190
|
+
detached: true,
|
|
1191
|
+
windowsHide: true,
|
|
1192
|
+
stdio: ["ignore", logFd, logFd],
|
|
1193
|
+
env: Object.assign({}, process.env, {
|
|
1194
|
+
CLAUDE_RELAY_CONFIG: configPath(),
|
|
1195
|
+
}),
|
|
1196
|
+
});
|
|
1197
|
+
child.unref();
|
|
1198
|
+
fs.closeSync(logFd);
|
|
1199
|
+
|
|
1200
|
+
newConfig.pid = child.pid;
|
|
1201
|
+
saveConfig(newConfig);
|
|
1202
|
+
|
|
1203
|
+
await new Promise(function (resolve) { setTimeout(resolve, 800); });
|
|
1204
|
+
|
|
1205
|
+
var alive = await isDaemonAliveAsync(newConfig);
|
|
1206
|
+
if (!alive) {
|
|
1207
|
+
log(sym.warn + " " + a.yellow + "Failed to restart with HTTPS, falling back to HTTP..." + a.reset);
|
|
1208
|
+
// Re-fork without TLS so the server is at least running
|
|
1209
|
+
newConfig.tls = false;
|
|
1210
|
+
saveConfig(newConfig);
|
|
1211
|
+
var logFd2 = fs.openSync(logFile, "a");
|
|
1212
|
+
var child2 = spawn(process.execPath, [daemonScript], {
|
|
1213
|
+
detached: true,
|
|
1214
|
+
windowsHide: true,
|
|
1215
|
+
stdio: ["ignore", logFd2, logFd2],
|
|
1216
|
+
env: Object.assign({}, process.env, {
|
|
1217
|
+
CLAUDE_RELAY_CONFIG: configPath(),
|
|
1218
|
+
}),
|
|
1219
|
+
});
|
|
1220
|
+
child2.unref();
|
|
1221
|
+
fs.closeSync(logFd2);
|
|
1222
|
+
newConfig.pid = child2.pid;
|
|
1223
|
+
saveConfig(newConfig);
|
|
1224
|
+
await new Promise(function (resolve) { setTimeout(resolve, 800); });
|
|
1225
|
+
startDaemonWatcher();
|
|
1226
|
+
callback(newConfig);
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
startDaemonWatcher();
|
|
1231
|
+
callback(newConfig);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1130
1234
|
// ==============================
|
|
1131
1235
|
// Show server started info
|
|
1132
1236
|
// ==============================
|
|
@@ -1196,6 +1300,7 @@ function showMainMenu(config, ip) {
|
|
|
1196
1300
|
switch (choice) {
|
|
1197
1301
|
case "notifications":
|
|
1198
1302
|
showSetupGuide(config, ip, function () {
|
|
1303
|
+
config = loadConfig() || config;
|
|
1199
1304
|
showMainMenu(config, ip);
|
|
1200
1305
|
});
|
|
1201
1306
|
break;
|
|
@@ -1246,17 +1351,11 @@ function showMainMenu(config, ip) {
|
|
|
1246
1351
|
],
|
|
1247
1352
|
keys: [
|
|
1248
1353
|
{ key: "o", onKey: function () {
|
|
1249
|
-
|
|
1250
|
-
var openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
1251
|
-
spawn(openCmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
1252
|
-
} catch (e) {}
|
|
1354
|
+
openUrl(url);
|
|
1253
1355
|
showMainMenu(config, ip);
|
|
1254
1356
|
}},
|
|
1255
1357
|
{ key: "s", onKey: function () {
|
|
1256
|
-
|
|
1257
|
-
var openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
1258
|
-
spawn(openCmd, ["https://github.com/chadbyte/claude-relay"], { stdio: "ignore", detached: true }).unref();
|
|
1259
|
-
} catch (e) {}
|
|
1358
|
+
openUrl("https://github.com/chadbyte/claude-relay");
|
|
1260
1359
|
showMainMenu(config, ip);
|
|
1261
1360
|
}},
|
|
1262
1361
|
],
|
|
@@ -1562,11 +1661,25 @@ function showSetupGuide(config, ip, goBack) {
|
|
|
1562
1661
|
log(sym.pointer + " " + a.bold + "HTTPS Setup (for push notifications)" + a.reset);
|
|
1563
1662
|
if (mcReady) {
|
|
1564
1663
|
log(sym.bar + " " + a.green + "mkcert is installed" + a.reset);
|
|
1664
|
+
if (!config.tls) {
|
|
1665
|
+
log(sym.bar + " " + a.dim + "Restarting server with HTTPS..." + a.reset);
|
|
1666
|
+
restartDaemonWithTLS(config, function (newConfig) {
|
|
1667
|
+
config = newConfig;
|
|
1668
|
+
log(sym.bar);
|
|
1669
|
+
showSetupQR();
|
|
1670
|
+
});
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1565
1673
|
log(sym.bar);
|
|
1566
1674
|
showSetupQR();
|
|
1567
1675
|
} else {
|
|
1568
1676
|
log(sym.bar + " " + a.yellow + "mkcert not found." + a.reset);
|
|
1569
|
-
|
|
1677
|
+
var mkcertHint = process.platform === "win32"
|
|
1678
|
+
? "choco install mkcert && mkcert -install"
|
|
1679
|
+
: process.platform === "darwin"
|
|
1680
|
+
? "brew install mkcert && mkcert -install"
|
|
1681
|
+
: "apt install mkcert && mkcert -install";
|
|
1682
|
+
log(sym.bar + " " + a.dim + "Install: " + a.reset + mkcertHint);
|
|
1570
1683
|
log(sym.bar);
|
|
1571
1684
|
promptSelect("Select", [
|
|
1572
1685
|
{ label: "Re-check", value: "recheck" },
|
|
@@ -1658,7 +1771,9 @@ function showSettingsMenu(config, ip) {
|
|
|
1658
1771
|
log(sym.bar + " mkcert " + mcStatus);
|
|
1659
1772
|
log(sym.bar + " HTTPS " + tlsStatus);
|
|
1660
1773
|
log(sym.bar + " PIN " + pinStatus);
|
|
1661
|
-
|
|
1774
|
+
if (process.platform === "darwin") {
|
|
1775
|
+
log(sym.bar + " Keep awake " + awakeStatus);
|
|
1776
|
+
}
|
|
1662
1777
|
log(sym.bar);
|
|
1663
1778
|
|
|
1664
1779
|
// Build items
|
|
@@ -1672,7 +1787,9 @@ function showSettingsMenu(config, ip) {
|
|
|
1672
1787
|
} else {
|
|
1673
1788
|
items.push({ label: "Set PIN", value: "pin" });
|
|
1674
1789
|
}
|
|
1675
|
-
|
|
1790
|
+
if (process.platform === "darwin") {
|
|
1791
|
+
items.push({ label: isAwake ? "Disable keep awake" : "Enable keep awake", value: "awake" });
|
|
1792
|
+
}
|
|
1676
1793
|
items.push({ label: "View logs", value: "logs" });
|
|
1677
1794
|
items.push({ label: "Back", value: "back" });
|
|
1678
1795
|
|
|
@@ -1680,6 +1797,7 @@ function showSettingsMenu(config, ip) {
|
|
|
1680
1797
|
switch (choice) {
|
|
1681
1798
|
case "guide":
|
|
1682
1799
|
showSetupGuide(config, ip, function () {
|
|
1800
|
+
config = loadConfig() || config;
|
|
1683
1801
|
showSettingsMenu(config, ip);
|
|
1684
1802
|
});
|
|
1685
1803
|
break;
|
package/lib/config.js
CHANGED
|
@@ -11,6 +11,9 @@ function configPath() {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function socketPath() {
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
return "\\\\.\\pipe\\claude-relay-daemon";
|
|
16
|
+
}
|
|
14
17
|
return path.join(CONFIG_DIR, "daemon.sock");
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -50,7 +53,8 @@ function isPidAlive(pid) {
|
|
|
50
53
|
function isDaemonAlive(config) {
|
|
51
54
|
if (!config || !config.pid) return false;
|
|
52
55
|
if (!isPidAlive(config.pid)) return false;
|
|
53
|
-
//
|
|
56
|
+
// Named pipes on Windows can't be stat'd, just check PID
|
|
57
|
+
if (process.platform === "win32") return true;
|
|
54
58
|
try {
|
|
55
59
|
fs.statSync(socketPath());
|
|
56
60
|
return true;
|
|
@@ -96,7 +100,9 @@ function generateSlug(projectPath, existingSlugs) {
|
|
|
96
100
|
|
|
97
101
|
function clearStaleConfig() {
|
|
98
102
|
try { fs.unlinkSync(configPath()); } catch (e) {}
|
|
99
|
-
|
|
103
|
+
if (process.platform !== "win32") {
|
|
104
|
+
try { fs.unlinkSync(socketPath()); } catch (e) {}
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
// --- ~/.clayrc (recent projects persistence) ---
|
package/lib/daemon.js
CHANGED
|
@@ -251,6 +251,10 @@ function gracefulShutdown() {
|
|
|
251
251
|
|
|
252
252
|
process.on("SIGTERM", gracefulShutdown);
|
|
253
253
|
process.on("SIGINT", gracefulShutdown);
|
|
254
|
+
// Windows emits SIGHUP when console window closes
|
|
255
|
+
if (process.platform === "win32") {
|
|
256
|
+
process.on("SIGHUP", gracefulShutdown);
|
|
257
|
+
}
|
|
254
258
|
|
|
255
259
|
process.on("uncaughtException", function (err) {
|
|
256
260
|
console.error("[daemon] Uncaught exception:", err);
|
package/lib/ipc.js
CHANGED
|
@@ -6,8 +6,10 @@ var fs = require("fs");
|
|
|
6
6
|
* handler(msg) should return a response object (or a Promise of one).
|
|
7
7
|
*/
|
|
8
8
|
function createIPCServer(sockPath, handler) {
|
|
9
|
-
// Remove stale socket file
|
|
10
|
-
|
|
9
|
+
// Remove stale socket file (not needed for Windows named pipes)
|
|
10
|
+
if (process.platform !== "win32") {
|
|
11
|
+
try { fs.unlinkSync(sockPath); } catch (e) {}
|
|
12
|
+
}
|
|
11
13
|
|
|
12
14
|
var server = net.createServer(function (conn) {
|
|
13
15
|
var buffer = "";
|
|
@@ -49,7 +51,9 @@ function createIPCServer(sockPath, handler) {
|
|
|
49
51
|
return {
|
|
50
52
|
close: function () {
|
|
51
53
|
server.close();
|
|
52
|
-
|
|
54
|
+
if (process.platform !== "win32") {
|
|
55
|
+
try { fs.unlinkSync(sockPath); } catch (e) {}
|
|
56
|
+
}
|
|
53
57
|
},
|
|
54
58
|
};
|
|
55
59
|
}
|
package/lib/project.js
CHANGED
|
@@ -478,7 +478,7 @@ function createProjectContext(opts) {
|
|
|
478
478
|
entries.push({
|
|
479
479
|
name: item.name,
|
|
480
480
|
type: item.isDirectory() ? "dir" : "file",
|
|
481
|
-
path: path.relative(cwd, path.join(fsDir, item.name)),
|
|
481
|
+
path: path.relative(cwd, path.join(fsDir, item.name)).split(path.sep).join("/"),
|
|
482
482
|
});
|
|
483
483
|
}
|
|
484
484
|
sendTo(ws, { type: "fs_list_result", path: msg.path || ".", entries: entries });
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -466,7 +466,7 @@ function createSDKBridge(opts) {
|
|
|
466
466
|
|
|
467
467
|
function permissionPushTitle(toolName, input) {
|
|
468
468
|
if (!input) return "Claude wants to use " + toolName;
|
|
469
|
-
var file = input.file_path ? input.file_path.split(
|
|
469
|
+
var file = input.file_path ? input.file_path.split(/[/\\]/).pop() : "";
|
|
470
470
|
switch (toolName) {
|
|
471
471
|
case "Bash": return "Claude wants to run a command";
|
|
472
472
|
case "Edit": return "Claude wants to edit " + (file || "a file");
|
|
@@ -487,7 +487,7 @@ function createSDKBridge(opts) {
|
|
|
487
487
|
if (toolName === "Bash" && input.command) {
|
|
488
488
|
text = input.command;
|
|
489
489
|
} else if (toolName === "Edit" && input.file_path) {
|
|
490
|
-
text = input.file_path.split(
|
|
490
|
+
text = input.file_path.split(/[/\\]/).pop() + ": " + (input.old_string || "").substring(0, 40) + " \u2192 " + (input.new_string || "").substring(0, 40);
|
|
491
491
|
} else if (toolName === "Write" && input.file_path) {
|
|
492
492
|
text = input.file_path;
|
|
493
493
|
} else if (input.file_path) {
|
package/lib/terminal.js
CHANGED
|
@@ -8,7 +8,8 @@ try {
|
|
|
8
8
|
function createTerminal(cwd, cols, rows) {
|
|
9
9
|
if (!pty) return null;
|
|
10
10
|
|
|
11
|
-
var shell = process.env.SHELL
|
|
11
|
+
var shell = process.env.SHELL
|
|
12
|
+
|| (process.platform === "win32" ? process.env.COMSPEC || "cmd.exe" : "/bin/bash");
|
|
12
13
|
var term = pty.spawn(shell, [], {
|
|
13
14
|
name: "xterm-256color",
|
|
14
15
|
cols: cols || 80,
|