@guuey/config 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +23 -25
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +41 -46
- package/dist/loader.d.ts +3 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +7 -2
- package/dist/schema.d.ts +83 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +82 -1
- package/package.json +1 -1
package/dist/agent.d.ts
CHANGED
|
@@ -66,8 +66,12 @@ export type ProfileAccess = z.infer<typeof ProfileAccessSchema>;
|
|
|
66
66
|
* Discriminated union on `kind` — one slot per hosting mode:
|
|
67
67
|
* - `colocated` — guuey-managed HTTP child inside the agent pod
|
|
68
68
|
* - `hosted` — guuey-hosted registry MCP (Starter+)
|
|
69
|
-
* - `
|
|
70
|
-
*
|
|
69
|
+
* - `external` — reached by URL: builder-hosted (plain / federated /
|
|
70
|
+
* caller-forwarded) or third-party OAuth (`credential: 'oauth'`)
|
|
71
|
+
*
|
|
72
|
+
* (`kind: 'proxied'` — the pre-registration placeholder the mcp-proxy broker
|
|
73
|
+
* once reserved — is gone: `credential: 'oauth'` on an `external` entry IS
|
|
74
|
+
* that arm, guuey#178 D1.)
|
|
71
75
|
*/
|
|
72
76
|
declare const McpServerSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
73
77
|
kind: z.ZodLiteral<"colocated">;
|
|
@@ -78,9 +82,6 @@ declare const McpServerSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
78
82
|
server: z.ZodOptional<z.ZodString>;
|
|
79
83
|
source: z.ZodOptional<z.ZodString>;
|
|
80
84
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
81
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
82
|
-
kind: z.ZodLiteral<"proxied">;
|
|
83
|
-
connection: z.ZodString;
|
|
84
85
|
}, z.core.$strict>, z.ZodObject<{
|
|
85
86
|
kind: z.ZodLiteral<"external">;
|
|
86
87
|
url: z.ZodURL;
|
|
@@ -89,7 +90,10 @@ declare const McpServerSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
89
90
|
sse: "sse";
|
|
90
91
|
}>>;
|
|
91
92
|
federate: z.ZodOptional<z.ZodBoolean>;
|
|
92
|
-
credential: z.ZodOptional<z.
|
|
93
|
+
credential: z.ZodOptional<z.ZodEnum<{
|
|
94
|
+
caller: "caller";
|
|
95
|
+
oauth: "oauth";
|
|
96
|
+
}>>;
|
|
93
97
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
94
98
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
95
99
|
mcpResourceUrl: z.ZodOptional<z.ZodURL>;
|
|
@@ -112,9 +116,6 @@ export declare const McpServerEntrySchema: z.ZodUnion<readonly [z.ZodDiscriminat
|
|
|
112
116
|
server: z.ZodOptional<z.ZodString>;
|
|
113
117
|
source: z.ZodOptional<z.ZodString>;
|
|
114
118
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
115
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
116
|
-
kind: z.ZodLiteral<"proxied">;
|
|
117
|
-
connection: z.ZodString;
|
|
118
119
|
}, z.core.$strict>, z.ZodObject<{
|
|
119
120
|
kind: z.ZodLiteral<"external">;
|
|
120
121
|
url: z.ZodURL;
|
|
@@ -123,7 +124,10 @@ export declare const McpServerEntrySchema: z.ZodUnion<readonly [z.ZodDiscriminat
|
|
|
123
124
|
sse: "sse";
|
|
124
125
|
}>>;
|
|
125
126
|
federate: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
-
credential: z.ZodOptional<z.
|
|
127
|
+
credential: z.ZodOptional<z.ZodEnum<{
|
|
128
|
+
caller: "caller";
|
|
129
|
+
oauth: "oauth";
|
|
130
|
+
}>>;
|
|
127
131
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
128
132
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
129
133
|
mcpResourceUrl: z.ZodOptional<z.ZodURL>;
|
|
@@ -251,9 +255,6 @@ export declare const McpServersSection: z.ZodRecord<z.ZodString, z.ZodUnion<read
|
|
|
251
255
|
server: z.ZodOptional<z.ZodString>;
|
|
252
256
|
source: z.ZodOptional<z.ZodString>;
|
|
253
257
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
254
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
255
|
-
kind: z.ZodLiteral<"proxied">;
|
|
256
|
-
connection: z.ZodString;
|
|
257
258
|
}, z.core.$strict>, z.ZodObject<{
|
|
258
259
|
kind: z.ZodLiteral<"external">;
|
|
259
260
|
url: z.ZodURL;
|
|
@@ -262,7 +263,10 @@ export declare const McpServersSection: z.ZodRecord<z.ZodString, z.ZodUnion<read
|
|
|
262
263
|
sse: "sse";
|
|
263
264
|
}>>;
|
|
264
265
|
federate: z.ZodOptional<z.ZodBoolean>;
|
|
265
|
-
credential: z.ZodOptional<z.
|
|
266
|
+
credential: z.ZodOptional<z.ZodEnum<{
|
|
267
|
+
caller: "caller";
|
|
268
|
+
oauth: "oauth";
|
|
269
|
+
}>>;
|
|
266
270
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
267
271
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
268
272
|
mcpResourceUrl: z.ZodOptional<z.ZodURL>;
|
|
@@ -300,9 +304,6 @@ export declare const AgentSectionV1: z.ZodObject<{
|
|
|
300
304
|
server: z.ZodOptional<z.ZodString>;
|
|
301
305
|
source: z.ZodOptional<z.ZodString>;
|
|
302
306
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
303
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
304
|
-
kind: z.ZodLiteral<"proxied">;
|
|
305
|
-
connection: z.ZodString;
|
|
306
307
|
}, z.core.$strict>, z.ZodObject<{
|
|
307
308
|
kind: z.ZodLiteral<"external">;
|
|
308
309
|
url: z.ZodURL;
|
|
@@ -311,7 +312,10 @@ export declare const AgentSectionV1: z.ZodObject<{
|
|
|
311
312
|
sse: "sse";
|
|
312
313
|
}>>;
|
|
313
314
|
federate: z.ZodOptional<z.ZodBoolean>;
|
|
314
|
-
credential: z.ZodOptional<z.
|
|
315
|
+
credential: z.ZodOptional<z.ZodEnum<{
|
|
316
|
+
caller: "caller";
|
|
317
|
+
oauth: "oauth";
|
|
318
|
+
}>>;
|
|
315
319
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
316
320
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
317
321
|
mcpResourceUrl: z.ZodOptional<z.ZodURL>;
|
|
@@ -409,12 +413,6 @@ export declare function validateNoLiteralSecrets(agent: GuueyAgent | undefined):
|
|
|
409
413
|
* violation messages (empty = clean).
|
|
410
414
|
*/
|
|
411
415
|
export declare function validateColocatedServerNames(agent: GuueyAgent | undefined): string[];
|
|
412
|
-
/**
|
|
413
|
-
* Validate that no `agent.mcpServers` entry uses `kind: 'proxied'`. Returns a
|
|
414
|
-
* list of human-readable violation messages (empty = clean), one per proxied
|
|
415
|
-
* entry, naming the server so the builder knows which ref to change.
|
|
416
|
-
*/
|
|
417
|
-
export declare function validateNoProxiedServers(agent: GuueyAgent | undefined): string[];
|
|
418
416
|
/**
|
|
419
417
|
* The reserved colocated key the auto-injected memory MCP is booted + spliced
|
|
420
418
|
* under (memmcp). NEVER builder-declarable — the memory child's own server
|
|
@@ -441,7 +439,7 @@ export declare const RESERVED_MCP_SERVER_NAMES: readonly string[];
|
|
|
441
439
|
* reserved, not just one hosting mode; a builder must not shadow it as
|
|
442
440
|
* `colocated`, `external`, or anything else). Returns a list of human-readable
|
|
443
441
|
* violation messages (empty = clean), one per reserved entry. Mirrors
|
|
444
|
-
* {@link
|
|
442
|
+
* {@link validateColocatedServerNames}'s shape.
|
|
445
443
|
*/
|
|
446
444
|
export declare function validateReservedServerNames(agent: GuueyAgent | undefined): string[];
|
|
447
445
|
/** One parsed `tools.allowlist` / `tools.denylist` entry. */
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,6EAKnB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAS/D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;EAAiC,CAAC;AAClE,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.js
CHANGED
|
@@ -107,21 +107,18 @@ const HostedMcp = z
|
|
|
107
107
|
message: 'hosted MCP needs `server`and/or`source`',
|
|
108
108
|
});
|
|
109
109
|
/**
|
|
110
|
-
* `kind: '
|
|
111
|
-
*
|
|
112
|
-
*/
|
|
113
|
-
const ProxiedMcp = z.strictObject({
|
|
114
|
-
kind: z.literal('proxied'),
|
|
115
|
-
/** mcp-proxy connection id (not yet available — rides with the mcp-proxy credential broker). */
|
|
116
|
-
connection: z.string().min(1),
|
|
117
|
-
});
|
|
118
|
-
/**
|
|
119
|
-
* `kind: 'external'` — builder-hosted MCP at an arbitrary URL.
|
|
110
|
+
* `kind: 'external'` — any MCP reached by URL: builder-hosted, or a
|
|
111
|
+
* third-party SaaS server (Linear, Notion, GitHub, …).
|
|
120
112
|
*
|
|
113
|
+
* One map, three credential sources + one forward (guuey#178 D1):
|
|
121
114
|
* - `transport` defaults to `'http'` (StreamableHTTP).
|
|
115
|
+
* - static `headers` (values may use `${env.NAME}`) — API-key servers.
|
|
122
116
|
* - `federate: true` makes guuey mint a per-invoke JWT with `aud = url` that
|
|
123
|
-
* the builder's MCP validates against the guuey JWKS.
|
|
124
|
-
*
|
|
117
|
+
* the builder's MCP validates against the guuey JWKS.
|
|
118
|
+
* - `credential: 'oauth'` — the server's users sign in with the server's OWN
|
|
119
|
+
* OAuth authorization server; guuey brokers the dance and the token
|
|
120
|
+
* (see the field doc). URL + `credential: 'oauth'` is the whole
|
|
121
|
+
* declaration — nothing is builder-registered; discovery does the rest.
|
|
125
122
|
* - `credential: 'caller'` forwards the invoke's own byo-verified bearer as
|
|
126
123
|
* this server's per-turn credential (guuey#179) — see the field doc.
|
|
127
124
|
*/
|
|
@@ -149,8 +146,23 @@ const ExternalMcp = z
|
|
|
149
146
|
* which app receives its token); FIRST-PARTY USE ONLY for now — gate this
|
|
150
147
|
* (URL allowlist or console warning) before opening it to arbitrary
|
|
151
148
|
* builders. Mutually exclusive with `federate: true`.
|
|
149
|
+
*
|
|
150
|
+
* `'oauth'` — the FOURTH mode (guuey#178): the server is a third-party
|
|
151
|
+
* MCP whose end users must sign in with the server's own OAuth
|
|
152
|
+
* authorization server (RFC 9728 / 8414 discovery, PKCE, RFC 8707
|
|
153
|
+
* resource). guuey runs the dance ONCE per (user, server) from a
|
|
154
|
+
* consent card in the chat, seals the token in the platform's
|
|
155
|
+
* credential broker, and the agent's calls to this server go through
|
|
156
|
+
* the broker's gateway (`https://mcp.<apex>/brokered/<appId>/<name>/`),
|
|
157
|
+
* which injects the user's token per call — the agent pod never holds
|
|
158
|
+
* the third-party token. The deploy-controller lowers this entry by
|
|
159
|
+
* setting {@link mcpResourceUrl} to that route while `url` keeps the
|
|
160
|
+
* upstream. Deploy-only: `guuey dev` has no lowered snapshot. Mutually
|
|
161
|
+
* exclusive with `federate: true` (the broker route IS the federation
|
|
162
|
+
* target) and with a declared `authorization` header (the broker
|
|
163
|
+
* injects the only Authorization this server ever sees).
|
|
152
164
|
*/
|
|
153
|
-
credential: z.
|
|
165
|
+
credential: z.enum(['caller', 'oauth']).optional(),
|
|
154
166
|
/** Static headers forwarded on every request. Values may use `${env.NAME}` placeholders. */
|
|
155
167
|
headers: HeadersSchema.optional(),
|
|
156
168
|
/** Local dev-loop port (`guuey dev`) this MCP is served on for name→localhost URL resolution. */
|
|
@@ -170,6 +182,13 @@ const ExternalMcp = z
|
|
|
170
182
|
})
|
|
171
183
|
.refine((v) => !(v.credential === 'caller' && v.federate === true), {
|
|
172
184
|
message: "credential: 'caller' cannot be combined with federate: true — the forwarded caller bearer IS the credential; a minted federation token would shadow it",
|
|
185
|
+
})
|
|
186
|
+
.refine((v) => !(v.credential === 'oauth' && v.federate === true), {
|
|
187
|
+
message: "credential: 'oauth' cannot be combined with federate: true — the brokered gateway route IS this server's federation target; the pod mints for it automatically",
|
|
188
|
+
})
|
|
189
|
+
.refine((v) => !(v.credential === 'oauth' &&
|
|
190
|
+
Object.keys(v.headers ?? {}).some((h) => h.toLowerCase() === 'authorization')), {
|
|
191
|
+
message: "credential: 'oauth' cannot be combined with an `authorization` header — the credential broker injects the user's OAuth token as the only Authorization this server sees",
|
|
173
192
|
});
|
|
174
193
|
/**
|
|
175
194
|
* A single MCP server entry inside `agent.mcpServers`.
|
|
@@ -177,13 +196,16 @@ const ExternalMcp = z
|
|
|
177
196
|
* Discriminated union on `kind` — one slot per hosting mode:
|
|
178
197
|
* - `colocated` — guuey-managed HTTP child inside the agent pod
|
|
179
198
|
* - `hosted` — guuey-hosted registry MCP (Starter+)
|
|
180
|
-
* - `
|
|
181
|
-
*
|
|
199
|
+
* - `external` — reached by URL: builder-hosted (plain / federated /
|
|
200
|
+
* caller-forwarded) or third-party OAuth (`credential: 'oauth'`)
|
|
201
|
+
*
|
|
202
|
+
* (`kind: 'proxied'` — the pre-registration placeholder the mcp-proxy broker
|
|
203
|
+
* once reserved — is gone: `credential: 'oauth'` on an `external` entry IS
|
|
204
|
+
* that arm, guuey#178 D1.)
|
|
182
205
|
*/
|
|
183
206
|
const McpServerSchema = z.discriminatedUnion('kind', [
|
|
184
207
|
ColocatedMcp,
|
|
185
208
|
HostedMcp,
|
|
186
|
-
ProxiedMcp,
|
|
187
209
|
ExternalMcp,
|
|
188
210
|
]);
|
|
189
211
|
/**
|
|
@@ -383,8 +405,8 @@ export const AgentSectionV1 = z.strictObject({
|
|
|
383
405
|
* (valid only under the `ggui` key):
|
|
384
406
|
* - `'colocated'` — guuey-managed HTTP child inside the agent pod
|
|
385
407
|
* - `'hosted'` — guuey-hosted registry MCP (Starter+)
|
|
386
|
-
* - `'
|
|
387
|
-
*
|
|
408
|
+
* - `'external'` — reached by URL (plain / federated / caller-forwarded /
|
|
409
|
+
* third-party OAuth via `credential: 'oauth'`)
|
|
388
410
|
*/
|
|
389
411
|
mcpServers: z
|
|
390
412
|
.record(z.string().min(1), McpServerEntrySchema)
|
|
@@ -564,33 +586,6 @@ export function validateColocatedServerNames(agent) {
|
|
|
564
586
|
}
|
|
565
587
|
return violations;
|
|
566
588
|
}
|
|
567
|
-
// ── No-proxied-servers validation (deploy-time contract enforcement) ────────
|
|
568
|
-
//
|
|
569
|
-
// `kind: 'proxied'` keeps its schema arm (the documented mcp-proxy
|
|
570
|
-
// credential-broker deferral — wires when the 2nd stored-creds customer signs
|
|
571
|
-
// up) but has NO runtime support today: a pod that booted one would silently
|
|
572
|
-
// miss those tools. `validateNoProxiedServers` is the synchronous deploy-time
|
|
573
|
-
// pre-flight (mirrors `validateColocatedServerNames`'s shape) that rejects it
|
|
574
|
-
// with a fast, actionable error. The deploy-controller's
|
|
575
|
-
// `resolveMcpServersInSnapshot` carries the authoritative backstop throw for
|
|
576
|
-
// code deploys, which never pass through this validator.
|
|
577
|
-
/**
|
|
578
|
-
* Validate that no `agent.mcpServers` entry uses `kind: 'proxied'`. Returns a
|
|
579
|
-
* list of human-readable violation messages (empty = clean), one per proxied
|
|
580
|
-
* entry, naming the server so the builder knows which ref to change.
|
|
581
|
-
*/
|
|
582
|
-
export function validateNoProxiedServers(agent) {
|
|
583
|
-
const violations = [];
|
|
584
|
-
const servers = agent?.mcpServers;
|
|
585
|
-
if (!servers)
|
|
586
|
-
return violations;
|
|
587
|
-
for (const [name, server] of declaredServerEntries(servers)) {
|
|
588
|
-
if (server.kind === 'proxied') {
|
|
589
|
-
violations.push(`MCP server "${name}": kind 'proxied' (the mcp-proxy credential broker) is not yet supported — use 'external', 'hosted', or 'colocated'.`);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
return violations;
|
|
593
|
-
}
|
|
594
589
|
// ── Reserved platform MCP server names (deploy-time reservation) ────────────
|
|
595
590
|
//
|
|
596
591
|
// Some `mcpServers` map keys are OWNED by the platform: the runtime splices a
|
|
@@ -633,7 +628,7 @@ export const RESERVED_MCP_SERVER_NAMES = [
|
|
|
633
628
|
* reserved, not just one hosting mode; a builder must not shadow it as
|
|
634
629
|
* `colocated`, `external`, or anything else). Returns a list of human-readable
|
|
635
630
|
* violation messages (empty = clean), one per reserved entry. Mirrors
|
|
636
|
-
* {@link
|
|
631
|
+
* {@link validateColocatedServerNames}'s shape.
|
|
637
632
|
*/
|
|
638
633
|
export function validateReservedServerNames(agent) {
|
|
639
634
|
const violations = [];
|
package/dist/loader.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export declare const DEFAULT_FIND_MAX_DEPTH = 8;
|
|
|
13
13
|
export declare function findGuueyJson(startDir?: string, maxDepth?: number): string | null;
|
|
14
14
|
/**
|
|
15
15
|
* Read + parse `guuey.json` from `path`. Throws if the file is missing,
|
|
16
|
-
* unreadable, malformed JSON,
|
|
16
|
+
* unreadable, malformed JSON, declares a `schema` version this package
|
|
17
|
+
* cannot honor (`GuueyJsonSchemaError` — `SCHEMA_TOO_NEW` / `SCHEMA_UNSUPPORTED`,
|
|
18
|
+
* see `assertSupportedGuueyJsonSchema`), or fails schema validation.
|
|
17
19
|
*
|
|
18
20
|
* Does NOT resolve `agent.systemPrompt.file` references. Use
|
|
19
21
|
* {@link loadGuueyJson} for file resolution.
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,WAAW,
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,MAAsB,EAChC,QAAQ,GAAE,MAA+B,GACxC,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAgB3D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAIvE;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,GAAG,EAAE,WAAW,CAAC;IACjB;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAI7D;AAyCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAM1E"}
|
package/dist/loader.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
23
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
24
|
-
import { GUUEY_JSON_FILENAME, parseGuueyJson, } from './schema.js';
|
|
24
|
+
import { GUUEY_JSON_FILENAME, assertSupportedGuueyJsonSchema, parseGuueyJson, } from './schema.js';
|
|
25
25
|
/** How many parent directories `findGuueyJson` will walk by default. */
|
|
26
26
|
export const DEFAULT_FIND_MAX_DEPTH = 8;
|
|
27
27
|
/**
|
|
@@ -48,7 +48,9 @@ export function findGuueyJson(startDir = process.cwd(), maxDepth = DEFAULT_FIND_
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Read + parse `guuey.json` from `path`. Throws if the file is missing,
|
|
51
|
-
* unreadable, malformed JSON,
|
|
51
|
+
* unreadable, malformed JSON, declares a `schema` version this package
|
|
52
|
+
* cannot honor (`GuueyJsonSchemaError` — `SCHEMA_TOO_NEW` / `SCHEMA_UNSUPPORTED`,
|
|
53
|
+
* see `assertSupportedGuueyJsonSchema`), or fails schema validation.
|
|
52
54
|
*
|
|
53
55
|
* Does NOT resolve `agent.systemPrompt.file` references. Use
|
|
54
56
|
* {@link loadGuueyJson} for file resolution.
|
|
@@ -66,6 +68,9 @@ export function readGuueyJsonFile(path) {
|
|
|
66
68
|
const msg = err instanceof Error ? err.message : String(err);
|
|
67
69
|
throw new Error(`guuey.json at ${path} is not valid JSON: ${msg}`);
|
|
68
70
|
}
|
|
71
|
+
// Version gate BEFORE the shape parse: a too-new document must say
|
|
72
|
+
// "upgrade", not `at "schema": expected "1"` (guuey#248 b2).
|
|
73
|
+
assertSupportedGuueyJsonSchema(json);
|
|
69
74
|
return parseGuueyJson(json);
|
|
70
75
|
}
|
|
71
76
|
/**
|
package/dist/schema.d.ts
CHANGED
|
@@ -41,6 +41,85 @@ import { z } from 'zod';
|
|
|
41
41
|
import { type GuueyAgent } from './agent.js';
|
|
42
42
|
import { type GuueyApp } from './app.js';
|
|
43
43
|
import { type GuueyGguiSection } from './ggui.js';
|
|
44
|
+
/**
|
|
45
|
+
* The ONE `guuey.json` `schema` value this package understands — the
|
|
46
|
+
* schema-version stance (guuey#248 b2):
|
|
47
|
+
*
|
|
48
|
+
* - The root `schema` is a decimal integer string (`"1"`, `"2"`, …), bumped
|
|
49
|
+
* only on a change that an older reader cannot interpret correctly.
|
|
50
|
+
* - A document declaring a NEWER schema than this constant is refused
|
|
51
|
+
* everywhere: the CLI (`SCHEMA_TOO_NEW` — upgrade `@guuey/cli`) and the
|
|
52
|
+
* reconcile / deploy-trigger APIs (`400 SCHEMA_UNSUPPORTED`). Never
|
|
53
|
+
* "best-effort parse what we recognize" — the unknown half is precisely
|
|
54
|
+
* the half that matters.
|
|
55
|
+
* - A document declaring an OLDER schema is accepted only when a migration
|
|
56
|
+
* to this version exists. Today there is exactly one version and no
|
|
57
|
+
* migrations, so the rule collapses to equal-or-refuse; when `"2"` ships,
|
|
58
|
+
* the `1 → 2` migration lands in this package and BOTH sides (CLI + API)
|
|
59
|
+
* pick it up through {@link classifyGuueyJsonSchema}.
|
|
60
|
+
*
|
|
61
|
+
* The CLI and the platform API compare against the SAME constant (both
|
|
62
|
+
* consume this package), so "the CLI accepted it but the API refused it"
|
|
63
|
+
* can only ever mean a version skew between the two — which is exactly the
|
|
64
|
+
* message the API's refusal names.
|
|
65
|
+
*/
|
|
66
|
+
export declare const SUPPORTED_GUUEY_JSON_SCHEMA = "1";
|
|
67
|
+
/**
|
|
68
|
+
* Where a raw `guuey.json` document's `schema` sits relative to
|
|
69
|
+
* {@link SUPPORTED_GUUEY_JSON_SCHEMA}:
|
|
70
|
+
*
|
|
71
|
+
* - `supported` — equal (or an older version a migration exists for; none
|
|
72
|
+
* today).
|
|
73
|
+
* - `newer` — a later version than this reader knows: refuse + upgrade.
|
|
74
|
+
* - `older` — an earlier version with NO migration: refuse.
|
|
75
|
+
* - `invalid` — absent, not a string, or not a decimal integer string; the
|
|
76
|
+
* full schema parse reports the precise issue, this verdict only says the
|
|
77
|
+
* version-gate cannot even compare.
|
|
78
|
+
*/
|
|
79
|
+
export type GuueyJsonSchemaVerdict = {
|
|
80
|
+
kind: 'supported';
|
|
81
|
+
found: string;
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'newer';
|
|
84
|
+
found: string;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'older';
|
|
87
|
+
found: string;
|
|
88
|
+
} | {
|
|
89
|
+
kind: 'invalid';
|
|
90
|
+
found: string | undefined;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Classify a raw (JSON-decoded, NOT yet schema-parsed) document's root
|
|
94
|
+
* `schema` against {@link SUPPORTED_GUUEY_JSON_SCHEMA}. Pure; never throws.
|
|
95
|
+
* Run it BEFORE `parseGuueyJson` so a too-new document gets the "upgrade
|
|
96
|
+
* your CLI" face instead of zod's `expected "1"` at the first field.
|
|
97
|
+
*/
|
|
98
|
+
export declare function classifyGuueyJsonSchema(raw: unknown): GuueyJsonSchemaVerdict;
|
|
99
|
+
/**
|
|
100
|
+
* Thrown by {@link assertSupportedGuueyJsonSchema} — carries the code the
|
|
101
|
+
* CLI prints and the API maps to its 400 (`SCHEMA_TOO_NEW` = upgrade the
|
|
102
|
+
* reader; `SCHEMA_UNSUPPORTED` = an older version no migration exists for).
|
|
103
|
+
* The message already names the found + supported versions and the remedy.
|
|
104
|
+
*/
|
|
105
|
+
export declare class GuueyJsonSchemaError extends Error {
|
|
106
|
+
readonly code: 'SCHEMA_TOO_NEW' | 'SCHEMA_UNSUPPORTED';
|
|
107
|
+
readonly found: string;
|
|
108
|
+
constructor(code: 'SCHEMA_TOO_NEW' | 'SCHEMA_UNSUPPORTED', found: string, message: string);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Refuse a document whose `schema` this reader cannot honor. `invalid`
|
|
112
|
+
* verdicts pass through untouched — the schema parse that follows reports
|
|
113
|
+
* the precise issue (`at "schema": expected "1"`), which is the right face
|
|
114
|
+
* for a typo; this gate exists for the version SKEW cases only.
|
|
115
|
+
*
|
|
116
|
+
* Runs inside `readGuueyJsonFile` / `loadGuueyJson` (every CLI read of a
|
|
117
|
+
* checked-in file — `deploy`, `dev`, `agent apply`, …) and in the platform's
|
|
118
|
+
* reconcile + deploy-trigger handlers, so both ends of the wire refuse the
|
|
119
|
+
* same documents for the same reason. The message names the remedy for the
|
|
120
|
+
* only reader outside the platform: the CLI.
|
|
121
|
+
*/
|
|
122
|
+
export declare function assertSupportedGuueyJsonSchema(raw: unknown): void;
|
|
44
123
|
/**
|
|
45
124
|
* Top-level guuey.json v1 schema.
|
|
46
125
|
*
|
|
@@ -86,9 +165,6 @@ export declare const GuueyJsonV1: z.ZodObject<{
|
|
|
86
165
|
server: z.ZodOptional<z.ZodString>;
|
|
87
166
|
source: z.ZodOptional<z.ZodString>;
|
|
88
167
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
89
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
90
|
-
kind: z.ZodLiteral<"proxied">;
|
|
91
|
-
connection: z.ZodString;
|
|
92
168
|
}, z.core.$strict>, z.ZodObject<{
|
|
93
169
|
kind: z.ZodLiteral<"external">;
|
|
94
170
|
url: z.ZodURL;
|
|
@@ -97,7 +173,10 @@ export declare const GuueyJsonV1: z.ZodObject<{
|
|
|
97
173
|
sse: "sse";
|
|
98
174
|
}>>;
|
|
99
175
|
federate: z.ZodOptional<z.ZodBoolean>;
|
|
100
|
-
credential: z.ZodOptional<z.
|
|
176
|
+
credential: z.ZodOptional<z.ZodEnum<{
|
|
177
|
+
caller: "caller";
|
|
178
|
+
oauth: "oauth";
|
|
179
|
+
}>>;
|
|
101
180
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
102
181
|
devPort: z.ZodOptional<z.ZodNumber>;
|
|
103
182
|
mcpResourceUrl: z.ZodOptional<z.ZodURL>;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmDtB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAG3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAExD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,WAAW,CAAC,SAAS,CAAC,CAE1C"}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,sBAAsB,CAY5E;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB;aAC7C,KAAK,EAAE,MAAM;gBADb,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,EAC7C,KAAK,EAAE,MAAM,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAkBjE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmDtB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAG3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAExD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,WAAW,CAAC,SAAS,CAAC,CAE1C"}
|
package/dist/schema.js
CHANGED
|
@@ -41,6 +41,87 @@ import { z } from 'zod';
|
|
|
41
41
|
import { AgentSectionV1 } from './agent.js';
|
|
42
42
|
import { AppSectionV1 } from './app.js';
|
|
43
43
|
import { GguiSectionV1 } from './ggui.js';
|
|
44
|
+
/**
|
|
45
|
+
* The ONE `guuey.json` `schema` value this package understands — the
|
|
46
|
+
* schema-version stance (guuey#248 b2):
|
|
47
|
+
*
|
|
48
|
+
* - The root `schema` is a decimal integer string (`"1"`, `"2"`, …), bumped
|
|
49
|
+
* only on a change that an older reader cannot interpret correctly.
|
|
50
|
+
* - A document declaring a NEWER schema than this constant is refused
|
|
51
|
+
* everywhere: the CLI (`SCHEMA_TOO_NEW` — upgrade `@guuey/cli`) and the
|
|
52
|
+
* reconcile / deploy-trigger APIs (`400 SCHEMA_UNSUPPORTED`). Never
|
|
53
|
+
* "best-effort parse what we recognize" — the unknown half is precisely
|
|
54
|
+
* the half that matters.
|
|
55
|
+
* - A document declaring an OLDER schema is accepted only when a migration
|
|
56
|
+
* to this version exists. Today there is exactly one version and no
|
|
57
|
+
* migrations, so the rule collapses to equal-or-refuse; when `"2"` ships,
|
|
58
|
+
* the `1 → 2` migration lands in this package and BOTH sides (CLI + API)
|
|
59
|
+
* pick it up through {@link classifyGuueyJsonSchema}.
|
|
60
|
+
*
|
|
61
|
+
* The CLI and the platform API compare against the SAME constant (both
|
|
62
|
+
* consume this package), so "the CLI accepted it but the API refused it"
|
|
63
|
+
* can only ever mean a version skew between the two — which is exactly the
|
|
64
|
+
* message the API's refusal names.
|
|
65
|
+
*/
|
|
66
|
+
export const SUPPORTED_GUUEY_JSON_SCHEMA = '1';
|
|
67
|
+
/**
|
|
68
|
+
* Classify a raw (JSON-decoded, NOT yet schema-parsed) document's root
|
|
69
|
+
* `schema` against {@link SUPPORTED_GUUEY_JSON_SCHEMA}. Pure; never throws.
|
|
70
|
+
* Run it BEFORE `parseGuueyJson` so a too-new document gets the "upgrade
|
|
71
|
+
* your CLI" face instead of zod's `expected "1"` at the first field.
|
|
72
|
+
*/
|
|
73
|
+
export function classifyGuueyJsonSchema(raw) {
|
|
74
|
+
const found = raw !== null && typeof raw === 'object' && 'schema' in raw
|
|
75
|
+
? raw.schema
|
|
76
|
+
: undefined;
|
|
77
|
+
if (typeof found !== 'string' || !/^[1-9][0-9]*$/.test(found)) {
|
|
78
|
+
return { kind: 'invalid', found: typeof found === 'string' ? found : undefined };
|
|
79
|
+
}
|
|
80
|
+
const have = Number.parseInt(SUPPORTED_GUUEY_JSON_SCHEMA, 10);
|
|
81
|
+
const got = Number.parseInt(found, 10);
|
|
82
|
+
if (got === have)
|
|
83
|
+
return { kind: 'supported', found };
|
|
84
|
+
return got > have ? { kind: 'newer', found } : { kind: 'older', found };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Thrown by {@link assertSupportedGuueyJsonSchema} — carries the code the
|
|
88
|
+
* CLI prints and the API maps to its 400 (`SCHEMA_TOO_NEW` = upgrade the
|
|
89
|
+
* reader; `SCHEMA_UNSUPPORTED` = an older version no migration exists for).
|
|
90
|
+
* The message already names the found + supported versions and the remedy.
|
|
91
|
+
*/
|
|
92
|
+
export class GuueyJsonSchemaError extends Error {
|
|
93
|
+
code;
|
|
94
|
+
found;
|
|
95
|
+
constructor(code, found, message) {
|
|
96
|
+
super(message);
|
|
97
|
+
this.code = code;
|
|
98
|
+
this.found = found;
|
|
99
|
+
this.name = 'GuueyJsonSchemaError';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Refuse a document whose `schema` this reader cannot honor. `invalid`
|
|
104
|
+
* verdicts pass through untouched — the schema parse that follows reports
|
|
105
|
+
* the precise issue (`at "schema": expected "1"`), which is the right face
|
|
106
|
+
* for a typo; this gate exists for the version SKEW cases only.
|
|
107
|
+
*
|
|
108
|
+
* Runs inside `readGuueyJsonFile` / `loadGuueyJson` (every CLI read of a
|
|
109
|
+
* checked-in file — `deploy`, `dev`, `agent apply`, …) and in the platform's
|
|
110
|
+
* reconcile + deploy-trigger handlers, so both ends of the wire refuse the
|
|
111
|
+
* same documents for the same reason. The message names the remedy for the
|
|
112
|
+
* only reader outside the platform: the CLI.
|
|
113
|
+
*/
|
|
114
|
+
export function assertSupportedGuueyJsonSchema(raw) {
|
|
115
|
+
const verdict = classifyGuueyJsonSchema(raw);
|
|
116
|
+
if (verdict.kind === 'newer') {
|
|
117
|
+
throw new GuueyJsonSchemaError('SCHEMA_TOO_NEW', verdict.found, `guuey.json declares schema "${verdict.found}", but this reader understands schema "${SUPPORTED_GUUEY_JSON_SCHEMA}" only. ` +
|
|
118
|
+
'Upgrade the tool reading it — for the CLI: npm i -g @guuey/cli@latest — and re-run.');
|
|
119
|
+
}
|
|
120
|
+
if (verdict.kind === 'older') {
|
|
121
|
+
throw new GuueyJsonSchemaError('SCHEMA_UNSUPPORTED', verdict.found, `guuey.json declares schema "${verdict.found}", but no migration to schema "${SUPPORTED_GUUEY_JSON_SCHEMA}" exists — ` +
|
|
122
|
+
`set "schema": "${SUPPORTED_GUUEY_JSON_SCHEMA}" and update the document to the current shape.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
44
125
|
/**
|
|
45
126
|
* Top-level guuey.json v1 schema.
|
|
46
127
|
*
|
|
@@ -54,7 +135,7 @@ import { GguiSectionV1 } from './ggui.js';
|
|
|
54
135
|
* Re-exports the sub-section types for consumer convenience.
|
|
55
136
|
*/
|
|
56
137
|
export const GuueyJsonV1 = z.strictObject({
|
|
57
|
-
schema: z.literal(
|
|
138
|
+
schema: z.literal(SUPPORTED_GUUEY_JSON_SCHEMA),
|
|
58
139
|
/** Stable agent identifier minted by the control plane on first `guuey create`. */
|
|
59
140
|
appId: z.string().min(1).max(128).optional(),
|
|
60
141
|
/** Workspace the project lives under. Optional — personal apps + freshly-linked apps. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guuey/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Open-source schemas + loaders for the three guuey config files: agent.json (declarative agent definition), guuey.json (hosted-deploy overlay), and the helper types around them. Consumed by @guuey/cli, the guuey backend, and devs writing their own agent or MCP server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|