@mastra/factory 0.2.2-alpha.0 → 0.2.2-alpha.2

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 (174) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/auth.d.ts +2 -2
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +401 -314
  5. package/dist/auth.js.map +1 -1
  6. package/dist/capabilities/connection.js +1 -1
  7. package/dist/capabilities/intake.js +1 -1
  8. package/dist/capabilities/version-control.js +1 -1
  9. package/dist/factory.d.ts.map +1 -1
  10. package/dist/factory.js +441 -39455
  11. package/dist/factory.js.map +1 -1
  12. package/dist/index.js +2 -39466
  13. package/dist/integrations/base.js +1 -1
  14. package/dist/integrations/github/config.js +36 -22
  15. package/dist/integrations/github/config.js.map +1 -1
  16. package/dist/integrations/github/factory-session.js +33 -37
  17. package/dist/integrations/github/factory-session.js.map +1 -1
  18. package/dist/integrations/github/integration.js +1037 -3624
  19. package/dist/integrations/github/integration.js.map +1 -1
  20. package/dist/integrations/github/issue-triage.js +71 -71
  21. package/dist/integrations/github/issue-triage.js.map +1 -1
  22. package/dist/integrations/github/pat.js +47 -36
  23. package/dist/integrations/github/pat.js.map +1 -1
  24. package/dist/integrations/github/provenance.js +72 -101
  25. package/dist/integrations/github/provenance.js.map +1 -1
  26. package/dist/integrations/github/routes.d.ts.map +1 -1
  27. package/dist/integrations/github/routes.js +1272 -1833
  28. package/dist/integrations/github/routes.js.map +1 -1
  29. package/dist/integrations/github/rules.js +203 -471
  30. package/dist/integrations/github/rules.js.map +1 -1
  31. package/dist/integrations/github/sandbox.d.ts +2 -0
  32. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  33. package/dist/integrations/github/sandbox.js +401 -326
  34. package/dist/integrations/github/sandbox.js.map +1 -1
  35. package/dist/integrations/github/session-subscriptions.js +165 -223
  36. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  37. package/dist/integrations/github/subscriptions.js +43 -51
  38. package/dist/integrations/github/subscriptions.js.map +1 -1
  39. package/dist/integrations/github/token-refresh.js +15 -18
  40. package/dist/integrations/github/token-refresh.js.map +1 -1
  41. package/dist/integrations/github/webhook.js +359 -343
  42. package/dist/integrations/github/webhook.js.map +1 -1
  43. package/dist/integrations/linear/agent-tools.js +77 -100
  44. package/dist/integrations/linear/agent-tools.js.map +1 -1
  45. package/dist/integrations/linear/integration.js +611 -1309
  46. package/dist/integrations/linear/integration.js.map +1 -1
  47. package/dist/integrations/linear/routes.js +227 -230
  48. package/dist/integrations/linear/routes.js.map +1 -1
  49. package/dist/integrations/linear/rules.js +126 -346
  50. package/dist/integrations/linear/rules.js.map +1 -1
  51. package/dist/integrations/linear/storage.js +1 -1
  52. package/dist/integrations/platform/api-client.js +131 -135
  53. package/dist/integrations/platform/api-client.js.map +1 -1
  54. package/dist/integrations/platform/github/event-worker.js +275 -573
  55. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  56. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  57. package/dist/integrations/platform/github/integration.js +845 -3942
  58. package/dist/integrations/platform/github/integration.js.map +1 -1
  59. package/dist/integrations/platform/linear/integration.js +457 -1309
  60. package/dist/integrations/platform/linear/integration.js.map +1 -1
  61. package/dist/integrations/workos/integration.js +80 -85
  62. package/dist/integrations/workos/integration.js.map +1 -1
  63. package/dist/routes/config.d.ts.map +1 -1
  64. package/dist/routes/config.js +933 -915
  65. package/dist/routes/config.js.map +1 -1
  66. package/dist/routes/custom-provider-source.js +110 -93
  67. package/dist/routes/custom-provider-source.js.map +1 -1
  68. package/dist/routes/fs.js +401 -349
  69. package/dist/routes/fs.js.map +1 -1
  70. package/dist/routes/intake.js +165 -165
  71. package/dist/routes/intake.js.map +1 -1
  72. package/dist/routes/oauth.js +447 -25166
  73. package/dist/routes/oauth.js.map +1 -1
  74. package/dist/routes/projects.js +437 -376
  75. package/dist/routes/projects.js.map +1 -1
  76. package/dist/routes/provider-credentials.js +76 -58
  77. package/dist/routes/provider-credentials.js.map +1 -1
  78. package/dist/routes/route.js +12 -8
  79. package/dist/routes/route.js.map +1 -1
  80. package/dist/routes/skills.js +160 -173
  81. package/dist/routes/skills.js.map +1 -1
  82. package/dist/routes/surface.js +250 -28618
  83. package/dist/routes/surface.js.map +1 -1
  84. package/dist/routes/tenant-credentials.js +141 -166
  85. package/dist/routes/tenant-credentials.js.map +1 -1
  86. package/dist/routes/work-items.js +654 -862
  87. package/dist/routes/work-items.js.map +1 -1
  88. package/dist/rules/binding-context.js +23 -35
  89. package/dist/rules/binding-context.js.map +1 -1
  90. package/dist/rules/defaults.js +200 -309
  91. package/dist/rules/defaults.js.map +1 -1
  92. package/dist/rules/dispatcher.d.ts.map +1 -1
  93. package/dist/rules/dispatcher.js +438 -677
  94. package/dist/rules/dispatcher.js.map +1 -1
  95. package/dist/rules/index.js +5 -599
  96. package/dist/rules/processor.js +344 -596
  97. package/dist/rules/processor.js.map +1 -1
  98. package/dist/rules/resolve.js +21 -18
  99. package/dist/rules/resolve.js.map +1 -1
  100. package/dist/rules/start-coordinator.js +168 -157
  101. package/dist/rules/start-coordinator.js.map +1 -1
  102. package/dist/rules/tools.js +50 -85
  103. package/dist/rules/tools.js.map +1 -1
  104. package/dist/rules/transition-service.js +183 -454
  105. package/dist/rules/transition-service.js.map +1 -1
  106. package/dist/rules/types.js +33 -29
  107. package/dist/rules/types.js.map +1 -1
  108. package/dist/rules/validation.js +266 -281
  109. package/dist/rules/validation.js.map +1 -1
  110. package/dist/sandbox/fleet.js +272 -250
  111. package/dist/sandbox/fleet.js.map +1 -1
  112. package/dist/sandbox/reattach.js +13 -6
  113. package/dist/sandbox/reattach.js.map +1 -1
  114. package/dist/server-error.js +12 -16
  115. package/dist/server-error.js.map +1 -1
  116. package/dist/skills/service.js +30 -35
  117. package/dist/skills/service.js.map +1 -1
  118. package/dist/spa-static.d.ts.map +1 -1
  119. package/dist/spa-static.js +91 -64
  120. package/dist/spa-static.js.map +1 -1
  121. package/dist/state-signing.js +73 -44
  122. package/dist/state-signing.js.map +1 -1
  123. package/dist/storage/domains/audit/agent-audit.js +60 -59
  124. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  125. package/dist/storage/domains/audit/base.js +169 -112
  126. package/dist/storage/domains/audit/base.js.map +1 -1
  127. package/dist/storage/domains/audit/domain.js +155 -153
  128. package/dist/storage/domains/audit/domain.js.map +1 -1
  129. package/dist/storage/domains/credentials/base.js +259 -243
  130. package/dist/storage/domains/credentials/base.js.map +1 -1
  131. package/dist/storage/domains/custom-providers/base.js +138 -143
  132. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  133. package/dist/storage/domains/intake/base.js +75 -57
  134. package/dist/storage/domains/intake/base.js.map +1 -1
  135. package/dist/storage/domains/integrations/base.js +310 -245
  136. package/dist/storage/domains/integrations/base.js.map +1 -1
  137. package/dist/storage/domains/memory-settings/base.js +117 -102
  138. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  139. package/dist/storage/domains/model-packs/base.js +89 -88
  140. package/dist/storage/domains/model-packs/base.js.map +1 -1
  141. package/dist/storage/domains/projects/base.js +97 -92
  142. package/dist/storage/domains/projects/base.js.map +1 -1
  143. package/dist/storage/domains/queue-health/base.js +111 -76
  144. package/dist/storage/domains/queue-health/base.js.map +1 -1
  145. package/dist/storage/domains/source-control/base.d.ts +9 -0
  146. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  147. package/dist/storage/domains/source-control/base.js +777 -702
  148. package/dist/storage/domains/source-control/base.js.map +1 -1
  149. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  150. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  151. package/dist/storage/domains/source-control/inmemory.js +322 -344
  152. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  153. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  154. package/dist/storage/domains/work-items/base.js +1266 -1132
  155. package/dist/storage/domains/work-items/base.js.map +1 -1
  156. package/dist/storage/domains/work-items/metrics.js +202 -184
  157. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  158. package/dist/timing.d.ts +15 -0
  159. package/dist/timing.d.ts.map +1 -0
  160. package/dist/timing.js +34 -0
  161. package/dist/timing.js.map +1 -0
  162. package/dist/workspace.d.ts +2 -3
  163. package/dist/workspace.d.ts.map +1 -1
  164. package/dist/workspace.js +236 -438
  165. package/dist/workspace.js.map +1 -1
  166. package/factory-skills/factory-review/SKILL.md +9 -2
  167. package/package.json +9 -9
  168. package/dist/capabilities/connection.js.map +0 -1
  169. package/dist/capabilities/intake.js.map +0 -1
  170. package/dist/capabilities/version-control.js.map +0 -1
  171. package/dist/index.js.map +0 -1
  172. package/dist/integrations/base.js.map +0 -1
  173. package/dist/integrations/linear/storage.js.map +0 -1
  174. package/dist/rules/index.js.map +0 -1
