td-barrage
v0.1.4
Published
TypeScript queue runner that loads a JSON task file, resolves dependencies, runs tasks through the OpenCode SDK, and resumes interrupted work.
Downloads
754
Readme
td-barrage
A small TypeScript queue runner for OpenCode tasks. It loads a JSON task file, resolves dependencies, runs ready tasks through the OpenCode SDK, persists state after every transition, and resumes interrupted work on the next invocation.
Requirements
- Node.js 20.11 or newer
- npm
- OpenCode available when running against a real server
Install
In a target project:
npm install --save-dev td-barrageOr run ad-hoc without installing:
npx td-barrage --tasks queue.jsonOnce installed, the td-barrage CLI is on the project PATH:
npx td-barrage --tasks queue.jsonFor local development on this package:
npm install
npm run buildTask File
Task files use a top-level tasks array. A bare array is also accepted when loading.
{
"tasks": [
{
"id": "build",
"prompt": "Create .queue/results/build.json with {\"status\":\"ok\"}.",
"success": { "strategy": "result_json", "path": ".queue/results/build.json" }
},
{
"id": "verify",
"prompt": "Verify the build result and create .queue/results/verify.json with {\"status\":\"ok\"}.",
"dependsOn": ["build"],
"timeoutMs": 600000,
"maxAttempts": 2,
"success": { "strategy": "result_json", "path": ".queue/results/verify.json" }
}
]
}Supported task fields:
id: unique task idprompt: prompt sent to OpenCodedependsOn: dependency idsstatus:pending,running,done,failed, orblockedattempts: persisted attempt countermaxAttempts: per-task retry limittimeoutMs: per-task prompt timeoutcwd: per-task working directorysuccess:default,file_exists, orresult_jsonrecoveryNotes,error,startedAt,finishedAt: runtime metadata
CLI
npm run dev -- --tasks queue.json
npm run dev -- queue.json --max-attempts 3 --cwd /path/to/work
npm run dev -- --tasks queue.json --dry-run
npm run dev -- --tasks queue.json --json
npm run dev -- --tasks queue.json --verbose --timestampsOptions:
--tasks, -t <path>: task file (also accepted as a positional argument)--max-attempts <n>: global retry limit (per-taskmaxAttemptsoverrides it)--cwd <path>: working directory for tasks that do not set their own--dry-run: print the topological plan (one id per line) and exit without running--no-commit: skip the git commit stage that otherwise runs after each successful task--json: emit newline-delimited JSON events instead of human output
Exit codes:
0: all reachable tasks completed1: at least one task failed or became blocked2: CLI usage or missing task-file error75: the run paused on a provider quota / rate-limit wall (see below)
Pausing on quota / rate limits
Running out of provider tokens is treated as a property of the run, not of any
task. When a prompt fails with a quota, rate-limit, or overload signal (HTTP
402/429/503/529, or matching error wording), the runner:
- rolls back the attempt it charged — the task did not really run, so its retry budget is untouched,
- leaves the task
pending(exactly as crash recovery does), - stops the rest of the sweep immediately, since every ready task would hit the same wall, and
- exits
75(EX_TEMPFAIL) with the reason on stderr.
Because state is persisted atomically after every transition, resuming is just
re-running the same command once tokens are back — work picks up where it left
off, with no failed or blocked tasks recorded from the pause. The distinct exit
code lets a wrapper or scheduler tell "try again later" apart from 1 ("something
is broken"). A Retry-After header, when present, is surfaced as a suggested
wait in the pause message.
Telemetry & output
By default the runner is deliberately verbose, narrating progress as it happens:
a startup header (version, task file, cwd, limits), the resolved
execution plan, a line as each task starts, and a line as each task finishes with
its duration and overall progress (2/5 complete). Failures, retries, and blocks
are always reported, along with a closing summary and total elapsed time.
Verbosity flags:
--verbose, -v: add per-step traces — the OpenCode session id and the success-check result for every task--quiet, -q: show only failures, blocks, and the final summary--timestamps: prefix each line with a wall-clock time--color/--no-color: force or disable ANSI color (auto-detected from the TTY otherwise; theNO_COLORandFORCE_COLORenvironment variables are honored)
--json emits every event — including the enriched fields above — as one JSON
object per line, regardless of verbosity, which is the format to pipe into log
aggregators or jq. --dry-run always prints a bare, uncolored id list so it
stays easy to pipe.
Success Checks
default succeeds unconditionally.
file_exists succeeds when the configured path exists relative to the task cwd.
result_json succeeds when the configured JSON file parses and contains "status": "ok".
Commits
After a task passes its success check, the orchestrator stages everything with
git add -A and commits it in the task's cwd with the message feat: <id>. A
clean working tree (nothing to commit) is a quiet no-op, and a commit that fails
outright is reported as a warning but never flips the task back to failed — the
task already succeeded. Pass --no-commit to skip this stage entirely.
Recovery
On startup, any task persisted as running is reset to pending, annotated in recoveryNotes, and retried. The attempts counter is preserved, so a task that crashed mid-run still counts that attempt.
Scripts
npm test
npm run test:watch
npm run typecheck
npm run build
npm run test:integrationIntegration tests are excluded by default. Run them with:
RUN_INTEGRATION=1 npm run test:integrationOptional integration environment variables:
OPENCODE_BIN: OpenCode executable, defaultopencodeOPENCODE_PORT: server port, default4096
Architecture
The orchestrator accepts its dependencies as arguments:
client: OpenCode client seamfs: filesystem seamclock: timer seam for timeout testslog: human or JSON event logger
This keeps most behavior covered by fast unit tests using a fake client and in-memory filesystem.
