@winstonfassett/webdev-gateway 0.1.0-alpha.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +78 -0
  4. package/dist/adapter-helpers.d.ts +64 -0
  5. package/dist/adapter-helpers.d.ts.map +1 -0
  6. package/dist/adapter-helpers.js +297 -0
  7. package/dist/adapter-helpers.js.map +1 -0
  8. package/dist/admin/assets/index-DEDI8OIx.css +2 -0
  9. package/dist/admin/assets/index-DaI40ww1.js +70 -0
  10. package/dist/admin/assets/tinykeys.module-CjuTRcEz.js +1 -0
  11. package/dist/admin/index.html +13 -0
  12. package/dist/admin-rpc.d.ts +27 -0
  13. package/dist/admin-rpc.d.ts.map +1 -0
  14. package/dist/admin-rpc.js +147 -0
  15. package/dist/admin-rpc.js.map +1 -0
  16. package/dist/admin.d.ts +10 -0
  17. package/dist/admin.d.ts.map +1 -0
  18. package/dist/admin.js +202 -0
  19. package/dist/admin.js.map +1 -0
  20. package/dist/auto-register.d.ts +10 -0
  21. package/dist/auto-register.d.ts.map +1 -0
  22. package/dist/auto-register.js +145 -0
  23. package/dist/auto-register.js.map +1 -0
  24. package/dist/cdp-relay.d.ts +110 -0
  25. package/dist/cdp-relay.d.ts.map +1 -0
  26. package/dist/cdp-relay.js +616 -0
  27. package/dist/cdp-relay.js.map +1 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +95 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/doctor.d.ts +6 -0
  33. package/dist/doctor.d.ts.map +1 -0
  34. package/dist/doctor.js +149 -0
  35. package/dist/doctor.js.map +1 -0
  36. package/dist/element-grab-client.js +305 -0
  37. package/dist/element-grab.d.ts +15 -0
  38. package/dist/element-grab.d.ts.map +1 -0
  39. package/dist/element-grab.js +102 -0
  40. package/dist/element-grab.js.map +1 -0
  41. package/dist/gateway.d.ts +5 -0
  42. package/dist/gateway.d.ts.map +1 -0
  43. package/dist/gateway.js +534 -0
  44. package/dist/gateway.js.map +1 -0
  45. package/dist/installer.d.ts +48 -0
  46. package/dist/installer.d.ts.map +1 -0
  47. package/dist/installer.js +637 -0
  48. package/dist/installer.js.map +1 -0
  49. package/dist/libs/element-source.js +35 -0
  50. package/dist/libs/modern-screenshot.js +14 -0
  51. package/dist/log-reader.d.ts +30 -0
  52. package/dist/log-reader.d.ts.map +1 -0
  53. package/dist/log-reader.js +174 -0
  54. package/dist/log-reader.js.map +1 -0
  55. package/dist/mcp-server.d.ts +22 -0
  56. package/dist/mcp-server.d.ts.map +1 -0
  57. package/dist/mcp-server.js +115 -0
  58. package/dist/mcp-server.js.map +1 -0
  59. package/dist/mcp-tools-core.d.ts +30 -0
  60. package/dist/mcp-tools-core.d.ts.map +1 -0
  61. package/dist/mcp-tools-core.js +375 -0
  62. package/dist/mcp-tools-core.js.map +1 -0
  63. package/dist/mcp-tools-full.d.ts +4 -0
  64. package/dist/mcp-tools-full.d.ts.map +1 -0
  65. package/dist/mcp-tools-full.js +141 -0
  66. package/dist/mcp-tools-full.js.map +1 -0
  67. package/dist/playwright-commands.d.ts +33 -0
  68. package/dist/playwright-commands.d.ts.map +1 -0
  69. package/dist/playwright-commands.js +356 -0
  70. package/dist/playwright-commands.js.map +1 -0
  71. package/dist/registry.d.ts +83 -0
  72. package/dist/registry.d.ts.map +1 -0
  73. package/dist/registry.js +205 -0
  74. package/dist/registry.js.map +1 -0
  75. package/dist/rpc-server.d.ts +54 -0
  76. package/dist/rpc-server.d.ts.map +1 -0
  77. package/dist/rpc-server.js +207 -0
  78. package/dist/rpc-server.js.map +1 -0
  79. package/dist/session.d.ts +13 -0
  80. package/dist/session.d.ts.map +1 -0
  81. package/dist/session.js +61 -0
  82. package/dist/session.js.map +1 -0
  83. package/dist/types.d.ts +76 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +2 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/webdev-client.js +20 -0
  88. package/dist/writers/base.d.ts +24 -0
  89. package/dist/writers/base.d.ts.map +1 -0
  90. package/dist/writers/base.js +98 -0
  91. package/dist/writers/base.js.map +1 -0
  92. package/dist/writers/console.d.ts +8 -0
  93. package/dist/writers/console.d.ts.map +1 -0
  94. package/dist/writers/console.js +14 -0
  95. package/dist/writers/console.js.map +1 -0
  96. package/dist/writers/dev-events.d.ts +28 -0
  97. package/dist/writers/dev-events.d.ts.map +1 -0
  98. package/dist/writers/dev-events.js +53 -0
  99. package/dist/writers/dev-events.js.map +1 -0
  100. package/dist/writers/errors.d.ts +8 -0
  101. package/dist/writers/errors.d.ts.map +1 -0
  102. package/dist/writers/errors.js +14 -0
  103. package/dist/writers/errors.js.map +1 -0
  104. package/dist/writers/network.d.ts +9 -0
  105. package/dist/writers/network.d.ts.map +1 -0
  106. package/dist/writers/network.js +17 -0
  107. package/dist/writers/network.js.map +1 -0
  108. package/dist/writers/server-console.d.ts +8 -0
  109. package/dist/writers/server-console.d.ts.map +1 -0
  110. package/dist/writers/server-console.js +14 -0
  111. package/dist/writers/server-console.js.map +1 -0
  112. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ var e=[`Shift`,`Meta`,`Alt`,`Control`],t=typeof navigator==`object`?navigator.platform:``,n=/Mac|iPod|iPhone|iPad/.test(t),r=n?`Meta`:`Control`,i=t===`Win32`?[`Control`,`Alt`]:n?[`Alt`]:[];function a(e,t){return typeof e.getModifierState==`function`&&(e.getModifierState(t)||i.includes(t)&&e.getModifierState(`AltGraph`))}function o(e){return e.trim().split(` `).map(function(e){var t=e.split(/\b\+/),n=t.pop();return[t=t.map(function(e){return e===`$mod`?r:e}),n]})}function s(t,n){n===void 0&&(n={});var r=n.timeout??1e3,i=Object.keys(t).map(function(e){return[o(e),t[e]]}),s=new Map,c=null;return function(t){t instanceof KeyboardEvent&&(i.forEach(function(n){var r=n[0],i=n[1],o=s.get(r)||r;(function(t,n){return!(n[1].toUpperCase()!==t.key.toUpperCase()&&n[1]!==t.code||n[0].find(function(e){return!a(t,e)})||e.find(function(e){return!n[0].includes(e)&&n[1]!==e&&a(t,e)}))})(t,o[0])?o.length>1?s.set(r,o.slice(1)):(s.delete(r),i(t)):a(t,t.key)||s.delete(r)}),c&&clearTimeout(c),c=setTimeout(s.clear.bind(s),r))}}function c(e,t,n){n===void 0&&(n={});var r=n.event??`keydown`,i=s(t,n);return e.addEventListener(r,i),function(){e.removeEventListener(r,i)}}export{s as createKeybindingsHandler,o as parseKeybinding,c as tinykeys};
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>webdev-mcp admin</title>
7
+ <script type="module" crossorigin src="/__admin/assets/index-DaI40ww1.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/__admin/assets/index-DEDI8OIx.css">
9
+ </head>
10
+ <body>
11
+ <div id="app"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Admin WebSocket RPC — capnweb RpcTarget exposing gateway state + live events.
3
+ *
4
+ * Single WS endpoint at /__admin/ws. Client connects with capnweb + PartySocket.
5
+ * Server exposes AdminAPI with methods for state queries + actions,
6
+ * and a subscribe() method returning a ReadableStream of events.
7
+ */
8
+ import { type WebSocket as WsWebSocket } from 'ws';
9
+ import { type ServerRegistry } from './registry.js';
10
+ import { type SessionState } from './session.js';
11
+ export interface AdminEvent {
12
+ type: string;
13
+ data: any;
14
+ ts: number;
15
+ }
16
+ export interface AdminDeps {
17
+ registry: ServerRegistry;
18
+ session: SessionState;
19
+ startedAt: number;
20
+ }
21
+ /** Call once at gateway startup to wire dependencies */
22
+ export declare function initAdminRpc(d: AdminDeps): void;
23
+ /** Set up /__admin/ws WebSocket upgrade handler */
24
+ export declare function setupAdminWebSocket(httpServer: {
25
+ on(event: string, listener: (...args: any[]) => void): void;
26
+ }): import("ws").Server<typeof WsWebSocket, typeof import("node:http").IncomingMessage>;
27
+ //# sourceMappingURL=admin-rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-rpc.d.ts","sourceRoot":"","sources":["../src/admin-rpc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAmB,KAAK,SAAS,IAAI,WAAW,EAAE,MAAM,IAAI,CAAA;AAInE,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAEtE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,GAAG,CAAA;IACT,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,cAAc,CAAA;IACxB,OAAO,EAAE,YAAY,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAID,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,QAExC;AAuHD,mDAAmD;AACnD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE;IAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAA;CAAE,uFAuB9G"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Admin WebSocket RPC — capnweb RpcTarget exposing gateway state + live events.
3
+ *
4
+ * Single WS endpoint at /__admin/ws. Client connects with capnweb + PartySocket.
5
+ * Server exposes AdminAPI with methods for state queries + actions,
6
+ * and a subscribe() method returning a ReadableStream of events.
7
+ */
8
+ import { RpcTarget, newWebSocketRpcSession } from 'capnweb';
9
+ import { WebSocketServer } from 'ws';
10
+ import { getAllBrowsers, browserCommand, onBrowserEvent, onLogEvent } from './rpc-server.js';
11
+ import { getMcpSessionCount } from './mcp-server.js';
12
+ import { getDiagnostics } from './log-reader.js';
13
+ import { onServerEvent } from './registry.js';
14
+ import { truncateChannelFiles } from './session.js';
15
+ let deps;
16
+ /** Call once at gateway startup to wire dependencies */
17
+ export function initAdminRpc(d) {
18
+ deps = d;
19
+ }
20
+ class AdminAPI extends RpcTarget {
21
+ /** Full gateway state snapshot */
22
+ getState() {
23
+ return {
24
+ uptime_ms: Date.now() - deps.startedAt,
25
+ mode: deps.registry.size() > 0 ? 'hybrid' : 'hub',
26
+ browsers: getAllBrowsers(),
27
+ servers: deps.registry.getAll(),
28
+ mcp_sessions: getMcpSessionCount(),
29
+ };
30
+ }
31
+ /** Query logs/diagnostics for a server */
32
+ getLogs(opts) {
33
+ let logPaths;
34
+ if (opts?.serverId) {
35
+ const server = deps.registry.get(opts.serverId);
36
+ logPaths = server?.logPaths ?? deps.session.files;
37
+ }
38
+ else {
39
+ logPaths = deps.session.files;
40
+ }
41
+ return getDiagnostics(logPaths, deps.session, {
42
+ limit: opts?.limit ?? 200,
43
+ level: opts?.level,
44
+ search: opts?.search,
45
+ browserId: opts?.browserId,
46
+ });
47
+ }
48
+ /** Execute JS in a browser */
49
+ async evalInBrowser(code, serverId) {
50
+ if (!code)
51
+ throw new Error('Missing code');
52
+ return browserCommand({ serverId }, 'eval', { code });
53
+ }
54
+ /**
55
+ * Clear logs server-side. Persists across reload.
56
+ * - browserId: sets a per-browser checkpoint; other browsers on the same
57
+ * server keep their logs (shared NDJSON file can't be truncated for one).
58
+ * - serverId: truncates that server's NDJSON files.
59
+ * - serverIds: truncates each listed server's files.
60
+ * - none: truncates session files + all registered servers' files.
61
+ */
62
+ clearLogs(opts) {
63
+ if (opts?.browserId) {
64
+ const ts = Date.now();
65
+ deps.session.browserCheckpoints[opts.browserId] = ts;
66
+ return { success: true, scope: 'browser', browserId: opts.browserId, ts };
67
+ }
68
+ const channels = opts?.channels;
69
+ const truncated = {};
70
+ if (opts?.serverIds?.length) {
71
+ for (const id of opts.serverIds) {
72
+ const server = deps.registry.get(id);
73
+ if (!server)
74
+ continue;
75
+ truncated[id] = truncateChannelFiles(server.logPaths, channels);
76
+ }
77
+ }
78
+ else if (opts?.serverId) {
79
+ const server = deps.registry.get(opts.serverId);
80
+ if (!server)
81
+ throw new Error(`Server ${opts.serverId} not found`);
82
+ truncated[opts.serverId] = truncateChannelFiles(server.logPaths, channels);
83
+ }
84
+ else {
85
+ truncated['__session'] = truncateChannelFiles(deps.session.files, channels);
86
+ for (const server of deps.registry.getAll()) {
87
+ truncated[server.id] = truncateChannelFiles(server.logPaths, channels);
88
+ }
89
+ // Whole-gateway clear supersedes any per-browser checkpoints
90
+ deps.session.browserCheckpoints = {};
91
+ }
92
+ deps.session.checkpointTs = Date.now();
93
+ return { success: true, truncated };
94
+ }
95
+ /** Subscribe to live events — returns a ReadableStream pushed by the server */
96
+ subscribe() {
97
+ let cleanup;
98
+ return new ReadableStream({
99
+ start(controller) {
100
+ const unsubBrowser = onBrowserEvent((event, data) => {
101
+ const type = event === 'connect' ? 'browser_connect'
102
+ : event === 'init' ? 'browser_init'
103
+ : 'browser_disconnect';
104
+ controller.enqueue({ type, data, ts: Date.now() });
105
+ });
106
+ const unsubLog = onLogEvent((data) => {
107
+ controller.enqueue({ type: 'log', data, ts: Date.now() });
108
+ });
109
+ const unsubServer = onServerEvent((event, data) => {
110
+ const type = event === 'register' ? 'server_register' : 'server_deregister';
111
+ controller.enqueue({ type, data, ts: Date.now() });
112
+ });
113
+ cleanup = () => {
114
+ unsubBrowser();
115
+ unsubLog();
116
+ unsubServer();
117
+ };
118
+ },
119
+ cancel() {
120
+ cleanup?.();
121
+ },
122
+ });
123
+ }
124
+ }
125
+ /** Set up /__admin/ws WebSocket upgrade handler */
126
+ export function setupAdminWebSocket(httpServer) {
127
+ const wss = new WebSocketServer({ noServer: true });
128
+ httpServer.on('upgrade', (request, socket, head) => {
129
+ const url = request.url ?? '';
130
+ if (url === '/__admin/ws' || url.startsWith('/__admin/ws?')) {
131
+ wss.handleUpgrade(request, socket, head, (ws) => {
132
+ wss.emit('connection', ws, request);
133
+ });
134
+ }
135
+ });
136
+ wss.on('connection', (ws) => {
137
+ const api = new AdminAPI();
138
+ // capnweb expects standard WebSocket API — ws package is compatible
139
+ newWebSocketRpcSession(ws, api);
140
+ console.log('[admin-rpc] Client connected');
141
+ ws.on('close', () => {
142
+ console.log('[admin-rpc] Client disconnected');
143
+ });
144
+ });
145
+ return wss;
146
+ }
147
+ //# sourceMappingURL=admin-rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-rpc.js","sourceRoot":"","sources":["../src/admin-rpc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { ServerRegistry } from './registry.js';
3
+ import { type SessionState } from './session.js';
4
+ export declare function handleAdmin(req: IncomingMessage, res: ServerResponse, url: string, opts: {
5
+ startedAt: number;
6
+ registry: ServerRegistry;
7
+ port: number;
8
+ session: SessionState;
9
+ }): boolean;
10
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAOhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AA4DtE,wBAAgB,WAAW,CACzB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACzF,OAAO,CAmJT"}
package/dist/admin.js ADDED
@@ -0,0 +1,202 @@
1
+ // Admin UI for webdev gateway
2
+ // Serves built admin at /__admin and JSON API at /__admin/api
3
+ import { readFileSync, existsSync, statSync } from 'node:fs';
4
+ import { join, dirname, extname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { getAllBrowsers, browserCommand } from './rpc-server.js';
7
+ import { getMcpSessionCount } from './mcp-server.js';
8
+ import { getDiagnostics } from './log-reader.js';
9
+ import { truncateChannelFiles } from './session.js';
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const ADMIN_DIR = join(__dirname, 'admin');
12
+ const MIME_TYPES = {
13
+ '.html': 'text/html',
14
+ '.js': 'application/javascript',
15
+ '.css': 'text/css',
16
+ '.svg': 'image/svg+xml',
17
+ '.png': 'image/png',
18
+ '.ico': 'image/x-icon',
19
+ '.json': 'application/json',
20
+ '.woff': 'font/woff',
21
+ '.woff2': 'font/woff2',
22
+ };
23
+ // Cache static files in memory (small admin bundle)
24
+ const fileCache = new Map();
25
+ function serveStatic(res, filePath) {
26
+ let cached = fileCache.get(filePath);
27
+ if (!cached) {
28
+ if (!existsSync(filePath))
29
+ return false;
30
+ try {
31
+ if (!statSync(filePath).isFile())
32
+ return false;
33
+ const content = readFileSync(filePath);
34
+ const mime = MIME_TYPES[extname(filePath)] || 'application/octet-stream';
35
+ cached = { content, mime };
36
+ fileCache.set(filePath, cached);
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ res.writeHead(200, {
43
+ 'Content-Type': cached.mime,
44
+ 'Cache-Control': 'no-cache',
45
+ 'Access-Control-Allow-Origin': '*',
46
+ });
47
+ res.end(cached.content);
48
+ return true;
49
+ }
50
+ function readBody(req) {
51
+ return new Promise((resolve, reject) => {
52
+ const chunks = [];
53
+ req.on('data', (c) => chunks.push(c));
54
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
55
+ req.on('error', reject);
56
+ });
57
+ }
58
+ function jsonResponse(res, status, data) {
59
+ res.writeHead(status, {
60
+ 'Content-Type': 'application/json',
61
+ 'Access-Control-Allow-Origin': '*',
62
+ });
63
+ res.end(JSON.stringify(data));
64
+ }
65
+ export function handleAdmin(req, res, url, opts) {
66
+ // CORS preflight
67
+ if (req.method === 'OPTIONS' && url.startsWith('/__admin/')) {
68
+ res.writeHead(204, {
69
+ 'Access-Control-Allow-Origin': '*',
70
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
71
+ 'Access-Control-Allow-Headers': 'Content-Type',
72
+ });
73
+ res.end();
74
+ return true;
75
+ }
76
+ // POST /__admin/eval — execute JS in a browser
77
+ if (url === '/__admin/eval' && req.method === 'POST') {
78
+ readBody(req).then(async (body) => {
79
+ try {
80
+ const { code, serverId } = JSON.parse(body);
81
+ if (!code) {
82
+ jsonResponse(res, 400, { error: 'Missing "code" field' });
83
+ return;
84
+ }
85
+ const result = await browserCommand({ serverId }, 'eval', { code });
86
+ jsonResponse(res, 200, { result });
87
+ }
88
+ catch (err) {
89
+ jsonResponse(res, 500, { error: err.message ?? String(err) });
90
+ }
91
+ });
92
+ return true;
93
+ }
94
+ // POST /__admin/logs/clear — clear logs server-side (truncate or checkpoint)
95
+ if (url === '/__admin/logs/clear' && req.method === 'POST') {
96
+ readBody(req).then((body) => {
97
+ try {
98
+ const data = body ? JSON.parse(body) : {};
99
+ const { serverId, serverIds, browserId, channels } = data;
100
+ if (browserId) {
101
+ const ts = Date.now();
102
+ opts.session.browserCheckpoints[browserId] = ts;
103
+ jsonResponse(res, 200, { success: true, scope: 'browser', browserId, ts });
104
+ return;
105
+ }
106
+ const truncated = {};
107
+ if (serverIds && serverIds.length > 0) {
108
+ for (const id of serverIds) {
109
+ const server = opts.registry.get(id);
110
+ if (!server)
111
+ continue;
112
+ truncated[id] = truncateChannelFiles(server.logPaths, channels);
113
+ }
114
+ }
115
+ else if (serverId) {
116
+ const server = opts.registry.get(serverId);
117
+ if (!server) {
118
+ jsonResponse(res, 404, { error: `Server ${serverId} not found` });
119
+ return;
120
+ }
121
+ truncated[serverId] = truncateChannelFiles(server.logPaths, channels);
122
+ }
123
+ else {
124
+ truncated['__session'] = truncateChannelFiles(opts.session.files, channels);
125
+ for (const server of opts.registry.getAll()) {
126
+ truncated[server.id] = truncateChannelFiles(server.logPaths, channels);
127
+ }
128
+ opts.session.browserCheckpoints = {};
129
+ }
130
+ opts.session.checkpointTs = Date.now();
131
+ jsonResponse(res, 200, { success: true, truncated });
132
+ }
133
+ catch (err) {
134
+ jsonResponse(res, 500, { error: err.message ?? String(err) });
135
+ }
136
+ });
137
+ return true;
138
+ }
139
+ // GET /__admin/logs — query diagnostics for a project
140
+ if (url.startsWith('/__admin/logs') && req.method === 'GET') {
141
+ const params = new URL(url, 'http://localhost').searchParams;
142
+ const serverId = params.get('server_id') || undefined;
143
+ const limit = parseInt(params.get('limit') || '200', 10);
144
+ const level = params.get('level') || undefined;
145
+ const search = params.get('search') || undefined;
146
+ const browserId = params.get('browser_id') || undefined;
147
+ let logPaths;
148
+ if (serverId) {
149
+ const server = opts.registry.get(serverId);
150
+ logPaths = server?.logPaths ?? opts.session.files;
151
+ }
152
+ else {
153
+ logPaths = opts.session.files;
154
+ }
155
+ const result = getDiagnostics(logPaths, opts.session, { limit, level, search, browserId });
156
+ jsonResponse(res, 200, result);
157
+ return true;
158
+ }
159
+ // JSON API
160
+ if (url === '/__admin/api') {
161
+ res.writeHead(200, {
162
+ 'Content-Type': 'application/json',
163
+ 'Access-Control-Allow-Origin': '*',
164
+ });
165
+ res.end(JSON.stringify({
166
+ uptime_ms: Date.now() - opts.startedAt,
167
+ mode: opts.registry.size() > 0 ? 'hybrid' : 'hub',
168
+ browsers: getAllBrowsers(),
169
+ servers: opts.registry.getAll(),
170
+ mcp_sessions: getMcpSessionCount(),
171
+ }));
172
+ return true;
173
+ }
174
+ // Serve built admin UI (allow querystring, e.g. /__admin?server=...)
175
+ const adminPath = url.split('?')[0];
176
+ if (adminPath === '/__admin' || adminPath === '/__admin/') {
177
+ const indexPath = join(ADMIN_DIR, 'index.html');
178
+ if (serveStatic(res, indexPath))
179
+ return true;
180
+ // Fallback: admin not built yet
181
+ res.writeHead(200, { 'Content-Type': 'text/html' });
182
+ res.end('<html><body style="font-family:system-ui;background:#0a0a0a;color:#888;padding:2rem"><h1>Admin not built</h1><p>Run <code>npm run build</code> in apps/admin-svelte/ first.</p></body></html>');
183
+ return true;
184
+ }
185
+ // Serve admin assets (/__admin/assets/*, etc.)
186
+ if (url.startsWith('/__admin/')) {
187
+ const assetPath = url.slice('/__admin/'.length).split('?')[0];
188
+ const filePath = join(ADMIN_DIR, assetPath);
189
+ if (serveStatic(res, filePath))
190
+ return true;
191
+ // SPA fallback: non-asset paths (no file extension) serve index.html
192
+ // so client-side routing works for /__admin/project/foo, etc.
193
+ if (req.method === 'GET' && !extname(assetPath)) {
194
+ const indexPath = join(ADMIN_DIR, 'index.html');
195
+ if (serveStatic(res, indexPath))
196
+ return true;
197
+ }
198
+ return false;
199
+ }
200
+ return false;
201
+ }
202
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** Optional callbacks for surfacing non-fatal events during auto-register */
2
+ export interface AutoRegisterReporter {
3
+ onParseError?: (filePath: string, error: Error) => void;
4
+ onPermissionError?: (filePath: string, error: Error) => void;
5
+ }
6
+ /** Register MCP in project-level config files */
7
+ export declare function autoRegister(cwd: string, mcpUrl: string, reporter?: AutoRegisterReporter): string[];
8
+ /** Register MCP in global/user-level config files */
9
+ export declare function autoRegisterGlobal(mcpUrl: string, reporter?: AutoRegisterReporter): string[];
10
+ //# sourceMappingURL=auto-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../src/auto-register.ts"],"names":[],"mappings":"AA0BA,6EAA6E;AAC7E,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACvD,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CAC7D;AAuFD,iDAAiD;AACjD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAcnG;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAc5F"}
@@ -0,0 +1,145 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { applyEdits, modify, parse, parseTree } from 'jsonc-parser';
5
+ /** Project-level config paths (relative to cwd) */
6
+ const PROJECT_AGENTS = {
7
+ claude: { relPath: '.mcp.json', serversKey: 'mcpServers' },
8
+ cursor: { relPath: '.cursor/mcp.json', serversKey: 'mcpServers' },
9
+ windsurf: { relPath: '.windsurf/mcp.json', serversKey: 'mcpServers' },
10
+ vscode: { relPath: '.vscode/mcp.json', serversKey: 'servers' },
11
+ };
12
+ /** Global/user-level config paths (relative to home dir) */
13
+ const GLOBAL_AGENTS = {
14
+ claude: { relPath: '.claude/settings.json', serversKey: 'mcpServers' },
15
+ cursor: { relPath: '.cursor/mcp.json', serversKey: 'mcpServers' },
16
+ windsurf: { relPath: '.windsurf/mcp.json', serversKey: 'mcpServers' },
17
+ };
18
+ /**
19
+ * Detect indentation from existing source. Returns 2 if file is empty/new or
20
+ * indent can't be inferred — matches the common style for `.json` / `.mcp.json`.
21
+ */
22
+ function detectIndent(source) {
23
+ const m = source.match(/\n([ \t]+)\S/);
24
+ if (!m)
25
+ return 2;
26
+ const indent = m[1];
27
+ if (indent.startsWith('\t'))
28
+ return 1;
29
+ return indent.length || 2;
30
+ }
31
+ function detectEol(source) {
32
+ return source.includes('\r\n') ? '\r\n' : '\n';
33
+ }
34
+ function upsertMcpServer(filePath, serversKey, mcpUrl, reporter) {
35
+ const dir = dirname(filePath);
36
+ const exists = existsSync(filePath);
37
+ const original = exists ? readFileSync(filePath, 'utf-8') : '';
38
+ // Validate parse first — surfaces JSONC-with-syntax-errors before we attempt edits.
39
+ if (exists && original.trim() !== '') {
40
+ try {
41
+ const errors = [];
42
+ parse(original, errors, { allowTrailingComma: true });
43
+ if (errors.length > 0) {
44
+ const first = errors[0];
45
+ throw new Error(`JSONC parse error at offset ${first.offset}`);
46
+ }
47
+ }
48
+ catch (err) {
49
+ if (reporter?.onParseError)
50
+ reporter.onParseError(filePath, err);
51
+ else
52
+ console.error(` Skipped ${filePath}: could not parse existing JSON`);
53
+ return false;
54
+ }
55
+ }
56
+ const formattingOptions = {
57
+ tabSize: detectIndent(original || '{}'),
58
+ insertSpaces: !(original.includes('\n\t')),
59
+ eol: detectEol(original),
60
+ };
61
+ const serverEntry = { url: mcpUrl };
62
+ let updated;
63
+ if (!exists || original.trim() === '') {
64
+ // Fresh file: write a minimal well-formed object with our entry.
65
+ const indent = ' '.repeat(formattingOptions.tabSize ?? 2);
66
+ const eol = formattingOptions.eol ?? '\n';
67
+ updated = `{${eol}${indent}"${serversKey}": {${eol}${indent}${indent}"webdev": ${JSON.stringify(serverEntry)}${eol}${indent}}${eol}}${eol}`;
68
+ }
69
+ else {
70
+ // Existing file: precise edit via jsonc-parser. Only the targeted key changes;
71
+ // comments, formatting, key order everywhere else are preserved byte-for-byte.
72
+ const tree = parseTree(original, [], { allowTrailingComma: true });
73
+ const hasServersKey = tree?.type === 'object' &&
74
+ tree.children?.some((c) => c.type === 'property' && c.children?.[0]?.value === serversKey);
75
+ if (!hasServersKey) {
76
+ // Parent missing: insert the whole `serversKey: { 'webdev': {...} }` block in one edit.
77
+ const edits = modify(original, [serversKey], { 'webdev': serverEntry }, { formattingOptions });
78
+ updated = applyEdits(original, edits);
79
+ }
80
+ else {
81
+ // Parent exists: just upsert our key. Replaces only the value if already present.
82
+ const edits = modify(original, [serversKey, 'webdev'], serverEntry, { formattingOptions });
83
+ updated = applyEdits(original, edits);
84
+ }
85
+ }
86
+ try {
87
+ mkdirSync(dir, { recursive: true });
88
+ writeFileSync(filePath, updated);
89
+ }
90
+ catch (err) {
91
+ const e = err;
92
+ if (e.code === 'EACCES' || e.code === 'EPERM') {
93
+ if (reporter?.onPermissionError)
94
+ reporter.onPermissionError(filePath, e);
95
+ else
96
+ console.error(` Skipped ${filePath}: permission denied (${e.code})`);
97
+ return false;
98
+ }
99
+ throw err;
100
+ }
101
+ return true;
102
+ }
103
+ /** Register MCP in project-level config files */
104
+ export function autoRegister(cwd, mcpUrl, reporter) {
105
+ const registered = [];
106
+ for (const [_agent, { relPath, serversKey }] of Object.entries(PROJECT_AGENTS)) {
107
+ const filePath = join(cwd, relPath);
108
+ if (upsertMcpServer(filePath, serversKey, mcpUrl, reporter)) {
109
+ registered.push(relPath);
110
+ }
111
+ }
112
+ // Ensure .webdev is gitignored
113
+ ensureGitignore(cwd, '.webdev');
114
+ return registered;
115
+ }
116
+ /** Register MCP in global/user-level config files */
117
+ export function autoRegisterGlobal(mcpUrl, reporter) {
118
+ const home = homedir();
119
+ const registered = [];
120
+ for (const [_agent, { relPath, serversKey }] of Object.entries(GLOBAL_AGENTS)) {
121
+ const filePath = join(home, relPath);
122
+ // Only write to global configs that already exist (don't create dirs for tools the user doesn't have)
123
+ if (!existsSync(dirname(filePath)))
124
+ continue;
125
+ if (upsertMcpServer(filePath, serversKey, mcpUrl, reporter)) {
126
+ registered.push(`~/${relPath}`);
127
+ }
128
+ }
129
+ return registered;
130
+ }
131
+ /** Add an entry to .gitignore if not already present */
132
+ function ensureGitignore(cwd, entry) {
133
+ const gitignorePath = join(cwd, '.gitignore');
134
+ if (existsSync(gitignorePath)) {
135
+ const content = readFileSync(gitignorePath, 'utf-8');
136
+ if (content.split('\n').some(line => line.trim() === entry))
137
+ return;
138
+ const needsNewline = content.length > 0 && !content.endsWith('\n');
139
+ appendFileSync(gitignorePath, (needsNewline ? '\n' : '') + entry + '\n');
140
+ }
141
+ else {
142
+ writeFileSync(gitignorePath, entry + '\n');
143
+ }
144
+ }
145
+ //# sourceMappingURL=auto-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-register.js","sourceRoot":"","sources":["../src/auto-register.ts"],"names":[],"mappings":"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"}