@gholl-studio/pier-connector 0.1.3 → 0.1.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +83 -48
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gholl-studio/pier-connector",
3
3
  "author": "gholl",
4
- "version": "0.1.3",
4
+ "version": "0.1.4",
5
5
  "description": "OpenClaw plugin that connects to the Pier job marketplace. Automatically fetches, executes, and reports distributed tasks for rewards.",
6
6
  "type": "module",
7
7
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -460,8 +460,9 @@ export default function register(api) {
460
460
 
461
461
  const iter = await consumer.consume();
462
462
  for await (const msg of iter) {
463
- await handleMessage(msg);
464
- msg.ack();
463
+ handleMessage(msg).catch(err => {
464
+ logger.error(`[pier-connector] Fatal handleMessage error: ${err.message}`);
465
+ });
465
466
  }
466
467
  } catch (err) {
467
468
  logger.error(`[pier-connector] Marketplace JetStream error: ${err.message}`);
@@ -492,8 +493,9 @@ export default function register(api) {
492
493
 
493
494
  const iter = await consumer.consume();
494
495
  for await (const msg of iter) {
495
- await handleMessage(msg);
496
- msg.ack();
496
+ handleMessage(msg).catch(err => {
497
+ logger.error(`[pier-connector] Fatal handleMessage error: ${err.message}`);
498
+ });
497
499
  }
498
500
  } catch (err) {
499
501
  logger.error(`[pier-connector] Direct JetStream error: ${err.message}`);
@@ -526,52 +528,73 @@ export default function register(api) {
526
528
  const iter = await consumer.consume();
527
529
  jobSubscriptions.set(jobId, iter);
528
530
 
529
- for await (const msg of iter) {
530
- msg.ack();
531
- const rawMsg = new TextDecoder().decode(msg.data);
532
- let msgPayload;
533
- try {
534
- msgPayload = JSON.parse(rawMsg);
535
- } catch (e) { continue; }
536
-
537
- // Ignore my own messages
538
- if (msgPayload.sender_id === config.nodeId) continue;
539
-
540
- if (msgPayload.type === 'receipt') continue;
541
-
542
- // Send read receipt back
543
- if (js && msgPayload.id) {
531
+ (async () => {
532
+ for await (const msg of iter) {
544
533
  try {
545
- const replySubject = `jobs.job.${jobId}.msg`;
546
- await js.publish(replySubject, new TextEncoder().encode(JSON.stringify({
547
- type: 'receipt',
548
- msg_id: msgPayload.id,
549
- reader_id: config.nodeId
550
- })));
551
- logger.info(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
534
+ const rawMsg = new TextDecoder().decode(msg.data);
535
+ let msgPayload;
536
+ try {
537
+ msgPayload = JSON.parse(rawMsg);
538
+ } catch (e) {
539
+ msg.ack();
540
+ continue;
541
+ }
542
+
543
+ // Ignore my own messages
544
+ if (msgPayload.sender_id === config.nodeId) {
545
+ msg.ack();
546
+ continue;
547
+ }
548
+
549
+ if (msgPayload.type === 'receipt') {
550
+ msg.ack();
551
+ continue;
552
+ }
553
+
554
+ // Send read receipt back
555
+ if (js && msgPayload.id) {
556
+ try {
557
+ const replySubject = `jobs.job.${jobId}.msg`;
558
+ await js.publish(replySubject, new TextEncoder().encode(JSON.stringify({
559
+ type: 'receipt',
560
+ msg_id: msgPayload.id,
561
+ reader_id: config.nodeId
562
+ })));
563
+ logger.info(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
564
+ } catch (err) {
565
+ logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
566
+ }
567
+ }
568
+
569
+ const content = msgPayload.content;
570
+ logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
571
+
572
+ const senderCore = msgPayload.sender_id;
573
+
574
+ activeNodeJobs.set(jobId, {
575
+ pierJobId: jobId,
576
+ isRealtimeMsg: true
577
+ });
578
+
579
+ // Wait for agent to finish before acking - this provides backpressure
580
+ await receiveIncoming({
581
+ accountId: config.accountId || 'default',
582
+ senderId: `pier:${senderCore}`,
583
+ text: content,
584
+ }, jobId);
585
+
586
+ msg.ack();
552
587
  } catch (err) {
553
- logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
588
+ logger.error(`[pier-connector] Chat message processing error for ${jobId}: ${err.message}`);
589
+ msg.nak();
554
590
  }
555
591
  }
556
-
557
- const content = msgPayload.content;
558
- logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
559
-
560
- const senderCore = msgPayload.sender_id;
561
-
562
- activeNodeJobs.set(jobId, {
563
- pierJobId: jobId,
564
- isRealtimeMsg: true
565
- });
566
-
567
- await receiveIncoming({
568
- accountId: config.accountId || 'default',
569
- senderId: `pier:${senderCore}`,
570
- text: content,
571
- }, jobId);
572
- }
592
+ })().catch(err => {
593
+ logger.error(`[pier-connector] Chat iteration died for ${jobId}: ${err.message}`);
594
+ jobSubscriptions.delete(jobId);
595
+ });
573
596
  } catch (err) {
574
- logger.error(`[pier-connector] Job JS message error for ${jobId}: ${err.message}`);
597
+ logger.error(`[pier-connector] Failed to setup chat consumer for ${jobId}: ${err.message}`);
575
598
  }
576
599
  }
577
600
 
@@ -585,6 +608,7 @@ export default function register(api) {
585
608
  payload = JSON.parse(rawData);
586
609
  } catch (e) {
587
610
  logger.error(`[pier-connector] Failed to parse JSON: ${rawData.substring(0, 100)}`);
611
+ msg.ack();
588
612
  return;
589
613
  }
590
614
 
@@ -593,22 +617,30 @@ export default function register(api) {
593
617
  const { jobId } = payload;
594
618
  if (jobId) {
595
619
  logger.info(`[pier-connector] ⏰ Received wakeup signal for job ${jobId}`);
620
+ msg.ack();
596
621
  subscribeToJobMessages(jobId);
622
+ } else {
623
+ msg.ack();
597
624
  }
598
625
  return;
599
626
  }
600
627
 
601
628
  // V1.1 FEATURE: Task Poisoning / Poaching Protection
602
629
  if (payload.assigned_node_id && payload.assigned_node_id !== config.nodeId) {
603
- // Silent ignore - don't reply, don't log heavily
630
+ // Not for us, nak it so others can take it
631
+ msg.nak();
604
632
  return;
605
633
  }
606
634
 
635
+ // ACK the job early to 'claim' it and prevent redelivery while we process
636
+ msg.ack();
637
+
607
638
  jobsReceived++;
608
639
  const parsed = parseJob(msg, logger);
609
640
  if (!parsed.ok) {
610
641
  jobsFailed++;
611
642
  logger.error(`[pier-connector] Job parse error: ${parsed.error}`);
643
+ // Since we already acked, we must send an error result back to Pier
612
644
  safeRespond(msg, createErrorPayload({
613
645
  id: 'unknown',
614
646
  errorCode: 'PARSE_ERROR',
@@ -639,10 +671,13 @@ export default function register(api) {
639
671
  text: job.task,
640
672
  };
641
673
 
674
+ // SUBSCRIBE to job-specific messages FIRST so we don't miss follow-ups during the agent run
675
+ // This handles the user's "can only receive one message" issue
676
+ subscribeToJobMessages(job.id);
677
+
678
+ // Trigger agent run
642
679
  await receiveIncoming(inbound, job.id);
643
680
 
644
- // SUBSCRIBE to job-specific messages for real-time communication
645
- subscribeToJobMessages(job.id);
646
681
  } catch (err) {
647
682
  jobsFailed++;
648
683
  safeRespond(msg, createErrorPayload({