@ggui-ai/mcp-server 0.6.3 → 0.8.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/dist/api-renders-routes.d.ts.map +1 -1
- package/dist/api-renders-routes.js +9 -1
- package/dist/browser-cors.d.ts +29 -0
- package/dist/browser-cors.d.ts.map +1 -0
- package/dist/browser-cors.js +64 -0
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +5 -1
- package/dist/code-store-fs.d.ts +3 -0
- package/dist/code-store-fs.d.ts.map +1 -1
- package/dist/code-store-fs.js +27 -3
- package/dist/console-session-routes.d.ts +10 -5
- package/dist/console-session-routes.d.ts.map +1 -1
- package/dist/console-session-routes.js +10 -5
- package/dist/ggui-session-channel/outbound.d.ts.map +1 -1
- package/dist/ggui-session-channel/outbound.js +12 -0
- package/dist/ggui-session-channel/socket-router.d.ts +33 -0
- package/dist/ggui-session-channel/socket-router.d.ts.map +1 -1
- package/dist/ggui-session-channel/socket-router.js +155 -0
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
- package/dist/ggui-session-channel/subscribe.js +43 -2
- package/dist/ggui-session-channel.d.ts +114 -0
- package/dist/ggui-session-channel.d.ts.map +1 -1
- package/dist/ggui-session-channel.js +77 -2
- package/dist/health-routes.d.ts +3 -0
- package/dist/health-routes.d.ts.map +1 -1
- package/dist/health-routes.js +11 -1
- package/dist/mcp-apps-outbound.d.ts +236 -31
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +940 -261
- package/dist/origin-validation.d.ts +120 -0
- package/dist/origin-validation.d.ts.map +1 -0
- package/dist/origin-validation.js +199 -0
- package/dist/render-read-gate.d.ts +50 -0
- package/dist/render-read-gate.d.ts.map +1 -0
- package/dist/render-read-gate.js +36 -0
- package/dist/runtime-bundle-route.d.ts +15 -0
- package/dist/runtime-bundle-route.d.ts.map +1 -1
- package/dist/runtime-bundle-route.js +20 -4
- package/dist/server.d.ts +227 -9
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +292 -28
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-renders-routes.d.ts","sourceRoot":"","sources":["../src/api-renders-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAO9F,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC;IACzD,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,KACV;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,oEAAoE;IACpE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"api-renders-routes.d.ts","sourceRoot":"","sources":["../src/api-renders-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAO9F,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC;IACzD,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,KACV;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,oEAAoE;IACpE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAgV9D"}
|
|
@@ -179,7 +179,15 @@ export function mountApiRendersRoutes(opts) {
|
|
|
179
179
|
renderCodeUrl = `${base}/code/${hash}.js`;
|
|
180
180
|
}
|
|
181
181
|
catch {
|
|
182
|
-
// Silent — caller falls back to live-mode delivery
|
|
182
|
+
// Silent — the caller falls back to live-mode delivery, and
|
|
183
|
+
// here that fallback is structural rather than hoped for:
|
|
184
|
+
// `mintBootstrap` is optional on the options type, but these
|
|
185
|
+
// routes mount only under `mcpAppsEnabled` + a render store
|
|
186
|
+
// + a token secret, which are the same preconditions that
|
|
187
|
+
// assign the minter. So a reader of THIS file cannot see the
|
|
188
|
+
// guarantee — it lives at the mount site, and it is what
|
|
189
|
+
// keeps the silence honest. Lose that pairing and this catch
|
|
190
|
+
// becomes a swallow.
|
|
183
191
|
}
|
|
184
192
|
try {
|
|
185
193
|
const bundle = await deriveContractBundle(render);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS for browser-resident MCP clients (ggui#438b).
|
|
3
|
+
*
|
|
4
|
+
* SCOPE: this is enablement, not conformance. No MCP spec layer requires
|
|
5
|
+
* CORS — the transport spec never mentions it, and the MCP Apps spec is
|
|
6
|
+
* fully host-mediated (the view postMessages its host; the host holds the
|
|
7
|
+
* MCP connection). claude.ai and ChatGPT connect to remote MCP servers
|
|
8
|
+
* from their BACKENDS, so they need none of this. It exists for clients
|
|
9
|
+
* where the browser itself holds the Streamable HTTP connection: guuey's
|
|
10
|
+
* browser host layer, local dev SPAs, Electron renderers.
|
|
11
|
+
*
|
|
12
|
+
* POSTURE: an origin ALLOWLIST, never `*`. The three existing `*` routes
|
|
13
|
+
* (runtime-bundle, code, api-renders) are credential-free or token-gated
|
|
14
|
+
* public reads; `/mcp` is different — `cookieAuthMiddleware` promotes the
|
|
15
|
+
* session cookie to a Bearer on it, so it belongs to the cookie-authed
|
|
16
|
+
* class. `Access-Control-Allow-Credentials` is NEVER set.
|
|
17
|
+
*
|
|
18
|
+
* LAYER RELATIONSHIP: origin-validation runs first and decides WHETHER a
|
|
19
|
+
* request executes at all (the spec-mandated 403); this layer decides HOW
|
|
20
|
+
* the browser is told it may read the response. A disallowed origin never
|
|
21
|
+
* reaches this layer's headers in production — its rejection branch is
|
|
22
|
+
* defense-in-depth. CORS is never the security boundary.
|
|
23
|
+
*/
|
|
24
|
+
import type { RequestHandler } from "express";
|
|
25
|
+
import { type OriginHostPolicy } from "./origin-validation.js";
|
|
26
|
+
export declare function createBrowserCorsMiddleware(opts: {
|
|
27
|
+
readonly policy: OriginHostPolicy;
|
|
28
|
+
}): RequestHandler;
|
|
29
|
+
//# sourceMappingURL=browser-cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-cors.d.ts","sourceRoot":"","sources":["../src/browser-cors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AA8BnF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC,GAAG,cAAc,CA+CjB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { validateOriginHost } from "./origin-validation.js";
|
|
2
|
+
/**
|
|
3
|
+
* Response headers the MCP client MUST be able to read.
|
|
4
|
+
*
|
|
5
|
+
* Both are silent-failure modes: the SDK client reads `Mcp-Session-Id`
|
|
6
|
+
* off every response and echoes it on subsequent requests — without
|
|
7
|
+
* exposure the browser hands back `null`, initialize appears to succeed,
|
|
8
|
+
* and every later request fails against a stateful server. It reads
|
|
9
|
+
* `WWW-Authenticate` off 401s to discover the OAuth resource metadata
|
|
10
|
+
* URL; without exposure, discovery silently degrades to guessing
|
|
11
|
+
* default well-known paths.
|
|
12
|
+
*/
|
|
13
|
+
const EXPOSE_HEADERS = "Mcp-Session-Id, WWW-Authenticate";
|
|
14
|
+
/**
|
|
15
|
+
* Fallback allow-headers when the preflight omits
|
|
16
|
+
* `Access-Control-Request-Headers`. `Authorization` MUST be named
|
|
17
|
+
* explicitly — per the Fetch spec, `*` does not cover it.
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_ALLOW_HEADERS = "Authorization, Content-Type, Accept, Mcp-Session-Id, MCP-Protocol-Version, Last-Event-ID";
|
|
20
|
+
/** GET and DELETE are permitted even though ggui answers them 405: the
|
|
21
|
+
* SDK client auto-attempts both and tolerates 405 — but only if the
|
|
22
|
+
* browser lets it read the status. */
|
|
23
|
+
const ALLOW_METHODS = "GET, POST, DELETE, OPTIONS";
|
|
24
|
+
const MAX_AGE_SECONDS = "600";
|
|
25
|
+
export function createBrowserCorsMiddleware(opts) {
|
|
26
|
+
const { policy } = opts;
|
|
27
|
+
return (req, res, next) => {
|
|
28
|
+
const origin = typeof req.headers.origin === "string" ? req.headers.origin : undefined;
|
|
29
|
+
// No Origin — a non-browser client. Nothing to negotiate.
|
|
30
|
+
if (origin === undefined || origin.length === 0) {
|
|
31
|
+
if (req.method === "OPTIONS") {
|
|
32
|
+
res.status(204).end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
next();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// The SAME allowlist that governs validation governs CORS, so an
|
|
39
|
+
// origin can never be CORS-allowed but Origin-rejected.
|
|
40
|
+
const allowed = validateOriginHost(req.headers.host, origin, policy) === null;
|
|
41
|
+
if (allowed) {
|
|
42
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
43
|
+
// Echoing a specific origin makes the response origin-dependent.
|
|
44
|
+
res.setHeader("Vary", "Origin");
|
|
45
|
+
res.setHeader("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
46
|
+
// NOTE: Access-Control-Allow-Credentials is deliberately never set.
|
|
47
|
+
}
|
|
48
|
+
if (req.method === "OPTIONS") {
|
|
49
|
+
if (allowed) {
|
|
50
|
+
res.setHeader("Access-Control-Allow-Methods", ALLOW_METHODS);
|
|
51
|
+
// Reflect the requested headers so host-injected extras (a
|
|
52
|
+
// request-id, say) do not require a code change here.
|
|
53
|
+
const requested = req.headers["access-control-request-headers"];
|
|
54
|
+
res.setHeader("Access-Control-Allow-Headers", typeof requested === "string" && requested.length > 0 ? requested : DEFAULT_ALLOW_HEADERS);
|
|
55
|
+
res.setHeader("Access-Control-Max-Age", MAX_AGE_SECONDS);
|
|
56
|
+
}
|
|
57
|
+
// A disallowed preflight gets a bare 204 with no CORS headers —
|
|
58
|
+
// the browser fails the request, which is the intended outcome.
|
|
59
|
+
res.status(204).end();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
next();
|
|
63
|
+
};
|
|
64
|
+
}
|
package/dist/build-mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-mcp.d.ts","sourceRoot":"","sources":["../src/build-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,iCAAiC,EACvC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iCAAiC,CAAC;IAC3D;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAElE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC;CACtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAChE,UAAU,EAAE,MAAM,cAAc,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,qBAA0B,GAC/B,SAAS,
|
|
1
|
+
{"version":3,"file":"build-mcp.d.ts","sourceRoot":"","sources":["../src/build-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,iCAAiC,EACvC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iCAAiC,CAAC;IAC3D;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAElE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC;CACtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAChE,UAAU,EAAE,MAAM,cAAc,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,qBAA0B,GAC/B,SAAS,CAqLX"}
|
package/dist/build-mcp.js
CHANGED
|
@@ -31,8 +31,12 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
31
31
|
if (opts.mcpAppsOutbound) {
|
|
32
32
|
installMcpAppsOutbound(server, {
|
|
33
33
|
...(opts.shellHtml !== undefined ? { shellHtml: opts.shellHtml } : {}),
|
|
34
|
+
// Thread the same per-request context accessor + logger the tool
|
|
35
|
+
// path uses (`getContext`, param 3 of `buildMcpServer`) so the
|
|
36
|
+
// per-session resource handler's render-read gate sees the
|
|
37
|
+
// caller (render-read-gate.ts).
|
|
34
38
|
...(opts.selfContained !== undefined
|
|
35
|
-
? { selfContained: opts.selfContained }
|
|
39
|
+
? { selfContained: { ...opts.selfContained, getContext, logger } }
|
|
36
40
|
: {}),
|
|
37
41
|
...(opts.publicBaseUrl !== undefined
|
|
38
42
|
? { publicBaseUrl: opts.publicBaseUrl }
|
package/dist/code-store-fs.d.ts
CHANGED
|
@@ -8,12 +8,15 @@ export interface FileSystemCodeStoreOptions {
|
|
|
8
8
|
readonly root?: string;
|
|
9
9
|
}
|
|
10
10
|
export declare class FileSystemCodeStore implements CodeStore {
|
|
11
|
+
/** Bodies live on disk under the cache root — restart-safe (#457). */
|
|
12
|
+
readonly durability: "durable";
|
|
11
13
|
private readonly root;
|
|
12
14
|
constructor(opts?: FileSystemCodeStoreOptions);
|
|
13
15
|
/** Absolute on-disk path for a given hash. Two-level sharded layout. */
|
|
14
16
|
private absPath;
|
|
15
17
|
put(hash: string, code: string): Promise<void>;
|
|
16
18
|
get(hash: string): Promise<string | null>;
|
|
19
|
+
delete(hash: string): Promise<void>;
|
|
17
20
|
hashOf(code: string): string;
|
|
18
21
|
}
|
|
19
22
|
//# sourceMappingURL=code-store-fs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-store-fs.d.ts","sourceRoot":"","sources":["../src/code-store-fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"code-store-fs.d.ts","sourceRoot":"","sources":["../src/code-store-fs.ts"],"names":[],"mappings":"AAuDA,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,0BAA0B,CAAC;AAElC,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,mBAAoB,YAAW,SAAS;IACnD,sEAAsE;IACtE,QAAQ,CAAC,UAAU,EAAG,SAAS,CAAU;IAEzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,IAAI,GAAE,0BAA+B;IAIjD,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAOT,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAG7B"}
|
package/dist/code-store-fs.js
CHANGED
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
* The store grows unbounded. Operators who care can periodically
|
|
30
30
|
* `rm -rf ~/.ggui/code-cache/` — every entry is content-addressable
|
|
31
31
|
* and immutable, so a re-fetch will repopulate from upstream code on
|
|
32
|
-
* next render.
|
|
32
|
+
* next render. `delete(hash)` removes one entry (unlink, ENOENT
|
|
33
|
+
* tolerated) for operators who need to act on a single bundle rather
|
|
34
|
+
* than the whole root. There is no eviction policy in the seam itself
|
|
35
|
+
* — nothing here decides what to remove or when.
|
|
33
36
|
*
|
|
34
37
|
* ## Permissions
|
|
35
38
|
*
|
|
@@ -39,11 +42,13 @@
|
|
|
39
42
|
* tighter perms wrap with their own umask.
|
|
40
43
|
*/
|
|
41
44
|
import { createHash } from 'node:crypto';
|
|
42
|
-
import { access, constants as fsConstants, mkdir, readFile, rename, writeFile, } from 'node:fs/promises';
|
|
45
|
+
import { access, constants as fsConstants, mkdir, readFile, rename, unlink, writeFile, } from 'node:fs/promises';
|
|
43
46
|
import { homedir } from 'node:os';
|
|
44
47
|
import { dirname, join } from 'node:path';
|
|
45
48
|
import { CODE_HASH_REGEX, } from '@ggui-ai/mcp-server-core';
|
|
46
49
|
export class FileSystemCodeStore {
|
|
50
|
+
/** Bodies live on disk under the cache root — restart-safe (#457). */
|
|
51
|
+
durability = 'durable';
|
|
47
52
|
root;
|
|
48
53
|
constructor(opts = {}) {
|
|
49
54
|
this.root = opts.root ?? join(homedir(), '.ggui', 'code-cache');
|
|
@@ -51,7 +56,8 @@ export class FileSystemCodeStore {
|
|
|
51
56
|
/** Absolute on-disk path for a given hash. Two-level sharded layout. */
|
|
52
57
|
absPath(hash) {
|
|
53
58
|
// Sharding: `<root>/<hash[0..2]>/<hash[2..]>.js`. Validation on
|
|
54
|
-
// every put + get below means `hash` is always 64
|
|
59
|
+
// every put + get + delete below means `hash` is always 64
|
|
60
|
+
// lowercase hex.
|
|
55
61
|
return join(this.root, hash.slice(0, 2), `${hash.slice(2)}.js`);
|
|
56
62
|
}
|
|
57
63
|
async put(hash, code) {
|
|
@@ -83,6 +89,24 @@ export class FileSystemCodeStore {
|
|
|
83
89
|
throw err;
|
|
84
90
|
}
|
|
85
91
|
}
|
|
92
|
+
async delete(hash) {
|
|
93
|
+
// Validate before deriving a path: an unchecked hash could carry
|
|
94
|
+
// `..` segments and unlink a file outside the cache root. Same
|
|
95
|
+
// narrowing `get` applies, and like `get` a malformed key is not
|
|
96
|
+
// an error — there is simply nothing here under that name.
|
|
97
|
+
if (!CODE_HASH_REGEX.test(hash))
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
await unlink(this.absPath(hash));
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
// Absent file = already removed. The shard directory is left in
|
|
104
|
+
// place; empty dirs cost one inode and the next put refills them.
|
|
105
|
+
if (err.code === 'ENOENT')
|
|
106
|
+
return;
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
86
110
|
hashOf(code) {
|
|
87
111
|
return createHash('sha256').update(code, 'utf-8').digest('hex');
|
|
88
112
|
}
|
|
@@ -8,11 +8,16 @@
|
|
|
8
8
|
*
|
|
9
9
|
* GET /ggui/console/session-resource?session=<sessionId>
|
|
10
10
|
* — production thin-shell HTML, wrapped as a ResourceContents
|
|
11
|
-
* blob (`text` IS `GGUI_RENDER_SHELL_HTML
|
|
12
|
-
* what
|
|
13
|
-
* ui://ggui/render
|
|
14
|
-
*
|
|
15
|
-
* the
|
|
11
|
+
* blob (`text` IS the `GGUI_RENDER_SHELL_HTML` constant). On a
|
|
12
|
+
* default mount this matches what a host fetches via MCP
|
|
13
|
+
* `resources/read ui://ggui/render`; a mount opted into
|
|
14
|
+
* `mcpApps.inlineRuntimeShell` (or a custom `shellHtml`) serves
|
|
15
|
+
* a different body on the MCP path while THIS route keeps the
|
|
16
|
+
* thin shell — its first-party srcdoc/WebView consumers load
|
|
17
|
+
* external scripts fine and rely on the pinned CSP hash. NO
|
|
18
|
+
* inlined bootstrap — console fetches the bootstrap separately
|
|
19
|
+
* (meta route below) and replies to the iframe's
|
|
20
|
+
* `ui/initialize` postMessage with it.
|
|
16
21
|
*
|
|
17
22
|
* GET /ggui/console/sessions/:sessionId/meta
|
|
18
23
|
* — slice-envelope JSON (`{ "ai.ggui/render": {...} }`, the same
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-session-routes.d.ts","sourceRoot":"","sources":["../src/console-session-routes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"console-session-routes.d.ts","sourceRoot":"","sources":["../src/console-session-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAQ5F,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAQ1D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,KACV;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,uEAAuE;IACvE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,YAAY,GAAG,4BAA4B,CA0V1F"}
|
|
@@ -8,11 +8,16 @@
|
|
|
8
8
|
*
|
|
9
9
|
* GET /ggui/console/session-resource?session=<sessionId>
|
|
10
10
|
* — production thin-shell HTML, wrapped as a ResourceContents
|
|
11
|
-
* blob (`text` IS `GGUI_RENDER_SHELL_HTML
|
|
12
|
-
* what
|
|
13
|
-
* ui://ggui/render
|
|
14
|
-
*
|
|
15
|
-
* the
|
|
11
|
+
* blob (`text` IS the `GGUI_RENDER_SHELL_HTML` constant). On a
|
|
12
|
+
* default mount this matches what a host fetches via MCP
|
|
13
|
+
* `resources/read ui://ggui/render`; a mount opted into
|
|
14
|
+
* `mcpApps.inlineRuntimeShell` (or a custom `shellHtml`) serves
|
|
15
|
+
* a different body on the MCP path while THIS route keeps the
|
|
16
|
+
* thin shell — its first-party srcdoc/WebView consumers load
|
|
17
|
+
* external scripts fine and rely on the pinned CSP hash. NO
|
|
18
|
+
* inlined bootstrap — console fetches the bootstrap separately
|
|
19
|
+
* (meta route below) and replies to the iframe's
|
|
20
|
+
* `ui/initialize` postMessage with it.
|
|
16
21
|
*
|
|
17
22
|
* GET /ggui/console/sessions/:sessionId/meta
|
|
18
23
|
* — slice-envelope JSON (`{ "ai.ggui/render": {...} }`, the same
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,wBAAwB,EAEzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,oEAAoE;AACpE,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,mBAAmB,GACnB,mBAAmB,GACnB,wBAAwB,GACxB,aAAa,CAAC;AAElB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,oEAAoE;IACpE,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;CAClF;AAED,+DAA+D;AAC/D,MAAM,WAAW,QAAQ;IACvB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,8DAA8D;IAC9D,SAAS,CACP,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,GAChC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,GAChC,IAAI,CAAC;IACR,sEAAsE;IACtE,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,4EAA4E;IAC5E,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1F,oEAAoE;IACpE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iEAAiE;IACjE,YAAY,CAAC,IAAI,EAAE;QACjB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,GAAG,IAAI,CAAC;IACT,sEAAsE;IACtE,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACrE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,wBAAwB,EAEzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,oEAAoE;AACpE,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,mBAAmB,GACnB,mBAAmB,GACnB,wBAAwB,GACxB,aAAa,CAAC;AAElB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,oEAAoE;IACpE,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;CAClF;AAED,+DAA+D;AAC/D,MAAM,WAAW,QAAQ;IACvB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,8DAA8D;IAC9D,SAAS,CACP,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,GAChC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,GAChC,IAAI,CAAC;IACR,sEAAsE;IACtE,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,4EAA4E;IAC5E,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1F,oEAAoE;IACpE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iEAAiE;IACjE,YAAY,CAAC,IAAI,EAAE;QACjB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,GAAG,IAAI,CAAC;IACT,sEAAsE;IACtE,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACrE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAoN3D"}
|
|
@@ -158,6 +158,18 @@ export function createOutbound(deps) {
|
|
|
158
158
|
for (const sub of deps.wsSubscribers) {
|
|
159
159
|
if (sub.sessionId !== sessionId)
|
|
160
160
|
continue;
|
|
161
|
+
// Mirror the in-process pump's replay-dedupe guard
|
|
162
|
+
// (subscriber-lifecycle.ts): a data frame whose per-session seq
|
|
163
|
+
// is <= the subscriber's replay snapshot was already delivered by
|
|
164
|
+
// subscribe-replay (policy 'all'/'latest') or is deliberately
|
|
165
|
+
// invisible to late subscribers (policy 'none') — never re-send.
|
|
166
|
+
// Unstamped frames were never buffered; replay cannot duplicate
|
|
167
|
+
// them, so they pass. Non-data frames carry no seq semantics.
|
|
168
|
+
if (frame.type === "data" &&
|
|
169
|
+
typeof frame.payload.seq === "number" &&
|
|
170
|
+
frame.payload.seq <= sub.replayCompletedSeq) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
161
173
|
send(sub.ws, frame);
|
|
162
174
|
}
|
|
163
175
|
}
|
|
@@ -14,6 +14,35 @@ import type { Subscriber } from "./internal-types.js";
|
|
|
14
14
|
import type { Outbound } from "./outbound.js";
|
|
15
15
|
import type { SubscribeHandlers } from "./subscribe.js";
|
|
16
16
|
import type { SubscriberLifecycle } from "./subscriber-lifecycle.js";
|
|
17
|
+
/**
|
|
18
|
+
* Monotonic counters for pre-subscribe cap-driven closures/refusals.
|
|
19
|
+
* The channel server owns the object (so its health getter can read a
|
|
20
|
+
* live snapshot) and hands the same reference to the router, which
|
|
21
|
+
* increments a field on every enforced cap. See {@link PreSubscribeCaps}.
|
|
22
|
+
*/
|
|
23
|
+
export interface PreSubscribeRejectionCounters {
|
|
24
|
+
/** Oversized pre-subscribe frames rejected (1009). */
|
|
25
|
+
payload: number;
|
|
26
|
+
/** Pre-subscribe sockets closed for exceeding the idle window (1008). */
|
|
27
|
+
idle: number;
|
|
28
|
+
/** Upgrades refused for exceeding the pre-subscribe connection ceiling (1013). */
|
|
29
|
+
connection: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolved pre-subscribe cap thresholds (ggui#444). Each bounds only
|
|
33
|
+
* what a NOT-YET-SUBSCRIBED (unauthenticated / unregistered) socket can
|
|
34
|
+
* consume — a socket that has completed a valid `subscribe` is a
|
|
35
|
+
* legitimate long-lived subscriber and is exempt from all three. A
|
|
36
|
+
* value of `0` disables that cap.
|
|
37
|
+
*/
|
|
38
|
+
export interface PreSubscribeCaps {
|
|
39
|
+
/** Per-frame byte ceiling on pre-subscribe frames. 0 = disabled. */
|
|
40
|
+
readonly maxPayloadBytes: number;
|
|
41
|
+
/** Idle window (ms) a socket has to complete a valid subscribe. 0 = disabled. */
|
|
42
|
+
readonly idleMs: number;
|
|
43
|
+
/** Concurrent pending (pre-subscribe) socket ceiling. 0 = disabled. */
|
|
44
|
+
readonly maxConnections: number;
|
|
45
|
+
}
|
|
17
46
|
export interface SocketRouterDeps {
|
|
18
47
|
readonly logger: Logger;
|
|
19
48
|
/** GguiSession backing store — observation-message patches persist here. */
|
|
@@ -27,6 +56,10 @@ export interface SocketRouterDeps {
|
|
|
27
56
|
readonly handleInboundAction: ActionIngress["handleInboundAction"];
|
|
28
57
|
readonly handleChannelSubscribe: ChannelSubscriptions["handleChannelSubscribe"];
|
|
29
58
|
readonly handleChannelUnsubscribe: ChannelSubscriptions["handleChannelUnsubscribe"];
|
|
59
|
+
/** Resolved pre-subscribe cap thresholds (ggui#444). */
|
|
60
|
+
readonly preSubscribeCaps: PreSubscribeCaps;
|
|
61
|
+
/** Shared monotonic counters incremented on each enforced cap. */
|
|
62
|
+
readonly preSubscribeRejections: PreSubscribeRejectionCounters;
|
|
30
63
|
}
|
|
31
64
|
/**
|
|
32
65
|
* Wire the channel's `connection` handling onto `wss`. Reads the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-router.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/socket-router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAInF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACvD,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC/D,QAAQ,CAAC,mBAAmB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACnE,QAAQ,CAAC,sBAAsB,EAAE,oBAAoB,CAAC,wBAAwB,CAAC,CAAC;IAChF,QAAQ,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-router.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/socket-router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAInF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;GAKG;AACH,MAAM,WAAW,6BAA6B;IAC5C,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACvD,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC/D,QAAQ,CAAC,mBAAmB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACnE,QAAQ,CAAC,sBAAsB,EAAE,oBAAoB,CAAC,wBAAwB,CAAC,CAAC;IAChF,QAAQ,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;IACpF,wDAAwD;IACxD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,kEAAkE;IAClE,QAAQ,CAAC,sBAAsB,EAAE,6BAA6B,CAAC;CAChE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAmarF"}
|
|
@@ -12,6 +12,65 @@
|
|
|
12
12
|
* frame processing per socket.
|
|
13
13
|
*/
|
|
14
14
|
export function attachSocketRouter(wss, deps) {
|
|
15
|
+
const { preSubscribeCaps: caps, preSubscribeRejections: rejections } = deps;
|
|
16
|
+
// --- Pre-subscribe cap state (ggui#444) -------------------------------
|
|
17
|
+
//
|
|
18
|
+
// These bound what a socket can consume BEFORE it completes a valid
|
|
19
|
+
// `subscribe`. The distinguishing signal is `subscribersByWs.get(ws)`:
|
|
20
|
+
// unset = pre-subscribe (bounded), set = registered subscriber
|
|
21
|
+
// (exempt). All three caps target the pre-subscribe window only.
|
|
22
|
+
/** Live count of PENDING (pre-subscribe) sockets — never counts subscribers. */
|
|
23
|
+
let preSubscribeCount = 0;
|
|
24
|
+
/** Sockets currently counted against {@link preSubscribeCount}. */
|
|
25
|
+
const countedPending = new WeakSet();
|
|
26
|
+
/** Per-socket idle-timeout handle, cleared on subscribe or close. */
|
|
27
|
+
const idleTimers = new WeakMap();
|
|
28
|
+
/**
|
|
29
|
+
* Sockets already closed for the pre-subscribe payload cap. A single
|
|
30
|
+
* abusive burst can deliver several oversized frames in one read
|
|
31
|
+
* before `ws.close()` settles the connection (each already-parsed
|
|
32
|
+
* frame still fires its own `message` event); without this guard
|
|
33
|
+
* every one of them would re-run the payload branch and re-increment
|
|
34
|
+
* `rejections.payload` for what is really one abusive socket. Holds
|
|
35
|
+
* no resource of its own, so it needs no explicit cleanup — the
|
|
36
|
+
* entry is collected once the socket is.
|
|
37
|
+
*/
|
|
38
|
+
const payloadRejected = new WeakSet();
|
|
39
|
+
/**
|
|
40
|
+
* Deduped cap-hit log keys. Capped so a flood cannot grow the set
|
|
41
|
+
* without bound; after the cap, rejections still enforce + count —
|
|
42
|
+
* they just stop logging. Mirrors the origin-rejection warn pattern.
|
|
43
|
+
*/
|
|
44
|
+
const warnedCaps = new Set();
|
|
45
|
+
function warnCapOnce(event, fields) {
|
|
46
|
+
if (!warnedCaps.has(event) && warnedCaps.size < 100) {
|
|
47
|
+
warnedCaps.add(event);
|
|
48
|
+
deps.logger.warn(event, fields);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Drop a socket out of the pending count exactly once. */
|
|
52
|
+
function releasePending(ws) {
|
|
53
|
+
if (countedPending.has(ws)) {
|
|
54
|
+
countedPending.delete(ws);
|
|
55
|
+
preSubscribeCount -= 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function clearIdleTimer(ws) {
|
|
59
|
+
const timer = idleTimers.get(ws);
|
|
60
|
+
if (timer !== undefined) {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
idleTimers.delete(ws);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A socket that has become a registered subscriber is a legitimate
|
|
67
|
+
* long-lived connection: disarm its idle timer and stop counting it
|
|
68
|
+
* against the pre-subscribe ceiling. Idempotent.
|
|
69
|
+
*/
|
|
70
|
+
function markSubscribed(ws) {
|
|
71
|
+
clearIdleTimer(ws);
|
|
72
|
+
releasePending(ws);
|
|
73
|
+
}
|
|
15
74
|
/**
|
|
16
75
|
* Tenancy guard for client-emitted observation messages
|
|
17
76
|
* (`host_context_observed` today). Returns `false`
|
|
@@ -59,6 +118,37 @@ export function attachSocketRouter(wss, deps) {
|
|
|
59
118
|
}
|
|
60
119
|
async function onMessage(ws, raw) {
|
|
61
120
|
const sub = deps.subscribersByWs.get(ws);
|
|
121
|
+
// Pre-subscribe per-frame payload cap. Bounds what a not-yet-
|
|
122
|
+
// subscribed (unauthenticated / unregistered) socket can buffer per
|
|
123
|
+
// frame. Subscribed sockets are exempt — their `action` frames have
|
|
124
|
+
// no protocol-level max size and are governed only by the coarse,
|
|
125
|
+
// global ws-level `maxPayload` memory backstop (see
|
|
126
|
+
// DEFAULT_WS_MAX_PAYLOAD_BYTES for its sizing rationale), never this
|
|
127
|
+
// tight handshake-sized ceiling. Measured before JSON.parse so a
|
|
128
|
+
// giant frame is dropped without being interpreted.
|
|
129
|
+
if (sub === undefined && caps.maxPayloadBytes > 0) {
|
|
130
|
+
// Already closed this socket for this cap: a same-segment burst
|
|
131
|
+
// can still deliver more already-parsed frames before
|
|
132
|
+
// `ws.close()` settles the connection. Drop them silently — one
|
|
133
|
+
// abusive socket is one rejection, not one per queued frame.
|
|
134
|
+
if (payloadRejected.has(ws)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (Buffer.byteLength(raw, "utf8") > caps.maxPayloadBytes) {
|
|
138
|
+
payloadRejected.add(ws);
|
|
139
|
+
rejections.payload += 1;
|
|
140
|
+
warnCapOnce("render_channel_pre_subscribe_payload_rejected", {
|
|
141
|
+
limitBytes: caps.maxPayloadBytes,
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
ws.close(1009, "pre_subscribe_payload_exceeded");
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* best-effort: socket may already be closing */
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
62
152
|
let message;
|
|
63
153
|
try {
|
|
64
154
|
message = JSON.parse(raw);
|
|
@@ -103,6 +193,14 @@ export function attachSocketRouter(wss, deps) {
|
|
|
103
193
|
await deps.handleSubscribe(ws, identity, message, cookieBound);
|
|
104
194
|
pendingIdentity.delete(ws);
|
|
105
195
|
pendingCookieBinding.delete(ws);
|
|
196
|
+
// A successful subscribe registers the subscriber (sets
|
|
197
|
+
// `subscribersByWs`). Only then does the socket graduate out of
|
|
198
|
+
// the pre-subscribe window: disarm its idle timer + free its
|
|
199
|
+
// ceiling slot. A REJECTED subscribe leaves it pending, so it
|
|
200
|
+
// stays bounded (and reapable) exactly as before.
|
|
201
|
+
if (deps.subscribersByWs.get(ws) !== undefined) {
|
|
202
|
+
markSubscribed(ws);
|
|
203
|
+
}
|
|
106
204
|
return;
|
|
107
205
|
}
|
|
108
206
|
case "ping":
|
|
@@ -170,6 +268,58 @@ export function attachSocketRouter(wss, deps) {
|
|
|
170
268
|
*/
|
|
171
269
|
const pendingCookieBinding = new WeakMap();
|
|
172
270
|
wss.on("connection", (ws, req) => {
|
|
271
|
+
// Pre-subscribe connection ceiling. Count only sockets that have
|
|
272
|
+
// NOT yet completed a valid subscribe — legitimate subscriber
|
|
273
|
+
// fan-out can be large and is never bounded here. Beyond the
|
|
274
|
+
// ceiling the socket is cleanly closed (1013 "try again later")
|
|
275
|
+
// without wiring any per-socket handlers or consuming a slot.
|
|
276
|
+
if (caps.maxConnections > 0 && preSubscribeCount >= caps.maxConnections) {
|
|
277
|
+
rejections.connection += 1;
|
|
278
|
+
warnCapOnce("render_channel_pre_subscribe_connection_rejected", {
|
|
279
|
+
limit: caps.maxConnections,
|
|
280
|
+
});
|
|
281
|
+
// This early-return path skips the normal per-socket wiring below,
|
|
282
|
+
// so attach a minimal `error` listener first: a `ws` is an
|
|
283
|
+
// EventEmitter and an unhandled `error` (a refused peer resetting
|
|
284
|
+
// mid-close is the likely case here) would otherwise crash the
|
|
285
|
+
// process — the abuse path must never take the server down.
|
|
286
|
+
ws.on("error", (err) => {
|
|
287
|
+
deps.logger.warn("render_channel_socket_error", { error: String(err) });
|
|
288
|
+
});
|
|
289
|
+
try {
|
|
290
|
+
ws.close(1013, "pre_subscribe_connection_ceiling");
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
/* best-effort: socket may already be closing */
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
preSubscribeCount += 1;
|
|
298
|
+
countedPending.add(ws);
|
|
299
|
+
// Pre-subscribe idle timeout. A socket that never completes a valid
|
|
300
|
+
// subscribe within the window is closed (1008). Armed here, cleared
|
|
301
|
+
// in `markSubscribed` on a successful subscribe, so a subscribed
|
|
302
|
+
// (long-idle / reconnecting) viewer is never reaped. `.unref()` so a
|
|
303
|
+
// pending timer never keeps the process alive on its own.
|
|
304
|
+
if (caps.idleMs > 0) {
|
|
305
|
+
const timer = setTimeout(() => {
|
|
306
|
+
idleTimers.delete(ws);
|
|
307
|
+
if (deps.subscribersByWs.get(ws) === undefined) {
|
|
308
|
+
rejections.idle += 1;
|
|
309
|
+
warnCapOnce("render_channel_pre_subscribe_idle_closed", {
|
|
310
|
+
idleMs: caps.idleMs,
|
|
311
|
+
});
|
|
312
|
+
try {
|
|
313
|
+
ws.close(1008, "pre_subscribe_idle_timeout");
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
/* best-effort: socket may already be closing */
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}, caps.idleMs);
|
|
320
|
+
timer.unref?.();
|
|
321
|
+
idleTimers.set(ws, timer);
|
|
322
|
+
}
|
|
173
323
|
// Bind the resolved identity from the upgrade phase. It was
|
|
174
324
|
// attached to the request object in handleUpgrade.
|
|
175
325
|
const identity = req.__gguiIdentity;
|
|
@@ -205,6 +355,11 @@ export function attachSocketRouter(wss, deps) {
|
|
|
205
355
|
ws.on("close", () => {
|
|
206
356
|
deps.unregister(ws);
|
|
207
357
|
pendingIdentity.delete(ws);
|
|
358
|
+
// A pending socket that closes before subscribing frees its idle
|
|
359
|
+
// timer + ceiling slot. For a socket that already subscribed,
|
|
360
|
+
// `markSubscribed` cleared both — these are then no-ops.
|
|
361
|
+
clearIdleTimer(ws);
|
|
362
|
+
releasePending(ws);
|
|
208
363
|
});
|
|
209
364
|
ws.on("error", (err) => {
|
|
210
365
|
deps.logger.warn("render_channel_socket_error", { error: String(err) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/subscribe.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/subscribe.ts"],"names":[],"mappings":"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"}
|