require 'optparse'
require 'json'
require 'pathname'
lib = File.expand_path('../lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'proofmode'
module ProofMode
class CLI
def self.start(args = ARGV)
new.run(args)
end
def run(args)
options = {}
parser = create_parser(options)
begin
parser.parse!(args)
rescue OptionParser::InvalidOption => e
puts "Error: #{e.message}"
puts parser
exit 1
end
command = args.shift
case command
when 'generate'
generate_proof(options)
when 'check'
check_files(options)
when 'version'
show_version
when nil
puts parser
exit 1
else
puts "Unknown command: #{command}"
puts parser
exit 1
end
rescue StandardError => e
puts "Error: #{e.message}"
puts e.backtrace if options[:debug]
exit 1
end
private
def create_parser(options)
OptionParser.new do |opts|
opts.banner = "Usage: proofmode [command] [options]"
opts.separator ""
opts.separator "Commands:"
opts.separator " generate Generate cryptographic proof for media files"
opts.separator " check Verify media files"
opts.separator " version Show version information"
opts.separator ""
opts.separator "Options:"
opts.on("-f", "--file FILE", "Input file") do |f|
options[:files] ||= []
options[:files] << f
end
opts.on("-d", "--dir DIRECTORY", "Input directory") do |d|
options[:dirs] ||= []
options[:dirs] << d
end
opts.on("-s", "--storage PATH", "Storage directory (default: ./proofmode)") do |s|
options[:storage] = s
end
opts.on("-e", "--email EMAIL", "Email for PGP key") do |e|
options[:email] = e
end
opts.on("-p", "--passphrase PASS", "Passphrase for PGP key") do |p|
options[:passphrase] = p
end
opts.on("-o", "--output FILE", "Output file for results (JSON)") do |o|
options[:output] = o
end
opts.on("--json", "Output results as JSON") do
options[:json] = true
end
opts.on("--debug", "Show debug information") do
options[:debug] = true
end
opts.on("-h", "--help", "Show this help message") do
puts opts
exit
end
end
end
def generate_proof(options)
storage_path = options[:storage] || "./proofmode"
email = options[:email] || "user@example.com"
passphrase = options[:passphrase] || "default_passphrase"
files = collect_files(options)
if files.empty?
puts "Error: No input files specified"
exit 1
end
generator = ProofMode::Generator.new(
storage_path: storage_path,
email: email,
passphrase: passphrase
)
results = []
files.each do |file|
unless File.exist?(file)
puts "Warning: File not found: #{file}"
next
end
puts "Generating proof for: #{file}" unless options[:json]
begin
hash = generator.generate_from_file(file)
result = { file: file, hash: hash, status: 'success' }
results << result
unless options[:json]
puts " Hash: #{hash}"
puts " Proof saved to: #{File.join(storage_path, hash[0..1], hash[2..3], hash)}"
end
rescue => e
result = { file: file, error: e.message, status: 'error' }
results << result
puts " Error: #{e.message}" unless options[:json]
end
end
if options[:json] || options[:output]
output = JSON.pretty_generate({
command: 'generate',
results: results,
storage_path: storage_path
})
if options[:output]
File.write(options[:output], output)
puts "Results written to: #{options[:output]}" unless options[:json]
else
puts output
end
end
end
def check_files(options)
files = collect_files(options)
if files.empty?
puts "Error: No input files specified"
exit 1
end
checker = ProofMode::Checker.new
begin
result_json = checker.check_files(files)
result = JSON.parse(result_json)
if options[:json]
puts JSON.pretty_generate(result)
elsif options[:output]
File.write(options[:output], JSON.pretty_generate(result))
puts "Results written to: #{options[:output]}"
else
display_check_results(result)
end
rescue => e
puts "Error checking files: #{e.message}"
exit 1
end
end
def display_check_results(result)
puts "\nVerification Results"
puts "===================="
result['files'].each do |file_result|
puts "\nFile: #{file_result['path']}"
if file_result['error']
puts " Error: #{file_result['error']}"
else
ver = file_result['verification_results']
if ver['c2pa']
status = ver['c2pa']['found'] ? '✓ Found' : '✗ Not found'
puts " C2PA: #{status}"
end
if ver['pgp']
status = ver['pgp']['found'] ? '✓ Found' : '✗ Not found'
puts " PGP: #{status}"
end
if ver['ots']
status = ver['ots']['found'] ? '✓ Found' : '✗ Not found'
puts " OpenTimestamps: #{status}"
end
if ver['exif']
status = ver['exif']['found'] ? '✓ Found' : '✗ Not found'
puts " EXIF: #{status}"
if ver['exif']['found'] && ver['exif']['data']
puts " Camera: #{ver['exif']['data']['make']} #{ver['exif']['data']['model']}" if ver['exif']['data']['make']
puts " Date: #{ver['exif']['data']['date_time']}" if ver['exif']['data']['date_time']
end
end
end
end
total = result['files'].length
errors = result['files'].count { |f| f['error'] }
puts "\nSummary: #{total} files checked, #{errors} errors"
end
def show_version
puts "ProofMode Ruby CLI v#{ProofMode::VERSION}"
puts "Using: #{ProofMode.implementation_type}"
end
def collect_files(options)
files = options[:files] || []
if options[:dirs]
options[:dirs].each do |dir|
if Dir.exist?(dir)
Dir.glob(File.join(dir, '*')).each do |path|
files << path if File.file?(path)
end
else
puts "Warning: Directory not found: #{dir}"
end
end
end
files.uniq
end
end
end
ProofMode::CLI.start if __FILE__ == $0