@mastra/factory 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/CHANGELOG.md +695 -0
  2. package/LICENSE.md +30 -0
  3. package/README.md +27 -0
  4. package/dist/auth.d.ts +181 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +505 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/capabilities/connection.d.ts +9 -0
  9. package/dist/capabilities/connection.d.ts.map +1 -0
  10. package/dist/capabilities/connection.js +1 -0
  11. package/dist/capabilities/intake.d.ts +140 -0
  12. package/dist/capabilities/intake.d.ts.map +1 -0
  13. package/dist/capabilities/intake.js +1 -0
  14. package/dist/capabilities/version-control.d.ts +245 -0
  15. package/dist/capabilities/version-control.d.ts.map +1 -0
  16. package/dist/capabilities/version-control.js +1 -0
  17. package/dist/factory.d.ts +156 -0
  18. package/dist/factory.d.ts.map +1 -0
  19. package/dist/factory.js +483 -0
  20. package/dist/factory.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +7 -0
  24. package/dist/integrations/base.d.ts +210 -0
  25. package/dist/integrations/base.d.ts.map +1 -0
  26. package/dist/integrations/base.js +1 -0
  27. package/dist/integrations/github/config.d.ts +60 -0
  28. package/dist/integrations/github/config.d.ts.map +1 -0
  29. package/dist/integrations/github/config.js +42 -0
  30. package/dist/integrations/github/config.js.map +1 -0
  31. package/dist/integrations/github/factory-session.d.ts +12 -0
  32. package/dist/integrations/github/factory-session.d.ts.map +1 -0
  33. package/dist/integrations/github/factory-session.js +37 -0
  34. package/dist/integrations/github/factory-session.js.map +1 -0
  35. package/dist/integrations/github/integration.d.ts +191 -0
  36. package/dist/integrations/github/integration.d.ts.map +1 -0
  37. package/dist/integrations/github/integration.js +1077 -0
  38. package/dist/integrations/github/integration.js.map +1 -0
  39. package/dist/integrations/github/issue-triage.d.ts +27 -0
  40. package/dist/integrations/github/issue-triage.d.ts.map +1 -0
  41. package/dist/integrations/github/issue-triage.js +82 -0
  42. package/dist/integrations/github/issue-triage.js.map +1 -0
  43. package/dist/integrations/github/pat.d.ts +38 -0
  44. package/dist/integrations/github/pat.d.ts.map +1 -0
  45. package/dist/integrations/github/pat.js +59 -0
  46. package/dist/integrations/github/pat.js.map +1 -0
  47. package/dist/integrations/github/provenance.d.ts +27 -0
  48. package/dist/integrations/github/provenance.d.ts.map +1 -0
  49. package/dist/integrations/github/provenance.js +75 -0
  50. package/dist/integrations/github/provenance.js.map +1 -0
  51. package/dist/integrations/github/routes.d.ts +69 -0
  52. package/dist/integrations/github/routes.d.ts.map +1 -0
  53. package/dist/integrations/github/routes.js +1324 -0
  54. package/dist/integrations/github/routes.js.map +1 -0
  55. package/dist/integrations/github/rules.d.ts +78 -0
  56. package/dist/integrations/github/rules.d.ts.map +1 -0
  57. package/dist/integrations/github/rules.js +362 -0
  58. package/dist/integrations/github/rules.js.map +1 -0
  59. package/dist/integrations/github/sandbox-release.d.ts +38 -0
  60. package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
  61. package/dist/integrations/github/sandbox-release.js +74 -0
  62. package/dist/integrations/github/sandbox-release.js.map +1 -0
  63. package/dist/integrations/github/sandbox.d.ts +281 -0
  64. package/dist/integrations/github/sandbox.d.ts.map +1 -0
  65. package/dist/integrations/github/sandbox.js +547 -0
  66. package/dist/integrations/github/sandbox.js.map +1 -0
  67. package/dist/integrations/github/session-subscriptions.d.ts +26 -0
  68. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -0
  69. package/dist/integrations/github/session-subscriptions.js +192 -0
  70. package/dist/integrations/github/session-subscriptions.js.map +1 -0
  71. package/dist/integrations/github/subscriptions.d.ts +54 -0
  72. package/dist/integrations/github/subscriptions.d.ts.map +1 -0
  73. package/dist/integrations/github/subscriptions.js +62 -0
  74. package/dist/integrations/github/subscriptions.js.map +1 -0
  75. package/dist/integrations/github/token-refresh.d.ts +12 -0
  76. package/dist/integrations/github/token-refresh.d.ts.map +1 -0
  77. package/dist/integrations/github/token-refresh.js +24 -0
  78. package/dist/integrations/github/token-refresh.js.map +1 -0
  79. package/dist/integrations/github/webhook.d.ts +87 -0
  80. package/dist/integrations/github/webhook.d.ts.map +1 -0
  81. package/dist/integrations/github/webhook.js +397 -0
  82. package/dist/integrations/github/webhook.js.map +1 -0
  83. package/dist/integrations/linear/agent-tools.d.ts +34 -0
  84. package/dist/integrations/linear/agent-tools.d.ts.map +1 -0
  85. package/dist/integrations/linear/agent-tools.js +85 -0
  86. package/dist/integrations/linear/agent-tools.js.map +1 -0
  87. package/dist/integrations/linear/integration.d.ts +233 -0
  88. package/dist/integrations/linear/integration.d.ts.map +1 -0
  89. package/dist/integrations/linear/integration.js +672 -0
  90. package/dist/integrations/linear/integration.js.map +1 -0
  91. package/dist/integrations/linear/routes.d.ts +61 -0
  92. package/dist/integrations/linear/routes.d.ts.map +1 -0
  93. package/dist/integrations/linear/routes.js +239 -0
  94. package/dist/integrations/linear/routes.js.map +1 -0
  95. package/dist/integrations/linear/rules.d.ts +42 -0
  96. package/dist/integrations/linear/rules.d.ts.map +1 -0
  97. package/dist/integrations/linear/rules.js +133 -0
  98. package/dist/integrations/linear/rules.js.map +1 -0
  99. package/dist/integrations/linear/storage.d.ts +65 -0
  100. package/dist/integrations/linear/storage.d.ts.map +1 -0
  101. package/dist/integrations/linear/storage.js +1 -0
  102. package/dist/integrations/platform/api-client.d.ts +26 -0
  103. package/dist/integrations/platform/api-client.d.ts.map +1 -0
  104. package/dist/integrations/platform/api-client.js +156 -0
  105. package/dist/integrations/platform/api-client.js.map +1 -0
  106. package/dist/integrations/platform/github/event-worker.d.ts +49 -0
  107. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
  108. package/dist/integrations/platform/github/event-worker.js +348 -0
  109. package/dist/integrations/platform/github/event-worker.js.map +1 -0
  110. package/dist/integrations/platform/github/integration.d.ts +50 -0
  111. package/dist/integrations/platform/github/integration.d.ts.map +1 -0
  112. package/dist/integrations/platform/github/integration.js +961 -0
  113. package/dist/integrations/platform/github/integration.js.map +1 -0
  114. package/dist/integrations/platform/linear/integration.d.ts +46 -0
  115. package/dist/integrations/platform/linear/integration.d.ts.map +1 -0
  116. package/dist/integrations/platform/linear/integration.js +483 -0
  117. package/dist/integrations/platform/linear/integration.js.map +1 -0
  118. package/dist/integrations/workos/integration.d.ts +39 -0
  119. package/dist/integrations/workos/integration.d.ts.map +1 -0
  120. package/dist/integrations/workos/integration.js +91 -0
  121. package/dist/integrations/workos/integration.js.map +1 -0
  122. package/dist/routes/config.d.ts +235 -0
  123. package/dist/routes/config.d.ts.map +1 -0
  124. package/dist/routes/config.js +972 -0
  125. package/dist/routes/config.js.map +1 -0
  126. package/dist/routes/custom-provider-source.d.ts +52 -0
  127. package/dist/routes/custom-provider-source.d.ts.map +1 -0
  128. package/dist/routes/custom-provider-source.js +124 -0
  129. package/dist/routes/custom-provider-source.js.map +1 -0
  130. package/dist/routes/fs.d.ts +151 -0
  131. package/dist/routes/fs.d.ts.map +1 -0
  132. package/dist/routes/fs.js +708 -0
  133. package/dist/routes/fs.js.map +1 -0
  134. package/dist/routes/intake.d.ts +23 -0
  135. package/dist/routes/intake.d.ts.map +1 -0
  136. package/dist/routes/intake.js +179 -0
  137. package/dist/routes/intake.js.map +1 -0
  138. package/dist/routes/oauth.d.ts +43 -0
  139. package/dist/routes/oauth.d.ts.map +1 -0
  140. package/dist/routes/oauth.js +463 -0
  141. package/dist/routes/oauth.js.map +1 -0
  142. package/dist/routes/projects.d.ts +19 -0
  143. package/dist/routes/projects.d.ts.map +1 -0
  144. package/dist/routes/projects.js +461 -0
  145. package/dist/routes/projects.js.map +1 -0
  146. package/dist/routes/provider-credentials.d.ts +81 -0
  147. package/dist/routes/provider-credentials.d.ts.map +1 -0
  148. package/dist/routes/provider-credentials.js +86 -0
  149. package/dist/routes/provider-credentials.js.map +1 -0
  150. package/dist/routes/route.d.ts +48 -0
  151. package/dist/routes/route.d.ts.map +1 -0
  152. package/dist/routes/route.js +15 -0
  153. package/dist/routes/route.js.map +1 -0
  154. package/dist/routes/skills.d.ts +30 -0
  155. package/dist/routes/skills.d.ts.map +1 -0
  156. package/dist/routes/skills.js +168 -0
  157. package/dist/routes/skills.js.map +1 -0
  158. package/dist/routes/surface.d.ts +91 -0
  159. package/dist/routes/surface.d.ts.map +1 -0
  160. package/dist/routes/surface.js +260 -0
  161. package/dist/routes/surface.js.map +1 -0
  162. package/dist/routes/tenant-credentials.d.ts +74 -0
  163. package/dist/routes/tenant-credentials.d.ts.map +1 -0
  164. package/dist/routes/tenant-credentials.js +156 -0
  165. package/dist/routes/tenant-credentials.js.map +1 -0
  166. package/dist/routes/work-items.d.ts +40 -0
  167. package/dist/routes/work-items.d.ts.map +1 -0
  168. package/dist/routes/work-items.js +695 -0
  169. package/dist/routes/work-items.js.map +1 -0
  170. package/dist/rules/binding-context.d.ts +5 -0
  171. package/dist/rules/binding-context.d.ts.map +1 -0
  172. package/dist/rules/binding-context.js +28 -0
  173. package/dist/rules/binding-context.js.map +1 -0
  174. package/dist/rules/defaults.d.ts +9 -0
  175. package/dist/rules/defaults.d.ts.map +1 -0
  176. package/dist/rules/defaults.js +260 -0
  177. package/dist/rules/defaults.js.map +1 -0
  178. package/dist/rules/dispatcher.d.ts +42 -0
  179. package/dist/rules/dispatcher.d.ts.map +1 -0
  180. package/dist/rules/dispatcher.js +518 -0
  181. package/dist/rules/dispatcher.js.map +1 -0
  182. package/dist/rules/index.d.ts +7 -0
  183. package/dist/rules/index.d.ts.map +1 -0
  184. package/dist/rules/index.js +5 -0
  185. package/dist/rules/processor.d.ts +54 -0
  186. package/dist/rules/processor.d.ts.map +1 -0
  187. package/dist/rules/processor.js +378 -0
  188. package/dist/rules/processor.js.map +1 -0
  189. package/dist/rules/resolve.d.ts +17 -0
  190. package/dist/rules/resolve.d.ts.map +1 -0
  191. package/dist/rules/resolve.js +30 -0
  192. package/dist/rules/resolve.js.map +1 -0
  193. package/dist/rules/start-coordinator.d.ts +60 -0
  194. package/dist/rules/start-coordinator.d.ts.map +1 -0
  195. package/dist/rules/start-coordinator.js +194 -0
  196. package/dist/rules/start-coordinator.js.map +1 -0
  197. package/dist/rules/tools.d.ts +10 -0
  198. package/dist/rules/tools.d.ts.map +1 -0
  199. package/dist/rules/tools.js +58 -0
  200. package/dist/rules/tools.js.map +1 -0
  201. package/dist/rules/transition-service.d.ts +49 -0
  202. package/dist/rules/transition-service.d.ts.map +1 -0
  203. package/dist/rules/transition-service.js +232 -0
  204. package/dist/rules/transition-service.js.map +1 -0
  205. package/dist/rules/types.d.ts +234 -0
  206. package/dist/rules/types.d.ts.map +1 -0
  207. package/dist/rules/types.js +38 -0
  208. package/dist/rules/types.js.map +1 -0
  209. package/dist/rules/validation.d.ts +11 -0
  210. package/dist/rules/validation.d.ts.map +1 -0
  211. package/dist/rules/validation.js +308 -0
  212. package/dist/rules/validation.js.map +1 -0
  213. package/dist/sandbox/fleet.d.ts +222 -0
  214. package/dist/sandbox/fleet.d.ts.map +1 -0
  215. package/dist/sandbox/fleet.js +322 -0
  216. package/dist/sandbox/fleet.js.map +1 -0
  217. package/dist/sandbox/reattach.d.ts +3 -0
  218. package/dist/sandbox/reattach.d.ts.map +1 -0
  219. package/dist/sandbox/reattach.js +16 -0
  220. package/dist/sandbox/reattach.js.map +1 -0
  221. package/dist/server-error.d.ts +12 -0
  222. package/dist/server-error.d.ts.map +1 -0
  223. package/dist/server-error.js +16 -0
  224. package/dist/server-error.js.map +1 -0
  225. package/dist/skills/service.d.ts +48 -0
  226. package/dist/skills/service.d.ts.map +1 -0
  227. package/dist/skills/service.js +40 -0
  228. package/dist/skills/service.js.map +1 -0
  229. package/dist/spa-static.d.ts +33 -0
  230. package/dist/spa-static.d.ts.map +1 -0
  231. package/dist/spa-static.js +104 -0
  232. package/dist/spa-static.js.map +1 -0
  233. package/dist/state-signing.d.ts +59 -0
  234. package/dist/state-signing.d.ts.map +1 -0
  235. package/dist/state-signing.js +83 -0
  236. package/dist/state-signing.js.map +1 -0
  237. package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
  238. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
  239. package/dist/storage/domains/audit/agent-audit.js +66 -0
  240. package/dist/storage/domains/audit/agent-audit.js.map +1 -0
  241. package/dist/storage/domains/audit/base.d.ts +140 -0
  242. package/dist/storage/domains/audit/base.d.ts.map +1 -0
  243. package/dist/storage/domains/audit/base.js +184 -0
  244. package/dist/storage/domains/audit/base.js.map +1 -0
  245. package/dist/storage/domains/audit/domain.d.ts +69 -0
  246. package/dist/storage/domains/audit/domain.d.ts.map +1 -0
  247. package/dist/storage/domains/audit/domain.js +167 -0
  248. package/dist/storage/domains/audit/domain.js.map +1 -0
  249. package/dist/storage/domains/channel-identity/base.d.ts +96 -0
  250. package/dist/storage/domains/channel-identity/base.d.ts.map +1 -0
  251. package/dist/storage/domains/channel-identity/base.js +149 -0
  252. package/dist/storage/domains/channel-identity/base.js.map +1 -0
  253. package/dist/storage/domains/credentials/base.d.ts +134 -0
  254. package/dist/storage/domains/credentials/base.d.ts.map +1 -0
  255. package/dist/storage/domains/credentials/base.js +273 -0
  256. package/dist/storage/domains/credentials/base.js.map +1 -0
  257. package/dist/storage/domains/custom-providers/base.d.ts +57 -0
  258. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
  259. package/dist/storage/domains/custom-providers/base.js +145 -0
  260. package/dist/storage/domains/custom-providers/base.js.map +1 -0
  261. package/dist/storage/domains/intake/base.d.ts +38 -0
  262. package/dist/storage/domains/intake/base.d.ts.map +1 -0
  263. package/dist/storage/domains/intake/base.js +80 -0
  264. package/dist/storage/domains/intake/base.js.map +1 -0
  265. package/dist/storage/domains/integrations/base.d.ts +126 -0
  266. package/dist/storage/domains/integrations/base.d.ts.map +1 -0
  267. package/dist/storage/domains/integrations/base.js +317 -0
  268. package/dist/storage/domains/integrations/base.js.map +1 -0
  269. package/dist/storage/domains/memory-settings/base.d.ts +62 -0
  270. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
  271. package/dist/storage/domains/memory-settings/base.js +126 -0
  272. package/dist/storage/domains/memory-settings/base.js.map +1 -0
  273. package/dist/storage/domains/model-packs/base.d.ts +49 -0
  274. package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
  275. package/dist/storage/domains/model-packs/base.js +96 -0
  276. package/dist/storage/domains/model-packs/base.js.map +1 -0
  277. package/dist/storage/domains/projects/base.d.ts +58 -0
  278. package/dist/storage/domains/projects/base.d.ts.map +1 -0
  279. package/dist/storage/domains/projects/base.js +111 -0
  280. package/dist/storage/domains/projects/base.js.map +1 -0
  281. package/dist/storage/domains/queue-health/base.d.ts +52 -0
  282. package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
  283. package/dist/storage/domains/queue-health/base.js +122 -0
  284. package/dist/storage/domains/queue-health/base.js.map +1 -0
  285. package/dist/storage/domains/source-control/base.d.ts +358 -0
  286. package/dist/storage/domains/source-control/base.d.ts.map +1 -0
  287. package/dist/storage/domains/source-control/base.js +908 -0
  288. package/dist/storage/domains/source-control/base.js.map +1 -0
  289. package/dist/storage/domains/source-control/inmemory.d.ts +175 -0
  290. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
  291. package/dist/storage/domains/source-control/inmemory.js +377 -0
  292. package/dist/storage/domains/source-control/inmemory.js.map +1 -0
  293. package/dist/storage/domains/work-items/base.d.ts +412 -0
  294. package/dist/storage/domains/work-items/base.d.ts.map +1 -0
  295. package/dist/storage/domains/work-items/base.js +1309 -0
  296. package/dist/storage/domains/work-items/base.js.map +1 -0
  297. package/dist/storage/domains/work-items/metrics.d.ts +101 -0
  298. package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
  299. package/dist/storage/domains/work-items/metrics.js +221 -0
  300. package/dist/storage/domains/work-items/metrics.js.map +1 -0
  301. package/dist/timing.d.ts +15 -0
  302. package/dist/timing.d.ts.map +1 -0
  303. package/dist/timing.js +34 -0
  304. package/dist/timing.js.map +1 -0
  305. package/dist/workspace.d.ts +24 -0
  306. package/dist/workspace.d.ts.map +1 -0
  307. package/dist/workspace.js +286 -0
  308. package/dist/workspace.js.map +1 -0
  309. package/factory-skills/configure-factory-rules/SKILL.md +67 -0
  310. package/factory-skills/factory-plan/SKILL.md +61 -0
  311. package/factory-skills/factory-review/SKILL.md +155 -0
  312. package/factory-skills/factory-triage/SKILL.md +77 -0
  313. package/package.json +81 -0
