@ggui-ai/mcp-server 0.1.0-rc.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/LICENSE +201 -0
- package/README.md +48 -0
- package/dist/admin-blueprints-transport.d.ts +114 -0
- package/dist/admin-blueprints-transport.d.ts.map +1 -0
- package/dist/admin-blueprints-transport.js +118 -0
- package/dist/admin-oauth-providers-transport.d.ts +40 -0
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -0
- package/dist/admin-oauth-providers-transport.js +263 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/build-mcp.d.ts +128 -0
- package/dist/build-mcp.d.ts.map +1 -0
- package/dist/build-mcp.js +113 -0
- package/dist/code-store-fs.d.ts +19 -0
- package/dist/code-store-fs.d.ts.map +1 -0
- package/dist/code-store-fs.js +98 -0
- package/dist/console-auth.d.ts +139 -0
- package/dist/console-auth.d.ts.map +1 -0
- package/dist/console-auth.js +102 -0
- package/dist/console-cache.d.ts +78 -0
- package/dist/console-cache.d.ts.map +1 -0
- package/dist/console-cache.js +105 -0
- package/dist/console-headers.d.ts +124 -0
- package/dist/console-headers.d.ts.map +1 -0
- package/dist/console-headers.js +49 -0
- package/dist/console-llm-trace.d.ts +66 -0
- package/dist/console-llm-trace.d.ts.map +1 -0
- package/dist/console-llm-trace.js +105 -0
- package/dist/console-payloads.d.ts +67 -0
- package/dist/console-payloads.d.ts.map +1 -0
- package/dist/console-payloads.js +105 -0
- package/dist/console-theme-routes.d.ts +111 -0
- package/dist/console-theme-routes.d.ts.map +1 -0
- package/dist/console-theme-routes.js +202 -0
- package/dist/console-timeline.d.ts +45 -0
- package/dist/console-timeline.d.ts.map +1 -0
- package/dist/console-timeline.js +169 -0
- package/dist/console-validator.d.ts +67 -0
- package/dist/console-validator.d.ts.map +1 -0
- package/dist/console-validator.js +105 -0
- package/dist/console-welcome.d.ts +7 -0
- package/dist/console-welcome.d.ts.map +1 -0
- package/dist/console-welcome.js +221 -0
- package/dist/csrf-middleware.d.ts +55 -0
- package/dist/csrf-middleware.d.ts.map +1 -0
- package/dist/csrf-middleware.js +138 -0
- package/dist/email-login.d.ts +174 -0
- package/dist/email-login.d.ts.map +1 -0
- package/dist/email-login.js +254 -0
- package/dist/email-resend.d.ts +29 -0
- package/dist/email-resend.d.ts.map +1 -0
- package/dist/email-resend.js +71 -0
- package/dist/email-sender-from-env.d.ts +34 -0
- package/dist/email-sender-from-env.d.ts.map +1 -0
- package/dist/email-sender-from-env.js +112 -0
- package/dist/email-smtp.d.ts +42 -0
- package/dist/email-smtp.d.ts.map +1 -0
- package/dist/email-smtp.js +81 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/instructions-presets.d.ts +112 -0
- package/dist/instructions-presets.d.ts.map +1 -0
- package/dist/instructions-presets.js +195 -0
- package/dist/llm-backed-negotiator.d.ts +178 -0
- package/dist/llm-backed-negotiator.d.ts.map +1 -0
- package/dist/llm-backed-negotiator.js +579 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/mcp-apps-inbound.d.ts +86 -0
- package/dist/mcp-apps-inbound.d.ts.map +1 -0
- package/dist/mcp-apps-inbound.js +278 -0
- package/dist/mcp-apps-outbound.d.ts +448 -0
- package/dist/mcp-apps-outbound.d.ts.map +1 -0
- package/dist/mcp-apps-outbound.js +1163 -0
- package/dist/mcp-mounts.d.ts +239 -0
- package/dist/mcp-mounts.d.ts.map +1 -0
- package/dist/mcp-mounts.js +222 -0
- package/dist/oauth-login-types.d.ts +160 -0
- package/dist/oauth-login-types.d.ts.map +1 -0
- package/dist/oauth-login-types.js +9 -0
- package/dist/oauth-login.d.ts +77 -0
- package/dist/oauth-login.d.ts.map +1 -0
- package/dist/oauth-login.js +455 -0
- package/dist/oauth-providers/github.d.ts +17 -0
- package/dist/oauth-providers/github.d.ts.map +1 -0
- package/dist/oauth-providers/github.js +89 -0
- package/dist/oauth-providers/google.d.ts +18 -0
- package/dist/oauth-providers/google.d.ts.map +1 -0
- package/dist/oauth-providers/google.js +59 -0
- package/dist/oauth-providers-store.d.ts +32 -0
- package/dist/oauth-providers-store.d.ts.map +1 -0
- package/dist/oauth-providers-store.js +291 -0
- package/dist/oauth.d.ts +347 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +686 -0
- package/dist/pairing-transport.d.ts +99 -0
- package/dist/pairing-transport.d.ts.map +1 -0
- package/dist/pairing-transport.js +223 -0
- package/dist/rate-limit-middleware.d.ts +36 -0
- package/dist/rate-limit-middleware.d.ts.map +1 -0
- package/dist/rate-limit-middleware.js +57 -0
- package/dist/render-gate.d.ts +87 -0
- package/dist/render-gate.d.ts.map +1 -0
- package/dist/render-gate.js +77 -0
- package/dist/render-rate-limit.d.ts +59 -0
- package/dist/render-rate-limit.d.ts.map +1 -0
- package/dist/render-rate-limit.js +73 -0
- package/dist/render-signing.d.ts +98 -0
- package/dist/render-signing.d.ts.map +1 -0
- package/dist/render-signing.js +113 -0
- package/dist/request-context.d.ts +113 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +154 -0
- package/dist/reserved-validators.d.ts +22 -0
- package/dist/reserved-validators.d.ts.map +1 -0
- package/dist/reserved-validators.js +101 -0
- package/dist/schema-compat.d.ts +167 -0
- package/dist/schema-compat.d.ts.map +1 -0
- package/dist/schema-compat.js +187 -0
- package/dist/security-headers-middleware.d.ts +38 -0
- package/dist/security-headers-middleware.d.ts.map +1 -0
- package/dist/security-headers-middleware.js +30 -0
- package/dist/server.d.ts +2060 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +6338 -0
- package/dist/session-channel.d.ts +651 -0
- package/dist/session-channel.d.ts.map +1 -0
- package/dist/session-channel.js +1756 -0
- package/dist/storage.d.ts +89 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +171 -0
- package/dist/thread-transport.d.ts +118 -0
- package/dist/thread-transport.d.ts.map +1 -0
- package/dist/thread-transport.js +478 -0
- package/dist/user-session-auth.d.ts +167 -0
- package/dist/user-session-auth.d.ts.map +1 -0
- package/dist/user-session-auth.js +148 -0
- package/package.json +76 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { SessionStore, ThreadStore, VectorStore } from '@ggui-ai/mcp-server-core';
|
|
2
|
+
import type { StorageConfig } from '@ggui-ai/project-config';
|
|
3
|
+
export interface ResolveStorageFromConfigOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Directory used to resolve relative `path` values in the storage
|
|
6
|
+
* config. Typically the directory containing `ggui.json` so a
|
|
7
|
+
* manifest saying `"path": "./ggui-sessions.sqlite"` lands next to
|
|
8
|
+
* the manifest (not CWD, which would silently create a file wherever
|
|
9
|
+
* the process happened to be started).
|
|
10
|
+
*
|
|
11
|
+
* Absolute paths in the config are honored verbatim. Omitting
|
|
12
|
+
* `baseDir` means relative paths resolve against `process.cwd()` —
|
|
13
|
+
* fine for ad-hoc programmatic callers, but `ggui serve` always
|
|
14
|
+
* passes the project root so the behavior is deterministic.
|
|
15
|
+
*/
|
|
16
|
+
readonly baseDir?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ResolvedStorageStores {
|
|
19
|
+
/** Concrete SessionStore, iff the config declared one. Undefined =
|
|
20
|
+
* caller falls back to createGguiServer's in-memory default. */
|
|
21
|
+
readonly sessionStore?: SessionStore;
|
|
22
|
+
/** Concrete VectorStore, iff the config declared one. Undefined =
|
|
23
|
+
* caller falls back to createGguiServer's in-memory default. */
|
|
24
|
+
readonly vectors?: VectorStore;
|
|
25
|
+
/** Concrete ThreadStore, iff the config declared one. Undefined
|
|
26
|
+
* ONLY when `storage.threads` is absent from the manifest — in
|
|
27
|
+
* that case the caller skips the `threads:` opt-in on
|
|
28
|
+
* `createGguiServer` and no thread routes mount at all.
|
|
29
|
+
*
|
|
30
|
+
* When the manifest declares `storage.threads`, a store is
|
|
31
|
+
* ALWAYS returned:
|
|
32
|
+
* - `driver: 'memory'` → `InMemoryThreadStore` (ephemeral but
|
|
33
|
+
* real; routes mount and work until restart).
|
|
34
|
+
* - `driver: 'sqlite'` → `SqliteThreadStore` (durable).
|
|
35
|
+
*
|
|
36
|
+
* This is the one semantic deviation from sessions/vectors, which
|
|
37
|
+
* treat `driver: 'memory'` as a no-op because `createGguiServer`
|
|
38
|
+
* already defaults those to in-memory stores internally. Threads
|
|
39
|
+
* don't have an implicit default — the whole route family is
|
|
40
|
+
* opt-in — so `'memory'` has to resolve to a real store or be
|
|
41
|
+
* rejected at schema time. Resolving to `InMemoryThreadStore` is
|
|
42
|
+
* the less-surprising of the two. */
|
|
43
|
+
readonly threadStore?: ThreadStore;
|
|
44
|
+
/**
|
|
45
|
+
* Durability claim for the resolved thread store. Present iff
|
|
46
|
+
* `threadStore` is present. `'durable'` for sqlite; `'ephemeral'`
|
|
47
|
+
* for memory. Callers pass this straight through to
|
|
48
|
+
* `createGguiServer({ threads: { durability } })` so the server's
|
|
49
|
+
* `/ggui/health` advertisement matches the active store.
|
|
50
|
+
*/
|
|
51
|
+
readonly threadDurability?: 'durable' | 'ephemeral';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Instantiate the concrete storage adapters declared in a parsed
|
|
55
|
+
* `ggui.json#storage` block.
|
|
56
|
+
*
|
|
57
|
+
* - Absent config → `{}` (every surface
|
|
58
|
+
* falls back to createGguiServer's
|
|
59
|
+
* in-memory defaults, or in the
|
|
60
|
+
* case of `threads:`, no thread
|
|
61
|
+
* routes at all).
|
|
62
|
+
*
|
|
63
|
+
* - `sessions` / `vectors`:
|
|
64
|
+
* - `driver: 'memory'` → omitted from the bundle
|
|
65
|
+
* (same fallback — declaring
|
|
66
|
+
* memory is the same outcome
|
|
67
|
+
* as omitting it; present
|
|
68
|
+
* for intent visibility).
|
|
69
|
+
* - `driver: 'sqlite'` → `SqliteSessionStore` /
|
|
70
|
+
* `SqliteVectorStore`.
|
|
71
|
+
*
|
|
72
|
+
* - `threads`:
|
|
73
|
+
* - `driver: 'memory'` → `InMemoryThreadStore` +
|
|
74
|
+
* `threadDurability: 'ephemeral'`.
|
|
75
|
+
* Routes mount; data lost on
|
|
76
|
+
* restart. Declaring this is
|
|
77
|
+
* how an operator asks for
|
|
78
|
+
* ephemeral threads.
|
|
79
|
+
* - `driver: 'sqlite'` → `SqliteThreadStore` +
|
|
80
|
+
* `threadDurability: 'durable'`.
|
|
81
|
+
*
|
|
82
|
+
* Dynamic import of `@ggui-ai/mcp-server-core/sqlite` means
|
|
83
|
+
* `better-sqlite3` is only required when the config actually declares
|
|
84
|
+
* sqlite somewhere. Memory-only configs don't touch the optional peer
|
|
85
|
+
* dep — `InMemoryThreadStore` is a static import because the in-memory
|
|
86
|
+
* subpath has no peer dep cost.
|
|
87
|
+
*/
|
|
88
|
+
export declare function resolveStorageFromConfig(config: StorageConfig | undefined, opts?: ResolveStorageFromConfigOptions): Promise<ResolvedStorageStores>;
|
|
89
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EACV,YAAY,EACZ,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,YAAY,CAAC,EAAE,YAAY,CAAC;IACrC;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;CACrD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,IAAI,GAAE,+BAAoC,GACzC,OAAO,CAAC,qBAAqB,CAAC,CAgDhC"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage config → concrete adapter resolver.
|
|
3
|
+
*
|
|
4
|
+
* Bridges `@ggui-ai/project-config`'s declarative `ggui.json#storage`
|
|
5
|
+
* block to the concrete `@ggui-ai/mcp-server-core` adapters. Thin and
|
|
6
|
+
* boring by design: one config → one set of instances.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this lives in @ggui-ai/mcp-server (not core, not project-config)
|
|
9
|
+
*
|
|
10
|
+
* - `@ggui-ai/project-config` is browser-safe + purely declarative.
|
|
11
|
+
* It must not import `better-sqlite3` (which is a Node addon) or
|
|
12
|
+
* any concrete adapter — that would poison the import graph for
|
|
13
|
+
* paste-a-manifest validators, dev UIs, the Studio dashboard.
|
|
14
|
+
*
|
|
15
|
+
* - `@ggui-ai/mcp-server-core` is the interfaces + reference adapters
|
|
16
|
+
* layer. It exposes the adapters on subpath exports
|
|
17
|
+
* (`/sqlite`, `/in-memory`, …) but intentionally doesn't know about
|
|
18
|
+
* `ggui.json` — keeping the interface layer free of file-format
|
|
19
|
+
* coupling lets non-OSS consumers (hosted closed runtimes, future
|
|
20
|
+
* private adapters) bind the same interfaces without dragging the
|
|
21
|
+
* OSS manifest schema through their code.
|
|
22
|
+
*
|
|
23
|
+
* - `@ggui-ai/mcp-server` is the OSS runtime that actually reads
|
|
24
|
+
* `ggui.json` and serves requests. That's where the bridge
|
|
25
|
+
* belongs — one hop away from the actual `ggui serve` caller.
|
|
26
|
+
*
|
|
27
|
+
* ## Why `better-sqlite3` gets imported dynamically
|
|
28
|
+
*
|
|
29
|
+
* `better-sqlite3` is an optional peer dep of `@ggui-ai/mcp-server-core`.
|
|
30
|
+
* If we `import { SqliteSessionStore } from '@ggui-ai/mcp-server-core/sqlite'`
|
|
31
|
+
* at the top of this file, any consumer that doesn't opt into SQLite
|
|
32
|
+
* storage still pays the peer-dep cost (the module graph resolves the
|
|
33
|
+
* subpath at import time, which tries to load better-sqlite3's N-API
|
|
34
|
+
* binary). Dynamic `await import(...)` keeps the cost truly optional:
|
|
35
|
+
* SQLite is loaded only when `storage.sessions.driver === 'sqlite'` or
|
|
36
|
+
* `storage.vectors.driver === 'sqlite'`.
|
|
37
|
+
*
|
|
38
|
+
* ## Why this is async + returns a bundle instead of augmenting createGguiServer
|
|
39
|
+
*
|
|
40
|
+
* `createGguiServer` stays synchronous — no public API break. Callers
|
|
41
|
+
* who want storage from config write:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* const { sessionStore, vectors } =
|
|
45
|
+
* await resolveStorageFromConfig(manifest.storage, { baseDir: projectRoot });
|
|
46
|
+
* const server = createGguiServer({ sessionStore, vectors });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Explicit instances passed to `createGguiServer` still win — this
|
|
50
|
+
* resolver is a convenience for the ggui.json path, not a requirement.
|
|
51
|
+
*/
|
|
52
|
+
import { mkdirSync } from 'node:fs';
|
|
53
|
+
import path from 'node:path';
|
|
54
|
+
// `InMemoryThreadStore` is a static import — unlike the sqlite
|
|
55
|
+
// adapters, the in-memory subpath carries no optional peer dep cost
|
|
56
|
+
// (no `better-sqlite3`, no N-API binding). `storage.threads.driver =
|
|
57
|
+
// 'memory'` uses this to mount actual thread routes instead of being
|
|
58
|
+
// a silent no-op like sessions/vectors (which createGguiServer
|
|
59
|
+
// already defaults to in-memory internally).
|
|
60
|
+
import { InMemoryThreadStore } from '@ggui-ai/mcp-server-core/in-memory';
|
|
61
|
+
/**
|
|
62
|
+
* Instantiate the concrete storage adapters declared in a parsed
|
|
63
|
+
* `ggui.json#storage` block.
|
|
64
|
+
*
|
|
65
|
+
* - Absent config → `{}` (every surface
|
|
66
|
+
* falls back to createGguiServer's
|
|
67
|
+
* in-memory defaults, or in the
|
|
68
|
+
* case of `threads:`, no thread
|
|
69
|
+
* routes at all).
|
|
70
|
+
*
|
|
71
|
+
* - `sessions` / `vectors`:
|
|
72
|
+
* - `driver: 'memory'` → omitted from the bundle
|
|
73
|
+
* (same fallback — declaring
|
|
74
|
+
* memory is the same outcome
|
|
75
|
+
* as omitting it; present
|
|
76
|
+
* for intent visibility).
|
|
77
|
+
* - `driver: 'sqlite'` → `SqliteSessionStore` /
|
|
78
|
+
* `SqliteVectorStore`.
|
|
79
|
+
*
|
|
80
|
+
* - `threads`:
|
|
81
|
+
* - `driver: 'memory'` → `InMemoryThreadStore` +
|
|
82
|
+
* `threadDurability: 'ephemeral'`.
|
|
83
|
+
* Routes mount; data lost on
|
|
84
|
+
* restart. Declaring this is
|
|
85
|
+
* how an operator asks for
|
|
86
|
+
* ephemeral threads.
|
|
87
|
+
* - `driver: 'sqlite'` → `SqliteThreadStore` +
|
|
88
|
+
* `threadDurability: 'durable'`.
|
|
89
|
+
*
|
|
90
|
+
* Dynamic import of `@ggui-ai/mcp-server-core/sqlite` means
|
|
91
|
+
* `better-sqlite3` is only required when the config actually declares
|
|
92
|
+
* sqlite somewhere. Memory-only configs don't touch the optional peer
|
|
93
|
+
* dep — `InMemoryThreadStore` is a static import because the in-memory
|
|
94
|
+
* subpath has no peer dep cost.
|
|
95
|
+
*/
|
|
96
|
+
export async function resolveStorageFromConfig(config, opts = {}) {
|
|
97
|
+
if (!config)
|
|
98
|
+
return {};
|
|
99
|
+
const sessionsSqlite = config.sessions?.driver === 'sqlite';
|
|
100
|
+
const vectorsSqlite = config.vectors?.driver === 'sqlite';
|
|
101
|
+
const threadsSqlite = config.threads?.driver === 'sqlite';
|
|
102
|
+
const threadsMemory = config.threads?.driver === 'memory';
|
|
103
|
+
const result = {};
|
|
104
|
+
// Handle the memory-threads branch BEFORE the sqlite dynamic import
|
|
105
|
+
// gate so `driver: 'memory'`-only configs don't pull in
|
|
106
|
+
// better-sqlite3 at all.
|
|
107
|
+
if (threadsMemory) {
|
|
108
|
+
result.threadStore = new InMemoryThreadStore();
|
|
109
|
+
result.threadDurability = 'ephemeral';
|
|
110
|
+
}
|
|
111
|
+
if (!sessionsSqlite && !vectorsSqlite && !threadsSqlite)
|
|
112
|
+
return result;
|
|
113
|
+
// Single dynamic import serves every sqlite adapter — better-sqlite3's
|
|
114
|
+
// N-API binding loads once even if the import lands twice; the
|
|
115
|
+
// subpath barrel is cached by Node's module loader.
|
|
116
|
+
const { SqliteSessionStore, SqliteVectorStore, SqliteThreadStore } = await import('@ggui-ai/mcp-server-core/sqlite');
|
|
117
|
+
if (config.sessions && config.sessions.driver === 'sqlite') {
|
|
118
|
+
const filename = resolveStoragePath(config.sessions.path, opts.baseDir);
|
|
119
|
+
ensureParentDir(filename);
|
|
120
|
+
result.sessionStore = new SqliteSessionStore({ filename });
|
|
121
|
+
}
|
|
122
|
+
if (config.vectors && config.vectors.driver === 'sqlite') {
|
|
123
|
+
const filename = resolveStoragePath(config.vectors.path, opts.baseDir);
|
|
124
|
+
ensureParentDir(filename);
|
|
125
|
+
result.vectors = new SqliteVectorStore({ filename });
|
|
126
|
+
}
|
|
127
|
+
if (config.threads && config.threads.driver === 'sqlite') {
|
|
128
|
+
const filename = resolveStoragePath(config.threads.path, opts.baseDir);
|
|
129
|
+
ensureParentDir(filename);
|
|
130
|
+
result.threadStore = new SqliteThreadStore({ filename });
|
|
131
|
+
result.threadDurability = 'durable';
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create the parent directory of a sqlite database file if it doesn't
|
|
137
|
+
* already exist. Turns the declarative `path: './data/sessions.sqlite'`
|
|
138
|
+
* into a working adapter without forcing the operator to mkdir by hand
|
|
139
|
+
* — better-sqlite3 refuses to open a file whose parent doesn't exist,
|
|
140
|
+
* and the parent is uninteresting bookkeeping the manifest already
|
|
141
|
+
* implies.
|
|
142
|
+
*
|
|
143
|
+
* Not "silent file creation" — the operator declared the path in
|
|
144
|
+
* `ggui.json`; honoring it is the whole point of opt-in. No-op for
|
|
145
|
+
* `:memory:` and for paths whose parent already exists.
|
|
146
|
+
*/
|
|
147
|
+
function ensureParentDir(resolvedPath) {
|
|
148
|
+
if (resolvedPath === ':memory:')
|
|
149
|
+
return;
|
|
150
|
+
mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolve a storage `path` from the manifest. Absolute paths pass
|
|
154
|
+
* through; relative paths resolve against `baseDir` (typically the
|
|
155
|
+
* ggui.json directory). `baseDir` absent falls back to `process.cwd()`
|
|
156
|
+
* so ad-hoc programmatic callers still work.
|
|
157
|
+
*
|
|
158
|
+
* `:memory:` is intentionally NOT special-cased here — callers who
|
|
159
|
+
* want an in-memory database should declare `driver: 'memory'` in the
|
|
160
|
+
* manifest. An explicit `:memory:` as a sqlite path is honored (passes
|
|
161
|
+
* straight through to better-sqlite3), but it's a power-user escape
|
|
162
|
+
* hatch, not the documented path.
|
|
163
|
+
*/
|
|
164
|
+
function resolveStoragePath(rawPath, baseDir) {
|
|
165
|
+
if (rawPath === ':memory:')
|
|
166
|
+
return rawPath;
|
|
167
|
+
if (path.isAbsolute(rawPath))
|
|
168
|
+
return rawPath;
|
|
169
|
+
const root = baseDir ?? process.cwd();
|
|
170
|
+
return path.resolve(root, rawPath);
|
|
171
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread transport — HTTP routes for the persistent-chat surface.
|
|
3
|
+
*
|
|
4
|
+
* Thin binding over `@ggui-ai/mcp-server-handlers/threads`. Every
|
|
5
|
+
* route:
|
|
6
|
+
*
|
|
7
|
+
* 1. Resolves identity via the existing `AuthAdapter` (same path
|
|
8
|
+
* `/mcp` + pairing use — no second identity plane).
|
|
9
|
+
* 2. Maps the identity to a stable `ownerId` string (see
|
|
10
|
+
* {@link ThreadOwnerResolver}).
|
|
11
|
+
* 3. Calls the shared handler with `{ ownerId, requestId }`.
|
|
12
|
+
* 4. Maps handler / store errors to stable HTTP status codes.
|
|
13
|
+
*
|
|
14
|
+
* Routes mounted:
|
|
15
|
+
*
|
|
16
|
+
* POST /threads → createThread
|
|
17
|
+
* GET /threads → listThreads
|
|
18
|
+
* GET /threads/:id → getThread
|
|
19
|
+
* PATCH /threads/:id → applyThreadAction
|
|
20
|
+
* GET /threads/:id/messages → listMessages
|
|
21
|
+
* POST /threads/:id/messages → appendMessage
|
|
22
|
+
* GET /threads/:id/stream → observeMessages (SSE)
|
|
23
|
+
*
|
|
24
|
+
* Error mapping (the ONLY transport-level semantic):
|
|
25
|
+
*
|
|
26
|
+
* InvalidThreadRequestError → 400 bad_request
|
|
27
|
+
* ThreadNotFoundError → 404 not_found (wrong-owner + missing
|
|
28
|
+
* collapse to the same code)
|
|
29
|
+
* InvalidThreadActionError → 400 bad_request (defense-in-depth;
|
|
30
|
+
* handler-level schema parses first)
|
|
31
|
+
* ThreadActionInvalidStateError → 409 conflict
|
|
32
|
+
* UnauthenticatedError → 401 unauthenticated
|
|
33
|
+
* (anything else) → 500 internal
|
|
34
|
+
*
|
|
35
|
+
* Error envelopes match the pairing-transport shape so clients see one
|
|
36
|
+
* error-body contract across every non-/mcp route:
|
|
37
|
+
*
|
|
38
|
+
* { error: { code: 'bad_request' | 'not_found' | 'conflict' | ...,
|
|
39
|
+
* message: string,
|
|
40
|
+
* details?: unknown } }
|
|
41
|
+
*/
|
|
42
|
+
import type { Express } from 'express';
|
|
43
|
+
import type { AuthAdapter, AuthResult, ThreadStore } from '@ggui-ai/mcp-server-core';
|
|
44
|
+
import type { Logger } from './logger.js';
|
|
45
|
+
/** Default URL prefix the thread routes are mounted at. */
|
|
46
|
+
export declare const DEFAULT_THREADS_PATH = "/threads";
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the stable thread-partition key (`ownerId`) from the
|
|
49
|
+
* authenticated identity.
|
|
50
|
+
*
|
|
51
|
+
* The protocol pins the canonical shapes:
|
|
52
|
+
* - Closed-cloud variants: `cognito_<sub>` / `guest_<uuidv4>` (not
|
|
53
|
+
* our concern here — the closed binding supplies its own resolver).
|
|
54
|
+
* - Self-hosted: `paired_<pairingId>` for pairing-minted tokens;
|
|
55
|
+
* kind-scoped fallback for everything else.
|
|
56
|
+
*
|
|
57
|
+
* The default {@link defaultThreadOwnerFromIdentity} implements that
|
|
58
|
+
* rule. Operators who need a different partition key (e.g. a multi-
|
|
59
|
+
* user OSS fork) pass their own via `threads.ownerFromIdentity`.
|
|
60
|
+
*/
|
|
61
|
+
export type ThreadOwnerResolver = (result: AuthResult) => string;
|
|
62
|
+
export declare const DEFAULT_BUILDER_OWNER_ID = "builder";
|
|
63
|
+
/**
|
|
64
|
+
* Default identity → ownerId mapping for the OSS server.
|
|
65
|
+
*
|
|
66
|
+
* Preserves the protocol's "self-hosted → typically `paired_<pairingId>`"
|
|
67
|
+
* convention whenever the token was minted by pairing: the bridge in
|
|
68
|
+
* `createGguiServer` passes `metadata: { pairingId }` on `onTokenIssued`,
|
|
69
|
+
* which surfaces here via `AuthResult.metadata.pairingId`. Fallbacks:
|
|
70
|
+
*
|
|
71
|
+
* - `source: 'cognito'` with a `sub` metadata field → `cognito_<sub>`
|
|
72
|
+
* (parallel adapters in any closed cloud runtime; not a shape the
|
|
73
|
+
* OSS server ships today but the mapping is stable so custom
|
|
74
|
+
* adapters are uniform).
|
|
75
|
+
* - `kind: 'user'` → `user_<workspaceId ?? userId>` — same partition
|
|
76
|
+
* rule `defaultAppIdFromIdentity` uses.
|
|
77
|
+
* - `kind: 'builder'` with no pairing metadata (dev mode / manual
|
|
78
|
+
* token) → {@link DEFAULT_BUILDER_OWNER_ID}.
|
|
79
|
+
*
|
|
80
|
+
* Keeping every OSS-reachable identity collapsed to ONE owner by
|
|
81
|
+
* default is correct: OSS is a single-operator tier. Operators who
|
|
82
|
+
* split owners across multiple tokens pass a custom resolver.
|
|
83
|
+
*/
|
|
84
|
+
export declare function defaultThreadOwnerFromIdentity(result: AuthResult): string;
|
|
85
|
+
export interface ThreadTransportOptions {
|
|
86
|
+
/** Required. Store implementation the handlers call through to. */
|
|
87
|
+
readonly store: ThreadStore;
|
|
88
|
+
/**
|
|
89
|
+
* Required. Same AuthAdapter the `/mcp` + live-channel + pairing
|
|
90
|
+
* endpoints use. Identity resolution goes through this — thread
|
|
91
|
+
* routes never invent their own auth path.
|
|
92
|
+
*/
|
|
93
|
+
readonly auth: AuthAdapter;
|
|
94
|
+
/** Structured logger. Child loggers are derived per-route. */
|
|
95
|
+
readonly logger: Logger;
|
|
96
|
+
/**
|
|
97
|
+
* URL prefix. Defaults to `/threads`. Six routes are registered
|
|
98
|
+
* beneath it:
|
|
99
|
+
* POST / GET on the prefix itself, GET / PATCH on `/:id`,
|
|
100
|
+
* GET / POST on `/:id/messages`.
|
|
101
|
+
*/
|
|
102
|
+
readonly path?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Identity → ownerId mapping. Defaults to
|
|
105
|
+
* {@link defaultThreadOwnerFromIdentity}. A hosted closed runtime
|
|
106
|
+
* overrides to map Cognito claims to `cognito_<sub>` / `guest_<id>`;
|
|
107
|
+
* OSS forks that partition multiple operators override as needed.
|
|
108
|
+
*/
|
|
109
|
+
readonly ownerFromIdentity?: ThreadOwnerResolver;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Mount the six thread routes onto an existing Express app.
|
|
113
|
+
*
|
|
114
|
+
* Idempotent is NOT a goal — call once per server. `createGguiServer`
|
|
115
|
+
* owns the single call site via `opts.threads`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function mountThreadTransport(app: Express, opts: ThreadTransportOptions): void;
|
|
118
|
+
//# sourceMappingURL=thread-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-transport.d.ts","sourceRoot":"","sources":["../src/thread-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAiBlC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,2DAA2D;AAC3D,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,CAAC;AAEjE,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAqBzE;AAED,MAAM,WAAW,sBAAsB;IACrC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;CAClD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,sBAAsB,GAC3B,IAAI,CAwYN"}
|