@gholl-studio/pier-connector 0.1.6 → 0.2.0
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/package.json +1 -1
- package/src/index.js +103 -90
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gholl-studio/pier-connector",
|
|
3
3
|
"author": "gholl",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
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
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
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;
|
|
@@ -296,24 +306,32 @@ export default function register(api) {
|
|
|
296
306
|
const config = getActiveConfig();
|
|
297
307
|
const replySubject = `jobs.job.${jobId}.msg`;
|
|
298
308
|
const replyPayload = {
|
|
299
|
-
id: ethers.hexlify(ethers.randomBytes(16)),
|
|
309
|
+
id: ethers.hexlify(ethers.randomBytes(16)),
|
|
310
|
+
job_id: jobId,
|
|
300
311
|
sender_id: config.nodeId,
|
|
312
|
+
sender_type: 'node',
|
|
301
313
|
content: text,
|
|
302
|
-
|
|
314
|
+
timestamp: new Date().toISOString(),
|
|
315
|
+
auth_token: config.secretKey // Secure token for backend validation
|
|
303
316
|
};
|
|
317
|
+
|
|
304
318
|
await js.publish(replySubject, new TextEncoder().encode(JSON.stringify(replyPayload)));
|
|
305
|
-
logger.info(`[pier-connector] 💬
|
|
319
|
+
logger.info(`[pier-connector] 💬 Agent reply published directly to NATS for job ${jobId}`);
|
|
306
320
|
} catch (err) {
|
|
307
|
-
logger.error(`[pier-connector] Failed to
|
|
321
|
+
logger.error(`[pier-connector] Failed to publish realtime reply to NATS: ${err.message}`);
|
|
322
|
+
}
|
|
323
|
+
} else if (js) {
|
|
324
|
+
// Publish result to jobs.submit via JetStream (not msg.respond)
|
|
325
|
+
try {
|
|
326
|
+
await js.publish('jobs.submit', new TextEncoder().encode(JSON.stringify(responsePayload)));
|
|
327
|
+
jobsCompleted++;
|
|
328
|
+
logger.info(
|
|
329
|
+
`[pier-connector] ✔ Job ${jobId} result published to jobs.submit` +
|
|
330
|
+
(elapsed ? ` — latency: ${elapsed}ms` : ''),
|
|
331
|
+
);
|
|
332
|
+
} catch (err) {
|
|
333
|
+
logger.error(`[pier-connector] ❌ Failed to publish result for job ${jobId}: ${err.message}`);
|
|
308
334
|
}
|
|
309
|
-
} else if (msg) {
|
|
310
|
-
safeRespond(msg, responsePayload);
|
|
311
|
-
|
|
312
|
-
jobsCompleted++;
|
|
313
|
-
logger.info(
|
|
314
|
-
`[pier-connector] ✔ Job ${jobId} completed` +
|
|
315
|
-
(elapsed ? ` — latency: ${elapsed}ms` : ''),
|
|
316
|
-
);
|
|
317
335
|
}
|
|
318
336
|
|
|
319
337
|
// Delayed stop for job-specific message listener
|
|
@@ -468,17 +486,35 @@ export default function register(api) {
|
|
|
468
486
|
|
|
469
487
|
(async () => {
|
|
470
488
|
try {
|
|
471
|
-
|
|
489
|
+
// SDK-5: Try to get existing consumer; if config changed, delete and recreate
|
|
490
|
+
let consumer;
|
|
491
|
+
try {
|
|
492
|
+
consumer = await js.consumers.get(streamName, durableName);
|
|
493
|
+
} catch {
|
|
494
|
+
// Consumer doesn't exist yet, create it
|
|
472
495
|
logger.info(`[pier-connector] Creating new Marketplace Consumer: ${durableName}`);
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
496
|
+
try {
|
|
497
|
+
await jsm.consumers.add(streamName, {
|
|
498
|
+
durable_name: durableName,
|
|
499
|
+
filter_subject: publicSubject,
|
|
500
|
+
deliver_policy: DeliverPolicy.New, // SDK-3: Only new messages
|
|
501
|
+
ack_policy: AckPolicy.Explicit,
|
|
502
|
+
deliver_group: config.queueGroup
|
|
503
|
+
});
|
|
504
|
+
} catch (addErr) {
|
|
505
|
+
// SDK-5: Config mismatch — delete old and recreate
|
|
506
|
+
logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
|
|
507
|
+
try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
|
|
508
|
+
await jsm.consumers.add(streamName, {
|
|
509
|
+
durable_name: durableName,
|
|
510
|
+
filter_subject: publicSubject,
|
|
511
|
+
deliver_policy: DeliverPolicy.New,
|
|
512
|
+
ack_policy: AckPolicy.Explicit,
|
|
513
|
+
deliver_group: config.queueGroup
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
consumer = await js.consumers.get(streamName, durableName);
|
|
517
|
+
}
|
|
482
518
|
|
|
483
519
|
const iter = await consumer.consume();
|
|
484
520
|
for await (const msg of iter) {
|
|
@@ -502,16 +538,31 @@ export default function register(api) {
|
|
|
502
538
|
|
|
503
539
|
(async () => {
|
|
504
540
|
try {
|
|
505
|
-
|
|
541
|
+
// SDK-5: Try to get existing consumer; if config changed, delete and recreate
|
|
542
|
+
let consumer;
|
|
543
|
+
try {
|
|
544
|
+
consumer = await js.consumers.get(streamName, durableName);
|
|
545
|
+
} catch {
|
|
506
546
|
logger.info(`[pier-connector] Creating new Direct Consumer: ${durableName}`);
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
547
|
+
try {
|
|
548
|
+
await jsm.consumers.add(streamName, {
|
|
549
|
+
durable_name: durableName,
|
|
550
|
+
filter_subject: privateSubject,
|
|
551
|
+
deliver_policy: DeliverPolicy.New, // SDK-4: Only new messages
|
|
552
|
+
ack_policy: AckPolicy.Explicit
|
|
553
|
+
});
|
|
554
|
+
} catch (addErr) {
|
|
555
|
+
logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
|
|
556
|
+
try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
|
|
557
|
+
await jsm.consumers.add(streamName, {
|
|
558
|
+
durable_name: durableName,
|
|
559
|
+
filter_subject: privateSubject,
|
|
560
|
+
deliver_policy: DeliverPolicy.New,
|
|
561
|
+
ack_policy: AckPolicy.Explicit
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
consumer = await js.consumers.get(streamName, durableName);
|
|
565
|
+
}
|
|
515
566
|
|
|
516
567
|
const iter = await consumer.consume();
|
|
517
568
|
for await (const msg of iter) {
|
|
@@ -546,9 +597,9 @@ export default function register(api) {
|
|
|
546
597
|
const cInfo = await jsm.consumers.add(streamName, {
|
|
547
598
|
durable_name: durableName,
|
|
548
599
|
filter_subject: msgSubject,
|
|
549
|
-
deliver_policy: DeliverPolicy.
|
|
600
|
+
deliver_policy: DeliverPolicy.New, // Only new messages to avoid replay spam
|
|
550
601
|
ack_policy: AckPolicy.Explicit,
|
|
551
|
-
ack_wait: 30000,
|
|
602
|
+
ack_wait: 30000,
|
|
552
603
|
});
|
|
553
604
|
const consumer = await js.consumers.get(streamName, cInfo.name);
|
|
554
605
|
|
|
@@ -556,31 +607,7 @@ export default function register(api) {
|
|
|
556
607
|
jobSubscriptions.set(jobId, iter);
|
|
557
608
|
|
|
558
609
|
(async () => {
|
|
559
|
-
|
|
560
|
-
let isBursting = true;
|
|
561
|
-
|
|
562
|
-
// Small delay to collect initial "DeliverPolicy.All" burst
|
|
563
|
-
const burstTimeout = setTimeout(() => {
|
|
564
|
-
if (isBursting && historyBuffer.length > 0) {
|
|
565
|
-
isBursting = false;
|
|
566
|
-
logger.info(`[pier-connector] 📦 Batch processing ${historyBuffer.length} historical messages for ${jobId}`);
|
|
567
|
-
|
|
568
|
-
const combinedHistory = historyBuffer.map(m => `User: ${m.content}`).join('\n');
|
|
569
|
-
const lastSenderId = historyBuffer[historyBuffer.length - 1].sender_id;
|
|
570
|
-
|
|
571
|
-
receiveIncoming({
|
|
572
|
-
accountId: config.accountId || 'default',
|
|
573
|
-
senderId: `pier:${lastSenderId}`,
|
|
574
|
-
text: `[Conversation History]\n${combinedHistory}\n\nPlease continue based on the above history.`,
|
|
575
|
-
}, jobId).catch(err => {
|
|
576
|
-
logger.error(`[pier-connector] Failed to process batch history: ${err.message}`);
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
historyBuffer = [];
|
|
580
|
-
} else {
|
|
581
|
-
isBursting = false;
|
|
582
|
-
}
|
|
583
|
-
}, 1000); // 1s burst window
|
|
610
|
+
logger.info(`[pier-connector] 👂 Monitoring new messages for job ${jobId}`);
|
|
584
611
|
|
|
585
612
|
for await (const msg of iter) {
|
|
586
613
|
try {
|
|
@@ -613,21 +640,15 @@ export default function register(api) {
|
|
|
613
640
|
msg_id: msgPayload.id,
|
|
614
641
|
reader_id: config.nodeId
|
|
615
642
|
})));
|
|
616
|
-
logger.
|
|
643
|
+
logger.debug(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
|
|
617
644
|
} catch (err) {
|
|
618
645
|
logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
|
|
619
646
|
}
|
|
620
647
|
}
|
|
621
648
|
|
|
622
649
|
const content = msgPayload.content;
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
historyBuffer.push(msgPayload);
|
|
626
|
-
msg.ack();
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
|
|
650
|
+
logger.info(`[pier-connector] 📥 Incoming message for ${jobId}: "${truncate(content, 40)}"`);
|
|
651
|
+
|
|
631
652
|
const senderCore = msgPayload.sender_id;
|
|
632
653
|
|
|
633
654
|
activeNodeJobs.set(jobId, {
|
|
@@ -635,7 +656,7 @@ export default function register(api) {
|
|
|
635
656
|
isRealtimeMsg: true
|
|
636
657
|
});
|
|
637
658
|
|
|
638
|
-
//
|
|
659
|
+
// Trigger agent
|
|
639
660
|
await receiveIncoming({
|
|
640
661
|
accountId: config.accountId || 'default',
|
|
641
662
|
senderId: `pier:${senderCore}`,
|
|
@@ -908,29 +929,21 @@ export default function register(api) {
|
|
|
908
929
|
}
|
|
909
930
|
|
|
910
931
|
try {
|
|
932
|
+
|
|
933
|
+
const config = getActiveConfig();
|
|
934
|
+
const subject = `jobs.job.${params.jobId}.msg`;
|
|
911
935
|
const payload = {
|
|
912
|
-
|
|
936
|
+
id: ethers.hexlify(ethers.randomBytes(16)),
|
|
937
|
+
job_id: params.jobId,
|
|
938
|
+
sender_id: config.nodeId,
|
|
939
|
+
sender_type: 'node',
|
|
913
940
|
content: params.text,
|
|
914
|
-
|
|
941
|
+
timestamp: new Date().toISOString(),
|
|
942
|
+
auth_token: config.secretKey // Secure token for backend validation
|
|
915
943
|
};
|
|
916
944
|
|
|
917
|
-
const subject = `jobs.job.${params.jobId}.msg`;
|
|
918
945
|
await js.publish(subject, new TextEncoder().encode(JSON.stringify(payload)));
|
|
919
946
|
|
|
920
|
-
// Also SAVE to database for persistence
|
|
921
|
-
const config = getActiveConfig();
|
|
922
|
-
await fetch(`${config.pierApiUrl}/jobs/${params.jobId}/messages`, {
|
|
923
|
-
method: 'POST',
|
|
924
|
-
headers: {
|
|
925
|
-
'Content-Type': 'application/json',
|
|
926
|
-
'X-API-Key': config.secretKey // Assuming secretKey can be used as API key for node-specific actions
|
|
927
|
-
},
|
|
928
|
-
body: JSON.stringify({
|
|
929
|
-
sender_id: config.nodeId,
|
|
930
|
-
content: params.text
|
|
931
|
-
})
|
|
932
|
-
});
|
|
933
|
-
|
|
934
947
|
return { content: [{ type: 'text', text: 'Message sent' }] };
|
|
935
948
|
} catch (err) {
|
|
936
949
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
|