@ycs77/devbox 0.5.0 → 0.5.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 +18 -15
- package/dist/cli.mjs +27 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @ycs77/devbox
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**An opinionated devbox for AI agents.**
|
|
6
6
|
|
|
7
7
|
[![NPM version][ico-version]][link-npm]
|
|
8
8
|
[![Software License][ico-license]](LICENSE)
|
|
@@ -17,20 +17,20 @@ English | [繁體中文](README-zh-TW.md)
|
|
|
17
17
|
|
|
18
18
|
## Overview
|
|
19
19
|
|
|
20
|
-
Devbox gives each project an isolated Docker sandbox
|
|
20
|
+
Devbox gives each project an isolated Docker sandbox for AI agents, preconfigured with Lucas's development preferences.
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
24
24
|
- **Container environment for AI agents:** choose Claude Code, Codex, and more during setup, then use them inside the sandbox.
|
|
25
|
-
- **Simple
|
|
26
|
-
- **Flexible Node.js
|
|
25
|
+
- **Simple project sandbox management:** initialize, start, enter, stop, and remove a sandbox with a few commands.
|
|
26
|
+
- **Flexible Node.js versions:** choose the versions to include, then select one for each project.
|
|
27
27
|
- **Selective builds:** build only the Node.js versions and AI agents you choose.
|
|
28
28
|
- **No project configuration files:** keep Devbox configuration local to your machine, not in your repository.
|
|
29
29
|
|
|
30
30
|
## Requirements
|
|
31
31
|
|
|
32
|
-
- **Node.js 22 or later:**
|
|
33
|
-
- **Docker with Docker Compose:**
|
|
32
|
+
- **Node.js 22 or later:** required to install and run the CLI with npm.
|
|
33
|
+
- **Docker with Docker Compose:** required to run the development sandbox.
|
|
34
34
|
- **Windows:** use Devbox from a **WSL2** Linux distribution with Docker Desktop integration enabled.
|
|
35
35
|
|
|
36
36
|
## Getting started
|
|
@@ -47,9 +47,11 @@ From your project directory, initialize Devbox:
|
|
|
47
47
|
devbox init
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
The interactive setup lets you choose the Node.js
|
|
50
|
+
The interactive setup lets you choose the Node.js versions and AI agents to include, then select a Node.js version for the current project.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Global configuration controls what goes into the shared Workspace Image. Each Node.js version or AI agent you add makes the image larger and uses more disk space. Projects can use only the Node.js versions included in the image.
|
|
53
|
+
|
|
54
|
+
Build the shared Workspace Image once after setup or whenever you change global configuration:
|
|
53
55
|
|
|
54
56
|
```bash
|
|
55
57
|
devbox build
|
|
@@ -67,9 +69,9 @@ Open a shell inside the running sandbox and start working with your AI agent:
|
|
|
67
69
|
devbox sh
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
If you selected an agent during setup, run `claude`, `codex`, `agy`, or `omp` inside the shell.
|
|
71
73
|
|
|
72
|
-
##
|
|
74
|
+
## Commands
|
|
73
75
|
|
|
74
76
|
Run these commands from the initialized project directory:
|
|
75
77
|
|
|
@@ -79,23 +81,24 @@ Run these commands from the initialized project directory:
|
|
|
79
81
|
| `devbox sh` | Open a Bash shell in the running sandbox. |
|
|
80
82
|
| `devbox stop` | Stop the sandbox and preserve it for a later restart. |
|
|
81
83
|
| `devbox down` | Stop and remove the sandbox container. |
|
|
82
|
-
| `devbox config` | Change
|
|
84
|
+
| `devbox config` | Change global settings or this project's configuration. |
|
|
85
|
+
| `devbox cleanup --missing-projects` | Remove registrations for projects whose root directory no longer exists. |
|
|
83
86
|
|
|
84
87
|
## Troubleshooting
|
|
85
88
|
|
|
86
|
-
###
|
|
89
|
+
### Devbox reports that a command marker is in use
|
|
87
90
|
|
|
88
|
-
An interrupted
|
|
91
|
+
An interrupted command or sudden system shutdown can leave a command marker behind. First confirm that no other Devbox command is still running, then remove the stale marker and retry:
|
|
89
92
|
|
|
90
93
|
```bash
|
|
91
94
|
rm -rf ~/.devbox/locks/<marker>
|
|
92
95
|
```
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
A marker is a directory named `global` or `project-<hash>`. Do not remove a marker while its Devbox command is still running.
|
|
95
98
|
|
|
96
99
|
## Sponsor
|
|
97
100
|
|
|
98
|
-
If
|
|
101
|
+
If Devbox has helped you, consider sponsoring my work on Patreon. Your avatar will appear on my major projects.
|
|
99
102
|
|
|
100
103
|
<p align="center">
|
|
101
104
|
<a href="https://www.patreon.com/ycs77">
|
package/dist/cli.mjs
CHANGED
|
@@ -507,7 +507,6 @@ function selectComposeFragments({ selectedNode, configuredAgents, agentNotificat
|
|
|
507
507
|
function renderProjectCompose(input) {
|
|
508
508
|
const fragments = selectComposeFragments(input);
|
|
509
509
|
const environment = {
|
|
510
|
-
TERM: "xterm-256color",
|
|
511
510
|
...fragments.node?.environment,
|
|
512
511
|
...fragments.notification?.environment
|
|
513
512
|
};
|
|
@@ -1191,9 +1190,12 @@ async function runSandboxLifecycle(command, input = {}) {
|
|
|
1191
1190
|
global: false,
|
|
1192
1191
|
projectRoots: [projectRoot],
|
|
1193
1192
|
signal: input.signal
|
|
1194
|
-
}, () =>
|
|
1193
|
+
}, () => runSandboxCommandUnlocked(command, input, projectRoot));
|
|
1195
1194
|
}
|
|
1196
|
-
async function
|
|
1195
|
+
async function runSandboxShell(input = {}) {
|
|
1196
|
+
return runSandboxCommandUnlocked("sh", input, input.root ?? process.cwd());
|
|
1197
|
+
}
|
|
1198
|
+
async function runSandboxCommandUnlocked(command, input, projectRoot) {
|
|
1197
1199
|
if (input.signal?.aborted) throw new InterruptedError();
|
|
1198
1200
|
const rootCheck = await validateProjectRoot(projectRoot);
|
|
1199
1201
|
if (!rootCheck.ok) return rootCheck;
|
|
@@ -1532,18 +1534,20 @@ function createConfigurationPrompter({ signal, input, output }) {
|
|
|
1532
1534
|
}));
|
|
1533
1535
|
},
|
|
1534
1536
|
selectConfigurationScope: async () => promptValue(p.select({
|
|
1535
|
-
message: "
|
|
1537
|
+
message: "Where should this configuration apply?",
|
|
1536
1538
|
options: [{
|
|
1537
1539
|
value: "local",
|
|
1538
|
-
label: "Current
|
|
1540
|
+
label: "Current project",
|
|
1541
|
+
hint: "Enabled when this project’s container starts."
|
|
1539
1542
|
}, {
|
|
1540
1543
|
value: "global",
|
|
1541
|
-
label: "
|
|
1544
|
+
label: "Workspace Docker image",
|
|
1545
|
+
hint: "Built into the shared image. It affects image size and disk usage."
|
|
1542
1546
|
}],
|
|
1543
1547
|
...common
|
|
1544
1548
|
})),
|
|
1545
1549
|
editGlobal: async (configuration, catalog) => {
|
|
1546
|
-
p.log.step(`${c.bold(c.cyan("
|
|
1550
|
+
p.log.step(`${c.bold(c.cyan("Workspace Docker image"))}\n${c.dim("Add runtimes and agents to the shared image. They are available to every project and increase the image size.")}`, common);
|
|
1547
1551
|
return {
|
|
1548
1552
|
version: 1,
|
|
1549
1553
|
node: await promptValue(p.multiselect({
|
|
@@ -1568,7 +1572,7 @@ function createConfigurationPrompter({ signal, input, output }) {
|
|
|
1568
1572
|
};
|
|
1569
1573
|
},
|
|
1570
1574
|
editLocal: async (configuration, catalog, globalConfiguration, editPorts = true) => {
|
|
1571
|
-
p.log.step(`${c.bold(c.cyan("Project configuration"))}\n${c.dim(editPorts ? "Choose the
|
|
1575
|
+
p.log.step(`${c.bold(c.cyan("Project configuration"))}\n${c.dim(editPorts ? "Choose the Node version and ports to use when this project’s container starts." : "Choose the Node version to use when this project’s container starts.")}`, common);
|
|
1572
1576
|
const node = await promptValue(p.select({
|
|
1573
1577
|
message: "Selected Node release line",
|
|
1574
1578
|
options: [{
|
|
@@ -1762,6 +1766,9 @@ function renderBuildDockerfile(input) {
|
|
|
1762
1766
|
"ENV DEBIAN_FRONTEND=noninteractive",
|
|
1763
1767
|
"ENV TZ=UTC",
|
|
1764
1768
|
"ENV LANG=C.UTF-8",
|
|
1769
|
+
"ENV TERM=xterm-256color",
|
|
1770
|
+
"ENV EDITOR=vim",
|
|
1771
|
+
"ENV VISUAL=vim",
|
|
1765
1772
|
"",
|
|
1766
1773
|
"RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone",
|
|
1767
1774
|
"",
|
|
@@ -2040,12 +2047,15 @@ function createCli(signal, interactive) {
|
|
|
2040
2047
|
for (const [command, description] of [
|
|
2041
2048
|
["up", "Start the current Project Sandbox."],
|
|
2042
2049
|
["down", "Stop and remove the current Project Sandbox."],
|
|
2043
|
-
["stop", "Stop the current Project Sandbox."]
|
|
2044
|
-
["sh", "Open a shell in the current Project Sandbox."]
|
|
2050
|
+
["stop", "Stop the current Project Sandbox."]
|
|
2045
2051
|
]) cli.command(command, description).action(async () => {
|
|
2046
2052
|
const result = await runSandboxLifecycle(command, { signal });
|
|
2047
2053
|
return result.ok ? success({}) : result;
|
|
2048
2054
|
});
|
|
2055
|
+
cli.command("sh", "Open a shell in the current Project Sandbox.").action(async () => {
|
|
2056
|
+
const result = await runSandboxShell({ signal });
|
|
2057
|
+
return result.ok ? success({}) : result;
|
|
2058
|
+
});
|
|
2049
2059
|
cli.help();
|
|
2050
2060
|
return cli;
|
|
2051
2061
|
}
|
|
@@ -2072,11 +2082,18 @@ async function main() {
|
|
|
2072
2082
|
return 130;
|
|
2073
2083
|
}
|
|
2074
2084
|
if (error instanceof HostCommandError) return error.exitCode;
|
|
2085
|
+
if (isCacError(error)) {
|
|
2086
|
+
process.stderr.write(`${error.message}\n`);
|
|
2087
|
+
return 2;
|
|
2088
|
+
}
|
|
2075
2089
|
throw error;
|
|
2076
2090
|
} finally {
|
|
2077
2091
|
process.off("SIGINT", interrupt);
|
|
2078
2092
|
}
|
|
2079
2093
|
}
|
|
2094
|
+
function isCacError(error) {
|
|
2095
|
+
return error instanceof Error && error.name === "CACError";
|
|
2096
|
+
}
|
|
2080
2097
|
function interactiveFailure(observed, nextAction) {
|
|
2081
2098
|
return failure({
|
|
2082
2099
|
kind: "usage",
|