@freema/drobek-modules 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WHKPW5MP.js → chunk-OCSLKNR2.js} +29 -14
- package/dist/index.js +1 -1
- package/dist/testing.js +49 -1
- package/package.json +2 -2
|
@@ -95,6 +95,8 @@ var CORE_ERROR_CODES = [
|
|
|
95
95
|
"domain_taken",
|
|
96
96
|
"domain_not_verified",
|
|
97
97
|
"dns_unavailable",
|
|
98
|
+
// proxy upstreams (MCP upstream tools)
|
|
99
|
+
"upstream_already_registered",
|
|
98
100
|
// compile.errors[]
|
|
99
101
|
"build_error",
|
|
100
102
|
"unresolved_import",
|
|
@@ -952,6 +954,20 @@ function hasControlBytes(chunk) {
|
|
|
952
954
|
return false;
|
|
953
955
|
}
|
|
954
956
|
|
|
957
|
+
// packages/core/dist/cookies.js
|
|
958
|
+
function readCookieValue(header, name) {
|
|
959
|
+
if (!header)
|
|
960
|
+
return null;
|
|
961
|
+
for (const part of header.split(";")) {
|
|
962
|
+
const eq20 = part.indexOf("=");
|
|
963
|
+
if (eq20 === -1)
|
|
964
|
+
continue;
|
|
965
|
+
if (part.slice(0, eq20).trim() === name)
|
|
966
|
+
return part.slice(eq20 + 1).trim();
|
|
967
|
+
}
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
|
|
955
971
|
// packages/core/dist/redis.js
|
|
956
972
|
import { Redis } from "ioredis";
|
|
957
973
|
var client = null;
|
|
@@ -2373,7 +2389,18 @@ var CONTROL_RE = new RegExp("[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]+", "
|
|
|
2373
2389
|
|
|
2374
2390
|
// packages/apps/dist/gallery.server.js
|
|
2375
2391
|
import { and as and12, asc as asc3, desc as desc6, eq as eq12, isNotNull as isNotNull7, isNull as isNull5, sql as sql8 } from "drizzle-orm";
|
|
2376
|
-
var itemColumns = {
|
|
2392
|
+
var itemColumns = {
|
|
2393
|
+
slug: apps.slug,
|
|
2394
|
+
name: apps.name,
|
|
2395
|
+
description: apps.galleryDescription,
|
|
2396
|
+
publishedAt: apps.publishedAt,
|
|
2397
|
+
// Names only, from the current saved configuration. A correlated aggregate
|
|
2398
|
+
// keeps pagination/counts intact and avoids a query per card. No extension
|
|
2399
|
+
// or module runtime dependency is needed by this public core API.
|
|
2400
|
+
modules: sql8`(select coalesce(jsonb_agg(${moduleConfigs.module} order by ${moduleConfigs.module}), '[]'::jsonb)
|
|
2401
|
+
from ${moduleConfigs} where ${moduleConfigs.appId} = ${apps.id}
|
|
2402
|
+
and jsonb_typeof(${moduleConfigs.config}) = 'object' and ${moduleConfigs.config} <> '{}'::jsonb)`
|
|
2403
|
+
};
|
|
2377
2404
|
|
|
2378
2405
|
// packages/modules/dist/limits.js
|
|
2379
2406
|
var LIMITS_CACHE_TTL_SEC = 60;
|
|
@@ -2570,20 +2597,8 @@ function endUserEpochKey(appId) {
|
|
|
2570
2597
|
}
|
|
2571
2598
|
var SESSION_PROVIDER_RE = /^[a-z][a-z0-9]{1,15}$/;
|
|
2572
2599
|
var SESSION_CONNECTION_RE = /^[A-Za-z0-9_-]{16,64}$/;
|
|
2573
|
-
function readCookie(header, name) {
|
|
2574
|
-
if (!header)
|
|
2575
|
-
return null;
|
|
2576
|
-
for (const part of header.split(";")) {
|
|
2577
|
-
const eq20 = part.indexOf("=");
|
|
2578
|
-
if (eq20 === -1)
|
|
2579
|
-
continue;
|
|
2580
|
-
if (part.slice(0, eq20).trim() === name)
|
|
2581
|
-
return part.slice(eq20 + 1).trim();
|
|
2582
|
-
}
|
|
2583
|
-
return null;
|
|
2584
|
-
}
|
|
2585
2600
|
function readEndUserToken(cookieHeader, secure) {
|
|
2586
|
-
const token =
|
|
2601
|
+
const token = readCookieValue(cookieHeader, endUserCookieName(secure));
|
|
2587
2602
|
return token && END_USER_TOKEN_RE.test(token) ? token : null;
|
|
2588
2603
|
}
|
|
2589
2604
|
function parseEndUserSession(raw) {
|
package/dist/index.js
CHANGED
package/dist/testing.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
runRoute,
|
|
24
24
|
sanitizeSubject,
|
|
25
25
|
workspaces
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-OCSLKNR2.js";
|
|
27
27
|
|
|
28
28
|
// packages/modules/dist/testing.js
|
|
29
29
|
import { existsSync } from "node:fs";
|
|
@@ -340,6 +340,48 @@ var TOOL_DOCS = [
|
|
|
340
340
|
returns: "{ removed:host, was_verified, was_primary, note }",
|
|
341
341
|
example: { app_id: "k3v9x0\u2026", host: "shop.example.com", user_confirmed: true }
|
|
342
342
|
},
|
|
343
|
+
{
|
|
344
|
+
name: "list_upstreams",
|
|
345
|
+
title: "List a workspace's proxy upstreams",
|
|
346
|
+
scope: "read (workspace-admin role in the workspace)",
|
|
347
|
+
description: "The external APIs registered for the proxy module in one workspace \u2014 the dashboard's workspace \u2192 Upstreams page: per upstream its `name` (what apps call it by), `base_url`, `allowed_methods`, `allowed_path_prefixes`, `auth_type` (none | bearer | header), `auth_header_name`, `has_secret` (never the secret) and `apps` \u2014 the apps whose assignment a workspace admin confirmed. Plus `upstreams_url`, the dashboard page. Workspace admins only (forbidden otherwise); get_app \u2192 modules.proxy.info shows any editor what one app may call. Read-only.",
|
|
348
|
+
annotations: READ_ONLY,
|
|
349
|
+
fields: [{ name: "workspace", type: "string", required: true, description: "The workspace slug." }],
|
|
350
|
+
returns: '{ workspace, upstreams:[{ name, base_url, allowed_methods, allowed_path_prefixes, auth_type:"none"|"bearer"|"header", auth_header_name, has_secret, apps:[slug], created_at }], upstreams_url }',
|
|
351
|
+
example: { workspace: "acme-crew" }
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "register_upstream",
|
|
355
|
+
title: "Register a proxy upstream",
|
|
356
|
+
scope: "write (workspace-admin role in the workspace)",
|
|
357
|
+
description: 'Register an external API for the proxy module in a workspace \u2014 what the dashboard\'s Upstreams page does, with the same checks: a public https or http base URL on port 80/443 (never a private address), the allowed HTTP methods and path prefixes apps may call under it. `auth_type: "none"` (an API without a key, e.g. https://pokeapi.co) registers at once. `bearer` / `header` need a key, and a key never passes through MCP: the answer is `registered: false` with `secret_url` \u2014 the Upstreams page with every field filled in; give the user that link, they paste the key and click Register. Never ask for the key in chat. A registered upstream does nothing yet: assign it to an app with configure_module(\'proxy\', { upstreams: { <name>: { rules: { call: "user" } } } }) \u2014 a workspace admin confirms that in the dashboard. A name the workspace already has answers upstream_already_registered.',
|
|
358
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
359
|
+
fields: [
|
|
360
|
+
{ name: "workspace", type: "string", required: true, description: "The workspace slug." },
|
|
361
|
+
{ name: "name", type: "string", required: true, description: "The name apps call it by, e.g. pokeapi." },
|
|
362
|
+
{ name: "base_url", type: "string", required: true, description: "The base URL, e.g. https://pokeapi.co." },
|
|
363
|
+
{ name: "allowed_methods", type: "string[]", required: true, description: 'e.g. ["GET"].' },
|
|
364
|
+
{ name: "allowed_path_prefixes", type: "string[]", required: true, description: 'e.g. ["/api/v2/"].' },
|
|
365
|
+
{ name: "auth_type", type: '"none" | "bearer" | "header"', required: true, description: "none registers now; bearer / header answer secret_url." },
|
|
366
|
+
{ name: "auth_header_name", type: "string (header only)", required: false, description: "The header that carries the key, e.g. X-Api-Key." }
|
|
367
|
+
],
|
|
368
|
+
returns: "{ registered:true, upstream:{ name, base_url, allowed_methods, allowed_path_prefixes, auth_type, auth_header_name, has_secret, apps, created_at }, next } \u2014 or { registered:false, name, secret_url, note } for bearer / header",
|
|
369
|
+
example: { workspace: "acme-crew", name: "pokeapi", base_url: "https://pokeapi.co", allowed_methods: ["GET"], allowed_path_prefixes: ["/api/v2/"], auth_type: "none" }
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "remove_upstream",
|
|
373
|
+
title: "Remove a proxy upstream",
|
|
374
|
+
scope: "write (workspace-admin role in the workspace)",
|
|
375
|
+
description: "Delete a registered upstream and its stored key \u2014 the dashboard's Delete on the Upstreams page. Every app that calls it gets 404 upstream_not_registered at once, so it needs `user_confirmed: true` \u2014 set it ONLY after the user explicitly said yes to removing exactly this upstream; without it the answer is user_confirmation_required with the `apps` that use it, and nothing changes. Registering the name again creates a new record: each app's assignment then needs a new confirmation.",
|
|
376
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
377
|
+
fields: [
|
|
378
|
+
{ name: "workspace", type: "string", required: true, description: "The workspace slug." },
|
|
379
|
+
{ name: "name", type: "string", required: true, description: "A registered upstream (list_upstreams lists them)." },
|
|
380
|
+
{ name: "user_confirmed", type: "boolean", required: false, description: "true ONLY after the user explicitly said yes to removing it." }
|
|
381
|
+
],
|
|
382
|
+
returns: "{ removed:name, apps:[slug], note }",
|
|
383
|
+
example: { workspace: "acme-crew", name: "pokeapi", user_confirmed: true }
|
|
384
|
+
},
|
|
343
385
|
{
|
|
344
386
|
name: "set_workspace_publishing",
|
|
345
387
|
title: "Set a workspace's publishing",
|
|
@@ -545,6 +587,12 @@ var ERROR_CATALOGUE = [
|
|
|
545
587
|
meaning: "A DNS lookup timed out or the resolver failed (SERVFAIL, network), so the records could not be checked. Nothing changed \u2014 an existing verification is kept.",
|
|
546
588
|
fix: "Try verify_domain again in a few minutes; if it keeps failing, the domain's DNS servers may be down \u2014 tell the user."
|
|
547
589
|
},
|
|
590
|
+
{
|
|
591
|
+
code: "upstream_already_registered",
|
|
592
|
+
surface: "MCP tool isError (register_upstream)",
|
|
593
|
+
meaning: "The workspace already has an upstream with this name. Nothing changed.",
|
|
594
|
+
fix: "list_upstreams shows it; use it as it is, pick another name, or remove_upstream it first (the user confirms) and register it again."
|
|
595
|
+
},
|
|
548
596
|
{
|
|
549
597
|
code: "internal_error",
|
|
550
598
|
surface: "MCP tool isError",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freema/drobek-modules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The drobek platform module contract, registry and runtime: defineModule, the ModuleContext and the test kit (@drobek/modules/testing).",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"LICENSE"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@drobek/sdk": "npm:@freema/drobek-sdk@0.
|
|
45
|
+
"@drobek/sdk": "npm:@freema/drobek-sdk@0.4.0",
|
|
46
46
|
"@paralleldrive/cuid2": "^2.2.2",
|
|
47
47
|
"esbuild": "^0.25.0",
|
|
48
48
|
"ioredis": "^5.6.0",
|