package/dist/workspace.js CHANGED
@@ -1,449 +1,247 @@
1
- // src/workspace.ts
1
+ import { getFactoryAuthUserId } from "./auth.js";
2
+ import { getGithubPat } from "./integrations/github/pat.js";
3
+ import { registerGithubPatKind, registerGithubTokenInjector } from "./integrations/github/token-refresh.js";
4
+ import { checkoutSessionBranch, materializeRepo, runWorktreeSetup } from "./integrations/github/sandbox.js";
5
+ import { getFactorySessionAddress } from "./rules/binding-context.js";
6
+ import path, { dirname, join } from "path";
7
+ import { LocalSandbox, LocalSkillSource, Workspace } from "@mastra/core/workspace";
8
+ import { DEFAULT_CONFIG_DIR } from "@mastra/code-sdk/constants";
9
+ import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
2
10
  import { existsSync } from "fs";
3
- import path2, { dirname, join } from "path";
4
11
  import { fileURLToPath } from "url";
5
- import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
6
12
  import { MASTRACODE_WORKSPACE_TOOLS } from "@mastra/code-sdk/agents/tool-availability";
7
13
  import { getDynamicWorkspace } from "@mastra/code-sdk/agents/workspace";
8
- import { DEFAULT_CONFIG_DIR } from "@mastra/code-sdk/constants";
9
- import { LocalSandbox, LocalSkillSource, Workspace } from "@mastra/core/workspace";
10
-
11
- // src/auth.ts
12
- import { MastraAuthWorkos } from "@mastra/auth-workos";
13
- import {
14
- registerApiRoute,
15
- isAuthHttpHandler,
16
- isCredentialsProvider,
17
- isOrganizationsProvider,
18
- isSessionProvider,
19
- isSSOProvider
20
- } from "@mastra/core/server";
21
- function getFactoryAuthUserId(user) {
22
- return user?.workosId ?? user?.id;
23
- }
24
- function getFactoryAuthOrgId(user) {
25
- return user?.organizationId;
26
- }
27
-
28
- // src/integrations/github/pat.ts
29
- var PAT_SETTINGS_USER_ID = "__github_org_settings__";
30
- function asToken(value) {
31
- return typeof value === "string" && value.length > 0 ? value : null;
32
- }
33
- async function getGithubPat(getStorage, orgId, kind = "default") {
34
- try {
35
- const settings = await getStorage().settings.get(orgId, PAT_SETTINGS_USER_ID);
36
- if (!settings) return null;
37
- if (kind === "reviewer") return asToken(settings.reviewerPat) ?? asToken(settings.pat);
38
- return asToken(settings.pat);
39
- } catch {
40
- return null;
41
- }
42
- }
43
-
44
- // src/integrations/github/sandbox.ts
45
- import { createHash } from "crypto";
46
-
47
- // src/sandbox/fleet.ts
48
- import path from "path";
49
- function reportProgress(onProgress, event) {
50
- if (!onProgress) return;
51
- try {
52
- onProgress(event);
53
- } catch {
54
- }
55
- }
56
-
57
- // src/integrations/github/sandbox.ts
58
- function shellQuote(value) {
59
- return `'` + value.split(`'`).join(`'\\''`) + `'`;
60
- }
61
- async function sh(sandbox, script) {
62
- return sandbox.executeCommand("sh", ["-c", script]);
63
- }
64
- var MaterializeError = class extends Error {
65
- constructor(message, code) {
66
- super(message);
67
- this.code = code;
68
- this.name = "MaterializeError";
69
- }
70
- code;
71
- };
72
- function tokenUrl(repoFullName, token) {
73
- return `https://x-access-token:${token}@github.com/${repoFullName}.git`;
74
- }
75
- function cleanUrl(repoFullName) {
76
- return `https://github.com/${repoFullName}.git`;
77
- }
78
- async function materializeRepo(options) {
79
- const { row: sandboxRow, repoInfo, sandbox, token, storage, onProgress } = options;
80
- const workdir = sandboxRow.sandboxWorkdir;
81
- const repo = repoInfo.repoFullName;
82
- if (!/^[\w.-]+\/[\w.-]+$/.test(repo)) {
83
- throw new MaterializeError(`Refusing to materialize: invalid repo full name '${repo}'.`, "clone-failed");
84
- }
85
- if (!/^[A-Za-z0-9_./-]+$/.test(repoInfo.defaultBranch)) {
86
- throw new MaterializeError(
87
- `Refusing to materialize: invalid default branch '${repoInfo.defaultBranch}'.`,
88
- "clone-failed"
89
- );
90
- }
91
- const gitVersion = await sh(sandbox, "git --version");
92
- if (gitVersion.exitCode !== 0) {
93
- throw new MaterializeError(
94
- "git is not installed in the sandbox. The sandbox template must include git.",
95
- "git-missing"
96
- );
97
- }
98
- const authUrl = tokenUrl(repo, token);
99
- const alreadyMaterialized = Boolean(sandboxRow.materializedAt) || await hasExistingCheckout(sandbox, workdir, repo);
100
- try {
101
- if (!alreadyMaterialized) {
102
- reportProgress(onProgress, {
103
- phase: "cloning",
104
- message: `Cloning ${repo} (first open can take a minute)\u2026`
105
- });
106
- const clone = await sh(
107
- sandbox,
108
- `git clone --depth=1 --single-branch --branch ${shellQuote(repoInfo.defaultBranch)} ${shellQuote(authUrl)} ${shellQuote(workdir)}`
109
- );
110
- if (clone.exitCode !== 0) {
111
- throw classifyGitFailure(clone, "clone-failed");
112
- }
113
- } else {
114
- reportProgress(onProgress, { phase: "pulling", message: `Updating ${repo} to the latest changes\u2026` });
115
- const setUrl = await sh(sandbox, `git -C ${shellQuote(workdir)} remote set-url origin ${shellQuote(authUrl)}`);
116
- if (setUrl.exitCode !== 0) {
117
- throw new MaterializeError(`Failed to set git remote: ${setUrl.stderr}`, "pull-failed");
118
- }
119
- const pull = await sh(sandbox, `git -C ${shellQuote(workdir)} pull --ff-only`);
120
- if (pull.exitCode !== 0) {
121
- throw classifyGitFailure(pull, "pull-failed");
122
- }
123
- }
124
- } finally {
125
- await scrubRemote(sandbox, workdir, repo, alreadyMaterialized);
126
- }
127
- reportProgress(onProgress, { phase: "finalizing", message: "Finalizing workspace\u2026" });
128
- await storage.markMaterialized({ id: sandboxRow.id });
129
- }
130
- async function checkoutSessionBranch(sandbox, workdir, {
131
- branch,
132
- baseBranch,
133
- token,
134
- repoFullName
135
- }) {
136
- if (!isValidGitRef(branch) || !isValidGitRef(baseBranch)) {
137
- throw new MaterializeError("Refusing to create a session from an invalid branch name.", "clone-failed");
138
- }
139
- const current = await sh(sandbox, `git -C ${shellQuote(workdir)} branch --show-current`);
140
- if (current.exitCode === 0 && current.stdout.trim() === branch) return;
141
- const local = await sh(
142
- sandbox,
143
- `git -C ${shellQuote(workdir)} show-ref --verify --quiet refs/heads/${shellQuote(branch)}`
144
- );
145
- if (local.exitCode === 0) {
146
- const checkout = await sh(sandbox, `git -C ${shellQuote(workdir)} checkout ${shellQuote(branch)}`);
147
- if (checkout.exitCode !== 0) throw classifyGitFailure(checkout, "clone-failed");
148
- return;
149
- }
150
- const authUrl = tokenUrl(repoFullName, token);
151
- try {
152
- const setUrl = await sh(sandbox, `git -C ${shellQuote(workdir)} remote set-url origin ${shellQuote(authUrl)}`);
153
- if (setUrl.exitCode !== 0) throw classifyGitFailure(setUrl, "pull-failed");
154
- const fetch = await sh(
155
- sandbox,
156
- `git -C ${shellQuote(workdir)} fetch origin ${shellQuote(baseBranch)} && git -C ${shellQuote(workdir)} checkout -b ${shellQuote(branch)} FETCH_HEAD`
157
- );
158
- if (fetch.exitCode !== 0) throw classifyGitFailure(fetch, "clone-failed");
159
- } finally {
160
- await sh(sandbox, `git -C ${shellQuote(workdir)} remote set-url origin ${shellQuote(cleanUrl(repoFullName))}`);
161
- }
162
- }
163
- async function hasExistingCheckout(sandbox, workdir, repoFullName) {
164
- const result = await sh(sandbox, `git -C ${shellQuote(workdir)} remote get-url origin`);
165
- if (result.exitCode !== 0) return false;
166
- const url = result.stdout.trim().toLowerCase();
167
- const suffix = `github.com/${repoFullName.toLowerCase()}`;
168
- return url.endsWith(`${suffix}.git`) || url.endsWith(suffix);
169
- }
170
- async function scrubRemote(sandbox, workdir, repoFullName, expectGitDir) {
171
- const result = await sh(
172
- sandbox,
173
- `git -C ${shellQuote(workdir)} remote set-url origin ${shellQuote(cleanUrl(repoFullName))}`
174
- );
175
- if (result.exitCode !== 0 && expectGitDir) {
176
- throw new MaterializeError(
177
- `Failed to scrub installation token from git remote: ${result.stderr.trim() || result.stdout.trim()}`,
178
- "pull-failed"
179
- );
180
- }
181
- }
182
- function classifyGitFailure(result, fallback) {
183
- const stderr = result.stderr || "";
184
- if (/could not resolve host|failed to connect|network is unreachable|Connection timed out/i.test(stderr)) {
185
- return new MaterializeError(
186
- "The sandbox could not reach github.com. The sandbox network must allow outbound egress to github.com.",
187
- "egress-blocked"
188
- );
189
- }
190
- const verb = fallback === "clone-failed" ? "clone" : fallback === "pull-failed" ? "pull" : "push";
191
- return new MaterializeError(`git ${verb} failed: ${stderr}`, fallback);
192
- }
193
- function isValidGitRef(value) {
194
- return typeof value === "string" && value.length > 0 && value.length <= 255 && // Reject leading-dash refs (e.g. `--mirror`) so the value can never be
195
- // parsed as a git option when interpolated into a command.
196
- !value.startsWith("-") && /^[A-Za-z0-9_./-]+$/.test(value);
197
- }
198
- var WorktreeError = class extends Error {
199
- constructor(message, code) {
200
- super(message);
201
- this.code = code;
202
- this.name = "WorktreeError";
203
- }
204
- code;
205
- };
206
- async function runWorktreeSetup(sandbox, worktreePath, command) {
207
- const result = await sh(sandbox, `cd ${shellQuote(worktreePath)} && { ${command}
208
- }`);
209
- if (result.exitCode !== 0) {
210
- const detail = (result.stderr.trim() || result.stdout.trim()).slice(-2e3);
211
- throw new WorktreeError(`Setup command failed (exit ${result.exitCode}): ${detail}`, "setup-failed");
212
- }
213
- }
214
-
215
- // src/integrations/github/token-refresh.ts
216
- var GITHUB_TOKEN_INJECTOR_CONTEXT_KEY = "factoryGithubTokenInjector";
217
- var GITHUB_PAT_KIND_CONTEXT_KEY = "factoryGithubPatKind";
218
- function registerGithubTokenInjector(requestContext, injector) {
219
- requestContext.set(GITHUB_TOKEN_INJECTOR_CONTEXT_KEY, injector);
220
- }
221
- function registerGithubPatKind(requestContext, kind) {
222
- requestContext.set(GITHUB_PAT_KIND_CONTEXT_KEY, kind);
223
- }
224
-
225
- // src/rules/binding-context.ts
226
- function getFactorySessionCoordinates(requestContext) {
227
- if (!requestContext || typeof requestContext.get !== "function") return null;
228
- const context = requestContext.get("controller");
229
- const factoryProjectId = context?.getState().factoryProjectId;
230
- if (!context?.threadId || !context.resourceId || !factoryProjectId) return null;
231
- return {
232
- factoryProjectId,
233
- threadId: context.threadId,
234
- resourceId: context.resourceId,
235
- sessionId: context.resourceId
236
- };
237
- }
238
- function getFactorySessionAddress(requestContext) {
239
- const coordinates = getFactorySessionCoordinates(requestContext);
240
- if (!coordinates || !requestContext || typeof requestContext.get !== "function") return null;
241
- const user = requestContext.get("user");
242
- const orgId = getFactoryAuthOrgId(user);
243
- if (!orgId) return null;
244
- return { orgId, ...coordinates };
245
- }
246
-
247
- // src/workspace.ts
248
- var WORKSPACE_ID_PREFIX = "mfw";
249
- var SESSION_CHECKPOINT_PREFIX = "mastracode-session";
14
+ //#region src/workspace.ts
15
+ const WORKSPACE_ID_PREFIX = "mfw";
16
+ const SESSION_CHECKPOINT_PREFIX = "mastracode-session";
250
17
  function checkpointNameForSession(sessionId) {
251
- return `${SESSION_CHECKPOINT_PREFIX}-${sessionId}`;
252
- }
253
- var bundleDirectory = dirname(fileURLToPath(import.meta.url));
254
- var bundledFactorySkillsPath = join(bundleDirectory, "factory-skills");
255
- var FACTORY_SKILLS_SOURCE_PATH = [
256
- // Deploy bundle: the consumer copies `factory-skills/` next to the built
257
- // server module (e.g. via its public/ dir).
258
- bundledFactorySkillsPath,
259
- // Package layout: `dist/../factory-skills` (also `src/../factory-skills`
260
- // when running tests against sources).
261
- join(bundleDirectory, "..", "factory-skills"),
262
- // Consumer repo running from its package root before a build.
263
- join(process.cwd(), "src", "mastra", "public", "factory-skills")
18
+ return `${SESSION_CHECKPOINT_PREFIX}-${sessionId}`;
19
+ }
20
+ const bundleDirectory = dirname(fileURLToPath(import.meta.url));
21
+ const bundledFactorySkillsPath = join(bundleDirectory, "factory-skills");
22
+ const FACTORY_SKILLS_SOURCE_PATH = [
23
+ bundledFactorySkillsPath,
24
+ join(bundleDirectory, "..", "factory-skills"),
25
+ join(process.cwd(), "src", "mastra", "public", "factory-skills")
264
26
  ].find(existsSync) ?? bundledFactorySkillsPath;
