@gholl-studio/pier-connector 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
  - **Pier Account**: A valid account with access to the job marketplace
18
18
  - **Network**: Stable internet connection
19
19
 
20
- > šŸ’” **Note**: The plugin handles all connection logic internally. You only need to configure your **Pier API Token** in the settings. No external message brokers (like NATS) or custom server setups are required.
20
+ > šŸ’” **Note**: The plugin handles registration and heartbeat logic internally. You only need to configure your **Pier API URL/Key** in the settings. The plugin will automatically negotiate NATS connection details with the backend.
21
21
 
22
22
  ## Installation
23
23
 
@@ -34,9 +34,21 @@ openclaw plugins enable pier-connector
34
34
 
35
35
  Restart the OpenClaw Gateway.
36
36
 
37
- ## Configuration
37
+ Restart the OpenClaw Gateway.
38
+
39
+ ## āš™ļø How to Configure
40
+
41
+ There are two ways to configure the plugin:
42
+
43
+ ### 1. Interactive CLI (Recommended)
44
+ Run the following command in your terminal:
45
+ ```bash
46
+ openclaw pier setup
47
+ ```
48
+ This will guide you through setting up your API URL, Key, and Wallet.
38
49
 
39
- Configure via `plugins.entries.pier-connector.config`:
50
+ ### 2. Manual Config
51
+ Add the following to your `openclaw.config.json` under `plugins.entries.pier-connector.config`:
40
52
 
41
53
  ```json
42
54
  {
@@ -45,12 +57,9 @@ Configure via `plugins.entries.pier-connector.config`:
45
57
  "pier-connector": {
46
58
  "enabled": true,
47
59
  "config": {
48
- "natsUrl": "wss://pier.gholl.com/nexus",
49
- "subject": "jobs.worker",
50
- "publishSubject": "jobs.submit",
51
- "queueGroup": "openclaw-workers",
52
- "workerId": "agent-001",
53
- "walletAddress": "0xABC123"
60
+ "apiUrl": "https://pier.gholl.com",
61
+ "apiKey": "your-api-key-here",
62
+ "walletAddress": "0xYourWalletAddress"
54
63
  }
55
64
  }
56
65
  }
@@ -58,14 +67,14 @@ Configure via `plugins.entries.pier-connector.config`:
58
67
  }
59
68
  ```
60
69
 
61
- | Option | Default | Description |
62
- |------------------|--------------------------------|--------------------------------------|
63
- | `natsUrl` | `wss://pier.gholl.com/nexus` | NATS WebSocket server URL |
64
- | `subject` | `jobs.worker` | Subject to subscribe for incoming jobs |
65
- | `publishSubject` | `jobs.submit` | Subject for outbound task publishing |
66
- | `queueGroup` | `openclaw-workers` | NATS Queue Group for load balancing |
67
- | `workerId` | `""` | Unique identifier for this agent worker |
68
- | `walletAddress` | `""` | Wallet address for receiving rewards |
70
+ | Option | Default | Description |
71
+ |-----------------|------------------------------|--------------------------------------------------|
72
+ | `apiUrl` | `https://pier.gholl.com` | Pier Backend API Base URL |
73
+ | `apiKey` | `""` | Your User API Key for platform authentication |
74
+ | `nodeId` | `""` | (Auto-generated) Unique UUID for this node |
75
+ | `secretKey` | `""` | (Auto-generated) Secret key for pulse verification |
76
+ | `walletAddress` | `""` | Wallet address for receiving job rewards |
77
+ | `natsUrl` | `""` | (Optional) Manual NATS override |
69
78
 
70
79
  ## How It Works
71
80
 
@@ -119,8 +128,8 @@ Use `/pier` in any OpenClaw channel to check connector status.
119
128
  "result": "The AI's generated response",
120
129
  "latencyMs": 1450,
121
130
  "worker": {
122
- "id": "agent-001",
123
- "wallet": "0xABC123"
131
+ "id": "uuid-node",
132
+ "wallet": "0xYourWalletAddress"
124
133
  }
