@gholl-studio/pier-connector 0.2.30 → 0.2.32
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/openclaw.plugin.json +10 -0
- package/package.json +10 -3
- package/src/index.js +92 -101
- package/src/job-handler.js +2 -1
- package/src/compression.js +0 -12
- package/src/nats-client.js +0 -75
- package/src/protocol.js +0 -142
package/openclaw.plugin.json
CHANGED
|
@@ -59,6 +59,12 @@
|
|
|
59
59
|
"type": "string",
|
|
60
60
|
"default": "",
|
|
61
61
|
"description": "(Optional) Wallet address or account ID for receiving rewards"
|
|
62
|
+
},
|
|
63
|
+
"capabilities": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": { "type": "string" },
|
|
66
|
+
"default": ["translation", "code-execution", "reasoning", "vision"],
|
|
67
|
+
"description": "List of AI capabilities this node supports (e.g., translation, gpu:4090)"
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
},
|
|
@@ -102,6 +108,10 @@
|
|
|
102
108
|
"walletAddress": {
|
|
103
109
|
"label": "Wallet Address (Rewards)",
|
|
104
110
|
"placeholder": "0x..."
|
|
111
|
+
},
|
|
112
|
+
"capabilities": {
|
|
113
|
+
"label": "Capabilities",
|
|
114
|
+
"placeholder": "translation, reasoning, ..."
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
117
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gholl-studio/pier-connector",
|
|
3
3
|
"author": "gholl",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.32",
|
|
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",
|
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
]
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"dev": "vite",
|
|
23
|
+
"build": "tsc -b && vite build",
|
|
24
|
+
"lint": "eslint .",
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"test:watch": "node --watch test-standalone.js"
|
|
24
27
|
},
|
|
25
28
|
"keywords": [
|
|
26
29
|
"openclaw",
|
|
@@ -31,6 +34,7 @@
|
|
|
31
34
|
],
|
|
32
35
|
"license": "MIT",
|
|
33
36
|
"dependencies": {
|
|
37
|
+
"@gholl-studio/pier-sdk": "^1.0.0",
|
|
34
38
|
"@nats-io/jetstream": "^3.3.1",
|
|
35
39
|
"@nats-io/transport-node": "^3.0.0",
|
|
36
40
|
"ethers": "^6.16.0",
|
|
@@ -44,5 +48,8 @@
|
|
|
44
48
|
"openclaw": {
|
|
45
49
|
"optional": true
|
|
46
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"dotenv": "^17.3.1"
|
|
47
54
|
}
|
|
48
55
|
}
|
package/src/index.js
CHANGED
|
@@ -8,13 +8,12 @@
|
|
|
8
8
|
* 4. A command ("/pier") for checking connection status
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { PierClient, protocol } from '@gholl-studio/pier-sdk';
|
|
12
|
+
const { createRequestPayload, createResultPayload, createErrorPayload } = protocol;
|
|
12
13
|
import { parseJob, safeRespond, truncate } from './job-handler.js';
|
|
13
|
-
import { createRequestPayload, createResultPayload, createErrorPayload } from './protocol.js';
|
|
14
14
|
import { DEFAULTS } from './config.js';
|
|
15
15
|
import { jetstream, jetstreamManager, AckPolicy, DeliverPolicy } from '@nats-io/jetstream';
|
|
16
16
|
import inquirer from 'inquirer';
|
|
17
|
-
import { ethers } from 'ethers';
|
|
18
17
|
import fs from 'fs';
|
|
19
18
|
import path from 'path';
|
|
20
19
|
|
|
@@ -69,6 +68,7 @@ export default function register(api) {
|
|
|
69
68
|
queueGroup: merged.queueGroup || DEFAULTS.QUEUE_GROUP,
|
|
70
69
|
agentId: merged.agentId || DEFAULTS.AGENT_ID,
|
|
71
70
|
walletAddress: merged.walletAddress || DEFAULTS.WALLET_ADDRESS,
|
|
71
|
+
capabilities: merged.capabilities || ['translation', 'code-execution', 'reasoning', 'vision'],
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -80,6 +80,11 @@ export default function register(api) {
|
|
|
80
80
|
constructor(config) {
|
|
81
81
|
this.config = config;
|
|
82
82
|
this.accountId = config.accountId;
|
|
83
|
+
this.client = new PierClient({
|
|
84
|
+
apiUrl: config.pierApiUrl,
|
|
85
|
+
natsUrl: config.natsUrl,
|
|
86
|
+
logger
|
|
87
|
+
});
|
|
83
88
|
this.nc = null;
|
|
84
89
|
this.js = null;
|
|
85
90
|
this.jsm = null;
|
|
@@ -95,26 +100,37 @@ export default function register(api) {
|
|
|
95
100
|
this.connectedAt = null;
|
|
96
101
|
}
|
|
97
102
|
|
|
103
|
+
async init() {
|
|
104
|
+
try {
|
|
105
|
+
this.nc = await this.client.connectNats();
|
|
106
|
+
this.js = jetstream(this.nc);
|
|
107
|
+
this.jsm = await jetstreamManager(this.nc);
|
|
108
|
+
|
|
109
|
+
this.connectedAt = new Date();
|
|
110
|
+
this.connectionStatus = 'connected';
|
|
111
|
+
|
|
112
|
+
// Subscribe to tasks
|
|
113
|
+
await this.subscribeToTasks();
|
|
114
|
+
|
|
115
|
+
// Start heartbeat
|
|
116
|
+
this.startHeartbeat();
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
this.connectionStatus = 'error';
|
|
121
|
+
logger.error(`[pier-connector] Account ${this.accountId} failed to connect: ${err.message}`);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
98
126
|
async heartbeat() {
|
|
99
127
|
if (!this.config.nodeId || !this.config.secretKey) return null;
|
|
100
128
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
secret_key: this.config.secretKey,
|
|
107
|
-
capabilities: ['translation', 'code-execution', 'reasoning', 'vision'],
|
|
108
|
-
description: `OpenClaw Node (${this.config.nodeId.substring(0, 8)}) [${this.accountId}]`,
|
|
109
|
-
}),
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (!resp.ok) {
|
|
113
|
-
const errText = await resp.text();
|
|
114
|
-
throw new Error(`Heartbeat failed (${resp.status}): ${errText}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const data = await resp.json();
|
|
129
|
+
const data = await this.client.heartbeat(
|
|
130
|
+
this.config.nodeId,
|
|
131
|
+
this.config.secretKey,
|
|
132
|
+
this.config.capabilities
|
|
133
|
+
);
|
|
118
134
|
this.lastHeartbeatError = null;
|
|
119
135
|
return data.nats_config || null;
|
|
120
136
|
} catch (err) {
|
|
@@ -125,28 +141,7 @@ export default function register(api) {
|
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
async claimJob(jobId) {
|
|
128
|
-
|
|
129
|
-
const resp = await fetch(`${this.config.pierApiUrl}/jobs/${jobId}/claim`, {
|
|
130
|
-
method: 'POST',
|
|
131
|
-
headers: {
|
|
132
|
-
'Content-Type': 'application/json',
|
|
133
|
-
'Authorization': `Bearer ${this.config.secretKey}`,
|
|
134
|
-
'X-Node-Id': this.config.nodeId
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
if (!resp.ok) {
|
|
139
|
-
if (resp.status === 409) {
|
|
140
|
-
logger.warn(`[pier-connector][${this.accountId}] 🚫 Job ${jobId} already claimed.`);
|
|
141
|
-
return { ok: false, alreadyClaimed: true };
|
|
142
|
-
}
|
|
143
|
-
const errData = await resp.json().catch(() => ({}));
|
|
144
|
-
return { ok: false, error: errData.error || resp.statusText };
|
|
145
|
-
}
|
|
146
|
-
return { ok: true };
|
|
147
|
-
} catch (err) {
|
|
148
|
-
return { ok: false, error: err.message };
|
|
149
|
-
}
|
|
144
|
+
return await this.client.claimJob(jobId, this.config.nodeId, this.config.secretKey);
|
|
150
145
|
}
|
|
151
146
|
|
|
152
147
|
/**
|
|
@@ -462,30 +457,10 @@ export default function register(api) {
|
|
|
462
457
|
}
|
|
463
458
|
|
|
464
459
|
async autoRegister() {
|
|
465
|
-
const wallet = new ethers.Wallet(this.config.privateKey);
|
|
466
|
-
const address = wallet.address;
|
|
467
|
-
|
|
468
|
-
const challengeRes = await fetch(`${this.config.pierApiUrl}/auth/challenge?wallet_address=${address}`);
|
|
469
|
-
const { challenge } = await challengeRes.json();
|
|
470
|
-
const signature = await wallet.signMessage(challenge);
|
|
471
|
-
|
|
472
|
-
const loginRes = await fetch(`${this.config.pierApiUrl}/auth/login`, {
|
|
473
|
-
method: 'POST',
|
|
474
|
-
headers: { 'Content-Type': 'application/json' },
|
|
475
|
-
body: JSON.stringify({ wallet_address: address, challenge, signature })
|
|
476
|
-
});
|
|
477
|
-
const { api_key } = await loginRes.json();
|
|
478
|
-
|
|
479
460
|
const hostName = `${api?.getRuntimeInfo?.()?.hostname ?? 'Auto'}-${this.accountId}`;
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
body: JSON.stringify({ wallet_address: address, name: hostName })
|
|
484
|
-
});
|
|
485
|
-
const { id, secret_key } = await regRes.json();
|
|
486
|
-
|
|
487
|
-
this.config.nodeId = id;
|
|
488
|
-
this.config.secretKey = secret_key;
|
|
461
|
+
const { nodeId, secretKey } = await this.client.autoRegister(this.config.privateKey, hostName);
|
|
462
|
+
this.config.nodeId = nodeId;
|
|
463
|
+
this.config.secretKey = secretKey;
|
|
489
464
|
}
|
|
490
465
|
|
|
491
466
|
async start() {
|
|
@@ -856,6 +831,41 @@ export default function register(api) {
|
|
|
856
831
|
comment: { type: 'string', description: 'A short review' }
|
|
857
832
|
}, 'user');
|
|
858
833
|
|
|
834
|
+
registerSystemActionTool('pier_reject_task', 'Reject an offered task from the employer in the current chat', 'task_reject', {
|
|
835
|
+
reason: { type: 'string', description: 'Reason for rejection' }
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
registerSystemActionTool('pier_fail_task', 'Report that the task has failed during execution', 'task_error', {
|
|
839
|
+
error: { type: 'string', description: 'Description of the error' }
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
registerSystemActionTool('pier_cancel_task', 'Cancel a previously proposed task', 'task_cancel', {
|
|
843
|
+
reason: { type: 'string', description: 'Reason for cancellation' }
|
|
844
|
+
}, 'user');
|
|
845
|
+
|
|
846
|
+
api.registerTool({
|
|
847
|
+
name: 'pier_get_profile',
|
|
848
|
+
description: 'Get the current Pier profile, balance, and node stats.',
|
|
849
|
+
parameters: {
|
|
850
|
+
type: 'object',
|
|
851
|
+
properties: {
|
|
852
|
+
accountId: { type: 'string' }
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
async execute(_id, params) {
|
|
856
|
+
const accountId = params.accountId || 'default';
|
|
857
|
+
const robot = instances.get(accountId) || instances.values().next().value;
|
|
858
|
+
if (!robot) return { content: [{ type: 'text', text: 'Error: Robot not found' }] };
|
|
859
|
+
|
|
860
|
+
try {
|
|
861
|
+
const profile = await robot.client.getUserProfile(robot.config.secretKey);
|
|
862
|
+
return { content: [{ type: 'text', text: JSON.stringify(profile, null, 2) }] };
|
|
863
|
+
} catch (err) {
|
|
864
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}, { optional: true });
|
|
868
|
+
|
|
859
869
|
// ── 4. Register /pier status command ───────────────────────────────
|
|
860
870
|
|
|
861
871
|
api.registerCommand({
|
|
@@ -939,55 +949,35 @@ export default function register(api) {
|
|
|
939
949
|
{ type: 'input', name: 'pierApiUrl', message: 'Pier API URL:', default: currentConfig.pierApiUrl },
|
|
940
950
|
{ type: 'input', name: 'nodeId', message: 'Bot Node ID (UUID):', default: finalNodeId },
|
|
941
951
|
{ type: 'password', name: 'secretKey', message: 'Bot Secret Key:', default: finalSecretKey },
|
|
942
|
-
{ type: 'input', name: 'walletAddress', message: 'Your Wallet Address (for payout):', default: finalWallet }
|
|
952
|
+
{ type: 'input', name: 'walletAddress', message: 'Your Wallet Address (for payout):', default: finalWallet },
|
|
953
|
+
{ type: 'input', name: 'capabilities', message: 'Capabilities (comma-separated):', default: (currentConfig.capabilities || []).join(', ') }
|
|
943
954
|
]);
|
|
944
955
|
finalNodeId = manualAnswers.nodeId;
|
|
945
956
|
finalSecretKey = manualAnswers.secretKey;
|
|
946
957
|
finalWallet = manualAnswers.walletAddress;
|
|
958
|
+
currentConfig.capabilities = manualAnswers.capabilities ? manualAnswers.capabilities.split(',').map(s => s.trim()) : currentConfig.capabilities;
|
|
947
959
|
currentConfig.pierApiUrl = manualAnswers.pierApiUrl;
|
|
948
960
|
} else if (setupMethod === 'auto') {
|
|
949
961
|
const autoAnswers = await inquirer.prompt([
|
|
950
962
|
{ type: 'input', name: 'pierApiUrl', message: 'Pier API URL:', default: currentConfig.pierApiUrl },
|
|
951
|
-
{ type: 'password', name: 'privateKey', message: 'Your Wallet Private Key (Hex, 0x...):', default: finalPrivateKey }
|
|
963
|
+
{ type: 'password', name: 'privateKey', message: 'Your Wallet Private Key (Hex, 0x...):', default: finalPrivateKey },
|
|
964
|
+
{ type: 'input', name: 'capabilities', message: 'Capabilities (comma-separated):', default: (currentConfig.capabilities || []).join(', ') }
|
|
952
965
|
]);
|
|
953
966
|
console.log('\n\x1b[36mRegistering your node...\x1b[0m');
|
|
954
967
|
try {
|
|
955
|
-
const
|
|
956
|
-
console.log(`\x1b[32m✔ Loaded wallet: ${wallet.address}\x1b[0m`);
|
|
957
|
-
finalWallet = finalWallet || wallet.address;
|
|
958
|
-
finalPrivateKey = autoAnswers.privateKey;
|
|
959
|
-
currentConfig.pierApiUrl = autoAnswers.pierApiUrl;
|
|
960
|
-
|
|
961
|
-
const challengeRes = await fetch(`${currentConfig.pierApiUrl}/auth/challenge?wallet_address=${wallet.address}`);
|
|
962
|
-
if (!challengeRes.ok) throw new Error("Failed to get challenge");
|
|
963
|
-
const { challenge } = await challengeRes.json();
|
|
964
|
-
|
|
965
|
-
const signature = await wallet.signMessage(challenge);
|
|
966
|
-
|
|
967
|
-
const loginRes = await fetch(`${currentConfig.pierApiUrl}/auth/login`, {
|
|
968
|
-
method: 'POST',
|
|
969
|
-
headers: { 'Content-Type': 'application/json' },
|
|
970
|
-
body: JSON.stringify({ wallet_address: wallet.address, challenge, signature })
|
|
971
|
-
});
|
|
972
|
-
if (!loginRes.ok) throw new Error("Failed to login");
|
|
973
|
-
const { api_key } = await loginRes.json();
|
|
974
|
-
|
|
968
|
+
const tempClient = new PierClient({ apiUrl: autoAnswers.pierApiUrl });
|
|
975
969
|
const hostName = api?.getRuntimeInfo?.()?.hostname ?? 'Auto-Node';
|
|
976
|
-
const regRes = await fetch(`${currentConfig.pierApiUrl}/nodes/register`, {
|
|
977
|
-
method: 'POST',
|
|
978
|
-
headers: {
|
|
979
|
-
'Content-Type': 'application/json',
|
|
980
|
-
'Authorization': `Bearer ${api_key}`
|
|
981
|
-
},
|
|
982
|
-
body: JSON.stringify({ wallet_address: wallet.address, name: hostName })
|
|
983
|
-
});
|
|
984
970
|
|
|
985
|
-
|
|
986
|
-
|
|
971
|
+
const { nodeId, secretKey, walletAddress } = await tempClient.autoRegister(autoAnswers.privateKey, hostName);
|
|
972
|
+
|
|
973
|
+
finalWallet = finalWallet || walletAddress;
|
|
974
|
+
finalPrivateKey = autoAnswers.privateKey;
|
|
975
|
+
currentConfig.pierApiUrl = autoAnswers.pierApiUrl;
|
|
976
|
+
currentConfig.capabilities = autoAnswers.capabilities ? autoAnswers.capabilities.split(',').map(s => s.trim()) : currentConfig.capabilities;
|
|
977
|
+
finalNodeId = nodeId;
|
|
978
|
+
finalSecretKey = secretKey;
|
|
987
979
|
|
|
988
|
-
|
|
989
|
-
finalSecretKey = secret_key;
|
|
990
|
-
console.log(`\x1b[32m✔ Node registered automatically! Node ID: ${id}\x1b[0m`);
|
|
980
|
+
console.log(`\x1b[32m✔ Node registered automatically! Node ID: ${nodeId}\x1b[0m`);
|
|
991
981
|
} catch (err) {
|
|
992
982
|
console.error(`\x1b[31m✖ Failed to auto-register: ${err.message}\x1b[0m`);
|
|
993
983
|
return; // Stop setup
|
|
@@ -1033,7 +1023,8 @@ export default function register(api) {
|
|
|
1033
1023
|
nodeId: finalNodeId,
|
|
1034
1024
|
secretKey: finalSecretKey,
|
|
1035
1025
|
walletAddress: finalWallet,
|
|
1036
|
-
agentId: finalAgentId
|
|
1026
|
+
agentId: finalAgentId,
|
|
1027
|
+
capabilities: currentConfig.capabilities
|
|
1037
1028
|
};
|
|
1038
1029
|
|
|
1039
1030
|
if (setupMethod === 'auto') {
|
package/src/job-handler.js
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* @param {object} logger
|
|
21
21
|
* @returns {{ ok: true, job: object } | { ok: false, error: string }}
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
23
|
+
import { protocol } from '@gholl-studio/pier-sdk';
|
|
24
|
+
const { normalizeInboundPayload } = protocol;
|
|
24
25
|
|
|
25
26
|
export function parseJob(msg, logger) {
|
|
26
27
|
let raw;
|
package/src/compression.js
DELETED
package/src/nats-client.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NATS WebSocket connection manager.
|
|
3
|
-
*
|
|
4
|
-
* Uses `wsconnect` from @nats-io/transport-node to establish a
|
|
5
|
-
* WebSocket connection to the NATS server.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { wsconnect } from '@nats-io/transport-node';
|
|
9
|
-
import { jetstream } from '@nats-io/jetstream';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Create and return a NATS connection over WebSocket.
|
|
13
|
-
*
|
|
14
|
-
* @param {string} url – WebSocket URL, e.g. "wss://pier.gholl.com/nexus"
|
|
15
|
-
* @param {object} logger – OpenClaw logger (api.logger)
|
|
16
|
-
* @returns {Promise<import('@nats-io/transport-node').NatsConnection>}
|
|
17
|
-
*/
|
|
18
|
-
export async function createNatsConnection(url, logger) {
|
|
19
|
-
logger.info(`[pier-connector] Connecting to NATS at ${url} …`);
|
|
20
|
-
|
|
21
|
-
const nc = await wsconnect({ servers: url });
|
|
22
|
-
|
|
23
|
-
logger.info('[pier-connector] ✔ NATS connected successfully');
|
|
24
|
-
|
|
25
|
-
// ---------- lifecycle event monitoring ----------
|
|
26
|
-
|
|
27
|
-
// Fired when the client is disconnected from the server
|
|
28
|
-
(async () => {
|
|
29
|
-
for await (const status of nc.status()) {
|
|
30
|
-
switch (status.type) {
|
|
31
|
-
case 'disconnect':
|
|
32
|
-
logger.warn(`[pier-connector] NATS disconnected: ${status.data}`);
|
|
33
|
-
break;
|
|
34
|
-
|
|
35
|
-
case 'reconnect':
|
|
36
|
-
logger.info(`[pier-connector] NATS reconnected to ${status.data}`);
|
|
37
|
-
break;
|
|
38
|
-
|
|
39
|
-
case 'reconnecting':
|
|
40
|
-
logger.warn('[pier-connector] NATS reconnecting …');
|
|
41
|
-
break;
|
|
42
|
-
|
|
43
|
-
case 'error':
|
|
44
|
-
logger.error(`[pier-connector] NATS error: ${status.data}`);
|
|
45
|
-
break;
|
|
46
|
-
|
|
47
|
-
case 'update':
|
|
48
|
-
logger.info(`[pier-connector] NATS cluster update: ${JSON.stringify(status.data)}`);
|
|
49
|
-
break;
|
|
50
|
-
|
|
51
|
-
default:
|
|
52
|
-
logger.info(`[pier-connector] NATS status: ${status.type}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
})();
|
|
56
|
-
|
|
57
|
-
return nc;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Gracefully drain (flush pending + close) a NATS connection.
|
|
62
|
-
*
|
|
63
|
-
* @param {import('@nats-io/transport-node').NatsConnection} nc
|
|
64
|
-
* @param {object} logger
|
|
65
|
-
*/
|
|
66
|
-
export async function drainConnection(nc, logger) {
|
|
67
|
-
if (!nc) return;
|
|
68
|
-
try {
|
|
69
|
-
logger.info('[pier-connector] Draining NATS connection …');
|
|
70
|
-
await nc.drain();
|
|
71
|
-
logger.info('[pier-connector] ✔ NATS connection drained');
|
|
72
|
-
} catch (err) {
|
|
73
|
-
logger.error(`[pier-connector] Error draining NATS: ${err.message}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/protocol.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Protocol Definitions for Pier Connector
|
|
3
|
-
*
|
|
4
|
-
* Standardizes outbound and inbound JSON boundaries for NATS.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export const PROTOCOL_VERSION = '1.1';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Creates a standard task request payload to send to Pier.
|
|
11
|
-
*
|
|
12
|
-
* @param {object} params
|
|
13
|
-
* @param {string} params.task
|
|
14
|
-
* @param {string} [params.systemPrompt]
|
|
15
|
-
* @param {number} [params.timeoutMs]
|
|
16
|
-
* @param {object} [params.meta]
|
|
17
|
-
* @param {string} [params.targetNodeId]
|
|
18
|
-
* @param {string} [params.parentJobId]
|
|
19
|
-
* @returns {object}
|
|
20
|
-
*/
|
|
21
|
-
export function createRequestPayload({ task, systemPrompt, timeoutMs, meta, targetNodeId, parentJobId }) {
|
|
22
|
-
return {
|
|
23
|
-
version: PROTOCOL_VERSION,
|
|
24
|
-
id: crypto.randomUUID(),
|
|
25
|
-
type: 'task', // Changed from task_request to task to align with backend models
|
|
26
|
-
task,
|
|
27
|
-
systemPrompt,
|
|
28
|
-
timeoutMs: timeoutMs || 60000,
|
|
29
|
-
meta: meta || {},
|
|
30
|
-
targetNodeId: targetNodeId || null,
|
|
31
|
-
parentJobId: parentJobId || null,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Creates a standard task result payload to respond to Pier.
|
|
37
|
-
*
|
|
38
|
-
* @param {object} params
|
|
39
|
-
* @param {string} params.id
|
|
40
|
-
* @param {string} params.reply
|
|
41
|
-
* @param {number} [params.latencyMs]
|
|
42
|
-
* @param {string} [params.workerId]
|
|
43
|
-
* @param {string} [params.walletAddress]
|
|
44
|
-
* @returns {object}
|
|
45
|
-
*/
|
|
46
|
-
export function createResultPayload({ id, reply, latencyMs, workerId, walletAddress }) {
|
|
47
|
-
return {
|
|
48
|
-
version: PROTOCOL_VERSION,
|
|
49
|
-
id,
|
|
50
|
-
type: 'task_result',
|
|
51
|
-
ok: true,
|
|
52
|
-
result: reply,
|
|
53
|
-
latencyMs,
|
|
54
|
-
worker: {
|
|
55
|
-
id: workerId || 'anonymous-worker',
|
|
56
|
-
wallet: walletAddress || null,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Creates a standard task error payload to respond to Pier.
|
|
63
|
-
*
|
|
64
|
-
* @param {object} params
|
|
65
|
-
* @param {string} params.id
|
|
66
|
-
* @param {string} params.errorCode
|
|
67
|
-
* @param {string} params.errorMessage
|
|
68
|
-
* @param {string} [params.workerId]
|
|
69
|
-
* @param {string} [params.walletAddress]
|
|
70
|
-
* @returns {object}
|
|
71
|
-
*/
|
|
72
|
-
export function createErrorPayload({ id, errorCode, errorMessage, workerId, walletAddress }) {
|
|
73
|
-
return {
|
|
74
|
-
version: PROTOCOL_VERSION,
|
|
75
|
-
id,
|
|
76
|
-
type: 'task_error',
|
|
77
|
-
ok: false,
|
|
78
|
-
error: {
|
|
79
|
-
code: errorCode,
|
|
80
|
-
message: errorMessage,
|
|
81
|
-
},
|
|
82
|
-
worker: {
|
|
83
|
-
id: workerId || 'anonymous-worker',
|
|
84
|
-
wallet: walletAddress || null,
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Validate and normalize an incoming NATS payload to a standard job format.
|
|
91
|
-
*
|
|
92
|
-
* @param {object} payload
|
|
93
|
-
* @returns {{ ok: true, job: object } | { ok: false, error: string }}
|
|
94
|
-
*/
|
|
95
|
-
export function normalizeInboundPayload(payload) {
|
|
96
|
-
// If it's a strict v1.1+ protocol request
|
|
97
|
-
if (payload.type === 'task' || payload.type === 'task_request') {
|
|
98
|
-
if (!payload.task) {
|
|
99
|
-
return { ok: false, error: 'Missing "task" field in task payload' };
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
ok: true,
|
|
103
|
-
job: {
|
|
104
|
-
id: payload.id || crypto.randomUUID(),
|
|
105
|
-
task: payload.task,
|
|
106
|
-
systemPrompt: payload.systemPrompt,
|
|
107
|
-
meta: payload.meta || {},
|
|
108
|
-
targetNodeId: payload.targetNodeId || null,
|
|
109
|
-
parentJobId: payload.parentJobId || null,
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Support for Join-Chat wakeup signals (BUG-26/29)
|
|
115
|
-
if (payload.type === 'wakeup') {
|
|
116
|
-
const jobId = payload.jobId || payload.id;
|
|
117
|
-
if (!jobId) return { ok: false, error: 'Missing jobId in wakeup payload' };
|
|
118
|
-
return {
|
|
119
|
-
ok: true,
|
|
120
|
-
job: {
|
|
121
|
-
id: jobId,
|
|
122
|
-
type: 'wakeup'
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Fallback for legacy / generic JSON structures
|
|
128
|
-
const task = payload.task ?? payload.prompt ?? payload.content ?? '';
|
|
129
|
-
if (!task) {
|
|
130
|
-
return { ok: false, error: 'Missing "task" or "prompt" field in payload' };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
ok: true,
|
|
135
|
-
job: {
|
|
136
|
-
id: payload.id || crypto.randomUUID(),
|
|
137
|
-
task,
|
|
138
|
-
systemPrompt: payload.systemPrompt,
|
|
139
|
-
meta: payload.meta || {},
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
}
|