omq-cli 0.3.1 → 0.5.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 887b1bd4a69ddbbcc0d4885c05c277f32285253e7d7163407ace5451ff87ba08
4
- data.tar.gz: 241d25d7b9e9be0a9ea59efb8a1f92c3f9667fafd73087763347a337c84d9299
3
+ metadata.gz: 5f3ca44a806e32560f7cd0d7381f0aecd670e475659c5329853c2596e114dc22
4
+ data.tar.gz: b14444d22ccad4e1209d4aaf7102cf4065450bb9892eca8498cdbd90d865936d
5
5
  SHA512:
6
- metadata.gz: d536665684b7c77cf5a724d057bf534b93dc19e5d0f2f9fa5035d384131353fd930b453b5e6417ca90bfa77a5a304a6759a6ae85de807e402b1ee07e5d78b033
7
- data.tar.gz: 214a3d68093f7ac1e04a2a3b7e0438459ce826907dd0ee5baab2ed86c6bc5f571b26eb332d91383e6935e893f41344f5e656d4286e930022a20ae43a48b61ac0
6
+ metadata.gz: 53ce8979e26384cd35cd0ee6e1cb57d24f10e25c4a27e81063d5c6436d2d109bbf6f1aa9eee362d97bf8e75b82f9780c27482414a70b163b452c9a5349582d4e
7
+ data.tar.gz: 6cfc6975d43a04198d89759c406e3068a72224206a265dc13976db8617ba3df3a2b39629c791f139b5b180db693453c1bbc271da9cd041caa78aaddd3c9f9989
data/CHANGELOG.md CHANGED
@@ -1,9 +1,78 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.2 — 2026-04-07
4
+
5
+ ### Fixed
6
+
7
+ - **Guard async-debug behind `OMQ_DEV`** — the Gemfile still caused the
8
+ openssl conflict on CI even after removing it from the gemspec.
9
+
10
+ ## 0.5.1 — 2026-04-07
11
+
12
+ ### Fixed
13
+
14
+ - **Move async-debug from runtime to dev dependency** — async-debug depends on
15
+ `openssl >= 3.0` which conflicts with Ruby's default openssl gem on CI.
16
+ Now only loaded when `OMQ_DEBUG_URI` is set, with a `LoadError` guard and
17
+ install hint.
18
+
19
+ ## 0.5.0 — 2026-04-07
20
+
21
+ ### Changed
22
+
23
+ - **rbnacl, zstd-ruby, and msgpack are now fixed dependencies** —
24
+ no more runtime detection or conditional test guards.
25
+ - **`--curve-crypto` renamed to `--crypto`** — applies to CURVE and future
26
+ mechanisms (e.g. BLAKE3ZMQ). Env var renamed from `OMQ_CURVE_CRYPTO` to
27
+ `OMQ_CRYPTO`.
28
+ - **CURVE requires system libsodium** — rbnacl is bundled but needs libsodium
29
+ installed (`apt install libsodium-dev` / `brew install libsodium`). nuckle
30
+ (pure Ruby) is available via `--crypto nuckle` but marked as DANGEROUS.
31
+
32
+ ### Removed
33
+
34
+ - **`has_zstd` / `has_msgpack` config fields** — no longer needed since both
35
+ gems are fixed dependencies.
36
+
37
+ ## 0.4.0 — 2026-04-07
38
+
39
+ ### Added
40
+
41
+ - **`--sndbuf` / `--rcvbuf` options** — set `SO_SNDBUF` and `SO_RCVBUF` kernel
42
+ buffer sizes. Accepts plain bytes or suffixed values (`4K`, `1M`).
43
+ - **Pipe FIFO ordering system test** — verifies sequential source batches are
44
+ never interleaved through a pipe.
45
+ - **Pipe producer-first system test** — verifies messages are delivered when
46
+ the producer finishes before the consumer connects.
47
+
48
+ ### Changed
49
+
50
+ - **Message traces moved to monitor events** — `-vvv` traces now use
51
+ `Socket#monitor(verbose: true)` instead of inline `trace_send`/`trace_recv`
52
+ calls, ensuring correct ordering with connection lifecycle events.
53
+
54
+ ### Fixed
55
+
56
+ - **Test helper `make_config`** — added missing `send_hwm`, `recv_hwm`,
57
+ `sndbuf`, `rcvbuf` fields and changed `verbose` default from `false` to `0`.
58
+
3
59
  ## 0.3.1 — 2026-04-07
