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
package/README.md
CHANGED
|
@@ -383,6 +383,7 @@ E2E test architecture: [`e2e/README.md`](e2e/README.md)
|
|
|
383
383
|
|
|
384
384
|
```bash
|
|
385
385
|
npm run dev # Dev server (watch mode)
|
|
386
|
+
npm run dev -- --https # Dev server with opt-in HTTPS (auto-generates self-signed cert)
|
|
386
387
|
npm start # Express server
|
|
387
388
|
npm test # Jest (*.test.ts files live next to source)
|
|
388
389
|
npm run e2e # Playwright E2E tests (e2e/*.test.ts)
|
|
@@ -413,7 +414,7 @@ npx shadow-claw webrtc listen # Start headless WebRTC Dat
|
|
|
413
414
|
npx shadow-claw peer-id # Get or generate persistent CLI Peer ID
|
|
414
415
|
```
|
|
415
416
|
|
|
416
|
-
Commands support `--transport webrtc` for direct peer-to-peer DataChannel execution with connected browser clients. Control plane authentication uses `SHADOWCLAW_CONTROL_TOKEN` (env) or `--token` flag. The control plane endpoint and token are printed to the console on server start.
|
|
417
|
+
Commands support `--transport webrtc` for direct peer-to-peer DataChannel execution with connected browser clients. Control plane authentication uses `SHADOWCLAW_CONTROL_TOKEN` (env) or `--token` flag, and supports HTTPS endpoints via `--https` (and `--insecure` for self-signed certs). The control plane endpoint and token are printed to the console on server start.
|
|
417
418
|
|
|
418
419
|
## License
|
|
419
420
|
|
package/bin/cli.mjs
CHANGED
|
@@ -173,6 +173,36 @@ async function handleDev(portArg, options) {
|
|
|
173
173
|
.filter(Boolean),
|
|
174
174
|
);
|
|
175
175
|
|
|
176
|
+
const isHttps = Boolean(
|
|
177
|
+
options.https ||
|
|
178
|
+
["1", "true", "yes"].includes(
|
|
179
|
+
(process.env.SHADOWCLAW_HTTPS || "").toLowerCase().trim(),
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
const certPath =
|
|
183
|
+
options.cert ||
|
|
184
|
+
(
|
|
185
|
+
process.env.SHADOWCLAW_TLS_CERT ||
|
|
186
|
+
process.env.SHADOWCLAW_CERT ||
|
|
187
|
+
""
|
|
188
|
+
).trim() ||
|
|
189
|
+
undefined;
|
|
190
|
+
const keyPath =
|
|
191
|
+
options.key ||
|
|
192
|
+
(
|
|
193
|
+
process.env.SHADOWCLAW_TLS_KEY ||
|
|
194
|
+
process.env.SHADOWCLAW_KEY ||
|
|
195
|
+
""
|
|
196
|
+
).trim() ||
|
|
197
|
+
undefined;
|
|
198
|
+
const sslDir = path.resolve(
|
|
199
|
+
contentRoot,
|
|
200
|
+
options.sslDir ||
|
|
201
|
+
process.env.SHADOWCLAW_SSL_DIR ||
|
|
202
|
+
process.env.SHADOWCLAW_TLS_DIR ||
|
|
203
|
+
".cache/tls",
|
|
204
|
+
);
|
|
205
|
+
|
|
176
206
|
await startServer({
|
|
177
207
|
port,
|
|
178
208
|
bindHost: host,
|
|
@@ -183,9 +213,14 @@ async function handleDev(portArg, options) {
|
|
|
183
213
|
rootPath: distPublicDir,
|
|
184
214
|
databaseDir,
|
|
185
215
|
allowPrivateProxy: Boolean(options.allowPrivateProxy),
|
|
216
|
+
https: isHttps,
|
|
217
|
+
certPath,
|
|
218
|
+
keyPath,
|
|
219
|
+
sslDir,
|
|
186
220
|
});
|
|
187
221
|
|
|
188
|
-
const
|
|
222
|
+
const protocol = isHttps ? "https" : "http";
|
|
223
|
+
const localUrl = `${protocol}://${host}:${port}`;
|
|
189
224
|
if (options.open) {
|
|
190
225
|
openBrowser(localUrl);
|
|
191
226
|
}
|
|
@@ -218,6 +253,13 @@ program
|
|
|
218
253
|
"Allow proxy to reach private/loopback addresses",
|
|
219
254
|
false,
|
|
220
255
|
)
|
|
256
|
+
.option("--https", "Enable HTTPS dev server", false)
|
|
257
|
+
.option("--cert <path>", "Path to existing TLS certificate file")
|
|
258
|
+
.option("--key <path>", "Path to existing TLS private key file")
|
|
259
|
+
.option(
|
|
260
|
+
"--ssl-dir <path>",
|
|
261
|
+
"Directory for TLS certificate generation/storage",
|
|
262
|
+
)
|
|
221
263
|
.option("-v, --verbose", "Enable verbose request/proxy logging", false)
|
|
222
264
|
.option("--open", "Automatically open default browser", false)
|
|
223
265
|
.action(handleDev);
|
|
@@ -245,6 +287,13 @@ program
|
|
|
245
287
|
"Allow proxy to reach private/loopback addresses",
|
|
246
288
|
false,
|
|
247
289
|
)
|
|
290
|
+
.option("--https", "Enable HTTPS dev server", false)
|
|
291
|
+
.option("--cert <path>", "Path to existing TLS certificate file")
|
|
292
|
+
.option("--key <path>", "Path to existing TLS private key file")
|
|
293
|
+
.option(
|
|
294
|
+
"--ssl-dir <path>",
|
|
295
|
+
"Directory for TLS certificate generation/storage",
|
|
296
|
+
)
|
|
248
297
|
.option("-v, --verbose", "Enable verbose request/proxy logging", false)
|
|
249
298
|
.option("--open", "Automatically open default browser", false)
|
|
250
299
|
.action(handleDev);
|
|
@@ -276,6 +325,13 @@ program
|
|
|
276
325
|
"Allow proxy to reach private/loopback addresses",
|
|
277
326
|
false,
|
|
278
327
|
)
|
|
328
|
+
.option("--https", "Enable HTTPS dev server", false)
|
|
329
|
+
.option("--cert <path>", "Path to existing TLS certificate file")
|
|
330
|
+
.option("--key <path>", "Path to existing TLS private key file")
|
|
331
|
+
.option(
|
|
332
|
+
"--ssl-dir <path>",
|
|
333
|
+
"Directory for TLS certificate generation/storage",
|
|
334
|
+
)
|
|
279
335
|
.option("-v, --verbose", "Enable verbose request/proxy logging", false)
|
|
280
336
|
.option("--open", "Automatically open default browser", false)
|
|
281
337
|
.action(async (portArg, options) => {
|
|
@@ -328,6 +384,36 @@ program
|
|
|
328
384
|
.filter(Boolean),
|
|
329
385
|
);
|
|
330
386
|
|
|
387
|
+
const isHttps = Boolean(
|
|
388
|
+
options.https ||
|
|
389
|
+
["1", "true", "yes"].includes(
|
|
390
|
+
(process.env.SHADOWCLAW_HTTPS || "").toLowerCase().trim(),
|
|
391
|
+
),
|
|
392
|
+
);
|
|
393
|
+
const certPath =
|
|
394
|
+
options.cert ||
|
|
395
|
+
(
|
|
396
|
+
process.env.SHADOWCLAW_TLS_CERT ||
|
|
397
|
+
process.env.SHADOWCLAW_CERT ||
|
|
398
|
+
""
|
|
399
|
+
).trim() ||
|
|
400
|
+
undefined;
|
|
401
|
+
const keyPath =
|
|
402
|
+
options.key ||
|
|
403
|
+
(
|
|
404
|
+
process.env.SHADOWCLAW_TLS_KEY ||
|
|
405
|
+
process.env.SHADOWCLAW_KEY ||
|
|
406
|
+
""
|
|
407
|
+
).trim() ||
|
|
408
|
+
undefined;
|
|
409
|
+
const sslDir = path.resolve(
|
|
410
|
+
contentRoot,
|
|
411
|
+
options.sslDir ||
|
|
412
|
+
process.env.SHADOWCLAW_SSL_DIR ||
|
|
413
|
+
process.env.SHADOWCLAW_TLS_DIR ||
|
|
414
|
+
".cache/tls",
|
|
415
|
+
);
|
|
416
|
+
|
|
331
417
|
await startServer({
|
|
332
418
|
port,
|
|
333
419
|
bindHost: host,
|
|
@@ -338,9 +424,14 @@ program
|
|
|
338
424
|
rootPath: distPublicDir,
|
|
339
425
|
databaseDir,
|
|
340
426
|
allowPrivateProxy: Boolean(options.allowPrivateProxy),
|
|
427
|
+
https: isHttps,
|
|
428
|
+
certPath,
|
|
429
|
+
keyPath,
|
|
430
|
+
sslDir,
|
|
341
431
|
});
|
|
342
432
|
|
|
343
|
-
const
|
|
433
|
+
const protocol = isHttps ? "https" : "http";
|
|
434
|
+
const localUrl = `${protocol}://${host}:${port}`;
|
|
344
435
|
if (options.open) {
|
|
345
436
|
openBrowser(localUrl);
|
|
346
437
|
}
|
|
@@ -478,6 +569,11 @@ program
|
|
|
478
569
|
"Use TLS (wss://) for the signaling server (for listen)",
|
|
479
570
|
false,
|
|
480
571
|
)
|
|
572
|
+
.option(
|
|
573
|
+
"--https",
|
|
574
|
+
"Alias for --secure: use TLS (wss://) for the signaling server",
|
|
575
|
+
false,
|
|
576
|
+
)
|
|
481
577
|
.option(
|
|
482
578
|
"--trusted-peer <id>",
|
|
483
579
|
"Accept connections only from this peer ID (repeatable, for listen)",
|
|
@@ -486,6 +582,11 @@ program
|
|
|
486
582
|
)
|
|
487
583
|
.option("--verbose", "Verbose connection logging (for listen)", false)
|
|
488
584
|
.option("--renew-peer-id", "Renew CLI peer ID before listening", false)
|
|
585
|
+
.option(
|
|
586
|
+
"-k, --insecure",
|
|
587
|
+
"Allow self-signed TLS certificates for the signaling server (wss://)",
|
|
588
|
+
true,
|
|
589
|
+
)
|
|
489
590
|
.action(async (action, customId, options) => {
|
|
490
591
|
if (action === "listen") {
|
|
491
592
|
await runWebRtcListenCommand(options);
|
|
@@ -507,6 +608,8 @@ program
|
|
|
507
608
|
.option("--host <host>", "Control plane host")
|
|
508
609
|
.option("--port <port>", "Control plane port")
|
|
509
610
|
.option("--token <token>", "Control token")
|
|
611
|
+
.option("--https", "Connect to server via HTTPS", false)
|
|
612
|
+
.option("-k, --insecure", "Allow self-signed TLS certificates", true)
|
|
510
613
|
.option("--transport <transport>", "Transport to use: http | webrtc", "http")
|
|
511
614
|
.option("--peer-id <id>", "Custom WebRTC CLI peer ID")
|
|
512
615
|
.option(
|
|
@@ -530,6 +633,8 @@ program
|
|
|
530
633
|
.option("--host <host>", "Control plane host")
|
|
531
634
|
.option("--port <port>", "Control plane port")
|
|
532
635
|
.option("--token <token>", "Control token")
|
|
636
|
+
.option("--https", "Connect to server via HTTPS", false)
|
|
637
|
+
.option("-k, --insecure", "Allow self-signed TLS certificates", true)
|
|
533
638
|
.option("--transport <transport>", "Transport to use: http | webrtc", "http")
|
|
534
639
|
.option("--peer-id <id>", "Custom WebRTC CLI peer ID")
|
|
535
640
|
.option("--renew-peer-id", "Renew WebRTC CLI peer ID before sending", false)
|
|
@@ -552,6 +657,8 @@ program
|
|
|
552
657
|
.option("--host <host>", "Control plane host")
|
|
553
658
|
.option("--port <port>", "Control plane port")
|
|
554
659
|
.option("--token <token>", "Control token")
|
|
660
|
+
.option("--https", "Connect to server via HTTPS", false)
|
|
661
|
+
.option("-k, --insecure", "Allow self-signed TLS certificates", true)
|
|
555
662
|
.option("--transport <transport>", "Transport to use: http | webrtc", "http")
|
|
556
663
|
.option("--peer-id <id>", "Custom WebRTC CLI peer ID")
|
|
557
664
|
.option(
|
|
@@ -575,6 +682,8 @@ program
|
|
|
575
682
|
.option("--host <host>", "Control plane host")
|
|
576
683
|
.option("--port <port>", "Control plane port")
|
|
577
684
|
.option("--token <token>", "Control token")
|
|
685
|
+
.option("--https", "Connect to server via HTTPS", false)
|
|
686
|
+
.option("-k, --insecure", "Allow self-signed TLS certificates", true)
|
|
578
687
|
.option("--transport <transport>", "Transport to use: http | webrtc", "http")
|
|
579
688
|
.option("--peer-id <id>", "Custom WebRTC CLI peer ID")
|
|
580
689
|
.option(
|
|
@@ -156,7 +156,23 @@ export async function runWebRtcListenCommand(options = {}) {
|
|
|
156
156
|
? parseInt(options.port, 10)
|
|
157
157
|
: parseInt(process.env.SHADOWCLAW_PORT || "8888", 10);
|
|
158
158
|
const peerPath = options.path || "/";
|
|
159
|
-
const secure = Boolean(
|
|
159
|
+
const secure = Boolean(
|
|
160
|
+
options.secure ||
|
|
161
|
+
options.https ||
|
|
162
|
+
["1", "true", "yes"].includes(
|
|
163
|
+
(process.env.SHADOWCLAW_HTTPS || "").toLowerCase().trim(),
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
// --insecure / -k: allow self-signed TLS certificates (default: true for dev)
|
|
167
|
+
const rejectUnauthorized =
|
|
168
|
+
options.insecure === false ||
|
|
169
|
+
["0", "false", "no"].includes(
|
|
170
|
+
(process.env.SHADOWCLAW_TLS_REJECT_UNAUTHORIZED || "")
|
|
171
|
+
.toLowerCase()
|
|
172
|
+
.trim(),
|
|
173
|
+
)
|
|
174
|
+
? true
|
|
175
|
+
: false;
|
|
160
176
|
const trustedPeerIds = options.trustedPeer
|
|
161
177
|
? Array.isArray(options.trustedPeer)
|
|
162
178
|
? options.trustedPeer
|
|
@@ -169,6 +185,9 @@ export async function runWebRtcListenCommand(options = {}) {
|
|
|
169
185
|
console.log(
|
|
170
186
|
`Signaling server : ${secure ? "wss" : "ws"}://${host}:${port}${peerPath}`,
|
|
171
187
|
);
|
|
188
|
+
if (secure && !rejectUnauthorized) {
|
|
189
|
+
console.log(`TLS verification : disabled (self-signed cert allowed)`);
|
|
190
|
+
}
|
|
172
191
|
if (trustedPeerIds.length > 0) {
|
|
173
192
|
console.log(`Trusted peers : ${trustedPeerIds.join(", ")}`);
|
|
174
193
|
} else {
|
|
@@ -186,6 +205,7 @@ export async function runWebRtcListenCommand(options = {}) {
|
|
|
186
205
|
port,
|
|
187
206
|
path: peerPath,
|
|
188
207
|
secure,
|
|
208
|
+
rejectUnauthorized,
|
|
189
209
|
trustedPeerIds,
|
|
190
210
|
peerId: options.peerId,
|
|
191
211
|
cacheDir: options.cacheDir,
|
|
@@ -72,7 +72,17 @@ export class CliControlClient {
|
|
|
72
72
|
this.port =
|
|
73
73
|
options.port || parseInt(process.env.SHADOWCLAW_PORT || "8888", 10);
|
|
74
74
|
this.token = options.token || resolveControlToken(options.token);
|
|
75
|
-
|
|
75
|
+
const isHttps = Boolean(
|
|
76
|
+
options.https ||
|
|
77
|
+
["1", "true", "yes"].includes(
|
|
78
|
+
(process.env.SHADOWCLAW_HTTPS || "").toLowerCase().trim(),
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
this.protocol = options.protocol || (isHttps ? "https" : "http");
|
|
82
|
+
this.rejectUnauthorized =
|
|
83
|
+
options.rejectUnauthorized !== undefined
|
|
84
|
+
? Boolean(options.rejectUnauthorized)
|
|
85
|
+
: Boolean(options.insecure === false);
|
|
76
86
|
this.transport = options.transport || "http";
|
|
77
87
|
this._webrtcClient =
|
|
78
88
|
this.transport === "webrtc"
|
|
@@ -81,6 +91,7 @@ export class CliControlClient {
|
|
|
81
91
|
port: this.port,
|
|
82
92
|
path: options.peerPath || "/",
|
|
83
93
|
secure: this.protocol === "https",
|
|
94
|
+
rejectUnauthorized: this.rejectUnauthorized,
|
|
84
95
|
peerId: options.peerId,
|
|
85
96
|
cacheDir: options.cacheDir,
|
|
86
97
|
renewPeerId: Boolean(options.renewPeerId),
|
|
@@ -99,35 +110,38 @@ export class CliControlClient {
|
|
|
99
110
|
headers["x-control-token"] = this.token;
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
113
|
+
const reqOptions = {
|
|
114
|
+
hostname: this.host,
|
|
115
|
+
port: this.port,
|
|
116
|
+
path: reqPath,
|
|
117
|
+
method,
|
|
118
|
+
headers,
|
|
119
|
+
timeout,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (this.protocol === "https") {
|
|
123
|
+
reqOptions.rejectUnauthorized = this.rejectUnauthorized;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const req = client.request(reqOptions, (res) => {
|
|
127
|
+
let data = "";
|
|
128
|
+
res.on("data", (chunk) => (data += chunk));
|
|
129
|
+
res.on("end", () => {
|
|
130
|
+
let parsed = data;
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(data);
|
|
133
|
+
} catch (_) {}
|
|
134
|
+
|
|
135
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
136
|
+
resolve(parsed);
|
|
137
|
+
} else {
|
|
138
|
+
const errMsg =
|
|
139
|
+
parsed?.error ||
|
|
140
|
+
`HTTP request failed with status ${res.statusCode}: ${data}`;
|
|
141
|
+
reject(new Error(errMsg));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
131
145
|
|
|
132
146
|
req.on("error", (err) => {
|
|
133
147
|
reject(
|
|
@@ -80,4 +80,14 @@ describe("CliControlClient", () => {
|
|
|
80
80
|
const backups = await client.listBackups();
|
|
81
81
|
expect(Array.isArray(backups)).toBe(true);
|
|
82
82
|
});
|
|
83
|
+
|
|
84
|
+
it("detects HTTPS protocol from options and environment", () => {
|
|
85
|
+
const client1 = new CliControlClient({ https: true });
|
|
86
|
+
expect(client1.protocol).toBe("https");
|
|
87
|
+
|
|
88
|
+
process.env.SHADOWCLAW_HTTPS = "1";
|
|
89
|
+
const client2 = new CliControlClient();
|
|
90
|
+
expect(client2.protocol).toBe("https");
|
|
91
|
+
delete process.env.SHADOWCLAW_HTTPS;
|
|
92
|
+
});
|
|
83
93
|
});
|
|
@@ -203,6 +203,7 @@ export class CliWebRtcControlClient {
|
|
|
203
203
|
options.port || parseInt(process.env.SHADOWCLAW_PORT || "8888", 10);
|
|
204
204
|
this.path = options.path || "/";
|
|
205
205
|
this.secure = options.secure ?? false;
|
|
206
|
+
this.rejectUnauthorized = options.rejectUnauthorized ?? false;
|
|
206
207
|
this.peer = null;
|
|
207
208
|
this.cacheDir = options.cacheDir;
|
|
208
209
|
this.cliPeerId =
|
|
@@ -221,12 +222,19 @@ export class CliWebRtcControlClient {
|
|
|
221
222
|
const Peer = mod.default?.Peer || mod.default || mod.Peer;
|
|
222
223
|
|
|
223
224
|
return new Promise((resolve, reject) => {
|
|
224
|
-
const
|
|
225
|
+
const peerConfig = {
|
|
225
226
|
host: this.host,
|
|
226
227
|
port: this.port,
|
|
227
228
|
path: this.path,
|
|
228
229
|
secure: this.secure,
|
|
229
|
-
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
if (this.secure) {
|
|
233
|
+
peerConfig.config = { iceServers: [] };
|
|
234
|
+
peerConfig.wsOptions = { rejectUnauthorized: this.rejectUnauthorized };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const peer = new Peer(this.cliPeerId, peerConfig);
|
|
230
238
|
|
|
231
239
|
this.peer = peer;
|
|
232
240
|
|
|
@@ -409,6 +417,7 @@ export class CliWebRtcListener {
|
|
|
409
417
|
options.port || parseInt(process.env.SHADOWCLAW_PORT || "8888", 10);
|
|
410
418
|
this.path = options.path || "/";
|
|
411
419
|
this.secure = options.secure ?? false;
|
|
420
|
+
this.rejectUnauthorized = options.rejectUnauthorized ?? false;
|
|
412
421
|
this.cacheDir = options.cacheDir;
|
|
413
422
|
this.verbose = options.verbose ?? false;
|
|
414
423
|
this.handlers = options.handlers || {};
|
|
@@ -602,12 +611,22 @@ export class CliWebRtcListener {
|
|
|
602
611
|
return new Promise((resolve, reject) => {
|
|
603
612
|
let isOpened = false;
|
|
604
613
|
|
|
605
|
-
const
|
|
614
|
+
const peerConfig = {
|
|
606
615
|
host: this.host,
|
|
607
616
|
port: this.port,
|
|
608
617
|
path: this.path,
|
|
609
618
|
secure: this.secure,
|
|
610
|
-
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// When using self-signed certificates (typical for local dev), Node's
|
|
622
|
+
// WebSocket client rejects the connection by default. Pass rejectUnauthorized
|
|
623
|
+
// via PeerJS's ws-specific config option to allow self-signed certs.
|
|
624
|
+
if (this.secure) {
|
|
625
|
+
peerConfig.config = { iceServers: [] };
|
|
626
|
+
peerConfig.wsOptions = { rejectUnauthorized: this.rejectUnauthorized };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const peer = new Peer(this.cliPeerId, peerConfig);
|
|
611
630
|
|
|
612
631
|
this._peer = peer;
|
|
613
632
|
this._running = true;
|
package/dist/public/AGENTS.md
CHANGED
|
@@ -159,7 +159,7 @@ Markdown and HTML preview work should preserve the Settings-backed iframe host a
|
|
|
159
159
|
### CLI & Dual-Root Build Pipeline
|
|
160
160
|
|
|
161
161
|
- **Dual-Root Path Resolution:** The build toolchain (`bin/build/build.mjs`) cleanly decouples `toolchainRoot` (the ShadowClaw package/repo root) from `contentRoot` (the consumer template project). In-repo builds (`resolve(contentRoot) === resolve(toolchainRoot)`) preserve the standalone in-tree compilation path. CLI/template consumer builds read pre-bundled web assets from `toolchainRoot/dist/public` and inject `pages/`, `site-config.json`, `assets/`, `.agents/`, and pretty routes from `contentRoot`, outputting to `<contentRoot>/dist/public`.
|
|
162
|
-
- **CLI Commands (`bin/cli.mjs`):** The `shadow-claw` / `shadowclaw` CLI provides `build`, `dev`, `run`, `serve`, `init`, `clients`, `send`, `backup`, `tasks`, `webrtc`, and `peer-id` commands. It supports running dev servers programmatically via `startServer` (`src/server/server.ts`) with custom `--root-path` and `--database-dir` arguments.
|
|
162
|
+
- **CLI Commands (`bin/cli.mjs`):** The `shadow-claw` / `shadowclaw` CLI provides `build`, `dev`, `run`, `serve`, `init`, `clients`, `send`, `backup`, `tasks`, `webrtc`, and `peer-id` commands. It supports running dev servers programmatically via `startServer` (`src/server/server.ts`) with custom `--root-path` and `--database-dir` arguments. Dev/run/serve commands accept `--https`, `--cert <path>`, `--key <path>`, and `--ssl-dir <path>` for opt-in HTTPS with auto-generated self-signed certs; control plane commands (`clients`, `send`, `backup`, `tasks`) accept `--https` and `-k, --insecure` to reach an HTTPS control plane server.
|
|
163
163
|
- **Naming Conventions:** Refer to the product/brand in prose and documentation as **ShadowClaw**. Use kebab-case **`shadow-claw`** for package name, CLI commands (`npx shadow-claw`), repositories, directory paths, and custom elements.
|
|
164
164
|
|
|
165
165
|
## What to Avoid
|
package/dist/public/README.md
CHANGED
|
@@ -383,6 +383,7 @@ E2E test architecture: [`e2e/README.md`](e2e/README.md)
|
|
|
383
383
|
|
|
384
384
|
```bash
|
|
385
385
|
npm run dev # Dev server (watch mode)
|
|
386
|
+
npm run dev -- --https # Dev server with opt-in HTTPS (auto-generates self-signed cert)
|
|
386
387
|
npm start # Express server
|
|
387
388
|
npm test # Jest (*.test.ts files live next to source)
|
|
388
389
|
npm run e2e # Playwright E2E tests (e2e/*.test.ts)
|
|
@@ -413,7 +414,7 @@ npx shadow-claw webrtc listen # Start headless WebRTC Dat
|
|
|
413
414
|
npx shadow-claw peer-id # Get or generate persistent CLI Peer ID
|
|
414
415
|
```
|
|
415
416
|
|
|
416
|
-
Commands support `--transport webrtc` for direct peer-to-peer DataChannel execution with connected browser clients. Control plane authentication uses `SHADOWCLAW_CONTROL_TOKEN` (env) or `--token` flag. The control plane endpoint and token are printed to the console on server start.
|
|
417
|
+
Commands support `--transport webrtc` for direct peer-to-peer DataChannel execution with connected browser clients. Control plane authentication uses `SHADOWCLAW_CONTROL_TOKEN` (env) or `--token` flag, and supports HTTPS endpoints via `--https` (and `--insecure` for self-signed certs). The control plane endpoint and token are printed to the console on server start.
|
|
417
418
|
|
|
418
419
|
## License
|
|
419
420
|
|
|
@@ -65,7 +65,7 @@ Step-by-step instructions for common dev tasks.
|
|
|
65
65
|
| [Protocol-Agnostic Integrations](guides/protocol-agnostic-integrations.md) | Plugin architecture and onboarding for external integrations |
|
|
66
66
|
| [Service Accounts & Credentials](guides/adding-service-accounts.md) | How to manage encrypted credentials for channels and services |
|
|
67
67
|
| [Configuring Messaging Channels](guides/configuring-messaging-channels.md) | User guide for Telegram and iMessage setup |
|
|
68
|
-
| [Server Development Configuration](guides/server-development-configuration.md) | CLI flags, CORS modes, host binding, port configuration, CSP report logging |
|
|
68
|
+
| [Server Development Configuration](guides/server-development-configuration.md) | CLI flags, CORS modes, host binding, port configuration, opt-in HTTPS/TLS, CSP report logging |
|
|
69
69
|
|
|
70
70
|
### Decisions
|
|
71
71
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="
|
|
1
|
+
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="95df1b02f85955a5d2de52937265867e899153be" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/"><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"}}</script>
|
|
2
2
|
<script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let n=`__shadowClawDefaultTrustedTypesPolicyState`,r=`default`,i=`shadowclaw-sandbox`;function a(){let e=globalThis;return e[n]||(e[n]={initialized:!1,policy:null}),e[n]}function o(){let e=a();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(r);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(r)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(r)??t.getPolicy(i)??null)}}}new class{loading=!1;models=new Map;getModelInfo(e){return this.models.get(e.toLowerCase())??null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e);!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function s(e){return e.replace(/^\/+|\/+$/g,``)}function c(e){let t=s(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),s(t)}function l(e){let t=s(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function u(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function d(e,t){let n=t||u();if(!n||!n.routes)return null;let r=c(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&c(t.prettyPath)===r)return l(e);return null}let f=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function te(){f=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=te);function p(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=m();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function m(){if(f!==null)return f;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return f=`/`,f;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return f=`/`,f;if(!n)return f=`/`+e[0]+`/`,f}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),f=t,f}catch{}}}let t=e.pathname||`/`;if(t===`/`)return f=`/`,f;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(f=`/`,f):(f=`/`+n.slice(0,r).join(`/`)+`/`,f):d(t)?(f=`/`,f):n.length===1&&!t.includes(`.`)?(f=`/`+n[0]+`/`,f):(f=`/`,f)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function re(){let e=p();return e?`shadowclaw-${e}`:`shadowclaw`}re();let h={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}};Object.fromEntries(Object.values(h).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]})),Object.fromEntries(Object.values(h).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
|
|
3
3
|
/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
|
|
4
4
|
function g(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function ie(e){if(Array.isArray(e))return e}function _(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function v(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
|
|
@@ -103,6 +103,30 @@ export SHADOWCLAW_CORS_ALLOWED_ORIGINS="https://example.com,https://app.example.
|
|
|
103
103
|
npm start -- 8888
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
### HTTPS Support (Opt-In)
|
|
107
|
+
|
|
108
|
+
**Flags:** `--https`, `--cert`, `--key`, `--ssl-dir`
|
|
109
|
+
|
|
110
|
+
HTTPS is opt-in and disabled by default.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Enable HTTPS with auto-generated self-signed dev certificate
|
|
114
|
+
npx shadow-claw dev --https
|
|
115
|
+
npm start -- 8888 --https
|
|
116
|
+
|
|
117
|
+
# Use custom TLS certificate and private key
|
|
118
|
+
npx shadow-claw dev --https --cert /path/to/cert.pem --key /path/to/key.pem
|
|
119
|
+
npm start -- 8888 --https --cert /path/to/cert.pem --key /path/to/key.pem
|
|
120
|
+
|
|
121
|
+
# Specify custom directory to store or load auto-generated certificate
|
|
122
|
+
npx shadow-claw dev --https --ssl-dir /path/to/tls
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
> When `--https` is specified without `--cert` and `--key`, the server uses OpenSSL (`openssl`) to automatically generate a self-signed development certificate (`cert.pem` and `key.pem`) in `.cache/tls` (or `--ssl-dir`). OpenSSL must be installed on your machine.
|
|
127
|
+
>
|
|
128
|
+
> Self-signed certificates are intended for local development and testing; browsers will display a standard trust warning until the certificate is manually trusted.
|
|
129
|
+
|
|
106
130
|
## Environment Variables
|
|
107
131
|
|
|
108
132
|
| Variable | Purpose | Example |
|
|
@@ -110,6 +134,10 @@ npm start -- 8888
|
|
|
110
134
|
| `SHADOWCLAW_HOST` | Server host (fallback) | `0.0.0.0` |
|
|
111
135
|
| `SHADOWCLAW_IP` | Server host alias | `192.168.1.100` |
|
|
112
136
|
| `SHADOWCLAW_BIND_IP` | Server host alias | `127.0.0.1` |
|
|
137
|
+
| `SHADOWCLAW_HTTPS` | Enable HTTPS dev server | `1`, `true`, `yes` |
|
|
138
|
+
| `SHADOWCLAW_TLS_CERT` | Path to custom TLS certificate | `/path/to/cert.pem` |
|
|
139
|
+
| `SHADOWCLAW_TLS_KEY` | Path to custom TLS private key | `/path/to/key.pem` |
|
|
140
|
+
| `SHADOWCLAW_SSL_DIR` | Directory for self-signed TLS certs | `/path/to/.cache/tls` |
|
|
113
141
|
| `SHADOWCLAW_CORS_MODE` | CORS policy | `private`, `all`, `localhost` |
|
|
114
142
|
| `SHADOWCLAW_CORS_ALLOWED_ORIGINS` | Explicit allowlist (CSV) | `https://a.com,https://b.com` |
|
|
115
143
|
| `SHADOWCLAW_ALLOW_PRIVATE_PROXY` | Allow `/proxy` to reach private IPs | `1`, `true`, `yes` |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="
|
|
1
|
+
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="95df1b02f85955a5d2de52937265867e899153be" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/"><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"}}</script>
|
|
2
2
|
<script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let n=`__shadowClawDefaultTrustedTypesPolicyState`,r=`default`,i=`shadowclaw-sandbox`;function a(){let e=globalThis;return e[n]||(e[n]={initialized:!1,policy:null}),e[n]}function o(){let e=a();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(r);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(r)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(r)??t.getPolicy(i)??null)}}}new class{loading=!1;models=new Map;getModelInfo(e){return this.models.get(e.toLowerCase())??null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e);!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function s(e){return e.replace(/^\/+|\/+$/g,``)}function c(e){let t=s(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),s(t)}function l(e){let t=s(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function u(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function d(e,t){let n=t||u();if(!n||!n.routes)return null;let r=c(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&c(t.prettyPath)===r)return l(e);return null}let f=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function te(){f=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=te);function p(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=m();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function m(){if(f!==null)return f;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return f=`/`,f;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return f=`/`,f;if(!n)return f=`/`+e[0]+`/`,f}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),f=t,f}catch{}}}let t=e.pathname||`/`;if(t===`/`)return f=`/`,f;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(f=`/`,f):(f=`/`+n.slice(0,r).join(`/`)+`/`,f):d(t)?(f=`/`,f):n.length===1&&!t.includes(`.`)?(f=`/`+n[0]+`/`,f):(f=`/`,f)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function re(){let e=p();return e?`shadowclaw-${e}`:`shadowclaw`}re();let h={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}};Object.fromEntries(Object.values(h).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]})),Object.fromEntries(Object.values(h).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
|
|
3
3
|
/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
|
|
4
4
|
function g(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function ie(e){if(Array.isArray(e))return e}function _(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function v(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
|