@ggui-ai/mcp-server 0.1.0-rc.1 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build-mcp.d.ts +8 -8
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +53 -12
- package/dist/console-auth.d.ts +7 -7
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/console-auth.js +4 -4
- package/dist/console-cache.d.ts +2 -2
- package/dist/console-cache.d.ts.map +1 -1
- package/dist/console-cache.js +10 -10
- package/dist/console-headers.d.ts +2 -2
- package/dist/console-headers.js +2 -2
- package/dist/console-payloads.d.ts +3 -3
- package/dist/console-payloads.d.ts.map +1 -1
- package/dist/console-payloads.js +10 -10
- package/dist/console-timeline.d.ts +12 -30
- package/dist/console-timeline.d.ts.map +1 -1
- package/dist/console-timeline.js +44 -45
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/instructions-presets.d.ts +1 -1
- package/dist/instructions-presets.d.ts.map +1 -1
- package/dist/instructions-presets.js +40 -22
- package/dist/llm-backed-negotiator.d.ts +6 -6
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +92 -91
- package/dist/mcp-apps-inbound.d.ts +3 -3
- package/dist/mcp-apps-inbound.d.ts.map +1 -1
- package/dist/mcp-apps-inbound.js +28 -23
- package/dist/mcp-apps-outbound.d.ts +136 -121
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +384 -414
- package/dist/mcp-mounts.d.ts +21 -20
- package/dist/mcp-mounts.d.ts.map +1 -1
- package/dist/mcp-mounts.js +25 -29
- package/dist/{session-channel.d.ts → render-channel.d.ts} +145 -102
- package/dist/render-channel.d.ts.map +1 -0
- package/dist/{session-channel.js → render-channel.js} +481 -462
- package/dist/schema-compat.d.ts +11 -11
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +6 -6
- package/dist/server.d.ts +224 -219
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1498 -1963
- package/dist/storage.d.ts +7 -7
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +12 -12
- package/package.json +12 -11
- package/dist/render-gate.d.ts +0 -87
- package/dist/render-gate.d.ts.map +0 -1
- package/dist/render-gate.js +0 -77
- package/dist/render-rate-limit.d.ts +0 -59
- package/dist/render-rate-limit.d.ts.map +0 -1
- package/dist/render-rate-limit.js +0 -73
- package/dist/render-signing.d.ts +0 -98
- package/dist/render-signing.d.ts.map +0 -1
- package/dist/render-signing.js +0 -113
- package/dist/session-channel.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ The simplest way to start a server is the `ggui` CLI shipped by `@ggui-ai/cli`:
|
|
|
19
19
|
npx @ggui-ai/cli serve
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
`ggui serve` boots this package with the OSS defaults — in-memory
|
|
22
|
+
`ggui serve` boots this package with the OSS defaults — in-memory render store
|
|
23
23
|
(or SQLite when `better-sqlite3` is installed), no-auth dev posture, and every UI
|
|
24
24
|
declared in `ggui.json` surfaced through the blueprint tools.
|
|
25
25
|
|
package/dist/build-mcp.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
12
12
|
import { type ZodRawShape } from 'zod';
|
|
13
13
|
import type { HandlerContext, SharedHandler } from '@ggui-ai/mcp-server-handlers';
|
|
14
14
|
import type { Logger } from './logger.js';
|
|
15
|
-
import { type
|
|
15
|
+
import { type GguiRenderResourceTemplateOptions } from './mcp-apps-outbound.js';
|
|
16
16
|
export interface ServerInfo {
|
|
17
17
|
readonly name: string;
|
|
18
18
|
readonly version: string;
|
|
@@ -22,7 +22,7 @@ export interface BuildMcpServerOptions {
|
|
|
22
22
|
/**
|
|
23
23
|
* When set, register the MCP Apps outbound wiring on every fresh
|
|
24
24
|
* server instance — advertises the `io.modelcontextprotocol/ui`
|
|
25
|
-
* capability and serves `ui://ggui/
|
|
25
|
+
* capability and serves `ui://ggui/render` via `resources/read`.
|
|
26
26
|
*
|
|
27
27
|
* Tool-declaration `_meta.ui.*` is INDEPENDENT of this flag; it's
|
|
28
28
|
* carried per-handler on `SharedHandler._meta`. A server can stamp
|
|
@@ -32,26 +32,26 @@ export interface BuildMcpServerOptions {
|
|
|
32
32
|
*/
|
|
33
33
|
readonly mcpAppsOutbound?: boolean;
|
|
34
34
|
/**
|
|
35
|
-
* Optional override for the `ui://ggui/
|
|
35
|
+
* Optional override for the `ui://ggui/render` shell body. Defaults
|
|
36
36
|
* to whatever shell the server was built with — either a placeholder
|
|
37
37
|
* or the real thin-shell HTML.
|
|
38
38
|
*/
|
|
39
39
|
readonly shellHtml?: string;
|
|
40
40
|
/**
|
|
41
|
-
* Per-
|
|
41
|
+
* Per-render self-contained shell options. When supplied,
|
|
42
42
|
* `installMcpAppsOutbound` ALSO registers
|
|
43
|
-
* `ui://ggui/
|
|
44
|
-
* `
|
|
43
|
+
* `ui://ggui/render/{renderId}` as a resource template — the URI
|
|
44
|
+
* `ggui_render.resultMeta` stamps on per-call `_meta.ui.resourceUri`
|
|
45
45
|
* for third-party MCP Apps hosts (Claude Desktop, claude.ai web)
|
|
46
46
|
* that don't speak ggui's custom postMessage protocol.
|
|
47
47
|
*
|
|
48
48
|
* Absent → only the legacy postMessage shell is registered (first-
|
|
49
49
|
* party hosts only).
|
|
50
50
|
*/
|
|
51
|
-
readonly selfContained?:
|
|
51
|
+
readonly selfContained?: GguiRenderResourceTemplateOptions;
|
|
52
52
|
/**
|
|
53
53
|
* Public origin the server is reachable at — forwarded to
|
|
54
|
-
* `installMcpAppsOutbound` so the static `ui://ggui/
|
|
54
|
+
* `installMcpAppsOutbound` so the static `ui://ggui/render`
|
|
55
55
|
* resource declares `_meta.ui.csp.{connectDomains,resourceDomains}`.
|
|
56
56
|
* Without this, spec-compliant hosts (Claude Desktop, claude.ai
|
|
57
57
|
* Connector, Claude Code) apply their default CSP (`connect-src
|
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;
|
|
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;AAEpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACd,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,CA8IX"}
|
package/dist/build-mcp.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* tool return advertises its shape — wire consumers can trust the output.
|
|
10
10
|
*/
|
|
11
11
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
import { registerAppTool } from '@modelcontextprotocol/ext-apps/server';
|
|
12
13
|
import { z } from 'zod';
|
|
13
14
|
import { installMcpAppsOutbound, } from './mcp-apps-outbound.js';
|
|
14
15
|
/**
|
|
@@ -56,17 +57,30 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
56
57
|
&& !handler.allowedFor.some((kind) => allowedKinds.includes(kind))) {
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
const cb = async (input, extra) => {
|
|
61
|
+
// Thread per-request `_meta` AND the cancellation `signal` onto the
|
|
62
|
+
// canonical context. The MCP SDK already parses `params._meta` for
|
|
63
|
+
// us and exposes it on `RequestHandlerExtra._meta`; handlers that
|
|
64
|
+
// read host-channel slices (e.g. `ai.ggui/host-session` on
|
|
65
|
+
// `ggui_handshake`) pick it up via `ctx.requestMeta` without
|
|
66
|
+
// touching the SDK surface themselves. The same
|
|
67
|
+
// `RequestHandlerExtra` carries `signal: AbortSignal` — fired by
|
|
68
|
+
// the SDK on a `notifications/cancelled` from the caller OR on
|
|
69
|
+
// transport close (this server wires `res.on("close") →
|
|
70
|
+
// transport.close()`, which aborts every in-flight request
|
|
71
|
+
// handler). `ggui_consume` reads `ctx.signal` to break its
|
|
72
|
+
// long-poll promptly on a disconnected consumer, releasing the
|
|
73
|
+
// active-consumer count instead of zombie-holding it to the
|
|
74
|
+
// deadline. Both ride the canonical context without leaking the
|
|
75
|
+
// SDK type into the handlers package.
|
|
76
|
+
const baseCtx = getContext();
|
|
77
|
+
const ctx = {
|
|
78
|
+
...baseCtx,
|
|
79
|
+
...(extra?._meta !== undefined
|
|
80
|
+
? { requestMeta: extra._meta }
|
|
81
|
+
: {}),
|
|
82
|
+
...(extra?.signal !== undefined ? { signal: extra.signal } : {}),
|
|
83
|
+
};
|
|
70
84
|
const start = Date.now();
|
|
71
85
|
try {
|
|
72
86
|
const data = await handler.handler(input, ctx);
|
|
@@ -99,7 +113,34 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
99
113
|
});
|
|
100
114
|
throw err;
|
|
101
115
|
}
|
|
102
|
-
}
|
|
116
|
+
};
|
|
117
|
+
const baseConfig = {
|
|
118
|
+
...(handler.title ? { title: handler.title } : {}),
|
|
119
|
+
description: handler.description,
|
|
120
|
+
inputSchema: handler.inputSchema,
|
|
121
|
+
outputSchema: handler.outputSchema,
|
|
122
|
+
};
|
|
123
|
+
// Dispatch on declaration-level UI meta presence. `registerAppTool`
|
|
124
|
+
// (from `@modelcontextprotocol/ext-apps/server`) normalizes the
|
|
125
|
+
// legacy flat key — when `_meta.ui.resourceUri` is set, it also
|
|
126
|
+
// stamps `_meta["ui/resourceUri"]` for older hosts. Letting the
|
|
127
|
+
// canonical helper do that work means ggui handlers carry the
|
|
128
|
+
// single canonical key only; the helper owns the back-compat
|
|
129
|
+
// shape. Handlers without `_meta.ui` fall through to plain
|
|
130
|
+
// `registerTool` — the ext-apps helper requires `_meta.ui` to be
|
|
131
|
+
// typed.
|
|
132
|
+
if (handler._meta && 'ui' in handler._meta) {
|
|
133
|
+
registerAppTool(server, handler.name, {
|
|
134
|
+
...baseConfig,
|
|
135
|
+
_meta: handler._meta,
|
|
136
|
+
}, cb);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
server.registerTool(handler.name, {
|
|
140
|
+
...baseConfig,
|
|
141
|
+
...(handler._meta ? { _meta: handler._meta } : {}),
|
|
142
|
+
}, cb);
|
|
143
|
+
}
|
|
103
144
|
}
|
|
104
145
|
return server;
|
|
105
146
|
}
|
package/dist/console-auth.d.ts
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* so the cookie never leaves the console's origin in the first
|
|
34
34
|
* place.
|
|
35
35
|
*
|
|
36
|
-
* Lifecycle. Cookie is minted at `POST /ggui/console/
|
|
36
|
+
* Lifecycle. Cookie is minted at `POST /ggui/console/render-cookie`
|
|
37
37
|
* (operator posts a shortCode → server resolves → sets Set-Cookie).
|
|
38
38
|
* Cookie is consumed at `GET /ws` upgrade (if the server composition
|
|
39
39
|
* wires `cookieAuth` to the session-channel). Never mutated in
|
|
@@ -49,7 +49,7 @@ import type { IncomingHttpHeaders } from 'node:http';
|
|
|
49
49
|
export declare const CONSOLE_COOKIE_NAME = "ggui_console_session";
|
|
50
50
|
/**
|
|
51
51
|
* Result of minting a cookie. Callers set the cookie on their response
|
|
52
|
-
* (the cookie endpoint) and echo the bound `
|
|
52
|
+
* (the cookie endpoint) and echo the bound `renderId`/`appId` back in
|
|
53
53
|
* the response body so the client SPA can bootstrap the viewer without
|
|
54
54
|
* a follow-up round-trip.
|
|
55
55
|
*/
|
|
@@ -57,11 +57,11 @@ export interface DevtoolCookieMint {
|
|
|
57
57
|
readonly cookieValue: string;
|
|
58
58
|
readonly setCookieHeader: string;
|
|
59
59
|
readonly expiresAt: number;
|
|
60
|
-
readonly
|
|
60
|
+
readonly renderId: string;
|
|
61
61
|
readonly appId: string;
|
|
62
62
|
}
|
|
63
63
|
export interface MintDevtoolCookieInput {
|
|
64
|
-
readonly
|
|
64
|
+
readonly renderId: string;
|
|
65
65
|
readonly appId: string;
|
|
66
66
|
/** HMAC secret. Shared with bootstrap + session tokens by design. */
|
|
67
67
|
readonly secret: string;
|
|
@@ -84,7 +84,7 @@ export interface MintDevtoolCookieInput {
|
|
|
84
84
|
readonly sameSite?: 'Strict' | 'Lax' | 'None';
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
* Mint a cookie value bound to `{
|
|
87
|
+
* Mint a cookie value bound to `{ renderId, appId }` and the formatted
|
|
88
88
|
* `Set-Cookie` header to send on the response. Caller does
|
|
89
89
|
* `res.setHeader('Set-Cookie', result.setCookieHeader)`.
|
|
90
90
|
*/
|
|
@@ -118,10 +118,10 @@ export declare function readDevtoolCookieFromHeaders(headers: IncomingHttpHeader
|
|
|
118
118
|
/**
|
|
119
119
|
* Verified claims extracted from an console cookie. Scope is
|
|
120
120
|
* deliberately narrow: just the binding the session-channel upgrade
|
|
121
|
-
* needs to enforce `subscribe.
|
|
121
|
+
* needs to enforce `subscribe.renderId === cookie.renderId`.
|
|
122
122
|
*/
|
|
123
123
|
export interface DevtoolCookieClaims {
|
|
124
|
-
readonly
|
|
124
|
+
readonly renderId: string;
|
|
125
125
|
readonly appId: string;
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-auth.d.ts","sourceRoot":"","sources":["../src/console-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"console-auth.d.ts","sourceRoot":"","sources":["../src/console-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,iBAAiB,CAqBnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAcf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mBAAmB,GAC3B,MAAM,GAAG,IAAI,CAIf;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,mBAAmB,GAAG,IAAI,CAO5B"}
|
package/dist/console-auth.js
CHANGED
|
@@ -7,13 +7,13 @@ import { DEFAULT_DEVTOOL_SESSION_TTL_SEC, mintDevtoolSessionToken, verifyToken,
|
|
|
7
7
|
*/
|
|
8
8
|
export const CONSOLE_COOKIE_NAME = 'ggui_console_session';
|
|
9
9
|
/**
|
|
10
|
-
* Mint a cookie value bound to `{
|
|
10
|
+
* Mint a cookie value bound to `{ renderId, appId }` and the formatted
|
|
11
11
|
* `Set-Cookie` header to send on the response. Caller does
|
|
12
12
|
* `res.setHeader('Set-Cookie', result.setCookieHeader)`.
|
|
13
13
|
*/
|
|
14
14
|
export function mintDevtoolCookie(input) {
|
|
15
15
|
const ttlSec = input.ttlSec ?? DEFAULT_DEVTOOL_SESSION_TTL_SEC;
|
|
16
|
-
const { token, claims } = mintDevtoolSessionToken({
|
|
16
|
+
const { token, claims } = mintDevtoolSessionToken({ renderId: input.renderId, appId: input.appId, ttlSec }, input.secret);
|
|
17
17
|
const attrs = [
|
|
18
18
|
`${CONSOLE_COOKIE_NAME}=${token}`,
|
|
19
19
|
`Path=${input.path ?? '/'}`,
|
|
@@ -27,7 +27,7 @@ export function mintDevtoolCookie(input) {
|
|
|
27
27
|
cookieValue: token,
|
|
28
28
|
setCookieHeader: attrs.join('; '),
|
|
29
29
|
expiresAt: claims.exp * 1000,
|
|
30
|
-
|
|
30
|
+
renderId: input.renderId,
|
|
31
31
|
appId: input.appId,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
@@ -96,7 +96,7 @@ export function verifyDevtoolCookie(cookieValue, secret) {
|
|
|
96
96
|
if (!result.ok)
|
|
97
97
|
return null;
|
|
98
98
|
return {
|
|
99
|
-
|
|
99
|
+
renderId: result.claims.renderId,
|
|
100
100
|
appId: result.claims.appId,
|
|
101
101
|
};
|
|
102
102
|
}
|
package/dist/console-cache.d.ts
CHANGED
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
* may want a durable LLM trace (cost analysis) but a lossy cache
|
|
32
32
|
* trace (ops signal only). One file per sink keeps that swap lean.
|
|
33
33
|
*/
|
|
34
|
-
import type {
|
|
35
|
-
import type {
|
|
34
|
+
import type { CacheTraceEvent, CacheTraceSink } from "@ggui-ai/mcp-server-handlers/renders";
|
|
35
|
+
import type { Express } from "express";
|
|
36
36
|
/** SSE listener — receives one event per cache lookup. */
|
|
37
37
|
type SseListener = (event: CacheTraceEvent) => void;
|
|
38
38
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-cache.d.ts","sourceRoot":"","sources":["../src/console-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"console-cache.d.ts","sourceRoot":"","sources":["../src/console-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAG1D,0DAA0D;AAC1D,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;gBAExC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAQjD,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAclC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE;IAKjD,iEAAiE;IACjE,SAAS,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,IAAI;IAO5C,uEAAuE;IACvE,aAAa,IAAI,MAAM;IAIvB,qEAAqE;IACrE,IAAI,IAAI,MAAM;CAGf;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAwCvF"}
|
package/dist/console-cache.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyDevtoolSecurityHeaders } from
|
|
1
|
+
import { applyDevtoolSecurityHeaders } from "./console-headers.js";
|
|
2
2
|
/**
|
|
3
3
|
* In-memory ring buffer + listener fanout. Implements
|
|
4
4
|
* {@link CacheTraceSink} so it can be passed to
|
|
@@ -68,11 +68,11 @@ export class BoundedCacheTraceSink {
|
|
|
68
68
|
*/
|
|
69
69
|
export function mountConsoleCacheRoutes(app, sink) {
|
|
70
70
|
// GET /ggui/console/cache/recent?limit=<n> — JSON snapshot.
|
|
71
|
-
app.get(
|
|
71
|
+
app.get("/ggui/console/cache/recent", (req, res) => {
|
|
72
72
|
applyDevtoolSecurityHeaders(res);
|
|
73
|
-
const limitRaw = req.query[
|
|
73
|
+
const limitRaw = req.query["limit"];
|
|
74
74
|
let limit = 100;
|
|
75
|
-
if (typeof limitRaw ===
|
|
75
|
+
if (typeof limitRaw === "string") {
|
|
76
76
|
const parsed = Number.parseInt(limitRaw, 10);
|
|
77
77
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
78
78
|
limit = Math.min(500, parsed);
|
|
@@ -83,21 +83,21 @@ export function mountConsoleCacheRoutes(app, sink) {
|
|
|
83
83
|
// GET /ggui/console/cache/stream — SSE live stream.
|
|
84
84
|
// Heartbeat comment every 15s so reverse proxies don't kill the
|
|
85
85
|
// connection on idle. Client cleanup unregisters the listener.
|
|
86
|
-
app.get(
|
|
86
|
+
app.get("/ggui/console/cache/stream", (req, res) => {
|
|
87
87
|
applyDevtoolSecurityHeaders(res);
|
|
88
|
-
res.setHeader(
|
|
89
|
-
res.setHeader(
|
|
90
|
-
res.setHeader(
|
|
88
|
+
res.setHeader("content-type", "text/event-stream");
|
|
89
|
+
res.setHeader("cache-control", "no-store");
|
|
90
|
+
res.setHeader("connection", "keep-alive");
|
|
91
91
|
// Flush headers immediately so the EventSource starts receiving.
|
|
92
92
|
res.flushHeaders?.();
|
|
93
93
|
const heartbeat = setInterval(() => {
|
|
94
94
|
// SSE comment frame — clients ignore but proxies see traffic.
|
|
95
|
-
res.write(
|
|
95
|
+
res.write(": ping\n\n");
|
|
96
96
|
}, 15000);
|
|
97
97
|
const off = sink.subscribe((event) => {
|
|
98
98
|
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
99
99
|
});
|
|
100
|
-
req.on(
|
|
100
|
+
req.on("close", () => {
|
|
101
101
|
clearInterval(heartbeat);
|
|
102
102
|
off();
|
|
103
103
|
});
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* neither enables `eval` / `new Function` (that needs
|
|
33
33
|
* `'unsafe-eval'`, which is explicitly absent).
|
|
34
34
|
*
|
|
35
|
-
* `<ggui-shell-hash>` is `
|
|
35
|
+
* `<ggui-shell-hash>` is `GGUI_RENDER_SHELL_SCRIPT_HASH` from
|
|
36
36
|
* `mcp-apps-outbound.ts` — the sha-256 source-expression
|
|
37
37
|
* authorising the inline `<script>` block of the production thin
|
|
38
38
|
* shell `<McpAppIframe>` mounts via `srcdoc`. `srcdoc` iframes
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
* door to inline `<script>` or `<style>` blocks (different CSP
|
|
56
56
|
* directive). Scoped, not dangerous.
|
|
57
57
|
* - `connect-src 'self'` — covers both same-origin `fetch()` to
|
|
58
|
-
* `/ggui/console/
|
|
58
|
+
* `/ggui/console/render-cookie` and the `new WebSocket('/ws')`
|
|
59
59
|
* upgrade. CSP Level 3 treats `'self'` as matching the document's
|
|
60
60
|
* origin across http/https/ws/wss, which is exactly the scope.
|
|
61
61
|
* - `img-src 'self' data:` — no images in the minimal viewer today,
|
package/dist/console-headers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GGUI_RENDER_SHELL_SCRIPT_HASH } from './mcp-apps-outbound.js';
|
|
2
2
|
/**
|
|
3
3
|
* The CSP directive string. Exported so tests can assert against the
|
|
4
4
|
* exact shape without hard-coding the directive order inside the test
|
|
@@ -7,7 +7,7 @@ import { GGUI_SESSION_SHELL_SCRIPT_HASH } from './mcp-apps-outbound.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export const DEVTOOL_CSP = [
|
|
9
9
|
"default-src 'none'",
|
|
10
|
-
`script-src 'self' blob: data: ${
|
|
10
|
+
`script-src 'self' blob: data: ${GGUI_RENDER_SHELL_SCRIPT_HASH}`,
|
|
11
11
|
// Google Fonts CDN allowlisted for the brand-kit Inter + Geist Mono
|
|
12
12
|
// pair used by the public welcome page (`/`). The same allowlist
|
|
13
13
|
// applies to every console-served HTML — every page may opt to load
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
* as they fire. Heartbeat every 15s to keep proxies awake.
|
|
16
16
|
*
|
|
17
17
|
* **Memory bound.** Default capacity = 100 events (vs LlmTraceSink's
|
|
18
|
-
* 200). Each `
|
|
18
|
+
* 200). Each `ggui_render` payload may carry full componentCode, base64
|
|
19
19
|
* blobs, or a fat `story.context`, so cap is tighter to keep peak
|
|
20
20
|
* memory bounded. At ~50 KB / event the worst-case ring is ~5 MB.
|
|
21
21
|
* Operator can override at construction.
|
|
22
22
|
*/
|
|
23
|
-
import type {
|
|
24
|
-
import type {
|
|
23
|
+
import type { PayloadTraceEvent, PayloadTraceSink } from "@ggui-ai/mcp-server-handlers/renders";
|
|
24
|
+
import type { Express } from "express";
|
|
25
25
|
/** SSE listener — receives one event per accepted payload. */
|
|
26
26
|
type SseListener = (event: PayloadTraceEvent) => void;
|
|
27
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-payloads.d.ts","sourceRoot":"","sources":["../src/console-payloads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"console-payloads.d.ts","sourceRoot":"","sources":["../src/console-payloads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAChG,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAG1D,8DAA8D;AAC9D,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;gBAExC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAQjD,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAcpC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,iBAAiB,EAAE;IAKnD,iEAAiE;IACjE,SAAS,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,IAAI;IAO5C,uEAAuE;IACvE,aAAa,IAAI,MAAM;IAIvB,qEAAqE;IACrE,IAAI,IAAI,MAAM;CAGf;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAwC5F"}
|
package/dist/console-payloads.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyDevtoolSecurityHeaders } from
|
|
1
|
+
import { applyDevtoolSecurityHeaders } from "./console-headers.js";
|
|
2
2
|
/**
|
|
3
3
|
* In-memory ring buffer + listener fanout. Implements
|
|
4
4
|
* {@link PayloadTraceSink} so it can be passed to
|
|
@@ -68,11 +68,11 @@ export class BoundedPayloadTraceSink {
|
|
|
68
68
|
*/
|
|
69
69
|
export function mountConsolePayloadsRoutes(app, sink) {
|
|
70
70
|
// GET /ggui/console/payloads/recent?limit=<n> — JSON snapshot.
|
|
71
|
-
app.get(
|
|
71
|
+
app.get("/ggui/console/payloads/recent", (req, res) => {
|
|
72
72
|
applyDevtoolSecurityHeaders(res);
|
|
73
|
-
const limitRaw = req.query[
|
|
73
|
+
const limitRaw = req.query["limit"];
|
|
74
74
|
let limit = 100;
|
|
75
|
-
if (typeof limitRaw ===
|
|
75
|
+
if (typeof limitRaw === "string") {
|
|
76
76
|
const parsed = Number.parseInt(limitRaw, 10);
|
|
77
77
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
78
78
|
limit = Math.min(500, parsed);
|
|
@@ -83,21 +83,21 @@ export function mountConsolePayloadsRoutes(app, sink) {
|
|
|
83
83
|
// GET /ggui/console/payloads/stream — SSE live stream.
|
|
84
84
|
// Heartbeat comment every 15s so reverse proxies don't kill the
|
|
85
85
|
// connection on idle. Client cleanup unregisters the listener.
|
|
86
|
-
app.get(
|
|
86
|
+
app.get("/ggui/console/payloads/stream", (req, res) => {
|
|
87
87
|
applyDevtoolSecurityHeaders(res);
|
|
88
|
-
res.setHeader(
|
|
89
|
-
res.setHeader(
|
|
90
|
-
res.setHeader(
|
|
88
|
+
res.setHeader("content-type", "text/event-stream");
|
|
89
|
+
res.setHeader("cache-control", "no-store");
|
|
90
|
+
res.setHeader("connection", "keep-alive");
|
|
91
91
|
// Flush headers immediately so the EventSource starts receiving.
|
|
92
92
|
res.flushHeaders?.();
|
|
93
93
|
const heartbeat = setInterval(() => {
|
|
94
94
|
// SSE comment frame — clients ignore but proxies see traffic.
|
|
95
|
-
res.write(
|
|
95
|
+
res.write(": ping\n\n");
|
|
96
96
|
}, 15000);
|
|
97
97
|
const off = sink.subscribe((event) => {
|
|
98
98
|
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
99
99
|
});
|
|
100
|
-
req.on(
|
|
100
|
+
req.on("close", () => {
|
|
101
101
|
clearInterval(heartbeat);
|
|
102
102
|
off();
|
|
103
103
|
});
|
|
@@ -1,45 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Console
|
|
3
|
-
* `/devtools/timeline` in the @ggui-ai/console SPA.
|
|
2
|
+
* Console timeline routes — `GET /ggui/console/timeline/*`.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* {@link SessionStreamBuffer} (outbound stream cursor). This module is
|
|
10
|
-
* a thin read-only window over both.
|
|
4
|
+
* Read-only event-log inspector. Lists rendered surfaces (renders) the
|
|
5
|
+
* server has materialized, drills into the per-render event ledger for
|
|
6
|
+
* a single id, and exposes the live-channel outbound cursor alongside
|
|
7
|
+
* for diagnostic context.
|
|
11
8
|
*
|
|
12
|
-
*
|
|
9
|
+
* Post-Phase-B (flatten-render-identity): renamed from the prior
|
|
10
|
+
* "sessions" terminology; the canonical addressable unit is now the
|
|
11
|
+
* render. The on-wire `renderId` path param replaces `sessionId`.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* expired) — operators frequently want to debug a session AFTER it
|
|
17
|
-
* terminated. Sorted most-recent-`lastActivityAt` first; defaults
|
|
18
|
-
* to 50 rows, clamped to [1, 200] via `?limit=`.
|
|
19
|
-
*
|
|
20
|
-
* - `GET /ggui/console/timeline/:sessionId/events` — the full event
|
|
21
|
-
* log for one session, oldest-first, drained from
|
|
22
|
-
* `sessionStore.observe(id, { tail: false })`. Also reports the
|
|
23
|
-
* outbound stream cursor (`streamBuffer.currentSeq`) so the
|
|
24
|
-
* operator can see live-channel progress without a separate fetch.
|
|
25
|
-
*
|
|
26
|
-
* Why REST-only (no SSE): replay is a snapshot. The operator picks a
|
|
27
|
-
* session and steps through what happened — they want a stable frozen
|
|
28
|
-
* view, not a live tail. SSE would force the scrubber to keep chasing
|
|
29
|
-
* a moving end-of-stream and complicate the UI without paying for it.
|
|
30
|
-
*
|
|
31
|
-
* Memory: bounded by whatever the underlying SessionStore retains.
|
|
32
|
-
* `InMemorySessionStore` keeps everything for the process lifetime —
|
|
13
|
+
* Memory: bounded by whatever the underlying RenderStore retains.
|
|
14
|
+
* `InMemoryRenderStore` keeps everything for the process lifetime —
|
|
33
15
|
* fine for OSS dev. A hosted closed runtime's persistent store is the
|
|
34
16
|
* durability surface.
|
|
35
17
|
*/
|
|
36
18
|
import type { Express } from 'express';
|
|
37
|
-
import type {
|
|
19
|
+
import type { RenderStore, SessionStreamBuffer } from '@ggui-ai/mcp-server-core';
|
|
38
20
|
/**
|
|
39
21
|
* Mount `/ggui/console/timeline/*` routes on `app`. Caller is
|
|
40
22
|
* responsible for admin-gating the path prefix beforehand — this
|
|
41
23
|
* function does not re-implement auth (matches the
|
|
42
24
|
* `mountConsoleLlmTraceRoutes` shape in console-llm-trace.ts).
|
|
43
25
|
*/
|
|
44
|
-
export declare function mountConsoleTimelineRoutes(app: Express,
|
|
26
|
+
export declare function mountConsoleTimelineRoutes(app: Express, renderStore: RenderStore | undefined, streamBuffer: SessionStreamBuffer | undefined): void;
|
|
45
27
|
//# sourceMappingURL=console-timeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-timeline.d.ts","sourceRoot":"","sources":["../src/console-timeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"console-timeline.d.ts","sourceRoot":"","sources":["../src/console-timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAEV,WAAW,EACX,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAiElC;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAC5C,IAAI,CA6JN"}
|