@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.
- package/CHANGELOG.md +36 -0
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +401 -314
- package/dist/auth.js.map +1 -1
- package/dist/capabilities/connection.js +1 -1
- package/dist/capabilities/intake.js +1 -1
- package/dist/capabilities/version-control.js +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +441 -39455
- package/dist/factory.js.map +1 -1
- package/dist/index.js +2 -39466
- package/dist/integrations/base.js +1 -1
- package/dist/integrations/github/config.js +36 -22
- package/dist/integrations/github/config.js.map +1 -1
- package/dist/integrations/github/factory-session.js +33 -37
- package/dist/integrations/github/factory-session.js.map +1 -1
- package/dist/integrations/github/integration.js +1037 -3624
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/issue-triage.js +71 -71
- package/dist/integrations/github/issue-triage.js.map +1 -1
- package/dist/integrations/github/pat.js +47 -36
- package/dist/integrations/github/pat.js.map +1 -1
- package/dist/integrations/github/provenance.js +72 -101
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +1272 -1833
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.js +203 -471
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +2 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +401 -326
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +165 -223
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/subscriptions.js +43 -51
- package/dist/integrations/github/subscriptions.js.map +1 -1
- package/dist/integrations/github/token-refresh.js +15 -18
- package/dist/integrations/github/token-refresh.js.map +1 -1
- package/dist/integrations/github/webhook.js +359 -343
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/linear/agent-tools.js +77 -100
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/integration.js +611 -1309
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/routes.js +227 -230
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.js +126 -346
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/linear/storage.js +1 -1
- package/dist/integrations/platform/api-client.js +131 -135
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +275 -573
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +845 -3942
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.js +457 -1309
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/workos/integration.js +80 -85
- package/dist/integrations/workos/integration.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +933 -915
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/custom-provider-source.js +110 -93
- package/dist/routes/custom-provider-source.js.map +1 -1
- package/dist/routes/fs.js +401 -349
- package/dist/routes/fs.js.map +1 -1
- package/dist/routes/intake.js +165 -165
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/oauth.js +447 -25166
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/projects.js +437 -376
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/provider-credentials.js +76 -58
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/routes/route.js +12 -8
- package/dist/routes/route.js.map +1 -1
- package/dist/routes/skills.js +160 -173
- package/dist/routes/skills.js.map +1 -1
- package/dist/routes/surface.js +250 -28618
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/tenant-credentials.js +141 -166
- package/dist/routes/tenant-credentials.js.map +1 -1
- package/dist/routes/work-items.js +654 -862
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.js +23 -35
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/defaults.js +200 -309
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +438 -677
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.js +5 -599
- package/dist/rules/processor.js +344 -596
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.js +21 -18
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.js +168 -157
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.js +50 -85
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.js +183 -454
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.js +33 -29
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.js +266 -281
- package/dist/rules/validation.js.map +1 -1
- package/dist/sandbox/fleet.js +272 -250
- package/dist/sandbox/fleet.js.map +1 -1
- package/dist/sandbox/reattach.js +13 -6
- package/dist/sandbox/reattach.js.map +1 -1
- package/dist/server-error.js +12 -16
- package/dist/server-error.js.map +1 -1
- package/dist/skills/service.js +30 -35
- package/dist/skills/service.js.map +1 -1
- package/dist/spa-static.d.ts.map +1 -1
- package/dist/spa-static.js +91 -64
- package/dist/spa-static.js.map +1 -1
- package/dist/state-signing.js +73 -44
- package/dist/state-signing.js.map +1 -1
- package/dist/storage/domains/audit/agent-audit.js +60 -59
- package/dist/storage/domains/audit/agent-audit.js.map +1 -1
- package/dist/storage/domains/audit/base.js +169 -112
- package/dist/storage/domains/audit/base.js.map +1 -1
- package/dist/storage/domains/audit/domain.js +155 -153
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/dist/storage/domains/credentials/base.js +259 -243
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +138 -143
- package/dist/storage/domains/custom-providers/base.js.map +1 -1
- package/dist/storage/domains/intake/base.js +75 -57
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.js +310 -245
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/memory-settings/base.js +117 -102
- package/dist/storage/domains/memory-settings/base.js.map +1 -1
- package/dist/storage/domains/model-packs/base.js +89 -88
- package/dist/storage/domains/model-packs/base.js.map +1 -1
- package/dist/storage/domains/projects/base.js +97 -92
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/queue-health/base.js +111 -76
- package/dist/storage/domains/queue-health/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +9 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +777 -702
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +322 -344
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +1266 -1132
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/storage/domains/work-items/metrics.js +202 -184
- package/dist/storage/domains/work-items/metrics.js.map +1 -1
- package/dist/timing.d.ts +15 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +34 -0
- package/dist/timing.js.map +1 -0
- package/dist/workspace.d.ts +2 -3
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +236 -438
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-review/SKILL.md +9 -2
- package/package.json +9 -9
- package/dist/capabilities/connection.js.map +0 -1
- package/dist/capabilities/intake.js.map +0 -1
- package/dist/capabilities/version-control.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integrations/base.js.map +0 -1
- package/dist/integrations/linear/storage.js.map +0 -1
- package/dist/rules/index.js.map +0 -1
package/dist/workspace.js
CHANGED
|
@@ -1,449 +1,247 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
266
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|