@bevel-software/platform-core-backend 0.13.5 → 0.14.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.
Files changed (120) hide show
  1. package/THIRD-PARTY-NOTICES.md +639 -433
  2. package/dist/core/create-core-server.d.ts.map +1 -1
  3. package/dist/core/create-core-server.js +6 -3
  4. package/dist/core/create-core-server.js.map +1 -1
  5. package/dist/core/create-core-services.d.ts +2 -0
  6. package/dist/core/create-core-services.d.ts.map +1 -1
  7. package/dist/core/create-core-services.js +9 -0
  8. package/dist/core/create-core-services.js.map +1 -1
  9. package/dist/core-config.d.ts.map +1 -1
  10. package/dist/core-config.js +12 -5
  11. package/dist/core-config.js.map +1 -1
  12. package/dist/modules/access-model/kb-read-filter.d.ts +12 -0
  13. package/dist/modules/access-model/kb-read-filter.d.ts.map +1 -1
  14. package/dist/modules/access-model/kb-read-filter.js +15 -0
  15. package/dist/modules/access-model/kb-read-filter.js.map +1 -1
  16. package/dist/modules/connection-probe/connection-probe.contract.d.ts +43 -0
  17. package/dist/modules/connection-probe/connection-probe.contract.d.ts.map +1 -0
  18. package/dist/modules/connection-probe/connection-probe.contract.js +2 -0
  19. package/dist/modules/connection-probe/connection-probe.contract.js.map +1 -0
  20. package/dist/modules/connection-probe/connection-probe.service.d.ts +94 -0
  21. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -0
  22. package/dist/modules/connection-probe/connection-probe.service.js +684 -0
  23. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -0
  24. package/dist/modules/connection-probe/index.d.ts +3 -0
  25. package/dist/modules/connection-probe/index.d.ts.map +1 -0
  26. package/dist/modules/connection-probe/index.js +3 -0
  27. package/dist/modules/connection-probe/index.js.map +1 -0
  28. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  29. package/dist/modules/diff/diff.routes.js +3 -5
  30. package/dist/modules/diff/diff.routes.js.map +1 -1
  31. package/dist/modules/kb-fs/mutex.d.ts +37 -0
  32. package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
  33. package/dist/modules/kb-fs/mutex.js +48 -5
  34. package/dist/modules/kb-fs/mutex.js.map +1 -1
  35. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  36. package/dist/modules/mcp/mcp.routes.js +95 -13
  37. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  38. package/dist/modules/secrets-vault/db-secrets-vault.service.js +1 -1
  39. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  40. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +6 -0
  41. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  42. package/dist/modules/secrets-vault/secrets-vault.routes.js +31 -1
  43. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  44. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  45. package/dist/modules/tool-manuals/mcp-json-discovery.js +10 -1
  46. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  47. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  48. package/dist/modules/tool-manuals/mcp-server-edit.service.js +10 -4
  49. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  50. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +83 -0
  51. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  52. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +9 -1
  53. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  54. package/dist/modules/tool-manuals/tool-manuals.service.js +181 -13
  55. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  56. package/dist/modules/workflow/git/git.service.d.ts +18 -1
  57. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  58. package/dist/modules/workflow/git/git.service.js +98 -6
  59. package/dist/modules/workflow/git/git.service.js.map +1 -1
  60. package/dist/modules/workflow/workflow.routes.d.ts +2 -1
  61. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  62. package/dist/modules/workflow/workflow.routes.js +98 -9
  63. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  64. package/dist/modules/workflow/workflow.service.d.ts +81 -8
  65. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  66. package/dist/modules/workflow/workflow.service.js +220 -43
  67. package/dist/modules/workflow/workflow.service.js.map +1 -1
  68. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  69. package/dist/modules/workspace/workspace.routes.js +2 -5
  70. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  71. package/dist/modules/workspace/workspace.service.d.ts +5 -1
  72. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  73. package/dist/modules/workspace/workspace.service.js +21 -4
  74. package/dist/modules/workspace/workspace.service.js.map +1 -1
  75. package/dist/shared/token-crypto.d.ts +17 -2
  76. package/dist/shared/token-crypto.d.ts.map +1 -1
  77. package/dist/shared/token-crypto.js +25 -8
  78. package/dist/shared/token-crypto.js.map +1 -1
  79. package/package.json +3 -3
  80. package/src/__tests__/core-config.admin.test.ts +21 -0
  81. package/src/core/create-core-server.ts +7 -2
  82. package/src/core/create-core-services.ts +11 -0
  83. package/src/core-config.ts +14 -7
  84. package/src/modules/access-model/kb-read-filter.ts +22 -0
  85. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +685 -0
  86. package/src/modules/connection-probe/connection-probe.contract.ts +44 -0
  87. package/src/modules/connection-probe/connection-probe.service.ts +734 -0
  88. package/src/modules/connection-probe/index.ts +2 -0
  89. package/src/modules/diff/diff.routes.ts +9 -5
  90. package/src/modules/kb-fs/__tests__/mutex.test.ts +107 -0
  91. package/src/modules/kb-fs/mutex.ts +50 -5
  92. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +89 -0
  93. package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +6 -34
  94. package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +8 -40
  95. package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +226 -0
  96. package/src/modules/mcp/mcp.routes.ts +482 -398
  97. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +12 -0
  98. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +10 -3
  99. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +26 -0
  100. package/src/modules/secrets-vault/db-secrets-vault.service.ts +1 -1
  101. package/src/modules/secrets-vault/secrets-vault.routes.ts +35 -1
  102. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +11 -0
  103. package/src/modules/tool-manuals/__tests__/tool-manuals.health-check.test.ts +104 -0
  104. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +56 -0
  105. package/src/modules/tool-manuals/mcp-json-discovery.ts +13 -1
  106. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -4
  107. package/src/modules/tool-manuals/tool-manuals.contract.ts +89 -0
  108. package/src/modules/tool-manuals/tool-manuals.service.ts +196 -14
  109. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +139 -0
  110. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +337 -0
  111. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +7 -1
  112. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +95 -8
  113. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +86 -0
  114. package/src/modules/workflow/git/git.service.ts +115 -7
  115. package/src/modules/workflow/workflow.routes.ts +104 -4
  116. package/src/modules/workflow/workflow.service.ts +245 -55
  117. package/src/modules/workspace/workspace.routes.ts +8 -4
  118. package/src/modules/workspace/workspace.service.ts +21 -3
  119. package/src/shared/__tests__/token-crypto.test.ts +34 -0
  120. package/src/shared/token-crypto.ts +28 -10
