@bevel-software/platform-core-backend 0.4.0 → 0.5.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/core/core-ports.d.ts +15 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +40 -6
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +17 -1
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +153 -29
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core-config.d.ts +70 -21
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +134 -58
- package/dist/core-config.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +6 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +74 -5
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +116 -15
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts +93 -0
- package/dist/modules/access/access-declarations.d.ts.map +1 -0
- package/dist/modules/access/access-declarations.js +168 -0
- package/dist/modules/access/access-declarations.js.map +1 -0
- package/dist/modules/access/access-mutation.service.js +4 -4
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-splice.d.ts +16 -1
- package/dist/modules/access/access-splice.d.ts.map +1 -1
- package/dist/modules/access/access-splice.js +40 -21
- package/dist/modules/access/access-splice.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +51 -0
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +8 -0
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +21 -2
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/admin/admin-access.service.d.ts +1 -1
- package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
- package/dist/modules/admin/admin-access.service.js +3 -2
- package/dist/modules/admin/admin-access.service.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +50 -1
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +39 -10
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +38 -16
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +124 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +45 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/groups/group-provision.service.d.ts +130 -0
- package/dist/modules/groups/group-provision.service.d.ts.map +1 -0
- package/dist/modules/groups/group-provision.service.js +288 -0
- package/dist/modules/groups/group-provision.service.js.map +1 -0
- package/dist/modules/groups/groups.contract.d.ts +106 -0
- package/dist/modules/groups/groups.contract.d.ts.map +1 -0
- package/dist/modules/groups/groups.contract.js +36 -0
- package/dist/modules/groups/groups.contract.js.map +1 -0
- package/dist/modules/groups/groups.routes.d.ts +42 -0
- package/dist/modules/groups/groups.routes.d.ts.map +1 -0
- package/dist/modules/groups/groups.routes.js +379 -0
- package/dist/modules/groups/groups.routes.js.map +1 -0
- package/dist/modules/groups/groups.service.d.ts +60 -0
- package/dist/modules/groups/groups.service.d.ts.map +1 -0
- package/dist/modules/groups/groups.service.js +172 -0
- package/dist/modules/groups/groups.service.js.map +1 -0
- package/dist/modules/groups/index.d.ts +7 -0
- package/dist/modules/groups/index.d.ts.map +1 -0
- package/dist/modules/groups/index.js +6 -0
- package/dist/modules/groups/index.js.map +1 -0
- package/dist/modules/groups/join-proposals.d.ts +53 -0
- package/dist/modules/groups/join-proposals.d.ts.map +1 -0
- package/dist/modules/groups/join-proposals.js +67 -0
- package/dist/modules/groups/join-proposals.js.map +1 -0
- package/dist/modules/groups/join-requests.service.d.ts +81 -0
- package/dist/modules/groups/join-requests.service.d.ts.map +1 -0
- package/dist/modules/groups/join-requests.service.js +135 -0
- package/dist/modules/groups/join-requests.service.js.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +20 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +59 -11
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +129 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -0
- package/dist/modules/settings/deployment-settings.service.js +368 -0
- package/dist/modules/settings/deployment-settings.service.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +49 -0
- package/dist/modules/settings/setup.routes.d.ts.map +1 -0
- package/dist/modules/settings/setup.routes.js +292 -0
- package/dist/modules/settings/setup.routes.js.map +1 -0
- package/dist/modules/skills/index.d.ts +2 -1
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +40 -0
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -0
- package/dist/modules/skills/pending-skills.service.js +147 -0
- package/dist/modules/skills/pending-skills.service.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +32 -3
- 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.routes.d.ts +3 -2
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +18 -1
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +22 -0
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +24 -12
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +1 -1
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +47 -2
- 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 +4 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +23 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +3 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +84 -17
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -2
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +4 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/event-bus.d.ts +11 -0
- package/dist/modules/workflow/event-bus.d.ts.map +1 -1
- package/dist/modules/workflow/event-bus.js +22 -0
- package/dist/modules/workflow/event-bus.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +9 -0
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +16 -0
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/branch-name.d.ts +1 -1
- package/dist/modules/workflow/git/branch-name.d.ts.map +1 -1
- package/dist/modules/workflow/git/branch-name.js +1 -1
- package/dist/modules/workflow/git/branch-name.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +31 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +157 -54
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +12 -6
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +19 -9
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +95 -14
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +97 -5
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +393 -114
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/bevel-ignore.d.ts +5 -0
- package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -1
- package/dist/modules/workspace/bevel-ignore.js +5 -1
- package/dist/modules/workspace/bevel-ignore.js.map +1 -1
- package/dist/modules/workspace/kb-seed.interface.d.ts +1 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +60 -5
- package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
- package/dist/modules/workspace/kb-seed.service.js +228 -51
- package/dist/modules/workspace/kb-seed.service.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts +2 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +51 -18
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +16 -4
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +36 -9
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +8 -3
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/kb-layout.test.js +2 -2
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +18 -0
- package/dist/test-setup.js.map +1 -0
- package/kb-template/.bevelignore +6 -2
- package/kb-template/AGENTS.md +272 -0
- package/kb-template/KnowledgeBase/How to get started.md +65 -0
- package/kb-template/access.md +36 -36
- package/migrations/0002_user_onboarding_done.sql +1 -0
- package/migrations/0003_deployment_settings.sql +9 -0
- package/migrations/meta/0001_snapshot.json +1486 -0
- package/migrations/meta/0002_snapshot.json +1493 -0
- package/migrations/meta/0003_snapshot.json +1552 -0
- package/migrations/meta/_journal.json +15 -1
- package/package.json +3 -3
- package/src/__tests__/branch-model.test.ts +79 -0
- package/src/__tests__/core-config.admin.test.ts +114 -0
- package/src/__tests__/database-url.test.ts +70 -0
- package/src/core/core-ports.ts +15 -0
- package/src/core/create-core-server.ts +55 -6
- package/src/core/create-core-services.ts +201 -36
- package/src/core-config.ts +139 -59
- package/src/modules/access/__tests__/access-declarations.test.ts +195 -0
- package/src/modules/access/__tests__/access-md-format.test.ts +191 -0
- package/src/modules/access/__tests__/access-splice.test.ts +21 -0
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +214 -0
- package/src/modules/access/access-control.interface.ts +11 -0
- package/src/modules/access/access-control.service.ts +113 -15
- package/src/modules/access/access-declarations.ts +251 -0
- package/src/modules/access/access-mutation.service.ts +4 -4
- package/src/modules/access/access-splice.ts +64 -21
- package/src/modules/access/access.routes.ts +61 -0
- package/src/modules/access/creator-access.ts +22 -2
- package/src/modules/admin/admin-access.service.ts +4 -3
- package/src/modules/auth/__tests__/auth.service.test.ts +57 -0
- package/src/modules/auth/__tests__/onboarding-done.route.test.ts +147 -0
- package/src/modules/auth/auth.routes.ts +50 -1
- package/src/modules/auth/auth.service.ts +55 -17
- package/src/modules/database/core-schema.ts +46 -0
- package/src/modules/groups/__tests__/group-index.service.test.ts +225 -0
- package/src/modules/groups/__tests__/group-provision.service.test.ts +247 -0
- package/src/modules/groups/__tests__/groups.routes.test.ts +493 -0
- package/src/modules/groups/__tests__/join-proposals.test.ts +101 -0
- package/src/modules/groups/__tests__/join-requests.service.test.ts +175 -0
- package/src/modules/groups/group-provision.service.ts +328 -0
- package/src/modules/groups/groups.contract.ts +107 -0
- package/src/modules/groups/groups.routes.ts +433 -0
- package/src/modules/groups/groups.service.ts +193 -0
- package/src/modules/groups/index.ts +17 -0
- package/src/modules/groups/join-proposals.ts +114 -0
- package/src/modules/groups/join-requests.service.ts +185 -0
- package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +283 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +68 -12
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +256 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +201 -0
- package/src/modules/settings/deployment-settings.service.ts +419 -0
- package/src/modules/settings/setup.routes.ts +317 -0
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +240 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
- package/src/modules/skills/index.ts +3 -0
- package/src/modules/skills/pending-skills.service.ts +168 -0
- package/src/modules/skills/skills.contract.ts +34 -3
- package/src/modules/skills/skills.routes.ts +23 -2
- package/src/modules/skills/skills.service.ts +24 -16
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +262 -0
- 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 +61 -23
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -156
- package/src/modules/tool-manuals/index.ts +2 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +50 -2
- package/src/modules/tool-manuals/tool-manuals.routes.ts +21 -0
- package/src/modules/tool-manuals/tool-manuals.service.ts +86 -17
- package/src/modules/tool-manuals/tool-manuals.tools.ts +206 -202
- package/src/modules/workflow/__tests__/event-bus.test.ts +35 -0
- package/src/modules/workflow/__tests__/pending-commits.enqueue-if-absent.test.ts +82 -72
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +295 -5
- package/src/modules/workflow/agent-tools/workflow.tools.ts +4 -2
- package/src/modules/workflow/event-bus.ts +25 -0
- package/src/modules/workflow/file-lock.service.ts +17 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +9 -49
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +101 -0
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +219 -211
- package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +132 -0
- package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +4 -2
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +275 -272
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +311 -306
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +44 -0
- package/src/modules/workflow/git/branch-name.ts +5 -1
- package/src/modules/workflow/git/git.service.ts +172 -64
- package/src/modules/workflow/git/pull-request.service.ts +22 -11
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +4 -1
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
- package/src/modules/workflow/workflow.routes.ts +91 -13
- package/src/modules/workflow/workflow.service.ts +365 -19
- package/src/modules/workspace/__tests__/kb-seed.service.test.ts +255 -24
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -233
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -176
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -303
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -276
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +14 -6
- package/src/modules/workspace/bevel-ignore.ts +66 -62
- package/src/modules/workspace/kb-seed.interface.ts +1 -1
- package/src/modules/workspace/kb-seed.service.ts +247 -53
- package/src/modules/workspace/workspace.routes.ts +54 -18
- package/src/modules/workspace/workspace.service.ts +45 -7
- package/src/modules/workspace/workspace.tools.ts +922 -917
- package/src/shared/__tests__/frontmatter-id.test.ts +53 -0
- package/src/shared/__tests__/join-request.test.ts +96 -0
- package/src/shared/__tests__/kb-layout.group.test.ts +45 -0
- package/src/shared/kb-layout.test.ts +2 -2
- package/src/test-setup.ts +18 -0
- package/kb-template/Agents/README.md +0 -38
- package/kb-template/CLAUDE.md +0 -419
- package/kb-template/Data/.gitkeep +0 -0
- package/kb-template/Data/README.md +0 -39
- package/kb-template/KnowledgeBase/README.md +0 -37
- package/kb-template/Pipelines/.gitkeep +0 -0
- package/kb-template/Pipelines/README.md +0 -38
- package/kb-template/Skills/.gitkeep +0 -0
- package/kb-template/Skills/README.md +0 -32
- package/kb-template/Tools/.gitkeep +0 -0
- package/kb-template/Tools/README.md +0 -42
- /package/kb-template/{Agents → Groups}/.gitkeep +0 -0
|
@@ -84,6 +84,10 @@ export function createToolManualsAgentRoutes(
|
|
|
84
84
|
* Browser-facing tool-manual routes (mounted under the JWT auth middleware):
|
|
85
85
|
* GET /tools — the caller's accessible `.tool` manuals (summaries).
|
|
86
86
|
* POST /tools/preview — validate a draft `.tool` for the renderer.
|
|
87
|
+
* GET /tools/:slug — one readable manual with description + capabilities
|
|
88
|
+
* (the tool page). Registered LAST so no future
|
|
89
|
+
* literal sibling is shadowed by the param segment;
|
|
90
|
+
* `preview` is POST-only, so it never collides.
|
|
87
91
|
*/
|
|
88
92
|
export function createToolManualsBrowserRoutes(toolManualService: IToolManualService): express.Router {
|
|
89
93
|
const router = express.Router();
|
|
@@ -106,5 +110,22 @@ export function createToolManualsBrowserRoutes(toolManualService: IToolManualSer
|
|
|
106
110
|
res.json(await toolManualService.preview(content));
|
|
107
111
|
});
|
|
108
112
|
|
|
113
|
+
router.get('/tools/:slug', async (req, res) => {
|
|
114
|
+
const email = req.userEmail;
|
|
115
|
+
if (!email) return void res.status(401).json({ error: 'Not authenticated' });
|
|
116
|
+
try {
|
|
117
|
+
// `getDetail` returns null for BOTH "no such slug" and "you can't read it",
|
|
118
|
+
// and this route keeps them indistinguishable: a distinct 403 would confirm
|
|
119
|
+
// the existence of a tool the caller isn't allowed to know about. Same
|
|
120
|
+
// fail-closed posture as the agent-facing `/tools/:slug/manual`.
|
|
121
|
+
const tool = await toolManualService.getDetail(email, String(req.params.slug));
|
|
122
|
+
if (!tool) return void res.status(404).json({ error: 'Not found' });
|
|
123
|
+
res.json({ tool });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error('[tool-manuals] detail failed:', err);
|
|
126
|
+
res.status(500).json({ error: 'Failed to load tool' });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
109
130
|
return router;
|
|
110
131
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
DefaultVariableSubstitutor,
|
|
10
10
|
type CallTemplate,
|
|
11
11
|
} from '@utcp/sdk';
|
|
12
|
-
import { DEFAULT_BRANCH,
|
|
12
|
+
import { DEFAULT_BRANCH, GROUPS_DIR } from '@bevel-software/platform-shared';
|
|
13
13
|
import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
14
14
|
import { workspaceIdForBranch } from '../workspace/workspace.service.js';
|
|
15
15
|
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
type IToolManualService,
|
|
29
29
|
type ToolManualDescriptor,
|
|
30
30
|
type ToolManualSummary,
|
|
31
|
+
type ToolManualDetail,
|
|
32
|
+
type ToolCapability,
|
|
31
33
|
type UtcpManualDict,
|
|
32
34
|
type ToolManualPreview,
|
|
33
35
|
type ToolManualType,
|
|
@@ -38,6 +40,13 @@ import {
|
|
|
38
40
|
|
|
39
41
|
const CACHE_TTL_MS = 60_000;
|
|
40
42
|
|
|
43
|
+
/**
|
|
44
|
+
* How many embedded tools a manual may advertise on the tool page. A `.tool` is
|
|
45
|
+
* author-written, so its `tools` array is unbounded input; the page renders one
|
|
46
|
+
* bullet per entry, and a thousand of them is a broken page, not a useful one.
|
|
47
|
+
*/
|
|
48
|
+
const MAX_CAPABILITIES = 100;
|
|
49
|
+
|
|
41
50
|
/**
|
|
42
51
|
* UTCP namespaces a user `.tool` may NOT claim: they belong to built-in manuals
|
|
43
52
|
* whose loopback creds are pre-seeded on the agent's code-mode client (the
|
|
@@ -154,16 +163,25 @@ export class ToolManualService implements IToolManualService {
|
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
async listAccessible(userEmail: string): Promise<ToolManualSummary[]> {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
return (await this.accessibleManuals(userEmail)).map(toSummary);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async listAllSummaries(): Promise<ToolManualSummary[]> {
|
|
170
|
+
return (await this.scan()).map(toSummary);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async getDetail(userEmail: string, slug: string): Promise<ToolManualDetail | null> {
|
|
174
|
+
// Resolved through `accessibleManuals` rather than `scan()` + a canRead call
|
|
175
|
+
// so the cache, the dedupe, the mcp-oauth decoration and the fail-closed
|
|
176
|
+
// batch ACL all apply exactly as they do to the catalog listing — one read
|
|
177
|
+
// model, no second place for the access rules to drift.
|
|
178
|
+
const found = (await this.accessibleManuals(userEmail)).find((m) => m.slug === slug);
|
|
179
|
+
if (!found) return null;
|
|
180
|
+
return {
|
|
181
|
+
...toSummary(found),
|
|
182
|
+
description: found.description ?? null,
|
|
183
|
+
capabilities: capabilitiesOf(found),
|
|
184
|
+
};
|
|
167
185
|
}
|
|
168
186
|
|
|
169
187
|
async listLocalOnly(userEmail: string): Promise<{ name: string; path: string }[]> {
|
|
@@ -252,7 +270,7 @@ export class ToolManualService implements IToolManualService {
|
|
|
252
270
|
async preview(content: string): Promise<ToolManualPreview> {
|
|
253
271
|
let descriptor: ToolManualDescriptor;
|
|
254
272
|
try {
|
|
255
|
-
descriptor = normalizeToolManual('draft', '
|
|
273
|
+
descriptor = normalizeToolManual('draft', 'Groups/draft.tool', content);
|
|
256
274
|
} catch (err) {
|
|
257
275
|
return { ok: false, errors: [err instanceof Error ? err.message : String(err)] };
|
|
258
276
|
}
|
|
@@ -467,12 +485,14 @@ export class ToolManualService implements IToolManualService {
|
|
|
467
485
|
} catch {
|
|
468
486
|
return [];
|
|
469
487
|
}
|
|
470
|
-
const
|
|
488
|
+
const kbRoot = path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName);
|
|
471
489
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
490
|
+
// A `.tool` sits under `Groups/`, beside the skills that use it.
|
|
491
|
+
const files: { abs: string; rel: string }[] = [];
|
|
492
|
+
const root = path.join(kbRoot, GROUPS_DIR);
|
|
493
|
+
for (const rel of await walkFiles(root, (n) => n.toLowerCase().endsWith('.tool'))) {
|
|
494
|
+
files.push({ abs: path.join(root, rel), rel: `${GROUPS_DIR}/${rel}` });
|
|
495
|
+
}
|
|
476
496
|
|
|
477
497
|
const parsed: ToolManualDescriptor[] = [];
|
|
478
498
|
for (const f of files) {
|
|
@@ -510,6 +530,47 @@ export class ToolManualService implements IToolManualService {
|
|
|
510
530
|
|
|
511
531
|
// --- helpers ------------------------------------------------------------------
|
|
512
532
|
|
|
533
|
+
/**
|
|
534
|
+
* The one descriptor → summary projection, shared by every list surface
|
|
535
|
+
* (`listAccessible`, `listAllSummaries`, and the summary half of `getDetail`).
|
|
536
|
+
*/
|
|
537
|
+
function toSummary(m: ToolManualDescriptor): ToolManualSummary {
|
|
538
|
+
return {
|
|
539
|
+
slug: m.slug,
|
|
540
|
+
name: m.name,
|
|
541
|
+
path: m.path,
|
|
542
|
+
type: m.type,
|
|
543
|
+
description: m.description,
|
|
544
|
+
variables: m.variables,
|
|
545
|
+
remote: m.remote,
|
|
546
|
+
setup: m.setup,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* What an `inline` manual's embedded tools say the assistant can do. `tools` is
|
|
552
|
+
* typed `unknown[]` (it is only validated when actually served as a UTCP
|
|
553
|
+
* manual), so every entry is re-checked here: an entry without a string `name`
|
|
554
|
+
* has nothing to display and is dropped rather than rendering a blank bullet.
|
|
555
|
+
* Non-inline manuals discover their tools over the network at call time, so
|
|
556
|
+
* there is nothing to derive without a round-trip this endpoint won't make.
|
|
557
|
+
*/
|
|
558
|
+
function capabilitiesOf(m: ToolManualDescriptor): ToolCapability[] {
|
|
559
|
+
if (m.type !== 'inline' || !Array.isArray(m.tools)) return [];
|
|
560
|
+
const out: ToolCapability[] = [];
|
|
561
|
+
for (const entry of m.tools) {
|
|
562
|
+
if (out.length >= MAX_CAPABILITIES) break;
|
|
563
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) continue;
|
|
564
|
+
const t = entry as Record<string, unknown>;
|
|
565
|
+
if (typeof t.name !== 'string' || !t.name.trim()) continue;
|
|
566
|
+
out.push({
|
|
567
|
+
name: t.name.trim(),
|
|
568
|
+
description: typeof t.description === 'string' && t.description.trim() ? t.description.trim() : null,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return out;
|
|
572
|
+
}
|
|
573
|
+
|
|
513
574
|
function baseName(rel: string): string {
|
|
514
575
|
const base = rel.slice(rel.lastIndexOf('/') + 1);
|
|
515
576
|
return base.replace(/\.tool$/i, '');
|
|
@@ -600,6 +661,14 @@ export function normalizeToolManual(
|
|
|
600
661
|
type,
|
|
601
662
|
};
|
|
602
663
|
|
|
664
|
+
// Cosmetic prose for the browser tool page. Unlike every other field here, a
|
|
665
|
+
// malformed value is IGNORED rather than thrown on: `description` buys the
|
|
666
|
+
// reader a sentence, and no sentence is worth taking a working integration out
|
|
667
|
+
// of the catalog (a throw skips the whole file). So: a non-empty string wins,
|
|
668
|
+
// anything else — number, object, null, blank — silently leaves it absent.
|
|
669
|
+
const description = typeof obj.description === 'string' ? obj.description.trim() : '';
|
|
670
|
+
if (description) descriptor.description = description;
|
|
671
|
+
|
|
603
672
|
const variables = normalizeVariables(obj.variables);
|
|
604
673
|
if (variables.length) descriptor.variables = variables;
|
|
605
674
|
|
|
@@ -1,202 +1,206 @@
|
|
|
1
|
-
import type { Router, RequestHandler } from 'express';
|
|
2
|
-
import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
|
|
3
|
-
import type { IToolRegistry, UtcpTool } from '../tool-registry/tool.contract.js';
|
|
4
|
-
import type { ToolContext } from '../tool-helpers/tool.contract.js';
|
|
5
|
-
import { toolDef } from '../tool-helpers/tool-def.js';
|
|
6
|
-
import type { ToolHandlerFactory } from '../tool-helpers/tool-handler.js';
|
|
7
|
-
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
8
|
-
import { workspaceIdForBranch } from '../workspace/workspace.service.js';
|
|
9
|
-
import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
|
|
10
|
-
import type { IToolManualService } from './tool-manuals.contract.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* What `list_tool_setup` needs from the secrets vault (structurally satisfied
|
|
14
|
-
* by `ISecretsVaultService.statusFor`). A local port keeps this module free of
|
|
15
|
-
* a secrets-vault import — the same decoupling discipline as
|
|
16
|
-
* `McpAuthDiscoveryPort` in the service.
|
|
17
|
-
*/
|
|
18
|
-
export interface VariableStatusPort {
|
|
19
|
-
statusFor(
|
|
20
|
-
userId: string,
|
|
21
|
-
keys: string[],
|
|
22
|
-
): Promise<{ key: string; adminConfigured: boolean; userConfigured: boolean; userAuthorized?: boolean }[]>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Registers the tool-manual catalog tools (both surfaces) and hosts their
|
|
27
|
-
* endpoints:
|
|
28
|
-
*
|
|
29
|
-
* - `list_local_tools` — local-only manuals (`remote: false`) the remote proxy
|
|
30
|
-
* can't serve; the agent reads their `.tool` path and self-configures.
|
|
31
|
-
* - `list_tool_setup` — configuration status of every accessible manual, so an
|
|
32
|
-
* agent can EXPLAIN the remaining setup to the admin. Deliberately
|
|
33
|
-
* read-only: secret VALUES are never set (or returned) through a tool — the
|
|
34
|
-
* admin pastes them into the tool editor; users sign in on /connect.
|
|
35
|
-
*/
|
|
36
|
-
export function registerToolManualsTools(
|
|
37
|
-
registry: IToolRegistry,
|
|
38
|
-
router: Router,
|
|
39
|
-
toolAuth: RequestHandler,
|
|
40
|
-
toolHandler: ToolHandlerFactory,
|
|
41
|
-
toolManualService: IToolManualService,
|
|
42
|
-
deps: {
|
|
43
|
-
accessControl: IAccessControl;
|
|
44
|
-
variableStatus: VariableStatusPort;
|
|
45
|
-
},
|
|
46
|
-
): void {
|
|
47
|
-
registry.registerExternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
|
|
48
|
-
registry.registerInternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
|
|
49
|
-
|
|
50
|
-
router.post(
|
|
51
|
-
'/agent/tools/list_local_tools',
|
|
52
|
-
toolAuth,
|
|
53
|
-
toolHandler(async (_args, ctx: ToolContext) => ({
|
|
54
|
-
tools: await toolManualService.listLocalOnly(ctx.user.email),
|
|
55
|
-
})),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
type: '
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
'
|
|
181
|
-
(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
1
|
+
import type { Router, RequestHandler } from 'express';
|
|
2
|
+
import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
|
|
3
|
+
import type { IToolRegistry, UtcpTool } from '../tool-registry/tool.contract.js';
|
|
4
|
+
import type { ToolContext } from '../tool-helpers/tool.contract.js';
|
|
5
|
+
import { toolDef } from '../tool-helpers/tool-def.js';
|
|
6
|
+
import type { ToolHandlerFactory } from '../tool-helpers/tool-handler.js';
|
|
7
|
+
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
8
|
+
import { workspaceIdForBranch } from '../workspace/workspace.service.js';
|
|
9
|
+
import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
|
|
10
|
+
import type { IToolManualService } from './tool-manuals.contract.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* What `list_tool_setup` needs from the secrets vault (structurally satisfied
|
|
14
|
+
* by `ISecretsVaultService.statusFor`). A local port keeps this module free of
|
|
15
|
+
* a secrets-vault import — the same decoupling discipline as
|
|
16
|
+
* `McpAuthDiscoveryPort` in the service.
|
|
17
|
+
*/
|
|
18
|
+
export interface VariableStatusPort {
|
|
19
|
+
statusFor(
|
|
20
|
+
userId: string,
|
|
21
|
+
keys: string[],
|
|
22
|
+
): Promise<{ key: string; adminConfigured: boolean; userConfigured: boolean; userAuthorized?: boolean }[]>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Registers the tool-manual catalog tools (both surfaces) and hosts their
|
|
27
|
+
* endpoints:
|
|
28
|
+
*
|
|
29
|
+
* - `list_local_tools` — local-only manuals (`remote: false`) the remote proxy
|
|
30
|
+
* can't serve; the agent reads their `.tool` path and self-configures.
|
|
31
|
+
* - `list_tool_setup` — configuration status of every accessible manual, so an
|
|
32
|
+
* agent can EXPLAIN the remaining setup to the admin. Deliberately
|
|
33
|
+
* read-only: secret VALUES are never set (or returned) through a tool — the
|
|
34
|
+
* admin pastes them into the tool editor; users sign in on /connect.
|
|
35
|
+
*/
|
|
36
|
+
export function registerToolManualsTools(
|
|
37
|
+
registry: IToolRegistry,
|
|
38
|
+
router: Router,
|
|
39
|
+
toolAuth: RequestHandler,
|
|
40
|
+
toolHandler: ToolHandlerFactory,
|
|
41
|
+
toolManualService: IToolManualService,
|
|
42
|
+
deps: {
|
|
43
|
+
accessControl: IAccessControl;
|
|
44
|
+
variableStatus: VariableStatusPort;
|
|
45
|
+
},
|
|
46
|
+
): void {
|
|
47
|
+
registry.registerExternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
|
|
48
|
+
registry.registerInternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
|
|
49
|
+
|
|
50
|
+
router.post(
|
|
51
|
+
'/agent/tools/list_local_tools',
|
|
52
|
+
toolAuth,
|
|
53
|
+
toolHandler(async (_args, ctx: ToolContext) => ({
|
|
54
|
+
tools: await toolManualService.listLocalOnly(ctx.user.email),
|
|
55
|
+
})),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// A FUNCTION, not a constant — this factory runs at boot, and on a
|
|
59
|
+
// setup-screen deployment the branch model is applied AFTER boot. Only a
|
|
60
|
+
// read inside a handler body sees the configured `DEFAULT_BRANCH`; a
|
|
61
|
+
// construction-time capture would hold the empty pre-setup id until restart.
|
|
62
|
+
const defaultWs = () => workspaceIdForBranch(DEFAULT_BRANCH);
|
|
63
|
+
const varKey = (manualName: string, varName: string) => utcpNamespacedKey(manualName, varName);
|
|
64
|
+
|
|
65
|
+
const listSetupDef = toolDef({
|
|
66
|
+
name: 'list_tool_setup',
|
|
67
|
+
description:
|
|
68
|
+
'Configuration status of every `.tool` the current user can access: what each tool needs set up and ' +
|
|
69
|
+
'what is already configured. Results are scoped to the caller — a `.tool` the caller cannot READ is ' +
|
|
70
|
+
'absent entirely, and all status flags reflect the caller\'s own state. Per tool: `setup` describes ' +
|
|
71
|
+
'an MCP server\'s sign-in requirement (`open` = none; `oauth-auto` = sign-in was configured ' +
|
|
72
|
+
'automatically; `oauth-manual` = the provider does not support automatic registration, so a writer ' +
|
|
73
|
+
'must declare the OAuth provider in the `.tool` file and paste its client secret into the tool ' +
|
|
74
|
+
'editor). Per variable: whether the shared (admin) value is set, whether the CURRENT user has ' +
|
|
75
|
+
'set/authorized their own, and whether it is an OAuth sign-in (users authorize those on the /connect ' +
|
|
76
|
+
'page, never by typing a value). `canWrite` = the caller may write THAT `.tool` FILE (per-file access ' +
|
|
77
|
+
'from its frontmatter `write:`/`owner:` verbs and the access.md chain — NOT a platform role), which ' +
|
|
78
|
+
'is exactly what gates setting its shared secrets: the people who manage the file configure the tool. ' +
|
|
79
|
+
'Secret VALUES are never returned and can never be set through a tool — an admin enters them in the ' +
|
|
80
|
+
'tool editor; users sign in on /connect.',
|
|
81
|
+
path: '/api/agent/tools/list_tool_setup',
|
|
82
|
+
inputs: { type: 'object', properties: {}, required: [], additionalProperties: false },
|
|
83
|
+
outputs: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
tools: {
|
|
87
|
+
type: 'array',
|
|
88
|
+
items: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
slug: { type: 'string' },
|
|
92
|
+
name: { type: 'string', description: 'The manual id — the namespace secrets bind to.' },
|
|
93
|
+
path: { type: 'string' },
|
|
94
|
+
type: { type: 'string' },
|
|
95
|
+
setup: {
|
|
96
|
+
type: ['object', 'null'],
|
|
97
|
+
description: 'MCP auto-discovery setup requirement; null for non-mcp tools.',
|
|
98
|
+
properties: { kind: { type: 'string' }, reason: { type: 'string' } },
|
|
99
|
+
},
|
|
100
|
+
canWrite: { type: 'boolean' },
|
|
101
|
+
variables: {
|
|
102
|
+
type: 'array',
|
|
103
|
+
items: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
name: { type: 'string' },
|
|
107
|
+
scope: { type: 'string', description: '`admin` (one shared value) or `user` (per user).' },
|
|
108
|
+
label: { type: ['string', 'null'] },
|
|
109
|
+
oauth: { type: 'boolean', description: 'Filled by signing in on /connect, not by a value.' },
|
|
110
|
+
adminConfigured: {
|
|
111
|
+
type: 'boolean',
|
|
112
|
+
description:
|
|
113
|
+
'Shared value set (plain admin vars), or the owner finished provider setup (OAuth vars).',
|
|
114
|
+
},
|
|
115
|
+
userConfigured: { type: 'boolean' },
|
|
116
|
+
authorized: { type: ['boolean', 'null'], description: 'OAuth vars: current user signed in.' },
|
|
117
|
+
},
|
|
118
|
+
required: ['name', 'scope', 'oauth', 'adminConfigured', 'userConfigured'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: ['slug', 'name', 'path', 'type', 'canWrite', 'variables'],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ['tools'],
|
|
127
|
+
},
|
|
128
|
+
tags: ['tools'],
|
|
129
|
+
});
|
|
130
|
+
registry.registerInternalTool(listSetupDef);
|
|
131
|
+
registry.registerExternalTool(listSetupDef);
|
|
132
|
+
router.post(
|
|
133
|
+
'/agent/tools/list_tool_setup',
|
|
134
|
+
toolAuth,
|
|
135
|
+
toolHandler(async (_args, ctx: ToolContext) => {
|
|
136
|
+
const manuals = await toolManualService.listAccessible(ctx.user.email);
|
|
137
|
+
const allKeys = manuals.flatMap((m) => (m.variables ?? []).map((v) => varKey(m.name, v.name)));
|
|
138
|
+
const status = await deps.variableStatus.statusFor(ctx.user.id, allKeys);
|
|
139
|
+
const statusByKey = new Map(status.map((s) => [s.key, s]));
|
|
140
|
+
const tools = await Promise.all(
|
|
141
|
+
manuals.map(async (m) => ({
|
|
142
|
+
slug: m.slug,
|
|
143
|
+
name: m.name,
|
|
144
|
+
path: m.path,
|
|
145
|
+
type: m.type,
|
|
146
|
+
setup: m.setup ?? null,
|
|
147
|
+
canWrite: await deps.accessControl.canWrite(defaultWs(), ctx.user.email, m.path),
|
|
148
|
+
variables: (m.variables ?? []).map((v) => {
|
|
149
|
+
const st = statusByKey.get(varKey(m.name, v.name));
|
|
150
|
+
const isOAuth = v.oauth != null;
|
|
151
|
+
return {
|
|
152
|
+
name: v.name,
|
|
153
|
+
scope: v.scope,
|
|
154
|
+
label: v.label ?? null,
|
|
155
|
+
oauth: isOAuth,
|
|
156
|
+
adminConfigured: st?.adminConfigured ?? false,
|
|
157
|
+
userConfigured: st?.userConfigured ?? false,
|
|
158
|
+
authorized: isOAuth ? (st?.userAuthorized ?? false) : null,
|
|
159
|
+
};
|
|
160
|
+
}),
|
|
161
|
+
})),
|
|
162
|
+
);
|
|
163
|
+
return { tools };
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** "Local-only tools configured for you: `a`, `b`." (or a none note), filtered to what the caller may read. */
|
|
169
|
+
async function localToolsLine(svc: IToolManualService, userEmail?: string): Promise<string> {
|
|
170
|
+
if (!userEmail) return 'No local-only tools are currently configured.';
|
|
171
|
+
const tools = await svc.listLocalOnly(userEmail);
|
|
172
|
+
if (tools.length === 0) return 'No local-only tools are currently configured.';
|
|
173
|
+
return `Local-only tools configured for you: ${tools.map((t) => `\`${t.name}\``).join(', ')}.`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: string): Promise<UtcpTool> {
|
|
177
|
+
return toolDef({
|
|
178
|
+
name: 'list_local_tools',
|
|
179
|
+
description:
|
|
180
|
+
'List tools your workspace admins configured that run ONLY in a local environment ' +
|
|
181
|
+
'(e.g. a self-hosted MCP server on localhost) and therefore cannot be called through this ' +
|
|
182
|
+
'remote endpoint. Each entry gives the tool’s name and its `.tool` file path in the knowledge ' +
|
|
183
|
+
'base — read that file with `read_file` and configure the tool yourself in your local setup ' +
|
|
184
|
+
'(e.g. add the MCP server to your client). ' +
|
|
185
|
+
(await localToolsLine(svc, userEmail)),
|
|
186
|
+
path: '/api/agent/tools/list_local_tools',
|
|
187
|
+
inputs: { type: 'object', properties: {}, additionalProperties: false },
|
|
188
|
+
outputs: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
tools: {
|
|
192
|
+
type: 'array',
|
|
193
|
+
description: 'Local-only tools (name + KB path).',
|
|
194
|
+
items: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
name: { type: 'string' },
|
|
198
|
+
path: { type: 'string', description: 'KB path of the `.tool` file (read it with read_file).' },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
tags: ['tools'],
|
|
205
|
+
});
|
|
206
|
+
}
|