shadow-claw 1.24.1 → 1.25.0
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 +2 -1
- package/bin/cli.mjs +111 -2
- package/bin/commands/webrtc-listen.mjs +21 -1
- package/bin/utils/control-client.mjs +44 -30
- package/bin/utils/control-client.test.mjs +10 -0
- package/bin/utils/webrtc-control-client.mjs +23 -4
- package/dist/public/AGENTS.md +1 -1
- package/dist/public/README.md +2 -1
- package/dist/public/docs/README.md +1 -1
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/guides/server-development-configuration.md +28 -0
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/cli.md +25 -19
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/{initControlPlane-DKNht5ph.js → initControlPlane-D5kHKcB5.js} +1 -1
- package/dist/public/llms.txt +1 -1
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{orchestrator-8w9-exfN.js → orchestrator-CnwkoBpe.js} +1 -1
- package/dist/public/{peerjs-D8hck8Ih.js → peerjs-CuSJUH-r.js} +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-Cs8wbk3d.js → shadow-claw-CUbhMmcB.js} +2 -2
- package/dist/public/{shadow-claw-chat-DGPKpmU5.js → shadow-claw-chat-BN85a1xk.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-B1F0jnPk.js → shadow-claw-control-plane-C0lSxLpW.js} +1 -1
- package/dist/public/{shadow-claw-settings-D6lPBn6m.js → shadow-claw-settings-BZWrAoQA.js} +1 -1
- package/dist/server.js +203 -203
- package/package.json +1 -1
- package/src/core/utils/initControlPlane.ts +2 -0
- package/src/server/config.test.ts +49 -0
- package/src/server/config.ts +47 -0
- package/src/server/control-plane.ts +2 -1
- package/src/server/peer.ts +6 -2
- package/src/server/server.test.ts +118 -0
- package/src/server/server.ts +20 -5
- package/src/server/tls.test.ts +266 -0
- package/src/server/tls.ts +178 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { exit } from "node:process";
|
|
6
|
+
import type { ServerConfig } from "./config.js";
|
|
7
|
+
|
|
8
|
+
export interface TlsCredentials {
|
|
9
|
+
key: Buffer;
|
|
10
|
+
cert: Buffer;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the list of Subject Alternative Names (SAN) for certificate generation.
|
|
15
|
+
* Automatically discovers active local network interface IPs, includes loopback addresses,
|
|
16
|
+
* and includes any explicitly configured bind host from CLI/environment.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveSanList(bindHost?: string): string[] {
|
|
19
|
+
const sanSet = new Set<string>(["DNS:localhost", "IP:127.0.0.1", "IP:::1"]);
|
|
20
|
+
|
|
21
|
+
// 1. Add all active local network interface IPs
|
|
22
|
+
try {
|
|
23
|
+
const interfaces = os.networkInterfaces();
|
|
24
|
+
for (const netList of Object.values(interfaces)) {
|
|
25
|
+
if (!netList) continue;
|
|
26
|
+
for (const net of netList) {
|
|
27
|
+
if (net?.address) {
|
|
28
|
+
sanSet.add(`IP:${net.address}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch {}
|
|
33
|
+
|
|
34
|
+
// 2. Add explicit bindHost if provided and not a catch-all
|
|
35
|
+
if (
|
|
36
|
+
bindHost &&
|
|
37
|
+
bindHost !== "0.0.0.0" &&
|
|
38
|
+
bindHost !== "127.0.0.1" &&
|
|
39
|
+
bindHost !== "localhost" &&
|
|
40
|
+
bindHost !== "::" &&
|
|
41
|
+
bindHost !== "::1"
|
|
42
|
+
) {
|
|
43
|
+
const isIp = /^(?:::ffff:)?(?:\d{1,3}\.){3}\d{1,3}$|^[a-fA-F0-9:]+$/.test(
|
|
44
|
+
bindHost,
|
|
45
|
+
);
|
|
46
|
+
sanSet.add(isIp ? `IP:${bindHost}` : `DNS:${bindHost}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Array.from(sanSet);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Ensure TLS certificate and key credentials exist for the HTTPS server.
|
|
54
|
+
*
|
|
55
|
+
* If both `certPath` and `keyPath` are provided in config, they are loaded.
|
|
56
|
+
* If only one is provided, an error is reported and the process exits.
|
|
57
|
+
* If neither is provided, loads `key.pem` and `cert.pem` from `config.sslDir`,
|
|
58
|
+
* or generates a new self-signed certificate using OpenSSL into `config.sslDir`.
|
|
59
|
+
*/
|
|
60
|
+
export function ensureTlsCredentials(config: ServerConfig): TlsCredentials {
|
|
61
|
+
if (config.certPath && config.keyPath) {
|
|
62
|
+
if (!fs.existsSync(config.certPath)) {
|
|
63
|
+
console.error(`TLS certificate not found: ${config.certPath}`);
|
|
64
|
+
exit(1);
|
|
65
|
+
}
|
|
66
|
+
if (!fs.existsSync(config.keyPath)) {
|
|
67
|
+
console.error(`TLS private key not found: ${config.keyPath}`);
|
|
68
|
+
exit(1);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
cert: fs.readFileSync(config.certPath),
|
|
72
|
+
key: fs.readFileSync(config.keyPath),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (config.certPath && !config.keyPath) {
|
|
77
|
+
console.error(
|
|
78
|
+
"TLS configuration error: --cert was provided without --key.",
|
|
79
|
+
);
|
|
80
|
+
exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!config.certPath && config.keyPath) {
|
|
84
|
+
console.error(
|
|
85
|
+
"TLS configuration error: --key was provided without --cert.",
|
|
86
|
+
);
|
|
87
|
+
exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const sslDir = path.resolve(config.sslDir);
|
|
91
|
+
const certFile = path.join(sslDir, "cert.pem");
|
|
92
|
+
const keyFile = path.join(sslDir, "key.pem");
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(certFile) && fs.existsSync(keyFile)) {
|
|
95
|
+
return {
|
|
96
|
+
cert: fs.readFileSync(certFile),
|
|
97
|
+
key: fs.readFileSync(keyFile),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!fs.existsSync(sslDir)) {
|
|
102
|
+
fs.mkdirSync(sslDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const sanList = resolveSanList(config.bindHost);
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
try {
|
|
109
|
+
execFileSync(
|
|
110
|
+
"openssl",
|
|
111
|
+
[
|
|
112
|
+
"req",
|
|
113
|
+
"-x509",
|
|
114
|
+
"-newkey",
|
|
115
|
+
"rsa:2048",
|
|
116
|
+
"-nodes",
|
|
117
|
+
"-keyout",
|
|
118
|
+
keyFile,
|
|
119
|
+
"-out",
|
|
120
|
+
certFile,
|
|
121
|
+
"-days",
|
|
122
|
+
"398",
|
|
123
|
+
"-subj",
|
|
124
|
+
"/CN=localhost",
|
|
125
|
+
"-addext",
|
|
126
|
+
`subjectAltName=${sanList.join(",")}`,
|
|
127
|
+
"-addext",
|
|
128
|
+
"extendedKeyUsage=serverAuth",
|
|
129
|
+
],
|
|
130
|
+
{ stdio: "pipe" },
|
|
131
|
+
);
|
|
132
|
+
} catch (extErr: any) {
|
|
133
|
+
if (extErr?.code === "ENOENT") {
|
|
134
|
+
throw extErr;
|
|
135
|
+
}
|
|
136
|
+
// Fallback if older OpenSSL doesn't support -addext
|
|
137
|
+
execFileSync(
|
|
138
|
+
"openssl",
|
|
139
|
+
[
|
|
140
|
+
"req",
|
|
141
|
+
"-x509",
|
|
142
|
+
"-newkey",
|
|
143
|
+
"rsa:2048",
|
|
144
|
+
"-nodes",
|
|
145
|
+
"-keyout",
|
|
146
|
+
keyFile,
|
|
147
|
+
"-out",
|
|
148
|
+
certFile,
|
|
149
|
+
"-days",
|
|
150
|
+
"398",
|
|
151
|
+
"-subj",
|
|
152
|
+
"/CN=localhost",
|
|
153
|
+
],
|
|
154
|
+
{ stdio: "pipe" },
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
} catch (err: any) {
|
|
158
|
+
if (err?.code === "ENOENT") {
|
|
159
|
+
console.error(
|
|
160
|
+
"Error: OpenSSL must be installed on your system to auto-generate a development TLS certificate. Please install openssl or supply --cert and --key.",
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
console.error(
|
|
164
|
+
`Error generating TLS certificate with OpenSSL: ${err?.message || String(err)}`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.warn(
|
|
171
|
+
`[server] Generated self-signed TLS certificate in ${sslDir} (suitable for local/dev use only).`,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
cert: fs.readFileSync(certFile),
|
|
176
|
+
key: fs.readFileSync(keyFile),
|
|
177
|
+
};
|
|
178
|
+
}
|