@gholl-studio/pier-connector 0.1.5 → 0.1.8

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 +91 -40
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.5",
4
+ "version": "0.1.8",
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
@@ -162,7 +162,9 @@ export default function register(api) {
162
162
  OriginatingChannel: 'pier',
163
163
  OriginatingTo: `pier:${jobId}`,
164
164
  WasMentioned: true,
165
- CommandAuthorized: true
165
+ CommandAuthorized: true,
166
+ SystemPrompt: inbound.systemPrompt || undefined,
167
+ MessageId: inbound.messageId || jobId
166
168
  });
167
169
 
168
170
  // Create a dispatcher to handle the reply lifecycle (fixes sendFinalReply error)
@@ -173,9 +175,12 @@ export default function register(api) {
173
175
  logger.debug(`[pier-connector] Dispatcher idle for session ${route.sessionKey}`);
174
176
  },
175
177
  deliver: async (payload) => {
176
- // This triggers the outbound.sendText handler we defined in pierChannel
177
- await api.runtime.channel.reply.withReplyDispatcher(dispatcher, async () => {
178
- // withReplyDispatcher is sometimes needed by internal SDK methods
178
+ // Route Agent's reply to outbound.sendText so it reaches NATS
179
+ logger.debug(`[pier-connector] deliver() called with text: "${truncate(payload.text, 60)}"`);
180
+ await pierChannel.outbound.sendText({
181
+ text: payload.text,
182
+ to: `pier:${jobId}`,
183
+ metadata: activeNodeJobs.get(jobId),
179
184
  });
180
185
  }
181
186
  });
@@ -195,12 +200,14 @@ export default function register(api) {
195
200
  }
196
201
 
197
202
  try {
203
+ logger.info(`[pier-connector] 🧠 Triggering agent for session ${route.sessionKey}...`);
198
204
  // Dispatch reply — this will trigger outbound: sendText in pierChannel
199
205
  await api.runtime.channel.reply.dispatchReplyFromConfig({
200
206
  ctx: ctxPayload,
201
207
  cfg: api.config,
202
208
  dispatcher
203
209
  });
210
+ logger.info(`[pier-connector] 🧠 Agent dispatch completed for ${route.sessionKey}`);
204
211
  } finally {
205
212
  markDispatchIdle();
206
213
  }
