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.
- package/dist/compat-matrix.js +0 -0
- package/dist/index.js +0 -0
- package/package.json +1 -1
- package/dist/commands/identity/index.js +0 -120
- package/dist/core/identity.js +0 -87
package/dist/compat-matrix.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -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
|
-
}
|
package/dist/core/identity.js
DELETED
|
@@ -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
|
-
}
|