fleetmesh-node 0.2.0__py3-none-any.whl

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 (338) hide show
  1. fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
  2. fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
  3. fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
  4. fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
  5. fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
  6. fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
  7. node/RELEASE +1 -0
  8. node/__init__.py +35 -0
  9. node/app/__init__.py +49 -0
  10. node/app/commands.py +265 -0
  11. node/app/config.py +131 -0
  12. node/app/control.py +670 -0
  13. node/app/core.py +867 -0
  14. node/app/plugins.py +76 -0
  15. node/app/router.py +83 -0
  16. node/app/socket.py +431 -0
  17. node/app/systemd.py +36 -0
  18. node/app/views.py +204 -0
  19. node/channels/__init__.py +378 -0
  20. node/channels/bus.py +331 -0
  21. node/channels/journal.py +46 -0
  22. node/commands/__init__.py +140 -0
  23. node/commands/adapter.py +210 -0
  24. node/commands/base.py +278 -0
  25. node/commands/cluster_cmds.py +183 -0
  26. node/commands/compute_cmds.py +401 -0
  27. node/commands/core.py +474 -0
  28. node/commands/gossip_cmds.py +106 -0
  29. node/commands/nsite_cmds.py +141 -0
  30. node/commands/peering.py +402 -0
  31. node/commands/plugin_cmds.py +135 -0
  32. node/commands/sidecar_cmds.py +120 -0
  33. node/commands/sync.py +388 -0
  34. node/commands/transport_cmds.py +451 -0
  35. node/commands/video_cmds.py +99 -0
  36. node/compartment/__init__.py +124 -0
  37. node/compartment/base.py +868 -0
  38. node/compartment/command.py +458 -0
  39. node/compartment/context.py +163 -0
  40. node/compartment/every.py +108 -0
  41. node/compartment/handler.py +131 -0
  42. node/compartment/lifecycle.py +460 -0
  43. node/compartment/lint.py +388 -0
  44. node/compartment/need.py +158 -0
  45. node/compartment/problem.py +521 -0
  46. node/compartment/refusals.py +120 -0
  47. node/compartment/registry.py +790 -0
  48. node/compartment/schema.py +290 -0
  49. node/compartment/sidecar.py +499 -0
  50. node/compartment/stores.py +662 -0
  51. node/compartment/wire.py +225 -0
  52. node/compartment/worker.py +61 -0
  53. node/config/__init__.py +43 -0
  54. node/config/base.py +116 -0
  55. node/config/change.py +441 -0
  56. node/config/changestore.py +201 -0
  57. node/config/compartments.py +1144 -0
  58. node/config/node.py +318 -0
  59. node/config/parameter.py +563 -0
  60. node/config/protocols.py +274 -0
  61. node/config/records.py +171 -0
  62. node/examples/__init__.py +1 -0
  63. node/examples/demo.py +226 -0
  64. node/examples/follower.py +46 -0
  65. node/interfaces.py +235 -0
  66. node/keystore/__init__.py +27 -0
  67. node/keystore/core.py +103 -0
  68. node/keystore/enclave.py +688 -0
  69. node/meshnode.py +111 -0
  70. node/metrics/__init__.py +396 -0
  71. node/metrics/adapters.py +102 -0
  72. node/metrics/face.py +110 -0
  73. node/planes/__init__.py +21 -0
  74. node/planes/identity/__init__.py +20 -0
  75. node/planes/identity/announce/__init__.py +9 -0
  76. node/planes/identity/announce/compartment.py +447 -0
  77. node/planes/identity/announce/types.py +26 -0
  78. node/planes/identity/discovery/__init__.py +9 -0
  79. node/planes/identity/discovery/compartment.py +522 -0
  80. node/planes/identity/discovery/types.py +13 -0
  81. node/planes/identity/linked/__init__.py +1 -0
  82. node/planes/identity/linked/compartment.py +270 -0
  83. node/planes/identity/linked/types.py +25 -0
  84. node/planes/identity/presence/__init__.py +7 -0
  85. node/planes/identity/presence/compartment.py +409 -0
  86. node/planes/identity/presence/types.py +14 -0
  87. node/planes/operations/__init__.py +13 -0
  88. node/planes/operations/scheduler/__init__.py +9 -0
  89. node/planes/operations/scheduler/compartment.py +353 -0
  90. node/planes/operations/scheduler/types.py +21 -0
  91. node/planes/operations/updates/__init__.py +17 -0
  92. node/planes/operations/updates/core.py +310 -0
  93. node/planes/operations/updates/store.py +79 -0
  94. node/planes/operations/updates/types.py +20 -0
  95. node/planes/peering/__init__.py +28 -0
  96. node/planes/peering/budget/__init__.py +43 -0
  97. node/planes/peering/budget/chain.py +43 -0
  98. node/planes/peering/budget/core.py +537 -0
  99. node/planes/peering/budget/evaluation.py +80 -0
  100. node/planes/peering/budget/meter.py +100 -0
  101. node/planes/peering/budget/receipts.py +439 -0
  102. node/planes/peering/budget/shape.py +101 -0
  103. node/planes/peering/budget/tick.py +110 -0
  104. node/planes/peering/budget/types.py +67 -0
  105. node/planes/peering/budget/windows.py +346 -0
  106. node/planes/peering/capacity/__init__.py +7 -0
  107. node/planes/peering/capacity/compartment.py +342 -0
  108. node/planes/peering/capacity/types.py +24 -0
  109. node/planes/peering/peering/__init__.py +25 -0
  110. node/planes/peering/peering/core.py +464 -0
  111. node/planes/peering/peering/envelope.py +142 -0
  112. node/planes/peering/peering/handshake.py +198 -0
  113. node/planes/peering/peering/shape.py +80 -0
  114. node/planes/peering/peering/types.py +39 -0
  115. node/planes/peering/reporter/__init__.py +42 -0
  116. node/planes/peering/reporter/constants.py +29 -0
  117. node/planes/peering/reporter/core.py +299 -0
  118. node/planes/peering/reporter/finding.py +74 -0
  119. node/planes/peering/reporter/findings.py +171 -0
  120. node/planes/peering/reporter/publish.py +285 -0
  121. node/planes/peering/reporter/reading.py +193 -0
  122. node/planes/peering/reporter/shape.py +86 -0
  123. node/planes/peering/reporter/types.py +50 -0
  124. node/planes/peering/strain/__init__.py +9 -0
  125. node/planes/peering/strain/compartment.py +204 -0
  126. node/planes/peering/strain/types.py +21 -0
  127. node/planes/peering/trust/__init__.py +9 -0
  128. node/planes/peering/trust/compartment.py +239 -0
  129. node/planes/peering/trust/types.py +30 -0
  130. node/planes/services/__init__.py +33 -0
  131. node/planes/services/compute/__init__.py +160 -0
  132. node/planes/services/compute/common.py +134 -0
  133. node/planes/services/compute/compartment.py +681 -0
  134. node/planes/services/compute/executors/__init__.py +121 -0
  135. node/planes/services/compute/executors/agent.py +840 -0
  136. node/planes/services/compute/executors/bacalhau.py +605 -0
  137. node/planes/services/compute/executors/base.py +127 -0
  138. node/planes/services/compute/executors/harness.py +439 -0
  139. node/planes/services/compute/executors/mcp.py +454 -0
  140. node/planes/services/compute/executors/media.py +898 -0
  141. node/planes/services/compute/executors/nondet.py +283 -0
  142. node/planes/services/compute/executors/providers.py +499 -0
  143. node/planes/services/compute/executors/registry.py +140 -0
  144. node/planes/services/compute/executors/wasm.py +244 -0
  145. node/planes/services/compute/executors/zk.py +604 -0
  146. node/planes/services/compute/jobs.py +227 -0
  147. node/planes/services/compute/load.py +180 -0
  148. node/planes/services/compute/market.py +433 -0
  149. node/planes/services/compute/sandbox.py +282 -0
  150. node/planes/services/compute/types.py +84 -0
  151. node/planes/services/compute_wire/__init__.py +58 -0
  152. node/planes/services/compute_wire/compartment.py +256 -0
  153. node/planes/services/compute_wire/messages.py +320 -0
  154. node/planes/services/compute_wire/sealing.py +147 -0
  155. node/planes/services/compute_wire/session.py +572 -0
  156. node/planes/services/compute_wire/types.py +49 -0
  157. node/planes/services/compute_wire/verify.py +331 -0
  158. node/planes/services/dvm/__init__.py +24 -0
  159. node/planes/services/dvm/gateway.py +526 -0
  160. node/planes/services/dvm/protocol.py +35 -0
  161. node/planes/services/dvm/translate.py +15 -0
  162. node/planes/services/dvm/types.py +25 -0
  163. node/planes/services/gateway/__init__.py +40 -0
  164. node/planes/services/gateway/settle.py +279 -0
  165. node/planes/services/gateway/translate.py +644 -0
  166. node/planes/services/gossip/__init__.py +22 -0
  167. node/planes/services/gossip/core.py +929 -0
  168. node/planes/services/gossip/envelope.py +154 -0
  169. node/planes/services/gossip/mesh.py +127 -0
  170. node/planes/services/gossip/types.py +50 -0
  171. node/planes/services/nsite/__init__.py +7 -0
  172. node/planes/services/nsite/client.py +62 -0
  173. node/planes/services/nsite/compartment.py +752 -0
  174. node/planes/services/nsite/types.py +44 -0
  175. node/planes/services/paidfetch/__init__.py +7 -0
  176. node/planes/services/paidfetch/compartment.py +862 -0
  177. node/planes/services/paidfetch/types.py +99 -0
  178. node/planes/services/settle/__init__.py +27 -0
  179. node/planes/services/settle/compartment.py +384 -0
  180. node/planes/services/settle/engine.py +456 -0
  181. node/planes/services/settle/gateways.py +541 -0
  182. node/planes/services/settle/payment.py +512 -0
  183. node/planes/services/settle/store.py +74 -0
  184. node/planes/services/settle/types.py +58 -0
  185. node/planes/services/sidecars/__init__.py +8 -0
  186. node/planes/services/sidecars/compartment.py +461 -0
  187. node/planes/services/sidecars/manifest.py +304 -0
  188. node/planes/services/sidecars/manifests/10-grasp.json +37 -0
  189. node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
  190. node/planes/services/sidecars/manifests/30-magpie.json +28 -0
  191. node/planes/services/sidecars/types.py +43 -0
  192. node/planes/services/video/__init__.py +106 -0
  193. node/planes/services/video/barter.py +407 -0
  194. node/planes/services/video/cli.py +158 -0
  195. node/planes/services/video/compartment.py +932 -0
  196. node/planes/services/video/ingest.py +524 -0
  197. node/planes/services/video/ledger.py +705 -0
  198. node/planes/services/video/packager.py +847 -0
  199. node/planes/services/video/player.py +456 -0
  200. node/planes/services/video/timed_segments.py +124 -0
  201. node/planes/services/video/transit.py +478 -0
  202. node/planes/store/__init__.py +24 -0
  203. node/planes/store/blob_replication/__init__.py +7 -0
  204. node/planes/store/blob_replication/compartment.py +496 -0
  205. node/planes/store/blob_replication/types.py +35 -0
  206. node/planes/store/blob_wire/__init__.py +48 -0
  207. node/planes/store/blob_wire/client.py +57 -0
  208. node/planes/store/blob_wire/core.py +359 -0
  209. node/planes/store/blob_wire/intake.py +74 -0
  210. node/planes/store/blob_wire/messages.py +85 -0
  211. node/planes/store/projected/__init__.py +7 -0
  212. node/planes/store/projected/compartment.py +258 -0
  213. node/planes/store/projected/types.py +29 -0
  214. node/planes/store/retention/__init__.py +7 -0
  215. node/planes/store/retention/compartment.py +214 -0
  216. node/planes/store/retention/types.py +12 -0
  217. node/planes/store/sync/__init__.py +35 -0
  218. node/planes/store/sync/core.py +311 -0
  219. node/planes/store/sync/envelope.py +106 -0
  220. node/planes/store/sync/handover.py +206 -0
  221. node/planes/store/sync/initiator.py +128 -0
  222. node/planes/store/sync/responder.py +114 -0
  223. node/planes/store/sync/rules.py +179 -0
  224. node/planes/store/sync/shape.py +80 -0
  225. node/planes/store/sync/types.py +51 -0
  226. node/planes/transport/__init__.py +23 -0
  227. node/planes/transport/bridge/__init__.py +13 -0
  228. node/planes/transport/bridge/compartment.py +785 -0
  229. node/planes/transport/bridge/ratelimit.py +44 -0
  230. node/planes/transport/bridge/types.py +43 -0
  231. node/planes/transport/lan/__init__.py +9 -0
  232. node/planes/transport/lan/compartment.py +446 -0
  233. node/planes/transport/lan/types.py +25 -0
  234. node/planes/transport/link/__init__.py +7 -0
  235. node/planes/transport/link/compartment.py +257 -0
  236. node/planes/transport/link/types.py +27 -0
  237. node/planes/transport/mediation/__init__.py +45 -0
  238. node/planes/transport/mediation/compartment.py +238 -0
  239. node/planes/transport/mediation/leaf.py +107 -0
  240. node/planes/transport/mediation/messages.py +302 -0
  241. node/planes/transport/mediation/service.py +233 -0
  242. node/planes/transport/mediation/store.py +37 -0
  243. node/planes/transport/mediation/types.py +20 -0
  244. node/planes/transport/p2p/__init__.py +7 -0
  245. node/planes/transport/p2p/compartment.py +349 -0
  246. node/planes/transport/p2p/types.py +22 -0
  247. node/protocols/__init__.py +277 -0
  248. node/protocols/activitypub/__init__.py +56 -0
  249. node/protocols/activitypub/actor.py +230 -0
  250. node/protocols/activitypub/federation.py +490 -0
  251. node/protocols/activitypub/media.py +145 -0
  252. node/protocols/activitypub/models.py +286 -0
  253. node/protocols/activitypub/package.py +271 -0
  254. node/protocols/activitypub/store.py +564 -0
  255. node/protocols/arcade/__init__.py +26 -0
  256. node/protocols/atproto/__init__.py +13 -0
  257. node/protocols/atproto/firehose.py +126 -0
  258. node/protocols/atproto/metering.py +165 -0
  259. node/protocols/atproto/package.py +155 -0
  260. node/protocols/atproto/repo.py +250 -0
  261. node/protocols/atproto/xrpc.py +285 -0
  262. node/protocols/base.py +109 -0
  263. node/protocols/circles/__init__.py +29 -0
  264. node/protocols/dm/__init__.py +50 -0
  265. node/protocols/grasp/__init__.py +22 -0
  266. node/protocols/isotime.py +15 -0
  267. node/protocols/nostr/__init__.py +51 -0
  268. node/protocols/nostr/blobstore.py +383 -0
  269. node/protocols/nostr/blossom.py +627 -0
  270. node/protocols/nostr/client.py +377 -0
  271. node/protocols/nostr/descriptor.py +211 -0
  272. node/protocols/nostr/eventstore.py +304 -0
  273. node/protocols/nostr/package.py +182 -0
  274. node/protocols/nostr/peers.py +109 -0
  275. node/protocols/nostr/relayserver.py +656 -0
  276. node/protocols/p2panda/__init__.py +22 -0
  277. node/protocols/p2panda/identity.py +19 -0
  278. node/protocols/p2panda/package.py +236 -0
  279. node/protocols/p2panda/rung.py +152 -0
  280. node/protocols/p2panda/store.py +273 -0
  281. node/protocols/rhio/__init__.py +33 -0
  282. node/protocols/rhio/package.py +275 -0
  283. node/protocols/rhio/rung.py +136 -0
  284. node/protocols/rhio/store.py +1423 -0
  285. node/protocols/ssb/muxrpc.py +256 -0
  286. node/protocols/ssb/package.py +267 -0
  287. node/protocols/textile/__init__.py +111 -0
  288. node/protocols/textile/transducers.py +259 -0
  289. node/release.py +119 -0
  290. node/storage/__init__.py +88 -0
  291. node/storage/accounts.py +305 -0
  292. node/storage/adapter.py +161 -0
  293. node/storage/base.py +385 -0
  294. node/storage/dialect.py +209 -0
  295. node/storage/grants.py +317 -0
  296. node/storage/locator.py +150 -0
  297. node/storage/postgres.py +294 -0
  298. node/storage/projected.py +953 -0
  299. node/storage/sharding.py +448 -0
  300. node/storage/standing.py +568 -0
  301. node/testnet/__init__.py +9 -0
  302. node/testnet/cluster.py +731 -0
  303. node/testnet/fips_daemon.py +230 -0
  304. node/transport/__init__.py +94 -0
  305. node/transport/const.py +110 -0
  306. node/transport/didcomm_inbound.py +251 -0
  307. node/transport/dnstxt.py +169 -0
  308. node/transport/fips_rung/__init__.py +67 -0
  309. node/transport/fips_rung/core.py +666 -0
  310. node/transport/fips_rung/listener.py +96 -0
  311. node/transport/fips_rung/types.py +12 -0
  312. node/transport/host.py +334 -0
  313. node/transport/iroh_rung/__init__.py +29 -0
  314. node/transport/iroh_rung/core.py +477 -0
  315. node/transport/iroh_rung/quic.py +942 -0
  316. node/transport/iroh_rung/types.py +22 -0
  317. node/transport/ladder.py +145 -0
  318. node/transport/lineproto.py +49 -0
  319. node/transport/mdns.py +303 -0
  320. node/transport/netfetch.py +283 -0
  321. node/transport/relay_rung/__init__.py +23 -0
  322. node/transport/relay_rung/cooldown.py +87 -0
  323. node/transport/relay_rung/core.py +138 -0
  324. node/transport/relay_rung/inbound.py +161 -0
  325. node/transport/relay_rung/ledger.py +104 -0
  326. node/transport/relay_rung/listen.py +121 -0
  327. node/transport/relay_rung/outbound.py +181 -0
  328. node/transport/relay_rung/shape.py +71 -0
  329. node/transport/relay_rung/types.py +58 -0
  330. node/transport/relay_rung/wire.py +63 -0
  331. node/transport/router.py +761 -0
  332. node/transport/rung.py +276 -0
  333. node/transport/ssb_rung.py +166 -0
  334. node/transport/ws.py +502 -0
  335. sidecar/fleetmesh_announce.py +338 -0
  336. sidecar/fleetmesh_p2p.py +373 -0
  337. sidecar/fleetmesh_rhio.py +352 -0
  338. sidecar/fleetmesh_ssb.py +603 -0
