@aotter/mantle 0.2.0-alpha.10 → 0.2.0-alpha.11
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/auth/adapter.js +1 -28
- package/dist/auth/admin.js +2 -5
- package/dist/auth/caller.js +0 -27
- package/dist/auth/consent.js +100 -0
- package/dist/auth/hardening.js +0 -7
- package/dist/auth/identity.js +0 -11
- package/dist/auth/oauth.js +0 -32
- package/dist/auth/resource-seeds.js +0 -2
- package/dist/auth/route-http.js +33 -0
- package/dist/auth/routes.js +5 -151
- package/dist/build/compiler.d.ts +12 -611
- package/dist/build/compiler.js +3 -7029
- package/dist/bun/assets.js +0 -2
- package/dist/domain/adminOrder.js +0 -2
- package/dist/domain/columns.js +0 -15
- package/dist/domain/describe/chunk-SGMRVDGE.js +1 -0
- package/dist/domain/describe/index.js +1 -115
- package/dist/domain/describe/target-hash.js +1 -12
- package/dist/domain/entry.js +0 -14
- package/dist/domain/generated-tools.js +0 -1
- package/dist/domain/input.js +0 -2
- package/dist/domain/paging.js +0 -17
- package/dist/domain/recipes.js +0 -2
- package/dist/domain/userReferences.js +0 -1
- package/dist/domain/wireScalars.js +0 -6
- package/dist/infrastructure/adminEntries.js +9 -6
- package/dist/infrastructure/adminPaging.js +0 -2
- package/dist/infrastructure/artifacts.js +0 -4
- package/dist/infrastructure/authSchema.js +0 -9
- package/dist/infrastructure/bundle.js +0 -29
- package/dist/infrastructure/cli.js +0 -7
- package/dist/infrastructure/generate.js +0 -14
- package/dist/infrastructure/generateAuth.js +1 -9
- package/dist/infrastructure/help.js +0 -5
- package/dist/infrastructure/init.js +0 -4
- package/dist/infrastructure/inputs.js +0 -9
- package/dist/infrastructure/mounts.js +0 -3
- package/dist/infrastructure/wrangler.js +0 -6
- package/dist/kernel/authorization.js +0 -1
- package/dist/kernel/binds.js +1 -11
- package/dist/kernel/caller-identity.d.ts +0 -3
- package/dist/kernel/caller-identity.js +0 -15
- package/dist/kernel/caller-mode.d.ts +4 -0
- package/dist/kernel/caller-mode.js +14 -0
- package/dist/kernel/contracts.d.ts +1 -9
- package/dist/kernel/contracts.js +1 -42
- package/dist/kernel/cron.js +0 -4
- package/dist/kernel/dialect/common.js +0 -3
- package/dist/kernel/dialect/postgres.js +0 -2
- package/dist/kernel/dialect/sqlite.js +1 -6
- package/dist/kernel/drain.js +2 -8
- package/dist/kernel/driver/bun-sqlite.js +0 -4
- package/dist/kernel/driver/d1.js +0 -1
- package/dist/kernel/driver/postgres.js +0 -15
- package/dist/kernel/driver/transaction-context.js +0 -1
- package/dist/kernel/email.js +0 -4
- package/dist/kernel/host-access.js +2 -3
- package/dist/kernel/host-caller.d.ts +1 -1
- package/dist/kernel/host-caller.js +3 -6
- package/dist/kernel/json-body.js +0 -2
- package/dist/kernel/mcp-apps.js +0 -5
- package/dist/kernel/mcp-call.js +0 -1
- package/dist/kernel/mcp-handler.js +2 -26
- package/dist/kernel/mcp.js +0 -5
- package/dist/kernel/media.js +0 -18
- package/dist/kernel/paging.js +1 -9
- package/dist/kernel/publishing.js +1 -2
- package/dist/kernel/r2-media.js +0 -7
- package/dist/kernel/rest-dispatch.js +0 -1
- package/dist/kernel/rest-router.js +2 -3
- package/dist/kernel/rest.js +0 -3
- package/dist/kernel/scalars.js +1 -9
- package/dist/kernel/search.js +0 -1
- package/dist/kernel/site.js +0 -2
- package/dist/kernel/store.js +1 -26
- package/dist/kernel/surface-failure.d.ts +10 -0
- package/dist/kernel/surface-failure.js +29 -0
- package/dist/kernel/surface.js +0 -3
- package/dist/kernel/sweep.js +0 -1
- package/dist/kernel/translations.js +0 -1
- package/dist/kernel/user-caller.js +0 -6
- package/dist/kernel/user-fields.d.ts +1 -1
- package/dist/kernel/user-fields.js +1 -11
- package/dist/kernel/validation.js +0 -8
- package/dist/kernel/web-gateway.js +0 -2
- package/dist/kernel/web-head.js +0 -1
- package/dist/kernel/web-index.js +0 -1
- package/dist/kernel/web-response.js +0 -1
- package/dist/kernel/web-serve.js +97 -0
- package/dist/kernel/web.d.ts +1 -1
- package/dist/kernel/web.js +10 -91
- package/dist/kernel/where.js +0 -6
- package/dist/usecase/adminBindings.js +28 -21
- package/dist/usecase/adminClient.js +0 -21
- package/dist/usecase/adminDescriptors.js +2 -9
- package/dist/usecase/generate.js +0 -6
- package/dist/usecase/handlers.js +0 -3
- package/dist/usecase/mcp-media.js +0 -1
- package/dist/usecase/mcp.js +0 -4
- package/dist/usecase/paging.js +0 -2
- package/dist/usecase/publishing.js +0 -4
- package/dist/usecase/rest.js +0 -1
- package/dist/usecase/site.js +0 -1
- package/dist/usecase/stores.js +1 -13
- package/dist/usecase/tool-version.js +0 -3
- package/dist/usecase/translations.js +0 -2
- package/dist/usecase/validation.js +0 -20
- package/docs/examples/README.md +1 -1
- package/docs/examples/app-shell/package.json +3 -3
- package/docs/examples/app-shell/src/routes.ts +3 -1
- package/docs/examples/app-shell/src/service.ts +2 -1
- package/docs/handbook/cloudflare/service-entry.md +3 -0
- package/docs/handbook/examples/hub.md +1 -1
- package/docs/handbook/guides/agent-setup.md +4 -4
- package/docs/handbook/guides/public-pages.md +6 -0
- package/docs/handbook/reference/admin-descriptors.md +4 -1
- package/docs/handbook/reference/schema.md +1 -1
- package/docs/handbook/releases/index.md +3 -3
- package/package.json +2 -2
- package/skills/README.md +1 -1
- package/dist/domain/describe/chunk-ZVXBGP7Y.js +0 -424
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
// src/build/spec/domain/model/ManifestGrammar.ts
|
|
2
|
-
function resolveLocalizedText(value, preferred, canonical2) {
|
|
3
|
-
if (value == null) return null;
|
|
4
|
-
if (typeof value === "string") return value;
|
|
5
|
-
if (Object.prototype.hasOwnProperty.call(value, preferred)) {
|
|
6
|
-
return value[preferred];
|
|
7
|
-
}
|
|
8
|
-
if (canonical2 && Object.prototype.hasOwnProperty.call(value, canonical2)) {
|
|
9
|
-
return value[canonical2];
|
|
10
|
-
}
|
|
11
|
-
const firstKey = Object.keys(value)[0];
|
|
12
|
-
return firstKey !== void 0 ? value[firstKey] : null;
|
|
13
|
-
}
|
|
14
|
-
var INPUT_RULE_KEYWORDS = ["dependentRequired", "anyOf", "if", "then", "else"];
|
|
15
|
-
|
|
16
|
-
// src/build/spec/domain/service/RootRefChain.ts
|
|
17
|
-
function lookupLocalRef(root, ref) {
|
|
18
|
-
if (typeof ref !== "string" || !ref.startsWith("#/")) return void 0;
|
|
19
|
-
let value = root;
|
|
20
|
-
try {
|
|
21
|
-
for (const key of decodeURIComponent(ref.slice(2)).split("/").map((key2) => key2.replace(/~1/g, "/").replace(/~0/g, "~"))) value = value && typeof value === "object" && Object.hasOwn(value, key) ? value[key] : void 0;
|
|
22
|
-
} catch {
|
|
23
|
-
return void 0;
|
|
24
|
-
}
|
|
25
|
-
return value && typeof value === "object" ? value : void 0;
|
|
26
|
-
}
|
|
27
|
-
function rootRefChain(input) {
|
|
28
|
-
const chain = [];
|
|
29
|
-
for (let schema = input; schema !== void 0; schema = lookupLocalRef(input, schema.$ref)) {
|
|
30
|
-
if (chain.includes(schema)) return void 0;
|
|
31
|
-
chain.push(schema);
|
|
32
|
-
if (schema.$ref === void 0) return chain;
|
|
33
|
-
}
|
|
34
|
-
return void 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/build/spec/domain/service/McpToolNaming.ts
|
|
38
|
-
function mcpToolNameSegment(name) {
|
|
39
|
-
return name.toLowerCase().replace(/-/g, "_");
|
|
40
|
-
}
|
|
41
|
-
function toolTitle(name) {
|
|
42
|
-
const words = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_.\s]+/g, " ").trim().toLowerCase();
|
|
43
|
-
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
44
|
-
}
|
|
45
|
-
var viewAnnotations = { readOnlyHint: true, destructiveHint: false };
|
|
46
|
-
function procedureAnnotations(p) {
|
|
47
|
-
const readOnlyHint = p.mcp?.readOnlyHint ?? false;
|
|
48
|
-
return { readOnlyHint, destructiveHint: !readOnlyHint && ("ref" in p.handler || overwrites(p.handler.sql.stmts)), ...p.mcp };
|
|
49
|
-
}
|
|
50
|
-
var overwrites = (node) => !!node && typeof node === "object" && ("DeleteStmt" in node || "UpdateStmt" in node || node.onConflictClause?.action === "ONCONFLICT_UPDATE" || Object.values(node).some(overwrites));
|
|
51
|
-
function mcpTools(plan, surface, locale = "en") {
|
|
52
|
-
const text = (t) => resolveLocalizedText(t, locale, "en") ?? void 0;
|
|
53
|
-
const tools = /* @__PURE__ */ new Map();
|
|
54
|
-
const add = (tool) => {
|
|
55
|
-
const prior = tools.get(tool.name);
|
|
56
|
-
if (prior && (prior.kind !== tool.kind || prior.source !== tool.source)) throw new TypeError(`MCP tool '${tool.name}' comes from both '${prior.source}' and '${tool.source}'.`);
|
|
57
|
-
tools.set(tool.name, tool);
|
|
58
|
-
};
|
|
59
|
-
for (const t of Object.values(plan.triggers)) {
|
|
60
|
-
if (t.source.kind !== "mcp" || t.source.surface !== surface) continue;
|
|
61
|
-
const p = plan.procedures[t.procedure];
|
|
62
|
-
const title = text(p.title);
|
|
63
|
-
add({
|
|
64
|
-
name: mcpToolNameSegment(t.procedure),
|
|
65
|
-
kind: "procedure",
|
|
66
|
-
source: t.procedure,
|
|
67
|
-
title: title ?? toolTitle(t.procedure),
|
|
68
|
-
description: text(p.description) ?? title ?? t.procedure,
|
|
69
|
-
inputSchema: p.input,
|
|
70
|
-
// structuredContent must be an object
|
|
71
|
-
...p.output.type === "object" ? { outputSchema: p.output } : {},
|
|
72
|
-
annotations: procedureAnnotations(p),
|
|
73
|
-
requires: p.requires
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
for (const [name, v] of Object.entries(plan.views)) {
|
|
77
|
-
if (v.surface !== surface) continue;
|
|
78
|
-
const chain = (v.input ? rootRefChain(v.input) ?? [v.input] : []).reverse();
|
|
79
|
-
const props = Object.assign({}, ...chain.map((s) => s.properties ?? {}));
|
|
80
|
-
const required = [...new Set(chain.flatMap((s) => s.required ?? []))];
|
|
81
|
-
if ("limit" in props || "cursor" in props) throw new TypeError(`View '${name}' declares an input named limit or cursor; the MCP tool reserves both for paging.`);
|
|
82
|
-
const rules = chain.map((s) => Object.fromEntries(INPUT_RULE_KEYWORDS.flatMap((k) => s[k] === void 0 ? [] : [[k, s[k]]]))).filter((r) => Object.keys(r).length);
|
|
83
|
-
const defs = v.input ? referencedDefs(v.input, props) : {};
|
|
84
|
-
const title = text(v.title);
|
|
85
|
-
add({
|
|
86
|
-
name: mcpToolNameSegment(name),
|
|
87
|
-
kind: "view",
|
|
88
|
-
source: name,
|
|
89
|
-
title: title ?? toolTitle(name),
|
|
90
|
-
description: text(v.description) ?? title ?? name,
|
|
91
|
-
inputSchema: {
|
|
92
|
-
type: "object",
|
|
93
|
-
properties: { ...props, limit: { type: "integer", minimum: 1, maximum: 500 }, cursor: { type: "string" } },
|
|
94
|
-
...required.length ? { required } : {},
|
|
95
|
-
...rules.length > 1 ? { allOf: rules } : rules[0],
|
|
96
|
-
...Object.keys(defs).length ? { $defs: defs } : {}
|
|
97
|
-
},
|
|
98
|
-
annotations: viewAnnotations,
|
|
99
|
-
requires: v.requires
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return [...tools.values()];
|
|
103
|
-
}
|
|
104
|
-
function referencedDefs(root, node, found = {}) {
|
|
105
|
-
if (!node || typeof node !== "object") return found;
|
|
106
|
-
const ref = node.$ref, [head, segment] = typeof ref === "string" && ref.startsWith("#/") ? decoded(ref.slice(2)).split("/") : [];
|
|
107
|
-
const key = head === "$defs" ? segment?.replace(/~1/g, "/").replace(/~0/g, "~") : void 0;
|
|
108
|
-
if (key !== void 0 && !Object.hasOwn(found, key) && root.$defs && Object.hasOwn(root.$defs, key)) {
|
|
109
|
-
found[key] = root.$defs[key];
|
|
110
|
-
referencedDefs(root, found[key], found);
|
|
111
|
-
}
|
|
112
|
-
for (const child of Object.values(node)) referencedDefs(root, child, found);
|
|
113
|
-
return found;
|
|
114
|
-
}
|
|
115
|
-
var decoded = (pointer) => {
|
|
116
|
-
try {
|
|
117
|
-
return decodeURIComponent(pointer);
|
|
118
|
-
} catch {
|
|
119
|
-
return "";
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// src/build/spec/domain/service/PlanFingerprint.ts
|
|
124
|
-
function canonical(v) {
|
|
125
|
-
if (Array.isArray(v)) return `[${v.map(canonical).join(",")}]`;
|
|
126
|
-
if (v && typeof v === "object")
|
|
127
|
-
return `{${Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(",")}}`;
|
|
128
|
-
return JSON.stringify(v) ?? "null";
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/domain/generated-tools.ts
|
|
132
|
-
var publishingTransitions = [["publish", ["draft"], "published"], ["unpublish", ["published", "archived"], "draft"], ["archive", ["draft", "published"], "archived"]];
|
|
133
|
-
var publishingInput = { type: "object", properties: { id: { type: "string", minLength: 1 }, expectedVersion: { type: "integer", minimum: 1 } }, required: ["id", "expectedVersion"], additionalProperties: false };
|
|
134
|
-
function mediaDefinitions() {
|
|
135
|
-
const define = (name, description, inputSchema, action, readOnlyHint = false) => ({ name, description, inputSchema, action, annotations: { readOnlyHint, destructiveHint: action === "delete", openWorldHint: true } });
|
|
136
|
-
const string = { type: "string" };
|
|
137
|
-
const texts = { alt: { type: "string", maxLength: 1e3 }, caption: { type: "string", maxLength: 1e3 } };
|
|
138
|
-
const input = (properties, required = []) => ({ type: "object", properties, required, additionalProperties: false });
|
|
139
|
-
return [
|
|
140
|
-
define("get_media_upload_policy", "Read declared image purpose MIME slots and byte caps before preparing variants or requesting an upload. Optimization runs locally, never in the host.", input({}), "policy", true),
|
|
141
|
-
define("create_media_upload", "Create short-lived PUT capabilities for image variants. Read site media purpose policy first; optimize images locally. Obtain attachment bytes yourself, then PUT each variant with its requiredHeaders and call commit_media_upload. Never store upload URLs as published images or ask the user to run terminal commands.", input({ filename: { type: "string", maxLength: 255 }, purpose: string, variants: { type: "array", minItems: 1, items: input({ mimeType: string, byteSize: { type: "integer", minimum: 1 }, role: { type: "string", enum: ["primary", "alternate", "fallback"] } }, ["mimeType", "byteSize", "role"]) }, ...texts }, ["filename", "purpose", "variants"]), "create"),
|
|
142
|
-
define("commit_media_upload", "Verify all uploaded variants and commit one permanent media asset. Store the returned asset id in fields referencing media_assets.id, never a temporary upload URL. Unknown or expired upload groups require a new create_media_upload.", input({ uploadGroupId: string, ...texts }, ["uploadGroupId"]), "commit"),
|
|
143
|
-
define("list_media_assets", "List committed image assets, newest first, with their permanent variants and alt/caption. Search matches id, alt or caption; reuse only a returned cursor.", input({ limit: { type: "integer", minimum: 1, maximum: 500 }, cursor: string, search: string }), "list", true),
|
|
144
|
-
define("get_media_asset", "Read one committed media asset and its permanent variant URLs.", input({ id: string }, ["id"]), "get", true),
|
|
145
|
-
define("update_media_asset", "Update committed asset alt or caption; omitted fields remain unchanged and empty strings clear text.", input({ id: string, ...texts }, ["id"]), "update"),
|
|
146
|
-
define("delete_media_asset", "Delete an asset's objects and library record. Check application references first. A partial failure can be retried using the same id.", input({ id: string }, ["id"]), "delete")
|
|
147
|
-
];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// src/domain/describe/targets.ts
|
|
151
|
-
function inspectSql(value) {
|
|
152
|
-
const schemas = /* @__PURE__ */ new Set(), readSchemas = /* @__PURE__ */ new Set();
|
|
153
|
-
const writes = [];
|
|
154
|
-
let readOnly = true;
|
|
155
|
-
const walk = (v) => {
|
|
156
|
-
if (!v || typeof v !== "object") return;
|
|
157
|
-
const n = v;
|
|
158
|
-
if (n.RangeVar?.mantle === "table") {
|
|
159
|
-
schemas.add(n.RangeVar.relname.toLowerCase());
|
|
160
|
-
readSchemas.add(n.RangeVar.relname.toLowerCase());
|
|
161
|
-
}
|
|
162
|
-
for (const kind of ["InsertStmt", "UpdateStmt", "DeleteStmt"]) if (n[kind]) {
|
|
163
|
-
readOnly = false;
|
|
164
|
-
if (n[kind].relation?.relname) writes.push({ schema: n[kind].relation.relname.toLowerCase(), operation: kind === "InsertStmt" ? "create" : kind === "UpdateStmt" ? "update" : "delete" });
|
|
165
|
-
if (n[kind].relation?.relname) {
|
|
166
|
-
schemas.add(n[kind].relation.relname.toLowerCase());
|
|
167
|
-
if (kind !== "InsertStmt") readSchemas.add(n[kind].relation.relname.toLowerCase());
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
Object.values(n).forEach(walk);
|
|
171
|
-
};
|
|
172
|
-
walk(value);
|
|
173
|
-
return { schemas: [...schemas].sort(), readSchemas: [...readSchemas].sort(), readOnly, writes };
|
|
174
|
-
}
|
|
175
|
-
function mergeRestrict(policies, conjunctive) {
|
|
176
|
-
return Object.fromEntries([...new Set(policies.flatMap((p) => Object.keys(p)))].sort().map((schema) => {
|
|
177
|
-
const rows = policies.map((p) => p[schema] ?? { terms: [] });
|
|
178
|
-
const terms = [...new Map(rows.flatMap((p) => p.terms).map((t) => [canonical(t), t])).values()].filter((t) => conjunctive || rows.every((p) => p.terms.some((v) => canonical(v) === canonical(t))));
|
|
179
|
-
const owner = rows.find((p) => p.owner)?.owner;
|
|
180
|
-
return [schema, { ...owner && (conjunctive || rows.every((p) => p.owner === owner)) ? { owner } : {}, terms }];
|
|
181
|
-
}));
|
|
182
|
-
}
|
|
183
|
-
function inspectBehavior(plan, ir, opaque, requires, restrict, writes) {
|
|
184
|
-
const inspected = inspectSql(ir);
|
|
185
|
-
if (writes) {
|
|
186
|
-
inspected.writes = [...writes];
|
|
187
|
-
inspected.schemas.push(...writes.map((w) => w.schema));
|
|
188
|
-
inspected.readSchemas.push(...writes.filter((w) => w.operation !== "create").map((w) => w.schema));
|
|
189
|
-
}
|
|
190
|
-
const dependencies = [], policies = [restrict ?? {}];
|
|
191
|
-
const procedures = /* @__PURE__ */ new Set(), hooks = /* @__PURE__ */ new Set();
|
|
192
|
-
const visit = (name) => {
|
|
193
|
-
if (procedures.has(name)) return;
|
|
194
|
-
procedures.add(name);
|
|
195
|
-
const p = plan.procedures[name];
|
|
196
|
-
if (!p) throw new TypeError(`Unknown dependency '${name}'.`);
|
|
197
|
-
dependencies.push({ procedure: name, declaration: p });
|
|
198
|
-
policies.push(p.restrict ?? {});
|
|
199
|
-
opaque ||= "ref" in p.handler;
|
|
200
|
-
const g = inspectSql("sql" in p.handler ? p.handler.sql.stmts : void 0);
|
|
201
|
-
inspected.schemas.push(...g.schemas);
|
|
202
|
-
inspected.readSchemas.push(...g.readSchemas);
|
|
203
|
-
inspected.writes.push(...g.writes);
|
|
204
|
-
inspected.readOnly &&= g.readOnly;
|
|
205
|
-
if (p.requires?.guard) visit(p.requires.guard.procedure);
|
|
206
|
-
};
|
|
207
|
-
if (requires?.guard) visit(requires.guard.procedure);
|
|
208
|
-
for (; ; ) {
|
|
209
|
-
const before = hooks.size;
|
|
210
|
-
for (const [id, t] of Object.entries(plan.triggers).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
211
|
-
if (t.source.kind !== "lifecycle" || hooks.has(id)) continue;
|
|
212
|
-
const source = t.source;
|
|
213
|
-
if (!opaque && !inspected.writes.some((w) => w.schema === source.schema.toLowerCase() && source.on.some((on) => on.endsWith(`_${w.operation}`)))) continue;
|
|
214
|
-
hooks.add(id);
|
|
215
|
-
dependencies.push({ trigger: id, source });
|
|
216
|
-
visit(t.procedure);
|
|
217
|
-
}
|
|
218
|
-
if (hooks.size === before) break;
|
|
219
|
-
}
|
|
220
|
-
const related = (names) => {
|
|
221
|
-
const all = new Set(opaque ? Object.keys(plan.schemas) : names);
|
|
222
|
-
for (const name of all) {
|
|
223
|
-
const parent = plan.schemas[name]?.translates?.parent.toLowerCase();
|
|
224
|
-
if (parent) all.add(parent);
|
|
225
|
-
}
|
|
226
|
-
return [...all].sort();
|
|
227
|
-
};
|
|
228
|
-
return {
|
|
229
|
-
...inspected,
|
|
230
|
-
schemas: related(inspected.schemas),
|
|
231
|
-
readSchemas: related(inspected.readSchemas),
|
|
232
|
-
opaque,
|
|
233
|
-
restrict: mergeRestrict(policies, true),
|
|
234
|
-
dependencies
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
var objectInput = { type: "object", properties: {} };
|
|
238
|
-
var pathTemplate = (path) => path.replace(/\{([^}]+)\}/g, ":$1");
|
|
239
|
-
var adminRoutes = [
|
|
240
|
-
"GET /me",
|
|
241
|
-
"GET /site",
|
|
242
|
-
"GET /collections",
|
|
243
|
-
"GET /views-manifest",
|
|
244
|
-
"GET /operations",
|
|
245
|
-
"GET /webmcp",
|
|
246
|
-
"POST /webmcp/:tool",
|
|
247
|
-
"GET /bootstrap",
|
|
248
|
-
"GET /views/:name/export",
|
|
249
|
-
"GET /views/:name",
|
|
250
|
-
"GET /view-batch",
|
|
251
|
-
"GET /developer/views/:name",
|
|
252
|
-
"POST /operations/:name",
|
|
253
|
-
"GET /developer-console",
|
|
254
|
-
"GET /collections/:name/statistics",
|
|
255
|
-
"GET /entries",
|
|
256
|
-
"GET /entries/export",
|
|
257
|
-
"GET /entries/:id",
|
|
258
|
-
"POST /entries",
|
|
259
|
-
"PATCH /entries/:id",
|
|
260
|
-
"POST /entries/:id/publish",
|
|
261
|
-
"POST /entries/:id/unpublish",
|
|
262
|
-
"POST /entries/:id/archive",
|
|
263
|
-
"DELETE /entries/:id",
|
|
264
|
-
"GET /site-settings",
|
|
265
|
-
"PATCH /site-settings",
|
|
266
|
-
"POST /media/uploads",
|
|
267
|
-
"POST /media/uploads/:groupId/commit",
|
|
268
|
-
"GET /media",
|
|
269
|
-
"GET /media/:id",
|
|
270
|
-
"PATCH /media/:id",
|
|
271
|
-
"DELETE /media/:id",
|
|
272
|
-
"GET /admin",
|
|
273
|
-
"GET /members",
|
|
274
|
-
"PATCH /admin/:id/role",
|
|
275
|
-
"POST /admin/invitations",
|
|
276
|
-
"DELETE /admin/invitations/:id"
|
|
277
|
-
];
|
|
278
|
-
function targetDefinitions(plan) {
|
|
279
|
-
const out = [];
|
|
280
|
-
const tools = [...mcpTools(plan, "public"), ...mcpTools(plan, "admin")];
|
|
281
|
-
for (const [kind, entries] of [["view", plan.views], ["procedure", plan.procedures]]) {
|
|
282
|
-
for (const [name, p] of Object.entries(entries).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
283
|
-
const view = kind === "view" ? plan.views[name] : void 0;
|
|
284
|
-
const proc = kind === "procedure" ? plan.procedures[name] : void 0;
|
|
285
|
-
const ir = view?.stmts ?? (proc && "sql" in proc.handler ? proc.handler.sql.stmts : void 0);
|
|
286
|
-
const inspected = inspectBehavior(plan, ir, !!proc && "ref" in proc.handler, p.requires, p.restrict);
|
|
287
|
-
const tool = tools.find((t) => t.kind === kind && t.source === name);
|
|
288
|
-
const title = resolveLocalizedText(p.title, "en", "en") ?? name;
|
|
289
|
-
const triggers = Object.entries(plan.triggers).filter(([, t]) => kind === "procedure" && t.procedure === name).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([id, t]) => ({ id, source: t.source }));
|
|
290
|
-
const surfaces = [.../* @__PURE__ */ new Set([
|
|
291
|
-
...view ? [view.surface, "admin", ...view.surface === "public" ? ["rest"] : []] : [],
|
|
292
|
-
...triggers.map((t) => t.source.kind === "mcp" ? t.source.surface : t.source.kind === "http" ? "rest" : t.source.kind)
|
|
293
|
-
])].sort();
|
|
294
|
-
if (!surfaces.length) surfaces.push("internal");
|
|
295
|
-
out.push({
|
|
296
|
-
id: `${kind}:${name}`,
|
|
297
|
-
name: tool?.name ?? mcpToolNameSegment(name),
|
|
298
|
-
kind,
|
|
299
|
-
surface: view?.surface ?? (surfaces.includes("public") || surfaces.includes("rest") ? "public" : surfaces.includes("admin") ? "admin" : "internal"),
|
|
300
|
-
surfaces,
|
|
301
|
-
triggers,
|
|
302
|
-
title,
|
|
303
|
-
description: resolveLocalizedText(p.description, "en", "en") ?? title,
|
|
304
|
-
inputSchema: tool?.inputSchema ?? view?.input ?? proc?.input ?? objectInput,
|
|
305
|
-
annotations: tool?.annotations ?? {},
|
|
306
|
-
requires: p.requires,
|
|
307
|
-
restrict: inspected.restrict,
|
|
308
|
-
schemas: inspected.schemas,
|
|
309
|
-
readSchemas: inspected.readSchemas,
|
|
310
|
-
readOnly: !inspected.opaque && inspected.readOnly,
|
|
311
|
-
opaque: inspected.opaque,
|
|
312
|
-
trustedOnly: !!proc?.trustedOnly,
|
|
313
|
-
variants: [],
|
|
314
|
-
behavior: { declaration: p, dependencies: inspected.dependencies }
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
const addRoute = (id, dependencies, inputSchema = objectInput) => {
|
|
319
|
-
const admin = id.startsWith("admin:");
|
|
320
|
-
out.push({
|
|
321
|
-
id,
|
|
322
|
-
name: id.replace(/[^a-zA-Z0-9_-]/g, "_"),
|
|
323
|
-
title: id,
|
|
324
|
-
description: id,
|
|
325
|
-
kind: admin ? "admin" : "rest",
|
|
326
|
-
surface: admin ? "admin" : "public",
|
|
327
|
-
surfaces: [admin ? "admin" : "rest"],
|
|
328
|
-
triggers: dependencies.flatMap((d) => d.triggers),
|
|
329
|
-
inputSchema,
|
|
330
|
-
annotations: {},
|
|
331
|
-
requires: admin ? { auth: { all: ["ctx.admin"] } } : dependencies[0]?.requires,
|
|
332
|
-
restrict: mergeRestrict(dependencies.map((d) => d.restrict), false),
|
|
333
|
-
schemas: [...new Set(dependencies.flatMap((d) => d.schemas))].sort(),
|
|
334
|
-
readSchemas: [...new Set(dependencies.flatMap((d) => d.readSchemas))].sort(),
|
|
335
|
-
readOnly: dependencies.length > 0 && dependencies.every((d) => d.readOnly) && (!admin || id.startsWith("admin:GET ") || id === "admin:POST /operations/:name" || id === "admin:POST /webmcp/:tool"),
|
|
336
|
-
opaque: dependencies.some((d) => d.opaque),
|
|
337
|
-
trustedOnly: dependencies.some((d) => d.trustedOnly),
|
|
338
|
-
variants: dependencies,
|
|
339
|
-
behavior: dependencies.map((d) => ({ id: d.id, behavior: d.behavior, surfaces: d.surfaces, triggers: d.triggers, input: d.inputSchema, requires: d.requires, restrict: d.restrict, trustedOnly: d.trustedOnly }))
|
|
340
|
-
});
|
|
341
|
-
};
|
|
342
|
-
const declarations = [...out];
|
|
343
|
-
const adminOperations = new Set(mcpTools(plan, "admin").filter((t) => t.kind === "procedure").map((t) => `procedure:${t.source}`));
|
|
344
|
-
for (const d of declarations) if (d.kind === "view" && d.surfaces.includes("public")) addRoute(`rest:GET /api/views/${d.id.slice(5)}`, [d], d.inputSchema);
|
|
345
|
-
for (const [, t] of Object.entries(plan.triggers).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) if (t.source.kind === "http") {
|
|
346
|
-
const d = declarations.find((d2) => d2.id === `procedure:${t.procedure}`);
|
|
347
|
-
addRoute(`rest:${t.source.method} ${pathTemplate(t.source.path)}`, [d], d.inputSchema);
|
|
348
|
-
}
|
|
349
|
-
const generated = [];
|
|
350
|
-
for (const [schema, s] of Object.entries(plan.schemas).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) if (s.publishing) for (const [verb, from, to] of publishingTransitions) {
|
|
351
|
-
addRoute(`procedure:${s.name}.${verb}`, [], publishingInput);
|
|
352
|
-
const d = out.at(-1);
|
|
353
|
-
const inspected = inspectBehavior(plan, void 0, false, void 0, void 0, [{ schema, operation: verb === "publish" ? "publish" : "update" }]);
|
|
354
|
-
Object.assign(d, {
|
|
355
|
-
kind: "procedure",
|
|
356
|
-
name: mcpToolNameSegment(`${verb}_${s.name}`),
|
|
357
|
-
surface: "admin",
|
|
358
|
-
surfaces: ["admin"],
|
|
359
|
-
requires: { auth: { all: ["ctx.admin"] } },
|
|
360
|
-
schemas: [.../* @__PURE__ */ new Set([schema, ...inspected.schemas])].sort(),
|
|
361
|
-
readSchemas: [.../* @__PURE__ */ new Set([schema, ...inspected.readSchemas])].sort(),
|
|
362
|
-
opaque: inspected.opaque,
|
|
363
|
-
behavior: { schema, verb, from, to, dependencies: inspected.dependencies }
|
|
364
|
-
});
|
|
365
|
-
generated.push(d);
|
|
366
|
-
}
|
|
367
|
-
for (const tool of mediaDefinitions()) {
|
|
368
|
-
addRoute(`admin:tool:${tool.name}`, [], tool.inputSchema);
|
|
369
|
-
const d = out.at(-1);
|
|
370
|
-
Object.assign(d, { name: tool.name, readOnly: tool.annotations.readOnlyHint, opaque: !tool.annotations.readOnlyHint, annotations: tool.annotations, behavior: { tool, schemas: plan.schemas } });
|
|
371
|
-
generated.push(d);
|
|
372
|
-
}
|
|
373
|
-
for (const route of adminRoutes) {
|
|
374
|
-
const deps = route.includes("/views/:") || route === "GET /view-batch" ? declarations.filter((d2) => d2.kind === "view" && (route.includes("/developer/") || plan.views[d2.id.slice(5)]?.surface === "admin")) : route.includes("/operations/:") ? declarations.filter((d2) => adminOperations.has(d2.id)) : route === "POST /webmcp/:tool" ? declarations.filter((d2) => mcpTools(plan, "admin").some((t) => `${t.kind}:${t.source}` === d2.id)).concat(generated) : [];
|
|
375
|
-
const generic = route.includes("/entries") || route.includes("/statistics") || route === "GET /bootstrap";
|
|
376
|
-
addRoute(`admin:${route}`, deps, deps.length && route !== "GET /view-batch" ? { ...objectInput, anyOf: deps.map((d2) => ({ type: "object", properties: { [route.includes("/webmcp/") ? "tool" : "name"]: { type: "string", const: route.includes("/webmcp/") ? d2.name : d2.id.slice(d2.kind === "view" ? 5 : 10) } } })) } : objectInput);
|
|
377
|
-
const d = out.at(-1);
|
|
378
|
-
if (generic) {
|
|
379
|
-
const operation = route === "POST /entries" ? "create" : route === "DELETE /entries/:id" ? "delete" : route.startsWith("GET ") ? void 0 : route.endsWith("/publish") ? "publish" : "update";
|
|
380
|
-
const inspected = inspectBehavior(plan, void 0, false, void 0, void 0, operation ? Object.keys(plan.schemas).map((schema) => ({ schema, operation })) : []);
|
|
381
|
-
d.schemas = Object.keys(plan.schemas).sort();
|
|
382
|
-
d.readSchemas = d.schemas;
|
|
383
|
-
d.opaque = inspected.opaque;
|
|
384
|
-
d.behavior = { schemas: plan.schemas, dependencies: inspected.dependencies };
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
return [...new Map(out.map((d) => [d.id, d])).values()].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// src/domain/describe/target-hash.ts
|
|
391
|
-
async function hash(value) {
|
|
392
|
-
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonical(value)));
|
|
393
|
-
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
394
|
-
}
|
|
395
|
-
function hashDefinition(plan, d) {
|
|
396
|
-
return hash({
|
|
397
|
-
id: d.id,
|
|
398
|
-
surfaces: d.surfaces,
|
|
399
|
-
triggers: d.triggers,
|
|
400
|
-
behavior: d.behavior,
|
|
401
|
-
input: d.inputSchema,
|
|
402
|
-
requires: d.requires,
|
|
403
|
-
restrict: d.restrict,
|
|
404
|
-
dialect: plan.dialect,
|
|
405
|
-
schemas: Object.fromEntries(d.schemas.map((name) => [name, plan.schemas[name]])),
|
|
406
|
-
userFields: plan.access?.userFields,
|
|
407
|
-
readOnly: d.readOnly,
|
|
408
|
-
opaque: d.opaque,
|
|
409
|
-
trustedOnly: d.trustedOnly
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
async function targetHash(plan, targetId) {
|
|
413
|
-
const target = targetDefinitions(plan).find((d) => d.id === targetId);
|
|
414
|
-
if (!target) throw new TypeError(`Unknown target '${targetId}'.`);
|
|
415
|
-
return hashDefinition(plan, target);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export {
|
|
419
|
-
canonical,
|
|
420
|
-
targetDefinitions,
|
|
421
|
-
hash,
|
|
422
|
-
hashDefinition,
|
|
423
|
-
targetHash
|
|
424
|
-
};
|