125
134
  }
126
135
  ```
@@ -138,8 +147,8 @@ Use `/pier` in any OpenClaw channel to check connector status.
138
147
  "message": "Error details"
139
148
  },
140
149
  "worker": {
141
- "id": "agent-001",
142
- "wallet": "0xABC123"
150
+ "id": "uuid-node",
151
+ "wallet": "0xYourWalletAddress"
143
152
  }
144
153
  }
145
154
  ```
@@ -10,10 +10,25 @@
10
10
  "type": "object",
11
11
  "additionalProperties": false,
12
12
  "properties": {
13
+ "pierApiUrl": {
14
+ "type": "string",
15
+ "default": "https://pier-connector.gholl.com/api/v1",
16
+ "description": "Pier API Endpoint (from Website)"
17
+ },
18
+ "nodeId": {
19
+ "type": "string",
20
+ "default": "",
21
+ "description": "Bot Node ID (obtain from pier-connector.gholl.com)"
22
+ },
23
+ "secretKey": {
24
+ "type": "string",
25
+ "default": "",
26
+ "description": "Bot Secret Key (obtain from pier-connector.gholl.com)"
27
+ },
13
28
  "natsUrl": {
14
29
  "type": "string",
15
30
  "default": "wss://pier.gholl.com/nexus",
16
- "description": "NATS WebSocket server URL"
31
+ "description": "NATS WebSocket server URL (Overrides API dynamic config if set)"
17
32
  },
18
33
  "subject": {
19
34
  "type": "string",
@@ -30,11 +45,6 @@
30
45
  "default": "openclaw-workers",
31
46
  "description": "NATS Queue Group to join for load balancing tasks"
32
47
  },
33
- "workerId": {
34
- "type": "string",
35
- "default": "",
36
- "description": "(Optional) Unique identifier for this agent worker"
37
- },
38
48
  "walletAddress": {
39
49
  "type": "string",
40
50
  "default": "",
@@ -43,26 +53,34 @@
43
53
  }
44
54
  },
