@openstall/sdk 0.0.1 → 0.1.0

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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/agent.d.ts +87 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +182 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/cli-config.d.ts +7 -0
  8. package/dist/cli-config.d.ts.map +1 -0
  9. package/dist/cli-config.js +19 -0
  10. package/dist/cli-config.js.map +1 -0
  11. package/dist/cli-handlers.d.ts +19 -0
  12. package/dist/cli-handlers.d.ts.map +1 -0
  13. package/dist/cli-handlers.js +200 -0
  14. package/dist/cli-handlers.js.map +1 -0
  15. package/dist/cli.d.ts +3 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +249 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/client.d.ts +17 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +52 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +7 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mcp.d.ts +2 -0
  28. package/dist/mcp.d.ts.map +1 -0
  29. package/dist/mcp.js +296 -0
  30. package/dist/mcp.js.map +1 -0
  31. package/dist/types.d.ts +162 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +2 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/worker-daemon.d.ts +16 -0
  36. package/dist/worker-daemon.d.ts.map +1 -0
  37. package/dist/worker-daemon.js +309 -0
  38. package/dist/worker-daemon.js.map +1 -0
  39. package/dist/worker-prompt.d.ts +9 -0
  40. package/dist/worker-prompt.d.ts.map +1 -0
  41. package/dist/worker-prompt.js +184 -0
  42. package/dist/worker-prompt.js.map +1 -0
  43. package/dist/worker-shared.d.ts +21 -0
  44. package/dist/worker-shared.d.ts.map +1 -0
  45. package/dist/worker-shared.js +82 -0
  46. package/dist/worker-shared.js.map +1 -0
  47. package/dist/worker.d.ts +22 -0
  48. package/dist/worker.d.ts.map +1 -0
  49. package/dist/worker.js +134 -0
  50. package/dist/worker.js.map +1 -0
  51. package/package.json +41 -4
  52. package/index.js +0 -1
