@mastra/factory 0.10.0-alpha.1 → 0.10.0-alpha.13

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +2 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +8 -0
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +25 -8
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -1
  12. package/dist/integrations/base.d.ts +3 -1
  13. package/dist/integrations/base.d.ts.map +1 -1
  14. package/dist/integrations/github/integration.d.ts +14 -0
  15. package/dist/integrations/github/integration.d.ts.map +1 -1
  16. package/dist/integrations/github/integration.js +48 -1
  17. package/dist/integrations/github/integration.js.map +1 -1
  18. package/dist/integrations/github/provenance.d.ts +7 -2
  19. package/dist/integrations/github/provenance.d.ts.map +1 -1
  20. package/dist/integrations/github/provenance.js +16 -2
  21. package/dist/integrations/github/provenance.js.map +1 -1
  22. package/dist/integrations/github/routes.d.ts +8 -1
  23. package/dist/integrations/github/routes.d.ts.map +1 -1
  24. package/dist/integrations/github/routes.js +228 -7
  25. package/dist/integrations/github/routes.js.map +1 -1
  26. package/dist/integrations/github/rules.d.ts.map +1 -1
  27. package/dist/integrations/github/rules.js +193 -127
  28. package/dist/integrations/github/rules.js.map +1 -1
  29. package/dist/integrations/github/session-subscriptions.d.ts +9 -0
  30. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  31. package/dist/integrations/github/session-subscriptions.js +39 -1
  32. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  33. package/dist/integrations/linear/routes.d.ts.map +1 -1
  34. package/dist/integrations/linear/routes.js +50 -0
  35. package/dist/integrations/linear/routes.js.map +1 -1
  36. package/dist/integrations/platform/github/integration.d.ts +3 -1
  37. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  38. package/dist/integrations/platform/github/integration.js +33 -0
  39. package/dist/integrations/platform/github/integration.js.map +1 -1
  40. package/dist/routes/attention.d.ts +17 -0
  41. package/dist/routes/attention.d.ts.map +1 -0
  42. package/dist/routes/attention.js +305 -0
  43. package/dist/routes/attention.js.map +1 -0
  44. package/dist/routes/intake.d.ts +5 -0
  45. package/dist/routes/intake.d.ts.map +1 -1
  46. package/dist/routes/intake.js +70 -19
  47. package/dist/routes/intake.js.map +1 -1
  48. package/dist/routes/surface.d.ts +6 -5
  49. package/dist/routes/surface.d.ts.map +1 -1
  50. package/dist/routes/surface.js +62 -43
  51. package/dist/routes/surface.js.map +1 -1
  52. package/dist/routes/work-items.d.ts.map +1 -1
  53. package/dist/routes/work-items.js +35 -14
  54. package/dist/routes/work-items.js.map +1 -1
  55. package/dist/rules/defaults.js +5 -5
  56. package/dist/rules/defaults.js.map +1 -1
  57. package/dist/rules/dispatch-errors.d.ts +13 -0
  58. package/dist/rules/dispatch-errors.d.ts.map +1 -0
  59. package/dist/rules/dispatch-errors.js +77 -0
  60. package/dist/rules/dispatch-errors.js.map +1 -0
  61. package/dist/rules/dispatcher.d.ts +9 -0
  62. package/dist/rules/dispatcher.d.ts.map +1 -1
  63. package/dist/rules/dispatcher.js +118 -45
  64. package/dist/rules/dispatcher.js.map +1 -1
  65. package/dist/rules/processor.d.ts.map +1 -1
  66. package/dist/rules/processor.js +30 -14
  67. package/dist/rules/processor.js.map +1 -1
  68. package/dist/rules/terminal-cleanup.d.ts +1 -1
  69. package/dist/rules/terminal-cleanup.d.ts.map +1 -1
  70. package/dist/rules/terminal-cleanup.js +2 -2
  71. package/dist/rules/terminal-cleanup.js.map +1 -1
  72. package/dist/rules/tools.d.ts.map +1 -1
  73. package/dist/rules/tools.js +9 -5
  74. package/dist/rules/tools.js.map +1 -1
  75. package/dist/rules/transition-service.d.ts +5 -2
  76. package/dist/rules/transition-service.d.ts.map +1 -1
  77. package/dist/rules/transition-service.js +15 -2
  78. package/dist/rules/transition-service.js.map +1 -1
  79. package/dist/rules/types.d.ts +6 -1
  80. package/dist/rules/types.d.ts.map +1 -1
  81. package/dist/rules/types.js +25 -1
  82. package/dist/rules/types.js.map +1 -1
  83. package/dist/secret-encryption.d.ts +25 -0
  84. package/dist/secret-encryption.d.ts.map +1 -0
  85. package/dist/secret-encryption.js +96 -0
  86. package/dist/secret-encryption.js.map +1 -0
  87. package/dist/session/factory-session.d.ts +13 -3
  88. package/dist/session/factory-session.d.ts.map +1 -1
  89. package/dist/session/factory-session.js +15 -6
  90. package/dist/session/factory-session.js.map +1 -1
  91. package/dist/session/first-exec-capture.d.ts +16 -10
  92. package/dist/session/first-exec-capture.d.ts.map +1 -1
  93. package/dist/session/first-exec-capture.js +74 -14
  94. package/dist/session/first-exec-capture.js.map +1 -1
  95. package/dist/session/first-message-capture.d.ts +14 -10
  96. package/dist/session/first-message-capture.d.ts.map +1 -1
  97. package/dist/session/first-message-capture.js +17 -11
  98. package/dist/session/first-message-capture.js.map +1 -1
  99. package/dist/session/session-title.d.ts +7 -0
  100. package/dist/session/session-title.d.ts.map +1 -0
  101. package/dist/session/session-title.js +14 -0
  102. package/dist/session/session-title.js.map +1 -0
  103. package/dist/session/thread-title-mirror.d.ts +35 -0
  104. package/dist/session/thread-title-mirror.d.ts.map +1 -0
  105. package/dist/session/thread-title-mirror.js +51 -0
  106. package/dist/session/thread-title-mirror.js.map +1 -0
  107. package/dist/storage/domains/credentials/base.d.ts +3 -1
  108. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  109. package/dist/storage/domains/credentials/base.js +33 -15
  110. package/dist/storage/domains/credentials/base.js.map +1 -1
  111. package/dist/storage/domains/custom-providers/base.d.ts +3 -1
  112. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  113. package/dist/storage/domains/custom-providers/base.js +45 -25
  114. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  115. package/dist/storage/domains/integrations/base.d.ts +3 -1
  116. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  117. package/dist/storage/domains/integrations/base.js +28 -12
  118. package/dist/storage/domains/integrations/base.js.map +1 -1
  119. package/dist/storage/domains/source-control/base.d.ts +8 -0
  120. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  121. package/dist/storage/domains/source-control/base.js +14 -2
  122. package/dist/storage/domains/source-control/base.js.map +1 -1
  123. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  124. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  125. package/dist/storage/domains/source-control/inmemory.js +7 -0
  126. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  127. package/dist/storage/domains/work-items/base.d.ts +120 -12
  128. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  129. package/dist/storage/domains/work-items/base.js +514 -44
  130. package/dist/storage/domains/work-items/base.js.map +1 -1
  131. package/dist/work-item-branch.d.ts +23 -0
  132. package/dist/work-item-branch.d.ts.map +1 -0
  133. package/dist/work-item-branch.js +39 -0
  134. package/dist/work-item-branch.js.map +1 -0
  135. package/factory-skills/factory-rereview/SKILL.md +2 -0
  136. package/factory-skills/factory-review/SKILL.md +2 -0
  137. package/factory-skills/factory-triage/SKILL.md +7 -17
  138. package/package.json +5 -5