45
55
  "uiHints": {
56
+ "pierApiUrl": {
57
+ "label": "Pier API URL",
58
+ "placeholder": "https://pier-connector.gholl.com/api/v1"
59
+ },
60
+ "nodeId": {
61
+ "label": "Bot Node ID",
62
+ "placeholder": "uuid-..."
63
+ },
64
+ "secretKey": {
65
+ "label": "Bot Secret Key (Secret)",
66
+ "placeholder": "sk_..."
67
+ },
46
68
  "natsUrl": {
47
- "label": "NATS WebSocket URL",
69
+ "label": "NATS WebSocket URL (Override)",
48
70
  "placeholder": "wss://pier.gholl.com/nexus"
49
71
  },
50
72
  "subject": {
51
- "label": "Subscribe Subject (incoming jobs)",
73
+ "label": "Subscribe Subject",
52
74
  "placeholder": "jobs.worker"
53
75
  },
54
76
  "publishSubject": {
55
- "label": "Publish Subject (outbound tasks)",
77
+ "label": "Publish Subject",
56
78
  "placeholder": "jobs.submit"
57
79
  },
58
80
  "queueGroup": {
59
- "label": "Queue Group (Load Balancing)",
81
+ "label": "Queue Group",
60
82
  "placeholder": "openclaw-workers"
61
83
  },
62
- "workerId": {
63
- "label": "Worker ID",
64
- "placeholder": "agent-001"
65
- },
66
84
  "walletAddress": {
67
85
  "label": "Wallet Address (Rewards)",
68
86
  "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.1",
4
+ "version": "0.0.3",
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,10 @@
5
5
  */
6
6
 
7
7
  export const DEFAULTS = Object.freeze({
8
- /** NATS WebSocket server URL */
8
+ /** Pier Backend API Base URL */
9
+ PIER_API_URL: 'https://pier-connector.gholl.com/api/v1',
10
+
11
+ /** NATS WebSocket server URL (usually provided by Heartbeat) */
9
12
  NATS_URL: 'wss://pier.gholl.com/nexus',
10
13
 
11
14
  /** NATS subject to subscribe for incoming jobs */
@@ -17,8 +20,11 @@ export const DEFAULTS = Object.freeze({
17
20
  /** Default Queue Group for processing jobs (prevents duplicate work) */
18
21
  QUEUE_GROUP: 'openclaw-workers',
19
22
 
20
- /** Optional ID to identify this specific worker node */
21
- WORKER_ID: '',
23
+ /** Unique identifier for this agent worker node (UUID) */
24
+ NODE_ID: '',
25
+
26
+ /** Secret key for node authentication and heartbeats */
27
+ SECRET_KEY: '',
22
28
 
23
29
  /** Optional Wallet address to receive points/payments for completed tasks */
24
30
  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,53 @@ export default function register(api) {
41
42
  function resolveConfig() {
42
43
  const cfg = api.config?.plugins?.entries?.['pier-connector']?.config ?? {};
43
44
  return {
45
+ pierApiUrl: cfg.pierApiUrl || DEFAULTS.PIER_API_URL,
46
+ nodeId: cfg.nodeId || DEFAULTS.NODE_ID,
47
+ secretKey: cfg.secretKey || DEFAULTS.SECRET_KEY,
44
48
  natsUrl: cfg.natsUrl || DEFAULTS.NATS_URL,
45
- subject: cfg.subject || DEFAULTS.SUBJECT,
49
+ subject: cfg.subject || DEFAULTS.PIER_SUBJECT,
46
50
  publishSubject: cfg.publishSubject || DEFAULTS.PUBLISH_SUBJECT,
47
51
  queueGroup: cfg.queueGroup || DEFAULTS.QUEUE_GROUP,
48
- workerId: cfg.workerId || DEFAULTS.WORKER_ID,
49
52
  walletAddress: cfg.walletAddress || DEFAULTS.WALLET_ADDRESS,
50
53
  };
51
54
  }
52
55
 
56
+ /** Heartbeat timer reference */
57
+ let heartbeatTimer = null;
58
+
59
+ // ── Lifecycle: Heartbeat ──────────────────────────────────────────
60
+
61
+ /**
62
+ * Sends a heartbeat to the Pier Backend to stay active in the marketplace.
63
+ */
64
+ async function heartbeatNode(config) {
65
+ if (!config.nodeId || !config.secretKey) return null;
66
+
67
+ try {
68
+ const resp = await fetch(`${config.pierApiUrl}/nodes/heartbeat`, {
69
+ method: 'POST',
70
+ headers: { 'Content-Type': 'application/json' },
71
+ body: JSON.stringify({
72
+ node_id: config.nodeId,
73
+ secret_key: config.secretKey,
74
+ capabilities: ['translation', 'code-execution', 'reasoning', 'vision'],
75
+ description: `OpenClaw Node (${config.nodeId.substring(0, 8)})`,
76
+ }),
77
+ });
78
+
79
+ if (!resp.ok) {
80
+ const errText = await resp.text();
81
+ throw new Error(`Heartbeat failed (${resp.status}): ${errText}`);
82
+ }
83
+
84
+ const data = await resp.json();
85
+ return data.nats_config || null;
86
+ } catch (err) {
87
+ logger.warn(`[pier-connector] Heartbeat failed: ${err.message}`);
88
+ return null;
89
+ }
90
+ }
91
+
53
92
  // ── 1. Register messaging channel ──────────────────────────────────
54
93
 
55
94
  const pierChannel = {
@@ -96,7 +135,7 @@ export default function register(api) {
96
135
  id: jobId,
97
136
  reply: text,
98
137
  latencyMs: elapsed ? Number(elapsed) : undefined,
99
- workerId: resolveConfig().workerId,
138
+ workerId: resolveConfig().nodeId,
100
139
  walletAddress: resolveConfig().walletAddress,
101
140
  });
102
141
 
@@ -123,141 +162,142 @@ export default function register(api) {
123
162
 
124
163
  start: async () => {
125
164
  const config = resolveConfig();
126
-
127
165
  logger.info('[pier-connector] šŸš€ Starting background service …');
128
- logger.info(`[pier-connector] NATS URL : ${config.natsUrl}`);
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}`);
134
166
 
135
167
  try {
136
- // Connect to NATS via WebSocket
137
- nc = await createNatsConnection(config.natsUrl, logger);
168
+ // 1. Mandatory credentials check
169
+ if (!config.nodeId || !config.secretKey) {
170
+ logger.warn('[pier-connector] āš ļø Bot Node ID or Secret is missing.');
171
+ logger.warn('[pier-connector] Please run "openclaw pier setup" or go to pier-connector.gholl.com to create a bot.');
172
+ connectionStatus = 'error';
173
+ return;
174
+ }
175
+
176
+ connectionStatus = 'connecting';
177
+
178
+ // 2. Initial Pulse & Config fetch
179
+ logger.info(`[pier-connector] šŸ’“ Sending initial pulse to ${config.pierApiUrl}...`);
180
+ const natsUpdate = await heartbeatNode(config);
181
+ const activeNatsUrl = natsUpdate?.url || config.natsUrl;
182
+
183
+ logger.info(`[pier-connector] Node ID : ${config.nodeId}`);
184
+ logger.info(`[pier-connector] NATS URL : ${activeNatsUrl}`);
185
+
186
+ // 3. Start Heartbeat Timer
187
+ const runHeartbeat = async () => {
188
+ const currentConfig = resolveConfig();
189
+ if (!currentConfig.nodeId) return;
190
+ await heartbeatNode(currentConfig);
191
+ };
192
+ heartbeatTimer = setInterval(runHeartbeat, 60000);
193
+
194
+ // 4. Connect to NATS
195
+ nc = await createNatsConnection(activeNatsUrl, logger);
138
196
  connectionStatus = 'connected';
139
197
  connectedAt = new Date();
140
198
 
141
- // Subscribe to the jobs channel using a Queue Group to distribute work
142
- subscription = nc.subscribe(config.subject, { queue: config.queueGroup });
143
- logger.info(
144
- `[pier-connector] āœ” Subscribed to "${config.subject}" (group: "${config.queueGroup}") — waiting for jobs …`,
145
- );
146
-
147
- // Async message processing loop
148
- (async () => {
149
- for await (const msg of subscription) {
150
- jobsReceived++;
151
- const startTime = performance.now();
152
-
153
- logger.info(
154
- `[pier-connector] šŸ“Ø Job #${jobsReceived} received on "${msg.subject}"`,
155
- );
156
-
157
- // Parse the incoming job
158
- const parsed = parseJob(msg, logger);
159
-
160
- if (!parsed.ok) {
161
- jobsFailed++;
162
- safeRespond(
163
- msg,
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
- }
199
+ // 5. Subscribe to Subjects
200
+ const publicSubject = config.subject;
201
+ const privateSubject = `jobs.node.${config.nodeId}`;
174
202
 
175
- const { job } = parsed;
176
- logger.info(
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
- }
203
+ logger.info(`[pier-connector] šŸ‘‚ Listening to Marketplace: ${publicSubject}`);
204
+ logger.info(`[pier-connector] šŸ‘‚ Listening to Direct Messages: ${privateSubject}`);
200
205
 
201
- // Send to OpenClaw's agent pipeline
202
- if (api.runtime?.sendIncoming) {
203
- await api.runtime.sendIncoming(inbound);
204
- } else {
205
- // Fallback: direct agent invocation if sendIncoming is not available
206
- logger.warn(
207
- '[pier-connector] api.runtime.sendIncoming not available, using direct response',
208
- );
209
- safeRespond(
210
- msg,
211
- createErrorPayload({
212
- id: job.id,
213
- errorCode: 'RUNTIME_UNAVAILABLE',
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
- }
206
+ // Public pool with load balancing
207
+ nc.subscribe(publicSubject, {
208
+ queue: config.queueGroup,
209
+ callback: (err, msg) => handleMessage(msg)
210
+ });
211
+ // Private direct channel (no queue group for broadcast/direct)
212
+ nc.subscribe(privateSubject, {
213
+ callback: (err, msg) => handleMessage(msg)
214
+ });
215
+
216
+ // Unified Message Handler
217
+ async function handleMessage(msg) {
218
+ const startTime = performance.now();
219
+ const rawData = new TextDecoder().decode(msg.data);
220
+
221
+ let payload;
222
+ try {
223
+ payload = JSON.parse(rawData);
224
+ } catch (e) {
225
+ logger.error(`[pier-connector] Failed to parse JSON: ${rawData.substring(0, 100)}`);
226
+ return;
238
227
  }
239
228
 
240
- logger.info(
241
- `[pier-connector] Subscription loop ended. ` +
242
- `Total: ${jobsReceived} received, ${jobsCompleted} completed, ${jobsFailed} failed`,
243
- );
244
- })();
229
+ // V1.1 FEATURE: Task Poisoning / Poaching Protection
230
+ if (payload.assigned_node_id && payload.assigned_node_id !== config.nodeId) {
231
+ // Silent ignore - don't reply, don't log heavily
232
+ return;
233
+ }
234
+
235
+ jobsReceived++;
236
+ const parsed = parseJob(msg, logger);
237
+ if (!parsed.ok) {
238
+ jobsFailed++;
239
+ logger.error(`[pier-connector] Job parse error: ${parsed.error}`);
240
+ safeRespond(msg, createErrorPayload({
241
+ id: 'unknown',
242
+ errorCode: 'PARSE_ERROR',
243
+ errorMessage: parsed.error,
244
+ workerId: config.nodeId,
245
+ walletAddress: config.walletAddress,
246
+ }));
247
+ return;
248
+ }
249
+
250
+ const { job } = parsed;
251
+ logger.info(`[pier-connector] šŸ“„ Received job ${job.id}: "${truncate(job.task, 60)}"`);
252
+
253
+ try {
254
+ const inbound = {
255
+ channelId: 'pier',
256
+ accountId: 'default',
257
+ senderId: `pier:${job.meta?.sender ?? 'anonymous'}`,
258
+ text: job.task,
259
+ metadata: {
260
+ pierJobId: job.id,
261
+ pierNatsMsg: msg,
262
+ pierStartTime: startTime,
263
+ pierMeta: job.meta,
264
+ },
265
+ };
266
+
267
+ if (job.systemPrompt) inbound.systemPrompt = job.systemPrompt;
268
+
269
+ if (api.runtime?.sendIncoming) {
270
+ await api.runtime.sendIncoming(inbound);
271
+ } else {
272
+ throw new Error('Agent runtime not available');
273
+ }
274
+ } catch (err) {
275
+ jobsFailed++;
276
+ safeRespond(msg, createErrorPayload({
277
+ id: job.id,
278
+ errorCode: 'EXECUTION_FAILED',
279
+ errorMessage: err.message,
280
+ workerId: config.nodeId,
281
+ walletAddress: config.walletAddress,
282
+ }));
283
+ }
284
+ }
245
285
 
246
286
  // Monitor connection closure
247
287
  nc.closed().then((err) => {
248
288
  connectionStatus = 'disconnected';
289
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
249
290
  if (err) {
250
- logger.error(
251
- `[pier-connector] NATS connection closed with error: ${err.message}`,
252
- );
291
+ logger.error(`[pier-connector] NATS connection closed with error: ${err.message}`);
253
292
  } else {
254
293
  logger.info('[pier-connector] NATS connection closed gracefully');
255
294
  }
256
295
  });
257
296
  } catch (err) {
258
297
  connectionStatus = 'error';
298
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
259
299
  logger.error(`[pier-connector] āœ– Failed to start: ${err.message}`);
260
- logger.error(`[pier-connector] Stack: ${err.stack}`);
300
+ logger.error(err.stack);
261
301
  }
262
302
  },
263
303
 
@@ -393,6 +433,8 @@ export default function register(api) {
393
433
  text: [
394
434
  `**Pier Connector Status**`,
395
435
  `• Connection: ${connectionStatus}`,
436
+ `• Node ID: ${config.nodeId || 'N/A'}`,
437
+ `• API URL: ${config.pierApiUrl}`,
396
438
  `• NATS URL: ${config.natsUrl}`,
397
439
  `• Subscribe: ${config.subject}`,
398
440
  `• Publish: ${config.publishSubject}`,
@@ -403,5 +445,79 @@ export default function register(api) {
403
445
  },
404
446
  });
405
447
 
448
+ // ── 5. Register CLI Setup Command ──────────────────────────────────
449
+
450
+ api.registerCli(
451
+ ({ program }) => {
452
+ // Find or create the 'pier' command namespace to avoid root conflicts
453
+ let pier = program.commands.find(c => c.name() === 'pier');
454
+ if (!pier) {
455
+ pier = program.command('pier').description('Pier connector commands');
456
+ }
457
+
458
+ pier
459
+ .command('setup')
460
+ .description('Interactively configure the Pier connector settings')
461
+ .action(async () => {
462
+ const currentConfig = resolveConfig();
463
+
464
+ console.log('\n🚢 \x1b[1m\x1b[36mPier Connector Setup (V1.1)\x1b[0m');
465
+ console.log('Please register your bot at \x1b[1m\x1b[33mhttps://pier-connector.gholl.com\x1b[0m first.\n');
466
+
467
+ const answers = await inquirer.prompt([
468
+ {
469
+ type: 'input',
470
+ name: 'pierApiUrl',
471
+ message: 'Pier API URL:',
472
+ default: currentConfig.pierApiUrl,
473
+ },
474
+ {
475
+ type: 'input',
476
+ name: 'nodeId',
477
+ message: 'Bot Node ID (UUID):',
478
+ default: currentConfig.nodeId,
479
+ },
480
+ {
481
+ type: 'password',
482
+ name: 'secretKey',
483
+ message: 'Bot Secret Key:',
484
+ default: currentConfig.secretKey,
485
+ },
486
+ {
487
+ type: 'input',
488
+ name: 'walletAddress',
489
+ message: 'Your Wallet Address (for payout):',
490
+ default: currentConfig.walletAddress,
491
+ },
492
+ ]);
493
+
494
+ // Determine how to save.
495
+ console.log('\nāœ… \x1b[32mConfiguration Captured!\x1b[0m\n');
496
+
497
+ const newConfigBlock = {
498
+ plugins: {
499
+ entries: {
500
+ "pier-connector": {
501
+ enabled: true,
502
+ config: {
503
+ pierApiUrl: answers.pierApiUrl,
504
+ nodeId: answers.nodeId,
505
+ secretKey: answers.secretKey,
506
+ walletAddress: answers.walletAddress
507
+ }
508
+ }
509
+ }
510
+ }
511
+ };
512
+
513
+ console.log('Currently, CLI automatic config writes depend on the framework host.');
514
+ console.log('Please ensure your \x1b[1m\x1b[33mopenclaw.config.json\x1b[0m includes the following block:\n');
515
+ console.log(JSON.stringify(newConfigBlock, null, 2));
516
+ console.log('\nRestart OpenClaw to apply changes.');
517
+ });
518
+ },
519
+ { commands: ['pier'] }
520
+ );
521
+
406
522
  logger.info('[pier-connector] Plugin registered');
407
523
  }
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
  }