@ggui-ai/mcp-server 0.2.0-alpha.4 → 0.3.0-rc.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/admin-blueprints-transport.d.ts.map +1 -1
- package/dist/admin-blueprints-transport.js +2 -1
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -1
- package/dist/admin-oauth-providers-transport.js +7 -5
- package/dist/api-renders-routes.d.ts +85 -0
- package/dist/api-renders-routes.d.ts.map +1 -0
- package/dist/api-renders-routes.js +372 -0
- package/dist/build-mcp.d.ts +1 -1
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +39 -5
- package/dist/code-routes.d.ts +47 -0
- package/dist/code-routes.d.ts.map +1 -0
- package/dist/code-routes.js +81 -0
- package/dist/code-store-fs.js +2 -2
- package/dist/console-auth.d.ts +10 -10
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/console-auth.js +5 -5
- package/dist/console-blueprint-routes.d.ts +71 -0
- package/dist/console-blueprint-routes.d.ts.map +1 -0
- package/dist/console-blueprint-routes.js +348 -0
- package/dist/console-chat-routes.d.ts +80 -0
- package/dist/console-chat-routes.d.ts.map +1 -0
- package/dist/console-chat-routes.js +182 -0
- package/dist/console-config-routes.d.ts +37 -0
- package/dist/console-config-routes.d.ts.map +1 -0
- package/dist/console-config-routes.js +91 -0
- package/dist/console-headers.d.ts +1 -1
- package/dist/console-info-routes.d.ts +84 -0
- package/dist/console-info-routes.d.ts.map +1 -0
- package/dist/console-info-routes.js +135 -0
- package/dist/console-keys-routes.d.ts +50 -0
- package/dist/console-keys-routes.d.ts.map +1 -0
- package/dist/console-keys-routes.js +222 -0
- package/dist/console-llm-keys-routes.d.ts +47 -0
- package/dist/console-llm-keys-routes.d.ts.map +1 -0
- package/dist/console-llm-keys-routes.js +443 -0
- package/dist/console-mcp-tools-routes.d.ts +41 -0
- package/dist/console-mcp-tools-routes.d.ts.map +1 -0
- package/dist/console-mcp-tools-routes.js +60 -0
- package/dist/console-registry-routes.d.ts +66 -0
- package/dist/console-registry-routes.d.ts.map +1 -0
- package/dist/console-registry-routes.js +276 -0
- package/dist/console-session-routes.d.ts +89 -0
- package/dist/console-session-routes.d.ts.map +1 -0
- package/dist/console-session-routes.js +385 -0
- package/dist/console-sessions-routes.d.ts +52 -0
- package/dist/console-sessions-routes.d.ts.map +1 -0
- package/dist/console-sessions-routes.js +106 -0
- package/dist/console-static-routes.d.ts +54 -0
- package/dist/console-static-routes.d.ts.map +1 -0
- package/dist/console-static-routes.js +190 -0
- package/dist/console-theme-routes.d.ts +3 -3
- package/dist/console-theme-routes.js +1 -1
- package/dist/console-timeline.d.ts +5 -5
- package/dist/console-timeline.d.ts.map +1 -1
- package/dist/console-timeline.js +27 -26
- package/dist/console-welcome.js +2 -2
- package/dist/email-login.d.ts.map +1 -1
- package/dist/email-login.js +2 -3
- package/dist/ggui-session-channel/action-ingress.d.ts +54 -0
- package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -0
- package/dist/ggui-session-channel/action-ingress.js +228 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts +97 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -0
- package/dist/ggui-session-channel/channel-subscriptions.js +224 -0
- package/dist/ggui-session-channel/internal-types.d.ts +102 -0
- package/dist/ggui-session-channel/internal-types.d.ts.map +1 -0
- package/dist/ggui-session-channel/internal-types.js +6 -0
- package/dist/ggui-session-channel/outbound.d.ts +81 -0
- package/dist/ggui-session-channel/outbound.d.ts.map +1 -0
- package/dist/ggui-session-channel/outbound.js +174 -0
- package/dist/ggui-session-channel/socket-router.d.ts +38 -0
- package/dist/ggui-session-channel/socket-router.d.ts.map +1 -0
- package/dist/ggui-session-channel/socket-router.js +213 -0
- package/dist/ggui-session-channel/subscribe.d.ts +165 -0
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscribe.js +370 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +40 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.js +123 -0
- package/dist/ggui-session-channel.d.ts +425 -0
- package/dist/ggui-session-channel.d.ts.map +1 -0
- package/dist/ggui-session-channel.js +262 -0
- package/dist/health-routes.d.ts +76 -0
- package/dist/health-routes.d.ts.map +1 -0
- package/dist/health-routes.js +145 -0
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/instructions-presets.d.ts +3 -3
- package/dist/instructions-presets.js +24 -24
- package/dist/llm-backed-negotiator.d.ts +68 -67
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +82 -221
- package/dist/mcp-apps-outbound.d.ts +47 -48
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +154 -177
- package/dist/mcp-endpoint-routes.d.ts +88 -0
- package/dist/mcp-endpoint-routes.d.ts.map +1 -0
- package/dist/mcp-endpoint-routes.js +359 -0
- package/dist/mcp-mounts.d.ts +2 -76
- package/dist/mcp-mounts.d.ts.map +1 -1
- package/dist/mcp-mounts.js +0 -76
- package/dist/oauth-as-routes.d.ts +60 -0
- package/dist/oauth-as-routes.d.ts.map +1 -0
- package/dist/oauth-as-routes.js +82 -0
- package/dist/oauth-clients-routes.d.ts +39 -0
- package/dist/oauth-clients-routes.d.ts.map +1 -0
- package/dist/oauth-clients-routes.js +87 -0
- package/dist/oauth-login-types.d.ts +1 -20
- package/dist/oauth-login-types.d.ts.map +1 -1
- package/dist/oauth-login-types.js +30 -7
- package/dist/oauth-login.d.ts.map +1 -1
- package/dist/oauth-login.js +3 -2
- package/dist/oauth-providers-store.d.ts.map +1 -1
- package/dist/oauth-providers-store.js +5 -5
- package/dist/oauth.d.ts +9 -8
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +41 -19
- package/dist/pairing-transport.d.ts.map +1 -1
- package/dist/pairing-transport.js +2 -1
- package/dist/request-context.d.ts +2 -2
- package/dist/request-context.js +2 -2
- package/dist/reserved-validators.d.ts.map +1 -1
- package/dist/reserved-validators.js +9 -1
- package/dist/route-param.d.ts +9 -0
- package/dist/route-param.d.ts.map +1 -0
- package/dist/route-param.js +10 -0
- package/dist/runtime-bundle-route.d.ts +43 -0
- package/dist/runtime-bundle-route.d.ts.map +1 -0
- package/dist/runtime-bundle-route.js +80 -0
- package/dist/schema-compat.d.ts +64 -62
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +23 -51
- package/dist/server.d.ts +179 -193
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +644 -3759
- package/dist/storage.d.ts +5 -5
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +5 -5
- package/dist/thread-transport.d.ts.map +1 -1
- package/dist/thread-transport.js +4 -3
- package/dist/user-session-auth.d.ts +7 -21
- package/dist/user-session-auth.d.ts.map +1 -1
- package/dist/user-session-auth.js +7 -28
- package/package.json +16 -15
- package/dist/mcp-apps-inbound.d.ts +0 -86
- package/dist/mcp-apps-inbound.d.ts.map +0 -1
- package/dist/mcp-apps-inbound.js +0 -283
- package/dist/render-channel.d.ts +0 -694
- package/dist/render-channel.d.ts.map +0 -1
- package/dist/render-channel.js +0 -1775
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console static-bundle + SPA serving routes.
|
|
3
|
+
*
|
|
4
|
+
* GET <consolePath> — mode-meta-injected `index.html`,
|
|
5
|
+
* welcome page, or onboarding redirect.
|
|
6
|
+
* <consolePath>/* (static) — `express.static` over the package's
|
|
7
|
+
* built `dist/` (HTML + JS + CSS).
|
|
8
|
+
* GET /admin/* + /devtools/* — admin-HTML gate (302 to
|
|
9
|
+
* `/admin-login?next=…` on miss).
|
|
10
|
+
* GET <consolePath>/* (SPA) — fallback to the rewritten
|
|
11
|
+
* `index.html` so the React router
|
|
12
|
+
* takes over for client-side routes.
|
|
13
|
+
*
|
|
14
|
+
* When the `distDir` doesn't exist on disk (e.g. operator forgot
|
|
15
|
+
* to run `pnpm --filter @ggui-ai/console build`), the static
|
|
16
|
+
* route is replaced with a 503 that points at the missing build —
|
|
17
|
+
* silent 404 would be mistaken for "console is broken" rather
|
|
18
|
+
* than "console wasn't built yet," which is a real debugging
|
|
19
|
+
* trap for self-hosted operators.
|
|
20
|
+
*/
|
|
21
|
+
import express from "express";
|
|
22
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { applyDevtoolSecurityHeaders } from "./console-headers.js";
|
|
25
|
+
import { renderWelcomeHtml } from "./console-welcome.js";
|
|
26
|
+
/**
|
|
27
|
+
* Mount the console static + SPA routes onto the express app.
|
|
28
|
+
* Returns nothing — the routes self-register.
|
|
29
|
+
*/
|
|
30
|
+
export function mountConsoleStaticRoutes(opts) {
|
|
31
|
+
const { app, consolePath, consoleDistDir, mode, serverName, welcomePage, landingRedirect, requestHasAdminAuth, logger, } = opts;
|
|
32
|
+
if (existsSync(consoleDistDir)) {
|
|
33
|
+
// Read + stamp `<meta name="ggui-mode" content="dev|prod">` into
|
|
34
|
+
// the SPA's `<head>` once at boot. The SPA's `mode.ts` reads the
|
|
35
|
+
// meta synchronously on first paint so `TopNav` renders the
|
|
36
|
+
// `/devtools` link without a `/info` round-trip flicker. Mode
|
|
37
|
+
// changes require a server restart — same shape as every other
|
|
38
|
+
// `CreateGguiServerOptions` field, no live-toggle ceremony.
|
|
39
|
+
const indexPath = path.join(consoleDistDir, "index.html");
|
|
40
|
+
const META_TAG = `<meta name="ggui-mode" content="${mode}">`;
|
|
41
|
+
let indexHtml;
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(indexPath, "utf-8");
|
|
44
|
+
// Inject right after `<head>` so the meta is available before
|
|
45
|
+
// any subsequent `<script>` tag executes. Idempotent: if a
|
|
46
|
+
// previous build somehow already inlined the meta, the
|
|
47
|
+
// injection still produces a valid (duplicate but harmless)
|
|
48
|
+
// tag — `mode.ts` reads the first hit.
|
|
49
|
+
indexHtml = raw.includes("<head>")
|
|
50
|
+
? raw.replace("<head>", `<head>${META_TAG}`)
|
|
51
|
+
: raw.replace(/^/, `${META_TAG}\n`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger.warn("console_index_read_failed", {
|
|
55
|
+
path: indexPath,
|
|
56
|
+
error: String(err),
|
|
57
|
+
});
|
|
58
|
+
indexHtml = `<!doctype html><html><head>${META_TAG}</head><body></body></html>`;
|
|
59
|
+
}
|
|
60
|
+
const sendConsoleHtml = (res) => {
|
|
61
|
+
applyDevtoolSecurityHeaders(res);
|
|
62
|
+
res.type("text/html").send(indexHtml);
|
|
63
|
+
};
|
|
64
|
+
// Welcome HTML — server-rendered landing for `consolePath === '/'`
|
|
65
|
+
// when the operator wires `welcomePage`. Identifies who runs
|
|
66
|
+
// the server (operator block; entirely hidden when unset),
|
|
67
|
+
// describes the public deep-link surfaces, and offers the
|
|
68
|
+
// operator-login affordance. No JS, no SPA mount, same security
|
|
69
|
+
// headers as `sendConsoleHtml`.
|
|
70
|
+
const welcomeEnabled = welcomePage !== undefined && consolePath === "/";
|
|
71
|
+
const sendWelcomeHtml = welcomeEnabled && welcomePage !== undefined
|
|
72
|
+
? (res) => {
|
|
73
|
+
applyDevtoolSecurityHeaders(res);
|
|
74
|
+
res.type("text/html").send(renderWelcomeHtml(welcomePage, serverName));
|
|
75
|
+
}
|
|
76
|
+
: null;
|
|
77
|
+
// Onboarding redirect — must run BEFORE express.static, which
|
|
78
|
+
// would otherwise serve `index.html` for `GET /` directly and
|
|
79
|
+
// never give the SPA fallback (or this redirect) a chance.
|
|
80
|
+
app.get(consolePath, (req, res, next) => {
|
|
81
|
+
// Static middleware fires on the trailing-slash variant when
|
|
82
|
+
// consolePath !== '/'. Both shapes route here.
|
|
83
|
+
if (req.path !== consolePath && req.path !== `${consolePath}/`) {
|
|
84
|
+
return next();
|
|
85
|
+
}
|
|
86
|
+
if (landingRedirect) {
|
|
87
|
+
const target = landingRedirect();
|
|
88
|
+
if (target && target !== req.path) {
|
|
89
|
+
res.redirect(302, target);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (sendWelcomeHtml) {
|
|
94
|
+
sendWelcomeHtml(res);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
sendConsoleHtml(res);
|
|
98
|
+
});
|
|
99
|
+
app.use(consolePath, express.static(consoleDistDir, {
|
|
100
|
+
// index:false — explicit handler above owns `/` so the meta
|
|
101
|
+
// tag gets injected. Without this, express.static would
|
|
102
|
+
// race the handler and sometimes serve the raw file.
|
|
103
|
+
index: false,
|
|
104
|
+
// Short cache — operators iterating on their server want
|
|
105
|
+
// fresh copies after a rebuild; production-hardening
|
|
106
|
+
// (etag, long-term caching for /assets/*) is a slice-3 polish
|
|
107
|
+
// concern.
|
|
108
|
+
maxAge: 0,
|
|
109
|
+
fallthrough: true,
|
|
110
|
+
// Attach the console security header set to every
|
|
111
|
+
// static response. `setHeaders` runs for successful hits
|
|
112
|
+
// (HTML + JS + CSS + asset 200s); misses that fall through
|
|
113
|
+
// to the SPA fallback below are covered by the fallback's
|
|
114
|
+
// explicit `applyDevtoolSecurityHeaders` call.
|
|
115
|
+
setHeaders: applyDevtoolSecurityHeaders,
|
|
116
|
+
}));
|
|
117
|
+
// Admin-HTML gate. The SPA's `/admin/*` and `/devtools/*` zones
|
|
118
|
+
// are operator-only — without this gate, every admin page is a
|
|
119
|
+
// GET away from anyone who guesses the path. The corresponding
|
|
120
|
+
// JSON APIs already gate at `/ggui/console/keys`, but the SPA
|
|
121
|
+
// shell itself was unauthenticated. Mounted only when the gate
|
|
122
|
+
// shape is available (admin token resolved + closure built).
|
|
123
|
+
//
|
|
124
|
+
// 302 to `/admin-login?next=<encoded-path>` on miss — the login
|
|
125
|
+
// page reads `next` from the query string and bounces back after
|
|
126
|
+
// a successful token paste. No client-side cookie set here; the
|
|
127
|
+
// existing `POST /ggui/console/admin-login` route owns minting.
|
|
128
|
+
//
|
|
129
|
+
// Scope:
|
|
130
|
+
// - GATED: `/admin/*`, `/devtools/*`
|
|
131
|
+
// - UNGATED: `/admin-login`, `/s/*`, `/preview/*`, `/` (welcome
|
|
132
|
+
// when wired, SPA index otherwise)
|
|
133
|
+
//
|
|
134
|
+
// The welcome page (`/`) is intentionally public — it's the
|
|
135
|
+
// operator-identification surface, not an admin tool. Public
|
|
136
|
+
// deep-link surfaces stay reachable: a render viewer URL
|
|
137
|
+
// `/s/<shortCode>` and a blueprint preview URL `/preview/<id>`
|
|
138
|
+
// are how end-users / blueprint authors land on the server.
|
|
139
|
+
if (requestHasAdminAuth !== null) {
|
|
140
|
+
const adminAuth = requestHasAdminAuth;
|
|
141
|
+
app.get(/^\/(admin|devtools)(\/.*)?$/, (req, res, next) => {
|
|
142
|
+
if (req.path === "/admin-login")
|
|
143
|
+
return next();
|
|
144
|
+
if (adminAuth(req))
|
|
145
|
+
return next();
|
|
146
|
+
applyDevtoolSecurityHeaders(res);
|
|
147
|
+
const next_ = encodeURIComponent(req.originalUrl || req.path);
|
|
148
|
+
res.redirect(302, `/admin-login?next=${next_}`);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// SPA fallback: the console client owns client-side routes
|
|
152
|
+
// (`/`, `/s/<shortCode>`, `/admin/*`, `/devtools/*`). An unknown
|
|
153
|
+
// sub-path under the mount must serve the rewritten
|
|
154
|
+
// `index.html` so the React router takes over.
|
|
155
|
+
//
|
|
156
|
+
// Express 5 / path-to-regexp v8 rejects the bare `'*'` and
|
|
157
|
+
// `'foo/*'` wildcard strings that worked in v6; named splats
|
|
158
|
+
// (`{*splat}`) or RegExp patterns are required. Using RegExp
|
|
159
|
+
// here matches the admin-gate pattern above and avoids the
|
|
160
|
+
// parser-version churn.
|
|
161
|
+
const spaFallbackPattern = consolePath === "/"
|
|
162
|
+
? /^\/.*$/
|
|
163
|
+
: new RegExp(`^${consolePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/.*$`);
|
|
164
|
+
app.get(spaFallbackPattern, (req, res, next) => {
|
|
165
|
+
// Only fallback for GET of non-asset, non-API paths.
|
|
166
|
+
// `express.static` already served any asset that exists;
|
|
167
|
+
// an `/assets/foo.js` that doesn't exist SHOULD 404 rather
|
|
168
|
+
// than returning HTML.
|
|
169
|
+
if (req.path.startsWith("/ggui/"))
|
|
170
|
+
return next();
|
|
171
|
+
if (req.path.startsWith(`${consolePath === "/" ? "" : consolePath}/assets/`)) {
|
|
172
|
+
return next();
|
|
173
|
+
}
|
|
174
|
+
sendConsoleHtml(res);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
logger.warn("console_dist_missing", {
|
|
179
|
+
distDir: consoleDistDir,
|
|
180
|
+
hint: "Run `pnpm --filter @ggui-ai/console build` to produce the static bundle. Serving 503 from the mount point until the bundle exists.",
|
|
181
|
+
});
|
|
182
|
+
app.use(consolePath, (_req, res) => {
|
|
183
|
+
applyDevtoolSecurityHeaders(res);
|
|
184
|
+
res
|
|
185
|
+
.status(503)
|
|
186
|
+
.type("text/plain")
|
|
187
|
+
.send("console bundle not built. Run:\n pnpm --filter @ggui-ai/console build\n");
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Authentication: piggy-backs on the same admin gate the LLM-keys
|
|
22
22
|
* routes use — operator-only since the value persists to ggui.json,
|
|
23
23
|
* which the OSS server treats as trusted manifest input. End-users
|
|
24
|
-
* who paired into a
|
|
24
|
+
* who paired into a render don't get to mutate the project's
|
|
25
25
|
* theme. Multi-tenant deployments may relax this in a follow-up by
|
|
26
26
|
* scoping overrides per user (overrides become server state, not
|
|
27
27
|
* file state).
|
|
@@ -81,8 +81,8 @@ interface MountOptions {
|
|
|
81
81
|
* Optional change notifier — fires every time the operator's
|
|
82
82
|
* theme selection changes through `POST /ggui/console/theme` (or
|
|
83
83
|
* the `/upload` variant). The CLI uses this to mirror writes into
|
|
84
|
-
* a shared mutable state cell that the
|
|
85
|
-
* `themeProvider` reads, so a save reaches the next
|
|
84
|
+
* a shared mutable state cell that the render handler's
|
|
85
|
+
* `themeProvider` reads, so a save reaches the next render without
|
|
86
86
|
* a server restart.
|
|
87
87
|
*
|
|
88
88
|
* Fires AFTER the on-disk write succeeds and AFTER the route's
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Authentication: piggy-backs on the same admin gate the LLM-keys
|
|
22
22
|
* routes use — operator-only since the value persists to ggui.json,
|
|
23
23
|
* which the OSS server treats as trusted manifest input. End-users
|
|
24
|
-
* who paired into a
|
|
24
|
+
* who paired into a render don't get to mutate the project's
|
|
25
25
|
* theme. Multi-tenant deployments may relax this in a follow-up by
|
|
26
26
|
* scoping overrides per user (overrides become server state, not
|
|
27
27
|
* file state).
|
|
@@ -8,20 +8,20 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Post-Phase-B (flatten-render-identity): renamed from the prior
|
|
10
10
|
* "sessions" terminology; the canonical addressable unit is now the
|
|
11
|
-
* render. The on-wire `
|
|
11
|
+
* render. The on-wire `sessionId` path param replaces the prior `renderId`.
|
|
12
12
|
*
|
|
13
|
-
* Memory: bounded by whatever the underlying
|
|
14
|
-
* `
|
|
13
|
+
* Memory: bounded by whatever the underlying GguiSessionStore retains.
|
|
14
|
+
* `InMemoryGguiSessionStore` keeps everything for the process lifetime —
|
|
15
15
|
* fine for OSS dev. A hosted closed runtime's persistent store is the
|
|
16
16
|
* durability surface.
|
|
17
17
|
*/
|
|
18
18
|
import type { Express } from 'express';
|
|
19
|
-
import type {
|
|
19
|
+
import type { GguiSessionStore, GguiSessionStreamBuffer } from '@ggui-ai/mcp-server-core';
|
|
20
20
|
/**
|
|
21
21
|
* Mount `/ggui/console/timeline/*` routes on `app`. Caller is
|
|
22
22
|
* responsible for admin-gating the path prefix beforehand — this
|
|
23
23
|
* function does not re-implement auth (matches the
|
|
24
24
|
* `mountConsoleLlmTraceRoutes` shape in console-llm-trace.ts).
|
|
25
25
|
*/
|
|
26
|
-
export declare function mountConsoleTimelineRoutes(app: Express, renderStore:
|
|
26
|
+
export declare function mountConsoleTimelineRoutes(app: Express, renderStore: GguiSessionStore | undefined, streamBuffer: GguiSessionStreamBuffer | undefined): void;
|
|
27
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;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAEV,
|
|
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,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAkElC;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,YAAY,EAAE,uBAAuB,GAAG,SAAS,GAChD,IAAI,CA6JN"}
|
package/dist/console-timeline.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { applyDevtoolSecurityHeaders } from './console-headers.js';
|
|
2
|
+
import { singleParam } from './route-param.js';
|
|
2
3
|
/**
|
|
3
|
-
* Drain {@link
|
|
4
|
+
* Drain {@link GguiSessionStore.observe} into an array. The observe iterator
|
|
4
5
|
* with `{ tail: false }` resolves cleanly after replaying every stored
|
|
5
6
|
* event, so this is a plain `for await`. Bound by the store's per-
|
|
6
7
|
* render retention (in-memory: full history; hosted: store-defined).
|
|
7
8
|
*/
|
|
8
|
-
async function
|
|
9
|
+
async function drainGguiSessionEvents(renderStore, sessionId) {
|
|
9
10
|
const out = [];
|
|
10
|
-
for await (const event of renderStore.observe(
|
|
11
|
+
for await (const event of renderStore.observe(sessionId, {
|
|
11
12
|
fromSeq: 1,
|
|
12
13
|
tail: false,
|
|
13
14
|
})) {
|
|
@@ -35,7 +36,7 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
35
36
|
}
|
|
36
37
|
// Zero-config shape: no store wired (pure-MCP boot) → empty list.
|
|
37
38
|
if (!renderStore) {
|
|
38
|
-
const body = {
|
|
39
|
+
const body = { sessions: [], total: 0 };
|
|
39
40
|
res.json(body);
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
@@ -59,14 +60,14 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
59
60
|
}
|
|
60
61
|
// Compute status from render state (the store doesn't
|
|
61
62
|
// surface its private `closed` flag uniformly across impls).
|
|
62
|
-
// We mirror the
|
|
63
|
+
// We mirror the InMemoryGguiSessionStore's rule:
|
|
63
64
|
// expired = expiresAt <= now, otherwise active. The
|
|
64
65
|
// 'completed' state needs the closed flag to disambiguate;
|
|
65
66
|
// we report 'active' until eviction. Operators reading the
|
|
66
67
|
// detail pane get the authoritative status from the store.
|
|
67
68
|
const status = row.expiresAt <= now ? 'expired' : 'active';
|
|
68
69
|
summaries.push({
|
|
69
|
-
|
|
70
|
+
sessionId: row.id,
|
|
70
71
|
appId: row.appId,
|
|
71
72
|
createdAt: row.createdAt,
|
|
72
73
|
lastActivityAt: row.lastActivityAt,
|
|
@@ -74,45 +75,45 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
74
75
|
streamSeq,
|
|
75
76
|
});
|
|
76
77
|
}
|
|
77
|
-
// Most-recent activity first. Tiebreak on
|
|
78
|
+
// Most-recent activity first. Tiebreak on sessionId for
|
|
78
79
|
// stable ordering across reloads.
|
|
79
80
|
summaries.sort((a, b) => {
|
|
80
81
|
const byRecency = b.lastActivityAt - a.lastActivityAt;
|
|
81
82
|
if (byRecency !== 0)
|
|
82
83
|
return byRecency;
|
|
83
|
-
return a.
|
|
84
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
84
85
|
});
|
|
85
86
|
const trimmed = summaries.slice(0, limit);
|
|
86
87
|
const body = {
|
|
87
|
-
|
|
88
|
+
sessions: trimmed,
|
|
88
89
|
total: summaries.length,
|
|
89
90
|
};
|
|
90
91
|
res.json(body);
|
|
91
92
|
}
|
|
92
93
|
catch (err) {
|
|
93
94
|
res.status(500).json({
|
|
94
|
-
error: '
|
|
95
|
+
error: 'timeline_sessions_list_failed',
|
|
95
96
|
message: err instanceof Error
|
|
96
|
-
? `
|
|
97
|
-
: `
|
|
97
|
+
? `GguiSession store failed to list — ${err.message}`
|
|
98
|
+
: `GguiSession store failed to list — ${String(err)}`,
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
|
-
// GET /ggui/console/timeline/:
|
|
102
|
-
app.get('/ggui/console/timeline/:
|
|
102
|
+
// GET /ggui/console/timeline/:sessionId/events
|
|
103
|
+
app.get('/ggui/console/timeline/:sessionId/events', async (req, res) => {
|
|
103
104
|
applyDevtoolSecurityHeaders(res);
|
|
104
|
-
const
|
|
105
|
-
if (!
|
|
105
|
+
const sessionId = singleParam(req.params['sessionId']);
|
|
106
|
+
if (!sessionId || sessionId.length === 0) {
|
|
106
107
|
res.status(400).json({
|
|
107
|
-
error: '
|
|
108
|
-
message: '
|
|
108
|
+
error: 'invalid_session_id',
|
|
109
|
+
message: 'sessionId path parameter is required',
|
|
109
110
|
});
|
|
110
111
|
return;
|
|
111
112
|
}
|
|
112
113
|
// Zero-config shape: no store wired → empty events.
|
|
113
114
|
if (!renderStore) {
|
|
114
115
|
const body = {
|
|
115
|
-
|
|
116
|
+
sessionId,
|
|
116
117
|
events: [],
|
|
117
118
|
streamSeq: 0,
|
|
118
119
|
status: 'unknown',
|
|
@@ -121,14 +122,14 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
try {
|
|
124
|
-
const stored = await renderStore.get(
|
|
125
|
+
const stored = await renderStore.get(sessionId);
|
|
125
126
|
if (!stored) {
|
|
126
127
|
// 404 is the right status — but we still return a well-
|
|
127
128
|
// formed body so the SPA can render an "expired/dropped"
|
|
128
129
|
// notice without a special-case branch. Body matches the
|
|
129
130
|
// schema; status code disambiguates.
|
|
130
131
|
const body = {
|
|
131
|
-
|
|
132
|
+
sessionId,
|
|
132
133
|
events: [],
|
|
133
134
|
streamSeq: 0,
|
|
134
135
|
status: 'unknown',
|
|
@@ -136,11 +137,11 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
136
137
|
res.status(404).json(body);
|
|
137
138
|
return;
|
|
138
139
|
}
|
|
139
|
-
const events = await
|
|
140
|
+
const events = await drainGguiSessionEvents(renderStore, sessionId);
|
|
140
141
|
let streamSeq = 0;
|
|
141
142
|
if (streamBuffer) {
|
|
142
143
|
try {
|
|
143
|
-
streamSeq = await streamBuffer.currentSeq(
|
|
144
|
+
streamSeq = await streamBuffer.currentSeq(sessionId);
|
|
144
145
|
}
|
|
145
146
|
catch {
|
|
146
147
|
// Best-effort. Inbound events stand on their own.
|
|
@@ -149,7 +150,7 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
149
150
|
const now = Date.now();
|
|
150
151
|
const status = stored.expiresAt <= now ? 'expired' : 'active';
|
|
151
152
|
const body = {
|
|
152
|
-
|
|
153
|
+
sessionId,
|
|
153
154
|
events,
|
|
154
155
|
streamSeq,
|
|
155
156
|
status,
|
|
@@ -160,8 +161,8 @@ export function mountConsoleTimelineRoutes(app, renderStore, streamBuffer) {
|
|
|
160
161
|
res.status(500).json({
|
|
161
162
|
error: 'timeline_events_drain_failed',
|
|
162
163
|
message: err instanceof Error
|
|
163
|
-
? `
|
|
164
|
-
: `
|
|
164
|
+
? `GguiSession store failed to drain — ${err.message}`
|
|
165
|
+
: `GguiSession store failed to drain — ${String(err)}`,
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
168
|
});
|
package/dist/console-welcome.js
CHANGED
|
@@ -202,9 +202,9 @@ export const renderWelcomeHtml = (inputs, serverName) => {
|
|
|
202
202
|
<p class="eyebrow">Public surfaces</p>
|
|
203
203
|
<div class="surfaces">
|
|
204
204
|
<div class="surface">
|
|
205
|
-
<span class="surface-label">
|
|
205
|
+
<span class="surface-label">GguiSession viewer</span>
|
|
206
206
|
<code>/s/<short-code></code>
|
|
207
|
-
<span class="desc">Open the live
|
|
207
|
+
<span class="desc">Open the live render your agent linked you to.</span>
|
|
208
208
|
</div>
|
|
209
209
|
<div class="surface">
|
|
210
210
|
<span class="surface-label">Blueprint preview</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-login.d.ts","sourceRoot":"","sources":["../src/email-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"email-login.d.ts","sourceRoot":"","sources":["../src/email-login.ts"],"names":[],"mappings":"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"}
|
package/dist/email-login.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { isRecord } from '@ggui-ai/protocol';
|
|
2
3
|
import { formatUserSessionCookieHeader } from './user-session-auth.js';
|
|
3
4
|
import { createConsoleLogger } from './logger.js';
|
|
4
5
|
/**
|
|
@@ -121,9 +122,7 @@ export function mountEmailLoginRoutes(app, opts) {
|
|
|
121
122
|
// email enumeration — see route docstring for why).
|
|
122
123
|
app.post(startPath, async (req, res) => {
|
|
123
124
|
const reqLogger = opts.logger.child({ route: 'POST ' + startPath });
|
|
124
|
-
const body =
|
|
125
|
-
? req.body
|
|
126
|
-
: {};
|
|
125
|
+
const body = isRecord(req.body) ? req.body : {};
|
|
127
126
|
const rawEmail = typeof body['email'] === 'string' ? body['email'] : '';
|
|
128
127
|
const email = rawEmail.trim().toLowerCase();
|
|
129
128
|
if (!EMAIL_RE.test(email) || email.length > 254) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound `action` ingress + consume bridge for the live channel —
|
|
3
|
+
* contract enforcement on user gestures arriving over WS, the
|
|
4
|
+
* dual-write onto the retained event ledger + the pending-events pipe
|
|
5
|
+
* (`ggui_consume`'s queue), and the ack carrying the ledger seq back
|
|
6
|
+
* to the client.
|
|
7
|
+
*/
|
|
8
|
+
import type { GguiSessionStore, PendingEventConsumer } from "@ggui-ai/mcp-server-core";
|
|
9
|
+
import type { WebSocketMessage } from "@ggui-ai/protocol/transport/websocket";
|
|
10
|
+
import type { WebSocket } from "ws";
|
|
11
|
+
import type { Logger } from "../logger.js";
|
|
12
|
+
import type { Subscriber } from "./internal-types.js";
|
|
13
|
+
import type { Outbound } from "./outbound.js";
|
|
14
|
+
export interface ActionIngressDeps {
|
|
15
|
+
readonly logger: Logger;
|
|
16
|
+
readonly renderStore: GguiSessionStore;
|
|
17
|
+
/**
|
|
18
|
+
* Pending-events pipe — see
|
|
19
|
+
* `GguiSessionChannelOptions.pendingEventConsumer` for the dual-write
|
|
20
|
+
* contract. Absent → ledger-only ingress.
|
|
21
|
+
*/
|
|
22
|
+
readonly pendingEventConsumer?: PendingEventConsumer;
|
|
23
|
+
readonly send: Outbound["send"];
|
|
24
|
+
readonly sendError: Outbound["sendError"];
|
|
25
|
+
}
|
|
26
|
+
export interface ActionIngress {
|
|
27
|
+
/**
|
|
28
|
+
* Handle an inbound `action` message — the canonical flat
|
|
29
|
+
* {@link ActionEnvelope} shape.
|
|
30
|
+
*
|
|
31
|
+
* Inbound actions are gated by {@link assertActionContract} only —
|
|
32
|
+
* the actionSpec payload check for `data:submit` types. (The
|
|
33
|
+
* pre-Phase-B `subscription.events` allowlist gate was deleted with
|
|
34
|
+
* the session-stack collapse; per-render event policy needs a new
|
|
35
|
+
* wire shape before any second gate can exist.)
|
|
36
|
+
*
|
|
37
|
+
* Accepted envelopes dual-write, mirroring the
|
|
38
|
+
* `ggui_runtime_submit_action` relay's posture:
|
|
39
|
+
*
|
|
40
|
+
* 1. The retained event ledger (`renderStore.appendEvent`) — the
|
|
41
|
+
* ack's `seq` source; failure is the load-bearing
|
|
42
|
+
* `APPEND_FAILED` path.
|
|
43
|
+
* 2. For `data:submit` only, the pending-events pipe
|
|
44
|
+
* ({@link ActionIngressDeps.pendingEventConsumer}) — the
|
|
45
|
+
* queue `ggui_consume` drains, so the agent receives the gesture
|
|
46
|
+
* mid-turn. Pipe failure degrades to ledger-only with a warn;
|
|
47
|
+
* it never changes the ack.
|
|
48
|
+
*/
|
|
49
|
+
handleInboundAction(ws: WebSocket, sub: Subscriber, message: WebSocketMessage & {
|
|
50
|
+
type: "action";
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
export declare function createActionIngress(deps: ActionIngressDeps): ActionIngress;
|
|
54
|
+
//# sourceMappingURL=action-ingress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-ingress.d.ts","sourceRoot":"","sources":["../../src/ggui-session-channel/action-ingress.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,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;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA+G9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,mBAAmB,CACjB,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAiJ1E"}
|