@mastra/code-sdk 1.0.3-alpha.1 → 1.1.0-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 +38 -0
- package/dist/acp/agent.js +155 -167
- package/dist/acp/agent.js.map +1 -1
- package/dist/acp/event-mapper.js +193 -168
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/acp/index.js +60 -49
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/server.js +31 -30
- package/dist/acp/server.js.map +1 -1
- package/dist/acp.js +1 -4
- package/dist/agents/credential-resolver.js +40 -29
- package/dist/agents/credential-resolver.js.map +1 -1
- package/dist/agents/custom-provider-source.js +16 -9
- package/dist/agents/custom-provider-source.js.map +1 -1
- package/dist/agents/instructions.js +27 -36
- package/dist/agents/instructions.js.map +1 -1
- package/dist/agents/mastracode-gateway.js +355 -428
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.js +87 -78
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/model.js +114 -119
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/modes/build.js +12 -13
- package/dist/agents/modes/build.js.map +1 -1
- package/dist/agents/modes/explore.js +14 -13
- package/dist/agents/modes/explore.js.map +1 -1
- package/dist/agents/modes/plan.js +14 -15
- package/dist/agents/modes/plan.js.map +1 -1
- package/dist/agents/prompts/agent-instructions.js +78 -69
- package/dist/agents/prompts/agent-instructions.js.map +1 -1
- package/dist/agents/prompts/build.js +19 -16
- package/dist/agents/prompts/build.js.map +1 -1
- package/dist/agents/prompts/fast.js +8 -4
- package/dist/agents/prompts/fast.js.map +1 -1
- package/dist/agents/prompts/index.js +48 -47
- package/dist/agents/prompts/index.js.map +1 -1
- package/dist/agents/prompts/model.js +6 -5
- package/dist/agents/prompts/model.js.map +1 -1
- package/dist/agents/prompts/plan.js +20 -16
- package/dist/agents/prompts/plan.js.map +1 -1
- package/dist/agents/prompts/tool-guidance.js +77 -109
- package/dist/agents/prompts/tool-guidance.js.map +1 -1
- package/dist/agents/sandbox-filesystem.js +245 -259
- package/dist/agents/sandbox-filesystem.js.map +1 -1
- package/dist/agents/sandbox-reattach.js +9 -11
- package/dist/agents/sandbox-reattach.js.map +1 -1
- package/dist/agents/thread-caveman-state.js +92 -81
- package/dist/agents/thread-caveman-state.js.map +1 -1
- package/dist/agents/tool-availability.js +62 -80
- package/dist/agents/tool-availability.js.map +1 -1
- package/dist/agents/tools.js +99 -139
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.js +232 -225
- package/dist/agents/workspace.js.map +1 -1
- package/dist/analytics.js +139 -128
- package/dist/analytics.js.map +1 -1
- package/dist/auth/authorization-input.js +39 -25
- package/dist/auth/authorization-input.js.map +1 -1
- package/dist/auth/device-code.js +112 -80
- package/dist/auth/device-code.js.map +1 -1
- package/dist/auth/index.js +2 -9
- package/dist/auth/pkce.js +27 -16
- package/dist/auth/pkce.js.map +1 -1
- package/dist/auth/providers/anthropic.js +107 -93
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.js +304 -293
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/openai-codex.js +442 -460
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.js +179 -145
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/auth/storage.js +182 -175
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.js +1 -1
- package/dist/clipboard/index.js +206 -172
- package/dist/clipboard/index.js.map +1 -1
- package/dist/constants.js +11 -16
- package/dist/constants.js.map +1 -1
- package/dist/error-classification.js +19 -11
- package/dist/error-classification.js.map +1 -1
- package/dist/evals/context-builder.js +145 -104
- package/dist/evals/context-builder.js.map +1 -1
- package/dist/evals/index.js +4 -7
- package/dist/evals/scorers/classify-command.js +62 -41
- package/dist/evals/scorers/classify-command.js.map +1 -1
- package/dist/evals/scorers/efficiency.js +239 -181
- package/dist/evals/scorers/efficiency.js.map +1 -1
- package/dist/evals/scorers/extract-tools.js +51 -47
- package/dist/evals/scorers/extract-tools.js.map +1 -1
- package/dist/evals/scorers/index.js +1 -5
- package/dist/evals/scorers/outcome.js +321 -212
- package/dist/evals/scorers/outcome.js.map +1 -1
- package/dist/goal-manager.js +266 -261
- package/dist/goal-manager.js.map +1 -1
- package/dist/goal-signal.js +14 -13
- package/dist/goal-signal.js.map +1 -1
- package/dist/headless/cli.js +153 -154
- package/dist/headless/cli.js.map +1 -1
- package/dist/headless/flags.js +180 -178
- package/dist/headless/flags.js.map +1 -1
- package/dist/headless/format.js +72 -71
- package/dist/headless/format.js.map +1 -1
- package/dist/headless/index.js +5 -31
- package/dist/headless/policy.js +35 -31
- package/dist/headless/policy.js.map +1 -1
- package/dist/headless/run-mc.js +332 -334
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/headless/types.js +16 -7
- package/dist/headless/types.js.map +1 -1
- package/dist/hooks/config.js +56 -61
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/executor.js +118 -116
- package/dist/hooks/executor.js.map +1 -1
- package/dist/hooks/index.js +4 -15
- package/dist/hooks/manager.js +221 -280
- package/dist/hooks/manager.js.map +1 -1
- package/dist/hooks/types.js +5 -4
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +632 -712
- package/dist/index.js.map +1 -1
- package/dist/ipc/ipc-reporter.js +19 -22
- package/dist/ipc/ipc-reporter.js.map +1 -1
- package/dist/lsp/client.js +248 -306
- package/dist/lsp/client.js.map +1 -1
- package/dist/lsp/index.js +1 -6
- package/dist/lsp/language.js +39 -43
- package/dist/lsp/language.js.map +1 -1
- package/dist/lsp/manager.js +78 -93
- package/dist/lsp/manager.js.map +1 -1
- package/dist/lsp/server.js +151 -120
- package/dist/lsp/server.js.map +1 -1
- package/dist/lsp/workspace.js +50 -47
- package/dist/lsp/workspace.js.map +1 -1
- package/dist/mcp/config.js +210 -199
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/index.js +2 -9
- package/dist/mcp/manager.js +482 -475
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.js +1 -1
- package/dist/onboarding/custom-packs.js +14 -17
- package/dist/onboarding/custom-packs.js.map +1 -1
- package/dist/onboarding/custom-providers.js +22 -20
- package/dist/onboarding/custom-providers.js.map +1 -1
- package/dist/onboarding/index.d.ts +1 -1
- package/dist/onboarding/index.d.ts.map +1 -1
- package/dist/onboarding/index.js +3 -21
- package/dist/onboarding/om-settings.d.ts +5 -0
- package/dist/onboarding/om-settings.d.ts.map +1 -1
- package/dist/onboarding/om-settings.js +41 -20
- package/dist/onboarding/om-settings.js.map +1 -1
- package/dist/onboarding/packs.d.ts +4 -6
- package/dist/onboarding/packs.d.ts.map +1 -1
- package/dist/onboarding/packs.js +154 -105
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/onboarding/settings.js +575 -517
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/permissions.js +112 -99
- package/dist/permissions.js.map +1 -1
- package/dist/plugin.js +21 -23
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/dependencies.js +64 -87
- package/dist/plugins/dependencies.js.map +1 -1
- package/dist/plugins/install.js +159 -169
- package/dist/plugins/install.js.map +1 -1
- package/dist/plugins/loader.js +170 -185
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/manager.js +374 -380
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/manifest.js +38 -63
- package/dist/plugins/manifest.js.map +1 -1
- package/dist/plugins/package-link.js +48 -64
- package/dist/plugins/package-link.js.map +1 -1
- package/dist/plugins/paths.js +18 -19
- package/dist/plugins/paths.js.map +1 -1
- package/dist/plugins/registry.js +97 -81
- package/dist/plugins/registry.js.map +1 -1
- package/dist/plugins/scaffold.js +62 -98
- package/dist/plugins/scaffold.js.map +1 -1
- package/dist/plugins/types.js +1 -1
- package/dist/processors/plan-rejection-abort.js +45 -44
- package/dist/processors/plan-rejection-abort.js.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.js +134 -125
- package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
- package/dist/providers/amazon-bedrock.js +68 -47
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/claude-max.js +153 -133
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.js +232 -230
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/openai-codex.js +271 -260
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/xai.js +57 -50
- package/dist/providers/xai.js.map +1 -1
- package/dist/schema.js +91 -89
- package/dist/schema.js.map +1 -1
- package/dist/theme-palette.js +18 -14
- package/dist/theme-palette.js.map +1 -1
- package/dist/tool-names.js +46 -37
- package/dist/tool-names.js.map +1 -1
- package/dist/tools/index.js +2 -8
- package/dist/tools/request-sandbox-access.js +71 -80
- package/dist/tools/request-sandbox-access.js.map +1 -1
- package/dist/tools/utils.js +26 -16
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/web-search.js +51 -55
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/binaries.js +65 -61
- package/dist/utils/binaries.js.map +1 -1
- package/dist/utils/debug-log.js +51 -47
- package/dist/utils/debug-log.js.map +1 -1
- package/dist/utils/errors.js +157 -192
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/gateway-sync.js +49 -37
- package/dist/utils/gateway-sync.js.map +1 -1
- package/dist/utils/path-security.js +7 -6
- package/dist/utils/path-security.js.map +1 -1
- package/dist/utils/plan-diff.js +58 -39
- package/dist/utils/plan-diff.js.map +1 -1
- package/dist/utils/plans.js +92 -74
- package/dist/utils/plans.js.map +1 -1
- package/dist/utils/project.js +318 -256
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/signals-pubsub.js +117 -99
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/slash-command-loader.js +118 -154
- package/dist/utils/slash-command-loader.js.map +1 -1
- package/dist/utils/slash-command-processor.js +92 -83
- package/dist/utils/slash-command-processor.js.map +1 -1
- package/dist/utils/stdin-pipe.js +87 -63
- package/dist/utils/stdin-pipe.js.map +1 -1
- package/dist/utils/storage-factory.js +104 -84
- package/dist/utils/storage-factory.js.map +1 -1
- package/dist/utils/storage-maintenance.js +249 -211
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/dist/utils/thread-lock.js +96 -92
- package/dist/utils/thread-lock.js.map +1 -1
- package/dist/utils/token-estimator.js +12 -12
- package/dist/utils/token-estimator.js.map +1 -1
- package/dist/utils/update-check.d.ts +1 -1
- package/dist/utils/update-check.js +341 -259
- package/dist/utils/update-check.js.map +1 -1
- package/dist/voice/stt-registry.js +131 -113
- package/dist/voice/stt-registry.js.map +1 -1
- package/package.json +12 -12
- package/dist/acp.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/evals/index.js.map +0 -1
- package/dist/evals/scorers/index.js.map +0 -1
- package/dist/headless/index.js.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/lsp/index.js.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/types.js.map +0 -1
- package/dist/onboarding/index.js.map +0 -1
- package/dist/plugins/types.js.map +0 -1
- package/dist/tools/index.js.map +0 -1
|
@@ -1,268 +1,254 @@
|
|
|
1
|
-
import { posix
|
|
1
|
+
import { posix } from "path";
|
|
2
|
+
//#region src/agents/sandbox-filesystem.ts
|
|
3
|
+
/**
|
|
4
|
+
* SandboxFilesystem
|
|
5
|
+
*
|
|
6
|
+
* A `WorkspaceFilesystem` that stores files inside a remote `MastraSandbox`
|
|
7
|
+
* (e.g. a Railway VM) rather than on the server host. File operations are
|
|
8
|
+
* implemented by shelling out through the sandbox's `executeCommand`, so the
|
|
9
|
+
* agent's file tools and command tools share one VM and one view of the repo.
|
|
10
|
+
*
|
|
11
|
+
* Paths are workspace-relative (`/src/foo.ts`) and resolve under the sandbox
|
|
12
|
+
* working directory (`basePath`). A traversal guard rejects any path that
|
|
13
|
+
* escapes the workdir, mirroring `LocalFilesystem`'s contained mode.
|
|
14
|
+
*
|
|
15
|
+
* Reads/writes use base64 over the wire so binary content survives the shell.
|
|
16
|
+
*/
|
|
17
|
+
/** Default per-command deadline so a hung sandbox can't block file tools forever. */
|
|
2
18
|
const COMMAND_TIMEOUT_MS = 3e4;
|
|
19
|
+
/** Single-quote a string for safe POSIX shell interpolation. */
|
|
3
20
|
function shellQuote(value) {
|
|
4
|
-
|
|
21
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
5
22
|
}
|
|
6
23
|
function isFileContentString(content) {
|
|
7
|
-
|
|
24
|
+
return typeof content === "string";
|
|
8
25
|
}
|
|
9
26
|
function toBuffer(content) {
|
|
10
|
-
|
|
11
|
-
|
|
27
|
+
if (isFileContentString(content)) return Buffer.from(content, "utf8");
|
|
28
|
+
return Buffer.from(content);
|
|
12
29
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const ctime = Number(ctimeStr);
|
|
234
|
-
return {
|
|
235
|
-
name: posixPath.basename(abs),
|
|
236
|
-
path: `/${posixPath.relative(this.basePath, abs)}`,
|
|
237
|
-
type,
|
|
238
|
-
size: type === "directory" ? 0 : size,
|
|
239
|
-
modifiedAt: new Date(mtime * 1e3),
|
|
240
|
-
createdAt: new Date((ctime > 0 ? ctime : mtime) * 1e3)
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
// ── Lifecycle ──────────────────────────────────────────────────────────
|
|
244
|
-
async init() {
|
|
245
|
-
await this.execOk(`mkdir -p ${shellQuote(this.basePath)}`, "init workdir");
|
|
246
|
-
}
|
|
247
|
-
async destroy() {
|
|
248
|
-
}
|
|
249
|
-
async isReady() {
|
|
250
|
-
const result = await this.exec(`test -d ${shellQuote(this.basePath)}`);
|
|
251
|
-
return result.exitCode === 0;
|
|
252
|
-
}
|
|
253
|
-
getInfo() {
|
|
254
|
-
return {
|
|
255
|
-
id: this.id,
|
|
256
|
-
name: this.name,
|
|
257
|
-
provider: this.provider,
|
|
258
|
-
metadata: { basePath: this.basePath, sandboxId: this.sandbox.id }
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
getInstructions() {
|
|
262
|
-
return `Files are stored in a remote sandbox at ${this.basePath}. Use absolute workspace paths like /src/index.ts. All reads, writes and commands run inside the same sandbox.`;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
export {
|
|
266
|
-
SandboxFilesystem
|
|
30
|
+
var SandboxFilesystem = class {
|
|
31
|
+
id;
|
|
32
|
+
name = "SandboxFilesystem";
|
|
33
|
+
provider = "sandbox";
|
|
34
|
+
basePath;
|
|
35
|
+
status = "ready";
|
|
36
|
+
sandbox;
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.sandbox = options.sandbox;
|
|
39
|
+
this.basePath = options.workdir;
|
|
40
|
+
this.id = options.id ?? `sandbox-fs:${options.sandbox.id}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a workspace path to an absolute path inside the sandbox, enforcing
|
|
44
|
+
* that it stays within the workdir.
|
|
45
|
+
*/
|
|
46
|
+
resolve(inputPath) {
|
|
47
|
+
const rel = inputPath.startsWith("/") ? inputPath.slice(1) : inputPath;
|
|
48
|
+
const resolved = posix.normalize(posix.join(this.basePath, rel));
|
|
49
|
+
const root = posix.normalize(this.basePath);
|
|
50
|
+
if (resolved !== root && !resolved.startsWith(`${root}/`)) throw new Error(`Path escapes workspace root: ${inputPath}`);
|
|
51
|
+
return resolved;
|
|
52
|
+
}
|
|
53
|
+
resolveAbsolutePath(inputPath) {
|
|
54
|
+
return this.resolve(inputPath);
|
|
55
|
+
}
|
|
56
|
+
async exec(script) {
|
|
57
|
+
return this.sandbox.executeCommand("sh", ["-c", script], { timeout: COMMAND_TIMEOUT_MS });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Lexical guard catches `..` traversal, but a symlink inside the workdir can
|
|
61
|
+
* still point outside it. After resolving a path that refers to an existing
|
|
62
|
+
* entry, verify its realpath is still contained in the workdir.
|
|
63
|
+
*/
|
|
64
|
+
async assertContainedRealpath(abs, inputPath) {
|
|
65
|
+
const result = await this.exec(`readlink -f -- ${shellQuote(abs)} 2>/dev/null`);
|
|
66
|
+
const real = result.stdout.trim();
|
|
67
|
+
if (result.exitCode !== 0 || !real) return;
|
|
68
|
+
const root = posix.normalize(this.basePath);
|
|
69
|
+
if (real !== root && !real.startsWith(`${root}/`)) throw new Error(`Path escapes workspace root (symlink): ${inputPath}`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Guard for write destinations. The lexical guard catches `..`, but a symlink
|
|
73
|
+
* inside the workdir can redirect a write outside it. For an existing target
|
|
74
|
+
* we check its realpath; for a not-yet-existing target we check the realpath
|
|
75
|
+
* of its nearest existing ancestor directory, since a symlinked parent is the
|
|
76
|
+
* escape vector (e.g. `link -> /etc` then writing `link/passwd`).
|
|
77
|
+
*/
|
|
78
|
+
async assertContainedDest(abs, inputPath) {
|
|
79
|
+
await this.assertContainedRealpath(abs, inputPath);
|
|
80
|
+
const parent = posix.dirname(abs);
|
|
81
|
+
if (parent && parent !== abs) await this.assertContainedRealpath(parent, inputPath);
|
|
82
|
+
}
|
|
83
|
+
async execOk(script, context) {
|
|
84
|
+
const result = await this.exec(script);
|
|
85
|
+
if (result.exitCode !== 0) throw new Error(`${context} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`);
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
async readFile(path, options) {
|
|
89
|
+
const abs = this.resolve(path);
|
|
90
|
+
await this.assertContainedRealpath(abs, path);
|
|
91
|
+
const result = await this.exec(`base64 < ${shellQuote(abs)}`);
|
|
92
|
+
if (result.exitCode !== 0) throw new Error(`File not found: ${path}`);
|
|
93
|
+
const buffer = Buffer.from(result.stdout.replace(/\s/g, ""), "base64");
|
|
94
|
+
if (options?.encoding) return buffer.toString(options.encoding);
|
|
95
|
+
return buffer;
|
|
96
|
+
}
|
|
97
|
+
async writeFile(path, content, options) {
|
|
98
|
+
const abs = this.resolve(path);
|
|
99
|
+
await this.assertContainedDest(abs, path);
|
|
100
|
+
const b64 = toBuffer(content).toString("base64");
|
|
101
|
+
const dir = posix.dirname(abs);
|
|
102
|
+
const mkdir = options?.recursive === false ? "" : `mkdir -p ${shellQuote(dir)} && `;
|
|
103
|
+
if (options?.overwrite === false) {
|
|
104
|
+
if (await this.exists(path)) throw new Error(`File already exists: ${path}`);
|
|
105
|
+
}
|
|
106
|
+
await this.execOk(`${mkdir}printf %s ${shellQuote(b64)} | base64 -d > ${shellQuote(abs)}`, `writeFile ${path}`);
|
|
107
|
+
}
|
|
108
|
+
async appendFile(path, content) {
|
|
109
|
+
const abs = this.resolve(path);
|
|
110
|
+
await this.assertContainedDest(abs, path);
|
|
111
|
+
const b64 = toBuffer(content).toString("base64");
|
|
112
|
+
await this.execOk(`mkdir -p ${shellQuote(posix.dirname(abs))} && printf %s ${shellQuote(b64)} | base64 -d >> ${shellQuote(abs)}`, `appendFile ${path}`);
|
|
113
|
+
}
|
|
114
|
+
async deleteFile(path, options) {
|
|
115
|
+
const abs = this.resolve(path);
|
|
116
|
+
const force = options?.force ? "-f " : "";
|
|
117
|
+
if ((await this.exec(`rm ${force}${shellQuote(abs)}`)).exitCode !== 0 && !options?.force) throw new Error(`File not found: ${path}`);
|
|
118
|
+
}
|
|
119
|
+
async copyFile(src, dest, options) {
|
|
120
|
+
const srcAbs = this.resolve(src);
|
|
121
|
+
const destAbs = this.resolve(dest);
|
|
122
|
+
await this.assertContainedRealpath(srcAbs, src);
|
|
123
|
+
await this.assertContainedDest(destAbs, dest);
|
|
124
|
+
const recursive = options?.recursive ? "-r " : "";
|
|
125
|
+
if (options?.overwrite === false) {
|
|
126
|
+
if (await this.exists(dest)) throw new Error(`Destination exists: ${dest}`);
|
|
127
|
+
}
|
|
128
|
+
await this.execOk(`cp ${recursive}${shellQuote(srcAbs)} ${shellQuote(destAbs)}`, `copyFile ${src} -> ${dest}`);
|
|
129
|
+
}
|
|
130
|
+
async moveFile(src, dest, options) {
|
|
131
|
+
const srcAbs = this.resolve(src);
|
|
132
|
+
const destAbs = this.resolve(dest);
|
|
133
|
+
await this.assertContainedRealpath(srcAbs, src);
|
|
134
|
+
await this.assertContainedDest(destAbs, dest);
|
|
135
|
+
if (options?.overwrite === false) {
|
|
136
|
+
if (await this.exists(dest)) throw new Error(`Destination exists: ${dest}`);
|
|
137
|
+
}
|
|
138
|
+
await this.execOk(`mv ${shellQuote(srcAbs)} ${shellQuote(destAbs)}`, `moveFile ${src} -> ${dest}`);
|
|
139
|
+
}
|
|
140
|
+
async mkdir(path, options) {
|
|
141
|
+
const abs = this.resolve(path);
|
|
142
|
+
await this.assertContainedDest(abs, path);
|
|
143
|
+
const flag = options?.recursive === false ? "" : "-p ";
|
|
144
|
+
await this.execOk(`mkdir ${flag}${shellQuote(abs)}`, `mkdir ${path}`);
|
|
145
|
+
}
|
|
146
|
+
async rmdir(path, options) {
|
|
147
|
+
const abs = this.resolve(path);
|
|
148
|
+
if (options?.recursive) {
|
|
149
|
+
const force = options?.force ? "-f " : "";
|
|
150
|
+
await this.execOk(`rm -r ${force}${shellQuote(abs)}`, `rmdir ${path}`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if ((await this.exec(`rmdir ${shellQuote(abs)}`)).exitCode !== 0 && !options?.force) throw new Error(`Directory not empty or not found: ${path}`);
|
|
154
|
+
}
|
|
155
|
+
async readdir(path, options) {
|
|
156
|
+
const abs = this.resolve(path);
|
|
157
|
+
await this.assertContainedRealpath(abs, path);
|
|
158
|
+
if (options?.recursive) {
|
|
159
|
+
const result = await this.exec(`find ${shellQuote(abs)} -mindepth 1 ${options.maxDepth ? `-maxdepth ${Number(options.maxDepth)} ` : ""}-printf '%y\\t%p\\n' 2>/dev/null`);
|
|
160
|
+
if (result.exitCode !== 0) throw new Error(`Directory not found: ${path}`);
|
|
161
|
+
return this.parseFindOutput(result.stdout, abs, options);
|
|
162
|
+
}
|
|
163
|
+
const result = await this.exec(`cd ${shellQuote(abs)} 2>/dev/null && for f in * .[!.]*; do [ -e "$f" ] || continue; if [ -d "$f" ]; then echo "d\t$f"; else echo "f\t$f"; fi; done`);
|
|
164
|
+
if (result.exitCode !== 0) throw new Error(`Directory not found: ${path}`);
|
|
165
|
+
return this.parseListOutput(result.stdout, options);
|
|
166
|
+
}
|
|
167
|
+
parseListOutput(stdout, options) {
|
|
168
|
+
const entries = [];
|
|
169
|
+
for (const line of stdout.split("\n")) {
|
|
170
|
+
if (!line) continue;
|
|
171
|
+
const tab = line.indexOf(" ");
|
|
172
|
+
if (tab < 0) continue;
|
|
173
|
+
const type = line.slice(0, tab) === "d" ? "directory" : "file";
|
|
174
|
+
const name = line.slice(tab + 1);
|
|
175
|
+
if (!name || name === "." || name === "..") continue;
|
|
176
|
+
if (type === "file" && !this.matchesExtension(name, options?.extension)) continue;
|
|
177
|
+
entries.push({
|
|
178
|
+
name,
|
|
179
|
+
type
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return entries;
|
|
183
|
+
}
|
|
184
|
+
parseFindOutput(stdout, base, options) {
|
|
185
|
+
const entries = [];
|
|
186
|
+
for (const line of stdout.split("\n")) {
|
|
187
|
+
if (!line) continue;
|
|
188
|
+
const tab = line.indexOf(" ");
|
|
189
|
+
if (tab < 0) continue;
|
|
190
|
+
const type = line.slice(0, tab) === "d" ? "directory" : "file";
|
|
191
|
+
const fullPath = line.slice(tab + 1);
|
|
192
|
+
const name = posix.relative(base, fullPath);
|
|
193
|
+
if (!name) continue;
|
|
194
|
+
if (type === "file" && !this.matchesExtension(name, options?.extension)) continue;
|
|
195
|
+
entries.push({
|
|
196
|
+
name,
|
|
197
|
+
type
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return entries;
|
|
201
|
+
}
|
|
202
|
+
matchesExtension(name, extension) {
|
|
203
|
+
if (!extension) return true;
|
|
204
|
+
return (Array.isArray(extension) ? extension : [extension]).some((ext) => name.endsWith(ext));
|
|
205
|
+
}
|
|
206
|
+
async exists(path) {
|
|
207
|
+
const abs = this.resolve(path);
|
|
208
|
+
return (await this.exec(`test -e ${shellQuote(abs)}`)).exitCode === 0;
|
|
209
|
+
}
|
|
210
|
+
async stat(path) {
|
|
211
|
+
const abs = this.resolve(path);
|
|
212
|
+
await this.assertContainedRealpath(abs, path);
|
|
213
|
+
const result = await this.exec(`stat -c '%F\\t%s\\t%Y\\t%W' ${shellQuote(abs)}`);
|
|
214
|
+
if (result.exitCode !== 0) throw new Error(`Path not found: ${path}`);
|
|
215
|
+
const [kind, sizeStr, mtimeStr, ctimeStr] = result.stdout.trim().split(" ");
|
|
216
|
+
const type = kind && kind.includes("directory") ? "directory" : "file";
|
|
217
|
+
const size = Number(sizeStr) || 0;
|
|
218
|
+
const mtime = Number(mtimeStr) || 0;
|
|
219
|
+
const ctime = Number(ctimeStr);
|
|
220
|
+
return {
|
|
221
|
+
name: posix.basename(abs),
|
|
222
|
+
path: `/${posix.relative(this.basePath, abs)}`,
|
|
223
|
+
type,
|
|
224
|
+
size: type === "directory" ? 0 : size,
|
|
225
|
+
modifiedAt: /* @__PURE__ */ new Date(mtime * 1e3),
|
|
226
|
+
createdAt: /* @__PURE__ */ new Date((ctime > 0 ? ctime : mtime) * 1e3)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
async init() {
|
|
230
|
+
await this.execOk(`mkdir -p ${shellQuote(this.basePath)}`, "init workdir");
|
|
231
|
+
}
|
|
232
|
+
async destroy() {}
|
|
233
|
+
async isReady() {
|
|
234
|
+
return (await this.exec(`test -d ${shellQuote(this.basePath)}`)).exitCode === 0;
|
|
235
|
+
}
|
|
236
|
+
getInfo() {
|
|
237
|
+
return {
|
|
238
|
+
id: this.id,
|
|
239
|
+
name: this.name,
|
|
240
|
+
provider: this.provider,
|
|
241
|
+
metadata: {
|
|
242
|
+
basePath: this.basePath,
|
|
243
|
+
sandboxId: this.sandbox.id
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
getInstructions() {
|
|
248
|
+
return `Files are stored in a remote sandbox at ${this.basePath}. Use absolute workspace paths like /src/index.ts. All reads, writes and commands run inside the same sandbox.`;
|
|
249
|
+
}
|
|
267
250
|
};
|
|
251
|
+
//#endregion
|
|
252
|
+
export { SandboxFilesystem };
|
|
253
|
+
|
|
268
254
|
//# sourceMappingURL=sandbox-filesystem.js.map
|