@@ -0,0 +1,461 @@
1
+ import { Route } from "./route.js";
2
+ import { registerApiRoute } from "@mastra/core/server";
3
+ //#region src/routes/projects.ts
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
+ const MAX_NAME_LENGTH = 200;
6
+ const MAX_DESCRIPTION_LENGTH = 2e3;
7
+ const MAX_SETUP_COMMAND_LENGTH = 2e3;
8
+ const MAX_BRANCH_LENGTH = 255;
9
+ const MAX_SANDBOX_PROVIDER_LENGTH = 100;
10
+ const MAX_SANDBOX_WORKDIR_LENGTH = 1e3;
11
+ const CONTROL_CHAR_RE = /[\0-\x08\x0b\x0c\x0e-\x1f\x7f]/;
12
+ function loose(context) {
13
+ return context;
14
+ }
15
+ async function readJson(context) {
16
+ try {
17
+ return await context.req.json();
18
+ } catch {
19
+ return;
20
+ }
21
+ }
22
+ function parseCreateInput(value) {
23
+ if (!value || typeof value !== "object") return null;
24
+ const input = value;
25
+ if (typeof input.name !== "string") return null;
26
+ const name = input.name.trim();
27
+ if (!name || name.length > MAX_NAME_LENGTH) return null;
28
+ if (input.description !== void 0 && input.description !== null && typeof input.description !== "string") return null;
29
+ const description = typeof input.description === "string" ? input.description.trim() || null : null;
30
+ if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
31
+ return {
32
+ name,
33
+ description
34
+ };
35
+ }
36
+ function parseUpdateInput(value) {
37
+ if (!value || typeof value !== "object") return null;
38
+ const input = value;
39
+ const patch = {};
40
+ if (input.name !== void 0) {
41
+ if (typeof input.name !== "string") return null;
42
+ const name = input.name.trim();
43
+ if (!name || name.length > MAX_NAME_LENGTH) return null;
44
+ patch.name = name;
45
+ }
46
+ if (input.description !== void 0) {
47
+ if (input.description !== null && typeof input.description !== "string") return null;
48
+ const description = typeof input.description === "string" ? input.description.trim() || null : null;
49
+ if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
50
+ patch.description = description;
51
+ }
52
+ if (input.defaultModelId !== void 0) {
53
+ const defaultModelId = parseOptionalString(input.defaultModelId, {
54
+ maxLength: MAX_NAME_LENGTH,
55
+ nullable: true
56
+ });
57
+ if (defaultModelId === false) return null;
58
+ patch.defaultModelId = defaultModelId ?? null;
59
+ }
60
+ if (input.slackWorkItemsEnabled !== void 0) {
61
+ if (typeof input.slackWorkItemsEnabled !== "boolean") return null;
62
+ patch.slackWorkItemsEnabled = input.slackWorkItemsEnabled;
63
+ }
64
+ return Object.keys(patch).length > 0 ? patch : null;
65
+ }
66
+ function parseConnectionInput(value) {
67
+ if (!value || typeof value !== "object") return null;
68
+ const input = value;
69
+ if (typeof input.integrationId !== "string" || !input.integrationId.trim()) return null;
70
+ if (typeof input.installationId !== "string" || !UUID_RE.test(input.installationId)) return null;
71
+ return {
72
+ integrationId: input.integrationId.trim(),
73
+ installationId: input.installationId
74
+ };
75
+ }
76
+ function parseOptionalString(value, { maxLength, nullable = false }) {
77
+ if (value === void 0) return void 0;
78
+ if (value === null) return nullable ? null : false;
79
+ if (typeof value !== "string") return false;
80
+ const normalized = value.trim();
81
+ if (!normalized) return nullable ? null : false;
82
+ if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;
83
+ return normalized;
84
+ }
85
+ function parseRepositoryLinkInput(value) {
86
+ if (!value || typeof value !== "object") return null;
87
+ const input = value;
88
+ if (typeof input.repositoryId !== "string" || !UUID_RE.test(input.repositoryId)) return null;
89
+ const branch = parseOptionalString(input.branch, {
90
+ maxLength: MAX_BRANCH_LENGTH,
91
+ nullable: true
92
+ });
93
+ const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });
94
+ const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });
95
+ const setupCommand = parseOptionalString(input.setupCommand, {
96
+ maxLength: MAX_SETUP_COMMAND_LENGTH,
97
+ nullable: true
98
+ });
99
+ if (branch === false || typeof sandboxProvider !== "string" || typeof sandboxWorkdir !== "string" || setupCommand === false) return null;
100
+ return {
101
+ repositoryId: input.repositoryId,
102
+ branch: branch ?? null,
103
+ sandboxProvider,
104
+ sandboxWorkdir,
105
+ setupCommand: setupCommand ?? null
106
+ };
107
+ }
108
+ function parseRepositoryUpdateInput(value) {
109
+ if (!value || typeof value !== "object") return null;
110
+ const input = value;
111
+ const patch = {};
112
+ const branch = parseOptionalString(input.branch, {
113
+ maxLength: MAX_BRANCH_LENGTH,
114
+ nullable: true
115
+ });
116
+ const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });
117
+ const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });
118
+ const setupCommand = parseOptionalString(input.setupCommand, {
119
+ maxLength: MAX_SETUP_COMMAND_LENGTH,
120
+ nullable: true
121
+ });
122
+ if (branch === false || sandboxProvider === false || sandboxProvider === null || sandboxWorkdir === false || sandboxWorkdir === null || setupCommand === false) return null;
123
+ if (branch !== void 0) patch.branch = branch;
124
+ if (sandboxProvider !== void 0) patch.sandboxProvider = sandboxProvider;
125
+ if (sandboxWorkdir !== void 0) patch.sandboxWorkdir = sandboxWorkdir;
126
+ if (setupCommand !== void 0) patch.setupCommand = setupCommand;
127
+ return Object.keys(patch).length > 0 ? patch : null;
128
+ }
129
+ var ProjectRoutes = class extends Route {
130
+ #versionControlIntegrationIds;
131
+ constructor(deps) {
132
+ super(deps);
133
+ this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);
134
+ }
135
+ async #projects() {
136
+ await this.deps.projects.ensureReady();
137
+ return this.deps.projects;
138
+ }
139
+ async #sourceControl() {
140
+ await this.deps.sourceControl.ensureReady();
141
+ return this.deps.sourceControl;
142
+ }
143
+ async #handles() {
144
+ const storage = await this.#sourceControl();
145
+ return [...this.#versionControlIntegrationIds].map((integrationId) => storage.forIntegration(integrationId));
146
+ }
147
+ async #project(orgId, id) {
148
+ return (await this.#projects()).get({
149
+ orgId,
150
+ id
151
+ });
152
+ }
153
+ async #findConnection({ orgId, projectId, id }) {
154
+ for (const handle of await this.#handles()) {
155
+ const connection = await handle.connections.get({
156
+ orgId,
157
+ id
158
+ });
159
+ if (connection?.factoryProjectId === projectId) return {
160
+ handle,
161
+ connection
162
+ };
163
+ }
164
+ return null;
165
+ }
166
+ async #findProjectRepository({ orgId, projectId, id }) {
167
+ for (const handle of await this.#handles()) {
168
+ const projectRepository = await handle.projectRepositories.get({
169
+ orgId,
170
+ id
171
+ });
172
+ if (!projectRepository) continue;
173
+ const connection = await handle.connections.get({
174
+ orgId,
175
+ id: projectRepository.connectionId
176
+ });
177
+ if (connection?.factoryProjectId === projectId) return {
178
+ handle,
179
+ connection,
180
+ projectRepository
181
+ };
182
+ }
183
+ return null;
184
+ }
185
+ async #repositoryPayload(handle, orgId, projectRepository) {
186
+ const repository = await handle.repositories.get({
187
+ orgId,
188
+ id: projectRepository.repositoryId
189
+ });
190
+ return {
191
+ ...projectRepository,
192
+ repository
193
+ };
194
+ }
195
+ async #resolveTenant(context) {
196
+ await this.deps.auth.ensureUser(context);
197
+ const tenant = this.deps.auth.tenant(context);
198
+ if (!tenant) return { response: context.json({ error: "unauthorized" }, 401) };
199
+ if (!tenant.orgId) return { response: context.json({
200
+ error: "organization_required",
201
+ message: "Factory projects require an organization."
202
+ }, 403) };
203
+ return {
204
+ orgId: tenant.orgId,
205
+ userId: tenant.userId
206
+ };
207
+ }
208
+ routes() {
209
+ return [
210
+ registerApiRoute("/web/factory/projects", {
211
+ method: "GET",
212
+ requiresAuth: false,
213
+ handler: async (routeContext) => {
214
+ const context = loose(routeContext);
215
+ const tenant = await this.#resolveTenant(context);
216
+ if ("response" in tenant) return tenant.response;
217
+ return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });
218
+ }
219
+ }),
220
+ registerApiRoute("/web/factory/projects", {
221
+ method: "POST",
222
+ requiresAuth: false,
223
+ handler: async (routeContext) => {
224
+ const context = loose(routeContext);
225
+ const tenant = await this.#resolveTenant(context);
226
+ if ("response" in tenant) return tenant.response;
227
+ const input = parseCreateInput(await readJson(context));
228
+ if (!input) return context.json({ error: "invalid_project" }, 400);
229
+ const project = await (await this.#projects()).create({
230
+ orgId: tenant.orgId,
231
+ userId: tenant.userId,
232
+ input
233
+ });
234
+ return context.json({ project }, 201);
235
+ }
236
+ }),
237
+ registerApiRoute("/web/factory/projects/:id", {
238
+ method: "GET",
239
+ requiresAuth: false,
240
+ handler: async (routeContext) => {
241
+ const context = loose(routeContext);
242
+ const tenant = await this.#resolveTenant(context);
243
+ if ("response" in tenant) return tenant.response;
244
+ const id = context.req.param("id");
245
+ if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
246
+ const project = await this.#project(tenant.orgId, id);
247
+ return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
248
+ }
249
+ }),
250
+ registerApiRoute("/web/factory/projects/:id", {
251
+ method: "PATCH",
252
+ requiresAuth: false,
253
+ handler: async (routeContext) => {
254
+ const context = loose(routeContext);
255
+ const tenant = await this.#resolveTenant(context);
256
+ if ("response" in tenant) return tenant.response;
257
+ const id = context.req.param("id");
258
+ if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
259
+ const input = parseUpdateInput(await readJson(context));
260
+ if (!input) return context.json({ error: "invalid_project" }, 400);
261
+ const project = await (await this.#projects()).update({
262
+ orgId: tenant.orgId,
263
+ id,
264
+ input
265
+ });
266
+ return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
267
+ }
268
+ }),
269
+ registerApiRoute("/web/factory/projects/:id", {
270
+ method: "DELETE",
271
+ requiresAuth: false,
272
+ handler: async (routeContext) => {
273
+ const context = loose(routeContext);
274
+ const tenant = await this.#resolveTenant(context);
275
+ if ("response" in tenant) return tenant.response;
276
+ const id = context.req.param("id");
277
+ if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
278
+ if (!await this.#project(tenant.orgId, id)) return context.json({ error: "Project not found" }, 404);
279
+ for (const handle of await this.#handles()) for (const connection of await handle.connections.list({
280
+ orgId: tenant.orgId,
281
+ factoryProjectId: id
282
+ })) await handle.connections.delete({
283
+ orgId: tenant.orgId,
284
+ id: connection.id
285
+ });
286
+ await (await this.#projects()).delete({
287
+ orgId: tenant.orgId,
288
+ id
289
+ });
290
+ return context.body(null, 204);
291
+ }
292
+ }),
293
+ registerApiRoute("/web/factory/projects/:id/source-control-connections", {
294
+ method: "GET",
295
+ requiresAuth: false,
296
+ handler: async (routeContext) => {
297
+ const context = loose(routeContext);
298
+ const tenant = await this.#resolveTenant(context);
299
+ if ("response" in tenant) return tenant.response;
300
+ const projectId = context.req.param("id");
301
+ if (!projectId || !UUID_RE.test(projectId) || !await this.#project(tenant.orgId, projectId)) return context.json({ error: "Project not found" }, 404);
302
+ const connections = [];
303
+ for (const handle of await this.#handles()) for (const connection of await handle.connections.list({
304
+ orgId: tenant.orgId,
305
+ factoryProjectId: projectId
306
+ })) {
307
+ const installation = await handle.installations.get({
308
+ orgId: tenant.orgId,
309
+ id: connection.installationId
310
+ });
311
+ if (!installation) continue;
312
+ const links = await handle.projectRepositories.list({
313
+ orgId: tenant.orgId,
314
+ connectionId: connection.id
315
+ });
316
+ connections.push({
317
+ ...connection,
318
+ installation,
319
+ repositories: await Promise.all(links.map((link) => this.#repositoryPayload(handle, tenant.orgId, link)))
320
+ });
321
+ }
322
+ return context.json({ connections });
323
+ }
324
+ }),
325
+ registerApiRoute("/web/factory/projects/:id/source-control-connections", {
326
+ method: "POST",
327
+ requiresAuth: false,
328
+ handler: async (routeContext) => {
329
+ const context = loose(routeContext);
330
+ const tenant = await this.#resolveTenant(context);
331
+ if ("response" in tenant) return tenant.response;
332
+ const projectId = context.req.param("id");
333
+ if (!projectId || !UUID_RE.test(projectId) || !await this.#project(tenant.orgId, projectId)) return context.json({ error: "Project not found" }, 404);
334
+ const input = parseConnectionInput(await readJson(context));
335
+ if (!input) return context.json({ error: "invalid_source_control_connection" }, 400);
336
+ if (!this.#versionControlIntegrationIds.has(input.integrationId)) return context.json({ error: "Source-control integration not found" }, 404);
337
+ const handle = (await this.#sourceControl()).forIntegration(input.integrationId);
338
+ if (!await handle.installations.get({
339
+ orgId: tenant.orgId,
340
+ id: input.installationId
341
+ })) return context.json({ error: "Source-control installation not found" }, 404);
342
+ const connection = await handle.connections.create({
343
+ orgId: tenant.orgId,
344
+ factoryProjectId: projectId,
345
+ installationId: input.installationId,
346
+ createdByUserId: tenant.userId
347
+ });
348
+ return context.json({ connection }, 201);
349
+ }
350
+ }),
351
+ registerApiRoute("/web/factory/projects/:id/source-control-connections/:connectionId", {
352
+ method: "DELETE",
353
+ requiresAuth: false,
354
+ handler: async (routeContext) => {
355
+ const context = loose(routeContext);
356
+ const tenant = await this.#resolveTenant(context);
357
+ if ("response" in tenant) return tenant.response;
358
+ const projectId = context.req.param("id");
359
+ const connectionId = context.req.param("connectionId");
360
+ if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId)) return context.json({ error: "Source-control connection not found" }, 404);
361
+ const found = await this.#findConnection({
362
+ orgId: tenant.orgId,
363
+ projectId,
364
+ id: connectionId
365
+ });
366
+ if (!found) return context.json({ error: "Source-control connection not found" }, 404);
367
+ await found.handle.connections.delete({
368
+ orgId: tenant.orgId,
369
+ id: connectionId
370
+ });
371
+ return context.body(null, 204);
372
+ }
373
+ }),
374
+ registerApiRoute("/web/factory/projects/:id/source-control-connections/:connectionId/repositories", {
375
+ method: "POST",
376
+ requiresAuth: false,
377
+ handler: async (routeContext) => {
378
+ const context = loose(routeContext);
379
+ const tenant = await this.#resolveTenant(context);
380
+ if ("response" in tenant) return tenant.response;
381
+ const projectId = context.req.param("id");
382
+ const connectionId = context.req.param("connectionId");
383
+ if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId)) return context.json({ error: "Source-control connection not found" }, 404);
384
+ const found = await this.#findConnection({
385
+ orgId: tenant.orgId,
386
+ projectId,
387
+ id: connectionId
388
+ });
389
+ if (!found) return context.json({ error: "Source-control connection not found" }, 404);
390
+ const input = parseRepositoryLinkInput(await readJson(context));
391
+ if (!input) return context.json({ error: "invalid_project_repository" }, 400);
392
+ const repository = await found.handle.repositories.get({
393
+ orgId: tenant.orgId,
394
+ id: input.repositoryId
395
+ });
396
+ if (!repository || repository.installationId !== found.connection.installationId) return context.json({ error: "Source-control repository not found" }, 404);
397
+ const projectRepository = await found.handle.projectRepositories.link({
398
+ orgId: tenant.orgId,
399
+ connectionId,
400
+ createdByUserId: tenant.userId,
401
+ ...input
402
+ });
403
+ return context.json({ projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) }, 201);
404
+ }
405
+ }),
406
+ registerApiRoute("/web/factory/projects/:id/repositories/:projectRepositoryId", {
407
+ method: "PATCH",
408
+ requiresAuth: false,
409
+ handler: async (routeContext) => {
410
+ const context = loose(routeContext);
411
+ const tenant = await this.#resolveTenant(context);
412
+ if ("response" in tenant) return tenant.response;
413
+ const projectId = context.req.param("id");
414
+ const projectRepositoryId = context.req.param("projectRepositoryId");
415
+ if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId)) return context.json({ error: "Project repository not found" }, 404);
416
+ const found = await this.#findProjectRepository({
417
+ orgId: tenant.orgId,
418
+ projectId,
419
+ id: projectRepositoryId
420
+ });
421
+ if (!found) return context.json({ error: "Project repository not found" }, 404);
422
+ const input = parseRepositoryUpdateInput(await readJson(context));
423
+ if (!input) return context.json({ error: "invalid_project_repository" }, 400);
424
+ const projectRepository = await found.handle.projectRepositories.update({
425
+ orgId: tenant.orgId,
426
+ id: projectRepositoryId,
427
+ input
428
+ });
429
+ return context.json({ projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) });
430
+ }
431
+ }),
432
+ registerApiRoute("/web/factory/projects/:id/repositories/:projectRepositoryId", {
433
+ method: "DELETE",
434
+ requiresAuth: false,
435
+ handler: async (routeContext) => {
436
+ const context = loose(routeContext);
437
+ const tenant = await this.#resolveTenant(context);
438
+ if ("response" in tenant) return tenant.response;
439
+ const projectId = context.req.param("id");
440
+ const projectRepositoryId = context.req.param("projectRepositoryId");
441
+ if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId)) return context.json({ error: "Project repository not found" }, 404);
442
+ const found = await this.#findProjectRepository({
443
+ orgId: tenant.orgId,
444
+ projectId,
445
+ id: projectRepositoryId
446
+ });
447
+ if (!found) return context.json({ error: "Project repository not found" }, 404);
448
+ await found.handle.projectRepositories.unlink({
449
+ orgId: tenant.orgId,
450
+ id: projectRepositoryId
451
+ });
452
+ return context.body(null, 204);
453
+ }
454
+ })
455
+ ];
456
+ }
457
+ };
458
+ //#endregion
459
+ export { ProjectRoutes };
460
+
461
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","names":["#versionControlIntegrationIds","#projects","#sourceControl","#handles","#project","#findConnection","#findProjectRepository","#repositoryPayload","#resolveTenant"],"sources":["../../src/routes/projects.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type {\n CreateFactoryProjectInput,\n FactoryProjectsStorage,\n UpdateFactoryProjectInput,\n} from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_NAME_LENGTH = 200;\nconst MAX_DESCRIPTION_LENGTH = 2_000;\nconst MAX_SETUP_COMMAND_LENGTH = 2_000;\nconst MAX_BRANCH_LENGTH = 255;\nconst MAX_SANDBOX_PROVIDER_LENGTH = 100;\nconst MAX_SANDBOX_WORKDIR_LENGTH = 1_000;\nconst CONTROL_CHAR_RE = /[\\0-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\nfunction loose(context: unknown): Context {\n return context as Context;\n}\n\nasync function readJson(context: Context): Promise<unknown | undefined> {\n try {\n return await context.req.json();\n } catch {\n return undefined;\n }\n}\n\nfunction parseCreateInput(value: unknown): CreateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n if (input.description !== undefined && input.description !== null && typeof input.description !== 'string')\n return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n return { name, description };\n}\n\nfunction parseUpdateInput(value: unknown): UpdateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateFactoryProjectInput = {};\n if (input.name !== undefined) {\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n patch.name = name;\n }\n if (input.description !== undefined) {\n if (input.description !== null && typeof input.description !== 'string') return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n patch.description = description;\n }\n if (input.defaultModelId !== undefined) {\n const defaultModelId = parseOptionalString(input.defaultModelId, { maxLength: MAX_NAME_LENGTH, nullable: true });\n if (defaultModelId === false) return null;\n patch.defaultModelId = defaultModelId ?? null;\n }\n if (input.slackWorkItemsEnabled !== undefined) {\n if (typeof input.slackWorkItemsEnabled !== 'boolean') return null;\n patch.slackWorkItemsEnabled = input.slackWorkItemsEnabled;\n }\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nfunction parseConnectionInput(value: unknown): { integrationId: string; installationId: string } | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.integrationId !== 'string' || !input.integrationId.trim()) return null;\n if (typeof input.installationId !== 'string' || !UUID_RE.test(input.installationId)) return null;\n return { integrationId: input.integrationId.trim(), installationId: input.installationId };\n}\n\nfunction parseOptionalString(\n value: unknown,\n { maxLength, nullable = false }: { maxLength: number; nullable?: boolean },\n): string | null | undefined | false {\n if (value === undefined) return undefined;\n if (value === null) return nullable ? null : false;\n if (typeof value !== 'string') return false;\n const normalized = value.trim();\n if (!normalized) return nullable ? null : false;\n if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;\n return normalized;\n}\n\nfunction parseRepositoryLinkInput(value: unknown): {\n repositoryId: string;\n branch: string | null;\n sandboxProvider: string;\n sandboxWorkdir: string;\n setupCommand: string | null;\n} | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.repositoryId !== 'string' || !UUID_RE.test(input.repositoryId)) return null;\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, { maxLength: MAX_SETUP_COMMAND_LENGTH, nullable: true });\n if (\n branch === false ||\n typeof sandboxProvider !== 'string' ||\n typeof sandboxWorkdir !== 'string' ||\n setupCommand === false\n )\n return null;\n return {\n repositoryId: input.repositoryId,\n branch: branch ?? null,\n sandboxProvider,\n sandboxWorkdir,\n setupCommand: setupCommand ?? null,\n };\n}\n\nfunction parseRepositoryUpdateInput(value: unknown): UpdateProjectRepositoryInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateProjectRepositoryInput = {};\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, { maxLength: MAX_SETUP_COMMAND_LENGTH, nullable: true });\n if (\n branch === false ||\n sandboxProvider === false ||\n sandboxProvider === null ||\n sandboxWorkdir === false ||\n sandboxWorkdir === null ||\n setupCommand === false\n )\n return null;\n if (branch !== undefined) patch.branch = branch;\n if (sandboxProvider !== undefined) patch.sandboxProvider = sandboxProvider;\n if (sandboxWorkdir !== undefined) patch.sandboxWorkdir = sandboxWorkdir;\n if (setupCommand !== undefined) patch.setupCommand = setupCommand;\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nexport interface ProjectRoutesDeps extends RouteDependencies {\n /** Factory projects domain backing the CRUD surface. */\n projects: FactoryProjectsStorage;\n /** Source-control domain the connection/repository routes fan out over. */\n sourceControl: SourceControlStorage;\n /** Integration ids allowed as source-control connection targets. */\n versionControlIntegrationIds?: string[];\n}\n\nexport class ProjectRoutes extends Route<ProjectRoutesDeps> {\n readonly #versionControlIntegrationIds: Set<string>;\n\n constructor(deps: ProjectRoutesDeps) {\n super(deps);\n this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);\n }\n\n async #projects(): Promise<FactoryProjectsStorage> {\n await this.deps.projects.ensureReady();\n return this.deps.projects;\n }\n\n async #sourceControl(): Promise<SourceControlStorage> {\n await this.deps.sourceControl.ensureReady();\n return this.deps.sourceControl;\n }\n\n async #handles(): Promise<SourceControlStorageHandle[]> {\n const storage = await this.#sourceControl();\n return [...this.#versionControlIntegrationIds].map(integrationId => storage.forIntegration(integrationId));\n }\n\n async #project(orgId: string, id: string) {\n return (await this.#projects()).get({ orgId, id });\n }\n\n async #findConnection({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const connection = await handle.connections.get({ orgId, id });\n if (connection?.factoryProjectId === projectId) return { handle, connection };\n }\n return null;\n }\n\n async #findProjectRepository({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const projectRepository = await handle.projectRepositories.get({ orgId, id });\n if (!projectRepository) continue;\n const connection = await handle.connections.get({ orgId, id: projectRepository.connectionId });\n if (connection?.factoryProjectId === projectId) return { handle, connection, projectRepository };\n }\n return null;\n }\n\n async #repositoryPayload(handle: SourceControlStorageHandle, orgId: string, projectRepository: ProjectRepository) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n return { ...projectRepository, repository };\n }\n\n async #resolveTenant(context: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(context);\n const tenant = this.deps.auth.tenant(context);\n if (!tenant) return { response: context.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: context.json(\n { error: 'organization_required', message: 'Factory projects require an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/factory/projects', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const input = parseCreateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).create({ orgId: tenant.orgId, userId: tenant.userId, input });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, id);\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const input = parseUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).update({ orgId: tenant.orgId, id, input });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n if (!(await this.#project(tenant.orgId, id))) return context.json({ error: 'Project not found' }, 404);\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({ orgId: tenant.orgId, factoryProjectId: id })) {\n await handle.connections.delete({ orgId: tenant.orgId, id: connection.id });\n }\n }\n await (await this.#projects()).delete({ orgId: tenant.orgId, id });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const connections = [];\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n })) {\n const installation = await handle.installations.get({\n orgId: tenant.orgId,\n id: connection.installationId,\n });\n // Skip orphaned connections whose installation was pruned (e.g.\n // the user uninstalled the GitHub App). Otherwise\n // `projectRepositories.list` throws `requireConnection` and the\n // whole endpoint 500s, which hangs the web UI on the page\n // loader for every project. New code cascade-deletes these on\n // installation removal, but this defensive skip lets already-\n // orphaned rows in existing databases self-heal on read.\n if (!installation) continue;\n const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });\n connections.push({\n ...connection,\n installation,\n repositories: await Promise.all(links.map(link => this.#repositoryPayload(handle, tenant.orgId, link))),\n });\n }\n }\n return context.json({ connections });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const input = parseConnectionInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_source_control_connection' }, 400);\n if (!this.#versionControlIntegrationIds.has(input.integrationId))\n return context.json({ error: 'Source-control integration not found' }, 404);\n const handle = (await this.#sourceControl()).forIntegration(input.integrationId);\n if (!(await handle.installations.get({ orgId: tenant.orgId, id: input.installationId })))\n return context.json({ error: 'Source-control installation not found' }, 404);\n const connection = await handle.connections.create({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n installationId: input.installationId,\n createdByUserId: tenant.userId,\n });\n return context.json({ connection }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n await found.handle.connections.delete({ orgId: tenant.orgId, id: connectionId });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId/repositories', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n const input = parseRepositoryLinkInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const repository = await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId });\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n ...input,\n });\n return context.json(\n { projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) },\n 201,\n );\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n const input = parseRepositoryUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const projectRepository = await found.handle.projectRepositories.update({\n orgId: tenant.orgId,\n id: projectRepositoryId,\n input,\n });\n return context.json({\n projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository!),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n await found.handle.projectRepositories.unlink({ orgId: tenant.orgId, id: projectRepositoryId });\n return context.body(null, 204);\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Per-request model-provider credential context.
3
+ *
4
+ * The provider config/OAuth routes serve two deployment shapes:
5
+ *
6
+ * - **Local** (no web auth adapter): credentials live in the server-global
7
+ * file-backed `AuthStorage` (`auth.json`) — unchanged TUI behavior.
8
+ * - **Tenant** (auth adapter active): credentials live in the
9
+ * `model-credentials` factory storage domain, scoped per `(orgId, userId)`
10
+ * with optional org-wide rows (`userId` absent).
11
+ *
12
+ * `resolveCredentialContext` decides which shape a request is in: an
13
+ * authenticated user always gets the tenant path; an unauthenticated request
14
+ * is rejected when auth is enabled and falls back to local otherwise.
15
+ */
16
+ import type { Context } from 'hono';
17
+ import type { CredentialRecord, LoginSessionKind, ModelCredentialsStorage } from '../storage/domains/credentials/base.js';
18
+ import type { RouteAuth } from './route.js';
19
+ /**
20
+ * OAuth credentials are stored under the auth provider id, which differs from
21
+ * the catalog provider id for OpenAI (stored as `openai-codex`). Tenant rows
22
+ * use the same keying so one lookup serves both OAuth tokens and API keys.
23
+ */
24
+ export declare function getAuthProviderId(provider: string): string;
25
+ /**
26
+ * Providers that support a browser-driven OAuth sign-in flow, keyed by
27
+ * *catalog* provider id (what the routes and settings UI use). All flows need
28
+ * no inbound connection to the server: Anthropic uses paste-code PKCE, the
29
+ * rest use RFC 8628-style device codes.
30
+ */
31
+ export declare const WEB_OAUTH_FLOW_KINDS: Readonly<Record<string, LoginSessionKind>>;
32
+ export type CredentialContext = {
33
+ mode: 'local';
34
+ } | {
35
+ mode: 'tenant';
36
+ storage: ModelCredentialsStorage;
37
+ orgId: string;
38
+ userId: string;
39
+ };
40
+ /**
41
+ * The tenant credentials domain, when registered and ready. `undefined` means
42
+ * the factory never ran (no handle threaded in) or the domain's init failed
43
+ * (fail-soft — callers report the feature unavailable instead of crashing).
44
+ */
45
+ export declare function getTenantCredentialsStorage(credentials: ModelCredentialsStorage | undefined): Promise<ModelCredentialsStorage | undefined>;
46
+ /**
47
+ * Org key for tenant rows. Personal accounts get an org bootstrapped by the
48
+ * auth adapter (`ensureOrg`); if one still isn't present, scope rows under a
49
+ * per-user synthetic org so credentials never become server-global.
50
+ */
51
+ export declare function tenantOrgId(tenant: {
52
+ orgId?: string;
53
+ userId: string;
54
+ }): string;
55
+ /**
56
+ * Resolve the credential context for a request, or a ready-to-return error
57
+ * response. Mutating credential routes call this and hard-fail (401/503)
58
+ * when the tenant path is required but unavailable.
59
+ */
60
+ export declare function resolveCredentialContext({ c, auth, credentials, }: {
61
+ c: Context;
62
+ auth: RouteAuth;
63
+ /** Tenant credential domain handle; absent in local (no-DB) mode. */
64
+ credentials?: ModelCredentialsStorage;
65
+ }): Promise<CredentialContext | {
66
+ response: Response;
67
+ }>;
68
+ /**
69
+ * List the caller's tenant credential records for provider listing, or
70
+ * `undefined` in local mode (caller should fall back to `AuthStorage`).
71
+ * Read-only and degradation-friendly: an authenticated caller with the domain
72
+ * unavailable gets an empty list (sources show `env`/`none`) instead of a 503
73
+ * so the settings page still renders.
74
+ */
75
+ export declare function listTenantCredentialsForRequest({ c, auth, credentials, }: {
76
+ c: Context;
77
+ auth: RouteAuth;
78
+ /** Tenant credential domain handle; absent in local (no-DB) mode. */
79
+ credentials?: ModelCredentialsStorage;
80
+ }): Promise<CredentialRecord[] | undefined>;
81
+ //# sourceMappingURL=provider-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-credentials.d.ts","sourceRoot":"","sources":["../../src/routes/provider-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAK3E,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,uBAAuB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAC/C,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAQ9C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,EAC7C,CAAC,EACD,IAAI,EACJ,WAAW,GACZ,EAAE;IACD,CAAC,EAAE,OAAO,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,GAAG,OAAO,CAAC,iBAAiB,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAuBtD;AAED;;;;;;GAMG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,CAAC,EACD,IAAI,EACJ,WAAW,GACZ,EAAE;IACD,CAAC,EAAE,OAAO,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,SAAS,CAAC,CAO1C"}