4
60
 
61
+ ### Added
62
+
63
+ - **`--send-hwm` / `--recv-hwm` options** — set send and receive high water
64
+ marks from the command line (default 1000, 0 = unbounded).
65
+ - **`OMQ_DEBUG` env var** — starts async-debug web UI on
66
+ `https://localhost:5050` (or custom port via `OMQ_DEBUG=PORT`).
67
+ - **Multi-level verbosity** — `-v` prints endpoints, `-vv` logs all
68
+ connection events (connect/disconnect/retry/timeout) via socket monitor,
69
+ `-vvv` also traces first 10 bytes of every sent/received message.
70
+
5
71
  ### Fixed
6
72
 
73
+ - **`omq pipe` slow reconnection** — sequential `peer_connected.wait` calls
74
+ blocked receiving until both PULL and PUSH peers connected in order. Now
75
+ waits concurrently using `Kernel#Barrier`.
7
76
  - **`-i` on recv-only sockets** — `pull -i 0.2` rate-limits receiving to
8
77
  one message every 200 ms using `Async::Loop.quantized`. Works on all
9
78
  recv-only socket types (pull, sub, gather, dish).
data/README.md CHANGED
@@ -352,22 +352,25 @@ omq keygen
352
352
  # OMQ_SERVER_PUBLIC='...'
353
353
  # OMQ_SERVER_SECRET='...'
354
354
 
355
- omq keygen --curve-crypto nuckle # pure Ruby backend
355
+ omq keygen --crypto nuckle # pure Ruby backend (DANGEROUS — not audited)
356
356
  ```
357
357
 
358
358
  Export the vars, then use `--curve-server` (server) or `--curve-server-key` (client).
359
359
 
360
360
  ## CURVE encryption
361
361
 
362
- End-to-end encryption using CurveZMQ. Requires a crypto backend:
363
- - **rbnacl** (recommended) — wraps libsodium, fast and audited. `gem install rbnacl`
364
- - **nuckle** — pure Ruby, no system dependencies, not audited. `gem install nuckle`
362
+ End-to-end encryption using CurveZMQ. Requires system libsodium:
365
363
 
366
- By default, `rbnacl` is used if installed. To use `nuckle` explicitly:
364
+ ```sh
365
+ apt install libsodium-dev # Debian/Ubuntu
366
+ brew install libsodium # macOS
367
+ ```
368
+
369
+ To use nuckle (pure Ruby, DANGEROUS — not audited) instead:
367
370
 
368
371
  ```sh
369
- omq rep -b tcp://:5555 --echo --curve-server --curve-crypto nuckle
370
- # or: OMQ_CURVE_CRYPTO=nuckle omq rep -b tcp://:5555 --echo --curve-server
372
+ omq rep -b tcp://:5555 --echo --curve-server --crypto nuckle
373
+ # or: OMQ_CRYPTO=nuckle omq rep -b tcp://:5555 --echo --curve-server
371
374
  ```
372
375
 
373
376
  ```sh
@@ -25,6 +25,7 @@ module OMQ
25
25
  # @return [void]
26
26
  def call(task)
27
27
  setup_socket
28
+ start_event_monitor if config.verbose >= 2
28
29
  maybe_start_transient_monitor(task)
29
30
  sleep(config.delay) if config.delay && config.recv_only?
30
31
  wait_for_peer if needs_peer_wait?
@@ -63,7 +64,7 @@ module OMQ
63
64
 
64
65
 
65
66
  def attach_endpoints
66
- SocketSetup.attach(@sock, config, verbose: config.verbose)
67
+ SocketSetup.attach(@sock, config, verbose: config.verbose >= 1)
67
68
  end
68
69
 
69
70
 
@@ -424,7 +425,42 @@ module OMQ
424
425
 
425
426
 
426
427
  def log(msg)
