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.
- package/README.md +20 -0
- package/dist/address-browser.d.ts +94 -0
- package/dist/address-browser.d.ts.map +1 -0
- package/dist/address-browser.js +159 -0
- package/dist/address.d.ts +82 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +283 -0
- package/dist/connect.d.ts +218 -19
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +411 -48
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/llm/llm-do.d.ts.map +1 -1
- package/dist/llm/llm-do.js +2 -1
- package/dist/llm/noop.d.ts +3 -1
- package/dist/llm/noop.d.ts.map +1 -1
- package/dist/llm/noop.js +3 -1
- package/dist/react/index.d.ts +203 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +211 -0
- package/dist/tools/email.js +6 -6
- package/dist/tools/replay.d.ts +4 -4
- package/dist/tools/replay.js +4 -4
- package/dist/tools/tool-executor.d.ts +2 -2
- package/dist/tools/tool-executor.js +2 -2
- package/dist/trust/index.d.ts +5 -2
- package/dist/trust/index.d.ts.map +1 -1
- package/dist/trust/index.js +5 -2
- package/dist/trust/tools.js +4 -4
- package/package.json +34 -1
- package/dist/examples/comprehensive-test.js +0 -314
- package/dist/examples/simple-test.js +0 -80
- package/dist/history/index.d.ts +0 -42
- package/dist/history/index.d.ts.map +0 -1
- package/dist/history/index.js +0 -140
- package/dist/src/core/agent.js +0 -368
- package/dist/src/history/index.js +0 -140
- package/dist/src/index.js +0 -34
- package/dist/src/llm/index.js +0 -22
- package/dist/src/llm/openai.js +0 -78
- package/dist/src/tools/tool-utils.js +0 -348
- 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
|
+
[](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"}
|
package/dist/address.js
ADDED
|
@@ -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
|
+
}
|