@companyhelm/runner 0.1.1 → 0.1.3
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/RUNTIME_IMAGE_VERSION +1 -1
- package/dist/commands/doctor.js +44 -0
- package/dist/commands/register-commands.js +2 -0
- package/dist/commands/root.js +132 -40
- package/dist/commands/runner/common.js +3 -1
- package/dist/commands/runner/start.js +3 -0
- package/dist/config.js +8 -2
- package/dist/preflight/check.js +2 -0
- package/dist/preflight/checks/linux/apparmor_restrict_unprivileged_userns_check.js +96 -0
- package/dist/preflight/entrypoints.js +53 -0
- package/dist/preflight/runner_preflight.js +56 -0
- package/dist/provisioning/host_provisioning/thread_metadata_store.js +249 -0
- package/dist/provisioning/host_provisioning/thread_metadata_types.js +2 -0
- package/dist/provisioning/host_provisioning/thread_workspace_provisioner.js +57 -0
- package/dist/provisioning/runtime_provisioning/script_renderer.js +130 -0
- package/dist/provisioning/runtime_provisioning/system_prompt.js +43 -0
- package/dist/provisioning/template_renderer.js +29 -0
- package/dist/service/docker/app_server_container.js +16 -1
- package/dist/service/sdk/refresh_models.js +8 -0
- package/dist/service/thread_lifecycle.js +46 -24
- package/dist/service/thread_turn_state.js +1 -0
- package/dist/templates/provisioning/runtime_agent_cli_config.sh.j2 +8 -0
- package/dist/templates/provisioning/runtime_agent_metadata.sh.j2 +8 -0
- package/dist/templates/provisioning/runtime_bashrc.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_codex_config.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_git_config.sh.j2 +28 -0
- package/dist/templates/provisioning/runtime_identity.sh.j2 +65 -0
- package/dist/templates/provisioning/runtime_thread_git_skills_clone.sh.j2 +11 -0
- package/dist/templates/provisioning/runtime_thread_git_skills_link.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_tooling_validation.sh.j2 +32 -0
- package/dist/templates/system_prompts/common.md.j2 +46 -0
- package/dist/templates/system_prompts/dedicated_workspace.md.j2 +5 -0
- package/dist/templates/system_prompts/shared_workspace.md.j2 +5 -0
- package/dist/utils/daemon_startup_watchdog.js +27 -0
- package/package.json +1 -1
- package/dist/service/workspace_agents.js +0 -82
- package/dist/templates/runtime_agents.md.j2 +0 -50
|
@@ -15,6 +15,7 @@ const dockerode_1 = __importDefault(require("dockerode"));
|
|
|
15
15
|
const node_child_process_1 = require("node:child_process");
|
|
16
16
|
const node_fs_1 = require("node:fs");
|
|
17
17
|
const node_path_1 = require("node:path");
|
|
18
|
+
const script_renderer_js_1 = require("../provisioning/runtime_provisioning/script_renderer.js");
|
|
18
19
|
const path_js_1 = require("../utils/path.js");
|
|
19
20
|
const runtime_bashrc_js_1 = require("./runtime_bashrc.js");
|
|
20
21
|
const runtime_shell_js_1 = require("./runtime_shell.js");
|
|
@@ -193,12 +194,6 @@ function buildRuntimeToolingValidationScript(user) {
|
|
|
193
194
|
return [
|
|
194
195
|
bootstrap,
|
|
195
196
|
"",
|
|
196
|
-
'if ! command -v companyhelm-agent >/dev/null 2>&1; then',
|
|
197
|
-
' echo "companyhelm-agent CLI is not available after sourcing nvm." >&2',
|
|
198
|
-
' echo "Fix: install @companyhelm/agent-cli in the runtime image." >&2',
|
|
199
|
-
" exit 1",
|
|
200
|
-
"fi",
|
|
201
|
-
"",
|
|
202
197
|
'if ! command -v aws >/dev/null 2>&1; then',
|
|
203
198
|
' echo "aws CLI is not available in runtime PATH." >&2',
|
|
204
199
|
' echo "Fix: install awscli in the runtime image." >&2',
|
|
@@ -442,6 +437,7 @@ class ThreadContainerService {
|
|
|
442
437
|
constructor(docker, runCommand = node_child_process_1.spawnSync) {
|
|
443
438
|
this.docker = docker ?? new dockerode_1.default();
|
|
444
439
|
this.runCommand = runCommand;
|
|
440
|
+
this.scriptRenderer = new script_renderer_js_1.RuntimeProvisioningScriptRenderer();
|
|
445
441
|
}
|
|
446
442
|
runDockerExecScript(args, contextMessage) {
|
|
447
443
|
const result = this.runCommand("docker", args, {
|
|
@@ -616,46 +612,72 @@ class ThreadContainerService {
|
|
|
616
612
|
}
|
|
617
613
|
}
|
|
618
614
|
async ensureRuntimeContainerIdentity(name, user) {
|
|
619
|
-
const script =
|
|
615
|
+
const script = this.scriptRenderer.renderIdentityScript(user);
|
|
620
616
|
this.runDockerExecScript(["exec", "-u", "0", name, "bash", "-lc", script], `Failed to provision runtime user '${user.agentUser}' in container '${name}'`);
|
|
621
617
|
}
|
|
622
618
|
async ensureRuntimeContainerTooling(name, user) {
|
|
623
|
-
const script =
|
|
624
|
-
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to validate runtime tooling (nvm/codex/
|
|
619
|
+
const script = this.scriptRenderer.renderToolingValidationScript(user);
|
|
620
|
+
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to validate runtime tooling (nvm/codex/aws/playwright) in container '${name}'`);
|
|
625
621
|
}
|
|
626
622
|
async ensureRuntimeContainerBashrc(name, user) {
|
|
627
|
-
const script =
|
|
623
|
+
const script = this.scriptRenderer.renderBashrcScript(user);
|
|
628
624
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to provision runtime .bashrc in container '${name}'`);
|
|
629
625
|
}
|
|
630
626
|
async ensureRuntimeContainerCodexConfig(name, user, configToml) {
|
|
631
|
-
const script =
|
|
632
|
-
"set -euo pipefail",
|
|
633
|
-
`AGENT_HOME=${shellQuote(user.agentHomeDirectory)}`,
|
|
634
|
-
`CONFIG_CONTENT=${shellQuote(configToml)}`,
|
|
635
|
-
"",
|
|
636
|
-
'install -d -m 0755 "$AGENT_HOME/.codex"',
|
|
637
|
-
'printf \'%s\' "$CONFIG_CONTENT" > "$AGENT_HOME/.codex/config.toml"',
|
|
638
|
-
'chmod 0644 "$AGENT_HOME/.codex/config.toml"',
|
|
639
|
-
].join("\n");
|
|
627
|
+
const script = this.scriptRenderer.renderCodexConfigScript(user, configToml);
|
|
640
628
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to write runtime Codex config.toml in container '${name}'`);
|
|
641
629
|
}
|
|
642
630
|
async ensureRuntimeContainerAgentCliConfig(name, user, config) {
|
|
643
|
-
const script =
|
|
644
|
-
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to write runtime
|
|
631
|
+
const script = this.scriptRenderer.renderAgentCliConfigScript(user, config);
|
|
632
|
+
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to write runtime agent config in container '${name}'`);
|
|
645
633
|
}
|
|
646
634
|
async ensureRuntimeContainerGitConfig(name, user, gitUserName, gitUserEmail) {
|
|
647
|
-
const script =
|
|
635
|
+
const script = this.scriptRenderer.renderGitConfigScript(gitUserName, gitUserEmail);
|
|
648
636
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to configure git author defaults in runtime container '${name}'`);
|
|
649
637
|
}
|
|
650
638
|
async ensureRuntimeContainerThreadGitSkills(name, user, options) {
|
|
651
639
|
if (options.packages.length === 0) {
|
|
652
640
|
return;
|
|
653
641
|
}
|
|
654
|
-
const cloneScript =
|
|
642
|
+
const cloneScript = this.scriptRenderer.renderThreadGitSkillsCloneScript(options);
|
|
655
643
|
this.runDockerExecScript(["exec", "-u", "0", name, "bash", "-lc", cloneScript], `Failed to provision thread git skills in runtime container '${name}'`);
|
|
656
|
-
const linkScript =
|
|
644
|
+
const linkScript = this.scriptRenderer.renderThreadGitSkillsLinkScript(user, options);
|
|
657
645
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", linkScript], `Failed to provision thread git skills in runtime container '${name}'`);
|
|
658
646
|
}
|
|
647
|
+
async ensureRuntimeContainerAgentMetadataFiles(name, user, files, contextMessage) {
|
|
648
|
+
if (files.length === 0) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const script = this.scriptRenderer.renderAgentMetadataScript(user, files);
|
|
652
|
+
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], contextMessage);
|
|
653
|
+
}
|
|
654
|
+
async ensureRuntimeContainerGithubInstallations(name, user, payload) {
|
|
655
|
+
await this.ensureRuntimeContainerAgentMetadataFiles(name, user, [
|
|
656
|
+
{
|
|
657
|
+
filename: "installations.json",
|
|
658
|
+
content: `${JSON.stringify(payload, null, 2)}\n`,
|
|
659
|
+
},
|
|
660
|
+
], `Failed to write runtime GitHub installations metadata in container '${name}'`);
|
|
661
|
+
}
|
|
662
|
+
async ensureRuntimeContainerThreadMetadata(name, user, payload) {
|
|
663
|
+
const files = [
|
|
664
|
+
{
|
|
665
|
+
filename: "thread-mcp.json",
|
|
666
|
+
content: `${JSON.stringify({ servers: payload.mcpServers }, null, 2)}\n`,
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
filename: "thread-git-skills.json",
|
|
670
|
+
content: `${JSON.stringify({ packages: payload.gitSkillPackages }, null, 2)}\n`,
|
|
671
|
+
},
|
|
672
|
+
];
|
|
673
|
+
if (payload.threadAgentCliConfig) {
|
|
674
|
+
files.push({
|
|
675
|
+
filename: "thread-agent-cli.json",
|
|
676
|
+
content: `${JSON.stringify(payload.threadAgentCliConfig, null, 2)}\n`,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
await this.ensureRuntimeContainerAgentMetadataFiles(name, user, files, `Failed to write runtime thread metadata in container '${name}'`);
|
|
680
|
+
}
|
|
659
681
|
async stopContainer(name) {
|
|
660
682
|
try {
|
|
661
683
|
await this.docker.getContainer(name).stop({ t: 10 });
|
|
@@ -12,6 +12,7 @@ async function loadThreadMessageExecutionState(stateDbPath, threadId) {
|
|
|
12
12
|
.select({
|
|
13
13
|
id: schema_js_1.threads.id,
|
|
14
14
|
workspace: schema_js_1.threads.workspace,
|
|
15
|
+
cliSecret: schema_js_1.threads.cliSecret,
|
|
15
16
|
sdkThreadId: schema_js_1.threads.sdkThreadId,
|
|
16
17
|
model: schema_js_1.threads.model,
|
|
17
18
|
reasoningLevel: schema_js_1.threads.reasoningLevel,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
set -euo pipefail
|
|
2
|
+
DEFAULT_GIT_USER_NAME={{default_git_user_name}}
|
|
3
|
+
DEFAULT_GIT_USER_EMAIL={{default_git_user_email}}
|
|
4
|
+
|
|
5
|
+
if ! command -v git >/dev/null 2>&1; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
if ! git config --global --get user.name >/dev/null 2>&1; then
|
|
10
|
+
git config --global user.name "$DEFAULT_GIT_USER_NAME"
|
|
11
|
+
fi
|
|
12
|
+
if ! git config --global --get user.email >/dev/null 2>&1; then
|
|
13
|
+
git config --global user.email "$DEFAULT_GIT_USER_EMAIL"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [ ! -d /workspace ]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
while IFS= read -r -d '' git_dir; do
|
|
21
|
+
repo_root="$(dirname "$git_dir")"
|
|
22
|
+
if ! git -C "$repo_root" config --local --get user.name >/dev/null 2>&1; then
|
|
23
|
+
git -C "$repo_root" config --local user.name "$DEFAULT_GIT_USER_NAME"
|
|
24
|
+
fi
|
|
25
|
+
if ! git -C "$repo_root" config --local --get user.email >/dev/null 2>&1; then
|
|
26
|
+
git -C "$repo_root" config --local user.email "$DEFAULT_GIT_USER_EMAIL"
|
|
27
|
+
fi
|
|
28
|
+
done < <(find /workspace -type d -name .git -print0 2>/dev/null || true)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
set -euo pipefail
|
|
2
|
+
AGENT_USER={{agent_user}}
|
|
3
|
+
AGENT_HOME={{agent_home}}
|
|
4
|
+
AGENT_UID={{agent_uid}}
|
|
5
|
+
AGENT_GID={{agent_gid}}
|
|
6
|
+
|
|
7
|
+
AGENT_GROUP="$AGENT_USER"
|
|
8
|
+
EXISTING_GID_GROUP="$(getent group "$AGENT_GID" | cut -d: -f1 || true)"
|
|
9
|
+
if [ -n "$EXISTING_GID_GROUP" ] && [ "$EXISTING_GID_GROUP" != "$AGENT_USER" ] && ! getent group "$AGENT_USER" >/dev/null 2>&1; then
|
|
10
|
+
groupmod -n "$AGENT_USER" "$EXISTING_GID_GROUP"
|
|
11
|
+
fi
|
|
12
|
+
if getent group "$AGENT_USER" >/dev/null 2>&1; then
|
|
13
|
+
if [ "$(getent group "$AGENT_USER" | cut -d: -f3)" != "$AGENT_GID" ]; then
|
|
14
|
+
groupmod -g "$AGENT_GID" "$AGENT_USER"
|
|
15
|
+
fi
|
|
16
|
+
else
|
|
17
|
+
groupadd -g "$AGENT_GID" "$AGENT_USER"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
EXISTING_UID_USER="$(getent passwd "$AGENT_UID" | cut -d: -f1 || true)"
|
|
21
|
+
if [ -n "$EXISTING_UID_USER" ] && [ "$EXISTING_UID_USER" != "$AGENT_USER" ] && ! id -u "$AGENT_USER" >/dev/null 2>&1; then
|
|
22
|
+
usermod -l "$AGENT_USER" -d "$AGENT_HOME" -g "$AGENT_GROUP" -s /bin/bash "$EXISTING_UID_USER"
|
|
23
|
+
elif id -u "$AGENT_USER" >/dev/null 2>&1; then
|
|
24
|
+
usermod -u "$AGENT_UID" -g "$AGENT_GROUP" -d "$AGENT_HOME" -s /bin/bash "$AGENT_USER"
|
|
25
|
+
else
|
|
26
|
+
useradd -m -d "$AGENT_HOME" -u "$AGENT_UID" -g "$AGENT_GROUP" -s /bin/bash "$AGENT_USER"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
install -d -m 0755 -o "$AGENT_UID" -g "$AGENT_GID" "$AGENT_HOME"
|
|
30
|
+
|
|
31
|
+
SUDOERS_FILE="/etc/sudoers.d/90-companyhelm-agent"
|
|
32
|
+
if command -v sudo >/dev/null 2>&1; then
|
|
33
|
+
install -d -m 0750 /etc/sudoers.d
|
|
34
|
+
printf '%s\n' "$AGENT_USER ALL=(ALL) NOPASSWD:ALL" > "$SUDOERS_FILE"
|
|
35
|
+
chmod 0440 "$SUDOERS_FILE"
|
|
36
|
+
if command -v visudo >/dev/null 2>&1; then
|
|
37
|
+
visudo -cf "$SUDOERS_FILE" >/dev/null
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
install -d -m 0755 -o "$AGENT_UID" -g "$AGENT_GID" "$AGENT_HOME/.codex"
|
|
42
|
+
install -d -m 0755 -o "$AGENT_UID" -g "$AGENT_GID" "$AGENT_HOME/.cache"
|
|
43
|
+
if [ -e "$AGENT_HOME/.codex/auth.json" ]; then
|
|
44
|
+
chown "$AGENT_UID:$AGENT_GID" "$AGENT_HOME/.codex/auth.json" || true
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
PLAYWRIGHT_SHARED_CACHE="/ms-playwright"
|
|
48
|
+
PLAYWRIGHT_DEFAULT_CACHE="$AGENT_HOME/.cache/ms-playwright"
|
|
49
|
+
install -d -m 0755 "$PLAYWRIGHT_SHARED_CACHE"
|
|
50
|
+
if [ "$(stat -c '%u:%g' "$PLAYWRIGHT_SHARED_CACHE" 2>/dev/null || true)" != "$AGENT_UID:$AGENT_GID" ]; then
|
|
51
|
+
chown -R "$AGENT_UID:$AGENT_GID" "$PLAYWRIGHT_SHARED_CACHE" || true
|
|
52
|
+
fi
|
|
53
|
+
if [ -L "$PLAYWRIGHT_DEFAULT_CACHE" ]; then
|
|
54
|
+
if [ "$(readlink "$PLAYWRIGHT_DEFAULT_CACHE" 2>/dev/null || true)" != "$PLAYWRIGHT_SHARED_CACHE" ]; then
|
|
55
|
+
rm -f "$PLAYWRIGHT_DEFAULT_CACHE"
|
|
56
|
+
ln -s "$PLAYWRIGHT_SHARED_CACHE" "$PLAYWRIGHT_DEFAULT_CACHE"
|
|
57
|
+
fi
|
|
58
|
+
elif [ ! -e "$PLAYWRIGHT_DEFAULT_CACHE" ]; then
|
|
59
|
+
ln -s "$PLAYWRIGHT_SHARED_CACHE" "$PLAYWRIGHT_DEFAULT_CACHE"
|
|
60
|
+
else
|
|
61
|
+
chown -R "$AGENT_UID:$AGENT_GID" "$PLAYWRIGHT_DEFAULT_CACHE" || true
|
|
62
|
+
fi
|
|
63
|
+
install -d -m 0755 -o "$AGENT_UID" -g "$AGENT_GID" "$AGENT_HOME/.companyhelm"
|
|
64
|
+
install -d -m 0755 -o "$AGENT_UID" -g "$AGENT_GID" "$AGENT_HOME/.companyhelm/agent"
|
|
65
|
+
chown -R "$AGENT_UID:$AGENT_GID" "$AGENT_HOME" || true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{{bootstrap}}
|
|
2
|
+
|
|
3
|
+
if ! command -v aws >/dev/null 2>&1; then
|
|
4
|
+
echo "aws CLI is not available in runtime PATH." >&2
|
|
5
|
+
echo "Fix: install awscli in the runtime image." >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
if ! command -v playwright >/dev/null 2>&1; then
|
|
10
|
+
echo "playwright CLI is not available after sourcing nvm." >&2
|
|
11
|
+
echo "Fix: install playwright in the runtime image (for example: npm install --global playwright)." >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
PLAYWRIGHT_CACHE_DIR="${PLAYWRIGHT_BROWSERS_PATH:-$HOME/.cache/ms-playwright}"
|
|
16
|
+
if [ ! -d "$PLAYWRIGHT_CACHE_DIR" ]; then
|
|
17
|
+
echo "Playwright browser cache directory is missing: $PLAYWRIGHT_CACHE_DIR" >&2
|
|
18
|
+
echo "Fix: set PLAYWRIGHT_BROWSERS_PATH and run npx playwright install chromium during runtime image build." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
CHROMIUM_BIN="$(find "$PLAYWRIGHT_CACHE_DIR" -type f -path "*/chrome-linux/chrome" -print -quit 2>/dev/null || true)"
|
|
23
|
+
if [ -z "$CHROMIUM_BIN" ]; then
|
|
24
|
+
echo "Playwright chromium binary is missing under $PLAYWRIGHT_CACHE_DIR." >&2
|
|
25
|
+
echo "Fix: run npx playwright install chromium while building the runtime image." >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [ ! -x "$CHROMIUM_BIN" ]; then
|
|
30
|
+
echo "Playwright chromium binary exists but is not executable: $CHROMIUM_BIN" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
## Workspace Structure
|
|
4
|
+
|
|
5
|
+
- You are running in a thread-specific container and workspace.
|
|
6
|
+
- This workspace may be shared or dedicated depending on runner startup configuration.
|
|
7
|
+
- Repositories should live in subdirectories of `/workspace`.
|
|
8
|
+
|
|
9
|
+
## Docker
|
|
10
|
+
|
|
11
|
+
Docker is available, the docker host runs in a separate container. The network is shared with the container.
|
|
12
|
+
- Only `/workspace` is shared with the docker host.
|
|
13
|
+
- `{{home_directory}}/.codex/auth.json` is also shared with the docker host.
|
|
14
|
+
- Nested DinD is not supported in this environment because the outer runtime already uses rootless DinD.
|
|
15
|
+
- If you need Docker in Docker, mount the configured host runtime instead of trying nested DinD.
|
|
16
|
+
|
|
17
|
+
## GitHub Installations
|
|
18
|
+
|
|
19
|
+
- Synced GitHub installation credentials are stored at `{{home_directory}}/.companyhelm/agent/installations.json`.
|
|
20
|
+
- Use `list-installations` to inspect installation IDs, repository scopes, tokens, and expiration timestamps.
|
|
21
|
+
- Use `gh-use-installation <installation-id>` to configure `gh` authentication for a specific installation.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
list-installations
|
|
25
|
+
gh-use-installation {installation_id}
|
|
26
|
+
gh auth status --hostname github.com
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Available CLI Tools
|
|
30
|
+
|
|
31
|
+
- `list-installations`: list synced GitHub installations with repositories, access tokens, and expirations.
|
|
32
|
+
- `gh-use-installation <installation-id>`: configure `gh` authentication for a selected GitHub installation token.
|
|
33
|
+
- `aws`: AWS CLI is pre-installed and available in `PATH`.
|
|
34
|
+
- For scripted PR creation and updates, use `gh pr create --body-file <path>` and `gh pr edit --body-file <path>`.
|
|
35
|
+
- Playwright CLI is already installed and available with Chromium pre-installed.
|
|
36
|
+
|
|
37
|
+
## Agent API
|
|
38
|
+
|
|
39
|
+
- Use the CompanyHelm agent REST API directly with `Authorization: Bearer {{agent_token}}`.
|
|
40
|
+
- Agent API base URL: `{{agent_api_url}}`
|
|
41
|
+
- The bearer token above is the existing thread secret.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
curl -H "Authorization: Bearer {{agent_token}}" \
|
|
45
|
+
{{agent_api_url}}/
|
|
46
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DaemonStartupWatchdog = void 0;
|
|
4
|
+
class DaemonStartupWatchdog {
|
|
5
|
+
constructor(timeoutMs, onTimeout) {
|
|
6
|
+
this.timeoutMs = timeoutMs;
|
|
7
|
+
this.onTimeout = onTimeout;
|
|
8
|
+
this.bump();
|
|
9
|
+
}
|
|
10
|
+
bump() {
|
|
11
|
+
if (this.timer) {
|
|
12
|
+
clearTimeout(this.timer);
|
|
13
|
+
}
|
|
14
|
+
this.timer = setTimeout(() => {
|
|
15
|
+
this.timer = undefined;
|
|
16
|
+
this.onTimeout();
|
|
17
|
+
}, this.timeoutMs);
|
|
18
|
+
}
|
|
19
|
+
finish() {
|
|
20
|
+
if (!this.timer) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
clearTimeout(this.timer);
|
|
24
|
+
this.timer = undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.DaemonStartupWatchdog = DaemonStartupWatchdog;
|
package/package.json
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderRuntimeAgentsMd = renderRuntimeAgentsMd;
|
|
4
|
-
exports.ensureWorkspaceAgentsMd = ensureWorkspaceAgentsMd;
|
|
5
|
-
const node_fs_1 = require("node:fs");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
|
-
const RUNTIME_AGENTS_TEMPLATE_PATH = "templates/runtime_agents.md.j2";
|
|
8
|
-
const DEFAULT_HOME_DIRECTORY = "/home/agent";
|
|
9
|
-
function renderJinjaTemplate(template, context) {
|
|
10
|
-
return template.replace(/{{\s*([a-zA-Z0-9_]+)\s*}}/g, (_match, key) => {
|
|
11
|
-
const value = context[key];
|
|
12
|
-
if (value === undefined) {
|
|
13
|
-
throw new Error(`Missing template value for key '${key}'`);
|
|
14
|
-
}
|
|
15
|
-
return value;
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
function resolveTemplatePath() {
|
|
19
|
-
const distRelativePath = (0, node_path_1.join)(__dirname, "..", RUNTIME_AGENTS_TEMPLATE_PATH);
|
|
20
|
-
if ((0, node_fs_1.existsSync)(distRelativePath)) {
|
|
21
|
-
return distRelativePath;
|
|
22
|
-
}
|
|
23
|
-
const sourceRelativePath = (0, node_path_1.join)(__dirname, "..", "..", "src", RUNTIME_AGENTS_TEMPLATE_PATH);
|
|
24
|
-
if ((0, node_fs_1.existsSync)(sourceRelativePath)) {
|
|
25
|
-
return sourceRelativePath;
|
|
26
|
-
}
|
|
27
|
-
throw new Error(`Runtime AGENTS template was not found at ${distRelativePath} or ${sourceRelativePath}`);
|
|
28
|
-
}
|
|
29
|
-
function extractTopLevelSections(markdown) {
|
|
30
|
-
const sections = [];
|
|
31
|
-
const headingRegex = /^## .+$/gm;
|
|
32
|
-
const matches = [...markdown.matchAll(headingRegex)];
|
|
33
|
-
for (let index = 0; index < matches.length; index += 1) {
|
|
34
|
-
const match = matches[index];
|
|
35
|
-
const marker = match[0].trim();
|
|
36
|
-
const start = match.index ?? 0;
|
|
37
|
-
const end = index + 1 < matches.length ? (matches[index + 1].index ?? markdown.length) : markdown.length;
|
|
38
|
-
const content = markdown.slice(start, end).trimEnd();
|
|
39
|
-
sections.push({ marker, content });
|
|
40
|
-
}
|
|
41
|
-
return sections;
|
|
42
|
-
}
|
|
43
|
-
function renderRuntimeAgentsMd(homeDirectory = DEFAULT_HOME_DIRECTORY) {
|
|
44
|
-
const template = (0, node_fs_1.readFileSync)(resolveTemplatePath(), "utf8");
|
|
45
|
-
return renderJinjaTemplate(template, {
|
|
46
|
-
home_directory: homeDirectory,
|
|
47
|
-
}).trim() + "\n";
|
|
48
|
-
}
|
|
49
|
-
function ensureWorkspaceAgentsMd(workspaceDirectory, homeDirectory = DEFAULT_HOME_DIRECTORY) {
|
|
50
|
-
(0, node_fs_1.mkdirSync)(workspaceDirectory, { recursive: true });
|
|
51
|
-
const agentsPath = (0, node_path_1.join)(workspaceDirectory, "AGENTS.md");
|
|
52
|
-
let existing = "";
|
|
53
|
-
try {
|
|
54
|
-
existing = (0, node_fs_1.readFileSync)(agentsPath, "utf8");
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
existing = "";
|
|
58
|
-
}
|
|
59
|
-
let rendered = "";
|
|
60
|
-
try {
|
|
61
|
-
rendered = renderRuntimeAgentsMd(homeDirectory);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const templateSections = extractTopLevelSections(rendered);
|
|
67
|
-
const pendingSections = templateSections
|
|
68
|
-
.filter((section) => !existing.includes(section.marker))
|
|
69
|
-
.map((section) => section.content);
|
|
70
|
-
if (pendingSections.length === 0) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const updated = existing.trim()
|
|
74
|
-
? `${existing.trimEnd()}\n\n${pendingSections.join("\n\n")}\n`
|
|
75
|
-
: rendered;
|
|
76
|
-
try {
|
|
77
|
-
(0, node_fs_1.writeFileSync)(agentsPath, updated, "utf8");
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// Best-effort workspace instruction file.
|
|
81
|
-
}
|
|
82
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Agent Instructions
|
|
2
|
-
|
|
3
|
-
## Workspace Structure
|
|
4
|
-
|
|
5
|
-
- You are running in a thread-specific container and workspace
|
|
6
|
-
- This workspace is not initialized as a Git repository by default. Repositories should live in a subdirectory of the workspace.
|
|
7
|
-
|
|
8
|
-
## Docker
|
|
9
|
-
|
|
10
|
-
Docker is available, the docker host runs in a separate container. The network is shared with container.
|
|
11
|
-
- only the `/workspace` is shared with the docker host
|
|
12
|
-
- `{{home_directory}}/.codex/auth.json` is also shared with the docker host
|
|
13
|
-
- Nested DinD is not supported in this environment because the outer runtime already uses rootless DinD.
|
|
14
|
-
- If you need to use Docker in Docker you can mount the docker socket in the container and use this environment DinD to run containers within containers.
|
|
15
|
-
|
|
16
|
-
## GitHub Installations
|
|
17
|
-
|
|
18
|
-
- Synced GitHub installation credentials are written to `/workspace/.companyhelm/installations.json`.
|
|
19
|
-
- Use `list-installations` to inspect installation IDs, repository scopes, tokens, and expiration timestamps.
|
|
20
|
-
- Use `gh-use-installation <installation-id>` to configure `gh` authentication for a specific installation.
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
# Inspect synced installation credentials
|
|
24
|
-
list-installations
|
|
25
|
-
|
|
26
|
-
# Configure gh to use installation 112331765
|
|
27
|
-
gh-use-installation 112331765
|
|
28
|
-
|
|
29
|
-
# Verify gh is authenticated for github.com
|
|
30
|
-
gh auth status --hostname github.com
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Available CLI Tools
|
|
34
|
-
|
|
35
|
-
- `list-installations`: list synced GitHub installations with repositories, access tokens, and expirations.
|
|
36
|
-
- `gh-use-installation <installation-id>`: configure `gh` authentication for a selected GitHub installation token.
|
|
37
|
-
- `aws`: AWS CLI is pre-installed and available in `PATH`.
|
|
38
|
-
- For scripted PR creation/updates, always use `gh pr create --body-file <path>` and `gh pr edit --body-file <path>` instead of inline `--body` to avoid shell interpolation of markdown backticks.
|
|
39
|
-
- DO NOT INSTALL PLAYWRIGHT IN THE RUNTIME IMAGE. Playwright CLI is already installed and available for browser automation tasks with Chromium pre-installed: `playwright open --browser=chromium ...`
|
|
40
|
-
|
|
41
|
-
## CompanyHelm Agent CLI
|
|
42
|
-
|
|
43
|
-
- `companyhelm-agent` is pre-installed in the runtime image.
|
|
44
|
-
- Thread bootstrap writes `{{home_directory}}/.config/companyhelm-agent-cli/config.json` with:
|
|
45
|
-
- `agent_api_url`: localhost targets are rewritten to `host.docker.internal` (for example `http://host.docker.internal:<port>`) for Docker-to-host access.
|
|
46
|
-
- `token`: sourced from the thread secret.
|
|
47
|
-
- Example commands:
|
|
48
|
-
- `companyhelm-agent task get --task-id <id>`
|
|
49
|
-
- `companyhelm-agent task dependencies --task-id <id>`
|
|
50
|
-
- `companyhelm-agent task update-status --task-id <id> --status <draft|pending|in_progress|completed>`
|