427
- $stderr.puts(msg) if config.verbose
428
+ $stderr.write("#{msg}\n") if config.verbose >= 1
429
+ end
430
+
431
+
432
+ # -vv: log connect/disconnect/retry/timeout events via Socket#monitor
433
+ # -vvv: also log message sent/received traces
434
+ def start_event_monitor
435
+ verbose = config.verbose >= 3
436
+ @sock.monitor(verbose: verbose) do |event|
437
+ case event.type
438
+ when :message_sent
439
+ $stderr.write("omq: >> #{msg_preview(event.detail[:parts])}\n")
440
+ when :message_received
441
+ $stderr.write("omq: << #{msg_preview(event.detail[:parts])}\n")
442
+ else
443
+ ep = event.endpoint ? " #{event.endpoint}" : ""
444
+ detail = event.detail ? " #{event.detail}" : ""
445
+ $stderr.write("omq: #{event.type}#{ep}#{detail}\n")
446
+ end
447
+ end
448
+ end
449
+
450
+
451
+ def msg_preview(parts)
452
+ parts.map { |p| preview_bytes(p) }.join(" | ")
453
+ end
454
+
455
+
456
+ def preview_bytes(str)
457
+ bytes = str.b
458
+ preview = bytes[0, 10].gsub(/[^[:print:]]/, ".")
459
+ if bytes.bytesize > 10
460
+ "#{preview}... (#{bytes.bytesize}B)"
461
+ else
462
+ preview
463
+ end
428
464
  end
429
465
  end
430
466
  end
@@ -213,20 +213,24 @@ module OMQ
213
213
  linger: 5,
214
214
  reconnect_ivl: nil,
215
215
  heartbeat_ivl: nil,
216
+ send_hwm: nil,
217
+ recv_hwm: nil,
218
+ sndbuf: nil,
219
+ rcvbuf: nil,
216
220
  conflate: false,
217
221
  compress: false,
218
222
  send_expr: nil,
219
223
  recv_expr: nil,
220
224
  parallel: nil,
221
225
  transient: false,
222
- verbose: false,
226
+ verbose: 0,
223
227
  quiet: false,
224
228
  echo: false,
225
229
  scripts: [],
226
230
  recv_maxsz: nil,
227
231
  curve_server: false,
228
232
  curve_server_key: nil,
229
- curve_crypto: nil,
233
+ crypto: nil,
230
234
  }.freeze
231
235
 
232
236
 
@@ -244,12 +248,9 @@ module OMQ
244
248
  end
245
249
 
246
250
 
247
- # Validates that required gems are available.
251
+ # Validates option combinations that depend on socket type.
248
252
  #
249
253
  def self.validate_gems!(config)
250
- abort "--msgpack requires the msgpack gem" if config.format == :msgpack && !config.has_msgpack
251
- abort "--compress requires the zstd-ruby gem" if config.compress && !config.has_zstd
252
-
253
254
  if config.recv_only? && (config.data || config.file)
254
255
  abort "--data/--file not valid for #{config.type_name} (receive-only)"
255
256
  end
@@ -336,6 +337,10 @@ module OMQ
336
337
  }
337
338
  o.on("--heartbeat-ivl SECS", Float, "ZMTP heartbeat interval (detects dead peers)") { |v| opts[:heartbeat_ivl] = v }
338
339
  o.on("--recv-maxsz COUNT", Integer, "Max inbound message size in bytes (larger messages dropped)") { |v| opts[:recv_maxsz] = v }
340
+ o.on("--send-hwm N", Integer, "Send high water mark (default 1000, 0=unbounded)") { |v| opts[:send_hwm] = v }
341
+ o.on("--recv-hwm N", Integer, "Recv high water mark (default 1000, 0=unbounded)") { |v| opts[:recv_hwm] = v }
342
+ o.on("--sndbuf N", "SO_SNDBUF kernel buffer size (e.g. 4K, 1M)") { |v| opts[:sndbuf] = parse_byte_size(v) }
343
+ o.on("--rcvbuf N", "SO_RCVBUF kernel buffer size (e.g. 4K, 1M)") { |v| opts[:rcvbuf] = parse_byte_size(v) }
339
344
 
340
345
  o.separator "\nDelivery:"
341
346
  o.on("--conflate", "Keep only last message per subscriber (PUB/RADIO)") { opts[:conflate] = true }
@@ -355,15 +360,16 @@ module OMQ
355
360
  opts[:parallel] = v || Etc.nprocessors
