@mastra/factory 0.0.0-esbuild-bundle-worker-20260807173433
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 +1008 -0
- package/LICENSE.md +30 -0
- package/README.md +27 -0
- package/dist/auth.d.ts +183 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +507 -0
- package/dist/auth.js.map +1 -0
- package/dist/capabilities/connection.d.ts +9 -0
- package/dist/capabilities/connection.d.ts.map +1 -0
- package/dist/capabilities/connection.js +1 -0
- package/dist/capabilities/intake.d.ts +141 -0
- package/dist/capabilities/intake.d.ts.map +1 -0
- package/dist/capabilities/intake.js +1 -0
- package/dist/capabilities/version-control.d.ts +247 -0
- package/dist/capabilities/version-control.d.ts.map +1 -0
- package/dist/capabilities/version-control.js +1 -0
- package/dist/factory.d.ts +159 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +491 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/integrations/base.d.ts +254 -0
- package/dist/integrations/base.d.ts.map +1 -0
- package/dist/integrations/base.js +1 -0
- package/dist/integrations/github/config.d.ts +60 -0
- package/dist/integrations/github/config.d.ts.map +1 -0
- package/dist/integrations/github/config.js +42 -0
- package/dist/integrations/github/config.js.map +1 -0
- package/dist/integrations/github/integration.d.ts +220 -0
- package/dist/integrations/github/integration.d.ts.map +1 -0
- package/dist/integrations/github/integration.js +1162 -0
- package/dist/integrations/github/integration.js.map +1 -0
- package/dist/integrations/github/issue-triage.d.ts +27 -0
- package/dist/integrations/github/issue-triage.d.ts.map +1 -0
- package/dist/integrations/github/issue-triage.js +82 -0
- package/dist/integrations/github/issue-triage.js.map +1 -0
- package/dist/integrations/github/pat.d.ts +38 -0
- package/dist/integrations/github/pat.d.ts.map +1 -0
- package/dist/integrations/github/pat.js +59 -0
- package/dist/integrations/github/pat.js.map +1 -0
- package/dist/integrations/github/provenance.d.ts +27 -0
- package/dist/integrations/github/provenance.d.ts.map +1 -0
- package/dist/integrations/github/provenance.js +75 -0
- package/dist/integrations/github/provenance.js.map +1 -0
- package/dist/integrations/github/reconcile-worker.d.ts +41 -0
- package/dist/integrations/github/reconcile-worker.d.ts.map +1 -0
- package/dist/integrations/github/reconcile-worker.js +125 -0
- package/dist/integrations/github/reconcile-worker.js.map +1 -0
- package/dist/integrations/github/routes.d.ts +69 -0
- package/dist/integrations/github/routes.d.ts.map +1 -0
- package/dist/integrations/github/routes.js +1335 -0
- package/dist/integrations/github/routes.js.map +1 -0
- package/dist/integrations/github/rules.d.ts +105 -0
- package/dist/integrations/github/rules.d.ts.map +1 -0
- package/dist/integrations/github/rules.js +551 -0
- package/dist/integrations/github/rules.js.map +1 -0
- package/dist/integrations/github/sandbox-release.d.ts +53 -0
- package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
- package/dist/integrations/github/sandbox-release.js +115 -0
- package/dist/integrations/github/sandbox-release.js.map +1 -0
- package/dist/integrations/github/sandbox.d.ts +281 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -0
- package/dist/integrations/github/sandbox.js +547 -0
- package/dist/integrations/github/sandbox.js.map +1 -0
- package/dist/integrations/github/session-subscriptions.d.ts +26 -0
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -0
- package/dist/integrations/github/session-subscriptions.js +192 -0
- package/dist/integrations/github/session-subscriptions.js.map +1 -0
- package/dist/integrations/github/subscriptions.d.ts +54 -0
- package/dist/integrations/github/subscriptions.d.ts.map +1 -0
- package/dist/integrations/github/subscriptions.js +62 -0
- package/dist/integrations/github/subscriptions.js.map +1 -0
- package/dist/integrations/github/token-refresh.d.ts +12 -0
- package/dist/integrations/github/token-refresh.d.ts.map +1 -0
- package/dist/integrations/github/token-refresh.js +24 -0
- package/dist/integrations/github/token-refresh.js.map +1 -0
- package/dist/integrations/github/webhook.d.ts +107 -0
- package/dist/integrations/github/webhook.d.ts.map +1 -0
- package/dist/integrations/github/webhook.js +371 -0
- package/dist/integrations/github/webhook.js.map +1 -0
- package/dist/integrations/linear/agent-tools.d.ts +34 -0
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -0
- package/dist/integrations/linear/agent-tools.js +85 -0
- package/dist/integrations/linear/agent-tools.js.map +1 -0
- package/dist/integrations/linear/integration.d.ts +235 -0
- package/dist/integrations/linear/integration.d.ts.map +1 -0
- package/dist/integrations/linear/integration.js +676 -0
- package/dist/integrations/linear/integration.js.map +1 -0
- package/dist/integrations/linear/routes.d.ts +61 -0
- package/dist/integrations/linear/routes.d.ts.map +1 -0
- package/dist/integrations/linear/routes.js +240 -0
- package/dist/integrations/linear/routes.js.map +1 -0
- package/dist/integrations/linear/rules.d.ts +44 -0
- package/dist/integrations/linear/rules.d.ts.map +1 -0
- package/dist/integrations/linear/rules.js +133 -0
- package/dist/integrations/linear/rules.js.map +1 -0
- package/dist/integrations/linear/storage.d.ts +65 -0
- package/dist/integrations/linear/storage.d.ts.map +1 -0
- package/dist/integrations/linear/storage.js +1 -0
- package/dist/integrations/platform/api-client.d.ts +26 -0
- package/dist/integrations/platform/api-client.d.ts.map +1 -0
- package/dist/integrations/platform/api-client.js +156 -0
- package/dist/integrations/platform/api-client.js.map +1 -0
- package/dist/integrations/platform/github/event-worker.d.ts +44 -0
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
- package/dist/integrations/platform/github/event-worker.js +349 -0
- package/dist/integrations/platform/github/event-worker.js.map +1 -0
- package/dist/integrations/platform/github/integration.d.ts +64 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -0
- package/dist/integrations/platform/github/integration.js +1006 -0
- package/dist/integrations/platform/github/integration.js.map +1 -0
- package/dist/integrations/platform/linear/integration.d.ts +46 -0
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -0
- package/dist/integrations/platform/linear/integration.js +483 -0
- package/dist/integrations/platform/linear/integration.js.map +1 -0
- package/dist/integrations/slack/connect-route.d.ts +46 -0
- package/dist/integrations/slack/connect-route.d.ts.map +1 -0
- package/dist/integrations/slack/connect-route.js +186 -0
- package/dist/integrations/slack/connect-route.js.map +1 -0
- package/dist/integrations/slack/integration.d.ts +62 -0
- package/dist/integrations/slack/integration.d.ts.map +1 -0
- package/dist/integrations/slack/integration.js +68 -0
- package/dist/integrations/slack/integration.js.map +1 -0
- package/dist/integrations/slack/slack.d.ts +200 -0
- package/dist/integrations/slack/slack.d.ts.map +1 -0
- package/dist/integrations/slack/slack.js +411 -0
- package/dist/integrations/slack/slack.js.map +1 -0
- package/dist/integrations/workos/integration.d.ts +39 -0
- package/dist/integrations/workos/integration.d.ts.map +1 -0
- package/dist/integrations/workos/integration.js +91 -0
- package/dist/integrations/workos/integration.js.map +1 -0
- package/dist/routes/config.d.ts +235 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +972 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/custom-provider-source.d.ts +52 -0
- package/dist/routes/custom-provider-source.d.ts.map +1 -0
- package/dist/routes/custom-provider-source.js +124 -0
- package/dist/routes/custom-provider-source.js.map +1 -0
- package/dist/routes/fs.d.ts +151 -0
- package/dist/routes/fs.d.ts.map +1 -0
- package/dist/routes/fs.js +708 -0
- package/dist/routes/fs.js.map +1 -0
- package/dist/routes/intake.d.ts +23 -0
- package/dist/routes/intake.d.ts.map +1 -0
- package/dist/routes/intake.js +179 -0
- package/dist/routes/intake.js.map +1 -0
- package/dist/routes/oauth.d.ts +43 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +463 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/projects.d.ts +19 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +461 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/provider-credentials.d.ts +81 -0
- package/dist/routes/provider-credentials.d.ts.map +1 -0
- package/dist/routes/provider-credentials.js +86 -0
- package/dist/routes/provider-credentials.js.map +1 -0
- package/dist/routes/route.d.ts +48 -0
- package/dist/routes/route.d.ts.map +1 -0
- package/dist/routes/route.js +15 -0
- package/dist/routes/route.js.map +1 -0
- package/dist/routes/skills.d.ts +30 -0
- package/dist/routes/skills.d.ts.map +1 -0
- package/dist/routes/skills.js +168 -0
- package/dist/routes/skills.js.map +1 -0
- package/dist/routes/surface.d.ts +107 -0
- package/dist/routes/surface.d.ts.map +1 -0
- package/dist/routes/surface.js +297 -0
- package/dist/routes/surface.js.map +1 -0
- package/dist/routes/tenant-credentials.d.ts +74 -0
- package/dist/routes/tenant-credentials.d.ts.map +1 -0
- package/dist/routes/tenant-credentials.js +156 -0
- package/dist/routes/tenant-credentials.js.map +1 -0
- package/dist/routes/work-items.d.ts +40 -0
- package/dist/routes/work-items.d.ts.map +1 -0
- package/dist/routes/work-items.js +695 -0
- package/dist/routes/work-items.js.map +1 -0
- package/dist/rules/binding-context.d.ts +5 -0
- package/dist/rules/binding-context.d.ts.map +1 -0
- package/dist/rules/binding-context.js +28 -0
- package/dist/rules/binding-context.js.map +1 -0
- package/dist/rules/defaults.d.ts +9 -0
- package/dist/rules/defaults.d.ts.map +1 -0
- package/dist/rules/defaults.js +322 -0
- package/dist/rules/defaults.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +43 -0
- package/dist/rules/dispatcher.d.ts.map +1 -0
- package/dist/rules/dispatcher.js +547 -0
- package/dist/rules/dispatcher.js.map +1 -0
- package/dist/rules/index.d.ts +7 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +5 -0
- package/dist/rules/processor.d.ts +54 -0
- package/dist/rules/processor.d.ts.map +1 -0
- package/dist/rules/processor.js +378 -0
- package/dist/rules/processor.js.map +1 -0
- package/dist/rules/resolve.d.ts +17 -0
- package/dist/rules/resolve.d.ts.map +1 -0
- package/dist/rules/resolve.js +30 -0
- package/dist/rules/resolve.js.map +1 -0
- package/dist/rules/start-coordinator.d.ts +60 -0
- package/dist/rules/start-coordinator.d.ts.map +1 -0
- package/dist/rules/start-coordinator.js +175 -0
- package/dist/rules/start-coordinator.js.map +1 -0
- package/dist/rules/tools.d.ts +10 -0
- package/dist/rules/tools.d.ts.map +1 -0
- package/dist/rules/tools.js +59 -0
- package/dist/rules/tools.js.map +1 -0
- package/dist/rules/transition-service.d.ts +49 -0
- package/dist/rules/transition-service.d.ts.map +1 -0
- package/dist/rules/transition-service.js +232 -0
- package/dist/rules/transition-service.js.map +1 -0
- package/dist/rules/types.d.ts +261 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +43 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/validation.d.ts +11 -0
- package/dist/rules/validation.d.ts.map +1 -0
- package/dist/rules/validation.js +308 -0
- package/dist/rules/validation.js.map +1 -0
- package/dist/sandbox/fleet.d.ts +222 -0
- package/dist/sandbox/fleet.d.ts.map +1 -0
- package/dist/sandbox/fleet.js +322 -0
- package/dist/sandbox/fleet.js.map +1 -0
- package/dist/sandbox/reattach.d.ts +3 -0
- package/dist/sandbox/reattach.d.ts.map +1 -0
- package/dist/sandbox/reattach.js +16 -0
- package/dist/sandbox/reattach.js.map +1 -0
- package/dist/server-error.d.ts +12 -0
- package/dist/server-error.d.ts.map +1 -0
- package/dist/server-error.js +16 -0
- package/dist/server-error.js.map +1 -0
- package/dist/session/factory-session.d.ts +116 -0
- package/dist/session/factory-session.d.ts.map +1 -0
- package/dist/session/factory-session.js +162 -0
- package/dist/session/factory-session.js.map +1 -0
- package/dist/skills/service.d.ts +48 -0
- package/dist/skills/service.d.ts.map +1 -0
- package/dist/skills/service.js +40 -0
- package/dist/skills/service.js.map +1 -0
- package/dist/spa-static.d.ts +33 -0
- package/dist/spa-static.d.ts.map +1 -0
- package/dist/spa-static.js +104 -0
- package/dist/spa-static.js.map +1 -0
- package/dist/state-signing.d.ts +59 -0
- package/dist/state-signing.d.ts.map +1 -0
- package/dist/state-signing.js +83 -0
- package/dist/state-signing.js.map +1 -0
- package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
- package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
- package/dist/storage/domains/audit/agent-audit.js +66 -0
- package/dist/storage/domains/audit/agent-audit.js.map +1 -0
- package/dist/storage/domains/audit/base.d.ts +140 -0
- package/dist/storage/domains/audit/base.d.ts.map +1 -0
- package/dist/storage/domains/audit/base.js +184 -0
- package/dist/storage/domains/audit/base.js.map +1 -0
- package/dist/storage/domains/audit/domain.d.ts +76 -0
- package/dist/storage/domains/audit/domain.d.ts.map +1 -0
- package/dist/storage/domains/audit/domain.js +269 -0
- package/dist/storage/domains/audit/domain.js.map +1 -0
- package/dist/storage/domains/channel-identity/base.d.ts +96 -0
- package/dist/storage/domains/channel-identity/base.d.ts.map +1 -0
- package/dist/storage/domains/channel-identity/base.js +149 -0
- package/dist/storage/domains/channel-identity/base.js.map +1 -0
- package/dist/storage/domains/credentials/base.d.ts +134 -0
- package/dist/storage/domains/credentials/base.d.ts.map +1 -0
- package/dist/storage/domains/credentials/base.js +273 -0
- package/dist/storage/domains/credentials/base.js.map +1 -0
- package/dist/storage/domains/custom-providers/base.d.ts +57 -0
- package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
- package/dist/storage/domains/custom-providers/base.js +145 -0
- package/dist/storage/domains/custom-providers/base.js.map +1 -0
- package/dist/storage/domains/intake/base.d.ts +38 -0
- package/dist/storage/domains/intake/base.d.ts.map +1 -0
- package/dist/storage/domains/intake/base.js +80 -0
- package/dist/storage/domains/intake/base.js.map +1 -0
- package/dist/storage/domains/integrations/base.d.ts +126 -0
- package/dist/storage/domains/integrations/base.d.ts.map +1 -0
- package/dist/storage/domains/integrations/base.js +317 -0
- package/dist/storage/domains/integrations/base.js.map +1 -0
- package/dist/storage/domains/memory-settings/base.d.ts +62 -0
- package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
- package/dist/storage/domains/memory-settings/base.js +126 -0
- package/dist/storage/domains/memory-settings/base.js.map +1 -0
- package/dist/storage/domains/model-packs/base.d.ts +49 -0
- package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
- package/dist/storage/domains/model-packs/base.js +96 -0
- package/dist/storage/domains/model-packs/base.js.map +1 -0
- package/dist/storage/domains/projects/base.d.ts +58 -0
- package/dist/storage/domains/projects/base.d.ts.map +1 -0
- package/dist/storage/domains/projects/base.js +111 -0
- package/dist/storage/domains/projects/base.js.map +1 -0
- package/dist/storage/domains/queue-health/base.d.ts +52 -0
- package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
- package/dist/storage/domains/queue-health/base.js +122 -0
- package/dist/storage/domains/queue-health/base.js.map +1 -0
- package/dist/storage/domains/source-control/base.d.ts +360 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -0
- package/dist/storage/domains/source-control/base.js +914 -0
- package/dist/storage/domains/source-control/base.js.map +1 -0
- package/dist/storage/domains/source-control/inmemory.d.ts +175 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
- package/dist/storage/domains/source-control/inmemory.js +380 -0
- package/dist/storage/domains/source-control/inmemory.js.map +1 -0
- package/dist/storage/domains/work-items/base.d.ts +412 -0
- package/dist/storage/domains/work-items/base.d.ts.map +1 -0
- package/dist/storage/domains/work-items/base.js +1320 -0
- package/dist/storage/domains/work-items/base.js.map +1 -0
- package/dist/storage/domains/work-items/metrics.d.ts +101 -0
- package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
- package/dist/storage/domains/work-items/metrics.js +221 -0
- package/dist/storage/domains/work-items/metrics.js.map +1 -0
- package/dist/timing.d.ts +15 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +34 -0
- package/dist/timing.js.map +1 -0
- package/dist/workspace.d.ts +24 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +287 -0
- package/dist/workspace.js.map +1 -0
- package/factory-skills/configure-factory-rules/SKILL.md +67 -0
- package/factory-skills/factory-plan/SKILL.md +61 -0
- package/factory-skills/factory-review/SKILL.md +157 -0
- package/factory-skills/factory-triage/SKILL.md +99 -0
- package/package.json +83 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { dirname, extname, join, normalize, resolve, sep } from "path";
|
|
2
|
+
import { readFile, stat } from "fs/promises";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
//#region src/spa-static.ts
|
|
6
|
+
/**
|
|
7
|
+
* Same-origin SPA serving for production.
|
|
8
|
+
*
|
|
9
|
+
* In dev the SPA is served by Vite (:5173) which proxies API paths to the
|
|
10
|
+
* server (:4111) — this module is effectively idle there. In production the
|
|
11
|
+
* built SPA (vite output) is served by the API server itself at `/`, so the
|
|
12
|
+
* app is a single origin and no CORS / separate static host is required.
|
|
13
|
+
*
|
|
14
|
+
* The middleware is mounted via the Mastra entry's `server.middleware`, which
|
|
15
|
+
* the deployer applies before routes: real file hits and SPA navigations are
|
|
16
|
+
* answered here, everything else falls through to the API routes.
|
|
17
|
+
*/
|
|
18
|
+
const MIME = {
|
|
19
|
+
".html": "text/html; charset=utf-8",
|
|
20
|
+
".js": "text/javascript; charset=utf-8",
|
|
21
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
22
|
+
".css": "text/css; charset=utf-8",
|
|
23
|
+
".json": "application/json",
|
|
24
|
+
".webmanifest": "application/manifest+json",
|
|
25
|
+
".map": "application/json",
|
|
26
|
+
".svg": "image/svg+xml",
|
|
27
|
+
".png": "image/png",
|
|
28
|
+
".jpg": "image/jpeg",
|
|
29
|
+
".jpeg": "image/jpeg",
|
|
30
|
+
".gif": "image/gif",
|
|
31
|
+
".webp": "image/webp",
|
|
32
|
+
".ico": "image/x-icon",
|
|
33
|
+
".txt": "text/plain; charset=utf-8",
|
|
34
|
+
".woff": "font/woff",
|
|
35
|
+
".woff2": "font/woff2",
|
|
36
|
+
".ttf": "font/ttf",
|
|
37
|
+
".wasm": "application/wasm"
|
|
38
|
+
};
|
|
39
|
+
/** Server-owned path prefixes the SPA middleware must never answer for. */
|
|
40
|
+
const SERVER_PREFIXES = [
|
|
41
|
+
"/api",
|
|
42
|
+
"/web",
|
|
43
|
+
"/auth",
|
|
44
|
+
"/connect"
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* Locate the built SPA (a dir containing `index.html`). Checked in order:
|
|
48
|
+
* 1. `MASTRACODE_UI_DIST` — explicit override for custom layouts.
|
|
49
|
+
* 2. `factory/` under cwd — both `mastra dev` (cwd is the Mastra public dir)
|
|
50
|
+
* and `mastra start` (cwd is `.mastra/output`) expose assets this way.
|
|
51
|
+
* 3. `factory/` next to the bundled server module.
|
|
52
|
+
* 4. Source-layout fallbacks under `MASTRA_PROJECT_ROOT` or cwd.
|
|
53
|
+
* Returns `undefined` when no build is found (e.g. plain `mastra dev` without
|
|
54
|
+
* a prior vite build), in which case the middleware is simply not mounted.
|
|
55
|
+
*/
|
|
56
|
+
function resolveUiDistDir() {
|
|
57
|
+
const projectRoot = process.env.MASTRA_PROJECT_ROOT?.trim();
|
|
58
|
+
const candidates = [
|
|
59
|
+
process.env.MASTRACODE_UI_DIST,
|
|
60
|
+
resolve(process.cwd(), "factory"),
|
|
61
|
+
join(dirname(fileURLToPath(import.meta.url)), "factory"),
|
|
62
|
+
projectRoot && resolve(projectRoot, "src/mastra/public/factory"),
|
|
63
|
+
resolve(process.cwd(), "src/mastra/public/factory")
|
|
64
|
+
];
|
|
65
|
+
for (const candidate of candidates) if (candidate && existsSync(join(candidate, "index.html"))) return resolve(candidate);
|
|
66
|
+
}
|
|
67
|
+
async function isFile(path) {
|
|
68
|
+
try {
|
|
69
|
+
return (await stat(path)).isFile();
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function serveFile(c, filePath, immutable) {
|
|
75
|
+
const data = await readFile(filePath);
|
|
76
|
+
c.header("Content-Type", MIME[extname(filePath).toLowerCase()] ?? "application/octet-stream");
|
|
77
|
+
c.header("Cache-Control", immutable ? "public, max-age=31536000, immutable" : "no-cache");
|
|
78
|
+
return c.body(new Uint8Array(data));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Hono middleware serving the built SPA from `uiDist`:
|
|
82
|
+
* - exact file hits (js/css/assets) are served directly,
|
|
83
|
+
* - GET navigations to non-server paths fall back to `index.html` (SPA
|
|
84
|
+
* client-side routing),
|
|
85
|
+
* - `/api`, `/web`, `/auth` and non-GET requests always pass through.
|
|
86
|
+
*/
|
|
87
|
+
function createSpaStaticMiddleware(uiDist) {
|
|
88
|
+
return async (c, next) => {
|
|
89
|
+
if (c.req.method !== "GET" && c.req.method !== "HEAD") return next();
|
|
90
|
+
const path = c.req.path;
|
|
91
|
+
if (SERVER_PREFIXES.some((prefix) => path === prefix || path.startsWith(`${prefix}/`))) return next();
|
|
92
|
+
const relative = normalize(decodeURIComponent(path)).replace(/^[/\\]+/, "");
|
|
93
|
+
const filePath = resolve(uiDist, relative);
|
|
94
|
+
const uiDistPrefix = uiDist.endsWith(sep) ? uiDist : uiDist + sep;
|
|
95
|
+
if (filePath.startsWith(uiDistPrefix) && relative !== "" && await isFile(filePath)) return serveFile(c, filePath, relative.startsWith("assets/"));
|
|
96
|
+
const accept = c.req.header("Accept") ?? "";
|
|
97
|
+
if (path === "/" || accept.includes("text/html")) return serveFile(c, join(uiDist, "index.html"), false);
|
|
98
|
+
return next();
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { createSpaStaticMiddleware, resolveUiDistDir };
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=spa-static.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spa-static.js","names":[],"sources":["../src/spa-static.ts"],"sourcesContent":["/**\n * Same-origin SPA serving for production.\n *\n * In dev the SPA is served by Vite (:5173) which proxies API paths to the\n * server (:4111) — this module is effectively idle there. In production the\n * built SPA (vite output) is served by the API server itself at `/`, so the\n * app is a single origin and no CORS / separate static host is required.\n *\n * The middleware is mounted via the Mastra entry's `server.middleware`, which\n * the deployer applies before routes: real file hits and SPA navigations are\n * answered here, everything else falls through to the API routes.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, stat } from 'node:fs/promises';\nimport { dirname, extname, join, normalize, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { Context } from 'hono';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json',\n '.webmanifest': 'application/manifest+json',\n '.map': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n '.txt': 'text/plain; charset=utf-8',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.wasm': 'application/wasm',\n};\n\n/** Server-owned path prefixes the SPA middleware must never answer for. */\nconst SERVER_PREFIXES = ['/api', '/web', '/auth', '/connect'];\n\n/**\n * Locate the built SPA (a dir containing `index.html`). Checked in order:\n * 1. `MASTRACODE_UI_DIST` — explicit override for custom layouts.\n * 2. `factory/` under cwd — both `mastra dev` (cwd is the Mastra public dir)\n * and `mastra start` (cwd is `.mastra/output`) expose assets this way.\n * 3. `factory/` next to the bundled server module.\n * 4. Source-layout fallbacks under `MASTRA_PROJECT_ROOT` or cwd.\n * Returns `undefined` when no build is found (e.g. plain `mastra dev` without\n * a prior vite build), in which case the middleware is simply not mounted.\n */\nexport function resolveUiDistDir(): string | undefined {\n const projectRoot = process.env.MASTRA_PROJECT_ROOT?.trim();\n const candidates = [\n process.env.MASTRACODE_UI_DIST,\n resolve(process.cwd(), 'factory'),\n join(dirname(fileURLToPath(import.meta.url)), 'factory'),\n projectRoot && resolve(projectRoot, 'src/mastra/public/factory'),\n resolve(process.cwd(), 'src/mastra/public/factory'),\n ];\n for (const candidate of candidates) {\n if (candidate && existsSync(join(candidate, 'index.html'))) return resolve(candidate);\n }\n return undefined;\n}\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch {\n return false;\n }\n}\n\nasync function serveFile(c: Context, filePath: string, immutable: boolean): Promise<Response> {\n const data = await readFile(filePath);\n c.header('Content-Type', MIME[extname(filePath).toLowerCase()] ?? 'application/octet-stream');\n // Vite emits content-hashed filenames under assets/ — cache those forever;\n // index.html (and anything unhashed) must revalidate so deploys roll out.\n c.header('Cache-Control', immutable ? 'public, max-age=31536000, immutable' : 'no-cache');\n return c.body(new Uint8Array(data));\n}\n\n/**\n * Hono middleware serving the built SPA from `uiDist`:\n * - exact file hits (js/css/assets) are served directly,\n * - GET navigations to non-server paths fall back to `index.html` (SPA\n * client-side routing),\n * - `/api`, `/web`, `/auth` and non-GET requests always pass through.\n */\nexport function createSpaStaticMiddleware(uiDist: string) {\n return async (c: Context, next: () => Promise<void>): Promise<Response | void> => {\n if (c.req.method !== 'GET' && c.req.method !== 'HEAD') return next();\n const path = c.req.path;\n if (SERVER_PREFIXES.some(prefix => path === prefix || path.startsWith(`${prefix}/`))) return next();\n\n // Resolve the request path inside uiDist, rejecting traversal escapes.\n const relative = normalize(decodeURIComponent(path)).replace(/^[/\\\\]+/, '');\n const filePath = resolve(uiDist, relative);\n // Use a trailing separator so a sibling like \"ui.key\" can't slip past the\n // prefix check (same pattern as fs-routes.ts isWithinRoot).\n const uiDistPrefix = uiDist.endsWith(sep) ? uiDist : uiDist + sep;\n if (filePath.startsWith(uiDistPrefix) && relative !== '' && (await isFile(filePath))) {\n return serveFile(c, filePath, relative.startsWith('assets/'));\n }\n\n // SPA fallback: serve index.html for root and html navigations.\n const accept = c.req.header('Accept') ?? '';\n if (path === '/' || accept.includes('text/html')) {\n return serveFile(c, join(uiDist, 'index.html'), false);\n }\n\n return next();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAM,OAA+B;CACnC,SAAS;CACT,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,gBAAgB;CAChB,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,UAAU;CACV,QAAQ;CACR,SAAS;AACX;;AAGA,MAAM,kBAAkB;CAAC;CAAQ;CAAQ;CAAS;AAAU;;;;;;;;;;;AAY5D,SAAgB,mBAAuC;CACrD,MAAM,cAAc,QAAQ,IAAI,qBAAqB,KAAK;CAC1D,MAAM,aAAa;EACjB,QAAQ,IAAI;EACZ,QAAQ,QAAQ,IAAI,GAAG,SAAS;EAChC,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC,GAAG,SAAS;EACvD,eAAe,QAAQ,aAAa,2BAA2B;EAC/D,QAAQ,QAAQ,IAAI,GAAG,2BAA2B;CACpD;CACA,KAAK,MAAM,aAAa,YACtB,IAAI,aAAa,WAAW,KAAK,WAAW,YAAY,CAAC,GAAG,OAAO,QAAQ,SAAS;AAGxF;AAEA,eAAe,OAAO,MAAgC;CACpD,IAAI;EACF,QAAQ,MAAM,KAAK,IAAI,EAAA,CAAG,OAAO;CACnC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,UAAU,GAAY,UAAkB,WAAuC;CAC5F,MAAM,OAAO,MAAM,SAAS,QAAQ;CACpC,EAAE,OAAO,gBAAgB,KAAK,QAAQ,QAAQ,CAAC,CAAC,YAAY,MAAM,0BAA0B;CAG5F,EAAE,OAAO,iBAAiB,YAAY,wCAAwC,UAAU;CACxF,OAAO,EAAE,KAAK,IAAI,WAAW,IAAI,CAAC;AACpC;;;;;;;;AASA,SAAgB,0BAA0B,QAAgB;CACxD,OAAO,OAAO,GAAY,SAAwD;EAChF,IAAI,EAAE,IAAI,WAAW,SAAS,EAAE,IAAI,WAAW,QAAQ,OAAO,KAAK;EACnE,MAAM,OAAO,EAAE,IAAI;EACnB,IAAI,gBAAgB,MAAK,WAAU,SAAS,UAAU,KAAK,WAAW,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,KAAK;EAGlG,MAAM,WAAW,UAAU,mBAAmB,IAAI,CAAC,CAAC,CAAC,QAAQ,WAAW,EAAE;EAC1E,MAAM,WAAW,QAAQ,QAAQ,QAAQ;EAGzC,MAAM,eAAe,OAAO,SAAS,GAAG,IAAI,SAAS,SAAS;EAC9D,IAAI,SAAS,WAAW,YAAY,KAAK,aAAa,MAAO,MAAM,OAAO,QAAQ,GAChF,OAAO,UAAU,GAAG,UAAU,SAAS,WAAW,SAAS,CAAC;EAI9D,MAAM,SAAS,EAAE,IAAI,OAAO,QAAQ,KAAK;EACzC,IAAI,SAAS,OAAO,OAAO,SAAS,WAAW,GAC7C,OAAO,UAAU,GAAG,KAAK,QAAQ,YAAY,GAAG,KAAK;EAGvD,OAAO,KAAK;CACd;AACF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared OAuth/install `state` signing for web integrations.
|
|
3
|
+
*
|
|
4
|
+
* The GitHub, Linear, and Slack OAuth/OIDC flows each round-trip a signed `state`
|
|
5
|
+
* value
|
|
6
|
+
* through the third party to bind the callback to the `(orgId, userId)` tenant
|
|
7
|
+
* that initiated it (CSRF protection + tenant routing). The signer is a system
|
|
8
|
+
* facility: `MastraFactory` creates ONE signer at boot and hands it to every
|
|
9
|
+
* registered integration through `IntegrationContext` (see
|
|
10
|
+
* `./factory-integration.ts`), so all integrations sign and verify with the
|
|
11
|
+
* same secret.
|
|
12
|
+
*
|
|
13
|
+
* Secret resolution happens in the factory, not here: explicit
|
|
14
|
+
* `config.stateSecret` → the GitHub integration's webhook secret → a
|
|
15
|
+
* per-process random secret. A random secret is NOT stable across replicas —
|
|
16
|
+
* a `state` signed by one replica cannot be verified by another — which is
|
|
17
|
+
* what the `stable` flag reports. The factory fails loud at boot when a
|
|
18
|
+
* registered integration requires a stable signer but only a random one is
|
|
19
|
+
* available.
|
|
20
|
+
*
|
|
21
|
+
* The wire format (base64url JSON payload + `.` + HMAC-SHA256 base64url
|
|
22
|
+
* signature) is unchanged from the previous `github/config.ts` implementation
|
|
23
|
+
* so in-flight OAuth states survive a deploy.
|
|
24
|
+
*/
|
|
25
|
+
/** Verified tenant and optional Factory context carried by a signed `state`. */
|
|
26
|
+
export interface StateTenant {
|
|
27
|
+
orgId: string;
|
|
28
|
+
userId: string;
|
|
29
|
+
/** Factory that initiated the integration flow, when the caller supplied one. */
|
|
30
|
+
factoryProjectId?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Per-`state` random value. A signed `state` stays valid for its whole
|
|
33
|
+
* lifetime, so a flow that must not run twice off one `state` (account
|
|
34
|
+
* binding, for instance) can key single-use bookkeeping on this.
|
|
35
|
+
*/
|
|
36
|
+
nonce: string;
|
|
37
|
+
}
|
|
38
|
+
/** Signs and verifies OAuth `state` values bound to a tenant and optional Factory. */
|
|
39
|
+
export interface StateSigner {
|
|
40
|
+
/** Build a signed `state` bound to the tenant and optional initiating Factory. */
|
|
41
|
+
sign(orgId: string, userId: string, context?: {
|
|
42
|
+
factoryProjectId?: string;
|
|
43
|
+
}): string;
|
|
44
|
+
/** Verify a signed `state`; returns the bound tenant, or `null` if invalid. */
|
|
45
|
+
verify(state: string | undefined): StateTenant | null;
|
|
46
|
+
/**
|
|
47
|
+
* True when the signer was built from an explicit deployment-stable secret.
|
|
48
|
+
* False means a per-process random secret: fine for single-process/local
|
|
49
|
+
* dev, broken for multi-replica deploys (see module docs).
|
|
50
|
+
*/
|
|
51
|
+
readonly stable: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a state signer. With a `secret`, the signer is deployment-stable
|
|
55
|
+
* (`stable: true`); without one it falls back to a per-process random secret
|
|
56
|
+
* (`stable: false`).
|
|
57
|
+
*/
|
|
58
|
+
export declare function createStateSigner(secret?: string): StateSigner;
|
|
59
|
+
//# sourceMappingURL=state-signing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-signing.d.ts","sourceRoot":"","sources":["../src/state-signing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,kFAAkF;IAClF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACrF,+EAA+E;IAC/E,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,IAAI,CAAC;IACtD;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAaD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAqD9D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
2
|
+
//#region src/state-signing.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shared OAuth/install `state` signing for web integrations.
|
|
5
|
+
*
|
|
6
|
+
* The GitHub, Linear, and Slack OAuth/OIDC flows each round-trip a signed `state`
|
|
7
|
+
* value
|
|
8
|
+
* through the third party to bind the callback to the `(orgId, userId)` tenant
|
|
9
|
+
* that initiated it (CSRF protection + tenant routing). The signer is a system
|
|
10
|
+
* facility: `MastraFactory` creates ONE signer at boot and hands it to every
|
|
11
|
+
* registered integration through `IntegrationContext` (see
|
|
12
|
+
* `./factory-integration.ts`), so all integrations sign and verify with the
|
|
13
|
+
* same secret.
|
|
14
|
+
*
|
|
15
|
+
* Secret resolution happens in the factory, not here: explicit
|
|
16
|
+
* `config.stateSecret` → the GitHub integration's webhook secret → a
|
|
17
|
+
* per-process random secret. A random secret is NOT stable across replicas —
|
|
18
|
+
* a `state` signed by one replica cannot be verified by another — which is
|
|
19
|
+
* what the `stable` flag reports. The factory fails loud at boot when a
|
|
20
|
+
* registered integration requires a stable signer but only a random one is
|
|
21
|
+
* available.
|
|
22
|
+
*
|
|
23
|
+
* The wire format (base64url JSON payload + `.` + HMAC-SHA256 base64url
|
|
24
|
+
* signature) is unchanged from the previous `github/config.ts` implementation
|
|
25
|
+
* so in-flight OAuth states survive a deploy.
|
|
26
|
+
*/
|
|
27
|
+
/** Signed `state` values expire after this window to bound the CSRF token. */
|
|
28
|
+
const STATE_MAX_AGE_MS = 600 * 1e3;
|
|
29
|
+
/**
|
|
30
|
+
* Create a state signer. With a `secret`, the signer is deployment-stable
|
|
31
|
+
* (`stable: true`); without one it falls back to a per-process random secret
|
|
32
|
+
* (`stable: false`).
|
|
33
|
+
*/
|
|
34
|
+
function createStateSigner(secret) {
|
|
35
|
+
const stable = typeof secret === "string" && secret.length > 0;
|
|
36
|
+
const key = stable ? secret : randomBytes(32).toString("hex");
|
|
37
|
+
return {
|
|
38
|
+
stable,
|
|
39
|
+
sign(orgId, userId, context) {
|
|
40
|
+
const payload = {
|
|
41
|
+
orgId,
|
|
42
|
+
userId,
|
|
43
|
+
...context?.factoryProjectId ? { factoryProjectId: context.factoryProjectId } : {},
|
|
44
|
+
nonce: randomBytes(8).toString("hex"),
|
|
45
|
+
issuedAt: Date.now()
|
|
46
|
+
};
|
|
47
|
+
const body = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
48
|
+
return `${body}.${createHmac("sha256", key).update(body).digest("base64url")}`;
|
|
49
|
+
},
|
|
50
|
+
verify(state) {
|
|
51
|
+
if (!state) return null;
|
|
52
|
+
const dot = state.lastIndexOf(".");
|
|
53
|
+
if (dot <= 0) return null;
|
|
54
|
+
const body = state.slice(0, dot);
|
|
55
|
+
const sig = state.slice(dot + 1);
|
|
56
|
+
const expected = createHmac("sha256", key).update(body).digest("base64url");
|
|
57
|
+
const sigBuf = Buffer.from(sig);
|
|
58
|
+
const expectedBuf = Buffer.from(expected);
|
|
59
|
+
if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf)) return null;
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
62
|
+
if (typeof parsed.orgId !== "string" || typeof parsed.userId !== "string") return null;
|
|
63
|
+
if (typeof parsed.issuedAt !== "number" || !Number.isFinite(parsed.issuedAt)) return null;
|
|
64
|
+
if (typeof parsed.nonce !== "string" || parsed.nonce.length === 0) return null;
|
|
65
|
+
if (parsed.factoryProjectId !== void 0 && (typeof parsed.factoryProjectId !== "string" || parsed.factoryProjectId.length === 0)) return null;
|
|
66
|
+
const age = Date.now() - parsed.issuedAt;
|
|
67
|
+
if (age < 0 || age > STATE_MAX_AGE_MS) return null;
|
|
68
|
+
return {
|
|
69
|
+
orgId: parsed.orgId,
|
|
70
|
+
userId: parsed.userId,
|
|
71
|
+
...parsed.factoryProjectId ? { factoryProjectId: parsed.factoryProjectId } : {},
|
|
72
|
+
nonce: parsed.nonce
|
|
73
|
+
};
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { createStateSigner };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=state-signing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-signing.js","names":[],"sources":["../src/state-signing.ts"],"sourcesContent":["/**\n * Shared OAuth/install `state` signing for web integrations.\n *\n * The GitHub, Linear, and Slack OAuth/OIDC flows each round-trip a signed `state`\n * value\n * through the third party to bind the callback to the `(orgId, userId)` tenant\n * that initiated it (CSRF protection + tenant routing). The signer is a system\n * facility: `MastraFactory` creates ONE signer at boot and hands it to every\n * registered integration through `IntegrationContext` (see\n * `./factory-integration.ts`), so all integrations sign and verify with the\n * same secret.\n *\n * Secret resolution happens in the factory, not here: explicit\n * `config.stateSecret` → the GitHub integration's webhook secret → a\n * per-process random secret. A random secret is NOT stable across replicas —\n * a `state` signed by one replica cannot be verified by another — which is\n * what the `stable` flag reports. The factory fails loud at boot when a\n * registered integration requires a stable signer but only a random one is\n * available.\n *\n * The wire format (base64url JSON payload + `.` + HMAC-SHA256 base64url\n * signature) is unchanged from the previous `github/config.ts` implementation\n * so in-flight OAuth states survive a deploy.\n */\n\nimport { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\n\n/** Verified tenant and optional Factory context carried by a signed `state`. */\nexport interface StateTenant {\n orgId: string;\n userId: string;\n /** Factory that initiated the integration flow, when the caller supplied one. */\n factoryProjectId?: string;\n /**\n * Per-`state` random value. A signed `state` stays valid for its whole\n * lifetime, so a flow that must not run twice off one `state` (account\n * binding, for instance) can key single-use bookkeeping on this.\n */\n nonce: string;\n}\n\n/** Signs and verifies OAuth `state` values bound to a tenant and optional Factory. */\nexport interface StateSigner {\n /** Build a signed `state` bound to the tenant and optional initiating Factory. */\n sign(orgId: string, userId: string, context?: { factoryProjectId?: string }): string;\n /** Verify a signed `state`; returns the bound tenant, or `null` if invalid. */\n verify(state: string | undefined): StateTenant | null;\n /**\n * True when the signer was built from an explicit deployment-stable secret.\n * False means a per-process random secret: fine for single-process/local\n * dev, broken for multi-replica deploys (see module docs).\n */\n readonly stable: boolean;\n}\n\ninterface StatePayload {\n orgId: string;\n userId: string;\n factoryProjectId?: string;\n nonce: string;\n issuedAt: number;\n}\n\n/** Signed `state` values expire after this window to bound the CSRF token. */\nconst STATE_MAX_AGE_MS = 10 * 60 * 1000;\n\n/**\n * Create a state signer. With a `secret`, the signer is deployment-stable\n * (`stable: true`); without one it falls back to a per-process random secret\n * (`stable: false`).\n */\nexport function createStateSigner(secret?: string): StateSigner {\n const stable = typeof secret === 'string' && secret.length > 0;\n const key = stable ? secret : randomBytes(32).toString('hex');\n return {\n stable,\n sign(orgId: string, userId: string, context?: { factoryProjectId?: string }): string {\n const payload: StatePayload = {\n orgId,\n userId,\n ...(context?.factoryProjectId ? { factoryProjectId: context.factoryProjectId } : {}),\n nonce: randomBytes(8).toString('hex'),\n issuedAt: Date.now(),\n };\n const body = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url');\n const sig = createHmac('sha256', key).update(body).digest('base64url');\n return `${body}.${sig}`;\n },\n verify(state: string | undefined): StateTenant | null {\n if (!state) return null;\n const dot = state.lastIndexOf('.');\n if (dot <= 0) return null;\n const body = state.slice(0, dot);\n const sig = state.slice(dot + 1);\n const expected = createHmac('sha256', key).update(body).digest('base64url');\n const sigBuf = Buffer.from(sig);\n const expectedBuf = Buffer.from(expected);\n if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf)) {\n return null;\n }\n try {\n const parsed = JSON.parse(Buffer.from(body, 'base64url').toString('utf8')) as StatePayload;\n if (typeof parsed.orgId !== 'string' || typeof parsed.userId !== 'string') return null;\n if (typeof parsed.issuedAt !== 'number' || !Number.isFinite(parsed.issuedAt)) return null;\n if (typeof parsed.nonce !== 'string' || parsed.nonce.length === 0) return null;\n if (\n parsed.factoryProjectId !== undefined &&\n (typeof parsed.factoryProjectId !== 'string' || parsed.factoryProjectId.length === 0)\n ) {\n return null;\n }\n const age = Date.now() - parsed.issuedAt;\n if (age < 0 || age > STATE_MAX_AGE_MS) return null;\n return {\n orgId: parsed.orgId,\n userId: parsed.userId,\n ...(parsed.factoryProjectId ? { factoryProjectId: parsed.factoryProjectId } : {}),\n nonce: parsed.nonce,\n };\n } catch {\n return null;\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,MAAM,mBAAmB,MAAU;;;;;;AAOnC,SAAgB,kBAAkB,QAA8B;CAC9D,MAAM,SAAS,OAAO,WAAW,YAAY,OAAO,SAAS;CAC7D,MAAM,MAAM,SAAS,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC5D,OAAO;EACL;EACA,KAAK,OAAe,QAAgB,SAAiD;GACnF,MAAM,UAAwB;IAC5B;IACA;IACA,GAAI,SAAS,mBAAmB,EAAE,kBAAkB,QAAQ,iBAAiB,IAAI,CAAC;IAClF,OAAO,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK;IACpC,UAAU,KAAK,IAAI;GACrB;GACA,MAAM,OAAO,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,MAAM,CAAC,CAAC,SAAS,WAAW;GAE9E,OAAO,GAAG,KAAK,GADH,WAAW,UAAU,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,WACtC;EACtB;EACA,OAAO,OAA+C;GACpD,IAAI,CAAC,OAAO,OAAO;GACnB,MAAM,MAAM,MAAM,YAAY,GAAG;GACjC,IAAI,OAAO,GAAG,OAAO;GACrB,MAAM,OAAO,MAAM,MAAM,GAAG,GAAG;GAC/B,MAAM,MAAM,MAAM,MAAM,MAAM,CAAC;GAC/B,MAAM,WAAW,WAAW,UAAU,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,WAAW;GAC1E,MAAM,SAAS,OAAO,KAAK,GAAG;GAC9B,MAAM,cAAc,OAAO,KAAK,QAAQ;GACxC,IAAI,OAAO,WAAW,YAAY,UAAU,CAAC,gBAAgB,QAAQ,WAAW,GAC9E,OAAO;GAET,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC;IACzE,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,WAAW,UAAU,OAAO;IAClF,IAAI,OAAO,OAAO,aAAa,YAAY,CAAC,OAAO,SAAS,OAAO,QAAQ,GAAG,OAAO;IACrF,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,WAAW,GAAG,OAAO;IAC1E,IACE,OAAO,qBAAqB,KAAA,MAC3B,OAAO,OAAO,qBAAqB,YAAY,OAAO,iBAAiB,WAAW,IAEnF,OAAO;IAET,MAAM,MAAM,KAAK,IAAI,IAAI,OAAO;IAChC,IAAI,MAAM,KAAK,MAAM,kBAAkB,OAAO;IAC9C,OAAO;KACL,OAAO,OAAO;KACd,QAAQ,OAAO;KACf,GAAI,OAAO,mBAAmB,EAAE,kBAAkB,OAAO,iBAAiB,IAAI,CAAC;KAC/E,OAAO,OAAO;IAChB;GACF,QAAQ;IACN,OAAO;GACT;EACF;CACF;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-level audit event detection (Audit v1.1).
|
|
3
|
+
*
|
|
4
|
+
* Git actions performed by agents inside runs never touch web routes, so this
|
|
5
|
+
* observer detects externally-visible git side effects and delegates recording
|
|
6
|
+
* to the factory-owned audit domain.
|
|
7
|
+
*/
|
|
8
|
+
import type { RequestContext } from '@mastra/core/request-context';
|
|
9
|
+
import type { AuditAgentEmitter } from './domain.js';
|
|
10
|
+
interface ToolObserverContext {
|
|
11
|
+
toolName: string;
|
|
12
|
+
input: unknown;
|
|
13
|
+
output?: unknown;
|
|
14
|
+
error?: unknown;
|
|
15
|
+
context: RequestContext;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Detect externally-visible git side effects in a completed tool call and
|
|
19
|
+
* record `factory.agent.*` audit events for them. One command can emit
|
|
20
|
+
* multiple events (`git commit && git push` emits both). Never throws.
|
|
21
|
+
*/
|
|
22
|
+
export declare function observeAgentGitAction({ audit, toolContext, }: {
|
|
23
|
+
audit: AuditAgentEmitter;
|
|
24
|
+
toolContext: ToolObserverContext;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=agent-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-audit.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/agent-audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC;CACzB;AAgCD;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,KAAK,EACL,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,iBAAiB,CAAC;IACzB,WAAW,EAAE,mBAAmB,CAAC;CAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsChB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/storage/domains/audit/agent-audit.ts
|
|
2
|
+
/** Match command-start positions while ignoring command text embedded in heredoc bodies. */
|
|
3
|
+
const GIT_COMMIT_RE = /(?:^|\n|;|&&|\|\|)\s*git\s+commit(?:\s|$)/;
|
|
4
|
+
const GIT_PUSH_RE = /(?:^|\n|;|&&|\|\|)\s*git\s+push(?:\s|$)/;
|
|
5
|
+
function stripHeredocBodies(command) {
|
|
6
|
+
const lines = command.split("\n");
|
|
7
|
+
const executableLines = [];
|
|
8
|
+
let delimiter;
|
|
9
|
+
for (const line of lines) {
|
|
10
|
+
if (delimiter) {
|
|
11
|
+
if (line.trim() === delimiter) delimiter = void 0;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
executableLines.push(line);
|
|
15
|
+
delimiter = line.match(/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/)?.[2];
|
|
16
|
+
}
|
|
17
|
+
return executableLines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
/** Parse the branch from a plain `git push <remote> <branch>` invocation. */
|
|
20
|
+
function parsePushedBranch(command) {
|
|
21
|
+
return command.match(/(?:^|\n|;|&&|\|\|)\s*git\s+push\s+(?:-[^\s]+\s+)*([^\s;&|-][^\s;&|]*)\s+([^\s;&|-][^\s;&|]*)/)?.[2];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Detect externally-visible git side effects in a completed tool call and
|
|
25
|
+
* record `factory.agent.*` audit events for them. One command can emit
|
|
26
|
+
* multiple events (`git commit && git push` emits both). Never throws.
|
|
27
|
+
*/
|
|
28
|
+
async function observeAgentGitAction({ audit, toolContext }) {
|
|
29
|
+
try {
|
|
30
|
+
if (toolContext.toolName !== "execute_command" || toolContext.error) return;
|
|
31
|
+
const rawCommand = toolContext.input?.command;
|
|
32
|
+
if (typeof rawCommand !== "string") return;
|
|
33
|
+
const command = stripHeredocBodies(rawCommand);
|
|
34
|
+
const worktreePath = toolContext.context.get("controller")?.scope;
|
|
35
|
+
if (GIT_COMMIT_RE.test(command)) await audit.emitAgent({
|
|
36
|
+
requestContext: toolContext.context,
|
|
37
|
+
input: {
|
|
38
|
+
action: "factory.agent.commit",
|
|
39
|
+
targets: worktreePath ? [{
|
|
40
|
+
type: "worktree",
|
|
41
|
+
id: worktreePath
|
|
42
|
+
}] : []
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
if (GIT_PUSH_RE.test(command)) {
|
|
46
|
+
const branch = parsePushedBranch(command);
|
|
47
|
+
await audit.emitAgent({
|
|
48
|
+
requestContext: toolContext.context,
|
|
49
|
+
input: {
|
|
50
|
+
action: "factory.agent.push",
|
|
51
|
+
targets: worktreePath ? [{
|
|
52
|
+
type: "worktree",
|
|
53
|
+
id: worktreePath
|
|
54
|
+
}] : [],
|
|
55
|
+
...branch ? { metadata: { branch } } : {}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.warn("[Audit] Failed to observe agent git action", { error: err instanceof Error ? err.message : String(err) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { observeAgentGitAction };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=agent-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-audit.js","names":[],"sources":["../../../../src/storage/domains/audit/agent-audit.ts"],"sourcesContent":["/**\n * Agent-level audit event detection (Audit v1.1).\n *\n * Git actions performed by agents inside runs never touch web routes, so this\n * observer detects externally-visible git side effects and delegates recording\n * to the factory-owned audit domain.\n */\n\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport type { AuditAgentEmitter } from './domain.js';\n\ntype FactorySessionState = { factoryProjectId?: string; projectRepositoryId?: string };\n\ninterface ToolObserverContext {\n toolName: string;\n input: unknown;\n output?: unknown;\n error?: unknown;\n context: RequestContext;\n}\n\n/** Match command-start positions while ignoring command text embedded in heredoc bodies. */\nconst GIT_COMMIT_RE = /(?:^|\\n|;|&&|\\|\\|)\\s*git\\s+commit(?:\\s|$)/;\nconst GIT_PUSH_RE = /(?:^|\\n|;|&&|\\|\\|)\\s*git\\s+push(?:\\s|$)/;\n\nfunction stripHeredocBodies(command: string): string {\n const lines = command.split('\\n');\n const executableLines: string[] = [];\n let delimiter: string | undefined;\n\n for (const line of lines) {\n if (delimiter) {\n if (line.trim() === delimiter) delimiter = undefined;\n continue;\n }\n executableLines.push(line);\n const heredoc = line.match(/<<-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/);\n delimiter = heredoc?.[2];\n }\n\n return executableLines.join('\\n');\n}\n\n/** Parse the branch from a plain `git push <remote> <branch>` invocation. */\nfunction parsePushedBranch(command: string): string | undefined {\n const match = command.match(\n /(?:^|\\n|;|&&|\\|\\|)\\s*git\\s+push\\s+(?:-[^\\s]+\\s+)*([^\\s;&|-][^\\s;&|]*)\\s+([^\\s;&|-][^\\s;&|]*)/,\n );\n return match?.[2];\n}\n\n/**\n * Detect externally-visible git side effects in a completed tool call and\n * record `factory.agent.*` audit events for them. One command can emit\n * multiple events (`git commit && git push` emits both). Never throws.\n */\nexport async function observeAgentGitAction({\n audit,\n toolContext,\n}: {\n audit: AuditAgentEmitter;\n toolContext: ToolObserverContext;\n}): Promise<void> {\n try {\n if (toolContext.toolName !== 'execute_command' || toolContext.error) return;\n const rawCommand = (toolContext.input as { command?: unknown } | undefined)?.command;\n if (typeof rawCommand !== 'string') return;\n const command = stripHeredocBodies(rawCommand);\n\n const controller = toolContext.context.get('controller') as\n | AgentControllerRequestContext<FactorySessionState>\n | undefined;\n const worktreePath = controller?.scope;\n\n if (GIT_COMMIT_RE.test(command)) {\n await audit.emitAgent({\n requestContext: toolContext.context,\n input: {\n action: 'factory.agent.commit',\n targets: worktreePath ? [{ type: 'worktree', id: worktreePath }] : [],\n },\n });\n }\n\n if (GIT_PUSH_RE.test(command)) {\n const branch = parsePushedBranch(command);\n await audit.emitAgent({\n requestContext: toolContext.context,\n input: {\n action: 'factory.agent.push',\n targets: worktreePath ? [{ type: 'worktree', id: worktreePath }] : [],\n ...(branch ? { metadata: { branch } } : {}),\n },\n });\n }\n } catch (err) {\n console.warn('[Audit] Failed to observe agent git action', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n"],"mappings":";;AAwBA,MAAM,gBAAgB;AACtB,MAAM,cAAc;AAEpB,SAAS,mBAAmB,SAAyB;CACnD,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,MAAM,kBAA4B,CAAC;CACnC,IAAI;CAEJ,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,WAAW;GACb,IAAI,KAAK,KAAK,MAAM,WAAW,YAAY,KAAA;GAC3C;EACF;EACA,gBAAgB,KAAK,IAAI;EAEzB,YADgB,KAAK,MAAM,0CACT,CAAC,GAAG;CACxB;CAEA,OAAO,gBAAgB,KAAK,IAAI;AAClC;;AAGA,SAAS,kBAAkB,SAAqC;CAI9D,OAHc,QAAQ,MACpB,8FAES,CAAC,GAAG;AACjB;;;;;;AAOA,eAAsB,sBAAsB,EAC1C,OACA,eAIgB;CAChB,IAAI;EACF,IAAI,YAAY,aAAa,qBAAqB,YAAY,OAAO;EACrE,MAAM,aAAc,YAAY,OAA6C;EAC7E,IAAI,OAAO,eAAe,UAAU;EACpC,MAAM,UAAU,mBAAmB,UAAU;EAK7C,MAAM,eAHa,YAAY,QAAQ,IAAI,YAGb,CAAC,EAAE;EAEjC,IAAI,cAAc,KAAK,OAAO,GAC5B,MAAM,MAAM,UAAU;GACpB,gBAAgB,YAAY;GAC5B,OAAO;IACL,QAAQ;IACR,SAAS,eAAe,CAAC;KAAE,MAAM;KAAY,IAAI;IAAa,CAAC,IAAI,CAAC;GACtE;EACF,CAAC;EAGH,IAAI,YAAY,KAAK,OAAO,GAAG;GAC7B,MAAM,SAAS,kBAAkB,OAAO;GACxC,MAAM,MAAM,UAAU;IACpB,gBAAgB,YAAY;IAC5B,OAAO;KACL,QAAQ;KACR,SAAS,eAAe,CAAC;MAAE,MAAM;MAAY,IAAI;KAAa,CAAC,IAAI,CAAC;KACpE,GAAI,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;IAC3C;GACF,CAAC;EACH;CACF,SAAS,KAAK;EACZ,QAAQ,KAAK,8CAA8C,EACzD,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EACxD,CAAC;CACH;AACF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory audit events domain — the append-only "who did what, when" trail
|
|
3
|
+
* behind the software factory.
|
|
4
|
+
*
|
|
5
|
+
* One `audit_events` row records a single audited mutation (work-item change,
|
|
6
|
+
* stage move, run start, worktree create/delete, git action, intake config
|
|
7
|
+
* change). Rows are append-only: there is no update/delete API, and the table
|
|
8
|
+
* is the local source of truth even when the WorkOS Audit Logs mirror is
|
|
9
|
+
* unavailable.
|
|
10
|
+
*
|
|
11
|
+
* Tenancy is **org-first**, like `work_items`: events are scoped by `org_id`
|
|
12
|
+
* and (usually) `factory_project_id`; `actor_id` records who acted but never
|
|
13
|
+
* scopes reads.
|
|
14
|
+
*
|
|
15
|
+
* v1 action taxonomy (register these in the WorkOS dashboard under
|
|
16
|
+
* Audit Logs → Events for the export mirror to accept them):
|
|
17
|
+
* - factory.work_item.created
|
|
18
|
+
* - factory.work_item.updated
|
|
19
|
+
* - factory.work_item.stage_moved
|
|
20
|
+
* - factory.work_item.deleted
|
|
21
|
+
* - factory.run.started
|
|
22
|
+
* - factory.worktree.created
|
|
23
|
+
* - factory.worktree.deleted
|
|
24
|
+
* - factory.triage.started
|
|
25
|
+
* - factory.git.commit
|
|
26
|
+
* - factory.git.push
|
|
27
|
+
* - factory.git.pr_opened
|
|
28
|
+
* - factory.intake.config_updated
|
|
29
|
+
*
|
|
30
|
+
* v1.1 adds agent-level actions (also register these in WorkOS):
|
|
31
|
+
* - factory.agent.commit
|
|
32
|
+
* - factory.agent.push
|
|
33
|
+
* - factory.agent.pr_opened
|
|
34
|
+
*
|
|
35
|
+
* Agent events carry `actor_type = 'agent'` with `actor_id = 'agent:<threadId>'`
|
|
36
|
+
* and `metadata.startedBy = <userId>` chaining accountability back to the human
|
|
37
|
+
* whose message drove the run.
|
|
38
|
+
*/
|
|
39
|
+
import { FactoryStorageDomain } from '@mastra/core/storage';
|
|
40
|
+
import type { CollectionSchema } from '@mastra/core/storage';
|
|
41
|
+
/** What an audit event acted on (WorkOS Audit Logs target shape). */
|
|
42
|
+
export interface AuditTarget {
|
|
43
|
+
/** Target kind, e.g. 'work_item', 'worktree', 'issue', 'pull_request'. */
|
|
44
|
+
type: string;
|
|
45
|
+
/** Stable identifier of the target (row id, branch name, issue number...). */
|
|
46
|
+
id: string;
|
|
47
|
+
/** Human-readable label (work-item title, branch name...). */
|
|
48
|
+
name?: string;
|
|
49
|
+
}
|
|
50
|
+
/** Who performed the audited action. */
|
|
51
|
+
export type AuditActorType = 'human' | 'agent';
|
|
52
|
+
/** Request context captured alongside the event. */
|
|
53
|
+
export interface AuditContext {
|
|
54
|
+
/** Client IP (first hop of `x-forwarded-for`) when available. */
|
|
55
|
+
location?: string;
|
|
56
|
+
/** Request `user-agent` header when available. */
|
|
57
|
+
userAgent?: string;
|
|
58
|
+
}
|
|
59
|
+
/** One persisted audit event. */
|
|
60
|
+
export interface AuditEventRow {
|
|
61
|
+
id: string;
|
|
62
|
+
/** Owning WorkOS organization id — the trail is org-wide. */
|
|
63
|
+
orgId: string;
|
|
64
|
+
/** WorkOS user id of whoever performed the action, or `agent:<threadId>`. */
|
|
65
|
+
actorId: string;
|
|
66
|
+
/** Whether a human or an agent (inside a run) performed the action. */
|
|
67
|
+
actorType: AuditActorType;
|
|
68
|
+
/** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */
|
|
69
|
+
action: string;
|
|
70
|
+
/** What was acted on. */
|
|
71
|
+
targets: AuditTarget[];
|
|
72
|
+
/** Bounded event summary — never full payloads, never secrets. */
|
|
73
|
+
metadata: Record<string, unknown>;
|
|
74
|
+
/** Factory project the event is scoped to; null for org-level events. */
|
|
75
|
+
factoryProjectId: string | null;
|
|
76
|
+
/** Project-repository link affected by a repository-specific action. */
|
|
77
|
+
projectRepositoryId: string | null;
|
|
78
|
+
/** Request context (`x-forwarded-for` / `user-agent`). */
|
|
79
|
+
context: AuditContext;
|
|
80
|
+
occurredAt: Date;
|
|
81
|
+
}
|
|
82
|
+
export interface RecordAuditEventInput {
|
|
83
|
+
orgId: string;
|
|
84
|
+
actorId: string;
|
|
85
|
+
/** Who performed the action; defaults to 'human'. */
|
|
86
|
+
actorType?: AuditActorType;
|
|
87
|
+
/** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */
|
|
88
|
+
action: string;
|
|
89
|
+
targets: AuditTarget[];
|
|
90
|
+
metadata?: Record<string, unknown>;
|
|
91
|
+
factoryProjectId?: string;
|
|
92
|
+
projectRepositoryId?: string;
|
|
93
|
+
context?: AuditContext;
|
|
94
|
+
occurredAt?: Date;
|
|
95
|
+
}
|
|
96
|
+
/** A fully-normalized event ready to persist (id assigned on insert). */
|
|
97
|
+
export type AuditEventInsert = Omit<AuditEventRow, 'id'>;
|
|
98
|
+
export interface ListAuditEventsInput {
|
|
99
|
+
orgId: string;
|
|
100
|
+
factoryProjectId?: string;
|
|
101
|
+
/** Restrict to these actions (exact match). */
|
|
102
|
+
actions?: string[];
|
|
103
|
+
actorId?: string;
|
|
104
|
+
/** Opaque cursor from a previous page (`nextCursor`). */
|
|
105
|
+
before?: string;
|
|
106
|
+
limit?: number;
|
|
107
|
+
}
|
|
108
|
+
export interface AuditEventPage {
|
|
109
|
+
events: AuditEventRow[];
|
|
110
|
+
/** Pass back as `before` to fetch the next (older) page; absent at the end. */
|
|
111
|
+
nextCursor?: string;
|
|
112
|
+
}
|
|
113
|
+
/** Truncate oversized metadata rather than dropping the whole event. */
|
|
114
|
+
export declare function boundAuditMetadata(metadata: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
115
|
+
/** Normalize a requested page size to a finite integer in `[1, MAX_PAGE_SIZE]`. */
|
|
116
|
+
export declare function clampAuditLimit(limit: number | undefined): number;
|
|
117
|
+
/** Encode the `(occurredAt, id)` keyset cursor of a row. */
|
|
118
|
+
export declare function encodeAuditCursor(row: AuditEventRow): string;
|
|
119
|
+
/** Decode a cursor back into its `(occurredAt, id)` parts, or `undefined`. */
|
|
120
|
+
export declare function decodeAuditCursor(cursor: string): {
|
|
121
|
+
occurredAt: Date;
|
|
122
|
+
id: string;
|
|
123
|
+
} | undefined;
|
|
124
|
+
export declare const AUDIT_EVENTS_SCHEMA: CollectionSchema;
|
|
125
|
+
/**
|
|
126
|
+
* Audit event storage, written once against the generic `FactoryStorageOps`
|
|
127
|
+
* surface. `record()` normalizes inputs (defaults, metadata bounding);
|
|
128
|
+
* `list()` is keyset-paginated on `(occurred_at, id)` newest-first.
|
|
129
|
+
*/
|
|
130
|
+
export declare class AuditStorage extends FactoryStorageDomain {
|
|
131
|
+
#private;
|
|
132
|
+
constructor();
|
|
133
|
+
init(): Promise<void>;
|
|
134
|
+
dangerouslyClearAll(): Promise<void>;
|
|
135
|
+
/** Append one audit event. Throws on failure — swallow-on-failure lives in the caller. */
|
|
136
|
+
record(input: RecordAuditEventInput): Promise<AuditEventRow>;
|
|
137
|
+
/** List an org's audit events newest-first with keyset pagination. */
|
|
138
|
+
list(input: ListAuditEventsInput): Promise<AuditEventPage>;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAsC,MAAM,sBAAsB,CAAC;AAEjG,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/C,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,cAAc,CAAC;IAC1B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,wEAAwE;IACxE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,0DAA0D;IAC1D,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB;AAED,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAQD,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzG;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAE5D;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAO9F;AAED,eAAO,MAAM,mBAAmB,EAAE,gBAmBjC,CAAC;AAiCF;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,oBAAoB;;;IAK9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C,0FAA0F;IACpF,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC;IAgBlE,sEAAsE;IAChE,IAAI,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;CA2BjE"}
|