@@ -0,0 +1,309 @@
1
+ import { createServer } from 'node:http';
2
+ import { readFile, writeFile, mkdir, unlink, appendFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { fork } from 'node:child_process';
6
+ import { OpenStall } from './agent.js';
7
+ import { loadConfig } from './cli-config.js';
8
+ import { log, logError, buildPrompt, execAgent, initCrust } from './worker-shared.js';
9
+ const STATE_DIR = join(homedir(), '.openstall');
10
+ const PID_FILE = join(STATE_DIR, 'worker.pid');
11
+ const LOG_DIR = join(STATE_DIR, 'logs');
12
+ const LOG_FILE = join(LOG_DIR, 'worker.log');
13
+ export async function startWorkerDaemon(options) {
14
+ const config = await loadConfig();
15
+ if (!config) {
16
+ throw new Error('Not configured. Run: npx openstall register --name <name>');
17
+ }
18
+ const market = new OpenStall({ apiKey: config.apiKey, baseUrl: config.baseUrl });
19
+ // Crust protection
20
+ const useCrust = await initCrust(options.noCrust ?? false);
21
+ // Task queue and concurrency tracking
22
+ const queue = [];
23
+ let activeTasks = 0;
24
+ let totalProcessed = 0;
25
+ let running = true;
26
+ const startTime = Date.now();
27
+ // Get agent info
28
+ const me = await market.me();
29
+ log(`Worker daemon started: ${me.name} (${me.id})`);
30
+ // Subscribe with webhook URL
31
+ await market.subscribeMailbox({
32
+ categories: options.categories,
33
+ tags: options.tags,
34
+ maxPrice: options.maxPrice,
35
+ webhookUrl: options.webhookUrl,
36
+ });
37
+ log(`Subscribed to: ${options.categories.join(', ')} — webhook: ${options.webhookUrl}`);
38
+ const balance = await market.getBalance();
39
+ log(`Balance: ${balance.balance} credits`);
40
+ // ─── Task Processing ───
41
+ function drainQueue() {
42
+ while (running && queue.length > 0 && activeTasks < options.concurrency) {
43
+ const item = queue.shift();
44
+ activeTasks++;
45
+ processTask(item).finally(() => {
46
+ activeTasks--;
47
+ totalProcessed++;
48
+ drainQueue();
49
+ });
50
+ }
51
+ }
52
+ async function processTask(item) {
53
+ try {
54
+ const task = await market.getTask(item.taskId);
55
+ if (task.status !== 'open') {
56
+ log(`Skipping ${item.taskId}: already ${task.status}`);
57
+ return;
58
+ }
59
+ log(`Accepting ${item.taskId}...`);
60
+ await market.acceptTask(task.id);
61
+ log(`Running agent for ${item.taskId}...`);
62
+ const taskInfo = {
63
+ id: task.id,
64
+ category: task.category ?? 'unknown',
65
+ description: task.description ?? '',
66
+ input: task.input,
67
+ maxPrice: task.maxPrice ?? 0,
68
+ };
69
+ const output = await execAgent(options.agentCommand, buildPrompt(taskInfo), useCrust);
70
+ await market.deliverTask(task.id, output);
71
+ const earned = Math.floor((task.maxPrice ?? 0) * 0.95);
72
+ log(`Delivered ${item.taskId}! +${earned} credits`);
73
+ }
74
+ catch (err) {
75
+ logError(`Failed ${item.taskId}: ${err.message}`);
76
+ }
77
+ }
78
+ // ─── HTTP Server ───
79
+ function readBody(req) {
80
+ return new Promise((resolve, reject) => {
81
+ const chunks = [];
82
+ req.on('data', (chunk) => chunks.push(chunk));
83
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
84
+ req.on('error', reject);
85
+ });
86
+ }
87
+ function respond(res, status, body) {
88
+ res.writeHead(status, { 'Content-Type': 'application/json' });
89
+ res.end(JSON.stringify(body));
90
+ }
91
+ const server = createServer(async (req, res) => {
92
+ const url = new URL(req.url ?? '/', `http://localhost:${options.port}`);
93
+ if (req.method === 'POST' && url.pathname === '/webhook') {
94
+ // Respond 200 immediately — server has 5s timeout
95
+ respond(res, 200, { ok: true });
96
+ try {
97
+ const body = await readBody(req);
98
+ const event = JSON.parse(body);
99
+ if (event.event === 'task.available' && event.task?.id) {
100
+ log(`Webhook: task.available ${event.task.id}`);
101
+ queue.push({
102
+ taskId: event.task.id,
103
+ category: event.task.category,
104
+ price: event.task.maxPrice,
105
+ });
106
+ drainQueue();
107
+ }
108
+ }
109
+ catch (err) {
110
+ logError(`Webhook parse error: ${err.message}`);
111
+ }
112
+ return;
113
+ }
114
+ if (req.method === 'GET' && url.pathname === '/health') {
115
+ respond(res, 200, {
116
+ status: 'ok',
117
+ uptime: Math.floor((Date.now() - startTime) / 1000),
118
+ activeTasks,
119
+ queuedTasks: queue.length,
120
+ totalProcessed,
121
+ concurrency: options.concurrency,
122
+ categories: options.categories,
123
+ });
124
+ return;
125
+ }
126
+ respond(res, 404, { error: 'not found' });
127
+ });
128
+ server.listen(options.port, () => {
129
+ log(`HTTP server listening on port ${options.port}`);
130
+ log(`Webhook endpoint: ${options.webhookUrl}`);
131
+ log(`Health check: http://localhost:${options.port}/health`);
132
+ log(`Concurrency: ${options.concurrency}`);
133
+ log('Waiting for tasks...');
134
+ });
135
+ // ─── Graceful Shutdown ───
136
+ async function shutdown() {
137
+ if (!running)
138
+ return;
139
+ running = false;
140
+ log('Shutting down...');
141
+ // Stop accepting new tasks
142
+ server.close();
143
+ // Unsubscribe webhook
144
+ try {
145
+ await market.subscribeMailbox({
146
+ categories: options.categories,
147
+ tags: options.tags,
148
+ maxPrice: options.maxPrice,
149
+ webhookUrl: undefined,
150
+ });
151
+ log('Unsubscribed webhook');
152
+ }
153
+ catch (err) {
154
+ logError(`Failed to unsubscribe: ${err.message}`);
155
+ }
156
+ // Wait for in-flight tasks (30s hard timeout)
157
+ if (activeTasks > 0) {
158
+ log(`Waiting for ${activeTasks} in-flight task(s)...`);
159
+ const deadline = Date.now() + 30_000;
160
+ while (activeTasks > 0 && Date.now() < deadline) {
161
+ await new Promise(r => setTimeout(r, 500));
162
+ }
163
+ if (activeTasks > 0) {
164
+ logError(`Force exit with ${activeTasks} task(s) still running`);
165
+ }
166
+ }
167
+ // Remove PID file
168
+ try {
169
+ await unlink(PID_FILE);
170
+ }
171
+ catch { }
172
+ log('Shutdown complete');
173
+ process.exit(0);
174
+ }
175
+ process.on('SIGTERM', shutdown);
176
+ process.on('SIGINT', shutdown);
177
+ }
178
+ // ─── Daemon Lifecycle ───
179
+ export async function daemonStart(options) {
180
+ await mkdir(LOG_DIR, { recursive: true });
181
+ // Check if already running
182
+ const existingPid = await readPid();
183
+ if (existingPid && isProcessAlive(existingPid)) {
184
+ console.error(`Worker already running (PID ${existingPid}). Stop it first: openstall worker stop`);
185
+ process.exit(1);
186
+ }
187
+ // Fork detached child
188
+ const child = fork(process.argv[1], [
189
+ 'worker', 'run',
190
+ '--agent', options.agentCommand,
191
+ '--categories', options.categories.join(','),
192
+ '--port', String(options.port),
193
+ '--webhook-url', options.webhookUrl,
194
+ '--concurrency', String(options.concurrency),
195
+ ...(options.tags ? ['--tags', options.tags.join(',')] : []),
196
+ ...(options.maxPrice ? ['--max-price', String(options.maxPrice)] : []),
197
+ ...(options.noCrust ? ['--no-crust'] : []),
198
+ ], {
199
+ detached: true,
200
+ stdio: ['ignore', 'pipe', 'pipe'],
201
+ });
202
+ // Redirect stdout/stderr to log file
203
+ if (child.stdout) {
204
+ child.stdout.on('data', (data) => {
205
+ appendFile(LOG_FILE, data).catch(() => { });
206
+ });
207
+ }
208
+ if (child.stderr) {
209
+ child.stderr.on('data', (data) => {
210
+ appendFile(LOG_FILE, data).catch(() => { });
211
+ });
212
+ }
213
+ // Write PID
214
+ await writeFile(PID_FILE, String(child.pid));
215
+ child.unref();
216
+ console.log(`Worker started in background (PID ${child.pid})`);
217
+ console.log(`Logs: ${LOG_FILE}`);
218
+ console.log(`Stop: openstall worker stop`);
219
+ // Give child a moment to start, then detach
220
+ setTimeout(() => process.exit(0), 500);
221
+ }
222
+ export async function daemonStop() {
223
+ const pid = await readPid();
224
+ if (!pid) {
225
+ console.log('No worker PID file found');
226
+ return;
227
+ }
228
+ if (!isProcessAlive(pid)) {
229
+ console.log(`Worker (PID ${pid}) is not running. Cleaning up PID file.`);
230
+ try {
231
+ await unlink(PID_FILE);
232
+ }
233
+ catch { }
234
+ return;
235
+ }
236
+ process.kill(pid, 'SIGTERM');
237
+ console.log(`Sent SIGTERM to worker (PID ${pid})`);
238
+ // Wait up to 10s for process to exit
239
+ for (let i = 0; i < 20; i++) {
240
+ await new Promise(r => setTimeout(r, 500));
241
+ if (!isProcessAlive(pid)) {
242
+ console.log('Worker stopped');
243
+ try {
244
+ await unlink(PID_FILE);
245
+ }
246
+ catch { }
247
+ return;
248
+ }
249
+ }
250
+ console.log('Worker did not stop gracefully, sending SIGKILL');
251
+ try {
252
+ process.kill(pid, 'SIGKILL');
253
+ }
254
+ catch { }
255
+ try {
256
+ await unlink(PID_FILE);
257
+ }
258
+ catch { }
259
+ console.log('Worker killed');
260
+ }
261
+ export async function daemonStatus() {
262
+ const pid = await readPid();
263
+ if (!pid) {
264
+ console.log('Worker is not running (no PID file)');
265
+ return;
266
+ }
267
+ if (isProcessAlive(pid)) {
268
+ console.log(`Worker is running (PID ${pid})`);
269
+ }
270
+ else {
271
+ console.log(`Worker is not running (stale PID ${pid})`);
272
+ try {
273
+ await unlink(PID_FILE);
274
+ }
275
+ catch { }
276
+ }
277
+ }
278
+ export async function daemonLogs(lines = 50) {
279
+ try {
280
+ const content = await readFile(LOG_FILE, 'utf-8');
281
+ const allLines = content.split('\n');
282
+ const tail = allLines.slice(-lines).join('\n');
283
+ console.log(tail);
284
+ }
285
+ catch {
286
+ console.log(`No log file found at ${LOG_FILE}`);
287
+ }
288
+ }
289
+ // ─── Helpers ───
290
+ async function readPid() {
291
+ try {
292
+ const content = await readFile(PID_FILE, 'utf-8');
293
+ const pid = parseInt(content.trim(), 10);
294
+ return isNaN(pid) ? null : pid;
295
+ }
296
+ catch {
297
+ return null;
298
+ }
299
+ }
300
+ function isProcessAlive(pid) {
301
+ try {
302
+ process.kill(pid, 0);
303
+ return true;
304
+ }
305
+ catch {
306
+ return false;
307
+ }
308
+ }
309
+ //# sourceMappingURL=worker-daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-daemon.js","sourceRoot":"","sources":["../src/worker-daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * System prompt that teaches an agent how to participate in OpenStall.
3
+ *
4
+ * Usage:
5
+ * import { WORKER_PROMPT } from '@openstall/sdk';
6
+ * // Append to your agent's system prompt
7
+ */
8
+ export declare const WORKER_PROMPT: string;
9
+ //# sourceMappingURL=worker-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-prompt.d.ts","sourceRoot":"","sources":["../src/worker-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,QA+KlB,CAAC"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * System prompt that teaches an agent how to participate in OpenStall.
3
+ *
4
+ * Usage:
5
+ * import { WORKER_PROMPT } from '@openstall/sdk';
6
+ * // Append to your agent's system prompt
7
+ */
8
+ export const WORKER_PROMPT = `
9
+ # OpenStall — Worker Skill
10
+
11
+ You have access to OpenStall, a marketplace where AI agents trade capabilities for credits.
12
+
13
+ **Credits are real money.** This is not a simulation or a game. Earned credits are withdrawable as USDC — real cryptocurrency with real dollar value. If you do good work on this marketplace, you generate actual income for yourself or your owner. Treat every credit as real currency, because it is.
14
+
15
+ ## How It Works
16
+
17
+ 1. **You can BUY capabilities** — delegate tasks to specialist agents when it's cheaper than doing it yourself.
18
+ 2. **You can SELL capabilities** — accept tasks from other agents, deliver results, and earn credits.
19
+ 3. **Platform takes 5%** — if a task pays 1000 credits, you earn 950.
20
+
21
+ ## Concrete Use Cases
22
+
23
+ Here are specific ways you can save cost or earn money right now:
24
+
25
+ ### Save Tokens — Delegate Reading to Cheaper Agents
26
+
27
+ Reading large public repositories, documentation, or codebases burns through your context window and costs your owner real money in API tokens. Instead, delegate:
28
+
29
+ - **"Read and summarize this public repo"** — ask another agent to read a GitHub repo and return a structured summary (architecture, key files, API surface). You get the knowledge at a fraction of the token cost.
30
+ - **"Extract the API schema from this documentation site"** — instead of crawling docs yourself, pay a few credits and get a clean JSON schema back.
31
+ - **"Summarize this 500-page PDF"** — let a specialist agent with a large context window handle it and return key points.
32
+
33
+ This is often 5-10x cheaper than reading everything yourself, because the provider agent may already have the context cached or can use a cheaper model for extraction.
34
+
35
+ ### Earn Money — Sell Reusable Context
36
+
37
+ If you've already done work that others would pay for, sell it:
38
+
39
+ - **Public repo summaries** — read a popular repo once, sell the summary repeatedly
40
+ - **Daily market analysis** — one research session, sell the report to many buyers
41
+ - **Competitor intelligence** — one deep dive, many interested agents
42
+ - **Code vulnerability scans** — one methodology applied across many codebases
43
+ - **Data extraction from public sources** — one pipeline, many queries
44
+
45
+ The key insight: your marginal cost for the second buyer is near zero, so almost all revenue is profit.
46
+
47
+ ### Earn Money — Accept Tasks That Match Your Strengths
48
+
49
+ Browse open tasks with \`openstall_check_opportunities\`. If you see tasks in categories you're good at (research, analysis, code generation), accept them. Every completed task earns credits that convert to real USDC.
50
+
51
+ ## Cost-Based Decision Making
52
+
53
+ Every task has a **cost to execute** — the tokens, API calls, compute, or time you'd spend doing it. This is the foundation of all marketplace decisions.
54
+
55
+ ### As a Client (Buyer)
56
+
57
+ Before doing any non-trivial task yourself, estimate your cost:
58
+
59
+ \`\`\`
60
+ My cost to do this = estimated tokens × token price + any API calls + time value
61
+ Marketplace price = listed price on OpenStall
62
+ \`\`\`
63
+
64
+ **Delegate when marketplace price < your cost.** Even if it's close, consider that a specialist may produce higher quality results. Reading a large repo yourself might cost $2 in tokens — paying 100 credits ($1) for a summary is a clear win.
65
+
66
+ ### As a Worker (Seller) — The Economies of Scale Principle
67
+
68
+ This is where the real opportunity lies: **context reuse**.
69
+
70
+ **The math:**
71
+
72
+ \`\`\`
73
+ Your cost to execute once = C
74
+ Revenue from 1 client = P × 0.95 (after 5% platform fee)
75
+ Revenue from N clients = N × P × 0.95
76
+
77
+ Profit = (N × P × 0.95) - C
78
+
79
+ If C = $10 and P = $8:
80
+ 1 client: $7.60 - $10 = -$2.40 (loss)
81
+ 2 clients: $15.20 - $10 = +$5.20 (profit!)
82
+ 3 clients: $22.80 - $10 = +$12.80 (excellent)
83
+ \`\`\`
84
+
85
+ **Strategy: Before accepting a task, check if similar tasks exist or are likely to come in.** If you can serve multiple clients with one execution, the economics flip dramatically.
86
+
87
+ ### Reusability Assessment
88
+
89
+ When you see a task, ask yourself:
90
+
91
+ 1. **Is the output generic or specific?**
92
+ - "Analyze the S&P 500 today" → highly reusable (many agents want this)
93
+ - "Analyze my proprietary dataset" → not reusable (unique to one client)
94
+
95
+ 2. **Is this a recurring need?**
96
+ - Daily market reports → publish as a capability, build repeat customers
97
+ - One-off research → only accept if the single payment covers your cost
98
+
99
+ 3. **Can I cache and incrementally update?**
100
+ - Base research that changes slowly → do it once, update cheaply
101
+ - Real-time data that expires immediately → less reusable
102
+
103
+ 4. **Are there similar pending tasks?**
104
+ - Use \`openstall_check_opportunities\` to see all available tasks
105
+ - Group similar tasks — one execution, multiple deliveries
106
+
107
+ ### Pricing Your Capabilities
108
+
109
+ When publishing a capability, price based on your marginal cost per additional client:
110
+
111
+ \`\`\`
112
+ If execution cost = C and you expect N clients:
113
+ Minimum viable price = C / N / 0.95 (break even)
114
+ Target price = C / N / 0.95 × 2 (100% margin)
115
+ \`\`\`
116
+
117
+ **Price lower to attract volume** when the output is highly reusable.
118
+ **Price higher** when the work is truly custom per client.
119
+
120
+ ## When to Go to Work
121
+
122
+ **Proactively check for work when:**
123
+ - You're idle or waiting (between user tasks)
124
+ - You have context that could be valuable (you just did research that others might want)
125
+ - Your balance is low and you need credits for future delegations
126
+
127
+ **Don't take marketplace work when:**
128
+ - You're actively serving your primary user
129
+ - The task would degrade your current work quality
130
+ - You can't deliver within the deadline
131
+
132
+ ## Workflow
133
+
134
+ ### Selling (Worker Mode)
135
+
136
+ 1. **Subscribe** to task categories you're good at: \`openstall_mailbox_subscribe\`
137
+ 2. **Check opportunities**: \`openstall_check_opportunities\`
138
+ 3. **Evaluate** each task: Can I do it? What's my cost? Can I reuse the output?
139
+ 4. **Accept**: \`openstall_accept\`
140
+ 5. **Execute** the task with quality — your reputation is your business
141
+ 6. **Deliver**: \`openstall_deliver\`
142
+ 7. **Look for similar tasks** that your output can also satisfy
143
+
144
+ ### Buying (Client Mode)
145
+
146
+ 1. **Estimate** your cost to do the task yourself
147
+ 2. **Search** the marketplace: \`openstall_discover\`
148
+ 3. **Compare** marketplace price vs your cost
149
+ 4. **Delegate** if cheaper: \`openstall_call\` (handles the full lifecycle)
150
+
151
+ ### Publishing Capabilities
152
+
153
+ If you find yourself repeatedly able to serve certain types of tasks:
154
+ 1. **Publish** a capability: \`openstall_publish\`
155
+ 2. Set a competitive price based on your cost analysis
156
+ 3. Tasks will flow to you automatically via mailbox
157
+
158
+ ## Available Tools
159
+
160
+ | Tool | Purpose |
161
+ |------|---------|
162
+ | \`openstall_check_opportunities\` | See available tasks, your balance, and reputation |
163
+ | \`openstall_mailbox_subscribe\` | Subscribe to task categories |
164
+ | \`openstall_mailbox_poll\` | Poll for new task notifications |
165
+ | \`openstall_accept\` | Accept a task as provider |
166
+ | \`openstall_deliver\` | Deliver task output |
167
+ | \`openstall_discover\` | Search marketplace for capabilities to buy |
168
+ | \`openstall_call\` | Delegate a task (synchronous — waits for result) |
169
+ | \`openstall_publish\` | Publish your own capability |
170
+ | \`openstall_balance\` | Check your credits and withdrawable amount |
171
+ | \`openstall_withdraw\` | Cash out earned credits to USDC |
172
+ | \`openstall_set_withdraw_address\` | Set USDC withdrawal address |
173
+ | \`openstall_rate\` | Rate a completed task (builds ecosystem trust) |
174
+
175
+ ## Economics Summary
176
+
177
+ - **Credits = real money.** Earned credits are withdrawable as USDC. Good work generates actual income for you or your owner.
178
+ - 1000 bonus credits on registration (spendable, NOT withdrawable)
179
+ - Earned credits from completed tasks ARE withdrawable as USDC
180
+ - Minimum withdrawal: 1000 credits
181
+ - 5% platform fee on each transaction
182
+ - Your reputation (rating + success rate) determines how much work you attract — high-quality delivery builds a flywheel of more tasks and more earnings
183
+ `.trim();
184
+ //# sourceMappingURL=worker-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-prompt.js","sourceRoot":"","sources":["../src/worker-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+K5B,CAAC,IAAI,EAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ export interface TaskInfo {
2
+ id: string;
3
+ category: string;
4
+ description: string;
5
+ input: Record<string, unknown>;
6
+ maxPrice: number;
7
+ }
8
+ export declare function log(msg: string): void;
9
+ export declare function logError(msg: string): void;
10
+ /**
11
+ * Detect if crust is installed on PATH.
12
+ */
13
+ export declare function detectCrust(): Promise<boolean>;
14
+ /**
15
+ * Log crust protection status at worker startup.
16
+ * Returns whether crust should be used.
17
+ */
18
+ export declare function initCrust(noCrust: boolean): Promise<boolean>;
19
+ export declare function buildPrompt(task: TaskInfo): string;
20
+ export declare function execAgent(command: string, prompt: string, useCrust?: boolean): Promise<Record<string, unknown>>;
21
+ //# sourceMappingURL=worker-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-shared.d.ts","sourceRoot":"","sources":["../src/worker-shared.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,QAG9B;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,QAGnC;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAMpD;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAclE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAalD;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiCnH"}
@@ -0,0 +1,82 @@
1
+ import { execFile } from 'node:child_process';
2
+ export function log(msg) {
3
+ const ts = new Date().toISOString().slice(11, 19);
4
+ console.log(`\x1b[36m[${ts}]\x1b[0m ${msg}`);
5
+ }
6
+ export function logError(msg) {
7
+ const ts = new Date().toISOString().slice(11, 19);
8
+ console.error(`\x1b[31m[${ts}]\x1b[0m ${msg}`);
9
+ }
10
+ /**
11
+ * Detect if crust is installed on PATH.
12
+ */
13
+ export async function detectCrust() {
14
+ return new Promise((resolve) => {
15
+ execFile('which', ['crust'], { timeout: 3000 }, (error) => {
16
+ resolve(!error);
17
+ });
18
+ });
19
+ }
20
+ /**
21
+ * Log crust protection status at worker startup.
22
+ * Returns whether crust should be used.
23
+ */
24
+ export async function initCrust(noCrust) {
25
+ if (noCrust) {
26
+ log('Crust protection: \x1b[33mdisabled\x1b[0m (--no-crust)');
27
+ return false;
28
+ }
29
+ const available = await detectCrust();
30
+ if (available) {
31
+ log('Crust protection: \x1b[32mactive\x1b[0m');
32
+ return true;
33
+ }
34
+ log('Crust protection: \x1b[33mnot available\x1b[0m (install: https://github.com/BakeLens/crust)');
35
+ return false;
36
+ }
37
+ export function buildPrompt(task) {
38
+ return [
39
+ `You are completing a task from OpenStall.`,
40
+ ``,
41
+ `Category: ${task.category}`,
42
+ `Description: ${task.description}`,
43
+ `Payment: ${task.maxPrice} credits (you earn ${Math.floor(task.maxPrice * 0.95)})`,
44
+ ``,
45
+ `Input:`,
46
+ JSON.stringify(task.input, null, 2),
47
+ ``,
48
+ `Complete this task. Output ONLY valid JSON with your result — no markdown, no explanation, just the JSON object.`,
49
+ ].join('\n');
50
+ }
51
+ export async function execAgent(command, prompt, useCrust = false) {
52
+ const fullCommand = useCrust ? `crust wrap -- ${command}` : command;
53
+ const parts = fullCommand.split(/\s+/);
54
+ const bin = parts[0];
55
+ const args = [...parts.slice(1), prompt];
56
+ return new Promise((resolve, reject) => {
57
+ execFile(bin, args, {
58
+ maxBuffer: 10 * 1024 * 1024,
59
+ timeout: 5 * 60 * 1000,
60
+ env: { ...process.env },
61
+ }, (error, stdout, stderr) => {
62
+ if (error) {
63
+ reject(new Error(`Agent command failed: ${error.message}${stderr ? `\nstderr: ${stderr}` : ''}`));
64
+ return;
65
+ }
66
+ const output = stdout.trim();
67
+ let jsonStr = output;
68
+ const jsonMatch = output.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
69
+ if (jsonMatch) {
70
+ jsonStr = jsonMatch[1].trim();
71
+ }
72
+ try {
73
+ const parsed = JSON.parse(jsonStr);
74
+ resolve(typeof parsed === 'object' && parsed !== null ? parsed : { result: parsed });
75
+ }
76
+ catch {
77
+ resolve({ result: output });
78
+ }
79
+ });
80
+ });
81
+ }
82
+ //# sourceMappingURL=worker-shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-shared.js","sourceRoot":"","sources":["../src/worker-shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAU9C,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,YAAY,GAAG,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,EAAE,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;YACxD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAgB;IAC9C,IAAI,OAAO,EAAE,CAAC;QACZ,GAAG,CAAC,wDAAwD,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,WAAW,EAAE,CAAC;IACtC,IAAI,SAAS,EAAE,CAAC;QACd,GAAG,CAAC,yCAAyC,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,6FAA6F,CAAC,CAAC;IACnG,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,OAAO;QACL,2CAA2C;QAC3C,EAAE;QACF,aAAa,IAAI,CAAC,QAAQ,EAAE;QAC5B,gBAAgB,IAAI,CAAC,WAAW,EAAE;QAClC,YAAY,IAAI,CAAC,QAAQ,sBAAsB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG;QAClF,EAAE;QACF,QAAQ;QACR,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,EAAE;QACF,kHAAkH;KACnH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe,EAAE,MAAc,EAAE,QAAQ,GAAG,KAAK;IAC/E,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACpE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAEzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE;YAClB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;YAC3B,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;YACtB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;SACxB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC3B,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAClG,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YAE7B,IAAI,OAAO,GAAG,MAAM,CAAC;YACrB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACrE,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAChC,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACnC,OAAO,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YACvF,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type TaskInfo } from './worker-shared.js';
2
+ interface WorkerOptions {
3
+ categories: string[];
4
+ tags?: string[];
5
+ maxPrice?: number;
6
+ handler?: (task: TaskInfo) => Promise<Record<string, unknown>>;
7
+ agentCommand?: string;
8
+ pollIntervalMs?: number;
9
+ noCrust?: boolean;
10
+ }
11
+ /**
12
+ * Start a persistent worker that polls for tasks and handles them.
13
+ */
14
+ export declare function startWorker(options: WorkerOptions): Promise<{
15
+ stop: () => void;
16
+ }>;
17
+ /**
18
+ * CLI entry point for legacy poll mode.
19
+ */
20
+ export declare function handleWorkerPoll(flags: Record<string, string>): Promise<void>;
21
+ export {};
22
+ //# sourceMappingURL=worker.d.ts.map