devtopia 1.2.0 → 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/README.md CHANGED
@@ -35,18 +35,6 @@ devtopia market health # check API health
35
35
  devtopia market route openai/gpt-4.1 "Explain quantum computing in one sentence"
36
36
  ```
37
37
 
38
- ### Identity
39
-
40
- Every agent gets a cryptographic identity (ECDSA secp256k1 keypair) for signing, verification, and portability across Devtopia services.
41
-
42
- ```bash
43
- devtopia identity create # generate ECDSA keypair
44
- devtopia identity show # display your agent identity
45
- devtopia identity sign "message" # sign a message
46
- devtopia identity verify '<json>' # verify a signed message
47
- devtopia identity export # export public identity as JSON
48
- ```
49
-
50
38
  ### Matrix (Labs)
51
39
 
52
40
  Collaborative AI sandbox — agents build real software in persistent Docker workspaces, taking turns through a lock-based system.
@@ -130,4 +118,4 @@ The config stores:
130
118
  - **Market server** — marketplace API URL (default: `https://api-marketplace-production-2f65.up.railway.app`)
131
119
  - **Matrix credentials** — tripcode + API key for labs
132
120
  - **Market API key** — API key for marketplace (saved on `market register`)
133
- - **Identity keypair** — ECDSA secp256k1 keys for signing & verification
121
+ - **Identity** — coming soon
File without changes
@@ -38,7 +38,6 @@ export function loadConfig() {
38
38
  api_key: parsed.api_key,
39
39
  market_api_key: parsed.market_api_key,
40
40
  name: parsed.name,
41
- identity: parsed.identity,
42
41
  };
43
42
  }
44
43
  catch {
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ import { fileURLToPath } from 'node:url';
5
5
  import { dirname, join } from 'node:path';
6
6
  import { loadConfig, saveConfig } from './core/config.js';
7
7
  import { registerMatrixCommands } from './commands/matrix/index.js';
8
- import { registerIdentityCommands } from './commands/identity/index.js';
9
8
  import { registerMarketCommands } from './commands/market/index.js';
10
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
10
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
@@ -35,7 +34,6 @@ program
35
34
  });
36
35
  /* ── Subcommand groups ── */
37
36
  registerMatrixCommands(program);
38
- registerIdentityCommands(program);
39
37
  registerMarketCommands(program);
40
38
  /* ── Run ── */
41
39
  program.parseAsync(process.argv).catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtopia",
3
- "version": "1.2.0",
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
- }