@prohost/cli 0.13.3 → 0.14.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/CHANGELOG.md +75 -0
- package/README.md +129 -0
- package/dist/agent/command.d.ts +8 -0
- package/dist/agent/command.js +40 -7
- package/dist/agent/daemon.d.ts +3 -1
- package/dist/agent/daemon.js +1 -2
- package/dist/agent/run.d.ts +30 -1
- package/dist/agent/run.js +221 -9
- package/dist/agent/runtime_status.d.ts +99 -3
- package/dist/agent/runtime_status.js +173 -5
- package/dist/agent/worktrees.d.ts +2 -0
- package/dist/agent/worktrees.js +3 -2
- package/dist/codehost/activity.d.ts +64 -0
- package/dist/codehost/activity.js +135 -0
- package/dist/codehost/agents.d.ts +31 -0
- package/dist/codehost/agents.js +57 -0
- package/dist/codehost/checkouts.d.ts +130 -0
- package/dist/codehost/checkouts.js +270 -0
- package/dist/codehost/claude_trust.d.ts +40 -0
- package/dist/codehost/claude_trust.js +129 -0
- package/dist/codehost/command.d.ts +25 -0
- package/dist/codehost/command.js +136 -0
- package/dist/codehost/decision.d.ts +156 -0
- package/dist/codehost/decision.js +553 -0
- package/dist/codehost/events.d.ts +49 -0
- package/dist/codehost/events.js +87 -0
- package/dist/codehost/folders.d.ts +122 -0
- package/dist/codehost/folders.js +317 -0
- package/dist/codehost/hooks.d.ts +178 -0
- package/dist/codehost/hooks.js +726 -0
- package/dist/codehost/host.d.ts +290 -0
- package/dist/codehost/host.js +625 -0
- package/dist/codehost/host_stats.d.ts +71 -0
- package/dist/codehost/host_stats.js +166 -0
- package/dist/codehost/invocations.d.ts +12 -0
- package/dist/codehost/invocations.js +82 -0
- package/dist/codehost/ledger.d.ts +35 -0
- package/dist/codehost/ledger.js +91 -0
- package/dist/codehost/local_server.d.ts +106 -0
- package/dist/codehost/local_server.js +339 -0
- package/dist/codehost/pty.d.ts +38 -0
- package/dist/codehost/pty.js +47 -0
- package/dist/codehost/ready.d.ts +31 -0
- package/dist/codehost/ready.js +109 -0
- package/dist/codehost/relay.d.ts +170 -0
- package/dist/codehost/relay.js +548 -0
- package/dist/codehost/settings.d.ts +52 -0
- package/dist/codehost/settings.js +114 -0
- package/dist/codehost/sizing.d.ts +163 -0
- package/dist/codehost/sizing.js +394 -0
- package/dist/codehost/terminals.d.ts +390 -0
- package/dist/codehost/terminals.js +965 -0
- package/dist/codehost/tmux.d.ts +196 -0
- package/dist/codehost/tmux.js +391 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,81 @@ Versions follow [semver](https://semver.org/). Publishing is automated: merging
|
|
|
4
4
|
a version bump to `main` triggers `.github/workflows/npm-publish-cli.yml`, which
|
|
5
5
|
builds via `prepack`, runs the suite, publishes, and tags `cli-v<version>`.
|
|
6
6
|
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
## 0.14.0
|
|
10
|
+
|
|
11
|
+
- **Code threads on this Mac: installed agents, honest shells, one prompt, who sizes.**
|
|
12
|
+
The host declares `code_thread_agent_claude_code` / `code_thread_agent_codex` for each
|
|
13
|
+
agent whose binary resolves on the daemon's `PATH` (re-checked per status frame), so a
|
|
14
|
+
`--host-only` Mac offers Claude Code and Codex, not only Shell; a start for one that is
|
|
15
|
+
missing fails saying which binary. A reply into a shell window is refused as the new
|
|
16
|
+
`shell_window` (was `terminal_gone`). New windows start at the size they'll be viewed at
|
|
17
|
+
(`resize-window` right after `new-window`), and a shell's first prompt waits for its first viewer's size to settle
|
|
18
|
+
(`tmux wait-for`), so it shows one prompt instead of one per resize. Each window is
|
|
19
|
+
sized by `resize-window` (so `window-size manual` per window; tmux 3.4 exits at start with
|
|
20
|
+
it in a config) to one viewer: whoever attaches, types, focuses or sends
|
|
21
|
+
`{"type":"claim"}` (never a watch viewer), applied once quiet 300 ms and never for a size it
|
|
22
|
+
already has, since Claude Code reprints its transcript on every width change. A viewer that
|
|
23
|
+
sent `{"type":"hello","client":{"kind","label"}}` is told who that is and whether the pane is
|
|
24
|
+
in copy mode (`{"type":"sizer",...,"scrolled"}`). Copy mode is the pane's, so it is left when
|
|
25
|
+
the sizing viewer changes and when the viewer that scrolled detaches. `scroll-on-clear off`:
|
|
26
|
+
a full-screen redraw no longer leaves a copy of the old screen in scrollback (Claude Code's
|
|
27
|
+
header repeated once per resize). No copy-mode position box and no coloured border at a
|
|
28
|
+
window's edge; typed text and relayed replies leave copy mode first (q / Esc / Ctrl+C there
|
|
29
|
+
still only leave it).
|
|
30
|
+
|
|
31
|
+
- **`runtime_status` reports the machine's short hostname** (`hostname`, up to its first dot). The
|
|
32
|
+
server keeps it against the socket and on a run started while this is the pairing's only socket,
|
|
33
|
+
for ProhostAI staff only. With `--machine`, that name is sent as given (`build.eu` stays whole)
|
|
34
|
+
instead of the real hostname, and one the server can't use as a hostname (`Front desk`) means no machine is recorded. An older server
|
|
35
|
+
ignores it.
|
|
36
|
+
- **`Claude Ultra` plan label.** `get_usage`'s `subscription_type: "ultra"` now reads
|
|
37
|
+
`Claude Ultra`. The value is inferred from `pro` / `max`; no real response has confirmed it yet.
|
|
38
|
+
- **Host Code threads on this Mac (off by default).** `prohost agent
|
|
39
|
+
code-host enable` lets `agent run` take `code_thread.start_requested`,
|
|
40
|
+
`code_thread.input_requested` and `code_thread.approval_decided`: one worktree and one window in a private tmux
|
|
41
|
+
server per thread, running Claude Code, Codex or a shell, re-adopted after a
|
|
42
|
+
restart. Claude Code / Codex hooks are passed on their command lines (never
|
|
43
|
+
installed globally) and post the thread's prompts, turns, permission prompts
|
|
44
|
+
and interrupts with a narrow, expiring token. `prohost agent code-host remote
|
|
45
|
+
on` additionally lets the machine's owner attach and type from ProhostAI
|
|
46
|
+
through the relay (`/v1/code-hosts/*`), or from the desktop app over
|
|
47
|
+
loopback. While hosting, the heartbeat carries optional `host` stats. New
|
|
48
|
+
optional dependency: `node-pty` 1.1.0.
|
|
49
|
+
- **`agent run --host-only`.** Hosts Code threads with no `--exec`: every
|
|
50
|
+
`agent.run_requested` is closed at once as failed ("This computer only hosts
|
|
51
|
+
Code threads; it doesn't answer messages."), nothing is executed, and the
|
|
52
|
+
harness declares `host_only`. Mutually exclusive with `--exec`;
|
|
53
|
+
`install-daemon --host-only` passes it through; listed in `--help`.
|
|
54
|
+
- **`code-host/activity.json` for keep-awake.** While hosting, the daemon
|
|
55
|
+
writes `{"v":1,"busy":bool,"updatedAt":ISO}` (0600, atomic, at least every
|
|
56
|
+
30 s; removed when hosting stops): busy while any hosted agent is mid-turn
|
|
57
|
+
(Claude Code `UserPromptSubmit` → `Stop`; a goal or typed reply → Codex's
|
|
58
|
+
turn-complete; Esc/Ctrl+C from an attached viewer ends it; 6 h cap per
|
|
59
|
+
turn). Readers treat `updatedAt` older than ~3 min as not busy.
|
|
60
|
+
- **Folder Code threads.** A `code_thread.start_requested` with `folder` runs
|
|
61
|
+
in that folder in place (no worktree, no branch), or with `separateCopy` in a
|
|
62
|
+
worktree and branch of the folder's git checkout. Only a folder this Mac has
|
|
63
|
+
opened, or one inside it (after realpath), starts; anything else is refused.
|
|
64
|
+
`prohost agent code-host folders add|list|remove` keeps the opened folders
|
|
65
|
+
(`code-host/folders.json`); the status frame reports them as
|
|
66
|
+
`code_host_folders` with `code_host_home`, and the host declares
|
|
67
|
+
`code_thread_folders`. A shell in an opened folder starts with `remote off`.
|
|
68
|
+
- **Folder threads, follow-ups.** A separate copy refuses a subfolder its
|
|
69
|
+
branch doesn't have, and one that resolves outside the new worktree (a
|
|
70
|
+
symlink on that branch), instead of starting in the worktree's root or in
|
|
71
|
+
the owner's files. Changes to `folders.json` (`folders add`/`remove`, a
|
|
72
|
+
start moving its folder to the front) take an inter-process lock, so two at
|
|
73
|
+
once never drop a folder. The tmux server and every pane no longer inherit
|
|
74
|
+
`ELECTRON_RUN_AS_NODE` / `PROHOST_CLI_RUN` from a daemon the desktop app runs
|
|
75
|
+
on Electron; the hook shim sets `ELECTRON_RUN_AS_NODE=1` for itself only.
|
|
76
|
+
- **The desktop app attaches locally with `remote off`.** The 127.0.0.1 socket
|
|
77
|
+
and `local.json` are served whenever hosting is on; with the remote switch
|
|
78
|
+
off they admit only a viewer token the relay vouches for as `local`, and
|
|
79
|
+
`remote off` ends every other desktop viewer. `code-host disable` closes the
|
|
80
|
+
socket at once.
|
|
81
|
+
|
|
7
82
|
## 0.13.3
|
|
8
83
|
|
|
9
84
|
- **Every turn the agent answers in reaches the thread.** When a command the
|
package/README.md
CHANGED
|
@@ -632,8 +632,137 @@ workspace's `.claude/`: a skill that points it at the repo's PR protocol
|
|
|
632
632
|
the `gh pr create` gate hook the fleet uses. Nothing else about the default
|
|
633
633
|
workspace changes.
|
|
634
634
|
|
|
635
|
+
### Hosting Code threads on this Mac (off by default)
|
|
636
|
+
|
|
637
|
+
ProhostAI can show agent terminals — Claude Code, Codex or a plain shell — as
|
|
638
|
+
**Code** threads in the inbox. This machine can host them, but only once its
|
|
639
|
+
owner says so, here, on this machine. Nothing the server sends can turn it on.
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
prohost agent code-host enable # host Code threads (needs --repos on agent run)
|
|
643
|
+
prohost agent code-host remote on # also let *you* attach and type from ProhostAI
|
|
644
|
+
prohost agent code-host status
|
|
645
|
+
prohost agent code-host remote off # refuses the next viewer at once
|
|
646
|
+
prohost agent code-host disable
|
|
647
|
+
|
|
648
|
+
prohost agent code-host folders add ~/code/app # open a folder for Code threads (the desktop app does this)
|
|
649
|
+
prohost agent code-host folders list [--json]
|
|
650
|
+
prohost agent code-host folders remove ~/code/app
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
Restart `agent run` (or the daemon) after `enable` / `disable`.
|
|
654
|
+
|
|
655
|
+
**Host only: Code threads without an agent that answers.** `prohost agent run
|
|
656
|
+
--host-only` (or `install-daemon --host-only`) takes no `--exec`. It keeps
|
|
657
|
+
everything hosting needs — the connection, presence, status, the desktop
|
|
658
|
+
app's local socket, `activity.json` — and executes nothing anyone sends: every
|
|
659
|
+
run requested of this agent (a mention, a DM) is closed at once as failed with
|
|
660
|
+
"This computer only hosts Code threads; it doesn't answer messages." It
|
|
661
|
+
declares the `host_only` capability. `--host-only` and `--exec` can't be used
|
|
662
|
+
together, and `prohost --help` lists `--host-only` on builds that have it. A
|
|
663
|
+
host-only agent runs no agent of its own, so it has no account: its Code
|
|
664
|
+
threads run Claude Code or Codex on this Mac's default login, as a terminal you
|
|
665
|
+
opened would (`--account` is refused with `--host-only`, and an account already
|
|
666
|
+
on the pairing is reported as unused at start). The
|
|
667
|
+
desktop app's "This Mac" daemon runs this way.
|
|
668
|
+
|
|
669
|
+
- **Folder threads, in place.** A thread can also run in a folder instead of a
|
|
670
|
+
repository: in that folder itself, with no worktree and no branch, the way a
|
|
671
|
+
terminal you open there would. The desktop app's folder picker adds what you
|
|
672
|
+
pick to this Mac's **opened folders** (`folders add`;
|
|
673
|
+
`$PROHOST_HOME/code-host/folders.json`, `0600`, the 50 most recent, each
|
|
674
|
+
stored as its realpath). A thread started from anywhere else — your phone,
|
|
675
|
+
the web — may only run in one of those folders or a folder inside one,
|
|
676
|
+
checked after resolving symlinks, so a link that points out of an opened
|
|
677
|
+
folder does not count as inside it. Anything else is refused with "This
|
|
678
|
+
folder hasn't been opened on this Mac." A thread that starts moves its
|
|
679
|
+
folder to the front of the list. The list (with whether each is a git
|
|
680
|
+
repository, and your home directory for showing `~/…`) is reported to
|
|
681
|
+
ProhostAI so your phone can offer it; `--repos` is not needed for folders.
|
|
682
|
+
- **Work in a separate copy.** For a folder in a git repository, a thread can
|
|
683
|
+
instead get its own worktree and branch of that checkout, exactly as a
|
|
684
|
+
repository thread does, opened in the same subfolder. Your checkout is never
|
|
685
|
+
switched. A folder outside git can't, and says so; neither can a subfolder
|
|
686
|
+
the branch doesn't have (untracked or ignored) or one that is a link out of
|
|
687
|
+
the copy on that branch — the thread is refused, never started elsewhere.
|
|
688
|
+
- **The goal is optional.** A thread with no goal opens Claude Code or Codex
|
|
689
|
+
with no prompt, or just the shell.
|
|
690
|
+
|
|
691
|
+
- **One worktree and one tmux window per repository thread.** The repository is matched
|
|
692
|
+
against `--repos` by its `origin` remote (else by directory name), checked out
|
|
693
|
+
under `$PROHOST_HOME/worktrees/`, and switched to the thread's branch. The
|
|
694
|
+
windows live in a private tmux server (`tmux -L prohost-<id>`; your own tmux
|
|
695
|
+
is untouched), so they survive the agent restarting and are re-adopted. You
|
|
696
|
+
need `tmux` installed (`brew install tmux`).
|
|
697
|
+
- **Hooks without touching your config.** Claude Code is started with
|
|
698
|
+
`--settings` and Codex with `-c notify=…`, naming a shim that does nothing
|
|
699
|
+
unless `$PROHOST_TERMINAL_ID` is set — your own `claude` sessions never see
|
|
700
|
+
them, and `~/.claude` / `~/.codex` are never written. They report the prompt,
|
|
701
|
+
the end of each turn, permission prompts and Esc/Ctrl+C to the thread, using a
|
|
702
|
+
narrow token the relay issues (`$PROHOST_HOME/code-host/hook.json`, `0600`,
|
|
703
|
+
expires in a day) — never your API key, never a Claude or Codex credential.
|
|
704
|
+
ProhostAI saves the conversation, not your terminal. An Allow / Deny chosen in
|
|
705
|
+
ProhostAI answers the prompt in the terminal (Enter, Down+Enter, Escape) —
|
|
706
|
+
only if that prompt is still the one on screen.
|
|
707
|
+
- **Reply recovery after a permission menu.** New Claude Code windows check
|
|
708
|
+
the installed version before choosing their hooks. Claude Code 2.1.295 or
|
|
709
|
+
newer records tool invocation IDs before permission processing; replies can
|
|
710
|
+
resume when the transcript confirms those calls finished. Recording failures
|
|
711
|
+
block the request. Older or unverifiable versions, already-running windows,
|
|
712
|
+
and subagent permission prompts wait for a new user prompt or a completed
|
|
713
|
+
turn when the menu's outcome is uncertain. Start a new window after upgrading
|
|
714
|
+
to use the invocation hooks.
|
|
715
|
+
Keep the daemon running: it reports verified completion back to the thread,
|
|
716
|
+
including after the waiting hook exits, and retries reports after reconnect.
|
|
717
|
+
- **Replies from ProhostAI go to the agent, never to a shell.** Typing a reply
|
|
718
|
+
or an Allow / Deny into a terminal from ProhostAI needs `remote on`, read at
|
|
719
|
+
every event. It reaches only a running Claude Code or Codex: never a shell
|
|
720
|
+
thread, and never the shell an agent leaves behind when it exits. Starting a
|
|
721
|
+
shell thread for a repository also needs `remote on`; a shell in an opened
|
|
722
|
+
folder starts with it off, where only the desktop app on this Mac can type
|
|
723
|
+
into it. A reply or decision that is redelivered
|
|
724
|
+
after a reconnect is never typed twice. A goal is always passed after `--`,
|
|
725
|
+
so it can't become an option.
|
|
726
|
+
- **A remote terminal is remote code execution on this Mac.** With `remote on`,
|
|
727
|
+
the machine's owner (whoever paired it) — and nobody else — can attach from the
|
|
728
|
+
web, phone or desktop app and type. Off, the relay refuses every viewer and
|
|
729
|
+
this machine refuses every dial. Terminal bytes are relayed, never stored.
|
|
730
|
+
- **The desktop app on this Mac attaches over `127.0.0.1`**, not the relay,
|
|
731
|
+
whenever hosting is on — with `remote off` too. The socket's port and token
|
|
732
|
+
are in `$PROHOST_HOME/code-host/local.json` (`0600`, new every start; removed
|
|
733
|
+
by `disable` at once). Any request with an `Origin` header — every browser —
|
|
734
|
+
is refused, and every attach's viewer token is checked with the relay, which
|
|
735
|
+
vouches for who it is. With `remote off` it admits only a token ProhostAI
|
|
736
|
+
minted for this Mac's own desktop app, which the relay refuses for any
|
|
737
|
+
relayed attach; `remote off` ends every other viewer at once.
|
|
738
|
+
- **Whether an agent is working, for keep-awake.** While hosting, the daemon
|
|
739
|
+
keeps `$PROHOST_HOME/code-host/activity.json` (`0600`, written atomically):
|
|
740
|
+
`{"v":1,"busy":true|false,"updatedAt":"<ISO>"}`. `busy` is true while any
|
|
741
|
+
thread's agent is mid-turn: from Claude Code's prompt to its `Stop`, from a
|
|
742
|
+
goal or a reply (not a `/` command) this Mac types to Codex's turn-complete, ended early by Esc
|
|
743
|
+
or Ctrl+C in an attached terminal, and never for longer than 6 hours per
|
|
744
|
+
turn. The file is rewritten at least every 30 s and removed when hosting
|
|
745
|
+
stops. **A reader must treat an `updatedAt` older than about 3 minutes as
|
|
746
|
+
not busy**, so a daemon that died never keeps the Mac awake. The desktop
|
|
747
|
+
app's "Keep this Mac awake while an agent is working" reads it. A prompt
|
|
748
|
+
typed straight into a Codex terminal isn't seen until that turn ends.
|
|
749
|
+
- **Attaching needs `node-pty`**, an optional dependency with prebuilt macOS
|
|
750
|
+
binaries. Where it doesn't load, threads still run; only attaching answers
|
|
751
|
+
`pty_unavailable`.
|
|
752
|
+
- While hosting, the heartbeat also reports this machine's CPU, memory,
|
|
753
|
+
memory pressure, battery and model (`vm_stat`, `sysctl`, `pmset`) for the
|
|
754
|
+
thread's Machine card.
|
|
755
|
+
|
|
635
756
|
### The trust model — read this once
|
|
636
757
|
|
|
758
|
+
**`--host-only` is the exception: it runs nothing anyone sends.** No message,
|
|
759
|
+
mention or DM executes a command on a host-only machine; each requested run is
|
|
760
|
+
refused. What it does run are Code-thread terminals, and only as described
|
|
761
|
+
under "Hosting Code threads on this Mac": a thread starts only in a repository
|
|
762
|
+
from `--repos` or a folder this Mac has opened; nothing from ProhostAI types
|
|
763
|
+
into one without `remote on`; with `remote off` only the desktop app on this
|
|
764
|
+
Mac attaches; and only the pairing's owner can attach at all.
|
|
765
|
+
|
|
637
766
|
`agent run` is a standing instruction to execute a command on your machine when
|
|
638
767
|
someone in your ProhostAI workspace asks. Be clear-eyed about what that means:
|
|
639
768
|
|
package/dist/agent/command.d.ts
CHANGED
|
@@ -57,4 +57,12 @@ export declare function parseConcurrency(raw: string | boolean | undefined): num
|
|
|
57
57
|
export declare function daemonEnvironment(): Record<string, string>;
|
|
58
58
|
/** Dispatch an ``agent`` subcommand. Returns the process exit code. */
|
|
59
59
|
export declare function runAgent(subcommand: string | undefined, flags: Flags, printHelp: () => void, args?: string[]): Promise<number>;
|
|
60
|
+
/**
|
|
61
|
+
* `--exec <command>` or `--host-only`, exactly one: what this harness does with a run. `null`
|
|
62
|
+
* after explaining why not. Host-only runs nothing anyone sends; it only hosts Code threads.
|
|
63
|
+
*/
|
|
64
|
+
export declare function execOrHostOnly(flags: Flags, verb: 'run' | 'install-daemon'): {
|
|
65
|
+
exec: string;
|
|
66
|
+
hostOnly: boolean;
|
|
67
|
+
} | null;
|
|
60
68
|
export {};
|
package/dist/agent/command.js
CHANGED
|
@@ -20,6 +20,8 @@ import { CredentialsMissingError, ensureWorkspace, loadCredentials, prohostHome,
|
|
|
20
20
|
import { DEFAULT_EXEC_TIMEOUT_MS, DEFAULT_IDLE_TIMEOUT_MS, runAgentHarness } from './run.js';
|
|
21
21
|
import { MAX_CONCURRENCY } from './scheduler.js';
|
|
22
22
|
import { ReposFlagError, isCheckout, parseReposFlag } from './worktrees.js';
|
|
23
|
+
import { codeHostCommand } from '../codehost/command.js';
|
|
24
|
+
import { loadSettings as loadCodeHostSettings } from '../codehost/settings.js';
|
|
23
25
|
import { announceUpgradeIfAvailable } from '../upgrade.js';
|
|
24
26
|
import { DEFAULT_WS_URL } from '../listen.js';
|
|
25
27
|
function flagString(flags, name) {
|
|
@@ -223,10 +225,12 @@ export async function runAgent(subcommand, flags, printHelp, args = []) {
|
|
|
223
225
|
return listCommand();
|
|
224
226
|
if (subcommand === 'workspace')
|
|
225
227
|
return workspaceCommand(args, flags);
|
|
228
|
+
if (subcommand === 'code-host')
|
|
229
|
+
return codeHostCommand(args, flags);
|
|
226
230
|
process.stderr.write(subcommand
|
|
227
231
|
? `Unknown agent subcommand: ${subcommand}\n\n`
|
|
228
232
|
: 'Missing agent subcommand — expected `pair`, `run`, `install-daemon`, `uninstall-daemon`, ' +
|
|
229
|
-
'`account`, `list`, or `
|
|
233
|
+
'`account`, `list`, `workspace`, or `code-host`.\n\n');
|
|
230
234
|
printHelp();
|
|
231
235
|
return 1;
|
|
232
236
|
}
|
|
@@ -256,12 +260,35 @@ function loadOrExplain(streamOverride) {
|
|
|
256
260
|
}
|
|
257
261
|
return credentials;
|
|
258
262
|
}
|
|
259
|
-
|
|
263
|
+
/**
|
|
264
|
+
* `--exec <command>` or `--host-only`, exactly one: what this harness does with a run. `null`
|
|
265
|
+
* after explaining why not. Host-only runs nothing anyone sends; it only hosts Code threads.
|
|
266
|
+
*/
|
|
267
|
+
export function execOrHostOnly(flags, verb) {
|
|
260
268
|
const exec = flagString(flags, 'exec');
|
|
269
|
+
const hostOnly = flags['host-only'] === true;
|
|
270
|
+
if (hostOnly && flags.exec !== undefined) {
|
|
271
|
+
process.stderr.write('--host-only and --exec can\'t be used together: a host-only agent runs no command.\n');
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
if (hostOnly && flags.account !== undefined) {
|
|
275
|
+
process.stderr.write("--host-only and --account can't be used together: a host-only agent runs no agent of its own, " +
|
|
276
|
+
"so its Code threads use this Mac's default Claude Code / Codex login.\n");
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
if (hostOnly)
|
|
280
|
+
return { exec: '', hostOnly: true };
|
|
261
281
|
if (!exec) {
|
|
262
|
-
process.stderr.write(
|
|
263
|
-
return
|
|
282
|
+
process.stderr.write(`Missing required flag: --exec (or --host-only). Example: prohost agent ${verb} --exec 'claude -p'\n`);
|
|
283
|
+
return null;
|
|
264
284
|
}
|
|
285
|
+
return { exec, hostOnly: false };
|
|
286
|
+
}
|
|
287
|
+
async function installDaemonCommand(flags) {
|
|
288
|
+
const mode = execOrHostOnly(flags, 'install-daemon');
|
|
289
|
+
if (!mode)
|
|
290
|
+
return 1;
|
|
291
|
+
const { exec } = mode;
|
|
265
292
|
const credentials = loadOrExplain(flagString(flags, 'url'));
|
|
266
293
|
if (!credentials)
|
|
267
294
|
return 1;
|
|
@@ -299,6 +326,7 @@ async function installDaemonCommand(flags) {
|
|
|
299
326
|
execPath: process.execPath,
|
|
300
327
|
scriptPath: cliEntrypoint(),
|
|
301
328
|
exec,
|
|
329
|
+
hostOnly: mode.hostOnly,
|
|
302
330
|
workdir,
|
|
303
331
|
timeoutSeconds,
|
|
304
332
|
idleTimeoutSeconds,
|
|
@@ -420,10 +448,14 @@ async function pairCommand(flags) {
|
|
|
420
448
|
return 0;
|
|
421
449
|
}
|
|
422
450
|
async function runCommand(flags) {
|
|
423
|
-
const
|
|
424
|
-
if (!
|
|
425
|
-
process.stderr.write("Missing required flag: --exec. Example: prohost agent run --exec 'claude -p'\n");
|
|
451
|
+
const mode = execOrHostOnly(flags, 'run');
|
|
452
|
+
if (!mode)
|
|
426
453
|
return 1;
|
|
454
|
+
const { exec, hostOnly } = mode;
|
|
455
|
+
if (hostOnly && !loadCodeHostSettings().enabled) {
|
|
456
|
+
// Not fatal: under a supervisor an exit would only restart into the same state.
|
|
457
|
+
process.stderr.write('! --host-only: Code-thread hosting is off on this machine (prohost agent code-host enable), ' +
|
|
458
|
+
'so this agent will refuse every run and host nothing until it is turned on and the agent restarts.\n');
|
|
427
459
|
}
|
|
428
460
|
const credentials = loadOrExplain(flagString(flags, 'url') ?? process.env.PROHOST_WS_URL);
|
|
429
461
|
if (!credentials)
|
|
@@ -465,6 +497,7 @@ async function runCommand(flags) {
|
|
|
465
497
|
try {
|
|
466
498
|
const metrics = await runAgentHarness({
|
|
467
499
|
exec,
|
|
500
|
+
hostOnly,
|
|
468
501
|
credentials,
|
|
469
502
|
url: flagString(flags, 'url') ?? process.env.PROHOST_WS_URL,
|
|
470
503
|
machine: flagString(flags, 'machine'),
|
package/dist/agent/daemon.d.ts
CHANGED
|
@@ -79,8 +79,10 @@ export interface HarnessInvocation {
|
|
|
79
79
|
execPath: string;
|
|
80
80
|
/** Absolute path of the CLI entrypoint script. */
|
|
81
81
|
scriptPath: string;
|
|
82
|
-
/** The `--exec` command, verbatim. */
|
|
82
|
+
/** The `--exec` command, verbatim. Unused with {@link hostOnly}. */
|
|
83
83
|
exec: string;
|
|
84
|
+
/** `--host-only` instead of `--exec`: the daemon hosts Code threads and runs nothing. */
|
|
85
|
+
hostOnly?: boolean;
|
|
84
86
|
workdir: string;
|
|
85
87
|
timeoutSeconds?: number;
|
|
86
88
|
/** Seconds of total silence before a run is treated as stalled. */
|
package/dist/agent/daemon.js
CHANGED
|
@@ -171,8 +171,7 @@ export function harnessArguments(invocation) {
|
|
|
171
171
|
invocation.scriptPath,
|
|
172
172
|
'agent',
|
|
173
173
|
'run',
|
|
174
|
-
'--exec',
|
|
175
|
-
invocation.exec,
|
|
174
|
+
...(invocation.hostOnly ? ['--host-only'] : ['--exec', invocation.exec]),
|
|
176
175
|
'--workdir',
|
|
177
176
|
invocation.workdir,
|
|
178
177
|
];
|
package/dist/agent/run.d.ts
CHANGED
|
@@ -28,10 +28,13 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import type { spawn } from 'node:child_process';
|
|
30
30
|
import WebSocket from 'ws';
|
|
31
|
+
import { HarnessAccount } from './account_runtime.js';
|
|
31
32
|
import type { CommandRunner } from './accounts.js';
|
|
32
33
|
import type { AgentCredentials } from './credentials.js';
|
|
33
34
|
import type { RunCapabilities } from './capabilities.js';
|
|
34
35
|
import type { QuotaProvider } from './runtime_status.js';
|
|
36
|
+
import type { CodeHostLike } from '../codehost/host.js';
|
|
37
|
+
import type { AccountEnvResult, CodeAgent } from '../codehost/terminals.js';
|
|
35
38
|
/**
|
|
36
39
|
* Default ceiling on one agent invocation: none.
|
|
37
40
|
*
|
|
@@ -72,9 +75,20 @@ export declare const DEFAULT_IDLE_TIMEOUT_MS = 900000;
|
|
|
72
75
|
* uses it to stop a run from sitting on CI after the PR is open.
|
|
73
76
|
*/
|
|
74
77
|
export declare const PAIRED_RUN_ENV_VAR = "PROHOST_PAIRED_RUN";
|
|
78
|
+
/** What a run sent to a `--host-only` harness is answered with: it never executes. */
|
|
79
|
+
export declare const HOST_ONLY_REFUSAL = "This computer only hosts Code threads; it doesn't answer messages.";
|
|
80
|
+
/** Declared by a `--host-only` harness: it executes no runs (the server may say so, or stop sending them). */
|
|
81
|
+
export declare const HOST_ONLY_CAPABILITY = "host_only";
|
|
75
82
|
export interface AgentRunOptions {
|
|
76
|
-
/** Shell command that runs the agent, e.g. ``claude -p``. */
|
|
83
|
+
/** Shell command that runs the agent, e.g. ``claude -p``. Empty with {@link hostOnly}. */
|
|
77
84
|
exec: string;
|
|
85
|
+
/**
|
|
86
|
+
* `--host-only`: this harness hosts Code threads (control socket, presence,
|
|
87
|
+
* status, the local server, activity) and executes nothing anyone sends. Every
|
|
88
|
+
* `agent.run_requested` it receives is closed at once as failed with
|
|
89
|
+
* {@link HOST_ONLY_REFUSAL}, so a mention never hangs.
|
|
90
|
+
*/
|
|
91
|
+
hostOnly?: boolean;
|
|
78
92
|
credentials: AgentCredentials;
|
|
79
93
|
/** Websocket endpoint override. Defaults to the paired value. */
|
|
80
94
|
url?: string;
|
|
@@ -176,6 +190,12 @@ export interface AgentRunOptions {
|
|
|
176
190
|
* inject a fake so no extra request lands in their recorded calls.
|
|
177
191
|
*/
|
|
178
192
|
capabilityProbe?: () => Promise<RunCapabilities>;
|
|
193
|
+
/**
|
|
194
|
+
* The Code-thread host (`codehost/host.ts`). Unset, one is built only when
|
|
195
|
+
* this machine's owner turned hosting on (`prohost agent code-host enable`);
|
|
196
|
+
* `null` never hosts. Tests inject a fake.
|
|
197
|
+
*/
|
|
198
|
+
codeHost?: CodeHostLike | null;
|
|
179
199
|
}
|
|
180
200
|
export interface AgentRunMetrics {
|
|
181
201
|
connections: number;
|
|
@@ -228,5 +248,14 @@ export declare function codexMcpEntryProvider(options: AgentRunOptions, log: (li
|
|
|
228
248
|
* Reconnects on any drop with jittered exponential backoff; an ``auth_error``
|
|
229
249
|
* is fatal, because retrying a rejected credential just hammers the server.
|
|
230
250
|
*/
|
|
251
|
+
/**
|
|
252
|
+
* The login a Code thread's agent runs on: the account picked for this
|
|
253
|
+
* agent, as its ordinary runs get it. A thread for the other CLI, or an
|
|
254
|
+
* account that is no longer on this machine, is refused rather than run on
|
|
255
|
+
* some other login — it would spend a subscription the agent doesn't report.
|
|
256
|
+
* A harness without accounts (a plain `--exec`) and shell threads use the
|
|
257
|
+
* operator's default login, exactly as everything else they run does.
|
|
258
|
+
*/
|
|
259
|
+
export declare function codeThreadAccountEnv(account: HarnessAccount | undefined, agent: CodeAgent): AccountEnvResult;
|
|
231
260
|
export declare function runAgentHarness(options: AgentRunOptions): Promise<AgentRunMetrics>;
|
|
232
261
|
export {};
|