number_station 0.3.0 → 0.5.0

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: 7074376c291002a254430a7f9d9126e4fd1004f77db7d3d063e8ab0cee401332
4
- data.tar.gz: d9262e7c76cf7c457835084e794ddb5d92816009a50cfbb6ec8bd88ba98daf1f
3
+ metadata.gz: d9d58534a90398f402d7fad891415ab81782d96aaa440964703c6d33a1e89f37
4
+ data.tar.gz: 7ca3efa06033aacc95db71c8bd9622f9869f82caa1e535a50f592d72b2f39a5e
5
5
  SHA512:
6
- metadata.gz: e016902c9071daea7cee1fe57a367674638f81397dabc73b3b3ec8d1db77161cf905d0a8c84e51b6b5a95568967b274af00dacc19aa928fe932f6201a0481354
7
- data.tar.gz: 228de828a66a1fbd85878ff33487b85522d4e30a628fd64c0cf9098a3ac26610fcd1d9ceeac918aa46e64e3c15aa890fb7d800af839351d6e067e34f3205b39a
6
+ metadata.gz: 73577af7d0e0e2d6b383398dc3c49dee4d2cfe816d0a6e5d19da5947409108bb79343ddc9fade867209f4dc4973c33d4083ea8098bf691766a2edb81ebd1391b
7
+ data.tar.gz: fff53ecba16b4b3d77674148f2982aa2022cae2e8f5ad0d4995a103562663a4b3bd0aae3337a7be0089c1bfcb152814453f8692456c8ca2af5f932ab5d04198e
data/README.asciidoc CHANGED
@@ -1,11 +1,15 @@
1
1
  == number_station ==
2
+
2
3
  This gem contains utilities to aid in the running of a number station.
3
4
 
4
5
  image:https://badge.fury.io/rb/number_station.svg["Gem Version", link="https://badge.fury.io/rb/number_station"]
5
6
 
6
7
  === Installation ===
7
8
 
8
- gem install number_station
9
+ [source,bash]
10
+ ----
11
+ gem install number_station
12
+ ----
9
13
 
10
14
  === Usage ===
11
15
 
@@ -13,7 +17,10 @@ image:https://badge.fury.io/rb/number_station.svg["Gem Version", link="https://b
13
17
 
14
18
  Create the initial configuration:
15
19
 
16
- number_station create_config
20
+ [source,bash]
21
+ ----
22
+ number_station create_config
23
+ ----
17
24
 
18
25
  This creates `~/number_station/conf.yaml` and copies template files (`intro_message.txt`, `outro_message.txt`, `repeat_message.txt`) to the same directory.
19
26
 
