@ycs77/devbox 0.3.1 → 0.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @ycs77/devbox
4
4
 
5
- **A containerized devbox for AI agents.**
5
+ **A opinionated devbox for AI agents.**
6
6
 
7
7
  [![NPM version][ico-version]][link-npm]
8
8
  [![Software License][ico-license]](LICENSE)
@@ -79,8 +79,7 @@ Run these commands from the initialized project directory:
79
79
  | `devbox sh` | Open a Bash shell in the running sandbox. |
80
80
  | `devbox stop` | Stop the sandbox and preserve it for a later restart. |
81
81
  | `devbox down` | Stop and remove the sandbox container. |
82
- | `devbox config` | Change the current project's Node.js selection. |
83
- | `devbox config -g` | Change the globally configured Node.js versions or AI agents. |
82
+ | `devbox config` | Change the globally configured or current project's Node.js selection. |
84
83
 
85
84
  ## Troubleshooting
86
85
 
package/dist/cli.mjs CHANGED
@@ -45,9 +45,11 @@ const PACKAGED_AGENTS = {
45
45
  },
46
46
  installation: {
47
47
  url: "https://claude.ai/install.sh",
48
- shell: "bash"
48
+ shell: "bash",
49
+ preInstallCommands: ["echo '{}' > /home/devbox/.claude.json"]
49
50
  },
50
51
  supportsNotifications: true,
52
+ notificationInstallationCommands: ["claude plugin marketplace add ycs77/claude-code-notifications", "claude plugin install notification-basic-wsl@ycs77-notifications"],
51
53
  supportsSkillInstallation: true
52
54
  },
53
55
  codex: {
@@ -60,7 +62,8 @@ const PACKAGED_AGENTS = {
60
62
  shell: "sh"
61
63
  },
62
64
  supportsNotifications: true,
63
- supportsSkillInstallation: true
65
+ supportsSkillInstallation: true,
66
+ notificationInstallationCommands: ["codex plugin marketplace add ycs77/codex-notifications", "codex plugin add notification-basic-wsl@ycs77-notifications"]
64
67
  },
65
68
  agy: {
66
69
  home: {
@@ -84,7 +87,8 @@ const PACKAGED_AGENTS = {
84
87
  shell: "sh"
85
88
  },
86
89
  supportsNotifications: true,
87
- supportsSkillInstallation: false
90
+ supportsSkillInstallation: false,
91
+ notificationInstallationCommands: ["omp plugin marketplace add ycs77/omp-notifications", "omp plugin install notification-basic@ycs77-notifications"]
88
92
  }
89
93
  };
