connectonion 0.0.3 → 0.0.5

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.
Files changed (43) hide show
  1. package/README.md +20 -0
  2. package/dist/address-browser.d.ts +94 -0
  3. package/dist/address-browser.d.ts.map +1 -0
  4. package/dist/address-browser.js +159 -0
  5. package/dist/address.d.ts +82 -0
  6. package/dist/address.d.ts.map +1 -0
  7. package/dist/address.js +283 -0
  8. package/dist/connect.d.ts +218 -19
  9. package/dist/connect.d.ts.map +1 -1
  10. package/dist/connect.js +411 -48
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +24 -1
  14. package/dist/llm/llm-do.d.ts.map +1 -1
  15. package/dist/llm/llm-do.js +2 -1
  16. package/dist/llm/noop.d.ts +3 -1
  17. package/dist/llm/noop.d.ts.map +1 -1
  18. package/dist/llm/noop.js +3 -1
  19. package/dist/react/index.d.ts +203 -0
  20. package/dist/react/index.d.ts.map +1 -0
  21. package/dist/react/index.js +211 -0
  22. package/dist/tools/email.js +6 -6
  23. package/dist/tools/replay.d.ts +4 -4
  24. package/dist/tools/replay.js +4 -4
  25. package/dist/tools/tool-executor.d.ts +2 -2
  26. package/dist/tools/tool-executor.js +2 -2
  27. package/dist/trust/index.d.ts +5 -2
  28. package/dist/trust/index.d.ts.map +1 -1
  29. package/dist/trust/index.js +5 -2
  30. package/dist/trust/tools.js +4 -4
  31. package/package.json +34 -1
  32. package/dist/examples/comprehensive-test.js +0 -314
  33. package/dist/examples/simple-test.js +0 -80
  34. package/dist/history/index.d.ts +0 -42
  35. package/dist/history/index.d.ts.map +0 -1
  36. package/dist/history/index.js +0 -140
  37. package/dist/src/core/agent.js +0 -368
  38. package/dist/src/history/index.js +0 -140
  39. package/dist/src/index.js +0 -34
  40. package/dist/src/llm/index.js +0 -22
  41. package/dist/src/llm/openai.js +0 -78
  42. package/dist/src/tools/tool-utils.js +0 -348
  43. package/dist/src/types.js +0 -8
package/README.md CHANGED
@@ -218,6 +218,26 @@ your-project/
218
218
  └── tsconfig.json
