Install from GitHub
Use Node.js 22.13 or newer. Clone the open source repository, then link the CLI locally:
git clone https://github.com/AssistOS-AI/Ploinky-Worker.git
cd Ploinky-Worker
npm link
pworkerpworker opens the interactive menu and starts the local proxy on first use. If you do not want to link the command, run node bin/pworker.mjs from the repository. Run pworker --help to see the complete CLI reference without starting the proxy.
Open source edition
Ploinky Workers is the open source part of more sophisticated internal Axiologic tooling. The open source edition is usable on its own. Axiologic can customize workflows, provider setups, batching, and local inference for particular use cases; more public features will be added gradually.
Connect a provider and configure a tier
- Choose Log in / connect provider in the menu.
- Enter a key for a built-in provider, add an OpenAI-compatible endpoint with an optional key, or start a configured local model.
- After the live model catalog responds, choose Configure tier, choose the tier, and select its primary model from one searchable list across connected providers.
- The selected model becomes primary; prior usable entries remain fallbacks.
- Confirm the tier mapping.
Each model row shows its provider, published input/output USD per million tokens where available, and plan request cost where reported. Use ↑/↓ and Enter to navigate. Esc or Back / Cancel leaves a step. The list excludes models known to require a separate credit balance and models that do not produce text. Tier setup is shown only when a provider is connected. Credentials are stored after a successful connection check; a tier mapping is stored only after its final confirmation.
Auto-configure price ladder first lists each connected provider with its usable-text-model and published-price counts. Choose one provider for the proposal, or explicitly select the separate all-provider mixing option. It creates a reviewable proposal from cheapest to most costly and excludes models known to need a separate credit balance. The proposal then requires two explicit confirmations, both defaulting to keeping the current tiers. OpenRouter loads its current top 60 popular models, so its picker remains manageable while keeping the selection dynamic.
Built-in providers include openference, openai, zai, deepseek, grok, and openrouter, alongside configured local models. The logical tiers are nano, micro, tiny, small, medium, good, and best; supertiny is an alias.
pworker provider myapi --endpoint https://example.com/v1 --key KEY --rpm 30
pworker tier small --provider myapi --model MODEL_ID --batch
pworker tier small --provider backup --model BACKUP_MODEL --add
pworker models
pworker models start LOCAL_NAME
pworker models stop LOCAL_NAMEThe provider and tier commands validate the live model list. They reject a model known to require a credit balance and a model that cannot return text. --add appends a fallback to a tier; without it, tier replaces the chain. --rpm is the provider's maximum requests per minute. Passing --key on a command line may leave it in shell history; the interactive key field is masked.
When an OpenAI-compatible provider does not publish prices in its model catalog, add them to that provider's modelPricing map in ~/.pworker/config.json. Prices use USD per million text tokens:
{
"providers": {
"myapi": {
"modelPricing": {
"current-model": { "inputUsdPerM": 0.25, "outputUsdPerM": 1.00 }
}
}
}
}The live provider catalog remains the source of selectable model IDs; this map only adds display prices.
Run and queue tasks
TASK can be a .json phase-map file (or a static JSON .mjs declaration), a text request, or - to read a request from standard input. --input supplies the start-phase value. JSON objects become named task variables; other values become ${input}. A text request is compiled and cached as a task file.
In the interactive CLI, open New task conversation to submit requests in a text-mode conversation. Every request begins in the background, and the request becomes that task's initial input, so you can submit another request while earlier work runs. Open Current tasks separately to inspect the ten newest saved tasks, their status, current phase, and results. The default working directory is where pworker was started; use pworker DIRECTORY or pworker --cwd DIRECTORY to open the menu for a different directory.
pworker run ./task.json --input '{"input":"hello"}'
pworker run 'Summarize the supplied text' --input 'Meeting notes...'
pworker run - --input 'hello' < request.txt
pworker queue ./task.json --input first --cwd ./project
pworker queue ./task.json --input second --cwd ./project
pworker queue list
pworker flush
pworker queue clearqueue stores work without executing it. flush explicitly sends the accumulated tasks and retains failed entries. Compatible phases can be combined into one model prompt when batching is enabled for their tier. Read the batching rules.
Return an ID and check progress
pworker run ./task.json --input hello --cwd ./project --async
pworker --status
pworker --status TASK_ID
pworker flush --async--async returns immediately with a task ID. --status lists detached tasks; --status TASK_ID shows one task's state, current phase, result, or error. States include queued, compiling, running, waiting, completed, cancelled, and failed. Waiting model phases retain checkpoints; provider cooldowns and pending phases can be recovered after restart. Status remains available after the caller exits. flush --async gives each queued task an ID and runs the group in one detached worker.
Working directory and file methods
The caller supplies the exact directory through --cwd DIR, --current-working-directory DIR, or currentWorkingDirectory in the input object. The task sees this.currentWorkingDirectory. Ploinky Workers does not choose or create a per-task subdirectory.
Phase code may call this.readFile(path), this.writeFile(path, text), this.listFiles(path, recursive), this.moveFile(from, to), this.makeDirectory(path), and this.removeFile(path). Paths are confined to the supplied directory, including after symlink resolution. Writes to .git, .pworker, and .agents are refused. File size and total write limits apply.
Proxy, storage, and monitoring
pworker start
pworker stats --since 24h
pworker stats --provider openference --since 7d --by day
pworker stats --json
pworker stop
pworker serve --host 127.0.0.1 --port 18080The proxy is independent of the interactive CLI. It listens on http://127.0.0.1:18080 by default. The proxy exposes chat completions, model lists, health, and statistics. It records request counts, byte totals, cache hits, and provider limits. Identical complete requests to the same effective model can be served from cache.
All model requests of a Pworker home go through its one proxy, so provider limits are applied in one place; a second proxy for the same home refuses to start. Every attempt, retries included, is counted when it is sent. A 429 pauses the provider for its full Retry-After and lowers an adaptive rate that recovers slowly; "pacing": "even" spreads requests evenly over the minute. See rate limits.
pworker stats reads the request log of the home, so it works without a running proxy. It prints one row per provider and model (with the tiers it served) and one per hour, or per day with --by day or a --since beyond two days: attempts sent upstream, successes, 429 responses, 429 responses that came while every configured rate was respected (429<lim, evidence that a provider throttles below its configured limit), other errors, retries, timeouts, cache hits, requests saved by batching, tokens, average queue wait, and the peak and average number of attempts per minute. --provider NAME filters, --json prints the same data as JSON, and --proxy prints the running proxy's raw /stats.
User settings and runtime data live in ~/.pworker/ or PWORKER_HOME. API keys are stored in private files under keys/; task files, jobs, request logs, and response cache have separate directories. PWORKER_PORT, PWORKER_URL, PWORKER_CONFIG, and PWORKER_TOKEN adjust the proxy and client environment.
Use the library
The library collects tasks and sends them through an explicit flush() call. The same phase format and tier configuration apply.
import { Pworker } from 'pworker';
import { createPworkerClient } from 'pworker/client';
const worker = new Pworker({
client: createPworkerClient({ purpose: 'my-batch' }),
config: { batching: { small: { enabled: true } } }
});
worker.enqueue(task, { input: 'one' });
worker.enqueue(task, { input: 'two' });
const results = await worker.flush();Understand the task format or check when requests can be combined.