@projectpac/executor-cli
v0.1.3
Published
Part of PAC: @projectpac/executor-cli.
Downloads
525
Readme
@projectpac/executor-cli
An executor that is a command line -- the shape every coding harness already has.
pi and claude are two rows of this in executors.yml, not two packages:
what differs between them is argv, not machinery.
What the seam needs from a subprocess is small: a prompt in, text out, a way to stop it, and a record of what happened. Everything here is about the ways a subprocess fails to be that.
- The prompt goes in on stdin by default, because that is the only route with no
length limit and no quoting.
fileandargsare there for commands that insist. - stdout is the answer; stderr is kept beside it in the turn's artifact directory, along with the prompt, because a human reading a trace afterwards needs what the model was told and not only what it said.
- Nothing is inherited that was not named. A model's process is the least trusted thing in the node, and the parent's environment is where a token lives.
- Output past a bound is a runaway process rather than an answer, and the command is killed.
- The command runs in its own process group, so stopping it reaches the whole tree rather than a shell that already forked the real work.
What a turn cost
Every harness reports it differently, so this is per command rather than per provider -- and unconfigured means unknown, which a budget treats as free. There are two shapes, and naming both is refused because a turn has one cost:
costPattern-- a regular expression over the command's output whose first capture is a decimal amount.claude -p --output-format jsonprints"total_cost_usd": 0.0123, so"total_cost_usd"\s*:\s*([0-9.]+)reads it.costFile-- a path inside the turn's own artifact directory, a dotted field, and how several amounts become one.piwritesmessage.usage.cost.totalfor each assistant message into the--sessionfile it was already told to keep, and prints nothing about cost at all, so reading the file is the only way to know:
"costFile": {
"path": "session.jsonl",
"field": "message.usage.cost.total",
"reduce": "sum"
}sum because each message's usage is its own; last is for a log that records
a running total instead. Either way the amount lands as minor units, rounded up,
so many small turns cannot slide under a budget -- and an unreadable file leaves
the cost unknown rather than failing the turn it only describes.
