@gholl-studio/pier-connector 0.1.0 → 0.1.2
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 +57 -24
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.
|
|
4
|
+
"version": "0.1.2",
|
|
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
|
@@ -42,14 +42,15 @@ export default function register(api) {
|
|
|
42
42
|
|
|
43
43
|
/** Connection status for the /pier command */
|
|
44
44
|
let connectionStatus = 'disconnected';
|
|
45
|
+
const jobSubscriptions = new Map();
|
|
46
|
+
const jobStopTimeouts = new Map();
|
|
47
|
+
const activeNodeJobs = new Map();
|
|
48
|
+
|
|
45
49
|
let jobsReceived = 0;
|
|
46
50
|
let jobsCompleted = 0;
|
|
47
51
|
let jobsFailed = 0;
|
|
48
52
|
let connectedAt = null;
|
|
49
|
-
|
|
50
|
-
/** @type {Map<string, import('@nats-io/transport-node').Subscription>} */
|
|
51
|
-
const jobSubscriptions = new Map();
|
|
52
|
-
const jobStopTimeouts = new Map();
|
|
53
|
+
let lastHeartbeatError = null;
|
|
53
54
|
|
|
54
55
|
// ── resolve plugin config ──────────────────────────────────────────
|
|
55
56
|
|
|
@@ -117,8 +118,10 @@ const jobStopTimeouts = new Map();
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
const data = await resp.json();
|
|
121
|
+
lastHeartbeatError = null;
|
|
120
122
|
return data.nats_config || null;
|
|
121
123
|
} catch (err) {
|
|
124
|
+
lastHeartbeatError = err.message;
|
|
122
125
|
logger.warn(`[pier-connector] Heartbeat failed: ${err.message}`);
|
|
123
126
|
return null;
|
|
124
127
|
}
|
|
@@ -148,15 +151,10 @@ const jobStopTimeouts = new Map();
|
|
|
148
151
|
accountId: accountId ?? 'default',
|
|
149
152
|
enabled: true,
|
|
150
153
|
},
|
|
154
|
+
isConfigured: (account) => Boolean(account.nodeId && account.secretKey) || Boolean(account.privateKey),
|
|
151
155
|
},
|
|
152
156
|
|
|
153
157
|
setup: {
|
|
154
|
-
checkAccountStatus: async ({ accountId }) => {
|
|
155
|
-
if (connectionStatus === 'connected') return { status: 'connected' };
|
|
156
|
-
if (connectionStatus === 'error') return { status: 'error', details: 'NATS connection error' };
|
|
157
|
-
if (connectionStatus === 'connecting') return { status: 'connecting' };
|
|
158
|
-
return { status: 'disconnected', details: connectionStatus };
|
|
159
|
-
},
|
|
160
158
|
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
161
159
|
const draft = structuredClone(cfg);
|
|
162
160
|
draft.channels = draft.channels || {};
|
|
@@ -188,7 +186,15 @@ const jobStopTimeouts = new Map();
|
|
|
188
186
|
* Send a reply text back through the NATS response.
|
|
189
187
|
* This is called by OpenClaw's agent after processing a job.
|
|
190
188
|
*/
|
|
191
|
-
sendText: async (
|
|
189
|
+
sendText: async (ctx) => {
|
|
190
|
+
const text = ctx.text;
|
|
191
|
+
let metadata = ctx.metadata;
|
|
192
|
+
|
|
193
|
+
if (!metadata && ctx.to) {
|
|
194
|
+
const toId = ctx.to.replace(/^pier:/, '');
|
|
195
|
+
metadata = activeNodeJobs.get(toId);
|
|
196
|
+
}
|
|
197
|
+
|
|
192
198
|
const jobId = metadata?.pierJobId;
|
|
193
199
|
const msg = metadata?.pierNatsMsg;
|
|
194
200
|
const isRealtimeMsg = metadata?.isRealtimeMsg;
|
|
@@ -244,7 +250,7 @@ const jobStopTimeouts = new Map();
|
|
|
244
250
|
jobSubscriptions.delete(jobId);
|
|
245
251
|
jobStopTimeouts.delete(jobId);
|
|
246
252
|
}
|
|
247
|
-
},
|
|
253
|
+
}, 3600000); // 1 hour inactivity timeout
|
|
248
254
|
|
|
249
255
|
jobStopTimeouts.set(jobId, timeout);
|
|
250
256
|
}
|
|
@@ -253,6 +259,25 @@ const jobStopTimeouts = new Map();
|
|
|
253
259
|
return { ok: true };
|
|
254
260
|
},
|
|
255
261
|
},
|
|
262
|
+
|
|
263
|
+
status: {
|
|
264
|
+
defaultRuntime: {
|
|
265
|
+
accountId: 'default',
|
|
266
|
+
running: false,
|
|
267
|
+
connected: false,
|
|
268
|
+
},
|
|
269
|
+
buildAccountSnapshot: ({ account }) => {
|
|
270
|
+
return {
|
|
271
|
+
accountId: account.accountId,
|
|
272
|
+
name: account.name,
|
|
273
|
+
enabled: account.enabled,
|
|
274
|
+
configured: Boolean(account.nodeId && account.secretKey) || Boolean(account.privateKey),
|
|
275
|
+
running: connectionStatus === 'connected' || connectionStatus === 'connecting',
|
|
276
|
+
connected: connectionStatus === 'connected' && !lastHeartbeatError,
|
|
277
|
+
lastError: lastHeartbeatError,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
},
|
|
256
281
|
};
|
|
257
282
|
|
|
258
283
|
api.registerChannel({ plugin: pierChannel });
|
|
@@ -473,15 +498,18 @@ const jobStopTimeouts = new Map();
|
|
|
473
498
|
const content = msgPayload.content;
|
|
474
499
|
logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
|
|
475
500
|
|
|
501
|
+
const senderCore = msgPayload.sender_id;
|
|
502
|
+
|
|
503
|
+
activeNodeJobs.set(senderCore, {
|
|
504
|
+
pierJobId: jobId,
|
|
505
|
+
isRealtimeMsg: true
|
|
506
|
+
});
|
|
507
|
+
|
|
476
508
|
await api.runtime.sendIncoming({
|
|
477
509
|
channelId: 'pier',
|
|
478
510
|
accountId: config.accountId || 'default',
|
|
479
|
-
senderId: `pier:${
|
|
511
|
+
senderId: `pier:${senderCore}`,
|
|
480
512
|
text: content,
|
|
481
|
-
metadata: {
|
|
482
|
-
pierJobId: jobId,
|
|
483
|
-
isRealtimeMsg: true
|
|
484
|
-
}
|
|
485
513
|
});
|
|
486
514
|
}
|
|
487
515
|
} catch (err) {
|
|
@@ -537,18 +565,22 @@ const jobStopTimeouts = new Map();
|
|
|
537
565
|
logger.info(`[pier-connector] 📥 Received job ${job.id}: "${truncate(job.task, 60)}"`);
|
|
538
566
|
|
|
539
567
|
try {
|
|
568
|
+
const senderCore = job.meta?.sender ?? 'anonymous';
|
|
569
|
+
|
|
570
|
+
activeNodeJobs.set(senderCore, {
|
|
571
|
+
pierJobId: job.id,
|
|
572
|
+
pierNatsMsg: msg,
|
|
573
|
+
pierStartTime: startTime,
|
|
574
|
+
pierMeta: job.meta,
|
|
575
|
+
isRealtimeMsg: false
|
|
576
|
+
});
|
|
577
|
+
|
|
540
578
|
const inbound = {
|
|
541
579
|
channelId: 'pier',
|
|
542
580
|
accountId: config.accountId || 'default',
|
|
543
|
-
senderId: `pier:${
|
|
581
|
+
senderId: `pier:${senderCore}`,
|
|
544
582
|
text: job.task,
|
|
545
583
|
assignedAgentId: config.agentId || undefined,
|
|
546
|
-
metadata: {
|
|
547
|
-
pierJobId: job.id,
|
|
548
|
-
pierNatsMsg: msg,
|
|
549
|
-
pierStartTime: startTime,
|
|
550
|
-
pierMeta: job.meta,
|
|
551
|
-
},
|
|
552
584
|
};
|
|
553
585
|
|
|
554
586
|
if (job.systemPrompt) inbound.systemPrompt = job.systemPrompt;
|
|
@@ -786,6 +818,7 @@ const jobStopTimeouts = new Map();
|
|
|
786
818
|
`• Publish: ${config.publishSubject}`,
|
|
787
819
|
`• Uptime: ${uptime}`,
|
|
788
820
|
`• Jobs: ${jobsReceived} received, ${jobsCompleted} completed, ${jobsFailed} failed`,
|
|
821
|
+
lastHeartbeatError ? `• \x1b[31mHeartbeat Error: ${lastHeartbeatError}\x1b[0m` : `• Heartbeat: OK`,
|
|
789
822
|
].join('\n'),
|
|
790
823
|
};
|
|
791
824
|
},
|