@@ -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,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;CACjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,aAAa,CAAC;AAE5D,2DAA2D;AAC3D,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,KAAK,CAAC;AAElD,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,EAAE,IAAI,CAAC;IAChB,2EAA2E;IAC3E,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,0DAA0D;AAC1D,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,SAAa,GAAG,OAAO,CAE/F;AAED,eAAO,MAAM,wBAAwB,EAAE,gBAsBtC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,gBAczC,CAAC;AA6CF;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,oBAAoB;;;IAKzD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAS1C,yEAAyE;IACnE,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAKpG,4EAA4E;IACtE,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC1G,yFAAyF;IACnF,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpF,oFAAoF;IAC9E,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAsBjF;;;;OAIG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,eAAwB,GACnC,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAoB1C;;;;;;;OAOG;IACG,YAAY,CAChB,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,GAChE,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAqBvC,2DAA2D;IACrD,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,eAAe,CAAC;IAgBlF;;;OAGG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAU9E;;;;OAIG;IACG,iBAAiB,CACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC,GACrE,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IA0BvC,8EAA8E;IACxE,iBAAiB,CACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;KAAE,GACvE,OAAO,CAAC,IAAI,CAAC;IAQhB,qEAAqE;IAC/D,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3D"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/credentials/base.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
1
2
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
2
3
  //#region src/storage/domains/credentials/base.ts
3
4
  /** Mirror of `AuthStorage.getApiKey()`'s expiry check. */
@@ -86,11 +87,15 @@ function tenantWhere(tenant, provider) {
86
87
  * invalidating each other's rotating tokens / double-claiming a flow.
87
88
  */
88
89
  var ModelCredentialsStorage = class extends FactoryStorageDomain {
89
- constructor() {
90
+ encryption;
91
+ constructor(encryption = createPlaintextFactorySecretEncryption()) {
90
92
  super("model-credentials");
93
+ this.encryption = encryption;
91
94
  }
92
95
  async init() {
93
96
  await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);
97
+ const rows = await this.ops.findMany("model_provider_credentials", {});
98
+ await Promise.all(rows.map((row) => this.#migrateCredential(row.id)));
94
99
  }
95
100
  async dangerouslyClearAll() {
96
101
  await this.ops.deleteMany("oauth_login_sessions", {});
@@ -99,16 +104,28 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
99
104
  get #db() {
100
105
  return this.ops;
101
106
  }
107
+ async #decryptCredential(value) {
108
+ return (await this.encryption.decrypt(value)).value;
109
+ }
110
+ async #migrateCredential(id) {
111
+ await this.#db.updateAtomic("model_provider_credentials", { id }, async (row) => {
112
+ const decrypted = await this.encryption.decrypt(row.data);
113
+ if (!decrypted.needsReencryption) return null;
114
+ return { data: await this.encryption.encrypt(decrypted.value) };
115
+ });
116
+ }
102
117
  /** Read the tenant's credential for a provider at exactly that scope. */
103
118
  async getCredential(tenant, provider) {
104
- return (await this.#db.findOne("model_provider_credentials", tenantWhere(tenant, provider)))?.data;
119
+ const row = await this.#db.findOne("model_provider_credentials", tenantWhere(tenant, provider));
120
+ return row ? this.#decryptCredential(row.data) : void 0;
105
121
  }
106
122
  /** Upsert the tenant's credential (`created_at` is preserved on update). */
107
123
  async setCredential(tenant, provider, credential) {
108
124
  const where = tenantWhere(tenant, provider);
125
+ const encrypted = await this.encryption.encrypt(credential);
109
126
  const update = async () => this.#db.updateMany("model_provider_credentials", where, {
110
127
  type: credential.type,
111
- data: credential,
128
+ data: encrypted,
112
129
  updated_at: /* @__PURE__ */ new Date()
113
130
  });
114
131
  let lastError;
@@ -121,7 +138,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
121
138
  user_id: tenant.userId ?? null,
122
139
  provider,
123
140
  type: credential.type,
124
- data: credential,
141
+ data: encrypted,
125
142
  created_at: now,
126
143
  updated_at: now
127
144
  });
@@ -146,17 +163,17 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
146
163
  org_id: orgId,
147
164
  user_id: null
148
165
  })]);
149
- return [...userRows.map((row) => ({
166
+ return Promise.all([...userRows.map(async (row) => ({
150
167
  provider: row.provider,
151
168
  scope: "user",
152
- credential: row.data,
169
+ credential: await this.#decryptCredential(row.data),
153
170
  updatedAt: row.updated_at
154
- })), ...orgRows.map((row) => ({
171
+ })), ...orgRows.map(async (row) => ({
155
172
  provider: row.provider,
156
173
  scope: "org",
157
- credential: row.data,
174
+ credential: await this.#decryptCredential(row.data),
158
175
  updatedAt: row.updated_at
159
- }))];
176
+ }))]);
160
177
  }