@@ -0,0 +1,401 @@
1
+ """Compute market, order book, DVM gateway, and settlement rails."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+
6
+ from node.config.parameter import Parameter
7
+ from node.planes.services.compute.compartment import Compute
8
+ from node.planes.services.dvm.gateway import DVMGateway
9
+ from node.planes.services.settle.compartment import Settlement
10
+ from node.planes.transport.bridge.compartment import RelayBridge
11
+ from .adapter import CommandDispatcher
12
+ from .base import _ask, add_command, emit, fail, load_json_arg
13
+
14
+
15
+ # ---- 1. Relay Bridge --------------------------------------------------------
16
+
17
+ def _bridge_status(r: dict) -> None:
18
+ print('Relay Bridge: %s' % ('enabled' if r.get('enabled') else 'disabled'))
19
+ print(' relays configured: %d, connected: %d' % (r.get('relays_configured', 0), r.get('relays_connected', 0)))
20
+ ing = r.get('ingress', {})
21
+ egr = r.get('egress', {})
22
+ print(' ingress: stored=%d, duplicates=%d, refused=%d' % (ing.get('stored', 0), ing.get('duplicates', 0), ing.get('refused', 0)))
23
+ print(' egress: sent=%d, failed=%d, pending=%d' % (egr.get('sent', 0), egr.get('failed', 0), egr.get('pending', 0)))
24
+ relays = r.get('relays', {})
25
+ if relays:
26
+ print('\n Relays:')
27
+ for url, stats in relays.items():
28
+ print(' %-30s %s (in=%d, out=%d, errs=%d)' % (
29
+ url, 'UP' if stats.get('connected') else 'DOWN',
30
+ stats.get('ingress_count', 0), stats.get('egress_count', 0), stats.get('errors_count', 0)))
31
+
32
+
33
+ def _bridge_cursor(res: list) -> None:
34
+ if not res:
35
+ print('No relay bridge checkpoints recorded.')
36
+ return
37
+ print('Bridge Checkpoints:')
38
+ for c in res:
39
+ print(f" {c.get('relay')}: last_reconciled={c.get('last_reconciled_at', 0)} last_event={c.get('last_event_at', 0)} (events={c.get('events_reconciled', 0)})")
40
+
41
+
42
+ def _cmd_bridge_publish(a: argparse.Namespace) -> int:
43
+ if not a.event:
44
+ fail('publish requires --event <json>')
45
+ targets = [a.relay] if a.relay else None
46
+ res = _ask(a.dir, 'bridge.publish', event=load_json_arg(a.event, '--event'), relays=targets, timeout=15)
47
+ return emit(a, res, lambda res: print(str(res)))
48
+
49
+
50
+ def _cmd_bridge_add_relay(a: argparse.Namespace) -> int:
51
+ if not a.relay:
52
+ fail('add-relay requires --relay <url>')
53
+ res = _ask(a.dir, 'bridge.add_relay', url=a.relay, timeout=10)
54
+ return emit(a, res, lambda res: print(f"added {a.relay}"))
55
+
56
+
57
+ def _cmd_bridge_remove_relay(a: argparse.Namespace) -> int:
58
+ if not a.relay:
59
+ fail('remove-relay requires --relay <url>')
60
+ res = _ask(a.dir, 'bridge.remove_relay', url=a.relay, timeout=10)
61
+ return emit(a, res, lambda res: print(f"removed {a.relay}"))
62
+
63
+
64
+ def _cmd_bridge_sync(a: argparse.Namespace) -> int:
65
+ f = load_json_arg(a.filter, '--filter') if a.filter else None
66
+ res = _ask(a.dir, 'bridge.sync', filter=f, relay=a.relay, timeout=30)
67
+ return emit(a, res, lambda res: print(f"sync complete: {res.get('ingested', 0)} ingested, {res.get('events_found', 0)} found"))
68
+
69
+
70
+ def _cmd_bridge_reconcile(a: argparse.Namespace) -> int:
71
+ f = load_json_arg(a.filter, '--filter') if a.filter else None
72
+ res = _ask(a.dir, 'bridge.reconcile', filter=f, relay=a.relay, timeout=30)
73
+ return emit(a, res, lambda res: print(f"reconcile complete: {res.get('stored', 0)} stored, {res.get('examined', 0)} examined from {res.get('relay')}"))
74
+
75
+
76
+ _bridge_map = {c.key: c for c in RelayBridge.COMMANDS}
77
+ bridge_dispatcher = CommandDispatcher('bridge', default_action='status')
78
+ bridge_dispatcher.add_command('status', _bridge_map['show'], human=_bridge_status, timeout=10)
79
+ bridge_dispatcher.add_command('cursor', _bridge_map['cursor'], human=_bridge_cursor, timeout=10)
80
+ bridge_dispatcher.add_custom('publish', _cmd_bridge_publish, parameters=(
81
+ Parameter(key='relay', name='relay', description='relay URL for bridge action', type=str, default=''),
82
+ Parameter(key='event', name='event', description='event JSON string for publish', type=str, default=''),
83
+ ))
84
+ bridge_dispatcher.add_custom('add-relay', _cmd_bridge_add_relay, parameters=(
85
+ Parameter(key='relay', name='relay', description='relay URL for bridge action', type=str, default=''),
86
+ ))
87
+ bridge_dispatcher.add_custom('remove-relay', _cmd_bridge_remove_relay, parameters=(
88
+ Parameter(key='relay', name='relay', description='relay URL for bridge action', type=str, default=''),
89
+ ))
90
+ bridge_dispatcher.add_custom('sync', _cmd_bridge_sync, parameters=(
91
+ Parameter(key='filter', name='filter', description='filter JSON string for sync or reconcile', type=str, default=''),
92
+ Parameter(key='relay', name='relay', description='relay URL for bridge action', type=str, default=''),
93
+ ))
94
+ bridge_dispatcher.add_custom('reconcile', _cmd_bridge_reconcile, parameters=(
95
+ Parameter(key='filter', name='filter', description='filter JSON string for sync or reconcile', type=str, default=''),
96
+ Parameter(key='relay', name='relay', description='relay URL for bridge action', type=str, default=''),
97
+ ))
98
+
99
+
100
+ def cmd_bridge(a: argparse.Namespace) -> int:
101
+ return bridge_dispatcher.dispatch(a)
102
+
103
+
104
+ # ---- 2. Compute Market & Order Book -----------------------------------------
105
+
106
+ def _compute_show(r: dict) -> None:
107
+ print('Compute Compartment:')
108
+ print(' offers published: %d' % r.get('offers_published', 0))
109
+ print(' active asks: %d' % r.get('active_asks', 0))
110
+ print(' order-book offers: %d' % r.get('indexed_offers', 0))
111
+ print(' order-book asks: %d' % r.get('indexed_asks', 0))
112
+ print(' receipts held: %d' % r.get('receipts_held', 0))
113
+ print(' jobs executed: %d' % r.get('jobs_executed', 0))
114
+ print(' available engines: %s' % ', '.join(r.get('available_engines', [])))
115
+ print(' active sessions: %d' % r.get('active_sessions', 0))
116
+
117
+
118
+ def _compute_book(r: dict) -> None:
119
+ print('Compute Order Book:')
120
+ print(' Offers (%d):' % r.get('offers_count', 0))
121
+ for off in r.get('offers', []):
122
+ execs = ', '.join(f"{e.get('engine')}@{e.get('rate')}{e.get('unit')}" for e in off.get('executors', []))
123
+ print(' - %s... [%s] exp=%s' % (off.get('pubkey', '')[:16], execs, off.get('expiration', 0)))
124
+ print(' Asks (%d):' % r.get('asks_count', 0))
125
+ for ask in r.get('asks', []):
126
+ print(' - %s: %s bid=%s%s exp=%s' % (
127
+ ask.get('ask_id'), ask.get('executor'), ask.get('bid_price'), ask.get('bid_currency'), ask.get('expiration', 0)
128
+ ))
129
+
130
+
131
+ def _compute_models(r: dict) -> None:
132
+ print('Available AI/LLM Models:')
133
+ print(' Default: %s' % r.get('default_model'))
134
+ print(' Endpoint: %s' % r.get('endpoint'))
135
+ print(' Features: %s' % ', '.join(r.get('features', [])))
136
+ print(' Models:')
137
+ for m in r.get('available_models', []):
138
+ print(' - %s' % m)
139
+
140
+
141
+ def _cmd_compute_offer(a: argparse.Namespace) -> int:
142
+ engine = getattr(a, 'engine', None) or 'wasm'
143
+ rate = getattr(a, 'rate', None) or '45'
144
+ r = _ask(a.dir, 'compute.offer', engine=engine, msat_per_mfuel=rate, timeout=10)
145
+ return emit(a, r, lambda r: print('Published Kind 11803 Compute Offer: %s' % r.get('id')))
146
+
147
+
148
+ def _cmd_compute_match(a: argparse.Namespace) -> int:
149
+ ask_id = getattr(a, 'ask', None)
150
+ if not ask_id:
151
+ fail('match requires --ask <ask-id>')
152
+ r = _ask(a.dir, 'compute.match', ask=ask_id, timeout=10)
153
+
154
+ def matches(r: list) -> None:
155
+ print('Matches for Ask %s:' % ask_id)
156
+ for m in r:
157
+ free_str = ' [FREE COMPUTE]' if m.get('is_free') else ''
158
+ print(' - Seller %s... Engine: %s Rate: %s%s (est: %s msat)%s' % (
159
+ m.get('seller_pubkey', '')[:16],
160
+ m.get('executor'),
161
+ m.get('rate'),
162
+ m.get('rate_unit'),
163
+ m.get('estimated_cost_msat'),
164
+ free_str,
165
+ ))
166
+ return emit(a, r, matches)
167
+
168
+
169
+ def _cmd_compute_stream(a: argparse.Namespace) -> int:
170
+ prompt = getattr(a, 'prompt', None)
171
+ if not prompt:
172
+ fail('stream requires --prompt <text>')
173
+ model = getattr(a, 'model', None)
174
+ kwargs = {'prompt': prompt, 'timeout': 30}
175
+ if model:
176
+ kwargs['model'] = model
177
+ r = _ask(a.dir, 'compute.stream', **kwargs)
178
+ return emit(a, r, lambda r: print(r.get('output', '')))
179
+
180
+
181
+ _compute_map = {c.key: c for c in Compute.COMMANDS}
182
+ compute_dispatcher = CommandDispatcher('compute', default_action='status')
183
+ compute_dispatcher.add_command('status', _compute_map['show'], human=_compute_show, timeout=10)
184
+ compute_dispatcher.add_command('show', _compute_map['show'], human=_compute_show, timeout=10)
185
+ compute_dispatcher.add_command('book', _compute_map['book'], human=_compute_book, timeout=10)
186
+ compute_dispatcher.add_command('receipts', _compute_map['receipts'], human=lambda r: print('Held Compute Receipts: %d' % len(r)), timeout=10)
187
+ compute_dispatcher.add_command('models', _compute_map['models'], human=_compute_models, timeout=10)
188
+ compute_dispatcher.add_command('list-models', _compute_map['models'], human=_compute_models, timeout=10)
189
+ compute_dispatcher.add_custom('offer', _cmd_compute_offer, parameters=(
190
+ Parameter(key='engine', name='engine', description='executor engine (wasm, llm, bacalhau, zk-verify-groth16, zkvm)', type=str, default=''),
191
+ Parameter(key='rate', name='rate', description='msat per mfuel rate (0 for free compute)', type=str, default=''),
192
+ ))
193
+ compute_dispatcher.add_custom('match', _cmd_compute_match, parameters=(
194
+ Parameter(key='ask', name='ask', description='ask id or event JSON for matching', type=str, default=''),
195
+ ))
196
+ compute_dispatcher.add_custom('stream', _cmd_compute_stream, parameters=(
197
+ Parameter(key='model', name='model', description='AI/LLM model name', type=str, default=''),
198
+ Parameter(key='prompt', name='prompt', description='prompt text for LLM streaming inference', type=str, default=''),
199
+ ))
200
+
201
+
202
+ def cmd_compute(a: argparse.Namespace) -> int:
203
+ return compute_dispatcher.dispatch(a)
204
+
205
+
206
+ # ---- 3. DVM Gateway ---------------------------------------------------------
207
+
208
+ def _dvm_show(r: dict) -> None:
209
+ print('DVM Gateway: %s' % ('enabled' if r.get('enabled') else 'disabled'))
210
+ print(' jobs received: %d' % r.get('jobs_received', 0))
211
+ print(' jobs succeeded: %d' % r.get('jobs_succeeded', 0))
212
+ print(' jobs failed: %d' % r.get('jobs_failed', 0))
213
+ print(' total fuel: %d' % r.get('total_fuel_used', 0))
214
+ print(' refusals: %d' % r.get('refusals', 0))
215
+
216
+
217
+ def _dvm_result(res: dict) -> None:
218
+ resp = res.get('response')
219
+ fb = res.get('feedback')
220
+ print('DVM Execution Result:')
221
+ if resp:
222
+ print(' Response (Kind %s): %s' % (resp.get('kind'), resp.get('content', '')))
223
+ if fb:
224
+ print(' Feedback: %s' % (fb.get('content', '')))
225
+
226
+
227
+ def _dvm_delegation(res: dict) -> None:
228
+ print('Order Book Delegation:')
229
+ print(' Ask ID: %s' % res.get('ask_id'))
230
+ print(' Matches Found: %d' % res.get('matches_found', 0))
231
+ for cand in res.get('candidates', []):
232
+ free_str = ' [FREE COMPUTE]' if cand.get('is_free') else ''
233
+ print(' - Seller: %s... Rate: %s%s (est: %s msat)%s' % (
234
+ cand.get('seller_pubkey', '')[:16],
235
+ cand.get('rate', 0),
236
+ cand.get('rate_unit', 'msat_per_mfuel'),
237
+ cand.get('estimated_cost_msat', 0),
238
+ free_str,
239
+ ))
240
+
241
+
242
+ def _cmd_dvm_process(a: argparse.Namespace) -> int:
243
+ if not getattr(a, 'request', None):
244
+ fail('process requires --request <nip-90-json>')
245
+ res = _ask(a.dir, 'dvm.process', request=load_json_arg(a.request, '--request'), timeout=30)
246
+ return emit(a, res, _dvm_result)
247
+
248
+
249
+ def _cmd_dvm_delegate(a: argparse.Namespace) -> int:
250
+ if not getattr(a, 'request', None):
251
+ fail('delegate requires --request <nip-90-json>')
252
+ res = _ask(a.dir, 'dvm.delegate', request=load_json_arg(a.request, '--request'), timeout=30)
253
+ return emit(a, res, _dvm_delegation)
254
+
255
+
256
+ _dvm_map = {c.key: c for c in DVMGateway.COMMANDS}
257
+ dvm_dispatcher = CommandDispatcher('dvm', default_action='status')
258
+ dvm_dispatcher.add_command('status', _dvm_map['show'], human=_dvm_show, timeout=10)
259
+ dvm_dispatcher.add_command('show', _dvm_map['show'], human=_dvm_show, timeout=10)
260
+ dvm_dispatcher.add_custom('process', _cmd_dvm_process, parameters=(
261
+ Parameter(key='request', name='request', description='NIP-90 request event JSON for process or delegate', type=str, default=''),
262
+ ))
263
+ dvm_dispatcher.add_custom('delegate', _cmd_dvm_delegate, parameters=(
264
+ Parameter(key='request', name='request', description='NIP-90 request event JSON for process or delegate', type=str, default=''),
265
+ ))
266
+
267
+
268
+ def cmd_dvm(a: argparse.Namespace) -> int:
269
+ return dvm_dispatcher.dispatch(a)
270
+
271
+
272
+ # ---- 4. Settlement Rails ----------------------------------------------------
273
+
274
+ def _settle_show(r: dict) -> None:
275
+ print('Settlement Rails: %s' % ', '.join(r.get('rails', [])))
276
+ escrows = r.get('escrows', [])
277
+ print('Active Escrows: %d' % len(escrows))
278
+ for e in escrows:
279
+ print(' - Ask: %s... Rail: %s Amount: %d msat Status: %s' % (
280
+ e.get('ask_id', '')[:16],
281
+ e.get('rail'),
282
+ e.get('amount_msat', 0),
283
+ e.get('status'),
284
+ ))
285
+
286
+
287
+ def _cmd_settle_create(a: argparse.Namespace) -> int:
288
+ for field in ('ask', 'buyer', 'seller', 'amount', 'hash'):
289
+ if not getattr(a, field, None):
290
+ fail(f'create requires --{field}')
291
+ res = _ask(a.dir, 'settle.create',
292
+ ask=a.ask, buyer=a.buyer, seller=a.seller,
293
+ amount_msat=int(a.amount), hash=a.hash,
294
+ rail=getattr(a, 'rail', 'lightning') or 'lightning',
295
+ timeout=10)
296
+ return emit(a, res, lambda res: print('Escrow Created: %s (%s msat on %s)' % (res.get('ask_id'), res.get('amount_msat'), res.get('rail'))))
297
+
298
+
299
+ def _cmd_settle_hold(a: argparse.Namespace) -> int:
300
+ if not getattr(a, 'ask', None):
301
+ fail('hold requires --ask')
302
+ res = _ask(a.dir, 'settle.hold', ask=a.ask, proof=getattr(a, 'proof', None), timeout=10)
303
+ return emit(a, {'held': res}, lambda _: print('Escrow Held: %s' % res), indent=None)
304
+
305
+
306
+ def _cmd_settle_settle(a: argparse.Namespace) -> int:
307
+ if not getattr(a, 'ask', None) or not getattr(a, 'preimage', None):
308
+ fail('settle requires --ask and --preimage')
309
+ res = _ask(a.dir, 'settle.settle', ask=a.ask, preimage=a.preimage, timeout=10)
310
+ return emit(a, {'settled': res}, lambda _: print('Escrow Settled: %s' % res), indent=None)
311
+
312
+
313
+ def _cmd_settle_cancel(a: argparse.Namespace) -> int:
314
+ if not getattr(a, 'ask', None):
315
+ fail('cancel requires --ask')
316
+ res = _ask(a.dir, 'settle.cancel', ask=a.ask, reason=getattr(a, 'reason', '') or '', timeout=10)
317
+ return emit(a, {'cancelled': res}, lambda _: print('Escrow Cancelled: %s' % res), indent=None)
318
+
319
+
320
+ def _cmd_settle_nwc_pay(a: argparse.Namespace) -> int:
321
+ if not getattr(a, 'hash', None) or not getattr(a, 'bolt11', None):
322
+ fail('nwc-pay requires --hash and --bolt11')
323
+ res = _ask(a.dir, 'settle.nwc_pay', hash=a.hash, bolt11=a.bolt11, timeout=30)
324
+ return emit(a, res, lambda res: print('NWC Payment Dispatched: Preimage %s' % res.get('preimage')))
325
+
326
+
327
+ def _cmd_settle_cashu_swap(a: argparse.Namespace) -> int:
328
+ if not getattr(a, 'hash', None) or not getattr(a, 'outputs', None):
329
+ fail('cashu-swap requires --hash and --outputs (JSON string)')
330
+ outputs = load_json_arg(a.outputs, '--outputs')
331
+ res = _ask(a.dir, 'settle.cashu_swap', hash=a.hash, outputs=outputs, timeout=30)
332
+ return emit(a, res, lambda res: print('Cashu Swap Completed: %d fresh signatures' % len(res.get('signatures', []))))
333
+
334
+
335
+ def _cmd_settle_cashu_melt(a: argparse.Namespace) -> int:
336
+ if not getattr(a, 'hash', None) or not getattr(a, 'bolt11', None):
337
+ fail('cashu-melt requires --hash and --bolt11')
338
+ res = _ask(a.dir, 'settle.cashu_melt', hash=a.hash, bolt11=a.bolt11, timeout=30)
339
+ return emit(a, res, lambda res: print('Cashu Melt Completed: Paid=%s Preimage=%s' % (res.get('paid'), res.get('payment_preimage'))))
340
+
341
+
342
+ _settle_map = {c.key: c for c in Settlement.COMMANDS}
343
+ settle_dispatcher = CommandDispatcher('settle', default_action='status')
344
+ settle_dispatcher.add_command('status', _settle_map['show'], human=_settle_show, timeout=10)
345
+ settle_dispatcher.add_command('show', _settle_map['show'], human=_settle_show, timeout=10)
346
+ settle_dispatcher.add_custom('create', _cmd_settle_create, parameters=(
347
+ Parameter(key='ask', name='ask', description='ask id', type=str, default=''),
348
+ Parameter(key='buyer', name='buyer', description='buyer pubkey', type=str, default=''),
349
+ Parameter(key='seller', name='seller', description='seller pubkey', type=str, default=''),
350
+ Parameter(key='amount', name='amount', description='amount in msats', type=int, default=0),
351
+ Parameter(key='hash', name='hash', description='payment hash (32-byte hex)', type=str, default=''),
352
+ Parameter(key='rail', name='rail', description='settlement rail', type=str, default='lightning', choices=('lightning', 'cashu', 'nwc', 'mock')),
353
+ ))
354
+ settle_dispatcher.add_custom('hold', _cmd_settle_hold, parameters=(
355
+ Parameter(key='ask', name='ask', description='ask id', type=str, default=''),
356
+ Parameter(key='proof', name='proof', description='cashu proof or secret token for hold', type=str, default=''),
357
+ ))
358
+ settle_dispatcher.add_custom('settle', _cmd_settle_settle, parameters=(
359
+ Parameter(key='ask', name='ask', description='ask id', type=str, default=''),
360
+ Parameter(key='preimage', name='preimage', description='payment preimage (32-byte hex)', type=str, default=''),
361
+ ))
362
+ settle_dispatcher.add_custom('cancel', _cmd_settle_cancel, parameters=(
363
+ Parameter(key='ask', name='ask', description='ask id', type=str, default=''),
364
+ Parameter(key='reason', name='reason', description='escrow cancellation reason', type=str, default=''),
365
+ ))
366
+ settle_dispatcher.add_custom('nwc-pay', _cmd_settle_nwc_pay, parameters=(
367
+ Parameter(key='hash', name='hash', description='payment hash (32-byte hex)', type=str, default=''),
368
+ Parameter(key='bolt11', name='bolt11', description='Lightning bolt11 invoice string', type=str, default=''),
369
+ ))
370
+ settle_dispatcher.add_custom('cashu-swap', _cmd_settle_cashu_swap, parameters=(
371
+ Parameter(key='hash', name='hash', description='payment hash (32-byte hex)', type=str, default=''),
372
+ Parameter(key='outputs', name='outputs', description='blinded outputs JSON for Cashu token swap', type=str, default=''),
373
+ ))
374
+ settle_dispatcher.add_custom('cashu-melt', _cmd_settle_cashu_melt, parameters=(
375
+ Parameter(key='hash', name='hash', description='payment hash (32-byte hex)', type=str, default=''),
376
+ Parameter(key='bolt11', name='bolt11', description='Lightning bolt11 invoice string', type=str, default=''),
377
+ ))
378
+
379
+
380
+ def cmd_settle(a: argparse.Namespace) -> int:
381
+ return settle_dispatcher.dispatch(a)
382
+
383
+
384
+ # ---- 5. Provider Registration -----------------------------------------------
385
+
386
+ class ComputeCommandProvider:
387
+ """Registers compute market, order book, DVM, settlement, and bridge commands."""
388
+
389
+ @classmethod
390
+ def register(cls, sub: argparse._SubParsersAction) -> None:
391
+ bg = add_command(sub, 'bridge', cmd_bridge, 'interact with the Universal Nostr Relay Bridge', js=True)
392
+ bridge_dispatcher.register_arguments(bg, action_choices=('status', 'publish', 'add-relay', 'remove-relay', 'sync', 'reconcile', 'cursor'))
393
+
394
+ cp = add_command(sub, 'compute', cmd_compute, 'inspect or interact with the compute market and order book', js=True)
395
+ compute_dispatcher.register_arguments(cp, action_choices=('status', 'show', 'book', 'offer', 'match', 'receipts', 'models', 'stream'))
396
+
397
+ dv = add_command(sub, 'dvm', cmd_dvm, 'interact with the NIP-90 Data Vending Machine Gateway', js=True)
398
+ dvm_dispatcher.register_arguments(dv, action_choices=('status', 'show', 'process', 'delegate'))
399
+
400
+ se = add_command(sub, 'settle', cmd_settle, 'inspect settlement rails, escrows, and execute live payments', js=True)
401
+ settle_dispatcher.register_arguments(se, action_choices=('status', 'show', 'create', 'hold', 'settle', 'cancel', 'nwc-pay', 'cashu-swap', 'cashu-melt'))