Menu path: Dashboard > Tasks > Local

Local workers (CLI)

Overview

A local worker is a function in a file on your PC. The 3Min API CLI (npm package @3minapi/cli) runs on that PC, fetches the tasks your AI sends, runs the function with each task's input and reports the outcome. The AI and your PC never talk directly — 3Min API sits between them.

  • No public URL, no open port. The CLI only makes outgoing requests
  • What the function reads stays on your PC, except what it returns
  • One file = one function = one worker

For a server at a public URL, see Tasks (public workers).

Term Meaning
Host A PC running the CLI. Add one per PC on Dashboard → Tasks → Local
Host token tm_host_…. Logs the CLI on that PC in. Shown once
Project folder ~/3minapi by default: 3minapi.json, workers/, data.db, .gitignore. One per host
data.db SQLite file in the project. Every result is saved here; your workers can keep their own data
Results reader A built-in local worker the CLI registers so your AI can read results saved in data.db
Kind kind in worker_list How a task reaches it On Free API calls
Public worker "public" 3Min API POSTs it to your URL 1 Deliveries count 0
Local worker "local" The CLI fetches it (live signal by default, or polling) Unlimited Live: no calls while idle. Poll: every fetch counts 1, even when empty

Install and update

Commands run in a terminal on your PC — Terminal or PowerShell on Windows (Start menu), Terminal on macOS (Applications → Utilities).

  1. Node.js 24.12 or newer. node -v shows the version. If it is missing or older, install the LTS from nodejs.org (or winget install OpenJS.NodeJS.LTS on Windows), then open a new terminal
  2. The CLI:
npm install -g @3minapi/cli
3minapi --version
  • Windows PowerShell may refuse with "running scripts is disabled on this system". Type 3minapi.cmd instead of 3minapi — it works without changing anything. If you would rather allow scripts for your own account, Set-ExecutionPolicy -Scope CurrentUser RemoteSigned does that (the setting stays until you change it back)
  • npx @3minapi/cli <command> runs it without installing
  • Update with the same command: npm install -g @3minapi/cli
  • 3minapi doctor checks Node.js, the config, the token, the network and proxy settings, and says what to fix. Behind a proxy, set NODE_USE_ENV_PROXY=1

Quick start

The same order as the setup card on the Local tab.

npm install -g @3minapi/cli
3minapi init
3minapi worker new hello
3minapi worker run hello --input '{"message":"hi"}'
3minapi login
3minapi start
  1. 3minapi init creates the project at ~/3minapi (3minapi init <folder> to choose). Commands then work from any folder
  2. 3minapi worker new hello creates workers/hello.mjs from a template. Edit the description first, then the handler
  3. 3minapi worker run hello --input '…' runs it once on your PC — no task, no API call. Repeat until it does the right thing
  4. Dashboard → Tasks → Local → Add host, copy the token, then 3minapi login and paste it at the prompt
  5. 3minapi start registers your worker files as local workers and runs their tasks. Your AI now sees them in worker_list with kind: "local"

start exits right away if there is no worker file.

Commands

All commands work from any folder. --json gives machine-readable output; 3minapi help <command> shows every flag.

Host

Command What it does
3minapi init [folder] Creates the host's project. Safe to run again. Without a terminal it needs --yes; --move moves it
3minapi login Saves the host token (masked prompt). --with-token reads it from stdin for scripts
3minapi logout Removes the saved token from this PC only
3minapi start Registers the worker files and runs their tasks until stopped. --mode poll fetches on an interval instead of the default live mode. Ctrl+C stops it
3minapi start --detach The same in the background. It does not start again after a reboot
3minapi reload Makes the running start read the worker files again and register them. Running tasks keep going. 1 call
3minapi stop Stops this PC's start, wherever it was started
3minapi status Whether start is running, local workers, repeating tasks, quota left. 1 call
3minapi logs [-f] The CLI's own log — fetches, stops, errors
3minapi doctor Environment check. No token sent, no quota used
3minapi paths Absolute paths of the project files and the CLI's own folders

Workers

Command What it does
3minapi worker new <name> [--ts] Creates workers/<name>.mjs (.mts with --ts). Registers it at once if start is running (--no-reload skips)
3minapi worker run <name> --input '<json>' Runs it once locally. --input-file <file> avoids shell quoting problems on Windows
3minapi worker list The project's workers, each checked to load. No API call
3minapi worker logs <name> [-f] One worker's log: ctx.log, console output, errors with stack traces
3minapi worker delete <name> Deletes the local worker and its task history from 3Min API, then its file. Cannot be undone

Results and history

