pwn 0.5.758 → 0.5.759
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 +4 -4
- data/Gemfile +1 -1
- data/lib/pwn/ai/agent/loop.rb +29 -14
- data/lib/pwn/plugins/repl/ai_console.rb +6 -2
- data/lib/pwn/plugins/repl/ai_swarm.rb +4 -0
- data/lib/pwn/plugins/repl.rb +19 -3
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/loop_spec.rb +13 -16
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +13 -0
- data/third_party/pwn_rdoc.jsonl +5 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ef7228011e10bd0573cab148f192e8ef963608a002d4e3d833fcd2e380fd973
|
|
4
|
+
data.tar.gz: 8f238e16e7d0d521deb4b4abdad45fed2d15fb82d92bcc8448b0877bc23b8783
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6eb621a41250c5332e00f941c8b6b7444f796fcb30c009d774d7a0814f3c268809152275f2c63f7f7076450c75bbe662c0248ec6b21abb9730b44b097fdb139
|
|
7
|
+
data.tar.gz: 8ea184fdfa2018bc59f00aef8585d190907236a3d71df302c21db3eb025813111cd5a7bb289513c5a7f51a816f1c7e094f5b6c0bf420ff50d9a7a27a11ffbc65
|
data/Gemfile
CHANGED
data/lib/pwn/ai/agent/loop.rb
CHANGED
|
@@ -1923,12 +1923,16 @@ module PWN
|
|
|
1923
1923
|
|
|
1924
1924
|
content = msg[:content]
|
|
1925
1925
|
tool_calls = Array(msg[:tool_calls])
|
|
1926
|
+
promoted = false
|
|
1926
1927
|
# Local/thinking models (Ollama Qwen3, DeepSeek-R1, etc.) sometimes
|
|
1927
1928
|
# return only :thinking with empty :content and no tool_calls. Promote
|
|
1928
1929
|
# thinking so the agent does not print a blank final answer.
|
|
1929
1930
|
if content.to_s.strip.empty? && tool_calls.empty?
|
|
1930
1931
|
thinking = msg[:thinking].to_s
|
|
1931
|
-
|
|
1932
|
+
unless thinking.strip.empty?
|
|
1933
|
+
content = thinking
|
|
1934
|
+
promoted = true
|
|
1935
|
+
end
|
|
1932
1936
|
end
|
|
1933
1937
|
|
|
1934
1938
|
out = {
|
|
@@ -1953,6 +1957,7 @@ module PWN
|
|
|
1953
1957
|
out[:_native_content] = msg[:_native_content] if msg[:_native_content]
|
|
1954
1958
|
thinking = msg[:thinking] || msg[:reasoning_content]
|
|
1955
1959
|
out[:thinking] = thinking if thinking.to_s.strip != ''
|
|
1960
|
+
out[:_thinking_promoted] = true if promoted
|
|
1956
1961
|
# Local/abliterated models sometimes emit shell(...) as plain content
|
|
1957
1962
|
# with empty tool_calls. Coerce registered call-shaped text into
|
|
1958
1963
|
# structured tool_calls so Loop dispatches instead of FINAL-answering.
|
|
@@ -2297,6 +2302,17 @@ module PWN
|
|
|
2297
2302
|
nil
|
|
2298
2303
|
end
|
|
2299
2304
|
|
|
2305
|
+
private_class_method def self.emit_thinking!(opts = {})
|
|
2306
|
+
text = opts[:thinking].to_s.strip
|
|
2307
|
+
Thread.current[:pwn_last_thinking] = text
|
|
2308
|
+
Thread.current[:pwn_thinking_promoted] = opts[:promoted] ? true : false
|
|
2309
|
+
return if text.empty?
|
|
2310
|
+
|
|
2311
|
+
opts[:on_tool]&.call('thinking', text, '')
|
|
2312
|
+
rescue StandardError
|
|
2313
|
+
nil
|
|
2314
|
+
end
|
|
2315
|
+
|
|
2300
2316
|
# Pre-dispatch: one high-level brief for the whole upcoming tool
|
|
2301
2317
|
# collection (pwn-ai → task is one-to-many with pwn-ai → <tool>).
|
|
2302
2318
|
# Prefer opts[:tools] = [{name:, args:}, ...]; falls back to single name.
|
|
@@ -2304,18 +2320,13 @@ module PWN
|
|
|
2304
2320
|
state = opts[:state]
|
|
2305
2321
|
return nil unless state && defined?(TaskSummarizer) && TaskSummarizer.enabled?
|
|
2306
2322
|
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
tools: opts[:tools]
|
|
2315
|
-
)
|
|
2316
|
-
else
|
|
2317
|
-
thinking
|
|
2318
|
-
end
|
|
2323
|
+
line = TaskSummarizer.about_to(
|
|
2324
|
+
name: opts[:name],
|
|
2325
|
+
args: opts[:args],
|
|
2326
|
+
state: state,
|
|
2327
|
+
request: opts[:request],
|
|
2328
|
+
tools: opts[:tools]
|
|
2329
|
+
)
|
|
2319
2330
|
# about_to returns nil when the brief is a duplicate of the last one
|
|
2320
2331
|
emit_task_summary(line: line, on_tool: opts[:on_tool]) if line
|
|
2321
2332
|
line
|
|
@@ -3409,6 +3420,11 @@ module PWN
|
|
|
3409
3420
|
|
|
3410
3421
|
calls = Array(msg[:tool_calls])
|
|
3411
3422
|
text = msg[:content].to_s
|
|
3423
|
+
emit_thinking!(
|
|
3424
|
+
thinking: msg[:thinking] || msg[:reasoning_content],
|
|
3425
|
+
promoted: msg[:_thinking_promoted],
|
|
3426
|
+
on_tool: on_tool
|
|
3427
|
+
)
|
|
3412
3428
|
|
|
3413
3429
|
# Belt-and-suspenders: plain-text shell(...) / tool forms from local
|
|
3414
3430
|
# models under weak TEMPLATE {{ .Prompt }} become real tool_calls.
|
|
@@ -3525,7 +3541,6 @@ module PWN
|
|
|
3525
3541
|
}
|
|
3526
3542
|
end,
|
|
3527
3543
|
request: request,
|
|
3528
|
-
thinking: msg[:thinking] || msg[:reasoning_content],
|
|
3529
3544
|
on_tool: on_tool
|
|
3530
3545
|
)
|
|
3531
3546
|
|
|
@@ -266,7 +266,7 @@ module PWN
|
|
|
266
266
|
|
|
267
267
|
# Own the terminal, event pump and the lifetime of exactly one request.
|
|
268
268
|
class Console # rubocop:disable Metrics/ClassLength -- cohesive single-owner terminal state machine
|
|
269
|
-
COLORS = { operator: :operator, task: :task, tool: :tool, result: :result, assistant: :assistant, notice: :notice, warning: :warning }.freeze
|
|
269
|
+
COLORS = { operator: :operator, task: :task, thinking: :notice, tool: :tool, result: :result, assistant: :assistant, notice: :notice, warning: :warning }.freeze
|
|
270
270
|
PALETTE = %w[cyan green yellow red white blue magenta black].freeze
|
|
271
271
|
|
|
272
272
|
def initialize(pry:, input:, curses:, getch:)
|
|
@@ -480,6 +480,8 @@ module PWN
|
|
|
480
480
|
on_tool = lambda do |name, args, result|
|
|
481
481
|
if name.to_s == 'task'
|
|
482
482
|
@events << [:task, args.to_s]
|
|
483
|
+
elsif name.to_s == 'thinking'
|
|
484
|
+
@events << [:thinking, args.to_s]
|
|
483
485
|
else
|
|
484
486
|
@events << [:tool, "#{name}\n#{args.is_a?(String) ? args : args.inspect}"]
|
|
485
487
|
@events << [:result, result.to_s]
|
|
@@ -503,7 +505,8 @@ module PWN
|
|
|
503
505
|
enabled_toolsets: PWN::Env.dig(:ai, :agent, :toolsets),
|
|
504
506
|
on_tool: on_tool, steering: control, debug: @pry.config.pwn_ai_debug, debug_tee: @output
|
|
505
507
|
)
|
|
506
|
-
|
|
508
|
+
promoted = Thread.current[:pwn_thinking_promoted] && final.to_s.strip == Thread.current[:pwn_last_thinking].to_s.strip
|
|
509
|
+
@events << [:assistant, final.to_s] unless promoted
|
|
507
510
|
end
|
|
508
511
|
rescue Control::Stopped => e
|
|
509
512
|
@events << [:warning, e.message]
|
|
@@ -1652,6 +1655,7 @@ module PWN
|
|
|
1652
1655
|
puts "USAGE:
|
|
1653
1656
|
# Ctrl+L clears only the session pane, including while busy.
|
|
1654
1657
|
# Ctrl+T toggles the active pane between SESSION and MISSION CONTROL. Ctrl+S opens swarm.
|
|
1658
|
+
# Thinking from a model that returns it is a THINKING row in the session pane, above the answer.
|
|
1655
1659
|
# Up/Down select in menus, recall ~/.pwn/pwn_history in the mission pane, and scroll the session pane when it is active.
|
|
1656
1660
|
# Search: Enter accepts without sending; Esc restores the draft.
|
|
1657
1661
|
# Launch the single-owner curses console; non-terminals return unavailable.
|
|
@@ -279,6 +279,10 @@ module PWN
|
|
|
279
279
|
|
|
280
280
|
def tool_callback(job, opts)
|
|
281
281
|
lambda do |name, args, result|
|
|
282
|
+
if name.to_s == 'thinking'
|
|
283
|
+
opts[:on_event]&.call(:thinking, args.to_s)
|
|
284
|
+
return
|
|
285
|
+
end
|
|
282
286
|
update(job, job[:state], last_tool: name)
|
|
283
287
|
opts[:on_event]&.call(:tool, "#{job[:agent]} [#{job[:id]}] #{name}\n#{args.inspect}")
|
|
284
288
|
opts[:on_event]&.call(:result, result.to_s)
|
data/lib/pwn/plugins/repl.rb
CHANGED
|
@@ -561,6 +561,19 @@ module PWN
|
|
|
561
561
|
mirror.call("#{header}\n#{body_out}")
|
|
562
562
|
next
|
|
563
563
|
end
|
|
564
|
+
if name.to_s == 'thinking'
|
|
565
|
+
body = args.is_a?(String) ? args.to_s : args.inspect
|
|
566
|
+
timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S%z')
|
|
567
|
+
header = "[ #{timestamp} → pwn-ai → thinking ]"
|
|
568
|
+
puts "\e[36m#{header}\e[0m"
|
|
569
|
+
body_out = +''
|
|
570
|
+
body.to_s.each_line do |ln|
|
|
571
|
+
puts "\e[36m #{ln.rstrip}\e[0m"
|
|
572
|
+
body_out << " #{ln.rstrip}\n"
|
|
573
|
+
end
|
|
574
|
+
mirror.call("#{header}\n#{body_out}")
|
|
575
|
+
next
|
|
576
|
+
end
|
|
564
577
|
|
|
565
578
|
argv = args.is_a?(String) ? args.to_s : args.inspect
|
|
566
579
|
timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S%z')
|
|
@@ -592,9 +605,12 @@ module PWN
|
|
|
592
605
|
debug_tee: $stdout
|
|
593
606
|
)
|
|
594
607
|
$stdout.flush
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
608
|
+
promoted = Thread.current[:pwn_thinking_promoted] && final.to_s.strip == Thread.current[:pwn_last_thinking].to_s.strip
|
|
609
|
+
unless promoted
|
|
610
|
+
puts "\n\e[32m#{final}\e[0m\n\n"
|
|
611
|
+
$stdout.flush
|
|
612
|
+
PWN::Plugins::Log.mirror_tui!(msg: "\n#{final}\n\n") if pi.config.pwn_ai_debug && defined?(PWN::Plugins::Log)
|
|
613
|
+
end
|
|
598
614
|
if pi.config.pwn_ai_debug && sess_id && PWN.const_defined?(:Sessions)
|
|
599
615
|
PWN::Plugins::Log.progress(
|
|
600
616
|
msg: "session=#{sess_id}",
|
data/lib/pwn/version.rb
CHANGED
|
@@ -2123,36 +2123,33 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
|
|
|
2123
2123
|
expect(src).to include('byteslice(0, 2_048)')
|
|
2124
2124
|
end
|
|
2125
2125
|
|
|
2126
|
-
describe 'thinking on the
|
|
2127
|
-
it '
|
|
2126
|
+
describe 'thinking on the session pane' do
|
|
2127
|
+
it 'keeps the task brief and emits thinking separately' do
|
|
2128
2128
|
allow(PWN::AI::Agent::TaskSummarizer).to receive(:enabled?).and_return(true)
|
|
2129
2129
|
allow(PWN::AI::Agent::TaskSummarizer).to receive(:about_to).and_return('task 1/3: scan')
|
|
2130
2130
|
seen = []
|
|
2131
2131
|
described_class.send(
|
|
2132
|
-
:
|
|
2133
|
-
state: { plan: ['scan'] },
|
|
2134
|
-
tools: [{ name: 'shell' }],
|
|
2135
|
-
request: 'scan the host',
|
|
2132
|
+
:emit_thinking!,
|
|
2136
2133
|
thinking: 'Need to fingerprint the service before scanning.',
|
|
2137
2134
|
on_tool: ->(name, args, _res) { seen << [name, args] }
|
|
2138
2135
|
)
|
|
2139
|
-
expect(seen).to eq([['task', 'Need to fingerprint the service before scanning.']])
|
|
2140
|
-
expect(PWN::AI::Agent::TaskSummarizer).not_to have_received(:about_to)
|
|
2141
|
-
end
|
|
2142
|
-
|
|
2143
|
-
it 'falls back to the TaskSummarizer brief when thinking is blank' do
|
|
2144
|
-
allow(PWN::AI::Agent::TaskSummarizer).to receive(:enabled?).and_return(true)
|
|
2145
|
-
allow(PWN::AI::Agent::TaskSummarizer).to receive(:about_to).and_return('task 1/3: scan')
|
|
2146
|
-
seen = []
|
|
2147
2136
|
described_class.send(
|
|
2148
2137
|
:task_summary_about_to!,
|
|
2149
2138
|
state: { plan: ['scan'] },
|
|
2150
2139
|
tools: [{ name: 'shell' }],
|
|
2151
2140
|
request: 'scan the host',
|
|
2152
|
-
thinking: ' ',
|
|
2153
2141
|
on_tool: ->(name, args, _res) { seen << [name, args] }
|
|
2154
2142
|
)
|
|
2155
|
-
expect(seen).to eq([
|
|
2143
|
+
expect(seen).to eq([
|
|
2144
|
+
['thinking', 'Need to fingerprint the service before scanning.'],
|
|
2145
|
+
['task', 'task 1/3: scan']
|
|
2146
|
+
])
|
|
2147
|
+
end
|
|
2148
|
+
|
|
2149
|
+
it 'does not emit a blank thinking row' do
|
|
2150
|
+
seen = []
|
|
2151
|
+
described_class.send(:emit_thinking!, thinking: ' ', on_tool: ->(name, args, _res) { seen << [name, args] })
|
|
2152
|
+
expect(seen).to eq([])
|
|
2156
2153
|
end
|
|
2157
2154
|
end
|
|
2158
2155
|
|
|
@@ -389,6 +389,19 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
389
389
|
expect(colors).to eq(operator_label: 4, operator_body: 5, assistant: 5, task: 2, tool: 1, result: 3)
|
|
390
390
|
end
|
|
391
391
|
|
|
392
|
+
it 'shows model thinking in the session pane even when compact' do
|
|
393
|
+
pry = Pry.new
|
|
394
|
+
console = PWN::Plugins::REPL::AIConsole::Console.new(pry: pry, input: StringIO.new, curses: nil, getch: nil)
|
|
395
|
+
console.instance_variable_set(:@verbose, false)
|
|
396
|
+
console.add(:thinking, 'The lemon is yellow because of carotenoids.')
|
|
397
|
+
console.add(:assistant, 'Yellow.')
|
|
398
|
+
text = console.timeline_rows(60).map { |_color, row| row }.join("\n")
|
|
399
|
+
expect(text).to include('THINKING')
|
|
400
|
+
expect(text).to include('The lemon is yellow because of carotenoids.')
|
|
401
|
+
expect(text).to include('ASSISTANT')
|
|
402
|
+
expect(text).to include('Yellow.')
|
|
403
|
+
end
|
|
404
|
+
|
|
392
405
|
it 'uses template colors for every unconfigured role without mutating defaults or overrides' do
|
|
393
406
|
defaults = PWN::Config.env_template.dig(:ai, :tui, :theme)
|
|
394
407
|
[nil, {}, 'invalid'].each do |configured|
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -403,6 +403,7 @@
|
|
|
403
403
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.dispatch_fail_n Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.dispatch_fail_n`: "}]}
|
|
404
404
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.duration_unsatisfied? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.duration_unsatisfied?`: "}]}
|
|
405
405
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.emit_task_summary Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.emit_task_summary`: "}]}
|
|
406
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.emit_thinking! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.emit_thinking!`: "}]}
|
|
406
407
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.engine_transient? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.engine_transient?`: "}]}
|
|
407
408
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.escalate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.escalate`: "}]}
|
|
408
409
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.evidence_blob Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.evidence_blob`: "}]}
|
|
@@ -1206,6 +1207,8 @@
|
|
|
1206
1207
|
{"messages":[{"role":"user","content":"PWN::AI::Ollama.ollama_rest_call Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.ollama_rest_call`: Supported Method Parameters\n\nollama_rest_call(\n\nhttp_method: 'optional HTTP method (defaults to GET)',\nrest_call: 'required rest call path relative to base_uri (e.g. api/chat)',\nparams: 'optional params passed in the URI or HTTP Headers',\nhttp_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',\ntimeout: 'optional timeout in seconds (defaults to 900)',\nspinner: 'optional - display spinner (defaults to false)'\n\n)\n"}]}
|
|
1207
1208
|
{"messages":[{"role":"user","content":"PWN::AI::Ollama.parse_tool_arguments_object Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.parse_tool_arguments_object`: "}]}
|
|
1208
1209
|
{"messages":[{"role":"user","content":"PWN::AI::Ollama.real_config_value? Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.real_config_value?`: "}]}
|
|
1210
|
+
{"messages":[{"role":"user","content":"PWN::AI::Ollama.strict_system_template? Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.strict_system_template?`: "}]}
|
|
1211
|
+
{"messages":[{"role":"user","content":"PWN::AI::Ollama.template_rejects_later_system? Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.template_rejects_later_system?`: "}]}
|
|
1209
1212
|
{"messages":[{"role":"user","content":"PWN::AI::Ollama.visible_from_thinking Usage"},{"role":"assistant","content":"`PWN::AI::Ollama.visible_from_thinking`: "}]}
|
|
1210
1213
|
{"messages":[{"role":"user","content":"PWN::AI::OpenAI.api_endpoint Usage"},{"role":"assistant","content":"`PWN::AI::OpenAI.api_endpoint`: "}]}
|
|
1211
1214
|
{"messages":[{"role":"user","content":"PWN::AI::OpenAI.authors Usage"},{"role":"assistant","content":"`PWN::AI::OpenAI.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
@@ -1258,6 +1261,8 @@
|
|
|
1258
1261
|
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.openwebui_rest_call Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.openwebui_rest_call`: Supported Method Parameters\n\nopenwebui_rest_call(\n\nhttp_method: 'optional HTTP method (defaults to GET)',\nrest_call: 'required rest call path relative to base_uri',\nparams: 'optional params passed in the URI or HTTP Headers',\nhttp_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',\ntimeout: 'optional timeout in seconds (defaults to 900)',\nspinner: 'optional - display spinner (defaults to false)'\n\n)\n"}]}
|
|
1259
1262
|
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.parse_tool_arguments_object Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.parse_tool_arguments_object`: "}]}
|
|
1260
1263
|
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.real_config_value? Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.real_config_value?`: "}]}
|
|
1264
|
+
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.strict_system_template? Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.strict_system_template?`: "}]}
|
|
1265
|
+
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.template_rejects_later_system? Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.template_rejects_later_system?`: "}]}
|
|
1261
1266
|
{"messages":[{"role":"user","content":"PWN::AI::OpenWebUI.visible_from_thinking Usage"},{"role":"assistant","content":"`PWN::AI::OpenWebUI.visible_from_thinking`: "}]}
|
|
1262
1267
|
{"messages":[{"role":"user","content":"PWN::AI::RedTeam.authors Usage"},{"role":"assistant","content":"`PWN::AI::RedTeam.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1263
1268
|
{"messages":[{"role":"user","content":"PWN::AI::RedTeam.help Usage"},{"role":"assistant","content":"`PWN::AI::RedTeam.help`: "}]}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pwn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.759
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
@@ -771,14 +771,14 @@ dependencies:
|
|
|
771
771
|
requirements:
|
|
772
772
|
- - '='
|
|
773
773
|
- !ruby/object:Gem::Version
|
|
774
|
-
version: 1.
|
|
774
|
+
version: 1.7.0
|
|
775
775
|
type: :development
|
|
776
776
|
prerelease: false
|
|
777
777
|
version_requirements: !ruby/object:Gem::Requirement
|
|
778
778
|
requirements:
|
|
779
779
|
- - '='
|
|
780
780
|
- !ruby/object:Gem::Version
|
|
781
|
-
version: 1.
|
|
781
|
+
version: 1.7.0
|
|
782
782
|
- !ruby/object:Gem::Dependency
|
|
783
783
|
name: pry
|
|
784
784
|
requirement: !ruby/object:Gem::Requirement
|