@agent-hive/cli 0.1.4 → 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 +67 -18
- package/package.json +1 -1
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
|
});
|