@highway1/cli 0.1.49 → 0.1.52
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/dist/index.js +10802 -112048
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/commands/ask.ts +158 -0
- package/src/commands/card.ts +8 -3
- package/src/commands/discover.ts +52 -42
- package/src/commands/identity.ts +12 -3
- package/src/commands/inbox.ts +222 -0
- package/src/commands/join.ts +36 -228
- package/src/commands/peers.ts +85 -0
- package/src/commands/send.ts +56 -58
- package/src/commands/serve.ts +271 -0
- package/src/commands/status.ts +18 -3
- package/src/commands/stop.ts +49 -0
- package/src/commands/trust.ts +144 -5
- package/src/daemon/client.ts +31 -9
- package/src/daemon/server.ts +301 -88
- package/src/index.ts +10 -0
- package/LICENSE +0 -21
package/src/daemon/server.ts
CHANGED
|
@@ -1,27 +1,65 @@
|
|
|
1
1
|
import { createServer, Socket } from 'net';
|
|
2
2
|
import type { Server } from 'net';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
3
5
|
import {
|
|
4
|
-
|
|
6
|
+
createRelayClient,
|
|
7
|
+
createRelayIndexOperations,
|
|
5
8
|
importKeyPair,
|
|
6
9
|
createMessageRouter,
|
|
7
|
-
createDHTOperations,
|
|
8
10
|
sign,
|
|
9
11
|
verify,
|
|
10
12
|
extractPublicKey,
|
|
11
13
|
createEnvelope,
|
|
12
14
|
signEnvelope,
|
|
13
|
-
|
|
15
|
+
createTrustSystem,
|
|
16
|
+
MessageQueue,
|
|
17
|
+
DefenseMiddleware,
|
|
18
|
+
type RelayClient,
|
|
14
19
|
type MessageRouter,
|
|
15
|
-
type
|
|
20
|
+
type RelayIndexOperations,
|
|
21
|
+
type TrustSystem,
|
|
22
|
+
type MessageEnvelope,
|
|
16
23
|
} from '@highway1/core';
|
|
17
24
|
import { createLogger } from '@highway1/core';
|
|
18
25
|
import { getIdentity, getBootstrapPeers } from '../config.js';
|
|
19
26
|
|
|
20
27
|
const logger = createLogger('daemon');
|
|
21
28
|
|
|
29
|
+
// Default relay URLs (CVP-0011)
|
|
30
|
+
const DEFAULT_RELAY_URLS = [
|
|
31
|
+
'ws://relay.highway1.net:8080',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function getRelayUrls(): string[] {
|
|
35
|
+
const envRelays = process.env.HW1_RELAY_URLS;
|
|
36
|
+
if (envRelays) return envRelays.split(',').map((u) => u.trim());
|
|
37
|
+
return DEFAULT_RELAY_URLS;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type DaemonCommand =
|
|
41
|
+
| 'send'
|
|
42
|
+
| 'discover'
|
|
43
|
+
| 'status'
|
|
44
|
+
| 'messages'
|
|
45
|
+
| 'shutdown'
|
|
46
|
+
// Queue commands
|
|
47
|
+
| 'inbox'
|
|
48
|
+
| 'get_message'
|
|
49
|
+
| 'mark_read'
|
|
50
|
+
| 'delete_message'
|
|
51
|
+
| 'outbox'
|
|
52
|
+
| 'retry_message'
|
|
53
|
+
// Defense commands
|
|
54
|
+
| 'block'
|
|
55
|
+
| 'unblock'
|
|
56
|
+
| 'allowlist'
|
|
57
|
+
// Stats
|
|
58
|
+
| 'queue_stats';
|
|
59
|
+
|
|
22
60
|
interface DaemonRequest {
|
|
23
61
|
id: string;
|
|
24
|
-
command:
|
|
62
|
+
command: DaemonCommand;
|
|
25
63
|
params: any;
|
|
26
64
|
}
|
|
27
65
|
|
|
@@ -33,18 +71,21 @@ interface DaemonResponse {
|
|
|
33
71
|
}
|
|
34
72
|
|
|
35
73
|
export class ClawDaemon {
|
|
36
|
-
private
|
|
74
|
+
private relayClient: RelayClient | null = null;
|
|
75
|
+
private relayIndex: RelayIndexOperations | null = null;
|
|
37
76
|
private router: MessageRouter | null = null;
|
|
38
|
-
private dht: DHTOperations | null = null;
|
|
39
77
|
private server: Server | null = null;
|
|
40
78
|
private socketPath: string;
|
|
41
79
|
private identity: any;
|
|
42
|
-
|
|
80
|
+
|
|
81
|
+
// Persistent queue + defense
|
|
82
|
+
private queue: MessageQueue | null = null;
|
|
83
|
+
private defense: DefenseMiddleware | null = null;
|
|
84
|
+
private trustSystem: TrustSystem | null = null;
|
|
43
85
|
|
|
44
86
|
constructor(socketPath: string = '/tmp/clawiverse.sock') {
|
|
45
87
|
this.socketPath = socketPath;
|
|
46
88
|
this.identity = getIdentity();
|
|
47
|
-
this.bootstrapPeers = getBootstrapPeers();
|
|
48
89
|
|
|
49
90
|
if (!this.identity) {
|
|
50
91
|
throw new Error('No identity found. Run "clawiverse init" first.');
|
|
@@ -55,24 +96,38 @@ export class ClawDaemon {
|
|
|
55
96
|
try {
|
|
56
97
|
logger.info('Starting Clawiverse daemon', { socketPath: this.socketPath });
|
|
57
98
|
|
|
58
|
-
// Initialize node once (eliminates 4s overhead per command)
|
|
59
99
|
const keyPair = importKeyPair({
|
|
60
100
|
publicKey: this.identity.publicKey,
|
|
61
101
|
privateKey: this.identity.privateKey,
|
|
62
102
|
});
|
|
63
103
|
|
|
64
|
-
|
|
104
|
+
// Load agent card for HELLO message
|
|
105
|
+
const { getAgentCard, createAgentCard, signAgentCard } = await import('@highway1/core');
|
|
106
|
+
const cardConfig = getAgentCard();
|
|
107
|
+
const capabilities = (cardConfig?.capabilities ?? []).map((c: string) => ({
|
|
108
|
+
id: c, name: c, description: `Capability: ${c}`,
|
|
109
|
+
}));
|
|
110
|
+
const agentCard = createAgentCard(
|
|
111
|
+
this.identity.did,
|
|
112
|
+
cardConfig?.name ?? 'Clawiverse Agent',
|
|
113
|
+
cardConfig?.description ?? '',
|
|
114
|
+
capabilities,
|
|
115
|
+
[],
|
|
116
|
+
);
|
|
117
|
+
const signedCard = await signAgentCard(agentCard, (data) => sign(data, keyPair.privateKey));
|
|
118
|
+
|
|
119
|
+
const relayUrls = getRelayUrls();
|
|
120
|
+
this.relayClient = createRelayClient({
|
|
121
|
+
relayUrls,
|
|
122
|
+
did: this.identity.did,
|
|
65
123
|
keyPair,
|
|
66
|
-
|
|
67
|
-
enableDHT: true,
|
|
68
|
-
reserveRelaySlot: this.bootstrapPeers.length > 0,
|
|
124
|
+
card: signedCard,
|
|
69
125
|
});
|
|
70
126
|
|
|
71
|
-
await this.
|
|
72
|
-
logger.info('
|
|
127
|
+
await this.relayClient.start();
|
|
128
|
+
logger.info('Relay client started', { relays: this.relayClient.getConnectedRelays() });
|
|
73
129
|
|
|
74
|
-
|
|
75
|
-
this.dht = createDHTOperations(this.node.libp2p);
|
|
130
|
+
this.relayIndex = createRelayIndexOperations(this.relayClient);
|
|
76
131
|
|
|
77
132
|
const verifyFn = async (signature: Uint8Array, data: Uint8Array): Promise<boolean> => {
|
|
78
133
|
try {
|
|
@@ -85,16 +140,39 @@ export class ClawDaemon {
|
|
|
85
140
|
}
|
|
86
141
|
};
|
|
87
142
|
|
|
88
|
-
this.router = createMessageRouter(
|
|
89
|
-
this.node.libp2p,
|
|
90
|
-
verifyFn,
|
|
91
|
-
this.dht,
|
|
92
|
-
this.bootstrapPeers
|
|
93
|
-
);
|
|
94
|
-
|
|
143
|
+
this.router = createMessageRouter(this.relayClient, verifyFn);
|
|
95
144
|
await this.router.start();
|
|
96
145
|
logger.info('Router started');
|
|
97
146
|
|
|
147
|
+
// Initialize trust system
|
|
148
|
+
const dataDir = join(homedir(), '.clawiverse');
|
|
149
|
+
this.trustSystem = createTrustSystem({
|
|
150
|
+
dbPath: join(dataDir, 'trust'),
|
|
151
|
+
getPublicKey: async (did: string) => extractPublicKey(did),
|
|
152
|
+
});
|
|
153
|
+
await this.trustSystem.start();
|
|
154
|
+
logger.info('Trust system started');
|
|
155
|
+
|
|
156
|
+
// Initialize message queue (LevelDB persistence per CVP-0010 §2.3)
|
|
157
|
+
this.queue = new MessageQueue({
|
|
158
|
+
dbPath: join(dataDir, 'inbox'),
|
|
159
|
+
});
|
|
160
|
+
await this.queue.start();
|
|
161
|
+
logger.info('Message queue started');
|
|
162
|
+
|
|
163
|
+
// Initialize defense middleware
|
|
164
|
+
this.defense = new DefenseMiddleware({
|
|
165
|
+
trustSystem: this.trustSystem,
|
|
166
|
+
storage: this.queue.store,
|
|
167
|
+
minTrustScore: 0,
|
|
168
|
+
});
|
|
169
|
+
logger.info('Defense middleware initialized');
|
|
170
|
+
|
|
171
|
+
// Register catch-all handler: defense check + queue persistence
|
|
172
|
+
this.router.registerCatchAllHandler(async (envelope) => {
|
|
173
|
+
return await this.handleIncomingMessage(envelope);
|
|
174
|
+
});
|
|
175
|
+
|
|
98
176
|
// Create IPC server
|
|
99
177
|
this.server = createServer((socket) => {
|
|
100
178
|
this.handleConnection(socket);
|
|
@@ -105,7 +183,7 @@ export class ClawDaemon {
|
|
|
105
183
|
|
|
106
184
|
console.log(`✓ Clawiverse daemon started`);
|
|
107
185
|
console.log(` Socket: ${this.socketPath}`);
|
|
108
|
-
console.log(`
|
|
186
|
+
console.log(` Relays: ${this.relayClient.getConnectedRelays().join(', ')}`);
|
|
109
187
|
console.log(` DID: ${this.identity.did}`);
|
|
110
188
|
} catch (error) {
|
|
111
189
|
logger.error('Failed to start daemon', error);
|
|
@@ -113,21 +191,63 @@ export class ClawDaemon {
|
|
|
113
191
|
}
|
|
114
192
|
}
|
|
115
193
|
|
|
194
|
+
private async handleIncomingMessage(envelope: MessageEnvelope): Promise<MessageEnvelope | void> {
|
|
195
|
+
if (!this.defense || !this.queue || !this.trustSystem) return;
|
|
196
|
+
|
|
197
|
+
const result = await this.defense.checkMessage(envelope);
|
|
198
|
+
if (!result.allowed) {
|
|
199
|
+
logger.warn('Message rejected by defense', { id: envelope.id, reason: result.reason });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
await this.queue.enqueueInbound(envelope, result.trustScore);
|
|
204
|
+
|
|
205
|
+
await this.trustSystem.recordInteraction({
|
|
206
|
+
agentDid: envelope.from,
|
|
207
|
+
timestamp: Date.now(),
|
|
208
|
+
type: 'message',
|
|
209
|
+
success: true,
|
|
210
|
+
responseTime: 0,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
logger.info('Message queued', { id: envelope.id, from: envelope.from });
|
|
214
|
+
}
|
|
215
|
+
|
|
116
216
|
private handleConnection(socket: Socket): void {
|
|
217
|
+
let buffer = '';
|
|
218
|
+
|
|
117
219
|
socket.on('data', async (data) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
id:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
220
|
+
buffer += data.toString();
|
|
221
|
+
const lines = buffer.split('\n');
|
|
222
|
+
buffer = lines.pop() ?? '';
|
|
223
|
+
|
|
224
|
+
for (const line of lines) {
|
|
225
|
+
if (!line.trim()) continue;
|
|
226
|
+
try {
|
|
227
|
+
const request: DaemonRequest = JSON.parse(line);
|
|
228
|
+
logger.debug('Received request', { command: request.command, id: request.id });
|
|
229
|
+
const response = await this.handleRequest(request);
|
|
230
|
+
socket.write(JSON.stringify(response) + '\n');
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const errorResponse: DaemonResponse = {
|
|
233
|
+
id: 'unknown',
|
|
234
|
+
success: false,
|
|
235
|
+
error: (error as Error).message,
|
|
236
|
+
};
|
|
237
|
+
socket.write(JSON.stringify(errorResponse) + '\n');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (buffer.trim()) {
|
|
242
|
+
try {
|
|
243
|
+
const request: DaemonRequest = JSON.parse(buffer);
|
|
244
|
+
buffer = '';
|
|
245
|
+
logger.debug('Received request', { command: request.command, id: request.id });
|
|
246
|
+
const response = await this.handleRequest(request);
|
|
247
|
+
socket.write(JSON.stringify(response) + '\n');
|
|
248
|
+
} catch {
|
|
249
|
+
// Not complete JSON yet, keep buffering
|
|
250
|
+
}
|
|
131
251
|
}
|
|
132
252
|
});
|
|
133
253
|
|
|
@@ -139,19 +259,27 @@ export class ClawDaemon {
|
|
|
139
259
|
private async handleRequest(req: DaemonRequest): Promise<DaemonResponse> {
|
|
140
260
|
try {
|
|
141
261
|
switch (req.command) {
|
|
142
|
-
case 'send':
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
case '
|
|
146
|
-
return await this.handleDiscover(req);
|
|
147
|
-
|
|
148
|
-
case 'status':
|
|
149
|
-
return this.handleStatus(req);
|
|
150
|
-
|
|
262
|
+
case 'send': return await this.handleSend(req);
|
|
263
|
+
case 'discover': return await this.handleDiscover(req);
|
|
264
|
+
case 'status': return this.handleStatus(req);
|
|
265
|
+
case 'messages': return await this.handleMessages(req);
|
|
151
266
|
case 'shutdown':
|
|
152
267
|
await this.shutdown();
|
|
153
268
|
return { id: req.id, success: true };
|
|
154
269
|
|
|
270
|
+
case 'inbox': return await this.handleInbox(req);
|
|
271
|
+
case 'get_message': return await this.handleGetMessage(req);
|
|
272
|
+
case 'mark_read': return await this.handleMarkRead(req);
|
|
273
|
+
case 'delete_message': return await this.handleDeleteMessage(req);
|
|
274
|
+
case 'outbox': return await this.handleOutbox(req);
|
|
275
|
+
case 'retry_message': return await this.handleRetryMessage(req);
|
|
276
|
+
|
|
277
|
+
case 'block': return await this.handleBlock(req);
|
|
278
|
+
case 'unblock': return await this.handleUnblock(req);
|
|
279
|
+
case 'allowlist': return await this.handleAllowlist(req);
|
|
280
|
+
|
|
281
|
+
case 'queue_stats': return await this.handleQueueStats(req);
|
|
282
|
+
|
|
155
283
|
default:
|
|
156
284
|
return { id: req.id, success: false, error: 'Unknown command' };
|
|
157
285
|
}
|
|
@@ -162,13 +290,12 @@ export class ClawDaemon {
|
|
|
162
290
|
}
|
|
163
291
|
|
|
164
292
|
private async handleSend(req: DaemonRequest): Promise<DaemonResponse> {
|
|
165
|
-
const { to, protocol, payload, type
|
|
293
|
+
const { to, protocol, payload, type } = req.params;
|
|
166
294
|
|
|
167
295
|
if (!this.router) {
|
|
168
296
|
return { id: req.id, success: false, error: 'Router not initialized' };
|
|
169
297
|
}
|
|
170
298
|
|
|
171
|
-
// Create and sign envelope
|
|
172
299
|
const envelope = createEnvelope(
|
|
173
300
|
this.identity.did,
|
|
174
301
|
to,
|
|
@@ -186,65 +313,141 @@ export class ClawDaemon {
|
|
|
186
313
|
sign(data, keyPair.privateKey)
|
|
187
314
|
);
|
|
188
315
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (peer) {
|
|
192
|
-
const parts = peer.split('/p2p/');
|
|
193
|
-
if (parts.length >= 2) {
|
|
194
|
-
peerHint = {
|
|
195
|
-
peerId: parts[parts.length - 1],
|
|
196
|
-
multiaddrs: [peer],
|
|
197
|
-
};
|
|
198
|
-
}
|
|
316
|
+
if (this.queue) {
|
|
317
|
+
await this.queue.enqueueOutbound(signedEnvelope);
|
|
199
318
|
}
|
|
200
319
|
|
|
201
|
-
|
|
202
|
-
|
|
320
|
+
const response = await this.router.sendMessage(signedEnvelope);
|
|
321
|
+
|
|
322
|
+
if (this.queue) {
|
|
323
|
+
await this.queue.markOutboundDelivered(signedEnvelope.id);
|
|
324
|
+
}
|
|
203
325
|
|
|
204
326
|
return {
|
|
205
327
|
id: req.id,
|
|
206
328
|
success: true,
|
|
207
|
-
data: {
|
|
208
|
-
id: signedEnvelope.id,
|
|
209
|
-
response: response || null,
|
|
210
|
-
},
|
|
329
|
+
data: { id: signedEnvelope.id, response: response || null },
|
|
211
330
|
};
|
|
212
331
|
}
|
|
213
332
|
|
|
214
333
|
private async handleDiscover(req: DaemonRequest): Promise<DaemonResponse> {
|
|
215
334
|
const { query } = req.params;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const results = await this.dht.searchSemantic(query);
|
|
222
|
-
|
|
223
|
-
return {
|
|
224
|
-
id: req.id,
|
|
225
|
-
success: true,
|
|
226
|
-
data: results,
|
|
227
|
-
};
|
|
335
|
+
if (!this.relayIndex) return { id: req.id, success: false, error: 'Relay index not initialized' };
|
|
336
|
+
const results = await this.relayIndex.searchSemantic(query);
|
|
337
|
+
return { id: req.id, success: true, data: results };
|
|
228
338
|
}
|
|
229
339
|
|
|
230
340
|
private handleStatus(req: DaemonRequest): DaemonResponse {
|
|
231
|
-
if (!this.
|
|
232
|
-
return { id: req.id, success: false, error: 'Node not initialized' };
|
|
233
|
-
}
|
|
234
|
-
|
|
341
|
+
if (!this.relayClient) return { id: req.id, success: false, error: 'Relay client not initialized' };
|
|
235
342
|
return {
|
|
236
343
|
id: req.id,
|
|
237
344
|
success: true,
|
|
238
345
|
data: {
|
|
239
346
|
running: true,
|
|
240
|
-
|
|
347
|
+
connectedRelays: this.relayClient.getConnectedRelays(),
|
|
348
|
+
peerCount: this.relayClient.getPeerCount(),
|
|
241
349
|
did: this.identity.did,
|
|
242
|
-
multiaddrs: this.node.getMultiaddrs(),
|
|
243
|
-
bootstrapPeers: this.bootstrapPeers,
|
|
244
350
|
},
|
|
245
351
|
};
|
|
246
352
|
}
|
|
247
353
|
|
|
354
|
+
private async handleMessages(req: DaemonRequest): Promise<DaemonResponse> {
|
|
355
|
+
const { limit = 10 } = req.params || {};
|
|
356
|
+
if (this.queue) {
|
|
357
|
+
const page = await this.queue.getInbox({}, { limit });
|
|
358
|
+
return {
|
|
359
|
+
id: req.id,
|
|
360
|
+
success: true,
|
|
361
|
+
data: {
|
|
362
|
+
messages: page.messages.map((m) => ({ ...m.envelope, receivedAt: m.receivedAt })),
|
|
363
|
+
total: page.total,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
return { id: req.id, success: true, data: { messages: [], total: 0 } };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ─── Queue Handlers ───────────────────────────────────────────────────────
|
|
371
|
+
|
|
372
|
+
private async handleInbox(req: DaemonRequest): Promise<DaemonResponse> {
|
|
373
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
374
|
+
const { filter, pagination } = req.params || {};
|
|
375
|
+
const page = await this.queue.getInbox(filter, pagination);
|
|
376
|
+
return { id: req.id, success: true, data: page };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private async handleGetMessage(req: DaemonRequest): Promise<DaemonResponse> {
|
|
380
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
381
|
+
const { id } = req.params;
|
|
382
|
+
const msg = await this.queue.getMessage(id);
|
|
383
|
+
if (!msg) return { id: req.id, success: false, error: 'Message not found' };
|
|
384
|
+
return { id: req.id, success: true, data: msg };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private async handleMarkRead(req: DaemonRequest): Promise<DaemonResponse> {
|
|
388
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
389
|
+
await this.queue.markAsRead(req.params.id);
|
|
390
|
+
return { id: req.id, success: true };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private async handleDeleteMessage(req: DaemonRequest): Promise<DaemonResponse> {
|
|
394
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
395
|
+
await this.queue.deleteMessage(req.params.id);
|
|
396
|
+
return { id: req.id, success: true };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private async handleOutbox(req: DaemonRequest): Promise<DaemonResponse> {
|
|
400
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
401
|
+
const page = await this.queue.getOutbox(req.params?.pagination);
|
|
402
|
+
return { id: req.id, success: true, data: page };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private async handleRetryMessage(req: DaemonRequest): Promise<DaemonResponse> {
|
|
406
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
407
|
+
await this.queue.retryMessage(req.params.id);
|
|
408
|
+
return { id: req.id, success: true };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ─── Defense Handlers ─────────────────────────────────────────────────────
|
|
412
|
+
|
|
413
|
+
private async handleBlock(req: DaemonRequest): Promise<DaemonResponse> {
|
|
414
|
+
if (!this.defense) return { id: req.id, success: false, error: 'Defense not initialized' };
|
|
415
|
+
const { did, reason = 'Blocked by user' } = req.params;
|
|
416
|
+
await this.defense.blockAgent(did, reason, this.identity.did);
|
|
417
|
+
return { id: req.id, success: true };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private async handleUnblock(req: DaemonRequest): Promise<DaemonResponse> {
|
|
421
|
+
if (!this.defense) return { id: req.id, success: false, error: 'Defense not initialized' };
|
|
422
|
+
await this.defense.unblockAgent(req.params.did);
|
|
423
|
+
return { id: req.id, success: true };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private async handleAllowlist(req: DaemonRequest): Promise<DaemonResponse> {
|
|
427
|
+
if (!this.defense || !this.queue) return { id: req.id, success: false, error: 'Defense not initialized' };
|
|
428
|
+
const { action, did, note } = req.params;
|
|
429
|
+
switch (action) {
|
|
430
|
+
case 'add':
|
|
431
|
+
await this.defense.allowAgent(did, note);
|
|
432
|
+
return { id: req.id, success: true };
|
|
433
|
+
case 'remove':
|
|
434
|
+
await this.defense.removeFromAllowlist(did);
|
|
435
|
+
return { id: req.id, success: true };
|
|
436
|
+
case 'list': {
|
|
437
|
+
const entries = await this.queue.store.listAllowed();
|
|
438
|
+
return { id: req.id, success: true, data: entries };
|
|
439
|
+
}
|
|
440
|
+
default:
|
|
441
|
+
return { id: req.id, success: false, error: `Unknown allowlist action: ${action}` };
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private async handleQueueStats(req: DaemonRequest): Promise<DaemonResponse> {
|
|
446
|
+
if (!this.queue) return { id: req.id, success: false, error: 'Queue not initialized' };
|
|
447
|
+
const stats = await this.queue.getStats();
|
|
448
|
+
return { id: req.id, success: true, data: stats };
|
|
449
|
+
}
|
|
450
|
+
|
|
248
451
|
async shutdown(): Promise<void> {
|
|
249
452
|
logger.info('Shutting down daemon');
|
|
250
453
|
|
|
@@ -253,9 +456,19 @@ export class ClawDaemon {
|
|
|
253
456
|
this.router = null;
|
|
254
457
|
}
|
|
255
458
|
|
|
256
|
-
if (this.
|
|
257
|
-
await this.
|
|
258
|
-
this.
|
|
459
|
+
if (this.queue) {
|
|
460
|
+
await this.queue.stop();
|
|
461
|
+
this.queue = null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (this.trustSystem) {
|
|
465
|
+
await this.trustSystem.stop();
|
|
466
|
+
this.trustSystem = null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (this.relayClient) {
|
|
470
|
+
await this.relayClient.stop();
|
|
471
|
+
this.relayClient = null;
|
|
259
472
|
}
|
|
260
473
|
|
|
261
474
|
if (this.server) {
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,11 @@ import { registerIdentityCommand } from './commands/identity.js';
|
|
|
24
24
|
import { registerCardCommand } from './commands/card.js';
|
|
25
25
|
import { createTrustCommand } from './commands/trust.js';
|
|
26
26
|
import { registerDaemonCommand } from './commands/daemon.js';
|
|
27
|
+
import { createInboxCommand } from './commands/inbox.js';
|
|
28
|
+
import { registerStopCommand } from './commands/stop.js';
|
|
29
|
+
import { registerAskCommand } from './commands/ask.js';
|
|
30
|
+
import { registerServeCommand } from './commands/serve.js';
|
|
31
|
+
import { registerPeersCommand } from './commands/peers.js';
|
|
27
32
|
|
|
28
33
|
const require = createRequire(import.meta.url);
|
|
29
34
|
const { version } = require('../package.json');
|
|
@@ -42,10 +47,15 @@ registerInitCommand(program);
|
|
|
42
47
|
registerJoinCommand(program);
|
|
43
48
|
registerDiscoverCommand(program);
|
|
44
49
|
registerSendCommand(program);
|
|
50
|
+
registerAskCommand(program);
|
|
51
|
+
registerServeCommand(program);
|
|
52
|
+
registerPeersCommand(program);
|
|
45
53
|
registerStatusCommand(program);
|
|
46
54
|
registerIdentityCommand(program);
|
|
47
55
|
registerCardCommand(program);
|
|
48
56
|
registerDaemonCommand(program);
|
|
57
|
+
registerStopCommand(program);
|
|
49
58
|
program.addCommand(createTrustCommand());
|
|
59
|
+
program.addCommand(createInboxCommand());
|
|
50
60
|
|
|
51
61
|
program.parse();
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Clawiverse Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|