shadow-claw 1.24.0 → 1.24.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/dist/public/AGENTS.md +7 -0
- package/dist/public/agent.worker.js +49 -49
- package/dist/public/backup-controller-CvzDOq4T.js +1 -0
- package/dist/public/{configurePeerJs-CxpZeDJJ.js → configurePeerJs-UfQ6HUlv.js} +1 -1
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/control-plane.md +11 -0
- package/dist/public/{downloadGroupFile-By0GR6eV.js → downloadGroupFile-9niHBj9n.js} +1 -1
- package/dist/public/{e2e-bridge-IGWsRl4i.js → e2e-bridge-D28tOggc.js} +1 -1
- package/dist/public/{file-viewer-D0eflgt6.js → file-viewer-BVg3B_JH.js} +1 -1
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/initControlPlane-DKNht5ph.js +1 -0
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{markdown-BnpKxSxl.js → markdown-SiFq0PTy.js} +1 -1
- package/dist/public/{orchestrator-nJj59RqD.js → orchestrator-8w9-exfN.js} +1 -1
- package/dist/public/{orchestrator-3h9xxHUm.js → orchestrator-tHWrqi_F.js} +5 -5
- package/dist/public/{peerjs-BP4laMXj.js → peerjs-D8hck8Ih.js} +1 -1
- package/dist/public/push-client-D0lkwrK0.js +1 -0
- package/dist/public/service-worker/fetch-proxy.js +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-CbwETBWf.js → shadow-claw-Cs8wbk3d.js} +2 -2
- package/dist/public/{shadow-claw-channels-BDWG5Qa3.js → shadow-claw-channels-mY-JMvib.js} +1 -1
- package/dist/public/{shadow-claw-chat-Crv0DTCY.js → shadow-claw-chat-DGPKpmU5.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-CX1IGbh9.js → shadow-claw-control-plane-B1F0jnPk.js} +2 -2
- package/dist/public/{shadow-claw-conversations-_9qAaRXk.js → shadow-claw-conversations-0Q-VC_7N.js} +1 -1
- package/dist/public/{shadow-claw-file-viewer-Blf6yGVD.js → shadow-claw-file-viewer-AZfG4D-u.js} +1 -1
- package/dist/public/{shadow-claw-files-CYySBDCl.js → shadow-claw-files-Bx4v0Ggp.js} +1 -1
- package/dist/public/{shadow-claw-git-v8S0JjuR.js → shadow-claw-git-DD7iQRxq.js} +2 -2
- package/dist/public/{shadow-claw-llm-GWgoVfhI.js → shadow-claw-llm-CfeDBZKB.js} +1 -1
- package/dist/public/{shadow-claw-networking-qHbP95z9.js → shadow-claw-networking-e0EarDVs.js} +1 -1
- package/dist/public/{shadow-claw-notifications-YyHOK2Fr.js → shadow-claw-notifications-BkxuDEQI.js} +3 -3
- package/dist/public/{shadow-claw-pages-DFZHhzFZ.js → shadow-claw-pages-BWSE91I1.js} +1 -1
- package/dist/public/{shadow-claw-settings-DhxrKIfA.js → shadow-claw-settings-D6lPBn6m.js} +2 -2
- package/dist/public/{shadow-claw-storage-BFynbP0j.js → shadow-claw-storage-Crs6LY15.js} +1 -1
- package/dist/public/{shadow-claw-task-server-3BIeb5oZ.js → shadow-claw-task-server-CKm_Ennl.js} +1 -1
- package/dist/public/{shadow-claw-tasks-Caxc9atL.js → shadow-claw-tasks-Czx6Ucf5.js} +1 -1
- package/dist/public/{shadow-claw-terminal-Beb5qF-U.js → shadow-claw-terminal-Dmtr9KY1.js} +1 -1
- package/dist/public/{shadow-claw-tools-BfJx3A9W.js → shadow-claw-tools-t3Qq4TDO.js} +1 -1
- package/dist/public/{shadow-claw-webvm-BCS8ykeP.js → shadow-claw-webvm-CIuSywD_.js} +1 -1
- package/dist/public/{syncWebMcpRegistration-B_M2fCjH.js → syncWebMcpRegistration-DzC80wVG.js} +1 -1
- package/dist/server.js +3 -3
- package/package.json +1 -1
- package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +3 -0
- package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.ts +43 -9
- package/src/core/backup-controller.ts +33 -4
- package/src/core/control-plane-client.test.ts +9 -2
- package/src/core/control-plane-client.ts +18 -2
- package/src/core/orchestrator/utils/operations/task.ts +46 -15
- package/src/server/app.ts +2 -0
- package/src/server/control-plane.test.ts +36 -0
- package/src/server/control-plane.ts +39 -13
- package/src/server/middleware/pna.test.ts +15 -3
- package/src/server/middleware/pna.ts +6 -2
- package/src/server/routes/backup.test.ts +14 -0
- package/src/server/routes/backup.ts +44 -15
- package/src/server/server.ts +2 -0
- package/src/service-worker/fetch-proxy-rules.test.ts +41 -0
- package/src/service-worker/fetch-proxy-rules.ts +15 -8
- package/src/service-worker/fetch-proxy.ts +18 -2
- package/src/subsystems/notifications/push-client.test.ts +5 -1
- package/src/subsystems/notifications/push-client.ts +50 -12
- package/dist/public/backup-controller-C_s9in2t.js +0 -1
- package/dist/public/initControlPlane-C9jpnWev.js +0 -1
- package/dist/public/push-client-CrjEXK3U.js +0 -1
|
@@ -64,4 +64,45 @@ describe("shouldBypassFetchProxy", () => {
|
|
|
64
64
|
),
|
|
65
65
|
).toBe(true);
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
it("bypasses loopback endpoints when accessed from GitHub Pages origin", () => {
|
|
69
|
+
const sseUrl = new URL(
|
|
70
|
+
"http://127.0.0.1:8888/api/control/events?clientId=shadow-claw-test",
|
|
71
|
+
);
|
|
72
|
+
expect(
|
|
73
|
+
shouldBypassFetchProxy(sseUrl, "https://xt-ml.github.io/shadow-claw/"),
|
|
74
|
+
).toBe(true);
|
|
75
|
+
|
|
76
|
+
const messagesUrl = new URL("http://localhost:8888/api/control/messages");
|
|
77
|
+
expect(
|
|
78
|
+
shouldBypassFetchProxy(
|
|
79
|
+
messagesUrl,
|
|
80
|
+
"https://xt-ml.github.io/shadow-claw/",
|
|
81
|
+
),
|
|
82
|
+
).toBe(true);
|
|
83
|
+
|
|
84
|
+
const wsUrl = new URL("http://[::1]:8888/ws/control");
|
|
85
|
+
expect(
|
|
86
|
+
shouldBypassFetchProxy(wsUrl, "https://xt-ml.github.io/shadow-claw/"),
|
|
87
|
+
).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("bypasses private LAN IP endpoints when accessed from GitHub Pages origin", () => {
|
|
91
|
+
const lanUrl = new URL(
|
|
92
|
+
"http://192.168.1.100:8888/api/control/events?clientId=shadow-claw-test",
|
|
93
|
+
);
|
|
94
|
+
expect(
|
|
95
|
+
shouldBypassFetchProxy(lanUrl, "https://xt-ml.github.io/shadow-claw/"),
|
|
96
|
+
).toBe(true);
|
|
97
|
+
|
|
98
|
+
const classBUrl = new URL("http://172.20.0.5:8888/api/control/events");
|
|
99
|
+
expect(
|
|
100
|
+
shouldBypassFetchProxy(classBUrl, "https://xt-ml.github.io/shadow-claw/"),
|
|
101
|
+
).toBe(true);
|
|
102
|
+
|
|
103
|
+
const classAUrl = new URL("http://10.0.0.2:8888/api/control/events");
|
|
104
|
+
expect(
|
|
105
|
+
shouldBypassFetchProxy(classAUrl, "https://xt-ml.github.io/shadow-claw/"),
|
|
106
|
+
).toBe(true);
|
|
107
|
+
});
|
|
67
108
|
});
|
|
@@ -44,16 +44,23 @@ export function shouldBypassFetchProxy(
|
|
|
44
44
|
return true;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const isLoopback =
|
|
47
|
+
const isLoopback =
|
|
48
|
+
hostname === "localhost" ||
|
|
49
|
+
hostname === "127.0.0.1" ||
|
|
50
|
+
hostname === "::1" ||
|
|
51
|
+
hostname === "[::1]";
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
if (isLoopback) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const isPrivateIp = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(
|
|
58
|
+
hostname,
|
|
51
59
|
);
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
isShareTargetPath;
|
|
61
|
+
if (isPrivateIp) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
57
64
|
|
|
58
|
-
return
|
|
65
|
+
return false;
|
|
59
66
|
}
|
|
@@ -282,7 +282,7 @@ self.addEventListener("fetch", (event: FetchEvent) => {
|
|
|
282
282
|
"proxy/" +
|
|
283
283
|
event.request.url;
|
|
284
284
|
|
|
285
|
-
const
|
|
285
|
+
const proxyFetchOpts: any = {
|
|
286
286
|
method: "POST",
|
|
287
287
|
headers: { "Content-Type": "application/json" },
|
|
288
288
|
body: JSON.stringify({
|
|
@@ -291,7 +291,23 @@ self.addEventListener("fetch", (event: FetchEvent) => {
|
|
|
291
291
|
headers,
|
|
292
292
|
body: bodyText,
|
|
293
293
|
}),
|
|
294
|
-
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const targetHostname = targetUrl.hostname.toLowerCase();
|
|
297
|
+
if (
|
|
298
|
+
targetHostname === "localhost" ||
|
|
299
|
+
targetHostname === "127.0.0.1" ||
|
|
300
|
+
targetHostname === "::1" ||
|
|
301
|
+
targetHostname === "[::1]"
|
|
302
|
+
) {
|
|
303
|
+
proxyFetchOpts.targetAddressSpace = "loopback";
|
|
304
|
+
} else if (
|
|
305
|
+
/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(targetHostname)
|
|
306
|
+
) {
|
|
307
|
+
proxyFetchOpts.targetAddressSpace = "private";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const proxyResponse = await fetch(targetUrl.href, proxyFetchOpts);
|
|
295
311
|
|
|
296
312
|
return proxyResponse;
|
|
297
313
|
} catch (err) {
|
|
@@ -88,7 +88,9 @@ describe("push-client", () => {
|
|
|
88
88
|
|
|
89
89
|
const key = await getVapidPublicKey();
|
|
90
90
|
expect(key).toBe("test-vapid-key");
|
|
91
|
-
expect(fetch).toHaveBeenCalledWith("/push/vapid-public-key"
|
|
91
|
+
expect(fetch).toHaveBeenCalledWith("/push/vapid-public-key", {
|
|
92
|
+
targetAddressSpace: "loopback",
|
|
93
|
+
});
|
|
92
94
|
});
|
|
93
95
|
});
|
|
94
96
|
|
|
@@ -113,6 +115,7 @@ describe("push-client", () => {
|
|
|
113
115
|
const postCall = (fetch as any).mock.calls[1];
|
|
114
116
|
expect(postCall[0]).toBe("/push/subscribe");
|
|
115
117
|
expect(postCall[1].method).toBe("POST");
|
|
118
|
+
expect(postCall[1].targetAddressSpace).toBe("loopback");
|
|
116
119
|
});
|
|
117
120
|
});
|
|
118
121
|
|
|
@@ -128,6 +131,7 @@ describe("push-client", () => {
|
|
|
128
131
|
method: "DELETE",
|
|
129
132
|
headers: { "Content-Type": "application/json" },
|
|
130
133
|
body: JSON.stringify({ endpoint: mockSubscription.endpoint }),
|
|
134
|
+
targetAddressSpace: "loopback",
|
|
131
135
|
});
|
|
132
136
|
});
|
|
133
137
|
|
|
@@ -12,7 +12,13 @@ import { getConfig } from "../../db/getConfig.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export async function getPushUrl(path: string): Promise<string> {
|
|
14
14
|
const db = await getDb();
|
|
15
|
-
|
|
15
|
+
let proxyUrl = await getConfig(db, CONFIG_KEYS.PUSH_PROXY_URL);
|
|
16
|
+
|
|
17
|
+
if (!proxyUrl && typeof localStorage !== "undefined") {
|
|
18
|
+
try {
|
|
19
|
+
proxyUrl = localStorage.getItem(CONFIG_KEYS.PUSH_PROXY_URL) || undefined;
|
|
20
|
+
} catch (_) {}
|
|
21
|
+
}
|
|
16
22
|
|
|
17
23
|
if (!proxyUrl) {
|
|
18
24
|
return path;
|
|
@@ -28,6 +34,32 @@ export async function getPushUrl(path: string): Promise<string> {
|
|
|
28
34
|
return `${normalizedProxy}${normalizedPath}`;
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
export function getPushFetchOptions(
|
|
38
|
+
url: string,
|
|
39
|
+
baseOptions: RequestInit = {},
|
|
40
|
+
): RequestInit {
|
|
41
|
+
const opts: any = { ...baseOptions };
|
|
42
|
+
try {
|
|
43
|
+
const locOrigin =
|
|
44
|
+
typeof location !== "undefined"
|
|
45
|
+
? location.origin
|
|
46
|
+
: "http://127.0.0.1:8888";
|
|
47
|
+
const u = new URL(url, locOrigin);
|
|
48
|
+
const host = u.hostname.toLowerCase();
|
|
49
|
+
if (
|
|
50
|
+
host === "localhost" ||
|
|
51
|
+
host === "127.0.0.1" ||
|
|
52
|
+
host === "::1" ||
|
|
53
|
+
host === "[::1]"
|
|
54
|
+
) {
|
|
55
|
+
opts.targetAddressSpace = "loopback";
|
|
56
|
+
} else if (/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(host)) {
|
|
57
|
+
opts.targetAddressSpace = "private";
|
|
58
|
+
}
|
|
59
|
+
} catch (_) {}
|
|
60
|
+
return opts;
|
|
61
|
+
}
|
|
62
|
+
|
|
31
63
|
/**
|
|
32
64
|
* Convert a URL-safe base64 string to a Uint8Array.
|
|
33
65
|
* Required for PushManager.subscribe() applicationServerKey.
|
|
@@ -45,7 +77,7 @@ export function urlBase64ToUint8Array(base64String: string): Uint8Array {
|
|
|
45
77
|
*/
|
|
46
78
|
export async function getVapidPublicKey(): Promise<string> {
|
|
47
79
|
const url = await getPushUrl("/push/vapid-public-key");
|
|
48
|
-
const res = await fetch(url);
|
|
80
|
+
const res = await fetch(url, getPushFetchOptions(url));
|
|
49
81
|
const data = await res.json();
|
|
50
82
|
|
|
51
83
|
return data.publicKey;
|
|
@@ -65,11 +97,14 @@ export async function subscribeToPush(): Promise<PushSubscription> {
|
|
|
65
97
|
});
|
|
66
98
|
|
|
67
99
|
const url = await getPushUrl("/push/subscribe");
|
|
68
|
-
await fetch(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
await fetch(
|
|
101
|
+
url,
|
|
102
|
+
getPushFetchOptions(url, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: { "Content-Type": "application/json" },
|
|
105
|
+
body: JSON.stringify(subscription),
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
73
108
|
|
|
74
109
|
return subscription;
|
|
75
110
|
}
|
|
@@ -90,11 +125,14 @@ export async function unsubscribeFromPush(): Promise<void> {
|
|
|
90
125
|
await subscription.unsubscribe();
|
|
91
126
|
|
|
92
127
|
const url = await getPushUrl("/push/subscribe");
|
|
93
|
-
await fetch(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
128
|
+
await fetch(
|
|
129
|
+
url,
|
|
130
|
+
getPushFetchOptions(url, {
|
|
131
|
+
method: "DELETE",
|
|
132
|
+
headers: { "Content-Type": "application/json" },
|
|
133
|
+
body: JSON.stringify({ endpoint }),
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
98
136
|
}
|
|
99
137
|
|
|
100
138
|
/**
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./ulid-BY7rQVLN.js";import{t}from"./buffer-9oRIc-5Z.js";var n=t(),r=class{_clientId;_token;_serverBaseUrl;_fileEnumerator;_fileReader;_onProgress;constructor(e){this._clientId=e.clientId,this._token=e.token,this._serverBaseUrl=e.serverBaseUrl||``,this._fileEnumerator=e.fileEnumerator||(async()=>[]),this._fileReader=e.fileReader||(async()=>null),this._onProgress=e.onProgress}async initiate(){let t=e(),r=await this._fileEnumerator(),i=0,a=0,o={"Content-Type":`application/json`};this._token&&(o[`x-control-token`]=this._token);for(let e=0;e<r.length;e++){let s=r[e],c=await this._fileReader(s);if(c==null)continue;let l,u=`utf-8`;typeof c==`string`?(l=c,i+=n.Buffer.from(c,`utf-8`).length):(l=n.Buffer.from(c).toString(`base64`),u=`base64`,i+=c.byteLength||c.length||0);let d=`${this._serverBaseUrl}/api/backup/upload`,f=await fetch(d,{method:`POST`,headers:o,body:JSON.stringify({clientId:this._clientId,backupId:t,path:s,content:l,encoding:u})});if(!f.ok){let e=await f.text().catch(()=>f.statusText);throw Error(`Upload failed for ${s} (${f.status}): ${e}`)}a++,this._onProgress?.({current:a,total:r.length,file:s})}let s=`${this._serverBaseUrl}/api/backup/complete`,c=await fetch(s,{method:`POST`,headers:o,body:JSON.stringify({clientId:this._clientId,backupId:t,fileCount:a,totalBytes:i})});if(!c.ok){let e=await c.text().catch(()=>c.statusText);throw Error(`Failed to complete backup: ${e}`)}return{success:!0,backupId:t,fileCount:a,totalBytes:i}}};export{r as BackupController};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-aKtaBQYM.js";import{r as t}from"./config-CrHchneq.js";import{i as n}from"./app-routes-Bs-riIvr.js";import{n as r}from"./txPromise-EBECky1b.js";import{t as i}from"./ulid-BY7rQVLN.js";import{t as a}from"./browser-nBz_r6l4.js";import{t as o}from"./getAllTasks-vpBlgGsc.js";var s=e(a(),1),c=class{_transport;_url;_httpUrl;_token;_clientId;_peerId;_deviceLabel;_capabilities;_version;_heartbeatIntervalMs;_reconnectDelayMs;_maxReconnectDelayMs;_handlers;_WebSocketClass;_EventSourceClass;_fetchFn;_ws=null;_es=null;_heartbeatTimer=null;_reconnectTimer=null;_currentReconnectDelay;_isDestroyed=!1;_state=`disconnected`;constructor(e){if(this._transport=e.transport||`sse`,this._clientId=e.clientId,this._peerId=e.peerId,this._deviceLabel=e.deviceLabel||`ShadowClaw Browser Client`,this._capabilities=e.capabilities||[`webmcp`,`opfs`,`push`],this._version=e.version||`1.23.4`,this._token=e.token,this._heartbeatIntervalMs=e.heartbeatIntervalMs||1e4,this._reconnectDelayMs=e.reconnectDelayMs||1e3,this._maxReconnectDelayMs=e.maxReconnectDelayMs||3e4,this._currentReconnectDelay=this._reconnectDelayMs,this._handlers=new Map,this._WebSocketClass=e.WebSocketClass||(typeof WebSocket<`u`?WebSocket:null),this._EventSourceClass=e.EventSourceClass||(typeof EventSource<`u`?EventSource:null),this._fetchFn=e.fetchFn||(typeof fetch<`u`?fetch.bind(globalThis):(()=>Promise.reject(Error(`fetch not available`)))),e.handlers)for(let[t,n]of Object.entries(e.handlers))this.registerHandler(t,n);let t=typeof location<`u`?location:null,n=t?`${t.protocol}//${t.host}`:`http://127.0.0.1:8888`,r=t?`${t.protocol===`https:`?`wss:`:`ws:`}//${t.host}`:`ws://127.0.0.1:8888`;this._url=e.url||`${r}/ws/control`,this._httpUrl=e.httpUrl||n}get transport(){return this._transport}registerHandler(e,t){this._handlers.set(e,t)}unregisterHandler(e){this._handlers.delete(e)}getState(){return this._state}connect(){this._isDestroyed||this._state===`connected`||this._state===`connecting`||(this._transport===`websocket`?this._connectWebSocket():this._connectSse())}_connectWebSocket(){if(!this._WebSocketClass){console.warn(`[ControlPlaneClient] WebSocket not supported in this environment`);return}this._state=`connecting`;let e=new URL(this._url,`ws://127.0.0.1:8888`);this._token&&e.searchParams.set(`token`,this._token);try{let t=new this._WebSocketClass(e.toString());this._ws=t,t.onopen=()=>{this._state=`connected`,this._currentReconnectDelay=this._reconnectDelayMs,this._sendRegister(),this._startHeartbeat()},t.onmessage=e=>{this._handleMessage(e.data)},t.onclose=()=>{this._cleanupConnection(),this._isDestroyed||this._scheduleReconnect()},t.onerror=e=>{console.warn(`[ControlPlaneClient] WebSocket error:`,e)}}catch(e){console.warn(`[ControlPlaneClient] Failed to establish WebSocket connection:`,e),this._cleanupConnection(),this._scheduleReconnect()}}_connectSse(){if(!this._EventSourceClass){(s.default===void 0||!s.default.env?.JEST_WORKER_ID)&&console.warn(`[ControlPlaneClient] EventSource not supported in this environment`);return}this._state=`connecting`;let e=new URL(`/api/control/events`,this._httpUrl);e.searchParams.set(`clientId`,this._clientId),this._token&&e.searchParams.set(`token`,this._token);try{let t=new this._EventSourceClass(e.toString());this._es=t,t.onopen=()=>{this._state=`connected`,this._currentReconnectDelay=this._reconnectDelayMs,this._sendRegister(),this._startHeartbeat()},typeof t.addEventListener==`function`&&t.addEventListener(`command:execute`,e=>{this._handleMessage(e.data)}),t.onmessage=e=>{this._handleMessage(e.data)},t.onerror=e=>{console.warn(`[ControlPlaneClient] SSE connection error:`,e),this._cleanupConnection(),this._isDestroyed||this._scheduleReconnect()}}catch(e){console.warn(`[ControlPlaneClient] Failed to establish SSE connection:`,e),this._cleanupConnection(),this._scheduleReconnect()}}disconnect(){this._isDestroyed=!0,this._cleanupConnection(),this._reconnectTimer&&=(clearTimeout(this._reconnectTimer),null)}_cleanupConnection(){if(this._state=`disconnected`,this._stopHeartbeat(),this._ws){try{this._ws.close()}catch{}this._ws=null}if(this._es){try{this._es.close()}catch{}this._es=null}}_scheduleReconnect(){this._reconnectTimer||this._isDestroyed||(this._reconnectTimer=setTimeout(()=>{this._reconnectTimer=null,this._isDestroyed||this.connect()},this._currentReconnectDelay),this._currentReconnectDelay=Math.min(this._currentReconnectDelay*1.5,this._maxReconnectDelayMs))}_startHeartbeat(){this._stopHeartbeat(),this._heartbeatTimer=setInterval(()=>{this._sendHeartbeat()},this._heartbeatIntervalMs)}_stopHeartbeat(){this._heartbeatTimer&&=(clearInterval(this._heartbeatTimer),null)}_sendRegister(){let e={clientId:this._clientId,deviceLabel:this._deviceLabel,capabilities:this._capabilities,version:this._version,transport:this._transport,peerId:this._peerId};this._sendMessage({id:i(),type:`client:register`,payload:e})}_sendHeartbeat(){let e={clientId:this._clientId,timestamp:Date.now()};this._sendMessage({id:i(),type:`client:heartbeat`,payload:e})}_sendMessage(e){if(this._transport===`websocket`){this._ws&&(this._ws.readyState===1||this._ws.readyState===this._WebSocketClass?.OPEN)&&this._ws.send(JSON.stringify(e));return}let t=new URL(`/api/control/messages`,this._httpUrl),n={"Content-Type":`application/json`};this._token&&(n[`x-control-token`]=this._token,n.Authorization=`Bearer ${this._token}`),this._fetchFn(t.toString(),{method:`POST`,headers:n,body:JSON.stringify(e)}).catch(e=>{console.warn(`[ControlPlaneClient] Failed to send message via HTTP POST:`,e)})}async _handleMessage(e){if(!e||typeof e!=`string`)return;let t;try{t=JSON.parse(e)}catch{return}if(t.type===`command:execute`){let{commandId:e,action:n,args:r}=t.payload,a=this._handlers.get(n);if(!a){this._sendMessage({id:i(),type:`command:result`,replyTo:t.id,payload:{commandId:e,success:!1,error:`Unknown action: ${n}`}});return}try{let n=await a(r);this._sendMessage({id:i(),type:`command:result`,replyTo:t.id,payload:{commandId:e,success:!0,data:n}})}catch(n){this._sendMessage({id:i(),type:`command:result`,replyTo:t.id,payload:{commandId:e,success:!1,error:n.message||String(n)}})}}}};function l(){if(typeof localStorage<`u`)try{let e=localStorage.getItem(t.CONTROL_PLANE_CLIENT_ID);if(e&&e.trim())return e.trim()}catch{}let e=n(),r=e?`${e}-${i().toLowerCase()}`:`client-${i().toLowerCase()}`;if(typeof localStorage<`u`)try{localStorage.setItem(t.CONTROL_PLANE_CLIENT_ID,r)}catch{}return r}function u(){if(typeof localStorage<`u`)try{let e=localStorage.getItem(t.CONTROL_PLANE_ENABLED);if(e!==null)return e===`true`||e===`1`}catch{}return!1}async function d(e){if(typeof localStorage<`u`)try{let e=localStorage.getItem(t.CONTROL_PLANE_ENABLED);if(e!==null)return e===`true`||e===`1`}catch{}if(e)try{let{getConfig:n}=await import(`./getConfig-D89uJgo5.js`).then(e=>e.n),r=await n(e,t.CONTROL_PLANE_ENABLED);if(r!=null)return r===`true`||r===`1`}catch{}return!1}function f(e){if(typeof EventSource>`u`&&typeof WebSocket>`u`)return!1;let t=e||(typeof location<`u`?location:null);return!(!t||t.protocol===`file:`||typeof document<`u`&&document.querySelector(`meta[name="shadowclaw-static-only"]`))}function p(e){let n=``;if(typeof localStorage<`u`)try{n=(localStorage.getItem(t.CONTROL_PLANE_URL)||``).trim()}catch{}let r=e||(typeof location<`u`?location:null),i=r&&(r.protocol===`file:`||r.hostname&&(r.hostname.endsWith(`.github.io`)||r.hostname.endsWith(`.pages.dev`))),a=n;a||=i?`http://127.0.0.1:8888`:r&&r.host?`${r.protocol}//${r.host}`:`http://127.0.0.1:8888`,a=a.replace(/\/+$/,``);let o=a.replace(/^https?:/,e=>e===`https:`?`wss:`:`ws:`);return{httpUrl:a,wsUrl:`${o}/ws/control`}}function m(){let e=[];return typeof navigator<`u`&&(navigator.storage&&typeof navigator.storage.getDirectory==`function`&&e.push(`opfs`),`serviceWorker`in navigator&&typeof window<`u`&&`PushManager`in window&&e.push(`push`),(typeof document<`u`&&document.modelContext||navigator&&navigator.modelContext)&&e.push(`webmcp`)),typeof RTCPeerConnection<`u`&&e.push(`peerjs`),e}function h(){if(typeof navigator>`u`)return`ShadowClaw Node`;let e=navigator.userAgent,t=`Desktop`;/iPad/i.test(e)?t=`iPad`:/iPhone/i.test(e)?t=`iPhone`:/Android/i.test(e)?t=`Android`:/Macintosh/i.test(e)?t=`Mac`:/Windows/i.test(e)?t=`Windows`:/Linux/i.test(e)&&(t=`Linux`);let n=`Browser`;return/Chrome/i.test(e)&&!/Edg/i.test(e)?n=`Chrome`:/Safari/i.test(e)&&!/Chrome/i.test(e)?n=`Safari`:/Firefox/i.test(e)?n=`Firefox`:/Edg/i.test(e)&&(n=`Edge`),`${t} ${n}`}let g=null;async function _(e,t={},n={}){let i=l(),a=h(),s=m();switch(e){case`send-message`:{if(!t.text)throw Error(`Missing text parameter`);let{orchestratorStore:e}=await import(`./orchestrator-3h9xxHUm.js`).then(e=>e.n),r=n.orchestrator||e.orchestrator,i=t.groupId||e.activeGroupId||`br:main`;return r?.browserChat?.submit?(r.browserChat.submit(t.text,i),{queued:!0,groupId:i}):r?.submitMessage?(r.submitMessage(t.text,i),{queued:!0,groupId:i}):e.sendMessage?(e.sendMessage(t.text),{queued:!0,groupId:i}):{queued:!1,error:`Orchestrator not ready`}}case`list-tasks`:{let e=await o(await r());return t?.groupId&&(e=e.filter(e=>e.groupId===t.groupId)),{tasks:e}}case`read-state`:{let{orchestratorStore:e}=await import(`./orchestrator-3h9xxHUm.js`).then(e=>e.n),t=n.orchestrator||e.orchestrator;return{clientId:i,deviceLabel:a,capabilities:s,activeGroupId:n.orchestrator?.activeGroupId||e.activeGroupId||t?.activeGroupId||`br:main`,state:n.orchestrator?.state||e.state||t?.state||`idle`}}case`invoke-tool`:if(!t.toolName)throw Error(`Missing toolName parameter`);return{result:await(typeof document<`u`&&document.modelContext||typeof navigator<`u`&&navigator.modelContext).invoke(t.toolName,t.input)};case`trigger-backup`:{let{orchestratorStore:e}=await import(`./orchestrator-3h9xxHUm.js`).then(e=>e.n),a=t.groupId||n.orchestrator?.activeGroupId||e.activeGroupId||`br:main`,o=await r(),{getGroupDir:s}=await import(`./getGroupDir-P1h9wl6S.js`).then(e=>e.n),c=await s(o,a);async function l(e,t=``){let n=[];for await(let[r,i]of e.entries()){let e=t?`${t}/${r}`:r;if(i.kind===`directory`){let t=await l(i,e);n.push(...t)}else n.push(e)}return n}async function u(e){try{let t=e.split(`/`),n=c;for(let e=0;e<t.length-1;e++)n=await n.getDirectoryHandle(t[e]);let r=await(await(await n.getFileHandle(t[t.length-1])).getFile()).arrayBuffer();return new Uint8Array(r)}catch{return null}}let{BackupController:d}=await import(`./backup-controller-C_s9in2t.js`);return await new d({clientId:i,token:t.token,fileEnumerator:()=>l(c),fileReader:e=>u(e)}).initiate()}default:throw Error(`Unknown action '${e}'`)}}function v(e={}){if(g){try{g.disconnect()}catch{}g=null}let n=e.clientId&&e.clientId.trim()||l(),r=m(),i=h(),a=null;if(typeof localStorage<`u`)try{let e=localStorage.getItem(t.CONTROL_PLANE_TRANSPORT);(e===`sse`||e===`websocket`)&&(a=e)}catch{}let o=e.transport||a||`sse`,s;if(typeof localStorage<`u`)try{s=localStorage.getItem(t.PEERJS_MY_PEER_ID)||void 0}catch{}let{httpUrl:u,wsUrl:d}=p(),v=new c({clientId:n,deviceLabel:i,capabilities:r,peerId:s,transport:o,httpUrl:u,url:d,handlers:{"send-message":t=>_(`send-message`,t,e),"list-tasks":t=>_(`list-tasks`,t,e),"read-state":t=>_(`read-state`,t,e),"invoke-tool":t=>_(`invoke-tool`,t,e),"trigger-backup":t=>_(`trigger-backup`,t,e)}});return e.autoConnect!==!1&&f()&&v.connect(),g=v,v}function y(){if(g){try{g.disconnect()}catch{}g=null}}export{v as createDefaultControlPlaneClient,_ as executeClientControlCommand,d as isControlPlaneEnabled,u as isControlPlaneEnabledSync,f as shouldConnectControlPlane,y as stopControlPlaneClient};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./config-CrHchneq.js";import{n as t}from"./txPromise-EBECky1b.js";import{t as n}from"./getConfig-D89uJgo5.js";async function r(r){let i=await n(await t(),e.PUSH_PROXY_URL);if(!i)return r;let a=r.startsWith(`/`)?r:`/${r}`;return`${i.endsWith(`/`)?i.slice(0,-1):i}${a}`}function i(e){let t=(e+`=`.repeat((4-e.length%4)%4)).replace(/-/g,`+`).replace(/_/g,`/`),n=atob(t);return Uint8Array.from([...n].map(e=>e.charCodeAt(0)))}async function a(){let e=await r(`/push/vapid-public-key`);return(await(await fetch(e)).json()).publicKey}async function o(){let e=await navigator.serviceWorker.ready,t=await a(),n=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:i(t)}),o=await r(`/push/subscribe`);return await fetch(o,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(n)}),n}async function s(){let e=await(await navigator.serviceWorker.ready).pushManager.getSubscription();if(!e)return;let{endpoint:t}=e;await e.unsubscribe();let n=await r(`/push/subscribe`);await fetch(n,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({endpoint:t})})}async function c(){return(await navigator.serviceWorker.ready).pushManager.getSubscription()}export{s as i,r as n,o as r,c as t};
|