@cloudflare/sandbox 0.13.0-next.649.1 → 0.13.0-next.681.1

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 (65) hide show
  1. package/Dockerfile +6 -11
  2. package/README.md +2 -2
  3. package/dist/bridge/index.d.ts.map +1 -1
  4. package/dist/bridge/index.js +68 -49
  5. package/dist/bridge/index.js.map +1 -1
  6. package/dist/codes-y-U76vnQ.d.ts +79 -0
  7. package/dist/codes-y-U76vnQ.d.ts.map +1 -0
  8. package/dist/contexts-DY1LHU1v.d.ts +327 -0
  9. package/dist/contexts-DY1LHU1v.d.ts.map +1 -0
  10. package/dist/{dist-mAH_7Ui7.js → dist-BStBkGIC.js} +50 -93
  11. package/dist/dist-BStBkGIC.js.map +1 -0
  12. package/dist/errors/index.d.ts +4 -0
  13. package/dist/errors/index.js +4 -0
  14. package/dist/errors-ewgSNicb.js +741 -0
  15. package/dist/errors-ewgSNicb.js.map +1 -0
  16. package/dist/{errors-CpoDEfUZ.js → errors-k3B8orjH.js} +41 -4
  17. package/dist/errors-k3B8orjH.js.map +1 -0
  18. package/dist/extensions/index.d.ts +2 -0
  19. package/dist/extensions/index.js +4 -0
  20. package/dist/extensions-CepYdzro.js +191 -0
  21. package/dist/extensions-CepYdzro.js.map +1 -0
  22. package/dist/git/index.d.ts +74 -0
  23. package/dist/git/index.d.ts.map +1 -0
  24. package/dist/git/index.js +345 -0
  25. package/dist/git/index.js.map +1 -0
  26. package/dist/index-B7QgIs0N.d.ts +1841 -0
  27. package/dist/index-B7QgIs0N.d.ts.map +1 -0
  28. package/dist/index-Dy6u9r60.d.ts +390 -0
  29. package/dist/index-Dy6u9r60.d.ts.map +1 -0
  30. package/dist/index.d.ts +27 -189
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +12 -43
  33. package/dist/index.js.map +1 -1
  34. package/dist/interpreter/index.d.ts +309 -0
  35. package/dist/interpreter/index.d.ts.map +1 -0
  36. package/dist/interpreter/index.js +264 -0
  37. package/dist/interpreter/index.js.map +1 -0
  38. package/dist/openai/index.d.ts +3 -2
  39. package/dist/openai/index.d.ts.map +1 -1
  40. package/dist/openai/index.js +2 -2
  41. package/dist/openai/index.js.map +1 -1
  42. package/dist/opencode/index.d.ts +137 -160
  43. package/dist/opencode/index.d.ts.map +1 -1
  44. package/dist/opencode/index.js +269 -159
  45. package/dist/opencode/index.js.map +1 -1
  46. package/dist/sandbox-DHNO89IF.d.ts +828 -0
  47. package/dist/sandbox-DHNO89IF.d.ts.map +1 -0
  48. package/dist/{sandbox-B-FLGbkP.js → sandbox-boKWPIcd.js} +8214 -7267
  49. package/dist/sandbox-boKWPIcd.js.map +1 -0
  50. package/dist/sidecar/index.d.ts +77 -0
  51. package/dist/sidecar/index.d.ts.map +1 -0
  52. package/dist/sidecar/index.js +201 -0
  53. package/dist/sidecar/index.js.map +1 -0
  54. package/dist/xterm/index.d.ts +6 -6
  55. package/dist/xterm/index.d.ts.map +1 -1
  56. package/dist/xterm/index.js +6 -6
  57. package/dist/xterm/index.js.map +1 -1
  58. package/package.json +36 -3
  59. package/dist/contexts-DPFhc2nR.d.ts +0 -238
  60. package/dist/contexts-DPFhc2nR.d.ts.map +0 -1
  61. package/dist/dist-mAH_7Ui7.js.map +0 -1
  62. package/dist/errors-CpoDEfUZ.js.map +0 -1
  63. package/dist/sandbox-44kEJjAc.d.ts +0 -2652
  64. package/dist/sandbox-44kEJjAc.d.ts.map +0 -1
  65. package/dist/sandbox-B-FLGbkP.js.map +0 -1