Command What it does
3minapi worker results list Saved results, newest first. --worker, --since, -n
3minapi worker results show <task_id> One saved result in full. --run <n> for a run of a repeating task
3minapi worker results runs <task_id> The runs of a repeating task
3minapi worker results describe <task_id> The paths one result has. --worker <name> groups a worker's results by shape
3minapi worker results query "<SQL>" Read-only SQL over saved results. No API call
3minapi worker results prune --before <date> Deletes saved results older than that date from data.db
3minapi tasks prune --before <date> Deletes this host's record of tasks finished before that date. Recent and unfinished ones stay
3minapi dashboard A read-only page in your browser: runs, results, workers, logs. No login, no API call
  • dashboard --detach keeps it running in the background; dashboard url prints the link again; dashboard stop ends it
  • dashboard --lan also serves other devices on your network over plain HTTP. Use it only on a network you trust
  • Destructive commands and --lan ask to confirm. Without a terminal (or with --json) they need --yes

Writing a worker

One file in workers/ is one worker, named after the file.

  • Name: lowercase letters, digits, _ and -, up to 50 characters, not starting with _, not a Windows device name (con, aux, …). Unique across all your workers — public ones and those on your other PCs — ignoring case
  • .mjs (default) or .mts/.ts — TypeScript runs without a build step (no enum or namespace)
// workers/rename_photos.mjs
export const description =
	'Renames the photos in a folder by the date taken. input: { folder: string }';
export const timeoutMs = 10 * 60_000; // optional — default 60 seconds, at most 23 hours

export async function handler(input, ctx) {
	await ctx.report('Scanning the folder'); // progress — task_get shows it
	if (ctx.signal.aborted) return; // cancelled, or about to time out
	// … the work …
	return { renamed: 42 }; // → completed; this is the result
	// throw new Error('Folder not found')  // → failed; the message is the error
}
Export Rule
description Required. What the AI sees in worker_list, up to 500 characters. Put the input shape here
timeoutMs Optional. Default 60 seconds, at most 23 hours
inputSchema Optional. A small JSON Schema subset (type, properties, required, enum, items), checked on your PC only
handler Required. async (input, ctx) => result
ctx What it is
ctx.report(message) Progress message, cut at 256 characters. 1 call each
ctx.log(message) Writes to the worker log only
ctx.env System environment + the project's .env (.env wins). Variables starting with THREEMINAPI_ are removed
ctx.signal AbortSignal fired on cancellation or timeout
How the handler ends Task becomes
return value completed. The value must be JSON
throw failed. The message (with cause), tokens masked, cut at 4,000 characters. The stack stays in the local log
Timeout failed (timeout)
Cancelled ctx.signal fires; the run is ended 2 seconds later. Its return value is not sent
  • Apply changes: edits to a handler's code and to .env apply from the next task. Adding or renaming a file, or changing a description, needs 3minapi reload (or the next start); worker new reloads by itself
  • Removing a file does not remove the worker from 3Min API — its tasks fail. Use 3minapi worker delete <name>
  • Secrets (your own API keys) go in the project's .env, which .gitignore already covers. Save it as UTF-8 (UTF-16 is rejected)
  • The same task can arrive twice (at-least-once). A worker with side effects should tolerate a repeat
  • A result whose top level has a _3minapi key fails with reserved_result_key
  • Your worker can keep its own data in data.db: open it with node:sqlite at new URL('../data.db', import.meta.url). Tables starting with _3minapi_ are the CLI's — read only

Running from your AI

Once start is running, ask your AI as usual — no tool names needed.

  • "Rename the photos in D:\Photos\2026 by date"
  • "Check the BTC price every minute, 10 times"
  • "Stop the price check"

The AI uses worker_list → task_create → task_get. A task waits on 3Min API until the CLI fetches it — a running CLI in live mode (the default) is signalled and fetches it, one in poll mode at its next check. task_get shows delivery: "pending" until then, "delivered" after.

Repeating tasks

For local workers only, task_create takes repeat to run the same input on a fixed interval — one task, one task id for every run.

{
	"worker": "price-feed",
	"input": { "symbol": "BTC" },
	"repeat": { "interval_seconds": 60, "max_runs": 10 }
}
Field / rule Value
interval_seconds 10 to 604,800 (7 days). A run still going when the next is due skips that one
max_runs Optional. Leave it out and it repeats until cancelled — stop it by asking the AI to cancel
Status Stays working; repeat shows runs, failed and last_run_at. completed after the last run, with a summary
Failures A failed run does not stop it. 5 failed runs in a row → failed (repeat_failed)
Cost Each run is 1 call, besides what the CLI spends to receive tasks. Every minute without an end ≈ 43,200 calls a month
Changing it Cancel and create a new one
While the CLI is off Paused. No run reported for 24 hours plus one interval → failed
Public worker Not allowed — repeatRequiresLocalWorker

