Skip to content

v0.72.0 — the billing layer leaves home

Choose a tag to compare

@ralyodio ralyodio released this 29 Aug 07:38
1ad1704

The billing layer leaves home

v0.71.0 baked billing into moshcode. This release takes half of it back out —
not because it was wrong to build, but because two of those six commands turned
out not to be moshcode ideas at all.

Tracking time and sending an invoice are useful under any agentic CLI, and
on Windows, where moshcode does not go. So they now also exist on their own:

npm install -g @profullstack/timer @profullstack/billing
# or, from here:
moshcode install timer billing
# or, with the rest of the set:
moshcode install cli-tools

Zero runtime dependencies, Node 20.11+, tested on Linux, macOS and Windows. Both
answer --json on every command — one document on stdout, errors on stderr,
distinct exit codes — so an agent can clock its own work and prepare an invoice
without a wrapper.

The rate model came with them intact. $100/hour/agent/upto:4 still means four
agents cost four hundred an hour and so do six, and the invoice now bills
agent-hours, so the printed quantity times the printed rate equals the
printed amount and a client can check the line by hand:

auth refactor    14 agent-hours @ $100.00    $1,400.00

Three hours with two agents plus two hours with six, capped at four. Each entry
is charged with its own agent count before the units are summed, never after —
averaging would bill a two-agent afternoon at the four-agent rate.

Nothing changes until you say so

/timer, /billing, /client, /rate, /payments and /team all still work
exactly as they did in v0.71.0. Installing the packages changes nothing on its
own. When you want to move:

billing import            # look at what would come across
billing import --apply    # move it: clients, rates, invoices, entries
export MOSHCODE_EXTERNAL_BILLING=1

Then /timer and /billing hand the command straight to the CLIs, the way
/gh conducts gh. billing import shows the plan before it writes and never
modifies ~/.moshcode/ — if the mapping is wrong, the originals are still
there.

It is opt-in for a reason, and the reason is a bug we shipped and caught.
Only half this layer has an outside home: /client, /rate, /payments and
/team stay here, because the rails and the permission model are moshcode's and
/client's freeform dotted fields have no shape in the package's typed client
model. Handing over the other half automatically split one person's records
across two stores — /client and /rate writing ~/.moshcode/business.json
while /billing read the package's own ledger, so the invoice for a client you
had just created did not exist. That is not a missing feature, it is somebody's
invoice failing to exist.

It also only broke on a machine that had installed the CLIs, so CI — which had
not — stayed green while every developer box that took the install went red.
There are two guards against it now, both verified to fail when the old
behaviour is put back, and one of them puts a fake timer on PATH so no CI
run can be the reason it passes.

When the whole layer has an outside home, the hand-over becomes the default.

Also here

Two plugins join the marketplace — /timer:* and /billing:* — fronting the
CLIs rather than this repo's code. Their command files carry the rules that
matter rather than the flags: never stop somebody else's clock, never issue an
invoice unprompted, always dry-run first.

SHARED_COMMANDS is keyed by plugin family now. The flat list only ever made
sense for the stocks/crypto pair — quote and lookup are market words, and
requiring them of a timer would have forced two commands nobody would run.

One bug fixed on the way, found by running the thing rather than reading it:
openPassthrough resolves { ok, code, signal }, not a number, so handing that
to process.exitCode threw after the child had already printed its output — a
successful run ending in a stack trace. A signal-killed child is now a non-zero
exit rather than a silent success.