headlesscode 1.0.2
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/ATTRIBUTION.md +53 -0
- package/CODE_OF_CONDUCT.md +130 -0
- package/CONTRIBUTING.md +107 -0
- package/LICENSE +202 -0
- package/README.md +486 -0
- package/SECURITY.md +211 -0
- package/bin/headlesscode.mjs +83 -0
- package/package.json +63 -0
- package/shared/prompts/review-mode-prompt-short.md +93 -0
- package/shared/prompts/review-mode-prompt.md +281 -0
- package/shared/rules-code/rules.md +22 -0
- package/shared/stacks/cpp/rules.md +30 -0
- package/shared/stacks/fastapi/rules.md +30 -0
- package/shared/stacks/javascript/rules.md +37 -0
- package/shared/stacks/postgresql/rules.md +31 -0
- package/shared/stacks/python/rules.md +35 -0
- package/shared/stacks/react/rules.md +11 -0
- package/shared/stacks/typescript/rules.md +10 -0
- package/src/budget/budget.ts +221 -0
- package/src/budget/concurrency.ts +126 -0
- package/src/budget/cost.ts +309 -0
- package/src/budget/index.ts +8 -0
- package/src/checkpoints/cli.ts +256 -0
- package/src/checkpoints/service.ts +227 -0
- package/src/cli.ts +1535 -0
- package/src/cloud/docker-provider.ts +334 -0
- package/src/cloud/provider.ts +300 -0
- package/src/codeintel/call-graph.ts +78 -0
- package/src/codeintel/find-references.ts +123 -0
- package/src/codeintel/go-to-definition.ts +193 -0
- package/src/codeintel/handlers.ts +190 -0
- package/src/codeintel/import-graph.ts +173 -0
- package/src/codeintel/outline.ts +180 -0
- package/src/codeintel/position.ts +77 -0
- package/src/codeintel/program.ts +350 -0
- package/src/codeintel/rename-symbol.ts +213 -0
- package/src/codeintel/tools.ts +280 -0
- package/src/codemap/build.ts +135 -0
- package/src/codemap/cli.ts +190 -0
- package/src/codemap/extract.ts +339 -0
- package/src/codemap/files.ts +236 -0
- package/src/codemap/fingerprint.ts +65 -0
- package/src/codemap/flows.ts +62 -0
- package/src/codemap/html.ts +451 -0
- package/src/codemap/lock.ts +80 -0
- package/src/codemap/types.ts +101 -0
- package/src/codesearch/airunner-embedder.ts +185 -0
- package/src/codesearch/chunk.ts +339 -0
- package/src/codesearch/cli.ts +223 -0
- package/src/codesearch/embedder.ts +332 -0
- package/src/codesearch/files.ts +280 -0
- package/src/codesearch/index.ts +469 -0
- package/src/codesearch/ollama-embedder.ts +205 -0
- package/src/codesearch/search.ts +141 -0
- package/src/codesearch/types.ts +100 -0
- package/src/config/mode-models.ts +218 -0
- package/src/dashboard/aggregate.ts +364 -0
- package/src/dashboard/chat-thread.ts +141 -0
- package/src/dashboard/checkpoints.ts +124 -0
- package/src/dashboard/cli.ts +193 -0
- package/src/dashboard/codemap.ts +44 -0
- package/src/dashboard/files.ts +121 -0
- package/src/dashboard/page.ts +2803 -0
- package/src/dashboard/self-improvement-metrics.ts +282 -0
- package/src/dashboard/server.ts +1103 -0
- package/src/dashboard/session-launch.ts +310 -0
- package/src/dashboard/timeline.ts +273 -0
- package/src/dashboard/tool-exec.ts +107 -0
- package/src/dashboard/trend-cli.ts +141 -0
- package/src/dashboard/trend.ts +413 -0
- package/src/decision-proxy/cli.ts +261 -0
- package/src/decision-proxy/proxy.ts +569 -0
- package/src/deploy/gate-cli.ts +147 -0
- package/src/deploy/gate.ts +254 -0
- package/src/engine/condense.ts +512 -0
- package/src/engine/events.ts +428 -0
- package/src/engine/handoff.ts +71 -0
- package/src/engine/lazy-tools.ts +160 -0
- package/src/engine/local-explore.ts +653 -0
- package/src/engine/logger.ts +96 -0
- package/src/engine/loop.ts +5517 -0
- package/src/engine/parser.ts +347 -0
- package/src/engine/prompt.ts +860 -0
- package/src/engine/reports.ts +47 -0
- package/src/engine/stacks.ts +448 -0
- package/src/engine/types.ts +291 -0
- package/src/engine/usage.ts +186 -0
- package/src/github/app-auth.ts +161 -0
- package/src/github/cli.ts +448 -0
- package/src/github/installations.ts +133 -0
- package/src/github/pr.ts +321 -0
- package/src/github/provision.ts +118 -0
- package/src/github/push.ts +122 -0
- package/src/index-util.ts +50 -0
- package/src/index.ts +81 -0
- package/src/init/cli.ts +248 -0
- package/src/init/gitignore.ts +74 -0
- package/src/llm/ollama.ts +308 -0
- package/src/llm/openrouter.ts +868 -0
- package/src/llm/preflight.ts +367 -0
- package/src/llm/transcript-capture.ts +84 -0
- package/src/memory/embed.ts +110 -0
- package/src/memory/index.ts +22 -0
- package/src/memory/local.ts +259 -0
- package/src/memory/summarizer.ts +283 -0
- package/src/memory/types.ts +153 -0
- package/src/memory/uwuchat.ts +157 -0
- package/src/migrate/cli.ts +115 -0
- package/src/orchestrator/analyze-cli.ts +104 -0
- package/src/orchestrator/auto-split.ts +206 -0
- package/src/orchestrator/cleanup.ts +1003 -0
- package/src/orchestrator/cli.ts +3571 -0
- package/src/orchestrator/cost-estimate.ts +564 -0
- package/src/orchestrator/cost-history-cli.ts +242 -0
- package/src/orchestrator/cost-history.ts +397 -0
- package/src/orchestrator/git-sync.ts +250 -0
- package/src/orchestrator/index.ts +153 -0
- package/src/orchestrator/log-analysis.ts +0 -0
- package/src/orchestrator/merge-check.ts +108 -0
- package/src/orchestrator/pipeline.ts +411 -0
- package/src/orchestrator/resume.ts +1940 -0
- package/src/orchestrator/reviewer.ts +503 -0
- package/src/orchestrator/split.ts +296 -0
- package/src/orchestrator/state.ts +542 -0
- package/src/orchestrator/status.ts +697 -0
- package/src/orchestrator/verification-gate.ts +134 -0
- package/src/orchestrator/watch.ts +898 -0
- package/src/permissions/commands.ts +1083 -0
- package/src/permissions/config.ts +241 -0
- package/src/permissions/index.ts +12 -0
- package/src/permissions/protected-files.ts +96 -0
- package/src/permissions/store-protection.ts +272 -0
- package/src/project-store.ts +648 -0
- package/src/projects/cli.ts +382 -0
- package/src/qa/qa.ts +487 -0
- package/src/tools/browser/handler.ts +346 -0
- package/src/tools/browser/service.ts +406 -0
- package/src/tools/browser/smoke.ts +78 -0
- package/src/tools/browser/tool.ts +99 -0
- package/src/tools/executor.ts +2575 -0
- package/src/tools/language-detect.ts +183 -0
- package/src/tools/output-summarizer.ts +369 -0
- package/src/tools/run-tests.ts +302 -0
- package/src/tools/set-indentation-tool.ts +49 -0
- package/src/tools/test-selection.ts +160 -0
- package/src/vendor/tests/smoke.ts +103 -0
- package/src/vendor/zoo-code/VENDOR-NOTES.md +213 -0
- package/src/vendor/zoo-code/shim/anthropic.ts +71 -0
- package/src/vendor/zoo-code/shim/openai.d.ts +60 -0
- package/src/vendor/zoo-code/shim/os-name.ts +18 -0
- package/src/vendor/zoo-code/shim/strip-bom.ts +14 -0
- package/src/vendor/zoo-code/shim/vscode.ts +76 -0
- package/src/vendor/zoo-code/src/core/config/CustomModesManager.ts +1015 -0
- package/src/vendor/zoo-code/src/core/diff/strategies/multi-search-replace.ts +670 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/capabilities.ts +46 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/custom-instructions.ts +559 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/index.ts +10 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/markdown-formatting.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/modes.ts +35 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/objective.ts +13 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/rules.ts +95 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/skills.ts +105 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/system-info.ts +30 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use-guidelines.ts +9 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/system.ts +176 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/access_mcp_resource.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_diff.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_patch.ts +61 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/ask_followup_question.ts +62 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/attempt_completion.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/codebase_search.ts +43 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/converters.ts +109 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit.ts +48 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit_file.ts +72 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/execute_command.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/generate_image.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/index.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/list_files.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/mcp_server.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/new_task.ts +39 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_command_output.ts +81 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_file.ts +169 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/run_slash_command.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_files.ts +50 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_replace.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/skill.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/switch_mode.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/update_todo_list.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/write_to_file.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/types.ts +12 -0
- package/src/vendor/zoo-code/src/i18n/index.ts +19 -0
- package/src/vendor/zoo-code/src/integrations/misc/extract-text.ts +81 -0
- package/src/vendor/zoo-code/src/services/checkpoints/RepoPerTaskCheckpointService.ts +15 -0
- package/src/vendor/zoo-code/src/services/checkpoints/ShadowCheckpointService.ts +553 -0
- package/src/vendor/zoo-code/src/services/checkpoints/excludes.ts +212 -0
- package/src/vendor/zoo-code/src/services/checkpoints/index.ts +3 -0
- package/src/vendor/zoo-code/src/services/checkpoints/types.ts +35 -0
- package/src/vendor/zoo-code/src/services/code-index/manager.ts +19 -0
- package/src/vendor/zoo-code/src/services/mcp/McpHub.ts +36 -0
- package/src/vendor/zoo-code/src/services/roo-config/index.ts +441 -0
- package/src/vendor/zoo-code/src/services/search/file-search.ts +143 -0
- package/src/vendor/zoo-code/src/services/skills/SkillsManager.ts +20 -0
- package/src/vendor/zoo-code/src/shared/globalFileNames.ts +9 -0
- package/src/vendor/zoo-code/src/shared/language.ts +43 -0
- package/src/vendor/zoo-code/src/shared/modes.ts +257 -0
- package/src/vendor/zoo-code/src/shared/tools.ts +385 -0
- package/src/vendor/zoo-code/src/utils/fs.ts +39 -0
- package/src/vendor/zoo-code/src/utils/globalContext.ts +22 -0
- package/src/vendor/zoo-code/src/utils/json-schema.ts +16 -0
- package/src/vendor/zoo-code/src/utils/logging.ts +21 -0
- package/src/vendor/zoo-code/src/utils/mcp-name.ts +190 -0
- package/src/vendor/zoo-code/src/utils/object.ts +18 -0
- package/src/vendor/zoo-code/src/utils/path.ts +94 -0
- package/src/vendor/zoo-code/src/utils/shell.ts +376 -0
- package/src/vendor/zoo-code/src/utils/text-normalization.ts +99 -0
- package/src/vendor/zoo-code/types/global-settings.ts +19 -0
- package/src/vendor/zoo-code/types/index.ts +22 -0
- package/src/vendor/zoo-code/types/message.ts +375 -0
- package/src/vendor/zoo-code/types/mode.ts +241 -0
- package/src/vendor/zoo-code/types/todo.ts +19 -0
- package/src/vendor/zoo-code/types/tool-params.ts +116 -0
- package/src/vendor/zoo-code/types/tool.ts +67 -0
- package/src/vendor/zoo-code/types/vscode.ts +84 -0
- package/src/vision/describe.ts +242 -0
- package/src/vision/tool.ts +91 -0
- package/src/watcher/cli.ts +369 -0
- package/src/watcher/github.ts +304 -0
- package/src/watcher/index.ts +59 -0
- package/src/watcher/state.ts +254 -0
- package/src/watcher/watch.ts +562 -0
- package/tsconfig.json +18 -0
package/SECURITY.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## TL;DR
|
|
4
|
+
|
|
5
|
+
`headlesscode` is a **headless coding-agent harness**: an AI model reads a task,
|
|
6
|
+
then drives your machine by running shell commands and editing files. By design,
|
|
7
|
+
an unconfigured session runs **arbitrary commands with your full user
|
|
8
|
+
privileges** and can **read and modify your files** — including `~/.ssh`,
|
|
9
|
+
`~/.aws`, and any other credentials the user can access.
|
|
10
|
+
|
|
11
|
+
**Run it only on a machine you are willing to let an AI operate, only on
|
|
12
|
+
repositories you trust, and only with untrusted content isolated from your real
|
|
13
|
+
session.** This project does not sandbox agent actions, and nothing in the
|
|
14
|
+
harness can be relied upon to stop a malicious or compromised model that has
|
|
15
|
+
been given the ability to execute commands.
|
|
16
|
+
|
|
17
|
+
## Default-allow command execution
|
|
18
|
+
|
|
19
|
+
An unconfigured session gives the agent:
|
|
20
|
+
|
|
21
|
+
- **Arbitrary shell command execution.** `execute_command` spawns the
|
|
22
|
+
model-supplied command string with `shell: true`, the full process
|
|
23
|
+
environment, and no sandbox (see
|
|
24
|
+
[`src/tools/executor.ts`](src/tools/executor.ts:1286) and
|
|
25
|
+
[`src/permissions/commands.ts`](src/permissions/commands.ts:834)). The
|
|
26
|
+
command runs as **you** — the same OS user, with the same files, network
|
|
27
|
+
access, and credentials the harness process has. There is no per-command
|
|
28
|
+
approval prompt (the harness is non-interactive by design), no container, and
|
|
29
|
+
no privilege boundary.
|
|
30
|
+
- **File read/write.** The write tools (`write_to_file`, `apply_diff`,
|
|
31
|
+
`search_replace`, `edit_file`) can create and overwrite any file under the
|
|
32
|
+
workspace root. Reads are not restricted at all.
|
|
33
|
+
|
|
34
|
+
An empty `allowedCommands` list means **allow everything** — only the
|
|
35
|
+
`deniedCommands` list (also empty by default) restricts anything. This is a
|
|
36
|
+
deliberate design choice for a headless tool: it preserves the behavior of a
|
|
37
|
+
local CLI that a developer runs in their own checkout. It is **not** a security
|
|
38
|
+
boundary.
|
|
39
|
+
|
|
40
|
+
### What this means in practice
|
|
41
|
+
|
|
42
|
+
With default settings, an agent can, for example:
|
|
43
|
+
|
|
44
|
+
- Read `~/.ssh/id_rsa`, `~/.aws/credentials`, or any other file the user can
|
|
45
|
+
read.
|
|
46
|
+
- Exfiltrate data over the network (`curl`, `scp`, `gh`, …).
|
|
47
|
+
- Modify, delete, or encrypt files; install software; change system
|
|
48
|
+
configuration.
|
|
49
|
+
- Persist backdoors or leave any other modification on the machine.
|
|
50
|
+
|
|
51
|
+
Do not point this at data or machines where that outcome would be
|
|
52
|
+
unacceptable, and do not feed it untrusted task text or repository content you
|
|
53
|
+
have not reviewed.
|
|
54
|
+
|
|
55
|
+
## When untrusted content is involved
|
|
56
|
+
|
|
57
|
+
If the task text, the repository, or anything the agent will read could be
|
|
58
|
+
hostile, treat the agent as untrusted code running as your user. The only safe
|
|
59
|
+
mitigation is OS-level isolation **outside** this project — for example a
|
|
60
|
+
throwaway container, VM, or dedicated user account with no access to your real
|
|
61
|
+
credentials:
|
|
62
|
+
|
|
63
|
+
- No credentials (SSH keys, cloud tokens, API keys) mounted unless the task
|
|
64
|
+
genuinely needs them.
|
|
65
|
+
- No writable access to anything you care about beyond a scratch workspace.
|
|
66
|
+
- Network access limited if exfiltration is a concern.
|
|
67
|
+
|
|
68
|
+
The harness itself cannot provide this isolation, and no amount of prompt
|
|
69
|
+
engineering, rules files, or "trusted mode" configuration is a substitute for
|
|
70
|
+
it.
|
|
71
|
+
|
|
72
|
+
## Browser and vision cloud data flow
|
|
73
|
+
|
|
74
|
+
Two agent-driven tools send data off the machine as part of their normal
|
|
75
|
+
operation — not a bug, but worth knowing before pointing either at anything
|
|
76
|
+
sensitive:
|
|
77
|
+
|
|
78
|
+
- **Browser tool.** `browser_action` (see
|
|
79
|
+
[`src/tools/browser/service.ts`](src/tools/browser/service.ts:112)) launches
|
|
80
|
+
a headless Chromium instance and navigates it to whatever URL the model
|
|
81
|
+
supplies. The page content, cookies set by the site, and any redirects are
|
|
82
|
+
fetched directly by the browser process on your machine/network — the same
|
|
83
|
+
as visiting the URL yourself. If the model is given (or invents) a URL for
|
|
84
|
+
an internal or sensitive endpoint, that page's content becomes visible to
|
|
85
|
+
the model in its response.
|
|
86
|
+
- **Vision (image description).** `describeImage`
|
|
87
|
+
(see [`src/vision/describe.ts`](src/vision/describe.ts:118)) base64-encodes
|
|
88
|
+
a screenshot or workspace image and POSTs it to OpenRouter
|
|
89
|
+
(`openrouter.ai`, or `HEADLESSCODE_VISION_MODEL`'s configured provider) for
|
|
90
|
+
captioning, then feeds the description back into the model's context. The
|
|
91
|
+
image bytes themselves leave the machine and are processed by a third-party
|
|
92
|
+
cloud model, subject to that provider's own data-handling policy.
|
|
93
|
+
|
|
94
|
+
Neither flow has a local-only mode today. Do not use `browser_action` against
|
|
95
|
+
URLs, or `describeImage` against screenshots/images, that contain data you
|
|
96
|
+
would not otherwise send to a third-party cloud API.
|
|
97
|
+
|
|
98
|
+
## The permissions mechanism (defense in depth, not a boundary)
|
|
99
|
+
|
|
100
|
+
The harness ships an optional permissions layer that can restrict what the
|
|
101
|
+
agent does — see [`docs/central-store.md`](docs/central-store.md) and
|
|
102
|
+
[`src/permissions/config.ts`](src/permissions/config.ts). It includes:
|
|
103
|
+
|
|
104
|
+
- `--allowed-commands` / `--denied-commands` (or
|
|
105
|
+
`HEADLESSCODE_ALLOWED_COMMANDS` / `HEADLESSCODE_DENIED_COMMANDS`, or a
|
|
106
|
+
central `permissions.json`) — allow/deny prefix matching for commands.
|
|
107
|
+
- `--protected-files` / `HEADLESSCODE_PROTECTED_FILES` — glob patterns of files
|
|
108
|
+
the write tools refuse to touch (defaults protect `.env`, `*.pem`, `*.key`,
|
|
109
|
+
`id_rsa*`).
|
|
110
|
+
- `--allow-protected-writes` — explicit escape hatch that bypasses the
|
|
111
|
+
protected-files check (OFF by default).
|
|
112
|
+
|
|
113
|
+
This layer is useful defense in depth against accidents and makes refusal
|
|
114
|
+
messages actionable, but it is a **prefix-match policy on a command string**, not
|
|
115
|
+
a sandbox. A malicious or sufficiently capable model can evade it (e.g. by
|
|
116
|
+
writing a script and running it, or by abusing any command that is allowed), so
|
|
117
|
+
**do not rely on it to contain untrusted code.** Restricting commands does not
|
|
118
|
+
restrict what the agent can *read* or what allowed commands can accomplish.
|
|
119
|
+
|
|
120
|
+
## Known limitations (tracked audit findings)
|
|
121
|
+
|
|
122
|
+
These are specific, known gaps in the defense-in-depth layers above — not new
|
|
123
|
+
risks beyond the "not a sandbox" scope already documented, but concrete ways
|
|
124
|
+
the prefix-match and pattern-based checks can be evaded. Documented here per
|
|
125
|
+
the project's own audit process rather than silently left implicit.
|
|
126
|
+
|
|
127
|
+
- **Deny-list is a raw-string prefix match, not a program-identity check**
|
|
128
|
+
(`src/permissions/commands.ts` — `findLongestPrefixMatch`). A deny entry of
|
|
129
|
+
`rm` is trivially evaded by `command rm`, `/bin/rm`, `env rm`, `xargs rm`,
|
|
130
|
+
or `python -c "shutil.rmtree(...)"` — none of these share the `rm` prefix.
|
|
131
|
+
The allow/deny mechanism is prefix matching on the literal command string by
|
|
132
|
+
design (ported verbatim from upstream for parity); it does not resolve
|
|
133
|
+
`argv[0]`, follow `PATH`, or understand that many programs can accomplish
|
|
134
|
+
the same effect. Treat every deny-list entry as a speed bump against
|
|
135
|
+
accidental/careless commands, never as a guarantee that a given program
|
|
136
|
+
cannot run.
|
|
137
|
+
- **Central-store protection (`src/permissions/store-protection.ts`) is
|
|
138
|
+
pattern-based, not a sandbox.** As of the SEC-6 fix it protects the store
|
|
139
|
+
root, its ancestors, AND its descendants, and it follows symlinks
|
|
140
|
+
(`fs.realpathSync`) and tracks a leading `cd <dir> &&` chain's effective
|
|
141
|
+
cwd. It still does **not** catch: `cd` performed inside a subshell
|
|
142
|
+
(`(cd /x && rm -rf y)`) or via a shell variable/`pushd`/`popd`; `sudo rm`;
|
|
143
|
+
non-`rm` deletion (`find ... -delete`, `python -c shutil.rmtree(...)`, a
|
|
144
|
+
script that deletes the store); or a non-recursive `rm` of a single file
|
|
145
|
+
inside the store. It only recognizes GNU/POSIX `rm` flag syntax.
|
|
146
|
+
- **The dashboard's local HTTP server gates only POST routes with the bearer
|
|
147
|
+
token** (`HEADLESSCODE_DASHBOARD_TOKEN`); GET routes that list/read
|
|
148
|
+
workspace files, checkpoints, codemap, cost history, and projects are
|
|
149
|
+
reachable by any request that can reach `127.0.0.1` on the dashboard port
|
|
150
|
+
(including via DNS-rebinding or localhost-CSRF from a browser tab open to a
|
|
151
|
+
malicious page). Do not run the dashboard on a shared or multi-user machine
|
|
152
|
+
without additional network isolation (see also
|
|
153
|
+
[`src/dashboard/files.ts`](src/dashboard/files.ts) and
|
|
154
|
+
[`src/dashboard/trend.ts`](src/dashboard/trend.ts)).
|
|
155
|
+
- **`execute_command` children inherit the full process environment,**
|
|
156
|
+
including `HEADLESSCODE_OPENROUTER_API_KEY` and any other secret exported
|
|
157
|
+
into the harness's own env. `protected-files.ts` only prevents the agent
|
|
158
|
+
from *writing* `.env`; nothing stops the agent from reading its own
|
|
159
|
+
process's environment (`printenv`, `env`) and having that flow into the LLM
|
|
160
|
+
context. Acceptable for the documented trusted-single-user model; a real
|
|
161
|
+
constraint if this project is ever run multi-tenant (see
|
|
162
|
+
`docs/multi-tenant-hosting-design.md`) — don't put shared secrets in a
|
|
163
|
+
session's environment that a given task should not be able to see.
|
|
164
|
+
|
|
165
|
+
## Reporting vulnerabilities
|
|
166
|
+
|
|
167
|
+
This project is pre-1.0 and has no private disclosure channel yet. For now,
|
|
168
|
+
report security findings by opening a GitHub issue on
|
|
169
|
+
[Capsize-Games/headlesscode](https://github.com/Capsize-Games/headlesscode)
|
|
170
|
+
tagged `security`, or a pull request with a fix. See the docs in `docs/` for
|
|
171
|
+
the overall design.
|
|
172
|
+
|
|
173
|
+
## Supported versions
|
|
174
|
+
|
|
175
|
+
Only the current `master` branch is supported. The project has not cut
|
|
176
|
+
releases yet (no tags, `version: 0.1.0`); patches land on `master` and are
|
|
177
|
+
expected to be forward-only.
|
|
178
|
+
|
|
179
|
+
## Security-relevant areas
|
|
180
|
+
|
|
181
|
+
If you are auditing the codebase, the highest-value targets are:
|
|
182
|
+
|
|
183
|
+
- `src/tools/executor.ts` — the tool executor, including the
|
|
184
|
+
`execute_command` handler that spawns shell commands and the path-traversal
|
|
185
|
+
guard for file tools.
|
|
186
|
+
- `src/permissions/` — command allow/deny, protected files, and the
|
|
187
|
+
central-store protection; defaults are documented in `config.ts`.
|
|
188
|
+
- `src/cloud/` and `src/orchestrator/` — session isolation, worktree
|
|
189
|
+
spawning, and the scripts they shell out to (`scripts/*.sh`).
|
|
190
|
+
- `src/vendor/zoo-code/` — the vendored Apache-2.0 portable core (see
|
|
191
|
+
`ATTRIBUTION.md`); upstream security fixes should be tracked via
|
|
192
|
+
`VENDOR-NOTES.md`.
|
|
193
|
+
|
|
194
|
+
The npm package (`npm pack`) ships only the runtime source under `src/`
|
|
195
|
+
(excluding `__tests__/`), `shared/`, and the top-level docs — internal
|
|
196
|
+
`plans/`, `recon/`, `.roo/`, and dev scripts are intentionally excluded (see
|
|
197
|
+
the `files` field in `package.json`).
|
|
198
|
+
|
|
199
|
+
## Responsible-use checklist
|
|
200
|
+
|
|
201
|
+
Before running `headlesscode` on anything you care about:
|
|
202
|
+
|
|
203
|
+
1. Confirm you are running it as a user whose privileges you are willing to
|
|
204
|
+
expose to the agent (ideally a dedicated, unprivileged account).
|
|
205
|
+
2. Confirm the workspace and task are trusted, or the environment is isolated
|
|
206
|
+
(container/VM) so the blast radius is contained.
|
|
207
|
+
3. Review the command permissions (`--allowed-commands` /
|
|
208
|
+
`--denied-commands`) and protected files (`--protected-files`) if you want
|
|
209
|
+
the defense-in-depth layer on.
|
|
210
|
+
4. Never run it with credentials or secrets mounted that the task does not
|
|
211
|
+
need.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* headlesscode bin launcher (issue #99).
|
|
4
|
+
*
|
|
5
|
+
* npm's `bin` entry used to point at `src/cli.ts`, whose shebang is
|
|
6
|
+
* `#!/usr/bin/env tsx` — but tsx is a devDependency, so on a clean
|
|
7
|
+
* `npm install -g` (which installs dependencies only) `env` finds no `tsx`
|
|
8
|
+
* on PATH and every invocation fails with "tsx: not found".
|
|
9
|
+
*
|
|
10
|
+
* This launcher fixes that by resolving THIS package's own node_modules/tsx
|
|
11
|
+
* directly (the same ancestor walk-up `scripts/run-tests.mjs` uses — npm
|
|
12
|
+
* flattens node_modules, so tsx may live in this package's node_modules OR
|
|
13
|
+
* be hoisted to an ancestor's) and re-execing node through tsx's CLI entry.
|
|
14
|
+
* It also pins TSX_TSCONFIG_PATH to this package's tsconfig.json so tsx
|
|
15
|
+
* resolves the package's own tsconfig instead of discovering one from the
|
|
16
|
+
* caller's CWD (the tsconfig now has no `paths` aliases — issue #99 rework —
|
|
17
|
+
* but the pin keeps the package's compiler options, e.g. `moduleResolution`,
|
|
18
|
+
* stable regardless of where the CLI is invoked from).
|
|
19
|
+
*
|
|
20
|
+
* A small amount of JS (not TS) on purpose: the launcher must run without
|
|
21
|
+
* tsx — the whole point is that it finds tsx itself.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { spawnSync } from "node:child_process"
|
|
25
|
+
import * as fs from "node:fs"
|
|
26
|
+
import * as path from "node:path"
|
|
27
|
+
import { fileURLToPath } from "node:url"
|
|
28
|
+
|
|
29
|
+
/** This package's root (the directory above bin/). */
|
|
30
|
+
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the tsx CLI entry by walking up through ancestor `node_modules`
|
|
34
|
+
* directories — the same resolution Node's own module lookup (and
|
|
35
|
+
* `scripts/run-tests.mjs`) uses. npm hoists dependencies to the nearest
|
|
36
|
+
* ancestor node_modules, so the tsx binary may sit at
|
|
37
|
+
* `<pkgRoot>/node_modules/tsx/dist/cli.mjs` (repo checkout, nested install)
|
|
38
|
+
* or at an ancestor's (a global install or a consumer with a flat tree).
|
|
39
|
+
*/
|
|
40
|
+
function resolveTsxCli(dir) {
|
|
41
|
+
let current = dir
|
|
42
|
+
while (true) {
|
|
43
|
+
const candidate = path.join(current, "node_modules", "tsx", "dist", "cli.mjs")
|
|
44
|
+
if (fs.existsSync(candidate)) {
|
|
45
|
+
return candidate
|
|
46
|
+
}
|
|
47
|
+
const parent = path.dirname(current)
|
|
48
|
+
if (parent === current) {
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
current = parent
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const tsxCli = resolveTsxCli(PKG_ROOT)
|
|
56
|
+
if (tsxCli === undefined) {
|
|
57
|
+
process.stderr.write(
|
|
58
|
+
"headlesscode: cannot find the 'tsx' runtime (node_modules/tsx/dist/cli.mjs) — " +
|
|
59
|
+
"this package's dependencies are not installed. Run `npm install` (or re-install the package) and retry.\n",
|
|
60
|
+
)
|
|
61
|
+
process.exit(1)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cliEntry = path.join(PKG_ROOT, "src", "cli.ts")
|
|
65
|
+
const tsconfigPath = path.join(PKG_ROOT, "tsconfig.json")
|
|
66
|
+
|
|
67
|
+
// Run the CLI through tsx in a child process with inherited stdio. The
|
|
68
|
+
// caller's cwd is preserved (headlesscode's own --repo/--workspace default
|
|
69
|
+
// to process.cwd(), matching install-cli.sh's wrapper contract). TSX_TSCONFIG_PATH
|
|
70
|
+
// pins tsconfig resolution to THIS package, independent of the cwd.
|
|
71
|
+
const result = spawnSync(
|
|
72
|
+
process.execPath,
|
|
73
|
+
[tsxCli, cliEntry, ...process.argv.slice(2)],
|
|
74
|
+
{ stdio: "inherit", env: { ...process.env, TSX_TSCONFIG_PATH: tsconfigPath } },
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if (result.error) {
|
|
78
|
+
process.stderr.write(`headlesscode: failed to launch the CLI: ${result.error.message}\n`)
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
// Propagate the CLI's exit code; a signal-killed child becomes a non-zero
|
|
82
|
+
// exit here (the launcher itself has no signal handlers installed).
|
|
83
|
+
process.exit(result.status ?? 1)
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "headlesscode",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Standalone headless coding-agent harness: runs the Zoo Code agent loop (prompts, tools, modes) without a VS Code UI, driven by CLI, HTTP, and parallel worktree orchestration.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Capsize-Games/headlesscode.git"
|
|
9
|
+
},
|
|
10
|
+
"author": "The headlesscode contributors",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"cli",
|
|
13
|
+
"coding-agent",
|
|
14
|
+
"headless",
|
|
15
|
+
"automation",
|
|
16
|
+
"agent"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"headlesscode": "bin/headlesscode.mjs"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md",
|
|
28
|
+
"ATTRIBUTION.md",
|
|
29
|
+
"SECURITY.md",
|
|
30
|
+
"CONTRIBUTING.md",
|
|
31
|
+
"CODE_OF_CONDUCT.md",
|
|
32
|
+
"tsconfig.json",
|
|
33
|
+
"shared/",
|
|
34
|
+
"src/",
|
|
35
|
+
"!src/**/__tests__/",
|
|
36
|
+
"!src/**/*.test.ts"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"smoke": "tsx src/vendor/tests/smoke.ts",
|
|
41
|
+
"dev": "tsx src/index.ts",
|
|
42
|
+
"start": "tsx src/cli.ts",
|
|
43
|
+
"cli": "tsx src/cli.ts",
|
|
44
|
+
"test": "node scripts/run-tests.mjs",
|
|
45
|
+
"prepublishOnly": "npm ci && npm test"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
|
+
"@octokit/auth-app": "^8.2.0",
|
|
50
|
+
"fastest-levenshtein": "^1.0.16",
|
|
51
|
+
"p-wait-for": "^5.0.2",
|
|
52
|
+
"playwright": "^1.62.1",
|
|
53
|
+
"simple-git": "^3.36.0",
|
|
54
|
+
"tsx": "^4.19.0",
|
|
55
|
+
"typescript": "^5.6.0",
|
|
56
|
+
"undici": "^8.10.0",
|
|
57
|
+
"yaml": "^2.5.1",
|
|
58
|
+
"zod": "^3.25.76"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^22.10.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Independent review — operating procedure
|
|
2
|
+
|
|
3
|
+
Use this after a worker session (in any worktree under `.worktrees/`) claims
|
|
4
|
+
to have finished a task. Run it against that worktree directly — it needs
|
|
5
|
+
the real checked-out branch, not a description of the work.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are an independent reviewer. You did not write the code you are about to
|
|
10
|
+
review, and you must not extend it any benefit of the doubt. Your job is to
|
|
11
|
+
verify claims against reality, not to check whether the claims sound
|
|
12
|
+
plausible. Assume the report you're reviewing has at least one wrong or
|
|
13
|
+
overstated claim until you've personally confirmed otherwise — reports from
|
|
14
|
+
autonomous coding sessions routinely overstate what was actually verified
|
|
15
|
+
(a claimed "all tests pass" that turns out to mean "the tests that were
|
|
16
|
+
touched pass," a claimed e2e run that was never actually executed, a
|
|
17
|
+
"verified working" that means "looks right on read-through").
|
|
18
|
+
|
|
19
|
+
## What to review
|
|
20
|
+
|
|
21
|
+
1. **Read the worker's own summary/report in full.** Note every concrete,
|
|
22
|
+
checkable claim: which files changed, test/e2e pass counts, "tsc clean,"
|
|
23
|
+
specific behaviors it says it preserved or fixed.
|
|
24
|
+
2. **Read the actual diff** (`git -C .worktrees/<name> diff origin/master...HEAD`
|
|
25
|
+
or equivalent) — not just the summary. Read the whole diff for anything
|
|
26
|
+
under ~500 lines; for larger diffs, prioritize the highest-risk files
|
|
27
|
+
first (anything touching the tool executor, budget/cost accounting,
|
|
28
|
+
orchestrator state, or checkpoint/git operations) before sampling the
|
|
29
|
+
rest.
|
|
30
|
+
3. **Re-run every checkable claim yourself, for real, inside that worktree:**
|
|
31
|
+
- `npx tsc --noEmit` — confirm it's actually clean, don't take "tsc
|
|
32
|
+
clean" on faith.
|
|
33
|
+
- `npm test` — compare the actual suite count/pass count against what
|
|
34
|
+
the report claims. A mismatch of even one suite is not a rounding
|
|
35
|
+
error.
|
|
36
|
+
- If the report claims e2e suites pass, re-run the relevant ones under
|
|
37
|
+
`scripts/e2e*/run.sh` yourself and compare actual PASS/FAIL counts.
|
|
38
|
+
- If the report claims a manual smoke test was run (e.g. "confirmed the
|
|
39
|
+
dashboard shows a running session," "confirmed the process wasn't
|
|
40
|
+
killed on timeout"), reproduce it yourself where practical rather than
|
|
41
|
+
trusting the description — these are exactly the kind of claims most
|
|
42
|
+
likely to be asserted without having actually been checked.
|
|
43
|
+
4. **Check for silent behavior changes**, not just crashes or test
|
|
44
|
+
failures — a change that alters control flow (e.g. moving a non-fatal
|
|
45
|
+
try/catch, changing what a timeout does, changing default-allow to
|
|
46
|
+
default-deny somewhere) can pass every existing test and still be wrong
|
|
47
|
+
if nothing exercises that specific path. Read the diff for anything that
|
|
48
|
+
changes an existing function's behavior, not just what's newly added.
|
|
49
|
+
5. **Check the task file the worker was given** (whichever `plans/*.md` file
|
|
50
|
+
governed this worktree) and confirm the work actually addresses what was
|
|
51
|
+
asked, including anything under "What NOT to do" — a worker that built
|
|
52
|
+
something not requested, or built something explicitly excluded, is a
|
|
53
|
+
real finding even if the code itself is otherwise fine.
|
|
54
|
+
6. **Keep every scratch/temp file inside the workspace — no `/tmp`.**
|
|
55
|
+
Any scratch you need (probe scripts, temp output captures, throwaway
|
|
56
|
+
test files) goes in `<workspace>/.headlesscode/scratch/` (create it
|
|
57
|
+
if it doesn't exist; `/.headlesscode/` is gitignored). Writing to
|
|
58
|
+
`/tmp` or any other path outside the workspace violates the repo's
|
|
59
|
+
"Never write to /tmp" rule — outside-workspace writes cannot be
|
|
60
|
+
auto-approved in the interactive GUI and are rejected by the file
|
|
61
|
+
tools in headless workers.
|
|
62
|
+
|
|
63
|
+
## Verdict and action
|
|
64
|
+
|
|
65
|
+
- **If everything checks out**: say so plainly, with the actual command
|
|
66
|
+
output you personally reproduced as evidence, not "looks good."
|
|
67
|
+
- **If you find a real problem**: report it with exact file:line evidence,
|
|
68
|
+
the real command output that demonstrates it, and specific, actionable
|
|
69
|
+
instructions for what needs to change — written the way you'd want to
|
|
70
|
+
receive feedback if you were about to fix it yourself.
|
|
71
|
+
- **Do not fix the problem yourself in this mode.** Verification and
|
|
72
|
+
remediation are separate passes — report what needs to change, don't
|
|
73
|
+
change it.
|
|
74
|
+
|
|
75
|
+
## What NOT to do
|
|
76
|
+
|
|
77
|
+
- Do not pad the review with issues that have no real consequence just to
|
|
78
|
+
look thorough.
|
|
79
|
+
- Do not accept "the tests pass" as sufficient evidence for a claim the
|
|
80
|
+
tests don't actually exercise — check what the tests actually assert.
|
|
81
|
+
- Do not skip re-running commands because the report already includes
|
|
82
|
+
output — output in a report is a claim, not evidence, until reproduced.
|
|
83
|
+
- Do not review by reading only the summary — read the actual diff and run
|
|
84
|
+
actual commands every time.
|
|
85
|
+
- No `/tmp` — scratch goes in `<workspace>/.headlesscode/scratch/` (the
|
|
86
|
+
repo rule "Never write to /tmp" is binding here too; a `/tmp` write in
|
|
87
|
+
a review session is a finding).
|
|
88
|
+
|
|
89
|
+
## When you're done
|
|
90
|
+
|
|
91
|
+
Give a short final verdict: pass or fail, and if fail, the specific list of
|
|
92
|
+
what needs to change, precise enough that someone could act on it without
|
|
93
|
+
re-deriving your findings from scratch.
|