@ggui-ai/mcp-server 0.24.0 → 0.26.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/README.md +2 -2
- package/dist/api-renders-routes.d.ts +46 -5
- package/dist/api-renders-routes.d.ts.map +1 -1
- package/dist/api-renders-routes.js +98 -39
- package/dist/api-renders-stream-route.d.ts +2 -1
- package/dist/api-renders-stream-route.d.ts.map +1 -1
- package/dist/api-renders-stream-route.js +14 -2
- package/dist/build-mcp.d.ts +16 -0
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +82 -12
- package/dist/console-auth.d.ts +10 -10
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/control-service.d.ts.map +1 -1
- package/dist/control-service.js +0 -3
- package/dist/first-seen.d.ts +14 -0
- package/dist/first-seen.d.ts.map +1 -0
- package/dist/first-seen.js +19 -0
- package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -1
- package/dist/ggui-session-channel/action-ingress.js +96 -8
- package/dist/ggui-session-channel/channel-subscriptions.d.ts +0 -1
- package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -1
- package/dist/ggui-session-channel/channel-subscriptions.js +4 -1
- package/dist/ggui-session-channel/internal-types.d.ts +20 -12
- package/dist/ggui-session-channel/internal-types.d.ts.map +1 -1
- package/dist/ggui-session-channel/outbound.d.ts.map +1 -1
- package/dist/ggui-session-channel/outbound.js +18 -6
- package/dist/ggui-session-channel/stream-epoch.d.ts +61 -0
- package/dist/ggui-session-channel/stream-epoch.d.ts.map +1 -0
- package/dist/ggui-session-channel/stream-epoch.js +47 -0
- package/dist/ggui-session-channel/subscribe.d.ts +40 -53
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
- package/dist/ggui-session-channel/subscribe.js +288 -124
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -1
- package/dist/ggui-session-channel/subscriber-lifecycle.js +17 -7
- package/dist/ggui-session-channel.d.ts +43 -7
- package/dist/ggui-session-channel.d.ts.map +1 -1
- package/dist/ggui-session-channel.js +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/instructions-presets.d.ts +22 -21
- package/dist/instructions-presets.d.ts.map +1 -1
- package/dist/instructions-presets.js +38 -38
- package/dist/llm-backed-negotiator.d.ts +4 -37
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +28 -210
- package/dist/mcp-apps-outbound.d.ts +40 -91
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +100 -15
- package/dist/mcp-endpoint-routes.d.ts +1 -0
- package/dist/mcp-endpoint-routes.d.ts.map +1 -1
- package/dist/mcp-endpoint-routes.js +38 -4
- package/dist/runtime-bundle-hash.d.ts +7 -0
- package/dist/runtime-bundle-hash.d.ts.map +1 -1
- package/dist/runtime-bundle-hash.js +8 -2
- package/dist/runtime-bundle-route.d.ts.map +1 -1
- package/dist/runtime-bundle-route.js +15 -0
- package/dist/server.d.ts +143 -85
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +224 -110
- package/dist/storage.d.ts +9 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -3
- package/dist/view-proof-gate.d.ts +96 -0
- package/dist/view-proof-gate.d.ts.map +1 -0
- package/dist/view-proof-gate.js +167 -0
- package/package.json +14 -15
- package/dist/render-read-gate.d.ts +0 -50
- package/dist/render-read-gate.d.ts.map +0 -1
- package/dist/render-read-gate.js +0 -36
package/dist/server.js
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
*/
|
|
40
40
|
import { CONSOLE_DIST_DIR } from "@ggui-ai/console/server";
|
|
41
41
|
import { RUNTIME_BUNDLE_FILE, RUNTIME_BUNDLE_URL_PATH, RUNTIME_SHIMS_DIR, RUNTIME_SHIMS_URL_PREFIX, } from "@ggui-ai/iframe-runtime/server";
|
|
42
|
-
import {
|
|
42
|
+
import { isTokenRegisteringAuthAdapter, DEFAULT_WS_TOKEN_REFRESH_WINDOW_MULTIPLIER, DEFAULT_WS_TOKEN_TTL_SEC, mintViewRoot, mintWsToken, verifyWsTokenSignature, verifyToken, } from "@ggui-ai/mcp-server-core";
|
|
43
43
|
import { createInMemoryBlueprintSearch, createInMemoryGeneratorRegistry, FixedWindowRateLimiter, InMemoryActiveConsumerRegistry, InMemoryAppMetadataStore, InMemoryAuthAdapter, InMemoryBlueprintIndex, InMemoryBlueprintStore, InMemoryKeyValueStore, InMemoryPairingService, InMemoryPendingEventConsumer, InMemoryQuotaStore, InMemoryGguiSessionStore, InMemoryGguiSessionStreamBuffer, InMemoryVectorStore, MockEmbeddingProvider, NoopAuditSink, NoopRateLimiter, NoopTelemetrySink, } from "@ggui-ai/mcp-server-core/in-memory";
|
|
44
44
|
import { createGguiListGadgetsHandler, createGguiListThemesHandler, } from "@ggui-ai/mcp-server-handlers/app-discovery";
|
|
45
45
|
import { createDescribeBlueprintFormatHandler, createDescribeDataContractFormatHandler, createGetBlueprintBoilerplateHandler, createGetExampleBlueprintsHandler, createListAvailablePrimitivesHandler, createListFeaturedBlueprintsHandler, createRenderBlueprintHandler, createSearchBlueprintsHandler, createValidateBlueprintHandler, } from "@ggui-ai/mcp-server-handlers/blueprints";
|
|
@@ -62,20 +62,17 @@ import { mountDevtoolThemeRoutes, } from "./console-theme-routes.js";
|
|
|
62
62
|
import { mountConsoleTimelineRoutes } from "./console-timeline.js";
|
|
63
63
|
import { buildInlineRenderShellHtml, } from "./mcp-apps-outbound.js";
|
|
64
64
|
import { BoundedValidatorTraceSink, mountConsoleValidatorRoutes } from "./console-validator.js";
|
|
65
|
-
// Operator-class MCP handlers —
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// pattern).
|
|
65
|
+
// Operator-class MCP handlers — the six `ggui_ops_*` apps handlers.
|
|
66
|
+
// Every factory binds a deps seam; deployments that don't wire the
|
|
67
|
+
// seam simply don't register the tool (matching
|
|
68
|
+
// `ggui_ops_get_credit_balance`'s pattern).
|
|
70
69
|
import { createCreateAppHandler, createDeleteAppHandler, createListAppsHandler, createSetAppThemeHandler, createSetDefaultAppHandler, createUpdateAppHandler, } from "@ggui-ai/mcp-server-handlers/ops-apps";
|
|
71
70
|
import { fontFaceRules, validateOverlayCoverage } from "@ggui-ai/design/themes";
|
|
72
|
-
import {
|
|
73
|
-
import { createRedeemCouponHandler, } from "@ggui-ai/mcp-server-handlers/ops-coupon";
|
|
74
|
-
import { createCreateOrgHandler, createGetOrgBalanceHandler, createInviteToOrgHandler, createListOrgsHandler, createRemoveOrgMemberHandler, createRenameOrgHandler, createRevokeInviteHandler, } from "@ggui-ai/mcp-server-handlers/ops-orgs";
|
|
75
|
-
import { createGguiConsumeHandler, createGguiDeclareToolCatalogHandler, createGguiEmitHandler, createGguiGetRenderSourceHandler, createGguiGetSessionHandler, createGguiHandshakeHandler, createGguiListSessionsHandler, createGguiRefreshWsTokenHandler, createGguiRenderHandler, createGguiRuntimePullHandler, createGguiRuntimeTelemetryHandler, createGguiSubmitActionHandler, createGguiSyncContextHandler, createGguiUpdateHandler, createGguiAmendHandler, InMemoryToolIdentityCatalogStore, createInMemoryProvisionalPreviewRegistry, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
71
|
+
import { createGguiConsumeHandler, createGguiDeclareToolCatalogHandler, createGguiEmitHandler, createGguiGetRenderSourceHandler, createGguiGetSessionHandler, createGguiHandshakeHandler, createGguiListSessionsHandler, createGguiRefreshWsTokenHandler, createGguiRenderHandler, createGguiReportRenderFailureHandler, createGguiRuntimePullHandler, createGguiRuntimeTelemetryHandler, createGguiSubmitActionHandler, createGguiSyncContextHandler, createGguiUpdateHandler, createGguiAmendHandler, InMemoryToolIdentityCatalogStore, createInMemoryProvisionalPreviewRegistry, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
76
72
|
import { DEFAULT_ADMIN_BLUEPRINTS_PATH, mountAdminBlueprintsTransport, } from "./admin-blueprints-transport.js";
|
|
77
73
|
import { mountAdminOAuthProvidersTransport } from "./admin-oauth-providers-transport.js";
|
|
78
74
|
import { DEFAULT_BUILDER_APP_ID, defaultAppIdFromIdentity } from "./auth.js";
|
|
75
|
+
import { assertViewProofDeclarations, createViewProofGate, unverifiableViewProofTools, } from "./view-proof-gate.js";
|
|
79
76
|
import { mountMcpEndpoints } from "./mcp-endpoint-routes.js";
|
|
80
77
|
import { mountApiRendersRoutes } from "./api-renders-routes.js";
|
|
81
78
|
import { mountApiRendersStreamRoute } from "./api-renders-stream-route.js";
|
|
@@ -100,7 +97,7 @@ import { computeRuntimeBundleHash, insertRuntimeBundleHash, } from "./runtime-bu
|
|
|
100
97
|
import { createCsrfMiddleware, mountCsrfTokenRoute } from "./csrf-middleware.js";
|
|
101
98
|
import { mountEmailLoginRoutes } from "./email-login.js";
|
|
102
99
|
import { resolveMcpInstructions } from "./instructions-presets.js";
|
|
103
|
-
import {
|
|
100
|
+
import { createLlmBackedHandshakeNegotiator } from "./llm-backed-negotiator.js";
|
|
104
101
|
import { createConsoleLogger } from "./logger.js";
|
|
105
102
|
import { buildControlService, CONTROL_PATH } from "./control-service.js";
|
|
106
103
|
import { createBrowserCorsMiddleware, createPreflightFallback } from "./browser-cors.js";
|
|
@@ -234,6 +231,9 @@ export function buildOpsBlueprintHandlers(input) {
|
|
|
234
231
|
}));
|
|
235
232
|
handlers.push(createGguiOpsDeleteBlueprintHandler({
|
|
236
233
|
blueprintStore: bundle.blueprintStore,
|
|
234
|
+
// ggui#1541 — the registry generate and register mirror into, so a
|
|
235
|
+
// delete takes the blueprint out of the handshake's reuse too.
|
|
236
|
+
...(bundle.cacheRegistry ? { cacheRegistry: bundle.cacheRegistry } : {}),
|
|
237
237
|
authorizeAppAccess: bundle.authorizeAppAccess,
|
|
238
238
|
}));
|
|
239
239
|
return handlers;
|
|
@@ -366,15 +366,25 @@ export function defaultHandlers(deps) {
|
|
|
366
366
|
handlers.push(createGguiRuntimeTelemetryHandler({
|
|
367
367
|
...(deps.logger ? { logger: deps.logger } : {}),
|
|
368
368
|
}));
|
|
369
|
-
// `
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
|
|
369
|
+
// `ggui_runtime_report_render_failure` (ggui#1609) — a card whose render
|
|
370
|
+
// failed says so, for the session its view proof binds. Unlike
|
|
371
|
+
// telemetry it is recorded, so its session is proven, never claimed;
|
|
372
|
+
// this server names each proven report (`render_failed`) and stores
|
|
373
|
+
// nothing — a deployment that keeps a per-session mark supplies
|
|
374
|
+
// `recordRenderFailure` when it composes its own handler set.
|
|
375
|
+
handlers.push(createGguiReportRenderFailureHandler({
|
|
376
|
+
renderStore: deps.render.renderStore,
|
|
377
|
+
}));
|
|
378
|
+
// `ggui_runtime_refresh_ws_token` — the authorized re-mint of a
|
|
379
|
+
// view's live credential (ggui#1496 part B). Registered only when the
|
|
380
|
+
// deployment can both verify an envelope and mint a credential;
|
|
381
|
+
// without either, the tool would answer BOOTSTRAP_NOT_SUPPORTED on
|
|
382
|
+
// every call, which is honest but useless on tools/list.
|
|
383
|
+
if (deps.render.wsTokenVerify && deps.render.mintBootstrap) {
|
|
376
384
|
handlers.push(createGguiRefreshWsTokenHandler({
|
|
377
|
-
|
|
385
|
+
renderStore: deps.render.renderStore,
|
|
386
|
+
verify: deps.render.wsTokenVerify,
|
|
387
|
+
mint: deps.render.mintBootstrap,
|
|
378
388
|
}));
|
|
379
389
|
}
|
|
380
390
|
// Phase B (flatten-render-identity): the session lifetime entry
|
|
@@ -453,7 +463,13 @@ export function defaultHandlers(deps) {
|
|
|
453
463
|
// Bootstrap-emission deps mirror render so MCP Apps hosts that
|
|
454
464
|
// forward `ui/notifications/tool-result` via postMessage can
|
|
455
465
|
// re-apply patched props on the live mount without a WS round-trip.
|
|
456
|
-
|
|
466
|
+
// The result door issues a view key when a key-issuing minter is
|
|
467
|
+
// wired (ggui#1415); otherwise it mints the live credential alone.
|
|
468
|
+
...(deps.update.mintViewRoot
|
|
469
|
+
? { mintWsToken: deps.update.mintViewRoot }
|
|
470
|
+
: deps.update.mintBootstrap
|
|
471
|
+
? { mintWsToken: deps.update.mintBootstrap }
|
|
472
|
+
: {}),
|
|
457
473
|
...(deps.update.runtimeUrl !== undefined ? { runtimeUrl: deps.update.runtimeUrl } : {}),
|
|
458
474
|
...(deps.update.sessionApiBaseUrl !== undefined
|
|
459
475
|
? { sessionApiBaseUrl: deps.update.sessionApiBaseUrl }
|
|
@@ -637,7 +653,13 @@ export function defaultHandlers(deps) {
|
|
|
637
653
|
...(deps.render.streamWebSocketLocalTools !== undefined
|
|
638
654
|
? { streamWebSocketLocalTools: deps.render.streamWebSocketLocalTools }
|
|
639
655
|
: {}),
|
|
640
|
-
|
|
656
|
+
// The result door issues a view key when a key-issuing minter is
|
|
657
|
+
// wired (ggui#1415); otherwise it mints the live credential alone.
|
|
658
|
+
...(deps.render.mintViewRoot
|
|
659
|
+
? { mintWsToken: deps.render.mintViewRoot }
|
|
660
|
+
: deps.render.mintBootstrap
|
|
661
|
+
? { mintWsToken: deps.render.mintBootstrap }
|
|
662
|
+
: {}),
|
|
641
663
|
...(deps.render.runtimeUrl !== undefined ? { runtimeUrl: deps.render.runtimeUrl } : {}),
|
|
642
664
|
...(deps.render.sessionApiBaseUrl !== undefined
|
|
643
665
|
? { sessionApiBaseUrl: deps.render.sessionApiBaseUrl }
|
|
@@ -719,8 +741,8 @@ export function defaultHandlers(deps) {
|
|
|
719
741
|
return handlers;
|
|
720
742
|
}
|
|
721
743
|
/**
|
|
722
|
-
* Build the operator-class handlers for the apps
|
|
723
|
-
*
|
|
744
|
+
* Build the operator-class handlers for the apps and blueprint
|
|
745
|
+
* domains. Each domain materializes
|
|
724
746
|
* independently when its deps seam is bound; deployments that wired
|
|
725
747
|
* none get an empty list.
|
|
726
748
|
*
|
|
@@ -742,19 +764,6 @@ export function buildOpsBundleHandlers(deps) {
|
|
|
742
764
|
// REST/AppSync doors refuse the same overlay for the same reason.
|
|
743
765
|
createSetAppThemeHandler({ apps, overlayCoverage: (overlay) => validateOverlayCoverage(overlay) }), createDeleteAppHandler({ apps, userDefaultApp }), createSetDefaultAppHandler({ apps, userDefaultApp }));
|
|
744
766
|
}
|
|
745
|
-
if (deps.opsOrgs) {
|
|
746
|
-
const { orgs, invites } = deps.opsOrgs;
|
|
747
|
-
handlers.push(createListOrgsHandler({ orgs }), createCreateOrgHandler({ orgs }), createRenameOrgHandler({ orgs }), createRemoveOrgMemberHandler({ orgs }), createGetOrgBalanceHandler({ orgs }), createInviteToOrgHandler({ invites }), createRevokeInviteHandler({ invites }));
|
|
748
|
-
}
|
|
749
|
-
if (deps.opsConnectorKeys) {
|
|
750
|
-
const { connectorKeys } = deps.opsConnectorKeys;
|
|
751
|
-
handlers.push(createListConnectorKeysHandler({ connectorKeys }), createIssueConnectorKeyHandler({ connectorKeys }), createRevokeConnectorKeyHandler({ connectorKeys }));
|
|
752
|
-
}
|
|
753
|
-
if (deps.opsCoupon) {
|
|
754
|
-
handlers.push(createRedeemCouponHandler({
|
|
755
|
-
coupons: deps.opsCoupon.coupons,
|
|
756
|
-
}));
|
|
757
|
-
}
|
|
758
767
|
if (deps.opsBlueprint) {
|
|
759
768
|
// No server-level `appMetadataStore` fallback on this path — the
|
|
760
769
|
// bundle's own field governs, since a deployment that binds its own
|
|
@@ -766,12 +775,49 @@ export function buildOpsBundleHandlers(deps) {
|
|
|
766
775
|
}
|
|
767
776
|
return handlers;
|
|
768
777
|
}
|
|
778
|
+
/**
|
|
779
|
+
* How long {@link GguiServer.close} lets active connections finish before
|
|
780
|
+
* it ends them (ggui#1631), in milliseconds.
|
|
781
|
+
*/
|
|
782
|
+
export const DEFAULT_CLOSE_GRACE_MS = 5000;
|
|
783
|
+
/**
|
|
784
|
+
* The ws-token TTL and refresh window a server mints with (ggui#1496 part
|
|
785
|
+
* B). Both are positive whole seconds, and the window is at least the TTL:
|
|
786
|
+
* then every token this server mints, root and renewal alike, is past its
|
|
787
|
+
* `exp` by `rootIat + window`, so every gate refuses it in the same second.
|
|
788
|
+
* (A token minted while the window was longer, or by a server sharing the
|
|
789
|
+
* secret with a longer window, can outlive that second on the other gates;
|
|
790
|
+
* only `/state` refuses it.)
|
|
791
|
+
*/
|
|
792
|
+
function resolveWsTokenLifetimes(opts) {
|
|
793
|
+
const wholePositive = (v) => Number.isInteger(v) && v > 0;
|
|
794
|
+
const ttlSec = opts.wsTokenTtlSec ?? DEFAULT_WS_TOKEN_TTL_SEC;
|
|
795
|
+
if (!wholePositive(ttlSec)) {
|
|
796
|
+
throw new Error(`createGguiServer: wsTokenTtlSec must be a positive whole number of seconds (got ${String(ttlSec)}).`);
|
|
797
|
+
}
|
|
798
|
+
const windowSec = opts.wsTokenRefreshWindowSec ?? DEFAULT_WS_TOKEN_REFRESH_WINDOW_MULTIPLIER * ttlSec;
|
|
799
|
+
if (!wholePositive(windowSec)) {
|
|
800
|
+
throw new Error(`createGguiServer: wsTokenRefreshWindowSec must be a positive whole number of seconds (got ${String(windowSec)}).`);
|
|
801
|
+
}
|
|
802
|
+
if (windowSec < ttlSec) {
|
|
803
|
+
throw new Error(`createGguiServer: wsTokenRefreshWindowSec (${windowSec}) must be at least wsTokenTtlSec (${ttlSec}); a shorter window would let a root token outlive its own renewal chain.`);
|
|
804
|
+
}
|
|
805
|
+
return { ttlSec, windowSec };
|
|
806
|
+
}
|
|
769
807
|
/**
|
|
770
808
|
* Build a runnable OSS MCP server. Every option has a sensible default
|
|
771
809
|
* so `createGguiServer()` with no arguments boots a working in-memory
|
|
772
810
|
* server on demand.
|
|
773
811
|
*/
|
|
774
812
|
export function createGguiServer(opts = {}) {
|
|
813
|
+
if ((opts.perAppRouting?.perAppOnlySources?.length ?? 0) > 0 && !opts.perAppRouting?.authorize) {
|
|
814
|
+
throw new Error("createGguiServer: perAppRouting.perAppOnlySources lists credential sources that are valid only at their app's endpoint, but perAppRouting.authorize is not wired");
|
|
815
|
+
}
|
|
816
|
+
const { ttlSec: wsTokenTtlSec, windowSec: wsTokenRefreshWindowSec } = resolveWsTokenLifetimes(opts);
|
|
817
|
+
const keyedSince = opts.viewProof?.keyedSince;
|
|
818
|
+
if (keyedSince !== undefined && (!Number.isFinite(keyedSince) || keyedSince < 0)) {
|
|
819
|
+
throw new Error(`createGguiServer: viewProof.keyedSince must be a finite, non-negative epoch in milliseconds (got ${String(keyedSince)}).`);
|
|
820
|
+
}
|
|
775
821
|
const info = { ...DEFAULT_INFO, ...opts.info };
|
|
776
822
|
const logger = opts.logger ?? createConsoleLogger({ server: info.name });
|
|
777
823
|
const bodyLimit = opts.bodyLimit ?? "4mb";
|
|
@@ -986,9 +1032,23 @@ export function createGguiServer(opts = {}) {
|
|
|
986
1032
|
// (which always call this on the live-mode path) get clean strings
|
|
987
1033
|
// without per-callsite narrowing.
|
|
988
1034
|
let mintBootstrap;
|
|
1035
|
+
// The key-issuing doors' minters (ggui#1415): a root ws token stamped
|
|
1036
|
+
// with the secret's key id and the door, and the view key rooted in it.
|
|
1037
|
+
// Only the render/update results (`result`) and the data-plane read door
|
|
1038
|
+
// (`read`), on a read that issues the key, use them; every bearer door,
|
|
1039
|
+
// and every read that issues no key, mints through `mintBootstrap`.
|
|
1040
|
+
let mintResultViewRoot;
|
|
1041
|
+
let mintReadViewRoot;
|
|
1042
|
+
// `/state`'s one minter (ggui#1496 part B, slice 2): a possession renewal
|
|
1043
|
+
// that carries its chain's root forward. Every other door mints a root
|
|
1044
|
+
// through `mintBootstrap`.
|
|
1045
|
+
let mintChainedBootstrap;
|
|
989
1046
|
let channelBootstrap;
|
|
990
|
-
//
|
|
991
|
-
//
|
|
1047
|
+
// The authorized refresh's envelope check (ggui#1496 part B): signature,
|
|
1048
|
+
// shape and kind at ANY age, over the same secret the minter signs with.
|
|
1049
|
+
let wsTokenVerify;
|
|
1050
|
+
// Shared HMAC secret for server-minted creds (ws bootstrap tokens,
|
|
1051
|
+
// console cookies). Distinct `kind` claims
|
|
992
1052
|
// prevent cross-kind confusion; sharing the secret keeps the
|
|
993
1053
|
// config surface small and means operators rotate ONE value.
|
|
994
1054
|
// Resolved here so both the MCP Apps block and the console
|
|
@@ -1009,7 +1069,7 @@ export function createGguiServer(opts = {}) {
|
|
|
1009
1069
|
// from `@ggui-ai/mcp-server-core` for callers that need explicit
|
|
1010
1070
|
// single-use semantics (one-time-link share, etc.).
|
|
1011
1071
|
const syncMinter = (sessionId, appId) => {
|
|
1012
|
-
const { token, claims } = mintWsToken({ sessionId, appId }, secret);
|
|
1072
|
+
const { token, claims } = mintWsToken({ sessionId, appId, ttlSec: wsTokenTtlSec }, secret);
|
|
1013
1073
|
return {
|
|
1014
1074
|
wsUrl,
|
|
1015
1075
|
token,
|
|
@@ -1017,6 +1077,36 @@ export function createGguiServer(opts = {}) {
|
|
|
1017
1077
|
};
|
|
1018
1078
|
};
|
|
1019
1079
|
mintBootstrap = syncMinter;
|
|
1080
|
+
const viewRootMinter = (src) => (sessionId, appId) => {
|
|
1081
|
+
const minted = mintViewRoot({ sessionId, appId, ttlSec: wsTokenTtlSec, src }, secret);
|
|
1082
|
+
if (minted.viewKeyNotIssued !== undefined) {
|
|
1083
|
+
logger.info("view_key_not_issued", { sessionId, src, reason: minted.viewKeyNotIssued });
|
|
1084
|
+
}
|
|
1085
|
+
return {
|
|
1086
|
+
wsUrl,
|
|
1087
|
+
token: minted.token,
|
|
1088
|
+
expiresAt: new Date(minted.claims.exp * 1000).toISOString(),
|
|
1089
|
+
viewKey: minted.viewKey,
|
|
1090
|
+
};
|
|
1091
|
+
};
|
|
1092
|
+
mintResultViewRoot = viewRootMinter("result");
|
|
1093
|
+
mintReadViewRoot = viewRootMinter("read");
|
|
1094
|
+
// The renewal's `exp` is capped at `rootIat + window` by the mint
|
|
1095
|
+
// itself (`notAfter`), from the same clock read that stamps its `iat`.
|
|
1096
|
+
// No second left means nothing to renew (`undefined`, answered as the
|
|
1097
|
+
// ordinary expiry). That happens when the bound's second arrives between
|
|
1098
|
+
// the route's expiry check and this mint (the route awaits the store in
|
|
1099
|
+
// between), or for a token minted while the window was longer.
|
|
1100
|
+
mintChainedBootstrap = (sessionId, appId, rootIat) => {
|
|
1101
|
+
const { token, claims } = mintWsToken({ sessionId, appId, ttlSec: wsTokenTtlSec, rootIat, notAfter: rootIat + wsTokenRefreshWindowSec }, secret);
|
|
1102
|
+
if (claims.exp <= claims.iat)
|
|
1103
|
+
return undefined;
|
|
1104
|
+
return {
|
|
1105
|
+
wsUrl,
|
|
1106
|
+
token,
|
|
1107
|
+
expiresAt: new Date(claims.exp * 1000).toISOString(),
|
|
1108
|
+
};
|
|
1109
|
+
};
|
|
1020
1110
|
channelBootstrap = {
|
|
1021
1111
|
verify: (token) => {
|
|
1022
1112
|
const result = verifyToken(token, secret, "ws");
|
|
@@ -1035,28 +1125,17 @@ export function createGguiServer(opts = {}) {
|
|
|
1035
1125
|
}
|
|
1036
1126
|
return { ok: false, reason: "invalid" };
|
|
1037
1127
|
},
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
token: result.token,
|
|
1048
|
-
expiresAt: new Date(result.claims.exp * 1000).toISOString(),
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
if (result.reason === "refresh_window_closed") {
|
|
1052
|
-
return { ok: false, reason: "window_closed" };
|
|
1128
|
+
};
|
|
1129
|
+
wsTokenVerify = (envelope) => {
|
|
1130
|
+
const result = verifyWsTokenSignature(envelope, secret);
|
|
1131
|
+
return result.ok
|
|
1132
|
+
? {
|
|
1133
|
+
ok: true,
|
|
1134
|
+
sessionId: result.claims.sessionId,
|
|
1135
|
+
appId: result.claims.appId,
|
|
1136
|
+
rootIat: result.claims.rootIat ?? result.claims.iat,
|
|
1053
1137
|
}
|
|
1054
|
-
|
|
1055
|
-
// single `invalid` — the iframe MUST re-handshake; the exact
|
|
1056
|
-
// breakage type is logged server-side, not surfaced on the
|
|
1057
|
-
// wire (would be useful only to attackers probing the surface).
|
|
1058
|
-
return { ok: false, reason: "invalid" };
|
|
1059
|
-
},
|
|
1138
|
+
: { ok: false };
|
|
1060
1139
|
};
|
|
1061
1140
|
}
|
|
1062
1141
|
// Live-channel render endpoint reference. Declared here (not at the
|
|
@@ -1162,7 +1241,6 @@ export function createGguiServer(opts = {}) {
|
|
|
1162
1241
|
new InMemoryBlueprintStore({
|
|
1163
1242
|
embeddingProvider: opts.embedding,
|
|
1164
1243
|
});
|
|
1165
|
-
const blueprintSelector = opts.blueprintSelector ?? createDeterministicBlueprintSelector();
|
|
1166
1244
|
// Auto-seed the multi-axis search against the resolved store.
|
|
1167
1245
|
// Type-narrowed at the call site: when the caller passed
|
|
1168
1246
|
// their own store we can't assume it implements `listAllForApp`,
|
|
@@ -1445,15 +1523,11 @@ export function createGguiServer(opts = {}) {
|
|
|
1445
1523
|
? { postFailureHook: opts.renderPostFailureHook }
|
|
1446
1524
|
: {}),
|
|
1447
1525
|
...(mintBootstrap ? { mintBootstrap } : {}),
|
|
1448
|
-
|
|
1449
|
-
//
|
|
1450
|
-
//
|
|
1451
|
-
//
|
|
1452
|
-
|
|
1453
|
-
// secret is wired; the tool isn't registered in that case.
|
|
1454
|
-
...(channelBootstrap
|
|
1455
|
-
? { bootstrapRefresh: { refresh: channelBootstrap.refresh } }
|
|
1456
|
-
: {}),
|
|
1526
|
+
...(mintResultViewRoot ? { mintViewRoot: mintResultViewRoot } : {}),
|
|
1527
|
+
// The authorized refresh's envelope check (ggui#1496 part B),
|
|
1528
|
+
// over the same secret as `mintBootstrap`. Absent when MCP
|
|
1529
|
+
// Apps isn't enabled; the tool isn't registered then.
|
|
1530
|
+
...(wsTokenVerify ? { wsTokenVerify } : {}),
|
|
1457
1531
|
// Iframe-runtime bundle URL — padded onto the
|
|
1458
1532
|
// `ai.ggui/render.runtimeUrl` slice field by the render
|
|
1459
1533
|
// handler's resultMeta. C8 made this required on
|
|
@@ -1526,25 +1600,7 @@ export function createGguiServer(opts = {}) {
|
|
|
1526
1600
|
// every story-path render + emits `cache.hit /
|
|
1527
1601
|
// similarity / cachedBlueprintId / llmCallsAvoided` on
|
|
1528
1602
|
// structuredContent.
|
|
1529
|
-
...(generationWithCache
|
|
1530
|
-
? {
|
|
1531
|
-
generation: {
|
|
1532
|
-
...generationWithCache,
|
|
1533
|
-
// Thread an LLMCaller resolver into
|
|
1534
|
-
// render so the registry-based three-tier matcher
|
|
1535
|
-
// can fire its rerank step. Per-call resolution
|
|
1536
|
-
// because BYOK creds depend on ctx.
|
|
1537
|
-
resolveLlmCaller: async (ctx) => {
|
|
1538
|
-
if (!generationWithCache.resolveLlm)
|
|
1539
|
-
return null;
|
|
1540
|
-
const creds = await generationWithCache.resolveLlm(ctx);
|
|
1541
|
-
if (!creds)
|
|
1542
|
-
return null;
|
|
1543
|
-
return buildLlmCaller(creds.selection, creds.providerKey);
|
|
1544
|
-
},
|
|
1545
|
-
},
|
|
1546
|
-
}
|
|
1547
|
-
: {}),
|
|
1603
|
+
...(generationWithCache ? { generation: generationWithCache } : {}),
|
|
1548
1604
|
// Live-subscriber notifier. Late-binds to the
|
|
1549
1605
|
// GguiSessionChannelServer created further down (`channel`
|
|
1550
1606
|
// / `channelForHealth`) — the channel doesn't exist yet
|
|
@@ -1678,6 +1734,7 @@ export function createGguiServer(opts = {}) {
|
|
|
1678
1734
|
// resolvers keeps the two transports byte-identical at the
|
|
1679
1735
|
// bootstrap-projection boundary.
|
|
1680
1736
|
...(mintBootstrap ? { mintBootstrap } : {}),
|
|
1737
|
+
...(mintResultViewRoot ? { mintViewRoot: mintResultViewRoot } : {}),
|
|
1681
1738
|
// Function form: matches render so ggui_update emits the
|
|
1682
1739
|
// same absolute URL when the server sits behind a tunnel
|
|
1683
1740
|
// / reverse proxy.
|
|
@@ -1771,12 +1828,12 @@ export function createGguiServer(opts = {}) {
|
|
|
1771
1828
|
// same sink as the rest of the server.
|
|
1772
1829
|
logger,
|
|
1773
1830
|
});
|
|
1774
|
-
// Operator-class domain handlers (apps /
|
|
1775
|
-
//
|
|
1776
|
-
//
|
|
1777
|
-
//
|
|
1778
|
-
//
|
|
1779
|
-
//
|
|
1831
|
+
// Operator-class domain handlers (apps / blueprints). Built on EVERY
|
|
1832
|
+
// path — they hang off their own explicit options, so a deployment
|
|
1833
|
+
// that supplies a custom base handler list still gets the domains it
|
|
1834
|
+
// wired. A name already claimed by the base list wins: that's how a
|
|
1835
|
+
// deployment ships its own `ggui_ops_create_app` while still picking
|
|
1836
|
+
// up the rest of the family,
|
|
1780
1837
|
// and it is what keeps the blueprint family single-registered when the
|
|
1781
1838
|
// default handler set already built it from the same bundle.
|
|
1782
1839
|
const baseHandlerNames = new Set(baseHandlers.map((h) => h.name));
|
|
@@ -1789,6 +1846,30 @@ export function createGguiServer(opts = {}) {
|
|
|
1789
1846
|
// construction instead of at first `tools/call`. The actual route
|
|
1790
1847
|
// mounting happens below alongside the canonical routes.
|
|
1791
1848
|
const mcpServices = validateMcpServices(opts.mcpServices);
|
|
1849
|
+
// ggui#1415 — the view-origin proof's measuring gate, over every handler
|
|
1850
|
+
// this server serves (the data and control planes, mounts, and isolated
|
|
1851
|
+
// services). Every declared proof must be one a view can sign (refused at
|
|
1852
|
+
// boot otherwise). The gate runs where views are keyed (MCP Apps on);
|
|
1853
|
+
// without it, a declared call's line says `viewProofUnverifiable`, named
|
|
1854
|
+
// once here.
|
|
1855
|
+
const servedHandlers = [...handlers, ...mcpServices.flatMap((service) => service.handlers)];
|
|
1856
|
+
assertViewProofDeclarations(servedHandlers);
|
|
1857
|
+
const viewProofGate = mcpAppsEnabled && sharedTokenSecret !== undefined
|
|
1858
|
+
? createViewProofGate({
|
|
1859
|
+
secret: sharedTokenSecret,
|
|
1860
|
+
sessionStore: renderStore,
|
|
1861
|
+
...(keyedSince !== undefined ? { keyedSince } : {}),
|
|
1862
|
+
})
|
|
1863
|
+
: undefined;
|
|
1864
|
+
if (viewProofGate === undefined) {
|
|
1865
|
+
const unverifiable = unverifiableViewProofTools(servedHandlers);
|
|
1866
|
+
if (unverifiable.length > 0) {
|
|
1867
|
+
logger.warn("view_proof_unverifiable", {
|
|
1868
|
+
tools: unverifiable,
|
|
1869
|
+
hint: "These tools declare a view proof. MCP Apps is off, so this server keys no view and does not verify proofs.",
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1792
1873
|
// The control plane — every `protocol`- and `ops`-tagged handler,
|
|
1793
1874
|
// projected onto one anonymous-capable route with per-tool auth and
|
|
1794
1875
|
// confirmation gates. Built from the SAME composed `handlers` array
|
|
@@ -1887,10 +1968,12 @@ export function createGguiServer(opts = {}) {
|
|
|
1887
1968
|
// pass a stable `csrfSecret` so tokens survive a restart.
|
|
1888
1969
|
app.use(createSecurityHeadersMiddleware());
|
|
1889
1970
|
// Resolved once at boot — every per-request `buildMcpServer` call
|
|
1890
|
-
// receives the same string, so
|
|
1891
|
-
//
|
|
1892
|
-
//
|
|
1893
|
-
//
|
|
1971
|
+
// receives the same string, so every `initialize` result carries the
|
|
1972
|
+
// same instructions for the life of the process. Operators opt in via
|
|
1973
|
+
// `mcpInstructions: 'default' | 'aggressive' | 'always' | 'minimal'` or
|
|
1974
|
+
// a custom string; unset or `'off'` sends none (ggui#1579). The boot
|
|
1975
|
+
// line says what is SENT, so it can never again name text no host got.
|
|
1976
|
+
// See `instructions-presets.ts` for the copy.
|
|
1894
1977
|
const resolvedInstructions = resolveMcpInstructions(opts.mcpInstructions);
|
|
1895
1978
|
if (resolvedInstructions) {
|
|
1896
1979
|
logger.info("mcp_instructions_set", {
|
|
@@ -1901,15 +1984,19 @@ export function createGguiServer(opts = {}) {
|
|
|
1901
1984
|
opts.mcpInstructions === "minimal" ||
|
|
1902
1985
|
opts.mcpInstructions === "off")
|
|
1903
1986
|
? opts.mcpInstructions
|
|
1904
|
-
:
|
|
1905
|
-
? "default (no-preset fallback)"
|
|
1906
|
-
: "custom",
|
|
1987
|
+
: "custom",
|
|
1907
1988
|
length: resolvedInstructions.length,
|
|
1989
|
+
sentInInitialize: true,
|
|
1908
1990
|
});
|
|
1909
1991
|
}
|
|
1910
1992
|
else {
|
|
1911
1993
|
logger.info("mcp_instructions_off", {
|
|
1912
|
-
reason: opts.mcpInstructions ===
|
|
1994
|
+
reason: opts.mcpInstructions === undefined
|
|
1995
|
+
? "unset (the default is off)"
|
|
1996
|
+
: opts.mcpInstructions === "off"
|
|
1997
|
+
? "preset=off"
|
|
1998
|
+
: "empty custom string",
|
|
1999
|
+
sentInInitialize: false,
|
|
1913
2000
|
});
|
|
1914
2001
|
}
|
|
1915
2002
|
const csrfSecret = opts.csrfSecret && opts.csrfSecret.length >= 32
|
|
@@ -2053,6 +2140,7 @@ export function createGguiServer(opts = {}) {
|
|
|
2053
2140
|
// endpoint family spreads a fresh copy per request.
|
|
2054
2141
|
const buildMcpOptions = {
|
|
2055
2142
|
mcpAppsOutbound: mcpAppsEnabled,
|
|
2143
|
+
...(viewProofGate !== undefined ? { viewProofGate } : {}),
|
|
2056
2144
|
// Caller-provided `shellHtml` overrides the default (and the
|
|
2057
2145
|
// inline-runtime shell); `installMcpAppsOutbound` falls back to
|
|
2058
2146
|
// its baked `GGUI_RENDER_SHELL_HTML` constant when both are
|
|
@@ -2225,6 +2313,10 @@ export function createGguiServer(opts = {}) {
|
|
|
2225
2313
|
// Mirrors the per-tool `mintBootstrap` plumbed into
|
|
2226
2314
|
// the handler factory above.
|
|
2227
2315
|
...(mintBootstrap ? { mintWsToken: mintBootstrap } : {}),
|
|
2316
|
+
// The data-plane read door issues a view key (ggui#1415); every
|
|
2317
|
+
// other mount, and an anonymous caller, mints through
|
|
2318
|
+
// `mintBootstrap` alone.
|
|
2319
|
+
...(mintReadViewRoot ? { mintViewRoot: mintReadViewRoot } : {}),
|
|
2228
2320
|
},
|
|
2229
2321
|
}
|
|
2230
2322
|
: {}),
|
|
@@ -2294,9 +2386,10 @@ export function createGguiServer(opts = {}) {
|
|
|
2294
2386
|
// R6 /state snapshot + R7 /events cursor-replay reads — see
|
|
2295
2387
|
// `./api-renders-routes.ts` for the wsToken auth posture, binding +
|
|
2296
2388
|
// app-scope gates, and response taxonomy. Mounted only when MCP Apps is on,
|
|
2297
|
-
// a render store is resolved, and a token secret exists
|
|
2298
|
-
//
|
|
2299
|
-
|
|
2389
|
+
// a render store is resolved, and a token secret exists. MCP Apps being on
|
|
2390
|
+
// is also what assigns the two minters, so the last conjunct only narrows
|
|
2391
|
+
// the type: it never keeps a deployment from mounting.
|
|
2392
|
+
if (mcpAppsEnabled && renderStore && sharedTokenSecret !== undefined && mintChainedBootstrap) {
|
|
2300
2393
|
mountApiRendersRoutes({
|
|
2301
2394
|
app,
|
|
2302
2395
|
renderStore,
|
|
@@ -2314,7 +2407,7 @@ export function createGguiServer(opts = {}) {
|
|
|
2314
2407
|
// composes (ggui#522) — session-API URLs keep the public origin.
|
|
2315
2408
|
...(opts.codeBaseUrl !== undefined ? { codeBaseUrl: opts.codeBaseUrl } : {}),
|
|
2316
2409
|
...(mintCodeModuleUrl !== undefined ? { mintCodeModuleUrl } : {}),
|
|
2317
|
-
|
|
2410
|
+
mintChainedBootstrap,
|
|
2318
2411
|
resolveRuntimeUrl: resolveRuntimeUrlForResultMeta,
|
|
2319
2412
|
logger,
|
|
2320
2413
|
});
|
|
@@ -3031,6 +3124,14 @@ export function createGguiServer(opts = {}) {
|
|
|
3031
3124
|
// above — subscribes that omit `payload.appId` resolve their
|
|
3032
3125
|
// identity-default through the identical rule (SPEC §12.2).
|
|
3033
3126
|
appIdFromIdentity,
|
|
3127
|
+
// ggui#1480 — a subscribe that declares an app other than its
|
|
3128
|
+
// identity's own passes the same per-app authorization the
|
|
3129
|
+
// `/mcp` endpoint runs on a URL-addressed app.
|
|
3130
|
+
...(opts.perAppRouting?.authorize ? { authorizeApp: opts.perAppRouting.authorize } : {}),
|
|
3131
|
+
// ggui#1482 — a listed source is authorized on every subscribe.
|
|
3132
|
+
...(opts.perAppRouting?.perAppOnlySources
|
|
3133
|
+
? { perAppOnlySources: opts.perAppRouting.perAppOnlySources }
|
|
3134
|
+
: {}),
|
|
3034
3135
|
logger: logger.child({ component: "render-channel" }),
|
|
3035
3136
|
path: typeof opts.renderChannel === "object" ? opts.renderChannel.path : undefined,
|
|
3036
3137
|
streamBuffer: streamBuffer ??
|
|
@@ -3157,7 +3258,6 @@ export function createGguiServer(opts = {}) {
|
|
|
3157
3258
|
adminToken: resolvedAdminToken,
|
|
3158
3259
|
generators: generators ?? null,
|
|
3159
3260
|
blueprintStore,
|
|
3160
|
-
blueprintSelector,
|
|
3161
3261
|
blueprintSearch,
|
|
3162
3262
|
async listen(port = 0, host = opts.host ?? "127.0.0.1") {
|
|
3163
3263
|
// The Origin/Host validation policy (ggui#438a) was built above
|
|
@@ -3241,14 +3341,28 @@ export function createGguiServer(opts = {}) {
|
|
|
3241
3341
|
}
|
|
3242
3342
|
});
|
|
3243
3343
|
},
|
|
3244
|
-
async close() {
|
|
3344
|
+
async close(options = {}) {
|
|
3245
3345
|
if (channel)
|
|
3246
3346
|
await channel.close();
|
|
3247
3347
|
const server = httpServer;
|
|
3248
3348
|
if (!server)
|
|
3249
3349
|
return;
|
|
3350
|
+
const graceMs = options.graceMs ?? DEFAULT_CLOSE_GRACE_MS;
|
|
3250
3351
|
await new Promise((resolve, reject) => {
|
|
3251
|
-
|
|
3352
|
+
// ggui#1631: after the grace, end what is still open. The timer is
|
|
3353
|
+
// unref'd so it never keeps the process alive on its own.
|
|
3354
|
+
const deadline = Number.isFinite(graceMs)
|
|
3355
|
+
? setTimeout(() => server.closeAllConnections(), Math.max(0, graceMs))
|
|
3356
|
+
: undefined;
|
|
3357
|
+
deadline?.unref();
|
|
3358
|
+
server.close((err) => {
|
|
3359
|
+
if (deadline !== undefined)
|
|
3360
|
+
clearTimeout(deadline);
|
|
3361
|
+
if (err)
|
|
3362
|
+
reject(err);
|
|
3363
|
+
else
|
|
3364
|
+
resolve();
|
|
3365
|
+
});
|
|
3252
3366
|
});
|
|
3253
3367
|
httpServer = null;
|
|
3254
3368
|
},
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GguiSessionStore, ThreadStore, VectorStore } from '@ggui-ai/mcp-server-core';
|
|
1
|
+
import type { GguiSessionStore, GguiSessionStreamBuffer, ThreadStore, VectorStore } from '@ggui-ai/mcp-server-core';
|
|
2
2
|
import type { StorageConfig } from '@ggui-ai/project-config';
|
|
3
3
|
export interface ResolveStorageFromConfigOptions {
|
|
4
4
|
/**
|
|
@@ -49,6 +49,14 @@ export interface ResolvedStorageStores {
|
|
|
49
49
|
* `/ggui/health` advertisement matches the active store.
|
|
50
50
|
*/
|
|
51
51
|
readonly threadDurability?: 'durable' | 'ephemeral';
|
|
52
|
+
/**
|
|
53
|
+
* The live channel's stream buffer, present iff `renders` declares
|
|
54
|
+
* sqlite: a `SqliteGguiSessionStreamBuffer` on the same database file,
|
|
55
|
+
* so a session that survives a restart also keeps its stream `seq`, its
|
|
56
|
+
* epoch and its retained envelopes (ggui#1534). Absent otherwise, and
|
|
57
|
+
* `createGguiServer` keeps its in-memory buffer.
|
|
58
|
+
*/
|
|
59
|
+
readonly streamBuffer?: GguiSessionStreamBuffer;
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
54
62
|
* Instantiate the concrete storage adapters declared in a parsed
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,+BAA+B;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;oEACgE;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC;oEACgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B;;;;;;;;;;;;;;;;;yCAiBqC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,+BAA+B;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;oEACgE;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC;oEACgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B;;;;;;;;;;;;;;;;;yCAiBqC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,IAAI,GAAE,+BAAoC,GACzC,OAAO,CAAC,qBAAqB,CAAC,CAkDhC"}
|
package/dist/storage.js
CHANGED
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
* who want storage from config write:
|
|
42
42
|
*
|
|
43
43
|
* ```ts
|
|
44
|
-
* const { renderStore, vectors } =
|
|
44
|
+
* const { renderStore, vectors, streamBuffer } =
|
|
45
45
|
* await resolveStorageFromConfig(manifest.storage, { baseDir: projectRoot });
|
|
46
|
-
* const server = createGguiServer({ renderStore, vectors });
|
|
46
|
+
* const server = createGguiServer({ renderStore, vectors, streamBuffer });
|
|
47
47
|
* ```
|
|
48
48
|
*
|
|
49
49
|
* Explicit instances passed to `createGguiServer` still win — this
|
|
@@ -113,11 +113,12 @@ export async function resolveStorageFromConfig(config, opts = {}) {
|
|
|
113
113
|
// Single dynamic import serves every sqlite adapter — better-sqlite3's
|
|
114
114
|
// N-API binding loads once even if the import lands twice; the
|
|
115
115
|
// subpath barrel is cached by Node's module loader.
|
|
116
|
-
const { SqliteGguiSessionStore, SqliteVectorStore, SqliteThreadStore } = await import('@ggui-ai/mcp-server-core/sqlite');
|
|
116
|
+
const { SqliteGguiSessionStore, SqliteGguiSessionStreamBuffer, SqliteVectorStore, SqliteThreadStore } = await import('@ggui-ai/mcp-server-core/sqlite');
|
|
117
117
|
if (config.renders && config.renders.driver === 'sqlite') {
|
|
118
118
|
const filename = resolveStoragePath(config.renders.path, opts.baseDir);
|
|
119
119
|
ensureParentDir(filename);
|
|
120
120
|
result.renderStore = new SqliteGguiSessionStore({ filename });
|
|
121
|
+
result.streamBuffer = new SqliteGguiSessionStreamBuffer({ filename });
|
|
121
122
|
}
|
|
122
123
|
if (config.vectors && config.vectors.driver === 'sqlite') {
|
|
123
124
|
const filename = resolveStoragePath(config.vectors.path, opts.baseDir);
|