@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,263 @@
|
|
|
1
|
+
import { resolveIdentity, UnauthenticatedError } from './auth.js';
|
|
2
|
+
export const DEFAULT_ADMIN_OAUTH_PROVIDERS_PATH = '/ggui/admin/oauth-providers';
|
|
3
|
+
function redact(record) {
|
|
4
|
+
return {
|
|
5
|
+
providerId: record.providerId,
|
|
6
|
+
clientId: record.clientId,
|
|
7
|
+
clientSecret: '<redacted>',
|
|
8
|
+
source: record.source,
|
|
9
|
+
enabled: record.enabled,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function isEnvOverriddenError(err) {
|
|
13
|
+
return err instanceof Error && err.message.startsWith('oauth_provider_env_overridden');
|
|
14
|
+
}
|
|
15
|
+
function isInvalidIdError(err) {
|
|
16
|
+
return (err instanceof Error &&
|
|
17
|
+
(err.message.startsWith('oauth_provider_invalid_id') ||
|
|
18
|
+
err.message.startsWith('oauth_provider_invalid_client_id') ||
|
|
19
|
+
err.message.startsWith('oauth_provider_invalid_client_secret')));
|
|
20
|
+
}
|
|
21
|
+
function isNotFoundError(err) {
|
|
22
|
+
return err instanceof Error && err.message.startsWith('oauth_provider_not_found');
|
|
23
|
+
}
|
|
24
|
+
export function mountAdminOAuthProvidersTransport(app, opts) {
|
|
25
|
+
const basePath = opts.path ?? DEFAULT_ADMIN_OAUTH_PROVIDERS_PATH;
|
|
26
|
+
const auditSink = opts.auditSink;
|
|
27
|
+
const emitAudit = async (entry, auditLogger) => {
|
|
28
|
+
if (!auditSink)
|
|
29
|
+
return;
|
|
30
|
+
try {
|
|
31
|
+
await auditSink.record({ at: Date.now(), ...entry });
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
auditLogger.warn('audit_emit_failed', {
|
|
35
|
+
action: entry.action,
|
|
36
|
+
error: String(err),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
// Builder-identity gate. Same shape as /admin/blueprints — 401 on
|
|
41
|
+
// missing/invalid bearer, 403 on non-builder. Returns true when
|
|
42
|
+
// the route handler may proceed; false when a response was already
|
|
43
|
+
// written.
|
|
44
|
+
const requireBuilder = async (req, res, reqLogger) => {
|
|
45
|
+
try {
|
|
46
|
+
const identity = await resolveIdentity(opts.auth, req);
|
|
47
|
+
if (identity.identity.kind !== 'builder') {
|
|
48
|
+
reqLogger.warn('admin_oauth_providers_forbidden', {
|
|
49
|
+
identityKind: identity.identity.kind,
|
|
50
|
+
});
|
|
51
|
+
res.status(403).json({
|
|
52
|
+
error: {
|
|
53
|
+
code: 'forbidden',
|
|
54
|
+
message: 'Admin OAuth providers requires a builder identity.',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof UnauthenticatedError) {
|
|
63
|
+
reqLogger.warn('admin_oauth_providers_unauthenticated', {
|
|
64
|
+
reason: err.message,
|
|
65
|
+
});
|
|
66
|
+
res.status(401).json({
|
|
67
|
+
error: { code: 'unauthenticated', message: err.message },
|
|
68
|
+
});
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
reqLogger.error('admin_oauth_providers_unexpected_error', {
|
|
72
|
+
error: String(err),
|
|
73
|
+
});
|
|
74
|
+
res.status(500).json({
|
|
75
|
+
error: { code: 'internal', message: 'Internal server error' },
|
|
76
|
+
});
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// --- GET base ---
|
|
81
|
+
app.get(basePath, async (req, res) => {
|
|
82
|
+
const reqLogger = opts.logger.child({ route: 'GET ' + basePath });
|
|
83
|
+
if (!(await requireBuilder(req, res, reqLogger)))
|
|
84
|
+
return;
|
|
85
|
+
try {
|
|
86
|
+
const records = await opts.store.list();
|
|
87
|
+
res.status(200).json({ providers: records.map(redact) });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
reqLogger.error('admin_oauth_providers_list_failed', {
|
|
91
|
+
error: String(err),
|
|
92
|
+
});
|
|
93
|
+
res.status(500).json({
|
|
94
|
+
error: { code: 'internal', message: 'Internal server error' },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// --- PUT base/:providerId ---
|
|
99
|
+
app.put(`${basePath}/:providerId`, async (req, res) => {
|
|
100
|
+
const reqLogger = opts.logger.child({ route: 'PUT ' + basePath + '/:providerId' });
|
|
101
|
+
if (!(await requireBuilder(req, res, reqLogger)))
|
|
102
|
+
return;
|
|
103
|
+
const providerId = req.params['providerId'] ?? '';
|
|
104
|
+
const body = (req.body ?? {});
|
|
105
|
+
const clientId = typeof body['clientId'] === 'string' ? body['clientId'] : undefined;
|
|
106
|
+
const clientSecret = typeof body['clientSecret'] === 'string' ? body['clientSecret'] : undefined;
|
|
107
|
+
const enabled = typeof body['enabled'] === 'boolean' ? body['enabled'] : undefined;
|
|
108
|
+
if (!clientId || !clientSecret) {
|
|
109
|
+
reqLogger.debug?.('admin_oauth_providers_bad_request', {
|
|
110
|
+
hasClientId: clientId !== undefined,
|
|
111
|
+
hasClientSecret: clientSecret !== undefined,
|
|
112
|
+
});
|
|
113
|
+
res.status(400).json({
|
|
114
|
+
error: {
|
|
115
|
+
code: 'bad_request',
|
|
116
|
+
message: 'Body requires non-empty string `clientId` and `clientSecret`.',
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const record = await opts.store.put({
|
|
123
|
+
providerId,
|
|
124
|
+
clientId,
|
|
125
|
+
clientSecret,
|
|
126
|
+
...(enabled !== undefined ? { enabled } : {}),
|
|
127
|
+
});
|
|
128
|
+
reqLogger.info('admin_oauth_providers_put', { providerId });
|
|
129
|
+
await emitAudit({
|
|
130
|
+
action: 'auth.oauth-config.write',
|
|
131
|
+
actor: { kind: 'builder' },
|
|
132
|
+
resource: { kind: 'oauth-provider', id: providerId },
|
|
133
|
+
metadata: { enabled: record.enabled },
|
|
134
|
+
}, reqLogger);
|
|
135
|
+
res.status(200).json(redact(record));
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (isEnvOverriddenError(err)) {
|
|
139
|
+
reqLogger.warn('admin_oauth_providers_env_overridden', { providerId });
|
|
140
|
+
res.status(409).json({
|
|
141
|
+
error: {
|
|
142
|
+
code: 'env_overridden',
|
|
143
|
+
message: err.message,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (isInvalidIdError(err)) {
|
|
149
|
+
reqLogger.warn('admin_oauth_providers_validation_failed', {
|
|
150
|
+
providerId,
|
|
151
|
+
reason: err.message,
|
|
152
|
+
});
|
|
153
|
+
res.status(400).json({
|
|
154
|
+
error: { code: 'bad_request', message: err.message },
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
reqLogger.error('admin_oauth_providers_put_failed', {
|
|
159
|
+
providerId,
|
|
160
|
+
error: String(err),
|
|
161
|
+
});
|
|
162
|
+
res.status(500).json({
|
|
163
|
+
error: { code: 'internal', message: 'Internal server error' },
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
// --- POST base/:providerId/toggle ---
|
|
168
|
+
app.post(`${basePath}/:providerId/toggle`, async (req, res) => {
|
|
169
|
+
const reqLogger = opts.logger.child({
|
|
170
|
+
route: 'POST ' + basePath + '/:providerId/toggle',
|
|
171
|
+
});
|
|
172
|
+
if (!(await requireBuilder(req, res, reqLogger)))
|
|
173
|
+
return;
|
|
174
|
+
const providerId = req.params['providerId'] ?? '';
|
|
175
|
+
const body = (req.body ?? {});
|
|
176
|
+
const enabled = typeof body['enabled'] === 'boolean' ? body['enabled'] : undefined;
|
|
177
|
+
if (enabled === undefined) {
|
|
178
|
+
res.status(400).json({
|
|
179
|
+
error: {
|
|
180
|
+
code: 'bad_request',
|
|
181
|
+
message: 'Body requires boolean `enabled`.',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
await opts.store.setEnabled(providerId, enabled);
|
|
188
|
+
reqLogger.info('admin_oauth_providers_toggle', { providerId, enabled });
|
|
189
|
+
await emitAudit({
|
|
190
|
+
action: 'auth.oauth-config.write',
|
|
191
|
+
actor: { kind: 'builder' },
|
|
192
|
+
resource: { kind: 'oauth-provider', id: providerId },
|
|
193
|
+
metadata: { enabled },
|
|
194
|
+
}, reqLogger);
|
|
195
|
+
res.status(204).end();
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
if (isEnvOverriddenError(err)) {
|
|
199
|
+
reqLogger.warn('admin_oauth_providers_env_overridden', { providerId });
|
|
200
|
+
res.status(409).json({
|
|
201
|
+
error: {
|
|
202
|
+
code: 'env_overridden',
|
|
203
|
+
message: err.message,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (isNotFoundError(err)) {
|
|
209
|
+
res.status(404).json({
|
|
210
|
+
error: { code: 'not_found', message: err.message },
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (isInvalidIdError(err)) {
|
|
215
|
+
res.status(400).json({
|
|
216
|
+
error: { code: 'bad_request', message: err.message },
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
reqLogger.error('admin_oauth_providers_toggle_failed', {
|
|
221
|
+
providerId,
|
|
222
|
+
error: String(err),
|
|
223
|
+
});
|
|
224
|
+
res.status(500).json({
|
|
225
|
+
error: { code: 'internal', message: 'Internal server error' },
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// --- DELETE base/:providerId ---
|
|
230
|
+
app.delete(`${basePath}/:providerId`, async (req, res) => {
|
|
231
|
+
const reqLogger = opts.logger.child({
|
|
232
|
+
route: 'DELETE ' + basePath + '/:providerId',
|
|
233
|
+
});
|
|
234
|
+
if (!(await requireBuilder(req, res, reqLogger)))
|
|
235
|
+
return;
|
|
236
|
+
const providerId = req.params['providerId'] ?? '';
|
|
237
|
+
try {
|
|
238
|
+
await opts.store.remove(providerId);
|
|
239
|
+
reqLogger.info('admin_oauth_providers_remove', { providerId });
|
|
240
|
+
await emitAudit({
|
|
241
|
+
action: 'auth.oauth-config.delete',
|
|
242
|
+
actor: { kind: 'builder' },
|
|
243
|
+
resource: { kind: 'oauth-provider', id: providerId },
|
|
244
|
+
}, reqLogger);
|
|
245
|
+
res.status(204).end();
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
if (isInvalidIdError(err)) {
|
|
249
|
+
res.status(400).json({
|
|
250
|
+
error: { code: 'bad_request', message: err.message },
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
reqLogger.error('admin_oauth_providers_remove_failed', {
|
|
255
|
+
providerId,
|
|
256
|
+
error: String(err),
|
|
257
|
+
});
|
|
258
|
+
res.status(500).json({
|
|
259
|
+
error: { code: 'internal', message: 'Internal server error' },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP → AuthAdapter bridge.
|
|
3
|
+
*
|
|
4
|
+
* Parses `Authorization: Bearer <token>` off the incoming request and
|
|
5
|
+
* delegates identity resolution to the supplied {@link AuthAdapter}.
|
|
6
|
+
* Unauthenticated requests are rejected with 401 before the MCP SDK
|
|
7
|
+
* ever sees the request body.
|
|
8
|
+
*/
|
|
9
|
+
import type { Request } from 'express';
|
|
10
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
11
|
+
import type { AuthAdapter, AuthResult } from '@ggui-ai/mcp-server-core';
|
|
12
|
+
export declare class UnauthenticatedError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve an identity from a normalized `(headers, remoteAddress)` pair.
|
|
17
|
+
* Both the Express `/mcp` path and the WebSocket live-channel `/ws`
|
|
18
|
+
* upgrade path funnel through this — so a single bearer-parsing +
|
|
19
|
+
* adapter-call codepath covers every OSS ingress point.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveIdentityFromHeaders(adapter: AuthAdapter, headers: IncomingHttpHeaders, remoteAddress?: string): Promise<AuthResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the caller's identity from the incoming Express request.
|
|
24
|
+
* Throws {@link UnauthenticatedError} when the adapter returns null
|
|
25
|
+
* so the HTTP layer can map to a single 401 response shape.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveIdentity(adapter: AuthAdapter, req: Request): Promise<AuthResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Derive a stable `appId` from an auth result.
|
|
30
|
+
*
|
|
31
|
+
* In OSS single-user mode every identity collapses to `{kind:'builder'}`
|
|
32
|
+
* which doesn't carry a tenant id. We fold these into a single
|
|
33
|
+
* well-known value (`DEFAULT_BUILDER_APP_ID`) so blueprint / vector
|
|
34
|
+
* scoping still works. Multi-tenant bindings (a hosted closed runtime)
|
|
35
|
+
* override this by passing `appIdFromIdentity` on `createGguiServer`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const DEFAULT_BUILDER_APP_ID = "builder";
|
|
38
|
+
export declare function defaultAppIdFromIdentity(result: AuthResult): string;
|
|
39
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAExE,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,mBAAmB,EAC5B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,UAAU,CAAC,CAMrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAqBnE"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP → AuthAdapter bridge.
|
|
3
|
+
*
|
|
4
|
+
* Parses `Authorization: Bearer <token>` off the incoming request and
|
|
5
|
+
* delegates identity resolution to the supplied {@link AuthAdapter}.
|
|
6
|
+
* Unauthenticated requests are rejected with 401 before the MCP SDK
|
|
7
|
+
* ever sees the request body.
|
|
8
|
+
*/
|
|
9
|
+
export class UnauthenticatedError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'UnauthenticatedError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve an identity from a normalized `(headers, remoteAddress)` pair.
|
|
17
|
+
* Both the Express `/mcp` path and the WebSocket live-channel `/ws`
|
|
18
|
+
* upgrade path funnel through this — so a single bearer-parsing +
|
|
19
|
+
* adapter-call codepath covers every OSS ingress point.
|
|
20
|
+
*/
|
|
21
|
+
export async function resolveIdentityFromHeaders(adapter, headers, remoteAddress) {
|
|
22
|
+
const flat = {};
|
|
23
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
24
|
+
if (typeof v === 'string')
|
|
25
|
+
flat[k] = v;
|
|
26
|
+
else if (Array.isArray(v))
|
|
27
|
+
flat[k] = v[0];
|
|
28
|
+
}
|
|
29
|
+
const result = await adapter.getIdentity({
|
|
30
|
+
headers: flat,
|
|
31
|
+
remoteAddress,
|
|
32
|
+
});
|
|
33
|
+
if (!result) {
|
|
34
|
+
throw new UnauthenticatedError('No valid credentials');
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the caller's identity from the incoming Express request.
|
|
40
|
+
* Throws {@link UnauthenticatedError} when the adapter returns null
|
|
41
|
+
* so the HTTP layer can map to a single 401 response shape.
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveIdentity(adapter, req) {
|
|
44
|
+
return resolveIdentityFromHeaders(adapter, req.headers, req.socket?.remoteAddress ?? req.ip ?? undefined);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Derive a stable `appId` from an auth result.
|
|
48
|
+
*
|
|
49
|
+
* In OSS single-user mode every identity collapses to `{kind:'builder'}`
|
|
50
|
+
* which doesn't carry a tenant id. We fold these into a single
|
|
51
|
+
* well-known value (`DEFAULT_BUILDER_APP_ID`) so blueprint / vector
|
|
52
|
+
* scoping still works. Multi-tenant bindings (a hosted closed runtime)
|
|
53
|
+
* override this by passing `appIdFromIdentity` on `createGguiServer`.
|
|
54
|
+
*/
|
|
55
|
+
export const DEFAULT_BUILDER_APP_ID = 'builder';
|
|
56
|
+
export function defaultAppIdFromIdentity(result) {
|
|
57
|
+
if (result.identity.kind === 'user') {
|
|
58
|
+
// Cloud auth adapters populate `appId` when known — per-app-scoped
|
|
59
|
+
// bearer key, URL-path-derived, or User.defaultAppId lookup. Read
|
|
60
|
+
// it with priority so handlers scope to the correct GguiApp.appId.
|
|
61
|
+
// OSS deployments leave the field undefined and fall through to the
|
|
62
|
+
// `workspaceId`/`userId` chain.
|
|
63
|
+
return (result.identity.appId ??
|
|
64
|
+
result.identity.workspaceId ??
|
|
65
|
+
result.identity.userId);
|
|
66
|
+
}
|
|
67
|
+
// `kind: 'app'` carries the appId directly — surfaced by API-key /
|
|
68
|
+
// OAuth-bearer adapters (e.g. an ApiKeyAuthAdapter on a hosted
|
|
69
|
+
// multi-tenant deployment). Falling through to DEFAULT_BUILDER_APP_ID
|
|
70
|
+
// here would discard the very tenant id the adapter just proved.
|
|
71
|
+
if (result.identity.kind === 'app') {
|
|
72
|
+
return result.identity.appId;
|
|
73
|
+
}
|
|
74
|
+
return DEFAULT_BUILDER_APP_ID;
|
|
75
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* buildMcpServer — register every shared handler on a fresh `McpServer`
|
|
3
|
+
* instance. One server per request (matches the hosted pattern); the
|
|
4
|
+
* `StreamableHTTPServerTransport` holds per-connection state so pooling
|
|
5
|
+
* isn't worth the locking.
|
|
6
|
+
*
|
|
7
|
+
* Output validation runs here via a zod object built from each handler's
|
|
8
|
+
* `outputSchema` raw shape. This enforces the ggui convention that every
|
|
9
|
+
* tool return advertises its shape — wire consumers can trust the output.
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
import { type ZodRawShape } from 'zod';
|
|
13
|
+
import type { HandlerContext, SharedHandler } from '@ggui-ai/mcp-server-handlers';
|
|
14
|
+
import type { Logger } from './logger.js';
|
|
15
|
+
import { type GguiSessionResourceTemplateOptions } from './mcp-apps-outbound.js';
|
|
16
|
+
export interface ServerInfo {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly version: string;
|
|
19
|
+
readonly description?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface BuildMcpServerOptions {
|
|
22
|
+
/**
|
|
23
|
+
* When set, register the MCP Apps outbound wiring on every fresh
|
|
24
|
+
* server instance — advertises the `io.modelcontextprotocol/ui`
|
|
25
|
+
* capability and serves `ui://ggui/session` via `resources/read`.
|
|
26
|
+
*
|
|
27
|
+
* Tool-declaration `_meta.ui.*` is INDEPENDENT of this flag; it's
|
|
28
|
+
* carried per-handler on `SharedHandler._meta`. A server can stamp
|
|
29
|
+
* those without turning the outbound wiring on, but serving the
|
|
30
|
+
* resource without stamping the declaration is pointless, so the
|
|
31
|
+
* canonical path is "enable both together" via `createGguiServer`.
|
|
32
|
+
*/
|
|
33
|
+
readonly mcpAppsOutbound?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Optional override for the `ui://ggui/session` shell body. Defaults
|
|
36
|
+
* to whatever shell the server was built with — either a placeholder
|
|
37
|
+
* or the real thin-shell HTML.
|
|
38
|
+
*/
|
|
39
|
+
readonly shellHtml?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Per-session self-contained shell options. When supplied,
|
|
42
|
+
* `installMcpAppsOutbound` ALSO registers
|
|
43
|
+
* `ui://ggui/session/{sessionId}` as a resource template — the URI
|
|
44
|
+
* `ggui_push.resultMeta` stamps on per-call `_meta.ui.resourceUri`
|
|
45
|
+
* for third-party MCP Apps hosts (Claude Desktop, claude.ai web)
|
|
46
|
+
* that don't speak ggui's custom postMessage protocol.
|
|
47
|
+
*
|
|
48
|
+
* Absent → only the legacy postMessage shell is registered (first-
|
|
49
|
+
* party hosts only).
|
|
50
|
+
*/
|
|
51
|
+
readonly selfContained?: GguiSessionResourceTemplateOptions;
|
|
52
|
+
/**
|
|
53
|
+
* Public origin the server is reachable at — forwarded to
|
|
54
|
+
* `installMcpAppsOutbound` so the static `ui://ggui/session`
|
|
55
|
+
* resource declares `_meta.ui.csp.{connectDomains,resourceDomains}`.
|
|
56
|
+
* Without this, spec-compliant hosts (Claude Desktop, claude.ai
|
|
57
|
+
* Connector, Claude Code) apply their default CSP (`connect-src
|
|
58
|
+
* 'none'`) and the iframe can't fetch the runtime bundle or open
|
|
59
|
+
* the WebSocket. Omit when running same-origin behind a first-party
|
|
60
|
+
* host that owns the iframe CSP itself.
|
|
61
|
+
*/
|
|
62
|
+
readonly publicBaseUrl?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Identity-kind allowlist for tool registration. When set, handlers
|
|
65
|
+
* whose `allowedFor` field is non-empty AND does NOT intersect this
|
|
66
|
+
* list are skipped at registration time (NOT registered with the MCP
|
|
67
|
+
* server, NOT visible in `tools/list`).
|
|
68
|
+
*
|
|
69
|
+
* Handlers without `allowedFor` are registered unconditionally per the
|
|
70
|
+
* "anyone authenticated" default in
|
|
71
|
+
* `packages/mcp-server-handlers/src/types.ts:151-153`. Omitting this
|
|
72
|
+
* option (or passing `undefined`) disables filtering entirely —
|
|
73
|
+
* today's behavior, kept for OSS callers (resolved as
|
|
74
|
+
* `kind: 'builder'`) so an OSS deployment never accidentally gates
|
|
75
|
+
* itself off.
|
|
76
|
+
*
|
|
77
|
+
* Production postures:
|
|
78
|
+
* - agent-builder posture: `allowedKinds: ['app']`
|
|
79
|
+
* - end-user / Connector posture: `allowedKinds: ['user']`
|
|
80
|
+
* - OSS local: omit (every handler registers regardless)
|
|
81
|
+
*/
|
|
82
|
+
readonly allowedKinds?: ReadonlyArray<'app' | 'user' | 'builder'>;
|
|
83
|
+
/**
|
|
84
|
+
* Server-level instructions string injected into the MCP
|
|
85
|
+
* `InitializeResult.instructions` field. Hosts (Claude.ai web,
|
|
86
|
+
* Claude Desktop, MCP Inspector) inject this into the LLM's system
|
|
87
|
+
* prompt as a top-level block, ABOVE per-tool descriptions —
|
|
88
|
+
* influencing "how should I behave with this server's tools
|
|
89
|
+
* generally?" vs. per-tool "should I pick THIS tool right now?"
|
|
90
|
+
*
|
|
91
|
+
* Resolved upstream by `resolveMcpInstructions` from a preset name
|
|
92
|
+
* or arbitrary string. Pass `undefined` here to omit the field
|
|
93
|
+
* (host falls back to per-tool descriptions only).
|
|
94
|
+
*
|
|
95
|
+
* See `instructions-presets.ts` for the supported preset enum and
|
|
96
|
+
* full rationale.
|
|
97
|
+
*/
|
|
98
|
+
readonly instructions?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Hooks invoked once the per-request `McpServer` is constructed,
|
|
101
|
+
* after the MCP-Apps outbound install (when enabled) and before
|
|
102
|
+
* any tool registration. Each entry receives the fresh `McpServer`
|
|
103
|
+
* and may register additional resources / resource templates.
|
|
104
|
+
*
|
|
105
|
+
* Use case: hosted deployments that mount cross-cutting MCP App
|
|
106
|
+
* UI bundles (e.g. a `ui://`-scheme resource for welcome /
|
|
107
|
+
* account-status cards) without baking the bundle's wiring into
|
|
108
|
+
* this OSS factory. The closure runs on every fresh server
|
|
109
|
+
* instance, mirroring the per-request `installMcpAppsOutbound`
|
|
110
|
+
* lifecycle.
|
|
111
|
+
*
|
|
112
|
+
* Each registrar SHOULD be idempotent across calls (the underlying
|
|
113
|
+
* SDK throws on duplicate URIs anyway). Errors thrown by a
|
|
114
|
+
* registrar propagate up — the request fails before any tool can
|
|
115
|
+
* dispatch, surfacing misconfiguration loudly rather than 404-ing
|
|
116
|
+
* `resources/read` later.
|
|
117
|
+
*/
|
|
118
|
+
readonly extraResources?: ReadonlyArray<(server: McpServer) => void>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build a fresh MCP server with every handler registered.
|
|
122
|
+
*
|
|
123
|
+
* `getContext` is a late-binding accessor so the HTTP layer can thread
|
|
124
|
+
* per-request context (via AsyncLocalStorage or a closure) without
|
|
125
|
+
* leaking the shape into this module.
|
|
126
|
+
*/
|
|
127
|
+
export declare function buildMcpServer(info: ServerInfo, handlers: ReadonlyArray<SharedHandler<ZodRawShape, ZodRawShape>>, getContext: () => HandlerContext, logger: Logger, opts?: BuildMcpServerOptions): McpServer;
|
|
128
|
+
//# sourceMappingURL=build-mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-mcp.d.ts","sourceRoot":"","sources":["../src/build-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACd,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,kCAAkC,EACxC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,kCAAkC,CAAC;IAC5D;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAElE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC;CACtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAChE,UAAU,EAAE,MAAM,cAAc,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,qBAA0B,GAC/B,SAAS,CA6FX"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* buildMcpServer — register every shared handler on a fresh `McpServer`
|
|
3
|
+
* instance. One server per request (matches the hosted pattern); the
|
|
4
|
+
* `StreamableHTTPServerTransport` holds per-connection state so pooling
|
|
5
|
+
* isn't worth the locking.
|
|
6
|
+
*
|
|
7
|
+
* Output validation runs here via a zod object built from each handler's
|
|
8
|
+
* `outputSchema` raw shape. This enforces the ggui convention that every
|
|
9
|
+
* tool return advertises its shape — wire consumers can trust the output.
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { installMcpAppsOutbound, } from './mcp-apps-outbound.js';
|
|
14
|
+
/**
|
|
15
|
+
* Build a fresh MCP server with every handler registered.
|
|
16
|
+
*
|
|
17
|
+
* `getContext` is a late-binding accessor so the HTTP layer can thread
|
|
18
|
+
* per-request context (via AsyncLocalStorage or a closure) without
|
|
19
|
+
* leaking the shape into this module.
|
|
20
|
+
*/
|
|
21
|
+
export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
22
|
+
const server = new McpServer({
|
|
23
|
+
name: info.name,
|
|
24
|
+
version: info.version,
|
|
25
|
+
...(info.description ? { description: info.description } : {}),
|
|
26
|
+
...(opts.instructions ? { instructions: opts.instructions } : {}),
|
|
27
|
+
});
|
|
28
|
+
if (opts.mcpAppsOutbound) {
|
|
29
|
+
installMcpAppsOutbound(server, {
|
|
30
|
+
...(opts.shellHtml !== undefined ? { shellHtml: opts.shellHtml } : {}),
|
|
31
|
+
...(opts.selfContained !== undefined
|
|
32
|
+
? { selfContained: opts.selfContained }
|
|
33
|
+
: {}),
|
|
34
|
+
...(opts.publicBaseUrl !== undefined
|
|
35
|
+
? { publicBaseUrl: opts.publicBaseUrl }
|
|
36
|
+
: {}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Per-request resource registrars supplied by the host. Run BEFORE
|
|
40
|
+
// tool registration so `tools/list` ordering is unaffected and any
|
|
41
|
+
// registrar-thrown error fails the request before tool dispatch.
|
|
42
|
+
if (opts.extraResources) {
|
|
43
|
+
for (const register of opts.extraResources) {
|
|
44
|
+
register(server);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const allowedKinds = opts.allowedKinds;
|
|
48
|
+
for (const handler of handlers) {
|
|
49
|
+
// Identity-kind gate. Skipping at registration time (rather than at
|
|
50
|
+
// call dispatch) means a curated deployment's `tools/list` reflects
|
|
51
|
+
// exactly what callers can use — no "ghost" tools that 401 on
|
|
52
|
+
// invocation. Handlers without `allowedFor` register regardless.
|
|
53
|
+
if (allowedKinds !== undefined
|
|
54
|
+
&& handler.allowedFor !== undefined
|
|
55
|
+
&& handler.allowedFor.length > 0
|
|
56
|
+
&& !handler.allowedFor.some((kind) => allowedKinds.includes(kind))) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
server.registerTool(handler.name, {
|
|
60
|
+
...(handler.title ? { title: handler.title } : {}),
|
|
61
|
+
description: handler.description,
|
|
62
|
+
inputSchema: handler.inputSchema,
|
|
63
|
+
outputSchema: handler.outputSchema,
|
|
64
|
+
// Forward declaration-level `_meta` (e.g. `_meta.ui.resourceUri`
|
|
65
|
+
// / `_meta.ui.visibility` stamped by the MCP Apps outbound path).
|
|
66
|
+
// Opaque to the transport — hosts consume it per their own spec.
|
|
67
|
+
...(handler._meta ? { _meta: handler._meta } : {}),
|
|
68
|
+
}, async (input) => {
|
|
69
|
+
const ctx = getContext();
|
|
70
|
+
const start = Date.now();
|
|
71
|
+
try {
|
|
72
|
+
const data = await handler.handler(input, ctx);
|
|
73
|
+
const validated = z.object(handler.outputSchema).parse(data);
|
|
74
|
+
// Per-result `_meta` — NOT merged into structuredContent, so
|
|
75
|
+
// agents that typecheck against the tool signature never see
|
|
76
|
+
// it. This is where view-only bootstrap material lives.
|
|
77
|
+
const meta = await handler.resultMeta?.(data, input, ctx);
|
|
78
|
+
logger.info('tool_invoked', {
|
|
79
|
+
tool: handler.name,
|
|
80
|
+
appId: ctx.appId,
|
|
81
|
+
outcome: 'success',
|
|
82
|
+
elapsedMs: Date.now() - start,
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
structuredContent: validated,
|
|
86
|
+
content: [
|
|
87
|
+
{ type: 'text', text: JSON.stringify(validated) },
|
|
88
|
+
],
|
|
89
|
+
...(meta !== undefined ? { _meta: meta } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger.warn('tool_invoked', {
|
|
94
|
+
tool: handler.name,
|
|
95
|
+
appId: ctx.appId,
|
|
96
|
+
outcome: 'error',
|
|
97
|
+
errorClass: errorClassName(err),
|
|
98
|
+
elapsedMs: Date.now() - start,
|
|
99
|
+
});
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return server;
|
|
105
|
+
}
|
|
106
|
+
function errorClassName(err) {
|
|
107
|
+
if (err instanceof Error) {
|
|
108
|
+
if (err.name && err.name !== 'Error')
|
|
109
|
+
return err.name;
|
|
110
|
+
return err.constructor.name || 'Error';
|
|
111
|
+
}
|
|
112
|
+
return 'Unknown';
|
|
113
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CodeStore } from '@ggui-ai/mcp-server-core';
|
|
2
|
+
/** Options for {@link FileSystemCodeStore}. */
|
|
3
|
+
export interface FileSystemCodeStoreOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem root for the cache. Defaults to `~/.ggui/code-cache/`.
|
|
6
|
+
* Tests typically override to a project-local tmp dir.
|
|
7
|
+
*/
|
|
8
|
+
readonly root?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class FileSystemCodeStore implements CodeStore {
|
|
11
|
+
private readonly root;
|
|
12
|
+
constructor(opts?: FileSystemCodeStoreOptions);
|
|
13
|
+
/** Absolute on-disk path for a given hash. Two-level sharded layout. */
|
|
14
|
+
private absPath;
|
|
15
|
+
put(hash: string, code: string): Promise<void>;
|
|
16
|
+
get(hash: string): Promise<string | null>;
|
|
17
|
+
hashOf(code: string): string;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=code-store-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-store-fs.d.ts","sourceRoot":"","sources":["../src/code-store-fs.ts"],"names":[],"mappings":"AAmDA,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,0BAA0B,CAAC;AAElC,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,mBAAoB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,IAAI,GAAE,0BAA+B;IAIjD,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAMT,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU/C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAG7B"}
|