@optimystic/reference-peer 0.0.1

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/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './cli.js';
package/src/mesh.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { spawn } from 'child_process'
2
+ import * as fs from 'fs'
3
+ import * as path from 'path'
4
+
5
+ type NodeInfo = {
6
+ peerId: string
7
+ multiaddrs: string[]
8
+ port: number
9
+ networkName: string
10
+ timestamp: number
11
+ pid: number
12
+ }
13
+
14
+ async function waitForFile(file: string, timeoutMs: number) {
15
+ const start = Date.now()
16
+ while (Date.now() - start < timeoutMs) {
17
+ if (fs.existsSync(file)) return
18
+ await new Promise(r => setTimeout(r, 100))
19
+ }
20
+ throw new Error(`Timeout waiting for ${file}`)
21
+ }
22
+
23
+ function startNode({ port, bootstrap, announceFile, extraArgs = [] as string[] }: { port: number, bootstrap?: string, announceFile?: string, extraArgs?: string[] }) {
24
+ const args = [
25
+ 'packages/reference-peer/dist/src/cli.js',
26
+ 'service',
27
+ '--port', String(port),
28
+ '--network', 'optimystic',
29
+ ...(bootstrap ? ['--bootstrap', bootstrap] : []),
30
+ '--storage', 'memory',
31
+ ...(announceFile ? ['--announce-file', announceFile] : [])
32
+ ].filter(Boolean)
33
+
34
+ const child = spawn('node', args, { stdio: 'inherit' })
35
+ return child
36
+ }
37
+
38
+ async function readNodeInfo(file: string): Promise<NodeInfo> {
39
+ const text = await fs.promises.readFile(file, 'utf-8')
40
+ return JSON.parse(text)
41
+ }
42
+
43
+ async function main() {
44
+ const n = parseInt(process.env.MESH_NODES || '2', 10)
45
+ const basePort = parseInt(process.env.MESH_BASE_PORT || '8011', 10)
46
+ const workDir = path.join(process.cwd(), '.mesh')
47
+ fs.mkdirSync(workDir, { recursive: true })
48
+
49
+ const files = Array.from({ length: n }, (_, i) => path.join(workDir, `node-${i + 1}.json`))
50
+ const children: ReturnType<typeof spawn>[] = []
51
+ const readyFile = path.join(workDir, 'mesh-ready.json')
52
+
53
+ // Delete old ready file to signal mesh is starting
54
+ if (fs.existsSync(readyFile)) {
55
+ fs.unlinkSync(readyFile)
56
+ }
57
+
58
+ // Check if we should reuse peer IDs (default: false for clean starts)
59
+ const reusePeerIds = process.env.MESH_REUSE_IDS === 'true'
60
+ const existingPeerIds: (string | undefined)[] = []
61
+
62
+ if (reusePeerIds) {
63
+ // Try to load existing peer IDs from previous runs
64
+ for (let i = 0; i < n; i++) {
65
+ const file = files[i]!
66
+ try {
67
+ if (fs.existsSync(file)) {
68
+ const info = await readNodeInfo(file)
69
+ existingPeerIds[i] = info.peerId
70
+ console.log(`Reusing peer ID from ${path.basename(file)}: ${info.peerId}`)
71
+ } else {
72
+ existingPeerIds[i] = undefined
73
+ }
74
+ } catch {
75
+ existingPeerIds[i] = undefined
76
+ }
77
+ }
78
+ } else {
79
+ // Clean start - delete old node files to prevent stale peer ID pollution
80
+ for (let i = 0; i < n; i++) {
81
+ const file = files[i]!
82
+ if (fs.existsSync(file)) {
83
+ fs.unlinkSync(file)
84
+ console.log(`Deleted old ${path.basename(file)} for clean start`)
85
+ }
86
+ existingPeerIds[i] = undefined
87
+ }
88
+ }
89
+
90
+ // Start nodes sequentially, each bootstrapping to all previous nodes
91
+ const allBootstraps: string[] = []
92
+
93
+ for (let i = 0; i < n; i++) {
94
+ const port = basePort + i
95
+ const file = files[i]!
96
+ const id = existingPeerIds[i]
97
+
98
+ // Use accumulated bootstrap list (empty for first node)
99
+ const bootstrap = allBootstraps.length > 0 ? allBootstraps.join(',') : undefined
100
+
101
+ children.push(startNode({
102
+ port,
103
+ bootstrap,
104
+ announceFile: file,
105
+ extraArgs: id ? ['--id', id] : []
106
+ }))
107
+ await waitForFile(file, 10000)
108
+
109
+ // Read node info and add to bootstrap list for next nodes
110
+ const nodeInfo = await readNodeInfo(file)
111
+ // Prefer localhost for same-machine testing
112
+ const localAddr = nodeInfo.multiaddrs.find(a => a.includes('/ip4/127.0.0.1/'))
113
+ if (localAddr) {
114
+ allBootstraps.push(localAddr)
115
+ } else if (nodeInfo.multiaddrs.length > 0) {
116
+ allBootstraps.push(nodeInfo.multiaddrs[0]!)
117
+ }
118
+ }
119
+
120
+ // Write ready file with all node info for clients to bootstrap from
121
+ const allNodes = await Promise.all(files.map(f => readNodeInfo(f)))
122
+ fs.writeFileSync(readyFile, JSON.stringify({
123
+ ready: true,
124
+ timestamp: Date.now(),
125
+ nodes: allNodes.map(n => ({ peerId: n.peerId, multiaddrs: n.multiaddrs }))
126
+ }, null, 2))
127
+
128
+ console.log('---- Mesh started. Press Ctrl+C to stop.')
129
+ }
130
+
131
+ void main().catch(err => {
132
+ console.error(err)
133
+ process.exit(1)
134
+ })
135
+
136
+