@agent-deck/backend 1.10.0 → 1.10.2
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/.tsbuildinfo +1 -1
- package/dist/cli-runtime.d.ts +9 -0
- package/dist/cli-runtime.d.ts.map +1 -1
- package/dist/cli-runtime.js +30 -0
- package/dist/cli-runtime.js.map +1 -1
- package/dist/execution-authority/bearer.d.ts +15 -0
- package/dist/execution-authority/bearer.d.ts.map +1 -0
- package/dist/execution-authority/bearer.js +35 -0
- package/dist/execution-authority/bearer.js.map +1 -0
- package/dist/execution-authority/index.d.ts +6 -0
- package/dist/execution-authority/index.d.ts.map +1 -0
- package/dist/execution-authority/index.js +8 -0
- package/dist/execution-authority/index.js.map +1 -0
- package/dist/execution-authority/ledger.d.ts +85 -0
- package/dist/execution-authority/ledger.d.ts.map +1 -0
- package/dist/execution-authority/ledger.js +507 -0
- package/dist/execution-authority/ledger.js.map +1 -0
- package/dist/execution-authority/store.d.ts +55 -0
- package/dist/execution-authority/store.d.ts.map +1 -0
- package/dist/execution-authority/store.js +256 -0
- package/dist/execution-authority/store.js.map +1 -0
- package/dist/execution-authority/types.d.ts +119 -0
- package/dist/execution-authority/types.d.ts.map +1 -0
- package/dist/execution-authority/types.js +9 -0
- package/dist/execution-authority/types.js.map +1 -0
- package/dist/index.js +20 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/execution-authority-http.d.ts +23 -0
- package/dist/lib/execution-authority-http.d.ts.map +1 -0
- package/dist/lib/execution-authority-http.js +101 -0
- package/dist/lib/execution-authority-http.js.map +1 -0
- package/dist/lib/fatal.d.ts +28 -0
- package/dist/lib/fatal.d.ts.map +1 -0
- package/dist/lib/fatal.js +122 -0
- package/dist/lib/fatal.js.map +1 -0
- package/dist/mcp-index.js +21 -15
- package/dist/mcp-index.js.map +1 -1
- package/dist/mcp-server.d.ts +4 -3
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +30 -13
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-session-binding.d.ts +4 -0
- package/dist/mcp-session-binding.d.ts.map +1 -1
- package/dist/mcp-session-binding.js +14 -0
- package/dist/mcp-session-binding.js.map +1 -1
- package/dist/mcp-stdio.d.ts +2 -0
- package/dist/mcp-stdio.d.ts.map +1 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/mcp-tools/register.d.ts +3 -0
- package/dist/mcp-tools/register.d.ts.map +1 -1
- package/dist/mcp-tools/register.js +20 -0
- package/dist/mcp-tools/register.js.map +1 -1
- package/dist/mcp-unassigned.d.ts +14 -0
- package/dist/mcp-unassigned.d.ts.map +1 -0
- package/dist/mcp-unassigned.js +23 -0
- package/dist/mcp-unassigned.js.map +1 -0
- package/dist/models/database.d.ts +1 -0
- package/dist/models/database.d.ts.map +1 -1
- package/dist/models/database.js +2 -1
- package/dist/models/database.js.map +1 -1
- package/dist/playbooks/playbook-parser.d.ts +8 -0
- package/dist/playbooks/playbook-parser.d.ts.map +1 -0
- package/dist/playbooks/playbook-parser.js +76 -0
- package/dist/playbooks/playbook-parser.js.map +1 -0
- package/dist/playbooks/playbook-service.d.ts +9 -0
- package/dist/playbooks/playbook-service.d.ts.map +1 -0
- package/dist/playbooks/playbook-service.js +107 -0
- package/dist/playbooks/playbook-service.js.map +1 -0
- package/dist/routes/execution-authority.d.ts +8 -0
- package/dist/routes/execution-authority.d.ts.map +1 -0
- package/dist/routes/execution-authority.js +498 -0
- package/dist/routes/execution-authority.js.map +1 -0
- package/dist/scope/bindings-sidecar.d.ts +9 -0
- package/dist/scope/bindings-sidecar.d.ts.map +1 -0
- package/dist/scope/bindings-sidecar.js +57 -0
- package/dist/scope/bindings-sidecar.js.map +1 -0
- package/dist/scope/repo-deck.d.ts +10 -0
- package/dist/scope/repo-deck.d.ts.map +1 -0
- package/dist/scope/repo-deck.js +63 -0
- package/dist/scope/repo-deck.js.map +1 -0
- package/dist/store/reindex.d.ts +18 -0
- package/dist/store/reindex.d.ts.map +1 -1
- package/dist/store/reindex.js +132 -19
- package/dist/store/reindex.js.map +1 -1
- package/dist/store/startup.d.ts.map +1 -1
- package/dist/store/startup.js +6 -1
- package/dist/store/startup.js.map +1 -1
- package/dist/test-local-mcp-e2e.d.ts +3 -0
- package/dist/test-local-mcp-e2e.d.ts.map +1 -0
- package/dist/test-local-mcp-e2e.js +104 -0
- package/dist/test-local-mcp-e2e.js.map +1 -0
- package/package.json +2 -2
- package/static-ui/favicon.png +0 -0
- package/dist/config/mcp-endpoint.d.ts +0 -6
- package/dist/config/mcp-endpoint.d.ts.map +0 -1
- package/dist/config/mcp-endpoint.js +0 -19
- package/dist/config/mcp-endpoint.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbook-parser.js","sourceRoot":"","sources":["../../src/playbooks/playbook-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Playbook, PlaybookSummary } from '@agent-deck/shared';
|
|
2
|
+
export declare function getGlobalPlaybooksDir(): string;
|
|
3
|
+
export declare function discoverPlaybookPaths(workspaceRoot?: string): Promise<Array<{
|
|
4
|
+
filePath: string;
|
|
5
|
+
source: PlaybookSummary['source'];
|
|
6
|
+
}>>;
|
|
7
|
+
export declare function listPlaybooks(workspaceRoot?: string): Promise<PlaybookSummary[]>;
|
|
8
|
+
export declare function getPlaybook(playbookId: string, workspaceRoot?: string): Promise<Playbook | null>;
|
|
9
|
+
//# sourceMappingURL=playbook-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbook-service.d.ts","sourceRoot":"","sources":["../../src/playbooks/playbook-service.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,eAAe,EAEhB,MAAM,oBAAoB,CAAC;AAK5B,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AA8CD,wBAAsB,qBAAqB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;CACnC,CAAC,CAAC,CA4BF;AAED,wBAAsB,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiBtF;AAED,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAW1B"}
|
|
@@ -0,0 +1,107 @@
|
|
|
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.getGlobalPlaybooksDir = getGlobalPlaybooksDir;
|
|
7
|
+
exports.discoverPlaybookPaths = discoverPlaybookPaths;
|
|
8
|
+
exports.listPlaybooks = listPlaybooks;
|
|
9
|
+
exports.getPlaybook = getPlaybook;
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const shared_1 = require("@agent-deck/shared");
|
|
13
|
+
const yaml_sync_1 = require("../vault/yaml-sync");
|
|
14
|
+
const repo_deck_1 = require("../scope/repo-deck");
|
|
15
|
+
const playbook_parser_1 = require("./playbook-parser");
|
|
16
|
+
function getGlobalPlaybooksDir() {
|
|
17
|
+
return node_path_1.default.join((0, yaml_sync_1.getAgentDeckHome)(), 'playbooks');
|
|
18
|
+
}
|
|
19
|
+
async function pathExists(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
await promises_1.default.access(filePath);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function listMarkdownFiles(dir) {
|
|
29
|
+
if (!(await pathExists(dir))) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
33
|
+
return entries
|
|
34
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
35
|
+
.map((entry) => node_path_1.default.join(dir, entry.name));
|
|
36
|
+
}
|
|
37
|
+
function resolvePlaybookPath(workspaceRoot, playbookPath) {
|
|
38
|
+
if (node_path_1.default.isAbsolute(playbookPath)) {
|
|
39
|
+
return playbookPath;
|
|
40
|
+
}
|
|
41
|
+
return node_path_1.default.join(node_path_1.default.resolve(workspaceRoot), playbookPath);
|
|
42
|
+
}
|
|
43
|
+
async function loadPlaybookFromFile(filePath, source) {
|
|
44
|
+
try {
|
|
45
|
+
const content = await promises_1.default.readFile(filePath, 'utf8');
|
|
46
|
+
const playbook = (0, playbook_parser_1.parsePlaybookFile)(content, filePath);
|
|
47
|
+
return { ...playbook, source };
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof playbook_parser_1.PlaybookParseError) {
|
|
51
|
+
console.warn(error.message);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function discoverPlaybookPaths(workspaceRoot) {
|
|
58
|
+
const discovered = new Map();
|
|
59
|
+
const addPath = (filePath, source) => {
|
|
60
|
+
const resolved = node_path_1.default.resolve(filePath);
|
|
61
|
+
if (!discovered.has(resolved)) {
|
|
62
|
+
discovered.set(resolved, { filePath: resolved, source });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
if (workspaceRoot) {
|
|
66
|
+
const manifest = await (0, repo_deck_1.loadRepoDeckManifest)(workspaceRoot);
|
|
67
|
+
for (const playbookPath of manifest?.playbooks ?? []) {
|
|
68
|
+
addPath(resolvePlaybookPath(workspaceRoot, playbookPath), 'repo');
|
|
69
|
+
}
|
|
70
|
+
const repoDir = node_path_1.default.join(node_path_1.default.resolve(workspaceRoot), shared_1.REPO_PLAYBOOKS_DIR);
|
|
71
|
+
for (const filePath of await listMarkdownFiles(repoDir)) {
|
|
72
|
+
addPath(filePath, 'repo');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const globalDir = getGlobalPlaybooksDir();
|
|
76
|
+
for (const filePath of await listMarkdownFiles(globalDir)) {
|
|
77
|
+
addPath(filePath, 'global');
|
|
78
|
+
}
|
|
79
|
+
return [...discovered.values()];
|
|
80
|
+
}
|
|
81
|
+
async function listPlaybooks(workspaceRoot) {
|
|
82
|
+
const paths = await discoverPlaybookPaths(workspaceRoot);
|
|
83
|
+
const summaries = [];
|
|
84
|
+
for (const { filePath, source } of paths) {
|
|
85
|
+
const playbook = await loadPlaybookFromFile(filePath, source);
|
|
86
|
+
if (playbook) {
|
|
87
|
+
summaries.push({
|
|
88
|
+
id: playbook.id,
|
|
89
|
+
path: playbook.path,
|
|
90
|
+
triggers: playbook.triggers,
|
|
91
|
+
source: playbook.source,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return summaries.sort((a, b) => a.id.localeCompare(b.id));
|
|
96
|
+
}
|
|
97
|
+
async function getPlaybook(playbookId, workspaceRoot) {
|
|
98
|
+
const paths = await discoverPlaybookPaths(workspaceRoot);
|
|
99
|
+
for (const { filePath, source } of paths) {
|
|
100
|
+
const playbook = await loadPlaybookFromFile(filePath, source);
|
|
101
|
+
if (playbook?.id === playbookId) {
|
|
102
|
+
return playbook;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=playbook-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbook-service.js","sourceRoot":"","sources":["../../src/playbooks/playbook-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
import type { ContractError } from '../execution-authority/types';
|
|
3
|
+
export declare class EnrollmentAuthError extends Error {
|
|
4
|
+
readonly contract: ContractError;
|
|
5
|
+
constructor(contract: ContractError);
|
|
6
|
+
}
|
|
7
|
+
export declare const registerExecutionAuthorityRoutes: FastifyPluginAsync;
|
|
8
|
+
//# sourceMappingURL=execution-authority.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-authority.d.ts","sourceRoot":"","sources":["../../src/routes/execution-authority.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAMlD,OAAO,KAAK,EAAe,aAAa,EAAwB,MAAM,8BAA8B,CAAC;AAGrG,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,QAAQ,EAAE,aAAa;gBAAvB,QAAQ,EAAE,aAAa;CAIpD;AAyKD,eAAO,MAAM,gCAAgC,EAAE,kBA2Z9C,CAAC"}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerExecutionAuthorityRoutes = exports.EnrollmentAuthError = void 0;
|
|
4
|
+
const http_auth_1 = require("../lib/http-auth");
|
|
5
|
+
const invalid_deck_id_path_segment_1 = require("../lib/invalid-deck-id-path-segment");
|
|
6
|
+
const auth_1 = require("../trusted-session/auth");
|
|
7
|
+
const bearer_1 = require("../execution-authority/bearer");
|
|
8
|
+
const execution_authority_http_1 = require("../lib/execution-authority-http");
|
|
9
|
+
class EnrollmentAuthError extends Error {
|
|
10
|
+
contract;
|
|
11
|
+
constructor(contract) {
|
|
12
|
+
super(contract.message);
|
|
13
|
+
this.contract = contract;
|
|
14
|
+
this.name = 'EnrollmentAuthError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.EnrollmentAuthError = EnrollmentAuthError;
|
|
18
|
+
function intersectTools(policy, hint) {
|
|
19
|
+
if (hint === undefined)
|
|
20
|
+
return policy;
|
|
21
|
+
const key = (t) => `${t.serviceId}\0${t.toolName}`;
|
|
22
|
+
const allowed = new Set(policy.map(key));
|
|
23
|
+
return hint.filter((t) => allowed.has(key(t)));
|
|
24
|
+
}
|
|
25
|
+
function requireEnrollmentAuth(request, store) {
|
|
26
|
+
const token = (0, http_auth_1.parseBearerToken)({ headers: request.headers });
|
|
27
|
+
if (!token) {
|
|
28
|
+
throw new EnrollmentAuthError({
|
|
29
|
+
ok: false,
|
|
30
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
31
|
+
message: 'Enrollment bearer required (enr_…:secret)',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const parsed = (0, bearer_1.parseEnrollmentBearer)(token);
|
|
35
|
+
if (!parsed) {
|
|
36
|
+
throw new EnrollmentAuthError({
|
|
37
|
+
ok: false,
|
|
38
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
39
|
+
message: 'Enrollment bearer required (enr_…:secret)',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const enrollment = store.getEnrollment(parsed.enrollmentId);
|
|
43
|
+
if (!enrollment) {
|
|
44
|
+
throw new EnrollmentAuthError({
|
|
45
|
+
ok: false,
|
|
46
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
47
|
+
message: 'Unknown enrollment',
|
|
48
|
+
correlation: { enrollmentId: parsed.enrollmentId },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (enrollment.status !== 'active') {
|
|
52
|
+
throw new EnrollmentAuthError({
|
|
53
|
+
ok: false,
|
|
54
|
+
error_code: 'ENROLLMENT_REVOKED',
|
|
55
|
+
message: 'Enrollment is revoked',
|
|
56
|
+
reason: 'enrollment_revoked',
|
|
57
|
+
correlation: { enrollmentId: parsed.enrollmentId },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!store.verifyEnrollmentSecret(parsed.enrollmentId, parsed.secret)) {
|
|
61
|
+
throw new EnrollmentAuthError({
|
|
62
|
+
ok: false,
|
|
63
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
64
|
+
message: 'Invalid enrollment secret',
|
|
65
|
+
correlation: { enrollmentId: parsed.enrollmentId },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return { enrollmentId: parsed.enrollmentId };
|
|
69
|
+
}
|
|
70
|
+
/** Shared enrollment bearer resolve used by coordinator metadata + mint routes. */
|
|
71
|
+
function resolveEnrollmentAuth(request, store) {
|
|
72
|
+
try {
|
|
73
|
+
const { enrollmentId } = requireEnrollmentAuth(request, store);
|
|
74
|
+
const enrollment = store.getEnrollment(enrollmentId);
|
|
75
|
+
if (!enrollment) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
contract: {
|
|
79
|
+
ok: false,
|
|
80
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
81
|
+
message: 'Unknown enrollment',
|
|
82
|
+
correlation: { enrollmentId },
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { ok: true, enrollmentId, enrollment };
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof EnrollmentAuthError) {
|
|
90
|
+
return { ok: false, contract: error.contract };
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function deckNotPermittedError(enrollmentId, deckId) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
error_code: 'RESOURCE_OUT_OF_SCOPE',
|
|
99
|
+
message: 'Deck is not in allowedDeckIds',
|
|
100
|
+
reason: 'deck_not_permitted',
|
|
101
|
+
correlation: { enrollmentId, deckId },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function deckNotFoundError(enrollmentId, deckId) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
error_code: 'RESOURCE_OUT_OF_SCOPE',
|
|
108
|
+
message: 'Deck not found',
|
|
109
|
+
reason: 'deck_not_found',
|
|
110
|
+
correlation: { enrollmentId, deckId },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Re-check enrollment after awaits so revoke cannot race a successful response. */
|
|
114
|
+
function revalidateActiveEnrollment(store, enrollmentId, deckId) {
|
|
115
|
+
const enrollment = store.getEnrollment(enrollmentId);
|
|
116
|
+
if (!enrollment || enrollment.status !== 'active') {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
error_code: 'ENROLLMENT_REVOKED',
|
|
120
|
+
message: 'Enrollment is revoked',
|
|
121
|
+
reason: 'enrollment_revoked',
|
|
122
|
+
correlation: { enrollmentId },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (deckId !== undefined && !enrollment.allowedDeckIds.includes(deckId)) {
|
|
126
|
+
return deckNotPermittedError(enrollmentId, deckId);
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
async function requireEnrollmentOrTrustedWriter(request, store) {
|
|
131
|
+
try {
|
|
132
|
+
return { kind: 'enrollment', ...requireEnrollmentAuth(request, store) };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (!(error instanceof EnrollmentAuthError))
|
|
136
|
+
throw error;
|
|
137
|
+
try {
|
|
138
|
+
await (0, auth_1.requireTrustedWriterBearer)(request);
|
|
139
|
+
return { kind: 'trusted-writer' };
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const registerExecutionAuthorityRoutes = async (fastify) => {
|
|
147
|
+
const store = () => fastify.executionAuthorityStore;
|
|
148
|
+
fastify.post('/enrollments', async (request, reply) => {
|
|
149
|
+
try {
|
|
150
|
+
await (0, auth_1.requireTrustedWriterBearer)(request);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (error instanceof auth_1.TrustedAuthError) {
|
|
154
|
+
return (0, auth_1.sendTrustedAuthError)(reply, error);
|
|
155
|
+
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
const body = (request.body ?? {});
|
|
159
|
+
if (!body.coordinatorId?.trim() || !Array.isArray(body.allowedDeckIds) || body.allowedDeckIds.length === 0) {
|
|
160
|
+
return reply.status(400).send({
|
|
161
|
+
ok: false,
|
|
162
|
+
error_code: 'INVALID_MINT_REQUEST',
|
|
163
|
+
message: 'coordinatorId and allowedDeckIds are required',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const result = store().enrollCoordinator({
|
|
167
|
+
coordinatorId: body.coordinatorId.trim(),
|
|
168
|
+
allowedDeckIds: body.allowedDeckIds.map(String),
|
|
169
|
+
});
|
|
170
|
+
if (!result.ok) {
|
|
171
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
172
|
+
}
|
|
173
|
+
return { ok: true, data: result.data };
|
|
174
|
+
});
|
|
175
|
+
fastify.get('/enrollments/:id', async (request, reply) => {
|
|
176
|
+
try {
|
|
177
|
+
await (0, auth_1.requireTrustedWriterBearer)(request);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
if (error instanceof auth_1.TrustedAuthError) {
|
|
181
|
+
return (0, auth_1.sendTrustedAuthError)(reply, error);
|
|
182
|
+
}
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
const enrollment = store().getEnrollment(request.params.id);
|
|
186
|
+
if (!enrollment) {
|
|
187
|
+
return reply.status(404).send({
|
|
188
|
+
ok: false,
|
|
189
|
+
error_code: 'COORDINATOR_NOT_ENROLLED',
|
|
190
|
+
message: 'Unknown enrollment',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return { ok: true, data: enrollment };
|
|
194
|
+
});
|
|
195
|
+
fastify.post('/enrollments/:id/revoke', async (request, reply) => {
|
|
196
|
+
try {
|
|
197
|
+
await (0, auth_1.requireTrustedWriterBearer)(request);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (error instanceof auth_1.TrustedAuthError) {
|
|
201
|
+
return (0, auth_1.sendTrustedAuthError)(reply, error);
|
|
202
|
+
}
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
const result = store().revokeEnrollment(request.params.id);
|
|
206
|
+
if (!result.ok) {
|
|
207
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
208
|
+
}
|
|
209
|
+
return { ok: true, data: result.data };
|
|
210
|
+
});
|
|
211
|
+
fastify.get('/decks', async (request, reply) => {
|
|
212
|
+
const auth = resolveEnrollmentAuth(request, store());
|
|
213
|
+
if (!auth.ok) {
|
|
214
|
+
return (0, execution_authority_http_1.sendContractError)(reply, auth.contract, (0, execution_authority_http_1.statusForContract)(auth.contract));
|
|
215
|
+
}
|
|
216
|
+
const decks = [];
|
|
217
|
+
for (const id of auth.enrollment.allowedDeckIds) {
|
|
218
|
+
const deck = await fastify.db.getDeck(id);
|
|
219
|
+
if (deck) {
|
|
220
|
+
decks.push({ id: deck.id, name: deck.name });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const stale = revalidateActiveEnrollment(store(), auth.enrollmentId);
|
|
224
|
+
if (stale) {
|
|
225
|
+
return (0, execution_authority_http_1.sendContractError)(reply, stale, (0, execution_authority_http_1.statusForContract)(stale));
|
|
226
|
+
}
|
|
227
|
+
return { ok: true, data: { decks } };
|
|
228
|
+
});
|
|
229
|
+
fastify.get('/decks/:deckId/playbooks', async (request, reply) => {
|
|
230
|
+
const auth = resolveEnrollmentAuth(request, store());
|
|
231
|
+
if (!auth.ok) {
|
|
232
|
+
return (0, execution_authority_http_1.sendContractError)(reply, auth.contract, (0, execution_authority_http_1.statusForContract)(auth.contract));
|
|
233
|
+
}
|
|
234
|
+
const { deckId } = request.params;
|
|
235
|
+
if ((0, invalid_deck_id_path_segment_1.invalidDeckIdPathSegment)(deckId)) {
|
|
236
|
+
const badSegment = {
|
|
237
|
+
ok: false,
|
|
238
|
+
error_code: 'INVALID_MINT_REQUEST',
|
|
239
|
+
message: 'deckId must be a single path segment',
|
|
240
|
+
correlation: { deckId },
|
|
241
|
+
};
|
|
242
|
+
return (0, execution_authority_http_1.sendContractError)(reply, badSegment, (0, execution_authority_http_1.statusForContract)(badSegment));
|
|
243
|
+
}
|
|
244
|
+
if (!auth.enrollment.allowedDeckIds.includes(deckId)) {
|
|
245
|
+
return (0, execution_authority_http_1.sendContractError)(reply, deckNotPermittedError(auth.enrollmentId, deckId));
|
|
246
|
+
}
|
|
247
|
+
// Fail closed for deleted-but-still-allowed decks (distinct from not-in-allowedDeckIds).
|
|
248
|
+
// Sibling GET /decks omits missing ids from the list; path-param discovery must not.
|
|
249
|
+
if (!(await fastify.db.hasDeck(deckId))) {
|
|
250
|
+
return (0, execution_authority_http_1.sendContractError)(reply, deckNotFoundError(auth.enrollmentId, deckId));
|
|
251
|
+
}
|
|
252
|
+
const playbooks = await fastify.playbookManager.listSummariesForDeck(deckId);
|
|
253
|
+
const stale = revalidateActiveEnrollment(store(), auth.enrollmentId, deckId);
|
|
254
|
+
if (stale) {
|
|
255
|
+
return (0, execution_authority_http_1.sendContractError)(reply, stale, (0, execution_authority_http_1.statusForContract)(stale));
|
|
256
|
+
}
|
|
257
|
+
return { ok: true, data: playbooks };
|
|
258
|
+
});
|
|
259
|
+
fastify.post('/authorities', async (request, reply) => {
|
|
260
|
+
const auth = resolveEnrollmentAuth(request, store());
|
|
261
|
+
if (!auth.ok) {
|
|
262
|
+
return (0, execution_authority_http_1.sendContractError)(reply, auth.contract, (0, execution_authority_http_1.statusForContract)(auth.contract));
|
|
263
|
+
}
|
|
264
|
+
const { enrollmentId } = auth;
|
|
265
|
+
const body = (request.body ?? {});
|
|
266
|
+
if (body.enrollmentId && body.enrollmentId !== enrollmentId) {
|
|
267
|
+
return reply.status(400).send({
|
|
268
|
+
ok: false,
|
|
269
|
+
error_code: 'INVALID_MINT_REQUEST',
|
|
270
|
+
message: 'enrollmentId does not match bearer',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (!body.runId?.trim() ||
|
|
274
|
+
!body.attemptId?.trim() ||
|
|
275
|
+
!body.deckId?.trim() ||
|
|
276
|
+
!body.idempotencyKey?.trim() ||
|
|
277
|
+
body.audience !== 'dealer-worker' ||
|
|
278
|
+
!(body.ttlMs > 0)) {
|
|
279
|
+
return reply.status(400).send({
|
|
280
|
+
ok: false,
|
|
281
|
+
error_code: 'INVALID_MINT_REQUEST',
|
|
282
|
+
message: 'runId, attemptId, deckId, audience=dealer-worker, idempotencyKey, ttlMs>0 required',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const deckId = body.deckId.trim();
|
|
286
|
+
// Scope check before existence so missing out-of-scope ids stay deck_not_permitted.
|
|
287
|
+
if (!auth.enrollment.allowedDeckIds.includes(deckId)) {
|
|
288
|
+
return (0, execution_authority_http_1.sendContractError)(reply, deckNotPermittedError(enrollmentId, deckId));
|
|
289
|
+
}
|
|
290
|
+
const deck = await fastify.db.getDeck(deckId);
|
|
291
|
+
if (!deck) {
|
|
292
|
+
return (0, execution_authority_http_1.sendContractError)(reply, deckNotFoundError(enrollmentId, deckId));
|
|
293
|
+
}
|
|
294
|
+
const services = deck.services ?? [];
|
|
295
|
+
const allowedTools = [];
|
|
296
|
+
const allowedServices = [];
|
|
297
|
+
for (const service of services) {
|
|
298
|
+
allowedServices.push(service.id);
|
|
299
|
+
const discovered = await fastify.serviceManager.discoverServiceTools(service.id, {
|
|
300
|
+
forAgent: true,
|
|
301
|
+
});
|
|
302
|
+
if (Array.isArray(discovered)) {
|
|
303
|
+
for (const tool of discovered) {
|
|
304
|
+
allowedTools.push({ serviceId: service.id, toolName: tool.name });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const narrowedTools = intersectTools(allowedTools, body.toolScopeHint);
|
|
309
|
+
const narrowedServiceIds = [...new Set(narrowedTools.map((t) => t.serviceId))];
|
|
310
|
+
// undefined hint → full deck services; explicit hint (incl. []) → services from snapshot only
|
|
311
|
+
const mintServices = body.toolScopeHint === undefined ? allowedServices : narrowedServiceIds;
|
|
312
|
+
const stale = revalidateActiveEnrollment(store(), enrollmentId, deckId);
|
|
313
|
+
if (stale) {
|
|
314
|
+
return (0, execution_authority_http_1.sendContractError)(reply, stale, (0, execution_authority_http_1.statusForContract)(stale));
|
|
315
|
+
}
|
|
316
|
+
const result = store().mintAuthority({
|
|
317
|
+
enrollmentId,
|
|
318
|
+
runId: body.runId.trim(),
|
|
319
|
+
attemptId: body.attemptId.trim(),
|
|
320
|
+
deckId,
|
|
321
|
+
audience: 'dealer-worker',
|
|
322
|
+
idempotencyKey: body.idempotencyKey.trim(),
|
|
323
|
+
allowedServices: mintServices,
|
|
324
|
+
allowedTools: narrowedTools,
|
|
325
|
+
ttlMs: body.ttlMs,
|
|
326
|
+
});
|
|
327
|
+
if (!result.ok) {
|
|
328
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
329
|
+
}
|
|
330
|
+
return { ok: true, data: result.data };
|
|
331
|
+
});
|
|
332
|
+
fastify.get('/authorities/:id', async (request, reply) => {
|
|
333
|
+
let principal;
|
|
334
|
+
try {
|
|
335
|
+
principal = await requireEnrollmentOrTrustedWriter(request, store());
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
if (error instanceof EnrollmentAuthError) {
|
|
339
|
+
return (0, execution_authority_http_1.sendContractError)(reply, error.contract, (0, execution_authority_http_1.statusForContract)(error.contract));
|
|
340
|
+
}
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
const peeked = store().peekAuthority(request.params.id);
|
|
344
|
+
if (!peeked ||
|
|
345
|
+
(principal.kind === 'enrollment' && peeked.enrollmentId !== principal.enrollmentId)) {
|
|
346
|
+
return (0, execution_authority_http_1.sendContractError)(reply, {
|
|
347
|
+
ok: false,
|
|
348
|
+
error_code: 'AUTHORITY_UNKNOWN',
|
|
349
|
+
message: 'Unknown authority',
|
|
350
|
+
correlation: { authorityId: request.params.id },
|
|
351
|
+
}, 404);
|
|
352
|
+
}
|
|
353
|
+
const result = store().inspectAuthority(request.params.id);
|
|
354
|
+
if (!result.ok) {
|
|
355
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
356
|
+
}
|
|
357
|
+
return { ok: true, data: result.data };
|
|
358
|
+
});
|
|
359
|
+
fastify.post('/authorities/:id/revoke', async (request, reply) => {
|
|
360
|
+
let principal;
|
|
361
|
+
try {
|
|
362
|
+
principal = await requireEnrollmentOrTrustedWriter(request, store());
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (error instanceof EnrollmentAuthError) {
|
|
366
|
+
return (0, execution_authority_http_1.sendContractError)(reply, error.contract, (0, execution_authority_http_1.statusForContract)(error.contract));
|
|
367
|
+
}
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const peeked = store().peekAuthority(request.params.id);
|
|
371
|
+
if (!peeked ||
|
|
372
|
+
(principal.kind === 'enrollment' && peeked.enrollmentId !== principal.enrollmentId)) {
|
|
373
|
+
return (0, execution_authority_http_1.sendContractError)(reply, {
|
|
374
|
+
ok: false,
|
|
375
|
+
error_code: 'AUTHORITY_UNKNOWN',
|
|
376
|
+
message: 'Unknown authority',
|
|
377
|
+
correlation: { authorityId: request.params.id },
|
|
378
|
+
}, 404);
|
|
379
|
+
}
|
|
380
|
+
const result = store().revokeAuthority(request.params.id);
|
|
381
|
+
if (!result.ok) {
|
|
382
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
383
|
+
}
|
|
384
|
+
return { ok: true, data: result.data };
|
|
385
|
+
});
|
|
386
|
+
fastify.get('/audit', async (request, reply) => {
|
|
387
|
+
let principal;
|
|
388
|
+
try {
|
|
389
|
+
principal = await requireEnrollmentOrTrustedWriter(request, store());
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
if (error instanceof EnrollmentAuthError) {
|
|
393
|
+
return (0, execution_authority_http_1.sendContractError)(reply, error.contract, (0, execution_authority_http_1.statusForContract)(error.contract));
|
|
394
|
+
}
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
const q = request.query;
|
|
398
|
+
if (principal.kind === 'enrollment') {
|
|
399
|
+
if (q.enrollmentId && q.enrollmentId !== principal.enrollmentId) {
|
|
400
|
+
return (0, execution_authority_http_1.sendContractError)(reply, {
|
|
401
|
+
ok: false,
|
|
402
|
+
error_code: 'RESOURCE_OUT_OF_SCOPE',
|
|
403
|
+
message: 'Cannot query audit for another enrollment',
|
|
404
|
+
correlation: { enrollmentId: principal.enrollmentId },
|
|
405
|
+
}, 403);
|
|
406
|
+
}
|
|
407
|
+
if (q.authorityId) {
|
|
408
|
+
const owned = store().peekAuthority(q.authorityId);
|
|
409
|
+
if (!owned || owned.enrollmentId !== principal.enrollmentId) {
|
|
410
|
+
return (0, execution_authority_http_1.sendContractError)(reply, {
|
|
411
|
+
ok: false,
|
|
412
|
+
error_code: 'AUTHORITY_UNKNOWN',
|
|
413
|
+
message: 'Unknown authority',
|
|
414
|
+
correlation: { authorityId: q.authorityId },
|
|
415
|
+
}, 404);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const events = store().listAuditEvents({
|
|
420
|
+
runId: q.runId,
|
|
421
|
+
attemptId: q.attemptId,
|
|
422
|
+
authorityId: q.authorityId,
|
|
423
|
+
enrollmentId: principal.kind === 'enrollment' ? principal.enrollmentId : q.enrollmentId,
|
|
424
|
+
requestId: q.requestId,
|
|
425
|
+
});
|
|
426
|
+
return { ok: true, data: { events } };
|
|
427
|
+
});
|
|
428
|
+
fastify.post('/mcp/connect', async (request, reply) => {
|
|
429
|
+
const body = (request.body ?? {});
|
|
430
|
+
const token = (0, http_auth_1.parseBearerToken)(request);
|
|
431
|
+
const fromBearer = token ? (0, bearer_1.parseAuthorityBearer)(token) : null;
|
|
432
|
+
const authorityId = fromBearer?.authorityId ?? body.authorityId?.trim();
|
|
433
|
+
const authoritySecret = fromBearer?.secret ?? body.authoritySecret?.trim();
|
|
434
|
+
const audience = body.audience?.trim() || 'dealer-worker';
|
|
435
|
+
if (!authorityId || !authoritySecret) {
|
|
436
|
+
return reply.status(401).send({
|
|
437
|
+
ok: false,
|
|
438
|
+
error_code: 'AUTHORITY_SECRET_INVALID',
|
|
439
|
+
message: 'authorityId and authoritySecret required',
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const auth = store().authenticateAuthority(authorityId, authoritySecret);
|
|
443
|
+
if (!auth.ok) {
|
|
444
|
+
return (0, execution_authority_http_1.sendContractError)(reply, auth, (0, execution_authority_http_1.statusForContract)(auth));
|
|
445
|
+
}
|
|
446
|
+
if (audience !== auth.data.audience) {
|
|
447
|
+
return reply.status(403).send({
|
|
448
|
+
ok: false,
|
|
449
|
+
error_code: 'AUDIENCE_MISMATCH',
|
|
450
|
+
message: 'Caller audience does not match authority audience',
|
|
451
|
+
correlation: { authorityId },
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
ok: true,
|
|
456
|
+
data: {
|
|
457
|
+
authorityId: auth.data.authorityId,
|
|
458
|
+
deckId: auth.data.deckId,
|
|
459
|
+
audience: auth.data.audience,
|
|
460
|
+
runId: auth.data.runId,
|
|
461
|
+
attemptId: auth.data.attemptId,
|
|
462
|
+
allowedServices: auth.data.allowedServices,
|
|
463
|
+
allowedTools: auth.data.allowedTools,
|
|
464
|
+
expiresAt: auth.data.expiresAt,
|
|
465
|
+
status: auth.data.status,
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
/** Authorize one tool call under execution authority (MCP / internal). */
|
|
470
|
+
fastify.post('/authorize-call', async (request, reply) => {
|
|
471
|
+
const body = (request.body ?? {});
|
|
472
|
+
if (!body.authorityId ||
|
|
473
|
+
!body.authoritySecret ||
|
|
474
|
+
!body.audience ||
|
|
475
|
+
!body.serviceId ||
|
|
476
|
+
!body.toolName) {
|
|
477
|
+
return reply.status(400).send({
|
|
478
|
+
ok: false,
|
|
479
|
+
error_code: 'INVALID_MINT_REQUEST',
|
|
480
|
+
message: 'authorityId, authoritySecret, audience, serviceId, toolName required',
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const result = store().invokeAuthorizedCall({
|
|
484
|
+
authorityId: body.authorityId,
|
|
485
|
+
authoritySecret: body.authoritySecret,
|
|
486
|
+
audience: body.audience,
|
|
487
|
+
serviceId: body.serviceId,
|
|
488
|
+
toolName: body.toolName,
|
|
489
|
+
requiresInteraction: body.requiresInteraction,
|
|
490
|
+
});
|
|
491
|
+
if (!result.ok) {
|
|
492
|
+
return (0, execution_authority_http_1.sendContractError)(reply, result, (0, execution_authority_http_1.statusForContract)(result));
|
|
493
|
+
}
|
|
494
|
+
return { ok: true, data: result.data };
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
exports.registerExecutionAuthorityRoutes = registerExecutionAuthorityRoutes;
|
|
498
|
+
//# sourceMappingURL=execution-authority.js.map
|