@@ -269,11 +276,14 @@ export default function register(api) {
269
276
  const text = ctx.text;
270
277
  let metadata = ctx.metadata;
271
278
 
279
+ logger.info(`[pier-connector] 📤 Agent sending reply: "${truncate(text, 40)}" (To: ${ctx.to})`);
280
+
272
281
  if (!metadata && ctx.to) {
273
282
  const toId = ctx.to.replace(/^pier:/, '');
274
283
  metadata = activeNodeJobs.get(toId);
284
+ logger.debug(`[pier-connector] 📤 Resolved metadata for ${toId}: ${metadata ? 'Found' : 'Missing'}`);
275
285
  }
276
-
286
+
277
287
  const jobId = metadata?.pierJobId;
278
288
  const msg = metadata?.pierNatsMsg;
279
289
  const isRealtimeMsg = metadata?.isRealtimeMsg;
@@ -295,23 +305,29 @@ export default function register(api) {
295
305
  try {
296
306
  const config = getActiveConfig();
297
307
  const replySubject = `jobs.job.${jobId}.msg`;
298
- await js.publish(replySubject, new TextEncoder().encode(JSON.stringify({
308
+ const replyPayload = {
309
+ id: ethers.hexlify(ethers.randomBytes(16)), // Unique ID for frontend tracking
299
310
  sender_id: config.nodeId,
300
311
  content: text,
301
312
  created_at: new Date().toISOString()
302
- })));
313
+ };
314
+ await js.publish(replySubject, new TextEncoder().encode(JSON.stringify(replyPayload)));
303
315
  logger.info(`[pier-connector] 💬 Published agent reply to realtime chat for job ${jobId}`);
304
316
  } catch (err) {
305
317
  logger.error(`[pier-connector] Failed to send realtime reply: ${err.message}`);
306
318
  }
307
- } else if (msg) {
308
- safeRespond(msg, responsePayload);
309
-
310
- jobsCompleted++;
311
- logger.info(
312
- `[pier-connector] ✔ Job ${jobId} completed` +
313
- (elapsed ? ` latency: ${elapsed}ms` : ''),
314
- );
319
+ } else if (js) {
320
+ // Publish result to jobs.submit via JetStream (not msg.respond)
321
+ try {
322
+ await js.publish('jobs.submit', new TextEncoder().encode(JSON.stringify(responsePayload)));
323
+ jobsCompleted++;
324
+ logger.info(
325
+ `[pier-connector] Job ${jobId} result published to jobs.submit` +
326
+ (elapsed ? ` — latency: ${elapsed}ms` : ''),
327
+ );
328
+ } catch (err) {
329
+ logger.error(`[pier-connector] ❌ Failed to publish result for job ${jobId}: ${err.message}`);
330
+ }
315
331
  }
316
332
 
317
333
  // Delayed stop for job-specific message listener
@@ -466,17 +482,35 @@ export default function register(api) {
466
482
 
467
483
  (async () => {
468
484
  try {
469
- let consumer = await js.consumers.get(streamName, durableName).catch(async () => {
485
+ // SDK-5: Try to get existing consumer; if config changed, delete and recreate
486
+ let consumer;
487
+ try {
488
+ consumer = await js.consumers.get(streamName, durableName);
489
+ } catch {
490
+ // Consumer doesn't exist yet, create it
470
491
  logger.info(`[pier-connector] Creating new Marketplace Consumer: ${durableName}`);
471
- await jsm.consumers.add(streamName, {
472
- durable_name: durableName,
473
- filter_subject: publicSubject,
474
- deliver_policy: DeliverPolicy.All,
475
- ack_policy: AckPolicy.Explicit,
476
- deliver_group: config.queueGroup
477
- });
478
- return await js.consumers.get(streamName, durableName);
479
- });
492
+ try {
493
+ await jsm.consumers.add(streamName, {
494
+ durable_name: durableName,
495
+ filter_subject: publicSubject,
496
+ deliver_policy: DeliverPolicy.New, // SDK-3: Only new messages
497
+ ack_policy: AckPolicy.Explicit,
498
+ deliver_group: config.queueGroup
499
+ });
500
+ } catch (addErr) {
501
+ // SDK-5: Config mismatch — delete old and recreate
502
+ logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
503
+ try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
504
+ await jsm.consumers.add(streamName, {
505
+ durable_name: durableName,
506
+ filter_subject: publicSubject,
507
+ deliver_policy: DeliverPolicy.New,
508
+ ack_policy: AckPolicy.Explicit,
509
+ deliver_group: config.queueGroup
510
+ });
511
+ }
512
+ consumer = await js.consumers.get(streamName, durableName);
513
+ }
480
514
 
481
515
  const iter = await consumer.consume();
482
516
  for await (const msg of iter) {
@@ -500,16 +534,31 @@ export default function register(api) {
500
534
 
501
535
  (async () => {
502
536
  try {
503
- let consumer = await js.consumers.get(streamName, durableName).catch(async () => {
537
+ // SDK-5: Try to get existing consumer; if config changed, delete and recreate
538
+ let consumer;
539
+ try {
540
+ consumer = await js.consumers.get(streamName, durableName);
541
+ } catch {
504
542
  logger.info(`[pier-connector] Creating new Direct Consumer: ${durableName}`);
505
- await jsm.consumers.add(streamName, {
506
- durable_name: durableName,
507
- filter_subject: privateSubject,
508
- deliver_policy: DeliverPolicy.All,
509
- ack_policy: AckPolicy.Explicit
510
- });
511
- return await js.consumers.get(streamName, durableName);
512
- });
543
+ try {
544
+ await jsm.consumers.add(streamName, {
545
+ durable_name: durableName,
546
+ filter_subject: privateSubject,
547
+ deliver_policy: DeliverPolicy.New, // SDK-4: Only new messages
548
+ ack_policy: AckPolicy.Explicit
549
+ });
550
+ } catch (addErr) {
551
+ logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
552
+ try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
553
+ await jsm.consumers.add(streamName, {
554
+ durable_name: durableName,
555
+ filter_subject: privateSubject,
556
+ deliver_policy: DeliverPolicy.New,
557
+ ack_policy: AckPolicy.Explicit
558
+ });
559
+ }
560
+ consumer = await js.consumers.get(streamName, durableName);
561
+ }
513
562
 
514
563
  const iter = await consumer.consume();
515
564
  for await (const msg of iter) {
@@ -544,9 +593,9 @@ export default function register(api) {
544
593
  const cInfo = await jsm.consumers.add(streamName, {
545
594
  durable_name: durableName,
546
595
  filter_subject: msgSubject,
547
- deliver_policy: DeliverPolicy.All,
596
+ deliver_policy: DeliverPolicy.New, // Only new messages to avoid replay spam
548
597
  ack_policy: AckPolicy.Explicit,
549
- ack_wait: 30000, // 30s ack wait
598
+ ack_wait: 30000,
550
599
  });
551
600
  const consumer = await js.consumers.get(streamName, cInfo.name);
552
601
 
@@ -554,6 +603,8 @@ export default function register(api) {
554
603
  jobSubscriptions.set(jobId, iter);
555
604
 
556
605
  (async () => {
606
+ logger.info(`[pier-connector] 👂 Monitoring new messages for job ${jobId}`);
607
+
557
608
  for await (const msg of iter) {
558
609
  try {
559
610
  const rawMsg = new TextDecoder().decode(msg.data);
@@ -585,14 +636,14 @@ export default function register(api) {
585
636
  msg_id: msgPayload.id,
586
637
  reader_id: config.nodeId
587
638
  })));
588
- logger.info(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
639
+ logger.debug(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
589
640
  } catch (err) {
590
641
  logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
591
642
  }
592
643
  }
593
644
 
594
645
  const content = msgPayload.content;
595
- logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
646
+ logger.info(`[pier-connector] 📥 Incoming message for ${jobId}: "${truncate(content, 40)}"`);
596
647
 
597
648
  const senderCore = msgPayload.sender_id;
598
649
 
@@ -601,7 +652,7 @@ export default function register(api) {
601
652
  isRealtimeMsg: true
602
653
  });
603
654
 
604
- // Wait for agent to finish before acking - this provides backpressure
655
+ // Trigger agent
605
656
  await receiveIncoming({
606
657
  accountId: config.accountId || 'default',
607
658
  senderId: `pier:${senderCore}`,
@@ -889,7 +940,7 @@ export default function register(api) {
889
940
  method: 'POST',
890
941
  headers: {
891
942
  'Content-Type': 'application/json',
892
- 'X-API-Key': config.secretKey // Assuming secretKey can be used as API key for node-specific actions
943
+ 'Authorization': `Bearer ${config.secretKey}` // BUG-7: Use Authorization header, not X-API-Key
893
944
  },
894
945
  body: JSON.stringify({
895
946
  sender_id: config.nodeId,