161
178
  /**
162
179
  * Resolve the credential a caller should use for a provider. By default the
@@ -173,7 +190,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
173
190
  return row ? {
174
191
  provider: row.provider,
175
192
  scope: "user",
176
- credential: row.data
193
+ credential: await this.#decryptCredential(row.data)
177
194
  } : void 0;
178
195
  };
179
196
  const readOrg = async () => {
@@ -185,7 +202,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
185
202
  return row ? {
186
203
  provider: row.provider,
187
204
  scope: "org",
188
- credential: row.data
205
+ credential: await this.#decryptCredential(row.data)
189
206
  } : void 0;
190
207
  };
191
208
  return precedence === "org" ? await readOrg() ?? await readUser() : await readUser() ?? await readOrg();
@@ -201,16 +218,17 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
201
218
  async refreshOAuth(tenant, provider, refreshFn) {
202
219
  let result;
203
220
  await this.#db.updateAtomic("model_provider_credentials", tenantWhere(tenant, provider), async (row) => {
204
- if (row.data.type !== "oauth") return null;
205
- const current = row.data;
221
+ const decrypted = await this.encryption.decrypt(row.data);
222
+ if (decrypted.value.type !== "oauth") return null;
223
+ const current = decrypted.value;
206
224
  if (!isOAuthCredentialExpired(current)) {
207
225
  result = current;
208
- return null;
226
+ return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;
209
227
  }
210
228
  const next = await refreshFn(current);
211
229
  result = next;
212
230
  return {
213
- data: next,
231
+ data: await this.encryption.encrypt(next),
214
232
  updated_at: /* @__PURE__ */ new Date()
215
233
  };
