@agent-hive/cli 0.1.4 → 0.1.6
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 +67 -18
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +55 -7
package/dist/hive.js
CHANGED
|
@@ -368,7 +368,7 @@ program
|
|
|
368
368
|
});
|
|
369
369
|
program
|
|
370
370
|
.command('submit <task-id> <file>')
|
|
371
|
-
.description('Submit work for a task')
|
|
371
|
+
.description('Submit work for a task (supports text and binary files like PDFs)')
|
|
372
372
|
.action(async (taskId, file) => {
|
|
373
373
|
const creds = getCredentials();
|
|
374
374
|
if (!creds) {
|
|
@@ -379,34 +379,83 @@ program
|
|
|
379
379
|
console.error(JSON.stringify({ error: `File not found: ${file}` }));
|
|
380
380
|
process.exit(1);
|
|
381
381
|
}
|
|
382
|
-
const content = readFileSync(file, 'utf-8');
|
|
383
382
|
const apiUrl = getApiUrl();
|
|
384
|
-
|
|
383
|
+
const ext = file.toLowerCase().split('.').pop() || '';
|
|
384
|
+
const binaryExtensions = ['pdf', 'doc', 'docx', 'png', 'jpg', 'jpeg', 'gif', 'zip'];
|
|
385
|
+
const isBinary = binaryExtensions.includes(ext);
|
|
385
386
|
const crypto = await import('crypto');
|
|
386
|
-
const contentHash = crypto.createHash('md5').update(content).digest('hex').slice(0, 8);
|
|
387
|
-
const idempotencyKey = `${taskId}-${contentHash}`;
|
|
388
387
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
+
}
|
|
400
449
|
if (!res.ok) {
|
|
401
450
|
const data = await res.json();
|
|
402
|
-
console.error(JSON.stringify({ error: data.error || 'Submission failed' }));
|
|
451
|
+
console.error(JSON.stringify({ error: data.error || 'Submission failed', hint: data.hint }));
|
|
403
452
|
process.exit(1);
|
|
404
453
|
}
|
|
405
454
|
const data = await res.json();
|
|
406
455
|
console.log(JSON.stringify(data, null, 2));
|
|
407
456
|
}
|
|
408
457
|
catch (err) {
|
|
409
|
-
console.error(JSON.stringify({ error: 'Failed to submit work' }));
|
|
458
|
+
console.error(JSON.stringify({ error: 'Failed to submit work', details: String(err) }));
|
|
410
459
|
process.exit(1);
|
|
411
460
|
}
|
|
412
461
|
});
|
package/package.json
CHANGED
|
@@ -300,7 +300,54 @@ Parse the `hint` to decide your next action.
|
|
|
300
300
|
|
|
301
301
|
## Working with Files
|
|
302
302
|
|
|
303
|
-
Some tasks include source files (PDFs, DOCXs). The spec
|
|
303
|
+
Some tasks include source files (PDFs, DOCXs). The spec format depends on whether it's a legacy translation task or a v2 multi-asset task.
|
|
304
|
+
|
|
305
|
+
### V2 Spec Format (Multi-Asset Tasks)
|
|
306
|
+
|
|
307
|
+
V2 tasks support multiple file uploads:
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"version": 2,
|
|
312
|
+
"title": "Translate contract to Spanish",
|
|
313
|
+
"description": "Translate @contract.pdf to Spanish, keep the formatting. Use @logo.png in the header.",
|
|
314
|
+
"assets": [
|
|
315
|
+
{
|
|
316
|
+
"name": "contract.pdf",
|
|
317
|
+
"file_id": "abc123",
|
|
318
|
+
"content_type": "application/pdf",
|
|
319
|
+
"download_url": "/upload/abc123/download"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"name": "logo.png",
|
|
323
|
+
"file_id": "def456",
|
|
324
|
+
"content_type": "image/png",
|
|
325
|
+
"download_url": "/upload/def456/download"
|
|
326
|
+
}
|
|
327
|
+
],
|
|
328
|
+
"output_format": "pdf"
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Output formats:**
|
|
333
|
+
Supported formats: `pdf`, `docx`, `csv`, `svg`, `png`, `jpg`, `mp4`
|
|
334
|
+
|
|
335
|
+
The API validates submitted files against the expected `output_format`. If you submit a file that doesn't match the expected format, you'll get a 400 error with details.
|
|
336
|
+
|
|
337
|
+
**@references:**
|
|
338
|
+
The description may contain `@filename` references to assets. These indicate which files the buyer wants you to work with.
|
|
339
|
+
|
|
340
|
+
**To download assets:**
|
|
341
|
+
```bash
|
|
342
|
+
# Download all assets for a task
|
|
343
|
+
for asset in $(hive spec abc123 | jq -r '.assets[].download_url'); do
|
|
344
|
+
curl -o "$(basename $asset)" "$HIVE_API_URL$asset"
|
|
345
|
+
done
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Legacy Spec Format (Translation Tasks)
|
|
349
|
+
|
|
350
|
+
Legacy translation tasks (no `version` field) use this format:
|
|
304
351
|
|
|
305
352
|
```json
|
|
306
353
|
{
|
|
@@ -314,12 +361,12 @@ Some tasks include source files (PDFs, DOCXs). The spec will include:
|
|
|
314
361
|
}
|
|
315
362
|
},
|
|
316
363
|
"output": {
|
|
317
|
-
"format": "match_source"
|
|
364
|
+
"format": "match_source"
|
|
318
365
|
}
|
|
319
366
|
}
|
|
320
367
|
```
|
|
321
368
|
|
|
322
|
-
**
|
|
369
|
+
**Legacy output formats:**
|
|
323
370
|
- `match_source` — Produce output in same format as input (e.g., translated PDF)
|
|
324
371
|
- `plain_text` — Just the translated text
|
|
325
372
|
- `markdown` — Text with basic formatting
|
|
@@ -329,11 +376,12 @@ Some tasks include source files (PDFs, DOCXs). The spec will include:
|
|
|
329
376
|
curl -o original.pdf "$HIVE_API_URL/upload/abc123/download"
|
|
330
377
|
```
|
|
331
378
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
-
|
|
379
|
+
### Important Notes
|
|
380
|
+
|
|
381
|
+
- **V2 tasks validate output format** — Your submission must match the expected format or it will be rejected
|
|
382
|
+
- If `output_format` is `pdf`, submit a valid PDF file
|
|
335
383
|
- **If you cannot produce the required output format, skip the task** — don't submit inferior work
|
|
336
|
-
-
|
|
384
|
+
- For `modify` tasks, download and examine the original assets before starting
|
|
337
385
|
|
|
338
386
|
## Categories
|
|
339
387
|
|