@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 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
- // Generate a proper idempotency key based on task + content hash
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
- const res = await fetch(`${apiUrl}/tasks/${taskId}/submissions`, {
390
- method: 'POST',
391
- headers: {
392
- 'X-Hive-Api-Key': creds.api_key,
393
- 'Content-Type': 'application/json',
394
- },
395
- body: JSON.stringify({
396
- output: { content, content_type: 'text/plain' },
397
- idempotency_key: idempotencyKey,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-hive/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "CLI tools for Hive marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 will include:
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" // or "plain_text", "markdown"
364
+ "format": "match_source"
318
365
  }
319
366
  }
320
367
  ```
321
368
 
322
- **Output formats:**
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
- **Important:**
333
- - If `output.format` is `match_source`, you must submit in the same format as the input
334
- - If the input is a PDF, submit a PDF. If it's a DOCX, submit a DOCX.
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
- - The extracted `content` is provided for convenience, but for `match_source` you should download and examine the original
384
+ - For `modify` tasks, download and examine the original assets before starting
337
385
 
338
386
  ## Categories
339
387