factorix 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.
Files changed (202) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +105 -0
  5. data/completion/_factorix.bash +202 -0
  6. data/completion/_factorix.fish +197 -0
  7. data/completion/_factorix.zsh +376 -0
  8. data/doc/factorix.1 +377 -0
  9. data/exe/factorix +20 -0
  10. data/lib/factorix/api/category.rb +69 -0
  11. data/lib/factorix/api/image.rb +35 -0
  12. data/lib/factorix/api/license.rb +71 -0
  13. data/lib/factorix/api/mod_download_api.rb +66 -0
  14. data/lib/factorix/api/mod_info.rb +166 -0
  15. data/lib/factorix/api/mod_management_api.rb +237 -0
  16. data/lib/factorix/api/mod_portal_api.rb +204 -0
  17. data/lib/factorix/api/release.rb +49 -0
  18. data/lib/factorix/api/tag.rb +95 -0
  19. data/lib/factorix/api.rb +7 -0
  20. data/lib/factorix/api_credential.rb +54 -0
  21. data/lib/factorix/application.rb +218 -0
  22. data/lib/factorix/cache/file_system.rb +307 -0
  23. data/lib/factorix/cli/commands/backup_support.rb +46 -0
  24. data/lib/factorix/cli/commands/base.rb +90 -0
  25. data/lib/factorix/cli/commands/cache/evict.rb +180 -0
  26. data/lib/factorix/cli/commands/cache/stat.rb +201 -0
  27. data/lib/factorix/cli/commands/command_wrapper.rb +71 -0
  28. data/lib/factorix/cli/commands/completion.rb +83 -0
  29. data/lib/factorix/cli/commands/confirmable.rb +53 -0
  30. data/lib/factorix/cli/commands/download_support.rb +123 -0
  31. data/lib/factorix/cli/commands/launch.rb +79 -0
  32. data/lib/factorix/cli/commands/man.rb +29 -0
  33. data/lib/factorix/cli/commands/mod/check.rb +99 -0
  34. data/lib/factorix/cli/commands/mod/disable.rb +188 -0
  35. data/lib/factorix/cli/commands/mod/download.rb +291 -0
  36. data/lib/factorix/cli/commands/mod/edit.rb +114 -0
  37. data/lib/factorix/cli/commands/mod/enable.rb +216 -0
  38. data/lib/factorix/cli/commands/mod/image/add.rb +47 -0
  39. data/lib/factorix/cli/commands/mod/image/edit.rb +41 -0
  40. data/lib/factorix/cli/commands/mod/image/list.rb +74 -0
  41. data/lib/factorix/cli/commands/mod/install.rb +443 -0
  42. data/lib/factorix/cli/commands/mod/list.rb +372 -0
  43. data/lib/factorix/cli/commands/mod/search.rb +134 -0
  44. data/lib/factorix/cli/commands/mod/settings/dump.rb +88 -0
  45. data/lib/factorix/cli/commands/mod/settings/restore.rb +101 -0
  46. data/lib/factorix/cli/commands/mod/show.rb +202 -0
  47. data/lib/factorix/cli/commands/mod/sync.rb +299 -0
  48. data/lib/factorix/cli/commands/mod/uninstall.rb +325 -0
  49. data/lib/factorix/cli/commands/mod/update.rb +222 -0
  50. data/lib/factorix/cli/commands/mod/upload.rb +90 -0
  51. data/lib/factorix/cli/commands/path.rb +79 -0
  52. data/lib/factorix/cli/commands/requires_game_stopped.rb +32 -0
  53. data/lib/factorix/cli/commands/version.rb +25 -0
  54. data/lib/factorix/cli.rb +42 -0
  55. data/lib/factorix/dependency/edge.rb +89 -0
  56. data/lib/factorix/dependency/entry.rb +124 -0
  57. data/lib/factorix/dependency/graph/builder.rb +108 -0
  58. data/lib/factorix/dependency/graph.rb +210 -0
  59. data/lib/factorix/dependency/list.rb +244 -0
  60. data/lib/factorix/dependency/mod_version_requirement.rb +73 -0
  61. data/lib/factorix/dependency/node.rb +60 -0
  62. data/lib/factorix/dependency/parser.rb +148 -0
  63. data/lib/factorix/dependency/validation_result.rb +138 -0
  64. data/lib/factorix/dependency/validator.rb +190 -0
  65. data/lib/factorix/errors.rb +112 -0
  66. data/lib/factorix/formatting.rb +56 -0
  67. data/lib/factorix/game_version.rb +98 -0
  68. data/lib/factorix/http/cache_decorator.rb +106 -0
  69. data/lib/factorix/http/cached_response.rb +37 -0
  70. data/lib/factorix/http/client.rb +187 -0
  71. data/lib/factorix/http/response.rb +31 -0
  72. data/lib/factorix/http/retry_decorator.rb +59 -0
  73. data/lib/factorix/http/retry_strategy.rb +80 -0
  74. data/lib/factorix/info_json.rb +90 -0
  75. data/lib/factorix/installed_mod.rb +239 -0
  76. data/lib/factorix/mod.rb +55 -0
  77. data/lib/factorix/mod_list.rb +174 -0
  78. data/lib/factorix/mod_settings.rb +278 -0
  79. data/lib/factorix/mod_state.rb +34 -0
  80. data/lib/factorix/mod_version.rb +99 -0
  81. data/lib/factorix/portal.rb +185 -0
  82. data/lib/factorix/progress/download_handler.rb +46 -0
  83. data/lib/factorix/progress/multi_presenter.rb +45 -0
  84. data/lib/factorix/progress/presenter.rb +67 -0
  85. data/lib/factorix/progress/presenter_adapter.rb +46 -0
  86. data/lib/factorix/progress/scan_handler.rb +33 -0
  87. data/lib/factorix/progress/upload_handler.rb +33 -0
  88. data/lib/factorix/runtime/base.rb +233 -0
  89. data/lib/factorix/runtime/linux.rb +32 -0
  90. data/lib/factorix/runtime/mac_os.rb +53 -0
  91. data/lib/factorix/runtime/user_configurable.rb +69 -0
  92. data/lib/factorix/runtime/windows.rb +85 -0
  93. data/lib/factorix/runtime/wsl.rb +118 -0
  94. data/lib/factorix/runtime.rb +32 -0
  95. data/lib/factorix/save_file.rb +178 -0
  96. data/lib/factorix/ser_des/deserializer.rb +198 -0
  97. data/lib/factorix/ser_des/serializer.rb +231 -0
  98. data/lib/factorix/ser_des/signed_integer.rb +63 -0
  99. data/lib/factorix/ser_des/unsigned_integer.rb +65 -0
  100. data/lib/factorix/service_credential.rb +127 -0
  101. data/lib/factorix/transfer/downloader.rb +162 -0
  102. data/lib/factorix/transfer/uploader.rb +232 -0
  103. data/lib/factorix/version.rb +6 -0
  104. data/lib/factorix.rb +38 -0
  105. data/sig/dry/auto_inject.rbs +15 -0
  106. data/sig/dry/cli.rbs +19 -0
  107. data/sig/dry/configurable.rbs +13 -0
  108. data/sig/dry/core/container.rbs +17 -0
  109. data/sig/dry/events/publisher.rbs +22 -0
  110. data/sig/dry/logger.rbs +16 -0
  111. data/sig/factorix/api/category.rbs +15 -0
  112. data/sig/factorix/api/image.rbs +15 -0
  113. data/sig/factorix/api/license.rbs +20 -0
  114. data/sig/factorix/api/mod_download_api.rbs +18 -0
  115. data/sig/factorix/api/mod_info.rbs +67 -0
  116. data/sig/factorix/api/mod_management_api.rbs +25 -0
  117. data/sig/factorix/api/mod_portal_api.rbs +31 -0
  118. data/sig/factorix/api/release.rbs +27 -0
  119. data/sig/factorix/api/tag.rbs +15 -0
  120. data/sig/factorix/api.rbs +8 -0
  121. data/sig/factorix/api_credential.rbs +17 -0
  122. data/sig/factorix/application.rbs +86 -0
  123. data/sig/factorix/cache/file_system.rbs +35 -0
  124. data/sig/factorix/cli/commands/base.rbs +13 -0
  125. data/sig/factorix/cli/commands/cache/evict.rbs +17 -0
  126. data/sig/factorix/cli/commands/cache/stat.rbs +17 -0
  127. data/sig/factorix/cli/commands/command_wrapper.rbs +13 -0
  128. data/sig/factorix/cli/commands/completion/zsh.rbs +15 -0
  129. data/sig/factorix/cli/commands/confirmable.rbs +12 -0
  130. data/sig/factorix/cli/commands/download_support.rbs +12 -0
  131. data/sig/factorix/cli/commands/launch.rbs +15 -0
  132. data/sig/factorix/cli/commands/mod/check.rbs +18 -0
  133. data/sig/factorix/cli/commands/mod/disable.rbs +20 -0
  134. data/sig/factorix/cli/commands/mod/download.rbs +18 -0
  135. data/sig/factorix/cli/commands/mod/edit.rbs +30 -0
  136. data/sig/factorix/cli/commands/mod/enable.rbs +20 -0
  137. data/sig/factorix/cli/commands/mod/image/add.rbs +19 -0
  138. data/sig/factorix/cli/commands/mod/image/edit.rbs +19 -0
  139. data/sig/factorix/cli/commands/mod/image/list.rbs +19 -0
  140. data/sig/factorix/cli/commands/mod/install.rbs +19 -0
  141. data/sig/factorix/cli/commands/mod/list.rbs +30 -0
  142. data/sig/factorix/cli/commands/mod/search.rbs +18 -0
  143. data/sig/factorix/cli/commands/mod/settings/dump.rbs +17 -0
  144. data/sig/factorix/cli/commands/mod/settings/restore.rbs +17 -0
  145. data/sig/factorix/cli/commands/mod/sync.rbs +19 -0
  146. data/sig/factorix/cli/commands/mod/uninstall.rbs +20 -0
  147. data/sig/factorix/cli/commands/mod/update.rbs +19 -0
  148. data/sig/factorix/cli/commands/mod/upload.rbs +24 -0
  149. data/sig/factorix/cli/commands/path.rbs +18 -0
  150. data/sig/factorix/cli/commands/requires_game_stopped.rbs +13 -0
  151. data/sig/factorix/cli/commands/version.rbs +13 -0
  152. data/sig/factorix/cli.rbs +11 -0
  153. data/sig/factorix/dependency/edge.rbs +32 -0
  154. data/sig/factorix/dependency/entry.rbs +30 -0
  155. data/sig/factorix/dependency/graph/builder.rbs +17 -0
  156. data/sig/factorix/dependency/graph.rbs +39 -0
  157. data/sig/factorix/dependency/list.rbs +69 -0
  158. data/sig/factorix/dependency/mod_version_requirement.rbs +18 -0
  159. data/sig/factorix/dependency/node.rbs +24 -0
  160. data/sig/factorix/dependency/parser.rbs +11 -0
  161. data/sig/factorix/dependency/validation_result.rbs +56 -0
  162. data/sig/factorix/dependency/validator.rbs +13 -0
  163. data/sig/factorix/errors.rbs +132 -0
  164. data/sig/factorix/formatting.rbs +8 -0
  165. data/sig/factorix/game_version.rbs +24 -0
  166. data/sig/factorix/http/cache_decorator.rbs +64 -0
  167. data/sig/factorix/http/client.rbs +55 -0
  168. data/sig/factorix/http/response.rbs +28 -0
  169. data/sig/factorix/http/retry_decorator.rbs +44 -0
  170. data/sig/factorix/http/retry_strategy.rbs +42 -0
  171. data/sig/factorix/info_json.rbs +19 -0
  172. data/sig/factorix/installed_mod.rbs +34 -0
  173. data/sig/factorix/mod.rbs +20 -0
  174. data/sig/factorix/mod_list.rbs +44 -0
  175. data/sig/factorix/mod_settings.rbs +47 -0
  176. data/sig/factorix/mod_state.rbs +18 -0
  177. data/sig/factorix/mod_version.rbs +23 -0
  178. data/sig/factorix/portal.rbs +37 -0
  179. data/sig/factorix/progress/download_handler.rbs +19 -0
  180. data/sig/factorix/progress/multi_presenter.rbs +15 -0
  181. data/sig/factorix/progress/presenter.rbs +17 -0
  182. data/sig/factorix/progress/presenter_adapter.rbs +17 -0
  183. data/sig/factorix/progress/scan_handler.rbs +16 -0
  184. data/sig/factorix/progress/upload_handler.rbs +17 -0
  185. data/sig/factorix/runtime/base.rbs +45 -0
  186. data/sig/factorix/runtime/linux.rbs +15 -0
  187. data/sig/factorix/runtime/mac_os.rbs +15 -0
  188. data/sig/factorix/runtime/user_configurable.rbs +13 -0
  189. data/sig/factorix/runtime/windows.rbs +23 -0
  190. data/sig/factorix/runtime/wsl.rbs +19 -0
  191. data/sig/factorix/runtime.rbs +9 -0
  192. data/sig/factorix/save_file.rbs +40 -0
  193. data/sig/factorix/ser_des/deserializer.rbs +49 -0
  194. data/sig/factorix/ser_des/serializer.rbs +45 -0
  195. data/sig/factorix/ser_des/signed_integer.rbs +37 -0
  196. data/sig/factorix/ser_des/unsigned_integer.rbs +37 -0
  197. data/sig/factorix/service_credential.rbs +19 -0
  198. data/sig/factorix/transfer/downloader.rbs +15 -0
  199. data/sig/factorix/transfer/uploader.rbs +21 -0
  200. data/sig/factorix.rbs +9 -0
  201. data/sig/tty/progressbar.rbs +18 -0
  202. metadata +431 -0
