number_station 0.4.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: 28bb32733b0cc2c4dd9a9258985a762e6cf558b9ec3a05d7438a5d4b53332dfe
4
- data.tar.gz: dc4f7d34bd69879eb4eda6ac0774f2fa20c65433d6d841a98a7f97968562daf8
3
+ metadata.gz: d9d58534a90398f402d7fad891415ab81782d96aaa440964703c6d33a1e89f37
4
+ data.tar.gz: 7ca3efa06033aacc95db71c8bd9622f9869f82caa1e535a50f592d72b2f39a5e
5
5
  SHA512:
6
- metadata.gz: a86c1b266dd7d5837bf99d1eafecb545fe36ebb86649013dee3bb9a5cff9d507021f257c8595452a0a0f2c91a96cbb579769cadf0245f3983bf9f31371e82891
7
- data.tar.gz: a8be3d5d110b854ac1254c9fdfb38b062dbb7fbbdbc828617c7c63fdfc40426627a6d1f086c772dfe6c6360a4a88f9eefdaea111d0ec1e6d7e98c53d308d236d
6
+ metadata.gz: 73577af7d0e0e2d6b383398dc3c49dee4d2cfe816d0a6e5d19da5947409108bb79343ddc9fade867209f4dc4973c33d4083ea8098bf691766a2edb81ebd1391b
7
+ data.tar.gz: fff53ecba16b4b3d77674148f2982aa2022cae2e8f5ad0d4995a103562663a4b3bd0aae3337a7be0089c1bfcb152814453f8692456c8ca2af5f932ab5d04198e
data/README.asciidoc CHANGED
@@ -69,7 +69,7 @@ number_station pad create [--name AGENT] [--numpads NUM] [--length LENGTH]
69
69
  ----
70
70
 
71
71
  If `--name` is provided, creates pad in `~/number_station/pads/AGENT/` directory.
72
- 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.
73
73
 
74
74
  *View pad statistics:*
75
75
 
@@ -80,6 +80,41 @@ number_station pad stats [--path PATH]
80
80
 
81
81
  Shows pad statistics grouped by agent, including unconsumed pad counts.
82
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
+
83
118
  ==== Message Encryption/Decryption ====
84
119
 
85
120
  *Encrypt:*
@@ -39,13 +39,16 @@ module NumberStation
39
39
  --name NAME - Agent name to associate with this pad. Creates subdirectory ~/number_station/pads/NAME/ if --path is not provided
40
40
  --path PATH - Directory where the pad file will be created (defaults to current directory, or ~/number_station/pads/NAME/ if --name is provided)
41
41
  --numpads NUM - Number of pads to generate (defaults to 500)
42
- --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)
43
43
 
44
44
  If no parameters are passed it will generate 500 one time pads in the current
45
45
  directory of size 500 characters.
46
46
 
47
47
  If --name is provided without --path, the pad will be created in ~/number_station/pads/NAME/
48
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
+
49
52
  Examples:
50
53
  number_station pad create --name Shadow --numpads 1000 --length 1000
51
54
  number_station pad create --name Shadow
@@ -420,7 +423,7 @@ module NumberStation
420
423
 
421
424
  desc "activate NAME", "Activate an agent"
422
425
  long_desc <<-ACTIVATE_AGENT_LONG_DESC
423
- 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.
424
427
 
425
428
  Parameters:
426
429
  NAME - The agent name (required)
@@ -428,6 +431,8 @@ module NumberStation
428
431
  Options:
429
432
  --start-date DATE - Set the start date (defaults to today if not specified)
430
433
 
434
+ When activating an agent, the end_date is automatically reset to null (clearing any previous end date).
435
+
431
436
  Examples:
432
437
  number_station agents activate Shadow
433
438
  number_station agents activate Shadow --start-date "2024-01-15"
@@ -748,47 +753,227 @@ module NumberStation
748
753
  puts "Generated AsciiDoc file: #{output_path}"
749
754
  end
750
755
 
751
- desc "convert_to_pdf ASCIIDOC_FILE", "Convert an AsciiDoc file to PDF using asciidoctor-pdf"
752
- long_desc <<-CONVERT_TO_PDF_LONG_DESC
753
- Convert an AsciiDoc file (typically generated by convert_pad_to_asciidoc) to PDF format.
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.
754
759
 
755
760
  Parameters:
756
- ASCIIDOC_FILE - Path to AsciiDoc file (e.g., Shadow-2026-01-12.asciidoc)
761
+ --padpath PADPATH - Path to pad file (e.g., ~/number_station/pads/Shadow/Shadow-2026-01-12.json)
757
762
 
758
- The command checks if asciidoctor-pdf is available before executing.
759
- Output saved as FILENAME.pdf in the same directory.
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.
760
768
 
761
769
  Example:
762
- number_station convert_to_pdf Shadow-2026-01-12.asciidoc
763
- CONVERT_TO_PDF_LONG_DESC
764
- def convert_to_pdf(asciidoc_file)
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
765
774
  ensure_config_loaded
766
775
 
767
- unless File.exist?(asciidoc_file)
768
- raise Thor::Error, "File not found: #{asciidoc_file}"
776
+ pad_path = options[:padpath]
777
+ unless File.exist?(pad_path)
778
+ raise Thor::Error, "Pad file not found: #{pad_path}"
769
779
  end
770
780
 
771
- # Check if asciidoctor-pdf is available
772
- # Check if command exists first (most reliable)
773
- unless NumberStation.command?('asciidoctor-pdf')
774
- raise Thor::Error, "asciidoctor-pdf is not available. Please install it with: gem install asciidoctor-pdf"
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}"
775
788
  end
776
789
 
777
- # Generate output filename: replace .asciidoc extension with .pdf
778
- input_basename = File.basename(asciidoc_file, File.extname(asciidoc_file))
779
- output_filename = "#{input_basename}.pdf"
780
- output_path = File.join(File.dirname(asciidoc_file), output_filename)
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)
781
794
 
782
- # Convert using asciidoctor-pdf command
783
- cmd = "asciidoctor-pdf #{asciidoc_file} -o #{output_path}"
784
- NumberStation.log.info "Running: #{cmd}"
785
- system(cmd)
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
786
901
 
787
- unless $?.success?
788
- raise Thor::Error, "PDF conversion failed with exit code #{$?.exitstatus}"
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}"
789
933
  end
790
934
 
791
- puts "Generated PDF file: #{output_path}"
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
792
977
  end
793
978
 
794
979
  desc "espeak XML_FILE", "Use espeak to read an XML file"
@@ -887,6 +1072,7 @@ module NumberStation
887
1072
 
888
1073
  If --agent is provided, the system will search for the oldest pad with unconsumed pads
889
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.
890
1076
 
891
1077
  If --agent is not provided and --padpath/--numpad are not specified, the system will
892
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.4.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.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Kirwan