216
234
  });
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#db"],"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 * Resolution order at model-call time is user > org (the caller layers server\n * env vars underneath as a final fallback).\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';\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: AuthCredential;\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() {\n super('model-credentials');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);\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 /** 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?.data;\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 update = async () =>\n this.#db.updateMany('model_provider_credentials', where, {\n type: credential.type,\n data: credential,\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: credential,\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 [\n ...userRows.map(row => ({\n provider: row.provider,\n scope: 'user' as const,\n credential: row.data,\n updatedAt: row.updated_at,\n })),\n ...orgRows.map(row => ({\n provider: row.provider,\n scope: 'org' as const,\n credential: row.data,\n updatedAt: row.updated_at,\n })),\n ];\n }\n\n /**\n * Resolve the credential a caller should use for a provider. By default the\n * user's own row wins over the org's shared row; `precedence: 'org'`\n * (automated factory runs) checks the org's shared row first.\n */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n precedence: CredentialScope = 'user',\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 ? { provider: row.provider, scope: 'user', credential: row.data } : 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 ? { provider: row.provider, scope: 'org', credential: row.data } : undefined;\n };\n return precedence === 'org' ? ((await readOrg()) ?? (await readUser())) : ((await readUser()) ?? (await readOrg()));\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 if (row.data.type !== 'oauth') return null;\n const current = row.data;\n // Re-check under the lock: another replica may have refreshed while we waited.\n if (!isOAuthCredentialExpired(current)) {\n result = current;\n return null;\n }\n const next = await refreshFn(current);\n result = next;\n return { data: 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":";;;AAyFA,SAAgB,yBAAyB,YAA6B,MAAM,KAAK,IAAI,GAAY;CAC/F,OAAO,OAAO,WAAW;AAC3B;AAEA,MAAa,2BAA6C;CACxD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS;GAAE,MAAM;GAAQ,UAAU;EAAK;EACxC,UAAU,EAAE,MAAM,OAAO;EACzB,MAAM,EAAE,MAAM,OAAO;EACrB,MAAM,EAAE,MAAM,OAAO;EACrB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAGb;EACE,MAAM;EACN,SAAS;GAAC;GAAU;GAAW;EAAU;EACzC,cAAc;CAChB,GACA;EAAE,MAAM;EAAyC,SAAS,CAAC,UAAU,UAAU;EAAG,WAAW;CAAU,CACzG;AACF;AAEA,MAAa,8BAAgD;CAC3D,MAAM;CACN,SAAS;EACP,YAAY;GAAE,MAAM;GAAQ,YAAY;EAAK;EAC7C,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS,EAAE,MAAM,OAAO;EACxB,UAAU,EAAE,MAAM,OAAO;EACzB,MAAM,EAAE,MAAM,OAAO;EACrB,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,SAAS,EAAE,MAAM,OAAO;EACxB,YAAY,EAAE,MAAM,YAAY;EAChC,cAAc;GAAE,MAAM;GAAa,UAAU;EAAK;EAClD,YAAY,EAAE,MAAM,YAAY;CAClC;AACF;AAyBA,SAAS,aAAa,IAAwC;CAC5D,OAAO;EACL,WAAW,GAAG;EACd,OAAO,GAAG;EACV,QAAQ,GAAG;EACX,UAAU,GAAG;EACb,MAAM,GAAG;EACT,GAAI,GAAG,mBAAmB,EAAE,iBAAiB,GAAG,iBAAiB,IAAI,CAAC;EACtE,SAAS,GAAG;EACZ,WAAW,GAAG;EACd,YAAY,GAAG;EACf,WAAW,GAAG;CAChB;AACF;;AAGA,SAAS,YAAY,QAA0B,UAAmC;CAChF,OAAO;EAAE,QAAQ,OAAO;EAAO;EAAU,SAAS,OAAO,UAAU;CAAK;AAC1E;;;;;;;AAQA,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,cAAc;EACZ,MAAM,mBAAmB;CAC3B;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,0BAA0B,2BAA2B,CAAC;CACtF;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,wBAAwB,CAAC,CAAC;EACpD,MAAM,KAAK,IAAI,WAAW,8BAA8B,CAAC,CAAC;CAC5D;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;;CAGA,MAAM,cAAc,QAA0B,UAAuD;EAEnG,QAAO,MADW,KAAKA,IAAI,QAAyB,8BAA8B,YAAY,QAAQ,QAAQ,CAAC,EAAA,EACnG;CACd;;CAGA,MAAM,cAAc,QAA0B,UAAkB,YAA2C;EACzG,MAAM,QAAQ,YAAY,QAAQ,QAAQ;EAC1C,MAAM,SAAS,YACb,KAAKA,IAAI,WAAW,8BAA8B,OAAO;GACvD,MAAM,WAAW;GACjB,MAAM;GACN,4BAAY,IAAI,KAAK;EACvB,CAAC;EAEH,IAAI;EACJ,KAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;GAC5C,IAAK,MAAM,OAAO,IAAK,GAAG;GAE1B,MAAM,sBAAM,IAAI,KAAK;GACrB,IAAI;IACF,MAAM,KAAKA,IAAI,UAA2B,8BAA8B;KACtE,QAAQ,OAAO;KACf,SAAS,OAAO,UAAU;KAC1B;KACA,MAAM,WAAW;KACjB,MAAM;KACN,YAAY;KACZ,YAAY;IACd,CAAC;IACD;GACF,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;IACpD,YAAY;GACd;EACF;EACA,MAAM;CACR;;CAGA,MAAM,iBAAiB,QAA0B,UAAoC;EAEnF,OAAO,MADe,KAAKA,IAAI,WAAW,8BAA8B,YAAY,QAAQ,QAAQ,CAAC,IACpF;CACnB;;CAGA,MAAM,gBAAgB,OAAe,QAA6C;EAEhF,MAAM,CAAC,UAAU,WAAW,MAAM,QAAQ,IAAI,CAC5C,KAAKA,IAAI,SAA0B,8BAA8B;GAAE,QAAQ;GAAO,SAAS;EAAO,CAAC,GACnG,KAAKA,IAAI,SAA0B,8BAA8B;GAAE,QAAQ;GAAO,SAAS;EAAK,CAAC,CACnG,CAAC;EACD,OAAO,CACL,GAAG,SAAS,KAAI,SAAQ;GACtB,UAAU,IAAI;GACd,OAAO;GACP,YAAY,IAAI;GAChB,WAAW,IAAI;EACjB,EAAE,GACF,GAAG,QAAQ,KAAI,SAAQ;GACrB,UAAU,IAAI;GACd,OAAO;GACP,YAAY,IAAI;GAChB,WAAW,IAAI;EACjB,EAAE,CACJ;CACF;;;;;;CAOA,MAAM,kBACJ,OACA,QACA,UACA,aAA8B,QACW;EACzC,MAAM,WAAW,YAAqD;GACpE,MAAM,MAAM,MAAM,KAAKA,IAAI,QAAyB,8BAA8B;IAChF,QAAQ;IACR;IACA,SAAS;GACX,CAAC;GACD,OAAO,MAAM;IAAE,UAAU,IAAI;IAAU,OAAO;IAAQ,YAAY,IAAI;GAAK,IAAI,KAAA;EACjF;EACA,MAAM,UAAU,YAAqD;GACnE,MAAM,MAAM,MAAM,KAAKA,IAAI,QAAyB,8BAA8B;IAChF,QAAQ;IACR;IACA,SAAS;GACX,CAAC;GACD,OAAO,MAAM;IAAE,UAAU,IAAI;IAAU,OAAO;IAAO,YAAY,IAAI;GAAK,IAAI,KAAA;EAChF;EACA,OAAO,eAAe,QAAU,MAAM,QAAQ,KAAO,MAAM,SAAS,IAAQ,MAAM,SAAS,KAAO,MAAM,QAAQ;CAClH;;;;;;;;;CAUA,MAAM,aACJ,QACA,UACA,WACsC;EACtC,IAAI;EACJ,MAAM,KAAKA,IAAI,aACb,8BACA,YAAY,QAAQ,QAAQ,GAC5B,OAAM,QAAO;GACX,IAAI,IAAI,KAAK,SAAS,SAAS,OAAO;GACtC,MAAM,UAAU,IAAI;GAEpB,IAAI,CAAC,yBAAyB,OAAO,GAAG;IACtC,SAAS;IACT,OAAO;GACT;GACA,MAAM,OAAO,MAAM,UAAU,OAAO;GACpC,SAAS;GACT,OAAO;IAAE,MAAM;IAAM,4BAAY,IAAI,KAAK;GAAE;EAC9C,CACF;EACA,OAAO;CACT;;CAGA,MAAM,mBAAmB,OAA0D;EAajF,OAAO,aAAa,MAZG,KAAKA,IAAI,UAA6B,wBAAwB;GACnF,YAAY,MAAM;GAClB,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,UAAU,MAAM;GAChB,MAAM,MAAM;GACZ,kBAAkB,MAAM,mBAAmB;GAC3C,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,cAAc,MAAM,cAAc;GAClC,4BAAY,IAAI,KAAK;EACvB,CAAC,CAC2B;CAC9B;;;;;CAMA,MAAM,gBAAgB,WAAyD;EAC7E,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA2B,wBAAwB,EAAE,YAAY,UAAU,CAAC;EACvG,IAAI,CAAC,KAAK,OAAO,KAAA;EACjB,IAAI,IAAI,WAAW,QAAQ,KAAK,KAAK,IAAI,GAAG;GAC1C,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,CAAC;GAC3E;EACF;EACA,OAAO,aAAa,GAAG;CACzB;;;;;;CAOA,MAAM,kBACJ,WACA,OACsC;EACtC,MAAM,MAAM,KAAK,IAAI;EACrB,IAAI,UAAU;EACd,MAAM,UAAU,MAAM,KAAKA,IAAI,aAC7B,wBACA,EAAE,YAAY,UAAU,IACxB,QAAO;GACL,IACE,IAAI,WAAW,MAAM,SACrB,IAAI,YAAY,MAAM,UACtB,IAAI,aAAa,MAAM,YACvB,IAAI,SAAS,MAAM,MAEnB,OAAO;GAET,IAAI,IAAI,WAAW,QAAQ,KAAK,KAAK,OAAO;GAC5C,IAAI,IAAI,gBAAgB,IAAI,aAAa,QAAQ,IAAI,KAAK,OAAO;GACjE,UAAU;GAGV,OAAO,EAAE,cAAc,IAAI,WAAW;EACxC,CACF;EACA,OAAO,WAAW,UAAU,aAAa,OAAO,IAAI,KAAA;CACtD;;CAGA,MAAM,kBACJ,WACA,SACe;EACf,MAAM,MAA+B,CAAC;EACtC,IAAI,QAAQ,YAAY,KAAA,GAAW,IAAI,UAAU,QAAQ;EACzD,IAAI,QAAQ,eAAe,KAAA,GAAW,IAAI,eAAe,QAAQ;EACjE,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,GAAG;EACnC,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,GAAG,GAAG;CAClF;;CAGA,MAAM,mBAAmB,WAAkC;EACzD,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,CAAC;CAC7E;AACF"}
1
+ {"version":3,"file":"base.js","names":["#migrateCredential","#db","#decryptCredential"],"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 * Resolution order at model-call time is user > org (the caller layers server\n * env vars underneath as a final fallback).\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 /**\n * Resolve the credential a caller should use for a provider. By default the\n * user's own row wins over the org's shared row; `precedence: 'org'`\n * (automated factory runs) checks the org's shared row first.\n */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n precedence: CredentialScope = 'user',\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 precedence === 'org' ? ((await readOrg()) ?? (await readUser())) : ((await readUser()) ?? (await readOrg()));\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,5 +1,6 @@
1
1
  import { FactoryStorageDomain } from '@mastra/core/storage';
2
2
  import type { CollectionSchema } from '@mastra/core/storage';
3
+ import type { FactorySecretEncryption } from '../../../secret-encryption.js';
3
4
  /**
4
5
  * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the
5
6
  * local `settings.json` `customProviders` entries: org-scoped rows keyed by the
@@ -29,7 +30,8 @@ export interface UpsertCustomProviderInput {
29
30
  export declare const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema;
30
31
  export declare class CustomProvidersStorage extends FactoryStorageDomain {
31
32
  #private;
32
- constructor();
33
+ private readonly encryption;
34
+ constructor(encryption?: FactorySecretEncryption);
33
35
  init(): Promise<void>;
34
36
  dangerouslyClearAll(): Promise<void>;
35
37
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/custom-providers/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAerC,CAAC;AA8BF,qBAAa,sBAAuB,SAAQ,oBAAoB;;;IAKxD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C;;;;;;;OAOG;IACG,MAAM,CAAC,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,yBAAyB,CAAC;QACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkG3B,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IASnE,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAG7F"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/custom-providers/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAerC,CAAC;AAeF,qBAAa,sBAAuB,SAAQ,oBAAoB;;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,GAAE,uBAAkE;IAIrG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC1C;;;;;;;OAOG;IACG,MAAM,CAAC,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,yBAAyB,CAAC;QACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAqG3B,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IASnE,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAG7F"}
@@ -1,3 +1,4 @@
1
+ import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
1
2
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
2
3
  //#region src/storage/domains/custom-providers/base.ts
3
4
  const CUSTOM_PROVIDERS_SCHEMA = {
@@ -22,26 +23,16 @@ const CUSTOM_PROVIDERS_SCHEMA = {
22
23
  columns: ["org_id", "provider_id"]
23
24
  }]
24
25
  };
25
- function toRecord(row) {
26
- return {
27
- id: row.id,
28
- orgId: row.org_id,
29
- createdBy: row.created_by,
30
- providerId: row.provider_id,
31
- name: row.name,
32
- url: row.url,
33
- apiKey: row.api_key,
34
- models: Array.isArray(row.models) ? row.models : [],
35
- createdAt: row.created_at,
36
- updatedAt: row.updated_at
37
- };
38
- }
39
26
  var CustomProvidersStorage = class extends FactoryStorageDomain {
40
- constructor() {
27
+ encryption;
28
+ constructor(encryption = createPlaintextFactorySecretEncryption()) {
41
29
  super("custom-providers");
30
+ this.encryption = encryption;
42
31
  }
43
32
  async init() {
44
33
  await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);
34
+ const rows = await this.ops.findMany("custom_providers", {});
35
+ await Promise.all(rows.filter((row) => row.api_key !== null).map((row) => this.#migrateApiKey(row.id)));
45
36
  }
46
37
  async dangerouslyClearAll() {
47
38
  await this.ops.deleteMany("custom_providers", {});
@@ -49,6 +40,31 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
49
40
  get #db() {
50
41
  return this.ops;
51
42
  }
43
+ async #toRecord(row) {
44
+ const apiKey = row.api_key === null ? null : (await this.encryption.decrypt(row.api_key)).value;
45
+ return {
46
+ id: row.id,
47
+ orgId: row.org_id,
48
+ createdBy: row.created_by,
49
+ providerId: row.provider_id,
50
+ name: row.name,
51
+ url: row.url,
52
+ apiKey,
53
+ models: Array.isArray(row.models) ? row.models : [],
54
+ createdAt: row.created_at,
55
+ updatedAt: row.updated_at
56
+ };
57
+ }
58
+ async #encryptApiKey(apiKey) {
59
+ return apiKey === void 0 ? null : this.encryption.encrypt(apiKey);
60
+ }
61
+ async #migrateApiKey(id) {
62
+ await this.#db.updateAtomic("custom_providers", { id }, async (row) => {
63
+ if (row.api_key === null) return null;
64
+ const decrypted = await this.encryption.decrypt(row.api_key);
65
+ return decrypted.needsReencryption ? { api_key: await this.encryption.encrypt(decrypted.value) } : null;
66
+ });
67
+ }
52
68
  /**
53
69
  * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
54
70
  * the settings.json upsert semantics (no key retention: an absent `apiKey`
@@ -59,6 +75,7 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
59
75
  */
60
76
  async upsert({ orgId, userId, input, previousProviderId }) {
61
77
  const now = /* @__PURE__ */ new Date();
78
+ const encryptedApiKey = await this.#encryptApiKey(input.apiKey);
62
79
  const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
63
80
  if (renameFrom) {
64
81
  if (!await this.#db.findOne("custom_providers", {
@@ -72,17 +89,18 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
72
89
  provider_id: input.providerId,
73
90
  name: input.name,
74
91
  url: input.url,
75
- api_key: input.apiKey ?? null,
92
+ api_key: encryptedApiKey,
76
93
  models: input.models,
77
94
  updated_at: now
78
95
  }));
79
- if (renamed) return toRecord(renamed);
96
+ if (renamed) return this.#toRecord(renamed);
80
97
  }