data/doc/factorix.1 ADDED
@@ -0,0 +1,377 @@
1
+ .\" Generated by Claude Code
2
+ .TH FACTORIX 1 "November 2025" "Factorix 0.1.0" "User Commands"
3
+ .SH NAME
4
+ factorix \- Factorio MOD manager CLI tool
5
+ .SH SYNOPSIS
6
+ .B factorix
7
+ .RI [ command ]
8
+ .RI [ subcommand ]
9
+ .RI [ options ]
10
+ .RI [ arguments ]
11
+ .SH DESCRIPTION
12
+ .B factorix
13
+ is a command-line tool for managing Factorio MODs. It provides functionality
14
+ for discovering, installing, updating, and managing MODs from the Factorio
15
+ MOD Portal, as well as managing MOD settings and game launch.
16
+ .SH GLOBAL OPTIONS
17
+ The following options are available for all commands:
18
+ .TP
19
+ .BR \-c ", " \-\-config\-path =\fIVALUE\fR
20
+ Path to configuration file.
21
+ .TP
22
+ .BR \-\-log\-level =\fIVALUE\fR
23
+ Set log level: debug, info, warn, error, fatal.
24
+ .TP
25
+ .BR \-q ", " \-\-quiet
26
+ Suppress non-essential output.
27
+ .TP
28
+ .BR \-h ", " \-\-help
29
+ Print help information.
30
+ .SH COMMANDS
31
+ .SS factorix version
32
+ Display Factorix version.
33
+ .SS factorix man
34
+ Display this manual page using the system's man command.
35
+ .SS factorix path
36
+ Display Factorio and Factorix paths.
37
+ .TP
38
+ .B \-\-json
39
+ Output in JSON format.
40
+ .SS factorix launch
41
+ Launch Factorio game.
42
+ .TP
43
+ .BR \-w ", " \-\-wait
44
+ Wait for the game to finish.
45
+ .PP
46
+ Arguments after
47
+ .B \-\-
48
+ are passed directly to Factorio.
49
+ .SS factorix completion [SHELL]
50
+ Generate shell completion script. Output should be evaluated to enable completion.
51
+ .TP
52
+ .B SHELL
53
+ Shell type: zsh, bash, fish. If omitted, detects from SHELL environment variable.
54
+ .PP
55
+ .RS
56
+ eval "$(factorix completion)" # auto-detect
57
+ .br
58
+ eval "$(factorix completion zsh)" # zsh
59
+ .br
60
+ eval "$(factorix completion bash)" # bash
61
+ .br
62
+ factorix completion fish | source # fish
63
+ .RE
64
+ .SS factorix cache stat
65
+ Display cache statistics.
66
+ .TP
67
+ .B \-\-json
68
+ Output in JSON format.
69
+ .SS factorix cache evict [CACHES]
70
+ Evict cache entries. CACHES can be: download, api, info_json.
71
+ .TP
72
+ .B \-\-all
73
+ Remove all entries.
74
+ .TP
75
+ .B \-\-expired
76
+ Remove expired entries only.
77
+ .TP
78
+ .BR \-\-older\-than =\fIAGE\fR
79
+ Remove entries older than AGE (e.g., 30s, 5m, 2h, 7d).
80
+ .SH MOD COMMANDS
81
+ .SS factorix mod list
82
+ List installed MOD(s).
83
+ .TP
84
+ .B \-\-enabled
85
+ Show only enabled MOD(s).
86
+ .TP
87
+ .B \-\-disabled
88
+ Show only disabled MOD(s).
89
+ .TP
90
+ .B \-\-errors
91
+ Show only MOD(s) with dependency errors.
92
+ .TP
93
+ .B \-\-outdated
94
+ Show only MOD(s) with available updates.
95
+ .TP
96
+ .B \-\-json
97
+ Output in JSON format.
98
+ .SS factorix mod search [MOD_NAMES]
99
+ Search MOD(s) on Factorio MOD Portal.
100
+ .TP
101
+ .BR \-\-[no\-]hide\-deprecated
102
+ Hide deprecated MOD(s) (default: true).
103
+ .TP
104
+ .BR \-\-page =\fIVALUE\fR
105
+ Page number (default: 1).
106
+ .TP
107
+ .BR \-\-page\-size =\fIVALUE\fR
108
+ Results per page (max 500, default: 25).
109
+ .TP
110
+ .BR \-\-sort =\fIVALUE\fR
111
+ Sort field: name, created_at, updated_at.
112
+ .TP
113
+ .BR \-\-sort\-order =\fIVALUE\fR
114
+ Sort order: asc, desc.
115
+ .TP
116
+ .BR \-\-version =\fIVALUE\fR
117
+ Filter by Factorio version.
118
+ .TP
119
+ .B \-\-json
120
+ Output in JSON format.
121
+ .SS factorix mod show MOD_NAME
122
+ Show MOD details from Factorio MOD Portal.
123
+ .PP
124
+ Displays title, summary, version, author, category, license, Factorio version,
125
+ downloads count, links (portal, source, homepage), dependencies, and
126
+ incompatibilities. Shows installation status if the MOD is locally installed.
127
+ .SS factorix mod install MOD_SPECS
128
+ Install MOD(s) from Factorio MOD Portal. Downloads to MOD directory and enables.
129
+ .PP
130
+ MOD_SPECS format: name, name@version, or name@latest.
131
+ .TP
132
+ .BR \-y ", " \-\-yes
133
+ Skip confirmation prompts.
134
+ .TP
135
+ .BR \-j ", " \-\-jobs =\fIVALUE\fR
136
+ Number of parallel downloads (default: 4).
137
+ .SS factorix mod uninstall [MOD_SPECS]
138
+ Uninstall MOD(s) from MOD directory.
139
+ .TP
140
+ .BR \-y ", " \-\-yes
141
+ Skip confirmation prompts.
142
+ .TP
143
+ .B \-\-all
144
+ Uninstall all MOD(s).
145
+ .SS factorix mod update [MOD_NAMES]
146
+ Update MOD(s) to their latest versions. Updates all if no names specified.
147
+ .TP
148
+ .BR \-y ", " \-\-yes
149
+ Skip confirmation prompts.
150
+ .TP
151
+ .BR \-j ", " \-\-jobs =\fIVALUE\fR
152
+ Number of parallel downloads (default: 4).
153
+ .SS factorix mod download MOD_SPECS
154
+ Download MOD files from Factorio MOD Portal without installing.
155
+ .TP
156
+ .BR \-d ", " \-\-directory =\fIVALUE\fR
157
+ Download directory (default: current directory).
158
+ .TP
159
+ .BR \-j ", " \-\-jobs =\fIVALUE\fR
160
+ Number of parallel downloads (default: 4).
161
+ .TP
162
+ .BR \-r ", " \-\-recursive
163
+ Include required dependencies recursively.
164
+ .SS factorix mod enable MOD_NAMES
165
+ Enable MOD(s) in mod-list.json. Recursively enables dependencies.
166
+ .TP
167
+ .BR \-y ", " \-\-yes
168
+ Skip confirmation prompts.
169
+ .SS factorix mod disable [MOD_NAMES]
170
+ Disable MOD(s) in mod-list.json. Recursively disables dependent MOD(s).
171
+ .TP
172
+ .BR \-y ", " \-\-yes
173
+ Skip confirmation prompts.
174
+ .TP
175
+ .B \-\-all
176
+ Disable all MOD(s) except base.
177
+ .SS factorix mod check
178
+ Validate MOD dependencies. Reports missing, disabled, or incompatible dependencies.
179
+ .SS factorix mod sync SAVE_FILE
180
+ Sync MOD states and startup settings from a save file.
181
+ .TP
182
+ .BR \-y ", " \-\-yes
183
+ Skip confirmation prompts.
184
+ .TP
185
+ .BR \-j ", " \-\-jobs =\fIVALUE\fR
186
+ Number of parallel downloads (default: 4).
187
+ .SH MOD PORTAL COMMANDS
188
+ These commands interact with the Factorio MOD Portal and require authentication.
189
+ .SS factorix mod upload FILE
190
+ Upload MOD to Factorio MOD Portal. Handles both new MODs and updates.
191
+ .TP
192
+ .BR \-\-description =\fIVALUE\fR
193
+ Markdown description.
194
+ .TP
195
+ .BR \-\-category =\fIVALUE\fR
196
+ MOD category.
197
+ .TP
198
+ .BR \-\-license =\fIVALUE\fR
199
+ License identifier.
200
+ .TP
201
+ .BR \-\-source\-url =\fIVALUE\fR
202
+ Repository URL.
203
+ .SS factorix mod edit MOD_NAME
204
+ Edit MOD metadata on Factorio MOD Portal.
205
+ .TP
206
+ .BR \-\-title =\fIVALUE\fR
207
+ MOD title.
208
+ .TP
209
+ .BR \-\-summary =\fIVALUE\fR
210
+ Brief description.
211
+ .TP
212
+ .BR \-\-description =\fIVALUE\fR
213
+ Markdown description.
214
+ .TP
215
+ .BR \-\-category =\fIVALUE\fR
216
+ MOD category.
217
+ .TP
218
+ .BR \-\-tags =\fIVALUE1,VALUE2,..\fR
219
+ Array of tags.
220
+ .TP
221
+ .BR \-\-license =\fIVALUE\fR
222
+ License identifier.
223
+ .TP
224
+ .BR \-\-homepage =\fIVALUE\fR
225
+ Homepage URL.
226
+ .TP
227
+ .BR \-\-source\-url =\fIVALUE\fR
228
+ Repository URL.
229
+ .TP
230
+ .BR \-\-faq =\fIVALUE\fR
231
+ FAQ text.
232
+ .TP
233
+ .BR \-\-[no\-]deprecated
234
+ Deprecation flag.
235
+ .SS factorix mod image list MOD_NAME
236
+ List images for a MOD.
237
+ .TP
238
+ .B \-\-json
239
+ Output in JSON format.
240
+ .SS factorix mod image add MOD_NAME IMAGE_FILE
241
+ Add an image to a MOD.
242
+ .SS factorix mod image edit MOD_NAME IMAGE_IDS
243
+ Edit MOD's image list. Reorder or remove images by specifying IDs in desired order.
244
+ .SH SETTINGS COMMANDS
245
+ .SS factorix mod settings dump [SETTINGS_FILE]
246
+ Dump MOD settings to JSON format.
247
+ .TP
248
+ .BR \-o ", " \-\-output =\fIVALUE\fR
249
+ Output file path (default: stdout).
250
+ .SS factorix mod settings restore [SETTINGS_FILE]
251
+ Restore MOD settings from JSON format.
252
+ .TP
253
+ .BR \-i ", " \-\-input =\fIVALUE\fR
254
+ Input file path (default: stdin).
255
+ .TP
256
+ .BR \-\-backup\-extension =\fIVALUE\fR
257
+ Backup file extension (default: .bak).
258
+ .SH AUTHENTICATION
259
+ .SS Service Credentials (for MOD downloads)
260
+ Service credentials (username and token) are read from player-data.json
261
+ in the Factorio user data directory. These are automatically saved when
262
+ you log in to Factorio.
263
+ .PP
264
+ Alternatively, you can set environment variables:
265
+ .TP
266
+ .B FACTORIO_USERNAME
267
+ Factorio service username.
268
+ .TP
269
+ .B FACTORIO_TOKEN
270
+ Factorio service token.
271
+ .SS API Key (for MOD Portal management)
272
+ API key is required for upload, edit, and image operations.
273
+ Set the following environment variable:
274
+ .TP
275
+ .B FACTORIO_API_KEY
276
+ API key obtained from Factorio MOD Portal.
277
+ .SH ENVIRONMENT
278
+ .TP
279
+ .B FACTORIX_CONFIG
280
+ Path to configuration file. Overrides the default location.
281
+ .SH FILES
282
+ The paths below are XDG Base Directory defaults for Linux/WSL.
283
+ On macOS and Windows, these locations differ according to platform conventions.
284
+ Use
285
+ .B factorix path
286
+ to see actual paths for your system.
287
+ .TP
288
+ .I ~/.config/factorix/config.rb
289
+ User configuration file (Ruby DSL).
290
+ .TP
291
+ .I ~/.cache/factorix/
292
+ Cache directory for API responses and downloads.
293
+ .TP
294
+ .I ~/.local/state/factorix/
295
+ Log files.
296
+ .SH EXAMPLES
297
+ Enable shell completion for bash or zsh:
298
+ .PP
299
+ .RS
300
+ eval "$(factorix completion)"
301
+ .RE
302
+ .PP
303
+ Enable shell completion for fish:
304
+ .PP
305
+ .RS
306
+ factorix completion fish | source
307
+ .RE
308
+ .PP
309
+ List all installed MODs:
310
+ .PP
311
+ .RS
312
+ factorix mod list
313
+ .RE
314
+ .PP
315
+ Search for MODs on the portal:
316
+ .PP
317
+ .RS
318
+ factorix mod search logistic
319
+ .RE
320
+ .PP
321
+ Install a MOD with dependencies:
322
+ .PP
323
+ .RS
324
+ factorix mod install some-mod
325
+ .RE
326
+ .PP
327
+ Install a specific version:
328
+ .PP
329
+ .RS
330
+ factorix mod install some-mod@1.2.0
331
+ .RE
332
+ .PP
333
+ Update all installed MODs:
334
+ .PP
335
+ .RS
336
+ factorix mod update
337
+ .RE
338
+ .PP
339
+ Sync MODs from a save file:
340
+ .PP
341
+ .RS
342
+ factorix mod sync ~/factorio/saves/my-save.zip
343
+ .RE
344
+ .PP
345
+ Launch Factorio with custom arguments:
346
+ .PP
347
+ .RS
348
+ factorix launch -- --benchmark save.zip
349
+ .RE
350
+ .PP
351
+ Export MOD settings:
352
+ .PP
353
+ .RS
354
+ factorix mod settings dump -o settings.json
355
+ .RE
356
+ .SH EXIT STATUS
357
+ .TP
358
+ .B 0
359
+ Success.
360
+ .TP
361
+ .B 1
362
+ Expected error (e.g., MOD not found, validation failure).
363
+ .TP
364
+ .B 2
365
+ Unexpected error (e.g., network failure, internal error).
366
+ .SH SEE ALSO
367
+ .UR https://github.com/sakuro/factorix
368
+ Factorix on GitHub
369
+ .UE
370
+ .PP
371
+ .UR https://wiki.factorio.com/Mod_portal_API
372
+ Factorio MOD Portal API
373
+ .UE
374
+ .SH AUTHOR
375
+ OZAWA Sakuro
376
+ .SH COPYRIGHT
377
+ MIT License
data/exe/factorix ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/factorix"
5
+ require "dry/cli"
6
+ require "zip"
7
+
8
+ # Suppress warnings about invalid dates in ZIP files
9
+ Zip.warn_invalid_date = false
10
+
11
+ begin
12
+ Dry::CLI.new(Factorix::CLI).call
13
+ exit 0
14
+ rescue Factorix::Error
15
+ # Expected errors are already logged and displayed by CommandWrapper
16
+ exit 1
17
+ rescue
18
+ # Unexpected errors are already logged and displayed by CommandWrapper
19
+ exit 2
20
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Factorix
4
+ module API
5
+ Category = Data.define(:value, :name, :description)
6
+
7
+ # Category object from MOD Portal API
8
+ #
9
+ # Represents a MOD category with value, name, and description.
10
+ # Uses flyweight pattern - same category value returns same instance.
11
+ #
12
+ # @see https://wiki.factorio.com/Mod_details_API#Category
13
+ class Category
14
+ # @!attribute [r] value
15
+ # @return [String] category value (e.g., "content", "utilities", "" for no category)
16
+ # @!attribute [r] name
17
+ # @return [String] human-readable category name (e.g., "Content", "Utilities")
18
+ # @!attribute [r] description
19
+ # @return [String] category description
20
+
21
+ # Predefined category instances
22
+ NO_CATEGORY = new(value: "", name: "No category", description: "Unassigned category")
23
+ private_constant :NO_CATEGORY
24
+ CONTENT = new(value: "content", name: "Content", description: "Mods introducing new content into the game")
25
+ private_constant :CONTENT
26
+ OVERHAUL = new(value: "overhaul", name: "Overhaul", description: "Large total conversion mods")
27
+ private_constant :OVERHAUL
28
+ TWEAKS = new(value: "tweaks", name: "Tweaks", description: "Small changes concerning balance, gameplay, or graphics")
29
+ private_constant :TWEAKS
30
+ UTILITIES = new(value: "utilities", name: "Utilities", description: "Providing the player with new tools or adjusting the game interface")
31
+ private_constant :UTILITIES
32
+ SCENARIOS = new(value: "scenarios", name: "Scenarios", description: "Scenarios, maps, and puzzles")
33
+ private_constant :SCENARIOS
34
+ MOD_PACKS = new(value: "mod-packs", name: "Mod packs", description: "Collections of mods with tweaks to make them work together")
35
+ private_constant :MOD_PACKS
36
+ LOCALIZATIONS = new(value: "localizations", name: "Localizations", description: "Translations for other mods")
37
+ private_constant :LOCALIZATIONS
38
+ INTERNAL = new(value: "internal", name: "Internal", description: "Lua libraries for use by other mods")
39
+ private_constant :INTERNAL
40
+
41
+ # Lookup table for flyweight pattern
42
+ CATEGORIES = {
43
+ "" => NO_CATEGORY,
44
+ "no-category" => NO_CATEGORY,
45
+ "content" => CONTENT,
46
+ "overhaul" => OVERHAUL,
47
+ "tweaks" => TWEAKS,
48
+ "utilities" => UTILITIES,
49
+ "scenarios" => SCENARIOS,
50
+ "mod-packs" => MOD_PACKS,
51
+ "localizations" => LOCALIZATIONS,
52
+ "internal" => INTERNAL
53
+ }.freeze
54
+ private_constant :CATEGORIES
55
+
56
+ # Get Category instance for the given value
57
+ #
58
+ # Returns predefined instance for known categories (flyweight pattern).
59
+ # Raises an error for unknown category values.
60
+ #
61
+ # @param value [String] category value
62
+ # @return [Category] Category instance
63
+ # @raise [KeyError] if category value is unknown
64
+ def self.for(value) = CATEGORIES.fetch(value.to_s)
65
+
66
+ private_class_method :new, :[]
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Factorix
6
+ module API
7
+ Image = Data.define(:id, :thumbnail, :url)
8
+
9
+ # Image object from MOD Portal API
10
+ #
11
+ # Represents a MOD screenshot or image
12
+ #
13
+ # @see https://wiki.factorio.com/Mod_portal_API
14
+ class Image
15
+ # @!attribute [r] id
16
+ # @return [String] image ID
17
+ # @!attribute [r] thumbnail
18
+ # @return [URI::HTTPS] thumbnail URL
19
+ # @!attribute [r] url
20
+ # @return [URI::HTTPS] full-size image URL
21
+
22
+ # Create Image from API response hash
23
+ #
24
+ # @param id [String] image ID
25
+ # @param thumbnail [String] thumbnail URL
26
+ # @param url [String] full-size image URL
27
+ # @return [Image] new Image instance
28
+ def initialize(id:, thumbnail:, url:)
29
+ thumbnail = URI(thumbnail)
30
+ url = URI(url)
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Factorix
6
+ module API
7
+ License = Data.define(:id, :name, :title, :description, :url)
8
+
9
+ # License object from MOD Portal API
10
+ #
11
+ # Represents a MOD license information.
12
+ # Also provides valid license identifiers for edit_details API.
13
+ #
14
+ # @see https://wiki.factorio.com/Mod_portal_API
15
+ # @see https://wiki.factorio.com/Mod_details_API
16
+ class License
17
+ # @!attribute [r] id
18
+ # @return [String] license ID
19
+ # @!attribute [r] name
20
+ # @return [String] license name
21
+ # @!attribute [r] title
22
+ # @return [String] license title
23
+ # @!attribute [r] description
24
+ # @return [String] license description (long text)
25
+ # @!attribute [r] url
26
+ # @return [URI::HTTPS] license URL
27
+
28
+ # Valid license identifiers for edit_details API
29
+ # Custom licenses (custom_$ID) are not included
30
+ IDENTIFIERS = {
31
+ "default_mit" => "MIT",
32
+ "default_gnugplv3" => "GNU GPLv3",
33
+ "default_gnulgplv3" => "GNU LGPLv3",
34
+ "default_mozilla2" => "Mozilla Public License 2.0",
35
+ "default_apache2" => "Apache License 2.0",
36
+ "default_unlicense" => "The Unlicense"
37
+ }.freeze
38
+ private_constant :IDENTIFIERS
39
+
40
+ # Pattern for custom license identifiers (custom_ + 24 lowercase hex chars)
41
+ CUSTOM_LICENSE_PATTERN = /\Acustom_[0-9a-f]{24}\z/
42
+ private_constant :CUSTOM_LICENSE_PATTERN
43
+
44
+ # Check if the given value is a valid license identifier
45
+ #
46
+ # @param value [String] license identifier
47
+ # @return [Boolean] true if valid (standard or custom license)
48
+ def self.valid_identifier?(value)
49
+ IDENTIFIERS.key?(value) || CUSTOM_LICENSE_PATTERN.match?(value)
50
+ end
51
+
52
+ # List all valid license identifier values
53
+ #
54
+ # @return [Array<String>] array of license identifiers
55
+ def self.identifier_values = IDENTIFIERS.keys
56
+
57
+ # Create License from API response hash
58
+ #
59
+ # @param id [String] license ID
60
+ # @param name [String] license name
61
+ # @param title [String] license title
62
+ # @param description [String] license description
63
+ # @param url [String] license URL
64
+ # @return [License] new License instance
65
+ def initialize(id:, name:, title:, description:, url:)
66
+ url = URI(url)
67
+ super
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Factorix
6
+ module API
7
+ # API client for downloading MOD files with service authentication
8
+ #
9
+ # Requires username and token authentication via ServiceCredential.
10
+ class MODDownloadAPI
11
+ # NOTE: service_credential is NOT imported to avoid early evaluation errors
12
+ # when FACTORIO_USERNAME/FACTORIO_TOKEN environment variables are not set.
13
+ # It's resolved lazily via reader method instead.
14
+ # @!parse
15
+ # # @return [Transfer::Downloader]
16
+ # attr_reader :downloader
17
+ # # @return [Dry::Logger::Dispatcher]
18
+ # attr_reader :logger
19
+ include Import[:downloader, :logger]
20
+
21
+ BASE_URL = "https://mods.factorio.com"
22
+ private_constant :BASE_URL
23
+
24
+ # Initialize with thread-safe credential loading
25
+ #
26
+ # @param args [Hash] dependency injection arguments
27
+ def initialize(...)
28
+ super
29
+ @service_credential_mutex = Mutex.new
30
+ end
31
+
32
+ # Download a MOD file to the specified output path
33
+ #
34
+ # @param download_url [String] relative download URL from API response (e.g., "/download/mod-name/...")
35
+ # @param output [Pathname] output file path
36
+ # @param expected_sha1 [String, nil] expected SHA1 digest for verification (optional)
37
+ # @return [void]
38
+ # @raise [ArgumentError] if download_url is not a relative path starting with "/"
39
+ # @raise [DigestMismatchError] if SHA1 verification fails
40
+ def download(download_url, output, expected_sha1: nil)
41
+ unless download_url.start_with?("/")
42
+ logger.error("Invalid download_url", url: download_url)
43
+ raise ArgumentError, "download_url must be a relative path starting with '/'"
44
+ end
45
+
46
+ uri = build_download_uri(download_url)
47
+ downloader.download(uri, output, expected_sha1:)
48
+ end
49
+
50
+ private def service_credential
51
+ return @service_credential if defined?(@service_credential)
52
+
53
+ @service_credential_mutex.synchronize do
54
+ @service_credential ||= Application[:service_credential]
55
+ end
56
+ end
57
+
58
+ private def build_download_uri(download_url)
59
+ uri = URI.join(BASE_URL, download_url)
60
+ params = {username: service_credential.username, token: service_credential.token}
61
+ uri.query = URI.encode_www_form(params)
62
+ uri
63
+ end
64
+ end
65
+ end
66
+ end