356
361
  }
357
362
 
358
- o.separator "\nCURVE encryption (requires rbnacl or nuckle gem):"
363
+ o.separator "\nCURVE encryption (requires system libsodium):"
359
364
  o.on("--curve-server", "Enable CURVE as server (generates keypair)") { opts[:curve_server] = true }
360
365
  o.on("--curve-server-key KEY", "Enable CURVE as client (server's Z85 public key)") { |v| opts[:curve_server_key] = v }
361
- o.on("--curve-crypto BACKEND", "Crypto backend: rbnacl (default if installed) or nuckle") { |v| opts[:curve_crypto] = v }
366
+ o.on("--crypto BACKEND", "Crypto backend: rbnacl (default) or nuckle (pure Ruby, DANGEROUS)") { |v| opts[:crypto] = v }
367
+ o.separator " Install libsodium: apt install libsodium-dev / brew install libsodium"
362
368
  o.separator " Env vars: OMQ_SERVER_KEY (client), OMQ_SERVER_PUBLIC + OMQ_SERVER_SECRET (server)"
363
- o.separator " OMQ_CURVE_CRYPTO (backend: rbnacl or nuckle)"
369
+ o.separator " OMQ_CRYPTO (backend: rbnacl or nuckle)"
364
370
 
365
371
  o.separator "\nOther:"
366
- o.on("-v", "--verbose", "Print connection events to stderr") { opts[:verbose] = true }
372
+ o.on("-v", "--verbose", "Verbosity: -v endpoints, -vv events, -vvv messages") { opts[:verbose] += 1 }
367
373
  o.on("-q", "--quiet", "Suppress message output") { opts[:quiet] = true }
368
374
  o.on( "--transient", "Exit when all peers disconnect") { opts[:transient] = true }
369
375
  o.on("-V", "--version") {
@@ -413,6 +419,25 @@ module OMQ
413
419
  end
414
420
 
415
421
 
422
+ # Parses a byte size string with optional K/M suffix.
423
+ #
424
+ # @param str [String] e.g. "4096", "4K", "1M"
425
+ # @return [Integer] size in bytes
426
+ #
427
+ def parse_byte_size(str)
428
+ case str
429
+ when /\A(\d+)[kK]\z/
430
+ $1.to_i * 1024
431
+ when /\A(\d+)[mM]\z/
432
+ $1.to_i * 1024 * 1024
433
+ when /\A\d+\z/
434
+ str.to_i
435
+ else
436
+ abort "invalid byte size: #{str} (use e.g. 4096, 4K, 1M)"
437
+ end
438
+ end
439
+
440
+
416
441
  # Validates option combinations, aborting on invalid combos.
417
442
  #
418
443
  # @param opts [Hash] parsed options from {#parse}
@@ -464,6 +489,14 @@ module OMQ
464
489
  (opts[:connects] + opts[:binds]).each do |url|
465
490
  abort "inproc not supported, use tcp:// or ipc://" if url.include?("inproc://")
466
491
  end
492
+
493
+ all_urls = if type_name == "pipe"
494
+ (opts[:in_endpoints] + opts[:out_endpoints] + opts[:endpoints]).map(&:url)
495
+ else
496
+ opts[:connects] + opts[:binds]
497
+ end
498
+ dups = all_urls.tally.select { |_, n| n > 1 }.keys
499
+ abort "duplicate endpoint: #{dups.first}" if dups.any?
467
500
  end
468
501
  end
469
502
  end
@@ -38,6 +38,10 @@ module OMQ
38
38
  :linger,
39
39
  :reconnect_ivl,
40
40
  :heartbeat_ivl,
41
+ :send_hwm,
42
+ :recv_hwm,
43
+ :sndbuf,
44
+ :rcvbuf,
41
45
  :conflate,
42
46
  :compress,
43
47
  :send_expr,
@@ -51,9 +55,7 @@ module OMQ
51
55
  :recv_maxsz,
52
56
  :curve_server,
53
57
  :curve_server_key,
54
- :curve_crypto,
55
- :has_msgpack,
56
- :has_zstd,
58
+ :crypto,
57
59
  :stdin_is_tty,
58
60
  ) do
59
61
  # @return [Boolean] true if this socket type only sends
