@aigne/afs-explorer 1.11.0-beta.10 → 1.11.0-beta.12
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/_virtual/rolldown_runtime.mjs +7 -0
- package/dist/index.cjs +9 -9
- package/dist/index.d.cts +9 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +9 -7
- package/dist/index.mjs.map +1 -1
- package/dist/ws-server.cjs +493 -0
- package/dist/ws-server.d.cts +69 -0
- package/dist/ws-server.d.cts.map +1 -0
- package/dist/ws-server.d.mts +69 -0
- package/dist/ws-server.d.mts.map +1 -0
- package/dist/ws-server.mjs +495 -0
- package/dist/ws-server.mjs.map +1 -0
- package/package.json +12 -36
- package/web/app.js +2881 -0
- package/web/index.html +150 -0
- package/web/state.js +343 -0
- package/web/styles.css +1789 -0
- package/dist/_virtual/rolldown_runtime.cjs +0 -29
- package/dist/server.cjs +0 -170
- package/dist/server.d.cts +0 -82
- package/dist/server.d.cts.map +0 -1
- package/dist/server.d.mts +0 -82
- package/dist/server.d.mts.map +0 -1
- package/dist/server.mjs +0 -165
- package/dist/server.mjs.map +0 -1
- package/html/assets/index-spiiiNWx.js +0 -191
- package/html/index.html +0 -13
package/dist/index.cjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_ws_server = require('./ws-server.cjs');
|
|
2
2
|
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
/**
|
|
5
|
-
* Start the AFS Explorer web server
|
|
5
|
+
* Start the AFS Explorer web server (Bun.serve + WebSocket JSON-RPC)
|
|
6
6
|
* @param afs - The AFS instance to explore
|
|
7
7
|
* @param options - Server options
|
|
8
|
-
* @returns
|
|
8
|
+
* @returns Object with port, url, and stop function
|
|
9
9
|
*/
|
|
10
10
|
async function startExplorer(afs, options = {}) {
|
|
11
|
-
const server = new
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const server = new require_ws_server.ExplorerWSServer(afs, options);
|
|
12
|
+
return {
|
|
13
|
+
...await server.start(),
|
|
14
|
+
stop: () => server.stop()
|
|
15
|
+
};
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
//#endregion
|
|
17
|
-
exports.
|
|
18
|
-
exports.createExplorerMiddleware = require_server.createExplorerMiddleware;
|
|
19
|
-
exports.createExplorerRouter = require_server.createExplorerRouter;
|
|
19
|
+
exports.ExplorerWSServer = require_ws_server.ExplorerWSServer;
|
|
20
20
|
exports.startExplorer = startExplorer;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfigManager, ExplorerWSServer, WSServerOptions } from "./ws-server.cjs";
|
|
2
2
|
import { AFS } from "@aigne/afs";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* Start the AFS Explorer web server
|
|
6
|
+
* Start the AFS Explorer web server (Bun.serve + WebSocket JSON-RPC)
|
|
7
7
|
* @param afs - The AFS instance to explore
|
|
8
8
|
* @param options - Server options
|
|
9
|
-
* @returns
|
|
9
|
+
* @returns Object with port, url, and stop function
|
|
10
10
|
*/
|
|
11
|
-
declare function startExplorer(afs: AFS, options?:
|
|
11
|
+
declare function startExplorer(afs: AFS, options?: WSServerOptions): Promise<{
|
|
12
|
+
port: number;
|
|
13
|
+
url: string;
|
|
14
|
+
stop: () => void;
|
|
15
|
+
}>;
|
|
12
16
|
//#endregion
|
|
13
|
-
export { type
|
|
17
|
+
export { type ConfigManager, ExplorerWSServer, type WSServerOptions, startExplorer };
|
|
14
18
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;AAWA;;;;;iBAAsB,aAAA,CACpB,GAAA,EAAK,GAAA,EACL,OAAA,GAAS,eAAA,GACR,OAAA;EAAU,IAAA;EAAc,GAAA;EAAa,IAAA;AAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfigManager, ExplorerWSServer, WSServerOptions } from "./ws-server.mjs";
|
|
2
2
|
import { AFS } from "@aigne/afs";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* Start the AFS Explorer web server
|
|
6
|
+
* Start the AFS Explorer web server (Bun.serve + WebSocket JSON-RPC)
|
|
7
7
|
* @param afs - The AFS instance to explore
|
|
8
8
|
* @param options - Server options
|
|
9
|
-
* @returns
|
|
9
|
+
* @returns Object with port, url, and stop function
|
|
10
10
|
*/
|
|
11
|
-
declare function startExplorer(afs: AFS, options?:
|
|
11
|
+
declare function startExplorer(afs: AFS, options?: WSServerOptions): Promise<{
|
|
12
|
+
port: number;
|
|
13
|
+
url: string;
|
|
14
|
+
stop: () => void;
|
|
15
|
+
}>;
|
|
12
16
|
//#endregion
|
|
13
|
-
export { type
|
|
17
|
+
export { type ConfigManager, ExplorerWSServer, type WSServerOptions, startExplorer };
|
|
14
18
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;AAWA;;;;;iBAAsB,aAAA,CACpB,GAAA,EAAK,GAAA,EACL,OAAA,GAAS,eAAA,GACR,OAAA;EAAU,IAAA;EAAc,GAAA;EAAa,IAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExplorerWSServer } from "./ws-server.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
/**
|
|
5
|
-
* Start the AFS Explorer web server
|
|
5
|
+
* Start the AFS Explorer web server (Bun.serve + WebSocket JSON-RPC)
|
|
6
6
|
* @param afs - The AFS instance to explore
|
|
7
7
|
* @param options - Server options
|
|
8
|
-
* @returns
|
|
8
|
+
* @returns Object with port, url, and stop function
|
|
9
9
|
*/
|
|
10
10
|
async function startExplorer(afs, options = {}) {
|
|
11
|
-
const server = new
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const server = new ExplorerWSServer(afs, options);
|
|
12
|
+
return {
|
|
13
|
+
...await server.start(),
|
|
14
|
+
stop: () => server.stop()
|
|
15
|
+
};
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
//#endregion
|
|
17
|
-
export {
|
|
19
|
+
export { ExplorerWSServer, startExplorer };
|
|
18
20
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AFS } from \"@aigne/afs\";\nimport {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AFS } from \"@aigne/afs\";\nimport { type ConfigManager, ExplorerWSServer, type WSServerOptions } from \"./ws-server.js\";\n\nexport { ExplorerWSServer, type ConfigManager, type WSServerOptions };\n\n/**\n * Start the AFS Explorer web server (Bun.serve + WebSocket JSON-RPC)\n * @param afs - The AFS instance to explore\n * @param options - Server options\n * @returns Object with port, url, and stop function\n */\nexport async function startExplorer(\n afs: AFS,\n options: WSServerOptions = {},\n): Promise<{ port: number; url: string; stop: () => void }> {\n const server = new ExplorerWSServer(afs, options);\n const info = await server.start();\n return { ...info, stop: () => server.stop() };\n}\n"],"mappings":";;;;;;;;;AAWA,eAAsB,cACpB,KACA,UAA2B,EAAE,EAC6B;CAC1D,MAAM,SAAS,IAAI,iBAAiB,KAAK,QAAQ;AAEjD,QAAO;EAAE,GADI,MAAM,OAAO,OAAO;EACf,YAAY,OAAO,MAAM;EAAE"}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
let node_fs = require("node:fs");
|
|
2
|
+
let node_http = require("node:http");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
|
|
5
|
+
//#region src/ws-server.ts
|
|
6
|
+
const MIME_TYPES = {
|
|
7
|
+
".html": "text/html; charset=utf-8",
|
|
8
|
+
".js": "application/javascript; charset=utf-8",
|
|
9
|
+
".css": "text/css; charset=utf-8",
|
|
10
|
+
".json": "application/json; charset=utf-8",
|
|
11
|
+
".svg": "image/svg+xml",
|
|
12
|
+
".png": "image/png",
|
|
13
|
+
".jpg": "image/jpeg",
|
|
14
|
+
".jpeg": "image/jpeg",
|
|
15
|
+
".gif": "image/gif",
|
|
16
|
+
".webp": "image/webp",
|
|
17
|
+
".bmp": "image/bmp",
|
|
18
|
+
".ico": "image/x-icon",
|
|
19
|
+
".pdf": "application/pdf",
|
|
20
|
+
".md": "text/markdown; charset=utf-8",
|
|
21
|
+
".txt": "text/plain; charset=utf-8"
|
|
22
|
+
};
|
|
23
|
+
function getMimeType(fileName) {
|
|
24
|
+
return MIME_TYPES[fileName.slice(fileName.lastIndexOf("."))] || "application/octet-stream";
|
|
25
|
+
}
|
|
26
|
+
var ExplorerWSServer = class {
|
|
27
|
+
httpServer;
|
|
28
|
+
wss;
|
|
29
|
+
afs;
|
|
30
|
+
options;
|
|
31
|
+
constructor(afs, options = {}) {
|
|
32
|
+
this.afs = afs;
|
|
33
|
+
this.options = options;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Broadcast a JSON-RPC notification to all connected WebSocket clients.
|
|
37
|
+
*/
|
|
38
|
+
broadcast(method, params) {
|
|
39
|
+
if (!this.wss) return;
|
|
40
|
+
const message = JSON.stringify({
|
|
41
|
+
method,
|
|
42
|
+
params
|
|
43
|
+
});
|
|
44
|
+
for (const client of this.wss.clients) if (client.readyState === 1) client.send(message);
|
|
45
|
+
}
|
|
46
|
+
async start() {
|
|
47
|
+
const port = this.options.port || 0;
|
|
48
|
+
const host = this.options.host || "localhost";
|
|
49
|
+
const afs = this.afs;
|
|
50
|
+
const opts = this.options;
|
|
51
|
+
const configManager = this.options.configManager;
|
|
52
|
+
const httpMiddleware = this.options.httpMiddleware;
|
|
53
|
+
const { WebSocketServer } = await import("ws");
|
|
54
|
+
const httpServer = (0, node_http.createServer)(async (req, res) => {
|
|
55
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
56
|
+
if (httpMiddleware) {
|
|
57
|
+
if (await httpMiddleware(req, res)) return;
|
|
58
|
+
}
|
|
59
|
+
if (url.pathname === "/api/read") {
|
|
60
|
+
const afsPath = url.searchParams.get("path");
|
|
61
|
+
if (!afsPath) {
|
|
62
|
+
res.writeHead(400);
|
|
63
|
+
res.end("Missing path parameter");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const content = (await afs.read(afsPath)).data?.content;
|
|
68
|
+
const mime = MIME_TYPES[afsPath.slice(afsPath.lastIndexOf("."))] || "application/octet-stream";
|
|
69
|
+
if (content instanceof Buffer || content instanceof Uint8Array) {
|
|
70
|
+
res.writeHead(200, {
|
|
71
|
+
"Content-Type": mime,
|
|
72
|
+
"Cache-Control": "no-cache"
|
|
73
|
+
});
|
|
74
|
+
res.end(Buffer.from(content));
|
|
75
|
+
} else if (content instanceof ArrayBuffer) {
|
|
76
|
+
res.writeHead(200, {
|
|
77
|
+
"Content-Type": mime,
|
|
78
|
+
"Cache-Control": "no-cache"
|
|
79
|
+
});
|
|
80
|
+
res.end(Buffer.from(new Uint8Array(content)));
|
|
81
|
+
} else {
|
|
82
|
+
const text = typeof content === "string" ? content : content !== void 0 && content !== null ? JSON.stringify(content, null, 2) : "";
|
|
83
|
+
res.writeHead(200, {
|
|
84
|
+
"Content-Type": mime.includes("charset") ? mime : `${mime}; charset=utf-8`,
|
|
85
|
+
"Cache-Control": "no-cache"
|
|
86
|
+
});
|
|
87
|
+
res.end(text);
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
res.writeHead(500);
|
|
91
|
+
res.end(err instanceof Error ? err.message : String(err));
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let fileName = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
96
|
+
fileName = fileName.replace(/\.\./g, "");
|
|
97
|
+
serveStatic(fileName, opts, res);
|
|
98
|
+
});
|
|
99
|
+
const wss = new WebSocketServer({
|
|
100
|
+
server: httpServer,
|
|
101
|
+
path: "/ws"
|
|
102
|
+
});
|
|
103
|
+
this.wss = wss;
|
|
104
|
+
wss.on("connection", (ws) => {
|
|
105
|
+
ws.on("message", async (raw) => {
|
|
106
|
+
let req;
|
|
107
|
+
try {
|
|
108
|
+
const text = typeof raw === "string" ? raw : raw.toString("utf-8");
|
|
109
|
+
req = JSON.parse(text);
|
|
110
|
+
} catch {
|
|
111
|
+
ws.send(JSON.stringify({
|
|
112
|
+
id: null,
|
|
113
|
+
error: {
|
|
114
|
+
code: -32700,
|
|
115
|
+
message: "Parse error"
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!req.method || req.id === void 0) {
|
|
121
|
+
ws.send(JSON.stringify({
|
|
122
|
+
id: req.id ?? null,
|
|
123
|
+
error: {
|
|
124
|
+
code: -32600,
|
|
125
|
+
message: "Invalid request"
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const response = await handleRPC(afs, req, configManager);
|
|
131
|
+
ws.send(JSON.stringify(response));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
this.httpServer = httpServer;
|
|
135
|
+
return new Promise((resolve) => {
|
|
136
|
+
httpServer.listen(port, host, () => {
|
|
137
|
+
const addr = httpServer.address();
|
|
138
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
139
|
+
const serverUrl = `http://${host}:${actualPort}`;
|
|
140
|
+
if (opts.open) {
|
|
141
|
+
const { exec } = require("node:child_process");
|
|
142
|
+
exec(`${process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"} ${serverUrl}`);
|
|
143
|
+
}
|
|
144
|
+
resolve({
|
|
145
|
+
port: actualPort,
|
|
146
|
+
url: serverUrl
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
stop() {
|
|
152
|
+
if (this.wss) {
|
|
153
|
+
this.wss.close();
|
|
154
|
+
this.wss = void 0;
|
|
155
|
+
}
|
|
156
|
+
if (this.httpServer) {
|
|
157
|
+
this.httpServer.close();
|
|
158
|
+
this.httpServer = void 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
function serveStatic(fileName, opts, res) {
|
|
163
|
+
if (opts.embeddedAssets && fileName in opts.embeddedAssets) {
|
|
164
|
+
res.writeHead(200, { "Content-Type": getMimeType(fileName) });
|
|
165
|
+
res.end(opts.embeddedAssets[fileName]);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (opts.webRoot) {
|
|
169
|
+
const filePath = (0, node_path.join)(opts.webRoot, fileName);
|
|
170
|
+
if ((0, node_fs.existsSync)(filePath)) {
|
|
171
|
+
const content = (0, node_fs.readFileSync)(filePath);
|
|
172
|
+
res.writeHead(200, {
|
|
173
|
+
"Content-Type": getMimeType(fileName),
|
|
174
|
+
"Cache-Control": "no-cache"
|
|
175
|
+
});
|
|
176
|
+
res.end(content);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
res.writeHead(404);
|
|
181
|
+
res.end("Not Found");
|
|
182
|
+
}
|
|
183
|
+
async function handleRPC(afs, req, configManager) {
|
|
184
|
+
const params = req.params || {};
|
|
185
|
+
try {
|
|
186
|
+
switch (req.method) {
|
|
187
|
+
case "list": {
|
|
188
|
+
const path = params.path || "/";
|
|
189
|
+
const maxDepth = params.maxDepth ?? 1;
|
|
190
|
+
const result = await afs.list(path, { maxDepth });
|
|
191
|
+
return {
|
|
192
|
+
id: req.id,
|
|
193
|
+
result: { list: result.data }
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case "read": {
|
|
197
|
+
const path = params.path;
|
|
198
|
+
if (!path) return {
|
|
199
|
+
id: req.id,
|
|
200
|
+
error: {
|
|
201
|
+
code: -32602,
|
|
202
|
+
message: "path is required"
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const entry = (await afs.read(path)).data;
|
|
206
|
+
let content = entry?.content;
|
|
207
|
+
if (content instanceof Buffer || content instanceof Uint8Array) content = new TextDecoder().decode(content);
|
|
208
|
+
else if (content instanceof ArrayBuffer) content = new TextDecoder().decode(new Uint8Array(content));
|
|
209
|
+
else if (typeof content === "object" && content !== null) content = JSON.stringify(content, null, 2);
|
|
210
|
+
else if (content !== void 0 && typeof content !== "string") content = String(content);
|
|
211
|
+
return {
|
|
212
|
+
id: req.id,
|
|
213
|
+
result: {
|
|
214
|
+
content,
|
|
215
|
+
entry
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
case "stat": {
|
|
220
|
+
const path = params.path;
|
|
221
|
+
if (!path) return {
|
|
222
|
+
id: req.id,
|
|
223
|
+
error: {
|
|
224
|
+
code: -32602,
|
|
225
|
+
message: "path is required"
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const result = await afs.stat(path);
|
|
229
|
+
return {
|
|
230
|
+
id: req.id,
|
|
231
|
+
result: result.data || null
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case "search": {
|
|
235
|
+
const path = params.path || "/";
|
|
236
|
+
const pattern = params.pattern;
|
|
237
|
+
if (!pattern) return {
|
|
238
|
+
id: req.id,
|
|
239
|
+
error: {
|
|
240
|
+
code: -32602,
|
|
241
|
+
message: "pattern is required"
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const result = await afs.search(path, pattern);
|
|
245
|
+
return {
|
|
246
|
+
id: req.id,
|
|
247
|
+
result
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
case "exec": {
|
|
251
|
+
const path = params.path;
|
|
252
|
+
if (!path) return {
|
|
253
|
+
id: req.id,
|
|
254
|
+
error: {
|
|
255
|
+
code: -32602,
|
|
256
|
+
message: "path is required"
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const args = params.args || {};
|
|
260
|
+
const result = await afs.exec(path, args, {});
|
|
261
|
+
return {
|
|
262
|
+
id: req.id,
|
|
263
|
+
result
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
case "explain": {
|
|
267
|
+
const path = params.path;
|
|
268
|
+
if (!path) return {
|
|
269
|
+
id: req.id,
|
|
270
|
+
error: {
|
|
271
|
+
code: -32602,
|
|
272
|
+
message: "path is required"
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const result = await afs.stat(path);
|
|
276
|
+
return {
|
|
277
|
+
id: req.id,
|
|
278
|
+
result: result.data || null
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
case "write": {
|
|
282
|
+
const path = params.path;
|
|
283
|
+
if (!path) return {
|
|
284
|
+
id: req.id,
|
|
285
|
+
error: {
|
|
286
|
+
code: -32602,
|
|
287
|
+
message: "path is required"
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const content = params.content;
|
|
291
|
+
await afs.write(path, { content });
|
|
292
|
+
return {
|
|
293
|
+
id: req.id,
|
|
294
|
+
result: { success: true }
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
case "delete": {
|
|
298
|
+
const path = params.path;
|
|
299
|
+
if (!path) return {
|
|
300
|
+
id: req.id,
|
|
301
|
+
error: {
|
|
302
|
+
code: -32602,
|
|
303
|
+
message: "path is required"
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const result = await afs.delete(path);
|
|
307
|
+
return {
|
|
308
|
+
id: req.id,
|
|
309
|
+
result
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
case "getMounts": {
|
|
313
|
+
const mounts = afs.getMounts();
|
|
314
|
+
const mountInfos = await Promise.all(mounts.map(async (m) => {
|
|
315
|
+
const ctor = m.module.constructor;
|
|
316
|
+
const manifest = typeof ctor.manifest === "function" ? ctor.manifest() : null;
|
|
317
|
+
let url;
|
|
318
|
+
try {
|
|
319
|
+
if (typeof m.module.stat === "function") url = (await m.module.stat("/", {}))?.data?.meta?.url || void 0;
|
|
320
|
+
} catch {}
|
|
321
|
+
return {
|
|
322
|
+
namespace: m.namespace,
|
|
323
|
+
path: m.path,
|
|
324
|
+
name: m.module.name,
|
|
325
|
+
description: m.module.description,
|
|
326
|
+
accessMode: m.module.accessMode,
|
|
327
|
+
category: manifest?.category || void 0,
|
|
328
|
+
tags: manifest?.tags || void 0,
|
|
329
|
+
url
|
|
330
|
+
};
|
|
331
|
+
}));
|
|
332
|
+
return {
|
|
333
|
+
id: req.id,
|
|
334
|
+
result: { mounts: mountInfos }
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
case "mount.list": {
|
|
338
|
+
if (!configManager) return {
|
|
339
|
+
id: req.id,
|
|
340
|
+
error: {
|
|
341
|
+
code: -32601,
|
|
342
|
+
message: "Mount management not available"
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const result = await configManager.getMountList();
|
|
346
|
+
return {
|
|
347
|
+
id: req.id,
|
|
348
|
+
result
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
case "mount.add":
|
|
352
|
+
if (!configManager) return {
|
|
353
|
+
id: req.id,
|
|
354
|
+
error: {
|
|
355
|
+
code: -32601,
|
|
356
|
+
message: "Mount management not available"
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
await configManager.addMount(params);
|
|
360
|
+
return {
|
|
361
|
+
id: req.id,
|
|
362
|
+
result: { success: true }
|
|
363
|
+
};
|
|
364
|
+
case "mount.remove": {
|
|
365
|
+
if (!configManager) return {
|
|
366
|
+
id: req.id,
|
|
367
|
+
error: {
|
|
368
|
+
code: -32601,
|
|
369
|
+
message: "Mount management not available"
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
const path = params.path;
|
|
373
|
+
if (!path) return {
|
|
374
|
+
id: req.id,
|
|
375
|
+
error: {
|
|
376
|
+
code: -32602,
|
|
377
|
+
message: "path is required"
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
await configManager.removeMount(path);
|
|
381
|
+
return {
|
|
382
|
+
id: req.id,
|
|
383
|
+
result: { success: true }
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
case "mount.update": {
|
|
387
|
+
if (!configManager) return {
|
|
388
|
+
id: req.id,
|
|
389
|
+
error: {
|
|
390
|
+
code: -32601,
|
|
391
|
+
message: "Mount management not available"
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const path = params.path;
|
|
395
|
+
if (!path) return {
|
|
396
|
+
id: req.id,
|
|
397
|
+
error: {
|
|
398
|
+
code: -32602,
|
|
399
|
+
message: "path is required"
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
const { path: _path, ...updates } = params;
|
|
403
|
+
await configManager.updateMount(path, updates);
|
|
404
|
+
return {
|
|
405
|
+
id: req.id,
|
|
406
|
+
result: { success: true }
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
case "mount.test": {
|
|
410
|
+
if (!configManager) return {
|
|
411
|
+
id: req.id,
|
|
412
|
+
error: {
|
|
413
|
+
code: -32601,
|
|
414
|
+
message: "Mount management not available"
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
const uri = params.uri;
|
|
418
|
+
if (!uri) return {
|
|
419
|
+
id: req.id,
|
|
420
|
+
error: {
|
|
421
|
+
code: -32602,
|
|
422
|
+
message: "uri is required"
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
const result = await configManager.testMount(uri, params.auth);
|
|
426
|
+
return {
|
|
427
|
+
id: req.id,
|
|
428
|
+
result
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
case "config.get": {
|
|
432
|
+
if (!configManager) return {
|
|
433
|
+
id: req.id,
|
|
434
|
+
error: {
|
|
435
|
+
code: -32601,
|
|
436
|
+
message: "Mount management not available"
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
const config = await configManager.getConfig();
|
|
440
|
+
return {
|
|
441
|
+
id: req.id,
|
|
442
|
+
result: { config }
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
case "config.reload":
|
|
446
|
+
if (!configManager) return {
|
|
447
|
+
id: req.id,
|
|
448
|
+
error: {
|
|
449
|
+
code: -32601,
|
|
450
|
+
message: "Mount management not available"
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
await configManager.reloadConfig();
|
|
454
|
+
return {
|
|
455
|
+
id: req.id,
|
|
456
|
+
result: { success: true }
|
|
457
|
+
};
|
|
458
|
+
case "registry.list": {
|
|
459
|
+
if (!configManager?.getRegistry) return {
|
|
460
|
+
id: req.id,
|
|
461
|
+
error: {
|
|
462
|
+
code: -32601,
|
|
463
|
+
message: "Registry not available"
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const providers = await configManager.getRegistry();
|
|
467
|
+
return {
|
|
468
|
+
id: req.id,
|
|
469
|
+
result: { providers }
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
default: return {
|
|
473
|
+
id: req.id,
|
|
474
|
+
error: {
|
|
475
|
+
code: -32601,
|
|
476
|
+
message: `Method not found: ${req.method}`
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
482
|
+
return {
|
|
483
|
+
id: req.id,
|
|
484
|
+
error: {
|
|
485
|
+
code: -32e3,
|
|
486
|
+
message
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
//#endregion
|
|
493
|
+
exports.ExplorerWSServer = ExplorerWSServer;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AFS } from "@aigne/afs";
|
|
2
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
3
|
+
|
|
4
|
+
//#region src/ws-server.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* ConfigManager interface for mount management operations.
|
|
7
|
+
* Implemented in CLI layer (knows config.toml path), injected into ws-server.
|
|
8
|
+
*/
|
|
9
|
+
interface ConfigManager {
|
|
10
|
+
getConfig(): Promise<unknown>;
|
|
11
|
+
getMountList(): Promise<{
|
|
12
|
+
mounts: unknown[];
|
|
13
|
+
failures: unknown[];
|
|
14
|
+
}>;
|
|
15
|
+
addMount(mount: Record<string, unknown>): Promise<void>;
|
|
16
|
+
removeMount(path: string): Promise<void>;
|
|
17
|
+
updateMount(path: string, updates: Record<string, unknown>): Promise<void>;
|
|
18
|
+
testMount(uri: string, auth?: string): Promise<{
|
|
19
|
+
success: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
providerName?: string;
|
|
22
|
+
}>;
|
|
23
|
+
reloadConfig(): Promise<void>;
|
|
24
|
+
/** Get available providers from runtime discovery (scan installed @aigne/afs-* packages). */
|
|
25
|
+
getRegistry?(): Promise<Array<{
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
category: string;
|
|
29
|
+
uriTemplate: string;
|
|
30
|
+
tags?: string[];
|
|
31
|
+
packageName?: string;
|
|
32
|
+
}>>;
|
|
33
|
+
}
|
|
34
|
+
interface WSServerOptions {
|
|
35
|
+
port?: number;
|
|
36
|
+
host?: string;
|
|
37
|
+
/** Path to web/ directory for dev mode serving. */
|
|
38
|
+
webRoot?: string;
|
|
39
|
+
/** Embedded assets (for compiled binary mode). */
|
|
40
|
+
embeddedAssets?: Record<string, string>;
|
|
41
|
+
/** Open browser on start. */
|
|
42
|
+
open?: boolean;
|
|
43
|
+
/** Optional ConfigManager for mount management RPCs. */
|
|
44
|
+
configManager?: ConfigManager;
|
|
45
|
+
/**
|
|
46
|
+
* Optional HTTP middleware. Called before static file serving.
|
|
47
|
+
* Return true if the middleware handled the request.
|
|
48
|
+
*/
|
|
49
|
+
httpMiddleware?: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
|
50
|
+
}
|
|
51
|
+
declare class ExplorerWSServer {
|
|
52
|
+
private httpServer?;
|
|
53
|
+
private wss?;
|
|
54
|
+
private afs;
|
|
55
|
+
private options;
|
|
56
|
+
constructor(afs: AFS, options?: WSServerOptions);
|
|
57
|
+
/**
|
|
58
|
+
* Broadcast a JSON-RPC notification to all connected WebSocket clients.
|
|
59
|
+
*/
|
|
60
|
+
broadcast(method: string, params?: Record<string, unknown>): void;
|
|
61
|
+
start(): Promise<{
|
|
62
|
+
port: number;
|
|
63
|
+
url: string;
|
|
64
|
+
}>;
|
|
65
|
+
stop(): void;
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { ConfigManager, ExplorerWSServer, WSServerOptions };
|
|
69
|
+
//# sourceMappingURL=ws-server.d.cts.map
|