219
219
  ```
220
220
 
221
+ ---
222
+
223
+ ## 💬 Join the Community
224
+
225
+ [![Discord](https://img.shields.io/discord/1234567890?color=7289da&label=Join%20Discord&logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/4xfD9k8AUF)
226
+
227
+ Get help, share agents, and discuss with 1000+ builders in our active community.
228
+
229
+ ---
230
+
231
+ ## ⭐ Show Your Support
232
+
233
+ If ConnectOnion helps you build better agents, **give it a star!** ⭐
234
+
235
+ It helps others discover the framework and motivates us to keep improving it.
236
+
237
+ [⭐ Star on GitHub](https://github.com/openonion/connectonion-ts)
238
+
239
+ ---
240
+
221
241
  ## 🤝 Contributing
222
242
 
223
243
  We love contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @purpose Browser-only Ed25519 key generation and signing using tweetnacl (lightweight alternative to Node.js crypto module)
3
+ * @llm-note
4
+ * Dependencies: imports from [tweetnacl (npm package)] | imported by [oo-chat/components/chat/use-agent-stream.ts, oo-chat/app/api/chat/route.ts (via dist)] | tested by manual browser testing
5
+ * Data flow: generateBrowser() → nacl.sign.keyPair() → converts to hex address → returns AddressData | signBrowser(addressData, message) → TextEncoder → nacl.sign.detached() → returns hex signature | saveBrowser(keys) → JSON.stringify → localStorage.setItem | loadBrowser() → localStorage.getItem → JSON.parse → hex to Uint8Array
6
+ * State/Effects: reads/writes localStorage key 'connectonion_keys' | uses window.localStorage directly | mutates browser storage | keys persist across browser sessions (until localStorage cleared)
7
+ * Integration: exposes generateBrowser(), saveBrowser(keys), loadBrowser(), signBrowser(addressData, message), createSignedPayloadBrowser(addressData, prompt, toAddress), AddressData type | browser-only (checks typeof window) | canonical JSON with sorted keys for consistent signatures
8
+ * Performance: tweetnacl Ed25519 (faster than WASM alternatives) | localStorage persistence (synchronous) | hex string encoding/decoding | ~1ms for key generation, <1ms for signing
9
+ * Errors: returns early if typeof window === 'undefined' | throws JSON.parse errors on invalid localStorage data | no key validation on signBrowser()
10
+ *
11
+ * Architecture:
12
+ * - Browser-only alternative to address.ts (which uses Node.js crypto)
13
+ * - Uses tweetnacl instead of Node.js crypto module (smaller bundle, browser-native)
14
+ * - localStorage for persistence instead of filesystem
15
+ * - Canonical JSON for signature consistency (sorted keys prevent signature drift)
16
+ *
17
+ * Browser Ed25519 Authentication Flow:
18
+ * 1. First Visit (no keys):
19
+ * loadBrowser() → returns null
20
+ * → generateBrowser() creates new keypair
21
+ * → saveBrowser() stores to localStorage['connectonion_keys']
22
+ *
23
+ * 2. Subsequent Visits:
24
+ * loadBrowser() → retrieves from localStorage
25
+ * → returns same AddressData (persistent identity)
26
+ *
27
+ * 3. Signing Messages:
28
+ * createSignedPayloadBrowser(keys, prompt, agentAddress)
29
+ * → creates payload: {prompt, to, timestamp}
30
+ * → canonicalJSON() sorts keys for consistent message
31
+ * → signBrowser() creates Ed25519 signature
32
+ * → returns {payload, from: publicKey, signature}
33
+ *
34
+ * 4. Agent Verification (remote):
35
+ * Agent receives {payload, from, signature}
36
+ * → Extracts public key from 'from' address
37
+ * → Verifies signature matches canonical payload
38
+ * → If valid: process request
39
+ * → If invalid: reject (tampered or wrong sender)
40
+ *
41
+ * Why Ed25519:
42
+ * - Fast: <1ms signing, verification
43
+ * - Small: 32-byte public keys, 64-byte signatures
44
+ * - Secure: Industry-standard elliptic curve cryptography
45
+ * - Deterministic: Same message + key = same signature
46
+ *
47
+ * Storage Format (localStorage):
48
+ * {
49
+ * "address": "0x<64 hex chars>", // Public key as hex (agent identifier)
50
+ * "shortAddress": "0x<6 chars>...<4>", // Display-friendly shortened version
51
+ * "publicKey": "<hex string>", // 32 bytes as hex
52
+ * "privateKey": "<hex string>" // 64 bytes (seed + pubkey) as hex
53
+ * }
54
+ *
55
+ * Related Files:
56
+ * - src/address.ts: Node.js version (uses crypto module, saves to filesystem)
57
+ * - oo-chat/components/chat/use-agent-stream.ts: Uses this for WebSocket auth
58
+ * - oo-chat/app/api/chat/route.ts: Server uses address.ts, but imports this via dist for types
59
+ */
60
+ export interface AddressData {
61
+ address: string;
62
+ shortAddress: string;
63
+ publicKey: Uint8Array;
64
+ privateKey: Uint8Array;
65
+ }
66
+ /**
67
+ * Generate Ed25519 key pair for browser.
68
+ */
69
+ export declare function generateBrowser(): AddressData;
70
+ /**
71
+ * Save keys to browser localStorage.
72
+ */
73
+ export declare function saveBrowser(keys: AddressData): void;
74
+ /**
75
+ * Load keys from browser localStorage.
76
+ */
77
+ export declare function loadBrowser(): AddressData | null;
78
+ /**
79
+ * Sign a message using tweetnacl.
80
+ */
81
+ export declare function signBrowser(addressData: AddressData, message: string): string;
82
+ /**
83
+ * Create a signed request payload for browser.
84
+ */
85
+ export declare function createSignedPayloadBrowser(addressData: AddressData, prompt: string, toAddress: string): {
86
+ payload: {
87
+ prompt: string;
88
+ to: string;
89
+ timestamp: number;
90
+ };
91
+ from: string;
92
+ signature: string;
93
+ };
94
+ //# sourceMappingURL=address-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address-browser.d.ts","sourceRoot":"","sources":["../src/address-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAaH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;CACxB;AAcD;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAY7C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAWnD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CA6BhD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAK7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAgBA"}
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ /**
3
+ * @purpose Browser-only Ed25519 key generation and signing using tweetnacl (lightweight alternative to Node.js crypto module)
4
+ * @llm-note
5
+ * Dependencies: imports from [tweetnacl (npm package)] | imported by [oo-chat/components/chat/use-agent-stream.ts, oo-chat/app/api/chat/route.ts (via dist)] | tested by manual browser testing
6
+ * Data flow: generateBrowser() → nacl.sign.keyPair() → converts to hex address → returns AddressData | signBrowser(addressData, message) → TextEncoder → nacl.sign.detached() → returns hex signature | saveBrowser(keys) → JSON.stringify → localStorage.setItem | loadBrowser() → localStorage.getItem → JSON.parse → hex to Uint8Array
7
+ * State/Effects: reads/writes localStorage key 'connectonion_keys' | uses window.localStorage directly | mutates browser storage | keys persist across browser sessions (until localStorage cleared)
8
+ * Integration: exposes generateBrowser(), saveBrowser(keys), loadBrowser(), signBrowser(addressData, message), createSignedPayloadBrowser(addressData, prompt, toAddress), AddressData type | browser-only (checks typeof window) | canonical JSON with sorted keys for consistent signatures
9
+ * Performance: tweetnacl Ed25519 (faster than WASM alternatives) | localStorage persistence (synchronous) | hex string encoding/decoding | ~1ms for key generation, <1ms for signing
10
+ * Errors: returns early if typeof window === 'undefined' | throws JSON.parse errors on invalid localStorage data | no key validation on signBrowser()
11
+ *
12
+ * Architecture:
13
+ * - Browser-only alternative to address.ts (which uses Node.js crypto)
14
+ * - Uses tweetnacl instead of Node.js crypto module (smaller bundle, browser-native)
15
+ * - localStorage for persistence instead of filesystem
16
+ * - Canonical JSON for signature consistency (sorted keys prevent signature drift)
17
+ *
18
+ * Browser Ed25519 Authentication Flow:
19
+ * 1. First Visit (no keys):
20
+ * loadBrowser() → returns null
21
+ * → generateBrowser() creates new keypair
22
+ * → saveBrowser() stores to localStorage['connectonion_keys']
23
+ *
24
+ * 2. Subsequent Visits:
25
+ * loadBrowser() → retrieves from localStorage
26
+ * → returns same AddressData (persistent identity)
27
+ *
28
+ * 3. Signing Messages:
29
+ * createSignedPayloadBrowser(keys, prompt, agentAddress)
30
+ * → creates payload: {prompt, to, timestamp}
31
+ * → canonicalJSON() sorts keys for consistent message
32
+ * → signBrowser() creates Ed25519 signature
33
+ * → returns {payload, from: publicKey, signature}
34
+ *
35
+ * 4. Agent Verification (remote):
36
+ * Agent receives {payload, from, signature}
37
+ * → Extracts public key from 'from' address
38
+ * → Verifies signature matches canonical payload
39
+ * → If valid: process request
40
+ * → If invalid: reject (tampered or wrong sender)
41
+ *
42
+ * Why Ed25519:
43
+ * - Fast: <1ms signing, verification
44
+ * - Small: 32-byte public keys, 64-byte signatures
45
+ * - Secure: Industry-standard elliptic curve cryptography
46
+ * - Deterministic: Same message + key = same signature
47
+ *
48
+ * Storage Format (localStorage):
49
+ * {
50
+ * "address": "0x<64 hex chars>", // Public key as hex (agent identifier)
51
+ * "shortAddress": "0x<6 chars>...<4>", // Display-friendly shortened version
52
+ * "publicKey": "<hex string>", // 32 bytes as hex
53
+ * "privateKey": "<hex string>" // 64 bytes (seed + pubkey) as hex
54
+ * }
55
+ *
56
+ * Related Files:
57
+ * - src/address.ts: Node.js version (uses crypto module, saves to filesystem)
58
+ * - oo-chat/components/chat/use-agent-stream.ts: Uses this for WebSocket auth
59
+ * - oo-chat/app/api/chat/route.ts: Server uses address.ts, but imports this via dist for types
60
+ */
61
+ Object.defineProperty(exports, "__esModule", { value: true });
62
+ exports.generateBrowser = generateBrowser;
63
+ exports.saveBrowser = saveBrowser;
64
+ exports.loadBrowser = loadBrowser;
65
+ exports.signBrowser = signBrowser;
66
+ exports.createSignedPayloadBrowser = createSignedPayloadBrowser;
67
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
68
+ const nacl = require('tweetnacl');
69
+ /**
70
+ * Canonical JSON with sorted keys for consistent signatures.
71
+ */
72
+ function canonicalJSON(obj) {
73
+ const sortedKeys = Object.keys(obj).sort();
74
+ const sortedObj = {};
75
+ for (const key of sortedKeys) {
76
+ sortedObj[key] = obj[key];
77
+ }
78
+ return JSON.stringify(sortedObj);
79
+ }
80
+ /**
81
+ * Generate Ed25519 key pair for browser.
82
+ */
83
+ function generateBrowser() {
84
+ const keyPair = nacl.sign.keyPair();
85
+ const address = '0x' + Array.from(keyPair.publicKey).map(b => b.toString(16).padStart(2, '0')).join('');
86
+ const shortAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
87
+ return {
88
+ address,
89
+ shortAddress,
90
+ publicKey: keyPair.publicKey,
91
+ privateKey: keyPair.secretKey, // 64 bytes: seed + public key
92
+ };
93
+ }
94
+ /**
95
+ * Save keys to browser localStorage.
96
+ */
97
+ function saveBrowser(keys) {
98
+ if (typeof window === 'undefined')
99
+ return;
100
+ const data = {
101
+ address: keys.address,
102
+ shortAddress: keys.shortAddress,
103
+ publicKey: Array.from(keys.publicKey).map(b => b.toString(16).padStart(2, '0')).join(''),
104
+ privateKey: Array.from(keys.privateKey).map(b => b.toString(16).padStart(2, '0')).join(''),
105
+ };
106
+ window.localStorage.setItem('connectonion_keys', JSON.stringify(data));
107
+ }
108
+ /**
109
+ * Load keys from browser localStorage.
110
+ */
111
+ function loadBrowser() {
112
+ if (typeof window === 'undefined')
113
+ return null;
114
+ const stored = window.localStorage.getItem('connectonion_keys');
115
+ if (!stored) {
116
+ return null;
117
+ }
118
+ try {
119
+ const data = JSON.parse(stored);
120
+ // Convert hex strings back to Uint8Array
121
+ const publicKey = new Uint8Array(data.publicKey.match(/.{2}/g).map(b => parseInt(b, 16)));
122
+ const privateKey = new Uint8Array(data.privateKey.match(/.{2}/g).map(b => parseInt(b, 16)));
123
+ return {
124
+ address: data.address,
125
+ shortAddress: data.shortAddress,
126
+ publicKey,
127
+ privateKey,
128
+ };
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ /**
135
+ * Sign a message using tweetnacl.
136
+ */
137
+ function signBrowser(addressData, message) {
138
+ const msgBytes = new TextEncoder().encode(message);
139
+ const signature = nacl.sign.detached(msgBytes, addressData.privateKey);
140
+ return Array.from(signature).map(b => b.toString(16).padStart(2, '0')).join('');
141
+ }
142
+ /**
143
+ * Create a signed request payload for browser.
144
+ */
145
+ function createSignedPayloadBrowser(addressData, prompt, toAddress) {
146
+ const timestamp = Math.floor(Date.now() / 1000);
147
+ const payload = {
148
+ prompt,
149
+ to: toAddress,
150
+ timestamp,
151
+ };
152
+ const canonicalMessage = canonicalJSON(payload);
153
+ const signature = signBrowser(addressData, canonicalMessage);
154
+ return {
155
+ payload,
156
+ from: addressData.address,
157
+ signature,
158
+ };
159
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @purpose Agent address/identity management with Ed25519 key generation, signing, and verification (Python address.py parity)
3
+ * @llm-note
4
+ * Dependencies: imports from [crypto, fs, path (Node.js built-ins, conditional)] | imported by [src/connect.ts, src/index.ts, tests/connect.test.ts, tests/address.test.ts, tests/e2e/signedAgent.test.ts] | tested by [tests/address.test.ts]
5
+ * Data flow: generate() → creates Ed25519 keypair → exports to raw buffers → returns AddressData{address: 0x..., publicKey, privateKey} | sign(addressData, message) → recreates privateKey from buffer → signs with crypto.sign() → returns hex signature | verify(address, message, signature) → recreates publicKey from address → verifies with crypto.verify() → returns boolean
6
+ * State/Effects: reads/writes .co/keys/agent.key (Node.js) | reads/writes localStorage connectonion_keys (browser) | conditional require() for Node.js modules | detects environment via globalThis.window check
7
+ * Integration: exposes generate(), load(coDir), save(), sign(addressData, message), verify(address, message, signature), createSignedPayload(addressData, prompt, toAddress), AddressData type | browser variants: generateBrowser(), loadBrowser(), saveBrowser(), signBrowser() | canonical JSON with sorted keys for consistent signatures
8
+ * Performance: Ed25519 keypair generation (crypto.generateKeyPairSync) | PKCS#8/SPKI DER encoding for key export | synchronous fs operations | localStorage for browser persistence
9
+ * Errors: throws if generate() called in browser (requires Node.js crypto) | throws if sign()/verify() called in browser (use signBrowser/verifyBrowser) | returns null if load() finds no keys | returns false on verify() failure
10
+ *
11
+ * Architecture:
12
+ * - Node.js: crypto module for Ed25519, fs for .co/keys/agent.key persistence
13
+ * - Browser: tweetnacl for Ed25519, localStorage for key persistence
14
+ * - Dual exports: regular functions (Node.js) and *Browser functions (browser)
15
+ * - Canonical JSON: sorted keys for consistent signature verification
16
+ */
17
+ export interface AddressData {
18
+ address: string;
19
+ shortAddress: string;
20
+ publicKey: Buffer | Uint8Array;
21
+ privateKey: Buffer | Uint8Array;
22
+ }
23
+ /**
24
+ * Generate a new agent address with Ed25519 keys (Node.js only).
25
+ */
26
+ export declare function generate(): AddressData;
27
+ /**
28
+ * Load existing agent keys from .co/keys/ directory (Node.js only).
29
+ */
30
+ export declare function load(coDir?: string): AddressData | null;
31
+ /**
32
+ * Save keys to browser localStorage.
33
+ */
34
+ export declare function saveBrowser(keys: AddressData): void;
35
+ /**
36
+ * Load keys from browser localStorage.
37
+ */
38
+ export declare function loadBrowser(): AddressData | null;
39
+ /**
40
+ * Generate keys in browser using tweetnacl Ed25519.
41
+ */
42
+ export declare function generateBrowser(): AddressData;
43
+ /**
44
+ * Sign a message in browser using tweetnacl.
45
+ */
46
+ export declare function signBrowser(addressData: AddressData, message: string): string;
47
+ /**
48
+ * Create a signed request payload for browser.
49
+ */
50
+ export declare function createSignedPayloadBrowser(addressData: AddressData, prompt: string, toAddress: string): {
51
+ payload: {
52
+ prompt: string;
53
+ to: string;
54
+ timestamp: number;
55
+ };
56
+ from: string;
57
+ signature: string;
58
+ };
59
+ /**
60
+ * Sign a message with the agent's private key (Node.js only).
61
+ */
62
+ export declare function sign(addressData: AddressData, message: string | Buffer): string;
63
+ /**
64
+ * Verify a signature using an agent's address (public key).
65
+ */
66
+ export declare function verify(address: string, message: string | Buffer, signature: string): boolean;
67
+ /**
68
+ * Create a signed request payload for agent requests.
69
+ * Always sign when keys are available (works with all trust levels).
70
+ *
71
+ * Uses canonical JSON (sorted keys) for consistent signatures.
72
+ */
73
+ export declare function createSignedPayload(addressData: AddressData, prompt: string, toAddress: string): {
74
+ payload: {
75
+ prompt: string;
76
+ to: string;
77
+ timestamp: number;
78
+ };
79
+ from: string;
80
+ signature: string;
81
+ };
82
+ //# sourceMappingURL=address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAwCD;;GAEG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAoBtC;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,GAAE,MAAc,GAAG,WAAW,GAAG,IAAI,CAuC9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAcnD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CA2BhD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAgB7C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAgBA;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAmB/E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA4B5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAiBA"}
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ /**
3
+ * @purpose Agent address/identity management with Ed25519 key generation, signing, and verification (Python address.py parity)
4
+ * @llm-note
5
+ * Dependencies: imports from [crypto, fs, path (Node.js built-ins, conditional)] | imported by [src/connect.ts, src/index.ts, tests/connect.test.ts, tests/address.test.ts, tests/e2e/signedAgent.test.ts] | tested by [tests/address.test.ts]
6
+ * Data flow: generate() → creates Ed25519 keypair → exports to raw buffers → returns AddressData{address: 0x..., publicKey, privateKey} | sign(addressData, message) → recreates privateKey from buffer → signs with crypto.sign() → returns hex signature | verify(address, message, signature) → recreates publicKey from address → verifies with crypto.verify() → returns boolean
7
+ * State/Effects: reads/writes .co/keys/agent.key (Node.js) | reads/writes localStorage connectonion_keys (browser) | conditional require() for Node.js modules | detects environment via globalThis.window check
8
+ * Integration: exposes generate(), load(coDir), save(), sign(addressData, message), verify(address, message, signature), createSignedPayload(addressData, prompt, toAddress), AddressData type | browser variants: generateBrowser(), loadBrowser(), saveBrowser(), signBrowser() | canonical JSON with sorted keys for consistent signatures
9
+ * Performance: Ed25519 keypair generation (crypto.generateKeyPairSync) | PKCS#8/SPKI DER encoding for key export | synchronous fs operations | localStorage for browser persistence
10
+ * Errors: throws if generate() called in browser (requires Node.js crypto) | throws if sign()/verify() called in browser (use signBrowser/verifyBrowser) | returns null if load() finds no keys | returns false on verify() failure
11
+ *
12
+ * Architecture:
13
+ * - Node.js: crypto module for Ed25519, fs for .co/keys/agent.key persistence
14
+ * - Browser: tweetnacl for Ed25519, localStorage for key persistence
15
+ * - Dual exports: regular functions (Node.js) and *Browser functions (browser)
16
+ * - Canonical JSON: sorted keys for consistent signature verification
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.generate = generate;
20
+ exports.load = load;
21
+ exports.saveBrowser = saveBrowser;
22
+ exports.loadBrowser = loadBrowser;
23
+ exports.generateBrowser = generateBrowser;
24
+ exports.signBrowser = signBrowser;
25
+ exports.createSignedPayloadBrowser = createSignedPayloadBrowser;
26
+ exports.sign = sign;
27
+ exports.verify = verify;
28
+ exports.createSignedPayload = createSignedPayload;
29
+ // Use dynamic imports for Node.js modules to support browser builds
30
+ let crypto = null;
31
+ let fs = null;
32
+ let path = null;
33
+ // Try to load Node.js modules (will fail in browser)
34
+ try {
35
+ crypto = require('crypto');
36
+ fs = require('fs');
37
+ path = require('path');
38
+ }
39
+ catch {
40
+ // Browser environment - crypto/fs not available
41
+ }
42
+ /**
43
+ * Check if running in browser environment.
44
+ */
45
+ function isBrowser() {
46
+ return typeof globalThis !== 'undefined' &&
47
+ typeof globalThis.window !== 'undefined' &&
48
+ typeof globalThis.localStorage !== 'undefined';
49
+ }
50
+ /**
51
+ * Get localStorage safely (browser only).
52
+ */
53
+ function getLocalStorage() {
54
+ if (isBrowser()) {
55
+ return globalThis.localStorage;
56
+ }
57
+ return null;
58
+ }
59
+ /**
60
+ * Canonical JSON with sorted keys for consistent signatures.
61
+ */
62
+ function canonicalJSON(obj) {
63
+ const sortedKeys = Object.keys(obj).sort();
64
+ const sortedObj = {};
65
+ for (const key of sortedKeys) {
66
+ sortedObj[key] = obj[key];
67
+ }
68
+ return JSON.stringify(sortedObj);
69
+ }
70
+ /**
71
+ * Generate a new agent address with Ed25519 keys (Node.js only).
72
+ */
73
+ function generate() {
74
+ if (!crypto) {
75
+ throw new Error('generate() requires Node.js environment with crypto module');
76
+ }
77
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
78
+ // Export keys to raw buffers
79
+ const publicKeyBuffer = publicKey.export({ type: 'spki', format: 'der' }).slice(-32);
80
+ const privateKeyBuffer = privateKey.export({ type: 'pkcs8', format: 'der' }).slice(-32);
81
+ const address = '0x' + publicKeyBuffer.toString('hex');
82
+ const shortAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
83
+ return {
84
+ address,
85
+ shortAddress,
86
+ publicKey: publicKeyBuffer,
87
+ privateKey: privateKeyBuffer,
88
+ };
89
+ }
90
+ /**
91
+ * Load existing agent keys from .co/keys/ directory (Node.js only).
92
+ */
93
+ function load(coDir = '.co') {
94
+ if (!crypto || !fs || !path) {
95
+ return null;
96
+ }
97
+ const keyFile = path.join(coDir, 'keys', 'agent.key');
98
+ if (!fs.existsSync(keyFile)) {
99
+ return null;
100
+ }
101
+ try {
102
+ const privateKeyBuffer = fs.readFileSync(keyFile);
103
+ // Recreate key objects from raw bytes
104
+ const privateKey = crypto.createPrivateKey({
105
+ key: Buffer.concat([
106
+ Buffer.from('302e020100300506032b657004220420', 'hex'), // PKCS#8 Ed25519 prefix
107
+ privateKeyBuffer,
108
+ ]),
109
+ format: 'der',
110
+ type: 'pkcs8',
111
+ });
112
+ const publicKey = crypto.createPublicKey(privateKey);
113
+ const publicKeyBuffer = publicKey.export({ type: 'spki', format: 'der' }).slice(-32);
114
+ const address = '0x' + publicKeyBuffer.toString('hex');
115
+ const shortAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
116
+ return {
117
+ address,
118
+ shortAddress,
119
+ publicKey: publicKeyBuffer,
120
+ privateKey: privateKeyBuffer,
121
+ };
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ /**
128
+ * Save keys to browser localStorage.
129
+ */
130
+ function saveBrowser(keys) {
131
+ const storage = getLocalStorage();
132
+ if (!storage) {
133
+ throw new Error('saveBrowser() requires browser environment');
134
+ }
135
+ const data = {
136
+ address: keys.address,
137
+ shortAddress: keys.shortAddress,
138
+ publicKey: Buffer.from(keys.publicKey).toString('hex'),
139
+ privateKey: Buffer.from(keys.privateKey).toString('hex'),
140
+ };
141
+ storage.setItem('connectonion_keys', JSON.stringify(data));
142
+ }
143
+ /**
144
+ * Load keys from browser localStorage.
145
+ */
146
+ function loadBrowser() {
147
+ const storage = getLocalStorage();
148
+ if (!storage) {
149
+ return null;
150
+ }
151
+ const stored = storage.getItem('connectonion_keys');
152
+ if (!stored) {
153
+ return null;
154
+ }
155
+ try {
156
+ const data = JSON.parse(stored);
157
+ return {
158
+ address: data.address,
159
+ shortAddress: data.shortAddress,
160
+ publicKey: Buffer.from(data.publicKey, 'hex'),
161
+ privateKey: Buffer.from(data.privateKey, 'hex'),
162
+ };
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
168
+ /**
169
+ * Generate keys in browser using tweetnacl Ed25519.
170
+ */
171
+ function generateBrowser() {
172
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
173
+ const nacl = require('tweetnacl');
174
+ // Generate Ed25519 key pair
175
+ const keyPair = nacl.sign.keyPair();
176
+ const address = '0x' + Array.from(keyPair.publicKey).map(b => b.toString(16).padStart(2, '0')).join('');
177
+ const shortAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
178
+ return {
179
+ address,
180
+ shortAddress,
181
+ publicKey: keyPair.publicKey,
182
+ privateKey: keyPair.secretKey, // 64 bytes: seed + public key
183
+ };
184
+ }
185
+ /**
186
+ * Sign a message in browser using tweetnacl.
187
+ */
188
+ function signBrowser(addressData, message) {
189
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
190
+ const nacl = require('tweetnacl');
191
+ const msgBytes = new TextEncoder().encode(message);
192
+ const signature = nacl.sign.detached(msgBytes, addressData.privateKey);
193
+ return Array.from(signature).map(b => b.toString(16).padStart(2, '0')).join('');
194
+ }
195
+ /**
196
+ * Create a signed request payload for browser.
197
+ */
198
+ function createSignedPayloadBrowser(addressData, prompt, toAddress) {
199
+ const timestamp = Math.floor(Date.now() / 1000);
200
+ const payload = {
201
+ prompt,
202
+ to: toAddress,
203
+ timestamp,
204
+ };
205
+ const canonicalMessage = canonicalJSON(payload);
206
+ const signature = signBrowser(addressData, canonicalMessage);
207
+ return {
208
+ payload,
209
+ from: addressData.address,
210
+ signature,
211
+ };
212
+ }
213
+ /**
214
+ * Sign a message with the agent's private key (Node.js only).
215
+ */
216
+ function sign(addressData, message) {
217
+ if (!crypto) {
218
+ throw new Error('sign() requires Node.js environment with crypto module');
219
+ }
220
+ const msgBuffer = typeof message === 'string' ? Buffer.from(message) : message;
221
+ // Recreate private key object
222
+ const privateKey = crypto.createPrivateKey({
223
+ key: Buffer.concat([
224
+ Buffer.from('302e020100300506032b657004220420', 'hex'),
225
+ Buffer.from(addressData.privateKey),
226
+ ]),
227
+ format: 'der',
228
+ type: 'pkcs8',
229
+ });
230
+ const signature = crypto.sign(null, msgBuffer, privateKey);
231
+ return signature.toString('hex');
232
+ }
233
+ /**
234
+ * Verify a signature using an agent's address (public key).
235
+ */
236
+ function verify(address, message, signature) {
237
+ if (!crypto) {
238
+ return false;
239
+ }
240
+ try {
241
+ if (!address.startsWith('0x') || address.length !== 66) {
242
+ return false;
243
+ }
244
+ const publicKeyBuffer = Buffer.from(address.slice(2), 'hex');
245
+ const msgBuffer = typeof message === 'string' ? Buffer.from(message) : message;
246
+ const sigBuffer = Buffer.from(signature, 'hex');
247
+ // Recreate public key object
248
+ const publicKey = crypto.createPublicKey({
249
+ key: Buffer.concat([
250
+ Buffer.from('302a300506032b6570032100', 'hex'), // SPKI Ed25519 prefix
251
+ publicKeyBuffer,
252
+ ]),
253
+ format: 'der',
254
+ type: 'spki',
255
+ });
256
+ return crypto.verify(null, msgBuffer, publicKey, sigBuffer);
257
+ }
258
+ catch {
259
+ return false;
260
+ }
261
+ }
262
+ /**
263
+ * Create a signed request payload for agent requests.
264
+ * Always sign when keys are available (works with all trust levels).
265
+ *
266
+ * Uses canonical JSON (sorted keys) for consistent signatures.
267
+ */
268
+ function createSignedPayload(addressData, prompt, toAddress) {
269
+ const timestamp = Math.floor(Date.now() / 1000);
270
+ const payload = {
271
+ prompt,
272
+ to: toAddress,
273
+ timestamp,
274
+ };
275
+ // Use canonical JSON with sorted keys for consistent signatures
276
+ const canonicalMessage = canonicalJSON(payload);
277
+ const signature = sign(addressData, canonicalMessage);
278
+ return {
279
+ payload,
280
+ from: addressData.address,
281
+ signature,
282
+ };
283
+ }