@mastra/factory 0.25.0-alpha.4 → 0.25.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.js +1 -2
- package/dist/boards/define-board.js +1 -2
- package/dist/boards/index.js +1 -2
- package/dist/boards/registry.js +1 -2
- package/dist/boards/relocate.js +1 -2
- package/dist/boards/review.js +1 -2
- package/dist/boards/semantics.js +1 -2
- package/dist/boards/transition-policy.js +1 -2
- package/dist/boards/work-tool-rules.js +1 -2
- package/dist/boards/work-transition-policy.js +1 -2
- package/dist/boards/work.js +1 -2
- package/dist/environment/build-runner.js +1 -2
- package/dist/environment/build-schedule.js +1 -2
- package/dist/environment/build-workflow.js +1 -2
- package/dist/environment/build.js +1 -2
- package/dist/environment/heads.js +1 -2
- package/dist/factory.d.ts +4 -2
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +43 -16
- package/dist/factory.js.map +1 -1
- package/dist/feed-events.js +1 -2
- package/dist/index.js +2 -2
- package/dist/integrations/channel-thread-announcements.d.ts +8 -0
- package/dist/integrations/channel-thread-announcements.d.ts.map +1 -0
- package/dist/integrations/channel-thread-announcements.js +16 -0
- package/dist/integrations/channel-thread-announcements.js.map +1 -0
- package/dist/integrations/github/acceptance-labels.js +1 -2
- package/dist/integrations/github/app-identity.js +1 -2
- package/dist/integrations/github/commits.js +1 -2
- package/dist/integrations/github/config.js +1 -2
- package/dist/integrations/github/default-rules.js +2 -3
- package/dist/integrations/github/integration.js +3 -4
- package/dist/integrations/github/issue-reconciler.js +1 -2
- package/dist/integrations/github/pat.js +1 -2
- package/dist/integrations/github/provenance.d.ts.map +1 -1
- package/dist/integrations/github/provenance.js +2 -5
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/reconcile-worker.js +1 -2
- package/dist/integrations/github/reconciliation-config.js +1 -2
- package/dist/integrations/github/routes.js +4 -5
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +14 -3
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox-release.js +1 -2
- package/dist/integrations/github/sandbox.js +1 -2
- package/dist/integrations/github/session-subscriptions.js +1 -2
- package/dist/integrations/github/settle-or-abort.js +1 -2
- package/dist/integrations/github/stale-reviews.js +1 -2
- package/dist/integrations/github/subscriptions.js +1 -2
- package/dist/integrations/github/token-refresh.js +1 -2
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +17 -3
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/gitlab/agent-tools.js +1 -2
- package/dist/integrations/gitlab/api.js +1 -2
- package/dist/integrations/gitlab/default-rules.js +1 -2
- package/dist/integrations/gitlab/integration.js +2 -3
- package/dist/integrations/gitlab/issue-reconciler.js +1 -2
- package/dist/integrations/gitlab/merge-request-reconciler.js +1 -2
- package/dist/integrations/gitlab/reconciler.js +1 -2
- package/dist/integrations/gitlab/reconciliation-config.js +1 -2
- package/dist/integrations/gitlab/routes.js +1 -2
- package/dist/integrations/gitlab/rules.js +1 -2
- package/dist/integrations/gitlab/session-subscriptions.js +1 -2
- package/dist/integrations/gitlab/subscriptions.js +1 -2
- package/dist/integrations/gitlab/version-control.js +1 -2
- package/dist/integrations/gitlab/webhook-dispatch.js +1 -2
- package/dist/integrations/gitlab/webhook.js +1 -2
- package/dist/integrations/incidentio/agent-tools.js +1 -2
- package/dist/integrations/incidentio/api.js +1 -2
- package/dist/integrations/incidentio/claim.js +1 -2
- package/dist/integrations/incidentio/default-rules.js +1 -2
- package/dist/integrations/incidentio/intake.js +1 -2
- package/dist/integrations/incidentio/integration.js +1 -2
- package/dist/integrations/incidentio/issue-reconciler.js +1 -2
- package/dist/integrations/incidentio/reconciliation-config.js +1 -2
- package/dist/integrations/incidentio/routes.js +1 -2
- package/dist/integrations/incidentio/rules.js +1 -2
- package/dist/integrations/issue-reconcile-worker.js +1 -2
- package/dist/integrations/issue-reconciler.js +1 -2
- package/dist/integrations/jira/adf.js +1 -2
- package/dist/integrations/jira/agent-tools.js +1 -2
- package/dist/integrations/jira/api.js +1 -2
- package/dist/integrations/jira/claim.js +1 -2
- package/dist/integrations/jira/default-rules.js +1 -2
- package/dist/integrations/jira/integration.js +1 -2
- package/dist/integrations/jira/issue-reconciler.js +1 -2
- package/dist/integrations/jira/reconciliation-config.js +1 -2
- package/dist/integrations/jira/routes.js +1 -2
- package/dist/integrations/jira/rules.js +1 -2
- package/dist/integrations/linear/agent-tools.d.ts +1 -1
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js +5 -5
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/claim.js +1 -2
- package/dist/integrations/linear/default-rules.js +1 -2
- package/dist/integrations/linear/integration.d.ts +2 -0
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +2 -2
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/issue-reconciler.js +1 -2
- package/dist/integrations/linear/reconciliation-config.js +1 -2
- package/dist/integrations/linear/routes.js +1 -2
- package/dist/integrations/linear/rules.js +1 -2
- package/dist/integrations/platform/api-client.js +1 -2
- package/dist/integrations/platform/connect/routes.js +1 -2
- package/dist/integrations/platform/github/event-worker.js +6 -2
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.js +1 -2
- package/dist/integrations/platform/gitlab/event-worker.js +1 -2
- package/dist/integrations/platform/gitlab/integration.js +1 -2
- package/dist/integrations/platform/incidentio/integration.js +1 -2
- package/dist/integrations/platform/jira/agent-tools.js +1 -2
- package/dist/integrations/platform/jira/integration.js +1 -2
- package/dist/integrations/platform/jira/routes.js +1 -2
- package/dist/integrations/platform/linear/event-worker.js +1 -2
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +2 -2
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/pull-request-head.js +1 -2
- package/dist/integrations/reconciliation-config.js +1 -2
- package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -1
- package/dist/integrations/slack/channel-reply-processor.js +11 -5
- package/dist/integrations/slack/channel-reply-processor.js.map +1 -1
- package/dist/integrations/slack/channel-session-link-processor.js +1 -2
- package/dist/integrations/slack/connect-route.js +1 -2
- package/dist/integrations/slack/emoji-shortcodes.generated.js +1 -2
- package/dist/integrations/slack/emoji.js +1 -2
- package/dist/integrations/slack/feed-publisher.js +1 -2
- package/dist/integrations/slack/integration.d.ts +5 -0
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +13 -2
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/reply-reminder-processor.js +1 -2
- package/dist/integrations/slack/reply-tool.js +1 -2
- package/dist/integrations/slack/share-file-tool.js +1 -2
- package/dist/integrations/slack/slack.js +2 -3
- package/dist/integrations/subscription-session.js +1 -2
- package/dist/integrations/workos/integration.js +1 -2
- package/dist/mcp/disconnect.js +1 -2
- package/dist/mcp/events.js +2 -3
- package/dist/mcp/routes.js +1 -2
- package/dist/packages/_internals/workspace/dist/index.js +1 -2
- package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
- package/dist/platform-auth-host.js +1 -2
- package/dist/provider-display-name.js +1 -2
- package/dist/review-verdict.d.ts +1 -0
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +9 -3
- package/dist/review-verdict.js.map +1 -1
- package/dist/routes/attention-activity.js +1 -2
- package/dist/routes/attention-parked.js +1 -2
- package/dist/routes/attention-proposed.js +1 -2
- package/dist/routes/attention-providers.js +1 -2
- package/dist/routes/attention.js +1 -2
- package/dist/routes/automation-runs.js +1 -2
- package/dist/routes/config.d.ts +4 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +7 -2
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.js +1 -2
- package/dist/routes/custom-provider-source.js +1 -2
- package/dist/routes/fs.js +1 -2
- package/dist/routes/intake.js +1 -2
- package/dist/routes/knowledge.js +1 -2
- package/dist/routes/oauth.js +1 -2
- package/dist/routes/projects.js +1 -2
- package/dist/routes/provider-credentials.js +1 -2
- package/dist/routes/route.js +1 -2
- package/dist/routes/session-access.js +1 -2
- package/dist/routes/skills.js +1 -2
- package/dist/routes/source-control-sessions.js +5 -6
- package/dist/routes/source-control-settings.js +1 -2
- package/dist/routes/surface.js +2 -3
- package/dist/routes/telemetry.js +1 -2
- package/dist/routes/tenant-credentials.js +1 -2
- package/dist/routes/work-items.js +2 -3
- package/dist/rules/binding-context.js +1 -2
- package/dist/rules/decision-applicability.js +1 -2
- package/dist/rules/dispatch-errors.js +1 -2
- package/dist/rules/dispatcher.js +1 -2
- package/dist/rules/event-rules.js +1 -2
- package/dist/rules/processor.js +1 -2
- package/dist/rules/resolve.js +1 -2
- package/dist/rules/review-source-tool.js +1 -2
- package/dist/rules/start-coordinator.js +1 -2
- package/dist/rules/terminal-cleanup.js +1 -2
- package/dist/rules/tools.js +1 -2
- package/dist/rules/transition-service.js +1 -2
- package/dist/rules/types.d.ts +4 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +1 -2
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.js +1 -2
- package/dist/sandbox/callback-factory-sandbox.js +1 -2
- package/dist/sandbox/git-ref.js +1 -2
- package/dist/sandbox/local-factory-sandbox.d.ts +12 -4
- package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
- package/dist/sandbox/local-factory-sandbox.js +32 -7
- package/dist/sandbox/local-factory-sandbox.js.map +1 -1
- package/dist/sandbox/materialization.js +1 -2
- package/dist/sandbox/session-retirement.js +1 -2
- package/dist/sandbox/session-sandbox.js +1 -2
- package/dist/sandbox/workdir.js +1 -2
- package/dist/secret-encryption.js +1 -2
- package/dist/server-error.js +1 -2
- package/dist/session/credential-policy.js +1 -2
- package/dist/session/default-model-hydration.js +1 -2
- package/dist/session/environment-repositories.js +1 -2
- package/dist/session/environment-state-processor.d.ts +0 -1
- package/dist/session/environment-state-processor.d.ts.map +1 -1
- package/dist/session/environment-state-processor.js +2 -3
- package/dist/session/environment-state-processor.js.map +1 -1
- package/dist/session/factory-session.js +1 -2
- package/dist/session/filesystem-capture.js +2 -3
- package/dist/session/filesystem-capture.js.map +1 -1
- package/dist/session/first-exec-capture.js +2 -3
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.js +1 -2
- package/dist/session/live-sessions.js +1 -2
- package/dist/session/memory-settings-hydration.js +1 -2
- package/dist/session/org-seed.js +1 -2
- package/dist/session/reply-credential-processor.js +1 -2
- package/dist/session/resource-migration.js +1 -2
- package/dist/session/run-audit.js +1 -2
- package/dist/session/session-threads.js +1 -2
- package/dist/session/session-title.js +1 -2
- package/dist/session/shell-commands.js +1 -2
- package/dist/session/source-control-tools.js +2 -3
- package/dist/session/thinking-default.js +1 -2
- package/dist/session/thread-session-stamp.js +1 -2
- package/dist/session/thread-title-mirror.js +1 -2
- package/dist/session/thread-title-mirror.js.map +1 -1
- package/dist/session/work-item-repository.js +1 -2
- package/dist/session-owner-signal.js +1 -2
- package/dist/skills/catalog.js +1 -2
- package/dist/skills/service.js +1 -2
- package/dist/spa-static.js +1 -2
- package/dist/state-signing.js +1 -2
- package/dist/storage/domains/audit/actions.js +1 -2
- package/dist/storage/domains/audit/actors.js +1 -2
- package/dist/storage/domains/audit/agent-audit.js +1 -2
- package/dist/storage/domains/audit/base.js +1 -2
- package/dist/storage/domains/audit/domain.js +1 -2
- package/dist/storage/domains/audit/wire.js +1 -2
- package/dist/storage/domains/channel-identity/base.js +1 -2
- package/dist/storage/domains/comments/activity.js +1 -2
- package/dist/storage/domains/comments/actor.js +1 -2
- package/dist/storage/domains/comments/base.js +1 -2
- package/dist/storage/domains/comments/domain.js +1 -2
- package/dist/storage/domains/comments/feed-context.js +1 -2
- package/dist/storage/domains/comments/parse.js +1 -2
- package/dist/storage/domains/comments/routes.js +1 -2
- package/dist/storage/domains/comments/schema.js +1 -2
- package/dist/storage/domains/comments/wire.js +1 -2
- package/dist/storage/domains/credentials/base.d.ts +4 -1
- package/dist/storage/domains/credentials/base.d.ts.map +1 -1
- package/dist/storage/domains/credentials/base.js +40 -44
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +1 -2
- package/dist/storage/domains/filesystem/base.js +1 -2
- package/dist/storage/domains/intake/base.js +1 -2
- package/dist/storage/domains/integrations/base.js +1 -2
- package/dist/storage/domains/memory-settings/base.js +1 -2
- package/dist/storage/domains/model-defaults/base.js +1 -2
- package/dist/storage/domains/projects/base.js +1 -2
- package/dist/storage/domains/queue-health/base.js +1 -2
- package/dist/storage/domains/source-control/base.js +1 -2
- package/dist/storage/domains/source-control/inmemory.js +1 -2
- package/dist/storage/domains/work-items/base.js +1 -2
- package/dist/storage/domains/work-items/board-version.js +1 -2
- package/dist/storage/domains/work-items/metrics.js +1 -2
- package/dist/telemetry-types.js +1 -2
- package/dist/telemetry.js +1 -2
- package/dist/timing.js +1 -2
- package/dist/tools/annotations.js +1 -2
- package/dist/tools/approval-tools.js +1 -2
- package/dist/tools/deliver.js +2 -3
- package/dist/tools/index.js +1 -2
- package/dist/tools/messaging-tools.js +1 -2
- package/dist/tools/permissions.js +1 -2
- package/dist/tools/read-tools.js +1 -2
- package/dist/tools/reports.js +1 -2
- package/dist/tools/run-watch.js +1 -2
- package/dist/tools/sandbox-tools.js +1 -2
- package/dist/tools/scope.js +2 -3
- package/dist/tools/session-tools.js +1 -2
- package/dist/tools/sessions-state-processor.js +1 -2
- package/dist/tools/side-sessions.js +1 -2
- package/dist/tools/work-item-tools.js +1 -2
- package/dist/work-item-branch.js +1 -2
- package/dist/work-item-branch.js.map +1 -1
- package/dist/work-items/create-work-item.js +1 -2
- package/dist/work-items/decision-actions.js +1 -2
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +1 -3
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-rereview/SKILL.md +12 -2
- package/factory-skills/factory-review/SKILL.md +12 -2
- package/factory-skills/factory-triage/SKILL.md +7 -7
- package/package.json +19 -17
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/storage/domains/comments/actor.ts
|
|
2
2
|
function isMentionableActorId(id) {
|
|
3
3
|
return id.length > 0 && !id.includes(":");
|
|
4
4
|
}
|
|
@@ -32,7 +32,6 @@ function actorFromChannelAuthor(external, link) {
|
|
|
32
32
|
external
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
//#endregion
|
|
36
35
|
export { actorFromAgentBinding, actorFromAuthUser, actorFromChannelAuthor, isMentionableActorId };
|
|
37
36
|
|
|
38
37
|
//# sourceMappingURL=actor.js.map
|
|
@@ -3,7 +3,7 @@ import { FACTORY_BOARD_VERSIONS_SCHEMA, bumpBoardVersions, trackBoardWrites } fr
|
|
|
3
3
|
import { externalSourceKey } from "../work-items/base.js";
|
|
4
4
|
import { fanOutWorkItemActivity, listActivityForUser } from "./activity.js";
|
|
5
5
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
6
|
-
|
|
6
|
+
// src/storage/domains/comments/base.ts
|
|
7
7
|
/**
|
|
8
8
|
* Work-item comments domain — one message list per work item, materialized
|
|
9
9
|
* from every source. Local creates and platform mirrors (Slack, Linear,
|
|
@@ -527,7 +527,6 @@ var WorkItemCommentsStorage = class extends FactoryStorageDomain {
|
|
|
527
527
|
}
|
|
528
528
|
}
|
|
529
529
|
};
|
|
530
|
-
//#endregion
|
|
531
530
|
export { CommentTokenConflictError, MAX_COMMENT_BODY_LENGTH, MAX_COMMENT_MENTIONS, MAX_COMMENT_QUOTE_LENGTH, WORK_ITEM_ACTIVITY_SCHEMA, WORK_ITEM_COMMENTS_SCHEMA, WORK_ITEM_COMMENT_MENTIONS_SCHEMA, WorkItemCommentsStorage, clampCommentLimit, commentBodyError, commentSourceKey, decodeCommentCursor, encodeCommentCursor, supersedesFeedActivity };
|
|
532
531
|
|
|
533
532
|
//# sourceMappingURL=base.js.map
|
|
@@ -4,7 +4,7 @@ import { factoryMentionAttentionIdentity } from "../work-items/base.js";
|
|
|
4
4
|
import { CommentTokenConflictError, commentBodyError } from "./base.js";
|
|
5
5
|
import { toWireComment } from "./wire.js";
|
|
6
6
|
import { buildCommentRoutes } from "./routes.js";
|
|
7
|
-
|
|
7
|
+
// src/storage/domains/comments/domain.ts
|
|
8
8
|
const MAX_ROSTER_SIZE = 100;
|
|
9
9
|
const ROSTER_CACHE_TTL_MS = 6e4;
|
|
10
10
|
var CommentsDomain = class {
|
|
@@ -279,7 +279,6 @@ var CommentsDomain = class {
|
|
|
279
279
|
});
|
|
280
280
|
}
|
|
281
281
|
};
|
|
282
|
-
//#endregion
|
|
283
282
|
export { CommentsDomain, toWireComment };
|
|
284
283
|
|
|
285
284
|
//# sourceMappingURL=domain.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/storage/domains/comments/feed-context.ts
|
|
2
2
|
/** The one block that may trail a skill envelope in a kickoff; both transcript renderers allow only it. */
|
|
3
3
|
const WORK_ITEM_FEED_TAG = "work-item-feed";
|
|
4
4
|
const MAX_FEED_COMMENTS = 20;
|
|
@@ -83,7 +83,6 @@ async function withWorkItemFeed(reader, scope, message) {
|
|
|
83
83
|
workItemId
|
|
84
84
|
}));
|
|
85
85
|
}
|
|
86
|
-
//#endregion
|
|
87
86
|
export { FactoryFeedReader, WORK_ITEM_FEED_TAG, withFeedContext, withWorkItemFeed };
|
|
88
87
|
|
|
89
88
|
//# sourceMappingURL=feed-context.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isMentionableActorId } from "./actor.js";
|
|
2
2
|
import { commentBodyError } from "./base.js";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/comments/parse.ts
|
|
4
4
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
5
|
const CLIENT_TOKEN_RE = /^[A-Za-z0-9-]{8,64}$/;
|
|
6
6
|
function isRecord(value) {
|
|
@@ -84,7 +84,6 @@ function parseEditCommentBody(raw) {
|
|
|
84
84
|
...expectedRevision !== void 0 ? { expectedRevision } : {}
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
-
//#endregion
|
|
88
87
|
export { UUID_RE, isRecord, parseCreateCommentBody, parseEditCommentBody, readJson };
|
|
89
88
|
|
|
90
89
|
//# sourceMappingURL=parse.js.map
|
|
@@ -7,7 +7,7 @@ import { toWireComment } from "./wire.js";
|
|
|
7
7
|
import { registerApiRoute } from "@mastra/core/server";
|
|
8
8
|
import { withAck } from "@mastra/core/events";
|
|
9
9
|
import { streamSSE } from "hono/streaming";
|
|
10
|
-
|
|
10
|
+
// src/storage/domains/comments/routes.ts
|
|
11
11
|
/**
|
|
12
12
|
* HTTP for the item feed. Every handler is the same shape: resolve the tenant,
|
|
13
13
|
* validate the path ids, delegate to a `CommentsDomain` service method, map its
|
|
@@ -296,7 +296,6 @@ function buildCommentRoutes(dependencies) {
|
|
|
296
296
|
})
|
|
297
297
|
];
|
|
298
298
|
}
|
|
299
|
-
//#endregion
|
|
300
299
|
export { buildCommentRoutes };
|
|
301
300
|
|
|
302
301
|
//# sourceMappingURL=routes.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/storage/domains/comments/schema.ts
|
|
2
2
|
const WORK_ITEM_COMMENTS_SCHEMA = {
|
|
3
3
|
name: "work_item_comments",
|
|
4
4
|
columns: {
|
|
@@ -173,7 +173,6 @@ const WORK_ITEM_ACTIVITY_SCHEMA = {
|
|
|
173
173
|
]
|
|
174
174
|
}]
|
|
175
175
|
};
|
|
176
|
-
//#endregion
|
|
177
176
|
export { WORK_ITEM_ACTIVITY_SCHEMA, WORK_ITEM_COMMENTS_SCHEMA, WORK_ITEM_COMMENT_MENTIONS_SCHEMA };
|
|
178
177
|
|
|
179
178
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/storage/domains/comments/wire.ts
|
|
2
2
|
const LOCAL_SOURCE_KEY_PREFIX = "local:comment:";
|
|
3
3
|
function toWireAuthor({ kind, id, displayName, avatarUrl }) {
|
|
4
4
|
return {
|
|
@@ -31,7 +31,6 @@ function toWireComment(comment, viewerId) {
|
|
|
31
31
|
...comment.deletedAt ? { deletedAt: comment.deletedAt.toISOString() } : {}
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { toWireComment };
|
|
36
35
|
|
|
37
36
|
//# sourceMappingURL=wire.js.map
|
|
@@ -39,6 +39,8 @@ export interface CredentialRecord {
|
|
|
39
39
|
scope: CredentialScope;
|
|
40
40
|
credential: AuthCredential;
|
|
41
41
|
updatedAt: Date;
|
|
42
|
+
/** When the last OAuth token refresh failed; cleared by the next successful refresh or a new sign-in. */
|
|
43
|
+
refreshFailedAt?: Date;
|
|
42
44
|
}
|
|
43
45
|
/** Result of per-caller resolution: the winning credential and its scope. */
|
|
44
46
|
export interface ResolvedCredential {
|
|
@@ -109,7 +111,8 @@ export declare class ModelCredentialsStorage extends FactoryStorageDomain {
|
|
|
109
111
|
* rotating refresh tokens. The expiry is re-checked under the lock —
|
|
110
112
|
* another replica may have refreshed already, in which case `refreshFn` is
|
|
111
113
|
* skipped and the fresh credential is returned. Returns `undefined` when no
|
|
112
|
-
* OAuth row exists for the tenant.
|
|
114
|
+
* OAuth row exists for the tenant. A failed `refreshFn` is recorded on the
|
|
115
|
+
* row (surfaced as `CredentialRecord.refreshFailedAt`) and rethrown.
|
|
113
116
|
*/
|
|
114
117
|
refreshOAuth(tenant: CredentialTenant, provider: string, refreshFn: (current: OAuthCredential) => Promise<OAuthCredential>): Promise<OAuthCredential | undefined>;
|
|
115
118
|
/** Persist a pending login flow started by a web route. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/credentials/base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAsC,MAAM,sBAAsB,CAAC;AAEjG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE7E,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7C,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/credentials/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
|
|
2
2
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/credentials/base.ts
|
|
4
4
|
/** Mirror of `AuthStorage.getApiKey()`'s expiry check. */
|
|
5
5
|
function isOAuthCredentialExpired(credential, now = Date.now()) {
|
|
6
6
|
return now >= credential.expires;
|
|
@@ -18,7 +18,11 @@ const MODEL_CREDENTIALS_SCHEMA = {
|
|
|
18
18
|
type: { type: "text" },
|
|
19
19
|
data: { type: "json" },
|
|
20
20
|
created_at: { type: "timestamp" },
|
|
21
|
-
updated_at: { type: "timestamp" }
|
|
21
|
+
updated_at: { type: "timestamp" },
|
|
22
|
+
refresh_failed_at: {
|
|
23
|
+
type: "timestamp",
|
|
24
|
+
nullable: true
|
|
25
|
+
}
|
|
22
26
|
},
|
|
23
27
|
uniqueIndexes: [{
|
|
24
28
|
name: "model_provider_credentials_user_unique",
|
|
@@ -107,6 +111,15 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
107
111
|
async #decryptCredential(value) {
|
|
108
112
|
return (await this.encryption.decrypt(value)).value;
|
|
109
113
|
}
|
|
114
|
+
async #toRecord(row, scope) {
|
|
115
|
+
return {
|
|
116
|
+
provider: row.provider,
|
|
117
|
+
scope,
|
|
118
|
+
credential: await this.#decryptCredential(row.data),
|
|
119
|
+
updatedAt: row.updated_at,
|
|
120
|
+
refreshFailedAt: row.refresh_failed_at ?? void 0
|
|
121
|
+
};
|
|
122
|
+
}
|
|
110
123
|
async #migrateCredential(id) {
|
|
111
124
|
await this.#db.updateAtomic("model_provider_credentials", { id }, async (row) => {
|
|
112
125
|
const decrypted = await this.encryption.decrypt(row.data);
|
|
@@ -126,7 +139,8 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
126
139
|
const update = async () => this.#db.updateMany("model_provider_credentials", where, {
|
|
127
140
|
type: credential.type,
|
|
128
141
|
data: encrypted,
|
|
129
|
-
updated_at: /* @__PURE__ */ new Date()
|
|
142
|
+
updated_at: /* @__PURE__ */ new Date(),
|
|
143
|
+
refresh_failed_at: null
|
|
130
144
|
});
|
|
131
145
|
let lastError;
|
|
132
146
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
@@ -163,45 +177,20 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
163
177
|
org_id: orgId,
|
|
164
178
|
user_id: null
|
|
165
179
|
})]);
|
|
166
|
-
return Promise.all([...userRows.map(
|
|
167
|
-
provider: row.provider,
|
|
168
|
-
scope: "user",
|
|
169
|
-
credential: await this.#decryptCredential(row.data),
|
|
170
|
-
updatedAt: row.updated_at
|
|
171
|
-
})), ...orgRows.map(async (row) => ({
|
|
172
|
-
provider: row.provider,
|
|
173
|
-
scope: "org",
|
|
174
|
-
credential: await this.#decryptCredential(row.data),
|
|
175
|
-
updatedAt: row.updated_at
|
|
176
|
-
}))]);
|
|
180
|
+
return Promise.all([...userRows.map((row) => this.#toRecord(row, "user")), ...orgRows.map((row) => this.#toRecord(row, "org"))]);
|
|
177
181
|
}
|
|
178
182
|
/** Resolve a provider credential from exactly one tenancy scope. */
|
|
179
183
|
async resolveCredential(orgId, userId, provider, scope) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
provider,
|
|
184
|
-
user_id: userId
|
|
185
|
-
});
|
|
186
|
-
return row ? {
|
|
187
|
-
provider: row.provider,
|
|
188
|
-
scope: "user",
|
|
189
|
-
credential: await this.#decryptCredential(row.data)
|
|
190
|
-
} : void 0;
|
|
184
|
+
const tenant = scope === "org" ? { orgId } : {
|
|
185
|
+
orgId,
|
|
186
|
+
userId
|
|
191
187
|
};
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return row ? {
|
|
199
|
-
provider: row.provider,
|
|
200
|
-
scope: "org",
|
|
201
|
-
credential: await this.#decryptCredential(row.data)
|
|
202
|
-
} : void 0;
|
|
203
|
-
};
|
|
204
|
-
return scope === "org" ? readOrg() : readUser();
|
|
188
|
+
const row = await this.#db.findOne("model_provider_credentials", tenantWhere(tenant, provider));
|
|
189
|
+
return row ? {
|
|
190
|
+
provider: row.provider,
|
|
191
|
+
scope,
|
|
192
|
+
credential: await this.#decryptCredential(row.data)
|
|
193
|
+
} : void 0;
|
|
205
194
|
}
|
|
206
195
|
/**
|
|
207
196
|
* Refresh the tenant's OAuth credential under the backend's atomic
|
|
@@ -209,10 +198,12 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
209
198
|
* rotating refresh tokens. The expiry is re-checked under the lock —
|
|
210
199
|
* another replica may have refreshed already, in which case `refreshFn` is
|
|
211
200
|
* skipped and the fresh credential is returned. Returns `undefined` when no
|
|
212
|
-
* OAuth row exists for the tenant.
|
|
201
|
+
* OAuth row exists for the tenant. A failed `refreshFn` is recorded on the
|
|
202
|
+
* row (surfaced as `CredentialRecord.refreshFailedAt`) and rethrown.
|
|
213
203
|
*/
|
|
214
204
|
async refreshOAuth(tenant, provider, refreshFn) {
|
|
215
205
|
let result;
|
|
206
|
+
let failure;
|
|
216
207
|
await this.#db.updateAtomic("model_provider_credentials", tenantWhere(tenant, provider), async (row) => {
|
|
217
208
|
const decrypted = await this.encryption.decrypt(row.data);
|
|
218
209
|
if (decrypted.value.type !== "oauth") return null;
|
|
@@ -221,13 +212,19 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
221
212
|
result = current;
|
|
222
213
|
return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;
|
|
223
214
|
}
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
try {
|
|
216
|
+
result = await refreshFn(current);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
failure = { error };
|
|
219
|
+
return { refresh_failed_at: /* @__PURE__ */ new Date() };
|
|
220
|
+
}
|
|
226
221
|
return {
|
|
227
|
-
data: await this.encryption.encrypt(
|
|
228
|
-
updated_at: /* @__PURE__ */ new Date()
|
|
222
|
+
data: await this.encryption.encrypt(result),
|
|
223
|
+
updated_at: /* @__PURE__ */ new Date(),
|
|
224
|
+
refresh_failed_at: null
|
|
229
225
|
};
|
|
230
226
|
});
|
|
227
|
+
if (failure) throw failure.error;
|
|
231
228
|
return result;
|
|
232
229
|
}
|
|
233
230
|
/** Persist a pending login flow started by a web route. */
|
|
@@ -288,7 +285,6 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
|
|
|
288
285
|
await this.#db.deleteMany("oauth_login_sessions", { session_id: sessionId });
|
|
289
286
|
}
|
|
290
287
|
};
|
|
291
|
-
//#endregion
|
|
292
288
|
export { MODEL_CREDENTIALS_SCHEMA, ModelCredentialsStorage, OAUTH_LOGIN_SESSIONS_SCHEMA, isOAuthCredentialExpired };
|
|
293
289
|
|
|
294
290
|
//# sourceMappingURL=base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/credentials/base.ts"],"sourcesContent":["/**\n * Model provider credentials domain — tenant-scoped OAuth tokens and API keys\n * for model providers (Anthropic/Claude Max, OpenAI Codex, GitHub Copilot,\n * xAI, plus plain API-key providers).\n *\n * Tenancy is two-level:\n * - **user-scoped** rows (`userId` present): personal plan OAuth tokens and\n * personal API keys. OAuth plan tokens are ONLY ever user-scoped — they are\n * personal subscriptions, and sharing one would bill/rate-limit a single\n * account for the whole org.\n * - **org-scoped** rows (`userId` absent): shared API keys set by an org\n * admin, inherited by every member.\n *\n * Model calls resolve exactly one scope: user or org. Deployed tenant stores\n * never fall back to the other scope or implicitly borrow server environment keys.\n *\n * Secrets posture: credentials are stored server-side only and never returned\n * to the client (same as `linear_connections.access_token`). Encryption at\n * rest is out of scope, matching the existing posture.\n *\n * The domain also owns `oauth_login_sessions`: pending web login flows\n * (paste-code PKCE verifiers, device-code poll state) persisted server-side so\n * any replica can complete or poll a flow started on another.\n */\n\nimport type { AuthCredential, OAuthCredential } from '@mastra/code-sdk/auth/types';\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\n/** Owning tenant of a credential row. `userId` absent = org-scoped row. */\nexport interface CredentialTenant {\n orgId: string;\n userId?: string;\n}\n\n/** Which tenancy level a credential row lives at. */\nexport type CredentialScope = 'user' | 'org';\n\n/** One stored credential, annotated with its scope. */\nexport interface CredentialRecord {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n updatedAt: Date;\n}\n\n/** Result of per-caller resolution: the winning credential and its scope. */\nexport interface ResolvedCredential {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n}\n\n/** Kind of pending web login flow a session row tracks. */\nexport type LoginSessionKind = 'paste-code' | 'device-code';\n\n/** Scope the completed credential should be written to. */\nexport type LoginCredentialScope = 'user' | 'org';\n\n/** One pending OAuth login flow, persisted so any replica can continue it. */\nexport interface LoginSessionRow {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n /** Where the completed credential lands; absent rows default to `'user'`. */\n credentialScope?: LoginCredentialScope;\n /** Serialized flow state (PKCE verifier, device-code pending state, ...). */\n pending: Record<string, unknown>;\n expiresAt: Date;\n /** Earliest time the next upstream poll is allowed (device-code flows). */\n nextPollAt: Date | null;\n createdAt: Date;\n}\n\nexport interface CreateLoginSessionInput {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n credentialScope?: LoginCredentialScope;\n pending: Record<string, unknown>;\n expiresAt: Date;\n nextPollAt?: Date | null;\n}\n\n/** Mirror of `AuthStorage.getApiKey()`'s expiry check. */\nexport function isOAuthCredentialExpired(credential: OAuthCredential, now = Date.now()): boolean {\n return now >= credential.expires;\n}\n\nexport const MODEL_CREDENTIALS_SCHEMA: CollectionSchema = {\n name: 'model_provider_credentials',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text', nullable: true },\n provider: { type: 'text' },\n type: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [\n // Scope is encoded in `user_id` nullability (NULL = org-scoped shared\n // row), enforced by two partial unique indexes.\n {\n name: 'model_provider_credentials_user_unique',\n columns: ['org_id', 'user_id', 'provider'],\n whereNotNull: 'user_id',\n },\n { name: 'model_provider_credentials_org_unique', columns: ['org_id', 'provider'], whereNull: 'user_id' },\n ],\n};\n\nexport const OAUTH_LOGIN_SESSIONS_SCHEMA: CollectionSchema = {\n name: 'oauth_login_sessions',\n columns: {\n session_id: { type: 'text', primaryKey: true },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n provider: { type: 'text' },\n kind: { type: 'text' },\n credential_scope: { type: 'text', nullable: true },\n pending: { type: 'json' },\n expires_at: { type: 'timestamp' },\n next_poll_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n },\n};\n\n/** Column shape of one `model_provider_credentials` row as returned by ops. */\ninterface CredentialDbRow extends Record<string, unknown> {\n id: string;\n provider: string;\n user_id: string | null;\n data: unknown;\n updated_at: Date;\n}\n\n/** Column shape of one `oauth_login_sessions` row as returned by ops. */\ninterface LoginSessionDbRow extends Record<string, unknown> {\n session_id: string;\n org_id: string;\n user_id: string;\n provider: string;\n kind: LoginSessionKind;\n credential_scope: LoginCredentialScope | null;\n pending: Record<string, unknown>;\n expires_at: Date;\n next_poll_at: Date | null;\n created_at: Date;\n}\n\nfunction toSessionRow(db: LoginSessionDbRow): LoginSessionRow {\n return {\n sessionId: db.session_id,\n orgId: db.org_id,\n userId: db.user_id,\n provider: db.provider,\n kind: db.kind,\n ...(db.credential_scope ? { credentialScope: db.credential_scope } : {}),\n pending: db.pending,\n expiresAt: db.expires_at,\n nextPollAt: db.next_poll_at,\n createdAt: db.created_at,\n };\n}\n\n/** Filter selecting exactly the tenant's row for a provider (`null` = org row). */\nfunction tenantWhere(tenant: CredentialTenant, provider: string): CollectionWhere {\n return { org_id: tenant.orgId, provider, user_id: tenant.userId ?? null };\n}\n\n/**\n * Model-credentials storage, written once against the generic\n * `FactoryStorageOps` surface. `refreshOAuth()` and `claimLoginSession()`\n * ride `updateAtomic` so concurrent replicas serialize instead of\n * invalidating each other's rotating tokens / double-claiming a flow.\n */\nexport class ModelCredentialsStorage extends FactoryStorageDomain {\n constructor(private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption()) {\n super('model-credentials');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);\n const rows = await this.ops.findMany<CredentialDbRow>('model_provider_credentials', {});\n await Promise.all(rows.map(row => this.#migrateCredential(row.id)));\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('oauth_login_sessions', {});\n await this.ops.deleteMany('model_provider_credentials', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #decryptCredential(value: unknown): Promise<AuthCredential> {\n return (await this.encryption.decrypt<AuthCredential>(value)).value;\n }\n\n async #migrateCredential(id: string): Promise<void> {\n await this.#db.updateAtomic<CredentialDbRow>('model_provider_credentials', { id }, async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (!decrypted.needsReencryption) return null;\n return { data: await this.encryption.encrypt(decrypted.value) };\n });\n }\n\n /** Read the tenant's credential for a provider at exactly that scope. */\n async getCredential(tenant: CredentialTenant, provider: string): Promise<AuthCredential | undefined> {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', tenantWhere(tenant, provider));\n return row ? this.#decryptCredential(row.data) : undefined;\n }\n\n /** Upsert the tenant's credential (`created_at` is preserved on update). */\n async setCredential(tenant: CredentialTenant, provider: string, credential: AuthCredential): Promise<void> {\n const where = tenantWhere(tenant, provider);\n const encrypted = await this.encryption.encrypt(credential);\n const update = async () =>\n this.#db.updateMany('model_provider_credentials', where, {\n type: credential.type,\n data: encrypted,\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne<CredentialDbRow>('model_provider_credentials', {\n org_id: tenant.orgId,\n user_id: tenant.userId ?? null,\n provider,\n type: credential.type,\n data: encrypted,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n }\n\n /** Delete the tenant's credential at exactly that scope. True when a row was removed. */\n async removeCredential(tenant: CredentialTenant, provider: string): Promise<boolean> {\n const deleted = await this.#db.deleteMany('model_provider_credentials', tenantWhere(tenant, provider));\n return deleted > 0;\n }\n\n /** All credentials visible to a user: their own rows plus the org's shared rows. */\n async listCredentials(orgId: string, userId: string): Promise<CredentialRecord[]> {\n // SQL `IN` never matches NULL, so the org rows need their own query.\n const [userRows, orgRows] = await Promise.all([\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: userId }),\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: null }),\n ]);\n return Promise.all([\n ...userRows.map(async row => ({\n provider: row.provider,\n scope: 'user' as const,\n credential: await this.#decryptCredential(row.data),\n updatedAt: row.updated_at,\n })),\n ...orgRows.map(async row => ({\n provider: row.provider,\n scope: 'org' as const,\n credential: await this.#decryptCredential(row.data),\n updatedAt: row.updated_at,\n })),\n ]);\n }\n\n /** Resolve a provider credential from exactly one tenancy scope. */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n scope: CredentialScope,\n ): Promise<ResolvedCredential | undefined> {\n const readUser = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: userId,\n });\n return row\n ? { provider: row.provider, scope: 'user', credential: await this.#decryptCredential(row.data) }\n : undefined;\n };\n const readOrg = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: null,\n });\n return row\n ? { provider: row.provider, scope: 'org', credential: await this.#decryptCredential(row.data) }\n : undefined;\n };\n return scope === 'org' ? readOrg() : readUser();\n }\n\n /**\n * Refresh the tenant's OAuth credential under the backend's atomic\n * read-modify-write so concurrent replicas don't invalidate each other's\n * rotating refresh tokens. The expiry is re-checked under the lock —\n * another replica may have refreshed already, in which case `refreshFn` is\n * skipped and the fresh credential is returned. Returns `undefined` when no\n * OAuth row exists for the tenant.\n */\n async refreshOAuth(\n tenant: CredentialTenant,\n provider: string,\n refreshFn: (current: OAuthCredential) => Promise<OAuthCredential>,\n ): Promise<OAuthCredential | undefined> {\n let result: OAuthCredential | undefined;\n await this.#db.updateAtomic<CredentialDbRow>(\n 'model_provider_credentials',\n tenantWhere(tenant, provider),\n async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (decrypted.value.type !== 'oauth') return null;\n const current = decrypted.value;\n // Re-check under the lock: another replica may have refreshed while we waited.\n if (!isOAuthCredentialExpired(current)) {\n result = current;\n return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;\n }\n const next = await refreshFn(current);\n result = next;\n return { data: await this.encryption.encrypt(next), updated_at: new Date() };\n },\n );\n return result;\n }\n\n /** Persist a pending login flow started by a web route. */\n async createLoginSession(input: CreateLoginSessionInput): Promise<LoginSessionRow> {\n const inserted = await this.#db.insertOne<LoginSessionDbRow>('oauth_login_sessions', {\n session_id: input.sessionId,\n org_id: input.orgId,\n user_id: input.userId,\n provider: input.provider,\n kind: input.kind,\n credential_scope: input.credentialScope ?? null,\n pending: input.pending,\n expires_at: input.expiresAt,\n next_poll_at: input.nextPollAt ?? null,\n created_at: new Date(),\n });\n return toSessionRow(inserted);\n }\n\n /**\n * Read a pending login flow. An expired session is deleted on read (TTL\n * cleanup) and reported as absent.\n */\n async getLoginSession(sessionId: string): Promise<LoginSessionRow | undefined> {\n const row = await this.#db.findOne<LoginSessionDbRow>('oauth_login_sessions', { session_id: sessionId });\n if (!row) return undefined;\n if (row.expires_at.getTime() <= Date.now()) {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n return undefined;\n }\n return toSessionRow(row);\n }\n\n /**\n * Atomically claim a due login session for one upstream completion/poll attempt.\n * Returns undefined when the session is missing, expired, owned by another\n * tenant/provider, or already claimed by a concurrent request.\n */\n async claimLoginSession(\n sessionId: string,\n owner: Pick<LoginSessionRow, 'orgId' | 'userId' | 'provider' | 'kind'>,\n ): Promise<LoginSessionRow | undefined> {\n const now = Date.now();\n let claimed = false;\n const updated = await this.#db.updateAtomic<LoginSessionDbRow>(\n 'oauth_login_sessions',\n { session_id: sessionId },\n row => {\n if (\n row.org_id !== owner.orgId ||\n row.user_id !== owner.userId ||\n row.provider !== owner.provider ||\n row.kind !== owner.kind\n ) {\n return null;\n }\n if (row.expires_at.getTime() <= now) return null;\n if (row.next_poll_at && row.next_poll_at.getTime() > now) return null;\n claimed = true;\n // Park the next poll at expiry so a concurrent claim loses until the\n // claimer either touches the session forward or deletes it.\n return { next_poll_at: row.expires_at };\n },\n );\n return claimed && updated ? toSessionRow(updated) : undefined;\n }\n\n /** Update a pending flow's serialized state and/or next allowed poll time. */\n async touchLoginSession(\n sessionId: string,\n updates: { pending?: Record<string, unknown>; nextPollAt?: Date | null },\n ): Promise<void> {\n const set: Record<string, unknown> = {};\n if (updates.pending !== undefined) set.pending = updates.pending;\n if (updates.nextPollAt !== undefined) set.next_poll_at = updates.nextPollAt;\n if (Object.keys(set).length === 0) return;\n await this.#db.updateMany('oauth_login_sessions', { session_id: sessionId }, set);\n }\n\n /** Remove a pending login flow (completed, cancelled, or failed). */\n async deleteLoginSession(sessionId: string): Promise<void> {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/credentials/base.ts"],"sourcesContent":["/**\n * Model provider credentials domain — tenant-scoped OAuth tokens and API keys\n * for model providers (Anthropic/Claude Max, OpenAI Codex, GitHub Copilot,\n * xAI, plus plain API-key providers).\n *\n * Tenancy is two-level:\n * - **user-scoped** rows (`userId` present): personal plan OAuth tokens and\n * personal API keys. OAuth plan tokens are ONLY ever user-scoped — they are\n * personal subscriptions, and sharing one would bill/rate-limit a single\n * account for the whole org.\n * - **org-scoped** rows (`userId` absent): shared API keys set by an org\n * admin, inherited by every member.\n *\n * Model calls resolve exactly one scope: user or org. Deployed tenant stores\n * never fall back to the other scope or implicitly borrow server environment keys.\n *\n * Secrets posture: credentials are stored server-side only and never returned\n * to the client (same as `linear_connections.access_token`). Encryption at\n * rest is out of scope, matching the existing posture.\n *\n * The domain also owns `oauth_login_sessions`: pending web login flows\n * (paste-code PKCE verifiers, device-code poll state) persisted server-side so\n * any replica can complete or poll a flow started on another.\n */\n\nimport type { AuthCredential, OAuthCredential } from '@mastra/code-sdk/auth/types';\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\n/** Owning tenant of a credential row. `userId` absent = org-scoped row. */\nexport interface CredentialTenant {\n orgId: string;\n userId?: string;\n}\n\n/** Which tenancy level a credential row lives at. */\nexport type CredentialScope = 'user' | 'org';\n\n/** One stored credential, annotated with its scope. */\nexport interface CredentialRecord {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n updatedAt: Date;\n /** When the last OAuth token refresh failed; cleared by the next successful refresh or a new sign-in. */\n refreshFailedAt?: Date;\n}\n\n/** Result of per-caller resolution: the winning credential and its scope. */\nexport interface ResolvedCredential {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n}\n\n/** Kind of pending web login flow a session row tracks. */\nexport type LoginSessionKind = 'paste-code' | 'device-code';\n\n/** Scope the completed credential should be written to. */\nexport type LoginCredentialScope = 'user' | 'org';\n\n/** One pending OAuth login flow, persisted so any replica can continue it. */\nexport interface LoginSessionRow {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n /** Where the completed credential lands; absent rows default to `'user'`. */\n credentialScope?: LoginCredentialScope;\n /** Serialized flow state (PKCE verifier, device-code pending state, ...). */\n pending: Record<string, unknown>;\n expiresAt: Date;\n /** Earliest time the next upstream poll is allowed (device-code flows). */\n nextPollAt: Date | null;\n createdAt: Date;\n}\n\nexport interface CreateLoginSessionInput {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n credentialScope?: LoginCredentialScope;\n pending: Record<string, unknown>;\n expiresAt: Date;\n nextPollAt?: Date | null;\n}\n\n/** Mirror of `AuthStorage.getApiKey()`'s expiry check. */\nexport function isOAuthCredentialExpired(credential: OAuthCredential, now = Date.now()): boolean {\n return now >= credential.expires;\n}\n\nexport const MODEL_CREDENTIALS_SCHEMA: CollectionSchema = {\n name: 'model_provider_credentials',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text', nullable: true },\n provider: { type: 'text' },\n type: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n refresh_failed_at: { type: 'timestamp', nullable: true },\n },\n uniqueIndexes: [\n // Scope is encoded in `user_id` nullability (NULL = org-scoped shared\n // row), enforced by two partial unique indexes.\n {\n name: 'model_provider_credentials_user_unique',\n columns: ['org_id', 'user_id', 'provider'],\n whereNotNull: 'user_id',\n },\n { name: 'model_provider_credentials_org_unique', columns: ['org_id', 'provider'], whereNull: 'user_id' },\n ],\n};\n\nexport const OAUTH_LOGIN_SESSIONS_SCHEMA: CollectionSchema = {\n name: 'oauth_login_sessions',\n columns: {\n session_id: { type: 'text', primaryKey: true },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n provider: { type: 'text' },\n kind: { type: 'text' },\n credential_scope: { type: 'text', nullable: true },\n pending: { type: 'json' },\n expires_at: { type: 'timestamp' },\n next_poll_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n },\n};\n\n/** Column shape of one `model_provider_credentials` row as returned by ops. */\ninterface CredentialDbRow extends Record<string, unknown> {\n id: string;\n provider: string;\n user_id: string | null;\n data: unknown;\n updated_at: Date;\n refresh_failed_at: Date | null;\n}\n\n/** Column shape of one `oauth_login_sessions` row as returned by ops. */\ninterface LoginSessionDbRow extends Record<string, unknown> {\n session_id: string;\n org_id: string;\n user_id: string;\n provider: string;\n kind: LoginSessionKind;\n credential_scope: LoginCredentialScope | null;\n pending: Record<string, unknown>;\n expires_at: Date;\n next_poll_at: Date | null;\n created_at: Date;\n}\n\nfunction toSessionRow(db: LoginSessionDbRow): LoginSessionRow {\n return {\n sessionId: db.session_id,\n orgId: db.org_id,\n userId: db.user_id,\n provider: db.provider,\n kind: db.kind,\n ...(db.credential_scope ? { credentialScope: db.credential_scope } : {}),\n pending: db.pending,\n expiresAt: db.expires_at,\n nextPollAt: db.next_poll_at,\n createdAt: db.created_at,\n };\n}\n\n/** Filter selecting exactly the tenant's row for a provider (`null` = org row). */\nfunction tenantWhere(tenant: CredentialTenant, provider: string): CollectionWhere {\n return { org_id: tenant.orgId, provider, user_id: tenant.userId ?? null };\n}\n\n/**\n * Model-credentials storage, written once against the generic\n * `FactoryStorageOps` surface. `refreshOAuth()` and `claimLoginSession()`\n * ride `updateAtomic` so concurrent replicas serialize instead of\n * invalidating each other's rotating tokens / double-claiming a flow.\n */\nexport class ModelCredentialsStorage extends FactoryStorageDomain {\n constructor(private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption()) {\n super('model-credentials');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);\n const rows = await this.ops.findMany<CredentialDbRow>('model_provider_credentials', {});\n await Promise.all(rows.map(row => this.#migrateCredential(row.id)));\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('oauth_login_sessions', {});\n await this.ops.deleteMany('model_provider_credentials', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #decryptCredential(value: unknown): Promise<AuthCredential> {\n return (await this.encryption.decrypt<AuthCredential>(value)).value;\n }\n\n async #toRecord(row: CredentialDbRow, scope: CredentialScope): Promise<CredentialRecord> {\n return {\n provider: row.provider,\n scope,\n credential: await this.#decryptCredential(row.data),\n updatedAt: row.updated_at,\n refreshFailedAt: row.refresh_failed_at ?? undefined,\n };\n }\n\n async #migrateCredential(id: string): Promise<void> {\n await this.#db.updateAtomic<CredentialDbRow>('model_provider_credentials', { id }, async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (!decrypted.needsReencryption) return null;\n return { data: await this.encryption.encrypt(decrypted.value) };\n });\n }\n\n /** Read the tenant's credential for a provider at exactly that scope. */\n async getCredential(tenant: CredentialTenant, provider: string): Promise<AuthCredential | undefined> {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', tenantWhere(tenant, provider));\n return row ? this.#decryptCredential(row.data) : undefined;\n }\n\n /** Upsert the tenant's credential (`created_at` is preserved on update). */\n async setCredential(tenant: CredentialTenant, provider: string, credential: AuthCredential): Promise<void> {\n const where = tenantWhere(tenant, provider);\n const encrypted = await this.encryption.encrypt(credential);\n const update = async () =>\n this.#db.updateMany('model_provider_credentials', where, {\n type: credential.type,\n data: encrypted,\n updated_at: new Date(),\n refresh_failed_at: null,\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne<CredentialDbRow>('model_provider_credentials', {\n org_id: tenant.orgId,\n user_id: tenant.userId ?? null,\n provider,\n type: credential.type,\n data: encrypted,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n }\n\n /** Delete the tenant's credential at exactly that scope. True when a row was removed. */\n async removeCredential(tenant: CredentialTenant, provider: string): Promise<boolean> {\n const deleted = await this.#db.deleteMany('model_provider_credentials', tenantWhere(tenant, provider));\n return deleted > 0;\n }\n\n /** All credentials visible to a user: their own rows plus the org's shared rows. */\n async listCredentials(orgId: string, userId: string): Promise<CredentialRecord[]> {\n // SQL `IN` never matches NULL, so the org rows need their own query.\n const [userRows, orgRows] = await Promise.all([\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: userId }),\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: null }),\n ]);\n return Promise.all([\n ...userRows.map(row => this.#toRecord(row, 'user')),\n ...orgRows.map(row => this.#toRecord(row, 'org')),\n ]);\n }\n\n /** Resolve a provider credential from exactly one tenancy scope. */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n scope: CredentialScope,\n ): Promise<ResolvedCredential | undefined> {\n const tenant = scope === 'org' ? { orgId } : { orgId, userId };\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', tenantWhere(tenant, provider));\n return row ? { provider: row.provider, scope, credential: await this.#decryptCredential(row.data) } : undefined;\n }\n\n /**\n * Refresh the tenant's OAuth credential under the backend's atomic\n * read-modify-write so concurrent replicas don't invalidate each other's\n * rotating refresh tokens. The expiry is re-checked under the lock —\n * another replica may have refreshed already, in which case `refreshFn` is\n * skipped and the fresh credential is returned. Returns `undefined` when no\n * OAuth row exists for the tenant. A failed `refreshFn` is recorded on the\n * row (surfaced as `CredentialRecord.refreshFailedAt`) and rethrown.\n */\n async refreshOAuth(\n tenant: CredentialTenant,\n provider: string,\n refreshFn: (current: OAuthCredential) => Promise<OAuthCredential>,\n ): Promise<OAuthCredential | undefined> {\n let result: OAuthCredential | undefined;\n let failure: { error: unknown } | undefined;\n await this.#db.updateAtomic<CredentialDbRow>(\n 'model_provider_credentials',\n tenantWhere(tenant, provider),\n async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (decrypted.value.type !== 'oauth') return null;\n const current = decrypted.value;\n // Re-check under the lock: another replica may have refreshed while we waited.\n if (!isOAuthCredentialExpired(current)) {\n result = current;\n return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;\n }\n try {\n result = await refreshFn(current);\n } catch (error) {\n failure = { error };\n return { refresh_failed_at: new Date() };\n }\n return { data: await this.encryption.encrypt(result), updated_at: new Date(), refresh_failed_at: null };\n },\n );\n if (failure) throw failure.error;\n return result;\n }\n\n /** Persist a pending login flow started by a web route. */\n async createLoginSession(input: CreateLoginSessionInput): Promise<LoginSessionRow> {\n const inserted = await this.#db.insertOne<LoginSessionDbRow>('oauth_login_sessions', {\n session_id: input.sessionId,\n org_id: input.orgId,\n user_id: input.userId,\n provider: input.provider,\n kind: input.kind,\n credential_scope: input.credentialScope ?? null,\n pending: input.pending,\n expires_at: input.expiresAt,\n next_poll_at: input.nextPollAt ?? null,\n created_at: new Date(),\n });\n return toSessionRow(inserted);\n }\n\n /**\n * Read a pending login flow. An expired session is deleted on read (TTL\n * cleanup) and reported as absent.\n */\n async getLoginSession(sessionId: string): Promise<LoginSessionRow | undefined> {\n const row = await this.#db.findOne<LoginSessionDbRow>('oauth_login_sessions', { session_id: sessionId });\n if (!row) return undefined;\n if (row.expires_at.getTime() <= Date.now()) {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n return undefined;\n }\n return toSessionRow(row);\n }\n\n /**\n * Atomically claim a due login session for one upstream completion/poll attempt.\n * Returns undefined when the session is missing, expired, owned by another\n * tenant/provider, or already claimed by a concurrent request.\n */\n async claimLoginSession(\n sessionId: string,\n owner: Pick<LoginSessionRow, 'orgId' | 'userId' | 'provider' | 'kind'>,\n ): Promise<LoginSessionRow | undefined> {\n const now = Date.now();\n let claimed = false;\n const updated = await this.#db.updateAtomic<LoginSessionDbRow>(\n 'oauth_login_sessions',\n { session_id: sessionId },\n row => {\n if (\n row.org_id !== owner.orgId ||\n row.user_id !== owner.userId ||\n row.provider !== owner.provider ||\n row.kind !== owner.kind\n ) {\n return null;\n }\n if (row.expires_at.getTime() <= now) return null;\n if (row.next_poll_at && row.next_poll_at.getTime() > now) return null;\n claimed = true;\n // Park the next poll at expiry so a concurrent claim loses until the\n // claimer either touches the session forward or deletes it.\n return { next_poll_at: row.expires_at };\n },\n );\n return claimed && updated ? toSessionRow(updated) : undefined;\n }\n\n /** Update a pending flow's serialized state and/or next allowed poll time. */\n async touchLoginSession(\n sessionId: string,\n updates: { pending?: Record<string, unknown>; nextPollAt?: Date | null },\n ): Promise<void> {\n const set: Record<string, unknown> = {};\n if (updates.pending !== undefined) set.pending = updates.pending;\n if (updates.nextPollAt !== undefined) set.next_poll_at = updates.nextPollAt;\n if (Object.keys(set).length === 0) return;\n await this.#db.updateMany('oauth_login_sessions', { session_id: sessionId }, set);\n }\n\n /** Remove a pending login flow (completed, cancelled, or failed). */\n async deleteLoginSession(sessionId: string): Promise<void> {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
|
|
2
2
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/custom-providers/base.ts
|
|
4
4
|
const CUSTOM_PROVIDERS_SCHEMA = {
|
|
5
5
|
name: "custom_providers",
|
|
6
6
|
columns: {
|
|
@@ -170,7 +170,6 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
|
|
|
170
170
|
}) > 0;
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
|
-
//#endregion
|
|
174
173
|
export { CUSTOM_PROVIDERS_SCHEMA, CustomProvidersStorage };
|
|
175
174
|
|
|
176
175
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FactoryStorageDomain } from "@mastra/core/storage";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/filesystem/base.ts
|
|
3
3
|
const SNAPSHOTS = "filesystem_snapshots";
|
|
4
4
|
const FILESYSTEM_SCHEMAS = [{
|
|
5
5
|
name: SNAPSHOTS,
|
|
@@ -73,7 +73,6 @@ var FilesystemStorage = class extends FactoryStorageDomain {
|
|
|
73
73
|
return this.ops.deleteMany(SNAPSHOTS, { resource_id: args.resourceId });
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
|
-
//#endregion
|
|
77
76
|
export { FILESYSTEM_SCHEMAS, FilesystemStorage };
|
|
78
77
|
|
|
79
78
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/intake/base.ts
|
|
3
3
|
/**
|
|
4
4
|
* Org-wide intake selections for every configured intake integration.
|
|
5
5
|
*
|
|
@@ -425,7 +425,6 @@ var IntakeStorage = class extends FactoryStorageDomain {
|
|
|
425
425
|
}, { isolationLevel: "serializable" });
|
|
426
426
|
}
|
|
427
427
|
};
|
|
428
|
-
//#endregion
|
|
429
428
|
export { DEFAULT_INTAKE_CONFIG, INTAKE_LABEL_ROUTES_SCHEMA, INTAKE_ORG_SETTINGS_SCHEMA, INTAKE_SOURCE_BINDINGS_SCHEMA, IntakeStorage, LEGACY_INTAKE_USER_SETTINGS_SCHEMA, mergeIntakeSelections, normalizeIntakeLabel, resolveIntakeLabelRoute };
|
|
430
429
|
|
|
431
430
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
|
|
2
2
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/integrations/base.ts
|
|
4
4
|
/**
|
|
5
5
|
* Generic integration storage — the default persistence surface for
|
|
6
6
|
* `FactoryIntegration`s.
|
|
@@ -334,7 +334,6 @@ var IntegrationStorage = class extends FactoryStorageDomain {
|
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
336
|
};
|
|
337
|
-
//#endregion
|
|
338
337
|
export { INTEGRATION_CONNECTIONS_SCHEMA, INTEGRATION_SETTINGS_SCHEMA, INTEGRATION_SUBSCRIPTIONS_SCHEMA, IntegrationStorage };
|
|
339
338
|
|
|
340
339
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/memory-settings/base.ts
|
|
3
3
|
/**
|
|
4
4
|
* Sentinel `user_id` for a factory project's shared memory settings. Factory
|
|
5
5
|
* runs are org-shared, so their OM configuration lives on the project — this
|
|
@@ -123,7 +123,6 @@ var MemorySettingsStorage = class extends FactoryStorageDomain {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
|
-
//#endregion
|
|
127
126
|
export { MEMORY_SETTINGS_SCHEMA, MemorySettingsStorage, factoryMemorySettingsUserId };
|
|
128
127
|
|
|
129
128
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
2
|
import { isThinkingLevelSetting } from "@mastra/code-sdk/thinking";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/model-defaults/base.ts
|
|
4
4
|
const USER_DEFAULT_MODELS_SCHEMA = {
|
|
5
5
|
name: "user_default_models",
|
|
6
6
|
columns: {
|
|
@@ -91,7 +91,6 @@ var ModelDefaultsStorage = class extends FactoryStorageDomain {
|
|
|
91
91
|
}) > 0;
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
|
-
//#endregion
|
|
95
94
|
export { ModelDefaultsStorage, USER_DEFAULT_MODELS_SCHEMA };
|
|
96
95
|
|
|
97
96
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FactoryStorageDomain } from "@mastra/core/storage";
|
|
2
2
|
import { isThinkingLevelSetting } from "@mastra/code-sdk/thinking";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/projects/base.ts
|
|
4
4
|
const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;
|
|
5
5
|
const DEFAULT_FACTORY_THINKING_LEVEL = "medium";
|
|
6
6
|
const FACTORY_MODEL_SCOPES = [
|
|
@@ -366,7 +366,6 @@ var FactoryProjectsStorage = class extends FactoryStorageDomain {
|
|
|
366
366
|
}) > 0 ? project : null;
|
|
367
367
|
}
|
|
368
368
|
};
|
|
369
|
-
//#endregion
|
|
370
369
|
export { DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES, DEFAULT_FACTORY_THINKING_LEVEL, FACTORY_MODEL_SCOPES, FACTORY_PROJECTS_SCHEMA, FactoryProjectsStorage, effectiveModelScope };
|
|
371
370
|
|
|
372
371
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/queue-health/base.ts
|
|
3
3
|
/**
|
|
4
4
|
* Queue-health threshold configuration domain: per-project age buckets for the
|
|
5
5
|
* Factory Overview queue-health chart.
|
|
@@ -116,7 +116,6 @@ var QueueHealthStorage = class extends FactoryStorageDomain {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
|
-
//#endregion
|
|
120
119
|
export { DEFAULT_QUEUE_HEALTH_CONFIG, QUEUE_HEALTH_SETTINGS_SCHEMA, QueueHealthStorage, assertValidThresholds, parseQueueHealthConfig, thresholdsOrDefault };
|
|
121
120
|
|
|
122
121
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FACTORY_PROJECTS_SCHEMA } from "../projects/base.js";
|
|
2
2
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
3
|
-
|
|
3
|
+
// src/storage/domains/source-control/base.ts
|
|
4
4
|
const FACTORY_PROJECTS = "factory_projects";
|
|
5
5
|
const INSTALLATIONS = "source_control_installations";
|
|
6
6
|
const REPOSITORIES = "source_control_repositories";
|
|
@@ -1249,7 +1249,6 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
1249
1249
|
};
|
|
1250
1250
|
}
|
|
1251
1251
|
};
|
|
1252
|
-
//#endregion
|
|
1253
1252
|
export { SOURCE_CONTROL_SCHEMAS, SessionBranchHeldError, SessionOwnershipMismatchError, SourceControlConnectionNotFoundError, SourceControlStorage };
|
|
1254
1253
|
|
|
1255
1254
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UniqueViolationError } from "@mastra/core/storage";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/source-control/inmemory.ts
|
|
3
3
|
/** In-memory provider-scoped source-control handle for route tests. */
|
|
4
4
|
var SourceControlStorageInMemory = class {
|
|
5
5
|
integrationId;
|
|
@@ -457,7 +457,6 @@ var SourceControlStorageInMemory = class {
|
|
|
457
457
|
listBySession: async ({ sessionId }) => this.sessionRepositoriesRows.filter((row) => row.sessionId === sessionId)
|
|
458
458
|
};
|
|
459
459
|
};
|
|
460
|
-
//#endregion
|
|
461
460
|
export { SourceControlStorageInMemory };
|
|
462
461
|
|
|
463
462
|
//# sourceMappingURL=inmemory.js.map
|
|
@@ -4,7 +4,7 @@ import { WORK_ITEM_ACTIVITY_SCHEMA, WORK_ITEM_COMMENTS_SCHEMA, WORK_ITEM_COMMENT
|
|
|
4
4
|
import { BoardScopes, FACTORY_BOARD_VERSIONS_SCHEMA, bumpBoardVersions, readBoardVersion, trackBoardWrites } from "./board-version.js";
|
|
5
5
|
import { createHash } from "crypto";
|
|
6
6
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
7
|
-
|
|
7
|
+
// src/storage/domains/work-items/base.ts
|
|
8
8
|
/**
|
|
9
9
|
* Factory work-items storage domain.
|
|
10
10
|
*
|
|
@@ -2742,7 +2742,6 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
|
|
|
2742
2742
|
return removed;
|
|
2743
2743
|
}
|
|
2744
2744
|
};
|
|
2745
|
-
//#endregion
|
|
2746
2745
|
export { ACTIVE_RUN_BINDING_STAGES, FACTORY_PULL_REQUEST_RECONCILIATION_KEY, FACTORY_RULE_MATERIALIZATION_KEY, FACTORY_WORK_ITEM_SUBSCRIPTIONS_SCHEMA, WORK_ITEMS_SCHEMA, WorkItemClaimConflictError, WorkItemRelationError, WorkItemUpdateConflictError, WorkItemsStorage, applyStageTransition, externalSourceKey, factoryActivityAttentionIdentity, factoryAgentWaitingAttentionIdentity, factoryAttentionKey, factoryDecisionAttentionIdentity, factoryDecisionHash, factoryMentionAttentionIdentity, factoryProposalAttentionIdentity, isAgentActor, stampSessions, validateParentRelation };
|
|
2747
2746
|
|
|
2748
2747
|
//# sourceMappingURL=base.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/work-items/board-version.ts
|
|
3
3
|
/**
|
|
4
4
|
* One row per project whose `version` is replaced after every committed write to that project's
|
|
5
5
|
* `work_items`. Readers of the full card list compare it against the version their snapshot was
|
|
@@ -104,7 +104,6 @@ async function readBoardVersion(ops, orgId, factoryProjectId) {
|
|
|
104
104
|
factory_project_id: factoryProjectId
|
|
105
105
|
}))?.version;
|
|
106
106
|
}
|
|
107
|
-
//#endregion
|
|
108
107
|
export { BoardScopes, FACTORY_BOARD_VERSIONS_SCHEMA, bumpBoardVersions, readBoardVersion, trackBoardWrites };
|
|
109
108
|
|
|
110
109
|
//# sourceMappingURL=board-version.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isAgentActor } from "./base.js";
|
|
2
|
-
|
|
2
|
+
// src/storage/domains/work-items/metrics.ts
|
|
3
3
|
/**
|
|
4
4
|
* Aggregation math for the Factory Overview page.
|
|
5
5
|
*
|
|
@@ -220,7 +220,6 @@ function computeFactoryMetrics(boardItems, window) {
|
|
|
220
220
|
agentCoverage: agentCoverage(items, window)
|
|
221
221
|
};
|
|
222
222
|
}
|
|
223
|
-
//#endregion
|
|
224
223
|
export { DEFAULT_METRICS_WINDOW, MAX_METRICS_WINDOW, computeFactoryMetrics, parseMetricsRange };
|
|
225
224
|
|
|
226
225
|
//# sourceMappingURL=metrics.js.map
|