deepspace 0.10.0 → 0.10.2
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/CHANGELOG.md +15 -0
- package/dist/{chunk-DQ4G6VL6.js → chunk-T7V4GUIN.js} +283 -180
- package/dist/chunk-T7V4GUIN.js.map +1 -0
- package/dist/cli.js +1010 -6123
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.d.ts +18 -0
- package/dist/{docs-client-core.js → documentation-client-core.js} +60 -51
- package/dist/documentation-client-core.js.map +1 -0
- package/dist/documentation-default-renderer.cjs +124 -0
- package/dist/documentation-react.d.ts +2 -0
- package/dist/{docs-react.js → documentation-react.js} +8 -8
- package/dist/documentation-runtime.js +71 -0
- package/dist/documentation-server-core.d.ts +11 -0
- package/dist/{docs-server-core.js → documentation-server-core.js} +13 -13
- package/dist/documentation-server-core.js.map +1 -0
- package/dist/documentation.d.ts +366 -0
- package/dist/documentation.js +3953 -0
- package/dist/documentation.js.map +1 -0
- package/dist/feature-installer.js +145 -0
- package/dist/feature-installer.js.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.js +5 -15
- package/dist/index.js.map +1 -1
- package/dist/public-BGRX_Cjd.d.ts +234 -0
- package/dist/schema.d.ts +186 -0
- package/dist/schema.js +610 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +19 -167
- package/dist/server.js +76 -1069
- package/dist/server.js.map +1 -1
- package/dist/testing.d.ts +5 -8
- package/dist/testing.js +48 -14
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +52 -173
- package/dist/worker.js +563 -492
- package/dist/worker.js.map +1 -1
- package/features/admin-page/src/admin-schema.ts +1 -1
- package/features/ai-chat/feature.json +2 -2
- package/features/ai-chat/src/ChatPanel.tsx +0 -1
- package/features/ai-chat/src/ai-chat-schema.ts +1 -2
- package/features/canvas/src/canvas-schema.ts +1 -1
- package/features/documentation/content/index.mdx +14 -0
- package/features/documentation/content/quickstart.mdx +12 -0
- package/features/documentation/documentation.json +18 -0
- package/features/documentation/feature.json +68 -0
- package/features/{docs → documents}/feature.json +27 -27
- package/features/{docs/src/DocsEditorPage.tsx → documents/src/DocumentsEditorPage.tsx} +74 -74
- package/features/{docs/src/DocsListPage.tsx → documents/src/DocumentsListPage.tsx} +102 -102
- package/features/{docs/src/DocsOutlinePanel.tsx → documents/src/DocumentsOutlinePanel.tsx} +9 -9
- package/features/{docs/src/DocsPresence.tsx → documents/src/DocumentsPresence.tsx} +18 -18
- package/features/{docs → documents}/src/InviteDialog.tsx +21 -21
- package/features/{docs → documents}/src/LibrarySidebar.tsx +25 -25
- package/features/documents/src/__tests__/documents-presence-access.test.ts +71 -0
- package/features/{docs/src/docs-library-types.ts → documents/src/documents-library-types.ts} +3 -3
- package/features/{docs/src/docs-schema.ts → documents/src/documents-schema.ts} +6 -6
- package/features/{docs/src/docs-ui.css → documents/src/documents-ui.css} +89 -89
- package/features/{docs → documents}/src/editor/DocEditorSurface.tsx +11 -11
- package/features/{docs/src/editor/DocsTiptapToolbar.tsx → documents/src/editor/DocumentsTiptapToolbar.tsx} +21 -21
- package/features/{docs → documents}/src/editor/useDocEditor.ts +4 -4
- package/features/{docs/src/use-docs-presence-access.ts → documents/src/use-documents-presence-access.ts} +51 -51
- package/features/file-attachments/src/attachments-schema.ts +1 -1
- package/features/items/src/items-schema.ts +1 -1
- package/features/leaderboard/src/leaderboard-schema.ts +1 -1
- package/features/messaging/src/ChatMultiPage.tsx +1 -1
- package/features/messaging/src/ChatPage.tsx +1 -1
- package/features/messaging/src/messaging-schema.ts +2 -2
- package/features/tasks/src/tasks-schema.ts +1 -1
- package/features/testing/src/test-schema.ts +2 -2
- package/package.json +12 -7
- package/dist/chunk-DQ4G6VL6.js.map +0 -1
- package/dist/docs-client-core.d.ts +0 -18
- package/dist/docs-client-core.js.map +0 -1
- package/dist/docs-react.d.ts +0 -2
- package/dist/docs-runtime.js +0 -71
- package/dist/docs-server-core.d.ts +0 -11
- package/dist/docs-server-core.js.map +0 -1
- package/dist/docs.d.ts +0 -350
- package/dist/docs.js +0 -5139
- package/dist/docs.js.map +0 -1
- package/dist/public-Cp1X8ZQ3.d.ts +0 -230
- package/features/docs/src/__tests__/docs-presence-access.test.ts +0 -71
- /package/dist/{docs-react.js.map → documentation-react.js.map} +0 -0
package/dist/worker.js
CHANGED
|
@@ -340,16 +340,6 @@ var ROLE_ANONYMOUS = "viewer";
|
|
|
340
340
|
var ROLE_DEFAULT = "member";
|
|
341
341
|
var ROLE_ADMIN = "admin";
|
|
342
342
|
|
|
343
|
-
// src/shared/users-schema.ts
|
|
344
|
-
var USERS_COLUMNS = [
|
|
345
|
-
{ name: "email", storage: "text", interpretation: "plain" },
|
|
346
|
-
{ name: "name", storage: "text", interpretation: "plain" },
|
|
347
|
-
{ name: "imageUrl", storage: "text", interpretation: "plain" },
|
|
348
|
-
{ name: "role", storage: "text", interpretation: "plain" },
|
|
349
|
-
{ name: "createdAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
350
|
-
{ name: "lastSeenAt", storage: "text", interpretation: { kind: "datetime" } }
|
|
351
|
-
];
|
|
352
|
-
|
|
353
343
|
// src/server/schemas/registry.ts
|
|
354
344
|
function collectionTableName(name) {
|
|
355
345
|
return `c_${name.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
@@ -550,6 +540,14 @@ var SYSTEM_MANAGED_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
550
540
|
"createdAt",
|
|
551
541
|
"lastSeenAt"
|
|
552
542
|
]);
|
|
543
|
+
var USERS_COLUMNS = [
|
|
544
|
+
{ name: "email", storage: "text", interpretation: "plain" },
|
|
545
|
+
{ name: "name", storage: "text", interpretation: "plain" },
|
|
546
|
+
{ name: "imageUrl", storage: "text", interpretation: "plain" },
|
|
547
|
+
{ name: "role", storage: "text", interpretation: "plain" },
|
|
548
|
+
{ name: "createdAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
549
|
+
{ name: "lastSeenAt", storage: "text", interpretation: { kind: "datetime" } }
|
|
550
|
+
];
|
|
553
551
|
var BASE_USERS_SCHEMA = {
|
|
554
552
|
name: "users",
|
|
555
553
|
columns: [...USERS_COLUMNS],
|
|
@@ -5107,7 +5105,8 @@ var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
|
5107
5105
|
"queue",
|
|
5108
5106
|
"browser_rendering",
|
|
5109
5107
|
"analytics_engine",
|
|
5110
|
-
"hyperdrive"
|
|
5108
|
+
"hyperdrive",
|
|
5109
|
+
"ratelimit"
|
|
5111
5110
|
]);
|
|
5112
5111
|
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
5113
5112
|
"ASSETS",
|
|
@@ -5115,6 +5114,7 @@ var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
|
5115
5114
|
"API_WORKER",
|
|
5116
5115
|
"DEEPSPACE_APP_ID",
|
|
5117
5116
|
"DEEPSPACE_RESOURCE_ID",
|
|
5117
|
+
"DEEPSPACE_RELEASE_OPERATION_ID",
|
|
5118
5118
|
"APP_NAME",
|
|
5119
5119
|
"OWNER_USER_ID",
|
|
5120
5120
|
"AUTH_JWT_PUBLIC_KEY",
|
|
@@ -5205,6 +5205,17 @@ function requiredFieldError(b) {
|
|
|
5205
5205
|
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
5206
5206
|
}
|
|
5207
5207
|
return null;
|
|
5208
|
+
case "ratelimit":
|
|
5209
|
+
if (!/^\d+$/u.test(b.namespace_id) || Number(b.namespace_id) <= 0) {
|
|
5210
|
+
return `ratelimit binding "${b.name}" requires a positive integer namespace_id string`;
|
|
5211
|
+
}
|
|
5212
|
+
if (!Number.isInteger(b.simple?.limit) || b.simple.limit <= 0) {
|
|
5213
|
+
return `ratelimit binding "${b.name}" requires simple.limit (positive integer)`;
|
|
5214
|
+
}
|
|
5215
|
+
if (b.simple.period !== 10 && b.simple.period !== 60) {
|
|
5216
|
+
return `ratelimit binding "${b.name}" requires simple.period (10 or 60)`;
|
|
5217
|
+
}
|
|
5218
|
+
return null;
|
|
5208
5219
|
case "ai":
|
|
5209
5220
|
case "browser_rendering":
|
|
5210
5221
|
case "analytics_engine":
|
|
@@ -5282,6 +5293,15 @@ function bindingManifestFromOutputConfig(outputConfig) {
|
|
|
5282
5293
|
const id = pluckString(h, "id");
|
|
5283
5294
|
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
5284
5295
|
}
|
|
5296
|
+
for (const r of asObjectArray(outputConfig.ratelimits)) {
|
|
5297
|
+
const name = pluckString(r, "name");
|
|
5298
|
+
const namespace_id = pluckString(r, "namespace_id");
|
|
5299
|
+
const simple = isObject(r.simple) ? r.simple : null;
|
|
5300
|
+
const limit = pluckNumber(simple, "limit");
|
|
5301
|
+
const period = pluckNumber(simple, "period");
|
|
5302
|
+
if (!name || !namespace_id || limit === null || period !== 10 && period !== 60) continue;
|
|
5303
|
+
out.push({ type: "ratelimit", name, namespace_id, simple: { limit, period } });
|
|
5304
|
+
}
|
|
5285
5305
|
return out;
|
|
5286
5306
|
}
|
|
5287
5307
|
function isObject(v) {
|
|
@@ -6026,8 +6046,8 @@ function validateAppMigrationIds(value) {
|
|
|
6026
6046
|
|
|
6027
6047
|
// src/shared/ai-models.ts
|
|
6028
6048
|
var DEEPSPACE_MODEL_CATALOG_PROVENANCE = {
|
|
6029
|
-
version: "2026-08-
|
|
6030
|
-
verifiedAt: "2026-08-
|
|
6049
|
+
version: "2026-08-04",
|
|
6050
|
+
verifiedAt: "2026-08-04",
|
|
6031
6051
|
sources: {
|
|
6032
6052
|
anthropic: "https://platform.claude.com/docs/en/api/beta/models/list",
|
|
6033
6053
|
openai: "https://developers.openai.com/api/docs/models/all",
|
|
@@ -6118,7 +6138,8 @@ var DEEPSPACE_AGENT_PROFILES = {
|
|
|
6118
6138
|
application: {
|
|
6119
6139
|
id: "application",
|
|
6120
6140
|
defaultModel: DEEPSPACE_AI_DEFAULTS.agent,
|
|
6121
|
-
maxSteps:
|
|
6141
|
+
maxSteps: 21,
|
|
6142
|
+
maxToolCalls: 20,
|
|
6122
6143
|
allowedTools: "application-defined"
|
|
6123
6144
|
},
|
|
6124
6145
|
documentation: {
|
|
@@ -6128,7 +6149,7 @@ var DEEPSPACE_AGENT_PROFILES = {
|
|
|
6128
6149
|
// step. The runner disables tools once the tool-call budget is exhausted.
|
|
6129
6150
|
maxSteps: 21,
|
|
6130
6151
|
maxToolCalls: 20,
|
|
6131
|
-
allowedTools: ["
|
|
6152
|
+
allowedTools: ["documentation_search", "documentation_read"]
|
|
6132
6153
|
}
|
|
6133
6154
|
};
|
|
6134
6155
|
function getDeepSpaceAIModel(modelId) {
|
|
@@ -6149,45 +6170,6 @@ function resolveDeepSpaceAgentModel(modelId, profileId = "application") {
|
|
|
6149
6170
|
return { modelId: model.id, provider: model.provider, model, profile };
|
|
6150
6171
|
}
|
|
6151
6172
|
|
|
6152
|
-
// src/shared/docs-routing.ts
|
|
6153
|
-
var DOCS_RESOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
6154
|
-
"7z",
|
|
6155
|
-
"avif",
|
|
6156
|
-
"css",
|
|
6157
|
-
"csv",
|
|
6158
|
-
"gif",
|
|
6159
|
-
"gz",
|
|
6160
|
-
"ico",
|
|
6161
|
-
"jpeg",
|
|
6162
|
-
"jpg",
|
|
6163
|
-
"js",
|
|
6164
|
-
"json",
|
|
6165
|
-
"map",
|
|
6166
|
-
"md",
|
|
6167
|
-
"mjs",
|
|
6168
|
-
"mp3",
|
|
6169
|
-
"mp4",
|
|
6170
|
-
"pdf",
|
|
6171
|
-
"png",
|
|
6172
|
-
"svg",
|
|
6173
|
-
"tar",
|
|
6174
|
-
"txt",
|
|
6175
|
-
"wasm",
|
|
6176
|
-
"webm",
|
|
6177
|
-
"webp",
|
|
6178
|
-
"woff",
|
|
6179
|
-
"woff2",
|
|
6180
|
-
"xml",
|
|
6181
|
-
"yaml",
|
|
6182
|
-
"yml",
|
|
6183
|
-
"zip"
|
|
6184
|
-
]);
|
|
6185
|
-
function isDocsResourcePath(pathname) {
|
|
6186
|
-
const leaf = pathname.slice(pathname.lastIndexOf("/") + 1);
|
|
6187
|
-
const extension = leaf.includes(".") ? leaf.slice(leaf.lastIndexOf(".") + 1).toLowerCase() : "";
|
|
6188
|
-
return extension !== "" && DOCS_RESOURCE_EXTENSIONS.has(extension);
|
|
6189
|
-
}
|
|
6190
|
-
|
|
6191
6173
|
// src/server/utils/cors.ts
|
|
6192
6174
|
function matchOrigin(origin, patterns) {
|
|
6193
6175
|
if (!origin) return null;
|
|
@@ -6732,11 +6714,7 @@ function streamDeepSpaceAgent(env, options) {
|
|
|
6732
6714
|
}
|
|
6733
6715
|
}
|
|
6734
6716
|
}
|
|
6735
|
-
const provider = createDeepSpaceAI(
|
|
6736
|
-
env,
|
|
6737
|
-
selection.provider,
|
|
6738
|
-
authToken ? { authToken } : {}
|
|
6739
|
-
);
|
|
6717
|
+
const provider = createDeepSpaceAI(env, selection.provider, authToken ? { authToken } : {});
|
|
6740
6718
|
const { prepareStep, tools, ...remainingStreamOptions } = streamOptions;
|
|
6741
6719
|
const boundedTools = limitToolExecutions(tools, selection.profile.maxToolCalls);
|
|
6742
6720
|
const result = streamText({
|
|
@@ -6764,22 +6742,27 @@ function enforceToolCallLimit(prepareStep, maxToolCalls) {
|
|
|
6764
6742
|
function limitToolExecutions(tools, maxToolCalls) {
|
|
6765
6743
|
if (!tools || maxToolCalls === void 0) return tools;
|
|
6766
6744
|
let executions = 0;
|
|
6767
|
-
return Object.fromEntries(
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6745
|
+
return Object.fromEntries(
|
|
6746
|
+
Object.entries(tools).map(([name, definition]) => {
|
|
6747
|
+
if (typeof definition.execute !== "function") return [name, definition];
|
|
6748
|
+
const execute = definition.execute;
|
|
6749
|
+
return [
|
|
6750
|
+
name,
|
|
6751
|
+
{
|
|
6752
|
+
...definition,
|
|
6753
|
+
execute: (...args) => {
|
|
6754
|
+
if (executions >= maxToolCalls) {
|
|
6755
|
+
return Promise.resolve({
|
|
6756
|
+
error: `The agent tool-call limit of ${maxToolCalls} has been reached`
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6759
|
+
executions++;
|
|
6760
|
+
return execute(...args);
|
|
6761
|
+
}
|
|
6777
6762
|
}
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
}];
|
|
6782
|
-
}));
|
|
6763
|
+
];
|
|
6764
|
+
})
|
|
6765
|
+
);
|
|
6783
6766
|
}
|
|
6784
6767
|
function deepSpaceAgentErrorSummary(error, context = {}) {
|
|
6785
6768
|
const details = [];
|
|
@@ -6844,7 +6827,10 @@ function safeLogText(value) {
|
|
|
6844
6827
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
6845
6828
|
return codePoint <= 31 || codePoint === 127 ? " " : character;
|
|
6846
6829
|
}).join("");
|
|
6847
|
-
return withoutControlCharacters.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(
|
|
6830
|
+
return withoutControlCharacters.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(
|
|
6831
|
+
/\b(api[_ -]?key|authorization|password|secret|token)\b\s*[:=]\s*\S+/gi,
|
|
6832
|
+
"$1=[redacted]"
|
|
6833
|
+
).trim().slice(0, 300);
|
|
6848
6834
|
}
|
|
6849
6835
|
|
|
6850
6836
|
// src/server/utils/composio-tools.ts
|
|
@@ -7283,11 +7269,10 @@ function formatTranscript(messages) {
|
|
|
7283
7269
|
}
|
|
7284
7270
|
function makeDefaultSummarizer(env, options = {}) {
|
|
7285
7271
|
return async (messages) => {
|
|
7286
|
-
const
|
|
7287
|
-
if (!model) throw new Error("DeepSpace summarization model is missing from the catalog");
|
|
7288
|
-
const provider = createDeepSpaceAI(env, model.provider, { authToken: options.authToken });
|
|
7272
|
+
const anthropic = createDeepSpaceAI(env, "anthropic", { authToken: options.authToken });
|
|
7289
7273
|
const { text } = await generateText({
|
|
7290
|
-
|
|
7274
|
+
// Stable alias — provider bug-fix snapshots roll forward without code change.
|
|
7275
|
+
model: anthropic("claude-haiku-4-5"),
|
|
7291
7276
|
// 2500 gives the model room to finish a sentence after hitting the
|
|
7292
7277
|
// "under 2000 tokens" soft limit in the prompt.
|
|
7293
7278
|
maxOutputTokens: 2500,
|
|
@@ -7423,9 +7408,189 @@ async function appendMessage(stub, msg) {
|
|
|
7423
7408
|
});
|
|
7424
7409
|
}
|
|
7425
7410
|
|
|
7426
|
-
// src/server/utils/
|
|
7411
|
+
// src/server/utils/metering.ts
|
|
7412
|
+
function meterUsage(env, kind, fields = {}) {
|
|
7413
|
+
const ds = env.USAGE_EVENTS;
|
|
7414
|
+
if (!ds) return false;
|
|
7415
|
+
try {
|
|
7416
|
+
ds.writeDataPoint({
|
|
7417
|
+
indexes: [env.OWNER_USER_ID ?? "unknown"],
|
|
7418
|
+
blobs: [
|
|
7419
|
+
env.DEEPSPACE_RESOURCE_ID ?? env.APP_NAME ?? "unknown",
|
|
7420
|
+
kind,
|
|
7421
|
+
fields.id ?? "",
|
|
7422
|
+
fields.op ?? ""
|
|
7423
|
+
],
|
|
7424
|
+
doubles: [fields.units ?? 0, fields.count ?? 0]
|
|
7425
|
+
});
|
|
7426
|
+
return true;
|
|
7427
|
+
} catch {
|
|
7428
|
+
return false;
|
|
7429
|
+
}
|
|
7430
|
+
}
|
|
7431
|
+
function meterAi(env, model, fields = {}) {
|
|
7432
|
+
const calls = fields.calls ?? 1;
|
|
7433
|
+
const inChars = fields.inputChars ?? 0;
|
|
7434
|
+
const outChars = fields.outputChars ?? 0;
|
|
7435
|
+
let ok = true;
|
|
7436
|
+
if (inChars > 0) {
|
|
7437
|
+
ok = meterUsage(env, "ai", { id: model, op: "input", units: inChars, count: calls }) && ok;
|
|
7438
|
+
}
|
|
7439
|
+
if (outChars > 0) {
|
|
7440
|
+
ok = meterUsage(env, "ai", { id: model, op: "output", units: outChars, count: calls }) && ok;
|
|
7441
|
+
}
|
|
7442
|
+
if (inChars === 0 && outChars === 0) {
|
|
7443
|
+
ok = meterUsage(env, "ai", { id: model, op: "call", units: 0, count: calls }) && ok;
|
|
7444
|
+
}
|
|
7445
|
+
return ok;
|
|
7446
|
+
}
|
|
7447
|
+
function meterVectorize(env, indexName, op, fields = {}) {
|
|
7448
|
+
const vectors = fields.vectors ?? 0;
|
|
7449
|
+
const dims = fields.dims ?? 0;
|
|
7450
|
+
const stored = fields.storedCount ?? 0;
|
|
7451
|
+
const units = op === "query" ? (vectors + stored) * dims : vectors * dims;
|
|
7452
|
+
return meterUsage(env, "vectorize", { id: indexName, op, units, count: vectors });
|
|
7453
|
+
}
|
|
7454
|
+
var CHARS_PER_TOKEN = 4;
|
|
7455
|
+
var AI_EMBED_USD_PER_M_TOKENS = 0.012;
|
|
7456
|
+
var VECTORIZE_QUERIED_USD_PER_M_DIMS = 0.01;
|
|
7457
|
+
var VECTORIZE_STORED_USD_PER_100M_DIMS = 0.05;
|
|
7458
|
+
var COST_RATES = {
|
|
7459
|
+
ai: {
|
|
7460
|
+
/**
|
|
7461
|
+
* USD per character of *embedding input* (bge-m3 / qwen3-embedding tier).
|
|
7462
|
+
* Renamed from `perChar` to make explicit that this rate does NOT
|
|
7463
|
+
* apply to LLM-generation output — see `priceBindingUsageEvent`.
|
|
7464
|
+
*/
|
|
7465
|
+
embedInputPerChar: AI_EMBED_USD_PER_M_TOKENS / 1e6 / CHARS_PER_TOKEN
|
|
7466
|
+
},
|
|
7467
|
+
vectorize: {
|
|
7468
|
+
/** USD per queried dimension (per query, per stored vector compared). */
|
|
7469
|
+
queriedPerDim: VECTORIZE_QUERIED_USD_PER_M_DIMS / 1e6,
|
|
7470
|
+
/** USD per stored dimension per month. */
|
|
7471
|
+
storedPerDimPerMonth: VECTORIZE_STORED_USD_PER_100M_DIMS / 1e8
|
|
7472
|
+
}
|
|
7473
|
+
};
|
|
7474
|
+
function priceBindingUsageEvent(kind, op, units) {
|
|
7475
|
+
if (kind === "ai" && op === "input") {
|
|
7476
|
+
return units * COST_RATES.ai.embedInputPerChar;
|
|
7477
|
+
}
|
|
7478
|
+
if (kind === "vectorize" && op === "query") {
|
|
7479
|
+
return units * COST_RATES.vectorize.queriedPerDim;
|
|
7480
|
+
}
|
|
7481
|
+
return 0;
|
|
7482
|
+
}
|
|
7483
|
+
|
|
7484
|
+
// src/server/utils/d1-migrations.ts
|
|
7485
|
+
var META_TABLE = "_dpc_migrations";
|
|
7486
|
+
var META_BOOTSTRAP_SQL = `CREATE TABLE IF NOT EXISTS ${META_TABLE} (idx INTEGER PRIMARY KEY, applied_at TEXT NOT NULL)`;
|
|
7487
|
+
async function runMigrations(db, migrations) {
|
|
7488
|
+
await db.prepare(META_BOOTSTRAP_SQL).run();
|
|
7489
|
+
const row = await db.prepare(`SELECT COUNT(*) AS n FROM ${META_TABLE}`).first();
|
|
7490
|
+
const fromVersion = row?.n ?? 0;
|
|
7491
|
+
if (fromVersion >= migrations.length) {
|
|
7492
|
+
return { fromVersion, toVersion: fromVersion, applied: 0 };
|
|
7493
|
+
}
|
|
7494
|
+
let current = fromVersion;
|
|
7495
|
+
for (let i = fromVersion; i < migrations.length; i++) {
|
|
7496
|
+
const statements = splitStatements(migrations[i]);
|
|
7497
|
+
for (const stmt of statements) {
|
|
7498
|
+
try {
|
|
7499
|
+
await db.prepare(stmt).run();
|
|
7500
|
+
} catch (err) {
|
|
7501
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7502
|
+
throw new Error(`Migration ${i} failed (db at version=${current}): ${msg}`);
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
await db.prepare(`INSERT INTO ${META_TABLE} (idx, applied_at) VALUES (?, ?)`).bind(i, (/* @__PURE__ */ new Date()).toISOString()).run();
|
|
7506
|
+
current = i + 1;
|
|
7507
|
+
}
|
|
7508
|
+
return { fromVersion, toVersion: current, applied: current - fromVersion };
|
|
7509
|
+
}
|
|
7510
|
+
function splitStatements(sql) {
|
|
7511
|
+
return sql.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7512
|
+
}
|
|
7513
|
+
|
|
7514
|
+
// src/documentation/worker/routes.ts
|
|
7515
|
+
import { Hono } from "hono";
|
|
7516
|
+
|
|
7517
|
+
// src/documentation/resource-path.ts
|
|
7518
|
+
function canonicalNativeDocumentationRequestPath(pathname) {
|
|
7519
|
+
try {
|
|
7520
|
+
return pathname.split("/").map((segment) => encodeURIComponent(decodeURIComponent(segment))).join("/");
|
|
7521
|
+
} catch {
|
|
7522
|
+
return null;
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
// src/documentation/worker/assistant.ts
|
|
7427
7527
|
import { tool as tool2 } from "ai";
|
|
7428
7528
|
import { z } from "zod/v4";
|
|
7529
|
+
|
|
7530
|
+
// src/documentation/types.ts
|
|
7531
|
+
var DOCUMENTATION_MANIFEST_VERSION = 2;
|
|
7532
|
+
var DocumentationError = class extends Error {
|
|
7533
|
+
code;
|
|
7534
|
+
diagnostics;
|
|
7535
|
+
constructor(message, code, diagnostics = []) {
|
|
7536
|
+
super(message);
|
|
7537
|
+
this.name = "DocumentationError";
|
|
7538
|
+
this.code = code;
|
|
7539
|
+
this.diagnostics = diagnostics;
|
|
7540
|
+
}
|
|
7541
|
+
};
|
|
7542
|
+
|
|
7543
|
+
// src/documentation/routing.ts
|
|
7544
|
+
function collapseRoute(value) {
|
|
7545
|
+
let route = `/${value}`.replace(/\/{2,}/g, "/");
|
|
7546
|
+
if (route.length > 1) route = route.replace(/\/$/, "");
|
|
7547
|
+
return route;
|
|
7548
|
+
}
|
|
7549
|
+
function normalizeRoute(value) {
|
|
7550
|
+
assertSafeRoute(value);
|
|
7551
|
+
let route = collapseRoute(value);
|
|
7552
|
+
route = route.replace(/\.(?:md|mdx)$/i, "");
|
|
7553
|
+
route = route.replace(/\/index$/i, "");
|
|
7554
|
+
return route || "/";
|
|
7555
|
+
}
|
|
7556
|
+
function normalizeDocumentationBasePath(value) {
|
|
7557
|
+
if (!value || value === "/") return "";
|
|
7558
|
+
const normalized = normalizeRoute(value);
|
|
7559
|
+
if (normalized === "/_documentation") {
|
|
7560
|
+
throw new DocumentationError("The internal /_documentation namespace cannot be a public documentation base", "documentation_base_invalid");
|
|
7561
|
+
}
|
|
7562
|
+
return normalized;
|
|
7563
|
+
}
|
|
7564
|
+
function documentationPublicPath(basePath, path) {
|
|
7565
|
+
const base = normalizeDocumentationBasePath(basePath);
|
|
7566
|
+
const suffix = path.startsWith("/") ? path : `/${path}`;
|
|
7567
|
+
if (suffix === "/") return base || "/";
|
|
7568
|
+
return `${base}${suffix}`;
|
|
7569
|
+
}
|
|
7570
|
+
function assertSafeRoute(value) {
|
|
7571
|
+
if (value.includes("\\") || value.includes("\0")) throw unsafeRoute(value);
|
|
7572
|
+
let decoded = value;
|
|
7573
|
+
for (let pass = 0; pass < 5; pass++) {
|
|
7574
|
+
let next;
|
|
7575
|
+
try {
|
|
7576
|
+
next = decodeURIComponent(decoded);
|
|
7577
|
+
} catch {
|
|
7578
|
+
throw unsafeRoute(value);
|
|
7579
|
+
}
|
|
7580
|
+
if (next === decoded) break;
|
|
7581
|
+
decoded = next;
|
|
7582
|
+
}
|
|
7583
|
+
if (decoded.includes("\\") || decoded.includes("\0") || decoded.split("/").some((segment) => segment === "." || segment === "..")) throw unsafeRoute(value);
|
|
7584
|
+
}
|
|
7585
|
+
function unsafeRoute(value) {
|
|
7586
|
+
return new DocumentationError(
|
|
7587
|
+
`Documentation route contains an unsafe path segment: ${value}`,
|
|
7588
|
+
"documentation_route_invalid",
|
|
7589
|
+
[{ code: "route_path_escape", message: value }]
|
|
7590
|
+
);
|
|
7591
|
+
}
|
|
7592
|
+
|
|
7593
|
+
// src/documentation/worker/assistant.ts
|
|
7429
7594
|
var MAX_CHUNKS = 1e4;
|
|
7430
7595
|
var MAX_CHUNK_CHARS = 2500;
|
|
7431
7596
|
var MAX_CORPUS_CHARS = 12e6;
|
|
@@ -7472,28 +7637,28 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
7472
7637
|
"your"
|
|
7473
7638
|
]);
|
|
7474
7639
|
var indexedChunkCache = /* @__PURE__ */ new WeakMap();
|
|
7475
|
-
function
|
|
7476
|
-
if (!Array.isArray(value)) throw new Error("
|
|
7640
|
+
function parseDocumentationCorpus(value) {
|
|
7641
|
+
if (!Array.isArray(value)) throw new Error("Documentation assistant index must be an array");
|
|
7477
7642
|
if (value.length > MAX_CHUNKS) {
|
|
7478
|
-
throw new Error(`
|
|
7643
|
+
throw new Error(`Documentation assistant index exceeds ${MAX_CHUNKS} chunks`);
|
|
7479
7644
|
}
|
|
7480
7645
|
const chunks = [];
|
|
7481
7646
|
let totalChars2 = 0;
|
|
7482
7647
|
for (let index = 0; index < value.length; index++) {
|
|
7483
7648
|
const raw = value[index];
|
|
7484
7649
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
7485
|
-
throw new Error(`
|
|
7650
|
+
throw new Error(`Documentation assistant chunk ${index} must be an object`);
|
|
7486
7651
|
}
|
|
7487
7652
|
const chunk = raw;
|
|
7488
7653
|
if (typeof chunk.id !== "string" || !chunk.id || typeof chunk.route !== "string" || !chunk.route.startsWith("/") || typeof chunk.title !== "string" || !chunk.title || typeof chunk.text !== "string" || !chunk.text) {
|
|
7489
|
-
throw new Error(`
|
|
7654
|
+
throw new Error(`Documentation assistant chunk ${index} has invalid required fields`);
|
|
7490
7655
|
}
|
|
7491
7656
|
if (chunk.text.length > MAX_CHUNK_CHARS) {
|
|
7492
|
-
throw new Error(`
|
|
7657
|
+
throw new Error(`Documentation assistant chunk ${chunk.id} exceeds ${MAX_CHUNK_CHARS} characters`);
|
|
7493
7658
|
}
|
|
7494
7659
|
totalChars2 += chunk.text.length;
|
|
7495
7660
|
if (totalChars2 > MAX_CORPUS_CHARS) {
|
|
7496
|
-
throw new Error(`
|
|
7661
|
+
throw new Error(`Documentation assistant corpus exceeds ${MAX_CORPUS_CHARS} characters`);
|
|
7497
7662
|
}
|
|
7498
7663
|
chunks.push({
|
|
7499
7664
|
id: chunk.id,
|
|
@@ -7505,7 +7670,7 @@ function parseDocsCorpus(value) {
|
|
|
7505
7670
|
}
|
|
7506
7671
|
return chunks;
|
|
7507
7672
|
}
|
|
7508
|
-
function
|
|
7673
|
+
function searchDocumentationCorpus(corpus, query, limit = 6) {
|
|
7509
7674
|
const normalized = normalize(query);
|
|
7510
7675
|
const terms = [
|
|
7511
7676
|
...new Set(
|
|
@@ -7539,8 +7704,8 @@ function searchDocsCorpus(corpus, query, limit = 6) {
|
|
|
7539
7704
|
};
|
|
7540
7705
|
}).filter((result) => result.score > 0).sort((left, right) => right.score - left.score || left.route.localeCompare(right.route)).slice(0, Math.max(1, Math.min(limit, 10)));
|
|
7541
7706
|
}
|
|
7542
|
-
function
|
|
7543
|
-
const normalizedRoute =
|
|
7707
|
+
function readDocumentationRoute(corpus, route) {
|
|
7708
|
+
const normalizedRoute = collapseRoute(route);
|
|
7544
7709
|
return corpus.filter((chunk) => chunk.route === normalizedRoute).slice(0, 12).map(({ route: chunkRoute, title, heading, text }) => ({
|
|
7545
7710
|
route: chunkRoute,
|
|
7546
7711
|
title,
|
|
@@ -7548,40 +7713,44 @@ function readDocsRoute(corpus, route) {
|
|
|
7548
7713
|
text
|
|
7549
7714
|
}));
|
|
7550
7715
|
}
|
|
7551
|
-
function
|
|
7716
|
+
function buildDocumentationAssistantTools(corpus, basePath = "/docs") {
|
|
7552
7717
|
return {
|
|
7553
|
-
|
|
7718
|
+
documentation_search: tool2({
|
|
7554
7719
|
description: "Search the current documentation. Use this before answering factual or how-to questions.",
|
|
7555
7720
|
inputSchema: z.object({
|
|
7556
7721
|
query: z.string().trim().min(2).max(300),
|
|
7557
7722
|
limit: z.number().int().min(1).max(10).optional()
|
|
7558
7723
|
}),
|
|
7559
7724
|
execute: async ({ query, limit }) => ({
|
|
7560
|
-
results:
|
|
7725
|
+
results: searchDocumentationCorpus(corpus, query, limit ?? 6).map((result) => ({
|
|
7726
|
+
...result,
|
|
7727
|
+
url: documentationPublicPath(basePath, result.route)
|
|
7728
|
+
}))
|
|
7561
7729
|
})
|
|
7562
7730
|
}),
|
|
7563
|
-
|
|
7564
|
-
description: "Read bounded source chunks for one exact documentation route returned by
|
|
7731
|
+
documentation_read: tool2({
|
|
7732
|
+
description: "Read bounded source chunks for one exact documentation route returned by documentation_search.",
|
|
7565
7733
|
inputSchema: z.object({ route: z.string().trim().startsWith("/").max(500) }),
|
|
7566
7734
|
execute: async ({ route }) => ({
|
|
7567
|
-
route:
|
|
7568
|
-
|
|
7735
|
+
route: collapseRoute(route),
|
|
7736
|
+
url: documentationPublicPath(basePath, route),
|
|
7737
|
+
chunks: readDocumentationRoute(corpus, route)
|
|
7569
7738
|
})
|
|
7570
7739
|
})
|
|
7571
7740
|
};
|
|
7572
7741
|
}
|
|
7573
|
-
function
|
|
7742
|
+
function buildDocumentationAssistantPrompt(appName, currentRoute) {
|
|
7574
7743
|
return [
|
|
7575
7744
|
`You are the public documentation assistant for "${appName}" on DeepSpace.`,
|
|
7576
|
-
"Answer only from the documentation available through
|
|
7745
|
+
"Answer only from the documentation available through documentation_search and documentation_read.",
|
|
7577
7746
|
"Search before answering unless the user is only greeting you.",
|
|
7578
|
-
"Use
|
|
7747
|
+
"Use documentation_read when search excerpts are insufficient or ambiguous.",
|
|
7579
7748
|
"Never claim access to application records, integrations, private data, or mutation tools.",
|
|
7580
7749
|
"If the documentation does not support an answer, say so plainly and suggest the closest documented page.",
|
|
7581
7750
|
"Cite factual answers with Markdown links to the exact documentation routes returned by tools.",
|
|
7582
7751
|
"Do not invent URLs, APIs, configuration keys, versions, or behavior.",
|
|
7583
7752
|
"Keep answers concise, practical, and explicit about uncertainty.",
|
|
7584
|
-
...currentRoute ? [`The reader is currently viewing ${
|
|
7753
|
+
...currentRoute ? [`The reader is currently viewing ${collapseRoute(currentRoute)}.`] : []
|
|
7585
7754
|
].join("\n");
|
|
7586
7755
|
}
|
|
7587
7756
|
function normalize(value) {
|
|
@@ -7589,7 +7758,7 @@ function normalize(value) {
|
|
|
7589
7758
|
}
|
|
7590
7759
|
function canonicalSearchTerm(term) {
|
|
7591
7760
|
if (SEARCH_STOP_WORDS.has(term)) return term;
|
|
7592
|
-
if (["doc", "docs", "document", "documents", "documentation"].includes(term)) return "
|
|
7761
|
+
if (["doc", "docs", "document", "documents", "documentation"].includes(term)) return "documentation";
|
|
7593
7762
|
if (["builds", "building", "built", "compile", "compiled", "compiles", "compiling"].includes(term)) {
|
|
7594
7763
|
return "build";
|
|
7595
7764
|
}
|
|
@@ -7609,11 +7778,6 @@ function indexChunk(chunk) {
|
|
|
7609
7778
|
indexedChunkCache.set(chunk, indexed);
|
|
7610
7779
|
return indexed;
|
|
7611
7780
|
}
|
|
7612
|
-
function normalizeDocsRoute(value) {
|
|
7613
|
-
let route = `/${value}`.replace(/\/{2,}/g, "/");
|
|
7614
|
-
if (route.length > 1) route = route.replace(/\/$/, "");
|
|
7615
|
-
return route;
|
|
7616
|
-
}
|
|
7617
7781
|
function countTerms(tokens, required) {
|
|
7618
7782
|
const counts = /* @__PURE__ */ new Map();
|
|
7619
7783
|
for (const token of tokens) {
|
|
@@ -7656,130 +7820,67 @@ function excerpt(text, terms) {
|
|
|
7656
7820
|
return `${start > 0 ? "\u2026" : ""}${text.slice(start, end).trim()}${end < text.length ? "\u2026" : ""}`;
|
|
7657
7821
|
}
|
|
7658
7822
|
|
|
7659
|
-
// src/
|
|
7660
|
-
var WINDOW_MS = 6e4;
|
|
7661
|
-
var CLIENT_REQUESTS_PER_WINDOW = 12;
|
|
7662
|
-
var APP_REQUESTS_PER_WINDOW = 120;
|
|
7663
|
-
var MAX_CONCURRENT_REQUESTS = 4;
|
|
7664
|
-
var LEASE_TTL_MS = 2 * 6e4;
|
|
7665
|
-
var DocsAssistantLimiter = class {
|
|
7666
|
-
sql;
|
|
7667
|
-
constructor(state) {
|
|
7668
|
-
this.sql = state.storage.sql;
|
|
7669
|
-
this.sql.exec(`
|
|
7670
|
-
CREATE TABLE IF NOT EXISTS request_windows (
|
|
7671
|
-
scope TEXT NOT NULL,
|
|
7672
|
-
client_key TEXT NOT NULL,
|
|
7673
|
-
window INTEGER NOT NULL,
|
|
7674
|
-
count INTEGER NOT NULL,
|
|
7675
|
-
PRIMARY KEY (scope, client_key, window)
|
|
7676
|
-
);
|
|
7677
|
-
CREATE TABLE IF NOT EXISTS leases (
|
|
7678
|
-
id TEXT PRIMARY KEY,
|
|
7679
|
-
expires_at INTEGER NOT NULL
|
|
7680
|
-
);
|
|
7681
|
-
`);
|
|
7682
|
-
}
|
|
7683
|
-
async fetch(request) {
|
|
7684
|
-
const url = new URL(request.url);
|
|
7685
|
-
if (request.method !== "POST") return Response.json({ error: "method_not_allowed" }, { status: 405 });
|
|
7686
|
-
if (url.pathname === "/acquire") return this.acquire(request);
|
|
7687
|
-
if (url.pathname === "/release") return this.release(request);
|
|
7688
|
-
return Response.json({ error: "not_found" }, { status: 404 });
|
|
7689
|
-
}
|
|
7690
|
-
async acquire(request) {
|
|
7691
|
-
const body = await request.json().catch(() => null);
|
|
7692
|
-
if (!body || typeof body.clientKey !== "string" || body.clientKey.length !== 64) {
|
|
7693
|
-
return Response.json({ error: "invalid_client_key" }, { status: 400 });
|
|
7694
|
-
}
|
|
7695
|
-
const now = Date.now();
|
|
7696
|
-
const window = Math.floor(now / WINDOW_MS);
|
|
7697
|
-
this.sql.exec("DELETE FROM request_windows WHERE window < ?", window - 1);
|
|
7698
|
-
this.sql.exec("DELETE FROM leases WHERE expires_at <= ?", now);
|
|
7699
|
-
if (!this.consumeWindow("client", body.clientKey, window, CLIENT_REQUESTS_PER_WINDOW)) {
|
|
7700
|
-
return Response.json({ ok: false, reason: "client_rate" }, { status: 429 });
|
|
7701
|
-
}
|
|
7702
|
-
if (!this.consumeWindow("app", "all", window, APP_REQUESTS_PER_WINDOW)) {
|
|
7703
|
-
return Response.json({ ok: false, reason: "app_rate" }, { status: 429 });
|
|
7704
|
-
}
|
|
7705
|
-
const concurrent = this.sql.exec("SELECT COUNT(*) AS count FROM leases").one().count;
|
|
7706
|
-
if (concurrent >= MAX_CONCURRENT_REQUESTS) {
|
|
7707
|
-
return Response.json({ ok: false, reason: "app_concurrency" }, { status: 429 });
|
|
7708
|
-
}
|
|
7709
|
-
const leaseId = crypto.randomUUID();
|
|
7710
|
-
this.sql.exec("INSERT INTO leases (id, expires_at) VALUES (?, ?)", leaseId, now + LEASE_TTL_MS);
|
|
7711
|
-
return Response.json({ ok: true, leaseId });
|
|
7712
|
-
}
|
|
7713
|
-
async release(request) {
|
|
7714
|
-
const body = await request.json().catch(() => null);
|
|
7715
|
-
if (!body || typeof body.leaseId !== "string") {
|
|
7716
|
-
return Response.json({ error: "invalid_lease_id" }, { status: 400 });
|
|
7717
|
-
}
|
|
7718
|
-
this.sql.exec("DELETE FROM leases WHERE id = ?", body.leaseId);
|
|
7719
|
-
return Response.json({ ok: true });
|
|
7720
|
-
}
|
|
7721
|
-
consumeWindow(scope, clientKey, window, limit) {
|
|
7722
|
-
const existing = this.sql.exec(
|
|
7723
|
-
"SELECT count FROM request_windows WHERE scope = ? AND client_key = ? AND window = ?",
|
|
7724
|
-
scope,
|
|
7725
|
-
clientKey,
|
|
7726
|
-
window
|
|
7727
|
-
).toArray()[0]?.count ?? 0;
|
|
7728
|
-
if (existing >= limit) return false;
|
|
7729
|
-
this.sql.exec(
|
|
7730
|
-
`INSERT INTO request_windows (scope, client_key, window, count)
|
|
7731
|
-
VALUES (?, ?, ?, 1)
|
|
7732
|
-
ON CONFLICT(scope, client_key, window) DO UPDATE SET count = count + 1`,
|
|
7733
|
-
scope,
|
|
7734
|
-
clientKey,
|
|
7735
|
-
window
|
|
7736
|
-
);
|
|
7737
|
-
return true;
|
|
7738
|
-
}
|
|
7739
|
-
};
|
|
7740
|
-
|
|
7741
|
-
// src/server/utils/docs-published-corpus.ts
|
|
7823
|
+
// src/documentation/worker/published-corpus.ts
|
|
7742
7824
|
var cachedCorpus;
|
|
7743
|
-
async function
|
|
7744
|
-
const response = await env.ASSETS.fetch(
|
|
7825
|
+
async function loadDocumentationPublishedManifest(env, requestUrl = "https://assets.local/", assetBasePath = "/_documentation") {
|
|
7826
|
+
const response = await env.ASSETS.fetch(
|
|
7827
|
+
new Request(new URL(`${assetBasePath}/manifest.json`, requestUrl))
|
|
7828
|
+
);
|
|
7745
7829
|
if (!response.ok) return null;
|
|
7746
7830
|
const value = await response.json().catch(() => null);
|
|
7747
7831
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
7748
7832
|
const manifest = value;
|
|
7749
7833
|
const assistant = parseAssistantPolicy(manifest.assistant);
|
|
7750
|
-
|
|
7834
|
+
const mcp = parseMcpPolicy(manifest.mcp);
|
|
7835
|
+
const routes = parsePublishedRoutes(manifest.routes);
|
|
7836
|
+
const resources = parsePublishedPaths(manifest.resources, true);
|
|
7837
|
+
if (manifest.version !== DOCUMENTATION_MANIFEST_VERSION || typeof manifest.sourceHash !== "string" || typeof manifest.name !== "string" || !assistant || !mcp || !routes || !resources)
|
|
7838
|
+
return null;
|
|
7751
7839
|
return {
|
|
7752
7840
|
sourceHash: manifest.sourceHash,
|
|
7753
7841
|
name: manifest.name,
|
|
7842
|
+
routes,
|
|
7843
|
+
resources,
|
|
7754
7844
|
assistant,
|
|
7755
|
-
|
|
7756
|
-
// docs bundle is rebuilt by an MCP-aware SDK release.
|
|
7757
|
-
mcp: parseMcpPolicy(manifest.mcp) ?? { access: "disabled" }
|
|
7845
|
+
mcp
|
|
7758
7846
|
};
|
|
7759
7847
|
}
|
|
7760
|
-
|
|
7848
|
+
function parsePublishedRoutes(value) {
|
|
7849
|
+
return parsePublishedPaths(value, false);
|
|
7850
|
+
}
|
|
7851
|
+
function parsePublishedPaths(value, allowEmpty) {
|
|
7852
|
+
if (!Array.isArray(value) || !allowEmpty && value.length === 0) return null;
|
|
7853
|
+
if (value.some((path) => typeof path !== "string" || !path.startsWith("/"))) return null;
|
|
7854
|
+
if (value.some((path) => canonicalNativeDocumentationRequestPath(path) !== path))
|
|
7855
|
+
return null;
|
|
7856
|
+
return [...new Set(value)];
|
|
7857
|
+
}
|
|
7858
|
+
async function loadDocumentationPublishedCorpus(env, sourceHash, requestUrl = "https://assets.local/", assetBasePath = "/_documentation") {
|
|
7761
7859
|
if (cachedCorpus?.sourceHash === sourceHash) return cachedCorpus.chunks;
|
|
7762
7860
|
const response = await env.ASSETS.fetch(
|
|
7763
|
-
new URL(
|
|
7861
|
+
new Request(new URL(`${assetBasePath}/assistant-index.json`, requestUrl))
|
|
7764
7862
|
);
|
|
7765
7863
|
if (!response.ok) throw new Error(`assistant-index.json returned ${response.status}`);
|
|
7766
|
-
const chunks =
|
|
7864
|
+
const chunks = parseDocumentationCorpus(await response.json());
|
|
7767
7865
|
cachedCorpus = { sourceHash, chunks };
|
|
7768
7866
|
return chunks;
|
|
7769
7867
|
}
|
|
7770
|
-
async function
|
|
7868
|
+
async function resolveDocumentationPublishedCorpus(env, sourceHash, requestUrl = "https://assets.local/", assetBasePath = "/_documentation") {
|
|
7771
7869
|
try {
|
|
7772
|
-
return {
|
|
7870
|
+
return {
|
|
7871
|
+
ok: true,
|
|
7872
|
+
chunks: await loadDocumentationPublishedCorpus(env, sourceHash, requestUrl, assetBasePath)
|
|
7873
|
+
};
|
|
7773
7874
|
} catch (error) {
|
|
7774
7875
|
return { ok: false, error };
|
|
7775
7876
|
}
|
|
7776
7877
|
}
|
|
7777
|
-
async function
|
|
7778
|
-
const route =
|
|
7878
|
+
async function readDocumentationPublishedPage(env, corpus, requestedRoute, requestUrl = "https://assets.local/", assetBasePath = "/_documentation") {
|
|
7879
|
+
const route = collapseRoute(requestedRoute.split("#", 1)[0] ?? "/");
|
|
7779
7880
|
const firstChunk = corpus.find((chunk) => chunk.route === route);
|
|
7780
7881
|
if (!firstChunk) return null;
|
|
7781
|
-
const pathname = route === "/" ?
|
|
7782
|
-
const response = await env.ASSETS.fetch(new URL(pathname, requestUrl)
|
|
7882
|
+
const pathname = route === "/" ? `${assetBasePath}/index.md` : `${assetBasePath}${route}.md`;
|
|
7883
|
+
const response = await env.ASSETS.fetch(new Request(new URL(pathname, requestUrl)));
|
|
7783
7884
|
if (!response.ok) return null;
|
|
7784
7885
|
const markdown = await response.text();
|
|
7785
7886
|
if (markdown.length > 5e5) throw new Error("Documentation page exceeds 500000 characters");
|
|
@@ -7801,40 +7902,38 @@ function parseMcpPolicy(value) {
|
|
|
7801
7902
|
return { access: policy.access };
|
|
7802
7903
|
}
|
|
7803
7904
|
|
|
7804
|
-
// src/
|
|
7805
|
-
async function
|
|
7905
|
+
// src/documentation/worker/public-limiter.ts
|
|
7906
|
+
async function documentationPublicClientKey(value) {
|
|
7806
7907
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
7807
7908
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7808
7909
|
}
|
|
7809
|
-
async function
|
|
7810
|
-
|
|
7811
|
-
const
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7910
|
+
async function checkDocumentationPublicLimit(env, clientKey, logPrefix) {
|
|
7911
|
+
const clientLimiter = env.DOCUMENTATION_CLIENT_RATE_LIMITER;
|
|
7912
|
+
const appLimiter = env.DOCUMENTATION_APP_RATE_LIMITER;
|
|
7913
|
+
if (!clientLimiter || !appLimiter) return { ok: false, status: 503 };
|
|
7914
|
+
try {
|
|
7915
|
+
const client = await clientLimiter.limit({ key: `${env.DEEPSPACE_APP_ID}:${clientKey}` });
|
|
7916
|
+
if (!client.success) return { ok: false, status: 429 };
|
|
7917
|
+
const app = await appLimiter.limit({ key: env.DEEPSPACE_APP_ID });
|
|
7918
|
+
return app.success ? { ok: true } : { ok: false, status: 429 };
|
|
7919
|
+
} catch (error) {
|
|
7920
|
+
console.error(`[${logPrefix}] limiter unavailable: ${documentationLimiterErrorSummary(error)}`);
|
|
7921
|
+
return { ok: false, status: 503 };
|
|
7922
|
+
}
|
|
7923
|
+
}
|
|
7924
|
+
function documentationLimiterErrorSummary(error) {
|
|
7925
|
+
if (!error || typeof error !== "object") return String(error);
|
|
7926
|
+
const value = error;
|
|
7927
|
+
return JSON.stringify({
|
|
7928
|
+
name: typeof value.name === "string" ? value.name : void 0,
|
|
7929
|
+
message: typeof value.message === "string" ? value.message : void 0,
|
|
7930
|
+
retryable: value.retryable === true,
|
|
7931
|
+
overloaded: value.overloaded === true,
|
|
7932
|
+
cause: typeof value.cause === "string" ? value.cause : void 0
|
|
7820
7933
|
});
|
|
7821
|
-
if (!response?.ok) {
|
|
7822
|
-
return { ok: false, status: response?.status === 429 ? 429 : 503 };
|
|
7823
|
-
}
|
|
7824
|
-
const body = await response.json().catch(() => null);
|
|
7825
|
-
if (!body || typeof body.leaseId !== "string") return { ok: false, status: 503 };
|
|
7826
|
-
return { ok: true, lease: { leaseId: body.leaseId, stub } };
|
|
7827
|
-
}
|
|
7828
|
-
async function releaseDocsPublicLease(lease, logPrefix) {
|
|
7829
|
-
if (!lease) return;
|
|
7830
|
-
await lease.stub.fetch("https://docs-limiter/release", {
|
|
7831
|
-
method: "POST",
|
|
7832
|
-
headers: { "Content-Type": "application/json" },
|
|
7833
|
-
body: JSON.stringify({ leaseId: lease.leaseId })
|
|
7834
|
-
}).catch((error) => console.error(`[${logPrefix}] limiter release failed:`, error));
|
|
7835
7934
|
}
|
|
7836
7935
|
|
|
7837
|
-
// src/
|
|
7936
|
+
// src/documentation/worker/request-body.ts
|
|
7838
7937
|
var RequestBodyTooLargeError = class extends Error {
|
|
7839
7938
|
constructor(maxBytes) {
|
|
7840
7939
|
super(`Request body exceeds ${maxBytes} bytes`);
|
|
@@ -7868,15 +7967,14 @@ async function readBoundedRequestText(request, maxBytes) {
|
|
|
7868
7967
|
return text + decoder.decode();
|
|
7869
7968
|
}
|
|
7870
7969
|
|
|
7871
|
-
// src/
|
|
7970
|
+
// src/documentation/worker/assistant-routes.ts
|
|
7872
7971
|
var MAX_QUESTION_CHARS = 4e3;
|
|
7873
7972
|
var MAX_HISTORY_MESSAGES = 12;
|
|
7874
7973
|
var MAX_HISTORY_CHARS = 2e4;
|
|
7875
7974
|
var MAX_REQUEST_BYTES = 128 * 1024;
|
|
7876
|
-
function
|
|
7877
|
-
app.post(
|
|
7878
|
-
|
|
7879
|
-
const manifest = await loadDocsPublishedManifest(c.env, c.req.url);
|
|
7975
|
+
function registerDocumentationAssistantRoutes(app, resolveAuth, publicBasePath = "/docs", assetBasePath = "/_documentation") {
|
|
7976
|
+
app.post(`${publicBasePath}/api/ai`, async (c) => {
|
|
7977
|
+
const manifest = await loadDocumentationPublishedManifest(c.env, c.req.url, assetBasePath);
|
|
7880
7978
|
if (!manifest || manifest.assistant.access === "disabled") return c.notFound();
|
|
7881
7979
|
let authToken;
|
|
7882
7980
|
let publicClientKey;
|
|
@@ -7890,7 +7988,19 @@ function registerDocsAssistantRoutes(app, resolveAuth) {
|
|
|
7890
7988
|
if (!c.env.APP_OWNER_JWT) {
|
|
7891
7989
|
return c.json({ error: "Public documentation assistant billing is not configured" }, 503);
|
|
7892
7990
|
}
|
|
7893
|
-
publicClientKey = await
|
|
7991
|
+
publicClientKey = await documentationPublicClientKey(
|
|
7992
|
+
c.req.header("CF-Connecting-IP") ?? "unknown"
|
|
7993
|
+
);
|
|
7994
|
+
}
|
|
7995
|
+
const limitResult = publicClientKey ? await checkDocumentationPublicLimit(c.env, publicClientKey, "documentation-assistant") : void 0;
|
|
7996
|
+
if (limitResult && !limitResult.ok) {
|
|
7997
|
+
return c.json(
|
|
7998
|
+
{
|
|
7999
|
+
error: limitResult.status === 429 ? "Documentation assistant rate limit exceeded. Try again shortly." : "Documentation assistant is temporarily unavailable"
|
|
8000
|
+
},
|
|
8001
|
+
limitResult.status,
|
|
8002
|
+
limitResult.status === 429 ? { "Retry-After": "60" } : void 0
|
|
8003
|
+
);
|
|
7894
8004
|
}
|
|
7895
8005
|
let body = null;
|
|
7896
8006
|
try {
|
|
@@ -7913,22 +8023,27 @@ function registerDocsAssistantRoutes(app, resolveAuth) {
|
|
|
7913
8023
|
const route = typeof body?.route === "string" && body.route.startsWith("/") ? body.route.slice(0, 500) : void 0;
|
|
7914
8024
|
let history;
|
|
7915
8025
|
try {
|
|
7916
|
-
history =
|
|
8026
|
+
history = normalizeDocumentationAssistantHistory(body?.messages);
|
|
7917
8027
|
} catch (error) {
|
|
7918
8028
|
return c.json({ error: error instanceof Error ? error.message : "messages are invalid" }, 400);
|
|
7919
8029
|
}
|
|
7920
|
-
const corpusResult = await
|
|
8030
|
+
const corpusResult = await resolveDocumentationPublishedCorpus(
|
|
8031
|
+
c.env,
|
|
8032
|
+
manifest.sourceHash,
|
|
8033
|
+
c.req.url,
|
|
8034
|
+
assetBasePath
|
|
8035
|
+
);
|
|
7921
8036
|
if (!corpusResult.ok) {
|
|
7922
|
-
console.error("[
|
|
8037
|
+
console.error("[documentation-assistant] corpus unavailable:", corpusResult.error);
|
|
7923
8038
|
return c.json({ error: "Documentation corpus is unavailable" }, 503);
|
|
7924
8039
|
}
|
|
7925
8040
|
const corpus = corpusResult.chunks;
|
|
7926
|
-
const selectedModel = resolveDeepSpaceAgentModel(
|
|
7927
|
-
manifest.assistant.model,
|
|
7928
|
-
"documentation"
|
|
7929
|
-
);
|
|
8041
|
+
const selectedModel = resolveDeepSpaceAgentModel(manifest.assistant.model, "documentation");
|
|
7930
8042
|
if (!selectedModel) {
|
|
7931
|
-
console.error(
|
|
8043
|
+
console.error(
|
|
8044
|
+
"[documentation-assistant] unsupported configured model:",
|
|
8045
|
+
manifest.assistant.model
|
|
8046
|
+
);
|
|
7932
8047
|
return c.json({ error: "Documentation assistant model is not supported" }, 503);
|
|
7933
8048
|
}
|
|
7934
8049
|
const diagnosticContext = {
|
|
@@ -7936,66 +8051,40 @@ function registerDocsAssistantRoutes(app, resolveAuth) {
|
|
|
7936
8051
|
provider: selectedModel.provider,
|
|
7937
8052
|
modelId: selectedModel.modelId
|
|
7938
8053
|
};
|
|
7939
|
-
if (publicClientKey && !c.env.DOCS_ASSISTANT_LIMITER) {
|
|
7940
|
-
return c.json({ error: "Documentation assistant rate limiting is not configured" }, 503);
|
|
7941
|
-
}
|
|
7942
|
-
const leaseResult = publicClientKey ? await acquireDocsPublicLease(c.env, publicClientKey, "docs-assistant") : void 0;
|
|
7943
|
-
if (leaseResult && !leaseResult.ok) {
|
|
7944
|
-
return c.json(
|
|
7945
|
-
{
|
|
7946
|
-
error: leaseResult.status === 429 ? "Documentation assistant rate limit exceeded. Try again shortly." : "Documentation assistant is temporarily unavailable"
|
|
7947
|
-
},
|
|
7948
|
-
leaseResult.status,
|
|
7949
|
-
leaseResult.status === 429 ? { "Retry-After": "60" } : void 0
|
|
7950
|
-
);
|
|
7951
|
-
}
|
|
7952
|
-
const lease = leaseResult?.ok ? leaseResult.lease : void 0;
|
|
7953
|
-
let released = false;
|
|
7954
|
-
const releaseLease = async () => {
|
|
7955
|
-
if (!lease || released) return;
|
|
7956
|
-
released = true;
|
|
7957
|
-
await releaseDocsPublicLease(lease, "docs-assistant");
|
|
7958
|
-
};
|
|
7959
8054
|
let result;
|
|
7960
8055
|
try {
|
|
7961
8056
|
result = streamDeepSpaceAgent(c.env, {
|
|
7962
8057
|
profile: "documentation",
|
|
7963
8058
|
modelId: selectedModel.modelId,
|
|
7964
8059
|
...authToken ? { authToken } : {},
|
|
7965
|
-
system:
|
|
8060
|
+
system: buildDocumentationAssistantPrompt(manifest.name || c.env.APP_NAME, route),
|
|
7966
8061
|
messages: [...history, { role: "user", content: question }],
|
|
7967
|
-
tools:
|
|
8062
|
+
tools: buildDocumentationAssistantTools(corpus, publicBasePath),
|
|
7968
8063
|
abortSignal: c.req.raw.signal,
|
|
7969
|
-
onAbort: () => releaseLease(),
|
|
7970
|
-
onFinish: releaseLease,
|
|
7971
8064
|
onError: ({ error }) => {
|
|
7972
8065
|
console.error(
|
|
7973
|
-
`[
|
|
8066
|
+
`[documentation-assistant] stream error: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
|
|
7974
8067
|
);
|
|
7975
|
-
void releaseLease();
|
|
7976
8068
|
}
|
|
7977
8069
|
}).result;
|
|
7978
8070
|
} catch (error) {
|
|
7979
8071
|
console.error(
|
|
7980
|
-
`[
|
|
8072
|
+
`[documentation-assistant] agent unavailable: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
|
|
7981
8073
|
);
|
|
7982
|
-
await releaseLease();
|
|
7983
8074
|
return c.json({ error: "Documentation assistant is not configured" }, 503);
|
|
7984
8075
|
}
|
|
7985
8076
|
return result.toUIMessageStreamResponse({
|
|
7986
8077
|
sendReasoning: false,
|
|
7987
8078
|
onError: (error) => {
|
|
7988
8079
|
console.error(
|
|
7989
|
-
`[
|
|
8080
|
+
`[documentation-assistant] response error: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
|
|
7990
8081
|
);
|
|
7991
|
-
void releaseLease();
|
|
7992
8082
|
return "The documentation assistant could not complete this response.";
|
|
7993
8083
|
}
|
|
7994
8084
|
});
|
|
7995
8085
|
});
|
|
7996
8086
|
}
|
|
7997
|
-
|
|
7998
|
-
function normalizeDocsAssistantHistory(value) {
|
|
8087
|
+
function normalizeDocumentationAssistantHistory(value) {
|
|
7999
8088
|
if (value === void 0) return [];
|
|
8000
8089
|
if (!Array.isArray(value)) throw new TypeError("messages must be an array");
|
|
8001
8090
|
if (value.length > MAX_HISTORY_MESSAGES) {
|
|
@@ -8028,21 +8117,62 @@ function normalizeDocsAssistantHistory(value) {
|
|
|
8028
8117
|
}
|
|
8029
8118
|
return messages;
|
|
8030
8119
|
}
|
|
8031
|
-
function
|
|
8032
|
-
|
|
8033
|
-
const
|
|
8034
|
-
|
|
8035
|
-
|
|
8120
|
+
function registerDocumentationStaticRoutes(app, publicBasePath = "/docs", assetBasePath = "/_documentation") {
|
|
8121
|
+
const serve = async (c, next) => {
|
|
8122
|
+
const manifest = await loadDocumentationPublishedManifest(c.env, c.req.url, assetBasePath);
|
|
8123
|
+
if (!manifest) return next();
|
|
8124
|
+
const requestUrl = new URL(c.req.url);
|
|
8125
|
+
const pathname = requestUrl.pathname;
|
|
8126
|
+
const publishedPath = documentationRequestPath(pathname, publicBasePath);
|
|
8127
|
+
let response;
|
|
8128
|
+
let status;
|
|
8129
|
+
if (publishedPath && isPublishedDocumentationRequest(publishedPath, manifest.routes, manifest.resources)) {
|
|
8130
|
+
requestUrl.pathname = documentationAssetPath(publishedPath, manifest.resources, assetBasePath);
|
|
8131
|
+
response = await fetchDocumentationAsset(c.env, c.req.raw, requestUrl, assetBasePath);
|
|
8132
|
+
status = response.status;
|
|
8133
|
+
} else {
|
|
8134
|
+
response = new Response(null, { status: 404 });
|
|
8135
|
+
status = 404;
|
|
8136
|
+
}
|
|
8036
8137
|
if (status === 404) {
|
|
8037
8138
|
const url = new URL(c.req.url);
|
|
8038
|
-
url.pathname =
|
|
8039
|
-
response = await c.env
|
|
8139
|
+
url.pathname = `${assetBasePath}/404.html`;
|
|
8140
|
+
response = await fetchDocumentationAsset(c.env, c.req.raw, url, assetBasePath);
|
|
8040
8141
|
status = 404;
|
|
8041
8142
|
}
|
|
8042
|
-
return
|
|
8043
|
-
}
|
|
8143
|
+
return secureDocumentationResponse(response, status, pathname);
|
|
8144
|
+
};
|
|
8145
|
+
app.get(publicBasePath || "/", serve);
|
|
8146
|
+
app.get(`${publicBasePath}/*`, serve);
|
|
8147
|
+
}
|
|
8148
|
+
function isPublishedDocumentationRequest(publishedPath, routes, resources) {
|
|
8149
|
+
return routes.includes(publishedPath) || resources.includes(publishedPath);
|
|
8150
|
+
}
|
|
8151
|
+
function documentationRequestPath(pathname, publicBasePath) {
|
|
8152
|
+
const logical = pathname === publicBasePath || !publicBasePath && pathname === "/" ? "/" : pathname.slice(publicBasePath.length) || "/";
|
|
8153
|
+
const clean = logical === "/" ? "/" : logical.replace(/\/$/, "");
|
|
8154
|
+
return canonicalNativeDocumentationRequestPath(clean);
|
|
8155
|
+
}
|
|
8156
|
+
async function fetchDocumentationAsset(env, request, initialUrl, assetBasePath) {
|
|
8157
|
+
let url = initialUrl;
|
|
8158
|
+
for (let redirects = 0; redirects < 3; redirects++) {
|
|
8159
|
+
const response = await env.ASSETS.fetch(new Request(url, request));
|
|
8160
|
+
const location = response.headers.get("Location");
|
|
8161
|
+
if (response.status < 300 || response.status >= 400 || !location) return response;
|
|
8162
|
+
const next = new URL(location, url);
|
|
8163
|
+
if (next.origin !== url.origin || !next.pathname.startsWith(`${assetBasePath}/`)) {
|
|
8164
|
+
return new Response("Invalid documentation asset redirect", { status: 502 });
|
|
8165
|
+
}
|
|
8166
|
+
await response.body?.cancel();
|
|
8167
|
+
url = next;
|
|
8168
|
+
}
|
|
8169
|
+
return new Response("Documentation asset redirect loop", { status: 508 });
|
|
8170
|
+
}
|
|
8171
|
+
function documentationAssetPath(publishedPath, resources, assetBasePath) {
|
|
8172
|
+
if (publishedPath === "/") return `${assetBasePath}/index.html`;
|
|
8173
|
+
return resources.includes(publishedPath) ? `${assetBasePath}${publishedPath}` : `${assetBasePath}${publishedPath}/index.html`;
|
|
8044
8174
|
}
|
|
8045
|
-
function
|
|
8175
|
+
function secureDocumentationResponse(response, status, pathname) {
|
|
8046
8176
|
const headers = new Headers(response.headers);
|
|
8047
8177
|
if (status !== 404 && pathname.endsWith(".md")) {
|
|
8048
8178
|
headers.set("Content-Type", "text/markdown; charset=utf-8");
|
|
@@ -8062,7 +8192,14 @@ function secureDocsResponse(response, status, pathname) {
|
|
|
8062
8192
|
return new Response(response.body, { status, headers });
|
|
8063
8193
|
}
|
|
8064
8194
|
|
|
8065
|
-
// src/
|
|
8195
|
+
// src/documentation/text.ts
|
|
8196
|
+
function documentationSubject(name) {
|
|
8197
|
+
const normalized = name.trim();
|
|
8198
|
+
if (!normalized || /^documentation$/i.test(normalized)) return "documentation";
|
|
8199
|
+
return /(?:\bdocs?|documentation)$/i.test(normalized) ? normalized : `${normalized} documentation`;
|
|
8200
|
+
}
|
|
8201
|
+
|
|
8202
|
+
// src/documentation/worker/mcp/protocol.ts
|
|
8066
8203
|
var MCP_SERVER_VERSION = "1.0.0";
|
|
8067
8204
|
var CURRENT_PROTOCOL_VERSION = "2025-11-25";
|
|
8068
8205
|
var SUPPORTED_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PROTOCOL_VERSION, "2025-06-18", "2025-03-26"]);
|
|
@@ -8077,11 +8214,11 @@ function initializeResponse(request) {
|
|
|
8077
8214
|
resources: { subscribe: false, listChanged: false }
|
|
8078
8215
|
},
|
|
8079
8216
|
serverInfo: {
|
|
8080
|
-
name: "DeepSpace
|
|
8217
|
+
name: "DeepSpace Documentation",
|
|
8081
8218
|
title: "DeepSpace documentation",
|
|
8082
8219
|
version: MCP_SERVER_VERSION
|
|
8083
8220
|
},
|
|
8084
|
-
instructions: "Search the published
|
|
8221
|
+
instructions: "Search the published documentation before answering, then read exact pages when full context is needed."
|
|
8085
8222
|
});
|
|
8086
8223
|
}
|
|
8087
8224
|
function hasValidOrigin(request) {
|
|
@@ -8156,10 +8293,10 @@ function mcpSecurityHeaders() {
|
|
|
8156
8293
|
};
|
|
8157
8294
|
}
|
|
8158
8295
|
|
|
8159
|
-
// src/
|
|
8160
|
-
var
|
|
8296
|
+
// src/documentation/worker/mcp/tools.ts
|
|
8297
|
+
var DOCUMENTATION_TOOLS = [
|
|
8161
8298
|
{
|
|
8162
|
-
name: "
|
|
8299
|
+
name: "documentation_search",
|
|
8163
8300
|
title: "Search documentation",
|
|
8164
8301
|
description: "Search the published documentation and return ranked, linked excerpts.",
|
|
8165
8302
|
inputSchema: {
|
|
@@ -8184,7 +8321,7 @@ var DOCS_TOOLS = [
|
|
|
8184
8321
|
}
|
|
8185
8322
|
},
|
|
8186
8323
|
{
|
|
8187
|
-
name: "
|
|
8324
|
+
name: "documentation_read",
|
|
8188
8325
|
title: "Read documentation page",
|
|
8189
8326
|
description: "Read one complete published documentation page as Markdown.",
|
|
8190
8327
|
inputSchema: {
|
|
@@ -8213,59 +8350,67 @@ var DOCS_TOOLS = [
|
|
|
8213
8350
|
}
|
|
8214
8351
|
}
|
|
8215
8352
|
];
|
|
8216
|
-
async function
|
|
8353
|
+
async function callDocumentationTool(rawRequest, request, corpus, env, basePath = "/docs", assetBasePath = "/_documentation") {
|
|
8217
8354
|
const params = objectValue(request.params);
|
|
8218
8355
|
const name = typeof params?.name === "string" ? params.name : "";
|
|
8219
8356
|
const args = objectValue(params?.arguments) ?? {};
|
|
8220
8357
|
const origin = new URL(rawRequest.url).origin;
|
|
8221
|
-
if (name === "
|
|
8358
|
+
if (name === "documentation_search") {
|
|
8222
8359
|
const query = typeof args.query === "string" ? args.query.trim() : "";
|
|
8223
8360
|
const limit = args.limit === void 0 ? 6 : args.limit;
|
|
8224
8361
|
if (query.length < 2 || query.length > 300 || !Number.isInteger(limit) || Number(limit) < 1 || Number(limit) > 10) {
|
|
8225
|
-
return mcpError(request.id, -32602, "Invalid
|
|
8362
|
+
return mcpError(request.id, -32602, "Invalid documentation_search arguments");
|
|
8226
8363
|
}
|
|
8227
8364
|
const value = {
|
|
8228
|
-
results:
|
|
8365
|
+
results: searchDocumentationCorpus(corpus, query, Number(limit)).map((result) => ({
|
|
8229
8366
|
...result,
|
|
8230
|
-
url: new URL(result.route, origin).toString()
|
|
8367
|
+
url: new URL(documentationPublicPath(basePath, result.route), origin).toString()
|
|
8231
8368
|
}))
|
|
8232
8369
|
};
|
|
8233
8370
|
return toolResult(request.id, value);
|
|
8234
8371
|
}
|
|
8235
|
-
if (name === "
|
|
8372
|
+
if (name === "documentation_read") {
|
|
8236
8373
|
const route = typeof args.route === "string" ? args.route.trim() : "";
|
|
8237
8374
|
if (!route.startsWith("/") || route.length > 500) {
|
|
8238
|
-
return mcpError(request.id, -32602, "Invalid
|
|
8375
|
+
return mcpError(request.id, -32602, "Invalid documentation_read arguments");
|
|
8239
8376
|
}
|
|
8240
8377
|
try {
|
|
8241
|
-
const page = await
|
|
8378
|
+
const page = await readDocumentationPublishedPage(
|
|
8379
|
+
env,
|
|
8380
|
+
corpus,
|
|
8381
|
+
route,
|
|
8382
|
+
rawRequest.url,
|
|
8383
|
+
assetBasePath
|
|
8384
|
+
);
|
|
8242
8385
|
if (!page) return toolError(request.id, `No published documentation page exists at ${route}`);
|
|
8243
8386
|
return toolResult(request.id, {
|
|
8244
8387
|
...page,
|
|
8245
|
-
url: new URL(page.route, origin).toString()
|
|
8388
|
+
url: new URL(documentationPublicPath(basePath, page.route), origin).toString()
|
|
8246
8389
|
});
|
|
8247
8390
|
} catch (error) {
|
|
8248
|
-
console.error("[
|
|
8391
|
+
console.error("[documentation-mcp] page unavailable:", error);
|
|
8249
8392
|
return toolError(request.id, "The documentation page could not be read");
|
|
8250
8393
|
}
|
|
8251
8394
|
}
|
|
8252
8395
|
return mcpError(request.id, -32602, `Unknown documentation tool: ${name || "<missing>"}`);
|
|
8253
8396
|
}
|
|
8254
8397
|
|
|
8255
|
-
// src/
|
|
8398
|
+
// src/documentation/worker/mcp-routes.ts
|
|
8256
8399
|
var MAX_REQUEST_CHARS = 64e3;
|
|
8257
8400
|
var MAX_REQUEST_BYTES2 = MAX_REQUEST_CHARS * 4;
|
|
8258
8401
|
var MAX_SKILL_CHARS = 256e3;
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8402
|
+
var DOCUMENTATION_BASE_PATH = "/docs";
|
|
8403
|
+
function registerDocumentationMcpRoutes(app, publicBasePath = DOCUMENTATION_BASE_PATH, assetBasePath = "/_documentation") {
|
|
8404
|
+
const mcpPath = documentationPublicPath(publicBasePath, "/mcp");
|
|
8405
|
+
app.get(mcpPath, async (c) => {
|
|
8406
|
+
if (!await publicManifest(c.req.raw, c.env, assetBasePath)) return c.notFound();
|
|
8262
8407
|
return new Response(null, {
|
|
8263
8408
|
status: 405,
|
|
8264
8409
|
headers: { Allow: "POST", ...mcpSecurityHeaders() }
|
|
8265
8410
|
});
|
|
8266
8411
|
});
|
|
8267
|
-
app.post(
|
|
8268
|
-
const manifest = await publicManifest(c.req.raw, c.env);
|
|
8412
|
+
app.post(mcpPath, async (c) => {
|
|
8413
|
+
const manifest = await publicManifest(c.req.raw, c.env, assetBasePath);
|
|
8269
8414
|
if (!manifest) return c.notFound();
|
|
8270
8415
|
if (!hasValidOrigin(c.req.raw)) {
|
|
8271
8416
|
return mcpError(null, -32e3, "Invalid Origin", 403);
|
|
@@ -8297,67 +8442,89 @@ function registerDocsMcpRoutes(app) {
|
|
|
8297
8442
|
if (request.method === "initialize") return initializeResponse(requestWithId);
|
|
8298
8443
|
if (request.method === "ping") return mcpResult(requestId, {});
|
|
8299
8444
|
if (request.method === "tools/list") {
|
|
8300
|
-
return mcpResult(requestId, { tools:
|
|
8445
|
+
return mcpResult(requestId, { tools: DOCUMENTATION_TOOLS });
|
|
8301
8446
|
}
|
|
8302
8447
|
if (request.method === "resources/list") {
|
|
8303
|
-
return mcpResult(requestId, {
|
|
8448
|
+
return mcpResult(requestId, {
|
|
8449
|
+
resources: [skillResource(c.req.raw, manifest, publicBasePath)]
|
|
8450
|
+
});
|
|
8304
8451
|
}
|
|
8305
8452
|
if (request.method === "resources/read") {
|
|
8306
|
-
return readSkillResource(
|
|
8453
|
+
return readSkillResource(
|
|
8454
|
+
c.req.raw,
|
|
8455
|
+
requestWithId,
|
|
8456
|
+
manifest,
|
|
8457
|
+
c.env,
|
|
8458
|
+
publicBasePath,
|
|
8459
|
+
assetBasePath
|
|
8460
|
+
);
|
|
8307
8461
|
}
|
|
8308
8462
|
if (request.method !== "tools/call") {
|
|
8309
8463
|
return mcpError(requestId, -32601, `Method not found: ${request.method}`);
|
|
8310
8464
|
}
|
|
8311
|
-
const
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
const corpus = corpusResult.chunks;
|
|
8317
|
-
const clientKey = await docsPublicClientKey(c.req.header("CF-Connecting-IP") ?? "unknown");
|
|
8318
|
-
const leaseResult = await acquireDocsPublicLease(c.env, clientKey, "docs-mcp");
|
|
8319
|
-
if (!leaseResult.ok) {
|
|
8465
|
+
const clientKey = await documentationPublicClientKey(
|
|
8466
|
+
c.req.header("CF-Connecting-IP") ?? "unknown"
|
|
8467
|
+
);
|
|
8468
|
+
const limitResult = await checkDocumentationPublicLimit(c.env, clientKey, "documentation-mcp");
|
|
8469
|
+
if (!limitResult.ok) {
|
|
8320
8470
|
return mcpError(
|
|
8321
8471
|
requestId,
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8472
|
+
limitResult.status === 429 ? -32029 : -32002,
|
|
8473
|
+
limitResult.status === 429 ? "Documentation tool rate limit exceeded. Try again shortly." : "Documentation tools are temporarily unavailable",
|
|
8474
|
+
limitResult.status,
|
|
8475
|
+
limitResult.status === 429 ? { "Retry-After": "60" } : void 0
|
|
8326
8476
|
);
|
|
8327
8477
|
}
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8478
|
+
const corpusResult = await resolveDocumentationPublishedCorpus(
|
|
8479
|
+
c.env,
|
|
8480
|
+
manifest.sourceHash,
|
|
8481
|
+
c.req.url,
|
|
8482
|
+
assetBasePath
|
|
8483
|
+
);
|
|
8484
|
+
if (!corpusResult.ok) {
|
|
8485
|
+
console.error("[documentation-mcp] corpus unavailable:", corpusResult.error);
|
|
8486
|
+
return mcpError(requestId, -32002, "Documentation corpus is unavailable", 503);
|
|
8332
8487
|
}
|
|
8488
|
+
const corpus = corpusResult.chunks;
|
|
8489
|
+
return callDocumentationTool(
|
|
8490
|
+
c.req.raw,
|
|
8491
|
+
requestWithId,
|
|
8492
|
+
corpus,
|
|
8493
|
+
c.env,
|
|
8494
|
+
publicBasePath,
|
|
8495
|
+
assetBasePath
|
|
8496
|
+
);
|
|
8333
8497
|
});
|
|
8334
8498
|
for (const path of [
|
|
8335
8499
|
"/.well-known/mcp",
|
|
8336
8500
|
"/.well-known/mcp.json",
|
|
8337
8501
|
"/.well-known/mcp/server-card.json"
|
|
8338
8502
|
]) {
|
|
8339
|
-
app.get(path, async (c) => {
|
|
8340
|
-
const manifest = await publicManifest(c.req.raw, c.env);
|
|
8503
|
+
app.get(documentationPublicPath(publicBasePath, path), async (c) => {
|
|
8504
|
+
const manifest = await publicManifest(c.req.raw, c.env, assetBasePath);
|
|
8341
8505
|
if (!manifest) return c.notFound();
|
|
8342
|
-
return discoveryResponse(c.req.raw, manifest);
|
|
8506
|
+
return discoveryResponse(c.req.raw, manifest, publicBasePath);
|
|
8343
8507
|
});
|
|
8344
8508
|
}
|
|
8345
|
-
app.get(
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8509
|
+
app.get(
|
|
8510
|
+
documentationPublicPath(publicBasePath, "/.well-known/mcp/server-cards.json"),
|
|
8511
|
+
async (c) => {
|
|
8512
|
+
const manifest = await publicManifest(c.req.raw, c.env, assetBasePath);
|
|
8513
|
+
if (!manifest) return c.notFound();
|
|
8514
|
+
return jsonResponse({ servers: [serverCard(c.req.raw, manifest, publicBasePath)] }, 200, {
|
|
8515
|
+
"Cache-Control": "public, max-age=300"
|
|
8516
|
+
});
|
|
8517
|
+
}
|
|
8518
|
+
);
|
|
8352
8519
|
}
|
|
8353
|
-
async function readSkillResource(rawRequest, request, manifest, env) {
|
|
8520
|
+
async function readSkillResource(rawRequest, request, manifest, env, publicBasePath, assetBasePath) {
|
|
8354
8521
|
const params = objectValue(request.params);
|
|
8355
8522
|
const requestedUri = typeof params?.uri === "string" ? params.uri : "";
|
|
8356
|
-
const resource = skillResource(rawRequest, manifest);
|
|
8523
|
+
const resource = skillResource(rawRequest, manifest, publicBasePath);
|
|
8357
8524
|
if (requestedUri !== resource.uri) {
|
|
8358
8525
|
return mcpError(request.id, -32602, "Unknown documentation resource");
|
|
8359
8526
|
}
|
|
8360
|
-
const assetUrl = new URL(
|
|
8527
|
+
const assetUrl = new URL(`${assetBasePath}/skill.md`, rawRequest.url);
|
|
8361
8528
|
const response = await env.ASSETS.fetch(new Request(assetUrl, { method: "GET" }));
|
|
8362
8529
|
if (!response.ok) return mcpError(request.id, -32002, "Documentation skill is unavailable", 503);
|
|
8363
8530
|
const text = await response.text();
|
|
@@ -8368,158 +8535,83 @@ async function readSkillResource(rawRequest, request, manifest, env) {
|
|
|
8368
8535
|
contents: [{ uri: resource.uri, mimeType: resource.mimeType, text }]
|
|
8369
8536
|
});
|
|
8370
8537
|
}
|
|
8371
|
-
function skillResource(request, manifest) {
|
|
8538
|
+
function skillResource(request, manifest, publicBasePath) {
|
|
8539
|
+
const subject = documentationSubject(manifest.name);
|
|
8372
8540
|
return {
|
|
8373
|
-
uri: new URL(
|
|
8374
|
-
name: `${
|
|
8541
|
+
uri: new URL(`${publicBasePath}/skill.md`, request.url).toString(),
|
|
8542
|
+
name: `${subject} skill`,
|
|
8375
8543
|
title: `Use ${manifest.name}`,
|
|
8376
|
-
description: `Agent instructions grounded in the published ${
|
|
8544
|
+
description: `Agent instructions grounded in the published ${subject}.`,
|
|
8377
8545
|
mimeType: "text/markdown"
|
|
8378
8546
|
};
|
|
8379
8547
|
}
|
|
8380
|
-
async function publicManifest(request, env) {
|
|
8381
|
-
|
|
8382
|
-
const manifest = await loadDocsPublishedManifest(env, request.url);
|
|
8548
|
+
async function publicManifest(request, env, assetBasePath) {
|
|
8549
|
+
const manifest = await loadDocumentationPublishedManifest(env, request.url, assetBasePath);
|
|
8383
8550
|
return manifest?.mcp.access === "public" ? manifest : null;
|
|
8384
8551
|
}
|
|
8385
|
-
function discoveryResponse(request, manifest) {
|
|
8386
|
-
return jsonResponse(serverCard(request, manifest), 200, {
|
|
8552
|
+
function discoveryResponse(request, manifest, publicBasePath) {
|
|
8553
|
+
return jsonResponse(serverCard(request, manifest, publicBasePath), 200, {
|
|
8387
8554
|
"Cache-Control": "public, max-age=300"
|
|
8388
8555
|
});
|
|
8389
8556
|
}
|
|
8390
|
-
function serverCard(request, manifest) {
|
|
8557
|
+
function serverCard(request, manifest, publicBasePath) {
|
|
8391
8558
|
return {
|
|
8392
8559
|
name: manifest.name,
|
|
8393
|
-
description: `Search and read the published ${manifest.name}
|
|
8560
|
+
description: `Search and read the published ${documentationSubject(manifest.name)}.`,
|
|
8394
8561
|
version: MCP_SERVER_VERSION,
|
|
8395
8562
|
protocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
8396
8563
|
transport: {
|
|
8397
8564
|
type: "streamable-http",
|
|
8398
|
-
url: new URL(
|
|
8565
|
+
url: new URL(`${publicBasePath}/mcp`, request.url).toString()
|
|
8399
8566
|
},
|
|
8400
8567
|
authentication: { required: false },
|
|
8401
8568
|
capabilities: {
|
|
8402
|
-
tools:
|
|
8403
|
-
|
|
8569
|
+
tools: DOCUMENTATION_TOOLS.map(({ name, title, description }) => ({
|
|
8570
|
+
name,
|
|
8571
|
+
title,
|
|
8572
|
+
description
|
|
8573
|
+
})),
|
|
8574
|
+
resources: [skillResource(request, manifest, publicBasePath)]
|
|
8404
8575
|
}
|
|
8405
8576
|
};
|
|
8406
8577
|
}
|
|
8407
8578
|
|
|
8408
|
-
// src/
|
|
8409
|
-
function
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
ds.writeDataPoint({
|
|
8428
|
-
indexes: [env.OWNER_USER_ID ?? "unknown"],
|
|
8429
|
-
blobs: [
|
|
8430
|
-
env.DEEPSPACE_RESOURCE_ID ?? env.APP_NAME ?? "unknown",
|
|
8431
|
-
kind,
|
|
8432
|
-
fields.id ?? "",
|
|
8433
|
-
fields.op ?? ""
|
|
8434
|
-
],
|
|
8435
|
-
doubles: [fields.units ?? 0, fields.count ?? 0]
|
|
8579
|
+
// src/documentation/worker/routes.ts
|
|
8580
|
+
function registerDeepSpaceDocumentation(app, options) {
|
|
8581
|
+
app.use(
|
|
8582
|
+
"*",
|
|
8583
|
+
async (c, next) => isPrivateDocumentationPath(new URL(c.req.url).pathname) ? c.notFound() : next()
|
|
8584
|
+
);
|
|
8585
|
+
const domains = new Set(
|
|
8586
|
+
options.config?.domains?.map((domain) => domain.trim().toLowerCase()) ?? []
|
|
8587
|
+
);
|
|
8588
|
+
if (domains.size) {
|
|
8589
|
+
const root = new Hono();
|
|
8590
|
+
registerDocumentationAssistantRoutes(root, options.resolveAuth, "", "/_documentation-root");
|
|
8591
|
+
registerDocumentationMcpRoutes(root, "", "/_documentation-root");
|
|
8592
|
+
registerDocumentationStaticRoutes(root, "", "/_documentation-root");
|
|
8593
|
+
app.all("*", (c, next) => {
|
|
8594
|
+
const url = new URL(c.req.url);
|
|
8595
|
+
if (!domains.has(url.hostname.toLowerCase()) || url.pathname === "/api/auth" || url.pathname.startsWith("/api/auth/"))
|
|
8596
|
+
return next();
|
|
8597
|
+
return root.fetch(c.req.raw, c.env);
|
|
8436
8598
|
});
|
|
8437
|
-
return true;
|
|
8438
|
-
} catch {
|
|
8439
|
-
return false;
|
|
8440
8599
|
}
|
|
8600
|
+
registerDocumentationAssistantRoutes(app, options.resolveAuth);
|
|
8601
|
+
registerDocumentationMcpRoutes(app);
|
|
8602
|
+
registerDocumentationStaticRoutes(app);
|
|
8441
8603
|
}
|
|
8442
|
-
function
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
ok = meterUsage(env, "ai", { id: model, op: "output", units: outChars, count: calls }) && ok;
|
|
8452
|
-
}
|
|
8453
|
-
if (inChars === 0 && outChars === 0) {
|
|
8454
|
-
ok = meterUsage(env, "ai", { id: model, op: "call", units: 0, count: calls }) && ok;
|
|
8455
|
-
}
|
|
8456
|
-
return ok;
|
|
8457
|
-
}
|
|
8458
|
-
function meterVectorize(env, indexName, op, fields = {}) {
|
|
8459
|
-
const vectors = fields.vectors ?? 0;
|
|
8460
|
-
const dims = fields.dims ?? 0;
|
|
8461
|
-
const stored = fields.storedCount ?? 0;
|
|
8462
|
-
const units = op === "query" ? (vectors + stored) * dims : vectors * dims;
|
|
8463
|
-
return meterUsage(env, "vectorize", { id: indexName, op, units, count: vectors });
|
|
8464
|
-
}
|
|
8465
|
-
var CHARS_PER_TOKEN = 4;
|
|
8466
|
-
var AI_EMBED_USD_PER_M_TOKENS = 0.012;
|
|
8467
|
-
var VECTORIZE_QUERIED_USD_PER_M_DIMS = 0.01;
|
|
8468
|
-
var VECTORIZE_STORED_USD_PER_100M_DIMS = 0.05;
|
|
8469
|
-
var COST_RATES = {
|
|
8470
|
-
ai: {
|
|
8471
|
-
/**
|
|
8472
|
-
* USD per character of *embedding input* (bge-m3 / qwen3-embedding tier).
|
|
8473
|
-
* Renamed from `perChar` to make explicit that this rate does NOT
|
|
8474
|
-
* apply to LLM-generation output — see `priceBindingUsageEvent`.
|
|
8475
|
-
*/
|
|
8476
|
-
embedInputPerChar: AI_EMBED_USD_PER_M_TOKENS / 1e6 / CHARS_PER_TOKEN
|
|
8477
|
-
},
|
|
8478
|
-
vectorize: {
|
|
8479
|
-
/** USD per queried dimension (per query, per stored vector compared). */
|
|
8480
|
-
queriedPerDim: VECTORIZE_QUERIED_USD_PER_M_DIMS / 1e6,
|
|
8481
|
-
/** USD per stored dimension per month. */
|
|
8482
|
-
storedPerDimPerMonth: VECTORIZE_STORED_USD_PER_100M_DIMS / 1e8
|
|
8483
|
-
}
|
|
8484
|
-
};
|
|
8485
|
-
function priceBindingUsageEvent(kind, op, units) {
|
|
8486
|
-
if (kind === "ai" && op === "input") {
|
|
8487
|
-
return units * COST_RATES.ai.embedInputPerChar;
|
|
8488
|
-
}
|
|
8489
|
-
if (kind === "vectorize" && op === "query") {
|
|
8490
|
-
return units * COST_RATES.vectorize.queriedPerDim;
|
|
8491
|
-
}
|
|
8492
|
-
return 0;
|
|
8493
|
-
}
|
|
8494
|
-
|
|
8495
|
-
// src/server/utils/d1-migrations.ts
|
|
8496
|
-
var META_TABLE = "_dpc_migrations";
|
|
8497
|
-
var META_BOOTSTRAP_SQL = `CREATE TABLE IF NOT EXISTS ${META_TABLE} (idx INTEGER PRIMARY KEY, applied_at TEXT NOT NULL)`;
|
|
8498
|
-
async function runMigrations(db, migrations) {
|
|
8499
|
-
await db.prepare(META_BOOTSTRAP_SQL).run();
|
|
8500
|
-
const row = await db.prepare(`SELECT COUNT(*) AS n FROM ${META_TABLE}`).first();
|
|
8501
|
-
const fromVersion = row?.n ?? 0;
|
|
8502
|
-
if (fromVersion >= migrations.length) {
|
|
8503
|
-
return { fromVersion, toVersion: fromVersion, applied: 0 };
|
|
8504
|
-
}
|
|
8505
|
-
let current = fromVersion;
|
|
8506
|
-
for (let i = fromVersion; i < migrations.length; i++) {
|
|
8507
|
-
const statements = splitStatements(migrations[i]);
|
|
8508
|
-
for (const stmt of statements) {
|
|
8509
|
-
try {
|
|
8510
|
-
await db.prepare(stmt).run();
|
|
8511
|
-
} catch (err) {
|
|
8512
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
8513
|
-
throw new Error(`Migration ${i} failed (db at version=${current}): ${msg}`);
|
|
8514
|
-
}
|
|
8515
|
-
}
|
|
8516
|
-
await db.prepare(`INSERT INTO ${META_TABLE} (idx, applied_at) VALUES (?, ?)`).bind(i, (/* @__PURE__ */ new Date()).toISOString()).run();
|
|
8517
|
-
current = i + 1;
|
|
8604
|
+
function isPrivateDocumentationPath(pathname) {
|
|
8605
|
+
let decoded = pathname;
|
|
8606
|
+
for (let round = 0; round < 3; round += 1) {
|
|
8607
|
+
const next = decoded.replace(
|
|
8608
|
+
/%([0-9a-f]{2})/giu,
|
|
8609
|
+
(_sequence, hex) => String.fromCharCode(Number.parseInt(hex, 16))
|
|
8610
|
+
);
|
|
8611
|
+
if (next === decoded) break;
|
|
8612
|
+
decoded = next;
|
|
8518
8613
|
}
|
|
8519
|
-
return {
|
|
8520
|
-
}
|
|
8521
|
-
function splitStatements(sql) {
|
|
8522
|
-
return sql.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8614
|
+
return /^\/_documentation(?:-root)?(?:\/|$)/u.test(decoded.replace(/\/{2,}/gu, "/"));
|
|
8523
8615
|
}
|
|
8524
8616
|
|
|
8525
8617
|
// src/server/auth/jwtVerifier.ts
|
|
@@ -8975,7 +9067,6 @@ export {
|
|
|
8975
9067
|
DIRECTORY_SCHEMAS,
|
|
8976
9068
|
DeepSpaceAgentModelError,
|
|
8977
9069
|
DeepSpaceAgentProfileError,
|
|
8978
|
-
DocsAssistantLimiter,
|
|
8979
9070
|
GLOBAL_DO_TYPES,
|
|
8980
9071
|
GLOBAL_DO_TYPE_NAMES,
|
|
8981
9072
|
GameRoom,
|
|
@@ -9011,7 +9102,6 @@ export {
|
|
|
9011
9102
|
VOTING_SCHEMAS,
|
|
9012
9103
|
WORKSPACE_SCHEMAS,
|
|
9013
9104
|
YjsRoom,
|
|
9014
|
-
acquireDocsPublicLease,
|
|
9015
9105
|
analyzePermissions,
|
|
9016
9106
|
apiWorkerFetch,
|
|
9017
9107
|
appendMessage,
|
|
@@ -9020,8 +9110,6 @@ export {
|
|
|
9020
9110
|
authWorkerFetch,
|
|
9021
9111
|
bindingManifestFromOutputConfig,
|
|
9022
9112
|
buildCronContext,
|
|
9023
|
-
buildDocsAssistantPrompt,
|
|
9024
|
-
buildDocsAssistantTools,
|
|
9025
9113
|
buildTableSelect,
|
|
9026
9114
|
buildUiParts,
|
|
9027
9115
|
callRegistry,
|
|
@@ -9050,9 +9138,6 @@ export {
|
|
|
9050
9138
|
deepSpaceAgentErrorSummary,
|
|
9051
9139
|
deleteChatCascade,
|
|
9052
9140
|
dispatch,
|
|
9053
|
-
docsAssistantErrorSummary,
|
|
9054
|
-
docsAssistantLimiterManifestEntry,
|
|
9055
|
-
docsPublicClientKey,
|
|
9056
9141
|
encode,
|
|
9057
9142
|
encodeAwarenessMessage,
|
|
9058
9143
|
encodeSyncStep1,
|
|
@@ -9070,15 +9155,12 @@ export {
|
|
|
9070
9155
|
handleSyncMessage,
|
|
9071
9156
|
hasContent,
|
|
9072
9157
|
isAutoProvision,
|
|
9073
|
-
isDocsResourcePath,
|
|
9074
9158
|
isOwner,
|
|
9075
9159
|
isTestAccountClaims,
|
|
9076
9160
|
isTestAccountEmail,
|
|
9077
9161
|
isTestAccountTier,
|
|
9078
9162
|
lintSchema,
|
|
9079
9163
|
listDeepSpaceAgentModels,
|
|
9080
|
-
loadDocsPublishedCorpus,
|
|
9081
|
-
loadDocsPublishedManifest,
|
|
9082
9164
|
loadMessages,
|
|
9083
9165
|
makeDefaultSummarizer,
|
|
9084
9166
|
matchOrigin,
|
|
@@ -9086,33 +9168,22 @@ export {
|
|
|
9086
9168
|
meterUsage,
|
|
9087
9169
|
meterVectorize,
|
|
9088
9170
|
noopPermissionContext,
|
|
9089
|
-
normalizeDocsAssistantHistory,
|
|
9090
|
-
normalizeDocsRoute,
|
|
9091
|
-
parseDocsCorpus,
|
|
9092
9171
|
parseQuotaLimit,
|
|
9093
9172
|
platformCorsPatterns,
|
|
9094
9173
|
platformWorkerFetch,
|
|
9095
9174
|
prepareMessagesWithCompaction,
|
|
9096
9175
|
priceBindingUsageEvent,
|
|
9097
|
-
readDocsPublishedPage,
|
|
9098
|
-
readDocsRoute,
|
|
9099
9176
|
readVarUint,
|
|
9100
9177
|
readVarUint8Array,
|
|
9101
9178
|
registerClientErrorRoute,
|
|
9102
|
-
|
|
9103
|
-
registerDocsAssistantRoutes,
|
|
9104
|
-
registerDocsMcpRoutes,
|
|
9105
|
-
registerDocsStaticRoutes,
|
|
9179
|
+
registerDeepSpaceDocumentation,
|
|
9106
9180
|
registryErrorJson,
|
|
9107
9181
|
registryStub,
|
|
9108
|
-
releaseDocsPublicLease,
|
|
9109
9182
|
resolveAppName,
|
|
9110
9183
|
resolveColumn,
|
|
9111
9184
|
resolveDeepSpaceAgentModel,
|
|
9112
|
-
resolveDocsPublishedCorpus,
|
|
9113
9185
|
rowToData,
|
|
9114
9186
|
runMigrations,
|
|
9115
|
-
searchDocsCorpus,
|
|
9116
9187
|
serverBuild,
|
|
9117
9188
|
streamDeepSpaceAgent,
|
|
9118
9189
|
timingSafeEqualHex,
|