@wizzlethorpe/vaults 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -17
- package/dist/build.js +433 -181
- package/dist/build.js.map +1 -1
- package/dist/commands/build.js +4 -4
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/init.js +13 -10
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/password.js +3 -1
- package/dist/commands/password.js.map +1 -1
- package/dist/commands/patreon.js +30 -20
- package/dist/commands/patreon.js.map +1 -1
- package/dist/commands/preview.js +10 -8
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/push.js +11 -9
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/role.js +5 -0
- package/dist/commands/role.js.map +1 -1
- package/dist/config.js +30 -15
- package/dist/config.js.map +1 -1
- package/dist/escape.js +29 -0
- package/dist/escape.js.map +1 -0
- package/dist/favicon.js +3 -36
- package/dist/favicon.js.map +1 -1
- package/dist/foundry-importer.js +61 -0
- package/dist/foundry-importer.js.map +1 -0
- package/dist/images.js +0 -30
- package/dist/images.js.map +1 -1
- package/dist/index.js +37 -4
- package/dist/index.js.map +1 -1
- package/dist/migrate/0.6-legacy-auth-settings.js +96 -0
- package/dist/migrate/0.6-legacy-auth-settings.js.map +1 -0
- package/dist/migrate/0.7-vaults-dir.js +70 -0
- package/dist/migrate/0.7-vaults-dir.js.map +1 -0
- package/dist/migrate/registry.js +6 -0
- package/dist/migrate/registry.js.map +1 -0
- package/dist/migrate/run.js +38 -0
- package/dist/migrate/run.js.map +1 -0
- package/dist/migrate/types.js +8 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/paths.js +66 -0
- package/dist/paths.js.map +1 -0
- package/dist/render/auth-template.js +23 -141
- package/dist/render/auth-template.js.map +1 -1
- package/dist/render/bases.js +56 -44
- package/dist/render/bases.js.map +1 -1
- package/dist/render/callouts.js +29 -10
- package/dist/render/callouts.js.map +1 -1
- package/dist/render/embed.js +124 -26
- package/dist/render/embed.js.map +1 -1
- package/dist/render/extensions.js +68 -0
- package/dist/render/extensions.js.map +1 -0
- package/dist/render/external-links.js +32 -0
- package/dist/render/external-links.js.map +1 -0
- package/dist/render/footer.js +37 -0
- package/dist/render/footer.js.map +1 -0
- package/dist/render/frontmatter.js +17 -0
- package/dist/render/frontmatter.js.map +1 -0
- package/dist/render/handlers/assets.js +123 -0
- package/dist/render/handlers/assets.js.map +1 -0
- package/dist/render/handlers/builtin/dice.js +78 -0
- package/dist/render/handlers/builtin/dice.js.map +1 -0
- package/dist/render/handlers/builtin/fm-code.js +50 -0
- package/dist/render/handlers/builtin/fm-code.js.map +1 -0
- package/dist/render/handlers/builtin/fm.js +83 -0
- package/dist/render/handlers/builtin/fm.js.map +1 -0
- package/dist/render/handlers/builtin/index.js +10 -0
- package/dist/render/handlers/builtin/index.js.map +1 -0
- package/dist/render/handlers/builtin/inline-format.js +26 -0
- package/dist/render/handlers/builtin/inline-format.js.map +1 -0
- package/dist/render/handlers/builtin/statblock.js +491 -0
- package/dist/render/handlers/builtin/statblock.js.map +1 -0
- package/dist/render/handlers/dispatch.js +182 -0
- package/dist/render/handlers/dispatch.js.map +1 -0
- package/dist/render/handlers/loader.js +90 -0
- package/dist/render/handlers/loader.js.map +1 -0
- package/dist/render/handlers/types.js +60 -0
- package/dist/render/handlers/types.js.map +1 -0
- package/dist/render/image-srcs.js +42 -0
- package/dist/render/image-srcs.js.map +1 -0
- package/dist/render/layout.js +62 -9
- package/dist/render/layout.js.map +1 -1
- package/dist/render/pipeline.js +37 -8
- package/dist/render/pipeline.js.map +1 -1
- package/dist/render/preview.js +10 -5
- package/dist/render/preview.js.map +1 -1
- package/dist/render/slug.js +5 -0
- package/dist/render/slug.js.map +1 -1
- package/dist/render/styles.js +118 -11
- package/dist/render/styles.js.map +1 -1
- package/dist/render/wikilink.js +15 -4
- package/dist/render/wikilink.js.map +1 -1
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/settings.js +25 -4
- package/dist/settings.js.map +1 -1
- package/dist/version.js +36 -0
- package/dist/version.js.map +1 -0
- package/package.json +12 -12
- package/dist/api.js +0 -42
- package/dist/api.js.map +0 -1
- package/dist/render/mcp-template.js +0 -239
- package/dist/render/mcp-template.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wizzlethorpe/vaults",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Sync an Obsidian vault to a Cloudflare-hosted wiki with role-based access. Renders locally, deploys to Cloudflare Pages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"obsidian",
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
"ttrpg",
|
|
11
11
|
"knowledge-base"
|
|
12
12
|
],
|
|
13
|
-
"homepage": "https://github.com/wizzlethorpe/vaults
|
|
14
|
-
"bugs": "https://github.com/wizzlethorpe/vaults
|
|
13
|
+
"homepage": "https://github.com/wizzlethorpe/vaults#readme",
|
|
14
|
+
"bugs": "https://github.com/wizzlethorpe/vaults/issues",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/wizzlethorpe/vaults
|
|
17
|
+
"url": "git+https://github.com/wizzlethorpe/vaults.git",
|
|
18
18
|
"directory": "cli"
|
|
19
19
|
},
|
|
20
20
|
"license": "MIT",
|
|
@@ -31,13 +31,6 @@
|
|
|
31
31
|
"README.md",
|
|
32
32
|
"LICENSE"
|
|
33
33
|
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "tsc",
|
|
36
|
-
"start": "node dist/index.js",
|
|
37
|
-
"test": "node --import tsx --test test/*.test.ts",
|
|
38
|
-
"typecheck:test": "tsc -p tsconfig.test.json --noEmit",
|
|
39
|
-
"prepublishOnly": "npm run build"
|
|
40
|
-
},
|
|
41
34
|
"dependencies": {
|
|
42
35
|
"@types/js-yaml": "^4.0.9",
|
|
43
36
|
"commander": "^12.1.0",
|
|
@@ -63,7 +56,14 @@
|
|
|
63
56
|
"@types/node": "^22.10.0",
|
|
64
57
|
"@types/picomatch": "^3.0.1",
|
|
65
58
|
"@types/unist": "^3.0.3",
|
|
59
|
+
"esbuild": "^0.28.0",
|
|
66
60
|
"tsx": "^4.21.0",
|
|
67
61
|
"typescript": "^5.9.3"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsc",
|
|
65
|
+
"start": "node dist/index.js",
|
|
66
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
67
|
+
"typecheck:test": "tsc -p tsconfig.test.json --noEmit"
|
|
68
68
|
}
|
|
69
|
-
}
|
|
69
|
+
}
|
package/dist/api.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export class ApiClient {
|
|
2
|
-
cfg;
|
|
3
|
-
constructor(cfg) {
|
|
4
|
-
this.cfg = cfg;
|
|
5
|
-
}
|
|
6
|
-
async getManifest() {
|
|
7
|
-
const res = await fetch(this.url("/api/manifest"), { headers: this.headers() });
|
|
8
|
-
if (!res.ok)
|
|
9
|
-
throw new Error(`GET /api/manifest failed: ${res.status} ${await res.text()}`);
|
|
10
|
-
const { files } = (await res.json());
|
|
11
|
-
return files;
|
|
12
|
-
}
|
|
13
|
-
async putSource(path, body, contentType) {
|
|
14
|
-
const res = await fetch(this.url(`/admin/source/${encodePath(path)}`), {
|
|
15
|
-
method: "PUT",
|
|
16
|
-
headers: { ...this.headers(), "Content-Type": contentType },
|
|
17
|
-
body: new Uint8Array(body),
|
|
18
|
-
});
|
|
19
|
-
if (!res.ok)
|
|
20
|
-
throw new Error(`PUT ${path} failed: ${res.status} ${await res.text()}`);
|
|
21
|
-
}
|
|
22
|
-
async deleteSource(path) {
|
|
23
|
-
const res = await fetch(this.url(`/admin/source/${encodePath(path)}`), {
|
|
24
|
-
method: "DELETE",
|
|
25
|
-
headers: this.headers(),
|
|
26
|
-
});
|
|
27
|
-
if (!res.ok && res.status !== 404) {
|
|
28
|
-
throw new Error(`DELETE ${path} failed: ${res.status} ${await res.text()}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
url(path) {
|
|
32
|
-
const base = this.cfg.url.replace(/\/$/, "");
|
|
33
|
-
return `${base}${path}`;
|
|
34
|
-
}
|
|
35
|
-
headers() {
|
|
36
|
-
return { Authorization: `Bearer ${this.cfg.apiKey}` };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function encodePath(path) {
|
|
40
|
-
return path.split("/").map(encodeURIComponent).join("/");
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,SAAS;IACA;IAApB,YAAoB,GAAgB;QAAhB,QAAG,GAAH,GAAG,CAAa;IAAG,CAAC;IAExC,KAAK,CAAC,WAAW;QACf,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5F,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,IAAyB,EAAE,WAAmB;QAC1E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YACrE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE;YAC3D,IAAI,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YACrE,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;SACxB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAEO,GAAG,CAAC,IAAY;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;IAC1B,CAAC;IAEO,OAAO;QACb,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IACxD,CAAC;CACF;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
// Pages Function for the MCP server. Reads from static assets via env.ASSETS
|
|
2
|
-
// — no R2 binding needed. Generated at build time so it ships alongside the
|
|
3
|
-
// auth middleware in dist/functions/.
|
|
4
|
-
//
|
|
5
|
-
// MCP tools (list_files, read_file, search_text, grep) operate on the .md
|
|
6
|
-
// source files we ship alongside the rendered .html. Role-aware: pulls the
|
|
7
|
-
// per-variant manifest matching the user's auth cookie.
|
|
8
|
-
export function renderMcpFunction(cfg) {
|
|
9
|
-
const rolesLiteral = JSON.stringify(cfg.roles);
|
|
10
|
-
return `// Auto-generated by the vaults CLI. Do not edit by hand.
|
|
11
|
-
const ROLES = ${rolesLiteral};
|
|
12
|
-
const COOKIE_NAME = "vault_role";
|
|
13
|
-
const PROTOCOL_VERSION = "2024-11-05";
|
|
14
|
-
const SERVER_INFO = { name: "vaults-template", version: "0.1.0" };
|
|
15
|
-
|
|
16
|
-
const TOOLS = [
|
|
17
|
-
{
|
|
18
|
-
name: "list_files",
|
|
19
|
-
description: "List markdown files in the vault. Optionally filter by path prefix.",
|
|
20
|
-
inputSchema: {
|
|
21
|
-
type: "object",
|
|
22
|
-
properties: { prefix: { type: "string" } },
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: "read_file",
|
|
27
|
-
description: "Read the full text content of a single vault markdown file.",
|
|
28
|
-
inputSchema: {
|
|
29
|
-
type: "object",
|
|
30
|
-
properties: { path: { type: "string" } },
|
|
31
|
-
required: ["path"],
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "search_text",
|
|
36
|
-
description: "Plain substring search across vault markdown. Case-insensitive.",
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: {
|
|
40
|
-
query: { type: "string" },
|
|
41
|
-
prefix: { type: "string" },
|
|
42
|
-
limit: { type: "number", default: 50 },
|
|
43
|
-
},
|
|
44
|
-
required: ["query"],
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: "grep",
|
|
49
|
-
description: "Regex search across vault markdown. JavaScript regex syntax.",
|
|
50
|
-
inputSchema: {
|
|
51
|
-
type: "object",
|
|
52
|
-
properties: {
|
|
53
|
-
pattern: { type: "string" },
|
|
54
|
-
flags: { type: "string", default: "" },
|
|
55
|
-
prefix: { type: "string" },
|
|
56
|
-
limit: { type: "number", default: 50 },
|
|
57
|
-
},
|
|
58
|
-
required: ["pattern"],
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
export const onRequestPost = async (ctx) => {
|
|
64
|
-
const { request, env } = ctx;
|
|
65
|
-
let req;
|
|
66
|
-
try { req = await request.json(); } catch { return rpcError(null, -32700, "Parse error"); }
|
|
67
|
-
if (req.jsonrpc !== "2.0" || typeof req.method !== "string") {
|
|
68
|
-
return rpcError(req.id ?? null, -32600, "Invalid Request");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
switch (req.method) {
|
|
73
|
-
case "initialize":
|
|
74
|
-
return rpcResult(req.id, {
|
|
75
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
76
|
-
capabilities: { tools: {} },
|
|
77
|
-
serverInfo: SERVER_INFO,
|
|
78
|
-
});
|
|
79
|
-
case "tools/list":
|
|
80
|
-
return rpcResult(req.id, { tools: TOOLS });
|
|
81
|
-
case "tools/call":
|
|
82
|
-
return await handleToolCall(req, ctx);
|
|
83
|
-
default:
|
|
84
|
-
return rpcError(req.id, -32601, "Method not found: " + req.method);
|
|
85
|
-
}
|
|
86
|
-
} catch (err) {
|
|
87
|
-
return rpcError(req.id, -32603, err instanceof Error ? err.message : "Internal error");
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
async function handleToolCall(req, ctx) {
|
|
92
|
-
const params = req.params ?? {};
|
|
93
|
-
const args = params.arguments ?? {};
|
|
94
|
-
|
|
95
|
-
// Determine which variant to read from based on the user's role cookie.
|
|
96
|
-
const role = await readRole(ctx.request, ctx.env);
|
|
97
|
-
const manifestUrl = new URL("/_variants/" + role + "/_manifest.json", ctx.request.url).toString();
|
|
98
|
-
const manifestRes = await ctx.env.ASSETS.fetch(manifestUrl);
|
|
99
|
-
let manifest;
|
|
100
|
-
if (manifestRes.ok) {
|
|
101
|
-
manifest = await manifestRes.json();
|
|
102
|
-
} else {
|
|
103
|
-
// Single-role build (no _variants prefix) — manifest sits at the root.
|
|
104
|
-
const rootUrl = new URL("/_manifest.json", ctx.request.url).toString();
|
|
105
|
-
const rootRes = await ctx.env.ASSETS.fetch(rootUrl);
|
|
106
|
-
if (!rootRes.ok) return rpcError(req.id, -32603, "Manifest not found");
|
|
107
|
-
manifest = await rootRes.json();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// The MCP tools work on .md sources only.
|
|
111
|
-
const mdFiles = manifest.files.filter((f) => f.path.endsWith(".md"));
|
|
112
|
-
|
|
113
|
-
switch (params.name) {
|
|
114
|
-
case "list_files": {
|
|
115
|
-
const prefix = String(args.prefix ?? "");
|
|
116
|
-
const matched = prefix
|
|
117
|
-
? mdFiles.filter((f) => f.path.startsWith(prefix))
|
|
118
|
-
: mdFiles;
|
|
119
|
-
return toolResult(req.id, matched.map((f) => f.path + " (" + f.size + " bytes)").join("\\n"));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
case "read_file": {
|
|
123
|
-
const path = String(args.path ?? "");
|
|
124
|
-
if (!path) return rpcError(req.id, -32602, "path is required");
|
|
125
|
-
const exists = mdFiles.some((f) => f.path === path);
|
|
126
|
-
if (!exists) return rpcError(req.id, -32602, "File not found: " + path);
|
|
127
|
-
const fileUrl = new URL("/" + path, ctx.request.url).toString();
|
|
128
|
-
const res = await ctx.env.ASSETS.fetch(fileUrl);
|
|
129
|
-
if (!res.ok) return rpcError(req.id, -32603, "Read failed: " + path);
|
|
130
|
-
return toolResult(req.id, await res.text());
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
case "search_text":
|
|
134
|
-
case "grep": {
|
|
135
|
-
const query = String(args.query ?? args.pattern ?? "");
|
|
136
|
-
if (!query) return rpcError(req.id, -32602, (params.name === "grep" ? "pattern" : "query") + " is required");
|
|
137
|
-
|
|
138
|
-
let matcher;
|
|
139
|
-
if (params.name === "grep") {
|
|
140
|
-
try { matcher = new RegExp(query, String(args.flags ?? "")); }
|
|
141
|
-
catch (err) { return rpcError(req.id, -32602, "Invalid regex: " + (err instanceof Error ? err.message : "?")); }
|
|
142
|
-
} else {
|
|
143
|
-
const lc = query.toLowerCase();
|
|
144
|
-
matcher = { test: (s) => s.toLowerCase().includes(lc) };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const prefix = String(args.prefix ?? "");
|
|
148
|
-
const limit = Math.min(Number(args.limit ?? 50), 200);
|
|
149
|
-
const candidates = prefix ? mdFiles.filter((f) => f.path.startsWith(prefix)) : mdFiles;
|
|
150
|
-
|
|
151
|
-
const results = [];
|
|
152
|
-
outer: for (const f of candidates) {
|
|
153
|
-
const fileUrl = new URL("/" + f.path, ctx.request.url).toString();
|
|
154
|
-
const res = await ctx.env.ASSETS.fetch(fileUrl);
|
|
155
|
-
if (!res.ok) continue;
|
|
156
|
-
const text = await res.text();
|
|
157
|
-
const lines = text.split("\\n");
|
|
158
|
-
for (let i = 0; i < lines.length; i++) {
|
|
159
|
-
if (matcher.test(lines[i])) {
|
|
160
|
-
const line = lines[i].trim();
|
|
161
|
-
results.push(f.path + ":" + (i + 1) + ": " + (line.length > 200 ? line.slice(0, 200) + "…" : line));
|
|
162
|
-
if (results.length >= limit) break outer;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return toolResult(req.id, results.length === 0 ? "No matches." : results.join("\\n"));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
default:
|
|
170
|
-
return rpcError(req.id, -32602, "Unknown tool: " + params.name);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function readRole(request, env) {
|
|
175
|
-
const fallback = ROLES[0] ?? "public";
|
|
176
|
-
if (!env.SESSION_SECRET) return fallback;
|
|
177
|
-
|
|
178
|
-
// Authorization: Bearer <token> first (curl, MCP-over-HTTP clients).
|
|
179
|
-
const auth = request.headers.get("Authorization") || "";
|
|
180
|
-
const bearerMatch = /^Bearer\\s+(.+)$/i.exec(auth);
|
|
181
|
-
if (bearerMatch) {
|
|
182
|
-
const role = await verifyToken(bearerMatch[1], env.SESSION_SECRET);
|
|
183
|
-
if (role && ROLES.includes(role)) return role;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ?_token=<token> — keeps cross-origin GETs CORS-simple (no preflight).
|
|
187
|
-
const queryToken = new URL(request.url).searchParams.get("_token");
|
|
188
|
-
if (queryToken) {
|
|
189
|
-
const role = await verifyToken(queryToken, env.SESSION_SECRET);
|
|
190
|
-
if (role && ROLES.includes(role)) return role;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Then cookie (browser-based MCP clients, if any).
|
|
194
|
-
const cookieHeader = request.headers.get("Cookie") || "";
|
|
195
|
-
const cookies = {};
|
|
196
|
-
for (const part of cookieHeader.split(/;\\s*/)) {
|
|
197
|
-
const eq = part.indexOf("=");
|
|
198
|
-
if (eq > 0) cookies[part.slice(0, eq)] = part.slice(eq + 1);
|
|
199
|
-
}
|
|
200
|
-
const cookie = cookies[COOKIE_NAME];
|
|
201
|
-
if (!cookie) return fallback;
|
|
202
|
-
const role = await verifyToken(cookie, env.SESSION_SECRET);
|
|
203
|
-
return role && ROLES.includes(role) ? role : fallback;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function verifyToken(token, secretHex) {
|
|
207
|
-
const parts = token.split(".");
|
|
208
|
-
if (parts.length !== 3) return null;
|
|
209
|
-
const [role, expStr, sig] = parts;
|
|
210
|
-
const exp = Number(expStr);
|
|
211
|
-
if (!Number.isFinite(exp) || exp < Math.floor(Date.now() / 1000)) return null;
|
|
212
|
-
const keyBytes = new Uint8Array(secretHex.length / 2);
|
|
213
|
-
for (let i = 0; i < keyBytes.length; i++) keyBytes[i] = parseInt(secretHex.slice(i * 2, i * 2 + 2), 16);
|
|
214
|
-
const key = await crypto.subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
215
|
-
const macBytes = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(role + "." + expStr));
|
|
216
|
-
let macHex = "";
|
|
217
|
-
for (const b of new Uint8Array(macBytes)) macHex += b.toString(16).padStart(2, "0");
|
|
218
|
-
if (macHex.length !== sig.length) return null;
|
|
219
|
-
let diff = 0;
|
|
220
|
-
for (let i = 0; i < macHex.length; i++) diff |= macHex.charCodeAt(i) ^ sig.charCodeAt(i);
|
|
221
|
-
return diff === 0 ? role : null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function rpcResult(id, result) {
|
|
225
|
-
return new Response(JSON.stringify({ jsonrpc: "2.0", id, result }), {
|
|
226
|
-
headers: { "Content-Type": "application/json" },
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
function rpcError(id, code, message) {
|
|
230
|
-
return new Response(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }), {
|
|
231
|
-
headers: { "Content-Type": "application/json" },
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
function toolResult(id, text) {
|
|
235
|
-
return rpcResult(id, { content: [{ type: "text", text }] });
|
|
236
|
-
}
|
|
237
|
-
`;
|
|
238
|
-
}
|
|
239
|
-
//# sourceMappingURL=mcp-template.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-template.js","sourceRoot":"","sources":["../../src/render/mcp-template.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,sCAAsC;AACtC,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,wDAAwD;AAOxD,MAAM,UAAU,iBAAiB,CAAC,GAAsB;IACtD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAE/C,OAAO;gBACO,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkO3B,CAAC;AACF,CAAC"}
|