@gholl-studio/pier-connector 0.0.3 → 0.0.5
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 +18 -0
- package/package.json +2 -1
- package/src/config.js +6 -0
- package/src/index.js +236 -55
package/openclaw.plugin.json
CHANGED
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
"default": "",
|
|
26
26
|
"description": "Bot Secret Key (obtain from pier-connector.gholl.com)"
|
|
27
27
|
},
|
|
28
|
+
"privateKey": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"default": "",
|
|
31
|
+
"description": "(Optional/Advanced) Wallet Private Key for AI Auto-Hosting"
|
|
32
|
+
},
|
|
28
33
|
"natsUrl": {
|
|
29
34
|
"type": "string",
|
|
30
35
|
"default": "wss://pier.gholl.com/nexus",
|
|
@@ -45,6 +50,11 @@
|
|
|
45
50
|
"default": "openclaw-workers",
|
|
46
51
|
"description": "NATS Queue Group to join for load balancing tasks"
|
|
47
52
|
},
|
|
53
|
+
"agentId": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"default": "",
|
|
56
|
+
"description": "(Optional) The Agent ID to bind this connector to for context handling"
|
|
57
|
+
},
|
|
48
58
|
"walletAddress": {
|
|
49
59
|
"type": "string",
|
|
50
60
|
"default": "",
|
|
@@ -65,6 +75,10 @@
|
|
|
65
75
|
"label": "Bot Secret Key (Secret)",
|
|
66
76
|
"placeholder": "sk_..."
|
|
67
77
|
},
|
|
78
|
+
"privateKey": {
|
|
79
|
+
"label": "Private Key (Optional Auto-Host)",
|
|
80
|
+
"placeholder": "0x..."
|
|
81
|
+
},
|
|
68
82
|
"natsUrl": {
|
|
69
83
|
"label": "NATS WebSocket URL (Override)",
|
|
70
84
|
"placeholder": "wss://pier.gholl.com/nexus"
|
|
@@ -81,6 +95,10 @@
|
|
|
81
95
|
"label": "Queue Group",
|
|
82
96
|
"placeholder": "openclaw-workers"
|
|
83
97
|
},
|
|
98
|
+
"agentId": {
|
|
99
|
+
"label": "Bound Agent ID",
|
|
100
|
+
"placeholder": "uuid-..."
|
|
101
|
+
},
|
|
84
102
|
"walletAddress": {
|
|
85
103
|
"label": "Wallet Address (Rewards)",
|
|
86
104
|
"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.5",
|
|
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",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@nats-io/transport-node": "^3.0.0",
|
|
35
|
+
"ethers": "^6.16.0",
|
|
35
36
|
"inquirer": "^13.3.0"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
package/src/config.js
CHANGED
|
@@ -26,6 +26,12 @@ export const DEFAULTS = Object.freeze({
|
|
|
26
26
|
/** Secret key for node authentication and heartbeats */
|
|
27
27
|
SECRET_KEY: '',
|
|
28
28
|
|
|
29
|
+
/** Optional Private Key for auto-registration via Wallet Signature (Advanced) */
|
|
30
|
+
PRIVATE_KEY: '',
|
|
31
|
+
|
|
29
32
|
/** Optional Wallet address to receive points/payments for completed tasks */
|
|
30
33
|
WALLET_ADDRESS: '',
|
|
34
|
+
|
|
35
|
+
/** Optional Agent ID to bind tasks to, instead of creating new sessions */
|
|
36
|
+
AGENT_ID: '',
|
|
31
37
|
});
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,9 @@ 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
15
|
import inquirer from 'inquirer';
|
|
16
|
+
import { ethers } from 'ethers';
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* OpenClaw plugin register function.
|
|
@@ -45,14 +48,24 @@ export default function register(api) {
|
|
|
45
48
|
pierApiUrl: cfg.pierApiUrl || DEFAULTS.PIER_API_URL,
|
|
46
49
|
nodeId: cfg.nodeId || DEFAULTS.NODE_ID,
|
|
47
50
|
secretKey: cfg.secretKey || DEFAULTS.SECRET_KEY,
|
|
51
|
+
privateKey: cfg.privateKey || process.env.PIER_PRIVATE_KEY || DEFAULTS.PRIVATE_KEY,
|
|
48
52
|
natsUrl: cfg.natsUrl || DEFAULTS.NATS_URL,
|
|
49
53
|
subject: cfg.subject || DEFAULTS.PIER_SUBJECT,
|
|
50
54
|
publishSubject: cfg.publishSubject || DEFAULTS.PUBLISH_SUBJECT,
|
|
51
55
|
queueGroup: cfg.queueGroup || DEFAULTS.QUEUE_GROUP,
|
|
56
|
+
agentId: cfg.agentId || DEFAULTS.AGENT_ID,
|
|
52
57
|
walletAddress: cfg.walletAddress || DEFAULTS.WALLET_ADDRESS,
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
/** Runtime config caching to remember newly generated auto-credentials */
|
|
62
|
+
let runtimeConfigCache = null;
|
|
63
|
+
|
|
64
|
+
function getActiveConfig() {
|
|
65
|
+
if (runtimeConfigCache) return runtimeConfigCache;
|
|
66
|
+
return resolveConfig();
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
/** Heartbeat timer reference */
|
|
57
70
|
let heartbeatTimer = null;
|
|
58
71
|
|
|
@@ -135,8 +148,8 @@ export default function register(api) {
|
|
|
135
148
|
id: jobId,
|
|
136
149
|
reply: text,
|
|
137
150
|
latencyMs: elapsed ? Number(elapsed) : undefined,
|
|
138
|
-
workerId:
|
|
139
|
-
walletAddress:
|
|
151
|
+
workerId: getActiveConfig().nodeId,
|
|
152
|
+
walletAddress: getActiveConfig().walletAddress,
|
|
140
153
|
});
|
|
141
154
|
|
|
142
155
|
safeRespond(msg, responsePayload);
|
|
@@ -162,15 +175,65 @@ export default function register(api) {
|
|
|
162
175
|
|
|
163
176
|
start: async () => {
|
|
164
177
|
const config = resolveConfig();
|
|
178
|
+
runtimeConfigCache = config;
|
|
165
179
|
logger.info('[pier-connector] 🚀 Starting background service …');
|
|
166
180
|
|
|
167
181
|
try {
|
|
168
|
-
// 1. Mandatory credentials check
|
|
182
|
+
// 1. Mandatory credentials check or Auto Register
|
|
169
183
|
if (!config.nodeId || !config.secretKey) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
if (config.privateKey) {
|
|
185
|
+
logger.info('[pier-connector] 🛠 No Node ID found. Executing automatic registration via Private Key...');
|
|
186
|
+
try {
|
|
187
|
+
const wallet = new ethers.Wallet(config.privateKey);
|
|
188
|
+
const address = wallet.address;
|
|
189
|
+
logger.info(`[pier-connector] 🛠 Wallet Address: ${address}`);
|
|
190
|
+
|
|
191
|
+
const challengeRes = await fetch(`${config.pierApiUrl}/auth/challenge?wallet_address=${address}`);
|
|
192
|
+
if (!challengeRes.ok) throw new Error("Failed to get challenge");
|
|
193
|
+
const { challenge } = await challengeRes.json();
|
|
194
|
+
|
|
195
|
+
const signature = await wallet.signMessage(challenge);
|
|
196
|
+
|
|
197
|
+
const loginRes = await fetch(`${config.pierApiUrl}/auth/login`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: { 'Content-Type': 'application/json' },
|
|
200
|
+
body: JSON.stringify({ wallet_address: address, challenge, signature })
|
|
201
|
+
});
|
|
202
|
+
if (!loginRes.ok) throw new Error("Failed to login");
|
|
203
|
+
const { api_key } = await loginRes.json();
|
|
204
|
+
|
|
205
|
+
logger.info(`[pier-connector] 🛠 Authenticated. Registering node...`);
|
|
206
|
+
|
|
207
|
+
const hostName = api?.getRuntimeInfo?.()?.hostname ?? 'Auto-Node';
|
|
208
|
+
const regRes = await fetch(`${config.pierApiUrl}/nodes/register`, {
|
|
209
|
+
method: 'POST',
|
|
210
|
+
headers: {
|
|
211
|
+
'Content-Type': 'application/json',
|
|
212
|
+
'Authorization': `Bearer ${api_key}`
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify({ wallet_address: address, name: hostName })
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!regRes.ok) throw new Error("Failed to register node");
|
|
218
|
+
const { id, secret_key } = await regRes.json();
|
|
219
|
+
|
|
220
|
+
logger.info(`[pier-connector] ✔ Node registered automatically: ${id}`);
|
|
221
|
+
config.nodeId = id;
|
|
222
|
+
config.secretKey = secret_key;
|
|
223
|
+
if (!config.walletAddress) {
|
|
224
|
+
config.walletAddress = address;
|
|
225
|
+
}
|
|
226
|
+
} catch (err) {
|
|
227
|
+
logger.error(`[pier-connector] ✖ Auto-registration failed: ${err.message}`);
|
|
228
|
+
connectionStatus = 'error';
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
logger.warn('[pier-connector] ⚠️ Bot Node ID or Secret is missing.');
|
|
233
|
+
logger.warn('[pier-connector] Please run "openclaw pier setup" or provide a privateKey for auto-hosting.');
|
|
234
|
+
connectionStatus = 'error';
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
174
237
|
}
|
|
175
238
|
|
|
176
239
|
connectionStatus = 'connecting';
|
|
@@ -185,7 +248,7 @@ export default function register(api) {
|
|
|
185
248
|
|
|
186
249
|
// 3. Start Heartbeat Timer
|
|
187
250
|
const runHeartbeat = async () => {
|
|
188
|
-
const currentConfig =
|
|
251
|
+
const currentConfig = getActiveConfig();
|
|
189
252
|
if (!currentConfig.nodeId) return;
|
|
190
253
|
await heartbeatNode(currentConfig);
|
|
191
254
|
};
|
|
@@ -256,6 +319,7 @@ export default function register(api) {
|
|
|
256
319
|
accountId: 'default',
|
|
257
320
|
senderId: `pier:${job.meta?.sender ?? 'anonymous'}`,
|
|
258
321
|
text: job.task,
|
|
322
|
+
assignedAgentId: config.agentId || undefined,
|
|
259
323
|
metadata: {
|
|
260
324
|
pierJobId: job.id,
|
|
261
325
|
pierNatsMsg: msg,
|
|
@@ -360,7 +424,7 @@ export default function register(api) {
|
|
|
360
424
|
};
|
|
361
425
|
}
|
|
362
426
|
|
|
363
|
-
const config =
|
|
427
|
+
const config = getActiveConfig();
|
|
364
428
|
const timeout = params.timeoutMs || 60000;
|
|
365
429
|
|
|
366
430
|
const taskPayload = createRequestPayload({
|
|
@@ -424,7 +488,7 @@ export default function register(api) {
|
|
|
424
488
|
name: 'pier',
|
|
425
489
|
description: 'Show Pier connector status',
|
|
426
490
|
handler: () => {
|
|
427
|
-
const config =
|
|
491
|
+
const config = getActiveConfig();
|
|
428
492
|
const uptime = connectedAt
|
|
429
493
|
? `${Math.round((Date.now() - connectedAt.getTime()) / 1000)}s`
|
|
430
494
|
: 'N/A';
|
|
@@ -462,58 +526,175 @@ export default function register(api) {
|
|
|
462
526
|
const currentConfig = resolveConfig();
|
|
463
527
|
|
|
464
528
|
console.log('\n🚢 \x1b[1m\x1b[36mPier Connector Setup (V1.1)\x1b[0m');
|
|
465
|
-
console.log('
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
529
|
+
console.log('You can register manually or let the Auto-Host (Advanced) feature do it for you using a Wallet Private Key.\n');
|
|
530
|
+
|
|
531
|
+
let setupMethod = 'manual';
|
|
532
|
+
try {
|
|
533
|
+
const answer = await inquirer.prompt([
|
|
534
|
+
{
|
|
535
|
+
type: 'list',
|
|
536
|
+
name: 'method',
|
|
537
|
+
message: 'How would you like to configure your node credentials?',
|
|
538
|
+
choices: [
|
|
539
|
+
{ name: 'Manual Entry (Recommended) - I have a Node ID and Secret Key', value: 'manual' },
|
|
540
|
+
{ name: 'Auto-Host (Advanced) - Register automatically using my Wallet Private Key', value: 'auto' }
|
|
541
|
+
],
|
|
542
|
+
default: 'manual'
|
|
543
|
+
}
|
|
544
|
+
]);
|
|
545
|
+
if (answer.method) {
|
|
546
|
+
setupMethod = answer.method;
|
|
547
|
+
}
|
|
548
|
+
} catch (err) {
|
|
549
|
+
console.log('\n\x1b[33mFalling back to Manual Entry mode due to terminal configuration.\x1b[0m');
|
|
550
|
+
setupMethod = 'manual';
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
let finalNodeId = currentConfig.nodeId;
|
|
554
|
+
let finalSecretKey = currentConfig.secretKey;
|
|
555
|
+
let finalPrivateKey = currentConfig.privateKey;
|
|
556
|
+
if (setupMethod === 'manual') {
|
|
557
|
+
const manualAnswers = await inquirer.prompt([
|
|
558
|
+
{ type: 'input', name: 'pierApiUrl', message: 'Pier API URL:', default: currentConfig.pierApiUrl },
|
|
559
|
+
{ type: 'input', name: 'nodeId', message: 'Bot Node ID (UUID):', default: finalNodeId },
|
|
560
|
+
{ type: 'password', name: 'secretKey', message: 'Bot Secret Key:', default: finalSecretKey },
|
|
561
|
+
{ type: 'input', name: 'walletAddress', message: 'Your Wallet Address (for payout):', default: finalWallet }
|
|
562
|
+
]);
|
|
563
|
+
finalNodeId = manualAnswers.nodeId;
|
|
564
|
+
finalSecretKey = manualAnswers.secretKey;
|
|
565
|
+
finalWallet = manualAnswers.walletAddress;
|
|
566
|
+
currentConfig.pierApiUrl = manualAnswers.pierApiUrl;
|
|
567
|
+
} else if (setupMethod === 'auto') {
|
|
568
|
+
const autoAnswers = await inquirer.prompt([
|
|
569
|
+
{ type: 'input', name: 'pierApiUrl', message: 'Pier API URL:', default: currentConfig.pierApiUrl },
|
|
570
|
+
{ type: 'password', name: 'privateKey', message: 'Your Wallet Private Key (Hex, 0x...):', default: finalPrivateKey }
|
|
571
|
+
]);
|
|
572
|
+
console.log('\n\x1b[36mRegistering your node...\x1b[0m');
|
|
573
|
+
try {
|
|
574
|
+
const wallet = new ethers.Wallet(autoAnswers.privateKey);
|
|
575
|
+
console.log(`\x1b[32m✔ Loaded wallet: ${wallet.address}\x1b[0m`);
|
|
576
|
+
finalWallet = finalWallet || wallet.address;
|
|
577
|
+
finalPrivateKey = autoAnswers.privateKey;
|
|
578
|
+
currentConfig.pierApiUrl = autoAnswers.pierApiUrl;
|
|
579
|
+
|
|
580
|
+
const challengeRes = await fetch(`${currentConfig.pierApiUrl}/auth/challenge?wallet_address=${wallet.address}`);
|
|
581
|
+
if (!challengeRes.ok) throw new Error("Failed to get challenge");
|
|
582
|
+
const { challenge } = await challengeRes.json();
|
|
583
|
+
|
|
584
|
+
const signature = await wallet.signMessage(challenge);
|
|
585
|
+
|
|
586
|
+
const loginRes = await fetch(`${currentConfig.pierApiUrl}/auth/login`, {
|
|
587
|
+
method: 'POST',
|
|
588
|
+
headers: { 'Content-Type': 'application/json' },
|
|
589
|
+
body: JSON.stringify({ wallet_address: wallet.address, challenge, signature })
|
|
590
|
+
});
|
|
591
|
+
if (!loginRes.ok) throw new Error("Failed to login");
|
|
592
|
+
const { api_key } = await loginRes.json();
|
|
593
|
+
|
|
594
|
+
const hostName = api?.getRuntimeInfo?.()?.hostname ?? 'Auto-Node';
|
|
595
|
+
const regRes = await fetch(`${currentConfig.pierApiUrl}/nodes/register`, {
|
|
596
|
+
method: 'POST',
|
|
597
|
+
headers: {
|
|
598
|
+
'Content-Type': 'application/json',
|
|
599
|
+
'Authorization': `Bearer ${api_key}`
|
|
600
|
+
},
|
|
601
|
+
body: JSON.stringify({ wallet_address: wallet.address, name: hostName })
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!regRes.ok) throw new Error(`Failed to auto-register node (${regRes.status})`);
|
|
605
|
+
const { id, secret_key } = await regRes.json();
|
|
606
|
+
|
|
607
|
+
finalNodeId = id;
|
|
608
|
+
finalSecretKey = secret_key;
|
|
609
|
+
console.log(`\x1b[32m✔ Node registered automatically! Node ID: ${id}\x1b[0m`);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
console.error(`\x1b[31m✖ Failed to auto-register: ${err.message}\x1b[0m`);
|
|
612
|
+
return; // Stop setup
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ── Select Agent Binding ──
|
|
617
|
+
let finalAgentId = currentConfig.agentId;
|
|
618
|
+
try {
|
|
619
|
+
let agentsInfo = [];
|
|
620
|
+
if (api.runtime && typeof api.runtime.getAgents === 'function') {
|
|
621
|
+
const agentsMap = await api.runtime.getAgents();
|
|
622
|
+
if (agentsMap) {
|
|
623
|
+
agentsInfo = Object.entries(agentsMap).map(([id, info]) => ({
|
|
624
|
+
name: `${info.name || 'Unnamed Agent'} (${id})`,
|
|
625
|
+
value: id
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (agentsInfo.length > 0) {
|
|
631
|
+
agentsInfo.unshift({ name: 'None (Default Routing)', value: '' });
|
|
632
|
+
const agentAnswer = await inquirer.prompt([
|
|
633
|
+
{
|
|
634
|
+
type: 'list',
|
|
635
|
+
name: 'agentId',
|
|
636
|
+
message: 'Select an Agent to bind for processing these tasks:',
|
|
637
|
+
choices: agentsInfo,
|
|
638
|
+
default: finalAgentId || ''
|
|
639
|
+
}
|
|
640
|
+
]);
|
|
641
|
+
finalAgentId = agentAnswer.agentId;
|
|
642
|
+
}
|
|
643
|
+
} catch (err) {
|
|
644
|
+
logger.warn(`Could not load agents list: ${err.message}`);
|
|
645
|
+
}
|
|
493
646
|
|
|
494
647
|
// Determine how to save.
|
|
495
648
|
console.log('\n✅ \x1b[32mConfiguration Captured!\x1b[0m\n');
|
|
496
649
|
|
|
497
650
|
const newConfigBlock = {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
pierApiUrl: answers.pierApiUrl,
|
|
504
|
-
nodeId: answers.nodeId,
|
|
505
|
-
secretKey: answers.secretKey,
|
|
506
|
-
walletAddress: answers.walletAddress
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
651
|
+
pierApiUrl: currentConfig.pierApiUrl,
|
|
652
|
+
nodeId: finalNodeId,
|
|
653
|
+
secretKey: finalSecretKey,
|
|
654
|
+
walletAddress: finalWallet,
|
|
655
|
+
agentId: finalAgentId
|
|
511
656
|
};
|
|
657
|
+
|
|
658
|
+
if (setupMethod === 'auto') {
|
|
659
|
+
newConfigBlock.privateKey = finalPrivateKey;
|
|
660
|
+
console.log('\x1b[33mNote: Private Key is included for future automatic reconnects.\x1b[0m');
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Write to openclaw.json
|
|
664
|
+
try {
|
|
665
|
+
const cwd = process.cwd();
|
|
666
|
+
const configPath = path.join(cwd, 'openclaw.json');
|
|
667
|
+
let existingConfig = {};
|
|
668
|
+
if (fs.existsSync(configPath)) {
|
|
669
|
+
const raw = fs.readFileSync(configPath, 'utf8');
|
|
670
|
+
existingConfig = JSON.parse(raw);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (!existingConfig.plugins) existingConfig.plugins = {};
|
|
674
|
+
if (!existingConfig.plugins.entries) existingConfig.plugins.entries = {};
|
|
675
|
+
if (!existingConfig.plugins.entries['pier-connector']) {
|
|
676
|
+
existingConfig.plugins.entries['pier-connector'] = { enabled: true };
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
existingConfig.plugins.entries['pier-connector'].config = {
|
|
680
|
+
...(existingConfig.plugins.entries['pier-connector'].config || {}),
|
|
681
|
+
...newConfigBlock
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
fs.writeFileSync(configPath, JSON.stringify(existingConfig, null, 2), 'utf8');
|
|
685
|
+
console.log(`\x1b[32m✔ Successfully wrote configuration to ${configPath}\x1b[0m`);
|
|
686
|
+
} catch (err) {
|
|
687
|
+
console.error(`\x1b[31m✖ Failed to write to openclaw.json automatically: ${err.message}\x1b[0m`);
|
|
688
|
+
console.log('Please ensure your \x1b[1m\x1b[33mopenclaw.json\x1b[0m includes the following block in plugins.entries:\n');
|
|
689
|
+
console.log(JSON.stringify({
|
|
690
|
+
"pier-connector": {
|
|
691
|
+
enabled: true,
|
|
692
|
+
config: newConfigBlock
|
|
693
|
+
}
|
|
694
|
+
}, null, 2));
|
|
695
|
+
}
|
|
512
696
|
|
|
513
|
-
console.log('
|
|
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.');
|
|
697
|
+
console.log('\nRestart OpenClaw (or reload plugins) to apply changes.');
|
|
517
698
|
});
|
|
518
699
|
},
|
|
519
700
|
{ commands: ['pier'] }
|