@mastra/factory 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150
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 +695 -0
- package/LICENSE.md +30 -0
- package/README.md +27 -0
- package/dist/auth.d.ts +181 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +505 -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 +140 -0
- package/dist/capabilities/intake.d.ts.map +1 -0
- package/dist/capabilities/intake.js +1 -0
- package/dist/capabilities/version-control.d.ts +245 -0
- package/dist/capabilities/version-control.d.ts.map +1 -0
- package/dist/capabilities/version-control.js +1 -0
- package/dist/factory.d.ts +156 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +483 -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 +210 -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/factory-session.d.ts +12 -0
- package/dist/integrations/github/factory-session.d.ts.map +1 -0
- package/dist/integrations/github/factory-session.js +37 -0
- package/dist/integrations/github/factory-session.js.map +1 -0
- package/dist/integrations/github/integration.d.ts +191 -0
- package/dist/integrations/github/integration.d.ts.map +1 -0
- package/dist/integrations/github/integration.js +1077 -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/routes.d.ts +69 -0
- package/dist/integrations/github/routes.d.ts.map +1 -0
- package/dist/integrations/github/routes.js +1324 -0
- package/dist/integrations/github/routes.js.map +1 -0
- package/dist/integrations/github/rules.d.ts +78 -0
- package/dist/integrations/github/rules.d.ts.map +1 -0
- package/dist/integrations/github/rules.js +362 -0
- package/dist/integrations/github/rules.js.map +1 -0
- package/dist/integrations/github/sandbox-release.d.ts +38 -0
- package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
- package/dist/integrations/github/sandbox-release.js +74 -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 +87 -0
- package/dist/integrations/github/webhook.d.ts.map +1 -0
- package/dist/integrations/github/webhook.js +397 -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 +233 -0
- package/dist/integrations/linear/integration.d.ts.map +1 -0
- package/dist/integrations/linear/integration.js +672 -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 +239 -0
- package/dist/integrations/linear/routes.js.map +1 -0
- package/dist/integrations/linear/rules.d.ts +42 -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 +49 -0
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
- package/dist/integrations/platform/github/event-worker.js +348 -0
- package/dist/integrations/platform/github/event-worker.js.map +1 -0
- package/dist/integrations/platform/github/integration.d.ts +50 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -0
- package/dist/integrations/platform/github/integration.js +961 -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/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 +91 -0
- package/dist/routes/surface.d.ts.map +1 -0
- package/dist/routes/surface.js +260 -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 +260 -0
- package/dist/rules/defaults.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +42 -0
- package/dist/rules/dispatcher.d.ts.map +1 -0
- package/dist/rules/dispatcher.js +518 -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 +194 -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 +58 -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 +234 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +38 -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/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 +69 -0
- package/dist/storage/domains/audit/domain.d.ts.map +1 -0
- package/dist/storage/domains/audit/domain.js +167 -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 +358 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -0
- package/dist/storage/domains/source-control/base.js +908 -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 +377 -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 +1309 -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 +286 -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 +155 -0
- package/factory-skills/factory-triage/SKILL.md +77 -0
- package/package.json +81 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { getGithubFeatureDiagnostics } from "../integrations/github/config.js";
|
|
2
|
+
import { invalidateCustomProvidersSnapshots } from "./custom-provider-source.js";
|
|
3
|
+
import { ensureFactoryRuleSession } from "../integrations/github/factory-session.js";
|
|
4
|
+
import { FactoryStartCoordinator } from "../rules/start-coordinator.js";
|
|
5
|
+
import { FactoryTransitionService } from "../rules/transition-service.js";
|
|
6
|
+
import { ConfigRoutes } from "./config.js";
|
|
7
|
+
import { buildFsRoutes } from "./fs.js";
|
|
8
|
+
import { IntakeRoutes } from "./intake.js";
|
|
9
|
+
import { OAuthRoutes } from "./oauth.js";
|
|
10
|
+
import { SkillRoutes } from "./skills.js";
|
|
11
|
+
import { invalidateTenantCredentialSnapshots } from "./tenant-credentials.js";
|
|
12
|
+
import { WorkItemRoutes } from "./work-items.js";
|
|
13
|
+
import { registerApiRoute } from "@mastra/core/server";
|
|
14
|
+
//#region src/routes/surface.ts
|
|
15
|
+
function guardIntegrationRoutes({ integration, ready, ensureReady, routes }) {
|
|
16
|
+
if (ready) return routes;
|
|
17
|
+
return routes.map((route) => {
|
|
18
|
+
if ("handler" in route) {
|
|
19
|
+
const handler = route.handler;
|
|
20
|
+
return {
|
|
21
|
+
...route,
|
|
22
|
+
handler: async (context) => {
|
|
23
|
+
try {
|
|
24
|
+
await ensureReady();
|
|
25
|
+
} catch {
|
|
26
|
+
return context.json({
|
|
27
|
+
error: "integration_unavailable",
|
|
28
|
+
message: `${integration.id} integration is unavailable.`
|
|
29
|
+
}, 503);
|
|
30
|
+
}
|
|
31
|
+
return handler(context, async () => {});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const createHandler = route.createHandler;
|
|
36
|
+
return {
|
|
37
|
+
...route,
|
|
38
|
+
createHandler: async (args) => {
|
|
39
|
+
const handler = await createHandler(args);
|
|
40
|
+
return async (context) => {
|
|
41
|
+
try {
|
|
42
|
+
await ensureReady();
|
|
43
|
+
} catch {
|
|
44
|
+
return context.json({
|
|
45
|
+
error: "integration_unavailable",
|
|
46
|
+
message: `${integration.id} integration is unavailable.`
|
|
47
|
+
}, 503);
|
|
48
|
+
}
|
|
49
|
+
return handler(context);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function factoryRuleBranch(item) {
|
|
56
|
+
const metadata = item.metadata ?? {};
|
|
57
|
+
const issueNumber = metadata.githubIssueNumber ?? metadata.number;
|
|
58
|
+
if (item.externalSource?.integrationId === "github" && item.externalSource.type === "issue" && typeof issueNumber === "number") return `factory/issue-${issueNumber}`;
|
|
59
|
+
const pullRequestNumber = metadata.githubPullRequestNumber ?? metadata.number;
|
|
60
|
+
if (item.externalSource?.integrationId === "github" && item.externalSource.type === "pull-request" && typeof pullRequestNumber === "number") return `factory/pr-${pullRequestNumber}`;
|
|
61
|
+
throw new Error("Factory skill invocation requires a GitHub issue or pull request number.");
|
|
62
|
+
}
|
|
63
|
+
async function prepareFactoryRuleBinding(github, coordinator, input) {
|
|
64
|
+
const branch = factoryRuleBranch(input.item);
|
|
65
|
+
const repositorySlug = typeof input.item.metadata?.repository === "string" ? input.item.metadata.repository : void 0;
|
|
66
|
+
const preparedSession = await ensureFactoryRuleSession({
|
|
67
|
+
github,
|
|
68
|
+
orgId: input.record.orgId,
|
|
69
|
+
factoryProjectId: input.record.factoryProjectId,
|
|
70
|
+
repositorySlug,
|
|
71
|
+
branch
|
|
72
|
+
});
|
|
73
|
+
const destinationStage = input.item.stages.length === 1 ? input.item.stages[0] : void 0;
|
|
74
|
+
if (!destinationStage) throw new Error("Factory skill invocation requires one exclusive board stage.");
|
|
75
|
+
await coordinator.prepare({
|
|
76
|
+
orgId: input.record.orgId,
|
|
77
|
+
userId: preparedSession.userId,
|
|
78
|
+
factoryProjectId: input.record.factoryProjectId,
|
|
79
|
+
sessionId: preparedSession.sessionId,
|
|
80
|
+
threadTitle: `${input.role === "review" ? "PR" : "Issue"}: ${input.item.title}`,
|
|
81
|
+
kickoffKey: input.record.id,
|
|
82
|
+
destinationStage,
|
|
83
|
+
workItem: {
|
|
84
|
+
id: input.item.id,
|
|
85
|
+
role: input.role,
|
|
86
|
+
input: {
|
|
87
|
+
externalSource: input.item.externalSource,
|
|
88
|
+
parentWorkItemId: input.item.parentWorkItemId,
|
|
89
|
+
title: input.item.title,
|
|
90
|
+
stages: ["intake"],
|
|
91
|
+
sessions: input.item.sessions,
|
|
92
|
+
metadata: input.item.metadata
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build the {@link IntegrationContext} handed to an integration when the
|
|
99
|
+
* factory collects its capabilities (routes, workers). One shape everywhere:
|
|
100
|
+
* `assembleFactoryApiRoutes` uses it per registration, and `MastraFactory` uses it
|
|
101
|
+
* when collecting integration workers at finalize.
|
|
102
|
+
*/
|
|
103
|
+
function buildIntegrationContext(deps, integrationId) {
|
|
104
|
+
return {
|
|
105
|
+
auth: deps.auth,
|
|
106
|
+
fleet: deps.fleet,
|
|
107
|
+
factoryStorage: deps.factoryStorage,
|
|
108
|
+
baseUrl: deps.publicOrigin,
|
|
109
|
+
controller: deps.controller,
|
|
110
|
+
stateSigner: deps.stateSigner,
|
|
111
|
+
storage: {
|
|
112
|
+
generic: deps.integrationStorage.forIntegration(integrationId),
|
|
113
|
+
sourceControl: deps.sourceControlStorage.forIntegration(integrationId),
|
|
114
|
+
projects: deps.domains.projects,
|
|
115
|
+
intake: deps.domains.intake,
|
|
116
|
+
channelIdentity: deps.domains.channelIdentity
|
|
117
|
+
},
|
|
118
|
+
...deps.factoryReady ? { rules: {
|
|
119
|
+
config: deps.rules,
|
|
120
|
+
workItems: deps.domains.workItems
|
|
121
|
+
} } : {},
|
|
122
|
+
...deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Disabled-status stub for the well-known integration ids. The SPA polls
|
|
127
|
+
* `/web/github/status` and `/web/linear/status` unconditionally, so when an
|
|
128
|
+
* integration is absent (or not ready) the status contract must still hold.
|
|
129
|
+
* Unknown custom ids get no stub — the SPA doesn't poll them.
|
|
130
|
+
*/
|
|
131
|
+
function disabledIntegrationStatusRoutes(deps, id, configured = false) {
|
|
132
|
+
if (id === "github") return [registerApiRoute("/web/github/status", {
|
|
133
|
+
method: "GET",
|
|
134
|
+
requiresAuth: false,
|
|
135
|
+
handler: (c) => c.json({
|
|
136
|
+
enabled: false,
|
|
137
|
+
connected: false,
|
|
138
|
+
installations: [],
|
|
139
|
+
reason: "missing_config",
|
|
140
|
+
diagnostics: getGithubFeatureDiagnostics({
|
|
141
|
+
github: void 0,
|
|
142
|
+
auth: deps.auth,
|
|
143
|
+
appDbConfigured: deps.factoryStorage !== void 0,
|
|
144
|
+
stateSigner: deps.stateSigner,
|
|
145
|
+
fleet: deps.fleet
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
})];
|
|
149
|
+
if (id === "linear") return [registerApiRoute("/web/linear/status", {
|
|
150
|
+
method: "GET",
|
|
151
|
+
requiresAuth: false,
|
|
152
|
+
handler: (c) => c.json({
|
|
153
|
+
enabled: false,
|
|
154
|
+
connected: false,
|
|
155
|
+
workspace: null,
|
|
156
|
+
reason: "missing_config",
|
|
157
|
+
diagnostics: {
|
|
158
|
+
linearAppConfigured: configured,
|
|
159
|
+
factoryAuthEnabled: deps.auth.enabled(),
|
|
160
|
+
appDbConfigured: true
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
})];
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Assemble the custom `/web/*` API routes as Mastra `server.apiRoutes`:
|
|
168
|
+
* - fs browser routes (project picker), confined to `fsRoot`
|
|
169
|
+
* - config routes (provider/API-key/model-pack/OM management)
|
|
170
|
+
* - every registered integration's `routes()` surface (full set when ready,
|
|
171
|
+
* disabled-status stub otherwise), plus stubs for absent known ids
|
|
172
|
+
*/
|
|
173
|
+
function assembleFactoryApiRoutes(deps) {
|
|
174
|
+
const emitAudit = (args) => deps.audit.emit(args);
|
|
175
|
+
const registrations = deps.integrations ?? [];
|
|
176
|
+
const githubRegistration = registrations.find(({ integration }) => integration.id === "github");
|
|
177
|
+
const githubStorage = githubRegistration ? deps.sourceControlStorage.forIntegration("github") : void 0;
|
|
178
|
+
const githubIntegration = githubRegistration?.integration;
|
|
179
|
+
const integrationRoutes = registrations.flatMap((registration) => {
|
|
180
|
+
const { integration } = registration;
|
|
181
|
+
if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);
|
|
182
|
+
const context = buildIntegrationContext({
|
|
183
|
+
...deps,
|
|
184
|
+
stateSigner: deps.stateSigner,
|
|
185
|
+
emitAudit
|
|
186
|
+
}, integration.id);
|
|
187
|
+
return guardIntegrationRoutes({
|
|
188
|
+
...registration,
|
|
189
|
+
routes: integration.routes(context)
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
const absentStubs = ["github", "linear"].filter((id) => !registrations.some(({ integration }) => integration.id === id)).flatMap((id) => disabledIntegrationStatusRoutes(deps, id));
|
|
193
|
+
const transitionService = deps.factoryReady ? deps.factoryTransitionService ?? new FactoryTransitionService({
|
|
194
|
+
rules: deps.rules,
|
|
195
|
+
storage: deps.domains.workItems
|
|
196
|
+
}) : void 0;
|
|
197
|
+
const startCoordinator = transitionService ? new FactoryStartCoordinator(deps.controller, deps.domains.workItems, transitionService, githubIntegration?.sourceControlStorage, deps.domains.memorySettings) : void 0;
|
|
198
|
+
if (transitionService && startCoordinator) deps.onFactoryRuntime?.({
|
|
199
|
+
transitionService,
|
|
200
|
+
...githubIntegration ? { prepareBinding: (input) => prepareFactoryRuleBinding(githubIntegration, startCoordinator, input) } : {}
|
|
201
|
+
});
|
|
202
|
+
return [
|
|
203
|
+
...buildFsRoutes({
|
|
204
|
+
root: deps.fsRoot,
|
|
205
|
+
sessionFs: {
|
|
206
|
+
auth: deps.auth,
|
|
207
|
+
fleet: deps.fleet,
|
|
208
|
+
sessions: deps.sourceControlStorage.forIntegration("github").sessions
|
|
209
|
+
}
|
|
210
|
+
}),
|
|
211
|
+
...new ConfigRoutes({
|
|
212
|
+
auth: deps.auth,
|
|
213
|
+
controller: deps.controller,
|
|
214
|
+
authStorage: deps.authStorage,
|
|
215
|
+
modelCredentials: deps.domains.modelCredentials,
|
|
216
|
+
modelPacks: deps.domains.modelPacks,
|
|
217
|
+
memorySettings: deps.domains.memorySettings,
|
|
218
|
+
customProviders: deps.domains.customProviders,
|
|
219
|
+
onCredentialsChanged: invalidateTenantCredentialSnapshots,
|
|
220
|
+
onCustomProvidersChanged: invalidateCustomProvidersSnapshots
|
|
221
|
+
}).routes(),
|
|
222
|
+
...new OAuthRoutes({
|
|
223
|
+
auth: deps.auth,
|
|
224
|
+
authStorage: deps.authStorage,
|
|
225
|
+
modelCredentials: deps.domains.modelCredentials,
|
|
226
|
+
onCredentialsChanged: invalidateTenantCredentialSnapshots
|
|
227
|
+
}).routes(),
|
|
228
|
+
...new SkillRoutes({
|
|
229
|
+
auth: deps.auth,
|
|
230
|
+
controllerId: deps.controllerId,
|
|
231
|
+
controller: deps.controller,
|
|
232
|
+
sourceControlStorage: githubStorage,
|
|
233
|
+
ensureSourceControlReady: githubRegistration?.ensureReady
|
|
234
|
+
}).routes(),
|
|
235
|
+
...integrationRoutes,
|
|
236
|
+
...absentStubs,
|
|
237
|
+
...deps.intakeReady ? new IntakeRoutes({
|
|
238
|
+
auth: deps.auth,
|
|
239
|
+
audit: deps.audit,
|
|
240
|
+
intake: deps.domains.intake,
|
|
241
|
+
integrations: (deps.integrations ?? []).flatMap(({ integration }) => integration.intake ? [{
|
|
242
|
+
id: integration.id,
|
|
243
|
+
intake: integration.intake
|
|
244
|
+
}] : [])
|
|
245
|
+
}).routes() : [],
|
|
246
|
+
...deps.factoryReady ? new WorkItemRoutes({
|
|
247
|
+
auth: deps.auth,
|
|
248
|
+
audit: deps.audit,
|
|
249
|
+
projects: deps.domains.projects,
|
|
250
|
+
workItems: deps.domains.workItems,
|
|
251
|
+
queueHealth: deps.domains.queueHealth,
|
|
252
|
+
transitionService,
|
|
253
|
+
startCoordinator
|
|
254
|
+
}).routes() : []
|
|
255
|
+
];
|
|
256
|
+
}
|
|
257
|
+
//#endregion
|
|
258
|
+
export { assembleFactoryApiRoutes, buildIntegrationContext, factoryRuleBranch };
|
|
259
|
+
|
|
260
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","names":[],"sources":["../../src/routes/surface.ts"],"sourcesContent":["import type { AuthStorage } from '@mastra/code-sdk/auth/storage';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactoryStorage } from '@mastra/core/storage';\n\nimport type { FactoryIntegration, IntegrationContext } from '../integrations/base.js';\nimport { getGithubFeatureDiagnostics } from '../integrations/github/config.js';\nimport { ensureFactoryRuleSession } from '../integrations/github/factory-session.js';\nimport type { GithubIntegration } from '../integrations/github/integration.js';\nimport type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';\nimport { FactoryStartCoordinator } from '../rules/start-coordinator.js';\nimport { FactoryTransitionService } from '../rules/transition-service.js';\nimport type { FactoryRules } from '../rules/types.js';\nimport type { SandboxFleet } from '../sandbox/fleet.js';\nimport type { StateSigner } from '../state-signing.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';\nimport type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport type { CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { IntegrationStorage } from '../storage/domains/integrations/base.js';\nimport type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { ModelPacksStorage } from '../storage/domains/model-packs/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';\nimport type { SourceControlStorage } from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { ConfigRoutes } from './config.js';\nimport { invalidateCustomProvidersSnapshots } from './custom-provider-source.js';\nimport { buildFsRoutes } from './fs.js';\nimport { IntakeRoutes } from './intake.js';\nimport { OAuthRoutes } from './oauth.js';\nimport type { RouteAuth } from './route.js';\nimport { SkillRoutes } from './skills.js';\nimport { invalidateTenantCredentialSnapshots } from './tenant-credentials.js';\nimport { WorkItemRoutes } from './work-items.js';\n\nexport interface IntegrationRegistration {\n integration: FactoryIntegration;\n ready: boolean;\n ensureReady: () => Promise<void>;\n}\n\nexport interface FactoryApiRoutesDeps {\n controllerId: string;\n controller: AgentController<MastraCodeState>;\n /** Request-auth seam threaded from the host (no service locator). */\n auth: RouteAuth;\n authStorage: AuthStorage;\n audit: AuditEmitter;\n fsRoot?: string;\n publicOrigin: string;\n stateSigner?: StateSigner;\n /** Sandbox fleet constructed by the factory (disabled when no machine). */\n fleet: SandboxFleet;\n /** Root factory storage backend (distributed locks, app-db diagnostics). */\n factoryStorage?: FactoryStorage;\n integrationStorage: IntegrationStorage;\n sourceControlStorage: SourceControlStorage;\n /** App-table domain handles, registered and owned by `MastraFactory.prepare()`. */\n domains: {\n intake: IntakeStorage;\n modelCredentials: ModelCredentialsStorage;\n memorySettings: MemorySettingsStorage;\n customProviders: CustomProvidersStorage;\n modelPacks: ModelPacksStorage;\n projects: FactoryProjectsStorage;\n queueHealth: QueueHealthStorage;\n workItems: WorkItemsStorage;\n channelIdentity: ChannelIdentityStorage;\n };\n integrations?: IntegrationRegistration[];\n intakeReady: boolean;\n factoryReady: boolean;\n /** Resolved Factory rule set, threaded from the host (no service locator). */\n rules: FactoryRules;\n factoryTransitionService?: FactoryTransitionService;\n onFactoryRuntime?: (runtime: {\n transitionService: FactoryTransitionService;\n prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n }) => void;\n}\n\nfunction guardIntegrationRoutes({\n integration,\n ready,\n ensureReady,\n routes,\n}: IntegrationRegistration & { routes: ApiRoute[] }): ApiRoute[] {\n if (ready) return routes;\n return routes.map(route => {\n if ('handler' in route) {\n const handler = route.handler;\n return {\n ...route,\n handler: async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context, async () => {});\n },\n };\n }\n\n const createHandler = route.createHandler;\n return {\n ...route,\n createHandler: async (args: Parameters<typeof createHandler>[0]) => {\n const handler = await createHandler(args);\n return async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context);\n };\n },\n };\n });\n}\n\nexport function factoryRuleBranch(item: FactoryBindingPreparationInput['item']): string {\n const metadata = item.metadata ?? {};\n const issueNumber = metadata.githubIssueNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'issue' &&\n typeof issueNumber === 'number'\n ) {\n return `factory/issue-${issueNumber}`;\n }\n const pullRequestNumber = metadata.githubPullRequestNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'pull-request' &&\n typeof pullRequestNumber === 'number'\n ) {\n return `factory/pr-${pullRequestNumber}`;\n }\n throw new Error('Factory skill invocation requires a GitHub issue or pull request number.');\n}\n\nasync function prepareFactoryRuleBinding(\n github: GithubIntegration,\n coordinator: FactoryStartCoordinator,\n input: FactoryBindingPreparationInput,\n): Promise<void> {\n const branch = factoryRuleBranch(input.item);\n const repositorySlug =\n typeof input.item.metadata?.repository === 'string' ? input.item.metadata.repository : undefined;\n const preparedSession = await ensureFactoryRuleSession({\n github,\n orgId: input.record.orgId,\n factoryProjectId: input.record.factoryProjectId,\n repositorySlug,\n branch,\n });\n const destinationStage = input.item.stages.length === 1 ? input.item.stages[0] : undefined;\n if (!destinationStage) throw new Error('Factory skill invocation requires one exclusive board stage.');\n\n await coordinator.prepare({\n orgId: input.record.orgId,\n userId: preparedSession.userId,\n factoryProjectId: input.record.factoryProjectId,\n sessionId: preparedSession.sessionId,\n threadTitle: `${input.role === 'review' ? 'PR' : 'Issue'}: ${input.item.title}`,\n kickoffKey: input.record.id,\n destinationStage: destinationStage as 'intake' | 'triage' | 'planning' | 'execute' | 'review' | 'done',\n workItem: {\n id: input.item.id,\n role: input.role,\n input: {\n externalSource: input.item.externalSource,\n parentWorkItemId: input.item.parentWorkItemId,\n title: input.item.title,\n stages: ['intake'],\n sessions: input.item.sessions,\n metadata: input.item.metadata,\n },\n },\n });\n}\n\n/**\n * Build the {@link IntegrationContext} handed to an integration when the\n * factory collects its capabilities (routes, workers). One shape everywhere:\n * `assembleFactoryApiRoutes` uses it per registration, and `MastraFactory` uses it\n * when collecting integration workers at finalize.\n */\nexport function buildIntegrationContext(\n deps: Pick<\n FactoryApiRoutesDeps,\n 'controller' | 'publicOrigin' | 'auth' | 'fleet' | 'factoryStorage' | 'integrationStorage' | 'sourceControlStorage'\n > & {\n stateSigner: StateSigner;\n emitAudit?: AuditEmitter['emit'];\n rules: FactoryRules;\n factoryReady: boolean;\n domains: Pick<FactoryApiRoutesDeps['domains'], 'projects' | 'intake' | 'workItems' | 'channelIdentity'>;\n },\n integrationId: string,\n): IntegrationContext {\n return {\n auth: deps.auth,\n fleet: deps.fleet,\n factoryStorage: deps.factoryStorage,\n baseUrl: deps.publicOrigin,\n controller: deps.controller,\n stateSigner: deps.stateSigner,\n storage: {\n generic: deps.integrationStorage.forIntegration(integrationId),\n sourceControl: deps.sourceControlStorage.forIntegration(integrationId),\n projects: deps.domains.projects,\n intake: deps.domains.intake,\n channelIdentity: deps.domains.channelIdentity,\n },\n ...(deps.factoryReady ? { rules: { config: deps.rules, workItems: deps.domains.workItems } } : {}),\n ...(deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}),\n };\n}\n\n/**\n * Disabled-status stub for the well-known integration ids. The SPA polls\n * `/web/github/status` and `/web/linear/status` unconditionally, so when an\n * integration is absent (or not ready) the status contract must still hold.\n * Unknown custom ids get no stub — the SPA doesn't poll them.\n */\nfunction disabledIntegrationStatusRoutes(deps: FactoryApiRoutesDeps, id: string, configured = false): ApiRoute[] {\n if (id === 'github') {\n return [\n registerApiRoute('/web/github/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n installations: [],\n reason: 'missing_config',\n diagnostics: getGithubFeatureDiagnostics({\n github: undefined,\n auth: deps.auth,\n appDbConfigured: deps.factoryStorage !== undefined,\n stateSigner: deps.stateSigner,\n fleet: deps.fleet,\n }),\n }),\n }),\n ];\n }\n if (id === 'linear') {\n return [\n registerApiRoute('/web/linear/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n workspace: null,\n reason: 'missing_config',\n diagnostics: {\n linearAppConfigured: configured,\n factoryAuthEnabled: deps.auth.enabled(),\n appDbConfigured: true,\n },\n }),\n }),\n ];\n }\n return [];\n}\n\n/**\n * Assemble the custom `/web/*` API routes as Mastra `server.apiRoutes`:\n * - fs browser routes (project picker), confined to `fsRoot`\n * - config routes (provider/API-key/model-pack/OM management)\n * - every registered integration's `routes()` surface (full set when ready,\n * disabled-status stub otherwise), plus stubs for absent known ids\n */\nexport function assembleFactoryApiRoutes(deps: FactoryApiRoutesDeps): ApiRoute[] {\n const emitAudit: AuditEmitter['emit'] = args => deps.audit.emit(args);\n const registrations = deps.integrations ?? [];\n const githubRegistration = registrations.find(({ integration }) => integration.id === 'github');\n const githubStorage = githubRegistration ? deps.sourceControlStorage.forIntegration('github') : undefined;\n const githubIntegration = githubRegistration?.integration as GithubIntegration | undefined;\n\n const integrationRoutes = registrations.flatMap(registration => {\n const { integration } = registration;\n if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);\n const context = buildIntegrationContext({ ...deps, stateSigner: deps.stateSigner, emitAudit }, integration.id);\n return guardIntegrationRoutes({ ...registration, routes: integration.routes(context) });\n });\n // Absent known integrations still get their disabled-status stub.\n const absentStubs = ['github', 'linear']\n .filter(id => !registrations.some(({ integration }) => integration.id === id))\n .flatMap(id => disabledIntegrationStatusRoutes(deps, id));\n\n const transitionService = deps.factoryReady\n ? (deps.factoryTransitionService ??\n new FactoryTransitionService({ rules: deps.rules, storage: deps.domains.workItems }))\n : undefined;\n const startCoordinator = transitionService\n ? new FactoryStartCoordinator(\n deps.controller,\n deps.domains.workItems,\n transitionService,\n githubIntegration?.sourceControlStorage,\n deps.domains.memorySettings,\n )\n : undefined;\n if (transitionService && startCoordinator) {\n deps.onFactoryRuntime?.({\n transitionService,\n ...(githubIntegration\n ? {\n prepareBinding: (input: FactoryBindingPreparationInput) =>\n prepareFactoryRuleBinding(githubIntegration, startCoordinator, input),\n }\n : {}),\n });\n }\n\n return [\n ...buildFsRoutes({\n root: deps.fsRoot,\n sessionFs: {\n auth: deps.auth,\n fleet: deps.fleet,\n sessions: deps.sourceControlStorage.forIntegration('github').sessions,\n },\n }),\n ...new ConfigRoutes({\n auth: deps.auth,\n controller: deps.controller,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n modelPacks: deps.domains.modelPacks,\n memorySettings: deps.domains.memorySettings,\n customProviders: deps.domains.customProviders,\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n onCustomProvidersChanged: invalidateCustomProvidersSnapshots,\n }).routes(),\n ...new OAuthRoutes({\n auth: deps.auth,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n }).routes(),\n ...new SkillRoutes({\n auth: deps.auth,\n controllerId: deps.controllerId,\n controller: deps.controller,\n sourceControlStorage: githubStorage,\n ensureSourceControlReady: githubRegistration?.ensureReady,\n }).routes(),\n ...integrationRoutes,\n ...absentStubs,\n ...(deps.intakeReady\n ? new IntakeRoutes({\n auth: deps.auth,\n audit: deps.audit,\n intake: deps.domains.intake,\n integrations: (deps.integrations ?? []).flatMap(({ integration }) =>\n integration.intake ? [{ id: integration.id, intake: integration.intake }] : [],\n ),\n }).routes()\n : []),\n ...(deps.factoryReady\n ? new WorkItemRoutes({\n auth: deps.auth,\n audit: deps.audit,\n projects: deps.domains.projects,\n workItems: deps.domains.workItems,\n queueHealth: deps.domains.queueHealth,\n transitionService,\n startCoordinator,\n }).routes()\n : []),\n ];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-tenant credential store for model resolution (deployed mode).
|
|
3
|
+
*
|
|
4
|
+
* The SDK's `resolveModel` asks the registered `CredentialStoreProvider` for a
|
|
5
|
+
* store synchronously, so this module keeps a small per-tenant **snapshot** of
|
|
6
|
+
* resolved credentials (user rows over org rows) hydrated from the
|
|
7
|
+
* `model-credentials` domain. The snapshot serves the gateway's synchronous
|
|
8
|
+
* path-selection reads (`get` / `getStoredApiKey`); the fetch-time
|
|
9
|
+
* `getApiKey` is authoritative — it re-resolves against the domain and
|
|
10
|
+
* refreshes expired OAuth tokens under the domain's row lock, so a slightly
|
|
11
|
+
* stale snapshot can never send an expired token upstream.
|
|
12
|
+
*
|
|
13
|
+
* Snapshots are primed per request by `createTenantCredentialPrimer` (mounted
|
|
14
|
+
* after the web auth gate) so the first model call of a request already sees
|
|
15
|
+
* the caller's credentials. This store explicitly disables the SDK's
|
|
16
|
+
* environment fallback so server-shell credentials never leak into tenants.
|
|
17
|
+
*/
|
|
18
|
+
import type { CredentialTenant as SdkCredentialTenant } from '@mastra/code-sdk/agents/credential-resolver';
|
|
19
|
+
import type { AuthCredential, CredentialStore } from '@mastra/code-sdk/auth/types';
|
|
20
|
+
import type { MiddlewareHandler } from 'hono';
|
|
21
|
+
import type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';
|
|
22
|
+
import type { RouteAuth } from './route.js';
|
|
23
|
+
export declare class TenantCredentialStore implements CredentialStore {
|
|
24
|
+
#private;
|
|
25
|
+
readonly allowEnvironmentFallback = false;
|
|
26
|
+
constructor(orgId: string, userId: string, credentials: ModelCredentialsStorage | undefined);
|
|
27
|
+
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
28
|
+
ensureFresh(now?: number): Promise<void>;
|
|
29
|
+
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
30
|
+
reload(): void;
|
|
31
|
+
get(provider: string): AuthCredential | undefined;
|
|
32
|
+
getStoredApiKey(provider: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Authoritative fetch-time resolution: re-reads the domain (user > org) and
|
|
35
|
+
* refreshes expired OAuth tokens under the domain's row lock. Mirrors
|
|
36
|
+
* `AuthStorage.getApiKey` semantics: `undefined` on missing credential or
|
|
37
|
+
* failed refresh (caller surfaces a re-login error).
|
|
38
|
+
*/
|
|
39
|
+
getApiKey(provider: string): Promise<string | undefined>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Register the web tenant credential store provider with the SDK. Called by
|
|
43
|
+
* the factory after storage init with the `model-credentials` domain handle;
|
|
44
|
+
* from then on `resolveModel` uses per-tenant credentials and the SDK skips
|
|
45
|
+
* the `loadStoredApiKeysIntoEnv` env side-channel.
|
|
46
|
+
*/
|
|
47
|
+
export declare function registerTenantCredentialResolver(credentials: ModelCredentialsStorage): void;
|
|
48
|
+
/** Test hook: clear registration and cached tenant snapshots. */
|
|
49
|
+
export declare function resetTenantCredentialResolverForTests(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Drop cached snapshots after a credential write so the change is visible to
|
|
52
|
+
* the next model call immediately instead of after the snapshot TTL. An org
|
|
53
|
+
* write affects every member's resolved view, so all stores under the org are
|
|
54
|
+
* invalidated; a user write only drops that user's store.
|
|
55
|
+
*/
|
|
56
|
+
export declare function invalidateTenantCredentialSnapshots(tenant: {
|
|
57
|
+
orgId: string;
|
|
58
|
+
userId?: string;
|
|
59
|
+
}): void;
|
|
60
|
+
/**
|
|
61
|
+
* Middleware mounted after the web auth gate: primes the caller's credential
|
|
62
|
+
* snapshot so the request's first model call sees their credentials without an
|
|
63
|
+
* async seam in model resolution. Cheap when fresh (TTL check), best-effort
|
|
64
|
+
* when not — a failed hydrate falls back to env vars, never blocks a request.
|
|
65
|
+
*/
|
|
66
|
+
export declare function primeTenantCredentials({ tenant, credentials, }: {
|
|
67
|
+
tenant: SdkCredentialTenant;
|
|
68
|
+
credentials: ModelCredentialsStorage;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
export declare function createTenantCredentialPrimer({ auth, credentials, }: {
|
|
71
|
+
auth: RouteAuth;
|
|
72
|
+
credentials: ModelCredentialsStorage;
|
|
73
|
+
}): MiddlewareHandler;
|
|
74
|
+
//# sourceMappingURL=tenant-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-credentials.d.ts","sourceRoot":"","sources":["../../src/routes/tenant-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,IAAI,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAG3G,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAG9C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AAEtF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5C,qBAAa,qBAAsB,YAAW,eAAe;;IAC3D,QAAQ,CAAC,wBAAwB,SAAS;gBAQ9B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,uBAAuB,GAAG,SAAS;IAM3F,qEAAqE;IAC/D,WAAW,CAAC,GAAG,SAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBlD,kFAAkF;IAClF,MAAM,IAAI,IAAI;IAMd,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIjD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKrD;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CA+C/D;AAmBD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,uBAAuB,GAAG,IAAI,CAE3F;AAED,iEAAiE;AACjE,wBAAgB,qCAAqC,IAAI,IAAI,CAG5D;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQpG;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,MAAM,EACN,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW,EAAE,uBAAuB,CAAC;CACtC,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhB;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,uBAAuB,CAAC;CACtC,GAAG,iBAAiB,CAYpB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { getTenantCredentialsStorage, tenantOrgId } from "./provider-credentials.js";
|
|
2
|
+
import { isOAuthCredentialExpired } from "../storage/domains/credentials/base.js";
|
|
3
|
+
import { setCredentialStoreProvider } from "@mastra/code-sdk/agents/credential-resolver";
|
|
4
|
+
import { getOAuthProvider } from "@mastra/code-sdk/auth/storage";
|
|
5
|
+
//#region src/routes/tenant-credentials.ts
|
|
6
|
+
/** How long a hydrated snapshot is considered fresh. */
|
|
7
|
+
const SNAPSHOT_TTL_MS = 15e3;
|
|
8
|
+
/** Cap on cached tenant stores; oldest-inserted evicted beyond this. */
|
|
9
|
+
const MAX_CACHED_TENANTS = 1e3;
|
|
10
|
+
var TenantCredentialStore = class {
|
|
11
|
+
allowEnvironmentFallback = false;
|
|
12
|
+
#orgId;
|
|
13
|
+
#userId;
|
|
14
|
+
#credentials;
|
|
15
|
+
#snapshot = /* @__PURE__ */ new Map();
|
|
16
|
+
#fetchedAt = 0;
|
|
17
|
+
#hydrating;
|
|
18
|
+
constructor(orgId, userId, credentials) {
|
|
19
|
+
this.#orgId = orgId;
|
|
20
|
+
this.#userId = userId;
|
|
21
|
+
this.#credentials = credentials;
|
|
22
|
+
}
|
|
23
|
+
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
24
|
+
async ensureFresh(now = Date.now()) {
|
|
25
|
+
if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;
|
|
26
|
+
this.#hydrating ??= this.#hydrate().finally(() => {
|
|
27
|
+
this.#hydrating = void 0;
|
|
28
|
+
});
|
|
29
|
+
await this.#hydrating;
|
|
30
|
+
}
|
|
31
|
+
async #hydrate() {
|
|
32
|
+
const storage = await getTenantCredentialsStorage(this.#credentials);
|
|
33
|
+
if (!storage) return;
|
|
34
|
+
const records = await storage.listCredentials(this.#orgId, this.#userId);
|
|
35
|
+
const next = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const record of records.filter((r) => r.scope === "org")) next.set(record.provider, record.credential);
|
|
37
|
+
for (const record of records.filter((r) => r.scope === "user")) next.set(record.provider, record.credential);
|
|
38
|
+
this.#snapshot = next;
|
|
39
|
+
this.#fetchedAt = Date.now();
|
|
40
|
+
}
|
|
41
|
+
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
42
|
+
reload() {
|
|
43
|
+
if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) this.ensureFresh().catch(() => {});
|
|
44
|
+
}
|
|
45
|
+
get(provider) {
|
|
46
|
+
return this.#snapshot.get(provider);
|
|
47
|
+
}
|
|
48
|
+
getStoredApiKey(provider) {
|
|
49
|
+
const cred = this.#snapshot.get(provider);
|
|
50
|
+
return cred?.type === "api_key" ? cred.key : void 0;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Authoritative fetch-time resolution: re-reads the domain (user > org) and
|
|
54
|
+
* refreshes expired OAuth tokens under the domain's row lock. Mirrors
|
|
55
|
+
* `AuthStorage.getApiKey` semantics: `undefined` on missing credential or
|
|
56
|
+
* failed refresh (caller surfaces a re-login error).
|
|
57
|
+
*/
|
|
58
|
+
async getApiKey(provider) {
|
|
59
|
+
const storage = await getTenantCredentialsStorage(this.#credentials);
|
|
60
|
+
if (!storage) {
|
|
61
|
+
const cred = this.#snapshot.get(provider);
|
|
62
|
+
if (cred?.type === "api_key") return cred.key;
|
|
63
|
+
if (cred?.type === "oauth" && !isOAuthCredentialExpired(cred)) return getOAuthProvider(provider)?.getApiKey(cred);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const resolved = await storage.resolveCredential(this.#orgId, this.#userId, provider);
|
|
67
|
+
if (!resolved) {
|
|
68
|
+
this.#snapshot.delete(provider);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.#snapshot.set(provider, resolved.credential);
|
|
72
|
+
if (resolved.credential.type === "api_key") return resolved.credential.key;
|
|
73
|
+
const oauthProvider = getOAuthProvider(provider);
|
|
74
|
+
if (!oauthProvider) return void 0;
|
|
75
|
+
if (!isOAuthCredentialExpired(resolved.credential)) return oauthProvider.getApiKey(resolved.credential);
|
|
76
|
+
const rowTenant = resolved.scope === "user" ? {
|
|
77
|
+
orgId: this.#orgId,
|
|
78
|
+
userId: this.#userId
|
|
79
|
+
} : { orgId: this.#orgId };
|
|
80
|
+
try {
|
|
81
|
+
const refreshed = await storage.refreshOAuth(rowTenant, provider, async (current) => ({
|
|
82
|
+
type: "oauth",
|
|
83
|
+
...await oauthProvider.refreshToken(current)
|
|
84
|
+
}));
|
|
85
|
+
if (!refreshed) return void 0;
|
|
86
|
+
this.#snapshot.set(provider, refreshed);
|
|
87
|
+
return oauthProvider.getApiKey(refreshed);
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const tenantStores = /* @__PURE__ */ new Map();
|
|
94
|
+
function storeFor(tenant, credentials) {
|
|
95
|
+
const orgId = tenantOrgId(tenant);
|
|
96
|
+
const key = `${orgId}\u0000${tenant.userId}`;
|
|
97
|
+
let store = tenantStores.get(key);
|
|
98
|
+
if (!store) {
|
|
99
|
+
if (tenantStores.size >= MAX_CACHED_TENANTS) {
|
|
100
|
+
const oldest = tenantStores.keys().next().value;
|
|
101
|
+
if (oldest !== void 0) tenantStores.delete(oldest);
|
|
102
|
+
}
|
|
103
|
+
store = new TenantCredentialStore(orgId, tenant.userId, credentials);
|
|
104
|
+
tenantStores.set(key, store);
|
|
105
|
+
}
|
|
106
|
+
return store;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Register the web tenant credential store provider with the SDK. Called by
|
|
110
|
+
* the factory after storage init with the `model-credentials` domain handle;
|
|
111
|
+
* from then on `resolveModel` uses per-tenant credentials and the SDK skips
|
|
112
|
+
* the `loadStoredApiKeysIntoEnv` env side-channel.
|
|
113
|
+
*/
|
|
114
|
+
function registerTenantCredentialResolver(credentials) {
|
|
115
|
+
setCredentialStoreProvider((tenant) => storeFor(tenant, credentials));
|
|
116
|
+
}
|
|
117
|
+
/** Test hook: clear registration and cached tenant snapshots. */
|
|
118
|
+
function resetTenantCredentialResolverForTests() {
|
|
119
|
+
setCredentialStoreProvider(void 0);
|
|
120
|
+
tenantStores.clear();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Drop cached snapshots after a credential write so the change is visible to
|
|
124
|
+
* the next model call immediately instead of after the snapshot TTL. An org
|
|
125
|
+
* write affects every member's resolved view, so all stores under the org are
|
|
126
|
+
* invalidated; a user write only drops that user's store.
|
|
127
|
+
*/
|
|
128
|
+
function invalidateTenantCredentialSnapshots(tenant) {
|
|
129
|
+
if (tenant.userId) {
|
|
130
|
+
tenantStores.delete(`${tenant.orgId}\u0000${tenant.userId}`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
for (const key of tenantStores.keys()) if (key.startsWith(`${tenant.orgId}\u0000`)) tenantStores.delete(key);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Middleware mounted after the web auth gate: primes the caller's credential
|
|
137
|
+
* snapshot so the request's first model call sees their credentials without an
|
|
138
|
+
* async seam in model resolution. Cheap when fresh (TTL check), best-effort
|
|
139
|
+
* when not — a failed hydrate falls back to env vars, never blocks a request.
|
|
140
|
+
*/
|
|
141
|
+
async function primeTenantCredentials({ tenant, credentials }) {
|
|
142
|
+
await storeFor(tenant, credentials).ensureFresh();
|
|
143
|
+
}
|
|
144
|
+
function createTenantCredentialPrimer({ auth, credentials }) {
|
|
145
|
+
return async (c, next) => {
|
|
146
|
+
const tenant = auth.tenant(c);
|
|
147
|
+
if (tenant) try {
|
|
148
|
+
await storeFor(tenant, credentials).ensureFresh();
|
|
149
|
+
} catch {}
|
|
150
|
+
await next();
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { TenantCredentialStore, createTenantCredentialPrimer, invalidateTenantCredentialSnapshots, primeTenantCredentials, registerTenantCredentialResolver, resetTenantCredentialResolverForTests };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=tenant-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-credentials.js","names":["#orgId","#userId","#credentials","#fetchedAt","#hydrating","#hydrate","#snapshot"],"sources":["../../src/routes/tenant-credentials.ts"],"sourcesContent":["/**\n * Per-tenant credential store for model resolution (deployed mode).\n *\n * The SDK's `resolveModel` asks the registered `CredentialStoreProvider` for a\n * store synchronously, so this module keeps a small per-tenant **snapshot** of\n * resolved credentials (user rows over org rows) hydrated from the\n * `model-credentials` domain. The snapshot serves the gateway's synchronous\n * path-selection reads (`get` / `getStoredApiKey`); the fetch-time\n * `getApiKey` is authoritative — it re-resolves against the domain and\n * refreshes expired OAuth tokens under the domain's row lock, so a slightly\n * stale snapshot can never send an expired token upstream.\n *\n * Snapshots are primed per request by `createTenantCredentialPrimer` (mounted\n * after the web auth gate) so the first model call of a request already sees\n * the caller's credentials. This store explicitly disables the SDK's\n * environment fallback so server-shell credentials never leak into tenants.\n */\n\nimport type { CredentialTenant as SdkCredentialTenant } from '@mastra/code-sdk/agents/credential-resolver';\nimport { setCredentialStoreProvider } from '@mastra/code-sdk/agents/credential-resolver';\nimport { getOAuthProvider } from '@mastra/code-sdk/auth/storage';\nimport type { AuthCredential, CredentialStore } from '@mastra/code-sdk/auth/types';\nimport type { MiddlewareHandler } from 'hono';\n\nimport { isOAuthCredentialExpired } from '../storage/domains/credentials/base.js';\nimport type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport { getTenantCredentialsStorage, tenantOrgId } from './provider-credentials.js';\nimport type { RouteAuth } from './route.js';\n\n/** How long a hydrated snapshot is considered fresh. */\nconst SNAPSHOT_TTL_MS = 15_000;\n\n/** Cap on cached tenant stores; oldest-inserted evicted beyond this. */\nconst MAX_CACHED_TENANTS = 1000;\n\nexport class TenantCredentialStore implements CredentialStore {\n readonly allowEnvironmentFallback = false;\n readonly #orgId: string;\n readonly #userId: string;\n readonly #credentials: ModelCredentialsStorage | undefined;\n #snapshot = new Map<string, AuthCredential>();\n #fetchedAt = 0;\n #hydrating: Promise<void> | undefined;\n\n constructor(orgId: string, userId: string, credentials: ModelCredentialsStorage | undefined) {\n this.#orgId = orgId;\n this.#userId = userId;\n this.#credentials = credentials;\n }\n\n /** Hydrate the snapshot when stale; coalesces concurrent callers. */\n async ensureFresh(now = Date.now()): Promise<void> {\n if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;\n this.#hydrating ??= this.#hydrate().finally(() => {\n this.#hydrating = undefined;\n });\n await this.#hydrating;\n }\n\n async #hydrate(): Promise<void> {\n const storage = await getTenantCredentialsStorage(this.#credentials);\n if (!storage) return; // Keep the last tenant-scoped snapshot.\n const records = await storage.listCredentials(this.#orgId, this.#userId);\n const next = new Map<string, AuthCredential>();\n // Org rows first so user rows overwrite them (user > org precedence).\n for (const record of records.filter(r => r.scope === 'org')) {\n next.set(record.provider, record.credential);\n }\n for (const record of records.filter(r => r.scope === 'user')) {\n next.set(record.provider, record.credential);\n }\n this.#snapshot = next;\n this.#fetchedAt = Date.now();\n }\n\n /** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */\n reload(): void {\n if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) {\n void this.ensureFresh().catch(() => {});\n }\n }\n\n get(provider: string): AuthCredential | undefined {\n return this.#snapshot.get(provider);\n }\n\n getStoredApiKey(provider: string): string | undefined {\n const cred = this.#snapshot.get(provider);\n return cred?.type === 'api_key' ? cred.key : undefined;\n }\n\n /**\n * Authoritative fetch-time resolution: re-reads the domain (user > org) and\n * refreshes expired OAuth tokens under the domain's row lock. Mirrors\n * `AuthStorage.getApiKey` semantics: `undefined` on missing credential or\n * failed refresh (caller surfaces a re-login error).\n */\n async getApiKey(provider: string): Promise<string | undefined> {\n const storage = await getTenantCredentialsStorage(this.#credentials);\n if (!storage) {\n // Domain unavailable: best effort from the snapshot; expired OAuth\n // tokens cannot be refreshed without the domain's lock.\n const cred = this.#snapshot.get(provider);\n if (cred?.type === 'api_key') return cred.key;\n if (cred?.type === 'oauth' && !isOAuthCredentialExpired(cred)) {\n return getOAuthProvider(provider)?.getApiKey(cred);\n }\n return undefined;\n }\n\n const resolved = await storage.resolveCredential(this.#orgId, this.#userId, provider);\n if (!resolved) {\n this.#snapshot.delete(provider);\n return undefined;\n }\n this.#snapshot.set(provider, resolved.credential);\n\n if (resolved.credential.type === 'api_key') {\n return resolved.credential.key;\n }\n\n const oauthProvider = getOAuthProvider(provider);\n if (!oauthProvider) return undefined;\n\n if (!isOAuthCredentialExpired(resolved.credential)) {\n return oauthProvider.getApiKey(resolved.credential);\n }\n\n // Refresh at the scope the credential actually lives at (OAuth rows are\n // user-scoped by policy, but resolve defensively from the record).\n const rowTenant = resolved.scope === 'user' ? { orgId: this.#orgId, userId: this.#userId } : { orgId: this.#orgId };\n try {\n const refreshed = await storage.refreshOAuth(rowTenant, provider, async current => ({\n type: 'oauth' as const,\n ...(await oauthProvider.refreshToken(current)),\n }));\n if (!refreshed) return undefined;\n this.#snapshot.set(provider, refreshed);\n return oauthProvider.getApiKey(refreshed);\n } catch {\n // Refresh failed — user needs to re-login (same posture as AuthStorage).\n return undefined;\n }\n }\n}\n\nconst tenantStores = new Map<string, TenantCredentialStore>();\n\nfunction storeFor(tenant: SdkCredentialTenant, credentials: ModelCredentialsStorage): TenantCredentialStore {\n const orgId = tenantOrgId(tenant);\n const key = `${orgId}\\u0000${tenant.userId}`;\n let store = tenantStores.get(key);\n if (!store) {\n if (tenantStores.size >= MAX_CACHED_TENANTS) {\n const oldest = tenantStores.keys().next().value;\n if (oldest !== undefined) tenantStores.delete(oldest);\n }\n store = new TenantCredentialStore(orgId, tenant.userId, credentials);\n tenantStores.set(key, store);\n }\n return store;\n}\n\n/**\n * Register the web tenant credential store provider with the SDK. Called by\n * the factory after storage init with the `model-credentials` domain handle;\n * from then on `resolveModel` uses per-tenant credentials and the SDK skips\n * the `loadStoredApiKeysIntoEnv` env side-channel.\n */\nexport function registerTenantCredentialResolver(credentials: ModelCredentialsStorage): void {\n setCredentialStoreProvider(tenant => storeFor(tenant, credentials));\n}\n\n/** Test hook: clear registration and cached tenant snapshots. */\nexport function resetTenantCredentialResolverForTests(): void {\n setCredentialStoreProvider(undefined);\n tenantStores.clear();\n}\n\n/**\n * Drop cached snapshots after a credential write so the change is visible to\n * the next model call immediately instead of after the snapshot TTL. An org\n * write affects every member's resolved view, so all stores under the org are\n * invalidated; a user write only drops that user's store.\n */\nexport function invalidateTenantCredentialSnapshots(tenant: { orgId: string; userId?: string }): void {\n if (tenant.userId) {\n tenantStores.delete(`${tenant.orgId}\\u0000${tenant.userId}`);\n return;\n }\n for (const key of tenantStores.keys()) {\n if (key.startsWith(`${tenant.orgId}\\u0000`)) tenantStores.delete(key);\n }\n}\n\n/**\n * Middleware mounted after the web auth gate: primes the caller's credential\n * snapshot so the request's first model call sees their credentials without an\n * async seam in model resolution. Cheap when fresh (TTL check), best-effort\n * when not — a failed hydrate falls back to env vars, never blocks a request.\n */\nexport async function primeTenantCredentials({\n tenant,\n credentials,\n}: {\n tenant: SdkCredentialTenant;\n credentials: ModelCredentialsStorage;\n}): Promise<void> {\n await storeFor(tenant, credentials).ensureFresh();\n}\n\nexport function createTenantCredentialPrimer({\n auth,\n credentials,\n}: {\n auth: RouteAuth;\n credentials: ModelCredentialsStorage;\n}): MiddlewareHandler {\n return async (c, next) => {\n const tenant = auth.tenant(c);\n if (tenant) {\n try {\n await storeFor(tenant, credentials).ensureFresh();\n } catch {\n // Fail open: model calls fall back to env credentials.\n }\n }\n await next();\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra `apiRoutes` for Factory work items (the kanban board).
|
|
3
|
+
*
|
|
4
|
+
* Registered alongside the other `/web/*` routes behind the host auth gate.
|
|
5
|
+
* The board is org-wide: every route re-resolves the caller's `(orgId, userId)`
|
|
6
|
+
* tenant and scopes reads/writes by `orgId`, so any org member sees and moves
|
|
7
|
+
* the same cards while `created_by` / stage history record who acted.
|
|
8
|
+
*/
|
|
9
|
+
import type { ApiRoute } from '@mastra/core/server';
|
|
10
|
+
import type { FactoryStartCoordinator } from '../rules/start-coordinator.js';
|
|
11
|
+
import type { FactoryTransitionService } from '../rules/transition-service.js';
|
|
12
|
+
import type { AuditEmitter } from '../storage/domains/audit/domain.js';
|
|
13
|
+
import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
14
|
+
import type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';
|
|
15
|
+
import type { CreateWorkItemInput, UpdateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
16
|
+
import type { RouteDependencies } from './route.js';
|
|
17
|
+
import { Route } from './route.js';
|
|
18
|
+
export interface WorkItemRoutesDeps extends RouteDependencies {
|
|
19
|
+
audit: AuditEmitter;
|
|
20
|
+
/** Factory projects domain — validates the `:id` project belongs to the caller's org. */
|
|
21
|
+
projects: FactoryProjectsStorage;
|
|
22
|
+
/** Work-items domain backing the kanban board. */
|
|
23
|
+
workItems: WorkItemsStorage;
|
|
24
|
+
/** Per-project queue-health threshold config. */
|
|
25
|
+
queueHealth: QueueHealthStorage;
|
|
26
|
+
/** Governed stage-transition service. Stage moves 503 when absent. */
|
|
27
|
+
transitionService?: Pick<FactoryTransitionService, 'transition' | 'ruleSetVersion'>;
|
|
28
|
+
/** Coordinator that binds a Factory run before dispatching its kickoff. */
|
|
29
|
+
startCoordinator?: Pick<FactoryStartCoordinator, 'prepare'>;
|
|
30
|
+
}
|
|
31
|
+
/** Validate an untrusted create body. Unknown keys are dropped. */
|
|
32
|
+
export declare function parseCreateWorkItem(body: unknown): CreateWorkItemInput | null;
|
|
33
|
+
/** Validate an untrusted patch body. Unknown keys are dropped. */
|
|
34
|
+
export declare function parseUpdateWorkItem(body: unknown): UpdateWorkItemInput | null;
|
|
35
|
+
export declare class WorkItemRoutes extends Route<WorkItemRoutesDeps> {
|
|
36
|
+
#private;
|
|
37
|
+
/** Build the Factory work-item routes as Mastra `apiRoutes`. */
|
|
38
|
+
routes(): ApiRoute[];
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=work-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-items.d.ts","sourceRoot":"","sources":["../../src/routes/work-items.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EACV,uBAAuB,EAGxB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAA4B,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAGzG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAElF,OAAO,KAAK,EACV,mBAAmB,EAInB,mBAAmB,EAKnB,gBAAgB,EACjB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,KAAK,EAAE,YAAY,CAAC;IACpB,yFAAyF;IACzF,QAAQ,EAAE,sBAAsB,CAAC;IACjC,kDAAkD;IAClD,SAAS,EAAE,gBAAgB,CAAC;IAC5B,iDAAiD;IACjD,WAAW,EAAE,kBAAkB,CAAC;IAChC,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,GAAG,gBAAgB,CAAC,CAAC;IACpF,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC;CAC7D;AAqED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAuB7E;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CA4B7E;AAmLD,qBAAa,cAAe,SAAQ,KAAK,CAAC,kBAAkB,CAAC;;IA0G3D,gEAAgE;IAChE,MAAM,IAAI,QAAQ,EAAE;CAuWrB"}
|