@snappedly-tools/shipyard 0.2.0
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/LICENSE +59 -0
- package/NOTICE +4 -0
- package/README.md +197 -0
- package/dist/MountConfig-bZoCs4Dd.d.ts +26 -0
- package/dist/SandboxProvider-KeFp_nju.d.ts +257 -0
- package/dist/chunk-4VDXZY7T.js +27624 -0
- package/dist/chunk-4VDXZY7T.js.map +1 -0
- package/dist/chunk-BU6XJFJ2.js +90 -0
- package/dist/chunk-BU6XJFJ2.js.map +1 -0
- package/dist/chunk-CP6YEQKU.js +42 -0
- package/dist/chunk-CP6YEQKU.js.map +1 -0
- package/dist/chunk-OAHPDSOJ.js +407 -0
- package/dist/chunk-OAHPDSOJ.js.map +1 -0
- package/dist/chunk-SRUH232X.js +26712 -0
- package/dist/chunk-SRUH232X.js.map +1 -0
- package/dist/chunk-YVJHSJUW.js +127 -0
- package/dist/chunk-YVJHSJUW.js.map +1 -0
- package/dist/index.d.ts +2780 -0
- package/dist/index.js +9843 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +20475 -0
- package/dist/main.js.map +1 -0
- package/dist/sandboxes/docker.d.ts +125 -0
- package/dist/sandboxes/docker.js +9 -0
- package/dist/sandboxes/docker.js.map +1 -0
- package/dist/sandboxes/no-sandbox.d.ts +37 -0
- package/dist/sandboxes/no-sandbox.js +7 -0
- package/dist/sandboxes/no-sandbox.js.map +1 -0
- package/dist/sandboxes/vercel.d.ts +104 -0
- package/dist/sandboxes/vercel.js +188 -0
- package/dist/sandboxes/vercel.js.map +1 -0
- package/dist/templates/blank/main.mts +13 -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.mts +205 -0
- package/dist/templates/parallel-planner/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner/plan-prompt.md +37 -0
- package/dist/templates/parallel-planner/template.json +4 -0
- package/dist/templates/parallel-planner-with-review/CODING_STANDARDS.md +27 -0
- package/dist/templates/parallel-planner-with-review/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner-with-review/main.mts +227 -0
- package/dist/templates/parallel-planner-with-review/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +37 -0
- package/dist/templates/parallel-planner-with-review/review-prompt.md +55 -0
- package/dist/templates/parallel-planner-with-review/template.json +4 -0
- package/dist/templates/sequential-reviewer/CODING_STANDARDS.md +27 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +53 -0
- package/dist/templates/sequential-reviewer/main.mts +120 -0
- package/dist/templates/sequential-reviewer/review-prompt.md +55 -0
- package/dist/templates/sequential-reviewer/template.json +4 -0
- package/dist/templates/simple-loop/main.mts +51 -0
- package/dist/templates/simple-loop/prompt.md +53 -0
- package/dist/templates/simple-loop/template.json +4 -0
- package/dist/workflow/coordinator/migrations/001_initial.sql +131 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# PolyForm Strict License 1.0.0
|
|
2
|
+
|
|
3
|
+
<https://polyformproject.org/licenses/strict/1.0.0>
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose, other than distributing the software or making changes or new works based on the software.
|
|
12
|
+
|
|
13
|
+
## Patent License
|
|
14
|
+
|
|
15
|
+
The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
|
|
16
|
+
|
|
17
|
+
## Noncommercial Purposes
|
|
18
|
+
|
|
19
|
+
Any noncommercial purpose is a permitted purpose.
|
|
20
|
+
|
|
21
|
+
## Personal Uses
|
|
22
|
+
|
|
23
|
+
Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, is use for a permitted purpose.
|
|
24
|
+
|
|
25
|
+
## Noncommercial Organizations
|
|
26
|
+
|
|
27
|
+
Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution is use for a permitted purpose regardless of the source of funding or obligations resulting from the funding.
|
|
28
|
+
|
|
29
|
+
## Fair Use
|
|
30
|
+
|
|
31
|
+
You may have "fair use" rights for the software under the law. These terms do not limit them.
|
|
32
|
+
|
|
33
|
+
## No Other Rights
|
|
34
|
+
|
|
35
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
|
|
36
|
+
|
|
37
|
+
## Patent Defense
|
|
38
|
+
|
|
39
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
40
|
+
|
|
41
|
+
## Violations
|
|
42
|
+
|
|
43
|
+
The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
|
|
44
|
+
|
|
45
|
+
## No Liability
|
|
46
|
+
|
|
47
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
48
|
+
|
|
49
|
+
## Definitions
|
|
50
|
+
|
|
51
|
+
The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
|
|
52
|
+
|
|
53
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
54
|
+
|
|
55
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
56
|
+
|
|
57
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
58
|
+
|
|
59
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
package/NOTICE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Shipyard
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/brand/shipyard-robot-on-boat.png" alt="A friendly robot steering a blue boat for Shipyard" width="320">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
> **Source-available toolkit for running AI coding agents in isolated sandboxes.**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/snappedly/shipyard/actions/workflows/ci.yml)
|
|
10
|
+
|
|
11
|
+
Shipyard runs AI coding agents in isolated sandboxes. Choose an agent, a
|
|
12
|
+
sandbox provider, and a prompt; Shipyard manages the sandbox lifecycle,
|
|
13
|
+
branches, logs, sessions, and commits.
|
|
14
|
+
|
|
15
|
+
## Why Shipyard
|
|
16
|
+
|
|
17
|
+
- **Isolated by default.** Keep agent work inside Docker locally or Vercel
|
|
18
|
+
Sandbox remotely.
|
|
19
|
+
- **Purpose-built agents.** Use Codex or Claude Code without carrying support
|
|
20
|
+
for unrelated coding-agent CLIs.
|
|
21
|
+
- **Reviewable changes.** Control how branches and worktrees move changes back
|
|
22
|
+
to the host repository.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
You need Node.js, Git, Docker, and credentials for your chosen coding agent.
|
|
27
|
+
Run these commands inside the Git repository the agent should change:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install --save-dev @snappedly-tools/shipyard
|
|
31
|
+
npx shipyard init
|
|
32
|
+
cp .shipyard/.env.example .shipyard/.env
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`init` asks which agent, sandbox provider, issue tracker, and starter template
|
|
36
|
+
to use. Start with the `blank` template. Put the requested credentials in
|
|
37
|
+
`.shipyard/.env`, then write one concrete task in `.shipyard/prompt.md`.
|
|
38
|
+
|
|
39
|
+
Run it:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npx shipyard run
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`shipyard run` builds the generated Docker image, executes the
|
|
46
|
+
generated TypeScript entry point, and cleans up the sandbox. Reuse the cached
|
|
47
|
+
image when its definition has not changed:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npx shipyard run --skip-build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Inspect the generated branch strategy and start from a clean working tree. A
|
|
54
|
+
completion marker means the agent stopped; inspect its commits and run the
|
|
55
|
+
repository's checks before keeping the result.
|
|
56
|
+
|
|
57
|
+
## JavaScript API
|
|
58
|
+
|
|
59
|
+
The generated `.shipyard/main.ts` or `.shipyard/main.mts` is ordinary
|
|
60
|
+
TypeScript:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { CODEX_MODELS, codex, run } from "@snappedly-tools/shipyard";
|
|
64
|
+
import { docker } from "@snappedly-tools/shipyard/sandboxes/docker";
|
|
65
|
+
|
|
66
|
+
const result = await run({
|
|
67
|
+
agent: codex(CODEX_MODELS.routine),
|
|
68
|
+
sandbox: docker(),
|
|
69
|
+
promptFile: ".shipyard/prompt.md",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(result.commits);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use a dedicated branch when the scaffold has already been committed:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const result = await run({
|
|
79
|
+
agent: codex(CODEX_MODELS.routine),
|
|
80
|
+
sandbox: docker(),
|
|
81
|
+
promptFile: ".shipyard/prompt.md",
|
|
82
|
+
branchStrategy: { type: "branch", branch: "agent/my-task" },
|
|
83
|
+
maxIterations: 1,
|
|
84
|
+
logging: { type: "stdout" },
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Shipyard supports Codex and Claude Code agents, with Docker, Vercel Sandbox,
|
|
89
|
+
and no-sandbox providers. `shipyard init` scaffolds Docker; configure Vercel
|
|
90
|
+
Sandbox or no-sandbox mode through the JavaScript interface.
|
|
91
|
+
|
|
92
|
+
To run directly on the host without isolation:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { CODEX_MODELS, codex, run } from "@snappedly-tools/shipyard";
|
|
96
|
+
import { noSandbox } from "@snappedly-tools/shipyard/sandboxes/no-sandbox";
|
|
97
|
+
|
|
98
|
+
const result = await run({
|
|
99
|
+
agent: codex(CODEX_MODELS.routine),
|
|
100
|
+
sandbox: noSandbox(),
|
|
101
|
+
promptFile: ".shipyard/prompt.md",
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
No-sandbox mode grants the agent the permissions of the Shipyard process. Use
|
|
106
|
+
it only with trusted repositories and prompts.
|
|
107
|
+
|
|
108
|
+
## Branch behavior
|
|
109
|
+
|
|
110
|
+
Docker keeps the repository and Git metadata inside the sandbox. It starts
|
|
111
|
+
from committed history and syncs changes back. The generated blank
|
|
112
|
+
template omits `branchStrategy`, which defaults to `merge-to-head`: commits
|
|
113
|
+
are transferred to a temporary host branch and merged into your current
|
|
114
|
+
branch. Direct `head` mode is not supported by Docker or Vercel Sandbox.
|
|
115
|
+
|
|
116
|
+
The explicit `branch` strategy keeps commits on the named branch for review.
|
|
117
|
+
Commit input files before either strategy, or use `copyToWorktree` for specific
|
|
118
|
+
untracked or ignored files.
|
|
119
|
+
|
|
120
|
+
## Authentication
|
|
121
|
+
|
|
122
|
+
`shipyard init` generates the environment variables and mounts required by
|
|
123
|
+
the chosen agent. Keep `.shipyard/.env`, agent login files, logs, and recovery
|
|
124
|
+
patches private.
|
|
125
|
+
|
|
126
|
+
For Codex, choose either:
|
|
127
|
+
|
|
128
|
+
- ChatGPT authentication: select `chatgpt` during init and complete the
|
|
129
|
+
browser login. The generated configuration mounts `~/.codex/auth.json`
|
|
130
|
+
read-only.
|
|
131
|
+
- API authentication: select `api-key` and put `OPENAI_API_KEY` in
|
|
132
|
+
`.shipyard/.env`.
|
|
133
|
+
|
|
134
|
+
For Claude Code, run `claude setup-token` and put the resulting value in the
|
|
135
|
+
generated `CLAUDE_CODE_OAUTH_TOKEN` entry.
|
|
136
|
+
|
|
137
|
+
## Generated files
|
|
138
|
+
|
|
139
|
+
| Path | Purpose |
|
|
140
|
+
| --------------------------------- | ------------------------------------------------------------ |
|
|
141
|
+
| `.shipyard/main.ts` or `main.mts` | Agent, sandbox, prompt, branch, hook, and iteration settings |
|
|
142
|
+
| `.shipyard/prompt.md` | Task given to the agent |
|
|
143
|
+
| `.shipyard/Dockerfile` | Sandbox image definition |
|
|
144
|
+
| `.shipyard/.env` | Untracked credentials passed into the sandbox |
|
|
145
|
+
| `.shipyard/logs/` | Run logs |
|
|
146
|
+
| `.shipyard/worktrees/` | Worktrees for separate-branch runs |
|
|
147
|
+
| `.shipyard/patches/` | Recovery artifacts preserved after some failures |
|
|
148
|
+
|
|
149
|
+
## Workflow templates
|
|
150
|
+
|
|
151
|
+
The bundled templates are:
|
|
152
|
+
|
|
153
|
+
- `blank`: one agent and your prompt;
|
|
154
|
+
- `simple-loop`: process issue-tracker tasks sequentially;
|
|
155
|
+
- `sequential-reviewer`: implement and review each task;
|
|
156
|
+
- `parallel-planner`: plan parallel work and merge its branches; and
|
|
157
|
+
- `parallel-planner-with-review`: add review to each parallel branch.
|
|
158
|
+
|
|
159
|
+
Read generated prompts before running an issue or merge workflow. Those
|
|
160
|
+
templates can close issues, create branches, and merge work.
|
|
161
|
+
|
|
162
|
+
The package also exports contracts for triage, implementation, review,
|
|
163
|
+
repair, handoff, and release recording. They are building blocks for hosted
|
|
164
|
+
automation, not a hosted service started by the quick-start commands.
|
|
165
|
+
|
|
166
|
+
## Security
|
|
167
|
+
|
|
168
|
+
Agents receive the permissions granted by their sandbox, mounts, credentials,
|
|
169
|
+
and hooks. Docker isolates repository and Git storage; it does not
|
|
170
|
+
mount the host checkout or Git metadata automatically. Explicit mounts must
|
|
171
|
+
stay outside the sandbox workspace and its ancestors.
|
|
172
|
+
|
|
173
|
+
Mounted credentials, devices, Docker sockets, groups, and network access can
|
|
174
|
+
still broaden an agent's access. Host hooks, no-sandbox mode, and custom
|
|
175
|
+
provider code retain their own trust requirements. Use
|
|
176
|
+
least-privilege credentials and review generated configuration before running
|
|
177
|
+
untrusted code.
|
|
178
|
+
|
|
179
|
+
See the [security evaluation](docs/security-evaluation.md) for findings,
|
|
180
|
+
fixes, and validation limits. Report vulnerabilities according to
|
|
181
|
+
[SECURITY.md](SECURITY.md).
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). The standard repository check is:
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
npm ci
|
|
189
|
+
npm run check
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
Shipyard is source-available under the
|
|
195
|
+
[PolyForm Strict License 1.0.0](LICENSE). It is available for permitted
|
|
196
|
+
noncommercial use; the license does not permit redistribution, modification,
|
|
197
|
+
or derivative works. Contact Snappedly to request a commercial license.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-facing mount configuration for bind-mount sandbox providers.
|
|
3
|
+
*
|
|
4
|
+
* Each entry describes a host directory to mount into the sandbox container.
|
|
5
|
+
*/
|
|
6
|
+
/** A single bind-mount descriptor for the Docker provider. */
|
|
7
|
+
interface MountConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Path on the host. Supports:
|
|
10
|
+
* - Absolute paths (`/data/cache`)
|
|
11
|
+
* - Tilde-expanded paths (`~/data` → `<home>/data`)
|
|
12
|
+
* - Relative paths (`data` or `./data`) — resolved from `process.cwd()`
|
|
13
|
+
*/
|
|
14
|
+
readonly hostPath: string;
|
|
15
|
+
/**
|
|
16
|
+
* Path inside the sandbox container. Supports:
|
|
17
|
+
* - Absolute paths (`/mnt/data`)
|
|
18
|
+
* - Tilde-expanded paths (`~/.npm` → `/home/agent/.npm`) — expanded using the provider's sandbox home directory
|
|
19
|
+
* - Relative paths (`data` or `./data`) — resolved from the worktree directory (`/home/agent/workspace`)
|
|
20
|
+
*/
|
|
21
|
+
readonly sandboxPath: string;
|
|
22
|
+
/** Mount as read-only. Defaults to `false`. */
|
|
23
|
+
readonly readonly?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type { MountConfig as M };
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox provider types — the pluggable interface for sandbox runtimes.
|
|
3
|
+
*
|
|
4
|
+
* Provider authors implement a small Promise-based interface. Shipyard
|
|
5
|
+
* handles worktree creation, git mount resolution, and commit extraction.
|
|
6
|
+
*/
|
|
7
|
+
/** Result of executing a command inside a sandbox. */
|
|
8
|
+
interface ExecResult {
|
|
9
|
+
readonly stdout: string;
|
|
10
|
+
readonly stderr: string;
|
|
11
|
+
readonly exitCode: number;
|
|
12
|
+
}
|
|
13
|
+
/** Options for interactiveExec — the streams the provider should wire to the spawned process. */
|
|
14
|
+
interface InteractiveExecOptions {
|
|
15
|
+
readonly stdin: NodeJS.ReadableStream;
|
|
16
|
+
readonly stdout: NodeJS.WritableStream;
|
|
17
|
+
readonly stderr: NodeJS.WritableStream;
|
|
18
|
+
readonly cwd?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Handle to a running bind-mount sandbox. */
|
|
21
|
+
interface BindMountSandboxHandle {
|
|
22
|
+
/** Absolute path to the worktree inside the sandbox. */
|
|
23
|
+
readonly worktreePath: string;
|
|
24
|
+
/**
|
|
25
|
+
* Execute a command in the sandbox.
|
|
26
|
+
*
|
|
27
|
+
* Implementations MUST support line-by-line streaming via `onLine`. This is
|
|
28
|
+
* how Shipyard delivers live feedback to the user and enforces idle timeouts —
|
|
29
|
+
* without a streaming implementation, neither will work. A buffered/batch
|
|
30
|
+
* implementation that only calls `onLine` after the process exits does NOT
|
|
31
|
+
* satisfy this contract.
|
|
32
|
+
*
|
|
33
|
+
* When `stdin` is set, the implementation pipes the string to the child
|
|
34
|
+
* process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
|
|
35
|
+
*/
|
|
36
|
+
exec(command: string, options?: {
|
|
37
|
+
onLine?: (line: string) => void;
|
|
38
|
+
cwd?: string;
|
|
39
|
+
sudo?: boolean;
|
|
40
|
+
stdin?: string;
|
|
41
|
+
/** Abort the command when the caller's operation is cancelled. */
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
/** Reject/terminate the command after this many combined output bytes. */
|
|
44
|
+
maxOutputBytes?: number;
|
|
45
|
+
}): Promise<ExecResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Launch an interactive process inside the sandbox.
|
|
48
|
+
* Optional — providers that support interactive sessions implement this.
|
|
49
|
+
* The provider detects TTY mode from the streams (e.g. stdin.isTTY) and
|
|
50
|
+
* allocates a pseudo-terminal accordingly.
|
|
51
|
+
*/
|
|
52
|
+
interactiveExec?(args: string[], options: InteractiveExecOptions): Promise<{
|
|
53
|
+
exitCode: number;
|
|
54
|
+
}>;
|
|
55
|
+
/** Copy a single file from the host into the sandbox. */
|
|
56
|
+
copyFileIn(hostPath: string, sandboxPath: string): Promise<void>;
|
|
57
|
+
/** Copy a single file from the sandbox to the host. */
|
|
58
|
+
copyFileOut(sandboxPath: string, hostPath: string): Promise<void>;
|
|
59
|
+
/** Tear down the sandbox. */
|
|
60
|
+
close(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** Options passed to a bind-mount provider's `create` function. */
|
|
63
|
+
interface BindMountCreateOptions {
|
|
64
|
+
/** Host-side path to the worktree directory. */
|
|
65
|
+
readonly worktreePath: string;
|
|
66
|
+
/** Host-side path to the original repo root. */
|
|
67
|
+
readonly hostRepoPath: string;
|
|
68
|
+
/** Volume mounts to apply (host:sandbox pairs). */
|
|
69
|
+
readonly mounts: Array<{
|
|
70
|
+
hostPath: string;
|
|
71
|
+
sandboxPath: string;
|
|
72
|
+
readonly?: boolean;
|
|
73
|
+
}>;
|
|
74
|
+
/** Environment variables to inject into the sandbox. */
|
|
75
|
+
readonly env: Record<string, string>;
|
|
76
|
+
}
|
|
77
|
+
/** Configuration for createBindMountSandboxProvider. */
|
|
78
|
+
interface BindMountSandboxProviderConfig {
|
|
79
|
+
/** Human-readable name for this provider (e.g. "docker"). */
|
|
80
|
+
readonly name: string;
|
|
81
|
+
/** Environment variables injected by this provider. Merged at launch time. */
|
|
82
|
+
readonly env?: Record<string, string>;
|
|
83
|
+
/**
|
|
84
|
+
* Absolute path to the home directory inside the sandbox (e.g. `"/home/agent"`).
|
|
85
|
+
* Used to expand `~` in user-provided `sandboxPath` mount configs.
|
|
86
|
+
* Set to `undefined` for providers that do not have a fixed home directory.
|
|
87
|
+
*/
|
|
88
|
+
readonly sandboxHomedir?: string;
|
|
89
|
+
/** Create a sandbox handle from the given options. */
|
|
90
|
+
readonly create: (options: BindMountCreateOptions) => Promise<BindMountSandboxHandle>;
|
|
91
|
+
}
|
|
92
|
+
/** Handle to a running isolated sandbox (extends bind-mount with file transfer). */
|
|
93
|
+
interface IsolatedSandboxHandle {
|
|
94
|
+
/** Absolute path to the worktree inside the sandbox. */
|
|
95
|
+
readonly worktreePath: string;
|
|
96
|
+
/**
|
|
97
|
+
* Execute a command in the sandbox.
|
|
98
|
+
*
|
|
99
|
+
* Implementations MUST support line-by-line streaming via `onLine`. This is
|
|
100
|
+
* how Shipyard delivers live feedback to the user and enforces idle timeouts —
|
|
101
|
+
* without a streaming implementation, neither will work. A buffered/batch
|
|
102
|
+
* implementation that only calls `onLine` after the process exits does NOT
|
|
103
|
+
* satisfy this contract.
|
|
104
|
+
*
|
|
105
|
+
* When `stdin` is set, the implementation pipes the string to the child
|
|
106
|
+
* process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
|
|
107
|
+
*/
|
|
108
|
+
exec(command: string, options?: {
|
|
109
|
+
onLine?: (line: string) => void;
|
|
110
|
+
cwd?: string;
|
|
111
|
+
sudo?: boolean;
|
|
112
|
+
stdin?: string;
|
|
113
|
+
/** Abort the command when the caller's operation is cancelled. */
|
|
114
|
+
signal?: AbortSignal;
|
|
115
|
+
/** Reject/terminate the command after this many combined output bytes. */
|
|
116
|
+
maxOutputBytes?: number;
|
|
117
|
+
}): Promise<ExecResult>;
|
|
118
|
+
/**
|
|
119
|
+
* Launch an interactive process inside the sandbox.
|
|
120
|
+
* Optional — providers that support interactive sessions implement this.
|
|
121
|
+
* The provider detects TTY mode from the streams (e.g. stdin.isTTY) and
|
|
122
|
+
* allocates a pseudo-terminal accordingly.
|
|
123
|
+
*/
|
|
124
|
+
interactiveExec?(args: string[], options: InteractiveExecOptions): Promise<{
|
|
125
|
+
exitCode: number;
|
|
126
|
+
}>;
|
|
127
|
+
/** Copy a file or directory from the host into the sandbox. */
|
|
128
|
+
copyIn(hostPath: string, sandboxPath: string): Promise<void>;
|
|
129
|
+
/** Copy a single file from the sandbox to the host. */
|
|
130
|
+
copyFileOut(sandboxPath: string, hostPath: string): Promise<void>;
|
|
131
|
+
/** Tear down the sandbox. */
|
|
132
|
+
close(): Promise<void>;
|
|
133
|
+
}
|
|
134
|
+
/** Options passed to an isolated provider's `create` function. */
|
|
135
|
+
interface IsolatedCreateOptions {
|
|
136
|
+
/** Original host repository path, used only for image naming; never mounted. */
|
|
137
|
+
readonly hostRepoPath?: string;
|
|
138
|
+
/** Environment variables to inject into the sandbox. */
|
|
139
|
+
readonly env: Record<string, string>;
|
|
140
|
+
}
|
|
141
|
+
/** Configuration for createIsolatedSandboxProvider. */
|
|
142
|
+
interface IsolatedSandboxProviderConfig {
|
|
143
|
+
/** Human-readable name for this provider (e.g. "vercel"). */
|
|
144
|
+
readonly name: string;
|
|
145
|
+
/** Environment variables injected by this provider. Merged at launch time. */
|
|
146
|
+
readonly env?: Record<string, string>;
|
|
147
|
+
/** Create an isolated sandbox handle from the given options. */
|
|
148
|
+
readonly create: (options: IsolatedCreateOptions) => Promise<IsolatedSandboxHandle>;
|
|
149
|
+
}
|
|
150
|
+
/** A bind-mount sandbox provider. */
|
|
151
|
+
interface BindMountSandboxProvider {
|
|
152
|
+
/** Human-readable provider name. */
|
|
153
|
+
readonly name: string;
|
|
154
|
+
/** Environment variables injected by this provider. */
|
|
155
|
+
readonly env: Record<string, string>;
|
|
156
|
+
/**
|
|
157
|
+
* Absolute path to the home directory inside the sandbox (e.g. `"/home/agent"`).
|
|
158
|
+
* `undefined` when the provider does not declare a sandbox home directory.
|
|
159
|
+
*/
|
|
160
|
+
readonly sandboxHomedir: string | undefined;
|
|
161
|
+
}
|
|
162
|
+
/** An isolated sandbox provider. */
|
|
163
|
+
interface IsolatedSandboxProvider {
|
|
164
|
+
/** Human-readable provider name. */
|
|
165
|
+
readonly name: string;
|
|
166
|
+
/** Environment variables injected by this provider. */
|
|
167
|
+
readonly env: Record<string, string>;
|
|
168
|
+
}
|
|
169
|
+
/** Handle to a no-sandbox session — runs commands directly on the host. */
|
|
170
|
+
interface NoSandboxHandle {
|
|
171
|
+
/** Absolute path to the worktree on the host. */
|
|
172
|
+
readonly worktreePath: string;
|
|
173
|
+
/**
|
|
174
|
+
* Execute a command on the host.
|
|
175
|
+
*
|
|
176
|
+
* Implementations MUST support line-by-line streaming via `onLine`. This is
|
|
177
|
+
* how Shipyard delivers live feedback to the user and enforces idle timeouts —
|
|
178
|
+
* without a streaming implementation, neither will work.
|
|
179
|
+
*
|
|
180
|
+
* When `stdin` is set, the implementation pipes the string to the child
|
|
181
|
+
* process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
|
|
182
|
+
*/
|
|
183
|
+
exec(command: string, options?: {
|
|
184
|
+
onLine?: (line: string) => void;
|
|
185
|
+
cwd?: string;
|
|
186
|
+
sudo?: boolean;
|
|
187
|
+
stdin?: string;
|
|
188
|
+
/** Abort the command when the caller's operation is cancelled. */
|
|
189
|
+
signal?: AbortSignal;
|
|
190
|
+
/** Reject/terminate the command after this many combined output bytes. */
|
|
191
|
+
maxOutputBytes?: number;
|
|
192
|
+
}): Promise<ExecResult>;
|
|
193
|
+
/**
|
|
194
|
+
* Launch an interactive process on the host with inherited stdio.
|
|
195
|
+
*/
|
|
196
|
+
interactiveExec(args: string[], options: InteractiveExecOptions): Promise<{
|
|
197
|
+
exitCode: number;
|
|
198
|
+
}>;
|
|
199
|
+
/** No-op — no container to tear down. */
|
|
200
|
+
close(): Promise<void>;
|
|
201
|
+
}
|
|
202
|
+
/** A no-sandbox provider — runs the agent directly on the host with no container isolation. */
|
|
203
|
+
interface NoSandboxProvider {
|
|
204
|
+
/** Human-readable provider name. */
|
|
205
|
+
readonly name: string;
|
|
206
|
+
/** Environment variables injected by this provider. */
|
|
207
|
+
readonly env: Record<string, string>;
|
|
208
|
+
}
|
|
209
|
+
/** Head strategy: agent writes directly to host working directory. Bind-mount only. */
|
|
210
|
+
interface HeadBranchStrategy {
|
|
211
|
+
readonly type: "head";
|
|
212
|
+
}
|
|
213
|
+
/** Merge-to-head strategy: temp branch, merge back to HEAD, delete temp branch. */
|
|
214
|
+
interface MergeToHeadBranchStrategy {
|
|
215
|
+
readonly type: "merge-to-head";
|
|
216
|
+
}
|
|
217
|
+
/** Branch strategy: commits land on an explicit named branch. */
|
|
218
|
+
interface NamedBranchStrategy {
|
|
219
|
+
readonly type: "branch";
|
|
220
|
+
readonly branch: string;
|
|
221
|
+
/**
|
|
222
|
+
* Git ref to use as the starting point when creating a new branch.
|
|
223
|
+
* Only used when the branch doesn't already exist — ignored otherwise.
|
|
224
|
+
* Callers are responsible for ensuring the ref is current (e.g. `git fetch`).
|
|
225
|
+
* Defaults to `HEAD` when omitted.
|
|
226
|
+
*/
|
|
227
|
+
readonly baseBranch?: string;
|
|
228
|
+
}
|
|
229
|
+
/** Branch strategy for bind-mount providers (all three variants). */
|
|
230
|
+
type BindMountBranchStrategy = HeadBranchStrategy | MergeToHeadBranchStrategy | NamedBranchStrategy;
|
|
231
|
+
/** Branch strategy for isolated providers (no head — can't write to host). */
|
|
232
|
+
type IsolatedBranchStrategy = MergeToHeadBranchStrategy | NamedBranchStrategy;
|
|
233
|
+
/** Branch strategy for no-sandbox providers (all three — same as bind-mount). */
|
|
234
|
+
type NoSandboxBranchStrategy = HeadBranchStrategy | MergeToHeadBranchStrategy | NamedBranchStrategy;
|
|
235
|
+
/** Union of all branch strategy variants. */
|
|
236
|
+
type BranchStrategy = BindMountBranchStrategy | IsolatedBranchStrategy | NoSandboxBranchStrategy;
|
|
237
|
+
/**
|
|
238
|
+
* A sandbox provider — the pluggable unit that `run()`, `interactive()`, and
|
|
239
|
+
* `createSandbox()` accept. Tagged for internal dispatch: "bind-mount",
|
|
240
|
+
* "isolated", or "none". When `NoSandboxProvider` is used, the agent runs
|
|
241
|
+
* directly on the host with no container isolation — opt in at your own risk.
|
|
242
|
+
*/
|
|
243
|
+
type SandboxProvider = BindMountSandboxProvider | IsolatedSandboxProvider | NoSandboxProvider;
|
|
244
|
+
/** @deprecated Use `SandboxProvider` — it now includes `NoSandboxProvider`. */
|
|
245
|
+
type AnySandboxProvider = SandboxProvider;
|
|
246
|
+
/**
|
|
247
|
+
* Create a bind-mount sandbox provider from a config object.
|
|
248
|
+
* The returned provider can be passed to `run()` or `createSandbox()`.
|
|
249
|
+
*/
|
|
250
|
+
declare const createBindMountSandboxProvider: (config: BindMountSandboxProviderConfig) => BindMountSandboxProvider;
|
|
251
|
+
/**
|
|
252
|
+
* Create an isolated sandbox provider from a config object.
|
|
253
|
+
* The returned provider can be passed to `run()` or `createSandbox()`.
|
|
254
|
+
*/
|
|
255
|
+
declare const createIsolatedSandboxProvider: (config: IsolatedSandboxProviderConfig) => IsolatedSandboxProvider;
|
|
256
|
+
|
|
257
|
+
export { type AnySandboxProvider as A, type BindMountSandboxHandle as B, type ExecResult as E, type HeadBranchStrategy as H, type IsolatedSandboxProvider as I, type MergeToHeadBranchStrategy as M, type NoSandboxProvider as N, type SandboxProvider as S, type BranchStrategy as a, type NamedBranchStrategy as b, type BindMountBranchStrategy as c, type BindMountCreateOptions as d, type BindMountSandboxProvider as e, type BindMountSandboxProviderConfig as f, type InteractiveExecOptions as g, type IsolatedBranchStrategy as h, type IsolatedCreateOptions as i, type IsolatedSandboxHandle as j, type IsolatedSandboxProviderConfig as k, type NoSandboxBranchStrategy as l, type NoSandboxHandle as m, createBindMountSandboxProvider as n, createIsolatedSandboxProvider as o };
|