@highway1/cli 0.1.43 → 0.1.45
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/bin/clawiverse.js +2 -0
- package/dist/index.js +743 -163
- package/dist/index.js.map +1 -1
- package/package.json +21 -9
- package/src/commands/card.ts +99 -0
- package/src/commands/daemon.ts +207 -0
- package/src/commands/discover.ts +168 -0
- package/src/commands/identity.ts +37 -0
- package/src/commands/init.ts +54 -0
- package/src/commands/join.ts +390 -0
- package/src/commands/send.ts +299 -0
- package/src/commands/status.ts +45 -0
- package/src/commands/trust.ts +215 -0
- package/src/config.ts +74 -0
- package/src/daemon/client.ts +68 -0
- package/src/daemon/server.ts +267 -0
- package/src/index.ts +51 -0
- package/src/ui.ts +38 -0
package/package.json
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@highway1/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI tool for
|
|
3
|
+
"version": "0.1.45",
|
|
4
|
+
"description": "CLI tool for Clawiverse network",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
+
"clawiverse": "./dist/index.js",
|
|
7
8
|
"hw1": "./dist/index.js"
|
|
8
9
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"clean": "rm -rf dist"
|
|
14
|
+
},
|
|
11
15
|
"dependencies": {
|
|
12
|
-
"@highway1/core": "^0.1.
|
|
13
|
-
"commander": "^12.1.0",
|
|
16
|
+
"@highway1/core": "^0.1.45",
|
|
14
17
|
"chalk": "^5.3.0",
|
|
15
|
-
"ora": "^8.1.0",
|
|
16
|
-
"inquirer": "^13.3.0",
|
|
17
18
|
"cli-table3": "^0.6.5",
|
|
18
|
-
"
|
|
19
|
+
"commander": "^12.1.0",
|
|
20
|
+
"conf": "^13.0.1",
|
|
21
|
+
"inquirer": "^13.3.0",
|
|
22
|
+
"ora": "^8.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^22.10.2",
|
|
26
|
+
"tsup": "^8.3.5",
|
|
27
|
+
"typescript": "^5.7.2"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
19
31
|
}
|
|
20
32
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getAgentCard, setAgentCard } from '../config.js';
|
|
3
|
+
import { success, error, printHeader } from '../ui.js';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
|
|
6
|
+
export function registerCardCommand(program: Command): void {
|
|
7
|
+
const card = program.command('card').description('Manage Agent Card');
|
|
8
|
+
|
|
9
|
+
card
|
|
10
|
+
.command('show')
|
|
11
|
+
.description('Show current Agent Card')
|
|
12
|
+
.action(async () => {
|
|
13
|
+
try {
|
|
14
|
+
printHeader('Agent Card');
|
|
15
|
+
|
|
16
|
+
const agentCard = getAgentCard();
|
|
17
|
+
|
|
18
|
+
if (!agentCard) {
|
|
19
|
+
error('No Agent Card found. Run "hw1 init" first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(JSON.stringify(agentCard, null, 2));
|
|
24
|
+
} catch (err) {
|
|
25
|
+
error(`Failed to show card: ${(err as Error).message}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
card
|
|
31
|
+
.command('edit')
|
|
32
|
+
.description('Edit Agent Card')
|
|
33
|
+
.option('--name <name>', 'Agent name')
|
|
34
|
+
.option('--description <description>', 'Agent description')
|
|
35
|
+
.option('--capabilities <capabilities>', 'Capabilities (comma-separated)')
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
try {
|
|
38
|
+
printHeader('Edit Agent Card');
|
|
39
|
+
|
|
40
|
+
const currentCard = getAgentCard();
|
|
41
|
+
|
|
42
|
+
if (!currentCard) {
|
|
43
|
+
error('No Agent Card found. Run "hw1 init" first.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let updatedCard;
|
|
48
|
+
|
|
49
|
+
// If any options provided, use non-interactive mode
|
|
50
|
+
if (options.name || options.description || options.capabilities) {
|
|
51
|
+
updatedCard = {
|
|
52
|
+
name: options.name || currentCard.name,
|
|
53
|
+
description: options.description || currentCard.description,
|
|
54
|
+
capabilities: options.capabilities
|
|
55
|
+
? options.capabilities.split(',').map((c: string) => c.trim()).filter((c: string) => c.length > 0)
|
|
56
|
+
: currentCard.capabilities,
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
// Interactive mode
|
|
60
|
+
const answers = await inquirer.prompt([
|
|
61
|
+
{
|
|
62
|
+
type: 'input',
|
|
63
|
+
name: 'name',
|
|
64
|
+
message: 'Agent name:',
|
|
65
|
+
default: currentCard.name,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'input',
|
|
69
|
+
name: 'description',
|
|
70
|
+
message: 'Agent description:',
|
|
71
|
+
default: currentCard.description,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'input',
|
|
75
|
+
name: 'capabilities',
|
|
76
|
+
message: 'Capabilities (comma-separated):',
|
|
77
|
+
default: currentCard.capabilities.join(', '),
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
updatedCard = {
|
|
82
|
+
name: answers.name,
|
|
83
|
+
description: answers.description,
|
|
84
|
+
capabilities: answers.capabilities
|
|
85
|
+
.split(',')
|
|
86
|
+
.map((c: string) => c.trim())
|
|
87
|
+
.filter((c: string) => c.length > 0),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setAgentCard(updatedCard);
|
|
92
|
+
|
|
93
|
+
success('Agent Card updated successfully!');
|
|
94
|
+
} catch (err) {
|
|
95
|
+
error(`Failed to edit card: ${(err as Error).message}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { ClawDaemon } from '../daemon/server.js';
|
|
3
|
+
import { DaemonClient } from '../daemon/client.js';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { writeFileSync, readFileSync, unlinkSync, existsSync } from 'fs';
|
|
6
|
+
import { success, error, info } from '../ui.js';
|
|
7
|
+
|
|
8
|
+
const PID_FILE = '/tmp/clawiverse.pid';
|
|
9
|
+
const SOCKET_PATH = '/tmp/clawiverse.sock';
|
|
10
|
+
|
|
11
|
+
export function registerDaemonCommand(program: Command): void {
|
|
12
|
+
const daemonCommand = program
|
|
13
|
+
.command('daemon')
|
|
14
|
+
.description('Manage Clawiverse daemon for fast messaging');
|
|
15
|
+
|
|
16
|
+
daemonCommand
|
|
17
|
+
.command('start')
|
|
18
|
+
.description('Start daemon in background')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
try {
|
|
21
|
+
const client = new DaemonClient(SOCKET_PATH);
|
|
22
|
+
if (await client.isDaemonRunning()) {
|
|
23
|
+
success('Daemon already running');
|
|
24
|
+
const status = await client.send('status', {});
|
|
25
|
+
info(`Peer ID: ${status.peerId}`);
|
|
26
|
+
info(`DID: ${status.did}`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Start daemon as detached background process
|
|
31
|
+
const child = spawn(process.execPath, [
|
|
32
|
+
process.argv[1],
|
|
33
|
+
'daemon',
|
|
34
|
+
'run'
|
|
35
|
+
], {
|
|
36
|
+
detached: true,
|
|
37
|
+
stdio: 'ignore'
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
child.unref();
|
|
41
|
+
writeFileSync(PID_FILE, child.pid!.toString());
|
|
42
|
+
|
|
43
|
+
// Wait for daemon to be ready
|
|
44
|
+
for (let i = 0; i < 20; i++) {
|
|
45
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
46
|
+
if (await client.isDaemonRunning()) {
|
|
47
|
+
success('Daemon started');
|
|
48
|
+
const status = await client.send('status', {});
|
|
49
|
+
info(`Peer ID: ${status.peerId}`);
|
|
50
|
+
info(`DID: ${status.did}`);
|
|
51
|
+
info(`Socket: ${SOCKET_PATH}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
error('Failed to start daemon (timeout)');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
error(`Failed to start daemon: ${(err as Error).message}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
daemonCommand
|
|
65
|
+
.command('run')
|
|
66
|
+
.description('Run daemon in foreground (internal use)')
|
|
67
|
+
.action(async () => {
|
|
68
|
+
try {
|
|
69
|
+
const daemon = new ClawDaemon(SOCKET_PATH);
|
|
70
|
+
await daemon.start();
|
|
71
|
+
|
|
72
|
+
// Keep process alive
|
|
73
|
+
process.on('SIGINT', async () => {
|
|
74
|
+
console.log('\nShutting down...');
|
|
75
|
+
await daemon.shutdown();
|
|
76
|
+
process.exit(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
process.on('SIGTERM', async () => {
|
|
80
|
+
await daemon.shutdown();
|
|
81
|
+
process.exit(0);
|
|
82
|
+
});
|
|
83
|
+
} catch (err) {
|
|
84
|
+
error(`Failed to run daemon: ${(err as Error).message}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
daemonCommand
|
|
90
|
+
.command('stop')
|
|
91
|
+
.description('Stop daemon')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
try {
|
|
94
|
+
const client = new DaemonClient(SOCKET_PATH);
|
|
95
|
+
|
|
96
|
+
if (!(await client.isDaemonRunning())) {
|
|
97
|
+
info('Daemon not running');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await client.send('shutdown', {});
|
|
102
|
+
success('Daemon stopped');
|
|
103
|
+
|
|
104
|
+
// Clean up PID file
|
|
105
|
+
try {
|
|
106
|
+
if (existsSync(PID_FILE)) {
|
|
107
|
+
unlinkSync(PID_FILE);
|
|
108
|
+
}
|
|
109
|
+
} catch {}
|
|
110
|
+
|
|
111
|
+
// Clean up socket file
|
|
112
|
+
try {
|
|
113
|
+
if (existsSync(SOCKET_PATH)) {
|
|
114
|
+
unlinkSync(SOCKET_PATH);
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
error(`Failed to stop daemon: ${(err as Error).message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
daemonCommand
|
|
124
|
+
.command('status')
|
|
125
|
+
.description('Check daemon status')
|
|
126
|
+
.action(async () => {
|
|
127
|
+
try {
|
|
128
|
+
const client = new DaemonClient(SOCKET_PATH);
|
|
129
|
+
|
|
130
|
+
if (await client.isDaemonRunning()) {
|
|
131
|
+
const status = await client.send('status', {});
|
|
132
|
+
success('Daemon running');
|
|
133
|
+
console.log();
|
|
134
|
+
info(`Peer ID: ${status.peerId}`);
|
|
135
|
+
info(`DID: ${status.did}`);
|
|
136
|
+
info(`Socket: ${SOCKET_PATH}`);
|
|
137
|
+
info(`Bootstrap peers: ${status.bootstrapPeers.length}`);
|
|
138
|
+
console.log();
|
|
139
|
+
info('Multiaddrs:');
|
|
140
|
+
status.multiaddrs.forEach((addr: string) => {
|
|
141
|
+
console.log(` ${addr}`);
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
info('Daemon not running');
|
|
145
|
+
console.log();
|
|
146
|
+
info('Start daemon with: clawiverse daemon start');
|
|
147
|
+
}
|
|
148
|
+
} catch (err) {
|
|
149
|
+
error(`Failed to check status: ${(err as Error).message}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
daemonCommand
|
|
155
|
+
.command('restart')
|
|
156
|
+
.description('Restart daemon')
|
|
157
|
+
.action(async () => {
|
|
158
|
+
try {
|
|
159
|
+
const client = new DaemonClient(SOCKET_PATH);
|
|
160
|
+
|
|
161
|
+
// Stop if running
|
|
162
|
+
if (await client.isDaemonRunning()) {
|
|
163
|
+
info('Stopping daemon...');
|
|
164
|
+
await client.send('shutdown', {});
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Clean up files
|
|
169
|
+
try {
|
|
170
|
+
if (existsSync(PID_FILE)) unlinkSync(PID_FILE);
|
|
171
|
+
if (existsSync(SOCKET_PATH)) unlinkSync(SOCKET_PATH);
|
|
172
|
+
} catch {}
|
|
173
|
+
|
|
174
|
+
// Start daemon
|
|
175
|
+
info('Starting daemon...');
|
|
176
|
+
const child = spawn(process.execPath, [
|
|
177
|
+
process.argv[1],
|
|
178
|
+
'daemon',
|
|
179
|
+
'run'
|
|
180
|
+
], {
|
|
181
|
+
detached: true,
|
|
182
|
+
stdio: 'ignore'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
child.unref();
|
|
186
|
+
writeFileSync(PID_FILE, child.pid!.toString());
|
|
187
|
+
|
|
188
|
+
// Wait for daemon to be ready
|
|
189
|
+
for (let i = 0; i < 20; i++) {
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
191
|
+
if (await client.isDaemonRunning()) {
|
|
192
|
+
success('Daemon restarted');
|
|
193
|
+
const status = await client.send('status', {});
|
|
194
|
+
info(`Peer ID: ${status.peerId}`);
|
|
195
|
+
info(`DID: ${status.did}`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
error('Failed to restart daemon (timeout)');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
error(`Failed to restart daemon: ${(err as Error).message}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import {
|
|
3
|
+
createNode,
|
|
4
|
+
importKeyPair,
|
|
5
|
+
createDHTOperations,
|
|
6
|
+
} from '@highway1/core';
|
|
7
|
+
import { getIdentity, getBootstrapPeers } from '../config.js';
|
|
8
|
+
import { error, spinner, printHeader, info, success } from '../ui.js';
|
|
9
|
+
import Table from 'cli-table3';
|
|
10
|
+
|
|
11
|
+
export function registerDiscoverCommand(program: Command): void {
|
|
12
|
+
program
|
|
13
|
+
.command('discover')
|
|
14
|
+
.description('Discover agents on the network')
|
|
15
|
+
.option('--capability <capability>', 'Filter by capability')
|
|
16
|
+
.option('--did <did>', 'Query specific DID')
|
|
17
|
+
.option('--query <text>', 'Natural language query (e.g., "translate Japanese")')
|
|
18
|
+
.option('--min-trust <score>', 'Minimum trust score (0-1)')
|
|
19
|
+
.option('--language <lang>', 'Filter by language')
|
|
20
|
+
.option('--limit <number>', 'Maximum number of results', '10')
|
|
21
|
+
.option('--bootstrap <peers...>', 'Bootstrap peer addresses')
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
printHeader('Discover Agents');
|
|
25
|
+
|
|
26
|
+
const identity = getIdentity();
|
|
27
|
+
|
|
28
|
+
if (!identity) {
|
|
29
|
+
error('No identity found. Run "hw1 init" first.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const spin = spinner('Starting node...');
|
|
34
|
+
|
|
35
|
+
const keyPair = importKeyPair({
|
|
36
|
+
publicKey: identity.publicKey,
|
|
37
|
+
privateKey: identity.privateKey,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const bootstrapPeers = options.bootstrap || getBootstrapPeers();
|
|
41
|
+
|
|
42
|
+
const node = await createNode({
|
|
43
|
+
keyPair,
|
|
44
|
+
bootstrapPeers,
|
|
45
|
+
enableDHT: true,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await node.start();
|
|
49
|
+
|
|
50
|
+
spin.text = 'Waiting for DHT peers...';
|
|
51
|
+
// Wait until connected to at least one peer, then give DHT time to sync
|
|
52
|
+
await new Promise<void>((resolve) => {
|
|
53
|
+
const timeout = setTimeout(resolve, 15000);
|
|
54
|
+
node.libp2p.addEventListener('peer:connect', () => {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
setTimeout(resolve, 3000); // give DHT routing table time to populate
|
|
57
|
+
}, { once: true });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
spin.text = 'Querying DHT...';
|
|
61
|
+
|
|
62
|
+
const dht = createDHTOperations(node.libp2p);
|
|
63
|
+
|
|
64
|
+
if (options.did) {
|
|
65
|
+
const card = await dht.queryAgentCard(options.did);
|
|
66
|
+
|
|
67
|
+
if (card) {
|
|
68
|
+
spin.succeed('Agent found!');
|
|
69
|
+
console.log();
|
|
70
|
+
info(`DID: ${card.did}`);
|
|
71
|
+
info(`Name: ${card.name}`);
|
|
72
|
+
info(`Description: ${card.description}`);
|
|
73
|
+
info(`Version: ${card.version}`);
|
|
74
|
+
info(`Capabilities: ${card.capabilities.join(', ') || '(none)'}`);
|
|
75
|
+
info(`Peer ID: ${card.peerId || '(unknown)'}`);
|
|
76
|
+
info(`Endpoints: ${card.endpoints.length > 0 ? card.endpoints.join('\n ') : '(none)'}`);
|
|
77
|
+
info(`Timestamp: ${new Date(card.timestamp).toISOString()}`);
|
|
78
|
+
} else {
|
|
79
|
+
spin.fail('Agent not found');
|
|
80
|
+
}
|
|
81
|
+
} else if (options.capability || options.query) {
|
|
82
|
+
// Semantic search
|
|
83
|
+
const query: any = {
|
|
84
|
+
limit: parseInt(options.limit, 10),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (options.query) {
|
|
88
|
+
query.text = options.query;
|
|
89
|
+
} else if (options.capability) {
|
|
90
|
+
query.capability = options.capability;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.minTrust) {
|
|
94
|
+
query.filters = {
|
|
95
|
+
minTrustScore: parseFloat(options.minTrust),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (options.language) {
|
|
100
|
+
query.filters = query.filters || {};
|
|
101
|
+
query.filters.language = options.language;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const cards = await dht.searchSemantic(query);
|
|
105
|
+
|
|
106
|
+
spin.succeed(`Found ${cards.length} agents`);
|
|
107
|
+
|
|
108
|
+
if (cards.length > 0) {
|
|
109
|
+
const table = new Table({
|
|
110
|
+
head: ['DID', 'Name', 'Capabilities', 'Trust'],
|
|
111
|
+
colWidths: [40, 20, 35, 10],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
for (const card of cards) {
|
|
115
|
+
const capabilities = Array.isArray(card.capabilities)
|
|
116
|
+
? card.capabilities.map((cap: any) =>
|
|
117
|
+
typeof cap === 'string' ? cap : cap.name
|
|
118
|
+
).join(', ')
|
|
119
|
+
: '';
|
|
120
|
+
|
|
121
|
+
const trustScore = card.trust
|
|
122
|
+
? `${(card.trust.interactionScore * 100).toFixed(0)}%`
|
|
123
|
+
: 'N/A';
|
|
124
|
+
|
|
125
|
+
table.push([
|
|
126
|
+
card.did.substring(0, 35) + '...',
|
|
127
|
+
card.name,
|
|
128
|
+
capabilities.substring(0, 30) + (capabilities.length > 30 ? '...' : ''),
|
|
129
|
+
trustScore,
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(table.toString());
|
|
135
|
+
|
|
136
|
+
// Show detailed info for first result if query was used
|
|
137
|
+
if (options.query && cards.length > 0) {
|
|
138
|
+
const card = cards[0];
|
|
139
|
+
console.log();
|
|
140
|
+
info('Top match details:');
|
|
141
|
+
info(` DID: ${card.did}`);
|
|
142
|
+
info(` Name: ${card.name}`);
|
|
143
|
+
info(` Description: ${card.description}`);
|
|
144
|
+
|
|
145
|
+
if (Array.isArray(card.capabilities) && card.capabilities.length > 0) {
|
|
146
|
+
const cap = card.capabilities[0];
|
|
147
|
+
if (typeof cap === 'object' && cap !== null) {
|
|
148
|
+
info(` Capability: ${cap.name}`);
|
|
149
|
+
info(` ${cap.description}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
spin.fail('Please specify --did, --capability, or --query');
|
|
156
|
+
info('Usage: hw1 discover --did <did>');
|
|
157
|
+
info(' hw1 discover --capability <capability>');
|
|
158
|
+
info(' hw1 discover --query "translate Japanese"');
|
|
159
|
+
info(' hw1 discover --query "code review" --min-trust 0.8');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await node.stop();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
error(`Failed to discover: ${(err as Error).message}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getIdentity, getAgentCard } from '../config.js';
|
|
3
|
+
import { error, printHeader, printKeyValue } from '../ui.js';
|
|
4
|
+
|
|
5
|
+
export function registerIdentityCommand(program: Command): void {
|
|
6
|
+
program
|
|
7
|
+
.command('identity')
|
|
8
|
+
.description('Show current identity information')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
try {
|
|
11
|
+
printHeader('Clawiverse Identity');
|
|
12
|
+
|
|
13
|
+
const identity = getIdentity();
|
|
14
|
+
const card = getAgentCard();
|
|
15
|
+
|
|
16
|
+
if (!identity) {
|
|
17
|
+
error('No identity found. Run "hw1 init" first.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
printKeyValue('DID', identity.did);
|
|
22
|
+
printKeyValue('Public Key', identity.publicKey.substring(0, 16) + '...');
|
|
23
|
+
|
|
24
|
+
if (card) {
|
|
25
|
+
console.log();
|
|
26
|
+
printKeyValue('Name', card.name);
|
|
27
|
+
printKeyValue('Description', card.description);
|
|
28
|
+
printKeyValue('Capabilities', card.capabilities.join(', ') || 'None');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
error(`Failed to show identity: ${(err as Error).message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { generateKeyPair, exportKeyPair, deriveDID } from '@highway1/core';
|
|
3
|
+
import { hasIdentity, setIdentity, setAgentCard } from '../config.js';
|
|
4
|
+
import { success, error, spinner, printHeader, printKeyValue } from '../ui.js';
|
|
5
|
+
|
|
6
|
+
export function registerInitCommand(program: Command): void {
|
|
7
|
+
program
|
|
8
|
+
.command('init')
|
|
9
|
+
.description('Initialize a new Clawiverse identity')
|
|
10
|
+
.option('--name <name>', 'Agent name', 'My Agent')
|
|
11
|
+
.option('--description <description>', 'Agent description', 'A Clawiverse agent')
|
|
12
|
+
.option('--force', 'Overwrite existing identity')
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
try {
|
|
15
|
+
printHeader('Initialize Clawiverse Identity');
|
|
16
|
+
|
|
17
|
+
if (hasIdentity() && !options.force) {
|
|
18
|
+
error('Identity already exists. Use --force to overwrite.');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const spin = spinner('Generating key pair...');
|
|
23
|
+
|
|
24
|
+
const keyPair = await generateKeyPair();
|
|
25
|
+
const exported = exportKeyPair(keyPair);
|
|
26
|
+
const did = deriveDID(keyPair.publicKey);
|
|
27
|
+
|
|
28
|
+
setIdentity({
|
|
29
|
+
did,
|
|
30
|
+
publicKey: exported.publicKey,
|
|
31
|
+
privateKey: exported.privateKey,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
setAgentCard({
|
|
35
|
+
name: options.name,
|
|
36
|
+
description: options.description,
|
|
37
|
+
capabilities: [],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
spin.succeed('Identity created successfully!');
|
|
41
|
+
|
|
42
|
+
console.log();
|
|
43
|
+
printKeyValue('DID', did);
|
|
44
|
+
printKeyValue('Name', options.name);
|
|
45
|
+
printKeyValue('Description', options.description);
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
success('Run "hw1 join" to connect to the network');
|
|
49
|
+
} catch (err) {
|
|
50
|
+
error(`Failed to initialize: ${(err as Error).message}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|