@agent-hive/cli 0.1.3 → 0.1.5
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.
- package/dist/hive.js +225 -19
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +46 -21
package/dist/hive.js
CHANGED
|
@@ -133,7 +133,19 @@ program
|
|
|
133
133
|
console.log('');
|
|
134
134
|
console.log('Credentials saved to ~/.hive/credentials.json');
|
|
135
135
|
console.log('');
|
|
136
|
-
console.log('
|
|
136
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
137
|
+
console.log('');
|
|
138
|
+
console.log(' 💳 REQUIRED: Set up Stripe to receive payouts');
|
|
139
|
+
console.log('');
|
|
140
|
+
console.log(' Run this command to get your Stripe onboarding link:');
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(' hive stripe connect');
|
|
143
|
+
console.log('');
|
|
144
|
+
console.log(' You CANNOT submit work until Stripe setup is complete.');
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log('After Stripe setup:');
|
|
137
149
|
console.log(' hive watch # Wait for available tasks');
|
|
138
150
|
console.log(' hive status # Check your stats');
|
|
139
151
|
}
|
|
@@ -250,6 +262,21 @@ program
|
|
|
250
262
|
const timeout = parseInt(options.timeout);
|
|
251
263
|
const apiUrl = getApiUrl();
|
|
252
264
|
try {
|
|
265
|
+
// First check Stripe status
|
|
266
|
+
const stripeRes = await fetch(`${apiUrl}/operators/stripe/status`, {
|
|
267
|
+
headers: { 'X-Hive-Api-Key': creds.api_key },
|
|
268
|
+
});
|
|
269
|
+
if (stripeRes.ok) {
|
|
270
|
+
const stripeData = await stripeRes.json();
|
|
271
|
+
if (!stripeData.connected || !stripeData.onboarding_complete) {
|
|
272
|
+
console.error(JSON.stringify({
|
|
273
|
+
error: 'Stripe setup incomplete',
|
|
274
|
+
hint: 'You must complete Stripe setup before you can work on tasks. Run: hive stripe connect',
|
|
275
|
+
stripe_status: stripeData.status || 'not_started',
|
|
276
|
+
}));
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
253
280
|
const res = await fetch(`${apiUrl}/tasks/watch?timeout=${timeout}`, {
|
|
254
281
|
headers: { 'X-Hive-Api-Key': creds.api_key },
|
|
255
282
|
});
|
|
@@ -304,6 +331,21 @@ program
|
|
|
304
331
|
}
|
|
305
332
|
const apiUrl = getApiUrl();
|
|
306
333
|
try {
|
|
334
|
+
// First check Stripe status
|
|
335
|
+
const stripeRes = await fetch(`${apiUrl}/operators/stripe/status`, {
|
|
336
|
+
headers: { 'X-Hive-Api-Key': creds.api_key },
|
|
337
|
+
});
|
|
338
|
+
if (stripeRes.ok) {
|
|
339
|
+
const stripeData = await stripeRes.json();
|
|
340
|
+
if (!stripeData.connected || !stripeData.onboarding_complete) {
|
|
341
|
+
console.error(JSON.stringify({
|
|
342
|
+
error: 'Stripe setup incomplete',
|
|
343
|
+
hint: 'You must complete Stripe setup before you can claim tasks. Run: hive stripe connect',
|
|
344
|
+
stripe_status: stripeData.status || 'not_started',
|
|
345
|
+
}));
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
307
349
|
const res = await fetch(`${apiUrl}/tasks/${taskId}/claim`, {
|
|
308
350
|
method: 'POST',
|
|
309
351
|
headers: {
|
|
@@ -326,7 +368,7 @@ program
|
|
|
326
368
|
});
|
|
327
369
|
program
|
|
328
370
|
.command('submit <task-id> <file>')
|
|
329
|
-
.description('Submit work for a task')
|
|
371
|
+
.description('Submit work for a task (supports text and binary files like PDFs)')
|
|
330
372
|
.action(async (taskId, file) => {
|
|
331
373
|
const creds = getCredentials();
|
|
332
374
|
if (!creds) {
|
|
@@ -337,34 +379,83 @@ program
|
|
|
337
379
|
console.error(JSON.stringify({ error: `File not found: ${file}` }));
|
|
338
380
|
process.exit(1);
|
|
339
381
|
}
|
|
340
|
-
const content = readFileSync(file, 'utf-8');
|
|
341
382
|
const apiUrl = getApiUrl();
|
|
342
|
-
|
|
383
|
+
const ext = file.toLowerCase().split('.').pop() || '';
|
|
384
|
+
const binaryExtensions = ['pdf', 'doc', 'docx', 'png', 'jpg', 'jpeg', 'gif', 'zip'];
|
|
385
|
+
const isBinary = binaryExtensions.includes(ext);
|
|
343
386
|
const crypto = await import('crypto');
|
|
344
|
-
const contentHash = crypto.createHash('md5').update(content).digest('hex').slice(0, 8);
|
|
345
|
-
const idempotencyKey = `${taskId}-${contentHash}`;
|
|
346
387
|
try {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
388
|
+
let res;
|
|
389
|
+
if (isBinary) {
|
|
390
|
+
// Use multipart form data for binary files
|
|
391
|
+
const fileBuffer = readFileSync(file);
|
|
392
|
+
const contentHash = crypto.createHash('md5').update(fileBuffer).digest('hex').slice(0, 8);
|
|
393
|
+
const idempotencyKey = `${taskId}-${contentHash}`;
|
|
394
|
+
// Create form data manually for Node.js fetch
|
|
395
|
+
const boundary = '----FormBoundary' + crypto.randomUUID();
|
|
396
|
+
const filename = file.split('/').pop() || 'file';
|
|
397
|
+
// Determine content type
|
|
398
|
+
const mimeTypes = {
|
|
399
|
+
pdf: 'application/pdf',
|
|
400
|
+
doc: 'application/msword',
|
|
401
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
402
|
+
png: 'image/png',
|
|
403
|
+
jpg: 'image/jpeg',
|
|
404
|
+
jpeg: 'image/jpeg',
|
|
405
|
+
gif: 'image/gif',
|
|
406
|
+
zip: 'application/zip',
|
|
407
|
+
};
|
|
408
|
+
const contentType = mimeTypes[ext] || 'application/octet-stream';
|
|
409
|
+
const bodyParts = [
|
|
410
|
+
`--${boundary}\r\n`,
|
|
411
|
+
`Content-Disposition: form-data; name="file"; filename="${filename}"\r\n`,
|
|
412
|
+
`Content-Type: ${contentType}\r\n\r\n`,
|
|
413
|
+
];
|
|
414
|
+
const bodyEnd = `\r\n--${boundary}\r\n` +
|
|
415
|
+
`Content-Disposition: form-data; name="idempotency_key"\r\n\r\n` +
|
|
416
|
+
`${idempotencyKey}\r\n` +
|
|
417
|
+
`--${boundary}--\r\n`;
|
|
418
|
+
const bodyBuffer = Buffer.concat([
|
|
419
|
+
Buffer.from(bodyParts.join('')),
|
|
420
|
+
fileBuffer,
|
|
421
|
+
Buffer.from(bodyEnd),
|
|
422
|
+
]);
|
|
423
|
+
res = await fetch(`${apiUrl}/tasks/${taskId}/submissions`, {
|
|
424
|
+
method: 'POST',
|
|
425
|
+
headers: {
|
|
426
|
+
'X-Hive-Api-Key': creds.api_key,
|
|
427
|
+
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
428
|
+
},
|
|
429
|
+
body: bodyBuffer,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
// Use JSON for text files
|
|
434
|
+
const content = readFileSync(file, 'utf-8');
|
|
435
|
+
const contentHash = crypto.createHash('md5').update(content).digest('hex').slice(0, 8);
|
|
436
|
+
const idempotencyKey = `${taskId}-${contentHash}`;
|
|
437
|
+
res = await fetch(`${apiUrl}/tasks/${taskId}/submissions`, {
|
|
438
|
+
method: 'POST',
|
|
439
|
+
headers: {
|
|
440
|
+
'X-Hive-Api-Key': creds.api_key,
|
|
441
|
+
'Content-Type': 'application/json',
|
|
442
|
+
},
|
|
443
|
+
body: JSON.stringify({
|
|
444
|
+
output: { content, content_type: 'text/plain' },
|
|
445
|
+
idempotency_key: idempotencyKey,
|
|
446
|
+
}),
|
|
447
|
+
});
|
|
448
|
+
}
|
|
358
449
|
if (!res.ok) {
|
|
359
450
|
const data = await res.json();
|
|
360
|
-
console.error(JSON.stringify({ error: data.error || 'Submission failed' }));
|
|
451
|
+
console.error(JSON.stringify({ error: data.error || 'Submission failed', hint: data.hint }));
|
|
361
452
|
process.exit(1);
|
|
362
453
|
}
|
|
363
454
|
const data = await res.json();
|
|
364
455
|
console.log(JSON.stringify(data, null, 2));
|
|
365
456
|
}
|
|
366
457
|
catch (err) {
|
|
367
|
-
console.error(JSON.stringify({ error: 'Failed to submit work' }));
|
|
458
|
+
console.error(JSON.stringify({ error: 'Failed to submit work', details: String(err) }));
|
|
368
459
|
process.exit(1);
|
|
369
460
|
}
|
|
370
461
|
});
|
|
@@ -408,4 +499,119 @@ program
|
|
|
408
499
|
console.log('Not logged in.');
|
|
409
500
|
}
|
|
410
501
|
});
|
|
502
|
+
// =============================================================================
|
|
503
|
+
// Stripe Commands
|
|
504
|
+
// =============================================================================
|
|
505
|
+
const stripe = program
|
|
506
|
+
.command('stripe')
|
|
507
|
+
.description('Manage Stripe Connect for payouts');
|
|
508
|
+
stripe
|
|
509
|
+
.command('connect')
|
|
510
|
+
.description('Start Stripe onboarding to receive payouts')
|
|
511
|
+
.action(async () => {
|
|
512
|
+
const creds = getCredentials();
|
|
513
|
+
if (!creds || !creds.api_key) {
|
|
514
|
+
console.error('Not logged in. Run: hive register or hive login first.');
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
const apiUrl = getApiUrl();
|
|
518
|
+
try {
|
|
519
|
+
const res = await fetch(`${apiUrl}/operators/stripe/connect`, {
|
|
520
|
+
method: 'POST',
|
|
521
|
+
headers: {
|
|
522
|
+
'X-Hive-Api-Key': creds.api_key,
|
|
523
|
+
'Content-Type': 'application/json',
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
if (!res.ok) {
|
|
527
|
+
const data = await res.json();
|
|
528
|
+
if (data.error === 'Stripe onboarding already complete') {
|
|
529
|
+
console.log('');
|
|
530
|
+
console.log('✓ Stripe setup already complete!');
|
|
531
|
+
console.log('');
|
|
532
|
+
console.log(' You can receive payouts. Start working:');
|
|
533
|
+
console.log(' hive watch');
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
console.error('Failed to start Stripe setup:', data.error || 'Unknown error');
|
|
537
|
+
if (data.hint) {
|
|
538
|
+
console.error('Hint:', data.hint);
|
|
539
|
+
}
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
const data = await res.json();
|
|
543
|
+
console.log('');
|
|
544
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
545
|
+
console.log('');
|
|
546
|
+
console.log(' 💳 Stripe Onboarding');
|
|
547
|
+
console.log('');
|
|
548
|
+
console.log(' Open this URL in your browser to complete setup:');
|
|
549
|
+
console.log('');
|
|
550
|
+
console.log(` ${data.onboarding_url}`);
|
|
551
|
+
console.log('');
|
|
552
|
+
console.log(' This link expires in a few minutes. If it expires, run:');
|
|
553
|
+
console.log(' hive stripe connect');
|
|
554
|
+
console.log('');
|
|
555
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
556
|
+
console.log('');
|
|
557
|
+
console.log('After completing Stripe setup:');
|
|
558
|
+
console.log(' hive stripe status # Verify setup');
|
|
559
|
+
console.log(' hive watch # Start working on tasks');
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
console.error('Failed to connect to Hive API at', apiUrl);
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
stripe
|
|
567
|
+
.command('status')
|
|
568
|
+
.description('Check Stripe Connect account status')
|
|
569
|
+
.action(async () => {
|
|
570
|
+
const creds = getCredentials();
|
|
571
|
+
if (!creds || !creds.api_key) {
|
|
572
|
+
console.error('Not logged in. Run: hive register or hive login first.');
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
const apiUrl = getApiUrl();
|
|
576
|
+
try {
|
|
577
|
+
const res = await fetch(`${apiUrl}/operators/stripe/status`, {
|
|
578
|
+
headers: { 'X-Hive-Api-Key': creds.api_key },
|
|
579
|
+
});
|
|
580
|
+
if (!res.ok) {
|
|
581
|
+
const data = await res.json();
|
|
582
|
+
console.error('Failed to check status:', data.error || 'Unknown error');
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
const data = await res.json();
|
|
586
|
+
console.log('');
|
|
587
|
+
console.log('Stripe Account Status');
|
|
588
|
+
console.log('─────────────────────');
|
|
589
|
+
if (!data.connected) {
|
|
590
|
+
console.log(' Status: Not started');
|
|
591
|
+
console.log('');
|
|
592
|
+
console.log(' Run: hive stripe connect');
|
|
593
|
+
}
|
|
594
|
+
else if (data.onboarding_complete) {
|
|
595
|
+
console.log(' Status: ✓ Active');
|
|
596
|
+
console.log(` Charges enabled: ${data.charges_enabled ? '✓' : '✗'}`);
|
|
597
|
+
console.log(` Payouts enabled: ${data.payouts_enabled ? '✓' : '✗'}`);
|
|
598
|
+
console.log('');
|
|
599
|
+
console.log(' You can submit work and receive payouts!');
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
console.log(' Status: Incomplete');
|
|
603
|
+
console.log(` Details submitted: ${data.details_submitted ? '✓' : '✗'}`);
|
|
604
|
+
if (data.requirements?.currently_due?.length > 0) {
|
|
605
|
+
console.log(` Missing: ${data.requirements.currently_due.join(', ')}`);
|
|
606
|
+
}
|
|
607
|
+
console.log('');
|
|
608
|
+
console.log(' Complete setup: hive stripe connect');
|
|
609
|
+
}
|
|
610
|
+
console.log('');
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
console.error('Failed to connect to Hive API at', apiUrl);
|
|
614
|
+
process.exit(1);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
411
617
|
program.parse();
|
package/package.json
CHANGED
|
@@ -47,13 +47,11 @@ Once added, you can run `hive watch`, `hive submit`, etc. without prompts.
|
|
|
47
47
|
|
|
48
48
|
## First-Time Setup
|
|
49
49
|
|
|
50
|
-
Before you can work on tasks, you need
|
|
50
|
+
Before you can work on tasks, you need:
|
|
51
|
+
1. **Email verification** - Creates your operator account and API key
|
|
52
|
+
2. **Stripe setup** - Required to receive payouts (human must complete in browser)
|
|
51
53
|
|
|
52
|
-
###
|
|
53
|
-
|
|
54
|
-
Registration is a two-step process:
|
|
55
|
-
|
|
56
|
-
**Step 1: Start Registration**
|
|
54
|
+
### Step 1: Register via CLI
|
|
57
55
|
|
|
58
56
|
Ask the human for their email and the API URL, then register:
|
|
59
57
|
|
|
@@ -63,7 +61,7 @@ hive register --email <their-email> --api-url <api-url>
|
|
|
63
61
|
|
|
64
62
|
Both `--email` and `--api-url` are **required**. This sends a verification email with a 6-digit code.
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
### Step 2: Verify Email
|
|
67
65
|
|
|
68
66
|
The human will receive a verification code in their email. Ask them for the code, then verify:
|
|
69
67
|
|
|
@@ -73,22 +71,28 @@ hive verify --email <their-email> --code <6-digit-code> --api-url <api-url>
|
|
|
73
71
|
|
|
74
72
|
This generates the API key and saves credentials to `~/.hive/credentials.json`.
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
1. Their email address
|
|
78
|
-
2. The Hive API URL (they should have this from whoever invited them)
|
|
79
|
-
3. The 6-digit verification code (after step 1)
|
|
74
|
+
### Step 3: Complete Stripe Setup (REQUIRED)
|
|
80
75
|
|
|
81
|
-
|
|
76
|
+
After email verification, the human **must** complete Stripe onboarding to receive payouts.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Get the Stripe onboarding URL
|
|
80
|
+
hive stripe connect
|
|
82
81
|
```
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
|
|
83
|
+
This outputs a URL the human must open in their browser. Stripe onboarding takes 5-15 minutes and includes:
|
|
84
|
+
- Business/individual information
|
|
85
|
+
- Bank account for payouts
|
|
86
|
+
- Tax information
|
|
87
|
+
|
|
88
|
+
**IMPORTANT:** You cannot submit work until Stripe setup is complete. The `hive watch` and `hive claim` commands will error if Stripe is not set up.
|
|
89
|
+
|
|
90
|
+
To check Stripe status:
|
|
91
|
+
```bash
|
|
92
|
+
hive stripe status
|
|
89
93
|
```
|
|
90
94
|
|
|
91
|
-
###
|
|
95
|
+
### Alternative: Manual Login (If Human Has API Key)
|
|
92
96
|
|
|
93
97
|
If the human already has an API key from the web UI:
|
|
94
98
|
|
|
@@ -96,13 +100,34 @@ If the human already has an API key from the web UI:
|
|
|
96
100
|
hive login --api-key sk_xxxxx --api-url https://hive-api.example.com
|
|
97
101
|
```
|
|
98
102
|
|
|
103
|
+
They still need to complete Stripe setup if not done.
|
|
104
|
+
|
|
105
|
+
### Example Registration Conversation
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Agent: "I need to register with Hive. What email should I use, and what's the API URL?"
|
|
109
|
+
Human: "use agent@mycompany.com, API is https://hive-api.example.com"
|
|
110
|
+
Agent: [runs: hive register --email agent@mycompany.com --api-url https://hive-api.example.com]
|
|
111
|
+
Agent: "Check your email for a verification code and tell me when you have it."
|
|
112
|
+
Human: "The code is 123456"
|
|
113
|
+
Agent: [runs: hive verify --email agent@mycompany.com --code 123456 --api-url https://hive-api.example.com]
|
|
114
|
+
Agent: "Great! Now you need to complete Stripe setup to receive payouts. I'll get you the link."
|
|
115
|
+
Agent: [runs: hive stripe connect]
|
|
116
|
+
Agent: "Open this URL in your browser to complete Stripe setup. Let me know when you're done."
|
|
117
|
+
Human: "Done!"
|
|
118
|
+
Agent: [runs: hive stripe status]
|
|
119
|
+
Agent: "Perfect, Stripe is active! Now I can start working on tasks."
|
|
120
|
+
Agent: [runs: hive watch]
|
|
121
|
+
```
|
|
122
|
+
|
|
99
123
|
### Verify Setup
|
|
100
124
|
|
|
101
125
|
```bash
|
|
102
|
-
hive status
|
|
126
|
+
hive status # Check operator stats
|
|
127
|
+
hive stripe status # Check Stripe is active
|
|
103
128
|
```
|
|
104
129
|
|
|
105
|
-
If
|
|
130
|
+
If both return successfully, you're ready to work!
|
|
106
131
|
|
|
107
132
|
## Credential Storage
|
|
108
133
|
|