data/lib/omq/cli/pipe.rb CHANGED
@@ -42,7 +42,7 @@ module OMQ
42
42
 
43
43
 
44
44
  def attach_endpoints(sock, endpoints)
45
- SocketSetup.attach_endpoints(sock, endpoints)
45
+ SocketSetup.attach_endpoints(sock, endpoints, verbose: config.verbose >= 1)
46
46
  end
47
47
 
48
48
 
@@ -58,9 +58,12 @@ module OMQ
58
58
  )
59
59
  compile_expr
60
60
  @sock = @pull # for eval instance_exec
61
+ start_event_monitors if config.verbose >= 2
61
62
  with_timeout(config.timeout) do
62
- @push.peer_connected.wait
63
- @pull.peer_connected.wait
63
+ Barrier do |barrier|
64
+ barrier.async(annotation: "wait push peer") { @push.peer_connected.wait }
65
+ barrier.async(annotation: "wait pull peer") { @pull.peer_connected.wait }
66
+ end
64
67
  end
65
68
  setup_sequential_transient(task)
66
69
  @sock.instance_exec(&@recv_begin_proc) if @recv_begin_proc
@@ -72,17 +75,21 @@ module OMQ
72
75
  end
73
76
 
74
77
 
75
- def apply_socket_intervals(sock)
78
+ def apply_socket_options(sock)
76
79
  sock.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
77
80
  sock.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
81
+ sock.send_hwm = config.send_hwm if config.send_hwm
82
+ sock.recv_hwm = config.recv_hwm if config.recv_hwm
83
+ sock.sndbuf = config.sndbuf if config.sndbuf
84
+ sock.rcvbuf = config.rcvbuf if config.rcvbuf
78
85
  end
79
86
 
80
87
 
81
88
  def build_pull_push(pull_opts, push_opts, in_eps, out_eps)
82
89
  pull = OMQ::PULL.new(**pull_opts)
83
90
  push = OMQ::PUSH.new(**push_opts)
84
- apply_socket_intervals(pull)
85
- apply_socket_intervals(push)
91
+ apply_socket_options(pull)
92
+ apply_socket_options(push)
86
93
  attach_endpoints(pull, in_eps)
87
94
  attach_endpoints(push, out_eps)
88
95
  [pull, push]
@@ -107,7 +114,10 @@ module OMQ
107
114
  break if parts.nil?
108
115
  parts = @fmt.decompress(parts)
109
116
  parts = eval_recv_expr(parts)
110
- @push.send(@fmt.compress(parts)) if parts && !parts.empty?
117
+ if parts && !parts.empty?
118
+ out = @fmt.compress(parts)
119
+ @push.send(out)
120
+ end
111
121
  i += 1
112
122
  break if n && n > 0 && i >= n
113
123
  end
@@ -143,9 +153,11 @@ module OMQ
143
153
 
144
154
  def wait_for_pairs(pairs)
145
155
  with_timeout(config.timeout) do
146
- pairs.each do |pull, push|
147
- push.peer_connected.wait
148
- pull.peer_connected.wait
156
+ Barrier do |barrier|
157
+ pairs.each do |pull, push|
158
+ barrier.async(annotation: "wait push peer") { push.peer_connected.wait }
159
+ barrier.async(annotation: "wait pull peer") { pull.peer_connected.wait }
160
+ end
149
161
  end
150
162
  end
151
163
  end
@@ -245,7 +257,7 @@ module OMQ
245
257
  workers.each do |w|
246
258
  w.value
247
259
  rescue Ractor::RemoteError => e
248
- $stderr.puts "omq: Ractor error: #{e.cause&.message || e.message}"
260
+ $stderr.write("omq: Ractor error: #{e.cause&.message || e.message}\n")
249
261
  end
250
262
  end
251
263
 
@@ -277,7 +289,35 @@ module OMQ
277
289
 
278
290
 
279
291
  def log(msg)
