@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,463 @@
|
|
|
1
|
+
import { getAuthProviderId, resolveCredentialContext } from "./provider-credentials.js";
|
|
2
|
+
import { Route } from "./route.js";
|
|
3
|
+
import { ModelCredentialsStorage } from "../storage/domains/credentials/base.js";
|
|
4
|
+
import { registerApiRoute } from "@mastra/core/server";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import { nextPollDelayMs } from "@mastra/code-sdk/auth/device-code";
|
|
7
|
+
import { completeAnthropicLogin, startAnthropicLogin } from "@mastra/code-sdk/auth/providers/anthropic";
|
|
8
|
+
import { copilotNextPollDelayMs, pollGitHubCopilotDeviceLogin, startGitHubCopilotDeviceLogin } from "@mastra/code-sdk/auth/providers/github-copilot";
|
|
9
|
+
import { pollCodexDeviceLogin, startCodexDeviceLogin } from "@mastra/code-sdk/auth/providers/openai-codex";
|
|
10
|
+
import { pollXAIDeviceLogin, startXAIDeviceLogin } from "@mastra/code-sdk/auth/providers/xai";
|
|
11
|
+
//#region src/routes/oauth.ts
|
|
12
|
+
/**
|
|
13
|
+
* Web OAuth sign-in routes for model providers (Settings › Providers).
|
|
14
|
+
*
|
|
15
|
+
* Wraps the SDK's step-based OAuth primitives (start/complete for Anthropic's
|
|
16
|
+
* paste-code PKCE flow, start/poll for the Codex/Copilot/xAI device flows) in
|
|
17
|
+
* HTTP routes. Flow state lives in login sessions — the `model-credentials`
|
|
18
|
+
* domain's `oauth_login_sessions` table in tenant mode (any replica can
|
|
19
|
+
* complete/poll), an in-memory store in local mode — so a flow can span
|
|
20
|
+
* requests. Completed credentials are always **user-scoped** (plan tokens are
|
|
21
|
+
* personal subscriptions) in tenant mode, or written to the file-backed
|
|
22
|
+
* `AuthStorage` in local mode.
|
|
23
|
+
*
|
|
24
|
+
* Tokens never leave the server; responses only carry flow metadata (URLs,
|
|
25
|
+
* user codes, poll delays).
|
|
26
|
+
*/
|
|
27
|
+
/** Lifetime of a paste-code session (Anthropic gives no explicit expiry). */
|
|
28
|
+
const PASTE_CODE_TTL_MS = 600 * 1e3;
|
|
29
|
+
/** Web OAuth flows keyed by *catalog* provider id (mirrors {@link WEB_OAUTH_FLOW_KINDS}). */
|
|
30
|
+
const OAUTH_FLOWS = {
|
|
31
|
+
anthropic: {
|
|
32
|
+
kind: "paste-code",
|
|
33
|
+
start: async () => {
|
|
34
|
+
const { url, verifier } = await startAnthropicLogin();
|
|
35
|
+
return {
|
|
36
|
+
url,
|
|
37
|
+
instructions: "Open the link, authorize, then paste the code shown on the final page.",
|
|
38
|
+
expiresAt: Date.now() + PASTE_CODE_TTL_MS,
|
|
39
|
+
pending: { verifier }
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
complete: (pending, code) => completeAnthropicLogin(code, String(pending.verifier ?? ""))
|
|
43
|
+
},
|
|
44
|
+
openai: {
|
|
45
|
+
kind: "device-code",
|
|
46
|
+
start: async () => {
|
|
47
|
+
const p = await startCodexDeviceLogin();
|
|
48
|
+
return {
|
|
49
|
+
url: p.url,
|
|
50
|
+
userCode: p.userCode,
|
|
51
|
+
instructions: p.instructions,
|
|
52
|
+
expiresAt: p.deadlineAt,
|
|
53
|
+
nextPollMs: p.intervalMs,
|
|
54
|
+
pending: { ...p }
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
poll: async (pending) => {
|
|
58
|
+
const r = await pollCodexDeviceLogin(pending);
|
|
59
|
+
if (r.status === "complete") return {
|
|
60
|
+
status: "complete",
|
|
61
|
+
credentials: r.credentials
|
|
62
|
+
};
|
|
63
|
+
if (r.status === "pending") return {
|
|
64
|
+
status: "pending",
|
|
65
|
+
nextPollMs: r.nextPollMs
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
status: "failed",
|
|
69
|
+
error: r.error
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"github-copilot": {
|
|
74
|
+
kind: "device-code",
|
|
75
|
+
start: async () => {
|
|
76
|
+
const p = await startGitHubCopilotDeviceLogin();
|
|
77
|
+
return {
|
|
78
|
+
url: p.url,
|
|
79
|
+
userCode: p.userCode,
|
|
80
|
+
instructions: p.instructions,
|
|
81
|
+
expiresAt: p.deadlineAt,
|
|
82
|
+
nextPollMs: copilotNextPollDelayMs(p),
|
|
83
|
+
pending: { ...p }
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
poll: async (pending) => {
|
|
87
|
+
const p = pending;
|
|
88
|
+
if (p.domain !== "github.com" || p.enterpriseDomain !== void 0) return {
|
|
89
|
+
status: "failed",
|
|
90
|
+
error: "Unsupported GitHub host"
|
|
91
|
+
};
|
|
92
|
+
const r = await pollGitHubCopilotDeviceLogin(p);
|
|
93
|
+
if (r.status === "complete") return {
|
|
94
|
+
status: "complete",
|
|
95
|
+
credentials: r.credentials
|
|
96
|
+
};
|
|
97
|
+
if (r.status === "pending") return {
|
|
98
|
+
status: "pending",
|
|
99
|
+
nextPollMs: r.nextPollMs,
|
|
100
|
+
pending: { ...r.pending }
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
status: "failed",
|
|
104
|
+
error: r.error
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
xai: {
|
|
109
|
+
kind: "device-code",
|
|
110
|
+
start: async () => {
|
|
111
|
+
const p = await startXAIDeviceLogin();
|
|
112
|
+
return {
|
|
113
|
+
url: p.url,
|
|
114
|
+
userCode: p.userCode,
|
|
115
|
+
instructions: p.instructions,
|
|
116
|
+
expiresAt: p.state.deadlineAt,
|
|
117
|
+
nextPollMs: nextPollDelayMs(p.state),
|
|
118
|
+
pending: { ...p }
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
poll: async (pending) => {
|
|
122
|
+
const r = await pollXAIDeviceLogin(pending);
|
|
123
|
+
if (r.status === "complete") return {
|
|
124
|
+
status: "complete",
|
|
125
|
+
credentials: r.credentials
|
|
126
|
+
};
|
|
127
|
+
if (r.status === "pending") return {
|
|
128
|
+
status: "pending",
|
|
129
|
+
nextPollMs: r.nextPollMs,
|
|
130
|
+
pending: { ...r.pending }
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
status: "failed",
|
|
134
|
+
error: r.error
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
function loose(c) {
|
|
140
|
+
return c;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Local-mode login sessions. Flows in local mode are single-process, so a
|
|
144
|
+
* process-local libsql `:memory:` database (which already handles TTL
|
|
145
|
+
* cleanup) is sufficient.
|
|
146
|
+
*/
|
|
147
|
+
let localSessionsPromise;
|
|
148
|
+
function localSessions() {
|
|
149
|
+
localSessionsPromise ??= (async () => {
|
|
150
|
+
const { LibSQLFactoryStorage } = await import("@mastra/libsql");
|
|
151
|
+
const storage = new LibSQLFactoryStorage({
|
|
152
|
+
id: "local-oauth-sessions",
|
|
153
|
+
url: ":memory:"
|
|
154
|
+
});
|
|
155
|
+
const store = storage.registerDomain(new ModelCredentialsStorage());
|
|
156
|
+
await storage.init();
|
|
157
|
+
return store;
|
|
158
|
+
})();
|
|
159
|
+
return localSessionsPromise;
|
|
160
|
+
}
|
|
161
|
+
const LOCAL_TENANT = {
|
|
162
|
+
orgId: "local",
|
|
163
|
+
userId: "local"
|
|
164
|
+
};
|
|
165
|
+
async function sessionStore(ctx) {
|
|
166
|
+
return ctx.mode === "tenant" ? ctx.storage : localSessions();
|
|
167
|
+
}
|
|
168
|
+
function sessionTenant(ctx) {
|
|
169
|
+
return ctx.mode === "tenant" ? {
|
|
170
|
+
orgId: ctx.orgId,
|
|
171
|
+
userId: ctx.userId
|
|
172
|
+
} : LOCAL_TENANT;
|
|
173
|
+
}
|
|
174
|
+
/** Load a session and verify it belongs to the caller + provider (else undefined). */
|
|
175
|
+
async function loadOwnedSession({ ctx, provider, sessionId }) {
|
|
176
|
+
const session = await (await sessionStore(ctx)).getLoginSession(sessionId);
|
|
177
|
+
if (!session) return void 0;
|
|
178
|
+
const tenant = sessionTenant(ctx);
|
|
179
|
+
if (session.orgId !== tenant.orgId || session.userId !== tenant.userId) return void 0;
|
|
180
|
+
if (session.provider !== provider) return void 0;
|
|
181
|
+
return session;
|
|
182
|
+
}
|
|
183
|
+
/** Persist completed OAuth credentials — always user-scoped in tenant mode. */
|
|
184
|
+
async function persistOAuthCredential({ ctx, provider, credentials, authStorage, onCredentialsChanged }) {
|
|
185
|
+
const authProviderId = getAuthProviderId(provider);
|
|
186
|
+
if (ctx.mode === "tenant") {
|
|
187
|
+
await ctx.storage.setCredential({
|
|
188
|
+
orgId: ctx.orgId,
|
|
189
|
+
userId: ctx.userId
|
|
190
|
+
}, authProviderId, {
|
|
191
|
+
type: "oauth",
|
|
192
|
+
...credentials
|
|
193
|
+
});
|
|
194
|
+
onCredentialsChanged({
|
|
195
|
+
orgId: ctx.orgId,
|
|
196
|
+
userId: ctx.userId
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (!authStorage) throw new Error("Credential storage is not available");
|
|
201
|
+
authStorage.set(authProviderId, {
|
|
202
|
+
type: "oauth",
|
|
203
|
+
...credentials
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function readJsonBody(c) {
|
|
207
|
+
try {
|
|
208
|
+
const body = await c.req.json();
|
|
209
|
+
return body && typeof body === "object" ? body : {};
|
|
210
|
+
} catch {
|
|
211
|
+
return {};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Provider OAuth sign-in routes as Mastra `apiRoutes`:
|
|
216
|
+
* - `POST /web/config/providers/:provider/oauth/start` — begin a sign-in flow
|
|
217
|
+
* - `POST /web/config/providers/:provider/oauth/complete` — paste-code exchange
|
|
218
|
+
* - `POST /web/config/providers/:provider/oauth/poll` — one device-code poll
|
|
219
|
+
* - `DELETE /web/config/providers/:provider/oauth/session/:sessionId` — cancel a flow
|
|
220
|
+
* - `DELETE /web/config/providers/:provider/oauth` — sign out (caller only)
|
|
221
|
+
*/
|
|
222
|
+
var OAuthRoutes = class extends Route {
|
|
223
|
+
routes() {
|
|
224
|
+
const { auth, authStorage, modelCredentials } = this.deps;
|
|
225
|
+
const onCredentialsChanged = this.deps.onCredentialsChanged ?? (() => {});
|
|
226
|
+
return [
|
|
227
|
+
registerApiRoute("/web/config/providers/:provider/oauth/start", {
|
|
228
|
+
method: "POST",
|
|
229
|
+
requiresAuth: false,
|
|
230
|
+
handler: async (c) => {
|
|
231
|
+
const ctx = await resolveCredentialContext({
|
|
232
|
+
c: loose(c),
|
|
233
|
+
auth,
|
|
234
|
+
credentials: modelCredentials
|
|
235
|
+
});
|
|
236
|
+
if ("response" in ctx) return ctx.response;
|
|
237
|
+
const provider = c.req.param("provider");
|
|
238
|
+
const flow = OAUTH_FLOWS[provider];
|
|
239
|
+
if (!flow) return c.json({
|
|
240
|
+
error: "oauth_not_supported",
|
|
241
|
+
message: `Provider does not support web sign-in`
|
|
242
|
+
}, 404);
|
|
243
|
+
const body = await readJsonBody(loose(c));
|
|
244
|
+
if (typeof body.mode === "string" && body.mode !== flow.kind) return c.json({
|
|
245
|
+
error: "invalid_mode",
|
|
246
|
+
message: `Unsupported mode for ${provider}: ${body.mode}`
|
|
247
|
+
}, 400);
|
|
248
|
+
let started;
|
|
249
|
+
try {
|
|
250
|
+
started = await flow.start();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 502);
|
|
253
|
+
}
|
|
254
|
+
const sessionId = randomUUID();
|
|
255
|
+
const tenant = sessionTenant(ctx);
|
|
256
|
+
await (await sessionStore(ctx)).createLoginSession({
|
|
257
|
+
sessionId,
|
|
258
|
+
orgId: tenant.orgId,
|
|
259
|
+
userId: tenant.userId,
|
|
260
|
+
provider,
|
|
261
|
+
kind: flow.kind,
|
|
262
|
+
pending: started.pending,
|
|
263
|
+
expiresAt: new Date(started.expiresAt),
|
|
264
|
+
nextPollAt: started.nextPollMs != null ? new Date(Date.now() + started.nextPollMs) : null
|
|
265
|
+
});
|
|
266
|
+
return c.json({
|
|
267
|
+
sessionId,
|
|
268
|
+
kind: flow.kind,
|
|
269
|
+
url: started.url,
|
|
270
|
+
...started.userCode ? { userCode: started.userCode } : {},
|
|
271
|
+
instructions: started.instructions,
|
|
272
|
+
expiresAt: started.expiresAt,
|
|
273
|
+
...started.nextPollMs != null ? { nextPollMs: started.nextPollMs } : {}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}),
|
|
277
|
+
registerApiRoute("/web/config/providers/:provider/oauth/complete", {
|
|
278
|
+
method: "POST",
|
|
279
|
+
requiresAuth: false,
|
|
280
|
+
handler: async (c) => {
|
|
281
|
+
const ctx = await resolveCredentialContext({
|
|
282
|
+
c: loose(c),
|
|
283
|
+
auth,
|
|
284
|
+
credentials: modelCredentials
|
|
285
|
+
});
|
|
286
|
+
if ("response" in ctx) return ctx.response;
|
|
287
|
+
const provider = c.req.param("provider");
|
|
288
|
+
const flow = OAUTH_FLOWS[provider];
|
|
289
|
+
if (!flow?.complete) return c.json({ error: "oauth_not_supported" }, 404);
|
|
290
|
+
const body = await readJsonBody(loose(c));
|
|
291
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
|
|
292
|
+
const code = typeof body.code === "string" ? body.code.trim() : "";
|
|
293
|
+
if (!sessionId || !code) return c.json({ error: "Missing required fields: sessionId, code" }, 400);
|
|
294
|
+
const session = await loadOwnedSession({
|
|
295
|
+
ctx,
|
|
296
|
+
provider,
|
|
297
|
+
sessionId
|
|
298
|
+
});
|
|
299
|
+
if (!session) return c.json({ error: "session_not_found" }, 404);
|
|
300
|
+
if (session.kind !== "paste-code") return c.json({ error: "wrong_session_kind" }, 400);
|
|
301
|
+
const claimed = await (await sessionStore(ctx)).claimLoginSession(sessionId, {
|
|
302
|
+
orgId: session.orgId,
|
|
303
|
+
userId: session.userId,
|
|
304
|
+
provider,
|
|
305
|
+
kind: "paste-code"
|
|
306
|
+
});
|
|
307
|
+
if (!claimed) return c.json({ error: "oauth_in_progress" }, 409);
|
|
308
|
+
let credentials;
|
|
309
|
+
try {
|
|
310
|
+
credentials = await flow.complete(claimed.pending, code);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });
|
|
313
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);
|
|
314
|
+
}
|
|
315
|
+
await persistOAuthCredential({
|
|
316
|
+
ctx,
|
|
317
|
+
provider,
|
|
318
|
+
credentials,
|
|
319
|
+
authStorage,
|
|
320
|
+
onCredentialsChanged
|
|
321
|
+
});
|
|
322
|
+
await (await sessionStore(ctx)).deleteLoginSession(sessionId);
|
|
323
|
+
return c.json({
|
|
324
|
+
status: "complete",
|
|
325
|
+
ok: true
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}),
|
|
329
|
+
registerApiRoute("/web/config/providers/:provider/oauth/poll", {
|
|
330
|
+
method: "POST",
|
|
331
|
+
requiresAuth: false,
|
|
332
|
+
handler: async (c) => {
|
|
333
|
+
const ctx = await resolveCredentialContext({
|
|
334
|
+
c: loose(c),
|
|
335
|
+
auth,
|
|
336
|
+
credentials: modelCredentials
|
|
337
|
+
});
|
|
338
|
+
if ("response" in ctx) return ctx.response;
|
|
339
|
+
const provider = c.req.param("provider");
|
|
340
|
+
const flow = OAUTH_FLOWS[provider];
|
|
341
|
+
if (!flow?.poll) return c.json({ error: "oauth_not_supported" }, 404);
|
|
342
|
+
const body = await readJsonBody(loose(c));
|
|
343
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
|
|
344
|
+
if (!sessionId) return c.json({ error: "Missing required field: sessionId" }, 400);
|
|
345
|
+
const session = await loadOwnedSession({
|
|
346
|
+
ctx,
|
|
347
|
+
provider,
|
|
348
|
+
sessionId
|
|
349
|
+
});
|
|
350
|
+
if (!session) return c.json({ error: "session_not_found" }, 404);
|
|
351
|
+
if (session.kind !== "device-code") return c.json({ error: "wrong_session_kind" }, 400);
|
|
352
|
+
const now = Date.now();
|
|
353
|
+
const nextPollAt = session.nextPollAt?.getTime();
|
|
354
|
+
if (nextPollAt != null && now < nextPollAt) return c.json({
|
|
355
|
+
status: "pending",
|
|
356
|
+
nextPollMs: nextPollAt - now
|
|
357
|
+
});
|
|
358
|
+
const claimed = await (await sessionStore(ctx)).claimLoginSession(sessionId, {
|
|
359
|
+
orgId: session.orgId,
|
|
360
|
+
userId: session.userId,
|
|
361
|
+
provider,
|
|
362
|
+
kind: "device-code"
|
|
363
|
+
});
|
|
364
|
+
if (!claimed) return c.json({
|
|
365
|
+
status: "pending",
|
|
366
|
+
nextPollMs: 250
|
|
367
|
+
});
|
|
368
|
+
let result;
|
|
369
|
+
try {
|
|
370
|
+
result = await flow.poll(claimed.pending);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
if (result.status === "complete") {
|
|
376
|
+
await persistOAuthCredential({
|
|
377
|
+
ctx,
|
|
378
|
+
provider,
|
|
379
|
+
credentials: result.credentials,
|
|
380
|
+
authStorage,
|
|
381
|
+
onCredentialsChanged
|
|
382
|
+
});
|
|
383
|
+
await (await sessionStore(ctx)).deleteLoginSession(sessionId);
|
|
384
|
+
return c.json({
|
|
385
|
+
status: "complete",
|
|
386
|
+
ok: true
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (result.status === "failed") {
|
|
390
|
+
await (await sessionStore(ctx)).deleteLoginSession(sessionId);
|
|
391
|
+
return c.json({
|
|
392
|
+
status: "failed",
|
|
393
|
+
error: result.error
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
await (await sessionStore(ctx)).touchLoginSession(sessionId, {
|
|
397
|
+
...result.pending ? { pending: result.pending } : {},
|
|
398
|
+
nextPollAt: new Date(now + result.nextPollMs)
|
|
399
|
+
});
|
|
400
|
+
return c.json({
|
|
401
|
+
status: "pending",
|
|
402
|
+
nextPollMs: result.nextPollMs
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}),
|
|
406
|
+
registerApiRoute("/web/config/providers/:provider/oauth/session/:sessionId", {
|
|
407
|
+
method: "DELETE",
|
|
408
|
+
requiresAuth: false,
|
|
409
|
+
handler: async (c) => {
|
|
410
|
+
const ctx = await resolveCredentialContext({
|
|
411
|
+
c: loose(c),
|
|
412
|
+
auth,
|
|
413
|
+
credentials: modelCredentials
|
|
414
|
+
});
|
|
415
|
+
if ("response" in ctx) return ctx.response;
|
|
416
|
+
const provider = c.req.param("provider");
|
|
417
|
+
const sessionId = c.req.param("sessionId");
|
|
418
|
+
if (await loadOwnedSession({
|
|
419
|
+
ctx,
|
|
420
|
+
provider,
|
|
421
|
+
sessionId
|
|
422
|
+
})) await (await sessionStore(ctx)).deleteLoginSession(sessionId);
|
|
423
|
+
return c.json({ ok: true });
|
|
424
|
+
}
|
|
425
|
+
}),
|
|
426
|
+
registerApiRoute("/web/config/providers/:provider/oauth", {
|
|
427
|
+
method: "DELETE",
|
|
428
|
+
requiresAuth: false,
|
|
429
|
+
handler: async (c) => {
|
|
430
|
+
const ctx = await resolveCredentialContext({
|
|
431
|
+
c: loose(c),
|
|
432
|
+
auth,
|
|
433
|
+
credentials: modelCredentials
|
|
434
|
+
});
|
|
435
|
+
if ("response" in ctx) return ctx.response;
|
|
436
|
+
const authProviderId = getAuthProviderId(c.req.param("provider"));
|
|
437
|
+
try {
|
|
438
|
+
if (ctx.mode === "tenant") {
|
|
439
|
+
await ctx.storage.removeCredential({
|
|
440
|
+
orgId: ctx.orgId,
|
|
441
|
+
userId: ctx.userId
|
|
442
|
+
}, authProviderId);
|
|
443
|
+
onCredentialsChanged({
|
|
444
|
+
orgId: ctx.orgId,
|
|
445
|
+
userId: ctx.userId
|
|
446
|
+
});
|
|
447
|
+
} else {
|
|
448
|
+
if (!authStorage) return c.json({ error: "Credential storage is not available" }, 503);
|
|
449
|
+
authStorage.remove(authProviderId);
|
|
450
|
+
}
|
|
451
|
+
return c.json({ ok: true });
|
|
452
|
+
} catch (error) {
|
|
453
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
];
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
//#endregion
|
|
461
|
+
export { OAuthRoutes };
|
|
462
|
+
|
|
463
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","names":[],"sources":["../../src/routes/oauth.ts"],"sourcesContent":["/**\n * Web OAuth sign-in routes for model providers (Settings › Providers).\n *\n * Wraps the SDK's step-based OAuth primitives (start/complete for Anthropic's\n * paste-code PKCE flow, start/poll for the Codex/Copilot/xAI device flows) in\n * HTTP routes. Flow state lives in login sessions — the `model-credentials`\n * domain's `oauth_login_sessions` table in tenant mode (any replica can\n * complete/poll), an in-memory store in local mode — so a flow can span\n * requests. Completed credentials are always **user-scoped** (plan tokens are\n * personal subscriptions) in tenant mode, or written to the file-backed\n * `AuthStorage` in local mode.\n *\n * Tokens never leave the server; responses only carry flow metadata (URLs,\n * user codes, poll delays).\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { nextPollDelayMs } from '@mastra/code-sdk/auth/device-code';\nimport { completeAnthropicLogin, startAnthropicLogin } from '@mastra/code-sdk/auth/providers/anthropic';\nimport {\n copilotNextPollDelayMs,\n pollGitHubCopilotDeviceLogin,\n startGitHubCopilotDeviceLogin,\n} from '@mastra/code-sdk/auth/providers/github-copilot';\nimport type { CopilotDeviceLoginPending } from '@mastra/code-sdk/auth/providers/github-copilot';\nimport { pollCodexDeviceLogin, startCodexDeviceLogin } from '@mastra/code-sdk/auth/providers/openai-codex';\nimport type { CodexDeviceLoginPending } from '@mastra/code-sdk/auth/providers/openai-codex';\nimport { pollXAIDeviceLogin, startXAIDeviceLogin } from '@mastra/code-sdk/auth/providers/xai';\nimport type { XAIDeviceLoginPending } from '@mastra/code-sdk/auth/providers/xai';\nimport type { AuthStorage } from '@mastra/code-sdk/auth/storage';\nimport type { OAuthCredentials } from '@mastra/code-sdk/auth/types';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport type { LoginSessionKind, LoginSessionRow } from '../storage/domains/credentials/base.js';\nimport { getAuthProviderId, resolveCredentialContext } from './provider-credentials.js';\nimport type { CredentialContext } from './provider-credentials.js';\nimport { Route } from './route.js';\nimport type { RouteDependencies } from './route.js';\n\n/** Lifetime of a paste-code session (Anthropic gives no explicit expiry). */\nconst PASTE_CODE_TTL_MS = 10 * 60 * 1000;\n\ninterface OAuthFlowStart {\n url: string;\n userCode?: string;\n instructions: string;\n /** ms epoch after which the flow expires. */\n expiresAt: number;\n /** Delay before the first upstream poll (device-code flows only). */\n nextPollMs?: number;\n /** Serializable flow state persisted in the login session. */\n pending: Record<string, unknown>;\n}\n\ntype OAuthFlowPoll =\n | { status: 'complete'; credentials: OAuthCredentials }\n | { status: 'pending'; nextPollMs: number; pending?: Record<string, unknown> }\n | { status: 'failed'; error: string };\n\ninterface OAuthFlow {\n kind: LoginSessionKind;\n start(): Promise<OAuthFlowStart>;\n /** Paste-code flows: exchange the pasted code for credentials. Throws on bad input. */\n complete?(pending: Record<string, unknown>, code: string): Promise<OAuthCredentials>;\n /** Device-code flows: perform exactly one upstream poll. */\n poll?(pending: Record<string, unknown>): Promise<OAuthFlowPoll>;\n}\n\n/** Web OAuth flows keyed by *catalog* provider id (mirrors {@link WEB_OAUTH_FLOW_KINDS}). */\nconst OAUTH_FLOWS: Record<string, OAuthFlow> = {\n anthropic: {\n kind: 'paste-code',\n start: async () => {\n const { url, verifier } = await startAnthropicLogin();\n return {\n url,\n instructions: 'Open the link, authorize, then paste the code shown on the final page.',\n expiresAt: Date.now() + PASTE_CODE_TTL_MS,\n pending: { verifier },\n };\n },\n complete: (pending, code) => completeAnthropicLogin(code, String(pending.verifier ?? '')),\n },\n openai: {\n kind: 'device-code',\n start: async () => {\n const p = await startCodexDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.deadlineAt,\n nextPollMs: p.intervalMs,\n pending: { ...p },\n };\n },\n poll: async pending => {\n const r = await pollCodexDeviceLogin(pending as unknown as CodexDeviceLoginPending);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs };\n return { status: 'failed', error: r.error };\n },\n },\n 'github-copilot': {\n kind: 'device-code',\n start: async () => {\n const p = await startGitHubCopilotDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.deadlineAt,\n nextPollMs: copilotNextPollDelayMs(p),\n pending: { ...p },\n };\n },\n poll: async pending => {\n const p = pending as unknown as CopilotDeviceLoginPending;\n // Web flows are always started against github.com (no Enterprise input).\n // Never let deserialized session state redirect server-side polling to\n // an arbitrary hostname.\n if (p.domain !== 'github.com' || p.enterpriseDomain !== undefined) {\n return { status: 'failed', error: 'Unsupported GitHub host' };\n }\n const r = await pollGitHubCopilotDeviceLogin(p);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs, pending: { ...r.pending } };\n return { status: 'failed', error: r.error };\n },\n },\n xai: {\n kind: 'device-code',\n start: async () => {\n const p = await startXAIDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.state.deadlineAt,\n nextPollMs: nextPollDelayMs(p.state),\n pending: { ...p },\n };\n },\n poll: async pending => {\n const r = await pollXAIDeviceLogin(pending as unknown as XAIDeviceLoginPending);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs, pending: { ...r.pending } };\n return { status: 'failed', error: r.error };\n },\n },\n};\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\n/**\n * Local-mode login sessions. Flows in local mode are single-process, so a\n * process-local libsql `:memory:` database (which already handles TTL\n * cleanup) is sufficient.\n */\nlet localSessionsPromise: Promise<ModelCredentialsStorage> | undefined;\nfunction localSessions(): Promise<ModelCredentialsStorage> {\n localSessionsPromise ??= (async () => {\n const { LibSQLFactoryStorage } = await import('@mastra/libsql');\n const storage = new LibSQLFactoryStorage({ id: 'local-oauth-sessions', url: ':memory:' });\n const store = storage.registerDomain(new ModelCredentialsStorage());\n await storage.init();\n return store;\n })();\n return localSessionsPromise;\n}\nconst LOCAL_TENANT = { orgId: 'local', userId: 'local' } as const;\n\nasync function sessionStore(ctx: CredentialContext): Promise<ModelCredentialsStorage> {\n return ctx.mode === 'tenant' ? ctx.storage : localSessions();\n}\n\nfunction sessionTenant(ctx: CredentialContext): { orgId: string; userId: string } {\n return ctx.mode === 'tenant' ? { orgId: ctx.orgId, userId: ctx.userId } : LOCAL_TENANT;\n}\n\n/** Load a session and verify it belongs to the caller + provider (else undefined). */\nasync function loadOwnedSession({\n ctx,\n provider,\n sessionId,\n}: {\n ctx: CredentialContext;\n provider: string;\n sessionId: string;\n}): Promise<LoginSessionRow | undefined> {\n const session = await (await sessionStore(ctx)).getLoginSession(sessionId);\n if (!session) return undefined;\n const tenant = sessionTenant(ctx);\n if (session.orgId !== tenant.orgId || session.userId !== tenant.userId) return undefined;\n if (session.provider !== provider) return undefined;\n return session;\n}\n\n/** Persist completed OAuth credentials — always user-scoped in tenant mode. */\nasync function persistOAuthCredential({\n ctx,\n provider,\n credentials,\n authStorage,\n onCredentialsChanged,\n}: {\n ctx: CredentialContext;\n provider: string;\n credentials: OAuthCredentials;\n authStorage: AuthStorage | undefined;\n onCredentialsChanged: (tenant: { orgId: string; userId?: string }) => void;\n}): Promise<void> {\n const authProviderId = getAuthProviderId(provider);\n if (ctx.mode === 'tenant') {\n await ctx.storage.setCredential({ orgId: ctx.orgId, userId: ctx.userId }, authProviderId, {\n type: 'oauth',\n ...credentials,\n });\n onCredentialsChanged({ orgId: ctx.orgId, userId: ctx.userId });\n return;\n }\n if (!authStorage) throw new Error('Credential storage is not available');\n authStorage.set(authProviderId, { type: 'oauth', ...credentials });\n}\n\nasync function readJsonBody(c: Context): Promise<Record<string, unknown>> {\n try {\n const body = (await c.req.json()) as unknown;\n return body && typeof body === 'object' ? (body as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nexport interface OAuthRoutesDeps extends RouteDependencies {\n /** File-backed credential store; used in local (no-auth) mode. */\n authStorage?: AuthStorage;\n /** Tenant credential domain handle; absent in local (no-DB) mode. */\n modelCredentials?: ModelCredentialsStorage;\n /** Notifies the host after tenant credentials change so caches can be dropped. */\n onCredentialsChanged?: (tenant: { orgId: string; userId?: string }) => void;\n}\n\n/**\n * Provider OAuth sign-in routes as Mastra `apiRoutes`:\n * - `POST /web/config/providers/:provider/oauth/start` — begin a sign-in flow\n * - `POST /web/config/providers/:provider/oauth/complete` — paste-code exchange\n * - `POST /web/config/providers/:provider/oauth/poll` — one device-code poll\n * - `DELETE /web/config/providers/:provider/oauth/session/:sessionId` — cancel a flow\n * - `DELETE /web/config/providers/:provider/oauth` — sign out (caller only)\n */\nexport class OAuthRoutes extends Route<OAuthRoutesDeps> {\n routes(): ApiRoute[] {\n const { auth, authStorage, modelCredentials } = this.deps;\n const onCredentialsChanged = this.deps.onCredentialsChanged ?? (() => {});\n\n return [\n registerApiRoute('/web/config/providers/:provider/oauth/start', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow) {\n return c.json({ error: 'oauth_not_supported', message: `Provider does not support web sign-in` }, 404);\n }\n // Body may carry `{ mode }` for future multi-mode providers; each web\n // flow currently has exactly one mode, so an unknown mode is rejected.\n const body = await readJsonBody(loose(c));\n if (typeof body.mode === 'string' && body.mode !== flow.kind) {\n return c.json({ error: 'invalid_mode', message: `Unsupported mode for ${provider}: ${body.mode}` }, 400);\n }\n\n let started: OAuthFlowStart;\n try {\n started = await flow.start();\n } catch (error) {\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 502);\n }\n\n const sessionId = randomUUID();\n const tenant = sessionTenant(ctx);\n await (\n await sessionStore(ctx)\n ).createLoginSession({\n sessionId,\n orgId: tenant.orgId,\n userId: tenant.userId,\n provider,\n kind: flow.kind,\n pending: started.pending,\n expiresAt: new Date(started.expiresAt),\n nextPollAt: started.nextPollMs != null ? new Date(Date.now() + started.nextPollMs) : null,\n });\n\n return c.json({\n sessionId,\n kind: flow.kind,\n url: started.url,\n ...(started.userCode ? { userCode: started.userCode } : {}),\n instructions: started.instructions,\n expiresAt: started.expiresAt,\n ...(started.nextPollMs != null ? { nextPollMs: started.nextPollMs } : {}),\n });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/complete', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow?.complete) return c.json({ error: 'oauth_not_supported' }, 404);\n\n const body = await readJsonBody(loose(c));\n const sessionId = typeof body.sessionId === 'string' ? body.sessionId : '';\n const code = typeof body.code === 'string' ? body.code.trim() : '';\n if (!sessionId || !code) return c.json({ error: 'Missing required fields: sessionId, code' }, 400);\n\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (!session) return c.json({ error: 'session_not_found' }, 404);\n if (session.kind !== 'paste-code') return c.json({ error: 'wrong_session_kind' }, 400);\n\n const claimed = await (\n await sessionStore(ctx)\n ).claimLoginSession(sessionId, {\n orgId: session.orgId,\n userId: session.userId,\n provider,\n kind: 'paste-code',\n });\n if (!claimed) return c.json({ error: 'oauth_in_progress' }, 409);\n\n let credentials: OAuthCredentials;\n try {\n credentials = await flow.complete(claimed.pending, code);\n } catch (error) {\n await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);\n }\n\n await persistOAuthCredential({ ctx, provider, credentials, authStorage, onCredentialsChanged });\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'complete', ok: true });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/poll', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow?.poll) return c.json({ error: 'oauth_not_supported' }, 404);\n\n const body = await readJsonBody(loose(c));\n const sessionId = typeof body.sessionId === 'string' ? body.sessionId : '';\n if (!sessionId) return c.json({ error: 'Missing required field: sessionId' }, 400);\n\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (!session) return c.json({ error: 'session_not_found' }, 404);\n if (session.kind !== 'device-code') return c.json({ error: 'wrong_session_kind' }, 400);\n\n // Server-side rate limit: at most one upstream poll per interval,\n // regardless of how eagerly the client calls this route.\n const now = Date.now();\n const nextPollAt = session.nextPollAt?.getTime();\n if (nextPollAt != null && now < nextPollAt) {\n return c.json({ status: 'pending', nextPollMs: nextPollAt - now });\n }\n\n const claimed = await (\n await sessionStore(ctx)\n ).claimLoginSession(sessionId, {\n orgId: session.orgId,\n userId: session.userId,\n provider,\n kind: 'device-code',\n });\n if (!claimed) return c.json({ status: 'pending', nextPollMs: 250 });\n\n let result: OAuthFlowPoll;\n try {\n result = await flow.poll(claimed.pending);\n } catch (error) {\n await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });\n throw error;\n }\n\n if (result.status === 'complete') {\n await persistOAuthCredential({\n ctx,\n provider,\n credentials: result.credentials,\n authStorage,\n onCredentialsChanged,\n });\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'complete', ok: true });\n }\n\n if (result.status === 'failed') {\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'failed', error: result.error });\n }\n\n await (\n await sessionStore(ctx)\n ).touchLoginSession(sessionId, {\n ...(result.pending ? { pending: result.pending } : {}),\n nextPollAt: new Date(now + result.nextPollMs),\n });\n return c.json({ status: 'pending', nextPollMs: result.nextPollMs });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/session/:sessionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const sessionId = c.req.param('sessionId');\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (session) await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ ok: true });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const authProviderId = getAuthProviderId(provider);\n try {\n if (ctx.mode === 'tenant') {\n // Caller's credential only — never touches org rows or other users.\n await ctx.storage.removeCredential({ orgId: ctx.orgId, userId: ctx.userId }, authProviderId);\n onCredentialsChanged({ orgId: ctx.orgId, userId: ctx.userId });\n } else {\n if (!authStorage) return c.json({ error: 'Credential storage is not available' }, 503);\n authStorage.remove(authProviderId);\n }\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);\n }\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ApiRoute } from '@mastra/core/server';
|
|
2
|
+
import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
3
|
+
import type { SourceControlStorage } from '../storage/domains/source-control/base.js';
|
|
4
|
+
import type { RouteDependencies } from './route.js';
|
|
5
|
+
import { Route } from './route.js';
|
|
6
|
+
export interface ProjectRoutesDeps extends RouteDependencies {
|
|
7
|
+
/** Factory projects domain backing the CRUD surface. */
|
|
8
|
+
projects: FactoryProjectsStorage;
|
|
9
|
+
/** Source-control domain the connection/repository routes fan out over. */
|
|
10
|
+
sourceControl: SourceControlStorage;
|
|
11
|
+
/** Integration ids allowed as source-control connection targets. */
|
|
12
|
+
versionControlIntegrationIds?: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare class ProjectRoutes extends Route<ProjectRoutesDeps> {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(deps: ProjectRoutesDeps);
|
|
17
|
+
routes(): ApiRoute[];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAEV,oBAAoB,EAGrB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AA2InC,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,wDAAwD;IACxD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,2EAA2E;IAC3E,aAAa,EAAE,oBAAoB,CAAC;IACpC,oEAAoE;IACpE,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;CACzC;AAED,qBAAa,aAAc,SAAQ,KAAK,CAAC,iBAAiB,CAAC;;gBAG7C,IAAI,EAAE,iBAAiB;IA8DnC,MAAM,IAAI,QAAQ,EAAE;CAoOrB"}
|