fantasy-cli 1.2.6
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +456 -0
- data/bin/gsd +8 -0
- data/bin/gsd-core-darwin-amd64 +0 -0
- data/bin/gsd-core-darwin-arm64 +0 -0
- data/bin/gsd-core-linux-amd64 +0 -0
- data/bin/gsd-core-linux-arm64 +0 -0
- data/bin/gsd-core-windows-amd64.exe +0 -0
- data/bin/gsd-core-windows-arm64.exe +0 -0
- data/bin/gsd-core.exe +0 -0
- data/lib/gsd/agents/coordinator.rb +195 -0
- data/lib/gsd/agents/task_manager.rb +158 -0
- data/lib/gsd/agents/worker.rb +162 -0
- data/lib/gsd/agents.rb +30 -0
- data/lib/gsd/ai/chat.rb +486 -0
- data/lib/gsd/ai/cli.rb +248 -0
- data/lib/gsd/ai/command_parser.rb +97 -0
- data/lib/gsd/ai/commands/base.rb +42 -0
- data/lib/gsd/ai/commands/clear.rb +20 -0
- data/lib/gsd/ai/commands/context.rb +30 -0
- data/lib/gsd/ai/commands/cost.rb +30 -0
- data/lib/gsd/ai/commands/export.rb +42 -0
- data/lib/gsd/ai/commands/help.rb +61 -0
- data/lib/gsd/ai/commands/model.rb +67 -0
- data/lib/gsd/ai/commands/reset.rb +22 -0
- data/lib/gsd/ai/config.rb +256 -0
- data/lib/gsd/ai/context.rb +324 -0
- data/lib/gsd/ai/cost_tracker.rb +361 -0
- data/lib/gsd/ai/git_context.rb +169 -0
- data/lib/gsd/ai/history.rb +384 -0
- data/lib/gsd/ai/providers/anthropic.rb +429 -0
- data/lib/gsd/ai/providers/base.rb +282 -0
- data/lib/gsd/ai/providers/lmstudio.rb +279 -0
- data/lib/gsd/ai/providers/ollama.rb +336 -0
- data/lib/gsd/ai/providers/openai.rb +396 -0
- data/lib/gsd/ai/providers/openrouter.rb +429 -0
- data/lib/gsd/ai/reference_resolver.rb +225 -0
- data/lib/gsd/ai/repl.rb +349 -0
- data/lib/gsd/ai/streaming.rb +438 -0
- data/lib/gsd/ai/ui.rb +429 -0
- data/lib/gsd/buddy/cli.rb +284 -0
- data/lib/gsd/buddy/gacha.rb +148 -0
- data/lib/gsd/buddy/renderer.rb +108 -0
- data/lib/gsd/buddy/species.rb +190 -0
- data/lib/gsd/buddy/stats.rb +156 -0
- data/lib/gsd/buddy.rb +28 -0
- data/lib/gsd/cli.rb +455 -0
- data/lib/gsd/commands.rb +198 -0
- data/lib/gsd/config.rb +183 -0
- data/lib/gsd/error.rb +188 -0
- data/lib/gsd/frontmatter.rb +123 -0
- data/lib/gsd/go/bridge.rb +173 -0
- data/lib/gsd/history.rb +76 -0
- data/lib/gsd/milestone.rb +75 -0
- data/lib/gsd/output.rb +184 -0
- data/lib/gsd/phase.rb +102 -0
- data/lib/gsd/plugins/base.rb +92 -0
- data/lib/gsd/plugins/cli.rb +330 -0
- data/lib/gsd/plugins/config.rb +164 -0
- data/lib/gsd/plugins/hooks.rb +132 -0
- data/lib/gsd/plugins/installer.rb +158 -0
- data/lib/gsd/plugins/loader.rb +122 -0
- data/lib/gsd/plugins/manager.rb +187 -0
- data/lib/gsd/plugins/marketplace.rb +142 -0
- data/lib/gsd/plugins/sandbox.rb +114 -0
- data/lib/gsd/plugins/search.rb +131 -0
- data/lib/gsd/plugins/validator.rb +157 -0
- data/lib/gsd/plugins.rb +48 -0
- data/lib/gsd/profile.rb +127 -0
- data/lib/gsd/research.rb +85 -0
- data/lib/gsd/roadmap.rb +90 -0
- data/lib/gsd/skills/bundled/commit.md +58 -0
- data/lib/gsd/skills/bundled/debug.md +28 -0
- data/lib/gsd/skills/bundled/explain.md +41 -0
- data/lib/gsd/skills/bundled/plan.md +42 -0
- data/lib/gsd/skills/bundled/verify.md +26 -0
- data/lib/gsd/skills/loader.rb +189 -0
- data/lib/gsd/state.rb +102 -0
- data/lib/gsd/template.rb +106 -0
- data/lib/gsd/tools/ask_user_question.rb +179 -0
- data/lib/gsd/tools/base.rb +204 -0
- data/lib/gsd/tools/bash.rb +246 -0
- data/lib/gsd/tools/file_edit.rb +297 -0
- data/lib/gsd/tools/file_read.rb +199 -0
- data/lib/gsd/tools/file_write.rb +153 -0
- data/lib/gsd/tools/glob.rb +202 -0
- data/lib/gsd/tools/grep.rb +227 -0
- data/lib/gsd/tools/gsd_frontmatter.rb +165 -0
- data/lib/gsd/tools/gsd_phase.rb +140 -0
- data/lib/gsd/tools/gsd_roadmap.rb +108 -0
- data/lib/gsd/tools/gsd_state.rb +143 -0
- data/lib/gsd/tools/gsd_template.rb +157 -0
- data/lib/gsd/tools/gsd_verify.rb +159 -0
- data/lib/gsd/tools/registry.rb +103 -0
- data/lib/gsd/tools/task.rb +235 -0
- data/lib/gsd/tools/todo_write.rb +290 -0
- data/lib/gsd/tools/web.rb +260 -0
- data/lib/gsd/tui/app.rb +366 -0
- data/lib/gsd/tui/auto_complete.rb +79 -0
- data/lib/gsd/tui/colors.rb +111 -0
- data/lib/gsd/tui/command_palette.rb +126 -0
- data/lib/gsd/tui/header.rb +38 -0
- data/lib/gsd/tui/input_box.rb +199 -0
- data/lib/gsd/tui/spinner.rb +40 -0
- data/lib/gsd/tui/status_bar.rb +51 -0
- data/lib/gsd/tui.rb +17 -0
- data/lib/gsd/validator.rb +216 -0
- data/lib/gsd/verify.rb +175 -0
- data/lib/gsd/version.rb +5 -0
- data/lib/gsd/workstream.rb +91 -0
- metadata +231 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
|
|
8
|
+
module Gsd
|
|
9
|
+
module AI
|
|
10
|
+
# History Management - Gerencia histórico de conversas
|
|
11
|
+
#
|
|
12
|
+
# Responsável por:
|
|
13
|
+
# - Manter histórico de mensagens
|
|
14
|
+
# - Adicionar mensagens de usuário e assistente
|
|
15
|
+
# - Adicionar resultados de tool calls
|
|
16
|
+
# - Limitar tamanho do histórico
|
|
17
|
+
# - Persistência em disco
|
|
18
|
+
# - Compressão de histórico antigo
|
|
19
|
+
# - Sumarização de conversas antigas
|
|
20
|
+
class History
|
|
21
|
+
attr_reader :messages, :max_messages, :max_tokens
|
|
22
|
+
|
|
23
|
+
# Inicializa o histórico
|
|
24
|
+
#
|
|
25
|
+
# @param max_messages [Integer] Número máximo de mensagens
|
|
26
|
+
# @param max_tokens [Integer] Número máximo de tokens
|
|
27
|
+
# @param persist_dir [String] Diretório para persistência
|
|
28
|
+
def initialize(max_messages: 100, max_tokens: 100_000, persist_dir: nil)
|
|
29
|
+
@messages = []
|
|
30
|
+
@max_messages = max_messages
|
|
31
|
+
@max_tokens = max_tokens
|
|
32
|
+
@persist_dir = persist_dir
|
|
33
|
+
@summaries = [] # Sumarizações de conversas antigas
|
|
34
|
+
|
|
35
|
+
load_from_disk if @persist_dir
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Adiciona mensagem do usuário
|
|
39
|
+
#
|
|
40
|
+
# @param content [String] Conteúdo da mensagem
|
|
41
|
+
# @return [void]
|
|
42
|
+
def add_user_message(content)
|
|
43
|
+
add_message(role: 'user', content: content)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Adiciona mensagem do assistente
|
|
47
|
+
#
|
|
48
|
+
# @param content [String] Conteúdo da mensagem
|
|
49
|
+
# @return [void]
|
|
50
|
+
def add_assistant_message(content)
|
|
51
|
+
add_message(role: 'assistant', content: content)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Adiciona mensagem de sistema
|
|
55
|
+
#
|
|
56
|
+
# @param content [String] Conteúdo da mensagem
|
|
57
|
+
# @return [void]
|
|
58
|
+
def add_system_message(content)
|
|
59
|
+
add_message(role: 'system', content: content)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Adiciona resultados de tool calls
|
|
63
|
+
#
|
|
64
|
+
# @param results [Array] Resultados das tools
|
|
65
|
+
# @return [void]
|
|
66
|
+
def add_tool_results(results)
|
|
67
|
+
content = build_tool_results_content(results)
|
|
68
|
+
add_message(role: 'tool', content: content)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Limpa o histórico
|
|
72
|
+
#
|
|
73
|
+
# @return [void]
|
|
74
|
+
def clear
|
|
75
|
+
@messages = []
|
|
76
|
+
@summaries = []
|
|
77
|
+
save_to_disk if @persist_dir
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Retorna o número de mensagens
|
|
81
|
+
#
|
|
82
|
+
# @return [Integer] Número de mensagens
|
|
83
|
+
def count
|
|
84
|
+
@messages.count
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Converte para formato da API do LLM
|
|
88
|
+
#
|
|
89
|
+
# @param include_summaries [Boolean] Incluir sumarizações antigas
|
|
90
|
+
# @return [Array] Mensagens formatadas
|
|
91
|
+
def to_api_messages(include_summaries: true)
|
|
92
|
+
result = []
|
|
93
|
+
|
|
94
|
+
# Adiciona sumarizações primeiro (se houver)
|
|
95
|
+
if include_summaries && @summaries.any?
|
|
96
|
+
result << {
|
|
97
|
+
role: 'system',
|
|
98
|
+
content: build_summaries_content
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Adiciona mensagens
|
|
103
|
+
result + @messages.map do |msg|
|
|
104
|
+
format_message_for_api(msg)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Trunca histórico se exceder limites
|
|
109
|
+
#
|
|
110
|
+
# @return [void]
|
|
111
|
+
def truncate_if_needed
|
|
112
|
+
# Trunca por número de mensagens
|
|
113
|
+
if @messages.count > @max_messages
|
|
114
|
+
# Move mensagens antigas para summaries antes de truncar
|
|
115
|
+
to_remove = @messages.count - @max_messages
|
|
116
|
+
if to_remove > 0
|
|
117
|
+
summarize_old_messages(to_remove)
|
|
118
|
+
@messages = @messages.last(@max_messages)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Trunca por tokens (estimativa simples)
|
|
123
|
+
while estimate_tokens > @max_tokens && @messages.count > 10
|
|
124
|
+
# Move mensagens antigas para summaries
|
|
125
|
+
to_remove = (@messages.count * 0.2).ceil # Remove 20% por vez
|
|
126
|
+
summarize_old_messages(to_remove)
|
|
127
|
+
@messages = @messages.last(@messages.count - to_remove)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Exporta histórico para JSON
|
|
132
|
+
#
|
|
133
|
+
# @return [String] JSON do histórico
|
|
134
|
+
def to_json
|
|
135
|
+
JSON.pretty_generate({
|
|
136
|
+
messages: @messages,
|
|
137
|
+
summaries: @summaries,
|
|
138
|
+
metadata: {
|
|
139
|
+
max_messages: @max_messages,
|
|
140
|
+
max_tokens: @max_tokens,
|
|
141
|
+
created_at: @created_at,
|
|
142
|
+
updated_at: Time.now.iso8601
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Importa histórico de JSON
|
|
148
|
+
#
|
|
149
|
+
# @param json [String] JSON do histórico
|
|
150
|
+
# @return [void]
|
|
151
|
+
def from_json(json)
|
|
152
|
+
data = JSON.parse(json, symbolize_names: true)
|
|
153
|
+
@messages = data[:messages] || []
|
|
154
|
+
@summaries = data[:summaries] || []
|
|
155
|
+
@created_at = data.dig(:metadata, :created_at)
|
|
156
|
+
save_to_disk if @persist_dir
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Retorna estatísticas do histórico
|
|
160
|
+
#
|
|
161
|
+
# @return [Hash] Estatísticas
|
|
162
|
+
def stats
|
|
163
|
+
{
|
|
164
|
+
total_messages: @messages.count,
|
|
165
|
+
user_messages: @messages.count { |m| m[:role] == 'user' },
|
|
166
|
+
assistant_messages: @messages.count { |m| m[:role] == 'assistant' },
|
|
167
|
+
tool_messages: @messages.count { |m| m[:role] == 'tool' },
|
|
168
|
+
estimated_tokens: estimate_tokens,
|
|
169
|
+
summaries_count: @summaries.count,
|
|
170
|
+
oldest_message: @messages.first&.dig(:timestamp),
|
|
171
|
+
newest_message: @messages.last&.dig(:timestamp)
|
|
172
|
+
}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Persiste histórico em disco
|
|
176
|
+
#
|
|
177
|
+
# @return [void]
|
|
178
|
+
def save_to_disk
|
|
179
|
+
return unless @persist_dir
|
|
180
|
+
|
|
181
|
+
FileUtils.mkdir_p(@persist_dir)
|
|
182
|
+
file = File.join(@persist_dir, 'history.json')
|
|
183
|
+
|
|
184
|
+
# Salva versão comprimida se for grande
|
|
185
|
+
json = to_json
|
|
186
|
+
if json.bytesize > 100_000 # 100KB
|
|
187
|
+
compressed = compress(json)
|
|
188
|
+
File.write(file + '.gz', compressed)
|
|
189
|
+
File.delete(file) if File.exist?(file)
|
|
190
|
+
else
|
|
191
|
+
File.write(file, json)
|
|
192
|
+
File.delete(file + '.gz') if File.exist?(file + '.gz')
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Carrega histórico do disco
|
|
197
|
+
#
|
|
198
|
+
# @return [void]
|
|
199
|
+
def load_from_disk
|
|
200
|
+
return unless @persist_dir
|
|
201
|
+
|
|
202
|
+
file = File.join(@persist_dir, 'history.json')
|
|
203
|
+
compressed_file = file + '.gz'
|
|
204
|
+
|
|
205
|
+
json = nil
|
|
206
|
+
if File.exist?(compressed_file)
|
|
207
|
+
json = decompress(File.read(compressed_file))
|
|
208
|
+
elsif File.exist?(file)
|
|
209
|
+
json = File.read(file)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
from_json(json) if json
|
|
213
|
+
rescue => e
|
|
214
|
+
warn "[History] Erro ao carregar histórico: #{e.message}"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
private
|
|
218
|
+
|
|
219
|
+
# Adiciona mensagem ao histórico
|
|
220
|
+
#
|
|
221
|
+
# @param role [String] Role da mensagem (user, assistant, tool)
|
|
222
|
+
# @param content [String] Conteúdo da mensagem
|
|
223
|
+
# @return [void]
|
|
224
|
+
def add_message(role:, content:)
|
|
225
|
+
@messages << {
|
|
226
|
+
role: role,
|
|
227
|
+
content: content,
|
|
228
|
+
timestamp: Time.now.iso8601
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
truncate_if_needed
|
|
232
|
+
save_to_disk if @persist_dir
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Constrói conteúdo para resultados de tools
|
|
236
|
+
#
|
|
237
|
+
# @param results [Array] Resultados das tools
|
|
238
|
+
# @return [String] Conteúdo formatado
|
|
239
|
+
def build_tool_results_content(results)
|
|
240
|
+
lines = ["## Tool Results\n"]
|
|
241
|
+
|
|
242
|
+
results.each do |result|
|
|
243
|
+
status = result[:success] ? '✅' : '❌'
|
|
244
|
+
lines << "### #{result[:tool_call_id]} #{status}"
|
|
245
|
+
|
|
246
|
+
if result[:success]
|
|
247
|
+
lines << "```json"
|
|
248
|
+
lines << JSON.pretty_generate(result[:result])
|
|
249
|
+
lines << "```"
|
|
250
|
+
else
|
|
251
|
+
lines << "Erro: #{result[:error]}"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
lines << ""
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
lines.join("\n")
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Formata mensagem para API do LLM
|
|
261
|
+
#
|
|
262
|
+
# @param msg [Hash] Mensagem
|
|
263
|
+
# @return [Hash] Mensagem formatada
|
|
264
|
+
def format_message_for_api(msg)
|
|
265
|
+
{
|
|
266
|
+
role: msg[:role],
|
|
267
|
+
content: msg[:content]
|
|
268
|
+
}
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Estima número de tokens (4 chars ≈ 1 token)
|
|
272
|
+
#
|
|
273
|
+
# @return [Integer] Tokens estimados
|
|
274
|
+
def estimate_tokens
|
|
275
|
+
total_chars = @messages.sum { |m| m[:content].to_s.length }
|
|
276
|
+
total_chars / 4
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Sumariza mensagens antigas
|
|
280
|
+
#
|
|
281
|
+
# @param count [Integer] Número de mensagens para sumarizar
|
|
282
|
+
# @return [void]
|
|
283
|
+
def summarize_old_messages(count)
|
|
284
|
+
return if count <= 0 || @messages.empty?
|
|
285
|
+
|
|
286
|
+
messages_to_summarize = @messages.first([count, @messages.count].min)
|
|
287
|
+
|
|
288
|
+
summary = {
|
|
289
|
+
id: SecureRandom.uuid,
|
|
290
|
+
created_at: Time.now.iso8601,
|
|
291
|
+
message_count: messages_to_summarize.count,
|
|
292
|
+
content: build_summary_content(messages_to_summarize)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@summaries << summary
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Constrói conteúdo de sumarização
|
|
299
|
+
#
|
|
300
|
+
# @param messages [Array] Mensagens para sumarizar
|
|
301
|
+
# @return [String] Conteúdo sumarizado
|
|
302
|
+
def build_summary_content(messages)
|
|
303
|
+
user_count = messages.count { |m| m[:role] == 'user' }
|
|
304
|
+
assistant_count = messages.count { |m| m[:role] == 'assistant' }
|
|
305
|
+
tool_count = messages.count { |m| m[:role] == 'tool' }
|
|
306
|
+
|
|
307
|
+
<<~SUMMARY
|
|
308
|
+
## Resumo de Conversa Antiga
|
|
309
|
+
|
|
310
|
+
Esta é uma sumarização de #{messages.count} mensagens antigas para economizar tokens.
|
|
311
|
+
|
|
312
|
+
### Estatísticas
|
|
313
|
+
- Mensagens do usuário: #{user_count}
|
|
314
|
+
- Mensagens do assistente: #{assistant_count}
|
|
315
|
+
- Tool calls: #{tool_count}
|
|
316
|
+
|
|
317
|
+
### Tópicos Discutidos
|
|
318
|
+
#{extract_topics(messages)}
|
|
319
|
+
|
|
320
|
+
### Ações Realizadas
|
|
321
|
+
#{extract_actions(messages)}
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
*Sumarizado em #{Time.now.strftime('%Y-%m-%d %H:%M')}*
|
|
325
|
+
SUMMARY
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Extrai tópicos das mensagens
|
|
329
|
+
#
|
|
330
|
+
# @param messages [Array] Mensagens
|
|
331
|
+
# @return [String] Tópicos extraídos
|
|
332
|
+
def extract_topics(messages)
|
|
333
|
+
# Implementação simples - em produção usaria NLP/LLM
|
|
334
|
+
"- Histórico de conversa (#{messages.count} mensagens)"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Extrai ações das mensagens
|
|
338
|
+
#
|
|
339
|
+
# @param messages [Array] Mensagens
|
|
340
|
+
# @return [String] Ações extraídas
|
|
341
|
+
def extract_actions(messages)
|
|
342
|
+
tool_messages = messages.select { |m| m[:role] == 'tool' }
|
|
343
|
+
if tool_messages.any?
|
|
344
|
+
"- #{tool_messages.count} tool calls executados"
|
|
345
|
+
else
|
|
346
|
+
"- Nenhuma ação específica registrada"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Constrói conteúdo de sumarizações
|
|
351
|
+
#
|
|
352
|
+
# @return [String] Conteúdo das sumarizações
|
|
353
|
+
def build_summaries_content
|
|
354
|
+
return "" if @summaries.empty?
|
|
355
|
+
|
|
356
|
+
lines = ["# Histórico Sumarizado\n"]
|
|
357
|
+
|
|
358
|
+
@summaries.each_with_index do |summary, i|
|
|
359
|
+
lines << "## Sumário #{i + 1}"
|
|
360
|
+
lines << summary[:content]
|
|
361
|
+
lines << ""
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
lines.join("\n")
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Comprime string com Gzip
|
|
368
|
+
#
|
|
369
|
+
# @param data [String] Dados para comprimir
|
|
370
|
+
# @return [String] Dados comprimidos
|
|
371
|
+
def compress(data)
|
|
372
|
+
Zlib::Deflate.deflate(data)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Descomprime string com Gzip
|
|
376
|
+
#
|
|
377
|
+
# @param data [String] Dados comprimidos
|
|
378
|
+
# @return [String] Dados descomprimidos
|
|
379
|
+
def decompress(data)
|
|
380
|
+
Zlib::Inflate.inflate(data)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|