280
- $stderr.puts(msg) if config.verbose
292
+ $stderr.write("#{msg}\n") if config.verbose >= 1
293
+ end
294
+
295
+
296
+ def start_event_monitors
297
+ verbose = config.verbose >= 3
298
+ [@pull, @push].each do |sock|
299
+ sock.monitor(verbose: verbose) do |event|
300
+ case event.type
301
+ when :message_sent
302
+ $stderr.write("omq: >> #{msg_preview(event.detail[:parts])}\n")
303
+ when :message_received
304
+ $stderr.write("omq: << #{msg_preview(event.detail[:parts])}\n")
305
+ else
306
+ ep = event.endpoint ? " #{event.endpoint}" : ""
307
+ detail = event.detail ? " #{event.detail}" : ""
308
+ $stderr.write("omq: #{event.type}#{ep}#{detail}\n")
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+
315
+ def msg_preview(parts)
316
+ parts.map { |p|
317
+ bytes = p.b
318
+ preview = bytes[0, 10].gsub(/[^[:print:]]/, ".")
319
+ bytes.bytesize > 10 ? "#{preview}... (#{bytes.bytesize}B)" : preview
320
+ }.join(" | ")
281
321
  end
282
322
  end
283
323
  end
@@ -15,6 +15,10 @@ module OMQ
15
15
  sock.recv_timeout = config.timeout if config.timeout
16
16
  sock.send_timeout = config.timeout if config.timeout
17
17
  sock.max_message_size = config.recv_maxsz if config.recv_maxsz
18
+ sock.send_hwm = config.send_hwm if config.send_hwm
19
+ sock.recv_hwm = config.recv_hwm if config.recv_hwm
20
+ sock.sndbuf = config.sndbuf if config.sndbuf
21
+ sock.rcvbuf = config.rcvbuf if config.rcvbuf
18
22
  sock.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
19
23
  sock.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
20
24
  sock.identity = config.identity if config.identity
@@ -40,8 +44,16 @@ module OMQ
40
44
  # Bind/connect +sock+ from an Array of Endpoint objects.
41
45
  # Used by PipeRunner, which works with structured endpoint lists.
42
46
  #
43
- def self.attach_endpoints(sock, endpoints)
44
- endpoints.each { |ep| ep.bind? ? sock.bind(ep.url) : sock.connect(ep.url) }
47
+ def self.attach_endpoints(sock, endpoints, verbose: false)
48
+ endpoints.each do |ep|
49
+ if ep.bind?
50
+ sock.bind(ep.url)
51
+ $stderr.puts "Bound to #{sock.last_endpoint}" if verbose
52
+ else
53
+ sock.connect(ep.url)
54
+ $stderr.puts "Connecting to #{ep.url}" if verbose
55
+ end
56
+ end
45
57
  end
46
58
 
47
59
 
@@ -66,7 +78,7 @@ module OMQ
66
78
 
67
79
  return unless server_key_z85 || server_mode
68
80
 
69
- crypto = CLI.load_curve_crypto(config.curve_crypto || ENV["OMQ_CURVE_CRYPTO"], verbose: config.verbose)
81
+ crypto = CLI.load_curve_crypto(config.crypto || ENV["OMQ_CRYPTO"], verbose: config.verbose >= 1)
70
82
  require "protocol/zmtp/mechanism/curve"
71
83
 
72
84
  if server_key_z85
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OMQ
4
4
  module CLI
5
- VERSION = "0.3.1"
5
+ VERSION = "0.5.2"
6
6
  end
7
7
  end
data/lib/omq/cli.rb CHANGED
@@ -122,12 +122,12 @@ module OMQ
122
122
  verbose = false
123
123
  while (arg = argv.shift)
124
124
  case arg
125
- when "--curve-crypto"
125
+ when "--crypto"
126
126
  crypto_name = argv.shift
127
127
  when "-v", "--verbose"
128
128
  verbose = true
129
129
  when "-h", "--help"
130
- puts "Usage: omq keygen [--curve-crypto rbnacl|nuckle] [-v]\n\n" \
130
+ puts "Usage: omq keygen [--crypto rbnacl|nuckle] [-v]\n\n" \
131
131
  "Generates a CURVE keypair for persistent server identity.\n" \
132
132
  "Output: Z85-encoded env vars for use with --curve-server."
133
133
  exit
@@ -135,7 +135,7 @@ module OMQ
135
135
  abort "omq keygen: unknown option: #{arg}"
136
136
  end
137
137
  end
