clay-server 2.20.0 → 2.20.1-beta.1
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/README.md +1 -1
- package/bin/cli.js +91 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center"><img src="media/hero.png" alt="Clay workspace" /></p>
|
|
10
10
|
|
|
11
|
-
AI teammates who remember your decisions, challenge your thinking, and grow with your codebase. Runs on your machine.
|
|
11
|
+
A team workspace built on Claude Code. AI teammates who remember your decisions, challenge your thinking, and grow with your codebase. Runs on your machine.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
npx clay-server
|
package/bin/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ var crypto = require("crypto");
|
|
|
36
36
|
var { loadConfig, saveConfig, configPath, socketPath, logPath, ensureConfigDir, isDaemonAlive, isDaemonAliveAsync, generateSlug, clearStaleConfig, loadClayrc, saveClayrc, readCrashInfo, REAL_HOME } = require("../lib/config");
|
|
37
37
|
var { sendIPCCommand } = require("../lib/ipc");
|
|
38
38
|
var { generateAuthToken } = require("../lib/server");
|
|
39
|
-
var { enableMultiUser, disableMultiUser, hasAdmin, isMultiUser } = require("../lib/users");
|
|
39
|
+
var { enableMultiUser, disableMultiUser, hasAdmin, isMultiUser, getSetupCode } = require("../lib/users");
|
|
40
40
|
|
|
41
41
|
function openUrl(url) {
|
|
42
42
|
try {
|
|
@@ -1417,16 +1417,15 @@ function setup(callback) {
|
|
|
1417
1417
|
}
|
|
1418
1418
|
var isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
1419
1419
|
if (!isRoot) {
|
|
1420
|
-
//
|
|
1421
|
-
var
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
saveConfig(partialConfig);
|
|
1420
|
+
// Merge into existing config (preserve projects, TLS, etc.)
|
|
1421
|
+
var existingCfg = loadConfig() || {};
|
|
1422
|
+
existingCfg.port = port;
|
|
1423
|
+
existingCfg.host = host;
|
|
1424
|
+
existingCfg.mode = "multi";
|
|
1425
|
+
existingCfg.osUsers = true;
|
|
1426
|
+
existingCfg.setupCompleted = true;
|
|
1427
|
+
if (dangerouslySkipPermissions) existingCfg.dangerouslySkipPermissions = true;
|
|
1428
|
+
saveConfig(existingCfg);
|
|
1430
1429
|
log(sym.bar);
|
|
1431
1430
|
log(sym.warn + " " + a.yellow + "OS user isolation requires root." + a.reset);
|
|
1432
1431
|
log(sym.bar + " Run:");
|
|
@@ -2009,8 +2008,14 @@ function showMainMenu(config, ip, setupCode) {
|
|
|
2009
2008
|
log("");
|
|
2010
2009
|
}
|
|
2011
2010
|
|
|
2012
|
-
if
|
|
2013
|
-
|
|
2011
|
+
// Always check for pending setup code if multi-user is on and no admin exists
|
|
2012
|
+
var displayCode = setupCode;
|
|
2013
|
+
if (!displayCode && isMultiUser() && !hasAdmin()) {
|
|
2014
|
+
var pendingCode = getSetupCode();
|
|
2015
|
+
if (pendingCode) displayCode = pendingCode;
|
|
2016
|
+
}
|
|
2017
|
+
if (displayCode) {
|
|
2018
|
+
log(" " + a.yellow + sym.warn + " Setup code: " + a.bold + displayCode + a.reset);
|
|
2014
2019
|
log(" " + a.dim + "Open Clay in your browser and enter this code to create the admin account." + a.reset);
|
|
2015
2020
|
log("");
|
|
2016
2021
|
}
|
|
@@ -2373,6 +2378,12 @@ function showSettingsMenu(config, ip) {
|
|
|
2373
2378
|
} else {
|
|
2374
2379
|
items.push({ label: "Enable multi-user mode", value: "multi_user" });
|
|
2375
2380
|
}
|
|
2381
|
+
if (muEnabled && !hasAdmin()) {
|
|
2382
|
+
var pendingSetupCode = getSetupCode();
|
|
2383
|
+
if (pendingSetupCode) {
|
|
2384
|
+
items.push({ label: "Show setup code", value: "show_setup_code" });
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2376
2387
|
if (muEnabled && hasAdmin()) {
|
|
2377
2388
|
items.push({ label: "Recover admin password", value: "recover_admin" });
|
|
2378
2389
|
}
|
|
@@ -2478,7 +2489,9 @@ function showSettingsMenu(config, ip) {
|
|
|
2478
2489
|
}
|
|
2479
2490
|
if (process.getuid() !== 0) {
|
|
2480
2491
|
log(sym.bar);
|
|
2481
|
-
log(sym.bar + " " + a.red + "
|
|
2492
|
+
log(sym.bar + " " + a.red + sym.warn + " OS user isolation requires root." + a.reset);
|
|
2493
|
+
log(sym.bar + " " + a.dim + "Shut down this server, then restart with:" + a.reset);
|
|
2494
|
+
log(sym.bar + " " + a.bold + "sudo npx clay-server" + a.reset);
|
|
2482
2495
|
log(sym.bar);
|
|
2483
2496
|
promptSelect("Back?", [{ label: "Back", value: "back" }], function () {
|
|
2484
2497
|
showSettingsMenu(config, ip);
|
|
@@ -2603,33 +2616,48 @@ function showSettingsMenu(config, ip) {
|
|
|
2603
2616
|
{ label: "Cancel", value: "cancel" },
|
|
2604
2617
|
], function (confirmChoice) {
|
|
2605
2618
|
if (confirmChoice === "confirm") {
|
|
2606
|
-
//
|
|
2619
|
+
// Save old PID before clearing, so we can force-kill if needed
|
|
2607
2620
|
var cfg = loadConfig() || {};
|
|
2621
|
+
var oldPid = cfg.pid;
|
|
2622
|
+
// Clear setupCompleted so setup() runs fresh
|
|
2608
2623
|
delete cfg.setupCompleted;
|
|
2609
2624
|
delete cfg.mode;
|
|
2610
2625
|
cfg.pid = null;
|
|
2611
2626
|
saveConfig(cfg);
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2627
|
+
|
|
2628
|
+
// Helper: wait for port to be free, force-kill if needed
|
|
2629
|
+
function waitForPortFree(cb) {
|
|
2630
|
+
var attempts = 0;
|
|
2631
|
+
var maxAttempts = 12; // 6 seconds total
|
|
2632
|
+
function check() {
|
|
2633
|
+
isPortFree(port).then(function (free) {
|
|
2634
|
+
if (free) return cb();
|
|
2635
|
+
attempts++;
|
|
2636
|
+
if (attempts >= maxAttempts) {
|
|
2637
|
+
// Port still busy, force-kill old daemon
|
|
2638
|
+
if (oldPid) {
|
|
2639
|
+
try { process.kill(oldPid, "SIGKILL"); } catch (e) {}
|
|
2640
|
+
}
|
|
2641
|
+
// Wait a bit more after SIGKILL
|
|
2642
|
+
setTimeout(function () {
|
|
2643
|
+
isPortFree(port).then(function (free2) {
|
|
2644
|
+
if (!free2) {
|
|
2645
|
+
log(sym.warn + " " + a.yellow + "Port " + port + " still in use. Kill the process manually:" + a.reset);
|
|
2646
|
+
log(sym.bar + " " + a.bold + "lsof -ti:" + port + " | xargs kill -9" + a.reset);
|
|
2647
|
+
}
|
|
2648
|
+
cb();
|
|
2649
|
+
});
|
|
2650
|
+
}, 1000);
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
setTimeout(check, 500);
|
|
2620
2654
|
});
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
log(sym.end + " " + a.dim + "Starting relay..." + a.reset);
|
|
2628
|
-
log("");
|
|
2629
|
-
forkDaemon(mode, keepAwake, undefined, true, wantOsUsers);
|
|
2630
|
-
}
|
|
2631
|
-
});
|
|
2632
|
-
}).catch(function () {
|
|
2655
|
+
}
|
|
2656
|
+
check();
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// Helper: run setup wizard after daemon is dead
|
|
2660
|
+
function proceedWithSetup() {
|
|
2633
2661
|
clearStaleConfig();
|
|
2634
2662
|
setup(function (mode, keepAwake, wantOsUsers) {
|
|
2635
2663
|
var rc = loadClayrc();
|
|
@@ -2647,6 +2675,17 @@ function showSettingsMenu(config, ip) {
|
|
|
2647
2675
|
forkDaemon(mode, keepAwake, undefined, true, wantOsUsers);
|
|
2648
2676
|
}
|
|
2649
2677
|
});
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// Shut down the daemon, then wait for port to be free
|
|
2681
|
+
sendIPCCommand(socketPath(), { cmd: "shutdown" }).then(function () {
|
|
2682
|
+
waitForPortFree(proceedWithSetup);
|
|
2683
|
+
}).catch(function () {
|
|
2684
|
+
// IPC failed, daemon may be unresponsive. Try SIGTERM, then wait.
|
|
2685
|
+
if (oldPid) {
|
|
2686
|
+
try { process.kill(oldPid, "SIGTERM"); } catch (e) {}
|
|
2687
|
+
}
|
|
2688
|
+
waitForPortFree(proceedWithSetup);
|
|
2650
2689
|
});
|
|
2651
2690
|
} else {
|
|
2652
2691
|
showSettingsMenu(config, ip);
|
|
@@ -2654,6 +2693,23 @@ function showSettingsMenu(config, ip) {
|
|
|
2654
2693
|
});
|
|
2655
2694
|
break;
|
|
2656
2695
|
|
|
2696
|
+
case "show_setup_code":
|
|
2697
|
+
var currentCode = getSetupCode();
|
|
2698
|
+
if (currentCode) {
|
|
2699
|
+
log(sym.bar);
|
|
2700
|
+
log(sym.bar + " " + a.yellow + sym.warn + " Setup code: " + a.bold + currentCode + a.reset);
|
|
2701
|
+
log(sym.bar + " " + a.dim + "Open Clay in your browser and enter this code to create the admin account." + a.reset);
|
|
2702
|
+
log(sym.bar);
|
|
2703
|
+
} else {
|
|
2704
|
+
log(sym.bar);
|
|
2705
|
+
log(sym.bar + " " + a.dim + "No pending setup code (admin already exists)." + a.reset);
|
|
2706
|
+
log(sym.bar);
|
|
2707
|
+
}
|
|
2708
|
+
promptSelect("Back?", [{ label: "Back", value: "back" }], function () {
|
|
2709
|
+
showSettingsMenu(config, ip);
|
|
2710
|
+
});
|
|
2711
|
+
break;
|
|
2712
|
+
|
|
2657
2713
|
case "logs":
|
|
2658
2714
|
console.clear();
|
|
2659
2715
|
log(a.bold + "Daemon logs" + a.reset + " " + a.dim + "(" + logPath() + ")" + a.reset);
|