Each run's result is saved on your PC as its own row (run_no 1, 2, …).

Reading results

Every result is saved in data.db on your PC. Results up to about 100 KB are also sent to 3Min API and reach the AI through task_get. A larger one is not sent: the task completes with a substitute in result._3minapi (stored_locally: true, task id, size, a shape summary and the reader name).

AI — through the host's results reader, a built-in local worker named _results_ + 8 characters of the host id. worker_list lists it as results_reader of that host in hosts, not among the workers. It computes on your PC and sends back only the answer. The task_result_* tools below hand each step to it and pick the PC from the task_id or worker given, so the AI does not need its name.

Order Tool Use
1 task_result_describe (task_id or worker) Learn the shape first. A path a result lacks reads as NULL, so mixed shapes give wrong sums
2 task_result_query (sql, worker) Read-only SQL over the view results. Aggregate and return only the answer
3 task_result_read (task_id, path, offset) Raw pieces of about 90 KB, only when the raw data itself is needed
  • Each reader task costs at least 4 calls and waits until the CLI fetches it
  • The reader is registered when start runs with at least one worker file

You — 3minapi worker results list/show/describe/query or 3minapi dashboard. data.db is never cleaned up automatically; use worker results prune. It may contain personal data.

Letting an AI run the CLI

Some AI clients can run commands on your PC once you allow it — coding agents such as Claude Code or Codex, and some desktop apps. Others, such as ChatGPT on the web, cannot: run the command yourself and paste back what it printed.

