@ai-hero/sandcastle 0.0.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.
- package/README.md +284 -0
- package/dist/AgentProvider.d.ts +8 -0
- package/dist/AgentProvider.d.ts.map +1 -0
- package/dist/AgentProvider.js +60 -0
- package/dist/AgentProvider.js.map +1 -0
- package/dist/Config.d.ts +35 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +56 -0
- package/dist/Config.js.map +1 -0
- package/dist/CopyToSandbox.d.ts +8 -0
- package/dist/CopyToSandbox.d.ts.map +1 -0
- package/dist/CopyToSandbox.js +32 -0
- package/dist/CopyToSandbox.js.map +1 -0
- package/dist/Display.d.ts +52 -0
- package/dist/Display.d.ts.map +1 -0
- package/dist/Display.js +132 -0
- package/dist/Display.js.map +1 -0
- package/dist/DockerLifecycle.d.ts +37 -0
- package/dist/DockerLifecycle.d.ts.map +1 -0
- package/dist/DockerLifecycle.js +109 -0
- package/dist/DockerLifecycle.js.map +1 -0
- package/dist/DockerSandbox.d.ts +6 -0
- package/dist/DockerSandbox.d.ts.map +1 -0
- package/dist/DockerSandbox.js +122 -0
- package/dist/DockerSandbox.js.map +1 -0
- package/dist/EnvResolver.d.ts +11 -0
- package/dist/EnvResolver.d.ts.map +1 -0
- package/dist/EnvResolver.js +43 -0
- package/dist/EnvResolver.js.map +1 -0
- package/dist/ErrorHandler.d.ts +15 -0
- package/dist/ErrorHandler.d.ts.map +1 -0
- package/dist/ErrorHandler.js +58 -0
- package/dist/ErrorHandler.js.map +1 -0
- package/dist/FilesystemSandbox.d.ts +6 -0
- package/dist/FilesystemSandbox.d.ts.map +1 -0
- package/dist/FilesystemSandbox.js +83 -0
- package/dist/FilesystemSandbox.js.map +1 -0
- package/dist/InitService.d.ts +11 -0
- package/dist/InitService.d.ts.map +1 -0
- package/dist/InitService.js +111 -0
- package/dist/InitService.js.map +1 -0
- package/dist/Orchestrator.d.ts +49 -0
- package/dist/Orchestrator.d.ts.map +1 -0
- package/dist/Orchestrator.js +155 -0
- package/dist/Orchestrator.js.map +1 -0
- package/dist/PromptArgumentSubstitution.d.ts +6 -0
- package/dist/PromptArgumentSubstitution.d.ts.map +1 -0
- package/dist/PromptArgumentSubstitution.js +33 -0
- package/dist/PromptArgumentSubstitution.js.map +1 -0
- package/dist/PromptPreprocessor.d.ts +7 -0
- package/dist/PromptPreprocessor.d.ts.map +1 -0
- package/dist/PromptPreprocessor.js +34 -0
- package/dist/PromptPreprocessor.js.map +1 -0
- package/dist/PromptResolver.d.ts +9 -0
- package/dist/PromptResolver.d.ts.map +1 -0
- package/dist/PromptResolver.js +26 -0
- package/dist/PromptResolver.js.map +1 -0
- package/dist/RecoveryMessage.d.ts +15 -0
- package/dist/RecoveryMessage.d.ts.map +1 -0
- package/dist/RecoveryMessage.js +81 -0
- package/dist/RecoveryMessage.js.map +1 -0
- package/dist/Sandbox.d.ts +23 -0
- package/dist/Sandbox.d.ts.map +1 -0
- package/dist/Sandbox.js +5 -0
- package/dist/Sandbox.js.map +1 -0
- package/dist/SandboxFactory.d.ts +56 -0
- package/dist/SandboxFactory.d.ts.map +1 -0
- package/dist/SandboxFactory.js +219 -0
- package/dist/SandboxFactory.js.map +1 -0
- package/dist/SandboxLifecycle.d.ts +32 -0
- package/dist/SandboxLifecycle.d.ts.map +1 -0
- package/dist/SandboxLifecycle.js +152 -0
- package/dist/SandboxLifecycle.js.map +1 -0
- package/dist/SyncService.d.ts +20 -0
- package/dist/SyncService.d.ts.map +1 -0
- package/dist/SyncService.js +504 -0
- package/dist/SyncService.js.map +1 -0
- package/dist/TokenResolver.d.ts +6 -0
- package/dist/TokenResolver.d.ts.map +1 -0
- package/dist/TokenResolver.js +43 -0
- package/dist/TokenResolver.js.map +1 -0
- package/dist/WorktreeManager.d.ts +42 -0
- package/dist/WorktreeManager.d.ts.map +1 -0
- package/dist/WorktreeManager.js +170 -0
- package/dist/WorktreeManager.js.map +1 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +217 -0
- package/dist/cli.js.map +1 -0
- package/dist/errors.d.ts +95 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +16 -0
- package/dist/main.js.map +1 -0
- package/dist/run.d.ts +91 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +155 -0
- package/dist/run.js.map +1 -0
- package/dist/templates/blank/main.ts +9 -0
- package/dist/templates/blank/prompt.md +12 -0
- package/dist/templates/blank/template.json +4 -0
- package/dist/templates/parallel-planner/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner/main.ts +200 -0
- package/dist/templates/parallel-planner/merge-prompt.md +22 -0
- package/dist/templates/parallel-planner/plan-prompt.md +33 -0
- package/dist/templates/parallel-planner/template.json +4 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +62 -0
- package/dist/templates/sequential-reviewer/main.ts +102 -0
- package/dist/templates/sequential-reviewer/review-prompt.md +43 -0
- package/dist/templates/sequential-reviewer/template.json +4 -0
- package/dist/templates/simple-loop/main.ts +37 -0
- package/dist/templates/simple-loop/prompt.md +51 -0
- package/dist/templates/simple-loop/template.json +4 -0
- package/dist/templates.d.ts +2 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +26 -0
- package/dist/templates.js.map +1 -0
- package/dist/terminalCleanup.d.ts +30 -0
- package/dist/terminalCleanup.d.ts.map +1 -0
- package/dist/terminalCleanup.js +37 -0
- package/dist/terminalCleanup.js.map +1 -0
- package/dist/testSandbox.d.ts +8 -0
- package/dist/testSandbox.d.ts.map +1 -0
- package/dist/testSandbox.js +101 -0
- package/dist/testSandbox.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build copy-pastable recovery commands for a failed sync-out.
|
|
3
|
+
* Pure function: takes failure state, returns formatted terminal output.
|
|
4
|
+
*/
|
|
5
|
+
export const buildRecoveryMessage = (input) => {
|
|
6
|
+
const { patchDir, failedStep, hasCommits, hasDiff, hasUntracked, branch } = input;
|
|
7
|
+
// When --branch is set, commands run inside .sandcastle/worktree,
|
|
8
|
+
// so patch paths need ../../ prefix to reach repo root
|
|
9
|
+
const cmdPatchDir = branch ? `../../${patchDir}` : patchDir;
|
|
10
|
+
// Determine the step number for the failed step
|
|
11
|
+
const steps = [];
|
|
12
|
+
if (hasCommits)
|
|
13
|
+
steps.push({ key: "commits", label: "committed changes", has: true });
|
|
14
|
+
if (hasDiff)
|
|
15
|
+
steps.push({ key: "diff", label: "uncommitted changes", has: true });
|
|
16
|
+
if (hasUntracked)
|
|
17
|
+
steps.push({ key: "untracked", label: "untracked files", has: true });
|
|
18
|
+
const failedIndex = steps.findIndex((s) => s.key === failedStep);
|
|
19
|
+
const failedStepInfo = steps[failedIndex];
|
|
20
|
+
const stepNumber = failedIndex + 1;
|
|
21
|
+
const lines = [];
|
|
22
|
+
lines.push(`Patch application failed at step ${stepNumber} (${failedStepInfo.label}).`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
// Add worktree setup preamble for --branch
|
|
25
|
+
if (branch) {
|
|
26
|
+
lines.push("Set up worktree, then resolve:");
|
|
27
|
+
lines.push(formatCommandBlock([
|
|
28
|
+
`git worktree add .sandcastle/worktree ${branch}`,
|
|
29
|
+
`cd .sandcastle/worktree`,
|
|
30
|
+
]));
|
|
31
|
+
lines.push("");
|
|
32
|
+
}
|
|
33
|
+
if (failedStep === "commits") {
|
|
34
|
+
// git am failure — lean on git's built-in workflow
|
|
35
|
+
lines.push("Resolve conflicts, then continue with:");
|
|
36
|
+
lines.push(` git am --continue`);
|
|
37
|
+
// Remaining steps after git am resolves
|
|
38
|
+
const remaining = buildRemainingCommands(cmdPatchDir, steps.slice(failedIndex + 1));
|
|
39
|
+
if (remaining.length > 0) {
|
|
40
|
+
lines.push("");
|
|
41
|
+
lines.push("After all commits are applied, run the remaining steps:");
|
|
42
|
+
lines.push(formatCommandBlock(remaining));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// diff or untracked failure — print remaining commands from failed step onward
|
|
47
|
+
const remaining = buildRemainingCommands(cmdPatchDir, steps.slice(failedIndex));
|
|
48
|
+
if (remaining.length > 0) {
|
|
49
|
+
lines.push("Run the remaining steps:");
|
|
50
|
+
lines.push(formatCommandBlock(remaining));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
};
|
|
55
|
+
const buildRemainingCommands = (patchDir, steps) => {
|
|
56
|
+
const commands = [];
|
|
57
|
+
for (const step of steps) {
|
|
58
|
+
if (!step.has)
|
|
59
|
+
continue;
|
|
60
|
+
if (step.key === "commits") {
|
|
61
|
+
commands.push(`git am --3way ${patchDir}/*.patch`);
|
|
62
|
+
}
|
|
63
|
+
else if (step.key === "diff") {
|
|
64
|
+
commands.push(`git apply ${patchDir}/changes.patch`);
|
|
65
|
+
}
|
|
66
|
+
else if (step.key === "untracked") {
|
|
67
|
+
commands.push(`cp -r ${patchDir}/untracked/* .`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return commands;
|
|
71
|
+
};
|
|
72
|
+
const formatCommandBlock = (commands) => {
|
|
73
|
+
if (commands.length === 1) {
|
|
74
|
+
return ` ${commands[0]}`;
|
|
75
|
+
}
|
|
76
|
+
// Join with && \ continuation
|
|
77
|
+
return commands
|
|
78
|
+
.map((cmd, i) => (i < commands.length - 1 ? ` ${cmd} && \\` : ` ${cmd}`))
|
|
79
|
+
.join("\n");
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=RecoveryMessage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecoveryMessage.js","sourceRoot":"","sources":["../src/RecoveryMessage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
import type { CopyError, ExecError } from "./errors.js";
|
|
3
|
+
export { type SandboxError } from "./errors.js";
|
|
4
|
+
export { ExecError, ExecHostError, CopyError, DockerError, SyncError, PromptError, AgentError, ConfigDirError, InitError, } from "./errors.js";
|
|
5
|
+
export interface ExecResult {
|
|
6
|
+
readonly stdout: string;
|
|
7
|
+
readonly stderr: string;
|
|
8
|
+
readonly exitCode: number;
|
|
9
|
+
}
|
|
10
|
+
export interface SandboxService {
|
|
11
|
+
readonly exec: (command: string, options?: {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
}) => Effect.Effect<ExecResult, ExecError>;
|
|
14
|
+
readonly execStreaming: (command: string, onStdoutLine: (line: string) => void, options?: {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
}) => Effect.Effect<ExecResult, ExecError>;
|
|
17
|
+
readonly copyIn: (hostPath: string, sandboxPath: string) => Effect.Effect<void, CopyError>;
|
|
18
|
+
readonly copyOut: (sandboxPath: string, hostPath: string) => Effect.Effect<void, CopyError>;
|
|
19
|
+
}
|
|
20
|
+
declare const Sandbox_base: Context.TagClass<Sandbox, "Sandbox", SandboxService>;
|
|
21
|
+
export declare class Sandbox extends Sandbox_base {
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=Sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sandbox.d.ts","sourceRoot":"","sources":["../src/Sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,SAAS,EACT,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,CACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAE1C,QAAQ,CAAC,aAAa,EAAE,CACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAE1C,QAAQ,CAAC,MAAM,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEpC,QAAQ,CAAC,OAAO,EAAE,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;CACrC;;AAED,qBAAa,OAAQ,SAAQ,YAG1B;CAAG"}
|
package/dist/Sandbox.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
export { ExecError, ExecHostError, CopyError, DockerError, SyncError, PromptError, AgentError, ConfigDirError, InitError, } from "./errors.js";
|
|
3
|
+
export class Sandbox extends Context.Tag("Sandbox")() {
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=Sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sandbox.js","sourceRoot":"","sources":["../src/Sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,QAAQ,CAAC;AAIzC,OAAO,EACL,SAAS,EACT,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,GACV,MAAM,aAAa,CAAC;AA+BrB,MAAM,OAAO,OAAQ,SAAQ,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAGhD;CAAG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { FileSystem } from "@effect/platform";
|
|
3
|
+
import { CopyError, ExecError, type DockerError, type WorktreeError } from "./errors.js";
|
|
4
|
+
import { Display } from "./Display.js";
|
|
5
|
+
export interface ExecResult {
|
|
6
|
+
readonly stdout: string;
|
|
7
|
+
readonly stderr: string;
|
|
8
|
+
readonly exitCode: number;
|
|
9
|
+
}
|
|
10
|
+
export interface SandboxService {
|
|
11
|
+
readonly exec: (command: string, options?: {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
}) => Effect.Effect<ExecResult, ExecError>;
|
|
14
|
+
readonly execStreaming: (command: string, onStdoutLine: (line: string) => void, options?: {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
}) => Effect.Effect<ExecResult, ExecError>;
|
|
17
|
+
readonly copyIn: (hostPath: string, sandboxPath: string) => Effect.Effect<void, CopyError>;
|
|
18
|
+
readonly copyOut: (sandboxPath: string, hostPath: string) => Effect.Effect<void, CopyError>;
|
|
19
|
+
}
|
|
20
|
+
declare const Sandbox_base: Context.TagClass<Sandbox, "Sandbox", SandboxService>;
|
|
21
|
+
export declare class Sandbox extends Sandbox_base {
|
|
22
|
+
}
|
|
23
|
+
/** The mount point inside the container where the project worktree is bound. */
|
|
24
|
+
export declare const SANDBOX_WORKSPACE_DIR = "/home/agent/workspace";
|
|
25
|
+
export interface SandboxInfo {
|
|
26
|
+
/** Host-side path to the worktree directory (worktree mode only). */
|
|
27
|
+
readonly hostWorktreePath?: string;
|
|
28
|
+
}
|
|
29
|
+
declare const SandboxFactory_base: Context.TagClass<SandboxFactory, "SandboxFactory", {
|
|
30
|
+
readonly withSandbox: <A, E, R>(makeEffect: (info: SandboxInfo) => Effect.Effect<A, E, R | Sandbox>) => Effect.Effect<A, E | DockerError | WorktreeError, Exclude<R, Sandbox>>;
|
|
31
|
+
}>;
|
|
32
|
+
export declare class SandboxFactory extends SandboxFactory_base {
|
|
33
|
+
}
|
|
34
|
+
declare const WorktreeSandboxConfig_base: Context.TagClass<WorktreeSandboxConfig, "WorktreeSandboxConfig", {
|
|
35
|
+
readonly imageName: string;
|
|
36
|
+
readonly env: Record<string, string>;
|
|
37
|
+
readonly hostRepoDir: string;
|
|
38
|
+
/** When specified, the worktree checks out this branch. Otherwise a temp branch is created. */
|
|
39
|
+
readonly branch?: string | undefined;
|
|
40
|
+
/** Paths relative to the host repo root to copy into the worktree before container start. */
|
|
41
|
+
readonly copyToSandbox?: string[] | undefined;
|
|
42
|
+
/** When specified, the agent name is included in the auto-generated branch and worktree names. */
|
|
43
|
+
readonly agentName?: string | undefined;
|
|
44
|
+
}>;
|
|
45
|
+
export declare class WorktreeSandboxConfig extends WorktreeSandboxConfig_base {
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Worktree sandbox mode: creates a git worktree and bind-mounts it into the
|
|
49
|
+
* container at SANDBOX_WORKSPACE_DIR. The host's .git directory is also bind-mounted at
|
|
50
|
+
* its original host path so the worktree's .git file pointer resolves correctly.
|
|
51
|
+
*/
|
|
52
|
+
export declare const WorktreeDockerSandboxFactory: {
|
|
53
|
+
layer: Layer.Layer<SandboxFactory, never, Display | FileSystem.FileSystem | WorktreeSandboxConfig>;
|
|
54
|
+
};
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=SandboxFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxFactory.d.ts","sourceRoot":"","sources":["../src/SandboxFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAW9C,OAAO,EACL,SAAS,EACT,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,CACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAE1C,QAAQ,CAAC,aAAa,EAAE,CACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAE1C,QAAQ,CAAC,MAAM,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEpC,QAAQ,CAAC,OAAO,EAAE,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;CACrC;;AAED,qBAAa,OAAQ,SAAQ,YAG1B;CAAG;AAgLN,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;;2BAK0B,CAAC,EAAE,CAAC,EAAE,CAAC;;AAHlC,qBAAa,cAAe,SAAQ,mBAOjC;CAAG;;;;;IAoBF,+FAA+F;;IAE/F,6FAA6F;;IAE7F,kGAAkG;;;AAVtG,qBAAa,qBAAsB,SAAQ,0BAaxC;CAAG;AAoBN;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;CA4HxC,CAAC"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { FileSystem } from "@effect/platform";
|
|
3
|
+
import { NodeFileSystem } from "@effect/platform-node";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { createInterface } from "node:readline";
|
|
8
|
+
import { startContainer, removeContainer, chownInContainer, } from "./DockerLifecycle.js";
|
|
9
|
+
import { CopyError, ExecError, } from "./errors.js";
|
|
10
|
+
import * as WorktreeManager from "./WorktreeManager.js";
|
|
11
|
+
import { copyToSandbox } from "./CopyToSandbox.js";
|
|
12
|
+
import { Display } from "./Display.js";
|
|
13
|
+
export class Sandbox extends Context.Tag("Sandbox")() {
|
|
14
|
+
}
|
|
15
|
+
const makeDockerSandbox = (containerName) => Effect.gen(function* () {
|
|
16
|
+
const fs = yield* FileSystem.FileSystem;
|
|
17
|
+
return {
|
|
18
|
+
exec: (command, options) => Effect.async((resume) => {
|
|
19
|
+
const args = ["exec"];
|
|
20
|
+
if (options?.cwd) {
|
|
21
|
+
args.push("-w", options.cwd);
|
|
22
|
+
}
|
|
23
|
+
args.push(containerName, "sh", "-c", command);
|
|
24
|
+
execFile("docker", args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
25
|
+
if (error && error.code === undefined) {
|
|
26
|
+
resume(Effect.fail(new ExecError({
|
|
27
|
+
command,
|
|
28
|
+
message: `docker exec failed: ${error.message}`,
|
|
29
|
+
})));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
resume(Effect.succeed({
|
|
33
|
+
stdout: stdout.toString(),
|
|
34
|
+
stderr: stderr.toString(),
|
|
35
|
+
exitCode: typeof error?.code === "number"
|
|
36
|
+
? error.code
|
|
37
|
+
: 0,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}),
|
|
42
|
+
execStreaming: (command, onStdoutLine, options) => Effect.async((resume) => {
|
|
43
|
+
const args = ["exec"];
|
|
44
|
+
if (options?.cwd) {
|
|
45
|
+
args.push("-w", options.cwd);
|
|
46
|
+
}
|
|
47
|
+
args.push(containerName, "sh", "-c", command);
|
|
48
|
+
const proc = spawn("docker", args, {
|
|
49
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
50
|
+
});
|
|
51
|
+
const stdoutChunks = [];
|
|
52
|
+
const stderrChunks = [];
|
|
53
|
+
const rl = createInterface({ input: proc.stdout });
|
|
54
|
+
rl.on("line", (line) => {
|
|
55
|
+
stdoutChunks.push(line);
|
|
56
|
+
onStdoutLine(line);
|
|
57
|
+
});
|
|
58
|
+
proc.stderr.on("data", (chunk) => {
|
|
59
|
+
stderrChunks.push(chunk.toString());
|
|
60
|
+
});
|
|
61
|
+
proc.on("error", (error) => {
|
|
62
|
+
resume(Effect.fail(new ExecError({
|
|
63
|
+
command,
|
|
64
|
+
message: `docker exec streaming failed: ${error.message}`,
|
|
65
|
+
})));
|
|
66
|
+
});
|
|
67
|
+
proc.on("close", (code) => {
|
|
68
|
+
resume(Effect.succeed({
|
|
69
|
+
stdout: stdoutChunks.join("\n"),
|
|
70
|
+
stderr: stderrChunks.join(""),
|
|
71
|
+
exitCode: code ?? 0,
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
}),
|
|
75
|
+
copyIn: (hostPath, sandboxPath) => Effect.gen(function* () {
|
|
76
|
+
const parentDir = dirname(sandboxPath);
|
|
77
|
+
yield* Effect.async((resume) => {
|
|
78
|
+
execFile("docker", ["exec", containerName, "mkdir", "-p", parentDir], (error) => {
|
|
79
|
+
if (error) {
|
|
80
|
+
resume(Effect.fail(new CopyError({
|
|
81
|
+
message: `Failed to create dir ${parentDir}: ${error.message}`,
|
|
82
|
+
})));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
resume(Effect.succeed(undefined));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
yield* Effect.async((resume) => {
|
|
90
|
+
execFile("docker", ["cp", hostPath, `${containerName}:${sandboxPath}`], (error) => {
|
|
91
|
+
if (error) {
|
|
92
|
+
resume(Effect.fail(new CopyError({
|
|
93
|
+
message: `Failed to copy ${hostPath} -> ${containerName}:${sandboxPath}: ${error.message}`,
|
|
94
|
+
})));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
resume(Effect.succeed(undefined));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}),
|
|
102
|
+
copyOut: (sandboxPath, hostPath) => Effect.gen(function* () {
|
|
103
|
+
yield* fs.makeDirectory(dirname(hostPath), { recursive: true }).pipe(Effect.mapError((error) => new CopyError({
|
|
104
|
+
message: `Failed to create host dir ${dirname(hostPath)}: ${error}`,
|
|
105
|
+
})));
|
|
106
|
+
yield* Effect.async((resume) => {
|
|
107
|
+
execFile("docker", ["cp", `${containerName}:${sandboxPath}`, hostPath], (error) => {
|
|
108
|
+
if (error) {
|
|
109
|
+
resume(Effect.fail(new CopyError({
|
|
110
|
+
message: `Failed to copy ${containerName}:${sandboxPath} -> ${hostPath}: ${error.message}`,
|
|
111
|
+
})));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
resume(Effect.succeed(undefined));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
const makeDockerSandboxLayer = (containerName) => Layer.effect(Sandbox, makeDockerSandbox(containerName)).pipe(Layer.provide(NodeFileSystem.layer));
|
|
122
|
+
/** The mount point inside the container where the project worktree is bound. */
|
|
123
|
+
export const SANDBOX_WORKSPACE_DIR = "/home/agent/workspace";
|
|
124
|
+
export class SandboxFactory extends Context.Tag("SandboxFactory")() {
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Synchronously force-remove a Docker container.
|
|
128
|
+
* Used in process exit handlers where async operations are not possible.
|
|
129
|
+
*/
|
|
130
|
+
const forceRemoveContainerSync = (containerName) => {
|
|
131
|
+
try {
|
|
132
|
+
execFileSync("docker", ["rm", "-f", containerName], { stdio: "ignore" });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Best-effort — container may already be gone
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
export class WorktreeSandboxConfig extends Context.Tag("WorktreeSandboxConfig")() {
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Synchronously force-remove a git worktree.
|
|
142
|
+
* Used in process exit handlers where async operations are not possible.
|
|
143
|
+
*/
|
|
144
|
+
const forceRemoveWorktreeSync = (worktreePath, repoDir) => {
|
|
145
|
+
try {
|
|
146
|
+
execFileSync("git", ["worktree", "remove", "--force", worktreePath], {
|
|
147
|
+
stdio: "ignore",
|
|
148
|
+
cwd: repoDir,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Best-effort — worktree may already be gone
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Worktree sandbox mode: creates a git worktree and bind-mounts it into the
|
|
157
|
+
* container at SANDBOX_WORKSPACE_DIR. The host's .git directory is also bind-mounted at
|
|
158
|
+
* its original host path so the worktree's .git file pointer resolves correctly.
|
|
159
|
+
*/
|
|
160
|
+
export const WorktreeDockerSandboxFactory = {
|
|
161
|
+
layer: Layer.effect(SandboxFactory, Effect.gen(function* () {
|
|
162
|
+
const { imageName, env, hostRepoDir, branch, copyToSandbox: copyPaths, agentName, } = yield* WorktreeSandboxConfig;
|
|
163
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
164
|
+
const display = yield* Display;
|
|
165
|
+
return {
|
|
166
|
+
withSandbox: (makeEffect) => {
|
|
167
|
+
const containerName = `sandcastle-${randomUUID()}`;
|
|
168
|
+
return Effect.acquireUseRelease(
|
|
169
|
+
// Acquire: prune stale worktrees (best-effort), create worktree, then start container
|
|
170
|
+
WorktreeManager.pruneStale(hostRepoDir)
|
|
171
|
+
.pipe(Effect.catchAll((e) => Effect.sync(() => {
|
|
172
|
+
console.error("[sandcastle] Warning: failed to prune stale worktrees:", e.message);
|
|
173
|
+
})))
|
|
174
|
+
.pipe(Effect.andThen(branch
|
|
175
|
+
? WorktreeManager.create(hostRepoDir, { branch })
|
|
176
|
+
: WorktreeManager.create(hostRepoDir, { agentName })))
|
|
177
|
+
.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem))
|
|
178
|
+
.pipe(Effect.flatMap((worktreeInfo) => (copyPaths && copyPaths.length > 0
|
|
179
|
+
? display.spinner("Copying to sandbox", copyToSandbox(copyPaths, hostRepoDir, worktreeInfo.path))
|
|
180
|
+
: Effect.succeed(undefined)).pipe(Effect.map(() => worktreeInfo))))
|
|
181
|
+
.pipe(Effect.flatMap((worktreeInfo) => {
|
|
182
|
+
const gitDir = join(hostRepoDir, ".git");
|
|
183
|
+
const volumeMounts = [
|
|
184
|
+
`${worktreeInfo.path}:${SANDBOX_WORKSPACE_DIR}`,
|
|
185
|
+
`${gitDir}:${gitDir}`,
|
|
186
|
+
];
|
|
187
|
+
const cleanup = () => {
|
|
188
|
+
forceRemoveContainerSync(containerName);
|
|
189
|
+
forceRemoveWorktreeSync(worktreeInfo.path, hostRepoDir);
|
|
190
|
+
};
|
|
191
|
+
const onSignal = () => {
|
|
192
|
+
cleanup();
|
|
193
|
+
process.exit(1);
|
|
194
|
+
};
|
|
195
|
+
const hostUid = process.getuid?.() ?? 1000;
|
|
196
|
+
const hostGid = process.getgid?.() ?? 1000;
|
|
197
|
+
return startContainer(containerName, imageName, env, {
|
|
198
|
+
volumeMounts,
|
|
199
|
+
workdir: SANDBOX_WORKSPACE_DIR,
|
|
200
|
+
user: `${hostUid}:${hostGid}`,
|
|
201
|
+
}).pipe(Effect.andThen(chownInContainer(containerName, `${hostUid}:${hostGid}`, "/home/agent")), Effect.tap(() => Effect.sync(() => {
|
|
202
|
+
process.on("exit", cleanup);
|
|
203
|
+
process.on("SIGINT", onSignal);
|
|
204
|
+
process.on("SIGTERM", onSignal);
|
|
205
|
+
})), Effect.map(() => ({ worktreeInfo, cleanup, onSignal })));
|
|
206
|
+
})),
|
|
207
|
+
// Use
|
|
208
|
+
({ worktreeInfo }) => makeEffect({ hostWorktreePath: worktreeInfo.path }).pipe(Effect.provide(makeDockerSandboxLayer(containerName))),
|
|
209
|
+
// Release: remove container, then remove worktree
|
|
210
|
+
({ worktreeInfo, cleanup, onSignal }) => Effect.sync(() => {
|
|
211
|
+
process.removeListener("exit", cleanup);
|
|
212
|
+
process.removeListener("SIGINT", onSignal);
|
|
213
|
+
process.removeListener("SIGTERM", onSignal);
|
|
214
|
+
}).pipe(Effect.andThen(removeContainer(containerName)), Effect.andThen(WorktreeManager.remove(worktreeInfo.path)), Effect.orDie));
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
})),
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=SandboxFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxFactory.js","sourceRoot":"","sources":["../src/SandboxFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Display } from "./Display.js";
|
|
3
|
+
import { type SandboxError } from "./errors.js";
|
|
4
|
+
import { Sandbox, type SandboxService } from "./SandboxFactory.js";
|
|
5
|
+
export type SandboxHooks = {
|
|
6
|
+
readonly onSandboxReady?: ReadonlyArray<{
|
|
7
|
+
readonly command: string;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
export interface SandboxLifecycleOptions {
|
|
11
|
+
readonly hostRepoDir: string;
|
|
12
|
+
readonly sandboxRepoDir: string;
|
|
13
|
+
readonly hooks?: SandboxHooks;
|
|
14
|
+
readonly branch?: string;
|
|
15
|
+
/** Host-side path to the worktree directory. Required when sandboxRepoDir
|
|
16
|
+
* is a container path that doesn't exist on the host (e.g. /home/agent/workspace). */
|
|
17
|
+
readonly hostWorktreePath?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SandboxContext {
|
|
20
|
+
readonly sandbox: SandboxService;
|
|
21
|
+
readonly sandboxRepoDir: string;
|
|
22
|
+
readonly baseHead: string;
|
|
23
|
+
}
|
|
24
|
+
export interface SandboxLifecycleResult<A> {
|
|
25
|
+
readonly result: A;
|
|
26
|
+
readonly branch: string;
|
|
27
|
+
readonly commits: {
|
|
28
|
+
sha: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare const withSandboxLifecycle: <A>(options: SandboxLifecycleOptions, work: (ctx: SandboxContext) => Effect.Effect<A, SandboxError, Display | Sandbox>) => Effect.Effect<SandboxLifecycleResult<A>, SandboxError, Display | Sandbox>;
|
|
32
|
+
//# sourceMappingURL=SandboxLifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxLifecycle.d.ts","sourceRoot":"","sources":["../src/SandboxLifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EACL,OAAO,EAEP,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAoB7B,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;2FACuF;IACvF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,kMA6LlC,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { Display } from "./Display.js";
|
|
5
|
+
import { ExecError, SyncError } from "./errors.js";
|
|
6
|
+
import { Sandbox, } from "./SandboxFactory.js";
|
|
7
|
+
const execOk = (sandbox, command, options) => Effect.flatMap(sandbox.exec(command, options), (result) => result.exitCode !== 0
|
|
8
|
+
? Effect.fail(new ExecError({
|
|
9
|
+
command,
|
|
10
|
+
message: `Command failed (exit ${result.exitCode}): ${command}\n${result.stderr}`,
|
|
11
|
+
}))
|
|
12
|
+
: Effect.succeed(result));
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
export const withSandboxLifecycle = (options, work) => Effect.gen(function* () {
|
|
15
|
+
const sandbox = yield* Sandbox;
|
|
16
|
+
const display = yield* Display;
|
|
17
|
+
const { hostRepoDir, sandboxRepoDir, hooks, branch, hostWorktreePath } = options;
|
|
18
|
+
// Without an explicit branch, record host's current branch for cherry-pick
|
|
19
|
+
const hostCurrentBranch = !branch
|
|
20
|
+
? yield* Effect.promise(async () => {
|
|
21
|
+
const { stdout } = await execAsync("git rev-parse --abbrev-ref HEAD", { cwd: hostRepoDir });
|
|
22
|
+
return stdout.trim();
|
|
23
|
+
})
|
|
24
|
+
: null;
|
|
25
|
+
// Read host git identity before entering the sandbox
|
|
26
|
+
const [hostGitName, hostGitEmail] = yield* Effect.promise(async () => {
|
|
27
|
+
const [nameResult, emailResult] = await Promise.all([
|
|
28
|
+
execAsync("git config user.name", { cwd: hostRepoDir })
|
|
29
|
+
.then((r) => r.stdout.trim())
|
|
30
|
+
.catch(() => ""),
|
|
31
|
+
execAsync("git config user.email", { cwd: hostRepoDir })
|
|
32
|
+
.then((r) => r.stdout.trim())
|
|
33
|
+
.catch(() => ""),
|
|
34
|
+
]);
|
|
35
|
+
return [nameResult, emailResult];
|
|
36
|
+
});
|
|
37
|
+
// Setup: onSandboxReady hooks
|
|
38
|
+
let resolvedBranch = "";
|
|
39
|
+
yield* display.taskLog("Setting up sandbox", (message) => Effect.gen(function* () {
|
|
40
|
+
// The bind-mounted worktree may be owned by a different UID (host user
|
|
41
|
+
// vs container user). Mark it safe so git doesn't reject it with
|
|
42
|
+
// "dubious ownership".
|
|
43
|
+
yield* execOk(sandbox, `git config --global --add safe.directory "${sandboxRepoDir}"`);
|
|
44
|
+
// Propagate host git identity into the sandbox so commits are attributed
|
|
45
|
+
// to the actual developer without requiring manual setup.
|
|
46
|
+
if (hostGitName) {
|
|
47
|
+
yield* execOk(sandbox, `git config --global user.name "${hostGitName.replace(/"/g, '\\"')}"`);
|
|
48
|
+
}
|
|
49
|
+
if (hostGitEmail) {
|
|
50
|
+
yield* execOk(sandbox, `git config --global user.email "${hostGitEmail.replace(/"/g, '\\"')}"`);
|
|
51
|
+
}
|
|
52
|
+
// Repo is bind-mounted — discover branch directly
|
|
53
|
+
resolvedBranch = (yield* execOk(sandbox, "git rev-parse --abbrev-ref HEAD", { cwd: sandboxRepoDir })).stdout.trim();
|
|
54
|
+
if (hooks?.onSandboxReady?.length) {
|
|
55
|
+
for (const hook of hooks.onSandboxReady) {
|
|
56
|
+
message(hook.command);
|
|
57
|
+
yield* execOk(sandbox, hook.command, { cwd: sandboxRepoDir });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
const targetBranch = branch ?? resolvedBranch;
|
|
62
|
+
// Record base HEAD
|
|
63
|
+
const baseHead = (yield* execOk(sandbox, "git rev-parse HEAD", {
|
|
64
|
+
cwd: sandboxRepoDir,
|
|
65
|
+
})).stdout.trim();
|
|
66
|
+
// Run the caller's work
|
|
67
|
+
const result = yield* work({ sandbox, sandboxRepoDir, baseHead });
|
|
68
|
+
// Collect commits and handle cherry-pick for temp branches
|
|
69
|
+
let commits;
|
|
70
|
+
let finalBranch;
|
|
71
|
+
// For host-side git operations in worktree mode, use hostWorktreePath
|
|
72
|
+
// (the real path on the host) instead of sandboxRepoDir (which may be a container path
|
|
73
|
+
// like /home/agent/workspace that doesn't exist on the host).
|
|
74
|
+
const hostSideWorktreePath = hostWorktreePath ?? sandboxRepoDir;
|
|
75
|
+
if (hostCurrentBranch !== null) {
|
|
76
|
+
// Temp branch mode: fast-forward host branch to temp branch, then delete temp branch.
|
|
77
|
+
// We use fast-forward instead of cherry-pick because cherry-pick breaks when the
|
|
78
|
+
// temp branch contains merge commits (e.g. a merge agent merging multiple parallel
|
|
79
|
+
// branches). The temp branch is always a direct descendant of hostCurrentBranch,
|
|
80
|
+
// so fast-forward is always valid.
|
|
81
|
+
// Check if there are any new commits on the temp branch
|
|
82
|
+
const hasNewCommits = yield* Effect.promise(async () => {
|
|
83
|
+
try {
|
|
84
|
+
const { stdout } = await execAsync(`git rev-list "${baseHead}..HEAD" --count`, { cwd: hostSideWorktreePath });
|
|
85
|
+
return parseInt(stdout.trim(), 10) > 0;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// Detach the worktree from the temp branch so the branch can be deleted
|
|
92
|
+
yield* execOk(sandbox, "git checkout --detach", { cwd: sandboxRepoDir });
|
|
93
|
+
if (hasNewCommits) {
|
|
94
|
+
// Fast-forward host's current branch to the temp branch
|
|
95
|
+
yield* Effect.tryPromise({
|
|
96
|
+
try: async () => {
|
|
97
|
+
try {
|
|
98
|
+
await execAsync(`git merge --ff-only "${resolvedBranch}"`, {
|
|
99
|
+
cwd: hostRepoDir,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new Error(`Fast-forward merge of '${resolvedBranch}' onto '${hostCurrentBranch}' failed. ` +
|
|
104
|
+
`The temporary branch '${resolvedBranch}' has been preserved. ` +
|
|
105
|
+
`To retry: git merge --ff-only ${resolvedBranch}, ` +
|
|
106
|
+
`then clean up: git branch -D ${resolvedBranch}`);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
catch: (e) => new SyncError({
|
|
110
|
+
message: String(e instanceof Error ? e.message : e),
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Delete the temp branch (now merged into host branch)
|
|
115
|
+
yield* Effect.promise(() => execAsync(`git branch -D "${resolvedBranch}"`, {
|
|
116
|
+
cwd: hostRepoDir,
|
|
117
|
+
}).catch(() => { }));
|
|
118
|
+
// Collect the commits now on the host branch
|
|
119
|
+
commits = yield* Effect.promise(async () => {
|
|
120
|
+
try {
|
|
121
|
+
const { stdout } = await execAsync(`git rev-list "${baseHead}..HEAD" --reverse`, { cwd: hostRepoDir });
|
|
122
|
+
const lines = stdout.trim();
|
|
123
|
+
if (!lines)
|
|
124
|
+
return [];
|
|
125
|
+
return lines.split("\n").map((sha) => ({ sha }));
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
finalBranch = hostCurrentBranch;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Explicit branch: commits stay on that branch
|
|
135
|
+
commits = yield* Effect.promise(async () => {
|
|
136
|
+
try {
|
|
137
|
+
const { stdout } = await execAsync(`git rev-list "${baseHead}..refs/heads/${targetBranch}" --reverse`, { cwd: hostRepoDir });
|
|
138
|
+
const lines = stdout.trim();
|
|
139
|
+
if (!lines)
|
|
140
|
+
return [];
|
|
141
|
+
return lines.split("\n").map((sha) => ({ sha }));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Branch doesn't exist on host (no commits were produced)
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
finalBranch = targetBranch;
|
|
149
|
+
}
|
|
150
|
+
return { result, branch: finalBranch, commits };
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=SandboxLifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxLifecycle.js","sourceRoot":"","sources":["../src/SandboxLifecycle.ts"],"names":[],"mappings":"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"}
|