@@ -122,8 +122,10 @@ export async function createCoreServer(
122
122
  // `exposedHeaders` lets a BROWSER-based MCP client (e.g. MCP Inspector) read
123
123
  // the Streamable-HTTP session header off the `initialize` response — custom
124
124
  // response headers are hidden from browser JS unless exposed, so without this
125
- // the client can't send `Mcp-Session-Id` back and every follow-up 400s with
126
- // "session id does not match any active session". `WWW-Authenticate` is
125
+ // the client can't send `Mcp-Session-Id` back at all, and every follow-up
126
+ // 400s with "Bad Request: Mcp-Session-Id header is required" — the
127
+ // missing-header case, not the unknown-session one (that answers 404
128
+ // "Session not found"). `WWW-Authenticate` is
127
129
  // exposed so a browser client can read the 401 challenge and start the OAuth
128
130
  // discovery flow. (Native clients like Claude Code aren't subject to CORS.)
129
131
  app.use(
@@ -301,6 +303,7 @@ export async function createCoreServer(
301
303
  secretsVault: core.secretsVaultService,
302
304
  toolManualService: core.toolManualService,
303
305
  accessControl: core.accessControl,
306
+ connectionProbe: core.connectionProbeService,
304
307
  stateSecret: core.config.jwtSecret,
305
308
  publicBackendUrl: core.config.publicBackendUrl,
306
309
  publicFrontendUrl: core.config.publicFrontendUrl,
@@ -399,6 +402,8 @@ export async function createCoreServer(
399
402
  core.workspaceService,
400
403
  core.authService,
401
404
  core.eventBus,
405
+ core.accessControl,
406
+ core.kbDirName,
402
407
  ));
403
408
  // SSE event-bus surface. The route owns its own auth gating via the
404
409
  // injected middleware (which accepts both Bearer and the `bevel_token`
@@ -46,6 +46,7 @@ import {
46
46
  McpOAuthDiscoveryService,
47
47
  registerBevelSecretsVariableLoader,
48
48
  } from '../modules/secrets-vault/index.js';
49
+ import { ConnectionProbeService } from '../modules/connection-probe/index.js';
49
50
  import { GitService } from '../modules/workflow/git/git.service.js';
50
51
  import { PullRequestService } from '../modules/workflow/git/pull-request.service.js';
51
52
  import { WorkspaceMutex } from '../modules/kb-fs/mutex.js';
@@ -169,6 +170,7 @@ export interface CoreServices {
169
170
  */
170
171
  kbDirName: string;
171
172
  secretsVaultService: DbSecretsVaultService;
173
+ connectionProbeService: ConnectionProbeService;
172
174
  externalApiKeyService: ExternalApiKeyService;
173
175
  internalTokenService: InternalTokenService;
174
176
  mcpService: McpService;
@@ -562,6 +564,14 @@ export async function createCoreServices(
562
564
  // UTCP client can resolve `${VAR}` from the caller's secrets at tool-call time.
563
565
  registerBevelSecretsVariableLoader(secretsVaultService);
564
566
 
567
+ // The credential probe: whether a tool's stored credential actually WORKS, as
568
+ // distinct from whether one is stored. Built here — after the vault and the
569
+ // tool catalog — because a probe needs both: the catalog to know what to
570
+ // call, the vault to resolve the credential to call it with. Holds no state
571
+ // and takes no `db`: a verdict is returned to the caller that asked for it and
572
+ // never outlives their page (see `ProbeVerdict`).
573
+ const connectionProbeService = new ConnectionProbeService(toolManualService, secretsVaultService);
574
+
565
575
  // Zero-config OAuth for bare `type: mcp` `.tool`s: when the remote server
566
576
  // demands OAuth (MCP authorization spec), discover its authorization server,
567
577
  // dynamically register a public PKCE client, and persist the provider as the
@@ -835,6 +845,7 @@ export async function createCoreServices(
835
845
  createSyncedGroupsMaterializer,
836
846
  updateCheckService,
837
847
  secretsVaultService,
848
+ connectionProbeService,
838
849
  externalApiKeyService,
839
850
  internalTokenService,
840
851
  mcpService,
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { defaultKbTemplateDir } from './assets.js';
5
+ import { assertKeyDecodesTo32Bytes } from './shared/token-crypto.js';
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
8
  dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
@@ -337,19 +338,25 @@ export class CoreConfig {
337
338
  .split(/[\s,]+/)
338
339
  .map((d) => d.trim().toLowerCase().replace(/^[@.]+/, ''))
339
340
  .filter((d) => d.length > 0);
340
- this.secretsEncKey = (
341
- process.env.SECRETS_ENC_KEY ||
342
- process.env.CONNECTOR_CONFIG_ENC_KEY ||
343
- process.env.SHAREPOINT_TOKEN_ENC_KEY ||
344
- ''
345
- ).trim();
346
- if (!this.secretsEncKey) {
341
+ // Which variable actually supplied the key decides whose name a bad key
342
+ // is reported under: a legacy deploy still on the fallback chain must be
343
+ // told to fix the variable it set, not one it never heard of.
344
+ const secretsKeySource = (
345
+ ['SECRETS_ENC_KEY', 'CONNECTOR_CONFIG_ENC_KEY', 'SHAREPOINT_TOKEN_ENC_KEY'] as const
346
+ ).find((name) => (process.env[name] ?? '').trim() !== '');
347
+ this.secretsEncKey = secretsKeySource ? process.env[secretsKeySource]!.trim() : '';
348
+ if (!secretsKeySource) {
347
349
  throw new Error(
348
350
  'SECRETS_ENC_KEY is required — it encrypts the secrets vault, the MCP OAuth ' +
349
351
  'tokens and the git credential saved from the setup screen. Generate one ' +
350
352
  `with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`,
351
353
  );
352
354
  }
355
+ // Validate the LENGTH here too, while the winning variable's name is in
356
+ // hand. Every later `new TokenCrypto(config.secretsEncKey)` blames
357
+ // SECRETS_ENC_KEY by default, which is only certainly right because a bad
358
+ // key never gets past this line.
359
+ assertKeyDecodesTo32Bytes(this.secretsEncKey, secretsKeySource);
353
360
  this.internalTokenSecret = (process.env.INTERNAL_TOKEN_SECRET || '').trim();
354
361
  // Setting DOMAIN declares "the bundled Caddy `https` profile fronts this
355
362
  // deployment" — one proxy hop, and the public origin IS that domain. The
@@ -42,6 +42,28 @@ export function toKbRelative(p: string, kbDirName: string): string | null {
42
42
  return rel.length > 0 ? rel : null;
43
43
  }
44
44
 
45
+ /**
46
+ * THE single-path read verdict every route module shares: may `userEmail`
47
+ * read the workspace-relative `wsPath`? Non-KB paths (outside `kbDirName`)
48
+ * carry no `read:` rules and pass; KB paths go through the FULL `canRead`
49
+ * (folder `access.md` chain + per-node frontmatter). Content, diff and
50
+ * history routes all gate through this one function so the rule cannot
51
+ * drift between the surfaces — a file's history and its diffs are its
52
+ * content, and must deny exactly where the content route denies. Throws
53
+ * whatever `canRead` throws: the caller decides how a verdict failure is
54
+ * reported, and MUST fail closed.
55
+ */
56
+ export async function canReadWorkspacePath(
57
+ canRead: (workspaceId: string, userEmail: string, kbRelPath: string) => Promise<boolean>,
58
+ workspaceId: string,
59
+ userEmail: string,
60
+ kbDirName: string,
61
+ wsPath: string,
62
+ ): Promise<boolean> {
63
+ const rel = toKbRelative(wsPath, kbDirName);
64
+ return rel === null || (await canRead(workspaceId, userEmail, rel));
65
+ }
66
+
45
67
  /**
46
68
  * Resolve read verdicts for a set of workspace-relative paths in ONE batched
47
69
  * access call. Non-KB paths (outside `kbDirName`) are always readable — they