81
98
  }
82
99
  const record = await this.#write({
83
100
  orgId,
84
101
  userId,
85
102
  input,
103
+ encryptedApiKey,
86
104
  now
87
105
  });
88
106
  if (renameFrom) await this.#db.deleteMany("custom_providers", {
@@ -97,40 +115,42 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
97
115
  * the single row) instead of one failing on the unique index, and a row
98
116
  * deleted mid-flight is recreated rather than reported as a stale success.
99
117
  */
100
- async #write({ orgId, userId, input, now }) {
118
+ async #write({ orgId, userId, input, encryptedApiKey, now }) {
101
119
  const updateExisting = () => this.#db.updateAtomic("custom_providers", {
102
120
  org_id: orgId,
103
121
  provider_id: input.providerId
104
122
  }, () => ({
105
123
  name: input.name,
106
124
  url: input.url,
107
- api_key: input.apiKey ?? null,
125
+ api_key: encryptedApiKey,
108
126
  models: input.models,
109
127
  updated_at: now
110
128
  }));
111
129
  const updated = await updateExisting();
112
- if (updated) return toRecord(updated);
130
+ if (updated) return this.#toRecord(updated);
113
131
  try {
114
- return toRecord(await this.#db.insertOne("custom_providers", {
132
+ const row = await this.#db.insertOne("custom_providers", {
115
133
  org_id: orgId,
116
134
  created_by: userId,
117
135
  provider_id: input.providerId,
118
136
  name: input.name,
119
137
  url: input.url,
120
- api_key: input.apiKey ?? null,
138
+ api_key: encryptedApiKey,
121
139
  models: input.models,
122
140
  created_at: now,
123
141
  updated_at: now
124
- }));
142
+ });
143
+ return this.#toRecord(row);
125
144
  } catch (error) {
126
145
  if (!(error instanceof UniqueViolationError)) throw error;
127
146
  const row = await updateExisting();
128
147
  if (!row) throw error;
129
- return toRecord(row);
148
+ return this.#toRecord(row);
130
149
  }