138
- crypto_name ||= ENV["OMQ_CURVE_CRYPTO"]
138
+ crypto_name ||= ENV["OMQ_CRYPTO"]
139
139
 
140
140
  crypto = load_curve_crypto(crypto_name, verbose: verbose)
141
141
  require "protocol/zmtp/mechanism/curve"
@@ -166,11 +166,11 @@ module OMQ
166
166
  require "rbnacl"
167
167
  RbNaCl
168
168
  rescue LoadError
169
- abort "CURVE requires a crypto backend. Install rbnacl (recommended):\n" \
170
- " gem install rbnacl # requires system libsodium\n" \
171
- "Or use pure Ruby (not audited):\n" \
172
- " --curve-crypto nuckle\n" \
173
- " # or: OMQ_CURVE_CRYPTO=nuckle"
169
+ abort "CURVE requires libsodium. Install it:\n" \
170
+ " apt install libsodium-dev # Debian/Ubuntu\n" \
171
+ " brew install libsodium # macOS\n" \
172
+ "Or use nuckle (pure Ruby, DANGEROUS — not audited):\n" \
173
+ " --crypto nuckle"
174
174
  end
175
175
  else
176
176
  abort "Unknown CURVE crypto backend: #{name}. Use 'rbnacl' or 'nuckle'."
@@ -204,11 +204,23 @@ module OMQ
204
204
  trap("INT") { Process.exit!(0) }
205
205
  trap("TERM") { Process.exit!(0) }
206
206
 
207
- Console.logger = Console::Logger.new(Console::Output::Null.new) unless config.verbose
207
+ Console.logger = Console::Logger.new(Console::Output::Null.new) unless config.verbose >= 1
208
+
209
+ debug_ep = nil
210
+
211
+ if ENV["OMQ_DEBUG_URI"]
212
+ begin
213
+ require "async/debug"
214
+ debug_ep = Async::HTTP::Endpoint.parse ENV["OMQ_DEBUG_URI"]
215
+ rescue LoadError
216
+ abort "OMQ_DEBUG_URI requires the async-debug gem: gem install async-debug"
217
+ end
218
+ end
208
219
 
209
220
  if config.type_name.nil?
210
221
  Object.include(OMQ) unless Object.include?(OMQ)
211
- Async do
222
+ Async annotation: 'omq' do
223
+ Async::Debug.serve(endpoint: debug_ep) if debug_ep
212
224
  config.scripts.each { |s| load_script(s) }
213
225
  rescue => e
214
226
  $stderr.puts "omq: #{e.message}"
@@ -219,7 +231,8 @@ module OMQ
219
231
 
220
232
  runner_class, socket_sym = RUNNER_MAP.fetch(config.type_name)
221
233
 
222
- Async do |task|
234
+ Async annotation: "omq #{config.type_name}" do |task|
235
+ Async::Debug.serve(endpoint: debug_ep) if debug_ep
223
236
  config.scripts.each { |s| load_script(s) }
224
237
  runner = if socket_sym
225
238
  runner_class.new(config, OMQ.const_get(socket_sym))
@@ -256,18 +269,6 @@ module OMQ
256
269
  opts = CliParser.parse(argv)
257
270
  CliParser.validate!(opts)
258
271
 
259
- opts[:has_msgpack] = begin
260
- require "msgpack"
261
- true
262
- rescue LoadError
263
- false
264
- end
265
- opts[:has_zstd] = begin
266
- require "zstd-ruby"
267
- true
268
- rescue LoadError
269
- false
270
- end
271
272
  opts[:stdin_is_tty] = $stdin.tty?
272
273
 
273
274
  Ractor.make_shareable(Config.new(**opts))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omq-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.14'
18
+ version: '0.15'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.14'
25
+ version: '0.15'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: omq-ractor
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +107,48 @@ dependencies:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0.1'
110
+ - !ruby/object:Gem::Dependency
111
+ name: msgpack
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: rbnacl
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '7.0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '7.0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: zstd-ruby
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
110
152
  description: Command-line tool for sending and receiving ZeroMQ messages on any socket
111
153
  type (REQ/REP, PUB/SUB, PUSH/PULL, DEALER/ROUTER, and all draft types). Supports
112
154
  Ruby eval (-e/-E), script handlers (-r), pipe virtual socket with Ractor parallelism,