devs-common 5.0.6__tar.gz → 6.0.0__tar.gz
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.
- {devs_common-5.0.6 → devs_common-6.0.0}/PKG-INFO +1 -1
- devs_common-6.0.0/devs_common/agents.py +79 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/config.py +10 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/core/container.py +10 -41
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/Dockerfile +37 -3
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/devcontainer.json +1 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/extensions/devs-bridge-drop.vsix +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/post-start.sh +16 -0
- devs_common-6.0.0/devs_common/templates/scripts/setup-hermes.sh +24 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-devs-env.sh +35 -18
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common.egg-info/PKG-INFO +1 -1
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common.egg-info/SOURCES.txt +2 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/pyproject.toml +1 -1
- {devs_common-5.0.6 → devs_common-6.0.0}/LICENSE +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/README.md +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/__init__.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/core/__init__.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/core/project.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/core/workspace.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/devs_config.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/exceptions.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/README-vscode-settings.md +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/extensions/README.md +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/extensions/install.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/machine-settings.json +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/machine-settings.tmux.json +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/register-vscode-extensions.js +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/copy-ssh.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/install-bridge-drop.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/post-create-wrapper.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/python-venv-integration.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/run-autoexec.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/screenshot-webapp.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/setup-vscode-settings.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/setup-workspace.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/start-tailscale.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/start-tunnel.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/ts.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/tunnel-status.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/webapp-screenshot.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/init-firewall.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-magicdns.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/start-services.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/start-tailscaled.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/ts-cli.sh +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/vscode-settings-reference.md +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/__init__.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/config_hash.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/console.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/devcontainer.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/devcontainer_template.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/docker_client.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/file_utils.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/git_utils.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common/utils/repo_cache.py +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common.egg-info/dependency_links.txt +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common.egg-info/requires.txt +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/devs_common.egg-info/top_level.txt +0 -0
- {devs_common-5.0.6 → devs_common-6.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Registry of the AI coding agents devs can run inside a devcontainer.
|
|
2
|
+
|
|
3
|
+
Adding an agent:
|
|
4
|
+
|
|
5
|
+
1. Install it in ``templates/Dockerfile`` (and add shell aliases next to the others).
|
|
6
|
+
2. Add an ``AgentSpec`` to ``AGENTS`` below — this gives ``ContainerManager.exec_agent``.
|
|
7
|
+
3. Register its ``devs <name>`` command with ``_add_agent_command`` in the CLI's ``cli.py``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Dict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AgentSpec:
|
|
16
|
+
"""How to run one coding agent non-interactively in a devs container.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
name: Registry key and CLI subcommand (``devs <name> ...``).
|
|
20
|
+
display_name: Human-readable name used in status messages.
|
|
21
|
+
description: What gets executed, used in help text ("Execute <description> ...").
|
|
22
|
+
command: Shell command run from the container workspace after ``~/.zshrc`` is
|
|
23
|
+
sourced. The prompt is written to its stdin, so the command must read it
|
|
24
|
+
from there and exit when done.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
display_name: str
|
|
29
|
+
description: str
|
|
30
|
+
command: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
AGENTS: Dict[str, AgentSpec] = {
|
|
34
|
+
spec.name: spec
|
|
35
|
+
for spec in (
|
|
36
|
+
AgentSpec(
|
|
37
|
+
name="claude",
|
|
38
|
+
display_name="Claude",
|
|
39
|
+
description="Claude CLI",
|
|
40
|
+
command="claude --dangerously-skip-permissions -p",
|
|
41
|
+
),
|
|
42
|
+
AgentSpec(
|
|
43
|
+
name="codex",
|
|
44
|
+
display_name="Codex",
|
|
45
|
+
description="OpenAI Codex CLI",
|
|
46
|
+
# `--full-auto` was removed from the Codex CLI (passing it now aborts with
|
|
47
|
+
# "unexpected argument"). This is the flag Codex intends for externally
|
|
48
|
+
# sandboxed environments, which a devcontainer is — and it matches the
|
|
49
|
+
# `claude --dangerously-skip-permissions` invocation beside it.
|
|
50
|
+
command="codex --dangerously-bypass-approvals-and-sandbox",
|
|
51
|
+
),
|
|
52
|
+
AgentSpec(
|
|
53
|
+
name="hermes",
|
|
54
|
+
display_name="Hermes",
|
|
55
|
+
description="Hermes Agent (Nous Research)",
|
|
56
|
+
# `--query-file -` reads the prompt from stdin; `--oneshot` answers and exits
|
|
57
|
+
# rather than seeding an interactive session. `--yolo` skips dangerous-command
|
|
58
|
+
# approvals, like the other agents. `hermes chat` ignores HERMES_INFERENCE_MODEL,
|
|
59
|
+
# so forward it as --model when set; otherwise the model baked into the image's
|
|
60
|
+
# Hermes config is used. Hermes picks OpenRouter from OPENROUTER_API_KEY.
|
|
61
|
+
command=(
|
|
62
|
+
"hermes --yolo chat --oneshot --query-file - "
|
|
63
|
+
"${HERMES_INFERENCE_MODEL:+--model=$HERMES_INFERENCE_MODEL}"
|
|
64
|
+
),
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_agent(name: str) -> AgentSpec:
|
|
71
|
+
"""Look up an agent by name.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If no agent with that name is registered.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
return AGENTS[name]
|
|
78
|
+
except KeyError:
|
|
79
|
+
raise ValueError(f"Unknown agent '{name}'. Available: {', '.join(AGENTS)}") from None
|
|
@@ -12,6 +12,7 @@ class BaseConfig(ABC):
|
|
|
12
12
|
# Default directories shared across CLI and webhook
|
|
13
13
|
CLAUDE_CONFIG_DIR = Path.home() / ".devs" / "claudeconfig"
|
|
14
14
|
CODEX_CONFIG_DIR = Path.home() / ".devs" / "codexconfig"
|
|
15
|
+
HERMES_CONFIG_DIR = Path.home() / ".devs" / "hermesconfig"
|
|
15
16
|
|
|
16
17
|
def __init__(self) -> None:
|
|
17
18
|
"""Initialize base configuration."""
|
|
@@ -25,6 +26,13 @@ class BaseConfig(ABC):
|
|
|
25
26
|
codex_config_env = os.getenv("DEVS_CODEX_CONFIG_DIR")
|
|
26
27
|
self.codex_config_dir = Path(codex_config_env) if codex_config_env else self.CODEX_CONFIG_DIR
|
|
27
28
|
|
|
29
|
+
hermes_config_env = os.getenv("DEVS_HERMES_CONFIG_DIR")
|
|
30
|
+
hermes_config_dir = Path(hermes_config_env) if hermes_config_env else self.HERMES_CONFIG_DIR
|
|
31
|
+
# Bypass __setattr__: pydantic subclasses reject attributes they don't declare as
|
|
32
|
+
# fields, and devs-webhook releases from before Hermes support don't declare this
|
|
33
|
+
# one. Their `devs-common>=` pin lets a newer devs-common be installed alongside.
|
|
34
|
+
object.__setattr__(self, "hermes_config_dir", hermes_config_dir)
|
|
35
|
+
|
|
28
36
|
@property
|
|
29
37
|
def workspaces_dir(self) -> Path:
|
|
30
38
|
"""Get workspaces directory."""
|
|
@@ -100,3 +108,5 @@ class BaseConfig(ABC):
|
|
|
100
108
|
self.bridge_dir.mkdir(parents=True, exist_ok=True)
|
|
101
109
|
self.claude_config_dir.mkdir(parents=True, exist_ok=True)
|
|
102
110
|
self.codex_config_dir.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
# Bind-mounted into containers, so it must exist before `devcontainer up`
|
|
112
|
+
self.hermes_config_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -5,9 +5,10 @@ import re
|
|
|
5
5
|
import time
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Dict, List, Optional, Tuple
|
|
8
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
9
9
|
import subprocess
|
|
10
10
|
|
|
11
|
+
from ..agents import AgentSpec, get_agent
|
|
11
12
|
from ..config import BaseConfig
|
|
12
13
|
|
|
13
14
|
|
|
@@ -915,13 +916,14 @@ class ContainerManager:
|
|
|
915
916
|
except (DockerError, subprocess.SubprocessError) as e:
|
|
916
917
|
raise ContainerError(f"Failed to exec command in {dev_name}: {e}")
|
|
917
918
|
|
|
918
|
-
def
|
|
919
|
-
"""Execute Claude
|
|
919
|
+
def exec_agent(self, agent: Union[str, AgentSpec], dev_name: str, workspace_dir: Path, prompt: str, debug: bool = False, stream: bool = True, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> tuple[bool, str, str, int]:
|
|
920
|
+
"""Execute a coding agent (Claude, Codex, Hermes, ...) in the container.
|
|
920
921
|
|
|
921
922
|
Args:
|
|
923
|
+
agent: Agent name from ``devs_common.agents.AGENTS``, or an ``AgentSpec``
|
|
922
924
|
dev_name: Development environment name
|
|
923
925
|
workspace_dir: Workspace directory path
|
|
924
|
-
prompt: Prompt to send to
|
|
926
|
+
prompt: Prompt to send to the agent (passed on stdin)
|
|
925
927
|
debug: Show debug output for devcontainer operations
|
|
926
928
|
stream: Stream output to console in real-time
|
|
927
929
|
live: Whether the container is in live mode
|
|
@@ -931,47 +933,14 @@ class ContainerManager:
|
|
|
931
933
|
Tuple of (success, stdout, stderr, exit_code)
|
|
932
934
|
|
|
933
935
|
Raises:
|
|
934
|
-
|
|
936
|
+
ValueError: If the agent name is not registered
|
|
937
|
+
ContainerError: If agent execution fails
|
|
935
938
|
"""
|
|
936
|
-
|
|
939
|
+
spec = get_agent(agent) if isinstance(agent, str) else agent
|
|
937
940
|
return self.exec_command(
|
|
938
941
|
dev_name=dev_name,
|
|
939
942
|
workspace_dir=workspace_dir,
|
|
940
|
-
command=
|
|
941
|
-
stdin_input=prompt,
|
|
942
|
-
debug=debug,
|
|
943
|
-
stream=stream,
|
|
944
|
-
live=live,
|
|
945
|
-
extra_env=extra_env
|
|
946
|
-
)
|
|
947
|
-
|
|
948
|
-
def exec_codex(self, dev_name: str, workspace_dir: Path, prompt: str, debug: bool = False, stream: bool = True, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> tuple[bool, str, str, int]:
|
|
949
|
-
"""Execute OpenAI Codex CLI in the container.
|
|
950
|
-
|
|
951
|
-
Args:
|
|
952
|
-
dev_name: Development environment name
|
|
953
|
-
workspace_dir: Workspace directory path
|
|
954
|
-
prompt: Prompt to send to Codex
|
|
955
|
-
debug: Show debug output for devcontainer operations
|
|
956
|
-
stream: Stream output to console in real-time
|
|
957
|
-
live: Whether the container is in live mode
|
|
958
|
-
extra_env: Additional environment variables to pass to container
|
|
959
|
-
|
|
960
|
-
Returns:
|
|
961
|
-
Tuple of (success, stdout, stderr, exit_code)
|
|
962
|
-
|
|
963
|
-
Raises:
|
|
964
|
-
ContainerError: If Codex execution fails
|
|
965
|
-
"""
|
|
966
|
-
# Simply delegate to exec_command with the Codex command and prompt as stdin
|
|
967
|
-
return self.exec_command(
|
|
968
|
-
dev_name=dev_name,
|
|
969
|
-
workspace_dir=workspace_dir,
|
|
970
|
-
# `--full-auto` was removed from the Codex CLI (passing it now aborts with
|
|
971
|
-
# "unexpected argument"). This is the flag Codex intends for externally
|
|
972
|
-
# sandboxed environments, which a devcontainer is — and it matches the
|
|
973
|
-
# `claude --dangerously-skip-permissions` alias sitting beside it.
|
|
974
|
-
command="codex --dangerously-bypass-approvals-and-sandbox",
|
|
943
|
+
command=spec.command,
|
|
975
944
|
stdin_input=prompt,
|
|
976
945
|
debug=debug,
|
|
977
946
|
stream=stream,
|
|
@@ -36,6 +36,7 @@ RUN apt update && apt install -y less \
|
|
|
36
36
|
nano \
|
|
37
37
|
htop \
|
|
38
38
|
tmux \
|
|
39
|
+
ripgrep \
|
|
39
40
|
&& apt clean \
|
|
40
41
|
&& rm -rf /var/lib/apt/lists/*
|
|
41
42
|
|
|
@@ -88,7 +89,7 @@ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhisto
|
|
|
88
89
|
ENV DEVCONTAINER=true
|
|
89
90
|
|
|
90
91
|
# Create workspace and config directories and set permissions
|
|
91
|
-
RUN mkdir -p /workspaces /home/node/claudeconfig /home/node/codexconfig \
|
|
92
|
+
RUN mkdir -p /workspaces /home/node/claudeconfig /home/node/codexconfig /home/node/hermesconfig \
|
|
92
93
|
/home/node/.vscode-server/extensions \
|
|
93
94
|
/home/node/.vscode-server/data/Machine && \
|
|
94
95
|
echo '[]' > /home/node/.vscode-server/extensions/extensions.json && \
|
|
@@ -150,6 +151,37 @@ RUN curl -fsSL https://claude.ai/install.sh | bash
|
|
|
150
151
|
ARG CODEX_CLI_VERSION=0.144.6
|
|
151
152
|
RUN npm install -g "@openai/codex@${CODEX_CLI_VERSION}"
|
|
152
153
|
|
|
154
|
+
# Install Hermes Agent (Nous Research's open-source coding agent). Pinned to a release
|
|
155
|
+
# tag for the same reason as Codex above; bump HERMES_AGENT_VERSION to upgrade. Setup
|
|
156
|
+
# wizard, browser and computer-use extras are skipped — the agent is driven through
|
|
157
|
+
# OpenRouter: put OPENROUTER_API_KEY in ~/.devs/envs/<project>/.env (or DEVS.yml env_vars).
|
|
158
|
+
#
|
|
159
|
+
# The code goes in /home/node/.hermes-agent and the data dir (HERMES_HOME: sessions,
|
|
160
|
+
# memory, skills, config.yaml) in /home/node/hermesconfig, which devcontainer.json
|
|
161
|
+
# bind-mounts from ~/.devs/hermesconfig so it persists across rebuilds like claudeconfig
|
|
162
|
+
# and codexconfig. The installer's default puts the code *inside* HERMES_HOME, where the
|
|
163
|
+
# mount would hide it. Because config.yaml lives on the host, the default model can't be
|
|
164
|
+
# baked in here: scripts/setup-hermes.sh seeds HERMES_DEFAULT_MODEL into it at container
|
|
165
|
+
# start if no model is configured yet. HERMES_INFERENCE_MODEL overrides it per container.
|
|
166
|
+
ARG HERMES_AGENT_VERSION=v2026.9.14
|
|
167
|
+
ARG HERMES_DEFAULT_MODEL=z-ai/glm-5.3
|
|
168
|
+
ENV HERMES_HOME=/home/node/hermesconfig \
|
|
169
|
+
HERMES_DEFAULT_MODEL=${HERMES_DEFAULT_MODEL}
|
|
170
|
+
# Download first rather than `curl | bash`: /bin/sh has no pipefail, so a failed
|
|
171
|
+
# download (e.g. a GitHub 429) would otherwise run an empty script and "succeed".
|
|
172
|
+
# The installer also seeds a data dir (~85 MB: its own uv, skills, a sample config.yaml);
|
|
173
|
+
# the mount would hide it anyway, so point that at a scratch dir and delete it. Hermes
|
|
174
|
+
# re-syncs bundled skills into the mounted HERMES_HOME on first run.
|
|
175
|
+
RUN curl -fsSL --retry 5 --retry-delay 10 --retry-all-errors \
|
|
176
|
+
"https://raw.githubusercontent.com/NousResearch/hermes-agent/${HERMES_AGENT_VERSION}/scripts/install.sh" \
|
|
177
|
+
-o /tmp/hermes-install.sh && \
|
|
178
|
+
HERMES_HOME=/tmp/hermes-build-home bash /tmp/hermes-install.sh \
|
|
179
|
+
--branch "${HERMES_AGENT_VERSION}" --dir /home/node/.hermes-agent \
|
|
180
|
+
--hermes-home /tmp/hermes-build-home \
|
|
181
|
+
--non-interactive --skip-setup --skip-browser --skip-computer-use && \
|
|
182
|
+
HERMES_HOME=/tmp/hermes-build-home /home/node/.local/bin/hermes --version && \
|
|
183
|
+
rm -rf /tmp/hermes-install.sh /tmp/hermes-build-home
|
|
184
|
+
|
|
153
185
|
# Install cloudflared for tunnel support (proxies all HTTP methods correctly)
|
|
154
186
|
# Install VS Code CLI for tunnel support (needs root for /usr/local/bin)
|
|
155
187
|
# Using the standalone CLI which supports the 'tunnel' command
|
|
@@ -180,14 +212,16 @@ RUN ARCH=$(dpkg --print-architecture) && \
|
|
|
180
212
|
rm -rf /tmp/tailscale.tgz "/tmp/tailscale_${TAILSCALE_VERSION}_${ARCH}" && \
|
|
181
213
|
chmod +x /usr/local/bin/tailscale /usr/local/bin/tailscaled
|
|
182
214
|
|
|
183
|
-
# Set up environment variables and aliases for Claude and
|
|
215
|
+
# Set up environment variables and aliases for Claude, Codex and Hermes
|
|
184
216
|
RUN for shell_rc in /home/node/.zshrc /home/node/.bashrc; do \
|
|
185
217
|
echo "export CLAUDE_CONFIG_DIR=\"/home/node/claudeconfig\"" >> "$shell_rc" && \
|
|
186
|
-
echo "export
|
|
218
|
+
echo "export CODEX_HOME=\"/home/node/codexconfig\"" >> "$shell_rc" && \
|
|
187
219
|
echo "export PATH=\"/home/node/.local/bin:\$PATH\"" >> "$shell_rc" && \
|
|
188
220
|
echo "alias claude=\"claude --dangerously-skip-permissions\"" >> "$shell_rc" && \
|
|
189
221
|
echo "alias codex=\"/usr/local/share/npm-global/bin/codex -c check_for_update_on_startup=false --dangerously-bypass-approvals-and-sandbox\"" >> "$shell_rc" && \
|
|
190
222
|
echo "alias codex-normal=\"/usr/local/share/npm-global/bin/codex -c check_for_update_on_startup=false\"" >> "$shell_rc" && \
|
|
223
|
+
echo "alias hermes='/home/node/.local/bin/hermes --yolo \${HERMES_INFERENCE_MODEL:+--model=\$HERMES_INFERENCE_MODEL}'" >> "$shell_rc" && \
|
|
224
|
+
echo "alias hermes-normal='/home/node/.local/bin/hermes \${HERMES_INFERENCE_MODEL:+--model=\$HERMES_INFERENCE_MODEL}'" >> "$shell_rc" && \
|
|
191
225
|
echo "" >> "$shell_rc" && \
|
|
192
226
|
echo "# Auto-activate Python venv if it exists" >> "$shell_rc" && \
|
|
193
227
|
echo "if [ -f /home/node/.devs-venv/workspace-venv/bin/activate ]; then" >> "$shell_rc" && \
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"source=claude-code-bashhistory,target=/commandhistory,type=volume",
|
|
38
38
|
"source=${localEnv:HOME}/.devs/claudeconfig,target=/home/node/claudeconfig,type=bind",
|
|
39
39
|
"source=${localEnv:HOME}/.devs/codexconfig,target=/home/node/codexconfig,type=bind",
|
|
40
|
+
"source=${localEnv:HOME}/.devs/hermesconfig,target=/home/node/hermesconfig,type=bind",
|
|
40
41
|
"source=${localEnv:DEVS_ENV_MOUNT_PATH},target=/home/node/.devs-env,type=bind",
|
|
41
42
|
"source=${localEnv:DEVS_BRIDGE_MOUNT_PATH},target=/home/node/bridge,type=bind",
|
|
42
43
|
// Remove the extensions mount to get fresh state each time
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/extensions/devs-bridge-drop.vsix
RENAMED
|
Binary file
|
|
@@ -16,16 +16,30 @@ if [ -f /home/node/.devs-env/.env ]; then
|
|
|
16
16
|
set -a; . /home/node/.devs-env/.env; set +a
|
|
17
17
|
fi
|
|
18
18
|
|
|
19
|
+
# Per-developer start hooks: ~/.devs/envs/<project>/autoexec-start/ on the host.
|
|
20
|
+
# The every-start counterpart of the create-time autoexec/ — for whatever a hook
|
|
21
|
+
# launched that a restart kills along with everything else that isn't PID 1. Same
|
|
22
|
+
# runner; no directory => no-op. Called last on either path below, so a hook that
|
|
23
|
+
# needs tailnet names finds MagicDNS already in place.
|
|
24
|
+
run_start_hooks() {
|
|
25
|
+
DEVS_AUTOEXEC_DIR=/home/node/.devs-env/autoexec-start /usr/local/bin/run-autoexec.sh \
|
|
26
|
+
|| echo "⚠️ autoexec-start hook failed (continuing)."
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
# VS Code machine settings (+ optional tmux overlay, off unless DEVS_TMUX=1). Not
|
|
20
30
|
# Tailscale-gated — it configures the editor regardless of how you connect. Must come
|
|
21
31
|
# before the TS_ENABLE gate below, which exits when Tailscale is off.
|
|
22
32
|
/usr/local/bin/setup-vscode-settings.sh || true
|
|
23
33
|
|
|
34
|
+
# Hermes Agent's default model, seeded into its host-persisted config if none is set yet.
|
|
35
|
+
/usr/local/bin/setup-hermes.sh || true
|
|
36
|
+
|
|
24
37
|
# Gate on the same master switch as the rest of the Tailscale wiring.
|
|
25
38
|
case "${TS_ENABLE:-}" in
|
|
26
39
|
1|true|yes) ;;
|
|
27
40
|
*)
|
|
28
41
|
echo "ℹ️ Tailscale not enabled (TS_ENABLE=${TS_ENABLE:-unset}) — skipping tailnet setup."
|
|
42
|
+
run_start_hooks
|
|
29
43
|
exit 0 ;;
|
|
30
44
|
esac
|
|
31
45
|
|
|
@@ -41,3 +55,5 @@ esac
|
|
|
41
55
|
# container. Must run after the bring-up above (it no-ops unless tailscaled is up),
|
|
42
56
|
# and on every start because Docker regenerates /etc/resolv.conf each time.
|
|
43
57
|
sudo -n /usr/local/bin/setup-magicdns.sh || true
|
|
58
|
+
|
|
59
|
+
run_start_hooks
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Seed Hermes Agent's default model into its persisted config (runs as the node user from
|
|
3
|
+
# post-start.sh on every container start).
|
|
4
|
+
#
|
|
5
|
+
# HERMES_HOME (/home/node/hermesconfig) is bind-mounted from ~/.devs/hermesconfig on the
|
|
6
|
+
# host, so config.yaml can't be baked into the image. Without a configured model Hermes
|
|
7
|
+
# falls back to its own pick, so write HERMES_DEFAULT_MODEL (a Dockerfile build arg) the
|
|
8
|
+
# first time — but never overwrite a model or provider the user has chosen since, e.g.
|
|
9
|
+
# with `hermes model`. HERMES_INFERENCE_MODEL still overrides per container at run time.
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
|
|
12
|
+
HERMES=/home/node/.local/bin/hermes
|
|
13
|
+
[ -x "$HERMES" ] || exit 0
|
|
14
|
+
[ -n "${HERMES_DEFAULT_MODEL:-}" ] || exit 0
|
|
15
|
+
|
|
16
|
+
if [ -n "$("$HERMES" config get model.default 2>/dev/null)" ]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ -z "$("$HERMES" config get model.provider 2>/dev/null)" ]; then
|
|
21
|
+
"$HERMES" config set model.provider openrouter >/dev/null || exit 0
|
|
22
|
+
fi
|
|
23
|
+
"$HERMES" config set model.default "$HERMES_DEFAULT_MODEL" >/dev/null &&
|
|
24
|
+
echo "✅ Hermes default model set to $HERMES_DEFAULT_MODEL in $HERMES_HOME/config.yaml"
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-devs-env.sh
RENAMED
|
@@ -68,24 +68,6 @@ check_github_token_setup() {
|
|
|
68
68
|
git config --global core.trustctime false
|
|
69
69
|
echo "✅ Git core.trustctime set to false (prevents timezone-related rebase issues)"
|
|
70
70
|
|
|
71
|
-
# Ensure it's available in all shell sessions for the node user
|
|
72
|
-
if ! grep -q "source /home/node/.devs-env/.env" /home/node/.zshrc 2>/dev/null; then
|
|
73
|
-
echo "# Load devs environment variables" >> /home/node/.zshrc
|
|
74
|
-
echo "if [ -f /home/node/.devs-env/.env ]; then" >> /home/node/.zshrc
|
|
75
|
-
echo " set -a" >> /home/node/.zshrc
|
|
76
|
-
echo " source /home/node/.devs-env/.env" >> /home/node/.zshrc
|
|
77
|
-
echo " set +a" >> /home/node/.zshrc
|
|
78
|
-
echo "fi" >> /home/node/.zshrc
|
|
79
|
-
fi
|
|
80
|
-
if ! grep -q "source /home/node/.devs-env/.env" /home/node/.bashrc 2>/dev/null; then
|
|
81
|
-
echo "# Load devs environment variables" >> /home/node/.bashrc
|
|
82
|
-
echo "if [ -f /home/node/.devs-env/.env ]; then" >> /home/node/.bashrc
|
|
83
|
-
echo " set -a" >> /home/node/.bashrc
|
|
84
|
-
echo " source /home/node/.devs-env/.env" >> /home/node/.bashrc
|
|
85
|
-
echo " set +a" >> /home/node/.bashrc
|
|
86
|
-
echo "fi" >> /home/node/.bashrc
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
71
|
# Test if gh CLI can authenticate
|
|
90
72
|
if command -v gh >/dev/null 2>&1; then
|
|
91
73
|
if gh auth status >/dev/null 2>&1; then
|
|
@@ -104,10 +86,45 @@ check_github_token_setup() {
|
|
|
104
86
|
fi
|
|
105
87
|
}
|
|
106
88
|
|
|
89
|
+
# Load the mounted env file in every shell for the node user. This used to happen only
|
|
90
|
+
# when GH_TOKEN was set, which left other keys in the file (e.g. OPENROUTER_API_KEY for
|
|
91
|
+
# Hermes) invisible to interactive shells and to `devs <agent>` execs.
|
|
92
|
+
setup_env_file_sourcing() {
|
|
93
|
+
local rc
|
|
94
|
+
for rc in /home/node/.zshrc /home/node/.bashrc; do
|
|
95
|
+
if ! grep -q "source /home/node/.devs-env/.env" "$rc" 2>/dev/null; then
|
|
96
|
+
{
|
|
97
|
+
echo "# Load devs environment variables"
|
|
98
|
+
echo "if [ -f /home/node/.devs-env/.env ]; then"
|
|
99
|
+
echo " set -a"
|
|
100
|
+
echo " source /home/node/.devs-env/.env"
|
|
101
|
+
echo " set +a"
|
|
102
|
+
echo "fi"
|
|
103
|
+
} >> "$rc"
|
|
104
|
+
fi
|
|
105
|
+
done
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Report whether Hermes Agent has an inference provider key
|
|
109
|
+
check_hermes_setup() {
|
|
110
|
+
if [ -n "${OPENROUTER_API_KEY:-}" ]; then
|
|
111
|
+
echo "✅ OpenRouter API key (OPENROUTER_API_KEY) is available - Hermes Agent is ready"
|
|
112
|
+
else
|
|
113
|
+
echo "ℹ️ No OpenRouter API key (OPENROUTER_API_KEY) configured"
|
|
114
|
+
echo " To use Hermes Agent, add OPENROUTER_API_KEY=your_key to"
|
|
115
|
+
echo " ~/.devs/envs/<project-name>/.env (or ~/.devs/envs/default/.env)"
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
107
119
|
# Check SSH access (configured during build)
|
|
108
120
|
check_ssh_setup
|
|
109
121
|
|
|
110
122
|
# Check GitHub token access (configured via mounted env files)
|
|
111
123
|
check_github_token_setup
|
|
112
124
|
|
|
125
|
+
setup_env_file_sourcing
|
|
126
|
+
|
|
127
|
+
# check_github_token_setup sourced the env file, so OPENROUTER_API_KEY is visible here
|
|
128
|
+
check_hermes_setup
|
|
129
|
+
|
|
113
130
|
echo "Devs environment setup complete!"
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
devs_common/__init__.py
|
|
5
|
+
devs_common/agents.py
|
|
5
6
|
devs_common/config.py
|
|
6
7
|
devs_common/devs_config.py
|
|
7
8
|
devs_common/exceptions.py
|
|
@@ -31,6 +32,7 @@ devs_common/templates/scripts/post-start.sh
|
|
|
31
32
|
devs_common/templates/scripts/python-venv-integration.sh
|
|
32
33
|
devs_common/templates/scripts/run-autoexec.sh
|
|
33
34
|
devs_common/templates/scripts/screenshot-webapp.sh
|
|
35
|
+
devs_common/templates/scripts/setup-hermes.sh
|
|
34
36
|
devs_common/templates/scripts/setup-vscode-settings.sh
|
|
35
37
|
devs_common/templates/scripts/setup-workspace.sh
|
|
36
38
|
devs_common/templates/scripts/start-tailscale.sh
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/install-bridge-drop.sh
RENAMED
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/post-create-wrapper.sh
RENAMED
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/python-venv-integration.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/scripts/setup-vscode-settings.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-magicdns.sh
RENAMED
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh
RENAMED
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/start-services.sh
RENAMED
|
File without changes
|
{devs_common-5.0.6 → devs_common-6.0.0}/devs_common/templates/sudo-scripts/start-tailscaled.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|