@bevel-software/platform-core-backend 0.7.4 → 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/LICENSE +202 -202
- package/THIRD-PARTY-NOTICES.md +428 -454
- package/dist/core/core-ports.d.ts +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +51 -7
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +5 -3
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +38 -21
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +8 -7
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +1 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +6 -6
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts +5 -5
- package/dist/modules/access/access-declarations.js +3 -3
- package/dist/modules/access/access-mutation.service.d.ts +3 -3
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +3 -3
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-splice.js +4 -4
- package/dist/modules/access/access-splice.js.map +1 -1
- package/dist/modules/access/access.routes.js +19 -19
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +2 -2
- package/dist/modules/access/creator-access.js +5 -5
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +18 -4
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +14 -4
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/code-mode/code-mode-names.d.ts +5 -13
- package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode-names.js +5 -27
- package/dist/modules/code-mode/code-mode-names.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +29 -7
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -52
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +33 -395
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +7 -0
- package/dist/modules/plugins/index.d.ts.map +1 -0
- package/dist/modules/plugins/index.js +6 -0
- package/dist/modules/plugins/index.js.map +1 -0
- package/dist/modules/plugins/join-proposals.d.ts +53 -0
- package/dist/modules/plugins/join-proposals.d.ts.map +1 -0
- package/dist/modules/plugins/join-proposals.js +67 -0
- package/dist/modules/plugins/join-proposals.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts +81 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-requests.service.js +135 -0
- package/dist/modules/plugins/join-requests.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +134 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.js +344 -0
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +106 -0
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.contract.js +36 -0
- package/dist/modules/plugins/plugins.contract.js.map +1 -0
- package/dist/modules/plugins/plugins.routes.d.ts +42 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.routes.js +379 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -0
- package/dist/modules/plugins/plugins.service.d.ts +60 -0
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.service.js +172 -0
- package/dist/modules/plugins/plugins.service.js.map +1 -0
- package/dist/modules/skills/pending-skills.service.d.ts +2 -2
- package/dist/modules/skills/pending-skills.service.js +7 -7
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +4 -4
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +1 -1
- package/dist/modules/skills/skills.service.js +5 -5
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +65 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js +276 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +92 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +328 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +38 -12
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +13 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +233 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +74 -37
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -3
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.js +2 -2
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +37 -9
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +17 -4
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +63 -9
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +1 -0
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +6 -1
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +2 -2
- package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
- package/dist/modules/workspace/kb-seed.service.js +43 -10
- package/dist/modules/workspace/kb-seed.service.js.map +1 -1
- package/dist/modules/workspace/plugins-migration.d.ts +50 -0
- package/dist/modules/workspace/plugins-migration.d.ts.map +1 -0
- package/dist/modules/workspace/plugins-migration.js +379 -0
- package/dist/modules/workspace/plugins-migration.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.js +3 -3
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +26 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +83 -12
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/shared/kb-layout.test.js +3 -3
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/utcp-namespace.d.ts +6 -27
- package/dist/shared/utcp-namespace.d.ts.map +1 -1
- package/dist/shared/utcp-namespace.js +6 -63
- package/dist/shared/utcp-namespace.js.map +1 -1
- package/dist/shared/variable-refs.d.ts +42 -0
- package/dist/shared/variable-refs.d.ts.map +1 -0
- package/dist/shared/variable-refs.js +60 -0
- package/dist/shared/variable-refs.js.map +1 -0
- package/kb-template/.bevelignore +1 -1
- package/kb-template/AGENTS.md +88 -35
- package/kb-template/KnowledgeBase/How to get started.md +10 -10
- package/kb-template/access.md +36 -36
- package/migrations/meta/0000_snapshot.json +1479 -1479
- package/package.json +5 -4
- package/src/assets.ts +25 -25
- package/src/core/core-ports.ts +106 -106
- package/src/core/create-core-server.ts +55 -9
- package/src/core/create-core-services.ts +48 -24
- package/src/index.ts +69 -69
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -98
- package/src/modules/access/__tests__/access-declarations.test.ts +28 -28
- package/src/modules/access/__tests__/access-md-format.test.ts +18 -18
- package/src/modules/access/__tests__/access-mutation.service.test.ts +5 -5
- package/src/modules/access/__tests__/access-splice.test.ts +2 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +16 -16
- package/src/modules/access/__tests__/grant-sources.test.ts +12 -12
- package/src/modules/access/__tests__/roles-admin.service.test.ts +13 -1
- package/src/modules/access/access-control.interface.ts +8 -7
- package/src/modules/access/access-control.service.ts +7 -7
- package/src/modules/access/access-declarations.ts +5 -5
- package/src/modules/access/access-mutation.service.ts +3 -3
- package/src/modules/access/access-splice.ts +4 -4
- package/src/modules/access/access.routes.ts +20 -20
- package/src/modules/access/creator-access.ts +5 -5
- package/src/modules/access/roles-admin.service.ts +13 -2
- package/src/modules/admin/admin-access.routes.ts +29 -29
- package/src/modules/auth/__tests__/auth.routes.test.ts +91 -91
- package/src/modules/auth/__tests__/rate-limit.test.ts +36 -36
- package/src/modules/auth/rate-limit.ts +45 -45
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +67 -0
- package/src/modules/code-mode/code-mode-names.ts +10 -36
- package/src/modules/code-mode/code-mode.tool.ts +27 -7
- package/src/modules/database/connection.ts +15 -15
- package/src/modules/database/schema.ts +11 -11
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -150
- package/src/modules/mcp/mcp.service.ts +57 -435
- package/src/modules/{groups → plugins}/__tests__/join-proposals.test.ts +1 -1
- package/src/modules/{groups → plugins}/__tests__/join-requests.service.test.ts +7 -7
- package/src/modules/{groups/__tests__/group-index.service.test.ts → plugins/__tests__/plugin-index.service.test.ts} +41 -41
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +312 -0
- package/src/modules/{groups/__tests__/groups.routes.test.ts → plugins/__tests__/plugins.routes.test.ts} +100 -100
- package/src/modules/plugins/index.ts +17 -0
- package/src/modules/{groups → plugins}/join-proposals.ts +2 -2
- package/src/modules/{groups → plugins}/join-requests.service.ts +8 -8
- package/src/modules/{groups/group-provision.service.ts → plugins/plugin-provision.service.ts} +139 -69
- package/src/modules/{groups/groups.contract.ts → plugins/plugins.contract.ts} +26 -26
- package/src/modules/{groups/groups.routes.ts → plugins/plugins.routes.ts} +102 -102
- package/src/modules/{groups/groups.service.ts → plugins/plugins.service.ts} +43 -43
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -143
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +14 -14
- package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
- package/src/modules/skills/pending-skills.service.ts +7 -7
- package/src/modules/skills/skills.contract.ts +4 -4
- package/src/modules/skills/skills.service.ts +5 -5
- package/src/modules/tool-auth/llm-usage-meter.ts +19 -19
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +198 -0
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +346 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +101 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +3 -3
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +27 -27
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +3 -3
- package/src/modules/tool-manuals/mcp-json-discovery.ts +328 -0
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +434 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +35 -12
- package/src/modules/tool-manuals/tool-manuals.routes.ts +222 -1
- package/src/modules/tool-manuals/tool-manuals.service.ts +82 -42
- package/src/modules/tool-manuals/tool-manuals.tools.ts +6 -3
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +118 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/branch-name.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +13 -8
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/git.service.ts +2 -2
- package/src/modules/workflow/pending-commits.service.ts +48 -12
- package/src/modules/workflow/pending-commits.worker.ts +64 -8
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/workflow-hooks.ts +101 -101
- package/src/modules/workflow/workflow.service.ts +13 -2
- package/src/modules/workspace/__tests__/kb-seed.service.test.ts +81 -13
- package/src/modules/workspace/__tests__/plugins-migration.test.ts +427 -0
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -237
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -236
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -179
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -320
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -337
- package/src/modules/workspace/__tests__/workspace.service.test.ts +116 -0
- package/src/modules/workspace/bevel-ignore.ts +66 -66
- package/src/modules/workspace/kb-seed.service.ts +38 -9
- package/src/modules/workspace/plugins-migration.ts +479 -0
- package/src/modules/workspace/session-sink.ts +25 -25
- package/src/modules/workspace/workspace.routes.ts +3 -3
- package/src/modules/workspace/workspace.service.ts +85 -14
- package/src/modules/workspace/workspace.tools.ts +922 -922
- package/src/shared/__tests__/join-request.test.ts +13 -13
- package/src/shared/__tests__/kb-layout.plugin.test.ts +45 -0
- package/src/shared/kb-layout.test.ts +3 -3
- package/src/shared/utcp-namespace.ts +10 -68
- package/src/shared/variable-refs.ts +64 -0
- package/src/modules/groups/__tests__/group-provision.service.test.ts +0 -247
- package/src/modules/groups/index.ts +0 -17
- package/src/shared/__tests__/kb-layout.group.test.ts +0 -45
- /package/kb-template/{Groups → Plugins}/.gitkeep +0 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import '../auth/auth.middleware.js'; // Express Request.userId / userEmail augmentation
|
|
3
|
+
import { DEFAULT_BRANCH, joinBranchFor, } from '@bevel-software/platform-shared';
|
|
4
|
+
import { spliceGrant } from '../access/access-splice.js';
|
|
5
|
+
import { WorkflowDomainError } from '../workflow/workflow.errors.js';
|
|
6
|
+
import { pluginsWorkspaceId } from './plugins.service.js';
|
|
7
|
+
import { PluginProvisionError } from './plugin-provision.service.js';
|
|
8
|
+
/**
|
|
9
|
+
* Browser-facing (JWT) plugin routes, mounted behind `authMiddleware`:
|
|
10
|
+
*
|
|
11
|
+
* GET /api/plugins → { plugins: PluginSummary[] }
|
|
12
|
+
* DELETE /api/plugins/:name → { ok } (owners)
|
|
13
|
+
* POST /api/plugins/:name/join-request → { ok, number } (opens a CR)
|
|
14
|
+
* GET /api/plugins/:name/join-requests → { requests } (managers)
|
|
15
|
+
* POST /api/plugins/:name/join-requests/:n/reconcile → { closed }
|
|
16
|
+
*
|
|
17
|
+
* Enumeration is three verdicts per (caller, plugin), all of them ordinary
|
|
18
|
+
* access resolution — no special cases, no side tables:
|
|
19
|
+
*
|
|
20
|
+
* member canRead on the plugin FOLDER
|
|
21
|
+
* manager canWrite on the folder's access.md (admin-rescued)
|
|
22
|
+
* discoverable canRead on the access.md FILE — in the body-governed format
|
|
23
|
+
* its own `read: everyone` frontmatter grants this
|
|
24
|
+
*
|
|
25
|
+
* All three false ⇒ the plugin is absent from the response entirely.
|
|
26
|
+
*
|
|
27
|
+
* A join request is a plain change request whose branch edits the plugin's
|
|
28
|
+
* `access.md`. Managers do NOT merge it: they read its individual proposals
|
|
29
|
+
* (see `join-proposals.ts`), grant the ones they accept through the ordinary
|
|
30
|
+
* access path, and the request retires itself once its rules are a subset of
|
|
31
|
+
* the default branch's — reconciled here, lazily on listing and eagerly right
|
|
32
|
+
* after a grant. Nothing about the lifecycle is stored; it is derived from
|
|
33
|
+
* two copies of one file.
|
|
34
|
+
*
|
|
35
|
+
* Auth gating is explicit and uniform: `authMiddleware` at the mount, PLUS a
|
|
36
|
+
* `req.userEmail` check in every handler (the skills-routes pattern) so a
|
|
37
|
+
* middleware change can never silently un-gate one. Nothing here is reachable
|
|
38
|
+
* with an agent connection key or a manual-auth bearer.
|
|
39
|
+
*/
|
|
40
|
+
export function createPluginsRoutes(pluginIndex, accessControl, workflow, workspaceService, joinRequests, provision, kbDirName, resolveUser) {
|
|
41
|
+
const router = express.Router();
|
|
42
|
+
/** The folder-chain probe for MEMBERSHIP — the folder itself. */
|
|
43
|
+
const memberProbe = (folder) => folder;
|
|
44
|
+
/** The FILE probe for discovery/management — the folder's access.md. */
|
|
45
|
+
const accessMdOf = (folder) => `${folder}/access.md`;
|
|
46
|
+
const probesFor = (plugins) => [
|
|
47
|
+
...new Set(plugins.flatMap((g) => g.folders.flatMap((f) => [memberProbe(f), accessMdOf(f)]))),
|
|
48
|
+
];
|
|
49
|
+
/** The caller's open join CR for `plugin`, or null. */
|
|
50
|
+
const openJoinCr = (mine, email, plugin) => mine.find((cr) => cr.state === 'open' && cr.branch === joinBranchFor(email, plugin)) ?? null;
|
|
51
|
+
router.get('/plugins', async (req, res) => {
|
|
52
|
+
const email = req.userEmail;
|
|
53
|
+
if (!email) {
|
|
54
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const catalog = await pluginIndex.catalog();
|
|
59
|
+
if (catalog.length === 0) {
|
|
60
|
+
res.json({ plugins: [] });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const wsId = pluginsWorkspaceId();
|
|
64
|
+
const probes = probesFor(catalog);
|
|
65
|
+
const [readable, writable, owned] = await Promise.all([
|
|
66
|
+
accessControl.canReadBatch(wsId, email, probes),
|
|
67
|
+
accessControl.canWriteBatch(wsId, email, probes),
|
|
68
|
+
accessControl.canOwnerBatch(wsId, email, probes),
|
|
69
|
+
]);
|
|
70
|
+
// Fail closed on every verdict: a path missing from the map is denied.
|
|
71
|
+
const any = (map, g, probe) => g.folders.some((f) => map.get(probe(f)) === true);
|
|
72
|
+
// The caller's own open join CRs — drives `hasRequested`. A CR-listing
|
|
73
|
+
// hiccup must not take plugin enumeration down: degrade to "nothing
|
|
74
|
+
// requested" and let the next load repair it.
|
|
75
|
+
let mine = [];
|
|
76
|
+
try {
|
|
77
|
+
mine = await workflow.listChangeRequestsAuthoredBy(email);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.warn(`[plugins] join-request lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
+
}
|
|
82
|
+
const plugins = [];
|
|
83
|
+
for (const g of catalog) {
|
|
84
|
+
const member = any(readable, g, memberProbe);
|
|
85
|
+
const manager = any(writable, g, accessMdOf);
|
|
86
|
+
// The FOLDER verdict, owner-lists-only — the delete gate, mirrored
|
|
87
|
+
// here so the UI shows the verb to exactly the people the DELETE
|
|
88
|
+
// route will let through.
|
|
89
|
+
const owner = any(owned, g, memberProbe);
|
|
90
|
+
const discoverable = member || any(readable, g, accessMdOf);
|
|
91
|
+
if (!member && !manager && !discoverable)
|
|
92
|
+
continue; // absent — fail closed
|
|
93
|
+
const joinCr = member ? null : openJoinCr(mine, email, g.name);
|
|
94
|
+
plugins.push({
|
|
95
|
+
name: g.name,
|
|
96
|
+
folders: g.folders,
|
|
97
|
+
canRead: member,
|
|
98
|
+
canWrite: manager,
|
|
99
|
+
isOwner: owner,
|
|
100
|
+
skillCount: g.skillCount,
|
|
101
|
+
toolCount: g.toolCount,
|
|
102
|
+
owners: g.owners,
|
|
103
|
+
writers: g.writers,
|
|
104
|
+
readers: g.readers,
|
|
105
|
+
hasRequested: joinCr !== null,
|
|
106
|
+
requestNumber: joinCr?.number ?? null,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
res.json({ plugins });
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
console.error('[plugins] failed to list plugins:', err);
|
|
113
|
+
res.status(500).json({ error: 'Failed to list plugins' });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* Open (or return the existing) join change request for the caller.
|
|
118
|
+
*
|
|
119
|
+
* Idempotent via the deterministic branch name: a second click finds the
|
|
120
|
+
* open CR and returns it. Every step is an existing primitive — branch,
|
|
121
|
+
* splice, commit-and-push, open CR — so the security story is exactly the
|
|
122
|
+
* workflow's: draft branches are ungated, and the merge gate requires an
|
|
123
|
+
* approver who can write the touched access.md.
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* Create a plugin — the ONE door through which `Plugins/<name>/` folders come
|
|
127
|
+
* to exist. Any authenticated user may create one; that is the product
|
|
128
|
+
* model (making a plugin makes you the one who runs it), and the seeded
|
|
129
|
+
* access.md immediately fences the new folder off from everyone else. See
|
|
130
|
+
* `PluginProvisionService` for why this is an endpoint and not a write path.
|
|
131
|
+
*/
|
|
132
|
+
router.post('/plugins', async (req, res) => {
|
|
133
|
+
const user = await resolveUser(req);
|
|
134
|
+
if (!user) {
|
|
135
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// `req.body` is undefined when no JSON body was sent at all — that is a
|
|
139
|
+
// 400, not a destructuring crash.
|
|
140
|
+
const { name } = (req.body ?? {});
|
|
141
|
+
if (typeof name !== 'string') {
|
|
142
|
+
res.status(400).json({ error: 'name is required in body' });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const result = await provision.createPlugin(user, name);
|
|
147
|
+
res.status(201).json(result);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (err instanceof PluginProvisionError) {
|
|
151
|
+
res.status(err.status).json({ error: err.message });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.error('[plugins] create failed:', err);
|
|
155
|
+
res.status(500).json({ error: 'Failed to create the plugin' });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
/**
|
|
159
|
+
* Ensure the caller's personal folder (`Plugins/personal-<id>/`) exists —
|
|
160
|
+
* idempotent; the UI calls it lazily right before the first personal-skill
|
|
161
|
+
* write. Private by construction: its access.md names only the caller.
|
|
162
|
+
*/
|
|
163
|
+
router.post('/plugins/personal', async (req, res) => {
|
|
164
|
+
const user = await resolveUser(req);
|
|
165
|
+
if (!user) {
|
|
166
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
res.json(await provision.ensurePersonalPlugin(user));
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.error('[plugins] personal-folder ensure failed:', err);
|
|
174
|
+
res.status(500).json({ error: 'Failed to prepare your personal folder' });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
/**
|
|
178
|
+
* Delete a plugin — the OWNER's verb, and only theirs. Creating a plugin
|
|
179
|
+
* makes you the one who runs it; deleting it is the other end of that same
|
|
180
|
+
* promise, so the gate is the `owner` verdict on the folder (owner-lists
|
|
181
|
+
* only, no admin rescue) — a manager who merely writes the access.md, and
|
|
182
|
+
* an admin rescued into managing it, do not get it.
|
|
183
|
+
*
|
|
184
|
+
* Fail-closed like every other plugin surface: an unknown plugin and a plugin
|
|
185
|
+
* the caller does not own answer IDENTICALLY, so probing the endpoint can
|
|
186
|
+
* confirm nothing about what exists. The mechanism (park, one commit,
|
|
187
|
+
* rollback on refusal) lives in `PluginProvisionService.deletePlugin`.
|
|
188
|
+
*/
|
|
189
|
+
router.delete('/plugins/:name', async (req, res) => {
|
|
190
|
+
const email = req.userEmail;
|
|
191
|
+
if (!email) {
|
|
192
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const user = await resolveUser(req);
|
|
197
|
+
if (!user) {
|
|
198
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// Case-sensitive, like `pluginOfPath` — the plugin name IS the folder name.
|
|
202
|
+
const plugin = (await pluginIndex.catalog()).find((g) => g.name === req.params.name);
|
|
203
|
+
const wsId = pluginsWorkspaceId();
|
|
204
|
+
const ownerVerdicts = plugin
|
|
205
|
+
? await Promise.all(plugin.folders.map((f) => accessControl.canOwner(wsId, email, memberProbe(f))))
|
|
206
|
+
: [];
|
|
207
|
+
if (!plugin || !ownerVerdicts.some((v) => v === true)) {
|
|
208
|
+
res.status(404).json({ error: 'Unknown plugin', kind: 'unknown-plugin' });
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await provision.deletePlugin(user, plugin.name);
|
|
212
|
+
pluginIndex.invalidate();
|
|
213
|
+
res.json({ ok: true });
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
if (err instanceof PluginProvisionError) {
|
|
217
|
+
res.status(err.status).json({ error: err.message });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
console.error('[plugins] delete failed:', err);
|
|
221
|
+
res.status(500).json({ error: 'Failed to delete the plugin' });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
router.post('/plugins/:name/join-request', async (req, res) => {
|
|
225
|
+
const email = req.userEmail;
|
|
226
|
+
if (!email) {
|
|
227
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const user = await resolveUser(req);
|
|
232
|
+
if (!user) {
|
|
233
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const catalog = await pluginIndex.catalog();
|
|
237
|
+
// Case-sensitive, like `pluginOfPath` — the plugin name IS the folder name.
|
|
238
|
+
const plugin = catalog.find((g) => g.name === req.params.name);
|
|
239
|
+
const wsId = pluginsWorkspaceId();
|
|
240
|
+
// Same ANY-folder shape `GET /plugins` resolves with, so a plugin can
|
|
241
|
+
// never be listed as discoverable there and rejected as unknown here.
|
|
242
|
+
const verdicts = plugin
|
|
243
|
+
? await accessControl.canReadBatch(wsId, email, plugin.folders.flatMap((f) => [memberProbe(f), accessMdOf(f)]))
|
|
244
|
+
: new Map();
|
|
245
|
+
const any = (probe) => plugin?.folders.some((f) => verdicts.get(probe(f)) === true) ?? false;
|
|
246
|
+
// Discovery gate, fail-closed: an unknown plugin and a plugin the caller
|
|
247
|
+
// cannot discover answer IDENTICALLY, so probing can't confirm existence.
|
|
248
|
+
if (!plugin || !any(accessMdOf)) {
|
|
249
|
+
res.status(404).json({ error: 'Unknown plugin', kind: 'unknown-plugin' });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (any(memberProbe)) {
|
|
253
|
+
// Access landed between page load and click — reload, don't ask.
|
|
254
|
+
res.status(409).json({ error: 'You can already read this plugin', kind: 'already-readable' });
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// The grant is written to the plugin's primary folder — the one the
|
|
258
|
+
// summary's `folders[0]` names and the banner's touched-path check
|
|
259
|
+
// expects.
|
|
260
|
+
const folder = plugin.folders[0];
|
|
261
|
+
const branch = joinBranchFor(email, plugin.name);
|
|
262
|
+
const existing = openJoinCr(await workflow.listChangeRequestsAuthoredBy(email), email, plugin.name);
|
|
263
|
+
if (existing) {
|
|
264
|
+
res.json({ ok: true, number: existing.number });
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// A leftover branch from a rejected/withdrawn request is reused — the
|
|
268
|
+
// grant commit is already on it and the splice below no-ops.
|
|
269
|
+
try {
|
|
270
|
+
await workflow.createBranch(pluginsWorkspaceId(), branch, DEFAULT_BRANCH);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// exists (or raced) — proceed against it
|
|
274
|
+
}
|
|
275
|
+
const ws = await workspaceService.getOrCreateForBranch(branch);
|
|
276
|
+
const accessPath = `${kbDirName}/${accessMdOf(folder)}`;
|
|
277
|
+
const current = await workspaceService.readFile(ws.id, accessPath).catch(() => '');
|
|
278
|
+
const spliced = spliceGrant(current, 'read', { kind: 'user', email: user.email, displayName: user.name }, { target: 'folder' });
|
|
279
|
+
if (spliced.changed) {
|
|
280
|
+
await workspaceService.writeFile(ws.id, accessPath, spliced.text);
|
|
281
|
+
await workflow.commitChanges(ws.id, user, `Request access to ${plugin.name}`);
|
|
282
|
+
}
|
|
283
|
+
const detail = await workflow.openChangeRequest(ws.id, user, {
|
|
284
|
+
sourceBranch: branch,
|
|
285
|
+
targetBranch: DEFAULT_BRANCH,
|
|
286
|
+
title: `Join request: ${plugin.name}`,
|
|
287
|
+
description: `${user.name} asked to join ${plugin.name}. A manager of the plugin accepts by ` +
|
|
288
|
+
`granting the access this branch proposes; the request closes itself once ` +
|
|
289
|
+
`every proposal has landed.`,
|
|
290
|
+
});
|
|
291
|
+
res.json({ ok: true, number: detail.number });
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
if (err instanceof WorkflowDomainError) {
|
|
295
|
+
res.status(err.status).json({ error: err.message, ...(err.payload ?? {}) });
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
console.error('[plugins] failed to open a join request:', err);
|
|
299
|
+
res.status(500).json({ error: 'Failed to request access' });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
/**
|
|
303
|
+
* Resolve the caller as a MANAGER of `name` — the only role that may see or
|
|
304
|
+
* settle its join requests. Returns the plugin's primary folder, or null
|
|
305
|
+
* after answering the request.
|
|
306
|
+
*
|
|
307
|
+
* A non-manager gets an empty list rather than a 403 (the frontend asks
|
|
308
|
+
* unconditionally, exactly as it does for every other plugin surface), and
|
|
309
|
+
* an unknown plugin is indistinguishable from an unmanaged one.
|
|
310
|
+
*/
|
|
311
|
+
async function requireManager(req, res, onDenied) {
|
|
312
|
+
const email = req.userEmail;
|
|
313
|
+
if (!email) {
|
|
314
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const plugin = (await pluginIndex.catalog()).find((g) => g.name === req.params.name);
|
|
318
|
+
if (!plugin) {
|
|
319
|
+
onDenied();
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
const writable = await accessControl.canWriteBatch(pluginsWorkspaceId(), email, plugin.folders.map(accessMdOf));
|
|
323
|
+
if (!plugin.folders.some((f) => writable.get(accessMdOf(f)) === true)) {
|
|
324
|
+
onDenied();
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const user = await resolveUser(req);
|
|
328
|
+
if (!user) {
|
|
329
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
return { plugin, folder: plugin.folders[0], user };
|
|
333
|
+
}
|
|
334
|
+
router.get('/plugins/:name/join-requests', async (req, res) => {
|
|
335
|
+
try {
|
|
336
|
+
const ctx = await requireManager(req, res, () => res.json({ requests: [] }));
|
|
337
|
+
if (!ctx)
|
|
338
|
+
return;
|
|
339
|
+
const crs = await workflow.listChangeRequests();
|
|
340
|
+
res.json({
|
|
341
|
+
requests: await joinRequests.list(ctx.plugin.name, ctx.folder, crs, ctx.user),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
console.error('[plugins] failed to list join requests:', err);
|
|
346
|
+
res.status(500).json({ error: 'Failed to list join requests' });
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
/**
|
|
350
|
+
* Settle one request if its proposals have all landed on the default
|
|
351
|
+
* branch. Called right after a grant so the banner updates in the same
|
|
352
|
+
* round-trip; the listing does the same thing lazily, so skipping this (a
|
|
353
|
+
* dropped response, a closed tab) only delays it.
|
|
354
|
+
*/
|
|
355
|
+
router.post('/plugins/:name/join-requests/:number/reconcile', async (req, res) => {
|
|
356
|
+
try {
|
|
357
|
+
const ctx = await requireManager(req, res, () => res.status(404).json({ error: 'Not found' }));
|
|
358
|
+
if (!ctx)
|
|
359
|
+
return;
|
|
360
|
+
const crNumber = Number(req.params.number);
|
|
361
|
+
if (!Number.isSafeInteger(crNumber) || crNumber <= 0) {
|
|
362
|
+
res.status(400).json({ error: 'Invalid change request number' });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const cr = await workflow.getChangeRequest(crNumber);
|
|
366
|
+
if (!cr) {
|
|
367
|
+
res.status(404).json({ error: 'Not found' });
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
res.json({ closed: await joinRequests.reconcile(ctx.plugin.name, ctx.folder, cr, ctx.user) });
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
console.error('[plugins] failed to reconcile a join request:', err);
|
|
374
|
+
res.status(500).json({ error: 'Failed to update the request' });
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
return router;
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=plugins.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.routes.js","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
2
|
+
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
3
|
+
import type { ISkillService } from '../skills/skills.contract.js';
|
|
4
|
+
import type { IToolManualService } from '../tool-manuals/tool-manuals.contract.js';
|
|
5
|
+
import type { PluginCatalogEntry, IPluginIndexService } from './plugins.contract.js';
|
|
6
|
+
/**
|
|
7
|
+
* The plugin index: every plugin folder in the default-branch KB, with its
|
|
8
|
+
* caller-independent totals and access principals.
|
|
9
|
+
*
|
|
10
|
+
* Two decisions worth keeping straight:
|
|
11
|
+
*
|
|
12
|
+
* - **Existence is an `access.md`, counting is not.** A plugin EXISTS because
|
|
13
|
+
* its folder carries an `access.md` — the file the provisioning endpoint
|
|
14
|
+
* seeds and the one every per-caller verdict (member / manager /
|
|
15
|
+
* discoverable) resolves against. A bare directory under `Plugins/` is NOT
|
|
16
|
+
* a plugin: git cannot record an empty folder, so deleting a plugin's files
|
|
17
|
+
* leaves its directory behind on live checkouts, and enumerating by
|
|
18
|
+
* directory would resurrect every such ghost. Counts come from the
|
|
19
|
+
* already-cached global catalogs — `skillService.listSkills(undefined)` and
|
|
20
|
+
* `toolManualService.listAllSummaries()` — bucketed by `pluginOfPath`. A
|
|
21
|
+
* second `walkFiles` pass would re-read the same tree and could disagree
|
|
22
|
+
* with the catalogs about what counts as a skill.
|
|
23
|
+
* - **Loose files are not plugins.** `Plugins/slack.tool` sits directly under
|
|
24
|
+
* the root, so it is a file, not a directory — the same ≥3-segment rule
|
|
25
|
+
* `pluginOfPath` applies, arrived at from the other side.
|
|
26
|
+
*
|
|
27
|
+
* Cached for {@link CACHE_TTL_MS} and dropped by `invalidate()` from the
|
|
28
|
+
* file-change subscriber, so a grant committed on the default branch is
|
|
29
|
+
* reflected within one round-trip rather than one TTL.
|
|
30
|
+
*/
|
|
31
|
+
export declare class PluginIndexService implements IPluginIndexService {
|
|
32
|
+
private readonly workspaceService;
|
|
33
|
+
private readonly accessControl;
|
|
34
|
+
private readonly skillService;
|
|
35
|
+
private readonly toolManualService;
|
|
36
|
+
private readonly kbDirName;
|
|
37
|
+
private readonly cache;
|
|
38
|
+
constructor(workspaceService: WorkspaceService, accessControl: IAccessControl, skillService: ISkillService, toolManualService: IToolManualService, kbDirName: string, now?: () => number);
|
|
39
|
+
invalidate(): void;
|
|
40
|
+
catalog(): Promise<PluginCatalogEntry[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Degrades on ANY failure rather than throwing. The Library must never break
|
|
43
|
+
* because a plugin folder can't be read — same philosophy as the skill and
|
|
44
|
+
* tool-manual scanners, and the reason the route's 500 is reserved for the
|
|
45
|
+
* per-caller half.
|
|
46
|
+
*
|
|
47
|
+
* `null` is that degraded case and is deliberately NOT the same value as an
|
|
48
|
+
* empty array: a KB with no plugin folders returns `[]`, which is true and
|
|
49
|
+
* cacheable, while a failure returns `null`, which `catalog()` serves as `[]`
|
|
50
|
+
* without storing it.
|
|
51
|
+
*/
|
|
52
|
+
private build;
|
|
53
|
+
/** name → repo-relative plugin folder (always a single-element list). */
|
|
54
|
+
private scanFolders;
|
|
55
|
+
private countSkills;
|
|
56
|
+
private countTools;
|
|
57
|
+
}
|
|
58
|
+
/** The default-branch workspace id every plugin resolution runs against. */
|
|
59
|
+
export declare function pluginsWorkspaceId(): string;
|
|
60
|
+
//# sourceMappingURL=plugins.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.service.d.ts","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIrF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAI1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;gBAGpC,gBAAgB,EAAE,gBAAgB,EAClC,aAAa,EAAE,cAAc,EAC7B,YAAY,EAAE,aAAa,EAC3B,iBAAiB,EAAE,kBAAkB,EACrC,SAAS,EAAE,MAAM,EAClC,GAAG,GAAE,MAAM,MAAiB;IAK9B,UAAU,IAAI,IAAI;IAIZ,OAAO,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAkB7C;;;;;;;;;;MAUE;YACW,KAAK;IAyCnB,yEAAyE;YAC3D,WAAW;YAkCX,WAAW;YAMX,UAAU;CAGzB;AAaD,4EAA4E;AAC5E,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { DEFAULT_BRANCH, PLUGINS_DIR, pluginOfPath, isPersonalPluginFolder, } from '@bevel-software/platform-shared';
|
|
4
|
+
import { workspaceIdForBranch } from '../workspace/workspace.service.js';
|
|
5
|
+
import { TtlCache } from '../../shared/ttl-cache.js';
|
|
6
|
+
const CACHE_TTL_MS = 60_000;
|
|
7
|
+
/**
|
|
8
|
+
* The plugin index: every plugin folder in the default-branch KB, with its
|
|
9
|
+
* caller-independent totals and access principals.
|
|
10
|
+
*
|
|
11
|
+
* Two decisions worth keeping straight:
|
|
12
|
+
*
|
|
13
|
+
* - **Existence is an `access.md`, counting is not.** A plugin EXISTS because
|
|
14
|
+
* its folder carries an `access.md` — the file the provisioning endpoint
|
|
15
|
+
* seeds and the one every per-caller verdict (member / manager /
|
|
16
|
+
* discoverable) resolves against. A bare directory under `Plugins/` is NOT
|
|
17
|
+
* a plugin: git cannot record an empty folder, so deleting a plugin's files
|
|
18
|
+
* leaves its directory behind on live checkouts, and enumerating by
|
|
19
|
+
* directory would resurrect every such ghost. Counts come from the
|
|
20
|
+
* already-cached global catalogs — `skillService.listSkills(undefined)` and
|
|
21
|
+
* `toolManualService.listAllSummaries()` — bucketed by `pluginOfPath`. A
|
|
22
|
+
* second `walkFiles` pass would re-read the same tree and could disagree
|
|
23
|
+
* with the catalogs about what counts as a skill.
|
|
24
|
+
* - **Loose files are not plugins.** `Plugins/slack.tool` sits directly under
|
|
25
|
+
* the root, so it is a file, not a directory — the same ≥3-segment rule
|
|
26
|
+
* `pluginOfPath` applies, arrived at from the other side.
|
|
27
|
+
*
|
|
28
|
+
* Cached for {@link CACHE_TTL_MS} and dropped by `invalidate()` from the
|
|
29
|
+
* file-change subscriber, so a grant committed on the default branch is
|
|
30
|
+
* reflected within one round-trip rather than one TTL.
|
|
31
|
+
*/
|
|
32
|
+
export class PluginIndexService {
|
|
33
|
+
workspaceService;
|
|
34
|
+
accessControl;
|
|
35
|
+
skillService;
|
|
36
|
+
toolManualService;
|
|
37
|
+
kbDirName;
|
|
38
|
+
cache;
|
|
39
|
+
constructor(workspaceService, accessControl, skillService, toolManualService, kbDirName, now = Date.now) {
|
|
40
|
+
this.workspaceService = workspaceService;
|
|
41
|
+
this.accessControl = accessControl;
|
|
42
|
+
this.skillService = skillService;
|
|
43
|
+
this.toolManualService = toolManualService;
|
|
44
|
+
this.kbDirName = kbDirName;
|
|
45
|
+
this.cache = new TtlCache(CACHE_TTL_MS, now);
|
|
46
|
+
}
|
|
47
|
+
invalidate() {
|
|
48
|
+
this.cache.invalidate();
|
|
49
|
+
}
|
|
50
|
+
async catalog() {
|
|
51
|
+
const cached = this.cache.get();
|
|
52
|
+
if (cached)
|
|
53
|
+
return cached;
|
|
54
|
+
const entries = await this.build();
|
|
55
|
+
// A failed scan and a KB with genuinely no plugin folders both serve `[]`,
|
|
56
|
+
// but only the second is a fact worth holding for the TTL. Caching the
|
|
57
|
+
// failure would hide every plugin from every user for the full 60s AFTER
|
|
58
|
+
// the cause is gone — a clone mid-creation, a readdir that raced a
|
|
59
|
+
// checkout — and `invalidate()` only fires on a file-change event that may
|
|
60
|
+
// never arrive in that window. So a degraded read is served, not stored,
|
|
61
|
+
// and the next caller retries.
|
|
62
|
+
if (entries === null)
|
|
63
|
+
return [];
|
|
64
|
+
this.cache.set(entries);
|
|
65
|
+
return entries;
|
|
66
|
+
}
|
|
67
|
+
// --- internal --------------------------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* Degrades on ANY failure rather than throwing. The Library must never break
|
|
70
|
+
* because a plugin folder can't be read — same philosophy as the skill and
|
|
71
|
+
* tool-manual scanners, and the reason the route's 500 is reserved for the
|
|
72
|
+
* per-caller half.
|
|
73
|
+
*
|
|
74
|
+
* `null` is that degraded case and is deliberately NOT the same value as an
|
|
75
|
+
* empty array: a KB with no plugin folders returns `[]`, which is true and
|
|
76
|
+
* cacheable, while a failure returns `null`, which `catalog()` serves as `[]`
|
|
77
|
+
* without storing it.
|
|
78
|
+
*/
|
|
79
|
+
async build() {
|
|
80
|
+
try {
|
|
81
|
+
const wsId = (await this.workspaceService.getOrCreateForBranch(DEFAULT_BRANCH)).id;
|
|
82
|
+
const kbRoot = path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName);
|
|
83
|
+
const folders = await this.scanFolders(kbRoot);
|
|
84
|
+
if (folders.size === 0)
|
|
85
|
+
return [];
|
|
86
|
+
const [skillCounts, toolCounts] = await Promise.all([
|
|
87
|
+
this.countSkills(),
|
|
88
|
+
this.countTools(),
|
|
89
|
+
]);
|
|
90
|
+
const entries = [];
|
|
91
|
+
for (const [name, pluginFolders] of folders) {
|
|
92
|
+
// One folder, one access boundary — the folder IS the plugin.
|
|
93
|
+
const [primary] = pluginFolders;
|
|
94
|
+
const [owners, writers, readers] = await Promise.all([
|
|
95
|
+
this.accessControl.eligibleOwners(wsId, primary),
|
|
96
|
+
this.accessControl.eligibleWriters(wsId, primary),
|
|
97
|
+
this.accessControl.eligibleReaders(wsId, primary),
|
|
98
|
+
]);
|
|
99
|
+
entries.push({
|
|
100
|
+
name,
|
|
101
|
+
folders: pluginFolders,
|
|
102
|
+
skillCount: skillCounts.get(name) ?? 0,
|
|
103
|
+
toolCount: toolCounts.get(name) ?? 0,
|
|
104
|
+
owners,
|
|
105
|
+
writers,
|
|
106
|
+
readers,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
console.warn(`[plugins] plugin index unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** name → repo-relative plugin folder (always a single-element list). */
|
|
117
|
+
async scanFolders(kbRoot) {
|
|
118
|
+
const byName = new Map();
|
|
119
|
+
let children;
|
|
120
|
+
try {
|
|
121
|
+
children = await fs.readdir(path.join(kbRoot, PLUGINS_DIR), { withFileTypes: true });
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return byName; // a KB without a `Plugins/` root simply has no plugins
|
|
125
|
+
}
|
|
126
|
+
const candidates = children.filter((child) => child.isDirectory() &&
|
|
127
|
+
!child.name.startsWith('.') &&
|
|
128
|
+
// Personal folders live under Plugins/ but are not plugins: one exists
|
|
129
|
+
// per person, private by construction, and listing them would put a
|
|
130
|
+
// locked row per employee in everyone's index.
|
|
131
|
+
!isPersonalPluginFolder(child.name));
|
|
132
|
+
// A plugin exists exactly when its folder carries an `access.md` (see the
|
|
133
|
+
// class doc) — stat that file, don't trust the directory.
|
|
134
|
+
const verdicts = await Promise.all(candidates.map(async (child) => {
|
|
135
|
+
try {
|
|
136
|
+
return (await fs.stat(path.join(kbRoot, PLUGINS_DIR, child.name, 'access.md'))).isFile();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}));
|
|
142
|
+
candidates.forEach((child, i) => {
|
|
143
|
+
if (verdicts[i])
|
|
144
|
+
byName.set(child.name, [`${PLUGINS_DIR}/${child.name}`]);
|
|
145
|
+
});
|
|
146
|
+
return byName;
|
|
147
|
+
}
|
|
148
|
+
async countSkills() {
|
|
149
|
+
// `undefined` is the documented GLOBAL, unfiltered mode — counts are a
|
|
150
|
+
// property of the plugin, not of who is asking.
|
|
151
|
+
return bucketByPlugin(await this.skillService.listSkills(undefined));
|
|
152
|
+
}
|
|
153
|
+
async countTools() {
|
|
154
|
+
return bucketByPlugin(await this.toolManualService.listAllSummaries());
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Count items per plugin folder name; ungrouped items (`null`) count nowhere. */
|
|
158
|
+
function bucketByPlugin(items) {
|
|
159
|
+
const counts = new Map();
|
|
160
|
+
for (const item of items) {
|
|
161
|
+
const plugin = pluginOfPath(item.path);
|
|
162
|
+
if (plugin === null)
|
|
163
|
+
continue;
|
|
164
|
+
counts.set(plugin, (counts.get(plugin) ?? 0) + 1);
|
|
165
|
+
}
|
|
166
|
+
return counts;
|
|
167
|
+
}
|
|
168
|
+
/** The default-branch workspace id every plugin resolution runs against. */
|
|
169
|
+
export function pluginsWorkspaceId() {
|
|
170
|
+
return workspaceIdForBranch(DEFAULT_BRANCH);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=plugins.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.service.js","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.service.ts"],"names":[],"mappings":"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"}
|