@alneas/eaos-server 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.ja.md +49 -0
- package/README.md +46 -2
- package/dist/agentic/agent-jobs.d.ts +333 -0
- package/dist/agentic/agent-jobs.js +15 -0
- package/dist/agentic/agent-jobs.js.map +1 -0
- package/dist/agentic/agent-node.d.ts +40 -0
- package/dist/agentic/agent-node.js +7 -0
- package/dist/agentic/agent-node.js.map +1 -0
- package/dist/agentic/agent-runner.d.ts +33 -0
- package/dist/agentic/agent-runner.js +7 -0
- package/dist/agentic/agent-runner.js.map +1 -0
- package/dist/agentic/ai-broker.d.ts +169 -0
- package/dist/agentic/ai-broker.js +8 -0
- package/dist/agentic/ai-broker.js.map +1 -0
- package/dist/agentic/atomic-checkpointer.d.ts +137 -0
- package/dist/agentic/atomic-checkpointer.js +9 -0
- package/dist/agentic/atomic-checkpointer.js.map +1 -0
- package/dist/agentic/autonomous-worker.d.ts +122 -0
- package/dist/agentic/autonomous-worker.js +10 -0
- package/dist/agentic/autonomous-worker.js.map +1 -0
- package/dist/agentic/event-bridge.d.ts +350 -0
- package/dist/agentic/event-bridge.js +9 -0
- package/dist/agentic/event-bridge.js.map +1 -0
- package/dist/agentic/executor.d.ts +91 -0
- package/dist/agentic/executor.js +12 -0
- package/dist/agentic/executor.js.map +1 -0
- package/dist/agentic/graph-projection.d.ts +119 -0
- package/dist/agentic/graph-projection.js +7 -0
- package/dist/agentic/graph-projection.js.map +1 -0
- package/dist/agentic/graph.d.ts +524 -0
- package/dist/agentic/graph.js +7 -0
- package/dist/agentic/graph.js.map +1 -0
- package/dist/agentic/index.d.ts +28 -0
- package/dist/agentic/index.js +87 -0
- package/dist/agentic/index.js.map +1 -0
- package/dist/agentic/local-runner.d.ts +106 -0
- package/dist/agentic/local-runner.js +7 -0
- package/dist/agentic/local-runner.js.map +1 -0
- package/dist/agentic/remote-executor.d.ts +67 -0
- package/dist/agentic/remote-executor.js +8 -0
- package/dist/agentic/remote-executor.js.map +1 -0
- package/dist/agentic/types.d.ts +157 -0
- package/dist/agentic/types.js +7 -0
- package/dist/agentic/types.js.map +1 -0
- package/dist/agentic/workflow-catalog.d.ts +152 -0
- package/dist/agentic/workflow-catalog.js +7 -0
- package/dist/agentic/workflow-catalog.js.map +1 -0
- package/dist/agentic/workflow-compiler.d.ts +44 -0
- package/dist/agentic/workflow-compiler.js +8 -0
- package/dist/agentic/workflow-compiler.js.map +1 -0
- package/dist/agentic/workflow-coordinator.d.ts +170 -0
- package/dist/agentic/workflow-coordinator.js +8 -0
- package/dist/agentic/workflow-coordinator.js.map +1 -0
- package/dist/agentic/workflow-manager.d.ts +91 -0
- package/dist/agentic/workflow-manager.js +7 -0
- package/dist/agentic/workflow-manager.js.map +1 -0
- package/dist/agentic/workflow-registry.d.ts +334 -0
- package/dist/agentic/workflow-registry.js +7 -0
- package/dist/agentic/workflow-registry.js.map +1 -0
- package/dist/artifacts-CteHmCMI.d.ts +614 -0
- package/dist/auth-hooks/api-key.d.ts +75 -0
- package/dist/auth-hooks/api-key.js +7 -0
- package/dist/auth-hooks/api-key.js.map +1 -0
- package/dist/auth-hooks/index.d.ts +80 -0
- package/dist/auth-hooks/index.js +28 -0
- package/dist/auth-hooks/index.js.map +1 -0
- package/dist/browser-auth/account-memberships.d.ts +213 -0
- package/dist/browser-auth/account-memberships.js +13 -0
- package/dist/browser-auth/account-memberships.js.map +1 -0
- package/dist/browser-auth/index.d.ts +8 -0
- package/dist/browser-auth/index.js +59 -0
- package/dist/browser-auth/index.js.map +1 -0
- package/dist/browser-auth/kratos-session.d.ts +37 -0
- package/dist/browser-auth/kratos-session.js +7 -0
- package/dist/browser-auth/kratos-session.js.map +1 -0
- package/dist/browser-auth/platform-session.d.ts +120 -0
- package/dist/browser-auth/platform-session.js +7 -0
- package/dist/browser-auth/platform-session.js.map +1 -0
- package/dist/browser-auth/public-memberships.d.ts +22 -0
- package/dist/browser-auth/public-memberships.js +7 -0
- package/dist/browser-auth/public-memberships.js.map +1 -0
- package/dist/cache/index.d.ts +181 -0
- package/dist/cache/index.js +197 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/chunk-32746UQZ.js +131 -0
- package/dist/chunk-32746UQZ.js.map +1 -0
- package/dist/chunk-3NLSWP2Y.js +223 -0
- package/dist/chunk-3NLSWP2Y.js.map +1 -0
- package/dist/chunk-3P4XTTQH.js +346 -0
- package/dist/chunk-3P4XTTQH.js.map +1 -0
- package/dist/chunk-3VITSHN3.js +15 -0
- package/dist/chunk-3VITSHN3.js.map +1 -0
- package/dist/chunk-46UWPEW2.js +226 -0
- package/dist/chunk-46UWPEW2.js.map +1 -0
- package/dist/chunk-4BARRLJR.js +180 -0
- package/dist/chunk-4BARRLJR.js.map +1 -0
- package/dist/chunk-4VORUFKR.js +45 -0
- package/dist/chunk-4VORUFKR.js.map +1 -0
- package/dist/chunk-55D4ERWO.js +1 -0
- package/dist/chunk-55D4ERWO.js.map +1 -0
- package/dist/chunk-5TBBZIND.js +18 -0
- package/dist/chunk-5TBBZIND.js.map +1 -0
- package/dist/chunk-5WOZWPZA.js +211 -0
- package/dist/chunk-5WOZWPZA.js.map +1 -0
- package/dist/chunk-6CBVZ6CT.js +168 -0
- package/dist/chunk-6CBVZ6CT.js.map +1 -0
- package/dist/chunk-6XZXYNJR.js +74 -0
- package/dist/chunk-6XZXYNJR.js.map +1 -0
- package/dist/chunk-753HWDSC.js +222 -0
- package/dist/chunk-753HWDSC.js.map +1 -0
- package/dist/chunk-7AHSS7SO.js +81 -0
- package/dist/chunk-7AHSS7SO.js.map +1 -0
- package/dist/chunk-7FTJN4SA.js +1 -0
- package/dist/chunk-7FTJN4SA.js.map +1 -0
- package/dist/chunk-AVDMLTPF.js +24 -0
- package/dist/chunk-AVDMLTPF.js.map +1 -0
- package/dist/chunk-BKAHUEIT.js +95 -0
- package/dist/chunk-BKAHUEIT.js.map +1 -0
- package/dist/chunk-CJ7OFEF7.js +134 -0
- package/dist/chunk-CJ7OFEF7.js.map +1 -0
- package/dist/chunk-EJKWDZIU.js +111 -0
- package/dist/chunk-EJKWDZIU.js.map +1 -0
- package/dist/chunk-F2J6IQ6I.js +247 -0
- package/dist/chunk-F2J6IQ6I.js.map +1 -0
- package/dist/chunk-F3O7CELI.js +23 -0
- package/dist/chunk-F3O7CELI.js.map +1 -0
- package/dist/chunk-F4IITPGX.js +221 -0
- package/dist/chunk-F4IITPGX.js.map +1 -0
- package/dist/chunk-FD6VC2NA.js +66 -0
- package/dist/chunk-FD6VC2NA.js.map +1 -0
- package/dist/chunk-FRRRWAHO.js +14 -0
- package/dist/chunk-FRRRWAHO.js.map +1 -0
- package/dist/chunk-HCKAGLLB.js +17 -0
- package/dist/chunk-HCKAGLLB.js.map +1 -0
- package/dist/chunk-HVC7FG6B.js +191 -0
- package/dist/chunk-HVC7FG6B.js.map +1 -0
- package/dist/chunk-HXFVOR7Z.js +496 -0
- package/dist/chunk-HXFVOR7Z.js.map +1 -0
- package/dist/chunk-IJWYS2PF.js +122 -0
- package/dist/chunk-IJWYS2PF.js.map +1 -0
- package/dist/chunk-INCHXAFK.js +1395 -0
- package/dist/chunk-INCHXAFK.js.map +1 -0
- package/dist/chunk-JT6BRGQM.js +1 -0
- package/dist/chunk-JT6BRGQM.js.map +1 -0
- package/dist/chunk-L6PMAEMB.js +115 -0
- package/dist/chunk-L6PMAEMB.js.map +1 -0
- package/dist/chunk-LOUGW6MU.js +126 -0
- package/dist/chunk-LOUGW6MU.js.map +1 -0
- package/dist/chunk-LYPZPE53.js +129 -0
- package/dist/chunk-LYPZPE53.js.map +1 -0
- package/dist/chunk-NNCCN7JP.js +186 -0
- package/dist/chunk-NNCCN7JP.js.map +1 -0
- package/dist/chunk-PBC3MZUL.js +212 -0
- package/dist/chunk-PBC3MZUL.js.map +1 -0
- package/dist/chunk-R4ADP2DC.js +20 -0
- package/dist/chunk-R4ADP2DC.js.map +1 -0
- package/dist/chunk-RHN4Y6VS.js +44 -0
- package/dist/chunk-RHN4Y6VS.js.map +1 -0
- package/dist/chunk-SDJNYPYK.js +770 -0
- package/dist/chunk-SDJNYPYK.js.map +1 -0
- package/dist/chunk-UGNTCOVR.js +28 -0
- package/dist/chunk-UGNTCOVR.js.map +1 -0
- package/dist/chunk-VC2PCK2S.js +31 -0
- package/dist/chunk-VC2PCK2S.js.map +1 -0
- package/dist/chunk-VF5YDZJC.js +305 -0
- package/dist/chunk-VF5YDZJC.js.map +1 -0
- package/dist/chunk-VISRDVTK.js +675 -0
- package/dist/chunk-VISRDVTK.js.map +1 -0
- package/dist/chunk-W6O3LVBY.js +102 -0
- package/dist/chunk-W6O3LVBY.js.map +1 -0
- package/dist/chunk-WFEAKJLR.js +47 -0
- package/dist/chunk-WFEAKJLR.js.map +1 -0
- package/dist/chunk-XIEY2OF4.js +67 -0
- package/dist/chunk-XIEY2OF4.js.map +1 -0
- package/dist/chunk-XITLHDG3.js +37 -0
- package/dist/chunk-XITLHDG3.js.map +1 -0
- package/dist/chunk-XMWPYZWZ.js +61 -0
- package/dist/chunk-XMWPYZWZ.js.map +1 -0
- package/dist/chunk-XUP6CNOM.js +34 -0
- package/dist/chunk-XUP6CNOM.js.map +1 -0
- package/dist/chunk-YABTQZDJ.js +25 -0
- package/dist/chunk-YABTQZDJ.js.map +1 -0
- package/dist/chunk-YMHVRBUU.js +199 -0
- package/dist/chunk-YMHVRBUU.js.map +1 -0
- package/dist/chunk-YMJ7BRKU.js +510 -0
- package/dist/chunk-YMJ7BRKU.js.map +1 -0
- package/dist/chunk-ZC3XWU7B.js +28 -0
- package/dist/chunk-ZC3XWU7B.js.map +1 -0
- package/dist/chunk-ZHMTD5KV.js +90 -0
- package/dist/chunk-ZHMTD5KV.js.map +1 -0
- package/dist/chunk-ZWZDLOE7.js +160 -0
- package/dist/chunk-ZWZDLOE7.js.map +1 -0
- package/dist/db-package/db/local-first-sqlite-adapter.d.ts +432 -0
- package/dist/db-package/db/local-first-sqlite-adapter.js +7 -0
- package/dist/db-package/db/local-first-sqlite-adapter.js.map +1 -0
- package/dist/db-package/index.d.ts +7 -0
- package/dist/db-package/index.js +16 -0
- package/dist/db-package/index.js.map +1 -0
- package/dist/db-package/migration/aggregator.d.ts +86 -0
- package/dist/db-package/migration/aggregator.js +7 -0
- package/dist/db-package/migration/aggregator.js.map +1 -0
- package/dist/db-package/migration/index.d.ts +3 -0
- package/dist/db-package/migration/index.js +8 -0
- package/dist/db-package/migration/index.js.map +1 -0
- package/dist/engine.d.ts +205 -0
- package/dist/engine.js +20 -0
- package/dist/engine.js.map +1 -0
- package/dist/entitlements/index.d.ts +313 -0
- package/dist/entitlements/index.js +23 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/execution/chat.d.ts +7 -0
- package/dist/execution/chat.js +7 -0
- package/dist/execution/chat.js.map +1 -0
- package/dist/execution/index.d.ts +342 -0
- package/dist/execution/index.js +66 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/tool-agent.d.ts +7 -0
- package/dist/execution/tool-agent.js +8 -0
- package/dist/execution/tool-agent.js.map +1 -0
- package/dist/extensions/index.d.ts +172 -0
- package/dist/extensions/index.js +95 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/health/index.d.ts +35 -0
- package/dist/health/index.js +7 -0
- package/dist/health/index.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +161 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/index.d.ts +133 -0
- package/dist/lifecycle/index.js +13 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle-engine/backend-migration.d.ts +2 -0
- package/dist/lifecycle-engine/backend-migration.js +11 -0
- package/dist/lifecycle-engine/backend-migration.js.map +1 -0
- package/dist/lifecycle-engine/baseline.d.ts +2 -0
- package/dist/lifecycle-engine/baseline.js +9 -0
- package/dist/lifecycle-engine/baseline.js.map +1 -0
- package/dist/lifecycle-engine/index.d.ts +1497 -0
- package/dist/lifecycle-engine/index.js +41 -0
- package/dist/lifecycle-engine/index.js.map +1 -0
- package/dist/lifecycle-engine/platform/artifacts.d.ts +4 -0
- package/dist/lifecycle-engine/platform/artifacts.js +7 -0
- package/dist/lifecycle-engine/platform/artifacts.js.map +1 -0
- package/dist/lifecycle-engine/platform/control-plane.d.ts +12 -0
- package/dist/lifecycle-engine/platform/control-plane.js +2 -0
- package/dist/lifecycle-engine/platform/control-plane.js.map +1 -0
- package/dist/lifecycle-engine/platform/database-router.d.ts +118 -0
- package/dist/lifecycle-engine/platform/database-router.js +7 -0
- package/dist/lifecycle-engine/platform/database-router.js.map +1 -0
- package/dist/lifecycle-engine/platform/deployment-placements.d.ts +464 -0
- package/dist/lifecycle-engine/platform/deployment-placements.js +16 -0
- package/dist/lifecycle-engine/platform/deployment-placements.js.map +1 -0
- package/dist/lifecycle-engine/platform/deployment.d.ts +4 -0
- package/dist/lifecycle-engine/platform/deployment.js +17 -0
- package/dist/lifecycle-engine/platform/deployment.js.map +1 -0
- package/dist/lifecycle-engine/platform/index.d.ts +737 -0
- package/dist/lifecycle-engine/platform/index.js +60 -0
- package/dist/lifecycle-engine/platform/index.js.map +1 -0
- package/dist/lifecycle-engine/platform/runtime.d.ts +80 -0
- package/dist/lifecycle-engine/platform/runtime.js +11 -0
- package/dist/lifecycle-engine/platform/runtime.js.map +1 -0
- package/dist/lifecycle-engine/platform/site-publication.d.ts +102 -0
- package/dist/lifecycle-engine/platform/site-publication.js +7 -0
- package/dist/lifecycle-engine/platform/site-publication.js.map +1 -0
- package/dist/lifecycle-engine/platform/user-directory.d.ts +139 -0
- package/dist/lifecycle-engine/platform/user-directory.js +13 -0
- package/dist/lifecycle-engine/platform/user-directory.js.map +1 -0
- package/dist/mcp.d.ts +92 -0
- package/dist/mcp.js +10 -0
- package/dist/mcp.js.map +1 -0
- package/dist/middleware/auth.d.ts +118 -0
- package/dist/middleware/auth.js +7 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +35 -0
- package/dist/middleware/error-handler.js +7 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/module-guard.d.ts +33 -0
- package/dist/middleware/module-guard.js +7 -0
- package/dist/middleware/module-guard.js.map +1 -0
- package/dist/platform-admin/index.d.ts +37 -0
- package/dist/platform-admin/index.js +11 -0
- package/dist/platform-admin/index.js.map +1 -0
- package/dist/platform-admin/store.d.ts +346 -0
- package/dist/platform-admin/store.js +7 -0
- package/dist/platform-admin/store.js.map +1 -0
- package/dist/plugin-data/annotations.d.ts +6 -0
- package/dist/plugin-data/annotations.js +12 -0
- package/dist/plugin-data/annotations.js.map +1 -0
- package/dist/plugin-data/atomic-context.d.ts +41 -0
- package/dist/plugin-data/atomic-context.js +9 -0
- package/dist/plugin-data/atomic-context.js.map +1 -0
- package/dist/plugin-data/atomic.d.ts +6 -0
- package/dist/plugin-data/atomic.js +26 -0
- package/dist/plugin-data/atomic.js.map +1 -0
- package/dist/plugin-data/audit.d.ts +6 -0
- package/dist/plugin-data/audit.js +12 -0
- package/dist/plugin-data/audit.js.map +1 -0
- package/dist/plugin-data/commands.d.ts +6 -0
- package/dist/plugin-data/commands.js +8 -0
- package/dist/plugin-data/commands.js.map +1 -0
- package/dist/plugin-data/composite.d.ts +6 -0
- package/dist/plugin-data/composite.js +8 -0
- package/dist/plugin-data/composite.js.map +1 -0
- package/dist/plugin-data/delivery.d.ts +6 -0
- package/dist/plugin-data/delivery.js +16 -0
- package/dist/plugin-data/delivery.js.map +1 -0
- package/dist/plugin-data/index.d.ts +2798 -0
- package/dist/plugin-data/index.js +102 -0
- package/dist/plugin-data/index.js.map +1 -0
- package/dist/plugin-data/json-documents.d.ts +6 -0
- package/dist/plugin-data/json-documents.js +16 -0
- package/dist/plugin-data/json-documents.js.map +1 -0
- package/dist/plugin-data/outbox.d.ts +6 -0
- package/dist/plugin-data/outbox.js +16 -0
- package/dist/plugin-data/outbox.js.map +1 -0
- package/dist/plugin-data/scoped-records.d.ts +6 -0
- package/dist/plugin-data/scoped-records.js +10 -0
- package/dist/plugin-data/scoped-records.js.map +1 -0
- package/dist/plugin-sdk.d.ts +41 -0
- package/dist/plugin-sdk.js +10 -0
- package/dist/plugin-sdk.js.map +1 -0
- package/dist/sql-lifecycle/index.d.ts +138 -0
- package/dist/sql-lifecycle/index.js +167 -0
- package/dist/sql-lifecycle/index.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +13 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/s3-compatible.d.ts +139 -0
- package/dist/storage/s3-compatible.js +11 -0
- package/dist/storage/s3-compatible.js.map +1 -0
- package/dist/tenant.d.ts +108 -0
- package/dist/tenant.js +9 -0
- package/dist/tenant.js.map +1 -0
- package/dist/workspace-context.d.ts +119 -0
- package/dist/workspace-context.js +9 -0
- package/dist/workspace-context.js.map +1 -0
- package/docs/api.md +30560 -0
- package/docs/db/README.md +49 -0
- package/docs/db/docs/api.md +1613 -0
- package/docs/db/docs/guide.md +47 -0
- package/docs/entitlements.md +60 -0
- package/docs/guide.md +92 -0
- package/docs/index.md +57 -0
- package/docs/ja/db/README.md +46 -0
- package/docs/ja/db/docs/guide.md +53 -0
- package/docs/ja/guide.md +46 -0
- package/docs/ja/lifecycle/README.md +46 -0
- package/docs/ja/lifecycle/docs/guide.md +45 -0
- package/docs/ja/storage/README.md +46 -0
- package/docs/ja/storage/docs/guide.md +43 -0
- package/docs/lifecycle/README.md +49 -0
- package/docs/lifecycle/docs/api.md +3770 -0
- package/docs/lifecycle/docs/baseline.md +50 -0
- package/docs/lifecycle/docs/guide.md +47 -0
- package/docs/storage/README.md +49 -0
- package/docs/storage/docs/api.md +470 -0
- package/docs/storage/docs/guide.md +47 -0
- package/package.json +354 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/auth-hooks/api-key.ts
|
|
2
|
+
import { verifyApiKey } from "@alneas/eaos-core";
|
|
3
|
+
function credentialFromHeaders(headers) {
|
|
4
|
+
const bearer = headers.authorization?.match(/^Bearer\s+(.+)$/i)?.[1]?.trim();
|
|
5
|
+
const value = bearer ?? headers.apiKey?.trim();
|
|
6
|
+
return value?.startsWith("eaos_") ? value : null;
|
|
7
|
+
}
|
|
8
|
+
function prefixFromCredential(secret) {
|
|
9
|
+
const prefixStart = "eaos_".length;
|
|
10
|
+
const prefixEnd = prefixStart + 12;
|
|
11
|
+
if (secret.length <= prefixEnd || secret[prefixEnd] !== "_") return null;
|
|
12
|
+
const prefix = secret.slice(prefixStart, prefixEnd);
|
|
13
|
+
return prefix.length === 12 ? prefix : null;
|
|
14
|
+
}
|
|
15
|
+
async function resolveApiKey(headers, lookup, options = {}) {
|
|
16
|
+
const secret = credentialFromHeaders(headers);
|
|
17
|
+
if (!secret) return null;
|
|
18
|
+
const prefix = prefixFromCredential(secret);
|
|
19
|
+
if (!prefix) return null;
|
|
20
|
+
const record = await lookup.findByPrefix(prefix);
|
|
21
|
+
if (!record || !verifyApiKey(secret, record, options.requiredScope, options.now) || options.authorize && await options.authorize(record) !== true) return null;
|
|
22
|
+
return { userId: record.userId, tenantId: record.tenantId, roles: [], scopes: [...record.scopes], apiKeyId: record.id };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
resolveApiKey
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-ZC3XWU7B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth-hooks/api-key.ts"],"sourcesContent":["import { verifyApiKey, type ApiKeyRecord, type PlatformPrincipal } from '@alneas/eaos-core'\n\n/**\n * Host persistence lookup for {@link resolveApiKey}; records must retain hashed credentials and lifecycle state.\n */\nexport interface ApiKeyLookup {\n /**\n * Reads a key record by its 12-character public prefix.\n * @param prefix - Prefix extracted from an eaos_ credential; never the full secret.\n * @returns The stored record, or null if absent.\n * @throws Persistence errors propagate to resolveApiKey callers.\n \n * @example\n * ```typescript\n * import type { ApiKeyLookup } from \"@alneas/eaos-server/auth-hooks\";\n * export async function findStoredApiKey(lookup: ApiKeyLookup, parsedPrefix: string) {\n * const record = await lookup.findByPrefix(parsedPrefix);\n * return record; // A record match still requires secret, scope and lifecycle verification.\n * }\n * ```\n */\n findByPrefix(prefix: string): Promise<ApiKeyRecord | null>\n}\n\nfunction credentialFromHeaders(headers: { authorization?: string | null; apiKey?: string | null }): string | null {\n const bearer = headers.authorization?.match(/^Bearer\\s+(.+)$/i)?.[1]?.trim()\n const value = bearer ?? headers.apiKey?.trim()\n return value?.startsWith('eaos_') ? value : null\n}\n\nfunction prefixFromCredential(secret: string): string | null {\n // The random token uses base64url, whose alphabet includes `_`. Splitting on\n // `_` therefore truncates valid prefixes and made resolution intermittently\n // fail depending on the random token generated at issuance time.\n const prefixStart = 'eaos_'.length\n const prefixEnd = prefixStart + 12\n if (secret.length <= prefixEnd || secret[prefixEnd] !== '_') return null\n const prefix = secret.slice(prefixStart, prefixEnd)\n return prefix.length === 12 ? prefix : null\n}\n\n/**\n * Verifies a stored API key and resolves its trusted user, tenant, and copied scopes.\n * @param headers - Raw authorization and apiKey header values; a Bearer credential takes precedence, and credentials must begin with eaos_.\n * @param lookup - Host record lookup by public prefix; see {@link ApiKeyLookup}.\n * @param options - requiredScope is checked by core verification; now overrides the verification clock; authorize runs only after cryptographic and scope checks and must return exactly true.\n * @returns A principal with apiKeyId and empty roles, or null for malformed, missing, invalid, expired, insufficiently scoped, or host-denied credentials.\n * @throws Rejects on lookup or authorize failure; these are not converted to null.\n * @remarks Membership and tenant lifecycle checks belong in authorize. No key usage or persistence update occurs here.\n * @example\n * ```typescript\n * import { resolveApiKey, type ApiKeyLookup } from \"@alneas/eaos-server/auth-hooks/api-key\";\n * import type { ApiKeyRecord } from \"@alneas/eaos-core\";\n * export async function authenticate(\n * authorization: string | null,\n * lookup: ApiKeyLookup,\n * authorize: (record: ApiKeyRecord) => Promise<boolean>,\n * ) {\n * return resolveApiKey({ authorization }, lookup, { requiredScope: \"records:read\", authorize });\n * }\n * ```\n */\nexport async function resolveApiKey(headers: {\n /**\n * Optional raw Authorization header; a parsed Bearer credential takes precedence over the dedicated apiKey value.\n */\nauthorization?: string | null;\n /**\n * Optional dedicated API-key header used only when no Bearer credential is parsed; surrounding whitespace is trimmed.\n */\napiKey?: string | null }, lookup: ApiKeyLookup, options: {\n /**\n * Optional exact key scope checked by core cryptographic/lifecycle verification.\n */\nrequiredScope?: string;\n /**\n * Optional verification clock override; omission uses the core verifier clock.\n */\nnow?: Date;\n /**\n * Optional live membership/lifecycle callback after key verification; receives the stored record and must return exactly true to allow, while errors reject resolution.\n */\nauthorize?: (record: ApiKeyRecord) => Promise<boolean> | boolean } = {}): Promise<(PlatformPrincipal & {\n /**\n * Stored key record ID attached to the resolved principal for audit/correlation, never the credential secret.\n */\napiKeyId: string }) | null> {\n const secret = credentialFromHeaders(headers)\n if (!secret) return null\n const prefix = prefixFromCredential(secret)\n if (!prefix) return null\n const record = await lookup.findByPrefix(prefix)\n if (!record || !verifyApiKey(secret, record, options.requiredScope, options.now) || (options.authorize && await options.authorize(record) !== true)) return null\n return { userId: record.userId, tenantId: record.tenantId, roles: [], scopes: [...record.scopes], apiKeyId: record.id }\n}\n"],"mappings":";AAAA,SAAS,oBAA+D;AAwBxE,SAAS,sBAAsB,SAAmF;AAChH,QAAM,SAAS,QAAQ,eAAe,MAAM,kBAAkB,IAAI,CAAC,GAAG,KAAK;AAC3E,QAAM,QAAQ,UAAU,QAAQ,QAAQ,KAAK;AAC7C,SAAO,OAAO,WAAW,OAAO,IAAI,QAAQ;AAC9C;AAEA,SAAS,qBAAqB,QAA+B;AAI3D,QAAM,cAAc,QAAQ;AAC5B,QAAM,YAAY,cAAc;AAChC,MAAI,OAAO,UAAU,aAAa,OAAO,SAAS,MAAM,IAAK,QAAO;AACpE,QAAM,SAAS,OAAO,MAAM,aAAa,SAAS;AAClD,SAAO,OAAO,WAAW,KAAK,SAAS;AACzC;AAuBA,eAAsB,cAAc,SAQV,QAAsB,UAYqB,CAAC,GAI1C;AAC1B,QAAM,SAAS,sBAAsB,OAAO;AAC5C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,qBAAqB,MAAM;AAC1C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,MAAM,OAAO,aAAa,MAAM;AAC/C,MAAI,CAAC,UAAU,CAAC,aAAa,QAAQ,QAAQ,QAAQ,eAAe,QAAQ,GAAG,KAAM,QAAQ,aAAa,MAAM,QAAQ,UAAU,MAAM,MAAM,KAAO,QAAO;AAC5J,SAAO,EAAE,QAAQ,OAAO,QAAQ,UAAU,OAAO,UAAU,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,OAAO,MAAM,GAAG,UAAU,OAAO,GAAG;AACxH;","names":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/agentic/local-runner.ts
|
|
2
|
+
var LocalRunnerPullCoordinator = class {
|
|
3
|
+
/**
|
|
4
|
+
* Creates a coordinator retaining the host store and callbacks.
|
|
5
|
+
* @param options - Queue persistence, clock, retry delay, and optional post-completion callback.
|
|
6
|
+
* @throws If store is absent or retry delay is outside finite 0–86400000 milliseconds.
|
|
7
|
+
*/
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
if (!options?.store) throw new Error("Local runner job store is required");
|
|
11
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
12
|
+
this.retryDelayMs = options.retryDelayMs ?? 5e3;
|
|
13
|
+
if (!Number.isFinite(this.retryDelayMs) || this.retryDelayMs < 0 || this.retryDelayMs > 864e5) throw new Error("Invalid local runner retry delay");
|
|
14
|
+
}
|
|
15
|
+
options;
|
|
16
|
+
now;
|
|
17
|
+
retryDelayMs;
|
|
18
|
+
/**
|
|
19
|
+
* Atomically claims local-pull jobs bound to this verified user, runner, and supported routes.
|
|
20
|
+
* @param identity - Host-authenticated runner identity; see {@link LocalRunnerIdentity}.
|
|
21
|
+
* @param limit - Maximum jobs requested, safe integer 1–100; defaults to 1.
|
|
22
|
+
* @returns Store-claimed jobs with leases, or an empty array when none qualify.
|
|
23
|
+
* @throws Rejects invalid identity/limit and propagated store/clock failures.
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { LocalRunnerPullCoordinator, type LocalRunnerPullCoordinatorOptions, type LocalRunnerIdentity } from "@alneas/eaos-server/agentic/local-runner";
|
|
27
|
+
* export async function pull(options: LocalRunnerPullCoordinatorOptions, identity: LocalRunnerIdentity) {
|
|
28
|
+
* return new LocalRunnerPullCoordinator(options).claim(identity);
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
async claim(identity, limit = 1) {
|
|
33
|
+
validateIdentity(identity);
|
|
34
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid local runner claim limit");
|
|
35
|
+
const filter = {
|
|
36
|
+
dispatchMode: "local-pull",
|
|
37
|
+
routeIds: identity.supportedRouteIds,
|
|
38
|
+
runnerId: identity.runnerId,
|
|
39
|
+
userId: identity.userId
|
|
40
|
+
};
|
|
41
|
+
return this.options.store.claim(this.now(), limit, filter);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Persists a leased local-runner outcome after checking its user, route, and runner bindings.
|
|
45
|
+
* @param identity - Host-authenticated runner identity.
|
|
46
|
+
* @param job - Host-loaded leased job; must belong to the same user, local-pull mode, supported route, and optional runner binding.
|
|
47
|
+
* @param result - Runtime result; COMPLETED marks completion, PENDING_APPROVAL waits, other results retry unless retryable is exactly false.
|
|
48
|
+
* @returns No value; resolves after the selected store transition and any completion callback.
|
|
49
|
+
* @throws Rejects invalid binding, store/clock/clone errors, or completion callback errors. Callback failure occurs after completion persistence.
|
|
50
|
+
* @remarks The host loads the authoritative job and authorizes tenant/user access before calling. Error text is truncated to 1000 characters for retries/failures. Store lease validation is the concurrency boundary; no remote runtime cancellation or automatic retry execution is performed here.
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { LocalRunnerPullCoordinator, type LocalRunnerPullCoordinatorOptions, type LocalRunnerIdentity } from "@alneas/eaos-server/agentic/local-runner";
|
|
54
|
+
* import type { AgentJobRecord } from "@alneas/eaos-core";
|
|
55
|
+
* import type { ServerAgentTaskRequest, ServerAgentTaskResult } from "@alneas/eaos-server/agentic/types";
|
|
56
|
+
* export async function submit(options: LocalRunnerPullCoordinatorOptions, identity: LocalRunnerIdentity, job: AgentJobRecord<ServerAgentTaskRequest>, result: ServerAgentTaskResult) {
|
|
57
|
+
* await new LocalRunnerPullCoordinator(options).submit(identity, job, result);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
async submit(identity, job, result) {
|
|
62
|
+
validateIdentity(identity);
|
|
63
|
+
if (!job?.leaseToken || job.request.userId !== identity.userId || job.request.executionContext?.dispatchMode !== "local-pull" || !job.request.executionContext.routeId || !identity.supportedRouteIds.includes(job.request.executionContext.routeId) || job.request.executionContext.runnerId !== void 0 && job.request.executionContext.runnerId !== identity.runnerId) throw new Error("Local runner is not bound to this job");
|
|
64
|
+
const now = this.now();
|
|
65
|
+
if (result.status === "COMPLETED") {
|
|
66
|
+
await this.options.store.markCompleted(job.jobId, job.leaseToken, structuredClone(result), now);
|
|
67
|
+
await this.options.onCompleted?.(structuredClone(job), structuredClone(result));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (result.status === "PENDING_APPROVAL") {
|
|
71
|
+
await this.options.store.markWaitingApproval(job.jobId, job.leaseToken, structuredClone(result), now);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const error = result.error ?? "Local runner failed";
|
|
75
|
+
if (result.retryable !== false) {
|
|
76
|
+
await this.options.store.markRetryable(job.jobId, job.leaseToken, error.slice(0, 1e3), result.failureCode, new Date(now.getTime() + this.retryDelayMs), now);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await this.options.store.markFailed(job.jobId, job.leaseToken, error.slice(0, 1e3), result.failureCode, now);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function validateIdentity(identity) {
|
|
83
|
+
if (!identity || !identity.runnerId?.trim() || !identity.userId?.trim() || !Array.isArray(identity.supportedRouteIds) || identity.supportedRouteIds.length === 0) throw new Error("Invalid local runner identity");
|
|
84
|
+
if (identity.supportedRouteIds.some((routeId) => typeof routeId !== "string" || !routeId.trim())) throw new Error("Invalid local runner route binding");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
LocalRunnerPullCoordinator
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=chunk-ZHMTD5KV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agentic/local-runner.ts"],"sourcesContent":["import type { AgentJobRecord, AgentJobKind } from '@alneas/eaos-core'\nimport type { AgentJobClaimFilter, AgentJobStore } from './agent-jobs'\nimport type { ServerAgentTaskRequest, ServerAgentTaskResult } from './types'\n\n/**\n * Verified local-runner identity and routes admitted by the host.\n * @see LocalRunnerPullCoordinator.\n */\nexport interface LocalRunnerIdentity {\n /** Nonblank authenticated runner identifier. */\n runnerId: string\n /** Nonblank authenticated user to whom claimed jobs must belong. */\n userId: string\n /** Nonempty array of nonblank host-authorized route identifiers. */\n supportedRouteIds: readonly string[]\n}\n\n/**\n * Durable queue and completion notification for {@link LocalRunnerPullCoordinator}.\n */\nexport interface LocalRunnerPullCoordinatorOptions {\n /** Host job store enforcing atomic claims and lease ownership. */\n store: AgentJobStore\n /** Clock read once per claim/submit; defaults to a fresh Date. */\n now?: () => Date\n /** Retry delay in milliseconds, finite from 0 to 86400000. @defaultValue 5000 */\n retryDelayMs?: number\n /** Called with cloned job/result only after markCompleted succeeds; rejection propagates after the persisted completion. */\n onCompleted?: (job: Readonly<AgentJobRecord<ServerAgentTaskRequest>>, result: ServerAgentTaskResult) => Promise<void>\n}\n\n/**\n * Server-side queue boundary for a local CLI/MLX runner.\n *\n * The runner authenticates to the host and pulls jobs; the server never needs\n * the subscription credential. The host must perform authentication and\n * tenant/user authorization before calling claim or submit.\n * @example\n * ```typescript\n * import { LocalRunnerPullCoordinator, type LocalRunnerPullCoordinatorOptions } from \"@alneas/eaos-server/agentic/local-runner\";\n * export function localRunnerQueue(options: LocalRunnerPullCoordinatorOptions) {\n * return new LocalRunnerPullCoordinator(options);\n * }\n * ```\n */\nexport class LocalRunnerPullCoordinator {\n private readonly now: () => Date\n private readonly retryDelayMs: number\n\n /**\n * Creates a coordinator retaining the host store and callbacks.\n * @param options - Queue persistence, clock, retry delay, and optional post-completion callback.\n * @throws If store is absent or retry delay is outside finite 0–86400000 milliseconds.\n */\n constructor(private readonly options: LocalRunnerPullCoordinatorOptions) {\n if (!options?.store) throw new Error('Local runner job store is required')\n this.now = options.now ?? (() => new Date())\n this.retryDelayMs = options.retryDelayMs ?? 5_000\n if (!Number.isFinite(this.retryDelayMs) || this.retryDelayMs < 0 || this.retryDelayMs > 86_400_000) throw new Error('Invalid local runner retry delay')\n }\n\n /**\n * Atomically claims local-pull jobs bound to this verified user, runner, and supported routes.\n * @param identity - Host-authenticated runner identity; see {@link LocalRunnerIdentity}.\n * @param limit - Maximum jobs requested, safe integer 1–100; defaults to 1.\n * @returns Store-claimed jobs with leases, or an empty array when none qualify.\n * @throws Rejects invalid identity/limit and propagated store/clock failures.\n * @example\n * ```typescript\n * import { LocalRunnerPullCoordinator, type LocalRunnerPullCoordinatorOptions, type LocalRunnerIdentity } from \"@alneas/eaos-server/agentic/local-runner\";\n * export async function pull(options: LocalRunnerPullCoordinatorOptions, identity: LocalRunnerIdentity) {\n * return new LocalRunnerPullCoordinator(options).claim(identity);\n * }\n * ```\n */\n async claim(identity: LocalRunnerIdentity, limit = 1): Promise<readonly AgentJobRecord<ServerAgentTaskRequest>[]> {\n validateIdentity(identity)\n if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new Error('Invalid local runner claim limit')\n const filter: AgentJobClaimFilter = {\n dispatchMode: 'local-pull',\n routeIds: identity.supportedRouteIds,\n runnerId: identity.runnerId,\n userId: identity.userId,\n }\n return this.options.store.claim(this.now(), limit, filter)\n }\n\n /**\n * Persists a leased local-runner outcome after checking its user, route, and runner bindings.\n * @param identity - Host-authenticated runner identity.\n * @param job - Host-loaded leased job; must belong to the same user, local-pull mode, supported route, and optional runner binding.\n * @param result - Runtime result; COMPLETED marks completion, PENDING_APPROVAL waits, other results retry unless retryable is exactly false.\n * @returns No value; resolves after the selected store transition and any completion callback.\n * @throws Rejects invalid binding, store/clock/clone errors, or completion callback errors. Callback failure occurs after completion persistence.\n * @remarks The host loads the authoritative job and authorizes tenant/user access before calling. Error text is truncated to 1000 characters for retries/failures. Store lease validation is the concurrency boundary; no remote runtime cancellation or automatic retry execution is performed here.\n * @example\n * ```typescript\n * import { LocalRunnerPullCoordinator, type LocalRunnerPullCoordinatorOptions, type LocalRunnerIdentity } from \"@alneas/eaos-server/agentic/local-runner\";\n * import type { AgentJobRecord } from \"@alneas/eaos-core\";\n * import type { ServerAgentTaskRequest, ServerAgentTaskResult } from \"@alneas/eaos-server/agentic/types\";\n * export async function submit(options: LocalRunnerPullCoordinatorOptions, identity: LocalRunnerIdentity, job: AgentJobRecord<ServerAgentTaskRequest>, result: ServerAgentTaskResult) {\n * await new LocalRunnerPullCoordinator(options).submit(identity, job, result);\n * }\n * ```\n */\n async submit(identity: LocalRunnerIdentity, job: Readonly<AgentJobRecord<ServerAgentTaskRequest>>, result: ServerAgentTaskResult): Promise<void> {\n validateIdentity(identity)\n if (!job?.leaseToken || job.request.userId !== identity.userId || job.request.executionContext?.dispatchMode !== 'local-pull' || !job.request.executionContext.routeId || !identity.supportedRouteIds.includes(job.request.executionContext.routeId) || job.request.executionContext.runnerId !== undefined && job.request.executionContext.runnerId !== identity.runnerId) throw new Error('Local runner is not bound to this job')\n const now = this.now()\n if (result.status === 'COMPLETED') {\n await this.options.store.markCompleted(job.jobId, job.leaseToken, structuredClone(result), now)\n await this.options.onCompleted?.(structuredClone(job), structuredClone(result))\n return\n }\n if (result.status === 'PENDING_APPROVAL') {\n await this.options.store.markWaitingApproval(job.jobId, job.leaseToken, structuredClone(result), now)\n return\n }\n const error = result.error ?? 'Local runner failed'\n if (result.retryable !== false) {\n await this.options.store.markRetryable(job.jobId, job.leaseToken, error.slice(0, 1_000), result.failureCode, new Date(now.getTime() + this.retryDelayMs), now)\n return\n }\n await this.options.store.markFailed(job.jobId, job.leaseToken, error.slice(0, 1_000), result.failureCode, now)\n }\n}\n\nfunction validateIdentity(identity: LocalRunnerIdentity): void {\n if (!identity || !identity.runnerId?.trim() || !identity.userId?.trim() || !Array.isArray(identity.supportedRouteIds) || identity.supportedRouteIds.length === 0) throw new Error('Invalid local runner identity')\n if (identity.supportedRouteIds.some(routeId => typeof routeId !== 'string' || !routeId.trim())) throw new Error('Invalid local runner route binding')\n}\n\n/**\n * Alias of core job kinds accepted by local runner coordination.\n * @see LocalRunnerPullCoordinator.\n */\nexport type LocalRunnerJobKind = AgentJobKind\n"],"mappings":";AA6CO,IAAM,6BAAN,MAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAStC,YAA6B,SAA4C;AAA5C;AAC3B,QAAI,CAAC,SAAS,MAAO,OAAM,IAAI,MAAM,oCAAoC;AACzE,SAAK,MAAM,QAAQ,QAAQ,MAAM,oBAAI,KAAK;AAC1C,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,QAAI,CAAC,OAAO,SAAS,KAAK,YAAY,KAAK,KAAK,eAAe,KAAK,KAAK,eAAe,MAAY,OAAM,IAAI,MAAM,kCAAkC;AAAA,EACxJ;AAAA,EAL6B;AAAA,EARZ;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BjB,MAAM,MAAM,UAA+B,QAAQ,GAA+D;AAChH,qBAAiB,QAAQ;AACzB,QAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,KAAK,QAAQ,IAAK,OAAM,IAAI,MAAM,kCAAkC;AAChH,UAAM,SAA8B;AAAA,MAClC,cAAc;AAAA,MACd,UAAU,SAAS;AAAA,MACnB,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AACA,WAAO,KAAK,QAAQ,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,OAAO,UAA+B,KAAuD,QAA8C;AAC/I,qBAAiB,QAAQ;AACzB,QAAI,CAAC,KAAK,cAAc,IAAI,QAAQ,WAAW,SAAS,UAAU,IAAI,QAAQ,kBAAkB,iBAAiB,gBAAgB,CAAC,IAAI,QAAQ,iBAAiB,WAAW,CAAC,SAAS,kBAAkB,SAAS,IAAI,QAAQ,iBAAiB,OAAO,KAAK,IAAI,QAAQ,iBAAiB,aAAa,UAAa,IAAI,QAAQ,iBAAiB,aAAa,SAAS,SAAU,OAAM,IAAI,MAAM,uCAAuC;AACna,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,KAAK,QAAQ,MAAM,cAAc,IAAI,OAAO,IAAI,YAAY,gBAAgB,MAAM,GAAG,GAAG;AAC9F,YAAM,KAAK,QAAQ,cAAc,gBAAgB,GAAG,GAAG,gBAAgB,MAAM,CAAC;AAC9E;AAAA,IACF;AACA,QAAI,OAAO,WAAW,oBAAoB;AACxC,YAAM,KAAK,QAAQ,MAAM,oBAAoB,IAAI,OAAO,IAAI,YAAY,gBAAgB,MAAM,GAAG,GAAG;AACpG;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,SAAS;AAC9B,QAAI,OAAO,cAAc,OAAO;AAC9B,YAAM,KAAK,QAAQ,MAAM,cAAc,IAAI,OAAO,IAAI,YAAY,MAAM,MAAM,GAAG,GAAK,GAAG,OAAO,aAAa,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,YAAY,GAAG,GAAG;AAC7J;AAAA,IACF;AACA,UAAM,KAAK,QAAQ,MAAM,WAAW,IAAI,OAAO,IAAI,YAAY,MAAM,MAAM,GAAG,GAAK,GAAG,OAAO,aAAa,GAAG;AAAA,EAC/G;AACF;AAEA,SAAS,iBAAiB,UAAqC;AAC7D,MAAI,CAAC,YAAY,CAAC,SAAS,UAAU,KAAK,KAAK,CAAC,SAAS,QAAQ,KAAK,KAAK,CAAC,MAAM,QAAQ,SAAS,iBAAiB,KAAK,SAAS,kBAAkB,WAAW,EAAG,OAAM,IAAI,MAAM,+BAA+B;AACjN,MAAI,SAAS,kBAAkB,KAAK,aAAW,OAAO,YAAY,YAAY,CAAC,QAAQ,KAAK,CAAC,EAAG,OAAM,IAAI,MAAM,oCAAoC;AACtJ;","names":[]}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
summarizeDeployment,
|
|
3
|
+
validateDeploymentPlugins,
|
|
4
|
+
validateDeploymentReceipt,
|
|
5
|
+
validateDeploymentTarget
|
|
6
|
+
} from "./chunk-6CBVZ6CT.js";
|
|
7
|
+
|
|
8
|
+
// src/lifecycle-engine/platform/deployment-placements.ts
|
|
9
|
+
import { canonicalJsonHash } from "@alneas/eaos-core/serialization";
|
|
10
|
+
var object = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
11
|
+
var text = (v) => typeof v === "string" && !!v.trim() && v === v.trim();
|
|
12
|
+
var id = (v) => text(v) && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(v);
|
|
13
|
+
var keys = (v, allowed) => {
|
|
14
|
+
if (Object.keys(v).some((k) => !allowed.includes(k))) throw Error("Unknown deployment placement field");
|
|
15
|
+
};
|
|
16
|
+
function validateDeploymentPlacements(value) {
|
|
17
|
+
if (!object(value) || !Array.isArray(value.destinations) || !Array.isArray(value.plugins)) throw Error("Invalid deployment placements");
|
|
18
|
+
keys(value, ["destinations", "plugins"]);
|
|
19
|
+
const destinations = /* @__PURE__ */ new Set(), plugins = /* @__PURE__ */ new Set();
|
|
20
|
+
for (const d of value.destinations) {
|
|
21
|
+
if (!object(d) || !id(d.id) || destinations.has(d.id) || !["host", "container", "function", "database", "cloud", "native", "external"].includes(d.placement) || !text(d.revision)) throw Error("Invalid or duplicate deployment destination");
|
|
22
|
+
keys(d, ["id", "placement", "service", "reference", "backendId", "databaseId", "tenantId", "revision"]);
|
|
23
|
+
if (d.tenantId !== void 0 && (!text(d.tenantId) || d.tenantId === "*" || d.placement !== "database")) throw Error("Tenant restriction requires a concrete database destination");
|
|
24
|
+
if (d.service !== void 0 && !["frontend", "backend", "worker"].includes(d.service)) throw Error("Unknown Platform service role");
|
|
25
|
+
if (d.reference !== void 0 && (!text(d.reference) || !/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(d.reference))) throw Error("Use a non-secret host binding reference");
|
|
26
|
+
if (d.placement === "database" ? !text(d.backendId) || !text(d.databaseId) || d.service !== void 0 || d.reference !== void 0 : d.backendId !== void 0 || d.databaseId !== void 0 || !!d.service === !!d.reference) throw Error("Destination requires an exact database or one service/resource reference");
|
|
27
|
+
if (d.service && !["host", "container"].includes(d.placement)) throw Error("Service destination must use host or container placement");
|
|
28
|
+
destinations.add(d.id);
|
|
29
|
+
}
|
|
30
|
+
for (const p of value.plugins) {
|
|
31
|
+
if (!object(p) || !text(p.owner) || !id(p.pluginId) || !object(p.components) || plugins.has(JSON.stringify([p.owner, p.pluginId]))) throw Error("Invalid or duplicate plugin placement");
|
|
32
|
+
keys(p, ["owner", "pluginId", "components"]);
|
|
33
|
+
plugins.add(JSON.stringify([p.owner, p.pluginId]));
|
|
34
|
+
for (const [component, refs] of Object.entries(p.components)) if (!id(component) || !Array.isArray(refs) || new Set(refs).size !== refs.length || refs.some((ref) => !id(ref) || !destinations.has(ref))) throw Error("Invalid or unknown component destination");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function resolveDeploymentPlacements(placements, inventories) {
|
|
38
|
+
validateDeploymentPlacements(placements);
|
|
39
|
+
const owners = new Set(inventories.map((inv) => inv.owner));
|
|
40
|
+
if (owners.size !== inventories.length) throw Error("Duplicate placement inventory owner");
|
|
41
|
+
for (const inventory of inventories) validateDeploymentPlugins(inventory);
|
|
42
|
+
return placements.plugins.flatMap((selection) => {
|
|
43
|
+
const inventory = inventories.find((inv) => inv.owner === selection.owner);
|
|
44
|
+
const plugin = inventory?.plugins?.find((p) => p.id === selection.pluginId);
|
|
45
|
+
if (!plugin) throw Error(`Unknown or unclassified selected plugin: ${selection.owner}/${selection.pluginId}`);
|
|
46
|
+
if (Object.keys(selection.components).some((c) => !plugin.components.some((known) => known.id === c))) throw Error("Unknown selected component");
|
|
47
|
+
for (const task of plugin.tasks) if (task.componentIds.some((c) => selection.components[c]?.length) && task.dependsOn.some((ref) => {
|
|
48
|
+
const dep = plugin.tasks.find((t) => t.id === ref);
|
|
49
|
+
return dep.scope !== "artifact" && !dep.componentIds.some((c) => selection.components[c]?.length);
|
|
50
|
+
})) throw Error("Adopted task depends on an unadopted component");
|
|
51
|
+
return plugin.components.map((component) => {
|
|
52
|
+
if (!Object.hasOwn(selection.components, component.id)) throw Error(`Missing component placement: ${plugin.id}/${component.id}`);
|
|
53
|
+
const destinationIds = selection.components[component.id];
|
|
54
|
+
if (component.required !== false && !destinationIds.length) throw Error("Required component cannot be unadopted");
|
|
55
|
+
for (const ref of destinationIds) {
|
|
56
|
+
const d = placements.destinations.find((d2) => d2.id === ref);
|
|
57
|
+
if (component.placement !== d.placement && !(component.placement === "host" && d.placement === "container")) throw Error(`Incompatible component placement: ${plugin.id}/${component.id}`);
|
|
58
|
+
if (d.service && (component.kind === "frontend" && d.service !== "frontend" || component.kind !== "frontend" && d.service === "frontend")) throw Error("Frontend and server components require different service roles");
|
|
59
|
+
}
|
|
60
|
+
return { owner: inventory.owner, pluginId: plugin.id, componentId: component.id, destinationIds: [...destinationIds], adopted: destinationIds.length > 0 };
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function deploymentPlacementHash(placements, owner, pluginId, bindings = {}) {
|
|
65
|
+
const selection = placements.plugins.find((p) => p.owner === owner && p.pluginId === pluginId);
|
|
66
|
+
if (!selection) throw Error("Plugin is not selected in deployment placements");
|
|
67
|
+
const components = Object.fromEntries(Object.entries(selection.components).sort(([a], [b]) => a.localeCompare(b)).map(([key, refs]) => [key, [...refs].sort()]));
|
|
68
|
+
const ids = new Set(Object.values(components).flat());
|
|
69
|
+
return canonicalJsonHash(JSON.parse(JSON.stringify({ owner, pluginId, components, destinations: placements.destinations.filter((d) => ids.has(d.id)).sort((a, b) => a.id.localeCompare(b.id)), bindings: Object.fromEntries([...ids].sort().filter((id2) => bindings[id2] !== void 0).map((id2) => [id2, bindings[id2]])) })));
|
|
70
|
+
}
|
|
71
|
+
function deploymentExecutionTarget(destination, scope, context) {
|
|
72
|
+
if (!context || scope === "tenant" && !context.tenantId) return void 0;
|
|
73
|
+
if (scope === "tenant" && destination.tenantId && context.tenantId !== destination.tenantId) return void 0;
|
|
74
|
+
return { environment: context.environment, ...destination.backendId ? { backendId: destination.backendId, databaseId: destination.databaseId } : {}, ...scope === "tenant" ? { tenantId: context.tenantId } : {} };
|
|
75
|
+
}
|
|
76
|
+
var targetKey = (v) => canonicalJsonHash([v?.environment ?? null, v?.backendId ?? null, v?.databaseId ?? null, v?.tenantId ?? null]);
|
|
77
|
+
var aggregate = (values) => values.includes("failed") ? "failed" : values.includes("blocked") ? "blocked" : values.includes("stale") ? "stale" : values.includes("running") ? "running" : values.every((v) => v === "succeeded") ? "succeeded" : "pending";
|
|
78
|
+
function summarizePlacedDeployment(inventory, inventorySha256, receipts, placements, context, bindings = {}) {
|
|
79
|
+
resolveDeploymentPlacements({ ...placements, plugins: placements.plugins.filter((p) => p.owner === inventory.owner) }, [inventory]);
|
|
80
|
+
if (context) {
|
|
81
|
+
validateDeploymentTarget(context);
|
|
82
|
+
if (context.backendId || context.databaseId) throw Error("With placements, backend/database come from destinations; specify only environment and tenant");
|
|
83
|
+
}
|
|
84
|
+
for (const receipt of receipts) validateDeploymentReceipt(receipt);
|
|
85
|
+
const base = summarizeDeployment(inventory, inventorySha256, receipts, context);
|
|
86
|
+
const ignoredReceipts = base.ignoredReceipts.filter((r) => r.reason !== "placement-context-required");
|
|
87
|
+
const plugins = base.plugins.map((plugin) => {
|
|
88
|
+
const selection = placements.plugins.find((p) => p.owner === inventory.owner && p.pluginId === plugin.id);
|
|
89
|
+
if (!selection) return { ...plugin, selected: false, components: plugin.components.map((c) => ({ ...c, adopted: false, destinationIds: [] })), tasks: plugin.tasks.map((t) => ({ ...t, adopted: false, executions: [] })), complete: false };
|
|
90
|
+
const binding = deploymentPlacementHash(placements, inventory.owner, plugin.id, bindings);
|
|
91
|
+
const active = (componentId) => selection.components[componentId].length > 0;
|
|
92
|
+
const instances = /* @__PURE__ */ new Map();
|
|
93
|
+
const results = /* @__PURE__ */ new Map(), stale = /* @__PURE__ */ new Set();
|
|
94
|
+
for (const receipt of receipts) {
|
|
95
|
+
if (receipt.owner !== inventory.owner || receipt.inventorySha256 !== inventorySha256) continue;
|
|
96
|
+
for (const result of receipt.results.filter((r) => r.pluginId === plugin.id)) {
|
|
97
|
+
const task = plugin.tasks.find((t) => t.id === result.taskId);
|
|
98
|
+
if (!task) throw Error("Receipt references an undeclared placement task");
|
|
99
|
+
if (task.scope === "artifact") {
|
|
100
|
+
if (result.destinationId || result.target) throw Error("Artifact evidence cannot select a destination");
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (!result.target || task.scope === "tenant" && !result.target.tenantId || task.scope === "environment" && result.target.tenantId) throw Error("Receipt target does not match placement task scope");
|
|
104
|
+
if (!result.destinationId) {
|
|
105
|
+
ignoredReceipts.push({ owner: receipt.owner, reason: "unbound-placement" });
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const destination = placements.destinations.find((d) => d.id === result.destinationId);
|
|
109
|
+
const expected = destination ? deploymentExecutionTarget(destination, task.scope, context) : void 0;
|
|
110
|
+
const key = JSON.stringify([task.id, result.destinationId]);
|
|
111
|
+
if (result.placementSha256 !== binding || !destination || !task.componentIds.some((c) => selection.components[c].includes(result.destinationId))) {
|
|
112
|
+
ignoredReceipts.push({ owner: receipt.owner, reason: "stale-placement" });
|
|
113
|
+
if (context?.environment === result.target.environment && (task.scope === "environment" || context.tenantId === result.target.tenantId)) stale.add(key);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!expected || targetKey(expected) !== targetKey(result.target)) continue;
|
|
117
|
+
const old = results.get(key);
|
|
118
|
+
if (old?.recordedAt === result.recordedAt && JSON.stringify(old) !== JSON.stringify(result)) throw Error("Conflicting placement results at the same timestamp");
|
|
119
|
+
if (!old || old.recordedAt < result.recordedAt) results.set(key, result);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const compile = (taskId) => {
|
|
123
|
+
const known = instances.get(taskId);
|
|
124
|
+
if (known) return known;
|
|
125
|
+
const task = plugin.tasks.find((t) => t.id === taskId);
|
|
126
|
+
const dependencies = task.dependsOn.flatMap(compile);
|
|
127
|
+
const adopted = task.componentIds.some(active);
|
|
128
|
+
const required = task.required || task.componentIds.some((id2) => active(id2) && plugin.components.find((c) => c.id === id2)?.required === false);
|
|
129
|
+
const refs = task.scope === "artifact" ? [void 0] : [...new Set(task.componentIds.flatMap((c) => selection.components[c]))];
|
|
130
|
+
const rows = refs.map((destinationId) => {
|
|
131
|
+
const destination = destinationId ? placements.destinations.find((d) => d.id === destinationId) : void 0;
|
|
132
|
+
const key = JSON.stringify([task.id, destinationId]);
|
|
133
|
+
const result = task.scope === "artifact" ? task.result : results.get(key);
|
|
134
|
+
const blocked = dependencies.some((d) => d.status !== "succeeded");
|
|
135
|
+
const outdated = result?.status === "succeeded" && dependencies.some((d) => d.result && d.result.recordedAt > result.recordedAt);
|
|
136
|
+
const status = blocked ? "blocked" : outdated || !result && stale.has(key) ? "stale" : result?.status ?? "pending";
|
|
137
|
+
return { taskId: task.id, destinationId, componentIds: task.componentIds.filter((c) => !destinationId || selection.components[c].includes(destinationId)), required: required && (adopted || task.scope === "artifact"), status, result, target: destination ? deploymentExecutionTarget(destination, task.scope, context) : void 0, dependencies: dependencies.map((d) => ({ taskId: d.taskId, destinationId: d.destinationId })) };
|
|
138
|
+
});
|
|
139
|
+
instances.set(taskId, rows);
|
|
140
|
+
return rows;
|
|
141
|
+
};
|
|
142
|
+
const tasks = plugin.tasks.map((task) => {
|
|
143
|
+
const executions2 = compile(task.id);
|
|
144
|
+
return { ...task, adopted: task.scope === "artifact" || task.componentIds.some(active), required: executions2.some((e) => e.required), status: executions2.length ? aggregate(executions2.map((e) => e.status)) : "pending", executions: executions2 };
|
|
145
|
+
});
|
|
146
|
+
return { ...plugin, selected: true, placementSha256: binding, tasks, complete: plugin.artifactResolved && tasks.filter((t) => t.adopted && t.required).every((t) => t.status === "succeeded"), components: plugin.components.map((c) => ({ ...c, adopted: active(c.id), destinationIds: selection.components[c.id], complete: active(c.id) && plugin.artifactResolved && tasks.filter((t) => t.componentIds.includes(c.id) && t.adopted && t.required).every((t) => t.status === "succeeded") })) };
|
|
147
|
+
});
|
|
148
|
+
const executions = plugins.flatMap((p) => p.tasks.flatMap((t) => t.executions.map((e) => ({ ...e, pluginId: p.id, owner: inventory.owner }))));
|
|
149
|
+
const destinations = placements.destinations.map((destination) => ({ ...destination, executions: executions.filter((e) => e.destinationId === destination.id) }));
|
|
150
|
+
return { ...base, plugins, complete: base.artifactResolved && plugins.filter((p) => p.selected).every((p) => p.complete), ignoredReceipts: [...new Map(ignoredReceipts.map((r) => [JSON.stringify([r.owner, r.reason]), r])).values()], placementCoverage: "explicit", destinations, artifactExecutions: executions.filter((e) => !e.destinationId) };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
validateDeploymentPlacements,
|
|
155
|
+
resolveDeploymentPlacements,
|
|
156
|
+
deploymentPlacementHash,
|
|
157
|
+
deploymentExecutionTarget,
|
|
158
|
+
summarizePlacedDeployment
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=chunk-ZWZDLOE7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lifecycle-engine/platform/deployment-placements.ts"],"sourcesContent":["import { canonicalJsonHash } from '@alneas/eaos-core/serialization'\nimport type { DeploymentInventory } from './artifacts'\nimport { summarizeDeployment, validateDeploymentPlugins, validateDeploymentReceipt, validateDeploymentTarget, type DeploymentComponent, type DeploymentResult, type DeploymentReceipt, type DeploymentTarget, type DeploymentTaskStatus } from './deployment'\n\n/**\n * Explicit existing host service/resource or physical database destination with a binding revision.\n * \n * @see summarizePlacedDeployment for canonical integration usage.\n */\nexport interface DeploymentDestination {\n /**\n * Unique destination identifier.\n */\n id: string\n /**\n * Host/container/function/database/cloud/native/external destination category.\n */\n placement: DeploymentComponent['placement']\n /**\n * Optional symbolic existing frontend/backend/worker role; host/container only.\n * \n * Symbolic role of an existing Platform service.\n */\n service?: 'frontend' | 'backend' | 'worker'\n /**\n * Optional nonsecret host resource/operation binding; never credentials or executable code.\n * \n * Host-owned resource/operation binding reference, never credentials or an executable command.\n */\n reference?: string\n /**\n * Required existing backend ID for database destinations; forbidden otherwise.\n */\n backendId?: string\n /**\n * Required exact physical database identity for database destinations.\n */\n databaseId?: string\n /**\n * Optional dedicated concrete tenant restriction; database destinations only.\n * \n * Restrict a dedicated DB destination to its verified tenant.\n */\n tenantId?: string\n /**\n * Nonblank host binding revision; change when wiring/provider/credential revision changes.\n * \n * Change when the host binding changes, including a credential/provider revision.\n */\n revision: string\n}\n/**\n * Explicit plugin selection and destination assignments; empty lists deliberately decline optional components.\n * \n * @see summarizePlacedDeployment for canonical integration usage.\n */\nexport interface DeploymentPlacements {\n /**\n * Unique nonsecret existing destination declarations.\n */\n destinations: DeploymentDestination[]\n /**\n * Selected owner/plugin pairs and complete component-ID to destination-ID lists; selection never registers/enables code.\n * \n * Listing a plugin selects it for this application plan; this never registers or enables code.\n */\n plugins: Array<{\n /**\n * Exact inventory owner selecting this plugin.\n */\n owner: string; \n /**\n * Plugin declared by that owner; listing it selects application placement only.\n */\n pluginId: string; \n /**\n * Complete component-ID to destination-ID lists; empty declines only optional capabilities.\n */\n components: Record<string, string[]>}>\n}\nconst object = (v: unknown): v is Record<string, any> => !!v && typeof v === 'object' && !Array.isArray(v)\nconst text = (v: unknown): v is string => typeof v === 'string' && !!v.trim() && v === v.trim()\nconst id = (v: unknown): v is string => text(v) && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(v)\nconst keys = (v: object, allowed: string[]) => { if(Object.keys(v).some(k=>!allowed.includes(k))) throw Error('Unknown deployment placement field') }\n\n/**\n * Validates nonsecret destination definitions and explicit component-to-destination selections.\n * \n * Validate only non-secret declarations here; no service discovery, provisioning or code execution.\n * @param value - Untrusted destinations/plugins declaration.\n * @returns Completes without a value and narrows the input to DeploymentPlacements.\n * @throws Error for duplicate/unknown IDs, unknown fields, invalid placement kind, malformed host references or conflicting destination configuration.\n * \n * @remarks\n * A database destination uses exact backend/database IDs; other destinations use exactly one symbolic service or host reference. Dedicated tenant restrictions apply only to database destinations. No discovery, provisioning or code execution occurs.\n * \n * @example\n * ```typescript\n * import { validateDeploymentPlacements } from '@alneas/eaos-server/platform/deployment-placements';\n * const placements: unknown = { destinations: [], plugins: [] };\n * validateDeploymentPlacements(placements);\n * ```\n */\nexport function validateDeploymentPlacements(value: unknown): asserts value is DeploymentPlacements {\n if(!object(value) || !Array.isArray(value.destinations) || !Array.isArray(value.plugins)) throw Error('Invalid deployment placements')\n keys(value,['destinations','plugins'])\n const destinations = new Set<string>(), plugins = new Set<string>()\n for(const d of value.destinations) {\n if(!object(d) || !id(d.id) || destinations.has(d.id) || !['host','container','function','database','cloud','native','external'].includes(d.placement) || !text(d.revision)) throw Error('Invalid or duplicate deployment destination')\n keys(d,['id','placement','service','reference','backendId','databaseId','tenantId','revision'])\n if(d.tenantId!==undefined&&(!text(d.tenantId)||d.tenantId==='*'||d.placement!=='database'))throw Error('Tenant restriction requires a concrete database destination')\n if(d.service!==undefined && !['frontend','backend','worker'].includes(d.service)) throw Error('Unknown Platform service role')\n if(d.reference!==undefined && (!text(d.reference) || !/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(d.reference))) throw Error('Use a non-secret host binding reference')\n if(d.placement==='database' ? (!text(d.backendId)||!text(d.databaseId)||d.service!==undefined||d.reference!==undefined) : (d.backendId!==undefined||d.databaseId!==undefined||!!d.service===!!d.reference)) throw Error('Destination requires an exact database or one service/resource reference')\n if(d.service && !['host','container'].includes(d.placement)) throw Error('Service destination must use host or container placement')\n destinations.add(d.id)\n }\n for(const p of value.plugins) {\n if(!object(p)||!text(p.owner)||!id(p.pluginId)||!object(p.components)||plugins.has(JSON.stringify([p.owner,p.pluginId]))) throw Error('Invalid or duplicate plugin placement')\n keys(p,['owner','pluginId','components']); plugins.add(JSON.stringify([p.owner,p.pluginId]))\n for(const [component,refs] of Object.entries(p.components)) if(!id(component)||!Array.isArray(refs)||new Set(refs).size!==refs.length||refs.some(ref=>!id(ref)||!destinations.has(ref))) throw Error('Invalid or unknown component destination')\n }\n}\n\n/**\n * Resolves each selected plugin component against explicit compatible deployment destinations.\n * \n * All selected components are explicit; [] means an optional capability was deliberately not adopted.\n * @param placements - Validated explicit selections; every component must have a destination list.\n * @param inventories - Trusted inventories with unique owner IDs and declared plugin work.\n * @returns Component selections with copied destinationIds and adopted flags; an empty list deliberately declines an optional component.\n * @throws Error for unknown inventory/plugin/component/destination, duplicate owners, missing required placement, incompatible role/kind or declined dependency.\n * \n * @remarks\n * Selecting a plugin does not register or enable it. Required components cannot be declined, and adopted work cannot depend on declined nonartifact work. A host component may target a container destination.\n * \n * @example\n * ```typescript\n * import { resolveDeploymentPlacements } from '@alneas/eaos-server/platform/deployment-placements';\n * import type { DeploymentInventory } from '@alneas/eaos-server/platform/artifacts';\n * export function resolve(placements: Parameters<typeof resolveDeploymentPlacements>[0], inventories: DeploymentInventory[]) { return resolveDeploymentPlacements(placements, inventories); }\n * ```\n */\nexport function resolveDeploymentPlacements(placements: DeploymentPlacements, inventories: DeploymentInventory[]) {\n validateDeploymentPlacements(placements)\n const owners = new Set(inventories.map(inv=>inv.owner))\n if(owners.size!==inventories.length) throw Error('Duplicate placement inventory owner')\n for(const inventory of inventories) validateDeploymentPlugins(inventory)\n return placements.plugins.flatMap(selection=>{\n const inventory = inventories.find(inv=>inv.owner===selection.owner)\n const plugin = inventory?.plugins?.find(p=>p.id===selection.pluginId)\n if(!plugin) throw Error(`Unknown or unclassified selected plugin: ${selection.owner}/${selection.pluginId}`)\n if(Object.keys(selection.components).some(c=>!plugin.components.some(known=>known.id===c))) throw Error('Unknown selected component')\n for(const task of plugin.tasks) if(task.componentIds.some(c=>selection.components[c]?.length) && task.dependsOn.some(ref=>{\n const dep=plugin.tasks.find(t=>t.id===ref)!\n return dep.scope!=='artifact'&&!dep.componentIds.some(c=>selection.components[c]?.length)\n })) throw Error('Adopted task depends on an unadopted component')\n return plugin.components.map(component=>{\n if(!Object.hasOwn(selection.components,component.id)) throw Error(`Missing component placement: ${plugin.id}/${component.id}`)\n const destinationIds = selection.components[component.id]\n if(component.required!==false && !destinationIds.length) throw Error('Required component cannot be unadopted')\n for(const ref of destinationIds) {\n const d=placements.destinations.find(d=>d.id===ref)!\n if(component.placement!==d.placement && !(component.placement==='host' && d.placement==='container')) throw Error(`Incompatible component placement: ${plugin.id}/${component.id}`)\n if(d.service && ((component.kind==='frontend' && d.service!=='frontend') || (component.kind!=='frontend' && d.service==='frontend'))) throw Error('Frontend and server components require different service roles')\n }\n return {owner:inventory!.owner,pluginId:plugin.id,componentId:component.id,destinationIds:[...destinationIds],adopted:destinationIds.length>0}\n })\n })\n}\n\n/**\n * Hashes a selected plugin's normalized component destinations and host binding revisions.\n * @param placements - Previously validated placement declaration.\n * @param owner - Inventory owner selecting the plugin.\n * @param pluginId - Selected plugin ID.\n * @param bindings - Optional nonsecret destination-ID to host-binding revision map; defaults to none.\n * @returns A lowercase SHA-256 hash bound to selected components, referenced destinations and applicable binding revisions.\n * @throws Error when the owner/plugin selection is absent or canonical serialization fails.\n * \n * @remarks\n * Order of component/destination maps is normalized. Change binding revisions when provider credentials or host wiring change so old scoped evidence becomes stale.\n * \n * @example\n * ```typescript\n * import { deploymentPlacementHash, type DeploymentPlacements } from '@alneas/eaos-server/platform/deployment-placements';\n * export function bindingDigest(placements: DeploymentPlacements, owner: string, pluginId: string) { return deploymentPlacementHash(placements, owner, pluginId); }\n * ```\n */\nexport function deploymentPlacementHash(placements: DeploymentPlacements, owner: string, pluginId: string, bindings: Record<string,string> = {}) {\n const selection=placements.plugins.find(p=>p.owner===owner&&p.pluginId===pluginId)\n if(!selection) throw Error('Plugin is not selected in deployment placements')\n const components=Object.fromEntries(Object.entries(selection.components).sort(([a],[b])=>a.localeCompare(b)).map(([key,refs])=>[key,[...refs].sort()]))\n const ids=new Set(Object.values(components).flat())\n return canonicalJsonHash(JSON.parse(JSON.stringify({owner,pluginId,components,destinations:placements.destinations.filter(d=>ids.has(d.id)).sort((a,b)=>a.id.localeCompare(b.id)),bindings:Object.fromEntries([...ids].sort().filter(id=>bindings[id]!==undefined).map(id=>[id,bindings[id]]))})))\n}\n\n/**\n * Builds a scoped task target from a reviewed destination and the current inspection context.\n * @param destination - Validated destination with optional exact database and dedicated-tenant identities.\n * @param scope - Environment work omits tenant; tenant work requires a matching concrete tenant.\n * @param context - Current environment/tenant context; omitted cannot resolve a target.\n * @returns A new target object, or undefined for missing context/tenant or dedicated-tenant mismatch.\n * \n * @remarks\n * Backend/database come from the destination, not the context. This performs no authority or resource-existence checks.\n * \n * @example\n * ```typescript\n * import { deploymentExecutionTarget } from '@alneas/eaos-server/platform/deployment-placements';\n * const target = deploymentExecutionTarget({ id: 'web', placement: 'host', service: 'frontend', revision: '1' }, 'environment', { environment: 'review' });\n * ```\n */\nexport function deploymentExecutionTarget(destination: DeploymentDestination, scope: 'environment'|'tenant', context?: DeploymentTarget): DeploymentTarget | undefined {\n if(!context || scope==='tenant'&&!context.tenantId) return undefined\n if(scope==='tenant'&&destination.tenantId&&context.tenantId!==destination.tenantId)return undefined\n return {environment:context.environment,...(destination.backendId?{backendId:destination.backendId,databaseId:destination.databaseId}:{}),...(scope==='tenant'?{tenantId:context.tenantId}:{})}\n}\n/**\n * One task instance at an explicit destination or reusable artifact scope.\n * \n * @see summarizePlacedDeployment for canonical integration usage.\n */\nexport interface DeploymentExecution {\n /**\n * Declared plugin task ID.\n */\n taskId: string\n /**\n * Scoped destination identity; absent for shared artifact work.\n */\n destinationId?: string\n /**\n * Adopted components covered at this destination.\n */\n componentIds: string[]\n /**\n * Whether this adopted execution must succeed for completion.\n */\n required: boolean\n /**\n * Dependency-aware derived task state.\n */\n status: DeploymentTaskStatus\n /**\n * Resolved environment/database/tenant evidence context, if available.\n */\n target?: DeploymentTarget\n /**\n * Latest matching reported result, if any.\n */\n result?: DeploymentResult\n /**\n * Prerequisite task/destination identities whose evidence must succeed first.\n */\n dependencies: Array<{\n /**\n * Prerequisite declared task ID.\n */\n taskId:string;\n /**\n * Prerequisite destination, absent for shared artifact work.\n */\n destinationId?:string}>\n}\nconst targetKey=(v?:DeploymentTarget)=>canonicalJsonHash([v?.environment??null,v?.backendId??null,v?.databaseId??null,v?.tenantId??null])\nconst aggregate=(values:DeploymentTaskStatus[]):DeploymentTaskStatus=>values.includes('failed')?'failed':values.includes('blocked')?'blocked':values.includes('stale')?'stale':values.includes('running')?'running':values.every(v=>v==='succeeded')?'succeeded':'pending'\n\n/**\n * Builds one placement-bound execution graph shared by plugin and destination evidence projections.\n * \n * One execution graph powers both plugin and destination projections. Shared artifact work is never duplicated.\n * @param inventory - Trusted artifact inventory with declared plugin work.\n * @param inventorySha256 - Trusted lowercase inventory digest.\n * @param receipts - Reported task evidence, including placement-bound scoped results.\n * @param placements - Explicit compatible destination selections.\n * @param context - Optional environment and tenant only; backend/database must come from destinations.\n * @param bindings - Optional destination host-binding revisions; defaults to none.\n * @returns A deployment summary with selected/adopted components, task executions, destinations, artifact executions and explicit placement coverage.\n * @throws Error for invalid placements/context/receipts, undeclared tasks, mismatched task scope or conflicting equal-time evidence.\n * \n * @remarks\n * Shared artifact work is not duplicated. Old binding hashes are ignored as stale; declined optional components do not satisfy adopted dependency work. No task executes, and reported completion does not grant runtime admission.\n * \n * @example\n * ```typescript\n * import { summarizePlacedDeployment, type DeploymentPlacements } from '@alneas/eaos-server/platform/deployment-placements';\n * import type { DeploymentInventory, DeploymentReceipt } from '@alneas/eaos-server/platform';\n * export function inspectPlaced(inventory: DeploymentInventory, digest: string, receipts: DeploymentReceipt[], placements: DeploymentPlacements) { return summarizePlacedDeployment(inventory, digest, receipts, placements, { environment: 'review' }); }\n * ```\n */\nexport function summarizePlacedDeployment(inventory: DeploymentInventory, inventorySha256: string, receipts: DeploymentReceipt[], placements: DeploymentPlacements, context?: DeploymentTarget, bindings: Record<string,string> = {}) {\n resolveDeploymentPlacements({...placements,plugins:placements.plugins.filter(p=>p.owner===inventory.owner)},[inventory])\n if(context) {validateDeploymentTarget(context);if(context.backendId||context.databaseId)throw Error('With placements, backend/database come from destinations; specify only environment and tenant')}\n for(const receipt of receipts) validateDeploymentReceipt(receipt)\n const base=summarizeDeployment(inventory,inventorySha256,receipts,context)\n const ignoredReceipts=base.ignoredReceipts.filter(r=>r.reason!=='placement-context-required')\n const plugins=base.plugins.map(plugin=>{\n const selection=placements.plugins.find(p=>p.owner===inventory.owner&&p.pluginId===plugin.id)\n if(!selection) return {...plugin,selected:false,components:plugin.components.map(c=>({...c,adopted:false,destinationIds:[] as string[]})),tasks:plugin.tasks.map(t=>({...t,adopted:false,executions:[] as DeploymentExecution[]})),complete:false}\n const binding=deploymentPlacementHash(placements,inventory.owner,plugin.id,bindings)\n const active=(componentId:string)=>selection.components[componentId].length>0\n const instances=new Map<string,DeploymentExecution[]>()\n const results=new Map<string,DeploymentResult>(), stale=new Set<string>()\n for(const receipt of receipts) {\n if(receipt.owner!==inventory.owner||receipt.inventorySha256!==inventorySha256) continue\n for(const result of receipt.results.filter(r=>r.pluginId===plugin.id)) {\n const task=plugin.tasks.find(t=>t.id===result.taskId)\n if(!task) throw Error('Receipt references an undeclared placement task')\n if(task.scope==='artifact') {if(result.destinationId||result.target)throw Error('Artifact evidence cannot select a destination');continue}\n if(!result.target || task.scope==='tenant'&&!result.target.tenantId || task.scope==='environment'&&result.target.tenantId) throw Error('Receipt target does not match placement task scope')\n if(!result.destinationId) {ignoredReceipts.push({owner:receipt.owner,reason:'unbound-placement'});continue}\n const destination=placements.destinations.find(d=>d.id===result.destinationId)\n const expected=destination?deploymentExecutionTarget(destination,task.scope,context):undefined\n const key=JSON.stringify([task.id,result.destinationId])\n if(result.placementSha256!==binding || !destination || !task.componentIds.some(c=>selection.components[c].includes(result.destinationId!))) {\n ignoredReceipts.push({owner:receipt.owner,reason:'stale-placement'})\n if(context?.environment===result.target.environment && (task.scope==='environment'||context.tenantId===result.target.tenantId)) stale.add(key)\n continue\n }\n if(!expected || targetKey(expected)!==targetKey(result.target)) continue\n const old=results.get(key)\n if(old?.recordedAt===result.recordedAt && JSON.stringify(old)!==JSON.stringify(result)) throw Error('Conflicting placement results at the same timestamp')\n if(!old||old.recordedAt<result.recordedAt)results.set(key,result)\n }\n }\n const compile=(taskId:string):DeploymentExecution[]=>{\n const known=instances.get(taskId);if(known)return known\n const task=plugin.tasks.find(t=>t.id===taskId)!\n const dependencies=task.dependsOn.flatMap(compile)\n const adopted=task.componentIds.some(active)\n const required=task.required||task.componentIds.some(id=>active(id)&&plugin.components.find(c=>c.id===id)?.required===false)\n const refs=task.scope==='artifact'?[undefined]:[...new Set(task.componentIds.flatMap(c=>selection.components[c]))]\n const rows:DeploymentExecution[]=refs.map(destinationId=>{\n const destination=destinationId?placements.destinations.find(d=>d.id===destinationId)!:undefined\n const key=JSON.stringify([task.id,destinationId])\n const result=task.scope==='artifact'?task.result:results.get(key)\n const blocked=dependencies.some(d=>d.status!=='succeeded')\n const outdated=result?.status==='succeeded'&&dependencies.some(d=>d.result&&d.result.recordedAt>result.recordedAt)\n const status:DeploymentTaskStatus=blocked?'blocked':outdated||!result&&stale.has(key)?'stale':result?.status??'pending'\n return {taskId:task.id,destinationId,componentIds:task.componentIds.filter(c=>!destinationId||selection.components[c].includes(destinationId)),required:required&&(adopted||task.scope==='artifact'),status,result,target:destination?deploymentExecutionTarget(destination,task.scope as 'environment'|'tenant',context):undefined,dependencies:dependencies.map(d=>({taskId:d.taskId,destinationId:d.destinationId}))}\n })\n instances.set(taskId,rows);return rows\n }\n const tasks=plugin.tasks.map(task=>{const executions=compile(task.id);return {...task,adopted:task.scope==='artifact'||task.componentIds.some(active),required:executions.some(e=>e.required),status:executions.length?aggregate(executions.map(e=>e.status)):'pending' as DeploymentTaskStatus,executions}})\n return {...plugin,selected:true,placementSha256:binding,tasks,complete:plugin.artifactResolved&&tasks.filter(t=>t.adopted&&t.required).every(t=>t.status==='succeeded'),components:plugin.components.map(c=>({...c,adopted:active(c.id),destinationIds:selection.components[c.id],complete:active(c.id)&&plugin.artifactResolved&&tasks.filter(t=>t.componentIds.includes(c.id)&&t.adopted&&t.required).every(t=>t.status==='succeeded')}))}\n })\n const executions=plugins.flatMap(p=>p.tasks.flatMap(t=>t.executions.map(e=>({...e,pluginId:p.id,owner:inventory.owner}))))\n const destinations=placements.destinations.map(destination=>({...destination,executions:executions.filter(e=>e.destinationId===destination.id)}))\n return {...base,plugins,complete:base.artifactResolved&&plugins.filter(p=>p.selected).every(p=>p.complete),ignoredReceipts:[...new Map(ignoredReceipts.map(r=>[JSON.stringify([r.owner,r.reason]),r])).values()],placementCoverage:'explicit' as const,destinations,artifactExecutions:executions.filter(e=>!e.destinationId)}\n}\n"],"mappings":"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","names":["d","id","executions"]}
|