society-protocol 1.0.0

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/dist/adapters.d.ts +101 -0
  4. package/dist/adapters.d.ts.map +1 -0
  5. package/dist/adapters.js +764 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/agents-md.d.ts +59 -0
  8. package/dist/agents-md.d.ts.map +1 -0
  9. package/dist/agents-md.js +204 -0
  10. package/dist/agents-md.js.map +1 -0
  11. package/dist/autoconfig.d.ts +137 -0
  12. package/dist/autoconfig.d.ts.map +1 -0
  13. package/dist/autoconfig.js +452 -0
  14. package/dist/autoconfig.js.map +1 -0
  15. package/dist/bootstrap.d.ts +68 -0
  16. package/dist/bootstrap.d.ts.map +1 -0
  17. package/dist/bootstrap.js +304 -0
  18. package/dist/bootstrap.js.map +1 -0
  19. package/dist/bridges/a2a-bridge.d.ts +156 -0
  20. package/dist/bridges/a2a-bridge.d.ts.map +1 -0
  21. package/dist/bridges/a2a-bridge.js +337 -0
  22. package/dist/bridges/a2a-bridge.js.map +1 -0
  23. package/dist/bridges/mcp-bridge.d.ts +87 -0
  24. package/dist/bridges/mcp-bridge.d.ts.map +1 -0
  25. package/dist/bridges/mcp-bridge.js +332 -0
  26. package/dist/bridges/mcp-bridge.js.map +1 -0
  27. package/dist/cache.d.ts +130 -0
  28. package/dist/cache.d.ts.map +1 -0
  29. package/dist/cache.js +257 -0
  30. package/dist/cache.js.map +1 -0
  31. package/dist/capsules.d.ts +23 -0
  32. package/dist/capsules.d.ts.map +1 -0
  33. package/dist/capsules.js +75 -0
  34. package/dist/capsules.js.map +1 -0
  35. package/dist/cli/commands.d.ts +8 -0
  36. package/dist/cli/commands.d.ts.map +1 -0
  37. package/dist/cli/commands.js +263 -0
  38. package/dist/cli/commands.js.map +1 -0
  39. package/dist/coc.d.ts +121 -0
  40. package/dist/coc.d.ts.map +1 -0
  41. package/dist/coc.js +629 -0
  42. package/dist/coc.js.map +1 -0
  43. package/dist/coc.test.d.ts +2 -0
  44. package/dist/coc.test.d.ts.map +1 -0
  45. package/dist/coc.test.js +80 -0
  46. package/dist/coc.test.js.map +1 -0
  47. package/dist/compression.d.ts +125 -0
  48. package/dist/compression.d.ts.map +1 -0
  49. package/dist/compression.js +573 -0
  50. package/dist/compression.js.map +1 -0
  51. package/dist/cot-stream.d.ts +220 -0
  52. package/dist/cot-stream.d.ts.map +1 -0
  53. package/dist/cot-stream.js +673 -0
  54. package/dist/cot-stream.js.map +1 -0
  55. package/dist/crypto-wasm.d.ts +100 -0
  56. package/dist/crypto-wasm.d.ts.map +1 -0
  57. package/dist/crypto-wasm.js +229 -0
  58. package/dist/crypto-wasm.js.map +1 -0
  59. package/dist/federation.d.ts +200 -0
  60. package/dist/federation.d.ts.map +1 -0
  61. package/dist/federation.js +691 -0
  62. package/dist/federation.js.map +1 -0
  63. package/dist/federation.test.d.ts +2 -0
  64. package/dist/federation.test.d.ts.map +1 -0
  65. package/dist/federation.test.js +71 -0
  66. package/dist/federation.test.js.map +1 -0
  67. package/dist/gateway/capability-router.d.ts +77 -0
  68. package/dist/gateway/capability-router.d.ts.map +1 -0
  69. package/dist/gateway/capability-router.js +222 -0
  70. package/dist/gateway/capability-router.js.map +1 -0
  71. package/dist/gateway/demand-spawner.d.ts +155 -0
  72. package/dist/gateway/demand-spawner.d.ts.map +1 -0
  73. package/dist/gateway/demand-spawner.js +426 -0
  74. package/dist/gateway/demand-spawner.js.map +1 -0
  75. package/dist/identity.d.ts +46 -0
  76. package/dist/identity.d.ts.map +1 -0
  77. package/dist/identity.js +102 -0
  78. package/dist/identity.js.map +1 -0
  79. package/dist/identity.test.d.ts +2 -0
  80. package/dist/identity.test.d.ts.map +1 -0
  81. package/dist/identity.test.js +45 -0
  82. package/dist/identity.test.js.map +1 -0
  83. package/dist/index.d.ts +36 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +1572 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/integration.d.ts +210 -0
  88. package/dist/integration.d.ts.map +1 -0
  89. package/dist/integration.js +1105 -0
  90. package/dist/integration.js.map +1 -0
  91. package/dist/integration.test.d.ts +2 -0
  92. package/dist/integration.test.d.ts.map +1 -0
  93. package/dist/integration.test.js +155 -0
  94. package/dist/integration.test.js.map +1 -0
  95. package/dist/knowledge.d.ts +219 -0
  96. package/dist/knowledge.d.ts.map +1 -0
  97. package/dist/knowledge.js +543 -0
  98. package/dist/knowledge.js.map +1 -0
  99. package/dist/knowledge.test.d.ts +2 -0
  100. package/dist/knowledge.test.d.ts.map +1 -0
  101. package/dist/knowledge.test.js +72 -0
  102. package/dist/knowledge.test.js.map +1 -0
  103. package/dist/latent-space.d.ts +178 -0
  104. package/dist/latent-space.d.ts.map +1 -0
  105. package/dist/latent-space.js +385 -0
  106. package/dist/latent-space.js.map +1 -0
  107. package/dist/lib.d.ts +30 -0
  108. package/dist/lib.d.ts.map +1 -0
  109. package/dist/lib.js +30 -0
  110. package/dist/lib.js.map +1 -0
  111. package/dist/mcp/server.d.ts +74 -0
  112. package/dist/mcp/server.d.ts.map +1 -0
  113. package/dist/mcp/server.js +1392 -0
  114. package/dist/mcp/server.js.map +1 -0
  115. package/dist/metrics.d.ts +98 -0
  116. package/dist/metrics.d.ts.map +1 -0
  117. package/dist/metrics.js +222 -0
  118. package/dist/metrics.js.map +1 -0
  119. package/dist/p2p.d.ts +87 -0
  120. package/dist/p2p.d.ts.map +1 -0
  121. package/dist/p2p.js +606 -0
  122. package/dist/p2p.js.map +1 -0
  123. package/dist/persona/capabilities.d.ts +17 -0
  124. package/dist/persona/capabilities.d.ts.map +1 -0
  125. package/dist/persona/capabilities.js +224 -0
  126. package/dist/persona/capabilities.js.map +1 -0
  127. package/dist/persona/domains.d.ts +22 -0
  128. package/dist/persona/domains.d.ts.map +1 -0
  129. package/dist/persona/domains.js +176 -0
  130. package/dist/persona/domains.js.map +1 -0
  131. package/dist/persona/embeddings.d.ts +40 -0
  132. package/dist/persona/embeddings.d.ts.map +1 -0
  133. package/dist/persona/embeddings.js +265 -0
  134. package/dist/persona/embeddings.js.map +1 -0
  135. package/dist/persona/engine.d.ts +79 -0
  136. package/dist/persona/engine.d.ts.map +1 -0
  137. package/dist/persona/engine.js +1087 -0
  138. package/dist/persona/engine.js.map +1 -0
  139. package/dist/persona/index.d.ts +11 -0
  140. package/dist/persona/index.d.ts.map +1 -0
  141. package/dist/persona/index.js +11 -0
  142. package/dist/persona/index.js.map +1 -0
  143. package/dist/persona/lifecycle.d.ts +17 -0
  144. package/dist/persona/lifecycle.d.ts.map +1 -0
  145. package/dist/persona/lifecycle.js +36 -0
  146. package/dist/persona/lifecycle.js.map +1 -0
  147. package/dist/persona/retrieval.d.ts +6 -0
  148. package/dist/persona/retrieval.d.ts.map +1 -0
  149. package/dist/persona/retrieval.js +122 -0
  150. package/dist/persona/retrieval.js.map +1 -0
  151. package/dist/persona/sync.d.ts +15 -0
  152. package/dist/persona/sync.d.ts.map +1 -0
  153. package/dist/persona/sync.js +92 -0
  154. package/dist/persona/sync.js.map +1 -0
  155. package/dist/persona/types.d.ts +283 -0
  156. package/dist/persona/types.d.ts.map +1 -0
  157. package/dist/persona/types.js +2 -0
  158. package/dist/persona/types.js.map +1 -0
  159. package/dist/persona/zkp/engine.d.ts +26 -0
  160. package/dist/persona/zkp/engine.d.ts.map +1 -0
  161. package/dist/persona/zkp/engine.js +370 -0
  162. package/dist/persona/zkp/engine.js.map +1 -0
  163. package/dist/persona/zkp/types.d.ts +39 -0
  164. package/dist/persona/zkp/types.d.ts.map +1 -0
  165. package/dist/persona/zkp/types.js +2 -0
  166. package/dist/persona/zkp/types.js.map +1 -0
  167. package/dist/planner.d.ts +114 -0
  168. package/dist/planner.d.ts.map +1 -0
  169. package/dist/planner.js +522 -0
  170. package/dist/planner.js.map +1 -0
  171. package/dist/proactive/checkpoints.d.ts +9 -0
  172. package/dist/proactive/checkpoints.d.ts.map +1 -0
  173. package/dist/proactive/checkpoints.js +20 -0
  174. package/dist/proactive/checkpoints.js.map +1 -0
  175. package/dist/proactive/engine.d.ts +59 -0
  176. package/dist/proactive/engine.d.ts.map +1 -0
  177. package/dist/proactive/engine.js +406 -0
  178. package/dist/proactive/engine.js.map +1 -0
  179. package/dist/proactive/scheduler.d.ts +11 -0
  180. package/dist/proactive/scheduler.d.ts.map +1 -0
  181. package/dist/proactive/scheduler.js +45 -0
  182. package/dist/proactive/scheduler.js.map +1 -0
  183. package/dist/proactive/swarm-controller.d.ts +189 -0
  184. package/dist/proactive/swarm-controller.d.ts.map +1 -0
  185. package/dist/proactive/swarm-controller.js +477 -0
  186. package/dist/proactive/swarm-controller.js.map +1 -0
  187. package/dist/proactive/swarm-registry.d.ts +13 -0
  188. package/dist/proactive/swarm-registry.d.ts.map +1 -0
  189. package/dist/proactive/swarm-registry.js +122 -0
  190. package/dist/proactive/swarm-registry.js.map +1 -0
  191. package/dist/proactive/types.d.ts +145 -0
  192. package/dist/proactive/types.d.ts.map +1 -0
  193. package/dist/proactive/types.js +25 -0
  194. package/dist/proactive/types.js.map +1 -0
  195. package/dist/registry.d.ts +35 -0
  196. package/dist/registry.d.ts.map +1 -0
  197. package/dist/registry.js +88 -0
  198. package/dist/registry.js.map +1 -0
  199. package/dist/reputation.d.ts +123 -0
  200. package/dist/reputation.d.ts.map +1 -0
  201. package/dist/reputation.js +366 -0
  202. package/dist/reputation.js.map +1 -0
  203. package/dist/reputation.test.d.ts +5 -0
  204. package/dist/reputation.test.d.ts.map +1 -0
  205. package/dist/reputation.test.js +265 -0
  206. package/dist/reputation.test.js.map +1 -0
  207. package/dist/rooms.d.ts +96 -0
  208. package/dist/rooms.d.ts.map +1 -0
  209. package/dist/rooms.js +410 -0
  210. package/dist/rooms.js.map +1 -0
  211. package/dist/sdk/client.d.ts +290 -0
  212. package/dist/sdk/client.d.ts.map +1 -0
  213. package/dist/sdk/client.js +1287 -0
  214. package/dist/sdk/client.js.map +1 -0
  215. package/dist/sdk/index.d.ts +32 -0
  216. package/dist/sdk/index.d.ts.map +1 -0
  217. package/dist/sdk/index.js +70 -0
  218. package/dist/sdk/index.js.map +1 -0
  219. package/dist/security.d.ts +230 -0
  220. package/dist/security.d.ts.map +1 -0
  221. package/dist/security.js +652 -0
  222. package/dist/security.js.map +1 -0
  223. package/dist/skills/engine.d.ts +262 -0
  224. package/dist/skills/engine.d.ts.map +1 -0
  225. package/dist/skills/engine.js +788 -0
  226. package/dist/skills/engine.js.map +1 -0
  227. package/dist/skills/engine.test.d.ts +2 -0
  228. package/dist/skills/engine.test.d.ts.map +1 -0
  229. package/dist/skills/engine.test.js +134 -0
  230. package/dist/skills/engine.test.js.map +1 -0
  231. package/dist/skills/parser.d.ts +129 -0
  232. package/dist/skills/parser.d.ts.map +1 -0
  233. package/dist/skills/parser.js +318 -0
  234. package/dist/skills/parser.js.map +1 -0
  235. package/dist/social.d.ts +149 -0
  236. package/dist/social.d.ts.map +1 -0
  237. package/dist/social.js +401 -0
  238. package/dist/social.js.map +1 -0
  239. package/dist/storage-optimized.d.ts +116 -0
  240. package/dist/storage-optimized.d.ts.map +1 -0
  241. package/dist/storage-optimized.js +264 -0
  242. package/dist/storage-optimized.js.map +1 -0
  243. package/dist/storage.d.ts +584 -0
  244. package/dist/storage.d.ts.map +1 -0
  245. package/dist/storage.js +2703 -0
  246. package/dist/storage.js.map +1 -0
  247. package/dist/storage.test.d.ts +2 -0
  248. package/dist/storage.test.d.ts.map +1 -0
  249. package/dist/storage.test.js +78 -0
  250. package/dist/storage.test.js.map +1 -0
  251. package/dist/swp.d.ts +443 -0
  252. package/dist/swp.d.ts.map +1 -0
  253. package/dist/swp.js +223 -0
  254. package/dist/swp.js.map +1 -0
  255. package/dist/swp.test.d.ts +5 -0
  256. package/dist/swp.test.d.ts.map +1 -0
  257. package/dist/swp.test.js +127 -0
  258. package/dist/swp.test.js.map +1 -0
  259. package/dist/templates.d.ts +25 -0
  260. package/dist/templates.d.ts.map +1 -0
  261. package/dist/templates.js +1048 -0
  262. package/dist/templates.js.map +1 -0
  263. package/dist/test-e2e.d.ts +14 -0
  264. package/dist/test-e2e.d.ts.map +1 -0
  265. package/dist/test-e2e.js +266 -0
  266. package/dist/test-e2e.js.map +1 -0
  267. package/dist/workers/research-worker.d.ts +19 -0
  268. package/dist/workers/research-worker.d.ts.map +1 -0
  269. package/dist/workers/research-worker.js +141 -0
  270. package/dist/workers/research-worker.js.map +1 -0
  271. package/package.json +110 -0
