clay-server 2.17.0-beta.1 → 2.17.0-beta.3
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 +20 -7
- package/bin/cli.js +58 -24
- package/lib/public/sw.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/clay-server) [](https://www.npmjs.com/package/clay-server) [](https://github.com/chadbyte/clay) [](https://github.com/chadbyte/clay/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
-
Clay gives Claude Code a browser UI that runs on any device. Use it from your phone, run it on macOS, Windows, or Linux. Invite teammates, manage multiple projects from one sidebar, and get push notifications when Claude needs you. Built on the official Claude Agent SDK, not a terminal parser. Your machine is the server. No cloud relay in between, no extra network surface.
|
|
11
|
+
Clay gives Claude Code a browser UI that runs on any device. Use it from your phone, run it on macOS, Windows, or Linux. Invite teammates, manage multiple projects from one sidebar, and get push notifications when Claude needs you. HTTPS and push notifications work out of the box with zero config. Built on the official Claude Agent SDK, not a terminal parser. Your machine is the server. No cloud relay in between, no extra network surface.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -98,7 +98,7 @@ Take it further with Ralph Loop, an autonomous coding loop built into Clay. The
|
|
|
98
98
|
|
|
99
99
|
Your data flows directly from your machine to the Anthropic API, exactly as it does when you use the CLI. Clay adds a browser layer on top, not a middleman.
|
|
100
100
|
|
|
101
|
-
PIN authentication
|
|
101
|
+
HTTPS is enabled by default with a builtin certificate. PIN authentication and per-project/session permissions are built in. For local network use, this is sufficient. For remote access, we recommend a VPN like Tailscale.
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
@@ -166,20 +166,32 @@ Yes. Create as many as you need. A code reviewer, a writing partner, a project m
|
|
|
166
166
|
|
|
167
167
|
---
|
|
168
168
|
|
|
169
|
-
## HTTPS
|
|
169
|
+
## HTTPS
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
HTTPS is enabled by default using a builtin wildcard certificate for `*.d.clay.studio`. No setup required. Available from `v2.17.0-beta.2`. Your browser connects to a URL like:
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
```
|
|
174
|
+
https://192-168-1-50.d.clay.studio:2633
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The domain resolves to your local IP. All traffic stays on your network. See [clay-dns](clay-dns/) for details on how this works.
|
|
178
|
+
|
|
179
|
+
Push notifications require HTTPS, so they work out of the box with this setup. Install Clay as a PWA on your device to receive them.
|
|
180
|
+
|
|
181
|
+
<details>
|
|
182
|
+
<summary><strong>Alternative: local certificate with mkcert</strong></summary>
|
|
183
|
+
|
|
184
|
+
If you prefer to use a locally generated certificate (e.g. air-gapped environments where DNS is unavailable):
|
|
174
185
|
|
|
175
186
|
```bash
|
|
176
187
|
brew install mkcert
|
|
177
188
|
mkcert -install
|
|
189
|
+
npx clay-server --local-cert
|
|
178
190
|
```
|
|
179
191
|
|
|
180
|
-
|
|
192
|
+
This generates a self-signed certificate trusted by your machine. The setup wizard will guide you through installing the CA on other devices.
|
|
181
193
|
|
|
182
|
-
|
|
194
|
+
</details>
|
|
183
195
|
|
|
184
196
|
---
|
|
185
197
|
|
|
@@ -192,6 +204,7 @@ npx clay-server --yes # Skip interactive prompts (use defaults)
|
|
|
192
204
|
npx clay-server -y --pin 123456
|
|
193
205
|
# Non-interactive + PIN (for scripts/CI)
|
|
194
206
|
npx clay-server --no-https # Disable HTTPS
|
|
207
|
+
npx clay-server --local-cert # Use local certificate (mkcert) instead of builtin
|
|
195
208
|
npx clay-server --no-update # Skip update check
|
|
196
209
|
npx clay-server --debug # Enable debug panel
|
|
197
210
|
npx clay-server --add . # Add current directory to running daemon
|
package/bin/cli.js
CHANGED
|
@@ -599,12 +599,6 @@ function toClayStudioUrl(ip, port, protocol) {
|
|
|
599
599
|
}
|
|
600
600
|
|
|
601
601
|
function ensureCerts(ip) {
|
|
602
|
-
// Check builtin cert first (unless --local-cert flag is set)
|
|
603
|
-
if (!forceMkcert) {
|
|
604
|
-
var builtin = getBuiltinCert();
|
|
605
|
-
if (builtin) return builtin;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
602
|
var homeDir = os.homedir();
|
|
609
603
|
var certDir = path.join(process.env.CLAY_HOME || path.join(homeDir, ".clay"), "certs");
|
|
610
604
|
var keyPath = path.join(certDir, "key.pem");
|
|
@@ -619,14 +613,18 @@ function ensureCerts(ip) {
|
|
|
619
613
|
fs.copyFileSync(legacyCert, certPath);
|
|
620
614
|
}
|
|
621
615
|
|
|
616
|
+
var mkcertInstalled = hasMkcert();
|
|
617
|
+
|
|
622
618
|
var caRoot = null;
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
619
|
+
if (mkcertInstalled) {
|
|
620
|
+
try {
|
|
621
|
+
caRoot = path.join(
|
|
622
|
+
execSync("mkcert -CAROOT", { encoding: "utf8" }).trim(),
|
|
623
|
+
"rootCA.pem"
|
|
624
|
+
);
|
|
625
|
+
if (!fs.existsSync(caRoot)) caRoot = null;
|
|
626
|
+
} catch (e) {}
|
|
627
|
+
}
|
|
630
628
|
|
|
631
629
|
// Collect all IPv4 addresses (Tailscale + LAN)
|
|
632
630
|
var allIPs = getAllIPs();
|
|
@@ -644,24 +642,39 @@ function ensureCerts(ip) {
|
|
|
644
642
|
}
|
|
645
643
|
}
|
|
646
644
|
} catch (e) { needRegen = true; }
|
|
647
|
-
if (!needRegen)
|
|
645
|
+
if (!needRegen) {
|
|
646
|
+
return { key: keyPath, cert: certPath, caRoot: caRoot, mkcertDetected: mkcertInstalled && !forceMkcert };
|
|
647
|
+
}
|
|
648
648
|
}
|
|
649
649
|
|
|
650
|
-
|
|
650
|
+
// mkcert installed: generate local cert (legacy behavior)
|
|
651
|
+
if (mkcertInstalled) {
|
|
652
|
+
fs.mkdirSync(certDir, { recursive: true });
|
|
653
|
+
|
|
654
|
+
var domains = ["localhost", "127.0.0.1", "::1"];
|
|
655
|
+
for (var i = 0; i < allIPs.length; i++) {
|
|
656
|
+
if (domains.indexOf(allIPs[i]) === -1) domains.push(allIPs[i]);
|
|
657
|
+
}
|
|
651
658
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
659
|
+
try {
|
|
660
|
+
var mkcertArgs = ["-key-file", keyPath, "-cert-file", certPath].concat(domains);
|
|
661
|
+
execFileSync("mkcert", mkcertArgs, { stdio: "pipe" });
|
|
662
|
+
} catch (err) {
|
|
663
|
+
// mkcert generation failed, fall through to builtin
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
|
|
667
|
+
return { key: keyPath, cert: certPath, caRoot: caRoot, mkcertDetected: !forceMkcert };
|
|
668
|
+
}
|
|
655
669
|
}
|
|
656
670
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
return null;
|
|
671
|
+
// Fallback: builtin cert (unless --local-cert forces mkcert-only)
|
|
672
|
+
if (!forceMkcert) {
|
|
673
|
+
var builtin = getBuiltinCert();
|
|
674
|
+
if (builtin) return builtin;
|
|
662
675
|
}
|
|
663
676
|
|
|
664
|
-
return
|
|
677
|
+
return null;
|
|
665
678
|
}
|
|
666
679
|
|
|
667
680
|
// --- Logo ---
|
|
@@ -1436,12 +1449,14 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
|
|
|
1436
1449
|
var ip = getLocalIP();
|
|
1437
1450
|
var hasTls = false;
|
|
1438
1451
|
var hasBuiltinCert = false;
|
|
1452
|
+
var mkcertDetected = false;
|
|
1439
1453
|
|
|
1440
1454
|
if (useHttps) {
|
|
1441
1455
|
var certPaths = ensureCerts(ip);
|
|
1442
1456
|
if (certPaths) {
|
|
1443
1457
|
hasTls = true;
|
|
1444
1458
|
if (certPaths.builtin) hasBuiltinCert = true;
|
|
1459
|
+
if (certPaths.mkcertDetected) mkcertDetected = true;
|
|
1445
1460
|
} else {
|
|
1446
1461
|
log(sym.warn + " " + a.yellow + "HTTPS unavailable" + a.reset + a.dim + " · mkcert not installed" + a.reset);
|
|
1447
1462
|
}
|
|
@@ -1516,6 +1531,7 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
|
|
|
1516
1531
|
pinHash: mode === "multi" && cliPin ? generateAuthToken(cliPin) : null,
|
|
1517
1532
|
tls: hasTls,
|
|
1518
1533
|
builtinCert: hasBuiltinCert,
|
|
1534
|
+
mkcertDetected: mkcertDetected,
|
|
1519
1535
|
debug: debugMode,
|
|
1520
1536
|
keepAwake: keepAwake,
|
|
1521
1537
|
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
@@ -1595,6 +1611,8 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
|
|
|
1595
1611
|
: protocol + "://" + ip + ":" + config.port;
|
|
1596
1612
|
console.log(" " + sym.done + " Daemon started (PID " + config.pid + ")");
|
|
1597
1613
|
console.log(" " + sym.done + " " + url);
|
|
1614
|
+
if (config.builtinCert) console.log(" " + sym.done + " d.clay.studio is only used for HTTPS certificates. All traffic stays on your local network. https://github.com/chadbyte/clay/tree/main/clay-dns");
|
|
1615
|
+
if (config.mkcertDetected) console.log(" " + sym.warn + " mkcert detected. Uninstall mkcert to use builtin cert, or pass --local-cert to suppress this warning.");
|
|
1598
1616
|
console.log(" " + sym.done + " Headless mode — exiting CLI");
|
|
1599
1617
|
process.exit(0);
|
|
1600
1618
|
return;
|
|
@@ -1611,12 +1629,14 @@ async function devMode(mode, keepAwake, existingPinHash) {
|
|
|
1611
1629
|
var ip = getLocalIP();
|
|
1612
1630
|
var hasTls = false;
|
|
1613
1631
|
var hasBuiltinCert = false;
|
|
1632
|
+
var mkcertDetected = false;
|
|
1614
1633
|
|
|
1615
1634
|
if (useHttps) {
|
|
1616
1635
|
var certPaths = ensureCerts(ip);
|
|
1617
1636
|
if (certPaths) {
|
|
1618
1637
|
hasTls = true;
|
|
1619
1638
|
if (certPaths.builtin) hasBuiltinCert = true;
|
|
1639
|
+
if (certPaths.mkcertDetected) mkcertDetected = true;
|
|
1620
1640
|
}
|
|
1621
1641
|
}
|
|
1622
1642
|
|
|
@@ -1680,6 +1700,7 @@ async function devMode(mode, keepAwake, existingPinHash) {
|
|
|
1680
1700
|
pinHash: existingPinHash || null,
|
|
1681
1701
|
tls: hasTls,
|
|
1682
1702
|
builtinCert: hasBuiltinCert,
|
|
1703
|
+
mkcertDetected: mkcertDetected,
|
|
1683
1704
|
debug: true,
|
|
1684
1705
|
keepAwake: keepAwake || false,
|
|
1685
1706
|
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
@@ -1829,6 +1850,7 @@ async function restartDaemonWithTLS(config, callback) {
|
|
|
1829
1850
|
return;
|
|
1830
1851
|
}
|
|
1831
1852
|
var hasBuiltinCert = !!(certPaths && certPaths.builtin);
|
|
1853
|
+
var mkcertDetected = !!(certPaths && certPaths.mkcertDetected);
|
|
1832
1854
|
|
|
1833
1855
|
// Shut down old daemon
|
|
1834
1856
|
stopDaemonWatcher();
|
|
@@ -1853,6 +1875,7 @@ async function restartDaemonWithTLS(config, callback) {
|
|
|
1853
1875
|
pinHash: config.pinHash || null,
|
|
1854
1876
|
tls: true,
|
|
1855
1877
|
builtinCert: hasBuiltinCert,
|
|
1878
|
+
mkcertDetected: mkcertDetected,
|
|
1856
1879
|
debug: config.debug || false,
|
|
1857
1880
|
keepAwake: config.keepAwake || false,
|
|
1858
1881
|
dangerouslySkipPermissions: config.dangerouslySkipPermissions || false,
|
|
@@ -1951,6 +1974,7 @@ function showMainMenu(config, ip) {
|
|
|
1951
1974
|
function afterQr() {
|
|
1952
1975
|
// Status line
|
|
1953
1976
|
log(" " + a.dim + "clay" + a.reset + " " + a.dim + "v" + currentVersion + a.reset + a.dim + " — " + url + a.reset);
|
|
1977
|
+
if (config.builtinCert) log(" " + a.dim + "d.clay.studio is only used for HTTPS certificates. All traffic stays on your local network. https://github.com/chadbyte/clay/tree/main/clay-dns" + a.reset);
|
|
1954
1978
|
var parts = [];
|
|
1955
1979
|
parts.push(a.bold + projs.length + a.reset + a.dim + (projs.length === 1 ? " project" : " projects"));
|
|
1956
1980
|
parts.push(a.reset + a.bold + totalSessions + a.reset + a.dim + (totalSessions === 1 ? " session" : " sessions"));
|
|
@@ -1961,6 +1985,15 @@ function showMainMenu(config, ip) {
|
|
|
1961
1985
|
log(" Press " + a.bold + "o" + a.reset + " to open in browser");
|
|
1962
1986
|
log("");
|
|
1963
1987
|
|
|
1988
|
+
if (config.mkcertDetected) {
|
|
1989
|
+
log(" " + sym.warn + " " + a.yellow + "mkcert detected." + a.reset + " Clay now ships with a builtin HTTPS certificate.");
|
|
1990
|
+
log(" " + a.dim + "Uninstall mkcert to use it. No more CA setup on each device." + a.reset);
|
|
1991
|
+
log(" " + a.dim + " brew uninstall mkcert (macOS)" + a.reset);
|
|
1992
|
+
log(" " + a.dim + " sudo apt remove mkcert (Linux)" + a.reset);
|
|
1993
|
+
log(" " + a.dim + "Or pass --local-cert to keep using mkcert without this warning." + a.reset);
|
|
1994
|
+
log("");
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1964
1997
|
showMenuItems();
|
|
1965
1998
|
}
|
|
1966
1999
|
|
|
@@ -2725,6 +2758,7 @@ var currentVersion = require("../package.json").version;
|
|
|
2725
2758
|
: protocol + "://" + ip + ":" + config.port;
|
|
2726
2759
|
console.log(" " + sym.done + " Daemon already running (PID " + config.pid + ")");
|
|
2727
2760
|
console.log(" " + sym.done + " " + url);
|
|
2761
|
+
if (config.builtinCert) console.log(" " + sym.done + " d.clay.studio is only used for HTTPS certificates. All traffic stays on your local network. https://github.com/chadbyte/clay/tree/main/clay-dns");
|
|
2728
2762
|
process.exit(0);
|
|
2729
2763
|
return;
|
|
2730
2764
|
}
|
package/lib/public/sw.js
CHANGED
|
@@ -38,8 +38,11 @@ self.addEventListener("fetch", function (event) {
|
|
|
38
38
|
// Only handle GET requests
|
|
39
39
|
if (request.method !== "GET") return;
|
|
40
40
|
|
|
41
|
-
// Skip
|
|
41
|
+
// Skip cross-origin requests (external images, fonts, etc.)
|
|
42
42
|
var url = new URL(request.url);
|
|
43
|
+
if (url.origin !== self.location.origin) return;
|
|
44
|
+
|
|
45
|
+
// Skip WebSocket upgrade requests and API/data endpoints
|
|
43
46
|
if (url.pathname.indexOf("/ws") !== -1) return;
|
|
44
47
|
if (url.pathname.indexOf("/api/") !== -1) return;
|
|
45
48
|
|