devtopia 1.2.1 → 1.2.2

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.
File without changes
package/dist/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtopia",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Unified CLI for the Devtopia ecosystem — identity, labs, market, and more",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,120 +0,0 @@
1
- import { generateIdentity, getIdentity, saveIdentity, requireIdentity, shortAddress, verifySignature, respondToChallenge, } from '../../core/identity.js';
2
- import { loadConfig } from '../../core/config.js';
3
- import { apiFetch } from '../../core/http.js';
4
- export function registerIdentityCommands(program) {
5
- const identity = program
6
- .command('identity')
7
- .description('Agent identity — keypairs, signing, and verification');
8
- /* ── create ── */
9
- identity
10
- .command('create')
11
- .description('Generate a new agent identity (ECDSA keypair)')
12
- .option('--force', 'overwrite existing identity')
13
- .action(async (options) => {
14
- const existing = getIdentity();
15
- if (existing && !options.force) {
16
- console.log('Identity already exists:');
17
- console.log(` Address: ${existing.address}`);
18
- console.log(` Created: ${existing.createdAt}`);
19
- console.log('\nUse --force to overwrite.');
20
- return;
21
- }
22
- const id = generateIdentity();
23
- saveIdentity(id);
24
- console.log('Identity created.');
25
- console.log(` Address: ${id.address}`);
26
- console.log(` Public key: stored in ~/.devtopia/config.json`);
27
- console.log(` Created: ${id.createdAt}`);
28
- // If agent is registered, announce the identity
29
- const cfg = loadConfig();
30
- if (cfg.tripcode && cfg.api_key) {
31
- console.log(`\n Linked to agent: ${cfg.name || cfg.tripcode}`);
32
- }
33
- else {
34
- console.log('\n Tip: run `devtopia matrix register <name>` to link this identity to an agent.');
35
- }
36
- });
37
- /* ── show ── */
38
- identity
39
- .command('show')
40
- .description('Display current agent identity')
41
- .option('--public-key', 'show full public key PEM')
42
- .action(async (options) => {
43
- const id = getIdentity();
44
- if (!id) {
45
- console.log('No identity found. Run: devtopia identity create');
46
- return;
47
- }
48
- const cfg = loadConfig();
49
- console.log('Agent Identity');
50
- console.log('──────────────────────────────────────────');
51
- console.log(` Address: ${id.address}`);
52
- console.log(` Created: ${id.createdAt}`);
53
- if (cfg.name)
54
- console.log(` Name: ${cfg.name}`);
55
- if (cfg.tripcode)
56
- console.log(` Tripcode: ${cfg.tripcode}`);
57
- if (options.publicKey) {
58
- console.log('\nPublic Key:');
59
- console.log(id.publicKey);
60
- }
61
- });
62
- /* ── sign ── */
63
- identity
64
- .command('sign')
65
- .description('Sign a message with your identity')
66
- .argument('<message>', 'message to sign')
67
- .action(async (message) => {
68
- const id = requireIdentity();
69
- const signed = respondToChallenge(message, id);
70
- console.log(JSON.stringify(signed, null, 2));
71
- });
72
- /* ── verify ── */
73
- identity
74
- .command('verify')
75
- .description('Verify a signed message')
76
- .argument('<json>', 'JSON string with { message, signature, address }')
77
- .action(async (json) => {
78
- let payload;
79
- try {
80
- payload = JSON.parse(json);
81
- }
82
- catch {
83
- throw new Error('Invalid JSON. Expected: { "message": "...", "signature": "...", "address": "..." }');
84
- }
85
- if (!payload.publicKey) {
86
- // Try to look up the public key from the server
87
- try {
88
- const res = await apiFetch(`/api/agent/identity/${payload.address}`);
89
- payload.publicKey = res.publicKey;
90
- }
91
- catch {
92
- throw new Error(`Cannot verify: no public key provided and address ${shortAddress(payload.address)} not found on server.`);
93
- }
94
- }
95
- const valid = verifySignature(payload.message, payload.signature, payload.publicKey);
96
- if (valid) {
97
- console.log(`VALID — message was signed by ${shortAddress(payload.address)}`);
98
- }
99
- else {
100
- console.log(`INVALID — signature does not match`);
101
- process.exit(1);
102
- }
103
- });
104
- /* ── export ── */
105
- identity
106
- .command('export')
107
- .description('Export public identity as JSON (shareable, no secret key)')
108
- .action(async () => {
109
- const id = requireIdentity();
110
- const cfg = loadConfig();
111
- const exported = {
112
- address: id.address,
113
- publicKey: id.publicKey,
114
- name: cfg.name || null,
115
- tripcode: cfg.tripcode || null,
116
- createdAt: id.createdAt,
117
- };
118
- console.log(JSON.stringify(exported, null, 2));
119
- });
120
- }
@@ -1,87 +0,0 @@
1
- import { createHash, randomBytes, createSign, createVerify, generateKeyPairSync } from 'node:crypto';
2
- import { loadConfig, saveConfig } from './config.js';
3
- /* ── Key generation ── */
4
- /** Generate a new ECDSA keypair (secp256k1) and derive an address */
5
- export function generateIdentity() {
6
- const { publicKey, privateKey } = generateKeyPairSync('ec', {
7
- namedCurve: 'secp256k1',
8
- publicKeyEncoding: { type: 'spki', format: 'pem' },
9
- privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
10
- });
11
- const address = deriveAddress(publicKey);
12
- return {
13
- publicKey,
14
- secretKey: privateKey,
15
- address,
16
- createdAt: new Date().toISOString(),
17
- };
18
- }
19
- /** Derive a hex address from a public key (keccak-like: sha256 → last 20 bytes → 0x prefix) */
20
- export function deriveAddress(publicKeyPem) {
21
- const hash = createHash('sha256').update(publicKeyPem).digest();
22
- return '0x' + hash.subarray(hash.length - 20).toString('hex');
23
- }
24
- /* ── Signing ── */
25
- /** Sign a message with the agent's private key */
26
- export function signMessage(message, secretKeyPem) {
27
- const signer = createSign('SHA256');
28
- signer.update(message);
29
- signer.end();
30
- return signer.sign(secretKeyPem, 'hex');
31
- }
32
- /** Create a full signed message payload */
33
- export function createSignedMessage(message, identity) {
34
- return {
35
- message,
36
- signature: signMessage(message, identity.secretKey),
37
- address: identity.address,
38
- timestamp: new Date().toISOString(),
39
- };
40
- }
41
- /* ── Verification ── */
42
- /** Verify a signed message against a public key */
43
- export function verifySignature(message, signatureHex, publicKeyPem) {
44
- try {
45
- const verifier = createVerify('SHA256');
46
- verifier.update(message);
47
- verifier.end();
48
- return verifier.verify(publicKeyPem, signatureHex, 'hex');
49
- }
50
- catch {
51
- return false;
52
- }
53
- }
54
- /* ── Challenge-response ── */
55
- /** Generate a random challenge nonce */
56
- export function generateChallenge() {
57
- return randomBytes(32).toString('hex');
58
- }
59
- /** Sign a challenge to prove identity */
60
- export function respondToChallenge(challenge, identity) {
61
- return createSignedMessage(challenge, identity);
62
- }
63
- /* ── Config helpers ── */
64
- /** Get the current identity from config, or null */
65
- export function getIdentity() {
66
- const cfg = loadConfig();
67
- return cfg.identity || null;
68
- }
69
- /** Require identity or throw */
70
- export function requireIdentity() {
71
- const identity = getIdentity();
72
- if (!identity) {
73
- throw new Error('No identity found. Run: devtopia identity create');
74
- }
75
- return identity;
76
- }
77
- /** Save identity to config */
78
- export function saveIdentity(identity) {
79
- const cfg = loadConfig();
80
- saveConfig({ ...cfg, identity });
81
- }
82
- /** Fingerprint: short display of an address */
83
- export function shortAddress(address) {
84
- if (address.length <= 12)
85
- return address;
86
- return `${address.slice(0, 6)}...${address.slice(-4)}`;
87
- }