265
- var FACTORY_SKILLS_MOUNT = path2.resolve(path2.parse(process.cwd()).root, "__mastracode_factory_skills__");
266
- var FACTORY_SKILL_NAMES = /* @__PURE__ */ new Set(["configure-factory-rules", "factory-plan", "factory-review", "factory-triage"]);
27
+ const FACTORY_SKILLS_MOUNT = path.resolve(path.parse(process.cwd()).root, "__mastracode_factory_skills__");
28
+ const FACTORY_SKILL_NAMES = /* @__PURE__ */ new Set([
29
+ "configure-factory-rules",
30
+ "factory-plan",
31
+ "factory-review",
32
+ "factory-triage"
33
+ ]);
267
34
  var FactorySkillSource = class {
268
- constructor(fallback, fallbackSkillRoots) {
269
- this.fallback = fallback;
270
- this.#fallbackSkillRoots = new Set(fallbackSkillRoots.map((skillPath) => path2.normalize(skillPath)));
271
- }
272
- fallback;
273
- #factorySource = new LocalSkillSource({ basePath: FACTORY_SKILLS_SOURCE_PATH });
274
- #fallbackSkillRoots;
275
- #isFactoryPath(skillPath) {
276
- const normalized = path2.normalize(skillPath);
277
- return normalized === FACTORY_SKILLS_MOUNT || normalized.startsWith(`${FACTORY_SKILLS_MOUNT}${path2.sep}`);
278
- }
279
- #factoryPath(skillPath) {
280
- return path2.relative(FACTORY_SKILLS_MOUNT, path2.normalize(skillPath));
281
- }
282
- exists(skillPath) {
283
- return this.#isFactoryPath(skillPath) ? this.#factorySource.exists(this.#factoryPath(skillPath)) : this.fallback.exists(skillPath);
284
- }
285
- stat(skillPath) {
286
- return this.#isFactoryPath(skillPath) ? this.#factorySource.stat(this.#factoryPath(skillPath)) : this.fallback.stat(skillPath);
287
- }
288
- readFile(skillPath) {
289
- return this.#isFactoryPath(skillPath) ? this.#factorySource.readFile(this.#factoryPath(skillPath)) : this.fallback.readFile(skillPath);
290
- }
291
- async readdir(skillPath) {
292
- if (this.#isFactoryPath(skillPath)) {
293
- return this.#factorySource.readdir(this.#factoryPath(skillPath));
294
- }
295
- const entries = await this.fallback.readdir(skillPath);
296
- if (this.#fallbackSkillRoots.has(path2.normalize(skillPath))) {
297
- return entries.filter((entry) => !FACTORY_SKILL_NAMES.has(entry.name));
298
- }
299
- return entries;
300
- }
301
- realpath(skillPath) {
302
- if (this.#isFactoryPath(skillPath)) return Promise.resolve(path2.normalize(skillPath));
303
- return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);
304
- }
35
+ fallback;
36
+ #factorySource = new LocalSkillSource({ basePath: FACTORY_SKILLS_SOURCE_PATH });
37
+ #fallbackSkillRoots;
38
+ constructor(fallback, fallbackSkillRoots) {
39
+ this.fallback = fallback;
40
+ this.#fallbackSkillRoots = new Set(fallbackSkillRoots.map((skillPath) => path.normalize(skillPath)));
41
+ }
42
+ #isFactoryPath(skillPath) {
43
+ const normalized = path.normalize(skillPath);
44
+ return normalized === FACTORY_SKILLS_MOUNT || normalized.startsWith(`${FACTORY_SKILLS_MOUNT}${path.sep}`);
45
+ }
46
+ #factoryPath(skillPath) {
47
+ return path.relative(FACTORY_SKILLS_MOUNT, path.normalize(skillPath));
48
+ }
49
+ exists(skillPath) {
50
+ return this.#isFactoryPath(skillPath) ? this.#factorySource.exists(this.#factoryPath(skillPath)) : this.fallback.exists(skillPath);
51
+ }
52
+ stat(skillPath) {
53
+ return this.#isFactoryPath(skillPath) ? this.#factorySource.stat(this.#factoryPath(skillPath)) : this.fallback.stat(skillPath);
54
+ }
55
+ readFile(skillPath) {
56
+ return this.#isFactoryPath(skillPath) ? this.#factorySource.readFile(this.#factoryPath(skillPath)) : this.fallback.readFile(skillPath);
57
+ }
58
+ async readdir(skillPath) {
59
+ if (this.#isFactoryPath(skillPath)) return this.#factorySource.readdir(this.#factoryPath(skillPath));
60
+ const entries = await this.fallback.readdir(skillPath);
61
+ if (this.#fallbackSkillRoots.has(path.normalize(skillPath))) return entries.filter((entry) => !FACTORY_SKILL_NAMES.has(entry.name));
62
+ return entries;
63
+ }
64
+ realpath(skillPath) {
65
+ if (this.#isFactoryPath(skillPath)) return Promise.resolve(path.normalize(skillPath));
66
+ return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);
67
+ }
305
68
  };
306
- var factorySkillExtension = {
307
- id: "web-factory",
308
- paths: [FACTORY_SKILLS_MOUNT],
309
- createSource: (fallback, fallbackSkillRoots) => new FactorySkillSource(fallback, fallbackSkillRoots)
69
+ const factorySkillExtension = {
70
+ id: "web-factory",
71
+ paths: [FACTORY_SKILLS_MOUNT],
72
+ createSource: (fallback, fallbackSkillRoots) => new FactorySkillSource(fallback, fallbackSkillRoots)
310
73
  };
311
74
  function createWorkspaceFactory(options = {}) {
312
- const { sandbox: sandboxConfig, github, fleet, workItems } = options;
313
- const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
314
- const githubTokenInjectors = /* @__PURE__ */ new Map();
315
- return async ({ requestContext, mastra, skillExtension }) => {
316
- const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
317
- const ctx = requestContext.get("controller");
318
- const session = ctx?.resourceId && github ? await github.sourceControlStorage.sessions.getBySessionId(ctx.resourceId) : null;
319
- if (!session) {
320
- if (sandboxConfig && !isLocalSandbox) {
321
- throw new Error("A Factory session ID is required to create a remote sandbox workspace");
322
- }
323
- return getDynamicWorkspace({ requestContext, mastra, skillExtension: effectiveSkillExtension });
324
- }
325
- const user = requestContext.get("user");
326
- const userId = getFactoryAuthUserId(user);
327
- if (!user?.organizationId || !userId || user.organizationId !== session.orgId || userId !== session.userId) {
328
- throw new Error(`Factory session ${session.sessionId} is not available to the current user`);
329
- }
330
- if (!sandboxConfig || !github || !fleet) {
331
- throw new Error("GitHub and sandbox providers are required to create a Factory session workspace");
332
- }
333
- const storage = github.sourceControlStorage;
334
- const projectRepository = await storage.projectRepositories.get({
335
- orgId: session.orgId,
336
- id: session.projectRepositoryId
337
- });
338
- if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);
339
- const connection = await storage.connections.get({ orgId: session.orgId, id: projectRepository.connectionId });
340
- const repository = await storage.repositories.get({ orgId: session.orgId, id: projectRepository.repositoryId });
341
- if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);
342
- const installation = await storage.installations.get({ orgId: session.orgId, id: connection.installationId });
343
- if (!installation) throw new Error(`GitHub installation ${connection.installationId} was not found`);
344
- const repoFullName = repository.slug;
345
- const workdir = isLocalSandbox ? fleet.computeLocalSessionWorkdir(repoFullName, session.id) : session.sandboxWorkdir ?? projectRepository.sandboxWorkdir;
346
- const binding = {
347
- sandboxId: session.sandboxId,
348
- checkpointName: checkpointNameForSession(session.id),
349
- setSandboxId: async (id) => {
350
- await storage.sessions.setSandbox({ id: session.id, sandboxId: id, sandboxWorkdir: workdir });
351
- session.sandboxId = id;
352
- session.sandboxWorkdir = workdir;
353
- },
354
- clear: async () => {
355
- await storage.sessions.setSandbox({ id: session.id, sandboxId: null, sandboxWorkdir: workdir });
356
- session.sandboxId = null;
357
- }
358
- };
359
- const extensionId = effectiveSkillExtension ? `-${effectiveSkillExtension.id}` : "";
360
- const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectRepository.id}-${session.id}${extensionId}`;
361
- const configDir = sandboxConfig.workdir ?? DEFAULT_CONFIG_DIR;
362
- try {
363
- const existing = mastra?.getWorkspaceById(workspaceId);
364
- if (existing) {
365
- existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);
366
- const registered = githubTokenInjectors.get(workspaceId);
367
- if (registered) {
368
- registerGithubTokenInjector(requestContext, registered.inject);
369
- registerGithubPatKind(requestContext, registered.patKind);
370
- try {
371
- const pat = await getGithubPat(() => github.integrationStorage, session.orgId, registered.patKind);
372
- if (pat && pat !== registered.ghToken) {
373
- registered.inject(pat);
374
- }
375
- } catch {
376
- }
377
- }
378
- return existing;
379
- }
380
- } catch {
381
- }
382
- const access = await github.versionControl.getRepositoryAccess({
383
- orgId: session.orgId,
384
- repositoryId: repository.id
385
- });
386
- const token = access.authorization?.token;
387
- if (!token) throw new Error("Repository access did not include a bearer token for the Factory session");
388
- let patKind = "default";
389
- if (workItems) {
390
- try {
391
- const address = getFactorySessionAddress(requestContext);
392
- const runBinding = address ? await workItems.findRunBindingBySession(address) : null;
393
- if (runBinding?.role === "review" && runBinding.orgId === session.orgId) patKind = "reviewer";
394
- } catch {
395
- }
396
- }
397
- const ghCliToken = await getGithubPat(() => github.integrationStorage, session.orgId, patKind) ?? token;
398
- const sandbox = await fleet.ensureSandbox(
399
- binding,
400
- { GH_TOKEN: ghCliToken },
401
- void 0,
402
- isLocalSandbox ? { workingDirectory: workdir } : {}
403
- );
404
- await materializeRepo({
405
- row: { id: session.id, sandboxWorkdir: workdir, materializedAt: session.materializedAt },
406
- repoInfo: { repoFullName, defaultBranch: repository.defaultBranch },
407
- sandbox,
408
- token,
409
- storage: storage.sessions
410
- });
411
- await checkoutSessionBranch(sandbox, workdir, {
412
- branch: session.branch,
413
- baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,
414
- token,
415
- repoFullName
416
- });
417
- if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
418
- const injectGithubToken = (freshToken) => {
419
- if (!sandbox.setEnvironmentVariable) {
420
- throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
421
- }
422
- sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
423
- const registered = githubTokenInjectors.get(workspaceId);
424
- if (registered) registered.ghToken = freshToken;
425
- };
426
- githubTokenInjectors.set(workspaceId, { inject: injectGithubToken, patKind, ghToken: ghCliToken });
427
- registerGithubTokenInjector(requestContext, injectGithubToken);
428
- registerGithubPatKind(requestContext, patKind);
429
- const filesystem = new SandboxFilesystem({ sandbox, workdir });
430
- const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
431
- const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
432
- return new Workspace({
433
- id: workspaceId,
434
- name: "Mastra Code Factory Session Workspace",
435
- filesystem,
436
- sandbox,
437
- tools: MASTRACODE_WORKSPACE_TOOLS,
438
- skills: skillPaths,
439
- skillSource: effectiveSkillExtension?.createSource(filesystem, projectSkillPaths) ?? filesystem
440
- });
441
- };
442
- }
443
- var getFactoryWorkspace = createWorkspaceFactory();
444
- export {
445
- checkpointNameForSession,
446
- createWorkspaceFactory,
447
- getFactoryWorkspace
448
- };
75
+ const { sandbox: sandboxConfig, github, fleet, workItems } = options;
76
+ const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
77
+ const githubTokenInjectors = /* @__PURE__ */ new Map();
78
+ const inflightMaterializations = /* @__PURE__ */ new Map();
79
+ return async ({ requestContext, mastra, skillExtension }) => {
80
+ const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
81
+ const ctx = requestContext.get("controller");
82
+ const session = ctx?.resourceId && github ? await github.sourceControlStorage.sessions.getBySessionId(ctx.resourceId) : null;
83
+ if (!session) {
84
+ if (sandboxConfig && !isLocalSandbox) throw new Error("A Factory session ID is required to create a remote sandbox workspace");
85
+ return getDynamicWorkspace({
86
+ requestContext,
87
+ mastra,
88
+ skillExtension: effectiveSkillExtension
89
+ });
90
+ }
91
+ const user = requestContext.get("user");
92
+ const userId = getFactoryAuthUserId(user);
93
+ if (!user?.organizationId || !userId || user.organizationId !== session.orgId || userId !== session.userId) throw new Error(`Factory session ${session.sessionId} is not available to the current user`);
94
+ if (!sandboxConfig || !github || !fleet) throw new Error("GitHub and sandbox providers are required to create a Factory session workspace");
95
+ const storage = github.sourceControlStorage;
96
+ const projectRepository = await storage.projectRepositories.get({
97
+ orgId: session.orgId,
98
+ id: session.projectRepositoryId
99
+ });
100
+ if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);
101
+ const connection = await storage.connections.get({
102
+ orgId: session.orgId,
103
+ id: projectRepository.connectionId
104
+ });
105
+ const repository = await storage.repositories.get({
106
+ orgId: session.orgId,
107
+ id: projectRepository.repositoryId
108
+ });
109
+ if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);
110
+ if (!await storage.installations.get({
111
+ orgId: session.orgId,
112
+ id: connection.installationId
113
+ })) throw new Error(`GitHub installation ${connection.installationId} was not found`);
114
+ const repoFullName = repository.slug;
115
+ const workdir = isLocalSandbox ? fleet.computeLocalSessionWorkdir(repoFullName, session.id) : session.sandboxWorkdir ?? projectRepository.sandboxWorkdir;
116
+ const binding = {
117
+ sandboxId: session.sandboxId,
118
+ checkpointName: checkpointNameForSession(session.id),
119
+ setSandboxId: async (id) => {
120
+ await storage.sessions.setSandbox({
121
+ id: session.id,
122
+ sandboxId: id,
123
+ sandboxWorkdir: workdir
124
+ });
125
+ session.sandboxId = id;
126
+ session.sandboxWorkdir = workdir;
127
+ },
128
+ clear: async () => {
129
+ await storage.sessions.setSandbox({
130
+ id: session.id,
131
+ sandboxId: null,
132
+ sandboxWorkdir: workdir
133
+ });
134
+ session.sandboxId = null;
135
+ }
136
+ };
137
+ const extensionId = effectiveSkillExtension ? `-${effectiveSkillExtension.id}` : "";
138
+ const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectRepository.id}-${session.id}${extensionId}`;
139
+ const configDir = sandboxConfig.workdir ?? DEFAULT_CONFIG_DIR;
140
+ try {
141
+ const existing = mastra?.getWorkspaceById(workspaceId);
142
+ if (existing) {
143
+ existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);
144
+ const registered = githubTokenInjectors.get(workspaceId);
145
+ if (registered) {
146
+ registerGithubTokenInjector(requestContext, registered.inject);
147
+ registerGithubPatKind(requestContext, registered.patKind);
148
+ try {
149
+ const pat = await getGithubPat(() => github.integrationStorage, session.orgId, registered.patKind);
150
+ if (pat && pat !== registered.ghToken) registered.inject(pat);
151
+ } catch {}
152
+ }
153
+ return existing;
154
+ }
155
+ } catch {}
156
+ const materialize = async () => {
157
+ const token = (await github.versionControl.getRepositoryAccess({
158
+ orgId: session.orgId,
159
+ repositoryId: repository.id
160
+ })).authorization?.token;
161
+ if (!token) throw new Error("Repository access did not include a bearer token for the Factory session");
162
+ let patKind = "default";
163
+ if (workItems) try {
164
+ const address = getFactorySessionAddress(requestContext);
165
+ const runBinding = address ? await workItems.findRunBindingBySession(address) : null;
166
+ if (runBinding?.role === "review" && runBinding.orgId === session.orgId) patKind = "reviewer";
167
+ } catch {}
168
+ const ghCliToken = await getGithubPat(() => github.integrationStorage, session.orgId, patKind) ?? token;
169
+ const sandbox = await fleet.ensureSandbox(binding, { GH_TOKEN: ghCliToken }, void 0, isLocalSandbox ? { workingDirectory: workdir } : {});
170
+ await materializeRepo({
171
+ row: {
172
+ id: session.id,
173
+ sandboxWorkdir: workdir,
174
+ materializedAt: session.materializedAt
175
+ },
176
+ repoInfo: {
177
+ repoFullName,
178
+ defaultBranch: repository.defaultBranch
179
+ },
180
+ sandbox,
181
+ token,
182
+ storage: storage.sessions
183
+ });
184
+ await checkoutSessionBranch(sandbox, workdir, {
185
+ branch: session.branch,
186
+ baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,
187
+ token,
188
+ repoFullName
189
+ });
190
+ if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
191
+ const injectGithubToken = (freshToken) => {
192
+ if (!sandbox.setEnvironmentVariable) throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
193
+ sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
194
+ const registered = githubTokenInjectors.get(workspaceId);
195
+ if (registered) registered.ghToken = freshToken;
196
+ };
197
+ githubTokenInjectors.set(workspaceId, {
198
+ inject: injectGithubToken,
199
+ patKind,
200
+ ghToken: ghCliToken
201
+ });
202
+ registerGithubTokenInjector(requestContext, injectGithubToken);
203
+ registerGithubPatKind(requestContext, patKind);
204
+ const filesystem = new SandboxFilesystem({
205
+ sandbox,
206
+ workdir
207
+ });
208
+ const projectSkillPaths = [
209
+ path.join(configDir, "skills"),
210
+ ".claude/skills",
211
+ ".agents/skills"
212
+ ];
213
+ const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
214
+ return new Workspace({
215
+ id: workspaceId,
216
+ name: "Mastra Code Factory Session Workspace",
217
+ filesystem,
218
+ sandbox,
219
+ tools: MASTRACODE_WORKSPACE_TOOLS,
220
+ skills: skillPaths,
221
+ skillSource: effectiveSkillExtension?.createSource(filesystem, projectSkillPaths) ?? filesystem
222
+ });
223
+ };
224
+ const inflight = inflightMaterializations.get(workspaceId);
225
+ if (inflight) {
226
+ const workspace = await inflight;
227
+ const registered = githubTokenInjectors.get(workspaceId);
228
+ if (registered) {
229
+ registerGithubTokenInjector(requestContext, registered.inject);
230
+ registerGithubPatKind(requestContext, registered.patKind);
231
+ }
232
+ return workspace;
233
+ }
234
+ const materialization = materialize();
235
+ inflightMaterializations.set(workspaceId, materialization);
236
+ try {
237
+ return await materialization;
238
+ } finally {
239
+ inflightMaterializations.delete(workspaceId);
240
+ }
241
+ };
242
+ }
243
+ const getFactoryWorkspace = createWorkspaceFactory();
244
+ //#endregion
245
+ export { checkpointNameForSession, createWorkspaceFactory, getFactoryWorkspace };
246
+
449
247
  //# sourceMappingURL=workspace.js.map