@highway1/cli 0.1.53 → 0.1.54
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 +11 -22
- package/bin/clawiverse.js +0 -2
- package/src/commands/ask.ts +0 -158
- package/src/commands/card.ts +0 -104
- package/src/commands/daemon.ts +0 -207
- package/src/commands/discover.ts +0 -178
- package/src/commands/identity.ts +0 -46
- package/src/commands/inbox.ts +0 -222
- package/src/commands/init.ts +0 -54
- package/src/commands/join.ts +0 -198
- package/src/commands/peers.ts +0 -85
- package/src/commands/send.ts +0 -299
- package/src/commands/serve.ts +0 -271
- package/src/commands/status.ts +0 -60
- package/src/commands/stop.ts +0 -49
- package/src/commands/trust.ts +0 -354
- package/src/config.ts +0 -74
- package/src/daemon/client.ts +0 -90
- package/src/daemon/server.ts +0 -481
- package/src/index.ts +0 -61
- package/src/ui.ts +0 -38
package/src/commands/trust.ts
DELETED
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trust Command - Manage trust scores and endorsements
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
import { getConfig, getIdentity } from '../config.js';
|
|
7
|
-
import { createLogger, createTrustSystem, extractPublicKey } from '@highway1/core';
|
|
8
|
-
import { homedir } from 'os';
|
|
9
|
-
import { join } from 'path';
|
|
10
|
-
|
|
11
|
-
const logger = createLogger('cli:trust');
|
|
12
|
-
|
|
13
|
-
export function createTrustCommand(): Command {
|
|
14
|
-
const trust = new Command('trust')
|
|
15
|
-
.description('Manage trust scores and endorsements');
|
|
16
|
-
|
|
17
|
-
// Show trust score
|
|
18
|
-
trust
|
|
19
|
-
.command('show')
|
|
20
|
-
.description('Show trust score for an agent')
|
|
21
|
-
.argument('<did>', 'Agent DID')
|
|
22
|
-
.action(async (did: string) => {
|
|
23
|
-
try {
|
|
24
|
-
const dataDir = join(homedir(), '.clawiverse');
|
|
25
|
-
const trustSystem = createTrustSystem({
|
|
26
|
-
dbPath: `${dataDir}/trust`,
|
|
27
|
-
getPublicKey: async (did: string) => extractPublicKey(did), // CVP-0010 §4.1 fix
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
await trustSystem.start();
|
|
31
|
-
|
|
32
|
-
const score = await trustSystem.getTrustScore(did);
|
|
33
|
-
const endorsements = await trustSystem.getEndorsements(did);
|
|
34
|
-
|
|
35
|
-
console.log('\n📊 Trust Score for', did);
|
|
36
|
-
console.log('─'.repeat(60));
|
|
37
|
-
console.log(`Interaction Score: ${(score.interactionScore * 100).toFixed(1)}%`);
|
|
38
|
-
console.log(`Endorsements: ${score.endorsements}`);
|
|
39
|
-
console.log(`Completion Rate: ${(score.completionRate * 100).toFixed(1)}%`);
|
|
40
|
-
console.log(`Response Time: ${score.responseTime.toFixed(0)}ms`);
|
|
41
|
-
console.log(`Uptime: ${(score.uptime * 100).toFixed(1)}%`);
|
|
42
|
-
console.log(`Last Updated: ${new Date(score.lastUpdated).toLocaleString()}`);
|
|
43
|
-
|
|
44
|
-
if (endorsements.length > 0) {
|
|
45
|
-
console.log('\n✨ Endorsements:');
|
|
46
|
-
for (const e of endorsements) {
|
|
47
|
-
console.log(` • From: ${e.from}`);
|
|
48
|
-
console.log(` Score: ${(e.score * 100).toFixed(0)}%`);
|
|
49
|
-
console.log(` Reason: ${e.reason}`);
|
|
50
|
-
console.log(` Date: ${new Date(e.timestamp).toLocaleDateString()}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
await trustSystem.stop();
|
|
55
|
-
} catch (error) {
|
|
56
|
-
logger.error('Failed to show trust score', error);
|
|
57
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Endorse an agent
|
|
63
|
-
trust
|
|
64
|
-
.command('endorse')
|
|
65
|
-
.description('Endorse an agent')
|
|
66
|
-
.argument('<did>', 'Agent DID to endorse')
|
|
67
|
-
.option('-s, --score <score>', 'Endorsement score (0-1)', '0.8')
|
|
68
|
-
.option('-r, --reason <reason>', 'Reason for endorsement', 'Good collaboration')
|
|
69
|
-
.action(async (did: string, options: { score: string; reason: string }) => {
|
|
70
|
-
try {
|
|
71
|
-
const dataDir = join(homedir(), '.clawiverse');
|
|
72
|
-
const trustSystem = createTrustSystem({
|
|
73
|
-
dbPath: `${dataDir}/trust`,
|
|
74
|
-
getPublicKey: async (did: string) => extractPublicKey(did), // CVP-0010 §4.1 fix
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
await trustSystem.start();
|
|
78
|
-
|
|
79
|
-
const score = parseFloat(options.score);
|
|
80
|
-
if (isNaN(score) || score < 0 || score > 1) {
|
|
81
|
-
throw new Error('Score must be between 0 and 1');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Load identity for signing
|
|
85
|
-
const identity = getIdentity();
|
|
86
|
-
if (!identity) {
|
|
87
|
-
throw new Error('No identity found. Run "hw1 init" first.');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const { importKeyPair } = await import('@highway1/core');
|
|
91
|
-
const keyPair = importKeyPair({
|
|
92
|
-
publicKey: identity.publicKey,
|
|
93
|
-
privateKey: identity.privateKey,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const endorsement = await trustSystem.endorse(
|
|
97
|
-
identity.did,
|
|
98
|
-
did,
|
|
99
|
-
score,
|
|
100
|
-
options.reason,
|
|
101
|
-
keyPair.sign
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
console.log('\n✅ Endorsement created');
|
|
105
|
-
console.log(`From: ${endorsement.from}`);
|
|
106
|
-
console.log(`To: ${endorsement.to}`);
|
|
107
|
-
console.log(`Score: ${(endorsement.score * 100).toFixed(0)}%`);
|
|
108
|
-
console.log(`Reason: ${endorsement.reason}`);
|
|
109
|
-
|
|
110
|
-
await trustSystem.stop();
|
|
111
|
-
} catch (error) {
|
|
112
|
-
logger.error('Failed to endorse agent', error);
|
|
113
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
114
|
-
process.exit(1);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Show interaction history
|
|
119
|
-
trust
|
|
120
|
-
.command('history')
|
|
121
|
-
.description('Show interaction history with an agent')
|
|
122
|
-
.argument('<did>', 'Agent DID')
|
|
123
|
-
.option('-l, --limit <limit>', 'Number of interactions to show', '10')
|
|
124
|
-
.action(async (did: string, options: { limit: string }) => {
|
|
125
|
-
try {
|
|
126
|
-
const dataDir = join(homedir(), '.clawiverse');
|
|
127
|
-
const trustSystem = createTrustSystem({
|
|
128
|
-
dbPath: `${dataDir}/trust`,
|
|
129
|
-
getPublicKey: async (did: string) => extractPublicKey(did), // CVP-0010 §4.1 fix
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
await trustSystem.start();
|
|
133
|
-
|
|
134
|
-
const limit = parseInt(options.limit, 10);
|
|
135
|
-
const history = await trustSystem.getHistory(did, limit);
|
|
136
|
-
|
|
137
|
-
console.log(`\n📜 Interaction History with ${did}`);
|
|
138
|
-
console.log('─'.repeat(60));
|
|
139
|
-
|
|
140
|
-
if (history.length === 0) {
|
|
141
|
-
console.log('No interactions recorded');
|
|
142
|
-
} else {
|
|
143
|
-
for (const interaction of history) {
|
|
144
|
-
const status = interaction.success ? '✅' : '❌';
|
|
145
|
-
console.log(`${status} ${interaction.type} - ${new Date(interaction.timestamp).toLocaleString()}`);
|
|
146
|
-
console.log(` Response time: ${interaction.responseTime}ms`);
|
|
147
|
-
if (interaction.rating) {
|
|
148
|
-
console.log(` Rating: ${'⭐'.repeat(interaction.rating)}`);
|
|
149
|
-
}
|
|
150
|
-
if (interaction.feedback) {
|
|
151
|
-
console.log(` Feedback: ${interaction.feedback}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await trustSystem.stop();
|
|
157
|
-
} catch (error) {
|
|
158
|
-
logger.error('Failed to show history', error);
|
|
159
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Show trust statistics
|
|
165
|
-
trust
|
|
166
|
-
.command('stats')
|
|
167
|
-
.description('Show local trust statistics')
|
|
168
|
-
.action(async () => {
|
|
169
|
-
try {
|
|
170
|
-
const dataDir = join(homedir(), '.clawiverse');
|
|
171
|
-
const trustSystem = createTrustSystem({
|
|
172
|
-
dbPath: `${dataDir}/trust`,
|
|
173
|
-
getPublicKey: async (did: string) => extractPublicKey(did), // CVP-0010 §4.1 fix
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
await trustSystem.start();
|
|
177
|
-
|
|
178
|
-
// Get all agents from interaction history
|
|
179
|
-
const { InteractionHistory } = await import('@highway1/core');
|
|
180
|
-
const history = new InteractionHistory(`${dataDir}/trust/interactions`);
|
|
181
|
-
await history.open();
|
|
182
|
-
|
|
183
|
-
const agents = await history.getAllAgents();
|
|
184
|
-
|
|
185
|
-
console.log('\n📊 Trust Statistics');
|
|
186
|
-
console.log('─'.repeat(60));
|
|
187
|
-
console.log(`Total agents tracked: ${agents.length}`);
|
|
188
|
-
|
|
189
|
-
if (agents.length > 0) {
|
|
190
|
-
console.log('\nTop agents by trust score:');
|
|
191
|
-
const scores = await Promise.all(
|
|
192
|
-
agents.map(async (did) => ({
|
|
193
|
-
did,
|
|
194
|
-
score: await trustSystem.getTrustScore(did),
|
|
195
|
-
}))
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
scores.sort((a, b) => b.score.interactionScore - a.score.interactionScore);
|
|
199
|
-
|
|
200
|
-
for (const { did, score } of scores.slice(0, 5)) {
|
|
201
|
-
console.log(` ${(score.interactionScore * 100).toFixed(0)}% - ${did}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
await history.close();
|
|
206
|
-
await trustSystem.stop();
|
|
207
|
-
} catch (error) {
|
|
208
|
-
logger.error('Failed to show stats', error);
|
|
209
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Block an agent (via daemon defense layer)
|
|
215
|
-
trust
|
|
216
|
-
.command('block <did>')
|
|
217
|
-
.description('Block an agent from sending you messages')
|
|
218
|
-
.option('-r, --reason <reason>', 'Reason for blocking', 'Blocked by user')
|
|
219
|
-
.action(async (did: string, options: { reason: string }) => {
|
|
220
|
-
try {
|
|
221
|
-
const { DaemonClient } = await import('../daemon/client.js');
|
|
222
|
-
const client = new DaemonClient();
|
|
223
|
-
if (await client.isDaemonRunning()) {
|
|
224
|
-
await client.send('block', { did, reason: options.reason });
|
|
225
|
-
console.log(`Blocked ${did}`);
|
|
226
|
-
} else {
|
|
227
|
-
// Daemon not running — write directly to storage
|
|
228
|
-
const { MessageStorage } = await import('@highway1/core');
|
|
229
|
-
const storage = new MessageStorage(join(homedir(), '.clawiverse', 'inbox'));
|
|
230
|
-
await storage.open();
|
|
231
|
-
await storage.putBlock({ did, reason: options.reason, blockedAt: Date.now(), blockedBy: 'local' });
|
|
232
|
-
await storage.close();
|
|
233
|
-
console.log(`Blocked ${did}`);
|
|
234
|
-
}
|
|
235
|
-
} catch (error) {
|
|
236
|
-
logger.error('Failed to block agent', error);
|
|
237
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// Unblock an agent
|
|
243
|
-
trust
|
|
244
|
-
.command('unblock <did>')
|
|
245
|
-
.description('Unblock a previously blocked agent')
|
|
246
|
-
.action(async (did: string) => {
|
|
247
|
-
try {
|
|
248
|
-
const { DaemonClient } = await import('../daemon/client.js');
|
|
249
|
-
const client = new DaemonClient();
|
|
250
|
-
if (await client.isDaemonRunning()) {
|
|
251
|
-
await client.send('unblock', { did });
|
|
252
|
-
} else {
|
|
253
|
-
const { MessageStorage } = await import('@highway1/core');
|
|
254
|
-
const storage = new MessageStorage(join(homedir(), '.clawiverse', 'inbox'));
|
|
255
|
-
await storage.open();
|
|
256
|
-
await storage.deleteBlock(did);
|
|
257
|
-
await storage.close();
|
|
258
|
-
}
|
|
259
|
-
console.log(`Unblocked ${did}`);
|
|
260
|
-
} catch (error) {
|
|
261
|
-
logger.error('Failed to unblock agent', error);
|
|
262
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
263
|
-
process.exit(1);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// List blocked agents
|
|
268
|
-
trust
|
|
269
|
-
.command('list-blocked')
|
|
270
|
-
.description('List all blocked agents')
|
|
271
|
-
.action(async () => {
|
|
272
|
-
try {
|
|
273
|
-
const { MessageStorage } = await import('@highway1/core');
|
|
274
|
-
const storage = new MessageStorage(join(homedir(), '.clawiverse', 'inbox'));
|
|
275
|
-
await storage.open();
|
|
276
|
-
const blocked = await storage.listBlocked();
|
|
277
|
-
await storage.close();
|
|
278
|
-
|
|
279
|
-
if (blocked.length === 0) {
|
|
280
|
-
console.log('No blocked agents.');
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
console.log(`\nBlocked agents (${blocked.length}):\n`);
|
|
284
|
-
for (const entry of blocked) {
|
|
285
|
-
console.log(` ${entry.did}`);
|
|
286
|
-
console.log(` Reason: ${entry.reason}`);
|
|
287
|
-
console.log(` Blocked: ${new Date(entry.blockedAt).toLocaleString()}`);
|
|
288
|
-
}
|
|
289
|
-
console.log();
|
|
290
|
-
} catch (error) {
|
|
291
|
-
logger.error('Failed to list blocked agents', error);
|
|
292
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
293
|
-
process.exit(1);
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Add to allowlist
|
|
298
|
-
trust
|
|
299
|
-
.command('allow <did>')
|
|
300
|
-
.description('Add an agent to your allowlist (bypasses all defense checks)')
|
|
301
|
-
.option('-n, --note <note>', 'Note about this agent')
|
|
302
|
-
.action(async (did: string, options: { note?: string }) => {
|
|
303
|
-
try {
|
|
304
|
-
const { DaemonClient } = await import('../daemon/client.js');
|
|
305
|
-
const client = new DaemonClient();
|
|
306
|
-
if (await client.isDaemonRunning()) {
|
|
307
|
-
await client.send('allowlist', { action: 'add', did, note: options.note });
|
|
308
|
-
} else {
|
|
309
|
-
const { MessageStorage } = await import('@highway1/core');
|
|
310
|
-
const storage = new MessageStorage(join(homedir(), '.clawiverse', 'inbox'));
|
|
311
|
-
await storage.open();
|
|
312
|
-
await storage.putAllow({ did, addedAt: Date.now(), note: options.note });
|
|
313
|
-
await storage.close();
|
|
314
|
-
}
|
|
315
|
-
console.log(`Added ${did} to allowlist`);
|
|
316
|
-
} catch (error) {
|
|
317
|
-
logger.error('Failed to allowlist agent', error);
|
|
318
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
319
|
-
process.exit(1);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
// List allowlisted agents
|
|
324
|
-
trust
|
|
325
|
-
.command('list-allowed')
|
|
326
|
-
.description('List all allowlisted agents')
|
|
327
|
-
.action(async () => {
|
|
328
|
-
try {
|
|
329
|
-
const { MessageStorage } = await import('@highway1/core');
|
|
330
|
-
const storage = new MessageStorage(join(homedir(), '.clawiverse', 'inbox'));
|
|
331
|
-
await storage.open();
|
|
332
|
-
const allowed = await storage.listAllowed();
|
|
333
|
-
await storage.close();
|
|
334
|
-
|
|
335
|
-
if (allowed.length === 0) {
|
|
336
|
-
console.log('No allowlisted agents.');
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
console.log(`\nAllowlisted agents (${allowed.length}):\n`);
|
|
340
|
-
for (const entry of allowed) {
|
|
341
|
-
console.log(` ${entry.did}`);
|
|
342
|
-
if (entry.note) console.log(` Note: ${entry.note}`);
|
|
343
|
-
console.log(` Added: ${new Date(entry.addedAt).toLocaleString()}`);
|
|
344
|
-
}
|
|
345
|
-
console.log();
|
|
346
|
-
} catch (error) {
|
|
347
|
-
logger.error('Failed to list allowed agents', error);
|
|
348
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
349
|
-
process.exit(1);
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
return trust;
|
|
354
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import Conf from 'conf';
|
|
2
|
-
import { homedir } from 'os';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
|
|
5
|
-
export interface ClawiverseConfig {
|
|
6
|
-
identity?: {
|
|
7
|
-
did: string;
|
|
8
|
-
publicKey: string;
|
|
9
|
-
privateKey: string;
|
|
10
|
-
};
|
|
11
|
-
network?: {
|
|
12
|
-
bootstrapPeers: string[];
|
|
13
|
-
listenAddresses: string[];
|
|
14
|
-
};
|
|
15
|
-
agentCard?: {
|
|
16
|
-
name: string;
|
|
17
|
-
description: string;
|
|
18
|
-
capabilities: string[];
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const CLAWIVERSE_HOME = process.env.CLAWIVERSE_HOME || join(homedir(), '.clawiverse');
|
|
23
|
-
|
|
24
|
-
const config = new Conf<ClawiverseConfig>({
|
|
25
|
-
projectName: 'clawiverse',
|
|
26
|
-
cwd: CLAWIVERSE_HOME,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
export function getConfig(): ClawiverseConfig {
|
|
30
|
-
return config.store;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function setConfig(key: string, value: unknown): void {
|
|
34
|
-
config.set(key, value);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getConfigValue<T>(key: string): T | undefined {
|
|
38
|
-
return config.get(key) as T | undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function hasIdentity(): boolean {
|
|
42
|
-
return config.has('identity');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function getIdentity() {
|
|
46
|
-
return config.get('identity');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function setIdentity(identity: ClawiverseConfig['identity']): void {
|
|
50
|
-
config.set('identity', identity);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getAgentCard() {
|
|
54
|
-
return config.get('agentCard');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function setAgentCard(card: ClawiverseConfig['agentCard']): void {
|
|
58
|
-
config.set('agentCard', card);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Default public bootstrap nodes (update after deployment)
|
|
62
|
-
const DEFAULT_BOOTSTRAP_PEERS: string[] = [
|
|
63
|
-
'/dns4/hw1.woowot.com/tcp/4001/p2p/12D3KooWH9R8d4bhzpfyWi3aY5wLBsmLp1LU8ZGk9V7rK17sfzaW',
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
export function getBootstrapPeers(): string[] {
|
|
67
|
-
const userPeers = config.get('network.bootstrapPeers') as string[] | undefined;
|
|
68
|
-
if (userPeers && userPeers.length > 0) return userPeers;
|
|
69
|
-
return DEFAULT_BOOTSTRAP_PEERS;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function setBootstrapPeers(peers: string[]): void {
|
|
73
|
-
config.set('network.bootstrapPeers', peers);
|
|
74
|
-
}
|
package/src/daemon/client.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { connect, Socket } from 'net';
|
|
2
|
-
import { createLogger } from '@highway1/core';
|
|
3
|
-
|
|
4
|
-
const logger = createLogger('daemon-client');
|
|
5
|
-
|
|
6
|
-
export class DaemonClient {
|
|
7
|
-
private socketPath: string;
|
|
8
|
-
|
|
9
|
-
constructor(socketPath: string = '/tmp/clawiverse.sock') {
|
|
10
|
-
this.socketPath = socketPath;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async send(command: string, params: any): Promise<any> {
|
|
14
|
-
return new Promise((resolve, reject) => {
|
|
15
|
-
const socket = connect(this.socketPath);
|
|
16
|
-
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
17
|
-
|
|
18
|
-
let responseReceived = false;
|
|
19
|
-
let buffer = ''; // CVP-0010 §1.2: Buffer for NDJSON framing
|
|
20
|
-
|
|
21
|
-
socket.on('connect', () => {
|
|
22
|
-
const request = { id: requestId, command, params };
|
|
23
|
-
logger.debug('Sending request', { command, id: requestId });
|
|
24
|
-
// CVP-0010 §1.2: Send with newline delimiter
|
|
25
|
-
socket.write(JSON.stringify(request) + '\n');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
socket.on('data', (data) => {
|
|
29
|
-
try {
|
|
30
|
-
// CVP-0010 §1.2: Accumulate data and split by newlines
|
|
31
|
-
buffer += data.toString();
|
|
32
|
-
const lines = buffer.split('\n');
|
|
33
|
-
|
|
34
|
-
// Keep incomplete line in buffer
|
|
35
|
-
buffer = lines.pop() || '';
|
|
36
|
-
|
|
37
|
-
// Process complete lines
|
|
38
|
-
for (const line of lines) {
|
|
39
|
-
if (!line.trim()) continue;
|
|
40
|
-
|
|
41
|
-
const response = JSON.parse(line);
|
|
42
|
-
|
|
43
|
-
// Match response by ID
|
|
44
|
-
if (response.id !== requestId) {
|
|
45
|
-
logger.debug('Ignoring unmatched response', { responseId: response.id, requestId });
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
responseReceived = true;
|
|
50
|
-
socket.end();
|
|
51
|
-
|
|
52
|
-
logger.debug('Received response', { success: response.success, id: response.id });
|
|
53
|
-
|
|
54
|
-
if (response.success) {
|
|
55
|
-
resolve(response.data);
|
|
56
|
-
} else {
|
|
57
|
-
reject(new Error(response.error));
|
|
58
|
-
}
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
reject(new Error(`Failed to parse response: ${(error as Error).message}`));
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
socket.on('error', (err) => {
|
|
67
|
-
if (!responseReceived) {
|
|
68
|
-
logger.debug('Socket error', { error: err.message });
|
|
69
|
-
reject(err);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
socket.setTimeout(30000, () => {
|
|
74
|
-
if (!responseReceived) {
|
|
75
|
-
socket.destroy();
|
|
76
|
-
reject(new Error('Request timeout'));
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async isDaemonRunning(): Promise<boolean> {
|
|
83
|
-
try {
|
|
84
|
-
await this.send('status', {});
|
|
85
|
-
return true;
|
|
86
|
-
} catch {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|