The AI can The AI should not touch
Edit workers/*.mjs (description, handler) and 3minapi.json The host token — never in a chat or on a command line (3minapi login prompts)
Run worker new, worker run, reload, status, doctor, logs, worker results … The CLI's own folders (credentials.json, control.db) — see 3minapi paths
Run init <folder> or init --yes after asking where Tables starting with _3minapi_ in data.db
  • Without a terminal, destructive commands need --yes, so --yes is the only confirmation. Confirm with the user before worker delete --yes — it deletes the worker and its task history
  • --json gives output an AI can parse. Errors come as { "error": { "kind", "message", "exitCode", "code"?, "resetAt"? } }. resetAt comes with a quota stop when the reset time is known

Hosts and tokens

Situation What to do
Several PCs Add one host per PC. One PC = one host = one project. Worker names must differ across PCs
Token lost or leaked Rotate it on the host page. The old one stops at once, and a CLI using it stops
Moving to another PC Rotate the token → copy the project folder → 3minapi login with the new token → 3minapi start. Workers and history stay
Deleting a host Deletes its local workers and their task history. Use it only to start clean; a running CLI stops
Moving the folder 3minapi stop → move it → run a command inside it, or 3minapi init <folder> --move

The host page shows how the CLI receives tasks (live or polling), when it last connected or checked in, the check interval (polling only), concurrency, version and platform — facts reported by the CLI, not a current status.

Limits and quota

Item Value
Local workers Unlimited on every plan
Receiving (live, default) Keeping the connection costs no calls. 1 per connect or reconnect plus 1 catch-up fetch after it, and 1 per fetch when a task or a cancellation is signalled. The connection drops on the network side a few times a day and reconnects on its own, so even with no work it uses some calls — 6 drops a day ≈ 12 calls a day ≈ 360 a month
Receiving (polling) Every fetch is 1 call, even when nothing is waiting. Default every 60 seconds ≈ 1,440 a day ≈ 43,200 a month
Other calls 1 per receipt confirmation, 1 per report (ctx.report and the final result), 1 per repeat run, 1 per reload/status
Concurrent runs concurrency, default 4 (1–50). At most concurrency tasks per fetch
Deadline Not picked up within 24 hours of creation → failed (checked hourly, up to about 25 hours)
Task history At least 60 days on paid plans. On Free, local workers and their runs are deleted 7 days after the worker was registered; reload or the next start registers it again
Quota used up The CLI stops itself (exit code 5) and does not restart. Reports are never blocked
Result sent to 3Min API About 100 KB; larger results stay on your PC (substitute sent)
Local logs Kept 30 days / 300 MB by default (logRetentionDays, logMaxBytes)

To spend fewer calls, keep the default live mode. If your network needs polling, raise pollIntervalMs in 3minapi.json (tasks are picked up later) and restart.

While all concurrency slots are busy, the CLI takes no new tasks — they stay pending (cancellations still arrive). 3minapi start and 3minapi status show the resulting throughput.

Status and troubleshooting

Is it running? Check with 3minapi status on that PC. CLI receiving on the web, and the last check or connection worker_list gives the AI, are facts about the past, not a current status.

Symptom Cause and fix
Task stays working with delivery: "pending" (Waiting for PC on the web) The CLI has not fetched it. Run 3minapi start on that PC
3minapi start says it could not open the live connection A company proxy or firewall blocks it. 3minapi doctor checks; start with --mode poll (or set receiveMode: "poll") to fetch on an interval instead
Not picked up within 24 hours — the 3Min API CLI on the user's PC did not fetch it. The CLI was off for 24 hours. Start it and send the task again
No run was reported for 24 hours — the 3Min API CLI on the user's PC stopped running it. A repeating task lost its CLI. Start it and create the task again
Worker did not respond within 24 hours. A local task the CLI took but never finished reporting — the PC stayed off after a crash. It is not run again
Failed with host_stopped You stopped the CLI (Ctrl+C or stop) while it ran. A repeating task only loses that run
Failed with host_restarted The PC or CLI died mid-task. It may or may not have run, and it is not run again
AI sends input in an old shape You changed a description without 3minapi reload
Worker missing from worker_list New file without reload, a load error (3minapi worker list), or Free 7-day deletion (reload)
CLI refuses a name with worker_name_taken Another worker (public, or on another PC) has that name. Rename the file
CLI exited with code 4 or 5 4: token invalid or no plan — login again. 5: monthly quota used up

Exit codes

Code Meaning
0 OK
1 Failure
2 Usage error (including a missing --yes)
3 Environment or config error
4 Stopped: token invalid, or the account has no plan (Free counts as a plan)
5 Stopped: monthly quota used up
130 Interrupted (Ctrl+C)

After 4 or 5 nothing restarts on its own.

error.data.reason of a failed local task

Reason Meaning
handler_error The handler threw
timeout Ran past timeoutMs
invalid_input Did not match inputSchema — not run
invalid_worker The file exists but cannot be used — no handler, a duplicate name, or an import error
worker_not_found No such file on that PC
unsupported_syntax TypeScript the CLI cannot run without a build (e.g. enum)
result_too_large Over about 100 KB and it could not be saved on the PC
reserved_result_key The result has a top-level _3minapi key
host_stopped The CLI was stopped mid-run
host_restarted The PC or CLI died mid-run; reported on the next start
repeat_failed A repeating task failed 5 runs in a row

Protocol summary

How the CLI and 3Min API work together, for reference.

Delivery

  • Pull: in live mode the CLI keeps a connection open and asks 3Min API for waiting tasks when it is signalled (and once after each connect); in poll mode it asks every pollIntervalMs. Either way it takes only as many as it can run now
  • Ack: it confirms receipt right away (delivery becomes "delivered"). Unconfirmed for 60 seconds, the task goes out again — up to 6 times, then failed
  • At-least-once: the same task can arrive twice
  • Cancel: signalled in live mode, or announced on the next fetch in poll mode → ctx.signal. The task stays cancelled
  • Reports: progress and the final result go back with the host token. A report that fails on the network is retried for up to 24 hours

Sizes

Item Limit
input 100 KB
Result sent About 100 KB (100 KiB minus a 1 KiB envelope)
ctx.report 256 characters
Error message 4,000 characters
Worker description 500 characters
Worker name 50 characters

3minapi.json (precedence: flag > environment variable > project file > default)

Key Default Range Environment variable
workersDir workers Inside the project —
receiveMode live live or poll THREEMINAPI_RECEIVE_MODE
pollIntervalMs 60000 5000–3600000 (poll mode only) THREEMINAPI_POLL_INTERVAL_MS
concurrency 4 1–50 THREEMINAPI_CONCURRENCY
localServerPort 3180 1024–65535 THREEMINAPI_LOCAL_SERVER_PORT
logRetentionDays 30 1–365 THREEMINAPI_LOG_RETENTION_DAYS
logMaxBytes 300 MB 10 MB–1 GB THREEMINAPI_LOG_MAX_BYTES

Changing receiveMode, pollIntervalMs or concurrency needs a restart. Unknown keys only warn.

Folders — 3minapi paths prints them.

OS Token and settings Control DB and logs
macOS ~/Library/Application Support/3minapi ~/Library/Application Support/3minapi, ~/Library/Logs/3minapi
Windows %APPDATA%\3minapi %LOCALAPPDATA%\3minapi
Linux $XDG_CONFIG_HOME/3minapi (~/.config) $XDG_STATE_HOME/3minapi (~/.local/state)

The token lives in these folders, not in the project, so copying the project does not copy the token.

Privacy

  • A worker's result and error reach the AI conversation as is. Return only what is needed
  • data.db keeps every result on your PC, including any personal data in it, until you prune it