@@ -0,0 +1,345 @@
1
+ import { d as DEFAULT_GIT_CLONE_TIMEOUT_MS, f as extractRepoName, p as redactCommand } from "../dist-BStBkGIC.js";
2
+ import { i as ErrorCode, r as getHttpStatus } from "../errors-k3B8orjH.js";
3
+ import { t as createErrorFromResponse } from "../errors-ewgSNicb.js";
4
+ import { t as SandboxExtension } from "../extensions-CepYdzro.js";
5
+
6
+ //#region ../../extensions/git/src/manager.ts
7
+ /**
8
+ * Pure git business logic for the git extension.
9
+ *
10
+ * No I/O — just command-argument building, output parsing, validation, and
11
+ * error-code classification. Ported from the former container-side
12
+ * `GitManager` so the extension can drive everything through `exec`.
13
+ */
14
+ const GIT_CLONE_KILL_GRACE_SECONDS = 5;
15
+ /** Format a millisecond timeout as a `timeout(1)`-friendly seconds string. */
16
+ function gitCloneTimeoutSeconds(timeoutMs) {
17
+ const timeoutSeconds = timeoutMs / 1e3;
18
+ return Number.isInteger(timeoutSeconds) ? String(timeoutSeconds) : timeoutSeconds.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
19
+ }
20
+ /**
21
+ * Generate the default target directory for a clone: `/workspace/<repoName>`.
22
+ */
23
+ function generateTargetDirectory(repoUrl) {
24
+ return `/workspace/${extractRepoName(repoUrl)}`;
25
+ }
26
+ /**
27
+ * Build git clone command arguments.
28
+ *
29
+ * Wraps the command with `timeout -k 5 <seconds>` to enforce a wall-clock
30
+ * limit, and configures git's own stalled-transfer detection via
31
+ * `http.lowSpeedLimit` and `http.lowSpeedTime`.
32
+ */
33
+ function buildCloneArgs(repoUrl, targetDir, options = {}) {
34
+ const timeoutSeconds = gitCloneTimeoutSeconds(options.cloneTimeoutMs ?? DEFAULT_GIT_CLONE_TIMEOUT_MS);
35
+ const args = [
36
+ "timeout",
37
+ "-k",
38
+ String(GIT_CLONE_KILL_GRACE_SECONDS),
39
+ String(timeoutSeconds),
40
+ "git",
41
+ "-c",
42
+ "http.lowSpeedLimit=1024",
43
+ "-c",
44
+ "http.lowSpeedTime=30",
45
+ "clone",
46
+ "--filter=blob:none"
47
+ ];
48
+ if (options.branch) args.push("--branch", options.branch);
49
+ if (options.depth !== void 0) args.push("--depth", String(options.depth));
50
+ args.push(repoUrl, targetDir);
51
+ return args;
52
+ }
53
+ function buildCheckoutArgs(branch) {
54
+ return [
55
+ "git",
56
+ "checkout",
57
+ branch
58
+ ];
59
+ }
60
+ function buildGetCurrentBranchArgs() {
61
+ return [
62
+ "git",
63
+ "branch",
64
+ "--show-current"
65
+ ];
66
+ }
67
+ function buildListBranchesArgs() {
68
+ return [
69
+ "git",
70
+ "branch",
71
+ "-a"
72
+ ];
73
+ }
74
+ /**
75
+ * Parse `git branch -a` output into a deduplicated array of branch names.
76
+ * Strips the current-branch marker, the `remotes/origin/` prefix, and the
77
+ * `HEAD` reference.
78
+ */
79
+ function parseBranchList(stdout) {
80
+ return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => line.replace(/^\*\s*/, "")).map((line) => line.replace(/^remotes\/origin\//, "")).filter((branch) => branch !== "HEAD" && !branch.includes("->")).filter((branch, index, array) => array.indexOf(branch) === index);
81
+ }
82
+ /** Validate a branch name (format only). */
83
+ function validateBranchName(branch) {
84
+ if (!branch || branch.trim().length === 0) return {
85
+ isValid: false,
86
+ error: "Branch name cannot be empty"
87
+ };
88
+ return { isValid: true };
89
+ }
90
+ /** Validate a git URL (format only — no allowlist). */
91
+ function validateGitUrl(url) {
92
+ const errors = [];
93
+ if (!url || typeof url !== "string") return {
94
+ isValid: false,
95
+ errors: ["Git URL must be a non-empty string"]
96
+ };
97
+ const trimmedUrl = url.trim();
98
+ if (trimmedUrl.length === 0) errors.push("Git URL cannot be empty");
99
+ if (trimmedUrl.length > 2048) errors.push("Git URL too long (max 2048 characters)");
100
+ if (trimmedUrl.includes("\0")) errors.push("Git URL contains null bytes");
101
+ return {
102
+ isValid: errors.length === 0,
103
+ errors
104
+ };
105
+ }
106
+ /** Validate a filesystem path (format only). */
107
+ function validatePath(path) {
108
+ const errors = [];
109
+ if (!path || typeof path !== "string") return {
110
+ isValid: false,
111
+ errors: ["Path must be a non-empty string"]
112
+ };
113
+ if (path.includes("\0")) errors.push("Path contains null bytes");
114
+ if (path.length > 4096) errors.push("Path too long (max 4096 characters)");
115
+ return {
116
+ isValid: errors.length === 0,
117
+ errors
118
+ };
119
+ }
120
+ /**
121
+ * Determine the appropriate {@link ErrorCode} for a failed git operation,
122
+ * based on the operation, the stderr/error text, and the process exit code.
123
+ */
124
+ function determineErrorCode(operation, error, exitCode) {
125
+ const lowerMessage = (typeof error === "string" ? error : error.message).toLowerCase();
126
+ if (exitCode === 124) return ErrorCode.GIT_NETWORK_ERROR;
127
+ if (exitCode === 128) {
128
+ if (lowerMessage.includes("not a git repository")) return ErrorCode.GIT_OPERATION_FAILED;
129
+ if (lowerMessage.includes("repository not found")) return ErrorCode.GIT_REPOSITORY_NOT_FOUND;
130
+ return ErrorCode.GIT_OPERATION_FAILED;
131
+ }
132
+ if (lowerMessage.includes("permission denied") || lowerMessage.includes("access denied")) return ErrorCode.GIT_AUTH_FAILED;
133
+ if (lowerMessage.includes("not found") || lowerMessage.includes("does not exist")) return ErrorCode.GIT_REPOSITORY_NOT_FOUND;
134
+ if (lowerMessage.includes("already exists")) return ErrorCode.GIT_CLONE_FAILED;
135
+ if (lowerMessage.includes("did not match") || lowerMessage.includes("pathspec")) return ErrorCode.GIT_BRANCH_NOT_FOUND;
136
+ if (lowerMessage.includes("authentication") || lowerMessage.includes("credentials")) return ErrorCode.GIT_AUTH_FAILED;
137
+ switch (operation) {
138
+ case "clone": return ErrorCode.GIT_CLONE_FAILED;
139
+ case "checkout": return ErrorCode.GIT_CHECKOUT_FAILED;
140
+ default: return ErrorCode.GIT_OPERATION_FAILED;
141
+ }
142
+ }
143
+
144
+ //#endregion
145
+ //#region ../../extensions/git/src/index.ts
146
+ /**
147
+ * Git extension for the Cloudflare Sandbox SDK.
148
+ *
149
+ * Git is just a sequence of shell commands, so this is an **SDK-only**
150
+ * extension: it drives the owning sandbox's unified `exec()` process surface
151
+ * over the existing control channel and needs no sidecar. All the git
152
+ * business logic that used to live in the container (argument building,
153
+ * branch parsing, error
154
+ * classification) is ported into `./manager` and runs in the Worker; only the
155
+ * `git` process itself runs in the container.
156
+ *
157
+ * Usage — attach it to a Sandbox subclass and expose delegate methods:
158
+ *
159
+ * ```ts
160
+ * import { Sandbox as BaseSandbox } from '@cloudflare/sandbox';
161
+ * import { withGit } from '@cloudflare/sandbox/git';
162
+ *
163
+ * export class Sandbox extends BaseSandbox<Env> {
164
+ * git = withGit(this);
165
+ * gitCheckout(repoUrl: string, options?: GitCheckoutOptions) {
166
+ * return this.git.checkout(repoUrl, options);
167
+ * }
168
+ * }
169
+ *
170
+ * await sandbox.gitCheckout('https://github.com/owner/repo.git');
171
+ * ```
172
+ */
173
+ const CLONE_PROCESS_TIMEOUT_BUFFER_MS = 1e4;
174
+ /**
175
+ * The git extension. Drives `git` in the container through the commands
176
+ * control sub-API and translates failures into the SDK's typed git errors.
177
+ */
178
+ var Git = class extends SandboxExtension {
179
+ #options;
180
+ constructor(sandbox, options = {}) {
181
+ super(sandbox);
182
+ this.#options = options;
183
+ }
184
+ /**
185
+ * Clone a repository. Returns the resolved target directory and the branch
186
+ * actually checked out (queried from git, not assumed).
187
+ */
188
+ async checkout(repoUrl, options = {}) {
189
+ const urlValidation = validateGitUrl(repoUrl);
190
+ if (!urlValidation.isValid) this.#throwValidation("repoUrl", urlValidation.errors, "INVALID_GIT_URL");
191
+ const targetDir = options.targetDir || generateTargetDirectory(repoUrl);
192
+ const cloneTimeoutMs = options.cloneTimeoutMs ?? DEFAULT_GIT_CLONE_TIMEOUT_MS;
193
+ if (!Number.isInteger(cloneTimeoutMs) || cloneTimeoutMs <= 0) this.#throwError(ErrorCode.VALIDATION_FAILED, `Invalid clone timeout '${options.cloneTimeoutMs}'. Must be a positive integer representing milliseconds.`, { validationErrors: [{
194
+ field: "cloneTimeoutMs",
195
+ message: "Clone timeout must be a positive integer representing milliseconds",
196
+ code: "INVALID_TIMEOUT"
197
+ }] });
198
+ const pathValidation = validatePath(targetDir);
199
+ if (!pathValidation.isValid) this.#throwValidation("targetDir", pathValidation.errors, "INVALID_PATH");
200
+ await this.#configureAuth(repoUrl, options.auth);
201
+ const sessionId = this.#sessionId(options);
202
+ const cloneResult = await this.#exec(buildCloneArgs(repoUrl, targetDir, options), sessionId, void 0, cloneTimeoutMs + CLONE_PROCESS_TIMEOUT_BUFFER_MS);
203
+ if (cloneResult.exitCode !== 0) {
204
+ if ([
205
+ 124,
206
+ 143,
207
+ -15
208
+ ].includes(cloneResult.exitCode)) this.#throwError(ErrorCode.GIT_NETWORK_ERROR, `Git clone timed out after ${gitCloneTimeoutSeconds(cloneTimeoutMs)} seconds for '${redactCommand(repoUrl)}'`, {
209
+ repository: redactCommand(repoUrl),
210
+ targetDir,
211
+ exitCode: 124,
212
+ stderr: "Operation timed out"
213
+ });
214
+ const code = determineErrorCode("clone", cloneResult.stderr || "Unknown error", cloneResult.exitCode);
215
+ this.#throwError(code, `Failed to clone repository '${redactCommand(repoUrl)}': ${redactCommand(cloneResult.stderr || "") || `exit code ${cloneResult.exitCode}`}`, {
216
+ repository: redactCommand(repoUrl),
217
+ targetDir,
218
+ exitCode: cloneResult.exitCode,
219
+ stderr: redactCommand(cloneResult.stderr || "")
220
+ });
221
+ }
222
+ const branchResult = await this.#exec(buildGetCurrentBranchArgs(), sessionId, targetDir);
223
+ return {
224
+ success: true,
225
+ repoUrl,
226
+ branch: branchResult.exitCode === 0 && branchResult.stdout.trim() ? branchResult.stdout.trim() : options.branch || "unknown",
227
+ targetDir,
228
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
229
+ };
230
+ }
231
+ /** Check out an existing branch in a cloned repository. */
232
+ async checkoutBranch(repoPath, branch, options = {}) {
233
+ const pathValidation = validatePath(repoPath);
234
+ if (!pathValidation.isValid) this.#throwValidation("repoPath", pathValidation.errors, "INVALID_PATH");
235
+ const branchValidation = validateBranchName(branch);
236
+ if (!branchValidation.isValid) this.#throwError(ErrorCode.VALIDATION_FAILED, `Invalid branch name '${branch}': ${branchValidation.error || "Invalid format"}`, { validationErrors: [{
237
+ field: "branch",
238
+ message: branchValidation.error || "Invalid branch name format",
239
+ code: "INVALID_BRANCH"
240
+ }] });
241
+ const result = await this.#exec(buildCheckoutArgs(branch), this.#sessionId(options), repoPath);
242
+ if (result.exitCode !== 0) {
243
+ const code = determineErrorCode("checkout", result.stderr || "Unknown error", result.exitCode);
244
+ this.#throwError(code, `Failed to checkout branch '${branch}' in '${repoPath}': ${result.stderr || `exit code ${result.exitCode}`}`, {
245
+ branch,
246
+ targetDir: repoPath,
247
+ exitCode: result.exitCode,
248
+ stderr: result.stderr
249
+ });
250
+ }
251
+ }
252
+ /** Return the current branch of a cloned repository. */
253
+ async getCurrentBranch(repoPath, options = {}) {
254
+ const pathValidation = validatePath(repoPath);
255
+ if (!pathValidation.isValid) this.#throwValidation("repoPath", pathValidation.errors, "INVALID_PATH");
256
+ const result = await this.#exec(buildGetCurrentBranchArgs(), this.#sessionId(options), repoPath);
257
+ if (result.exitCode !== 0) this.#throwError(determineErrorCode("getCurrentBranch", result.stderr || "Unknown error", result.exitCode), `Failed to get current branch in '${repoPath}': ${result.stderr || `exit code ${result.exitCode}`}`, {
258
+ targetDir: repoPath,
259
+ exitCode: result.exitCode,
260
+ stderr: result.stderr
261
+ });
262
+ return result.stdout.trim();
263
+ }
264
+ /** List local and remote branches of a cloned repository. */
265
+ async listBranches(repoPath, options = {}) {
266
+ const pathValidation = validatePath(repoPath);
267
+ if (!pathValidation.isValid) this.#throwValidation("repoPath", pathValidation.errors, "INVALID_PATH");
268
+ const result = await this.#exec(buildListBranchesArgs(), this.#sessionId(options), repoPath);
269
+ if (result.exitCode !== 0) this.#throwError(determineErrorCode("listBranches", result.stderr || "Unknown error", result.exitCode), `Failed to list branches in '${repoPath}': ${result.stderr || `exit code ${result.exitCode}`}`, {
270
+ targetDir: repoPath,
271
+ exitCode: result.exitCode,
272
+ stderr: result.stderr
273
+ });
274
+ return parseBranchList(result.stdout);
275
+ }
276
+ async #configureAuth(repoUrl, authOverride) {
277
+ const hosts = this.#authHosts(authOverride);
278
+ if (Object.keys(hosts).length === 0) return;
279
+ const hostname = this.#authHostname(repoUrl);
280
+ if (hostname === void 0 || !hosts[hostname]) return;
281
+ if (!this.httpAuthInterceptor) this.#throwError(ErrorCode.VALIDATION_FAILED, "Git extension authentication requires exporting ContainerProxy from the Worker entrypoint. Import ContainerProxy from @cloudflare/sandbox and export it from your Worker to use git auth interception.", {
282
+ repository: redactCommand(repoUrl),
283
+ host: hostname
284
+ });
285
+ await this.httpAuthInterceptor({ hosts: { [hostname]: hosts[hostname] } });
286
+ }
287
+ #authHostname(repoUrl) {
288
+ try {
289
+ return new URL(repoUrl).hostname;
290
+ } catch {
291
+ return;
292
+ }
293
+ }
294
+ #authHosts(authOverride) {
295
+ if (authOverride === false) return {};
296
+ const config = authOverride ?? this.#options.auth;
297
+ if (!config) return {};
298
+ const hosts = { ...config.hosts ?? {} };
299
+ if (config.github) hosts["github.com"] = config.github;
300
+ if (config.gitlab) hosts["gitlab.com"] = config.gitlab;
301
+ if (config.bitbucket) hosts["bitbucket.org"] = config.bitbucket;
302
+ return hosts;
303
+ }
304
+ #sessionId(options) {
305
+ return options.sessionId;
306
+ }
307
+ async #exec(command, sessionId, cwd, timeout) {
308
+ const result = await this.exec(command, {
309
+ ...sessionId !== void 0 && { sessionId },
310
+ ...cwd !== void 0 && { cwd },
311
+ ...timeout !== void 0 && { timeout },
312
+ stdout: "pipe",
313
+ stderr: "pipe"
314
+ }).output({ encoding: "utf8" });
315
+ return {
316
+ stdout: result.stdout,
317
+ stderr: result.stderr,
318
+ exitCode: result.exitCode
319
+ };
320
+ }
321
+ #throwValidation(field, errors, code) {
322
+ this.#throwError(code === "INVALID_GIT_URL" ? ErrorCode.INVALID_GIT_URL : ErrorCode.VALIDATION_FAILED, `Invalid ${field}: ${errors.join(", ")}`, { validationErrors: errors.map((message) => ({
323
+ field,
324
+ message,
325
+ code
326
+ })) });
327
+ }
328
+ #throwError(code, message, context) {
329
+ throw createErrorFromResponse({
330
+ code,
331
+ message,
332
+ context,
333
+ httpStatus: getHttpStatus(code),
334
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
335
+ });
336
+ }
337
+ };
338
+ /** Factory — the consumer-facing API. */
339
+ function withGit(sandbox, options = {}) {
340
+ return new Git(sandbox, options);
341
+ }
342
+
343
+ //#endregion
344
+ export { Git, withGit as default, withGit };
345
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["errors: string[]","#options","#throwValidation","#throwError","#configureAuth","#sessionId","#exec","#authHosts","#authHostname","hosts: Record<string, GitHostAuth>"],"sources":["../../../../extensions/git/src/manager.ts","../../../../extensions/git/src/index.ts"],"sourcesContent":["/**\n * Pure git business logic for the git extension.\n *\n * No I/O — just command-argument building, output parsing, validation, and\n * error-code classification. Ported from the former container-side\n * `GitManager` so the extension can drive everything through `exec`.\n */\n\nimport { DEFAULT_GIT_CLONE_TIMEOUT_MS, extractRepoName } from '@repo/shared';\nimport { ErrorCode } from '@repo/shared/errors';\nimport type { GitCheckoutOptions } from './types.js';\n\nexport { DEFAULT_GIT_CLONE_TIMEOUT_MS };\n\nconst GIT_CLONE_KILL_GRACE_SECONDS = 5;\n\n/** Format a millisecond timeout as a `timeout(1)`-friendly seconds string. */\nexport function gitCloneTimeoutSeconds(timeoutMs: number): string {\n const timeoutSeconds = timeoutMs / 1000;\n return Number.isInteger(timeoutSeconds)\n ? String(timeoutSeconds)\n : timeoutSeconds\n .toFixed(3)\n .replace(/\\.0+$/, '')\n .replace(/(\\.\\d*?)0+$/, '$1');\n}\n\n/**\n * Generate the default target directory for a clone: `/workspace/<repoName>`.\n */\nexport function generateTargetDirectory(repoUrl: string): string {\n return `/workspace/${extractRepoName(repoUrl)}`;\n}\n\n/**\n * Build git clone command arguments.\n *\n * Wraps the command with `timeout -k 5 <seconds>` to enforce a wall-clock\n * limit, and configures git's own stalled-transfer detection via\n * `http.lowSpeedLimit` and `http.lowSpeedTime`.\n */\nexport function buildCloneArgs(\n repoUrl: string,\n targetDir: string,\n options: GitCheckoutOptions = {}\n): string[] {\n const timeoutMs = options.cloneTimeoutMs ?? DEFAULT_GIT_CLONE_TIMEOUT_MS;\n const timeoutSeconds = gitCloneTimeoutSeconds(timeoutMs);\n const args = [\n 'timeout',\n '-k',\n String(GIT_CLONE_KILL_GRACE_SECONDS),\n String(timeoutSeconds),\n 'git',\n '-c',\n 'http.lowSpeedLimit=1024',\n '-c',\n 'http.lowSpeedTime=30',\n 'clone',\n '--filter=blob:none'\n ];\n\n if (options.branch) {\n args.push('--branch', options.branch);\n }\n\n if (options.depth !== undefined) {\n args.push('--depth', String(options.depth));\n }\n\n args.push(repoUrl, targetDir);\n\n return args;\n}\n\nexport function buildCheckoutArgs(branch: string): string[] {\n return ['git', 'checkout', branch];\n}\n\nexport function buildGetCurrentBranchArgs(): string[] {\n return ['git', 'branch', '--show-current'];\n}\n\nexport function buildListBranchesArgs(): string[] {\n return ['git', 'branch', '-a'];\n}\n\n/**\n * Parse `git branch -a` output into a deduplicated array of branch names.\n * Strips the current-branch marker, the `remotes/origin/` prefix, and the\n * `HEAD` reference.\n */\nexport function parseBranchList(stdout: string): string[] {\n return stdout\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map((line) => line.replace(/^\\*\\s*/, ''))\n .map((line) => line.replace(/^remotes\\/origin\\//, ''))\n .filter((branch) => branch !== 'HEAD' && !branch.includes('->'))\n .filter((branch, index, array) => array.indexOf(branch) === index);\n}\n\n/** Validate a branch name (format only). */\nexport function validateBranchName(branch: string): {\n isValid: boolean;\n error?: string;\n} {\n if (!branch || branch.trim().length === 0) {\n return { isValid: false, error: 'Branch name cannot be empty' };\n }\n return { isValid: true };\n}\n\n/** Validate a git URL (format only — no allowlist). */\nexport function validateGitUrl(url: string): {\n isValid: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n if (!url || typeof url !== 'string') {\n return { isValid: false, errors: ['Git URL must be a non-empty string'] };\n }\n\n const trimmedUrl = url.trim();\n\n if (trimmedUrl.length === 0) {\n errors.push('Git URL cannot be empty');\n }\n if (trimmedUrl.length > 2048) {\n errors.push('Git URL too long (max 2048 characters)');\n }\n if (trimmedUrl.includes('\\0')) {\n errors.push('Git URL contains null bytes');\n }\n\n return { isValid: errors.length === 0, errors };\n}\n\n/** Validate a filesystem path (format only). */\nexport function validatePath(path: string): {\n isValid: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n if (!path || typeof path !== 'string') {\n return { isValid: false, errors: ['Path must be a non-empty string'] };\n }\n if (path.includes('\\0')) {\n errors.push('Path contains null bytes');\n }\n if (path.length > 4096) {\n errors.push('Path too long (max 4096 characters)');\n }\n\n return { isValid: errors.length === 0, errors };\n}\n\n/**\n * Determine the appropriate {@link ErrorCode} for a failed git operation,\n * based on the operation, the stderr/error text, and the process exit code.\n */\nexport function determineErrorCode(\n operation: string,\n error: Error | string,\n exitCode?: number\n): ErrorCode {\n const errorMessage = typeof error === 'string' ? error : error.message;\n const lowerMessage = errorMessage.toLowerCase();\n\n // Exit code 124: timeout command killed the process.\n if (exitCode === 124) {\n return ErrorCode.GIT_NETWORK_ERROR;\n }\n\n // Exit code 128: git-specific fatal errors.\n if (exitCode === 128) {\n if (lowerMessage.includes('not a git repository')) {\n return ErrorCode.GIT_OPERATION_FAILED;\n }\n if (lowerMessage.includes('repository not found')) {\n return ErrorCode.GIT_REPOSITORY_NOT_FOUND;\n }\n return ErrorCode.GIT_OPERATION_FAILED;\n }\n\n if (\n lowerMessage.includes('permission denied') ||\n lowerMessage.includes('access denied')\n ) {\n return ErrorCode.GIT_AUTH_FAILED;\n }\n if (\n lowerMessage.includes('not found') ||\n lowerMessage.includes('does not exist')\n ) {\n return ErrorCode.GIT_REPOSITORY_NOT_FOUND;\n }\n if (lowerMessage.includes('already exists')) {\n return ErrorCode.GIT_CLONE_FAILED;\n }\n if (\n lowerMessage.includes('did not match') ||\n lowerMessage.includes('pathspec')\n ) {\n return ErrorCode.GIT_BRANCH_NOT_FOUND;\n }\n if (\n lowerMessage.includes('authentication') ||\n lowerMessage.includes('credentials')\n ) {\n return ErrorCode.GIT_AUTH_FAILED;\n }\n\n switch (operation) {\n case 'clone':\n return ErrorCode.GIT_CLONE_FAILED;\n case 'checkout':\n return ErrorCode.GIT_CHECKOUT_FAILED;\n default:\n return ErrorCode.GIT_OPERATION_FAILED;\n }\n}\n","/**\n * Git extension for the Cloudflare Sandbox SDK.\n *\n * Git is just a sequence of shell commands, so this is an **SDK-only**\n * extension: it drives the owning sandbox's unified `exec()` process surface\n * over the existing control channel and needs no sidecar. All the git\n * business logic that used to live in the container (argument building,\n * branch parsing, error\n * classification) is ported into `./manager` and runs in the Worker; only the\n * `git` process itself runs in the container.\n *\n * Usage — attach it to a Sandbox subclass and expose delegate methods:\n *\n * ```ts\n * import { Sandbox as BaseSandbox } from '@cloudflare/sandbox';\n * import { withGit } from '@cloudflare/sandbox/git';\n *\n * export class Sandbox extends BaseSandbox<Env> {\n * git = withGit(this);\n * gitCheckout(repoUrl: string, options?: GitCheckoutOptions) {\n * return this.git.checkout(repoUrl, options);\n * }\n * }\n *\n * await sandbox.gitCheckout('https://github.com/owner/repo.git');\n * ```\n */\n\nimport { createErrorFromResponse } from '@cloudflare/sandbox/errors';\nimport {\n SandboxExtension,\n type SandboxLike\n} from '@cloudflare/sandbox/extensions';\nimport { redactCommand } from '@repo/shared';\nimport {\n ErrorCode,\n type ErrorResponse,\n getHttpStatus\n} from '@repo/shared/errors';\nimport {\n buildCheckoutArgs,\n buildCloneArgs,\n buildGetCurrentBranchArgs,\n buildListBranchesArgs,\n DEFAULT_GIT_CLONE_TIMEOUT_MS,\n determineErrorCode,\n generateTargetDirectory,\n gitCloneTimeoutSeconds,\n parseBranchList,\n validateBranchName,\n validateGitUrl,\n validatePath\n} from './manager.js';\nimport type {\n GitCheckoutOptions,\n GitCheckoutResult,\n GitExtensionOptions,\n GitHostAuth,\n GitSessionOptions\n} from './types.js';\n\nexport type {\n GitAuthConfig,\n GitCheckoutOptions,\n GitCheckoutResult,\n GitExtensionOptions,\n GitHostAuth,\n GitSessionOptions\n} from './types.js';\nexport { withGit as default };\n\nconst CLONE_PROCESS_TIMEOUT_BUFFER_MS = 10_000;\n\n/** Shape returned by a completed git process. */\ninterface ExecOutcome {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * The git extension. Drives `git` in the container through the commands\n * control sub-API and translates failures into the SDK's typed git errors.\n */\nexport class Git extends SandboxExtension {\n readonly #options: GitExtensionOptions;\n\n constructor(sandbox: SandboxLike, options: GitExtensionOptions = {}) {\n super(sandbox);\n this.#options = options;\n }\n\n /**\n * Clone a repository. Returns the resolved target directory and the branch\n * actually checked out (queried from git, not assumed).\n */\n async checkout(\n repoUrl: string,\n options: GitCheckoutOptions = {}\n ): Promise<GitCheckoutResult> {\n const urlValidation = validateGitUrl(repoUrl);\n if (!urlValidation.isValid) {\n this.#throwValidation('repoUrl', urlValidation.errors, 'INVALID_GIT_URL');\n }\n\n const targetDir = options.targetDir || generateTargetDirectory(repoUrl);\n const cloneTimeoutMs =\n options.cloneTimeoutMs ?? DEFAULT_GIT_CLONE_TIMEOUT_MS;\n\n if (!Number.isInteger(cloneTimeoutMs) || cloneTimeoutMs <= 0) {\n this.#throwError(\n ErrorCode.VALIDATION_FAILED,\n `Invalid clone timeout '${options.cloneTimeoutMs}'. Must be a positive integer representing milliseconds.`,\n {\n validationErrors: [\n {\n field: 'cloneTimeoutMs',\n message:\n 'Clone timeout must be a positive integer representing milliseconds',\n code: 'INVALID_TIMEOUT'\n }\n ]\n }\n );\n }\n\n const pathValidation = validatePath(targetDir);\n if (!pathValidation.isValid) {\n this.#throwValidation('targetDir', pathValidation.errors, 'INVALID_PATH');\n }\n\n await this.#configureAuth(repoUrl, options.auth);\n\n const sessionId = this.#sessionId(options);\n const cloneResult = await this.#exec(\n buildCloneArgs(repoUrl, targetDir, options),\n sessionId,\n undefined,\n cloneTimeoutMs + CLONE_PROCESS_TIMEOUT_BUFFER_MS\n );\n\n if (cloneResult.exitCode !== 0) {\n if ([124, 143, -15].includes(cloneResult.exitCode)) {\n this.#throwError(\n ErrorCode.GIT_NETWORK_ERROR,\n `Git clone timed out after ${gitCloneTimeoutSeconds(\n cloneTimeoutMs\n )} seconds for '${redactCommand(repoUrl)}'`,\n {\n repository: redactCommand(repoUrl),\n targetDir,\n exitCode: 124,\n stderr: 'Operation timed out'\n }\n );\n }\n\n const code = determineErrorCode(\n 'clone',\n cloneResult.stderr || 'Unknown error',\n cloneResult.exitCode\n );\n this.#throwError(\n code,\n `Failed to clone repository '${redactCommand(repoUrl)}': ${\n redactCommand(cloneResult.stderr || '') ||\n `exit code ${cloneResult.exitCode}`\n }`,\n {\n repository: redactCommand(repoUrl),\n targetDir,\n exitCode: cloneResult.exitCode,\n stderr: redactCommand(cloneResult.stderr || '')\n }\n );\n }\n\n // Query the branch actually checked out rather than assuming.\n const branchResult = await this.#exec(\n buildGetCurrentBranchArgs(),\n sessionId,\n targetDir\n );\n const branch =\n branchResult.exitCode === 0 && branchResult.stdout.trim()\n ? branchResult.stdout.trim()\n : options.branch || 'unknown';\n\n return {\n success: true,\n repoUrl,\n branch,\n targetDir,\n timestamp: new Date().toISOString()\n };\n }\n\n /** Check out an existing branch in a cloned repository. */\n async checkoutBranch(\n repoPath: string,\n branch: string,\n options: GitSessionOptions = {}\n ): Promise<void> {\n const pathValidation = validatePath(repoPath);\n if (!pathValidation.isValid) {\n this.#throwValidation('repoPath', pathValidation.errors, 'INVALID_PATH');\n }\n\n const branchValidation = validateBranchName(branch);\n if (!branchValidation.isValid) {\n this.#throwError(\n ErrorCode.VALIDATION_FAILED,\n `Invalid branch name '${branch}': ${branchValidation.error || 'Invalid format'}`,\n {\n validationErrors: [\n {\n field: 'branch',\n message: branchValidation.error || 'Invalid branch name format',\n code: 'INVALID_BRANCH'\n }\n ]\n }\n );\n }\n\n const result = await this.#exec(\n buildCheckoutArgs(branch),\n this.#sessionId(options),\n repoPath\n );\n\n if (result.exitCode !== 0) {\n const code = determineErrorCode(\n 'checkout',\n result.stderr || 'Unknown error',\n result.exitCode\n );\n this.#throwError(\n code,\n `Failed to checkout branch '${branch}' in '${repoPath}': ${\n result.stderr || `exit code ${result.exitCode}`\n }`,\n {\n branch,\n targetDir: repoPath,\n exitCode: result.exitCode,\n stderr: result.stderr\n }\n );\n }\n }\n\n /** Return the current branch of a cloned repository. */\n async getCurrentBranch(\n repoPath: string,\n options: GitSessionOptions = {}\n ): Promise<string> {\n const pathValidation = validatePath(repoPath);\n if (!pathValidation.isValid) {\n this.#throwValidation('repoPath', pathValidation.errors, 'INVALID_PATH');\n }\n\n const result = await this.#exec(\n buildGetCurrentBranchArgs(),\n this.#sessionId(options),\n repoPath\n );\n\n if (result.exitCode !== 0) {\n this.#throwError(\n determineErrorCode(\n 'getCurrentBranch',\n result.stderr || 'Unknown error',\n result.exitCode\n ),\n `Failed to get current branch in '${repoPath}': ${\n result.stderr || `exit code ${result.exitCode}`\n }`,\n {\n targetDir: repoPath,\n exitCode: result.exitCode,\n stderr: result.stderr\n }\n );\n }\n\n return result.stdout.trim();\n }\n\n /** List local and remote branches of a cloned repository. */\n async listBranches(\n repoPath: string,\n options: GitSessionOptions = {}\n ): Promise<string[]> {\n const pathValidation = validatePath(repoPath);\n if (!pathValidation.isValid) {\n this.#throwValidation('repoPath', pathValidation.errors, 'INVALID_PATH');\n }\n\n const result = await this.#exec(\n buildListBranchesArgs(),\n this.#sessionId(options),\n repoPath\n );\n\n if (result.exitCode !== 0) {\n this.#throwError(\n determineErrorCode(\n 'listBranches',\n result.stderr || 'Unknown error',\n result.exitCode\n ),\n `Failed to list branches in '${repoPath}': ${\n result.stderr || `exit code ${result.exitCode}`\n }`,\n {\n targetDir: repoPath,\n exitCode: result.exitCode,\n stderr: result.stderr\n }\n );\n }\n\n return parseBranchList(result.stdout);\n }\n\n // --- internals -----------------------------------------------------------\n\n async #configureAuth(\n repoUrl: string,\n authOverride: GitCheckoutOptions['auth']\n ): Promise<void> {\n const hosts = this.#authHosts(authOverride);\n if (Object.keys(hosts).length === 0) {\n return;\n }\n\n const hostname = this.#authHostname(repoUrl);\n if (hostname === undefined || !hosts[hostname]) {\n return;\n }\n\n if (!this.httpAuthInterceptor) {\n this.#throwError(\n ErrorCode.VALIDATION_FAILED,\n 'Git extension authentication requires exporting ContainerProxy from the Worker entrypoint. Import ContainerProxy from @cloudflare/sandbox and export it from your Worker to use git auth interception.',\n {\n repository: redactCommand(repoUrl),\n host: hostname\n }\n );\n }\n\n await this.httpAuthInterceptor({ hosts: { [hostname]: hosts[hostname] } });\n }\n\n #authHostname(repoUrl: string): string | undefined {\n try {\n return new URL(repoUrl).hostname;\n } catch {\n return undefined;\n }\n }\n\n #authHosts(\n authOverride: GitCheckoutOptions['auth']\n ): Record<string, GitHostAuth> {\n if (authOverride === false) {\n return {};\n }\n const config = authOverride ?? this.#options.auth;\n if (!config) {\n return {};\n }\n\n const hosts: Record<string, GitHostAuth> = { ...(config.hosts ?? {}) };\n if (config.github) {\n hosts['github.com'] = config.github;\n }\n if (config.gitlab) {\n hosts['gitlab.com'] = config.gitlab;\n }\n if (config.bitbucket) {\n hosts['bitbucket.org'] = config.bitbucket;\n }\n return hosts;\n }\n\n #sessionId(options: GitSessionOptions): string | undefined {\n return options.sessionId;\n }\n\n async #exec(\n command: string[],\n sessionId: string | undefined,\n cwd?: string,\n timeout?: number\n ): Promise<ExecOutcome> {\n const result = await this.exec(command, {\n ...(sessionId !== undefined && { sessionId }),\n ...(cwd !== undefined && { cwd }),\n ...(timeout !== undefined && { timeout }),\n stdout: 'pipe',\n stderr: 'pipe'\n }).output({ encoding: 'utf8' });\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode\n };\n }\n\n #throwValidation(field: string, errors: string[], code: string): never {\n this.#throwError(\n code === 'INVALID_GIT_URL'\n ? ErrorCode.INVALID_GIT_URL\n : ErrorCode.VALIDATION_FAILED,\n `Invalid ${field}: ${errors.join(', ')}`,\n {\n validationErrors: errors.map((message) => ({ field, message, code }))\n }\n );\n }\n\n #throwError(\n code: ErrorCode,\n message: string,\n context: Record<string, unknown>\n ): never {\n const response: ErrorResponse = {\n code,\n message,\n context,\n httpStatus: getHttpStatus(code),\n timestamp: new Date().toISOString()\n };\n throw createErrorFromResponse(response);\n }\n}\n\n/** Factory — the consumer-facing API. */\nexport function withGit(\n sandbox: SandboxLike,\n options: GitExtensionOptions = {}\n): Git {\n return new Git(sandbox, options);\n}\n"],"mappings":"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"}