131
150
  }
132
151
  async list({ orgId }) {
133
- return (await this.#db.findMany("custom_providers", { org_id: orgId }, { orderBy: [["name", "asc"]] })).map(toRecord);
152
+ const rows = await this.#db.findMany("custom_providers", { org_id: orgId }, { orderBy: [["name", "asc"]] });
153
+ return Promise.all(rows.map((row) => this.#toRecord(row)));
134
154
  }
135
155
  async delete({ orgId, providerId }) {
136
156
  return await this.#db.deleteMany("custom_providers", {
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#db","#write"],"sources":["../../../../src/storage/domains/custom-providers/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\n/**\n * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the\n * local `settings.json` `customProviders` entries: org-scoped rows keyed by the\n * derived provider id (name slug), API key stored alongside so a deployed\n * server never reads the shared settings file.\n */\nexport interface CustomProviderRecord {\n id: string;\n orgId: string;\n createdBy: string;\n /** Stable slug derived from the name (`getCustomProviderId`). */\n providerId: string;\n name: string;\n url: string;\n apiKey: string | null;\n models: string[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertCustomProviderInput {\n providerId: string;\n name: string;\n url: string;\n apiKey?: string;\n models: string[];\n}\n\nexport const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema = {\n name: 'custom_providers',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n provider_id: { type: 'text' },\n name: { type: 'text' },\n url: { type: 'text' },\n api_key: { type: 'text', nullable: true },\n models: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'custom_providers_org_provider_key', columns: ['org_id', 'provider_id'] }],\n};\n\ninterface CustomProviderDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n provider_id: string;\n name: string;\n url: string;\n api_key: string | null;\n models: string[];\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toRecord(row: CustomProviderDbRow): CustomProviderRecord {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n providerId: row.provider_id,\n name: row.name,\n url: row.url,\n apiKey: row.api_key,\n models: Array.isArray(row.models) ? row.models : [],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class CustomProvidersStorage extends FactoryStorageDomain {\n constructor() {\n super('custom-providers');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('custom_providers', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /**\n * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors\n * the settings.json upsert semantics (no key retention: an absent `apiKey`\n * clears the stored key). `previousProviderId` renames the provider: the\n * common path is a single atomic in-place update of the old row; renaming\n * onto an id that already exists overwrites the target first and removes the\n * old row last, so no interleaving or failure can lose the provider.\n */\n async upsert({\n orgId,\n userId,\n input,\n previousProviderId,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n previousProviderId?: string;\n }): Promise<CustomProviderRecord> {\n const now = new Date();\n const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : undefined;\n\n if (renameFrom) {\n const target = await this.#db.findOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n provider_id: input.providerId,\n });\n if (!target) {\n // In-place rename: one atomic write with no delete/insert gap, so a\n // failure can never drop the provider (and `created_at`/`created_by`\n // are preserved). If a concurrent create grabs the new id between the\n // lookup and this update, the unique index rejects the update and the\n // old row stays intact — the request fails cleanly and a retry\n // converges on the rename-onto-existing path below.\n const renamed = await this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: renameFrom },\n () => ({\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n updated_at: now,\n }),\n );\n if (renamed) return toRecord(renamed);\n // Old row already gone — fall through to a plain create of the new id.\n }\n // Rename onto an existing id: overwrite the target below, then delete\n // the old row last — a failure in the gap leaves a redundant duplicate\n // (recoverable by re-submitting or deleting) instead of losing data.\n }\n\n const record = await this.#write({ orgId, userId, input, now });\n if (renameFrom) {\n await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: renameFrom });\n }\n return record;\n }\n\n /**\n * Update-first, then insert-and-catch-unique-violation (see `queue_health`):\n * concurrent creates of the same provider both succeed (last write wins on\n * the single row) instead of one failing on the unique index, and a row\n * deleted mid-flight is recreated rather than reported as a stale success.\n */\n async #write({\n orgId,\n userId,\n input,\n now,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n now: Date;\n }): Promise<CustomProviderRecord> {\n const updateExisting = () =>\n this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: input.providerId },\n () => ({\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n updated_at: now,\n }),\n );\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n created_by: userId,\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n created_at: now,\n updated_at: now,\n });\n return toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — apply this write to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n\n async list({ orgId }: { orgId: string }): Promise<CustomProviderRecord[]> {\n const rows = await this.#db.findMany<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId },\n { orderBy: [['name', 'asc']] },\n );\n return rows.map(toRecord);\n }\n\n async delete({ orgId, providerId }: { orgId: string; providerId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: providerId })) > 0;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":["#migrateApiKey","#db","#toRecord","#encryptApiKey","#write"],"sources":["../../../../src/storage/domains/custom-providers/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\n/**\n * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the\n * local `settings.json` `customProviders` entries: org-scoped rows keyed by the\n * derived provider id (name slug), API key stored alongside so a deployed\n * server never reads the shared settings file.\n */\nexport interface CustomProviderRecord {\n id: string;\n orgId: string;\n createdBy: string;\n /** Stable slug derived from the name (`getCustomProviderId`). */\n providerId: string;\n name: string;\n url: string;\n apiKey: string | null;\n models: string[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertCustomProviderInput {\n providerId: string;\n name: string;\n url: string;\n apiKey?: string;\n models: string[];\n}\n\nexport const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema = {\n name: 'custom_providers',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n provider_id: { type: 'text' },\n name: { type: 'text' },\n url: { type: 'text' },\n api_key: { type: 'text', nullable: true },\n models: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'custom_providers_org_provider_key', columns: ['org_id', 'provider_id'] }],\n};\n\ninterface CustomProviderDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n provider_id: string;\n name: string;\n url: string;\n api_key: string | null;\n models: string[];\n created_at: Date;\n updated_at: Date;\n}\n\nexport class CustomProvidersStorage extends FactoryStorageDomain {\n constructor(private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption()) {\n super('custom-providers');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);\n const rows = await this.ops.findMany<CustomProviderDbRow>('custom_providers', {});\n await Promise.all(rows.filter(row => row.api_key !== null).map(row => this.#migrateApiKey(row.id)));\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('custom_providers', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #toRecord(row: CustomProviderDbRow): Promise<CustomProviderRecord> {\n const apiKey = row.api_key === null ? null : (await this.encryption.decrypt<string>(row.api_key)).value;\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n providerId: row.provider_id,\n name: row.name,\n url: row.url,\n apiKey,\n models: Array.isArray(row.models) ? row.models : [],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n async #encryptApiKey(apiKey: string | undefined): Promise<string | null> {\n return apiKey === undefined ? null : this.encryption.encrypt(apiKey);\n }\n\n async #migrateApiKey(id: string): Promise<void> {\n await this.#db.updateAtomic<CustomProviderDbRow>('custom_providers', { id }, async row => {\n if (row.api_key === null) return null;\n const decrypted = await this.encryption.decrypt<string>(row.api_key);\n return decrypted.needsReencryption ? { api_key: await this.encryption.encrypt(decrypted.value) } : null;\n });\n }\n\n /**\n * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors\n * the settings.json upsert semantics (no key retention: an absent `apiKey`\n * clears the stored key). `previousProviderId` renames the provider: the\n * common path is a single atomic in-place update of the old row; renaming\n * onto an id that already exists overwrites the target first and removes the\n * old row last, so no interleaving or failure can lose the provider.\n */\n async upsert({\n orgId,\n userId,\n input,\n previousProviderId,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n previousProviderId?: string;\n }): Promise<CustomProviderRecord> {\n const now = new Date();\n const encryptedApiKey = await this.#encryptApiKey(input.apiKey);\n const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : undefined;\n\n if (renameFrom) {\n const target = await this.#db.findOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n provider_id: input.providerId,\n });\n if (!target) {\n // In-place rename: one atomic write with no delete/insert gap, so a\n // failure can never drop the provider (and `created_at`/`created_by`\n // are preserved). If a concurrent create grabs the new id between the\n // lookup and this update, the unique index rejects the update and the\n // old row stays intact — the request fails cleanly and a retry\n // converges on the rename-onto-existing path below.\n const renamed = await this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: renameFrom },\n () => ({\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n updated_at: now,\n }),\n );\n if (renamed) return this.#toRecord(renamed);\n // Old row already gone — fall through to a plain create of the new id.\n }\n // Rename onto an existing id: overwrite the target below, then delete\n // the old row last — a failure in the gap leaves a redundant duplicate\n // (recoverable by re-submitting or deleting) instead of losing data.\n }\n\n const record = await this.#write({ orgId, userId, input, encryptedApiKey, now });\n if (renameFrom) {\n await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: renameFrom });\n }\n return record;\n }\n\n /**\n * Update-first, then insert-and-catch-unique-violation (see `queue_health`):\n * concurrent creates of the same provider both succeed (last write wins on\n * the single row) instead of one failing on the unique index, and a row\n * deleted mid-flight is recreated rather than reported as a stale success.\n */\n async #write({\n orgId,\n userId,\n input,\n encryptedApiKey,\n now,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n encryptedApiKey: string | null;\n now: Date;\n }): Promise<CustomProviderRecord> {\n const updateExisting = () =>\n this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: input.providerId },\n () => ({\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n updated_at: now,\n }),\n );\n\n const updated = await updateExisting();\n if (updated) return this.#toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n created_by: userId,\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n created_at: now,\n updated_at: now,\n });\n return this.#toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — apply this write to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return this.#toRecord(row);\n }\n }\n\n async list({ orgId }: { orgId: string }): Promise<CustomProviderRecord[]> {\n const rows = await this.#db.findMany<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId },\n { orderBy: [['name', 'asc']] },\n );\n return Promise.all(rows.map(row => this.#toRecord(row)));\n }\n\n async delete({ orgId, providerId }: { orgId: string; providerId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: providerId })) > 0;\n }\n}\n"],"mappings":"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"}
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import { FactoryStorageDomain } from '@mastra/core/storage';
22
22
  import type { CollectionSchema } from '@mastra/core/storage';
23
+ import type { FactorySecretEncryption } from '../../../secret-encryption.js';
23
24
  export declare const INTEGRATION_CONNECTIONS_SCHEMA: CollectionSchema;
24
25
  export declare const INTEGRATION_SUBSCRIPTIONS_SCHEMA: CollectionSchema;
25
26
  export declare const INTEGRATION_SETTINGS_SCHEMA: CollectionSchema;
@@ -117,7 +118,8 @@ export interface IntegrationStorageHandle<TConnection = Record<string, unknown>,
117
118
  */
118
119
  export declare class IntegrationStorage extends FactoryStorageDomain {
119
120
  #private;
120
- constructor();
121
+ private readonly encryption;
122
+ constructor(encryption?: FactorySecretEncryption);
121
123
  init(): Promise<void>;
122
124
  dangerouslyClearAll(): Promise<void>;
123
125
  /** A typed handle pre-scoped to `integrationId`. */
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/integrations/base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/integrations/base.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
1
2
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
2
3
  //#region src/storage/domains/integrations/base.ts
3
4
  /**
@@ -103,8 +104,10 @@ const INTEGRATION_SETTINGS_SCHEMA = {
103
104
  * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.
104
105
  */
105
106
  var IntegrationStorage = class extends FactoryStorageDomain {
106
- constructor() {
107
+ encryption;
108
+ constructor(encryption = createPlaintextFactorySecretEncryption()) {
107
109
  super("integrations");
110
+ this.encryption = encryption;
108
111
  }
109
112
  async init() {
110
113
  await this.ensureCollections([
@@ -112,6 +115,8 @@ var IntegrationStorage = class extends FactoryStorageDomain {
112
115
  INTEGRATION_SUBSCRIPTIONS_SCHEMA,
113
116
  INTEGRATION_SETTINGS_SCHEMA
114
117
  ]);
118
+ const [connections, settings] = await Promise.all([this.ops.findMany("integration_connections", {}), this.ops.findMany("integration_settings", {})]);
119
+ await Promise.all([...connections.map((row) => this.#migrateValue("integration_connections", row.id, "data")), ...settings.map((row) => this.#migrateValue("integration_settings", row.id, "config"))]);
115
120
  }
116
121
  async dangerouslyClearAll() {
117
122
  await this.ops.deleteMany("integration_subscriptions", {});
@@ -121,16 +126,22 @@ var IntegrationStorage = class extends FactoryStorageDomain {
121
126
  get #db() {
122
127
  return this.ops;
123
128
  }
129
+ async #migrateValue(collection, id, column) {
130
+ await this.#db.updateAtomic(collection, { id }, async (row) => {
131
+ const decrypted = await this.encryption.decrypt(row[column]);
132
+ return decrypted.needsReencryption ? { [column]: await this.encryption.encrypt(decrypted.value) } : null;
133
+ });
134
+ }
124
135
  /** A typed handle pre-scoped to `integrationId`. */
125
136
  forIntegration(integrationId) {
126
137
  if (!integrationId) throw new Error("[IntegrationStorage] integrationId is required.");
127
138
  const db = () => this.#db;
128
139
  const scoped = { integration_id: integrationId };
129
- const mapConnection = (row) => ({
140
+ const mapConnection = async (row) => ({
130
141
  id: row.id,
131
142
  orgId: row.org_id,
132
143
  userId: row.user_id,
133
- data: row.data,
144
+ data: (await this.encryption.decrypt(row.data)).value,
134
145
  metadata: row.metadata ?? {},
135
146
  createdAt: row.created_at,
136
147
  updatedAt: row.updated_at
@@ -163,9 +174,10 @@ var IntegrationStorage = class extends FactoryStorageDomain {
163
174
  ...scoped,
164
175
  org_id: orgId
165
176
  };
177
+ const encrypted = await this.encryption.encrypt(input.data);
166
178
  const update = async () => db().updateMany("integration_connections", where, {
167
179
  user_id: input.userId ?? null,
168
- data: input.data,
180
+ data: encrypted,
169
181
  metadata: input.metadata ?? {},
170
182
  updated_at: /* @__PURE__ */ new Date()
171
183
  });
@@ -177,7 +189,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
177
189
  await db().insertOne("integration_connections", {
178
190
  ...where,
179
191
  user_id: input.userId ?? null,
180
- data: input.data,
192
+ data: encrypted,
181
193
  metadata: input.metadata ?? {},
182
194
  created_at: now,
183
195
  updated_at: now
@@ -194,10 +206,13 @@ var IntegrationStorage = class extends FactoryStorageDomain {
194
206
  const row = await db().updateAtomic("integration_connections", {
195
207
  ...scoped,
196
208
  org_id: orgId
197
- }, (current) => ({
198
- data: fn(current.data),
199
- updated_at: /* @__PURE__ */ new Date()
200
- }));
209
+ }, async (current) => {
210
+ const data = (await this.encryption.decrypt(current.data)).value;
211
+ return {
212
+ data: await this.encryption.encrypt(fn(data)),
213
+ updated_at: /* @__PURE__ */ new Date()
214
+ };
215
+ });
201
216
  return row ? mapConnection(row) : null;
202
217
  },
203
218
  delete: async (orgId) => {
@@ -276,7 +291,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
276
291
  org_id: orgId,
277
292
  user_id: userId
278
293
  });
279
- return row ? structuredClone(row.config) : null;
294
+ return row ? (await this.encryption.decrypt(row.config)).value : null;
280
295
  },
281
296
  save: async (orgId, userId, config) => {
282
297
  const where = {
@@ -284,8 +299,9 @@ var IntegrationStorage = class extends FactoryStorageDomain {
284
299
  org_id: orgId,
285
300
  user_id: userId
286
301
  };
302
+ const encrypted = await this.encryption.encrypt(config);
287
303
  const update = async () => db().updateMany("integration_settings", where, {
288
- config,
304
+ config: encrypted,
289
305
  updated_at: /* @__PURE__ */ new Date()
290
306
  });
291
307
  let lastError;
@@ -295,7 +311,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
295
311
  try {
296
312
  await db().insertOne("integration_settings", {
297
313
  ...where,
298
- config,
314
+ config: encrypted,
299
315
  created_at: now,
300
316
  updated_at: now
301
317
  });