90
94
  const PACKAGED_RUNTIME_CATALOG = {
@@ -460,6 +464,7 @@ function selectComposeFragments({ selectedNode, configuredAgents, agentNotificat
460
464
  function renderProjectCompose(input) {
461
465
  const fragments = selectComposeFragments(input);
462
466
  const environment = {
467
+ TERM: "xterm-256color",
463
468
  ...fragments.node?.environment,
464
469
  ...fragments.notification?.environment
465
470
  };
@@ -496,7 +501,7 @@ function renderProjectCompose(input) {
496
501
  },
497
502
  services: { devbox: {
498
503
  image: WORKSPACE_IMAGE,
499
- container_name: input.sandboxName,
504
+ container_name: `devbox-${input.sandboxName}`,
500
505
  working_dir: `/workspace/${input.sandboxName}`,
501
506
  ...Object.keys(environment).length === 0 ? {} : { environment },
502
507
  volumes
@@ -521,7 +526,7 @@ function validatesProjectCompose(source, input) {
521
526
  const metadata = definition["x-devbox"];
522
527
  if (metadata.version !== 1 || metadata.project_root !== input.projectRoot || metadata.compose_name !== input.sandboxName || metadata.sandbox_service !== "devbox") return false;
523
528
  const service = definition.services.devbox;
524
- if (service.image !== "devbox-workspace:latest" || service.container_name !== input.sandboxName || service.working_dir !== `/workspace/${input.sandboxName}` || "build" in service) return false;
529
+ if (service.image !== "devbox-workspace:latest" || service.container_name !== `devbox-${input.sandboxName}` || service.working_dir !== `/workspace/${input.sandboxName}` || "build" in service) return false;
525
530
  return input.selectedNode === null && service.environment?.NODE_VERSION === void 0 || input.selectedNode !== null && service.environment?.NODE_VERSION === input.selectedNode;
526
531
  } catch {
527
532
  return false;
@@ -1635,6 +1640,7 @@ async function buildWorkspaceUnlocked(input) {
1635
1640
  nodeRuntimes,
1636
1641
  buildNodeRuntime: buildNodeRuntimeRecipe(globalConfiguration),
1637
1642
  agents,
1643
+ agentNotifications: globalConfiguration.agent_notifications,
1638
1644
  skillAgents: globalConfiguration.agent.filter((agent) => PACKAGED_AGENTS[agent]?.supportsSkillInstallation === true)
1639
1645
  });
1640
1646
  await mkdir(paths.buildContext, { recursive: true });
@@ -1700,7 +1706,10 @@ function renderBuildDockerfile(input) {
1700
1706
  for (const recipe of input.nodeRuntimes) lines.push(...renderNodeRuntimeStage(recipe));
1701
1707
  lines.push("WORKDIR /workspace", "USER devbox", "");
1702
1708
  if (input.buildNodeRuntime !== void 0) lines.push("# Configure pnpm for the non-root Sandbox user", "RUN mkdir -p /home/devbox/.config/pnpm /home/devbox/.pnpm-store \\", " && printf 'storeDir: /home/devbox/.pnpm-store\\n' > /home/devbox/.config/pnpm/config.yaml \\", " && chown devbox:devbox /home/devbox/.config/pnpm/config.yaml", "");
1703
- for (const agent of input.agents) lines.push(`# Install ${agent.name}`, `RUN mkdir -p ${agent.recipe.home.target} \\`, ` && curl -fsSL ${agent.recipe.installation.url} | ${agent.recipe.installation.shell}`, "");
1709
+ for (const agent of input.agents) {
1710
+ const preInstallCommands = agent.recipe.installation.preInstallCommands ?? [];
1711
+ lines.push(`# Install ${agent.name}`, `RUN mkdir -p ${agent.recipe.home.target} \\`, ...preInstallCommands.map((command) => ` && ${command} \\`), ` && curl -fsSL ${agent.recipe.installation.url} | ${agent.recipe.installation.shell}`, "");
1712
+ }
1704
1713
  if (input.agents.length > 0) lines.push("# Set the PATH to include AI tools", "ENV PATH=\"/home/devbox/.local/bin:${PATH}\"", "");
1705
1714
  if (input.buildNodeRuntime !== void 0 && input.skillAgents.length > 0) {
1706
1715
  const skillAgentArguments = input.skillAgents.map((agent) => `-a ${agent}`).join(" ");
@@ -1722,6 +1731,8 @@ function renderBuildDockerfile(input) {
1722
1731
  }
1723
1732
  lines.push(`RUN ${ownershipCommands.join(" \\\n && ")}`, "");
1724
1733
  }
1734
+ const notificationInstallationCommands = input.agentNotifications ? input.agents.flatMap((agent) => agent.recipe.supportsNotifications ? agent.recipe.notificationInstallationCommands : []) : [];
1735
+ if (notificationInstallationCommands.length > 0) lines.push("# Install Agent Notification Plugins", "USER devbox", "RUN set -eux \\", ...notificationInstallationCommands.map((command, index) => ` && ${command}${index < notificationInstallationCommands.length - 1 ? " \\" : ""}`), "USER root", "");
1725
1736
  lines.push("COPY entrypoint.sh /usr/local/bin/entrypoint.sh", "COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf", "RUN chmod +x /usr/local/bin/entrypoint.sh", "", "ENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]");
1726
1737
  return `${lines.join("\n")}\n`;
1727
1738
  }
@@ -1761,7 +1772,7 @@ function renderEntrypoint(input) {
1761
1772
  "set -eu",
1762
1773
  ""
1763
1774
  ];
1764
- if (input.nodeRuntimes.length > 0) lines.push("# Set the Node.js release line to use", "NODE_VERSION=\"${NODE_VERSION:-}\"", "if [ -n \"$NODE_VERSION\" ]; then", " case \"$NODE_VERSION\" in", " *[!0-9]*)", " echo \"NODE_VERSION must be a numeric release line: $NODE_VERSION\" >&2", " exit 1", " ;;", " esac", "", " NODE_RUNTIME_ROOT=\"/opt/devbox/runtimes/node/$NODE_VERSION\"", " if [ ! -x \"$NODE_RUNTIME_ROOT/bin/node\" ]; then", " echo \"Node.js release line $NODE_VERSION is not installed.\" >&2", " exit 1", " fi", "", " export NODE_RUNTIME_ROOT", " export PATH=\"$NODE_RUNTIME_ROOT/bin:$PATH\"", "fi", "");
1775
+ if (input.nodeRuntimes.length > 0) lines.push("# Set the Node.js release line to use", "NODE_VERSION=\"${NODE_VERSION:-}\"", "if [ -n \"$NODE_VERSION\" ]; then", " case \"$NODE_VERSION\" in", " *[!0-9]*)", " echo \"NODE_VERSION must be a numeric release line: $NODE_VERSION\" >&2", " exit 1", " ;;", " esac", "", " NODE_RUNTIME_ROOT=\"/opt/devbox/runtimes/node/$NODE_VERSION\"", " if [ ! -x \"$NODE_RUNTIME_ROOT/bin/node\" ]; then", " echo \"Node.js release line $NODE_VERSION is not installed.\" >&2", " exit 1", " fi", "", " if ! grep -q \"# Devbox\" /etc/bash.bashrc; then", " printf \"\\n# Devbox\\nexport PATH=\\\"$NODE_RUNTIME_ROOT/bin:\\$PATH\\\"\\n\" >> /etc/bash.bashrc", " fi", "", " export PATH=\"$NODE_RUNTIME_ROOT/bin:$PATH\"", "fi", "");
1765
1776
  if (input.agents.some((agent) => agent.name === "agy")) lines.push("# Link shared Agent Skills", "if [ ! -L \"/home/devbox/.gemini/skills\" ]; then", " ln -sfn /home/devbox/.agents/skills /home/devbox/.gemini/skills", " chown -h devbox:devbox /home/devbox/.gemini/skills", "fi", "");
1766
1777
  lines.push("if [ $# -gt 0 ]; then", " exec gosu devbox \"$@\"", "else", " exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf", "fi");
1767
1778
  return `${lines.join("\n")}\n`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ycs77/devbox",
3
- "version": "0.3.1",
4
- "description": "A containerized devbox for AI agents",
3
+ "version": "0.4.0",
4
+ "description": "A Lucas Yang's opinionated devbox for AI agents",
5
5
  "keywords": [
6
6
  "agent",
7
7
  "agentic",
@@ -40,6 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^26.2.0",
43
+ "actionspack": "^0.1.5",
43
44
  "bumpp": "^12.2.0",
44
45
  "memfs": "^4.68.1",
45
46
  "oxfmt": "^0.63.0",
@@ -60,7 +61,8 @@
60
61
  "typecheck": "tsc --noEmit",
61
62
  "test": "vitest",
62
63
  "check": "pnpm lint && pnpm fmt --check && pnpm typecheck && pnpm test --run",
63
- "smoke:cli": "node scripts/package-devbox-smoke.mjs",
64
+ "smoke": "node scripts/package-devbox-smoke.mjs",
65
+ "update-actions": "actionspack update",
64
66
  "release": "bumpp --commit \"Release v%s\""
65
67
  }
66
68
  }