@agent-deck/backend 1.6.2 → 1.7.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 (162) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/export-import/sanitize-for-export.d.ts +9 -0
  3. package/dist/export-import/sanitize-for-export.d.ts.map +1 -1
  4. package/dist/export-import/sanitize-for-export.js +21 -0
  5. package/dist/export-import/sanitize-for-export.js.map +1 -1
  6. package/dist/lib/agent-deck-context.d.ts +4 -2
  7. package/dist/lib/agent-deck-context.d.ts.map +1 -1
  8. package/dist/lib/agent-deck-context.js +35 -8
  9. package/dist/lib/agent-deck-context.js.map +1 -1
  10. package/dist/lib/backend-api-error.d.ts +8 -0
  11. package/dist/lib/backend-api-error.d.ts.map +1 -0
  12. package/dist/lib/backend-api-error.js +35 -0
  13. package/dist/lib/backend-api-error.js.map +1 -0
  14. package/dist/lib/bound-deck-scope.d.ts +6 -1
  15. package/dist/lib/bound-deck-scope.d.ts.map +1 -1
  16. package/dist/lib/bound-deck-scope.js +21 -2
  17. package/dist/lib/bound-deck-scope.js.map +1 -1
  18. package/dist/lib/client-scope.d.ts.map +1 -1
  19. package/dist/lib/client-scope.js +6 -23
  20. package/dist/lib/client-scope.js.map +1 -1
  21. package/dist/lib/dashboard-auth.d.ts +7 -0
  22. package/dist/lib/dashboard-auth.d.ts.map +1 -0
  23. package/dist/lib/dashboard-auth.js +38 -0
  24. package/dist/lib/dashboard-auth.js.map +1 -0
  25. package/dist/lib/http-auth.d.ts +5 -0
  26. package/dist/lib/http-auth.d.ts.map +1 -0
  27. package/dist/lib/http-auth.js +11 -0
  28. package/dist/lib/http-auth.js.map +1 -0
  29. package/dist/lib/route-policy.d.ts +13 -0
  30. package/dist/lib/route-policy.d.ts.map +1 -0
  31. package/dist/lib/route-policy.js +50 -0
  32. package/dist/lib/route-policy.js.map +1 -0
  33. package/dist/mcp-server.d.ts +7 -0
  34. package/dist/mcp-server.d.ts.map +1 -1
  35. package/dist/mcp-server.js +101 -23
  36. package/dist/mcp-server.js.map +1 -1
  37. package/dist/mcp-session-binding.d.ts +16 -3
  38. package/dist/mcp-session-binding.d.ts.map +1 -1
  39. package/dist/mcp-session-binding.js +40 -5
  40. package/dist/mcp-session-binding.js.map +1 -1
  41. package/dist/mcp-tools/policy.d.ts +20 -0
  42. package/dist/mcp-tools/policy.d.ts.map +1 -0
  43. package/dist/mcp-tools/policy.js +61 -0
  44. package/dist/mcp-tools/policy.js.map +1 -0
  45. package/dist/mcp-tools/profile.d.ts +2 -2
  46. package/dist/mcp-tools/register.d.ts +13 -0
  47. package/dist/mcp-tools/register.d.ts.map +1 -1
  48. package/dist/mcp-tools/register.js +154 -89
  49. package/dist/mcp-tools/register.js.map +1 -1
  50. package/dist/mcp-tools/test-harness.d.ts +7 -2
  51. package/dist/mcp-tools/test-harness.d.ts.map +1 -1
  52. package/dist/mcp-tools/test-harness.js +37 -9
  53. package/dist/mcp-tools/test-harness.js.map +1 -1
  54. package/dist/models/database.d.ts +2 -0
  55. package/dist/models/database.d.ts.map +1 -1
  56. package/dist/models/database.js +3 -0
  57. package/dist/models/database.js.map +1 -1
  58. package/dist/routes/credentials.d.ts.map +1 -1
  59. package/dist/routes/credentials.js +25 -23
  60. package/dist/routes/credentials.js.map +1 -1
  61. package/dist/routes/decks.d.ts.map +1 -1
  62. package/dist/routes/decks.js +75 -33
  63. package/dist/routes/decks.js.map +1 -1
  64. package/dist/routes/oauth.d.ts.map +1 -1
  65. package/dist/routes/oauth.js +16 -5
  66. package/dist/routes/oauth.js.map +1 -1
  67. package/dist/routes/playbooks.d.ts +0 -2
  68. package/dist/routes/playbooks.d.ts.map +1 -1
  69. package/dist/routes/playbooks.js +58 -117
  70. package/dist/routes/playbooks.js.map +1 -1
  71. package/dist/routes/scope.d.ts.map +1 -1
  72. package/dist/routes/scope.js +6 -2
  73. package/dist/routes/scope.js.map +1 -1
  74. package/dist/routes/services.d.ts.map +1 -1
  75. package/dist/routes/services.js +65 -16
  76. package/dist/routes/services.js.map +1 -1
  77. package/dist/routes/trusted-session.d.ts +10 -0
  78. package/dist/routes/trusted-session.d.ts.map +1 -0
  79. package/dist/routes/trusted-session.js +383 -0
  80. package/dist/routes/trusted-session.js.map +1 -0
  81. package/dist/scope/live-display-registry.d.ts +2 -1
  82. package/dist/scope/live-display-registry.d.ts.map +1 -1
  83. package/dist/scope/live-display-registry.js +3 -1
  84. package/dist/scope/live-display-registry.js.map +1 -1
  85. package/dist/server/index.d.ts +4 -1
  86. package/dist/server/index.d.ts.map +1 -1
  87. package/dist/server/index.js +28 -3
  88. package/dist/server/index.js.map +1 -1
  89. package/dist/services/collection-warning-service.d.ts +3 -0
  90. package/dist/services/collection-warning-service.d.ts.map +1 -1
  91. package/dist/services/collection-warning-service.js +24 -1
  92. package/dist/services/collection-warning-service.js.map +1 -1
  93. package/dist/services/oauth-manager.d.ts +2 -0
  94. package/dist/services/oauth-manager.d.ts.map +1 -1
  95. package/dist/services/oauth-manager.js +12 -0
  96. package/dist/services/oauth-manager.js.map +1 -1
  97. package/dist/services/service-manager.d.ts +21 -1
  98. package/dist/services/service-manager.d.ts.map +1 -1
  99. package/dist/services/service-manager.js +95 -10
  100. package/dist/services/service-manager.js.map +1 -1
  101. package/dist/test/auth-fixtures.d.ts +4 -0
  102. package/dist/test/auth-fixtures.d.ts.map +1 -0
  103. package/dist/test/auth-fixtures.js +28 -0
  104. package/dist/test/auth-fixtures.js.map +1 -0
  105. package/dist/trusted-session/admin-secret.d.ts +6 -0
  106. package/dist/trusted-session/admin-secret.d.ts.map +1 -0
  107. package/dist/trusted-session/admin-secret.js +63 -0
  108. package/dist/trusted-session/admin-secret.js.map +1 -0
  109. package/dist/trusted-session/auth.d.ts +29 -0
  110. package/dist/trusted-session/auth.d.ts.map +1 -0
  111. package/dist/trusted-session/auth.js +176 -0
  112. package/dist/trusted-session/auth.js.map +1 -0
  113. package/dist/trusted-session/policy-hook.d.ts +12 -0
  114. package/dist/trusted-session/policy-hook.d.ts.map +1 -0
  115. package/dist/trusted-session/policy-hook.js +71 -0
  116. package/dist/trusted-session/policy-hook.js.map +1 -0
  117. package/dist/trusted-session/route-policy-registry.d.ts +20 -0
  118. package/dist/trusted-session/route-policy-registry.d.ts.map +1 -0
  119. package/dist/trusted-session/route-policy-registry.js +165 -0
  120. package/dist/trusted-session/route-policy-registry.js.map +1 -0
  121. package/dist/trusted-session/store.d.ts +93 -0
  122. package/dist/trusted-session/store.d.ts.map +1 -0
  123. package/dist/trusted-session/store.js +451 -0
  124. package/dist/trusted-session/store.js.map +1 -0
  125. package/dist/vault/index.d.ts +1 -0
  126. package/dist/vault/index.d.ts.map +1 -1
  127. package/dist/vault/index.js +1 -0
  128. package/dist/vault/index.js.map +1 -1
  129. package/dist/vault/service-header-vault.d.ts +16 -0
  130. package/dist/vault/service-header-vault.d.ts.map +1 -0
  131. package/dist/vault/service-header-vault.js +54 -0
  132. package/dist/vault/service-header-vault.js.map +1 -0
  133. package/package.json +5 -5
  134. package/static-ui/assets/index-DUyowkPn.js +499 -0
  135. package/static-ui/assets/index-cJDgESGn.css +1 -0
  136. package/static-ui/index.html +2 -2
  137. package/dist/mcp-stdio.d.ts +0 -2
  138. package/dist/mcp-stdio.d.ts.map +0 -1
  139. package/dist/mcp-stdio.js.map +0 -1
  140. package/dist/playbooks/playbook-parser.d.ts +0 -8
  141. package/dist/playbooks/playbook-parser.d.ts.map +0 -1
  142. package/dist/playbooks/playbook-parser.js +0 -76
  143. package/dist/playbooks/playbook-parser.js.map +0 -1
  144. package/dist/playbooks/playbook-service.d.ts +0 -9
  145. package/dist/playbooks/playbook-service.d.ts.map +0 -1
  146. package/dist/playbooks/playbook-service.js +0 -107
  147. package/dist/playbooks/playbook-service.js.map +0 -1
  148. package/dist/scope/bindings-sidecar.d.ts +0 -9
  149. package/dist/scope/bindings-sidecar.d.ts.map +0 -1
  150. package/dist/scope/bindings-sidecar.js +0 -57
  151. package/dist/scope/bindings-sidecar.js.map +0 -1
  152. package/dist/scope/repo-deck.d.ts +0 -10
  153. package/dist/scope/repo-deck.d.ts.map +0 -1
  154. package/dist/scope/repo-deck.js +0 -63
  155. package/dist/scope/repo-deck.js.map +0 -1
  156. package/dist/test-local-mcp-e2e.d.ts +0 -3
  157. package/dist/test-local-mcp-e2e.d.ts.map +0 -1
  158. package/dist/test-local-mcp-e2e.js +0 -104
  159. package/dist/test-local-mcp-e2e.js.map +0 -1
  160. package/static-ui/assets/index-C5IxoxXU.js +0 -485
  161. package/static-ui/assets/index-D2xN_rHi.css +0 -1
  162. package/static-ui/favicon.png +0 -0
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dashboardAuthHeaders = dashboardAuthHeaders;
4
+ exports.agentSessionHeaders = agentSessionHeaders;
5
+ const shared_1 = require("@agent-deck/shared");
6
+ const auth_1 = require("../trusted-session/auth");
7
+ const store_1 = require("../trusted-session/store");
8
+ function dashboardAuthHeaders() {
9
+ const token = (0, auth_1.createDashboardSessionToken)();
10
+ return {
11
+ cookie: `${shared_1.AGENT_DECK_DASHBOARD_COOKIE}=${encodeURIComponent(token)}`,
12
+ };
13
+ }
14
+ function agentSessionHeaders(db, deckId, workspaceDigest = 'test-workspace-digest') {
15
+ const store = new store_1.TrustedSessionStore(db.getSqliteDatabase());
16
+ const workspace = store.getOrCreateWorkspaceKey(workspaceDigest);
17
+ const secret = (0, store_1.generateGrantSecret)();
18
+ const pending = store.createPendingGrant(workspace.id, deckId, secret);
19
+ store.activateGrant(pending.id);
20
+ const grant = store.findActiveGrantBySecret(secret);
21
+ const session = store.createRuntimeSession({
22
+ workspaceKeyId: workspace.id,
23
+ workspaceGrantId: grant.id,
24
+ deckId,
25
+ });
26
+ return { [shared_1.AGENT_DECK_SESSION_HEADER]: session.sessionId };
27
+ }
28
+ //# sourceMappingURL=auth-fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-fixtures.js","sourceRoot":"","sources":["../../src/test/auth-fixtures.ts"],"names":[],"mappings":";;AASA,oDAKC;AAED,kDAiBC;AAjCD,+CAG4B;AAG5B,kDAAsE;AACtE,oDAAoF;AAEpF,SAAgB,oBAAoB;IAClC,MAAM,KAAK,GAAG,IAAA,kCAA2B,GAAE,CAAC;IAC5C,OAAO;QACL,MAAM,EAAE,GAAG,oCAA2B,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE;KACtE,CAAC;AACJ,CAAC;AAED,SAAgB,mBAAmB,CACjC,EAAmB,EACnB,MAAc,EACd,eAAe,GAAG,uBAAuB;IAEzC,MAAM,KAAK,GAAG,IAAI,2BAAmB,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,IAAA,2BAAmB,GAAE,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,uBAAuB,CAAC,MAAM,CAAE,CAAC;IACrD,MAAM,OAAO,GAAG,KAAK,CAAC,oBAAoB,CAAC;QACzC,cAAc,EAAE,SAAS,CAAC,EAAE;QAC5B,gBAAgB,EAAE,KAAK,CAAC,EAAE;QAC1B,MAAM;KACP,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,kCAAyB,CAAC,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function getAdminSecretPath(): Promise<string>;
2
+ export declare function ensureAdminSecret(): Promise<string>;
3
+ export declare function readAdminSecretFromEnvOrFile(): Promise<string | null>;
4
+ export declare function hashAdminSecret(secret: string): string;
5
+ export declare function verifyAdminSecret(provided: string, expected: string): boolean;
6
+ //# sourceMappingURL=admin-secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-secret.d.ts","sourceRoot":"","sources":["../../src/trusted-session/admin-secret.ts"],"names":[],"mappings":"AASA,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAE1D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBzD;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc3E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAO7E"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAdminSecretPath = getAdminSecretPath;
7
+ exports.ensureAdminSecret = ensureAdminSecret;
8
+ exports.readAdminSecretFromEnvOrFile = readAdminSecretFromEnvOrFile;
9
+ exports.hashAdminSecret = hashAdminSecret;
10
+ exports.verifyAdminSecret = verifyAdminSecret;
11
+ const node_crypto_1 = require("node:crypto");
12
+ const promises_1 = __importDefault(require("node:fs/promises"));
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const shared_1 = require("@agent-deck/shared");
15
+ const ADMIN_SECRET_FILENAME = 'admin-secret';
16
+ const ADMIN_SECRET_ENV = 'AGENT_DECK_ADMIN_SECRET';
17
+ async function getAdminSecretPath() {
18
+ return node_path_1.default.join((0, shared_1.resolveAgentDeckHome)(), ADMIN_SECRET_FILENAME);
19
+ }
20
+ async function ensureAdminSecret() {
21
+ const secretPath = await getAdminSecretPath();
22
+ try {
23
+ const existing = await promises_1.default.readFile(secretPath, 'utf8');
24
+ const trimmed = existing.trim();
25
+ if (trimmed.length >= 32) {
26
+ return trimmed;
27
+ }
28
+ }
29
+ catch {
30
+ // create below
31
+ }
32
+ const secret = (0, node_crypto_1.randomBytes)(32).toString('base64url');
33
+ await promises_1.default.mkdir(node_path_1.default.dirname(secretPath), { recursive: true, mode: 0o700 });
34
+ await promises_1.default.writeFile(secretPath, `${secret}\n`, { encoding: 'utf8', mode: 0o600 });
35
+ return secret;
36
+ }
37
+ async function readAdminSecretFromEnvOrFile() {
38
+ const fromEnv = process.env[ADMIN_SECRET_ENV]?.trim();
39
+ if (fromEnv && fromEnv.length >= 32) {
40
+ return fromEnv;
41
+ }
42
+ try {
43
+ const secretPath = await getAdminSecretPath();
44
+ const value = await promises_1.default.readFile(secretPath, 'utf8');
45
+ const trimmed = value.trim();
46
+ return trimmed.length >= 32 ? trimmed : null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ function hashAdminSecret(secret) {
53
+ return (0, node_crypto_1.createHash)('sha256').update(secret, 'utf8').digest('hex');
54
+ }
55
+ function verifyAdminSecret(provided, expected) {
56
+ const digest = Buffer.from(hashAdminSecret(provided), 'hex');
57
+ const expectedDigest = Buffer.from(hashAdminSecret(expected), 'hex');
58
+ if (digest.length !== expectedDigest.length) {
59
+ return false;
60
+ }
61
+ return (0, node_crypto_1.timingSafeEqual)(digest, expectedDigest);
62
+ }
63
+ //# sourceMappingURL=admin-secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-secret.js","sourceRoot":"","sources":["../../src/trusted-session/admin-secret.ts"],"names":[],"mappings":";;;;;AASA,gDAEC;AAED,8CAgBC;AAED,oEAcC;AAED,0CAEC;AAED,8CAOC;AA1DD,6CAAuE;AACvE,gEAAkC;AAClC,0DAA6B;AAE7B,+CAA0D;AAE1D,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAC7C,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAE5C,KAAK,UAAU,kBAAkB;IACtC,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAA,6BAAoB,GAAE,EAAE,qBAAqB,CAAC,CAAC;AAClE,CAAC;AAEM,KAAK,UAAU,iBAAiB;IACrC,MAAM,UAAU,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACrD,MAAM,kBAAE,CAAC,KAAK,CAAC,mBAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3E,MAAM,kBAAE,CAAC,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,OAAO,MAAM,CAAC;AAChB,CAAC;AAEM,KAAK,UAAU,4BAA4B;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACpC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,OAAO,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAAC,MAAc;IAC5C,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,SAAgB,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;IAClE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAA,6BAAe,EAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { FastifyReply, FastifyRequest } from 'fastify';
2
+ import { type AgentSessionMode, type RuntimeSession, type TrustedSessionErrorCode } from '@agent-deck/shared';
3
+ import type { TrustedSessionStore } from './store';
4
+ export type AuthPolicy = 'allowPublic' | 'requireTrustedWriter' | 'requireAgentResource' | 'requireAgentOrDashboard' | 'requireDeckAdmin' | 'requireDashboard';
5
+ export type RequestPrincipal = {
6
+ kind: 'public';
7
+ } | {
8
+ kind: 'dashboard';
9
+ } | {
10
+ kind: 'agent';
11
+ session: RuntimeSession;
12
+ mode: AgentSessionMode;
13
+ deckId: string;
14
+ workspaceKey: string;
15
+ };
16
+ export declare class TrustedAuthError extends Error {
17
+ readonly code: TrustedSessionErrorCode;
18
+ constructor(code: TrustedSessionErrorCode, message: string);
19
+ }
20
+ export declare function createDashboardSessionToken(): string;
21
+ export declare function validateDashboardSessionToken(token: string): boolean;
22
+ export declare function resolveRequestPrincipal(request: FastifyRequest, store: TrustedSessionStore): Promise<RequestPrincipal>;
23
+ export declare function requireTrustedWriterBearer(request: FastifyRequest): Promise<void>;
24
+ export declare function enforcePolicy(policy: AuthPolicy, principal: RequestPrincipal): void;
25
+ export declare function sendTrustedAuthError(reply: FastifyReply, error: TrustedAuthError): void;
26
+ export declare function getAgentDeckId(principal: RequestPrincipal): string | null;
27
+ export declare function isDashboardPrincipal(principal: RequestPrincipal): boolean;
28
+ export declare function isAgentAdmin(principal: RequestPrincipal): boolean;
29
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/trusted-session/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAG7B,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,MAAM,UAAU,GAClB,aAAa,GACb,sBAAsB,GACtB,sBAAsB,GACtB,yBAAyB,GACzB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAAE,uBAAuB;gBAA7B,IAAI,EAAE,uBAAuB,EAC7C,OAAO,EAAE,MAAM;CAKlB;AAqBD,wBAAgB,2BAA2B,IAAI,MAAM,CAIpD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAUpE;AA+DD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA8B3B;AAED,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,GAAG,IAAI,CA0BnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAGvF;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAEzE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAEzE;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrustedAuthError = void 0;
4
+ exports.createDashboardSessionToken = createDashboardSessionToken;
5
+ exports.validateDashboardSessionToken = validateDashboardSessionToken;
6
+ exports.resolveRequestPrincipal = resolveRequestPrincipal;
7
+ exports.requireTrustedWriterBearer = requireTrustedWriterBearer;
8
+ exports.enforcePolicy = enforcePolicy;
9
+ exports.sendTrustedAuthError = sendTrustedAuthError;
10
+ exports.getAgentDeckId = getAgentDeckId;
11
+ exports.isDashboardPrincipal = isDashboardPrincipal;
12
+ exports.isAgentAdmin = isAgentAdmin;
13
+ const node_crypto_1 = require("node:crypto");
14
+ const shared_1 = require("@agent-deck/shared");
15
+ const http_auth_1 = require("../lib/http-auth");
16
+ const admin_secret_1 = require("./admin-secret");
17
+ class TrustedAuthError extends Error {
18
+ code;
19
+ constructor(code, message) {
20
+ super(message);
21
+ this.code = code;
22
+ this.name = 'TrustedAuthError';
23
+ }
24
+ }
25
+ exports.TrustedAuthError = TrustedAuthError;
26
+ const dashboardSessions = new Map();
27
+ const DASHBOARD_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
28
+ function parseDashboardCookie(request) {
29
+ const cookieHeader = request.headers.cookie;
30
+ if (!cookieHeader || typeof cookieHeader !== 'string') {
31
+ return null;
32
+ }
33
+ const prefix = `${shared_1.AGENT_DECK_DASHBOARD_COOKIE}=`;
34
+ for (const part of cookieHeader.split(';')) {
35
+ const trimmed = part.trim();
36
+ if (trimmed.startsWith(prefix)) {
37
+ return decodeURIComponent(trimmed.slice(prefix.length));
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ function createDashboardSessionToken() {
43
+ const token = (0, node_crypto_1.randomBytes)(32).toString('base64url');
44
+ dashboardSessions.set(token, { expiresAt: Date.now() + DASHBOARD_SESSION_TTL_MS });
45
+ return token;
46
+ }
47
+ function validateDashboardSessionToken(token) {
48
+ const entry = dashboardSessions.get(token);
49
+ if (!entry) {
50
+ return false;
51
+ }
52
+ if (entry.expiresAt <= Date.now()) {
53
+ dashboardSessions.delete(token);
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ async function resolveDashboardPrincipal(request) {
59
+ const dashboardToken = parseDashboardCookie(request);
60
+ if (dashboardToken && validateDashboardSessionToken(dashboardToken)) {
61
+ return { kind: 'dashboard' };
62
+ }
63
+ const bearer = (0, http_auth_1.parseBearerToken)(request);
64
+ if (bearer) {
65
+ const expected = await (0, admin_secret_1.readAdminSecretFromEnvOrFile)();
66
+ if (expected && (0, admin_secret_1.verifyAdminSecret)(bearer, expected)) {
67
+ return { kind: 'dashboard' };
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ function resolveAgentFromSessionHeader(request, store) {
73
+ const sessionHeader = request.headers[shared_1.AGENT_DECK_SESSION_HEADER];
74
+ const sessionId = typeof sessionHeader === 'string' ? sessionHeader.trim() : '';
75
+ if (!sessionId) {
76
+ return null;
77
+ }
78
+ const row = store.getRuntimeSessionRow(sessionId);
79
+ if (!row) {
80
+ throw new TrustedAuthError('SESSION_INVALID', 'Runtime session absent or expired');
81
+ }
82
+ if (row.revoked_at) {
83
+ throw new TrustedAuthError('SESSION_REVOKED', 'Grant rotation or explicit revocation ended the session');
84
+ }
85
+ if (Date.parse(row.expires_at) <= Date.now()) {
86
+ throw new TrustedAuthError('SESSION_INVALID', 'Runtime session absent or expired');
87
+ }
88
+ const session = store.touchRuntimeSession(sessionId);
89
+ if (!session) {
90
+ throw new TrustedAuthError('SESSION_INVALID', 'Runtime session absent or expired');
91
+ }
92
+ return session;
93
+ }
94
+ function resolveAgentFromGrantSecret(grantSecret, store) {
95
+ const grant = store.findActiveGrantBySecret(grantSecret);
96
+ if (!grant) {
97
+ throw new TrustedAuthError('GRANT_REQUIRED', 'No valid workspace grant');
98
+ }
99
+ return store.createRuntimeSession({
100
+ workspaceKeyId: grant.workspace_key_id,
101
+ workspaceGrantId: grant.id,
102
+ deckId: grant.deck_id,
103
+ });
104
+ }
105
+ async function resolveRequestPrincipal(request, store) {
106
+ const dashboard = await resolveDashboardPrincipal(request);
107
+ if (dashboard) {
108
+ return dashboard;
109
+ }
110
+ const sessionFromHeader = resolveAgentFromSessionHeader(request, store);
111
+ if (sessionFromHeader) {
112
+ return {
113
+ kind: 'agent',
114
+ session: sessionFromHeader,
115
+ mode: sessionFromHeader.mode,
116
+ deckId: sessionFromHeader.deckId,
117
+ workspaceKey: sessionFromHeader.workspaceKey,
118
+ };
119
+ }
120
+ const grantSecret = (0, http_auth_1.parseBearerToken)(request);
121
+ if (grantSecret) {
122
+ const session = resolveAgentFromGrantSecret(grantSecret, store);
123
+ return {
124
+ kind: 'agent',
125
+ session,
126
+ mode: session.mode,
127
+ deckId: session.deckId,
128
+ workspaceKey: session.workspaceKey,
129
+ };
130
+ }
131
+ throw new TrustedAuthError('GRANT_REQUIRED', 'No valid workspace grant');
132
+ }
133
+ async function requireTrustedWriterBearer(request) {
134
+ const bearer = (0, http_auth_1.parseBearerToken)(request);
135
+ const expected = await (0, admin_secret_1.readAdminSecretFromEnvOrFile)();
136
+ if (!bearer || !expected || !(0, admin_secret_1.verifyAdminSecret)(bearer, expected)) {
137
+ throw new TrustedAuthError('DASHBOARD_REQUIRED', 'Trusted writer authentication required');
138
+ }
139
+ }
140
+ function enforcePolicy(policy, principal) {
141
+ if (policy === 'allowPublic') {
142
+ return;
143
+ }
144
+ if (policy === 'requireDashboard') {
145
+ if (principal.kind !== 'dashboard') {
146
+ throw new TrustedAuthError('DASHBOARD_REQUIRED', 'Dashboard authentication required');
147
+ }
148
+ return;
149
+ }
150
+ if (policy === 'requireAgentOrDashboard') {
151
+ if (principal.kind === 'dashboard' || principal.kind === 'agent') {
152
+ return;
153
+ }
154
+ throw new TrustedAuthError('GRANT_REQUIRED', 'No valid workspace grant');
155
+ }
156
+ if (principal.kind !== 'agent') {
157
+ throw new TrustedAuthError('GRANT_REQUIRED', 'No valid workspace grant');
158
+ }
159
+ if (policy === 'requireDeckAdmin' && principal.mode !== 'agent-admin') {
160
+ throw new TrustedAuthError('ADMIN_REQUIRED', 'Deck-admin elevation is required');
161
+ }
162
+ }
163
+ function sendTrustedAuthError(reply, error) {
164
+ const body = (0, shared_1.trustedSessionError)(error.code, error.message);
165
+ reply.status((0, shared_1.httpStatusForTrustedError)(error.code)).send(body);
166
+ }
167
+ function getAgentDeckId(principal) {
168
+ return principal.kind === 'agent' ? principal.deckId : null;
169
+ }
170
+ function isDashboardPrincipal(principal) {
171
+ return principal.kind === 'dashboard';
172
+ }
173
+ function isAgentAdmin(principal) {
174
+ return principal.kind === 'agent' && principal.mode === 'agent-admin';
175
+ }
176
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/trusted-session/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import { type RequestPrincipal } from './auth';
3
+ import { type RegisteredRoute } from './route-policy-registry';
4
+ declare module 'fastify' {
5
+ interface FastifyRequest {
6
+ requestPrincipal?: RequestPrincipal;
7
+ }
8
+ }
9
+ export declare const registeredHttpRoutes: RegisteredRoute[];
10
+ export declare function registerHttpPolicyHook(fastify: FastifyInstance): void;
11
+ export declare function assertAllRoutesHavePolicies(routes?: RegisteredRoute[]): void;
12
+ //# sourceMappingURL=policy-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-hook.d.ts","sourceRoot":"","sources":["../../src/trusted-session/policy-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAE7E,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC;CACF;AAED,eAAO,MAAM,oBAAoB,EAAE,eAAe,EAAO,CAAC;AAE1D,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CA6DrE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,GAAE,eAAe,EAAyB,GAAG,IAAI,CAMlG"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registeredHttpRoutes = void 0;
4
+ exports.registerHttpPolicyHook = registerHttpPolicyHook;
5
+ exports.assertAllRoutesHavePolicies = assertAllRoutesHavePolicies;
6
+ const auth_1 = require("./auth");
7
+ const route_policy_registry_1 = require("./route-policy-registry");
8
+ exports.registeredHttpRoutes = [];
9
+ function registerHttpPolicyHook(fastify) {
10
+ exports.registeredHttpRoutes.length = 0;
11
+ fastify.addHook('onRoute', (routeOptions) => {
12
+ const methods = Array.isArray(routeOptions.method)
13
+ ? routeOptions.method
14
+ : [routeOptions.method];
15
+ for (const method of methods) {
16
+ exports.registeredHttpRoutes.push({
17
+ method: String(method).toUpperCase(),
18
+ url: routeOptions.url,
19
+ });
20
+ }
21
+ });
22
+ fastify.addHook('onRequest', async (request, reply) => {
23
+ const pathname = request.url.split('?')[0];
24
+ if (!(0, route_policy_registry_1.shouldApplyHttpPolicy)(pathname)) {
25
+ return;
26
+ }
27
+ if (request.method === 'HEAD') {
28
+ return;
29
+ }
30
+ const policy = (0, route_policy_registry_1.resolveRoutePolicy)(request.method, pathname);
31
+ if (!policy) {
32
+ return (0, auth_1.sendTrustedAuthError)(reply, new auth_1.TrustedAuthError('DASHBOARD_REQUIRED', `No authorization policy for ${request.method} ${pathname}`));
33
+ }
34
+ if (policy === 'allowPublic') {
35
+ request.requestPrincipal = { kind: 'public' };
36
+ return;
37
+ }
38
+ if (policy === 'requireTrustedWriter') {
39
+ try {
40
+ await (0, auth_1.requireTrustedWriterBearer)(request);
41
+ request.requestPrincipal = { kind: 'dashboard' };
42
+ }
43
+ catch (error) {
44
+ if (error instanceof auth_1.TrustedAuthError) {
45
+ return (0, auth_1.sendTrustedAuthError)(reply, error);
46
+ }
47
+ throw error;
48
+ }
49
+ return;
50
+ }
51
+ try {
52
+ const principal = await (0, auth_1.resolveRequestPrincipal)(request, fastify.trustedSessionStore);
53
+ (0, auth_1.enforcePolicy)(policy, principal);
54
+ request.requestPrincipal = principal;
55
+ }
56
+ catch (error) {
57
+ if (error instanceof auth_1.TrustedAuthError) {
58
+ return (0, auth_1.sendTrustedAuthError)(reply, error);
59
+ }
60
+ throw error;
61
+ }
62
+ });
63
+ }
64
+ function assertAllRoutesHavePolicies(routes = exports.registeredHttpRoutes) {
65
+ const unmatched = (0, route_policy_registry_1.findUnmatchedRoutes)(routes);
66
+ if (unmatched.length > 0) {
67
+ const lines = unmatched.map((route) => `${route.method} ${route.url}`).join('\n');
68
+ throw new Error(`HTTP routes missing authorization policy:\n${lines}`);
69
+ }
70
+ }
71
+ //# sourceMappingURL=policy-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-hook.js","sourceRoot":"","sources":["../../src/trusted-session/policy-hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { AuthPolicy } from './auth';
2
+ export type RoutePolicyRule = {
3
+ methods: string[];
4
+ /** Regex against pathname (no query). Use [^/]+ for :param segments. */
5
+ pattern: RegExp;
6
+ policy: AuthPolicy;
7
+ };
8
+ /**
9
+ * Authoritative HTTP route → policy map (NOT-45 §6).
10
+ * Every /api/* route and /health must match exactly one rule.
11
+ */
12
+ export declare const HTTP_ROUTE_POLICIES: RoutePolicyRule[];
13
+ export declare function shouldApplyHttpPolicy(pathname: string): boolean;
14
+ export declare function resolveRoutePolicy(method: string, pathname: string): AuthPolicy | null;
15
+ export type RegisteredRoute = {
16
+ method: string;
17
+ url: string;
18
+ };
19
+ export declare function findUnmatchedRoutes(routes: RegisteredRoute[]): RegisteredRoute[];
20
+ //# sourceMappingURL=route-policy-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-policy-registry.d.ts","sourceRoot":"","sources":["../../src/trusted-session/route-policy-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,eAAe,EA2HhD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAQtF;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAmBhF"}
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_ROUTE_POLICIES = void 0;
4
+ exports.shouldApplyHttpPolicy = shouldApplyHttpPolicy;
5
+ exports.resolveRoutePolicy = resolveRoutePolicy;
6
+ exports.findUnmatchedRoutes = findUnmatchedRoutes;
7
+ /**
8
+ * Authoritative HTTP route → policy map (NOT-45 §6).
9
+ * Every /api/* route and /health must match exactly one rule.
10
+ */
11
+ exports.HTTP_ROUTE_POLICIES = [
12
+ // Public
13
+ { methods: ['GET'], pattern: /^\/health$/, policy: 'allowPublic' },
14
+ { methods: ['GET'], pattern: /^\/$/, policy: 'allowPublic' },
15
+ { methods: ['GET'], pattern: /^\/api\/oauth\/callback$/, policy: 'allowPublic' },
16
+ { methods: ['GET'], pattern: /^\/api\/oauth\/[^/]+\/callback$/, policy: 'allowPublic' },
17
+ { methods: ['GET'], pattern: /^\/api\/services\/[^/]+\/icon$/, policy: 'allowPublic' },
18
+ { methods: ['GET'], pattern: /^\/api\/credentials\/[^/]+\/icon$/, policy: 'allowPublic' },
19
+ { methods: ['GET'], pattern: /^\/api\/scope\/display$/, policy: 'allowPublic' },
20
+ { methods: ['GET'], pattern: /^\/api\/scope\/bindings$/, policy: 'allowPublic' },
21
+ { methods: ['GET'], pattern: /^\/api\/trusted-session\/admin\/challenges$/, policy: 'allowPublic' },
22
+ { methods: ['POST'], pattern: /^\/api\/trusted-session\/mcp\/connect$/, policy: 'allowPublic' },
23
+ { methods: ['POST'], pattern: /^\/api\/dashboard-auth\/bootstrap\/session$/, policy: 'allowPublic' },
24
+ // Trusted writer (admin secret bearer only — enforced in policy hook)
25
+ { methods: ['POST'], pattern: /^\/api\/trusted-session\/workspace-grants\/issue$/, policy: 'requireTrustedWriter' },
26
+ {
27
+ methods: ['POST'],
28
+ pattern: /^\/api\/trusted-session\/workspace-grants\/[^/]+\/revoke-pending$/,
29
+ policy: 'requireTrustedWriter',
30
+ },
31
+ {
32
+ methods: ['POST'],
33
+ pattern: /^\/api\/trusted-session\/workspace-grants\/[^/]+\/activate$/,
34
+ policy: 'requireTrustedWriter',
35
+ },
36
+ { methods: ['POST'], pattern: /^\/api\/dashboard-auth\/bootstrap\/nonce$/, policy: 'requireTrustedWriter' },
37
+ // Agent resource (runtime session or grant bearer)
38
+ { methods: ['GET'], pattern: /^\/api\/trusted-session\/runtime-session$/, policy: 'requireAgentResource' },
39
+ {
40
+ methods: ['POST'],
41
+ pattern: /^\/api\/trusted-session\/admin\/request-elevation$/,
42
+ policy: 'requireAgentResource',
43
+ },
44
+ { methods: ['POST'], pattern: /^\/api\/trusted-session\/admin\/exit$/, policy: 'requireAgentResource' },
45
+ { methods: ['POST'], pattern: /^\/api\/trusted-session\/bind-workspace$/, policy: 'requireAgentResource' },
46
+ { methods: ['GET'], pattern: /^\/api\/scope\/deck$/, policy: 'requireAgentResource' },
47
+ { methods: ['POST'], pattern: /^\/api\/scope\/live-display$/, policy: 'requireAgentResource' },
48
+ { methods: ['DELETE'], pattern: /^\/api\/scope\/live-display\/[^/]+$/, policy: 'requireAgentResource' },
49
+ {
50
+ methods: ['POST'],
51
+ pattern: /^\/api\/scope\/live-display\/[^/]+\/touch$/,
52
+ policy: 'requireAgentResource',
53
+ },
54
+ { methods: ['POST'], pattern: /^\/api\/scope\/deck-workspace$/, policy: 'requireAgentResource' },
55
+ // Deck admin (agent-admin mode)
56
+ { methods: ['POST'], pattern: /^\/api\/decks$/, policy: 'requireDeckAdmin' },
57
+ // Dashboard-only
58
+ { methods: ['GET'], pattern: /^\/api\/decks\/active$/, policy: 'requireDashboard' },
59
+ { methods: ['PUT'], pattern: /^\/api\/decks\/[^/]+$/, policy: 'requireDashboard' },
60
+ { methods: ['DELETE'], pattern: /^\/api\/decks\/[^/]+$/, policy: 'requireDashboard' },
61
+ { methods: ['POST'], pattern: /^\/api\/decks\/[^/]+\/activate$/, policy: 'requireDashboard' },
62
+ { methods: ['POST'], pattern: /^\/api\/services$/, policy: 'requireDashboard' },
63
+ { methods: ['PUT'], pattern: /^\/api\/services\/[^/]+$/, policy: 'requireDashboard' },
64
+ { methods: ['DELETE'], pattern: /^\/api\/services\/[^/]+$/, policy: 'requireDashboard' },
65
+ { methods: ['POST'], pattern: /^\/api\/services\/[^/]+\/refresh-icon$/, policy: 'requireDashboard' },
66
+ { methods: ['GET'], pattern: /^\/api\/services\/[^/]+\/health$/, policy: 'requireDashboard' },
67
+ { methods: ['GET'], pattern: /^\/api\/credentials\/collection$/, policy: 'requireDashboard' },
68
+ { methods: ['GET'], pattern: /^\/api\/credentials\/vault$/, policy: 'requireDashboard' },
69
+ { methods: ['POST'], pattern: /^\/api\/credentials$/, policy: 'requireDashboard' },
70
+ { methods: ['PUT'], pattern: /^\/api\/credentials\/[^/]+$/, policy: 'requireDashboard' },
71
+ { methods: ['POST'], pattern: /^\/api\/credentials\/[^/]+\/rotate$/, policy: 'requireDashboard' },
72
+ { methods: ['DELETE'], pattern: /^\/api\/credentials\/[^/]+$/, policy: 'requireDashboard' },
73
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/collection$/, policy: 'requireDashboard' },
74
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/vault$/, policy: 'requireDashboard' },
75
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/dependents\/check$/, policy: 'requireDashboard' },
76
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/[^/]+\/events\/count$/, policy: 'requireDashboard' },
77
+ { methods: ['POST'], pattern: /^\/api\/playbooks$/, policy: 'requireDashboard' },
78
+ { methods: ['PUT'], pattern: /^\/api\/playbooks\/[^/]+$/, policy: 'requireDashboard' },
79
+ { methods: ['DELETE'], pattern: /^\/api\/playbooks\/[^/]+$/, policy: 'requireDashboard' },
80
+ { methods: ['PUT'], pattern: /^\/api\/services\/[^/]+\/tool-settings$/, policy: 'requireDashboard' },
81
+ { methods: ['GET'], pattern: /^\/api\/playbook-patches$/, policy: 'requireDashboard' },
82
+ { methods: ['GET'], pattern: /^\/api\/playbook-patches\/[^/]+\/preview$/, policy: 'requireDashboard' },
83
+ { methods: ['POST'], pattern: /^\/api\/playbook-patches\/[^/]+\/accept$/, policy: 'requireDashboard' },
84
+ { methods: ['POST'], pattern: /^\/api\/playbook-patches\/[^/]+\/reject$/, policy: 'requireDashboard' },
85
+ { methods: ['GET'], pattern: /^\/api\/feedback-signals$/, policy: 'requireDashboard' },
86
+ { methods: ['GET'], pattern: /^\/api\/feedback-signals\/count$/, policy: 'requireDashboard' },
87
+ { methods: ['POST'], pattern: /^\/api\/feedback-signals\/discard$/, policy: 'requireDashboard' },
88
+ { methods: ['POST'], pattern: /^\/api\/feedback-signals\/import$/, policy: 'requireDashboard' },
89
+ { methods: ['GET'], pattern: /^\/api\/collection\/warnings$/, policy: 'requireDashboard' },
90
+ { methods: ['POST'], pattern: /^\/api\/export$/, policy: 'requireDashboard' },
91
+ { methods: ['POST'], pattern: /^\/api\/import$/, policy: 'requireDashboard' },
92
+ { methods: ['GET'], pattern: /^\/api\/oauth\/[^/]+\/discover$/, policy: 'requireDashboard' },
93
+ { methods: ['GET'], pattern: /^\/api\/oauth\/[^/]+\/setup$/, policy: 'requireDashboard' },
94
+ { methods: ['POST'], pattern: /^\/api\/oauth\/[^/]+\/connect$/, policy: 'requireDashboard' },
95
+ { methods: ['POST'], pattern: /^\/api\/oauth\/[^/]+\/auto-setup$/, policy: 'requireDashboard' },
96
+ { methods: ['GET'], pattern: /^\/api\/oauth\/[^/]+\/authorize$/, policy: 'requireDashboard' },
97
+ { methods: ['POST'], pattern: /^\/api\/oauth\/[^/]+\/refresh$/, policy: 'requireDashboard' },
98
+ { methods: ['GET'], pattern: /^\/api\/oauth\/[^/]+\/status$/, policy: 'requireDashboard' },
99
+ { methods: ['POST'], pattern: /^\/api\/mcp\/discover$/, policy: 'requireDashboard' },
100
+ { methods: ['POST'], pattern: /^\/api\/local-mcp\/import$/, policy: 'requireDashboard' },
101
+ { methods: ['GET'], pattern: /^\/api\/local-mcp\/sample-config$/, policy: 'requireDashboard' },
102
+ { methods: ['POST'], pattern: /^\/api\/local-mcp\/[^/]+\/start$/, policy: 'requireDashboard' },
103
+ { methods: ['POST'], pattern: /^\/api\/local-mcp\/[^/]+\/stop$/, policy: 'requireDashboard' },
104
+ { methods: ['GET'], pattern: /^\/api\/local-mcp\/[^/]+\/status$/, policy: 'requireDashboard' },
105
+ { methods: ['GET'], pattern: /^\/api\/local-mcp\/list$/, policy: 'requireDashboard' },
106
+ { methods: ['POST'], pattern: /^\/api\/trusted-session\/admin\/approve$/, policy: 'requireDashboard' },
107
+ // Agent or dashboard (dual-auth routes — handler branches on principal kind)
108
+ { methods: ['GET'], pattern: /^\/api\/decks$/, policy: 'requireAgentOrDashboard' },
109
+ { methods: ['GET'], pattern: /^\/api\/decks\/[^/]+$/, policy: 'requireAgentOrDashboard' },
110
+ { methods: ['GET'], pattern: /^\/api\/decks\/[^/]+\/services$/, policy: 'requireAgentOrDashboard' },
111
+ { methods: ['POST'], pattern: /^\/api\/decks\/[^/]+\/services$/, policy: 'requireAgentOrDashboard' },
112
+ { methods: ['DELETE'], pattern: /^\/api\/decks\/[^/]+\/services$/, policy: 'requireAgentOrDashboard' },
113
+ { methods: ['PUT'], pattern: /^\/api\/decks\/[^/]+\/services\/reorder$/, policy: 'requireAgentOrDashboard' },
114
+ { methods: ['DELETE'], pattern: /^\/api\/decks\/[^/]+\/services\/clear$/, policy: 'requireAgentOrDashboard' },
115
+ { methods: ['POST'], pattern: /^\/api\/decks\/[^/]+\/credentials$/, policy: 'requireAgentOrDashboard' },
116
+ { methods: ['DELETE'], pattern: /^\/api\/decks\/[^/]+\/credentials$/, policy: 'requireAgentOrDashboard' },
117
+ { methods: ['POST'], pattern: /^\/api\/decks\/[^/]+\/playbooks$/, policy: 'requireAgentOrDashboard' },
118
+ { methods: ['DELETE'], pattern: /^\/api\/decks\/[^/]+\/playbooks$/, policy: 'requireAgentOrDashboard' },
119
+ { methods: ['GET'], pattern: /^\/api\/services$/, policy: 'requireAgentOrDashboard' },
120
+ { methods: ['GET'], pattern: /^\/api\/services\/[^/]+$/, policy: 'requireAgentOrDashboard' },
121
+ { methods: ['GET'], pattern: /^\/api\/services\/[^/]+\/tools$/, policy: 'requireAgentOrDashboard' },
122
+ { methods: ['POST'], pattern: /^\/api\/services\/[^/]+\/call$/, policy: 'requireAgentOrDashboard' },
123
+ { methods: ['GET'], pattern: /^\/api\/credentials$/, policy: 'requireAgentOrDashboard' },
124
+ { methods: ['GET'], pattern: /^\/api\/credentials\/[^/]+$/, policy: 'requireAgentOrDashboard' },
125
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/summaries$/, policy: 'requireAgentOrDashboard' },
126
+ { methods: ['GET'], pattern: /^\/api\/playbooks$/, policy: 'requireAgentOrDashboard' },
127
+ { methods: ['GET'], pattern: /^\/api\/playbooks\/[^/]+$/, policy: 'requireAgentOrDashboard' },
128
+ { methods: ['POST'], pattern: /^\/api\/playbook-patches$/, policy: 'requireAgentOrDashboard' },
129
+ ];
130
+ function shouldApplyHttpPolicy(pathname) {
131
+ if (pathname.startsWith('/api/ws/')) {
132
+ return false;
133
+ }
134
+ return pathname === '/health' || pathname === '/' || pathname.startsWith('/api/');
135
+ }
136
+ function resolveRoutePolicy(method, pathname) {
137
+ const upper = method.toUpperCase();
138
+ for (const rule of exports.HTTP_ROUTE_POLICIES) {
139
+ if (rule.methods.includes(upper) && rule.pattern.test(pathname)) {
140
+ return rule.policy;
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ function findUnmatchedRoutes(routes) {
146
+ const unmatched = [];
147
+ for (const route of routes) {
148
+ const methods = route.method.split(',').map((m) => m.trim().toUpperCase());
149
+ if (methods.every((method) => method === 'HEAD')) {
150
+ continue;
151
+ }
152
+ const pathname = route.url.split('?')[0];
153
+ if (!shouldApplyHttpPolicy(pathname)) {
154
+ continue;
155
+ }
156
+ const matched = methods
157
+ .filter((method) => method !== 'HEAD')
158
+ .some((method) => resolveRoutePolicy(method, pathname) !== null);
159
+ if (!matched) {
160
+ unmatched.push(route);
161
+ }
162
+ }
163
+ return unmatched;
164
+ }
165
+ //# sourceMappingURL=route-policy-registry.js.map