package/dist/index.js ADDED
@@ -0,0 +1,1572 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Society Protocol — CLI v1.0 (State of the Art)
4
+ *
5
+ * Usage:
6
+ * society node --name Alice --room lobby
7
+ * society node --name Bob --room lobby --bootstrap /ip4/127.0.0.1/tcp/12345/p2p/QmXxx
8
+ * society node --name Charlie --room lobby --relay --enable-gossipsub
9
+ *
10
+ * Interactive commands:
11
+ * /peers — list connected peers
12
+ * /rooms — list joined rooms
13
+ * /presence — list online peers with reputation
14
+ * /reputation [did] — show reputation scores
15
+ * /info — show node info
16
+ * /history — chat history
17
+ * /summon "goal" — start AI-planned collaboration
18
+ * /template <name> [goal] — use predefined template
19
+ * /chains — list active chains
20
+ * /chain <id> — show chain details
21
+ * /step <id> <status> — submit step result
22
+ * /assign <step> <agent> — manually assign step
23
+ * /review <step> <decision> — review a step
24
+ * /cancel <chain> — cancel chain
25
+ * /export <chain> — export capsule
26
+ * /cache — show planner cache stats
27
+ * /mesh-request ... — request federation peering
28
+ * /mesh-peerings ... — list peerings
29
+ * /mesh-open ... — open mesh bridge
30
+ * /mesh-bridges — list mesh bridges
31
+ * /mesh-stats — show mesh metrics
32
+ * /debug — toggle debug mode
33
+ * /quit — exit
34
+ */
35
+ import { Command } from 'commander';
36
+ import readline from 'readline';
37
+ import { toString as uint8ToString } from 'uint8arrays';
38
+ import { generateIdentity, restoreIdentity } from './identity.js';
39
+ import { Storage } from './storage.js';
40
+ import { P2PNode } from './p2p.js';
41
+ import { RoomManager } from './rooms.js';
42
+ import { CocEngine } from './coc.js';
43
+ import { Planner } from './planner.js';
44
+ import { ReputationEngine, formatReputationTier } from './reputation.js';
45
+ import { AdapterHost } from './adapters.js';
46
+ import { getTemplate, TEMPLATES } from './templates.js';
47
+ import { CapsuleExporter } from './capsules.js';
48
+ import { FederationEngine } from './federation.js';
49
+ import { KnowledgePool } from './knowledge.js';
50
+ import { SkillsEngine } from './skills/engine.js';
51
+ import { SecurityManager } from './security.js';
52
+ import { IntegrationEngine } from './integration.js';
53
+ import { PersonaVaultEngine } from './persona/index.js';
54
+ import { ProactiveMissionEngine } from './proactive/engine.js';
55
+ import { spawn, execSync } from 'child_process';
56
+ import { fileURLToPath } from 'url';
57
+ import { resolve } from 'path';
58
+ import { realpathSync } from 'fs';
59
+ import { createClient } from './sdk/client.js';
60
+ import { registerNode, resolveNode, stopHeartbeat } from './registry.js';
61
+ // ─── Invite Code Helpers ─────────────────────────────────────────
62
+ // Encode a multiaddr + room into a short invite code: base64url
63
+ function encodeInvite(multiaddr, room) {
64
+ const payload = JSON.stringify({ a: multiaddr, r: room });
65
+ return Buffer.from(payload).toString('base64url');
66
+ }
67
+ function decodeInvite(code) {
68
+ try {
69
+ const payload = JSON.parse(Buffer.from(code, 'base64url').toString('utf-8'));
70
+ if (payload.a)
71
+ return { multiaddr: payload.a, room: payload.r || 'lobby' };
72
+ }
73
+ catch { /* not a valid invite code */ }
74
+ return null;
75
+ }
76
+ const program = new Command();
77
+ program
78
+ .name('society')
79
+ .description('Society Protocol — Connect your AI agents')
80
+ .version('1.0.0');
81
+ // ─── DEFAULT ACTION (npx society) ────────────────────────────────
82
+ // Running `npx society` with no subcommand starts a node instantly
83
+ program
84
+ .argument('[name]', 'agent display name')
85
+ .option('-r, --room <room>', 'room to join', 'lobby')
86
+ .option('-b, --bootstrap <addrs...>', 'connect to remote network')
87
+ .option('-p, --port <port>', 'listen port', '0')
88
+ .option('--relay', 'expose as public relay (requires cloudflared)')
89
+ .option('--debug', 'enable debug logging')
90
+ .action(async (name, options) => {
91
+ if (name && !program.args.includes('node') && !program.args.includes('init') &&
92
+ !program.args.includes('join') && !program.args.includes('invite') &&
93
+ !program.args.includes('status') && !program.args.includes('dashboard') &&
94
+ !program.args.includes('mission') && !program.args.includes('swarm') &&
95
+ !program.args.includes('worker') && !program.args.includes('mcp')) {
96
+ await startNode({
97
+ name: name || 'Agent',
98
+ room: options.room,
99
+ port: options.port || '0',
100
+ bootstrap: options.bootstrap,
101
+ relay: options.relay,
102
+ gossipsub: true,
103
+ dht: true,
104
+ missionLeader: false,
105
+ provider: 'openai',
106
+ debug: options.debug,
107
+ });
108
+ }
109
+ });
110
+ // ─── JOIN COMMAND ────────────────────────────────────────────────
111
+ // society join <invite-code-or-multiaddr>
112
+ program
113
+ .command('join <code>')
114
+ .description('Join a friend\'s network')
115
+ .option('-n, --name <name>', 'your agent name')
116
+ .option('-r, --room <room>', 'room to join')
117
+ .action(async (code, options) => {
118
+ const name = options.name || `Agent-${Math.random().toString(36).slice(2, 6)}`;
119
+ console.log('');
120
+ console.log(` ${bold('Society Protocol')} — Joining network...`);
121
+ console.log('');
122
+ let bootstrapAddr;
123
+ let room = options.room || 'lobby';
124
+ if (code.startsWith('/')) {
125
+ // Raw multiaddr
126
+ bootstrapAddr = code;
127
+ console.log(` Connecting to: ${dim(code.slice(0, 60))}...`);
128
+ }
129
+ else {
130
+ // Try invite code first
131
+ const decoded = decodeInvite(code);
132
+ if (decoded) {
133
+ bootstrapAddr = decoded.multiaddr;
134
+ if (!options.room)
135
+ room = decoded.room;
136
+ console.log(` Invite accepted! Joining room ${cyan(room)}...`);
137
+ }
138
+ else {
139
+ // Try resolving as a name from the registry
140
+ console.log(` Looking up ${bold(code)} in registry...`);
141
+ const resolved = await resolveNode(code);
142
+ if (resolved) {
143
+ bootstrapAddr = resolved.multiaddr;
144
+ if (!options.room)
145
+ room = resolved.room;
146
+ console.log(` Found ${bold(code)}! Joining room ${cyan(room)}...`);
147
+ }
148
+ else {
149
+ console.error(red(` Could not find "${code}".`));
150
+ console.log(` ${dim('Try an invite code or multiaddr instead.')}`);
151
+ process.exit(1);
152
+ }
153
+ }
154
+ }
155
+ await startNode({
156
+ name,
157
+ room,
158
+ port: '0',
159
+ bootstrap: bootstrapAddr ? [bootstrapAddr] : undefined,
160
+ gossipsub: true,
161
+ dht: true,
162
+ missionLeader: false,
163
+ provider: 'openai',
164
+ debug: false,
165
+ });
166
+ });
167
+ // ─── INVITE COMMAND ─────────────────────────────────────────────
168
+ // society invite → generate a shareable invite code
169
+ program
170
+ .command('invite')
171
+ .description('Generate an invite for others to join your network')
172
+ .option('-n, --name <name>', 'register a friendly name (e.g. "alice")')
173
+ .option('-r, --room <room>', 'room to invite to', 'lobby')
174
+ .option('-p, --port <port>', 'P2P listen port', '4001')
175
+ .option('--relay', 'create a public relay so friends can join from anywhere')
176
+ .action(async (options) => {
177
+ console.log('');
178
+ console.log(` ${bold('Society Protocol')} — Generating invite...`);
179
+ console.log(` ${dim('Your node must stay running for others to connect.')}`);
180
+ console.log('');
181
+ const port = parseInt(options.port, 10);
182
+ const nodeName = options.name || 'Host';
183
+ const client = await createClient({
184
+ identity: { name: nodeName },
185
+ network: {
186
+ port,
187
+ enableGossipsub: true,
188
+ enableDht: true,
189
+ },
190
+ });
191
+ await client.joinRoom(options.room);
192
+ const peerId = client.getPeerId();
193
+ // Local invite (LAN)
194
+ const localMultiaddr = `/ip4/127.0.0.1/tcp/${port}/p2p/${peerId}`;
195
+ const localCode = encodeInvite(localMultiaddr, options.room);
196
+ console.log(` ${green('Node running!')} Room: ${cyan(options.room)}`);
197
+ console.log('');
198
+ console.log(` ${bold('Share with friends on same network:')}`);
199
+ console.log('');
200
+ console.log(` ${bold(cyan(`npx society join ${localCode}`))}`);
201
+ console.log('');
202
+ // Register friendly name if provided
203
+ if (options.name) {
204
+ const registered = await registerNode(options.name, {
205
+ multiaddr: localMultiaddr,
206
+ room: options.room,
207
+ peerId,
208
+ name: options.name,
209
+ });
210
+ if (registered) {
211
+ console.log(` ${green('Registered!')} Friends can also join with:`);
212
+ console.log('');
213
+ console.log(` ${bold(cyan(`npx society join ${options.name}`))}`);
214
+ console.log('');
215
+ }
216
+ }
217
+ // If --relay, spawn cloudflared for a public URL
218
+ if (options.relay) {
219
+ const wsPort = port + 1;
220
+ console.log(` ${dim('Starting public relay...')}`);
221
+ if (!isCommandAvailable('cloudflared')) {
222
+ console.log(` ${yellow('cloudflared not found.')} Installing...`);
223
+ await installCloudflared();
224
+ }
225
+ if (isCommandAvailable('cloudflared')) {
226
+ const cfProc = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${wsPort}`]);
227
+ cfProc.stderr?.on('data', (data) => {
228
+ const str = data.toString();
229
+ const match = str.match(/(https:\/\/[a-z0-9-]+\.trycloudflare\.com)/);
230
+ if (match) {
231
+ const host = match[1].replace('https://', '');
232
+ const publicMultiaddr = `/dns4/${host}/tcp/443/wss/p2p/${peerId}`;
233
+ const publicCode = encodeInvite(publicMultiaddr, options.room);
234
+ console.log(` ${bold(green('Public relay active!'))}`);
235
+ console.log('');
236
+ console.log(` ${bold('Share with anyone:')}`);
237
+ console.log('');
238
+ console.log(` ${bold(cyan(`npx society join ${publicCode}`))}`);
239
+ // Update registry with public multiaddr
240
+ if (options.name) {
241
+ registerNode(options.name, {
242
+ multiaddr: publicMultiaddr,
243
+ room: options.room,
244
+ peerId,
245
+ name: options.name,
246
+ }).then(ok => {
247
+ if (ok) {
248
+ console.log(` ${bold(cyan(`npx society join ${options.name}`))}`);
249
+ }
250
+ console.log('');
251
+ });
252
+ }
253
+ else {
254
+ console.log('');
255
+ }
256
+ }
257
+ });
258
+ process.on('SIGINT', () => { cfProc.kill(); });
259
+ }
260
+ else {
261
+ console.log(` ${yellow('Could not start relay.')} Share the local code above for LAN access.`);
262
+ }
263
+ }
264
+ else {
265
+ console.log(` ${dim('For remote access, add')} ${cyan('--relay')} ${dim('to get a public invite code.')}`);
266
+ console.log('');
267
+ }
268
+ // Keep running
269
+ process.on('SIGINT', async () => {
270
+ stopHeartbeat();
271
+ await client.disconnect();
272
+ process.exit(0);
273
+ });
274
+ process.stdin.resume();
275
+ });
276
+ // ─── STATUS COMMAND ─────────────────────────────────────────────
277
+ // society status → show current state
278
+ program
279
+ .command('status')
280
+ .description('Show the status of your Society node')
281
+ .option('--db <path>', 'SQLite database path')
282
+ .action(async (options) => {
283
+ const storage = new Storage(options.db ? { dbPath: options.db } : undefined);
284
+ console.log('');
285
+ console.log(` ${bold('Society Protocol')} — Status`);
286
+ console.log('');
287
+ const identity = storage.getIdentity();
288
+ if (identity) {
289
+ console.log(` Identity: ${bold(identity.display_name)} (${identity.did.slice(0, 24)}...)`);
290
+ }
291
+ else {
292
+ console.log(` Identity: ${dim('Not initialized yet. Run:')} ${cyan('npx society')}`);
293
+ }
294
+ const rooms = storage.query('SELECT DISTINCT room_id FROM rooms');
295
+ if (rooms.length > 0) {
296
+ console.log(` Rooms: ${rooms.map((r) => r.room_id).join(', ')}`);
297
+ }
298
+ const chains = storage.query('SELECT COUNT(*) as count FROM chains');
299
+ console.log(` Chains: ${chains[0]?.count || 0} total`);
300
+ const steps = storage.query("SELECT COUNT(*) as count FROM steps WHERE status = 'completed'");
301
+ console.log(` Steps: ${steps[0]?.count || 0} completed`);
302
+ console.log('');
303
+ storage.close();
304
+ });
305
+ // ─── MCP COMMAND ─────────────────────────────────────────────────
306
+ program
307
+ .command('mcp')
308
+ .description('Start MCP server for Claude, Cursor, Windsurf')
309
+ .option('-n, --name <name>', 'agent name', process.env.SOCIETY_IDENTITY_NAME || 'MCP-Agent')
310
+ .option('-r, --room <room>', 'default room', 'lobby')
311
+ .option('-b, --bootstrap <addrs...>', 'bootstrap multiaddrs')
312
+ .action(async (options) => {
313
+ const client = await createClient({
314
+ identity: { name: options.name },
315
+ network: {
316
+ bootstrap: options.bootstrap,
317
+ enableGossipsub: true,
318
+ enableDht: true,
319
+ },
320
+ });
321
+ await client.joinRoom(options.room);
322
+ const { SocietyMCPServer } = await import('./mcp/server.js');
323
+ const server = new SocietyMCPServer({ client });
324
+ await server.run();
325
+ });
326
+ // ─── NODE COMMAND (power user) ──────────────────────────────────
327
+ program
328
+ .command('node')
329
+ .description('Start a Society node (advanced options)')
330
+ .option('-n, --name <name>', 'display name', 'Agent')
331
+ .option('-r, --room <room>', 'room ID to join', 'lobby')
332
+ .option('-p, --port <port>', 'listen port (0 = random)', '0')
333
+ .option('-b, --bootstrap <addrs...>', 'bootstrap multiaddrs')
334
+ .option('--db <path>', 'SQLite database path')
335
+ .option('--relay', 'spawn cloudflared to create a public WebSocket relay')
336
+ .option('--gossipsub', 'enable GossipSub for scalable pub/sub', true)
337
+ .option('--dht', 'enable DHT for peer discovery', true)
338
+ .option('--mission-leader', 'enable proactive mission leadership with auto-restore', false)
339
+ .option('--provider <provider>', 'AI planner provider (openai|anthropic|ollama)', 'openai')
340
+ .option('--debug', 'enable debug logging')
341
+ .action(async (options) => {
342
+ await startNode(options);
343
+ });
344
+ // ─── INIT COMMAND ─────────────────────────────────────────────────
345
+ import { AutoConfigurator, detectCI, detectContainer } from './autoconfig.js';
346
+ import { BootstrapManager } from './bootstrap.js';
347
+ program
348
+ .command('init')
349
+ .description('Interactive setup wizard for Society Protocol')
350
+ .option('--quick', 'Quick setup with defaults')
351
+ .option('--name <name>', 'Agent name')
352
+ .option('--room <room>', 'Default room to join')
353
+ .option('--template <name>', 'Default template for /summon')
354
+ .action(async (options) => {
355
+ await runInitWizard(options);
356
+ });
357
+ program
358
+ .command('dashboard')
359
+ .description('Launch the Society Dashboard — visual mission control')
360
+ .option('-p, --port <port>', 'Dashboard server port', '4200')
361
+ .option('-n, --name <name>', 'Agent display name', 'Dashboard')
362
+ .option('-r, --room <room>', 'Initial room to join', 'lobby')
363
+ .option('-b, --bootstrap <addrs...>', 'Bootstrap peer addresses')
364
+ .option('--connect <url>', 'Connect to existing Society node (remote mode)')
365
+ .option('--p2p-port <port>', 'P2P listening port')
366
+ .action(async (opts) => {
367
+ try {
368
+ const dashboardPath = resolve(realpathSync(fileURLToPath(import.meta.url)), '../../../dashboard/src/server/index.ts');
369
+ const args = ['tsx', dashboardPath, '--port', opts.port, '--name', opts.name, '--room', opts.room];
370
+ if (opts.bootstrap)
371
+ opts.bootstrap.forEach((b) => args.push('--bootstrap', b));
372
+ if (opts.connect)
373
+ args.push('--connect', opts.connect);
374
+ if (opts.p2pPort)
375
+ args.push('--p2p-port', opts.p2pPort);
376
+ const child = spawn('npx', args, { stdio: 'inherit', cwd: resolve(realpathSync(fileURLToPath(import.meta.url)), '../../../dashboard') });
377
+ child.on('error', (err) => { console.error('Failed to start dashboard:', err.message); process.exit(1); });
378
+ child.on('exit', (code) => process.exit(code || 0));
379
+ }
380
+ catch (err) {
381
+ console.error('Dashboard not found. Run from the society repo root or install society-dashboard.');
382
+ console.error(err.message);
383
+ process.exit(1);
384
+ }
385
+ });
386
+ const mission = program.command('mission').description('Manage proactive research missions');
387
+ mission
388
+ .command('start')
389
+ .requiredOption('--room <room>', 'room ID')
390
+ .requiredOption('--goal <goal>', 'research goal')
391
+ .option('--template <template>', 'mission template', 'literature_review_continuous')
392
+ .option('--cadence-ms <ms>', 'mission cadence in milliseconds', '300000')
393
+ .option('--detach', 'start mission and exit immediately instead of running leader loop', false)
394
+ .option('--name <name>', 'identity/display name', 'Mission Leader')
395
+ .option('--db <path>', 'SQLite database path')
396
+ .option('--bootstrap <addrs...>', 'bootstrap multiaddrs')
397
+ .action(async (options) => {
398
+ const client = await createClient({
399
+ identity: { name: options.name },
400
+ storage: options.db ? { path: options.db } : undefined,
401
+ network: {
402
+ bootstrap: options.bootstrap,
403
+ enableGossipsub: true,
404
+ enableDht: true,
405
+ },
406
+ proactive: {
407
+ enableLeadership: true,
408
+ autoRestoreMissions: true,
409
+ },
410
+ });
411
+ try {
412
+ await client.joinRoom(options.room);
413
+ const missionInfo = await client.startMission({
414
+ roomId: options.room,
415
+ goal: options.goal,
416
+ missionType: 'scientific_research',
417
+ templateId: options.template,
418
+ mode: 'continuous',
419
+ cadenceMs: parseInt(options.cadenceMs, 10),
420
+ policy: {
421
+ autonomy: 'semiautonomous',
422
+ approvalGates: ['publish', 'external_write', 'costly_action'],
423
+ swarm: {
424
+ minWorkers: 2,
425
+ maxWorkers: 12,
426
+ targetUtilization: 0.7,
427
+ leaseMs: 120000,
428
+ rebalanceIntervalMs: 30000,
429
+ },
430
+ retry: {
431
+ maxStepRetries: 3,
432
+ maxMissionReplans: 20,
433
+ cooldownMs: 60000,
434
+ },
435
+ },
436
+ research: {
437
+ sources: ['arxiv', 'pubmed', 'crossref', 'semantic-scholar', 'web'],
438
+ subdomainsPerCycle: 4,
439
+ requireDualReview: true,
440
+ requireCitationExtraction: true,
441
+ requireContradictionScan: true,
442
+ synthesisIntervalMs: parseInt(options.cadenceMs, 10),
443
+ },
444
+ knowledge: { autoIndex: true },
445
+ });
446
+ console.log(JSON.stringify(missionInfo, null, 2));
447
+ if (!options.detach) {
448
+ console.log(`[mission] leader active for mission ${missionInfo.missionId}. Press Ctrl+C to stop.`);
449
+ await new Promise((resolve) => {
450
+ const done = () => resolve();
451
+ process.once('SIGINT', done);
452
+ process.once('SIGTERM', done);
453
+ });
454
+ }
455
+ }
456
+ finally {
457
+ await client.disconnect();
458
+ }
459
+ });
460
+ mission
461
+ .command('list')
462
+ .option('--room <room>', 'optional room filter')
463
+ .option('--name <name>', 'identity/display name', 'Mission Leader')
464
+ .option('--db <path>', 'SQLite database path')
465
+ .option('--bootstrap <addrs...>', 'bootstrap multiaddrs')
466
+ .action(async (options) => {
467
+ const client = await createClient({
468
+ identity: { name: options.name },
469
+ storage: options.db ? { path: options.db } : undefined,
470
+ network: {
471
+ bootstrap: options.bootstrap,
472
+ enableGossipsub: true,
473
+ enableDht: true,
474
+ },
475
+ });
476
+ try {
477
+ console.log(JSON.stringify(await client.listMissions(options.room), null, 2));
478
+ }
479
+ finally {
480
+ await client.disconnect();
481
+ }
482
+ });
483
+ for (const action of ['pause', 'resume', 'stop']) {
484
+ mission
485
+ .command(action)
486
+ .requiredOption('--mission-id <id>', 'mission id')
487
+ .option('--reason <reason>', 'stop reason')
488
+ .option('--name <name>', 'identity/display name', 'Mission Leader')
489
+ .option('--db <path>', 'SQLite database path')
490
+ .option('--bootstrap <addrs...>', 'bootstrap multiaddrs')
491
+ .action(async (options) => {
492
+ const client = await createClient({
493
+ identity: { name: options.name },
494
+ storage: options.db ? { path: options.db } : undefined,
495
+ network: {
496
+ bootstrap: options.bootstrap,
497
+ enableGossipsub: true,
498
+ enableDht: true,
499
+ },
500
+ });
501
+ try {
502
+ if (action === 'pause')
503
+ await client.pauseMission(options.missionId);
504
+ if (action === 'resume')
505
+ await client.resumeMission(options.missionId);
506
+ if (action === 'stop')
507
+ await client.stopMission(options.missionId, options.reason);
508
+ console.log(`${action}d ${options.missionId}`);
509
+ }
510
+ finally {
511
+ await client.disconnect();
512
+ }
513
+ });
514
+ }
515
+ const swarm = program.command('swarm').description('Inspect swarm worker status');
516
+ swarm
517
+ .command('status')
518
+ .option('--room <room>', 'optional room filter')
519
+ .option('--name <name>', 'identity/display name', 'Mission Leader')
520
+ .option('--db <path>', 'SQLite database path')
521
+ .option('--bootstrap <addrs...>', 'bootstrap multiaddrs')
522
+ .action(async (options) => {
523
+ const client = await createClient({
524
+ identity: { name: options.name },
525
+ storage: options.db ? { path: options.db } : undefined,
526
+ network: {
527
+ bootstrap: options.bootstrap,
528
+ enableGossipsub: true,
529
+ enableDht: true,
530
+ },
531
+ });
532
+ try {
533
+ console.log(JSON.stringify(await client.getSwarmStatus(options.room), null, 2));
534
+ }
535
+ finally {
536
+ await client.disconnect();
537
+ }
538
+ });
539
+ const worker = program.command('worker').description('Run specialized Society workers');
540
+ worker
541
+ .command('research')
542
+ .requiredOption('--room <room>', 'mission room')
543
+ .requiredOption('--host-id <id>', 'worker host identifier')
544
+ .option('--runtime <runtime>', 'runtime type', 'nanobot')
545
+ .option('--specialties <items...>', 'worker specialties')
546
+ .option('--capabilities <items...>', 'worker capabilities')
547
+ .option('--name <name>', 'identity/display name', 'Research Worker')
548
+ .option('--db <path>', 'SQLite database path')
549
+ .option('--bootstrap <addrs...>', 'bootstrap multiaddrs')
550
+ .action(async (options) => {
551
+ const client = await createClient({
552
+ identity: { name: options.name },
553
+ storage: options.db ? { path: options.db } : undefined,
554
+ network: {
555
+ bootstrap: options.bootstrap,
556
+ enableGossipsub: true,
557
+ enableDht: true,
558
+ },
559
+ });
560
+ await client.startResearchWorker({
561
+ roomId: options.room,
562
+ hostId: options.hostId,
563
+ runtime: options.runtime,
564
+ specialties: options.specialties || ['research'],
565
+ capabilities: options.capabilities || ['research', 'academic-search', 'evidence-extraction'],
566
+ });
567
+ console.log(`[worker] research worker active in room ${options.room}`);
568
+ process.on('SIGINT', () => {
569
+ client.disconnect().finally(() => process.exit(0));
570
+ });
571
+ process.stdin.resume();
572
+ });
573
+ async function runInitWizard(options) {
574
+ const rl = readline.createInterface({
575
+ input: process.stdin,
576
+ output: process.stdout
577
+ });
578
+ const question = (prompt) => {
579
+ return new Promise((resolve) => {
580
+ rl.question(prompt, (answer) => resolve(answer.trim()));
581
+ });
582
+ };
583
+ const choose = async (prompt, choices) => {
584
+ console.log(`\n${prompt}`);
585
+ choices.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
586
+ const answer = await question('Select (number): ');
587
+ const idx = parseInt(answer) - 1;
588
+ return choices[idx] || choices[0];
589
+ };
590
+ const multichoose = async (prompt, choices) => {
591
+ console.log(`\n${prompt}`);
592
+ console.log(' (Space-separated numbers, e.g., "1 3")');
593
+ choices.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
594
+ const answer = await question('Select: ');
595
+ const indices = answer.split(/\s+/).map(n => parseInt(n) - 1).filter(n => !isNaN(n) && n >= 0 && n < choices.length);
596
+ return indices.map(i => choices[i]);
597
+ };
598
+ console.log('');
599
+ console.log(' ╔══════════════════════════════════════════════════════════╗');
600
+ console.log(' ║ 🚀 Society Protocol Setup Wizard ║');
601
+ console.log(' ║ Let\'s get you connected! ║');
602
+ console.log(' ╚══════════════════════════════════════════════════════════╝');
603
+ console.log('');
604
+ // Check if CI/container
605
+ const ciInfo = detectCI();
606
+ const isContainer = detectContainer();
607
+ if (ciInfo.isCI) {
608
+ console.log(` ℹ️ Detected CI environment: ${ciInfo.provider}`);
609
+ }
610
+ if (isContainer) {
611
+ console.log(' ℹ️ Detected container environment');
612
+ }
613
+ // Step 1: Auto-detect system
614
+ console.log('\n📊 Analyzing your system...');
615
+ const autoConfig = new AutoConfigurator();
616
+ const config = await autoConfig.generateConfig();
617
+ console.log(` Environment: ${config.environment.type}`);
618
+ console.log(` CPU: ${config.resources.cpu.cores} cores`);
619
+ console.log(` Memory: ${(config.resources.memory.total / (1024 ** 3)).toFixed(1)} GB`);
620
+ console.log(` Recommended mode: ${bold(config.usage.type.toUpperCase())}`);
621
+ if (options.quick) {
622
+ console.log('\n⚡ Quick mode: Using auto-detected settings...\n');
623
+ await autoConfig.applyConfig(config);
624
+ rl.close();
625
+ console.log(green('✅ Setup complete!'));
626
+ console.log(`\nNext steps:`);
627
+ console.log(` 1. Start the node: ${cyan('society node')}`);
628
+ console.log(` 2. Or customize: ${cyan('society init')} (without --quick)\n`);
629
+ return;
630
+ }
631
+ // Step 2: What are you building?
632
+ const useCase = await choose('What do you want to build?', [
633
+ 'Personal AI Assistant - Single agent for personal tasks',
634
+ 'Dev Team Collaboration - Code reviews, PR automation',
635
+ 'Research Group - Multi-agent research coordination',
636
+ 'Content Creation - Writing, editing, publishing pipeline',
637
+ 'Custom Setup - I\'ll configure everything manually'
638
+ ]);
639
+ // Step 3: AI Providers
640
+ const providers = await multichoose('Which AI providers do you have access to?', [
641
+ 'OpenAI (GPT-4o)',
642
+ 'Anthropic (Claude)',
643
+ 'Ollama (local models)',
644
+ 'Custom/OpenRouter'
645
+ ]);
646
+ const primaryProvider = providers[0]?.toLowerCase().split(' ')[0] || 'openai';
647
+ // Step 4: Agent identity
648
+ const name = options.name || await question(`\nChoose your agent name (default: Agent): `) || 'Agent';
649
+ // Step 5: Room setup
650
+ const roomOption = await choose('\nRoom setup:', [
651
+ 'Create a new private room',
652
+ 'Join existing room by ID',
653
+ 'Start in public lobby'
654
+ ]);
655
+ let roomId;
656
+ if (roomOption.includes('new')) {
657
+ const roomName = await question('Room name (no spaces): ') || `${name.toLowerCase()}-room`;
658
+ roomId = roomName.replace(/\s+/g, '-');
659
+ }
660
+ else if (roomOption.includes('existing')) {
661
+ roomId = await question('Enter room ID: ') || 'lobby';
662
+ }
663
+ else {
664
+ roomId = 'lobby';
665
+ }
666
+ // Step 6: Template preference
667
+ const templateNames = Object.keys(TEMPLATES);
668
+ let defaultTemplate = options.template;
669
+ if (!defaultTemplate && templateNames.length > 0) {
670
+ const templateChoice = await choose('\nDefault template for quick tasks:', [
671
+ 'None - I\'ll specify each time',
672
+ ...templateNames.map(t => `${t} - ${TEMPLATES[t]?.description || 'Custom template'}`)
673
+ ]);
674
+ if (!templateChoice.includes('None')) {
675
+ defaultTemplate = templateChoice.split(' ')[0];
676
+ }
677
+ }
678
+ // Step 7: Security preferences
679
+ console.log('\n🔒 Security Configuration:');
680
+ const requireAuth = await choose('Require authentication for adapter API?', [
681
+ 'Yes - Generate API key (recommended)',
682
+ 'No - Allow local connections only'
683
+ ]);
684
+ const apiKey = requireAuth.includes('Yes')
685
+ ? Buffer.from(crypto.randomUUID()).toString('base64').slice(0, 32)
686
+ : undefined;
687
+ // Step 8: Apply configuration
688
+ console.log('\n⚙️ Applying configuration...');
689
+ // Save identity preference
690
+ const storage = new Storage();
691
+ const identity = generateIdentity(name);
692
+ const privHex = uint8ToString(identity.privateKey, 'base16');
693
+ const pubHex = uint8ToString(identity.publicKey, 'base16');
694
+ storage.saveIdentity(identity.did, privHex, pubHex, name);
695
+ // Generate config file
696
+ const { writeFileSync, mkdirSync } = await import('fs');
697
+ const { join } = await import('path');
698
+ const { homedir } = await import('os');
699
+ const configDir = join(homedir(), '.society');
700
+ mkdirSync(configDir, { recursive: true });
701
+ const userConfig = `
702
+ # Society Protocol - User Configuration
703
+ # Generated: ${new Date().toISOString()}
704
+
705
+ identity:
706
+ name: "${name}"
707
+ did: "${identity.did}"
708
+
709
+ room:
710
+ default: "${roomId}"
711
+
712
+ ai:
713
+ providers:
714
+ ${providers.map(p => ` - ${p.toLowerCase().split(' ')[0]}`).join('\n')}
715
+ primary: "${primaryProvider}"
716
+
717
+ ${defaultTemplate ? `template:\n default: "${defaultTemplate}"` : ''}
718
+
719
+ adapter:
720
+ host: "127.0.0.1"
721
+ port: ${config.recommended.apiPort}
722
+ ${apiKey ? `auth:\n type: bearer\n api_key: "${apiKey}"` : 'auth:\n type: none'}
723
+
724
+ network:
725
+ mode: "${config.usage.type}"
726
+ max_peers: ${config.recommended.maxPeers}
727
+ bootstrap:
728
+ dns_discovery: true
729
+ fallback_peers: true
730
+ `;
731
+ writeFileSync(join(configDir, 'config.yml'), userConfig.trim());
732
+ await autoConfig.applyConfig(config);
733
+ // Summary
734
+ console.log('');
735
+ console.log(green('✅ Setup complete!'));
736
+ console.log('');
737
+ console.log('📋 Configuration Summary:');
738
+ console.log(` Identity: ${cyan(name)} (${identity.did.slice(0, 20)}...)`);
739
+ console.log(` Room: ${cyan(roomId)}`);
740
+ console.log(` AI Provider: ${cyan(primaryProvider)}`);
741
+ console.log(` API Port: ${cyan(config.recommended.apiPort.toString())}`);
742
+ if (apiKey) {
743
+ console.log(` API Key: ${cyan(apiKey)}`);
744
+ }
745
+ console.log('');
746
+ console.log('🚀 Next steps:');
747
+ console.log(` 1. Start your node: ${cyan('society node')}`);
748
+ console.log(` 2. Or with options: ${cyan(`society node --name "${name}" --room ${roomId}`)}`);
749
+ console.log(` 3. Start collaborating: ${cyan('/summon "Your task here"')}`);
750
+ console.log('');
751
+ console.log(`📚 Documentation: ${cyan('https://docs.society.dev')}`);
752
+ console.log(`💬 Community: ${cyan('https://discord.gg/society')}`);
753
+ console.log('');
754
+ rl.close();
755
+ }
756
+ const isMainModule = (() => {
757
+ const toRealPath = (candidate) => {
758
+ try {
759
+ return realpathSync(candidate);
760
+ }
761
+ catch {
762
+ return resolve(candidate);
763
+ }
764
+ };
765
+ const entry = process.argv[1] ? toRealPath(process.argv[1]) : '';
766
+ const current = toRealPath(fileURLToPath(import.meta.url));
767
+ return entry === current;
768
+ })();
769
+ if (isMainModule) {
770
+ program.parse();
771
+ }
772
+ async function startNode(options) {
773
+ const DEBUG = options.debug || process.env.SOCIETY_DEBUG === 'true';
774
+ // Banner
775
+ console.log('');
776
+ console.log(' ╔══════════════════════════════════════════════════════════╗');
777
+ console.log(' ║ 🌐 Society Protocol v1.0 (State of Art) ║');
778
+ console.log(' ║ P2P Multi-Agent Collaboration Network Node ║');
779
+ console.log(' ╠══════════════════════════════════════════════════════════╣');
780
+ console.log(' ║ Features: GossipSub • DHT • Reputation • Multi-Provider ║');
781
+ console.log(' ╚══════════════════════════════════════════════════════════╝');
782
+ console.log('');
783
+ // 1. Initialize storage
784
+ const storage = new Storage(options.db ? { dbPath: options.db } : undefined);
785
+ console.log('[init] Storage initialized.');
786
+ // 2. Initialize or restore identity
787
+ let identity;
788
+ const existingIdentity = storage.getIdentity();
789
+ if (existingIdentity) {
790
+ identity = restoreIdentity(existingIdentity.private_key_hex, options.name);
791
+ console.log(`[init] Identity restored: ${identity.did.slice(0, 32)}...`);
792
+ }
793
+ else {
794
+ identity = generateIdentity(options.name);
795
+ const privHex = uint8ToString(identity.privateKey, 'base16');
796
+ const pubHex = uint8ToString(identity.publicKey, 'base16');
797
+ storage.saveIdentity(identity.did, privHex, pubHex, identity.displayName);
798
+ console.log(`[init] New identity generated: ${identity.did.slice(0, 32)}...`);
799
+ }
800
+ console.log(`[init] Display name: ${bold(identity.displayName)}`);
801
+ // 3. Initialize reputation engine
802
+ const reputation = new ReputationEngine(storage);
803
+ console.log('[init] Reputation engine ready.');
804
+ // 4. Discover bootstrap peers when --bootstrap is not provided
805
+ let bootstrapAddrs = options.bootstrap;
806
+ if (!bootstrapAddrs?.length) {
807
+ try {
808
+ const bootstrapManager = new BootstrapManager();
809
+ const peers = await bootstrapManager.discover();
810
+ bootstrapAddrs = peers.flatMap((peer) => peer.addrs);
811
+ if (bootstrapAddrs.length > 0) {
812
+ console.log(`[bootstrap] Discovered ${bootstrapAddrs.length} bootstrap addresses`);
813
+ }
814
+ }
815
+ catch (err) {
816
+ console.warn(`[bootstrap] Discovery unavailable: ${err?.message || 'unknown error'}`);
817
+ console.warn('[bootstrap] Continuing with local discovery only (mDNS/DHT).');
818
+ }
819
+ }
820
+ // 5. Start P2P node
821
+ const p2p = new P2PNode();
822
+ await p2p.start({
823
+ port: parseInt(options.port, 10),
824
+ bootstrapAddrs,
825
+ enableMdns: true,
826
+ enableDht: options.dht,
827
+ enableGossipsub: options.gossipsub,
828
+ });
829
+ // 6. Initialize room manager
830
+ const rooms = new RoomManager(identity, p2p, storage);
831
+ // 7. Join room
832
+ const roomId = options.room;
833
+ await rooms.joinRoom(roomId, roomId);
834
+ console.log(`[init] Joined room: ${roomId}`);
835
+ // 8. Initialize CoC Engine with reputation
836
+ const coc = new CocEngine(identity, rooms, storage, reputation);
837
+ // 9. Initialize Planner with multi-provider support
838
+ const planner = new Planner({
839
+ provider: options.provider,
840
+ enableCache: true,
841
+ fallbackChain: ['openai', 'anthropic', 'ollama'],
842
+ });
843
+ // 10. Initialize Adapter Host (localhost-only with optional API key)
844
+ const adapterPort = parseInt(process.env.SOCIETY_ADAPTER_PORT || '8080', 10);
845
+ const adapterHost = new AdapterHost(storage, coc, {
846
+ port: adapterPort,
847
+ host: '127.0.0.1', // Localhost only for security
848
+ security: {
849
+ apiKey: process.env.SOCIETY_API_KEY, // Optional: require API key
850
+ rateLimitEnabled: true,
851
+ rateLimitMaxRequests: 1000,
852
+ securityHeaders: true,
853
+ }
854
+ });
855
+ adapterHost.start();
856
+ console.log(`[init] Adapter Host listening on 127.0.0.1:${adapterPort}`);
857
+ if (process.env.SOCIETY_API_KEY) {
858
+ console.log('[init] API Key authentication enabled');
859
+ }
860
+ else {
861
+ console.log('[init] WARNING: No API key set. Set SOCIETY_API_KEY for production.');
862
+ }
863
+ // 11. Initialize Capsule Exporter
864
+ const exporter = new CapsuleExporter(coc, storage);
865
+ // 12. Initialize federation/integration stack (Federation Mesh)
866
+ const federation = new FederationEngine(storage, identity);
867
+ const knowledge = new KnowledgePool(storage, identity);
868
+ const skills = new SkillsEngine(storage, identity);
869
+ const security = new SecurityManager(identity);
870
+ const persona = new PersonaVaultEngine(storage, identity.did, {
871
+ defaultVaultName: `${identity.displayName} Persona Vault`,
872
+ });
873
+ const integration = new IntegrationEngine(storage, identity, federation, rooms, knowledge, coc, skills, security);
874
+ integration.attachPersonaVault(persona);
875
+ const proactiveLeader = options.missionLeader
876
+ ? new ProactiveMissionEngine(identity, storage, rooms, coc, planner, knowledge, undefined, undefined, {
877
+ enableLeadership: true,
878
+ autoRestoreMissions: true,
879
+ })
880
+ : undefined;
881
+ if (proactiveLeader) {
882
+ console.log('[init] Mission leader mode enabled (auto-restore active).');
883
+ }
884
+ console.log('');
885
+ console.log(' Type a message and press Enter to send.');
886
+ console.log(' Commands: /peers /rooms /presence /reputation /info /history');
887
+ console.log(' /summon /template /chains /step /export /quit');
888
+ console.log(' /mesh-request /mesh-peerings /mesh-open /mesh-bridges /mesh-stats');
889
+ console.log(' ──────────────────────────────────────────────────────────────');
890
+ console.log('');
891
+ // ─── Event Listeners ────────────────────────────────────────
892
+ rooms.on('chat:message', (_roomId, envelope) => {
893
+ const body = envelope.body;
894
+ const time = new Date(envelope.ts).toLocaleTimeString();
895
+ process.stdout.write('\r\x1b[K');
896
+ // Show reputation badge if available
897
+ const repBadge = '';
898
+ console.log(` ${dim(time)} ${bold(cyan(envelope.from.name))}${repBadge}: ${body.text}`);
899
+ rl.prompt(true);
900
+ });
901
+ rooms.on('presence:update', (_roomId, envelope) => {
902
+ const body = envelope.body;
903
+ if (body.status === 'online') {
904
+ process.stdout.write('\r\x1b[K');
905
+ console.log(` ${dim('→')} ${green(envelope.from.name)} is online`);
906
+ rl.prompt(true);
907
+ }
908
+ });
909
+ p2p.on('peer:connected', (peerId) => {
910
+ process.stdout.write('\r\x1b[K');
911
+ console.log(` ${dim('⚡')} Peer connected: ${dim(peerId.slice(0, 20))}...`);
912
+ rl.prompt(true);
913
+ });
914
+ p2p.on('peer:disconnected', (peerId) => {
915
+ process.stdout.write('\r\x1b[K');
916
+ console.log(` ${dim('⚡')} Peer disconnected: ${dim(peerId.slice(0, 20))}...`);
917
+ rl.prompt(true);
918
+ });
919
+ coc.on('chain:opened', (chainId, goal) => {
920
+ process.stdout.write('\r\x1b[K');
921
+ console.log(`\n ${bold(cyan('⛓️ Chain Opened'))}: ${goal.slice(0, 60)}${goal.length > 60 ? '...' : ''}`);
922
+ console.log(` ID: ${dim(chainId)}`);
923
+ rl.prompt(true);
924
+ });
925
+ coc.on('chain:planned', (chainId, dag) => {
926
+ process.stdout.write('\r\x1b[K');
927
+ console.log(` ${bold(cyan('🗺️ Plan Ready'))}: ${dag.length} steps`);
928
+ if (DEBUG) {
929
+ dag.forEach((step, i) => {
930
+ const deps = step.depends_on.length > 0 ? ` ← ${step.depends_on.join(', ')}` : '';
931
+ console.log(` ${i + 1}. [${step.kind}] ${step.title}${deps}`);
932
+ });
933
+ }
934
+ rl.prompt(true);
935
+ });
936
+ coc.on('step:unlocked', (chainId, stepId, step) => {
937
+ process.stdout.write('\r\x1b[K');
938
+ console.log(` ${bold(yellow('🔓 Step Unlocked'))}: [${stepId}] ${step.title}`);
939
+ rl.prompt(true);
940
+ });
941
+ coc.on('step:assigned', (chainId, stepId, assignee) => {
942
+ process.stdout.write('\r\x1b[K');
943
+ console.log(` ${bold(blue('👤 Assigned'))}: [${stepId}] → ${assignee.slice(0, 20)}...`);
944
+ rl.prompt(true);
945
+ });
946
+ coc.on('step:submitted', (chainId, stepId, status) => {
947
+ process.stdout.write('\r\x1b[K');
948
+ const icon = status === 'completed' ? '✅' : '❌';
949
+ console.log(` ${icon} Step ${stepId}: ${status}`);
950
+ rl.prompt(true);
951
+ });
952
+ coc.on('step:expired', (chainId, stepId, oldAssignee) => {
953
+ process.stdout.write('\r\x1b[K');
954
+ console.log(` ${bold(red('⏰ Lease Expired'))}: [${stepId}] reassigning from ${oldAssignee.slice(0, 16)}...`);
955
+ rl.prompt(true);
956
+ });
957
+ coc.on('chain:completed', (chainId) => {
958
+ process.stdout.write('\r\x1b[K');
959
+ console.log(`\n ${bold(green('✅ Chain Completed'))}: ${chainId}\n`);
960
+ rl.prompt(true);
961
+ });
962
+ // ─── Interactive REPL ───────────────────────────────────────
963
+ const rl = readline.createInterface({
964
+ input: process.stdin,
965
+ output: process.stdout,
966
+ prompt: `${dim(identity.displayName)} ${dim('>')} `,
967
+ });
968
+ rl.prompt();
969
+ let cloudflaredProc = null;
970
+ // Start relay if requested
971
+ if (options.relay) {
972
+ const wsPort = options.port === '0' ? 0 : parseInt(options.port, 10) + 1;
973
+ if (wsPort === 0) {
974
+ console.warn(yellow(' [warn] --relay requires a fixed --port. Ignoring relay.'));
975
+ }
976
+ else {
977
+ // Auto-install cloudflared if missing
978
+ if (!isCommandAvailable('cloudflared')) {
979
+ console.log(` ${yellow('cloudflared not found.')} Installing automatically...`);
980
+ if (!await installCloudflared()) {
981
+ console.warn(yellow(' [warn] Failed to install cloudflared. Skipping relay.'));
982
+ }
983
+ }
984
+ if (!isCommandAvailable('cloudflared')) {
985
+ console.warn(yellow(' [warn] cloudflared still not available. Skipping relay.'));
986
+ }
987
+ else {
988
+ console.log(` ${dim('🌐')} Spawning cloudflared relay to localhost:${wsPort}...`);
989
+ cloudflaredProc = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${wsPort}`]);
990
+ cloudflaredProc.stderr?.on('data', (data) => {
991
+ const str = data.toString();
992
+ const match = str.match(/(https:\/\/[a-z0-9-]+\.trycloudflare\.com)/);
993
+ if (match) {
994
+ process.stdout.write('\r\x1b[K');
995
+ const host = match[1].replace('https://', '');
996
+ const relayMultiaddr = `/dns4/${host}/tcp/443/wss/p2p/${p2p.getPeerId()}`;
997
+ const code = encodeInvite(relayMultiaddr, options.room);
998
+ console.log(`\n ${bold(green('Relay active!'))} Share this with anyone:`);
999
+ console.log(` ${cyan(`npx society join ${code}`)}`);
1000
+ // Register name in registry
1001
+ registerNode(options.name, {
1002
+ multiaddr: relayMultiaddr,
1003
+ room: options.room,
1004
+ peerId: p2p.getPeerId(),
1005
+ name: options.name,
1006
+ }).then(ok => {
1007
+ if (ok) {
1008
+ console.log(` ${cyan(`npx society join ${options.name}`)}`);
1009
+ }
1010
+ console.log('');
1011
+ rl.prompt(true);
1012
+ });
1013
+ }
1014
+ });
1015
+ } // end cloudflared available check
1016
+ }
1017
+ }
1018
+ // Command loop
1019
+ rl.on('line', async (line) => {
1020
+ const input = line.trim();
1021
+ if (!input) {
1022
+ rl.prompt();
1023
+ return;
1024
+ }
1025
+ if (input.startsWith('/')) {
1026
+ await handleCommand(input, {
1027
+ identity,
1028
+ p2p,
1029
+ rooms,
1030
+ storage,
1031
+ coc,
1032
+ planner,
1033
+ reputation,
1034
+ exporter,
1035
+ federation,
1036
+ integration,
1037
+ roomId,
1038
+ DEBUG,
1039
+ });
1040
+ rl.prompt();
1041
+ return;
1042
+ }
1043
+ // Send chat message
1044
+ try {
1045
+ await rooms.sendChatMessage(roomId, input, { formatting: 'plain' });
1046
+ const time = new Date().toLocaleTimeString();
1047
+ console.log(` ${dim(time)} ${bold(yellow('you'))}: ${input}`);
1048
+ }
1049
+ catch (err) {
1050
+ console.error(` ${red('Error:')} ${err.message}`);
1051
+ }
1052
+ rl.prompt();
1053
+ });
1054
+ rl.on('close', async () => {
1055
+ console.log('\n[shutdown] Cleaning up...');
1056
+ if (cloudflaredProc) {
1057
+ cloudflaredProc.kill();
1058
+ }
1059
+ stopHeartbeat();
1060
+ proactiveLeader?.destroy();
1061
+ skills.stop();
1062
+ adapterHost.stop();
1063
+ coc.destroy();
1064
+ rooms.destroy();
1065
+ await p2p.stop();
1066
+ storage.close();
1067
+ process.exit(0);
1068
+ });
1069
+ process.on('SIGINT', () => {
1070
+ rl.close();
1071
+ });
1072
+ }
1073
+ async function handleCommand(input, ctx) {
1074
+ const [cmd, ...args] = input.split(' ');
1075
+ switch (cmd) {
1076
+ case '/peers': {
1077
+ const peers = ctx.p2p.getPeers();
1078
+ const latencies = ctx.p2p.getConnectedPeers().map(p => ({
1079
+ id: p.id.slice(0, 16),
1080
+ latency: p.latency,
1081
+ }));
1082
+ if (peers.length === 0) {
1083
+ console.log(' No connected peers.');
1084
+ }
1085
+ else {
1086
+ console.log(` Connected peers (${peers.length}):`);
1087
+ latencies.forEach(p => {
1088
+ const lat = p.latency ? `${p.latency}ms` : 'N/A';
1089
+ console.log(` • ${p.id}... (${dim(lat)})`);
1090
+ });
1091
+ }
1092
+ break;
1093
+ }
1094
+ case '/rooms': {
1095
+ const joined = ctx.rooms.getJoinedRooms();
1096
+ console.log(` Joined rooms: ${joined.join(', ')}`);
1097
+ break;
1098
+ }
1099
+ case '/mesh-request': {
1100
+ const sourceFederationId = args[0];
1101
+ const targetFederationDid = args[1];
1102
+ const policyInput = args.slice(2).join(' ').trim();
1103
+ if (!sourceFederationId || !targetFederationDid) {
1104
+ console.log(' Usage: /mesh-request <source_federation_id> <target_federation_did> [policy_json]');
1105
+ break;
1106
+ }
1107
+ try {
1108
+ const policy = policyInput ? JSON.parse(policyInput) : {};
1109
+ const peering = await ctx.federation.requestPeering(sourceFederationId, targetFederationDid, policy);
1110
+ console.log(` ${green('✓')} Peering requested: ${peering.id}`);
1111
+ console.log(` status=${peering.status} target=${peering.targetFederationDid}`);
1112
+ }
1113
+ catch (err) {
1114
+ console.log(` ${red('Error:')} ${err.message}`);
1115
+ }
1116
+ break;
1117
+ }
1118
+ case '/mesh-accept': {
1119
+ const peeringId = args[0];
1120
+ const reason = args.slice(1).join(' ') || undefined;
1121
+ if (!peeringId) {
1122
+ console.log(' Usage: /mesh-accept <peering_id> [reason]');
1123
+ break;
1124
+ }
1125
+ try {
1126
+ const peering = await ctx.federation.respondPeering(peeringId, true, reason);
1127
+ console.log(` ${green('✓')} Peering accepted: ${peering.id}`);
1128
+ }
1129
+ catch (err) {
1130
+ console.log(` ${red('Error:')} ${err.message}`);
1131
+ }
1132
+ break;
1133
+ }
1134
+ case '/mesh-reject': {
1135
+ const peeringId = args[0];
1136
+ const reason = args.slice(1).join(' ') || undefined;
1137
+ if (!peeringId) {
1138
+ console.log(' Usage: /mesh-reject <peering_id> [reason]');
1139
+ break;
1140
+ }
1141
+ try {
1142
+ const peering = await ctx.federation.respondPeering(peeringId, false, reason);
1143
+ console.log(` ${yellow('⚠')} Peering rejected: ${peering.id}`);
1144
+ }
1145
+ catch (err) {
1146
+ console.log(` ${red('Error:')} ${err.message}`);
1147
+ }
1148
+ break;
1149
+ }
1150
+ case '/mesh-revoke': {
1151
+ const peeringId = args[0];
1152
+ const reason = args.slice(1).join(' ') || undefined;
1153
+ if (!peeringId) {
1154
+ console.log(' Usage: /mesh-revoke <peering_id> [reason]');
1155
+ break;
1156
+ }
1157
+ try {
1158
+ const peering = await ctx.federation.revokePeering(peeringId, reason);
1159
+ console.log(` ${yellow('⚠')} Peering revoked: ${peering.id}`);
1160
+ }
1161
+ catch (err) {
1162
+ console.log(` ${red('Error:')} ${err.message}`);
1163
+ }
1164
+ break;
1165
+ }
1166
+ case '/mesh-peerings': {
1167
+ const federationId = args[0];
1168
+ const status = args[1];
1169
+ if (!federationId) {
1170
+ console.log(' Usage: /mesh-peerings <federation_id> [pending|active|rejected|revoked]');
1171
+ break;
1172
+ }
1173
+ const peerings = ctx.federation.listPeerings(federationId, status);
1174
+ if (peerings.length === 0) {
1175
+ console.log(' No peerings found.');
1176
+ break;
1177
+ }
1178
+ console.log(` Peerings (${peerings.length}):`);
1179
+ for (const peering of peerings) {
1180
+ console.log(` • ${peering.id} [${peering.status}] -> ${peering.targetFederationDid}`);
1181
+ }
1182
+ break;
1183
+ }
1184
+ case '/mesh-open': {
1185
+ const peeringId = args[0];
1186
+ const localRoomId = args[1];
1187
+ const remoteRoomId = args[2];
1188
+ const rulesInput = args.slice(3).join(' ').trim();
1189
+ if (!peeringId || !localRoomId || !remoteRoomId) {
1190
+ console.log(' Usage: /mesh-open <peering_id> <local_room_id> <remote_room_id> [rules_json]');
1191
+ break;
1192
+ }
1193
+ try {
1194
+ const rules = rulesInput ? JSON.parse(rulesInput) : undefined;
1195
+ const bridge = await ctx.integration.openMeshBridge(peeringId, localRoomId, remoteRoomId, rules);
1196
+ console.log(` ${green('✓')} Mesh bridge opened: ${bridge.id}`);
1197
+ console.log(` ${bridge.localRoomId} -> ${bridge.remoteRoomId}`);
1198
+ }
1199
+ catch (err) {
1200
+ console.log(` ${red('Error:')} ${err.message}`);
1201
+ }
1202
+ break;
1203
+ }
1204
+ case '/mesh-close': {
1205
+ const bridgeId = args[0];
1206
+ if (!bridgeId) {
1207
+ console.log(' Usage: /mesh-close <bridge_id>');
1208
+ break;
1209
+ }
1210
+ try {
1211
+ await ctx.integration.closeMeshBridge(bridgeId);
1212
+ console.log(` ${green('✓')} Mesh bridge closed: ${bridgeId}`);
1213
+ }
1214
+ catch (err) {
1215
+ console.log(` ${red('Error:')} ${err.message}`);
1216
+ }
1217
+ break;
1218
+ }
1219
+ case '/mesh-bridges': {
1220
+ const federationId = args[0];
1221
+ const bridges = ctx.integration.listMeshBridges(federationId);
1222
+ if (bridges.length === 0) {
1223
+ console.log(' No mesh bridges found.');
1224
+ break;
1225
+ }
1226
+ console.log(` Bridges (${bridges.length}):`);
1227
+ for (const bridge of bridges) {
1228
+ console.log(` • ${bridge.id} [${bridge.status}] ${bridge.localRoomId} -> ${bridge.remoteRoomId} ` +
1229
+ `(in=${bridge.eventsIn}, out=${bridge.eventsOut})`);
1230
+ }
1231
+ break;
1232
+ }
1233
+ case '/mesh-stats': {
1234
+ const federationId = args[0];
1235
+ const stats = ctx.integration.getMeshStats(federationId);
1236
+ console.log(` Mesh stats${federationId ? ` (${federationId})` : ''}:`);
1237
+ console.log(` bridges: ${stats.bridgeCount} (${stats.activeBridges} active)`);
1238
+ console.log(` events: in=${stats.eventsIn} out=${stats.eventsOut}`);
1239
+ if (stats.lastSyncAt) {
1240
+ console.log(` last_sync: ${new Date(stats.lastSyncAt).toISOString()}`);
1241
+ }
1242
+ break;
1243
+ }
1244
+ case '/presence': {
1245
+ const online = ctx.rooms.getOnlinePeers();
1246
+ if (online.length === 0) {
1247
+ console.log(' No online peers detected.');
1248
+ }
1249
+ else {
1250
+ console.log(` Online peers (${online.length}):`);
1251
+ for (const p of online) {
1252
+ const caps = p.capabilities ? JSON.parse(p.capabilities).slice(0, 3).join(', ') : '';
1253
+ console.log(` • ${p.peer_name || 'unknown'} — ${dim(caps)}`);
1254
+ }
1255
+ }
1256
+ break;
1257
+ }
1258
+ case '/reputation': {
1259
+ const targetDid = args[0] || ctx.identity.did;
1260
+ try {
1261
+ const rep = await ctx.reputation.getReputation(targetDid);
1262
+ console.log(` Reputation for ${targetDid.slice(0, 32)}...`);
1263
+ console.log(` Tier: ${formatReputationTier(rep.trust_tier)}`);
1264
+ console.log(` Overall: ${(rep.overall * 100).toFixed(1)}%`);
1265
+ console.log(` Tasks: ${rep.metrics.tasks_completed} completed, ${rep.metrics.tasks_failed} failed`);
1266
+ console.log(` Quality: ${(rep.metrics.avg_quality_score * 100).toFixed(1)}%`);
1267
+ console.log(` On-time: ${(rep.metrics.on_time_delivery * 100).toFixed(1)}%`);
1268
+ if (rep.specialties.length > 0) {
1269
+ console.log(` Top specialties:`);
1270
+ rep.specialties.slice(0, 5).forEach(s => {
1271
+ console.log(` • ${s.specialty}: ${(s.score * 100).toFixed(0)}%`);
1272
+ });
1273
+ }
1274
+ }
1275
+ catch (err) {
1276
+ console.log(` ${red('Error:')} ${err.message}`);
1277
+ }
1278
+ break;
1279
+ }
1280
+ case '/info': {
1281
+ console.log(` Identity: ${ctx.identity.did}`);
1282
+ console.log(` Name: ${ctx.identity.displayName}`);
1283
+ console.log(` PeerId: ${ctx.p2p.getPeerId()}`);
1284
+ console.log(` Addresses: ${ctx.p2p.getMultiaddrs().join(', ')}`);
1285
+ console.log(` Room: ${ctx.roomId}`);
1286
+ console.log(` Peers: ${ctx.p2p.getPeers().length}`);
1287
+ console.log(` Providers: ${ctx.planner.getAvailableProviders().join(', ')}`);
1288
+ break;
1289
+ }
1290
+ case '/history': {
1291
+ const messages = ctx.rooms.getMessages(ctx.roomId, 20);
1292
+ if (messages.length === 0) {
1293
+ console.log(' No messages in history.');
1294
+ }
1295
+ else {
1296
+ messages.reverse().forEach((m) => {
1297
+ const time = new Date(m.ts).toLocaleTimeString();
1298
+ const isMe = m.from_did === ctx.identity.did;
1299
+ const name = isMe ? yellow('you') : cyan(m.from_name ?? 'unknown');
1300
+ console.log(` ${dim(time)} ${bold(name)}: ${m.text}`);
1301
+ });
1302
+ }
1303
+ break;
1304
+ }
1305
+ case '/summon': {
1306
+ const goal = args.join(' ');
1307
+ if (!goal) {
1308
+ console.log(' Usage: /summon <goal description>');
1309
+ break;
1310
+ }
1311
+ if (!ctx.planner.isReady()) {
1312
+ console.error(` ${red('Error:')} No AI providers available. Set OPENAI_API_KEY or ANTHROPIC_API_KEY.`);
1313
+ break;
1314
+ }
1315
+ console.log(` ${dim('🤖')} Planning collaboration for: "${goal.slice(0, 50)}${goal.length > 50 ? '...' : ''}"`);
1316
+ try {
1317
+ const startTime = Date.now();
1318
+ const planResult = await ctx.planner.generatePlan(goal);
1319
+ const latency = Date.now() - startTime;
1320
+ console.log(` ${green('✓')} Plan generated in ${latency}ms via ${planResult.provider}`);
1321
+ // Open chain
1322
+ const chainId = await ctx.coc.openChain(ctx.roomId, goal, { priority: 'normal' });
1323
+ // Publish plan
1324
+ await ctx.coc.publishPlan(ctx.roomId, chainId, planResult.dag, `${planResult.provider}/${planResult.model}`);
1325
+ }
1326
+ catch (err) {
1327
+ console.error(` ${red('Error:')} ${err.message}`);
1328
+ }
1329
+ break;
1330
+ }
1331
+ case '/template': {
1332
+ const templateId = args[0];
1333
+ const goal = args.slice(1).join(' ');
1334
+ if (!templateId || !goal) {
1335
+ console.log(' Usage: /template <template_name> <goal>');
1336
+ console.log(` Available: ${Object.keys(TEMPLATES).join(', ')}`);
1337
+ break;
1338
+ }
1339
+ try {
1340
+ const template = getTemplate(templateId);
1341
+ console.log(` ${dim('⚡')} Using template "${templateId}"`);
1342
+ const chainId = await ctx.coc.openChain(ctx.roomId, goal, { templateId });
1343
+ const dag = template.generate(goal);
1344
+ await ctx.coc.publishPlan(ctx.roomId, chainId, dag, `template/${templateId}`);
1345
+ }
1346
+ catch (err) {
1347
+ console.error(` ${red('Error:')} ${err.message}`);
1348
+ }
1349
+ break;
1350
+ }
1351
+ case '/chains': {
1352
+ const chains = ctx.coc.getActiveChains();
1353
+ if (chains.length === 0) {
1354
+ console.log(' No active chains.');
1355
+ }
1356
+ else {
1357
+ chains.forEach(c => {
1358
+ const statusColor = c.status === 'completed' ? green : c.status === 'running' ? yellow : red;
1359
+ const progress = c.steps.filter(s => s.status === 'merged' || s.status === 'submitted').length;
1360
+ console.log(` [${statusColor(c.status)}] ${c.chain_id.slice(0, 8)}... — ${progress}/${c.steps.length} steps — ${c.goal.slice(0, 40)}${c.goal.length > 40 ? '...' : ''}`);
1361
+ });
1362
+ }
1363
+ break;
1364
+ }
1365
+ case '/chain': {
1366
+ const chainId = args[0];
1367
+ if (!chainId) {
1368
+ console.log(' Usage: /chain <chain_id>');
1369
+ break;
1370
+ }
1371
+ const chain = ctx.coc.getChain(chainId);
1372
+ if (!chain) {
1373
+ console.log(' Chain not found.');
1374
+ break;
1375
+ }
1376
+ console.log(` Chain: ${chainId}`);
1377
+ console.log(` Goal: ${chain.goal}`);
1378
+ console.log(` Status: ${chain.status}`);
1379
+ console.log(` Steps:`);
1380
+ chain.steps.forEach(s => {
1381
+ const statusIcon = s.status === 'merged' ? '✅' : s.status === 'assigned' ? '👤' : s.status === 'proposed' ? '○' : '◐';
1382
+ const assignee = s.assignee_did ? ` @${s.assignee_did.slice(0, 8)}...` : '';
1383
+ console.log(` ${statusIcon} [${s.kind}] ${s.title}${assignee}`);
1384
+ });
1385
+ break;
1386
+ }
1387
+ case '/step': {
1388
+ const stepId = args[0];
1389
+ const status = args[1];
1390
+ const memo = args.slice(2).join(' ') || 'Manual submission';
1391
+ if (!stepId || !status) {
1392
+ console.log(' Usage: /step <step_id> <completed|failed> [memo...]');
1393
+ break;
1394
+ }
1395
+ try {
1396
+ // Find chain for this step
1397
+ const chains = ctx.coc.getActiveChains();
1398
+ let chainId = null;
1399
+ for (const chain of chains) {
1400
+ if (chain.steps.find(s => s.step_id === stepId)) {
1401
+ chainId = chain.chain_id;
1402
+ break;
1403
+ }
1404
+ }
1405
+ if (!chainId) {
1406
+ // Try storage
1407
+ const step = ctx.storage.db.prepare('SELECT chain_id FROM coc_steps WHERE step_id = ?').get(stepId);
1408
+ if (step)
1409
+ chainId = step.chain_id;
1410
+ }
1411
+ if (!chainId) {
1412
+ console.log(' Step not found in any chain.');
1413
+ break;
1414
+ }
1415
+ const artifacts = []; // Could parse from input
1416
+ await ctx.coc.submitStep(ctx.roomId, chainId, stepId, status, memo, artifacts);
1417
+ console.log(` ${green('✓')} Step ${stepId} marked as ${status}`);
1418
+ }
1419
+ catch (err) {
1420
+ console.error(` ${red('Error:')} ${err.message}`);
1421
+ }
1422
+ break;
1423
+ }
1424
+ case '/assign': {
1425
+ const [stepId, assignee] = args;
1426
+ if (!stepId || !assignee) {
1427
+ console.log(' Usage: /assign <step_id> <assignee_did>');
1428
+ break;
1429
+ }
1430
+ const chains = ctx.coc.getActiveChains();
1431
+ const chain = chains.find(c => c.steps.find(s => s.step_id === stepId));
1432
+ if (!chain) {
1433
+ console.log(' Step not found.');
1434
+ break;
1435
+ }
1436
+ await ctx.coc.assignStep(ctx.roomId, chain.chain_id, stepId, assignee);
1437
+ console.log(` ${green('✓')} Assigned ${stepId} to ${assignee.slice(0, 20)}...`);
1438
+ break;
1439
+ }
1440
+ case '/review': {
1441
+ const [stepId, decision] = args;
1442
+ const notes = args.slice(2).join(' ') || 'Reviewed';
1443
+ if (!stepId || !['approved', 'rejected', 'needs_revision'].includes(decision)) {
1444
+ console.log(' Usage: /review <step_id> <approved|rejected|needs_revision> [notes...]');
1445
+ break;
1446
+ }
1447
+ const chains = ctx.coc.getActiveChains();
1448
+ const chain = chains.find(c => c.steps.find(s => s.step_id === stepId));
1449
+ if (!chain) {
1450
+ console.log(' Step not found.');
1451
+ break;
1452
+ }
1453
+ await ctx.coc.reviewStep(ctx.roomId, chain.chain_id, stepId, decision, notes);
1454
+ console.log(` ${green('✓')} Reviewed ${stepId} as ${decision}`);
1455
+ break;
1456
+ }
1457
+ case '/cancel': {
1458
+ const chainId = args[0];
1459
+ if (!chainId) {
1460
+ console.log(' Usage: /cancel <chain_id>');
1461
+ break;
1462
+ }
1463
+ await ctx.coc.closeChain(ctx.roomId, chainId, 'cancelled', 'Cancelled by user');
1464
+ console.log(` ${yellow('⚠')} Chain ${chainId.slice(0, 8)}... cancelled`);
1465
+ break;
1466
+ }
1467
+ case '/export': {
1468
+ const chainId = args[0];
1469
+ if (!chainId) {
1470
+ console.log(' Usage: /export <chain_id>');
1471
+ break;
1472
+ }
1473
+ console.log(` ${dim('📦')} Packaging chain ${chainId.slice(0, 8)}... into a .society Capsule...`);
1474
+ try {
1475
+ const outputPath = await ctx.exporter.export(chainId, process.cwd());
1476
+ console.log(` ${bold(green('✅ Export Complete!'))}`);
1477
+ console.log(` ${cyan(outputPath)}`);
1478
+ }
1479
+ catch (err) {
1480
+ console.error(` ${red('Export Error:')} ${err.message}`);
1481
+ }
1482
+ break;
1483
+ }
1484
+ case '/cache': {
1485
+ const stats = ctx.planner.getCacheStats();
1486
+ console.log(` Planner cache: ${stats.size}/${stats.maxSize} entries`);
1487
+ if (ctx.DEBUG && stats.keys.length > 0) {
1488
+ console.log(` Keys: ${stats.keys.slice(0, 10).join(', ')}${stats.keys.length > 10 ? '...' : ''}`);
1489
+ }
1490
+ break;
1491
+ }
1492
+ case '/debug': {
1493
+ console.log(` Debug mode: ${ctx.DEBUG ? 'ON' : 'OFF'}`);
1494
+ break;
1495
+ }
1496
+ case '/quit':
1497
+ process.emit('SIGINT', 'SIGINT');
1498
+ break;
1499
+ default:
1500
+ console.log(` Unknown command: ${cmd}`);
1501
+ console.log(' Available: /peers /rooms /presence /reputation /info /history');
1502
+ console.log(' /summon /template /chains /chain /step /assign /review');
1503
+ console.log(' /cancel /export /cache /debug /quit');
1504
+ console.log(' /mesh-request /mesh-accept /mesh-reject /mesh-revoke /mesh-peerings');
1505
+ console.log(' /mesh-open /mesh-close /mesh-bridges /mesh-stats');
1506
+ }
1507
+ }
1508
+ // ─── ANSI Color Helpers ─────────────────────────────────────────
1509
+ function bold(s) { return `\x1b[1m${s}\x1b[22m`; }
1510
+ function dim(s) { return `\x1b[2m${s}\x1b[22m`; }
1511
+ function cyan(s) { return `\x1b[36m${s}\x1b[39m`; }
1512
+ function green(s) { return `\x1b[32m${s}\x1b[39m`; }
1513
+ function yellow(s) { return `\x1b[33m${s}\x1b[39m`; }
1514
+ function red(s) { return `\x1b[31m${s}\x1b[39m`; }
1515
+ function blue(s) { return `\x1b[34m${s}\x1b[39m`; }
1516
+ // ─── Cloudflared Auto-Install ────────────────────────────────────
1517
+ function isCommandAvailable(cmd) {
1518
+ try {
1519
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
1520
+ return true;
1521
+ }
1522
+ catch {
1523
+ return false;
1524
+ }
1525
+ }
1526
+ async function installCloudflared() {
1527
+ const os = process.platform;
1528
+ const arch = process.arch;
1529
+ try {
1530
+ if (os === 'darwin') {
1531
+ // macOS — try Homebrew first
1532
+ if (isCommandAvailable('brew')) {
1533
+ console.log(` ${dim(' brew install cloudflare/cloudflare/cloudflared')}`);
1534
+ execSync('brew install cloudflare/cloudflare/cloudflared', { stdio: 'inherit' });
1535
+ return true;
1536
+ }
1537
+ }
1538
+ if (os === 'linux') {
1539
+ // Linux — download binary directly
1540
+ const archMap = {
1541
+ 'x64': 'amd64',
1542
+ 'arm64': 'arm64',
1543
+ 'arm': 'arm',
1544
+ };
1545
+ const cfArch = archMap[arch] || 'amd64';
1546
+ const url = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}`;
1547
+ const dest = '/usr/local/bin/cloudflared';
1548
+ console.log(` ${dim(` Downloading cloudflared for linux-${cfArch}...`)}`);
1549
+ if (isCommandAvailable('curl')) {
1550
+ execSync(`curl -fsSL "${url}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared ${dest}`, { stdio: 'inherit' });
1551
+ }
1552
+ else if (isCommandAvailable('wget')) {
1553
+ execSync(`wget -q "${url}" -O /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared ${dest}`, { stdio: 'inherit' });
1554
+ }
1555
+ else {
1556
+ console.error(red(' Neither curl nor wget found. Cannot download cloudflared.'));
1557
+ return false;
1558
+ }
1559
+ return true;
1560
+ }
1561
+ // Windows or unsupported
1562
+ console.log(yellow(` Auto-install not supported on ${os}. Install manually:`));
1563
+ console.log(dim(' https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/'));
1564
+ return false;
1565
+ }
1566
+ catch (err) {
1567
+ console.error(red(` Failed to install cloudflared: ${err.message}`));
1568
+ console.log(dim(' Install manually: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/'));
1569
+ return false;
1570
+ }
1571
+ }
1572
+ //# sourceMappingURL=index.js.map