@@ -22,64 +29,147 @@ This creates `~/number_station/conf.yaml` and copies template files (`intro_mess
22
29
  Manage agents using the `agents` subcommand:
23
30
 
24
31
  *Create an agent:*
25
- number_station agents create NAME [--location LOCATION] [--handler HANDLER]
32
+
33
+ [source,bash]
34
+ ----
35
+ number_station agents create NAME [--location LOCATION] [--handler HANDLER]
36
+ ----
26
37
 
27
38
  *Activate/Deactivate:*
28
- number_station agents activate NAME [--start-date DATE]
29
- number_station agents deactivate NAME [--end-date DATE]
39
+
40
+ [source,bash]
41
+ ----
42
+ number_station agents activate NAME [--start-date DATE]
43
+ number_station agents deactivate NAME [--end-date DATE]
44
+ ----
30
45
 
31
46
  *Update handler codeword:*
32
- number_station agents update-handler NAME HANDLER
47
+
48
+ [source,bash]
49
+ ----
50
+ number_station agents update-handler NAME HANDLER
51
+ ----
33
52
 
34
53
  *View agents:*
35
- number_station agents list # Active agents only
36
- number_station agents list-all # All agents with history
37
- number_station agents stats # Detailed statistics
54
+
55
+ [source,bash]
56
+ ----
57
+ number_station agents list # Active agents only
58
+ number_station agents list-all # All agents with history
59
+ number_station agents stats # Detailed statistics
60
+ ----
38
61
 
39
62
  ==== One-Time Pads ====
40
63
 
41
64
  *Create pads:*
42
- number_station pad create [--name AGENT] [--numpads NUM] [--length LENGTH]
65
+
66
+ [source,bash]
67
+ ----
68
+ number_station pad create [--name AGENT] [--numpads NUM] [--length LENGTH]
69
+ ----
43
70
 
44
71
  If `--name` is provided, creates pad in `~/number_station/pads/AGENT/` directory.
45
- Defaults: 500 pads, 500 characters (rounded up to nearest multiple of 5).
72
+ Defaults: 500 pads, 500 characters. Pad length is automatically rounded up to the nearest multiple of 5.
46
73
 
47
74
  *View pad statistics:*
48
- number_station pad stats [--path PATH]
75
+
76
+ [source,bash]
77
+ ----
78
+ number_station pad stats [--path PATH]
79
+ ----
49
80
 
50
81
  Shows pad statistics grouped by agent, including unconsumed pad counts.
51
82
 
83
+ *Convert pad to AsciiDoc:*
84
+
85
+ [source,bash]
86
+ ----
87
+ number_station convert_pad_to_asciidoc --padpath PAD_FILE
88
+ ----
89
+
90
+ Converts a pad file to AsciiDoc format with each pad as a chapter. Output saved as `FILENAME.asciidoc`.
91
+ Pads are formatted in groups of 5 characters with page breaks after every 2nd pad.
92
+
93
+ *Convert pad to LaTeX:*
94
+
95
+ [source,bash]
96
+ ----
97
+ number_station convert_pad_to_latex --padpath PAD_FILE
98
+ ----
99
+
100
+ Converts a pad file to LaTeX format with landscape orientation. Output saved as `FILENAME.tex`.
101
+ Pads are formatted in groups of 5 characters, arranged in a 2x2 grid (4 pads per page), with each pad taking up one quadrant of the page.
102
+
103
+ *Convert to PDF:*
104
+
105
+ [source,bash]
106
+ ----
107
+ number_station convert_to_pdf INPUT_FILE
108
+ ----
109
+
110
+ Converts an AsciiDoc (`.asciidoc` or `.adoc`) or LaTeX (`.tex`) file to PDF.
111
+ +
112
+ For AsciiDoc files: Uses asciidoctor-pdf. Requires asciidoctor-pdf gem to be installed.
113
+ +
114
+ For LaTeX files: Uses pdflatex. Requires a LaTeX distribution (e.g., texlive) to be installed.
115
+ +
116
+ Output saved as `FILENAME.pdf` in the same directory.
117
+
52
118
  ==== Message Encryption/Decryption ====
53
119
 
54
120
  *Encrypt:*
55
- number_station encrypt [MESSAGE] [--file FILE] [--agent AGENT] [--padpath PADPATH] [--numpad NUMPAD]
121
+
122
+ [source,bash]
123
+ ----
124
+ number_station encrypt [MESSAGE] [--file FILE] [--agent AGENT] [--padpath PADPATH] [--numpad NUMPAD]
125
+ ----
56
126
 
57
127
  If `--agent` is provided, searches for oldest pad in agent-specific directory.
58
128
  Only active agents can encrypt messages.
59
129
 
60
130
  *Decrypt:*
61
- number_station decrypt [MESSAGE] [--file FILE] [--padpath PADPATH] [--numpad NUMPAD]
131
+
132
+ [source,bash]
133
+ ----
134
+ number_station decrypt [MESSAGE] [--file FILE] [--padpath PADPATH] [--numpad NUMPAD]
135
+ ----
62
136
 
63
137
  ==== Phonetic Conversion ====
64
138
 
65
139
  *Convert to phonetic:*
66
- number_station convert_to_phonetic FILE [--intro INTRO_FILE] [--outro OUTRO_FILE] [--repeat REPEAT_FILE]
140
+
141
+ [source,bash]
142
+ ----
143
+ number_station convert_to_phonetic FILE [--intro INTRO_FILE] [--outro OUTRO_FILE] [--repeat REPEAT_FILE]
144
+ ----
67
145
 
68
146
  Converts encrypted message to phonetic alphabet. Output saved as `FILENAME_phonetic.txt`.
69
147
  Intro, outro, and repeat messages are included as-is (not converted).
70
148
 
71
149
  *Convert to espeak XML:*
72
- number_station convert_to_espeak PHONETIC_FILE
150
+
151
+ [source,bash]
152
+ ----
153
+ number_station convert_to_espeak PHONETIC_FILE
154
+ ----
73
155
 
74
156
  Generates GLaDOS-style espeak XML file. Output saved as `FILENAME.xml`.
75
157
 
76
158
  *Generate MP3:*
77
- number_station convert_to_mp3 XML_FILE
159
+
160
+ [source,bash]
161
+ ----
162
+ number_station convert_to_mp3 XML_FILE
163
+ ----
78
164
 
79
165
  Converts XML to MP3 using espeak and ffmpeg. Output saved as `FILENAME.mp3`.
80
166
 
81
167
  *Play audio:*
82
- number_station espeak XML_FILE
168
+
169
+ [source,bash]
170
+ ----
171
+ number_station espeak XML_FILE
172
+ ----
83
173
 
84
174
  Plays the XML file using espeak with GLaDOS voice settings.
85
175
 
@@ -22,6 +22,7 @@
22
22
  require 'thor'
23
23
  require 'fileutils'
24
24
  require 'date'
25
+ require 'json'
25
26
 
26
27
  module NumberStation
27
28
  class Pad < Thor
@@ -38,13 +39,16 @@ module NumberStation
38
39
  --name NAME - Agent name to associate with this pad. Creates subdirectory ~/number_station/pads/NAME/ if --path is not provided
39
40
  --path PATH - Directory where the pad file will be created (defaults to current directory, or ~/number_station/pads/NAME/ if --name is provided)
40
41
  --numpads NUM - Number of pads to generate (defaults to 500)
41
- --length LENGTH - Length of each pad in characters (defaults to 500)
42
+ --length LENGTH - Length of each pad in characters (defaults to 500, rounded up to nearest multiple of 5)
42
43
 
43
44
  If no parameters are passed it will generate 500 one time pads in the current
44
45
  directory of size 500 characters.
45
46
 
46
47
  If --name is provided without --path, the pad will be created in ~/number_station/pads/NAME/
47
48
 
49
+ Note: Pad length is automatically rounded up to the nearest multiple of 5 to ensure
50
+ proper formatting in 5-character groups.
51
+
48
52
  Examples:
49
53
  number_station pad create --name Shadow --numpads 1000 --length 1000
50
54
  number_station pad create --name Shadow
@@ -419,7 +423,7 @@ module NumberStation
419
423
 
420
424
  desc "activate NAME", "Activate an agent"
421
425
  long_desc <<-ACTIVATE_AGENT_LONG_DESC
422
- Activate an agent by name. Sets active status to true and optionally sets start_date.
426
+ Activate an agent by name. Sets active status to true, optionally sets start_date, and resets end_date to null.
423
427
 
424
428
  Parameters:
425
429
  NAME - The agent name (required)
@@ -427,6 +431,8 @@ module NumberStation
427
431
  Options:
428
432
  --start-date DATE - Set the start date (defaults to today if not specified)
429
433
 
434
+ When activating an agent, the end_date is automatically reset to null (clearing any previous end date).
435
+
430
436
  Examples:
431
437
  number_station agents activate Shadow
432
438
  number_station agents activate Shadow --start-date "2024-01-15"
@@ -676,6 +682,300 @@ module NumberStation
676
682
  puts "Generated GLaDOS espeak XML: #{output_path}"
677
683
  end
678
684
 
685
+ desc "convert_pad_to_asciidoc --padpath PADPATH", "Convert a pad file to AsciiDoc format"
686
+ long_desc <<-CONVERT_PAD_TO_ASCIIDOC_LONG_DESC
687
+ Convert a one-time pad file to AsciiDoc format with each pad as a chapter.
688
+
689
+ Parameters:
690
+ --padpath PADPATH - Path to pad file (e.g., ~/number_station/pads/Shadow/Shadow-2026-01-12.json)
691
+
692
+ Each pad in the file is converted to groups of 5 characters, separated by spaces.
693
+ Output saved as FILENAME.asciidoc in the same directory.
694
+
695
+ Example:
696
+ number_station convert_pad_to_asciidoc --padpath ~/number_station/pads/Shadow/Shadow-2026-01-12.json
697
+ CONVERT_PAD_TO_ASCIIDOC_LONG_DESC
698
+ option :padpath, type: :string, required: true
699
+ def convert_pad_to_asciidoc
700
+ ensure_config_loaded
701
+
702
+ pad_path = options[:padpath]
703
+ unless File.exist?(pad_path)
704
+ raise Thor::Error, "Pad file not found: #{pad_path}"
705
+ end
706
+
707
+ # Read pad file
708
+ pad_data = JSON.parse(File.read(pad_path))
709
+ pad_id = pad_data["id"]
710
+ pads_hash = pad_data["pads"]
711
+
712
+ if pads_hash.nil? || pads_hash.empty?
713
+ raise Thor::Error, "No pads found in file: #{pad_path}"
714
+ end
715
+
716
+ # Generate output filename: replace .json extension with .asciidoc
717
+ input_basename = File.basename(pad_path, File.extname(pad_path))
718
+ output_filename = "#{input_basename}.asciidoc"
719
+ output_path = File.join(File.dirname(pad_path), output_filename)
720
+
721
+ # Build AsciiDoc content
722
+ asciidoc_content = []
723
+ asciidoc_content << "= One-Time Pad: #{input_basename}"
724
+ asciidoc_content << ""
725
+
726
+ # Process each pad
727
+ pads_hash.sort_by { |k, v| k.to_i }.each do |pad_num, pad_info|
728
+ hex_key = pad_info["key"]
729
+
730
+ # Convert hex string to groups of 5 characters, separated by spaces
731
+ # Remove any existing formatting and group by 5
732
+ cleaned_hex = hex_key.gsub(/[\s\n\r]/, '')
733
+ grouped_hex = cleaned_hex.chars.each_slice(5).map(&:join).join(' ')
734
+
735
+ # Add chapter heading for this pad
736
+ asciidoc_content << "== Pad #{pad_num}"
737
+ asciidoc_content << ""
738
+ asciidoc_content << "[source]"
739
+ asciidoc_content << "----"
740
+ asciidoc_content << grouped_hex
741
+ asciidoc_content << "----"
742
+ asciidoc_content << ""
743
+
744
+ # Add page break after every 2nd pad (after pad 1, 3, 5, etc.)
745
+ if pad_num.to_i % 2 == 1
746
+ asciidoc_content << "<<<"
747
+ asciidoc_content << ""
748
+ end
749
+ end
750
+
751
+ # Write AsciiDoc file
752
+ File.write(output_path, asciidoc_content.join("\n"))
753
+ puts "Generated AsciiDoc file: #{output_path}"
754
+ end
755
+
756
+ desc "convert_pad_to_latex --padpath PADPATH", "Convert a pad file to LaTeX format"
757
+ long_desc <<-CONVERT_PAD_TO_LATEX_LONG_DESC
758
+ Convert a one-time pad file to LaTeX format with landscape orientation.
759
+
760
+ Parameters:
761
+ --padpath PADPATH - Path to pad file (e.g., ~/number_station/pads/Shadow/Shadow-2026-01-12.json)
762
+
763
+ Each pad in the file is converted to groups of 5 characters, separated by spaces.
764
+ Pads are arranged in a 2x2 grid (4 pads per page), with each pad taking up one quadrant.
765
+ Output saved as FILENAME.tex in the same directory.
766
+
767
+ The LaTeX file uses landscape orientation and can be converted to PDF using convert_to_pdf.
768
+
769
+ Example:
770
+ number_station convert_pad_to_latex --padpath ~/number_station/pads/Shadow/Shadow-2026-01-12.json
771
+ CONVERT_PAD_TO_LATEX_LONG_DESC
772
+ option :padpath, type: :string, required: true
773
+ def convert_pad_to_latex
774
+ ensure_config_loaded
775
+
776
+ pad_path = options[:padpath]
777
+ unless File.exist?(pad_path)
778
+ raise Thor::Error, "Pad file not found: #{pad_path}"
779
+ end
780
+
781
+ # Read pad file
782
+ pad_data = JSON.parse(File.read(pad_path))
783
+ pad_id = pad_data["id"]
784
+ pads_hash = pad_data["pads"]
785
+
786
+ if pads_hash.nil? || pads_hash.empty?
787
+ raise Thor::Error, "No pads found in file: #{pad_path}"
788
+ end
789
+
790
+ # Generate output filename: replace .json extension with .tex
791
+ input_basename = File.basename(pad_path, File.extname(pad_path))
792
+ output_filename = "#{input_basename}.tex"
793
+ output_path = File.join(File.dirname(pad_path), output_filename)
794
+
795
+ # Build LaTeX content
796
+ latex_content = []
797
+ latex_content << "\\documentclass[10pt]{article}"
798
+ latex_content << "\\usepackage[utf8]{inputenc}"
799
+ latex_content << "\\usepackage[landscape,margin=0.5in]{geometry}"
800
+ latex_content << "\\usepackage{fancyhdr}"
801
+ latex_content << "\\usepackage{listings}"
802
+ latex_content << "\\lstset{"
803
+ latex_content << " basicstyle=\\ttfamily\\small,"
804
+ latex_content << " breaklines=true,"
805
+ latex_content << " breakatwhitespace=false,"
806
+ latex_content << " columns=fullflexible,"
807
+ latex_content << " frame=none,"
808
+ latex_content << " showstringspaces=false"
809
+ latex_content << "}"
810
+ latex_content << "\\pagestyle{fancy}"
811
+ latex_content << "\\fancyhf{}"
812
+ latex_content << "\\fancyfoot[C]{One-Time Pad: #{input_basename}}"
813
+ latex_content << "\\renewcommand{\\headrulewidth}{0pt}"
814
+ latex_content << "\\renewcommand{\\footrulewidth}{0pt}"
815
+ latex_content << "\\begin{document}"
816
+ latex_content << ""
817
+
818
+ # Process pads in groups of 4 (one page = 4 quadrants)
819
+ pads_array = pads_hash.sort_by { |k, v| k.to_i }
820
+ pads_array.each_slice(4).with_index do |pad_group, index|
821
+ # Start a new page for each group of 4 pads (except the first)
822
+ if index > 0
823
+ latex_content << "\\newpage"
824
+ latex_content << ""
825
+ end
826
+
827
+ # Create 2x2 grid using minipage environments
828
+ latex_content << "\\noindent"
829
+ latex_content << "\\begin{minipage}[t]{0.48\\textwidth}"
830
+ latex_content << "\\vspace{0pt}"
831
+
832
+ # Top-left quadrant (pad 0)
833
+ if pad_group[0]
834
+ pad_num, pad_info = pad_group[0]
835
+ hex_key = pad_info["key"]
836
+ cleaned_hex = hex_key.gsub(/[\s\n\r]/, '')
837
+ grouped_hex = cleaned_hex.chars.each_slice(5).map(&:join).join(' ')
838
+ latex_content << "\\textbf{Pad #{pad_num}}\\\\"
839
+ latex_content << "\\begin{lstlisting}"
840
+ latex_content << grouped_hex
841
+ latex_content << "\\end{lstlisting}"
842
+ end
843
+
844
+ latex_content << "\\end{minipage}"
845
+ latex_content << "\\hfill"
846
+ latex_content << "\\begin{minipage}[t]{0.48\\textwidth}"
847
+ latex_content << "\\vspace{0pt}"
848
+
849
+ # Top-right quadrant (pad 1)
850
+ if pad_group[1]
851
+ pad_num, pad_info = pad_group[1]
852
+ hex_key = pad_info["key"]
853
+ cleaned_hex = hex_key.gsub(/[\s\n\r]/, '')
854
+ grouped_hex = cleaned_hex.chars.each_slice(5).map(&:join).join(' ')
855
+ latex_content << "\\textbf{Pad #{pad_num}}\\\\"
856
+ latex_content << "\\begin{lstlisting}"
857
+ latex_content << grouped_hex
858
+ latex_content << "\\end{lstlisting}"
859
+ end
860
+
861
+ latex_content << "\\end{minipage}"
862
+ latex_content << ""
863
+ latex_content << "\\\\[1cm]"
864
+ latex_content << ""
865
+ latex_content << "\\noindent"
866
+ latex_content << "\\begin{minipage}[t]{0.48\\textwidth}"
867
+ latex_content << "\\vspace{0pt}"
868
+
869
+ # Bottom-left quadrant (pad 2)
870
+ if pad_group[2]
871
+ pad_num, pad_info = pad_group[2]
872
+ hex_key = pad_info["key"]
873
+ cleaned_hex = hex_key.gsub(/[\s\n\r]/, '')
874
+ grouped_hex = cleaned_hex.chars.each_slice(5).map(&:join).join(' ')
875
+ latex_content << "\\textbf{Pad #{pad_num}}\\\\"
876
+ latex_content << "\\begin{lstlisting}"
877
+ latex_content << grouped_hex
878
+ latex_content << "\\end{lstlisting}"
879
+ end
880
+
881
+ latex_content << "\\end{minipage}"
882
+ latex_content << "\\hfill"
883
+ latex_content << "\\begin{minipage}[t]{0.48\\textwidth}"
884
+ latex_content << "\\vspace{0pt}"
885
+
886
+ # Bottom-right quadrant (pad 3)
887
+ if pad_group[3]
888
+ pad_num, pad_info = pad_group[3]
889
+ hex_key = pad_info["key"]
890
+ cleaned_hex = hex_key.gsub(/[\s\n\r]/, '')
891
+ grouped_hex = cleaned_hex.chars.each_slice(5).map(&:join).join(' ')
892
+ latex_content << "\\textbf{Pad #{pad_num}}\\\\"
893
+ latex_content << "\\begin{lstlisting}"
894
+ latex_content << grouped_hex
895
+ latex_content << "\\end{lstlisting}"
896
+ end
897
+
898
+ latex_content << "\\end{minipage}"
899
+ latex_content << ""
900
+ end
901
+
902
+ latex_content << "\\end{document}"
903
+
904
+ # Write LaTeX file
905
+ File.write(output_path, latex_content.join("\n"))
906
+ puts "Generated LaTeX file: #{output_path}"
907
+ end
908
+
909
+ desc "convert_to_pdf INPUT_FILE", "Convert an AsciiDoc or LaTeX file to PDF"
910
+ long_desc <<-CONVERT_TO_PDF_LONG_DESC
911
+ Convert an AsciiDoc or LaTeX file to PDF format.
912
+
913
+ Parameters:
914
+ INPUT_FILE - Path to AsciiDoc file (e.g., Shadow-2026-01-12.asciidoc) or LaTeX file (e.g., Shadow-2026-01-12.tex)
915
+
916
+ For AsciiDoc files:
917
+ Uses asciidoctor-pdf (checks availability before executing).
918
+ Output saved as FILENAME.pdf in the same directory.
919
+
920
+ For LaTeX files:
921
+ Uses pdflatex (checks availability before executing).
922
+ Output saved as FILENAME.pdf in the same directory.
923
+
924
+ Examples:
925
+ number_station convert_to_pdf Shadow-2026-01-12.asciidoc
926
+ number_station convert_to_pdf Shadow-2026-01-12.tex
927
+ CONVERT_TO_PDF_LONG_DESC
928
+ def convert_to_pdf(input_file)
929
+ ensure_config_loaded
930
+
931
+ unless File.exist?(input_file)
932
+ raise Thor::Error, "File not found: #{input_file}"
933
+ end
934
+
935
+ file_ext = File.extname(input_file).downcase
936
+ input_basename = File.basename(input_file, file_ext)
937
+ output_filename = "#{input_basename}.pdf"
938
+ output_path = File.join(File.dirname(input_file), output_filename)
939
+
940
+ if file_ext == '.tex'
941
+ # LaTeX file - use pdflatex
942
+ unless NumberStation.command?('pdflatex')
943
+ raise Thor::Error, "pdflatex is not available. Please install a LaTeX distribution (e.g., texlive)"
944
+ end
945
+
946
+ # Run pdflatex in the directory containing the .tex file
947
+ file_dir = File.dirname(input_file)
948
+ file_name = File.basename(input_file)
949
+ cmd = "cd #{file_dir} && pdflatex -interaction=nonstopmode #{file_name}"
950
+ NumberStation.log.info "Running: #{cmd}"
951
+ system(cmd)
952
+
953
+ unless $?.success?
954
+ raise Thor::Error, "PDF conversion failed with exit code #{$?.exitstatus}"
955
+ end
956
+
957
+ puts "Generated PDF file: #{output_path}"
958
+ elsif file_ext == '.asciidoc' || file_ext == '.adoc'
959
+ # AsciiDoc file - use asciidoctor-pdf
960
+ unless NumberStation.command?('asciidoctor-pdf')
961
+ raise Thor::Error, "asciidoctor-pdf is not available. Please install it with: gem install asciidoctor-pdf"
962
+ end
963
+
964
+ # Convert using asciidoctor-pdf command
965
+ cmd = "asciidoctor-pdf #{input_file} -o #{output_path}"
966
+ NumberStation.log.info "Running: #{cmd}"
967
+ system(cmd)
968
+
969
+ unless $?.success?
970
+ raise Thor::Error, "PDF conversion failed with exit code #{$?.exitstatus}"
971
+ end
972
+
973
+ puts "Generated PDF file: #{output_path}"
974
+ else
975
+ raise Thor::Error, "Unsupported file type: #{file_ext}. Supported types: .asciidoc, .adoc, .tex"
976
+ end
977
+ end
978
+
679
979
  desc "espeak XML_FILE", "Use espeak to read an XML file"
680
980
  long_desc <<-ESPEAK_LONG_DESC
681
981
  Use the espeak utility to read an XML file with GLaDOS-style voice settings.
@@ -772,6 +1072,7 @@ module NumberStation
772
1072
 
773
1073
  If --agent is provided, the system will search for the oldest pad with unconsumed pads
774
1074
  in the agent-specific directory (~/number_station/pads/AGENT/).
1075
+ Note: The agent must be active. Encryption will fail if the agent is inactive.
775
1076
 
776
1077
  If --agent is not provided and --padpath/--numpad are not specified, the system will
777
1078
  search for the oldest pad with unconsumed pads in ~/number_station/pads/.
@@ -20,5 +20,5 @@
20
20
  =end
21
21
 
22
22
  module NumberStation
23
- VERSION = "0.3.0"
23
+ VERSION = "0.5.0"
24
24
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: number_station
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Kirwan