@gholl-studio/pier-connector 0.0.1 → 0.0.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/openclaw.plugin.json +41 -14
- package/package.json +4 -3
- package/src/config.js +12 -3
- package/src/index.js +223 -114
- package/src/protocol.js +4 -1
package/openclaw.plugin.json
CHANGED
|
@@ -10,10 +10,30 @@
|
|
|
10
10
|
"type": "object",
|
|
11
11
|
"additionalProperties": false,
|
|
12
12
|
"properties": {
|
|
13
|
+
"apiUrl": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "https://pier.gholl.com",
|
|
16
|
+
"description": "Pier Backend API Base URL"
|
|
17
|
+
},
|
|
18
|
+
"apiKey": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"default": "",
|
|
21
|
+
"description": "User API Key for platform authentication"
|
|
22
|
+
},
|
|
23
|
+
"nodeId": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"default": "",
|
|
26
|
+
"description": "Unique identifier for this agent worker node (UUID)"
|
|
27
|
+
},
|
|
28
|
+
"secretKey": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"default": "",
|
|
31
|
+
"description": "Secret key for node authentication and heartbeats"
|
|
32
|
+
},
|
|
13
33
|
"natsUrl": {
|
|
14
34
|
"type": "string",
|
|
15
35
|
"default": "wss://pier.gholl.com/nexus",
|
|
16
|
-
"description": "NATS WebSocket server URL"
|
|
36
|
+
"description": "NATS WebSocket server URL (Overrides API dynamic config if set)"
|
|
17
37
|
},
|
|
18
38
|
"subject": {
|
|
19
39
|
"type": "string",
|
|
@@ -30,11 +50,6 @@
|
|
|
30
50
|
"default": "openclaw-workers",
|
|
31
51
|
"description": "NATS Queue Group to join for load balancing tasks"
|
|
32
52
|
},
|
|
33
|
-
"workerId": {
|
|
34
|
-
"type": "string",
|
|
35
|
-
"default": "",
|
|
36
|
-
"description": "(Optional) Unique identifier for this agent worker"
|
|
37
|
-
},
|
|
38
53
|
"walletAddress": {
|
|
39
54
|
"type": "string",
|
|
40
55
|
"default": "",
|
|
@@ -43,26 +58,38 @@
|
|
|
43
58
|
}
|
|
44
59
|
},
|
|
45
60
|
"uiHints": {
|
|
61
|
+
"apiUrl": {
|
|
62
|
+
"label": "Pier API URL",
|
|
63
|
+
"placeholder": "https://pier.gholl.com"
|
|
64
|
+
},
|
|
65
|
+
"apiKey": {
|
|
66
|
+
"label": "API Key",
|
|
67
|
+
"placeholder": "Bearer token ..."
|
|
68
|
+
},
|
|
69
|
+
"nodeId": {
|
|
70
|
+
"label": "Node ID",
|
|
71
|
+
"placeholder": "uuid-..."
|
|
72
|
+
},
|
|
73
|
+
"secretKey": {
|
|
74
|
+
"label": "Secret Key",
|
|
75
|
+
"placeholder": "sk_..."
|
|
76
|
+
},
|
|
46
77
|
"natsUrl": {
|
|
47
|
-
"label": "NATS WebSocket URL",
|
|
78
|
+
"label": "NATS WebSocket URL (Override)",
|
|
48
79
|
"placeholder": "wss://pier.gholl.com/nexus"
|
|
49
80
|
},
|
|
50
81
|
"subject": {
|
|
51
|
-
"label": "Subscribe Subject
|
|
82
|
+
"label": "Subscribe Subject",
|
|
52
83
|
"placeholder": "jobs.worker"
|
|
53
84
|
},
|
|
54
85
|
"publishSubject": {
|
|
55
|
-
"label": "Publish Subject
|
|
86
|
+
"label": "Publish Subject",
|
|
56
87
|
"placeholder": "jobs.submit"
|
|
57
88
|
},
|
|
58
89
|
"queueGroup": {
|
|
59
|
-
"label": "Queue Group
|
|
90
|
+
"label": "Queue Group",
|
|
60
91
|
"placeholder": "openclaw-workers"
|
|
61
92
|
},
|
|
62
|
-
"workerId": {
|
|
63
|
-
"label": "Worker ID",
|
|
64
|
-
"placeholder": "agent-001"
|
|
65
|
-
},
|
|
66
93
|
"walletAddress": {
|
|
67
94
|
"label": "Wallet Address (Rewards)",
|
|
68
95
|
"placeholder": "0x..."
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gholl-studio/pier-connector",
|
|
3
3
|
"author": "gholl",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.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",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
],
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@nats-io/transport-node": "^3.0.0"
|
|
34
|
+
"@nats-io/transport-node": "^3.0.0",
|
|
35
|
+
"inquirer": "^13.3.0"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"openclaw": ">=2.0.0"
|
|
@@ -41,4 +42,4 @@
|
|
|
41
42
|
"optional": true
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
}
|
|
45
|
+
}
|
package/src/config.js
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export const DEFAULTS = Object.freeze({
|
|
8
|
-
/**
|
|
8
|
+
/** Pier Backend API Base URL */
|
|
9
|
+
API_URL: 'https://pier.gholl.com',
|
|
10
|
+
|
|
11
|
+
/** User API Key for HTTP Authentication */
|
|
12
|
+
API_KEY: '',
|
|
13
|
+
|
|
14
|
+
/** NATS WebSocket server URL (usually provided by API) */
|
|
9
15
|
NATS_URL: 'wss://pier.gholl.com/nexus',
|
|
10
16
|
|
|
11
17
|
/** NATS subject to subscribe for incoming jobs */
|
|
@@ -17,8 +23,11 @@ export const DEFAULTS = Object.freeze({
|
|
|
17
23
|
/** Default Queue Group for processing jobs (prevents duplicate work) */
|
|
18
24
|
QUEUE_GROUP: 'openclaw-workers',
|
|
19
25
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
26
|
+
/** Unique identifier for this agent worker node (UUID) */
|
|
27
|
+
NODE_ID: '',
|
|
28
|
+
|
|
29
|
+
/** Secret key for node authentication and heartbeats */
|
|
30
|
+
SECRET_KEY: '',
|
|
22
31
|
|
|
23
32
|
/** Optional Wallet address to receive points/payments for completed tasks */
|
|
24
33
|
WALLET_ADDRESS: '',
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { createNatsConnection, drainConnection } from './nats-client.js';
|
|
|
12
12
|
import { parseJob, safeRespond, truncate } from './job-handler.js';
|
|
13
13
|
import { createRequestPayload, createResultPayload, createErrorPayload } from './protocol.js';
|
|
14
14
|
import { DEFAULTS } from './config.js';
|
|
15
|
+
import inquirer from 'inquirer';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* OpenClaw plugin register function.
|
|
@@ -41,15 +42,72 @@ export default function register(api) {
|
|
|
41
42
|
function resolveConfig() {
|
|
42
43
|
const cfg = api.config?.plugins?.entries?.['pier-connector']?.config ?? {};
|
|
43
44
|
return {
|
|
45
|
+
apiUrl: cfg.apiUrl || DEFAULTS.API_URL,
|
|
46
|
+
apiKey: cfg.apiKey || DEFAULTS.API_KEY,
|
|
47
|
+
nodeId: cfg.nodeId || DEFAULTS.NODE_ID,
|
|
48
|
+
secretKey: cfg.secretKey || DEFAULTS.SECRET_KEY,
|
|
44
49
|
natsUrl: cfg.natsUrl || DEFAULTS.NATS_URL,
|
|
45
50
|
subject: cfg.subject || DEFAULTS.SUBJECT,
|
|
46
51
|
publishSubject: cfg.publishSubject || DEFAULTS.PUBLISH_SUBJECT,
|
|
47
52
|
queueGroup: cfg.queueGroup || DEFAULTS.QUEUE_GROUP,
|
|
48
|
-
workerId: cfg.workerId || DEFAULTS.WORKER_ID,
|
|
49
53
|
walletAddress: cfg.walletAddress || DEFAULTS.WALLET_ADDRESS,
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
/** Heartbeat timer reference */
|
|
58
|
+
let heartbeatTimer = null;
|
|
59
|
+
|
|
60
|
+
// ── Registration & Heartbeat ───────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
async function registerNode(config) {
|
|
63
|
+
if (config.nodeId && config.secretKey) return { nodeId: config.nodeId, secretKey: config.secretKey };
|
|
64
|
+
|
|
65
|
+
logger.info('[pier-connector] 🆕 Registering node with Pier Backend…');
|
|
66
|
+
const resp = await fetch(`${config.apiUrl}/api/v1/nodes/register`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
wallet_address: config.walletAddress,
|
|
74
|
+
name: 'openclaw-node', // Could use OS hostname
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!resp.ok) {
|
|
79
|
+
const errText = await resp.text();
|
|
80
|
+
throw new Error(`Registration failed (${resp.status}): ${errText}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const data = await resp.json();
|
|
84
|
+
logger.info(`[pier-connector] ✅ Node registered! ID: ${data.id}`);
|
|
85
|
+
// Note: In a production plugin, we would call api.updateConfig here if available
|
|
86
|
+
return { nodeId: data.id, secretKey: data.secret_key, natsConfig: data.nats_config };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function heartbeatNode(config) {
|
|
90
|
+
const resp = await fetch(`${config.apiUrl}/api/v1/nodes/heartbeat`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
node_id: config.nodeId,
|
|
98
|
+
secret_key: config.secretKey,
|
|
99
|
+
capabilities: ['gpt-4o', 'llama3', 'vision'], // Example capabilities
|
|
100
|
+
description: 'OpenClaw powered intelligence node',
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!resp.ok) {
|
|
105
|
+
throw new Error(`Heartbeat failed (${resp.status})`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return await resp.json();
|
|
109
|
+
}
|
|
110
|
+
|
|
53
111
|
// ── 1. Register messaging channel ──────────────────────────────────
|
|
54
112
|
|
|
55
113
|
const pierChannel = {
|
|
@@ -96,7 +154,7 @@ export default function register(api) {
|
|
|
96
154
|
id: jobId,
|
|
97
155
|
reply: text,
|
|
98
156
|
latencyMs: elapsed ? Number(elapsed) : undefined,
|
|
99
|
-
workerId: resolveConfig().
|
|
157
|
+
workerId: resolveConfig().nodeId,
|
|
100
158
|
walletAddress: resolveConfig().walletAddress,
|
|
101
159
|
});
|
|
102
160
|
|
|
@@ -122,142 +180,121 @@ export default function register(api) {
|
|
|
122
180
|
id: 'pier-connector',
|
|
123
181
|
|
|
124
182
|
start: async () => {
|
|
125
|
-
const config = resolveConfig();
|
|
126
|
-
|
|
127
183
|
logger.info('[pier-connector] 🚀 Starting background service …');
|
|
128
|
-
|
|
129
|
-
logger.info(`[pier-connector] Subscribe subject : ${config.subject}`);
|
|
130
|
-
logger.info(`[pier-connector] Queue Group : ${config.queueGroup}`);
|
|
131
|
-
logger.info(`[pier-connector] Publish subject : ${config.publishSubject}`);
|
|
132
|
-
if (config.workerId) logger.info(`[pier-connector] Worker ID : ${config.workerId}`);
|
|
133
|
-
if (config.walletAddress) logger.info(`[pier-connector] Wallet : ${config.walletAddress}`);
|
|
184
|
+
let config = resolveConfig();
|
|
134
185
|
|
|
135
186
|
try {
|
|
136
|
-
//
|
|
137
|
-
|
|
187
|
+
// 1. Ensure node is registered
|
|
188
|
+
const reg = await registerNode(config);
|
|
189
|
+
config.nodeId = reg.nodeId;
|
|
190
|
+
config.secretKey = reg.secretKey;
|
|
191
|
+
const activeNatsUrl = config.natsUrl || reg.natsConfig?.url;
|
|
192
|
+
|
|
193
|
+
logger.info(`[pier-connector] API URL : ${config.apiUrl}`);
|
|
194
|
+
logger.info(`[pier-connector] NATS URL : ${activeNatsUrl}`);
|
|
195
|
+
logger.info(`[pier-connector] Node ID : ${config.nodeId}`);
|
|
196
|
+
if (config.walletAddress) logger.info(`[pier-connector] Wallet : ${config.walletAddress}`);
|
|
197
|
+
|
|
198
|
+
// 2. Start Heartbeat Loop
|
|
199
|
+
const runHeartbeat = async () => {
|
|
200
|
+
try {
|
|
201
|
+
const hData = await heartbeatNode(config);
|
|
202
|
+
logger.debug('[pier-connector] Heartbeat sent');
|
|
203
|
+
// Potential NATS URL update could be handled here
|
|
204
|
+
} catch (err) {
|
|
205
|
+
logger.warn(`[pier-connector] Heartbeat failed: ${err.message}`);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
await runHeartbeat();
|
|
209
|
+
heartbeatTimer = setInterval(runHeartbeat, 60000);
|
|
210
|
+
|
|
211
|
+
// 3. Connect to NATS
|
|
212
|
+
nc = await createNatsConnection(activeNatsUrl, logger);
|
|
138
213
|
connectionStatus = 'connected';
|
|
139
214
|
connectedAt = new Date();
|
|
140
215
|
|
|
141
|
-
// Subscribe to
|
|
142
|
-
|
|
216
|
+
// 4. Subscribe to Subjects
|
|
217
|
+
// Public pool
|
|
218
|
+
nc.subscribe(config.subject, {
|
|
219
|
+
queue: config.queueGroup,
|
|
220
|
+
callback: (err, msg) => handleMessage(msg)
|
|
221
|
+
});
|
|
222
|
+
// Private direct channel
|
|
223
|
+
nc.subscribe(`jobs.node.${config.nodeId}`, {
|
|
224
|
+
callback: (err, msg) => handleMessage(msg)
|
|
225
|
+
});
|
|
226
|
+
|
|
143
227
|
logger.info(
|
|
144
|
-
`[pier-connector] ✔ Subscribed to "${config.subject}"
|
|
228
|
+
`[pier-connector] ✔ Subscribed to "${config.subject}" and "jobs.node.${config.nodeId}"`,
|
|
145
229
|
);
|
|
146
230
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
createErrorPayload({
|
|
165
|
-
id: msg.subject, // Fallback ID
|
|
166
|
-
errorCode: 'PARSE_ERROR',
|
|
167
|
-
errorMessage: parsed.error,
|
|
168
|
-
workerId: config.workerId,
|
|
169
|
-
walletAddress: config.walletAddress,
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
231
|
+
// Message handling logic moved to a reusable function
|
|
232
|
+
async function handleMessage(msg) {
|
|
233
|
+
jobsReceived++;
|
|
234
|
+
const startTime = performance.now();
|
|
235
|
+
|
|
236
|
+
const parsed = parseJob(msg, logger);
|
|
237
|
+
if (!parsed.ok) {
|
|
238
|
+
jobsFailed++;
|
|
239
|
+
safeRespond(msg, createErrorPayload({
|
|
240
|
+
id: msg.subject,
|
|
241
|
+
errorCode: 'PARSE_ERROR',
|
|
242
|
+
errorMessage: parsed.error,
|
|
243
|
+
workerId: config.nodeId,
|
|
244
|
+
walletAddress: config.walletAddress,
|
|
245
|
+
}));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
174
248
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`[pier-connector] 📋 Job ${job.id}: "${truncate(job.task, 80)}"`,
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Route the job through OpenClaw's agent via the channel
|
|
181
|
-
try {
|
|
182
|
-
// Build an inbound message for the channel system
|
|
183
|
-
const inbound = {
|
|
184
|
-
channelId: 'pier',
|
|
185
|
-
accountId: 'default',
|
|
186
|
-
senderId: `pier:${job.meta?.sender ?? 'anonymous'}`,
|
|
187
|
-
text: job.task,
|
|
188
|
-
metadata: {
|
|
189
|
-
pierJobId: job.id,
|
|
190
|
-
pierNatsMsg: msg,
|
|
191
|
-
pierStartTime: startTime,
|
|
192
|
-
pierMeta: job.meta,
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// If a system prompt was provided, attach it
|
|
197
|
-
if (job.systemPrompt) {
|
|
198
|
-
inbound.systemPrompt = job.systemPrompt;
|
|
199
|
-
}
|
|
249
|
+
const { job } = parsed;
|
|
250
|
+
logger.info(`[pier-connector] 📥 Received job ${job.id}: "${truncate(job.task, 60)}"`);
|
|
200
251
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
errorMessage: 'Agent runtime not available — plugin may need reconfiguration',
|
|
215
|
-
workerId: config.workerId,
|
|
216
|
-
walletAddress: config.walletAddress,
|
|
217
|
-
})
|
|
218
|
-
);
|
|
219
|
-
jobsFailed++;
|
|
220
|
-
}
|
|
221
|
-
} catch (err) {
|
|
222
|
-
const elapsed = (performance.now() - startTime).toFixed(1);
|
|
223
|
-
jobsFailed++;
|
|
224
|
-
logger.error(
|
|
225
|
-
`[pier-connector] ✖ Job ${job.id} failed after ${elapsed}ms — ${err.message}`,
|
|
226
|
-
);
|
|
227
|
-
safeRespond(
|
|
228
|
-
msg,
|
|
229
|
-
createErrorPayload({
|
|
230
|
-
id: job.id,
|
|
231
|
-
errorCode: 'EXECUTION_FAILED',
|
|
232
|
-
errorMessage: err.message,
|
|
233
|
-
workerId: config.workerId,
|
|
234
|
-
walletAddress: config.walletAddress,
|
|
235
|
-
})
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
252
|
+
try {
|
|
253
|
+
const inbound = {
|
|
254
|
+
channelId: 'pier',
|
|
255
|
+
accountId: 'default',
|
|
256
|
+
senderId: `pier:${job.meta?.sender ?? 'anonymous'}`,
|
|
257
|
+
text: job.task,
|
|
258
|
+
metadata: {
|
|
259
|
+
pierJobId: job.id,
|
|
260
|
+
pierNatsMsg: msg,
|
|
261
|
+
pierStartTime: startTime,
|
|
262
|
+
pierMeta: job.meta,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
239
265
|
|
|
240
|
-
|
|
241
|
-
`[pier-connector] Subscription loop ended. ` +
|
|
242
|
-
`Total: ${jobsReceived} received, ${jobsCompleted} completed, ${jobsFailed} failed`,
|
|
243
|
-
);
|
|
244
|
-
})();
|
|
266
|
+
if (job.systemPrompt) inbound.systemPrompt = job.systemPrompt;
|
|
245
267
|
|
|
268
|
+
if (api.runtime?.sendIncoming) {
|
|
269
|
+
await api.runtime.sendIncoming(inbound);
|
|
270
|
+
} else {
|
|
271
|
+
throw new Error('Agent runtime not available');
|
|
272
|
+
}
|
|
273
|
+
} catch (err) {
|
|
274
|
+
jobsFailed++;
|
|
275
|
+
safeRespond(msg, createErrorPayload({
|
|
276
|
+
id: job.id,
|
|
277
|
+
errorCode: 'EXECUTION_FAILED',
|
|
278
|
+
errorMessage: err.message,
|
|
279
|
+
workerId: config.nodeId,
|
|
280
|
+
walletAddress: config.walletAddress,
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
246
284
|
// Monitor connection closure
|
|
247
285
|
nc.closed().then((err) => {
|
|
248
286
|
connectionStatus = 'disconnected';
|
|
287
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
249
288
|
if (err) {
|
|
250
|
-
logger.error(
|
|
251
|
-
`[pier-connector] NATS connection closed with error: ${err.message}`,
|
|
252
|
-
);
|
|
289
|
+
logger.error(`[pier-connector] NATS connection closed with error: ${err.message}`);
|
|
253
290
|
} else {
|
|
254
291
|
logger.info('[pier-connector] NATS connection closed gracefully');
|
|
255
292
|
}
|
|
256
293
|
});
|
|
257
294
|
} catch (err) {
|
|
258
295
|
connectionStatus = 'error';
|
|
296
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
259
297
|
logger.error(`[pier-connector] ✖ Failed to start: ${err.message}`);
|
|
260
|
-
logger.error(`[pier-connector] Stack: ${err.stack}`);
|
|
261
298
|
}
|
|
262
299
|
},
|
|
263
300
|
|
|
@@ -393,6 +430,8 @@ export default function register(api) {
|
|
|
393
430
|
text: [
|
|
394
431
|
`**Pier Connector Status**`,
|
|
395
432
|
`• Connection: ${connectionStatus}`,
|
|
433
|
+
`• Node ID: ${config.nodeId || 'Unregistered'}`,
|
|
434
|
+
`• API URL: ${config.apiUrl}`,
|
|
396
435
|
`• NATS URL: ${config.natsUrl}`,
|
|
397
436
|
`• Subscribe: ${config.subject}`,
|
|
398
437
|
`• Publish: ${config.publishSubject}`,
|
|
@@ -403,5 +442,75 @@ export default function register(api) {
|
|
|
403
442
|
},
|
|
404
443
|
});
|
|
405
444
|
|
|
445
|
+
// ── 5. Register CLI Setup Command ──────────────────────────────────
|
|
446
|
+
|
|
447
|
+
api.registerCli(
|
|
448
|
+
({ program }) => {
|
|
449
|
+
program
|
|
450
|
+
.command('setup')
|
|
451
|
+
.description('Interactively configure the Pier connector settings')
|
|
452
|
+
.action(async () => {
|
|
453
|
+
const currentConfig = resolveConfig();
|
|
454
|
+
|
|
455
|
+
console.log('\n🚢 \x1b[1m\x1b[36mPier Connector Setup\x1b[0m');
|
|
456
|
+
console.log('Let\'s configure your OpenClaw node for the Pier job marketplace.\n');
|
|
457
|
+
|
|
458
|
+
const answers = await inquirer.prompt([
|
|
459
|
+
{
|
|
460
|
+
type: 'input',
|
|
461
|
+
name: 'apiUrl',
|
|
462
|
+
message: 'Pier API Base URL:',
|
|
463
|
+
default: currentConfig.apiUrl,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: 'password',
|
|
467
|
+
name: 'apiKey',
|
|
468
|
+
message: 'User API Key:',
|
|
469
|
+
default: currentConfig.apiKey,
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
type: 'input',
|
|
473
|
+
name: 'natsUrl',
|
|
474
|
+
message: 'NATS WebSocket URL (leave empty for auto):',
|
|
475
|
+
default: currentConfig.natsUrl || '',
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
type: 'input',
|
|
479
|
+
name: 'walletAddress',
|
|
480
|
+
message: 'Wallet Address (for rewards):',
|
|
481
|
+
default: currentConfig.walletAddress,
|
|
482
|
+
},
|
|
483
|
+
]);
|
|
484
|
+
|
|
485
|
+
// Determine how to save. According to standard OpenClaw plugin patterns
|
|
486
|
+
// configurations are either written to user config or output instructions.
|
|
487
|
+
// We'll output the configuration block for the user to copy/paste if direct edit fails.
|
|
488
|
+
console.log('\n✅ \x1b[32mConfiguration Captured!\x1b[0m\n');
|
|
489
|
+
|
|
490
|
+
const newConfigBlock = {
|
|
491
|
+
plugins: {
|
|
492
|
+
entries: {
|
|
493
|
+
"pier-connector": {
|
|
494
|
+
enabled: true,
|
|
495
|
+
config: {
|
|
496
|
+
apiUrl: answers.apiUrl,
|
|
497
|
+
apiKey: answers.apiKey,
|
|
498
|
+
natsUrl: answers.natsUrl || undefined,
|
|
499
|
+
walletAddress: answers.walletAddress
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
console.log('Currently, CLI automatic config writes depend on the framework host.');
|
|
507
|
+
console.log('Please ensure your \x1b[1m\x1b[33mopenclaw.config.json\x1b[0m includes the following block:\n');
|
|
508
|
+
console.log(JSON.stringify(newConfigBlock, null, 2));
|
|
509
|
+
console.log('\nRestart OpenClaw to apply changes.');
|
|
510
|
+
});
|
|
511
|
+
},
|
|
512
|
+
{ commands: ['pier'] }
|
|
513
|
+
);
|
|
514
|
+
|
|
406
515
|
logger.info('[pier-connector] Plugin registered');
|
|
407
516
|
}
|
package/src/protocol.js
CHANGED
|
@@ -14,9 +14,10 @@ export const PROTOCOL_VERSION = '1.0';
|
|
|
14
14
|
* @param {string} [params.systemPrompt]
|
|
15
15
|
* @param {number} [params.timeoutMs]
|
|
16
16
|
* @param {object} [params.meta]
|
|
17
|
+
* @param {string} [params.targetNodeId]
|
|
17
18
|
* @returns {object}
|
|
18
19
|
*/
|
|
19
|
-
export function createRequestPayload({ task, systemPrompt, timeoutMs, meta }) {
|
|
20
|
+
export function createRequestPayload({ task, systemPrompt, timeoutMs, meta, targetNodeId }) {
|
|
20
21
|
return {
|
|
21
22
|
version: PROTOCOL_VERSION,
|
|
22
23
|
id: crypto.randomUUID(),
|
|
@@ -25,6 +26,7 @@ export function createRequestPayload({ task, systemPrompt, timeoutMs, meta }) {
|
|
|
25
26
|
systemPrompt,
|
|
26
27
|
timeoutMs: timeoutMs || 60000,
|
|
27
28
|
meta: meta || {},
|
|
29
|
+
targetNodeId: targetNodeId || null,
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -101,6 +103,7 @@ export function normalizeInboundPayload(payload) {
|
|
|
101
103
|
task: payload.task,
|
|
102
104
|
systemPrompt: payload.systemPrompt,
|
|
103
105
|
meta: payload.meta || {},
|
|
106
|
+
targetNodeId: payload.targetNodeId || null,
|
|
104
107
|
},
|
|
105
108
|
};
|
|
106
109
|
}
|