@endyai/atq 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +109 -0
  2. package/cli.js +35 -0
  3. package/index.js +69 -0
  4. package/package.json +14 -0
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # atq
2
+
3
+ A task queue for agentic workloads.
4
+
5
+ ## The problem
6
+
7
+ You have a large batch of tasks that need to be processed using an agent (language, reasoning, tool use tasks).
8
+
9
+ Running these through a single long-lived agent doesn't work:
10
+
11
+ - **Context bloat.** The agent accumulates results from previous tasks. Gets slower, more expensive, less focused.
12
+ - **Fragile batching.** One failure midway can stall everything. Tracking what succeeded is painful.
13
+ - **No concurrency.** Processing items one at a time when each task is independent is just slow.
14
+
15
+ ## The approach
16
+
17
+ Each task gets its own fresh agent with a clean context. A pool of agents process items concurrently — one finishes, the next starts immediately.
18
+
19
+ - **Fresh context per task.** Each agent only sees the item it's working on. Better focus, lower cost, no cross-contamination.
20
+ - **Concurrent by default.** Control the pool size with `concurrency`.
21
+ - **Stream results.** Results stream to stdout as agents complete.
22
+
23
+ ```mermaid
24
+ graph LR
25
+ stdin["stdin (JSONL)"] --> atq
26
+ atq --> A1[Agent 1]
27
+ atq --> A2[Agent 2]
28
+ atq --> A3[Agent N]
29
+ A1 --> stdout["stdout (JSONL)"]
30
+ A2 --> stdout
31
+ A3 --> stdout
32
+ ```
33
+
34
+ ## CLI
35
+
36
+ ```bash
37
+ cat companies.jsonl | atq -p "Normalize this company name. Return just the name." -c 10 -m claude-sonnet-4-6
38
+ ```
39
+
40
+ Input is piped via stdin (one JSON object per line):
41
+ ```jsonl
42
+ {"name": "Google LLC"}
43
+ {"name": "APPLE INC."}
44
+ {"name": "Meta Platforms, Inc."}
45
+ ```
46
+
47
+ **Output** (stdout, one JSON line per completed item):
48
+ ```jsonl
49
+ {"item":{"name":"Google LLC"},"output":"Google"}
50
+ {"item":{"name":"APPLE INC."},"output":"Apple"}
51
+ {"item":{"name":"Meta Platforms, Inc."},"output":"Meta"}
52
+ ```
53
+
54
+ **Progress** (stderr):
55
+ ```
56
+ [1/3]
57
+ [2/3]
58
+ [3/3]
59
+ ```
60
+
61
+ ### Flags
62
+
63
+ | Short | Long | Required | Default | Description |
64
+ |-------|------|----------|---------|-------------|
65
+ | `-p` | `--prompt` | yes | — | Instructions for the agent |
66
+ | `-c` | `--concurrency` | no | `10` | Max parallel agents |
67
+ | `-m` | `--model` | no | — | Model name |
68
+ | `-k` | `--api-key` | no | — | Anthropic API key |
69
+
70
+ ## SDK
71
+
72
+ ```js
73
+ import { Task } from 'atq';
74
+
75
+ const task = new Task({
76
+ prompt: 'Normalize this company name. Return just the name.',
77
+ concurrency: 10,
78
+ model: 'claude-sonnet-4-6',
79
+ items: [{ name: 'Google LLC' }, { name: 'APPLE INC.' }],
80
+ });
81
+
82
+ for await (const { item, output, progress } of task.run()) {
83
+ console.log(`[${progress.completed}/${progress.total}] ${item.name} → ${output}`);
84
+ }
85
+ ```
86
+
87
+ ### `new Task(options)`
88
+
89
+ | Option | Type | Default | Description |
90
+ |--------|------|---------|-------------|
91
+ | `prompt` | `string` | — | Instructions for the agent |
92
+ | `concurrency` | `number` | `10` | Max parallel agents |
93
+ | `items` | `array` | `[]` | Items to pre-load into the queue |
94
+ | `model` | `string` | — | Model name |
95
+ | `apiKey` | `string` | — | Anthropic API key |
96
+
97
+ ### `.add(item)`
98
+
99
+ Add an item to the queue.
100
+
101
+ ### `.run()`
102
+
103
+ Async generator. Each yield: `{ item, output, progress: { completed, total } }`
104
+
105
+ ## Install
106
+
107
+ ```
108
+ npm install atq
109
+ ```
package/cli.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { parseArgs } from 'node:util';
5
+ import { Task } from './index.js';
6
+
7
+ const { values } = parseArgs({
8
+ options: {
9
+ prompt: { type: 'string', short: 'p' },
10
+ concurrency: { type: 'string', short: 'c' },
11
+ model: { type: 'string', short: 'm' },
12
+ 'api-key': { type: 'string', short: 'k' },
13
+ },
14
+ });
15
+
16
+ if (!values.prompt) {
17
+ console.error('Usage: cat items.jsonl | atq -p "..." [-c 10] [-m model] [-k api-key]');
18
+ process.exit(1);
19
+ }
20
+
21
+ const lines = readFileSync('/dev/stdin', 'utf8').trim().split('\n');
22
+ const items = lines.map(line => JSON.parse(line));
23
+
24
+ const task = new Task({
25
+ prompt: values.prompt,
26
+ concurrency: values.concurrency ? parseInt(values.concurrency) : 10,
27
+ model: values.model,
28
+ apiKey: values['api-key'],
29
+ items,
30
+ });
31
+
32
+ for await (const { item, output, progress } of task.run()) {
33
+ process.stderr.write(`[${progress.completed}/${progress.total}]\n`);
34
+ console.log(JSON.stringify({ item, output }));
35
+ }
package/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { query } from '@anthropic-ai/claude-agent-sdk';
2
+
3
+ export class Task {
4
+ constructor({ prompt, concurrency = 10, items = [], model, apiKey }) {
5
+ this.prompt = prompt;
6
+ this.concurrency = concurrency;
7
+ this.model = model;
8
+ this.apiKey = apiKey;
9
+ this.items = [...items];
10
+ }
11
+
12
+ add(item) {
13
+ this.items.push(item);
14
+ }
15
+
16
+ async *run() {
17
+ const total = this.items.length;
18
+ if (total === 0) return;
19
+
20
+ const prompt = this.prompt || '';
21
+
22
+ let completed = 0;
23
+ let next = 0;
24
+ let running = 0;
25
+
26
+ const buffer = [];
27
+ let waiting = null;
28
+
29
+ const push = (result) => {
30
+ if (waiting) { const r = waiting; waiting = null; r(result); }
31
+ else buffer.push(result);
32
+ };
33
+
34
+ const pull = () => {
35
+ if (buffer.length > 0) return Promise.resolve(buffer.shift());
36
+ return new Promise(r => { waiting = r; });
37
+ };
38
+
39
+ const exec = async (item) => {
40
+ let result = '';
41
+ const opts = { systemPrompt: prompt };
42
+ if (this.model) opts.model = this.model;
43
+ if (this.apiKey) opts.apiKey = this.apiKey;
44
+ for await (const msg of query(JSON.stringify(item), opts)) {
45
+ if (msg.type === 'text') result += msg.content;
46
+ }
47
+ return result.trim();
48
+ };
49
+
50
+ const fill = () => {
51
+ while (next < total && running < this.concurrency) {
52
+ const item = this.items[next++];
53
+ running++;
54
+ exec(item).then(raw => {
55
+ running--;
56
+ completed++;
57
+ push({ item, output: raw, progress: { completed, total } });
58
+ fill();
59
+ });
60
+ }
61
+ };
62
+
63
+ fill();
64
+
65
+ while (completed < total || buffer.length > 0) {
66
+ yield await pull();
67
+ }
68
+ }
69
+ }
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@endyai/atq",
3
+ "version": "0.0.1",
4
+ "description": "A task queue for agentic workloads",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "exports": "./index.js",
8
+ "bin": {
9
+ "atq": "./cli.js"
10
+ },
11
+ "dependencies": {
12
+ "@anthropic-ai/claude-agent-sdk": "^0.2.81"
13
+ }
14
+ }