@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.
Files changed (3) hide show
  1. package/README.md +18 -15
  2. package/dist/cli.mjs +27 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @ycs77/devbox
4
4
 
5
- **A opinionated devbox for AI agents.**
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 designed for AI agents, with Lucas's opinionated development setup. Choose the Node.js release lines and AI agents you use, then work from a consistent container without configuring it yourself.
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 setup and project lifecycle:** initialize, start, enter, stop, and remove a sandbox with a few commands.
26
- - **Flexible Node.js environments:** configure Node.js 22 and 24, then select a version for each project.
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:** installs and runs the CLI with npm.
33
- - **Docker with Docker Compose:** runs the development sandbox.
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 release lines and AI agents to configure, then select the Node.js version for the current project.
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
- Build the shared workspace image once after setup or after changing global configuration:
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
- For example, run `claude`, `codex`, `agy`, or `omp` after opening the shell when that agent was selected during setup.
72
+ If you selected an agent during setup, run `claude`, `codex`, `agy`, or `omp` inside the shell.
71
73
 
72
- ## Everyday commands
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 the globally configured or current project's Node.js selection. |
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
- ### A command says a Devbox marker is occupied
89
+ ### Devbox reports that a command marker is in use
87
90
 
88
- An interrupted process or sudden system shutdown can leave a command marker behind. First confirm that no Devbox process is still running, then remove only the stale marker and retry:
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
- Markers are directories named `global` or `project-<hash>`. Do not remove a marker while its Devbox command is still running.
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 you think this package has helped you, please consider [Becoming a sponsor](https://www.patreon.com/ycs77) to support my work~ and your avatar will be visible on my major projects.
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
- }, () => runSandboxLifecycleUnlocked(command, input, projectRoot));
1193
+ }, () => runSandboxCommandUnlocked(command, input, projectRoot));
1195
1194
  }
1196
- async function runSandboxLifecycleUnlocked(command, input, projectRoot) {
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: "Configuration scope",
1537
+ message: "Where should this configuration apply?",
1536
1538
  options: [{
1537
1539
  value: "local",
1538
- label: "Current Project"
1540
+ label: "Current project",
1541
+ hint: "Enabled when this project’s container starts."
1539
1542
  }, {
1540
1543
  value: "global",
1541
- label: "Global"
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("Global configuration"))}\n${c.dim("Choose the runtimes and agents available to every Project.")}`, common);
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 runtime and published ports for this Project." : "Choose the replacement runtime for this Project.")}`, common);
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycs77/devbox",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "A Lucas Yang's opinionated devbox for AI agents",
5
5
  "keywords": [
6
6
  "agent",