@provos/ironcurtain 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -66
- package/dist/cli-help.d.ts +63 -0
- package/dist/cli-help.js +102 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.js +78 -52
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +136 -2
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/generated/tool-annotations.json +487 -338
- package/dist/config/index.d.ts +21 -1
- package/dist/config/index.js +12 -4
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-servers.json +2 -8
- package/dist/config/paths.d.ts +30 -0
- package/dist/config/paths.js +58 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/tool-description-hints.json +15 -0
- package/dist/config/types.d.ts +13 -0
- package/dist/config/user-config.d.ts +21 -0
- package/dist/config/user-config.js +8 -0
- package/dist/config/user-config.js.map +1 -1
- package/dist/cron/compile-task-policy.d.ts +17 -0
- package/dist/cron/compile-task-policy.js +39 -0
- package/dist/cron/compile-task-policy.js.map +1 -0
- package/dist/cron/cron-scheduler.d.ts +65 -0
- package/dist/cron/cron-scheduler.js +184 -0
- package/dist/cron/cron-scheduler.js.map +1 -0
- package/dist/cron/file-lock.d.ts +41 -0
- package/dist/cron/file-lock.js +156 -0
- package/dist/cron/file-lock.js.map +1 -0
- package/dist/cron/format-utils.d.ts +18 -0
- package/dist/cron/format-utils.js +145 -0
- package/dist/cron/format-utils.js.map +1 -0
- package/dist/cron/git-sync.d.ts +22 -0
- package/dist/cron/git-sync.js +121 -0
- package/dist/cron/git-sync.js.map +1 -0
- package/dist/cron/headless-transport.d.ts +27 -0
- package/dist/cron/headless-transport.js +43 -0
- package/dist/cron/headless-transport.js.map +1 -0
- package/dist/cron/job-commands.d.ts +39 -0
- package/dist/cron/job-commands.js +522 -0
- package/dist/cron/job-commands.js.map +1 -0
- package/dist/cron/job-store.d.ts +35 -0
- package/dist/cron/job-store.js +111 -0
- package/dist/cron/job-store.js.map +1 -0
- package/dist/cron/types.d.ts +127 -0
- package/dist/cron/types.js +25 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/daemon/control-socket.d.ts +127 -0
- package/dist/daemon/control-socket.js +261 -0
- package/dist/daemon/control-socket.js.map +1 -0
- package/dist/daemon/daemon-command.d.ts +13 -0
- package/dist/daemon/daemon-command.js +230 -0
- package/dist/daemon/daemon-command.js.map +1 -0
- package/dist/daemon/ironcurtain-daemon.d.ts +90 -0
- package/dist/daemon/ironcurtain-daemon.js +548 -0
- package/dist/daemon/ironcurtain-daemon.js.map +1 -0
- package/dist/docker/adapters/claude-code.js +8 -48
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/adapters/goose.d.ts +58 -0
- package/dist/docker/adapters/goose.js +250 -0
- package/dist/docker/adapters/goose.js.map +1 -0
- package/dist/docker/adapters/shared-scripts.d.ts +29 -0
- package/dist/docker/adapters/shared-scripts.js +80 -0
- package/dist/docker/adapters/shared-scripts.js.map +1 -0
- package/dist/docker/agent-adapter.d.ts +12 -0
- package/dist/docker/agent-adapter.js.map +1 -1
- package/dist/docker/agent-registry.d.ts +6 -1
- package/dist/docker/agent-registry.js +10 -1
- package/dist/docker/agent-registry.js.map +1 -1
- package/dist/docker/docker-agent-session.d.ts +2 -25
- package/dist/docker/docker-agent-session.js +7 -106
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +8 -0
- package/dist/docker/docker-infrastructure.js +22 -14
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/mitm-proxy.d.ts +5 -2
- package/dist/docker/mitm-proxy.js +169 -46
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +15 -2
- package/dist/docker/oauth-credentials.js +92 -8
- package/dist/docker/oauth-credentials.js.map +1 -1
- package/dist/docker/oauth-token-manager.d.ts +80 -0
- package/dist/docker/oauth-token-manager.js +133 -0
- package/dist/docker/oauth-token-manager.js.map +1 -0
- package/dist/docker/pty-session.d.ts +2 -0
- package/dist/docker/pty-session.js +17 -8
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/escalation/escalation-watcher.d.ts +2 -0
- package/dist/escalation/escalation-watcher.js +5 -0
- package/dist/escalation/escalation-watcher.js.map +1 -1
- package/dist/escalation/listener-command.js +9 -58
- package/dist/escalation/listener-command.js.map +1 -1
- package/dist/escalation/listener-lock.d.ts +20 -0
- package/dist/escalation/listener-lock.js +69 -0
- package/dist/escalation/listener-lock.js.map +1 -0
- package/dist/escalation/session-registry.js +1 -14
- package/dist/escalation/session-registry.js.map +1 -1
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/mux/mux-app.d.ts +24 -0
- package/dist/mux/mux-app.js +450 -0
- package/dist/mux/mux-app.js.map +1 -0
- package/dist/mux/mux-command.d.ts +7 -0
- package/dist/mux/mux-command.js +104 -0
- package/dist/mux/mux-command.js.map +1 -0
- package/dist/mux/mux-escalation-manager.d.ts +48 -0
- package/dist/mux/mux-escalation-manager.js +162 -0
- package/dist/mux/mux-escalation-manager.js.map +1 -0
- package/dist/mux/mux-input-handler.d.ts +54 -0
- package/dist/mux/mux-input-handler.js +512 -0
- package/dist/mux/mux-input-handler.js.map +1 -0
- package/dist/mux/mux-renderer.d.ts +106 -0
- package/dist/mux/mux-renderer.js +850 -0
- package/dist/mux/mux-renderer.js.map +1 -0
- package/dist/mux/mux-splash.d.ts +17 -0
- package/dist/mux/mux-splash.js +323 -0
- package/dist/mux/mux-splash.js.map +1 -0
- package/dist/mux/paste-interceptor.d.ts +22 -0
- package/dist/mux/paste-interceptor.js +143 -0
- package/dist/mux/paste-interceptor.js.map +1 -0
- package/dist/mux/pty-bridge.d.ts +79 -0
- package/dist/mux/pty-bridge.js +151 -0
- package/dist/mux/pty-bridge.js.map +1 -0
- package/dist/mux/trusted-input.d.ts +27 -0
- package/dist/mux/trusted-input.js +27 -0
- package/dist/mux/trusted-input.js.map +1 -0
- package/dist/mux/types.d.ts +99 -0
- package/dist/mux/types.js +57 -0
- package/dist/mux/types.js.map +1 -0
- package/dist/pipeline/annotate.js +2 -1
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/compile.d.ts +4 -34
- package/dist/pipeline/compile.js +29 -568
- package/dist/pipeline/compile.js.map +1 -1
- package/dist/pipeline/constitution-compiler.js +1 -1
- package/dist/pipeline/mcp-connections.d.ts +16 -0
- package/dist/pipeline/mcp-connections.js +61 -0
- package/dist/pipeline/mcp-connections.js.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +86 -0
- package/dist/pipeline/pipeline-runner.js +554 -0
- package/dist/pipeline/pipeline-runner.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +29 -2
- package/dist/pipeline/pipeline-shared.js +68 -12
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/refresh-lists.js +38 -12
- package/dist/pipeline/refresh-lists.js.map +1 -1
- package/dist/pipeline/scenario-generator.js +2 -1
- package/dist/pipeline/scenario-generator.js.map +1 -1
- package/dist/pipeline/tool-annotator.d.ts +3 -3
- package/dist/pipeline/tool-annotator.js +96 -4
- package/dist/pipeline/tool-annotator.js.map +1 -1
- package/dist/pipeline/types.d.ts +52 -0
- package/dist/sandbox/index.js +40 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/session/agent-session.d.ts +3 -0
- package/dist/session/agent-session.js +13 -1
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/index.js +65 -9
- package/dist/session/index.js.map +1 -1
- package/dist/session/preflight.js +26 -6
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/prompts.d.ts +14 -0
- package/dist/session/prompts.js +39 -2
- package/dist/session/prompts.js.map +1 -1
- package/dist/session/session-manager.d.ts +105 -0
- package/dist/session/session-manager.js +134 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +33 -0
- package/dist/session/workspace-validation.d.ts +19 -0
- package/dist/session/workspace-validation.js +66 -0
- package/dist/session/workspace-validation.js.map +1 -0
- package/dist/signal/bot-command.d.ts +2 -0
- package/dist/signal/bot-command.js +8 -0
- package/dist/signal/bot-command.js.map +1 -1
- package/dist/signal/format.d.ts +27 -0
- package/dist/signal/format.js +59 -0
- package/dist/signal/format.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +27 -25
- package/dist/signal/signal-bot-daemon.js +145 -130
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-container.js +63 -19
- package/dist/signal/signal-container.js.map +1 -1
- package/dist/signal/signal-transport.js +3 -8
- package/dist/signal/signal-transport.js.map +1 -1
- package/dist/trusted-process/auto-approver.d.ts +13 -4
- package/dist/trusted-process/auto-approver.js +12 -5
- package/dist/trusted-process/auto-approver.js.map +1 -1
- package/dist/trusted-process/domain-utils.d.ts +38 -4
- package/dist/trusted-process/domain-utils.js +118 -11
- package/dist/trusted-process/domain-utils.js.map +1 -1
- package/dist/trusted-process/escalation.js +5 -0
- package/dist/trusted-process/escalation.js.map +1 -1
- package/dist/trusted-process/index.js +6 -2
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/mcp-client-manager.js +11 -5
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-proxy-server.d.ts +2 -0
- package/dist/trusted-process/mcp-proxy-server.js +98 -32
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/permissive-output-validator.d.ts +23 -0
- package/dist/trusted-process/permissive-output-validator.js +10 -0
- package/dist/trusted-process/permissive-output-validator.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +27 -4
- package/dist/trusted-process/policy-engine.js +152 -57
- package/dist/trusted-process/policy-engine.js.map +1 -1
- package/dist/trusted-process/tool-description-hints.d.ts +19 -0
- package/dist/trusted-process/tool-description-hints.js +53 -0
- package/dist/trusted-process/tool-description-hints.js.map +1 -0
- package/dist/types/argument-roles.d.ts +33 -1
- package/dist/types/argument-roles.js +129 -1
- package/dist/types/argument-roles.js.map +1 -1
- package/docker/Dockerfile.goose +34 -0
- package/docker/entrypoint-goose.sh +30 -0
- package/package.json +6 -1
- package/src/config/generated/tool-annotations.json +487 -338
- package/src/config/mcp-servers.json +2 -8
package/README.md
CHANGED
|
@@ -9,8 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
_\*When someone writes "secure," you should immediately be skeptical. [What do we mean by secure?](https://ironcurtain.dev)_
|
|
11
11
|
|
|
12
|
+
> [!WARNING]
|
|
12
13
|
> **Research Prototype.** IronCurtain is an early-stage research project exploring how to make AI agents safe enough to be genuinely useful. APIs, configuration formats, and architecture may change. Contributions and feedback are welcome.
|
|
13
14
|
|
|
15
|
+
## Demo
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<img src="demo.gif" alt="IronCurtain mux demo: trusted input from command mode enables auto-approval of git clone and git push" width="800">
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
The agent is asked to clone a repository and push changes. Both `git_clone` and `git_push` are escalated by the policy engine, but the [auto-approver](#auto-approve-escalations) approves them automatically — the user's trusted input from command mode (Ctrl-A) provided clear intent, so no manual `/approve` was needed.
|
|
22
|
+
|
|
14
23
|
## The Problem
|
|
15
24
|
|
|
16
25
|
Autonomous AI agents can manage files, run git commands, send messages, and interact with APIs on your behalf. But today's agent frameworks give the agent the same privileges as the user such as full access to the filesystem, credentials, and network. Security researchers call this **ambient authority**, and it means a single prompt injection or multi-turn drift can cause an agent to delete files, exfiltrate data, or push malicious code.
|
|
@@ -30,14 +39,6 @@ The key ideas:
|
|
|
30
39
|
- **Semantic interposition.** Instead of giving the agent raw system access, all interactions go through [MCP](https://modelcontextprotocol.io/) servers (filesystem, git, etc.). Every tool call passes through a policy engine that can **allow**, **deny**, or **escalate** to the user for approval.
|
|
31
40
|
- **Defense in depth.** Agent code runs in a V8 isolate with no direct access to the host. The only way out is through semantically meaningful MCP tool calls and every one is checked against policy.
|
|
32
41
|
|
|
33
|
-
## Demo
|
|
34
|
-
|
|
35
|
-
<p align="center">
|
|
36
|
-
<img src="demo.gif" alt="IronCurtain demo: agent clones a repo, policy escalates git_clone for approval, user approves, then auto-approve handles git push" width="800">
|
|
37
|
-
</p>
|
|
38
|
-
|
|
39
|
-
The agent clones a repository and edits a file. The policy engine escalates `git_clone` for human approval. After the user types `/approve`, the agent completes the task. On the second request ("ok. git push to origin please"), [auto-approve](#auto-approve-escalations) recognizes the explicit intent and approves `git_push` automatically — no interruption needed.
|
|
40
|
-
|
|
41
42
|
## Architecture
|
|
42
43
|
|
|
43
44
|
IronCurtain supports two session modes with different trust models:
|
|
@@ -82,7 +83,7 @@ npm install
|
|
|
82
83
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
83
84
|
```
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
You can also place keys in a `.env` file in the project root (loaded automatically via `dotenv`), or add them to `~/.ironcurtain/config.json` via `ironcurtain config`. Environment variables take precedence over config file values. Supported: `ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, `OPENAI_API_KEY`.
|
|
86
87
|
|
|
87
88
|
**2. Run the first-start wizard** (runs automatically on first `ironcurtain start`, or explicitly):
|
|
88
89
|
|
|
@@ -128,6 +129,17 @@ Send one task and exit when the agent finishes:
|
|
|
128
129
|
ironcurtain start "Summarize the files in the current directory"
|
|
129
130
|
```
|
|
130
131
|
|
|
132
|
+
### Workspace mode
|
|
133
|
+
|
|
134
|
+
Point the agent at an existing directory instead of a fresh sandbox:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
ironcurtain start -w ./my-project "Fix the failing tests"
|
|
138
|
+
ironcurtain start --workspace /home/user/repos/my-app
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The workspace replaces the session sandbox as the agent's working area. All session infrastructure (logs, escalations, audit) still lives under `~/.ironcurtain/sessions/`. The path is validated to prevent use of sensitive directories (root, home, `~/.ironcurtain/`, or paths overlapping with protected paths).
|
|
142
|
+
|
|
131
143
|
### Session resume
|
|
132
144
|
|
|
133
145
|
Resume a previous session's conversation history:
|
|
@@ -138,15 +150,26 @@ ironcurtain start --resume <session-id>
|
|
|
138
150
|
|
|
139
151
|
Session IDs are printed on session start and stored under `~/.ironcurtain/sessions/`.
|
|
140
152
|
|
|
141
|
-
###
|
|
153
|
+
### Terminal multiplexer (recommended for Docker Agent Mode)
|
|
142
154
|
|
|
143
|
-
|
|
155
|
+
The terminal multiplexer is the recommended way to use IronCurtain with Docker Agent Mode. It gives you the full power of your agent's interactive TUI (Claude Code or Goose) while IronCurtain mediates every tool call through its policy engine. All in a single terminal.
|
|
144
156
|
|
|
145
157
|
```bash
|
|
146
|
-
ironcurtain
|
|
158
|
+
ironcurtain mux
|
|
147
159
|
```
|
|
148
160
|
|
|
149
|
-
**
|
|
161
|
+
**Key capabilities:**
|
|
162
|
+
|
|
163
|
+
- **Full agent TUI** — The agent (Claude Code or Goose) runs in a PTY inside a Docker container with no network access. You interact with it exactly as if it were running locally.
|
|
164
|
+
- **Inline escalation handling** — When a tool call needs approval, an escalation panel overlays the viewport. No separate terminal needed — press Ctrl-A to enter command mode, `/approve N` or `/deny N`, and return.
|
|
165
|
+
- **Trusted user input** — Text typed in command mode (Ctrl-A) is captured on the host side before entering the container. This creates a verified intent signal that the [auto-approver](#auto-approve-escalations) can use — e.g., typing "push my changes to origin" will auto-approve a subsequent `git_push` escalation.
|
|
166
|
+
- **Tab management** — Spawn multiple concurrent sessions (`/new`), switch between them (`/tab N`, Alt-1..9), close them (`/close`).
|
|
167
|
+
|
|
168
|
+
See [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) for the full walkthrough: input modes, trusted input security model, escalation workflow, and keyboard reference.
|
|
169
|
+
|
|
170
|
+
### PTY mode with a separate escalation listener
|
|
171
|
+
|
|
172
|
+
An alternative to the mux — run a raw PTY session in one terminal and handle escalations in another:
|
|
150
173
|
|
|
151
174
|
```bash
|
|
152
175
|
# Terminal 1 — interactive Claude Code session
|
|
@@ -156,33 +179,9 @@ ironcurtain start --pty
|
|
|
156
179
|
ironcurtain escalation-listener
|
|
157
180
|
```
|
|
158
181
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
1. When `--pty` starts, it writes a registration file to `~/.ironcurtain/pty-registry/session-<id>.json` containing the session ID, escalation directory path, display label, and process PID.
|
|
162
|
-
2. The escalation listener polls `pty-registry/` every second. It discovers new sessions, attaches an escalation watcher to each one, and detaches watchers when sessions end. Stale registrations (process PID no longer alive) are removed automatically, so a crashed PTY session doesn't leave phantom entries.
|
|
163
|
-
|
|
164
|
-
**The escalation flow:**
|
|
165
|
-
|
|
166
|
-
When a tool call is escalated, the PTY session emits a **BEL character** (`\x07`) — your terminal bell or visual flash signals that attention is needed. Switch to the listener terminal. The listener TUI re-renders in place: active sessions are listed with sequential display numbers, and each pending escalation shows the tool name, arguments, and the policy reason for the escalation.
|
|
167
|
-
|
|
168
|
-
**Listener commands:**
|
|
169
|
-
|
|
170
|
-
| Command | Description |
|
|
171
|
-
| -------------- | ----------------------------------------- |
|
|
172
|
-
| `/approve N` | Approve escalation #N |
|
|
173
|
-
| `/deny N` | Deny escalation #N |
|
|
174
|
-
| `/approve all` | Approve all pending escalations |
|
|
175
|
-
| `/deny all` | Deny all pending escalations |
|
|
176
|
-
| `/sessions` | Show detailed session information |
|
|
177
|
-
| `/quit` | Exit the listener |
|
|
178
|
-
|
|
179
|
-
Escalation numbers are sequential across all sessions. If you have two active PTY sessions, their escalations share a single numbered list in the dashboard — escalation #3 might be from session [1] and #4 from session [2]. Use the number shown in the dashboard to resolve them.
|
|
182
|
+
This workflow does not support trusted input or auto-approval for PTY sessions. The mux is recommended instead.
|
|
180
183
|
|
|
181
|
-
**
|
|
182
|
-
|
|
183
|
-
**Single-instance lock:** Only one escalation listener may run at a time, enforced via a PID-checked lock file at `~/.ironcurtain/escalation-listener.lock`. A stale lock from a crashed process is cleaned up automatically on the next start.
|
|
184
|
-
|
|
185
|
-
**Emergency exit and terminal recovery:** Press `Ctrl-\` to trigger a graceful shutdown of the PTY session (stops containers, proxies, and performs async cleanup). If the process is killed ungracefully (e.g., `kill -9`), run `reset` in that terminal to restore normal terminal mode.
|
|
184
|
+
**Emergency exit and terminal recovery:** Press `Ctrl-\` to trigger a graceful shutdown of the PTY session. If the process is killed ungracefully, run `reset` in that terminal to restore normal terminal mode.
|
|
186
185
|
|
|
187
186
|
### Signal messaging transport
|
|
188
187
|
|
|
@@ -195,19 +194,35 @@ ironcurtain bot # Start the Signal bot daemon
|
|
|
195
194
|
|
|
196
195
|
See [TRANSPORT.md](TRANSPORT.md) for setup instructions, architecture details, and why we chose Signal over alternatives like Telegram.
|
|
197
196
|
|
|
197
|
+
### Daemon mode
|
|
198
|
+
|
|
199
|
+
A unified long-running daemon that combines Signal messaging with scheduled cron jobs. Define recurring tasks with per-job security policies, and IronCurtain runs them headlessly on a cron schedule.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
ironcurtain daemon add-job # Interactive wizard to define a scheduled job
|
|
203
|
+
ironcurtain daemon # Start the daemon (Signal + cron)
|
|
204
|
+
ironcurtain daemon --no-signal # Cron-only mode (no Signal transport)
|
|
205
|
+
ironcurtain daemon list-jobs # List jobs with schedule and status
|
|
206
|
+
ironcurtain daemon logs <id> # Show recent run summaries
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Each job has its own task description, security constitution (compiled into per-job policy rules), persistent workspace, optional git repo sync, and configurable resource budgets. Escalations are auto-denied in headless mode unless Signal is configured for approval routing.
|
|
210
|
+
|
|
211
|
+
See [DAEMON.md](DAEMON.md) for the full setup guide, job definition reference, and troubleshooting.
|
|
212
|
+
|
|
198
213
|
## Session Commands
|
|
199
214
|
|
|
200
215
|
Commands available during an **interactive** or **single-shot** session:
|
|
201
216
|
|
|
202
|
-
| Command | Description
|
|
203
|
-
| ---------- |
|
|
204
|
-
| `/approve` | Approve the pending escalation
|
|
205
|
-
| `/deny` | Deny the pending escalation
|
|
206
|
-
| `/budget` | Show resource consumption (tokens, steps, cost)
|
|
207
|
-
| `/logs` | Display diagnostic events
|
|
208
|
-
| `/quit` | End the session
|
|
217
|
+
| Command | Description |
|
|
218
|
+
| ---------- | ----------------------------------------------- |
|
|
219
|
+
| `/approve` | Approve the pending escalation |
|
|
220
|
+
| `/deny` | Deny the pending escalation |
|
|
221
|
+
| `/budget` | Show resource consumption (tokens, steps, cost) |
|
|
222
|
+
| `/logs` | Display diagnostic events |
|
|
223
|
+
| `/quit` | End the session |
|
|
209
224
|
|
|
210
|
-
In **PTY mode**, use the
|
|
225
|
+
In **PTY mode**, use the [terminal multiplexer](#terminal-multiplexer-recommended-for-docker-agent-mode) (`ironcurtain mux`) for inline escalation handling and trusted input support.
|
|
211
226
|
|
|
212
227
|
## Policy: Constitution → Enforcement
|
|
213
228
|
|
|
@@ -242,9 +257,9 @@ compiles to:
|
|
|
242
257
|
|
|
243
258
|
```json
|
|
244
259
|
[
|
|
245
|
-
{ "tool": "git_status", "decision": "allow",
|
|
246
|
-
{ "tool": "git_diff",
|
|
247
|
-
{ "tool": "git_push",
|
|
260
|
+
{ "tool": "git_status", "decision": "allow", "condition": { "directory": { "within": "$SANDBOX" } } },
|
|
261
|
+
{ "tool": "git_diff", "decision": "allow", "condition": { "directory": { "within": "$SANDBOX" } } },
|
|
262
|
+
{ "tool": "git_push", "decision": "escalate", "reason": "Remote-contacting git operations require human approval" }
|
|
248
263
|
]
|
|
249
264
|
```
|
|
250
265
|
|
|
@@ -292,6 +307,7 @@ IronCurtain stores configuration and session data in `~/.ironcurtain/`:
|
|
|
292
307
|
├── generated/ # User-compiled policy artifacts (overrides package defaults)
|
|
293
308
|
├── signal-data/ # Signal transport persistent data (registration keys)
|
|
294
309
|
├── pty-registry/ # Active PTY session registrations (auto-managed)
|
|
310
|
+
├── jobs/ # Cron job definitions, workspaces, and run records
|
|
295
311
|
├── sessions/
|
|
296
312
|
│ └── {sessionId}/
|
|
297
313
|
│ ├── sandbox/ # Per-session filesystem sandbox
|
|
@@ -376,11 +392,11 @@ After compilation, review the updated `tool-annotations.json` and `compiled-poli
|
|
|
376
392
|
|
|
377
393
|
IronCurtain ships with four pre-configured MCP servers. All tool calls are governed by your compiled policy.
|
|
378
394
|
|
|
379
|
-
| Server | Tools | Key capabilities
|
|
380
|
-
| -------------- | ----- |
|
|
381
|
-
| **Filesystem** | 14 | Read, write, edit, search files; directory tree; move; diff calculation
|
|
382
|
-
| **Git** | 27 | Full git workflow: status, diff, log, commit, branch, push/pull/fetch, clone, stash, blame
|
|
383
|
-
| **Fetch** | 2 | HTTP GET with HTML-to-markdown conversion; `web_search` (see [Web Search](#web-search))
|
|
395
|
+
| Server | Tools | Key capabilities |
|
|
396
|
+
| -------------- | ----- | ----------------------------------------------------------------------------------------------------------------- |
|
|
397
|
+
| **Filesystem** | 14 | Read, write, edit, search files; directory tree; move; diff calculation |
|
|
398
|
+
| **Git** | 27 | Full git workflow: status, diff, log, commit, branch, push/pull/fetch, clone, stash, blame |
|
|
399
|
+
| **Fetch** | 2 | HTTP GET with HTML-to-markdown conversion; `web_search` (see [Web Search](#web-search)) |
|
|
384
400
|
| **GitHub** | 41 | Issues, PRs, code search, reviews via `ghcr.io/github/github-mcp-server`; requires a GitHub personal access token |
|
|
385
401
|
|
|
386
402
|
Read-only operations are allowed by default policy; mutations (writes, pushes, PR creation) escalate for human approval.
|
|
@@ -411,17 +427,17 @@ See [docs/SECURITY_CONCERNS.md](docs/SECURITY_CONCERNS.md) for a detailed threat
|
|
|
411
427
|
|
|
412
428
|
## Troubleshooting
|
|
413
429
|
|
|
414
|
-
| Issue
|
|
415
|
-
|
|
|
416
|
-
| **Missing API key**
|
|
417
|
-
| **Sandbox unavailable**
|
|
418
|
-
| **Budget exhausted**
|
|
419
|
-
| **Node version errors**
|
|
420
|
-
| **Policy doesn't match intent**
|
|
421
|
-
| **Auto-approve not triggering**
|
|
422
|
-
| **PTY terminal garbled after exit**
|
|
423
|
-
| **
|
|
424
|
-
| **Signal bot not responding**
|
|
430
|
+
| Issue | Guidance |
|
|
431
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
432
|
+
| **Missing API key** | Set the environment variable (`ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, or `OPENAI_API_KEY`) or add the corresponding key to `~/.ironcurtain/config.json`. |
|
|
433
|
+
| **Sandbox unavailable** | OS-level sandboxing requires `bubblewrap` and `socat`. Install both, or set `"sandboxPolicy": "warn"` in your MCP server config for development. |
|
|
434
|
+
| **Budget exhausted** | Adjust limits in `~/.ironcurtain/config.json` under `resourceBudget`. Set any individual limit to `null` to disable it. |
|
|
435
|
+
| **Node version errors** | Node.js 22+ is required (`isolated-vm` needs `>=22.0.0`). Maximum supported is Node 25 (`<26`). |
|
|
436
|
+
| **Policy doesn't match intent** | Review `compiled-policy.json` to see the generated rules. Run `ironcurtain customize-policy` to refine your constitution, then `ironcurtain compile-policy` to recompile. Specific wording produces better rules — vague phrasing leads to vague policy. |
|
|
437
|
+
| **Auto-approve not triggering** | The auto-approver only approves when the user's message explicitly authorizes the action (e.g., "push to origin" for `git_push`). Vague messages always escalate to human review. Verify `autoApprove.enabled` is `true` in `config.json`. |
|
|
438
|
+
| **PTY/mux terminal garbled after exit** | Run `reset` in that terminal to restore normal mode. This is needed when the process is killed ungracefully and raw mode is not restored. |
|
|
439
|
+
| **Mux/listener: "already running"** | Only one mux or escalation-listener can run at a time. The lock at `~/.ironcurtain/escalation-listener.lock` is auto-cleared if the previous process is dead. If it persists, check the PID in the lock file. |
|
|
440
|
+
| **Signal bot not responding** | Verify the signal-cli container is running (`docker ps \| grep ironcurtain-signal`). Check that Signal is configured (`ironcurtain setup-signal`). See [TRANSPORT.md](TRANSPORT.md) for detailed troubleshooting. |
|
|
425
441
|
|
|
426
442
|
## Development
|
|
427
443
|
|
|
@@ -450,7 +466,10 @@ src/
|
|
|
450
466
|
├── trusted-process/ # Policy engine, MCP proxy, audit log, escalation handler
|
|
451
467
|
├── pipeline/ # Constitution → policy compilation pipeline
|
|
452
468
|
├── escalation/ # Escalation listener: session registry, TUI dashboard, state
|
|
469
|
+
├── mux/ # Terminal multiplexer: PTY bridge, renderer, trusted input
|
|
453
470
|
├── signal/ # Signal messaging transport (bot daemon, setup, formatting)
|
|
471
|
+
├── daemon/ # Unified daemon (Signal + cron scheduler, control socket)
|
|
472
|
+
├── cron/ # Cron job management (scheduler, job store, git sync, policy)
|
|
454
473
|
├── docker/ # Docker agent mode, PTY session, MITM proxy, adapters
|
|
455
474
|
├── servers/ # Built-in MCP servers (fetch, web search providers)
|
|
456
475
|
└── types/ # Shared type definitions
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI help formatting module.
|
|
3
|
+
*
|
|
4
|
+
* Provides a declarative CommandSpec interface and formatting utilities
|
|
5
|
+
* so every subcommand produces consistent, aligned help output.
|
|
6
|
+
*/
|
|
7
|
+
export interface CommandOption {
|
|
8
|
+
/** The long flag name, without dashes (e.g., 'agent'). */
|
|
9
|
+
readonly flag: string;
|
|
10
|
+
/** Optional single-character short alias (e.g., 'a'). */
|
|
11
|
+
readonly short?: string;
|
|
12
|
+
/** Human-readable description. */
|
|
13
|
+
readonly description: string;
|
|
14
|
+
/** Placeholder for the value (e.g., '<name>'). Omit for boolean flags. */
|
|
15
|
+
readonly placeholder?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CommandSpec {
|
|
18
|
+
/** The full command name as invoked (e.g., 'ironcurtain daemon'). */
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** One-line summary of what the command does. */
|
|
21
|
+
readonly description: string;
|
|
22
|
+
/** Usage lines shown at the top. Each string is a separate line. */
|
|
23
|
+
readonly usage: readonly string[];
|
|
24
|
+
/** Named subcommands, displayed in a table after the usage block. */
|
|
25
|
+
readonly subcommands?: readonly {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly description: string;
|
|
28
|
+
}[];
|
|
29
|
+
/** Command-line options, displayed in a table after subcommands. */
|
|
30
|
+
readonly options?: readonly CommandOption[];
|
|
31
|
+
/** Example invocations shown at the bottom. */
|
|
32
|
+
readonly examples?: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Formats a CommandSpec into a complete help string.
|
|
36
|
+
*
|
|
37
|
+
* Layout:
|
|
38
|
+
* <name> - <description>
|
|
39
|
+
*
|
|
40
|
+
* Usage:
|
|
41
|
+
* <usage lines>
|
|
42
|
+
*
|
|
43
|
+
* Subcommands: (if any)
|
|
44
|
+
* <table>
|
|
45
|
+
*
|
|
46
|
+
* Options: (if any)
|
|
47
|
+
* <table>
|
|
48
|
+
*
|
|
49
|
+
* Examples: (if any)
|
|
50
|
+
* <lines>
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatHelp(spec: CommandSpec): string;
|
|
53
|
+
/**
|
|
54
|
+
* Prints formatted help to stderr.
|
|
55
|
+
*/
|
|
56
|
+
export declare function printHelp(spec: CommandSpec): void;
|
|
57
|
+
/**
|
|
58
|
+
* Convenience: if `values.help` is truthy, prints help and returns true.
|
|
59
|
+
* Callers can use: `if (checkHelp(values, spec)) return;`
|
|
60
|
+
*/
|
|
61
|
+
export declare function checkHelp(values: {
|
|
62
|
+
help?: boolean;
|
|
63
|
+
}, spec: CommandSpec): boolean;
|
package/dist/cli-help.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI help formatting module.
|
|
3
|
+
*
|
|
4
|
+
* Provides a declarative CommandSpec interface and formatting utilities
|
|
5
|
+
* so every subcommand produces consistent, aligned help output.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Formatting
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/** Pads `text` with spaces on the right to reach at least `width` characters. */
|
|
11
|
+
function pad(text, width) {
|
|
12
|
+
return text.length >= width ? text : text + ' '.repeat(width - text.length);
|
|
13
|
+
}
|
|
14
|
+
/** Formats a single option into its flag string (e.g., '-a, --agent <name>'). */
|
|
15
|
+
function formatOptionFlag(opt) {
|
|
16
|
+
const parts = [];
|
|
17
|
+
if (opt.short) {
|
|
18
|
+
parts.push(`-${opt.short},`);
|
|
19
|
+
}
|
|
20
|
+
let longFlag = `--${opt.flag}`;
|
|
21
|
+
if (opt.placeholder) {
|
|
22
|
+
longFlag += ` ${opt.placeholder}`;
|
|
23
|
+
}
|
|
24
|
+
if (opt.short) {
|
|
25
|
+
parts.push(longFlag);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Indent to align with flags that have a short alias.
|
|
29
|
+
// "-X, " is 4 chars, so we add 4 spaces of leading indent.
|
|
30
|
+
parts.push(` ${longFlag}`);
|
|
31
|
+
}
|
|
32
|
+
return parts.join(' ');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Renders a two-column table (label + description) with consistent alignment.
|
|
36
|
+
* Each row is indented by 2 spaces.
|
|
37
|
+
*/
|
|
38
|
+
function formatTable(rows) {
|
|
39
|
+
const maxLabel = rows.reduce((max, r) => Math.max(max, r.label.length), 0);
|
|
40
|
+
const colWidth = maxLabel + 2; // 2 spaces between columns
|
|
41
|
+
return rows.map((r) => ` ${pad(r.label, colWidth)}${r.description}`).join('\n');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Formats a CommandSpec into a complete help string.
|
|
45
|
+
*
|
|
46
|
+
* Layout:
|
|
47
|
+
* <name> - <description>
|
|
48
|
+
*
|
|
49
|
+
* Usage:
|
|
50
|
+
* <usage lines>
|
|
51
|
+
*
|
|
52
|
+
* Subcommands: (if any)
|
|
53
|
+
* <table>
|
|
54
|
+
*
|
|
55
|
+
* Options: (if any)
|
|
56
|
+
* <table>
|
|
57
|
+
*
|
|
58
|
+
* Examples: (if any)
|
|
59
|
+
* <lines>
|
|
60
|
+
*/
|
|
61
|
+
export function formatHelp(spec) {
|
|
62
|
+
const sections = [];
|
|
63
|
+
// Header
|
|
64
|
+
sections.push(`${spec.name} - ${spec.description}`);
|
|
65
|
+
// Usage
|
|
66
|
+
sections.push('Usage:\n' + spec.usage.map((u) => ` ${u}`).join('\n'));
|
|
67
|
+
// Subcommands
|
|
68
|
+
if (spec.subcommands && spec.subcommands.length > 0) {
|
|
69
|
+
const rows = spec.subcommands.map((s) => ({ label: s.name, description: s.description }));
|
|
70
|
+
sections.push('Subcommands:\n' + formatTable(rows));
|
|
71
|
+
}
|
|
72
|
+
// Options
|
|
73
|
+
if (spec.options && spec.options.length > 0) {
|
|
74
|
+
const rows = spec.options.map((o) => ({
|
|
75
|
+
label: formatOptionFlag(o),
|
|
76
|
+
description: o.description,
|
|
77
|
+
}));
|
|
78
|
+
sections.push('Options:\n' + formatTable(rows));
|
|
79
|
+
}
|
|
80
|
+
// Examples
|
|
81
|
+
if (spec.examples && spec.examples.length > 0) {
|
|
82
|
+
sections.push('Examples:\n' + spec.examples.map((e) => ` ${e}`).join('\n'));
|
|
83
|
+
}
|
|
84
|
+
return sections.join('\n\n');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Prints formatted help to stderr.
|
|
88
|
+
*/
|
|
89
|
+
export function printHelp(spec) {
|
|
90
|
+
console.error(formatHelp(spec));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Convenience: if `values.help` is truthy, prints help and returns true.
|
|
94
|
+
* Callers can use: `if (checkHelp(values, spec)) return;`
|
|
95
|
+
*/
|
|
96
|
+
export function checkHelp(values, spec) {
|
|
97
|
+
if (!values.help)
|
|
98
|
+
return false;
|
|
99
|
+
printHelp(spec);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=cli-help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
import { dirname, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
|
+
import { checkHelp, printHelp as printSpecHelp } from './cli-help.js';
|
|
7
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
function getVersion() {
|
|
9
10
|
// Works from both dist/ and src/ since package.json is one level up in either case.
|
|
@@ -11,53 +12,48 @@ function getVersion() {
|
|
|
11
12
|
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
12
13
|
return pkg.version;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ironcurtain refresh-lists --list major-news # Refresh a single list
|
|
57
|
-
ironcurtain refresh-lists --with-mcp # Include MCP-backed lists
|
|
58
|
-
ironcurtain customize-policy # Customize policy interactively
|
|
59
|
-
`.trim());
|
|
60
|
-
}
|
|
15
|
+
const topLevelSpec = {
|
|
16
|
+
name: 'ironcurtain',
|
|
17
|
+
description: 'Secure agent runtime with policy-driven tool mediation',
|
|
18
|
+
usage: ['ironcurtain <command> [options]'],
|
|
19
|
+
subcommands: [
|
|
20
|
+
{ name: 'start [task]', description: 'Run the agent (interactive or single-shot)' },
|
|
21
|
+
{ name: 'daemon', description: 'Unified Signal + cron daemon' },
|
|
22
|
+
{ name: 'mux', description: 'Terminal multiplexer for PTY sessions (requires node-pty)' },
|
|
23
|
+
{ name: 'escalation-listener', description: 'Aggregate escalation notifications from PTY sessions' },
|
|
24
|
+
{ name: 'bot', description: "Alias for 'daemon' (backward compatible)" },
|
|
25
|
+
{ name: 'setup', description: 'Run the first-start wizard (always runs)' },
|
|
26
|
+
{ name: 'setup-signal', description: 'Interactive Signal transport onboarding' },
|
|
27
|
+
{ name: 'annotate-tools', description: 'Classify MCP tool arguments via LLM' },
|
|
28
|
+
{ name: 'compile-policy', description: 'Compile constitution into enforceable policy rules' },
|
|
29
|
+
{ name: 'refresh-lists', description: 'Re-resolve dynamic lists without full recompilation' },
|
|
30
|
+
{ name: 'customize-policy', description: 'Customize your policy via LLM-assisted conversation' },
|
|
31
|
+
{ name: 'config', description: 'Edit configuration interactively' },
|
|
32
|
+
{ name: 'help', description: 'Show this help message' },
|
|
33
|
+
],
|
|
34
|
+
options: [
|
|
35
|
+
{ flag: 'help', short: 'h', description: 'Show this help message' },
|
|
36
|
+
{ flag: 'version', short: 'v', description: 'Show version number' },
|
|
37
|
+
],
|
|
38
|
+
examples: [
|
|
39
|
+
'ironcurtain start "Summarize files in ." # Single-shot task',
|
|
40
|
+
'ironcurtain start # Interactive session',
|
|
41
|
+
'ironcurtain start --resume <session-id> # Resume a session',
|
|
42
|
+
'ironcurtain start -w ./my-project "Fix bugs" # Work in existing directory',
|
|
43
|
+
'ironcurtain start --agent claude-code "task" # Docker: Claude Code',
|
|
44
|
+
'ironcurtain start --pty # PTY mode',
|
|
45
|
+
'ironcurtain daemon # Start the daemon',
|
|
46
|
+
'ironcurtain daemon list-jobs # List scheduled jobs',
|
|
47
|
+
'ironcurtain compile-policy # Compile policy',
|
|
48
|
+
'ironcurtain refresh-lists --with-mcp # Refresh dynamic lists',
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
const setupSignalSpec = {
|
|
52
|
+
name: 'ironcurtain setup-signal',
|
|
53
|
+
description: 'Interactive Signal transport onboarding',
|
|
54
|
+
usage: ['ironcurtain setup-signal [options]'],
|
|
55
|
+
options: [{ flag: 're-trust', description: 'Re-verify a changed identity key' }],
|
|
56
|
+
};
|
|
61
57
|
const { values, positionals } = parseArgs({
|
|
62
58
|
args: process.argv.slice(2),
|
|
63
59
|
options: {
|
|
@@ -72,8 +68,11 @@ if (values.version) {
|
|
|
72
68
|
process.exit(0);
|
|
73
69
|
}
|
|
74
70
|
const subcommand = positionals[0];
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
// Show top-level help when no subcommand is given or 'help' is the subcommand.
|
|
72
|
+
// When a recognized subcommand is present, --help passes through to the
|
|
73
|
+
// subcommand handler via process.argv.slice(3).
|
|
74
|
+
if (!subcommand || subcommand === 'help') {
|
|
75
|
+
printSpecHelp(topLevelSpec);
|
|
77
76
|
process.exit(0);
|
|
78
77
|
}
|
|
79
78
|
switch (subcommand) {
|
|
@@ -112,10 +111,35 @@ switch (subcommand) {
|
|
|
112
111
|
await runFirstStart();
|
|
113
112
|
break;
|
|
114
113
|
}
|
|
114
|
+
case 'daemon': {
|
|
115
|
+
const { runDaemonCommand } = await import('./daemon/daemon-command.js');
|
|
116
|
+
await runDaemonCommand(process.argv.slice(3));
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
115
119
|
case 'bot': {
|
|
120
|
+
// 'bot' is an alias for 'daemon' (backward compatibility)
|
|
121
|
+
const botArgs = process.argv.slice(3);
|
|
122
|
+
if (botArgs.includes('--help') || botArgs.includes('-h')) {
|
|
123
|
+
const { botSpec } = await import('./signal/bot-command.js');
|
|
124
|
+
printSpecHelp(botSpec);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
116
127
|
const agentName = values.agent;
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
if (botArgs.some((a) => !a.startsWith('-'))) {
|
|
129
|
+
// Has subcommands -- route through daemon command
|
|
130
|
+
const { runDaemonCommand } = await import('./daemon/daemon-command.js');
|
|
131
|
+
await runDaemonCommand(botArgs);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// No subcommands -- existing bot behavior
|
|
135
|
+
const { runBot } = await import('./signal/bot-command.js');
|
|
136
|
+
await runBot({ agent: agentName });
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case 'mux': {
|
|
141
|
+
const { main: muxMain } = await import('./mux/mux-command.js');
|
|
142
|
+
await muxMain(process.argv.slice(3));
|
|
119
143
|
break;
|
|
120
144
|
}
|
|
121
145
|
case 'escalation-listener': {
|
|
@@ -124,6 +148,8 @@ switch (subcommand) {
|
|
|
124
148
|
break;
|
|
125
149
|
}
|
|
126
150
|
case 'setup-signal': {
|
|
151
|
+
if (checkHelp({ help: process.argv.includes('--help') || process.argv.includes('-h') }, setupSignalSpec))
|
|
152
|
+
break;
|
|
127
153
|
const reTrust = process.argv.includes('--re-trust');
|
|
128
154
|
const { runSignalSetup } = await import('./signal/setup-signal.js');
|
|
129
155
|
await runSignalSetup({ reTrust });
|
|
@@ -131,7 +157,7 @@ switch (subcommand) {
|
|
|
131
157
|
}
|
|
132
158
|
default:
|
|
133
159
|
console.error(`Unknown command: ${subcommand}\n`);
|
|
134
|
-
|
|
160
|
+
printSpecHelp(topLevelSpec);
|
|
135
161
|
process.exit(1);
|
|
136
162
|
}
|
|
137
163
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|