@uofx/cli 1.0.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 (238) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +444 -0
  3. package/THIRD-PARTY-NOTICES.txt +894 -0
  4. package/dist/application/dtos/index.js +24 -0
  5. package/dist/application/dtos/request/delete-instance.request.dto.js +3 -0
  6. package/dist/application/dtos/request/get-config.request.dto.js +3 -0
  7. package/dist/application/dtos/request/get-credentials.request.dto.js +3 -0
  8. package/dist/application/dtos/request/index.js +27 -0
  9. package/dist/application/dtos/request/install-instance.request.dto.js +3 -0
  10. package/dist/application/dtos/request/list-charts.request.dto.js +3 -0
  11. package/dist/application/dtos/request/set-config.request.dto.js +16 -0
  12. package/dist/application/dtos/request/setup-environment.request.dto.js +16 -0
  13. package/dist/application/dtos/request/show-logs.request.dto.js +19 -0
  14. package/dist/application/dtos/request/start-instance.request.dto.js +3 -0
  15. package/dist/application/dtos/request/stop-instance.request.dto.js +3 -0
  16. package/dist/application/dtos/response/credentials.response.dto.js +3 -0
  17. package/dist/application/dtos/response/delete-instance.response.dto.js +3 -0
  18. package/dist/application/dtos/response/index.js +26 -0
  19. package/dist/application/dtos/response/install-instance.response.dto.js +3 -0
  20. package/dist/application/dtos/response/instance-list.response.dto.js +3 -0
  21. package/dist/application/dtos/response/instance-status.response.dto.js +3 -0
  22. package/dist/application/dtos/response/setup-result.response.dto.js +3 -0
  23. package/dist/application/dtos/response/show-logs.response.dto.js +3 -0
  24. package/dist/application/dtos/response/start-instance.response.dto.js +3 -0
  25. package/dist/application/dtos/response/stop-instance.response.dto.js +3 -0
  26. package/dist/application/index.js +25 -0
  27. package/dist/application/interfaces/index.js +24 -0
  28. package/dist/application/interfaces/use-case.interface.js +3 -0
  29. package/dist/application/use-cases/config/get-config.use-case.js +66 -0
  30. package/dist/application/use-cases/config/set-config.use-case.js +49 -0
  31. package/dist/application/use-cases/credentials/get-credentials.use-case.js +57 -0
  32. package/dist/application/use-cases/index.js +28 -0
  33. package/dist/application/use-cases/instance/delete-instance.use-case.js +81 -0
  34. package/dist/application/use-cases/instance/index.js +23 -0
  35. package/dist/application/use-cases/instance/install-instance.use-case.js +424 -0
  36. package/dist/application/use-cases/instance/list-charts.use-case.js +43 -0
  37. package/dist/application/use-cases/instance/list-instances.use-case.js +62 -0
  38. package/dist/application/use-cases/instance/start-instance.use-case.js +154 -0
  39. package/dist/application/use-cases/instance/stop-instance.use-case.js +55 -0
  40. package/dist/application/use-cases/logs/show-logs.use-case.js +66 -0
  41. package/dist/application/use-cases/setup/setup-environment.use-case.js +53 -0
  42. package/dist/cli.js +286 -0
  43. package/dist/constants/config-defaults.js +23 -0
  44. package/dist/constants/defaults.js +89 -0
  45. package/dist/constants/deployment.js +39 -0
  46. package/dist/constants/environments.js +93 -0
  47. package/dist/constants/index.js +53 -0
  48. package/dist/constants/oci-artifacts.js +25 -0
  49. package/dist/constants/paths.js +92 -0
  50. package/dist/constants/timeouts.js +60 -0
  51. package/dist/di/container.js +34 -0
  52. package/dist/di/index.js +22 -0
  53. package/dist/di/modules/application.module.js +54 -0
  54. package/dist/di/modules/infrastructure.module.js +206 -0
  55. package/dist/di/modules/interceptor.module.js +68 -0
  56. package/dist/di/modules/presentation.module.js +31 -0
  57. package/dist/di/tokens.js +149 -0
  58. package/dist/domain/decorators/sensitive.decorator.js +39 -0
  59. package/dist/domain/entities/credentials-resolver.entity.js +127 -0
  60. package/dist/domain/entities/credentials.entity.js +65 -0
  61. package/dist/domain/entities/delete-instance-validation.entity.js +100 -0
  62. package/dist/domain/entities/deployment-parameters.entity.js +120 -0
  63. package/dist/domain/entities/environment-validation.entity.js +125 -0
  64. package/dist/domain/entities/index.js +29 -0
  65. package/dist/domain/entities/instance-lifecycle-state.entity.js +100 -0
  66. package/dist/domain/entities/instance-list-aggregator.entity.js +104 -0
  67. package/dist/domain/entities/instance-metadata.entity.js +86 -0
  68. package/dist/domain/entities/instance-status.entity.js +79 -0
  69. package/dist/domain/entities/instance.entity.js +128 -0
  70. package/dist/domain/entities/log-filter.entity.js +141 -0
  71. package/dist/domain/index.js +29 -0
  72. package/dist/domain/interfaces/safe-loggable.interface.js +3 -0
  73. package/dist/domain/ports/app-config.port.js +3 -0
  74. package/dist/domain/ports/base-image.port.js +3 -0
  75. package/dist/domain/ports/chart-version.port.js +3 -0
  76. package/dist/domain/ports/correlation-id.port.js +3 -0
  77. package/dist/domain/ports/credentials.port.js +3 -0
  78. package/dist/domain/ports/deployment.port.js +3 -0
  79. package/dist/domain/ports/error-handler.port.js +3 -0
  80. package/dist/domain/ports/index.js +46 -0
  81. package/dist/domain/ports/instance-manager.port.js +3 -0
  82. package/dist/domain/ports/instance-metadata.port.js +8 -0
  83. package/dist/domain/ports/instance-storage.port.js +3 -0
  84. package/dist/domain/ports/k8s-deployer.port.js +3 -0
  85. package/dist/domain/ports/logger.port.js +14 -0
  86. package/dist/domain/ports/output.port.js +3 -0
  87. package/dist/domain/ports/runtime-environment.port.js +6 -0
  88. package/dist/domain/ports/user-interaction.port.js +9 -0
  89. package/dist/domain/ports/user-settings.port.js +3 -0
  90. package/dist/domain/types/index.js +22 -0
  91. package/dist/domain/types/logger.types.js +29 -0
  92. package/dist/domain/types/validation.types.js +9 -0
  93. package/dist/domain/value-objects/acr-credentials.value-object.js +92 -0
  94. package/dist/domain/value-objects/chart-version.value-object.js +124 -0
  95. package/dist/domain/value-objects/config-log-level.value-object.js +84 -0
  96. package/dist/domain/value-objects/connection-info.value-object.js +65 -0
  97. package/dist/domain/value-objects/index.js +25 -0
  98. package/dist/domain/value-objects/instance-name.value-object.js +91 -0
  99. package/dist/domain/value-objects/jwt-key.value-object.js +97 -0
  100. package/dist/domain/value-objects/mssql-password.value-object.js +140 -0
  101. package/dist/domain/value-objects/rsa-key-pair.value-object.js +181 -0
  102. package/dist/index.js +6 -0
  103. package/dist/infrastructure/config/app-config.interface.js +3 -0
  104. package/dist/infrastructure/config/app-config.service.js +280 -0
  105. package/dist/infrastructure/config/config-validator.js +31 -0
  106. package/dist/infrastructure/config/crypto.service.js +125 -0
  107. package/dist/infrastructure/deployment/deployment.adapter.js +118 -0
  108. package/dist/infrastructure/deployment/interfaces/acr-credential-manager.interface.js +3 -0
  109. package/dist/infrastructure/deployment/interfaces/app-manager.interface.js +3 -0
  110. package/dist/infrastructure/deployment/interfaces/helm-registry.interface.js +3 -0
  111. package/dist/infrastructure/deployment/interfaces/infra-manager.interface.js +3 -0
  112. package/dist/infrastructure/deployment/interfaces/k8s-job-runner.interface.js +3 -0
  113. package/dist/infrastructure/deployment/interfaces/mssql-database-init.interface.js +3 -0
  114. package/dist/infrastructure/deployment/interfaces/mssql-helm-deployment.interface.js +3 -0
  115. package/dist/infrastructure/deployment/interfaces/mssql-storage.interface.js +3 -0
  116. package/dist/infrastructure/deployment/interfaces/mssql-user-manager.interface.js +3 -0
  117. package/dist/infrastructure/deployment/interfaces/oci-artifact.interface.js +3 -0
  118. package/dist/infrastructure/deployment/interfaces/secret-manager.interface.js +3 -0
  119. package/dist/infrastructure/deployment/interfaces/service-manager.interface.js +3 -0
  120. package/dist/infrastructure/deployment/interfaces/version-compatibility.interface.js +3 -0
  121. package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +144 -0
  122. package/dist/infrastructure/deployment/services/app-manager.service.js +193 -0
  123. package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +163 -0
  124. package/dist/infrastructure/deployment/services/helm-registry.service.js +126 -0
  125. package/dist/infrastructure/deployment/services/infra-manager.service.js +130 -0
  126. package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +194 -0
  127. package/dist/infrastructure/deployment/services/mssql-database-init.service.js +139 -0
  128. package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +100 -0
  129. package/dist/infrastructure/deployment/services/mssql-storage.service.js +54 -0
  130. package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +66 -0
  131. package/dist/infrastructure/deployment/services/oci-artifact.service.js +289 -0
  132. package/dist/infrastructure/deployment/services/secret-manager.service.js +179 -0
  133. package/dist/infrastructure/deployment/services/service-manager.service.js +82 -0
  134. package/dist/infrastructure/deployment/services/version-compatibility.service.js +291 -0
  135. package/dist/infrastructure/environment/interfaces/hardware-info.interface.js +3 -0
  136. package/dist/infrastructure/environment/interfaces/network-checker.interface.js +3 -0
  137. package/dist/infrastructure/environment/services/hardware-info.service.js +135 -0
  138. package/dist/infrastructure/environment/services/network-checker.service.js +142 -0
  139. package/dist/infrastructure/environment/windows-environment.adapter.js +162 -0
  140. package/dist/infrastructure/errors/app-error.js +73 -0
  141. package/dist/infrastructure/errors/error-handler.interface.js +3 -0
  142. package/dist/infrastructure/errors/error-handler.js +218 -0
  143. package/dist/infrastructure/errors/exit-codes.js +27 -0
  144. package/dist/infrastructure/errors/index.js +25 -0
  145. package/dist/infrastructure/execution/builders/base-command.builder.js +122 -0
  146. package/dist/infrastructure/execution/builders/host-command.builder.js +58 -0
  147. package/dist/infrastructure/execution/builders/windows-host-command.builder.js +50 -0
  148. package/dist/infrastructure/execution/builders/wsl-command.builder.js +29 -0
  149. package/dist/infrastructure/execution/command-builder.js +252 -0
  150. package/dist/infrastructure/execution/command-executor.service.js +230 -0
  151. package/dist/infrastructure/execution/environments/wsl-execution.environment.js +70 -0
  152. package/dist/infrastructure/execution/execution-environment.factory.js +53 -0
  153. package/dist/infrastructure/execution/index.js +25 -0
  154. package/dist/infrastructure/execution/interfaces/command-builder.interface.js +3 -0
  155. package/dist/infrastructure/execution/interfaces/command-executor.interface.js +3 -0
  156. package/dist/infrastructure/execution/interfaces/execution-environment-factory.interface.js +3 -0
  157. package/dist/infrastructure/execution/interfaces/execution-environment.interface.js +7 -0
  158. package/dist/infrastructure/execution/interfaces/host-command-builder.interface.js +3 -0
  159. package/dist/infrastructure/execution/interfaces/index.js +23 -0
  160. package/dist/infrastructure/execution/interfaces/script-executor.interface.js +3 -0
  161. package/dist/infrastructure/execution/script-executor.service.js +171 -0
  162. package/dist/infrastructure/http/http-client.service.js +176 -0
  163. package/dist/infrastructure/http/index.js +18 -0
  164. package/dist/infrastructure/http/interfaces/http-client.interface.js +3 -0
  165. package/dist/infrastructure/interceptors/index.js +8 -0
  166. package/dist/infrastructure/interceptors/interceptor.factory.js +44 -0
  167. package/dist/infrastructure/interceptors/interceptor.interface.js +3 -0
  168. package/dist/infrastructure/interceptors/logging.interceptor.js +171 -0
  169. package/dist/infrastructure/logger/correlation-id.adapter.js +68 -0
  170. package/dist/infrastructure/logger/index.js +23 -0
  171. package/dist/infrastructure/logger/interfaces/index.js +22 -0
  172. package/dist/infrastructure/logger/interfaces/log-reader.repository.interface.js +7 -0
  173. package/dist/infrastructure/logger/interfaces/log-writer.repository.interface.js +7 -0
  174. package/dist/infrastructure/logger/logger.adapter.js +274 -0
  175. package/dist/infrastructure/logger/services/file-log-reader.repository.js +148 -0
  176. package/dist/infrastructure/logger/services/file-log-writer.repository.js +307 -0
  177. package/dist/infrastructure/logger/services/index.js +22 -0
  178. package/dist/infrastructure/persistence/index.js +25 -0
  179. package/dist/infrastructure/persistence/instance-metadata.adapter.js +100 -0
  180. package/dist/infrastructure/persistence/instance-storage.adapter.js +64 -0
  181. package/dist/infrastructure/persistence/interfaces/config.repository.interface.js +3 -0
  182. package/dist/infrastructure/persistence/interfaces/index.js +22 -0
  183. package/dist/infrastructure/persistence/interfaces/instance.repository.interface.js +3 -0
  184. package/dist/infrastructure/persistence/services/file-system-config.repository.js +168 -0
  185. package/dist/infrastructure/persistence/services/file-system-instance.repository.js +170 -0
  186. package/dist/infrastructure/persistence/services/index.js +22 -0
  187. package/dist/infrastructure/persistence/user-settings.adapter.js +55 -0
  188. package/dist/infrastructure/platform-detector.js +71 -0
  189. package/dist/infrastructure/platforms/windows/interfaces/microk8s.interface.js +3 -0
  190. package/dist/infrastructure/platforms/windows/interfaces/rootfs-manager.interface.js +3 -0
  191. package/dist/infrastructure/platforms/windows/interfaces/windows-features.interface.js +3 -0
  192. package/dist/infrastructure/platforms/windows/interfaces/windows-info.interface.js +3 -0
  193. package/dist/infrastructure/platforms/windows/interfaces/wsl-config.interface.js +3 -0
  194. package/dist/infrastructure/platforms/windows/interfaces/wsl-info.interface.js +3 -0
  195. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-inspection.interface.js +3 -0
  196. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-lifecycle.interface.js +3 -0
  197. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-naming.interface.js +3 -0
  198. package/dist/infrastructure/platforms/windows/interfaces/wsl-manager.interface.js +3 -0
  199. package/dist/infrastructure/platforms/windows/interfaces/wsl-resources.interface.js +8 -0
  200. package/dist/infrastructure/platforms/windows/interfaces/wsl-updater.interface.js +3 -0
  201. package/dist/infrastructure/platforms/windows/interfaces/wslconfig-parser.interface.js +3 -0
  202. package/dist/infrastructure/platforms/windows/parsers/wsl-version.parser.js +133 -0
  203. package/dist/infrastructure/platforms/windows/services/microk8s.service.js +168 -0
  204. package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +336 -0
  205. package/dist/infrastructure/platforms/windows/services/windows-features.service.js +191 -0
  206. package/dist/infrastructure/platforms/windows/services/windows-info.service.js +138 -0
  207. package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +171 -0
  208. package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +226 -0
  209. package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +325 -0
  210. package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +442 -0
  211. package/dist/infrastructure/platforms/windows/services/wsl-instance-naming.service.js +93 -0
  212. package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +273 -0
  213. package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +222 -0
  214. package/dist/infrastructure/platforms/windows/wsl-base-image.adapter.js +41 -0
  215. package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +150 -0
  216. package/dist/infrastructure/utils/error-formatter.util.js +29 -0
  217. package/dist/infrastructure/utils/file-operations.util.js +201 -0
  218. package/dist/infrastructure/utils/input-validator.util.js +152 -0
  219. package/dist/infrastructure/utils/retry.util.js +98 -0
  220. package/dist/presentation/controllers/config.controller.js +146 -0
  221. package/dist/presentation/controllers/credentials.controller.js +105 -0
  222. package/dist/presentation/controllers/index.js +25 -0
  223. package/dist/presentation/controllers/instance.controller.js +363 -0
  224. package/dist/presentation/controllers/logs.controller.js +103 -0
  225. package/dist/presentation/controllers/setup.controller.js +175 -0
  226. package/dist/presentation/interfaces/cli-options.interface.js +8 -0
  227. package/dist/presentation/prompts/acr-credentials.prompt.js +76 -0
  228. package/dist/presentation/prompts/index.js +21 -0
  229. package/dist/presentation/ui/cli-progress.service.js +193 -0
  230. package/dist/presentation/ui/constants/output-symbols.js +42 -0
  231. package/dist/presentation/ui/index.js +27 -0
  232. package/dist/presentation/ui/interaction.service.js +276 -0
  233. package/dist/presentation/ui/interfaces/cli-progress.interface.js +9 -0
  234. package/dist/presentation/ui/interfaces/output-formatter.interface.js +23 -0
  235. package/dist/presentation/ui/log-level.enum.js +66 -0
  236. package/dist/presentation/ui/output-builder.service.js +378 -0
  237. package/dist/presentation/ui/output-formatter.service.js +393 -0
  238. package/package.json +65 -0
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WslVersionParser = void 0;
4
+ /**
5
+ * WSL 版本解析器
6
+ *
7
+ * 負責解析 WSL 命令輸出的版本資訊
8
+ * 支援英文與中文(繁體/簡體)系統語言
9
+ *
10
+ * 設計原則:
11
+ * - 將 Regex 解析邏輯隔離,方便測試
12
+ * - 容錯編碼問題(Unicode BOM、不同編碼)
13
+ * - 支援多語系輸出
14
+ */
15
+ class WslVersionParser {
16
+ /**
17
+ * 解析 WSL 版本號
18
+ * @param output wsl --version 的輸出
19
+ * @returns WSL 版本號,解析失敗返回 null
20
+ *
21
+ * @example
22
+ * // English: "WSL version: 2.0.9.0"
23
+ * // Chinese: "WSL 版本: 2.0.9.0"
24
+ * parseWslVersion(output) // "2.0.9.0"
25
+ */
26
+ static parseWslVersion(output) {
27
+ if (!output || typeof output !== 'string') {
28
+ return null;
29
+ }
30
+ // 清理輸出(移除 BOM、標準化空白)
31
+ const cleanOutput = this.cleanOutput(output);
32
+ // 支援多種格式:
33
+ // - English: "WSL version: 2.0.9.0" or "WSL version 2.0.9.0"
34
+ // - Chinese (Traditional): "WSL 版本: 2.0.9.0" or "WSL 版本: 2.0.9.0"
35
+ // - Chinese (Simplified): "WSL 版本: 2.0.9.0"
36
+ const patterns = [
37
+ /WSL\s+version[:\s]+([0-9.]+)/i,
38
+ /WSL\s*版本[::\s]+([0-9.]+)/,
39
+ ];
40
+ for (const pattern of patterns) {
41
+ const match = cleanOutput.match(pattern);
42
+ if (match && match[1]) {
43
+ return match[1];
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * 解析 Kernel 版本號
50
+ * @param output wsl --version 的輸出
51
+ * @returns Kernel 版本號,解析失敗返回 null
52
+ *
53
+ * @example
54
+ * // English: "Kernel version: 5.15.133.1-1"
55
+ * // Chinese: "核心版本: 5.15.133.1-1"
56
+ * parseKernelVersion(output) // "5.15.133.1-1"
57
+ */
58
+ static parseKernelVersion(output) {
59
+ if (!output || typeof output !== 'string') {
60
+ return null;
61
+ }
62
+ const cleanOutput = this.cleanOutput(output);
63
+ // 支援多種格式:
64
+ // - English: "Kernel version: 5.15.133.1-1"
65
+ // - Chinese: "核心版本: 5.15.133.1-1"
66
+ const patterns = [
67
+ /Kernel\s+version[:\s]+([0-9.-]+)/i,
68
+ /核心版本[::\s]+([0-9.-]+)/,
69
+ ];
70
+ for (const pattern of patterns) {
71
+ const match = cleanOutput.match(pattern);
72
+ if (match && match[1]) {
73
+ return match[1];
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ /**
79
+ * 解析預設 WSL 版本
80
+ * @param output wsl --status 的輸出
81
+ * @returns 預設版本號 (1 或 2),解析失敗返回 null
82
+ *
83
+ * @example
84
+ * // English: "Default Version: 2"
85
+ * // Chinese: "預設版本: 2" or "默认版本: 2"
86
+ * parseDefaultVersion(output) // 2
87
+ */
88
+ static parseDefaultVersion(output) {
89
+ if (!output || typeof output !== 'string') {
90
+ return null;
91
+ }
92
+ const cleanOutput = this.cleanOutput(output);
93
+ // 支援多種格式:
94
+ // - English: "Default Version: 2"
95
+ // - Chinese (Traditional): "預設版本: 2"
96
+ // - Chinese (Simplified): "默认版本: 2"
97
+ const patterns = [
98
+ /Default\s+Version[:\s]+(\d+)/i,
99
+ /預設版本[::\s]+(\d+)/,
100
+ /默认版本[::\s]+(\d+)/,
101
+ ];
102
+ for (const pattern of patterns) {
103
+ const match = cleanOutput.match(pattern);
104
+ if (match && match[1]) {
105
+ const version = parseInt(match[1], 10);
106
+ if (!isNaN(version) && (version === 1 || version === 2)) {
107
+ return version;
108
+ }
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ /**
114
+ * 清理命令輸出
115
+ * - 移除 Unicode BOM
116
+ * - 標準化換行符
117
+ * - 移除多餘空白
118
+ */
119
+ static cleanOutput(output) {
120
+ return output
121
+ // 移除 UTF-8 BOM
122
+ .replace(/^\uFEFF/, '')
123
+ // 移除 UTF-16 BOM
124
+ .replace(/^\uFFFE/, '')
125
+ // 標準化換行符
126
+ .replace(/\r\n/g, '\n')
127
+ .replace(/\r/g, '\n')
128
+ // 移除多餘空白但保留結構
129
+ .trim();
130
+ }
131
+ }
132
+ exports.WslVersionParser = WslVersionParser;
133
+ //# sourceMappingURL=wsl-version.parser.js.map
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MicroK8sService = void 0;
16
+ const tsyringe_1 = require("tsyringe");
17
+ const tokens_1 = require("../../../../di/tokens");
18
+ const index_1 = require("../../../../constants/index");
19
+ const input_validator_util_1 = require("../../../utils/input-validator.util");
20
+ /**
21
+ * MicroK8s 服務
22
+ *
23
+ * 負責 MicroK8s 的狀態檢查、啟動、停止與等待操作
24
+ */
25
+ let MicroK8sService = class MicroK8sService {
26
+ constructor(envFactory, logger) {
27
+ this.envFactory = envFactory;
28
+ this.logger = logger;
29
+ }
30
+ /**
31
+ * 檢查 MicroK8s 是否正在執行
32
+ */
33
+ async isRunning(instanceName) {
34
+ try {
35
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
36
+ const env = this.envFactory.getForInstance(instanceName);
37
+ const builder = env.shell('systemctl')
38
+ .arg('is-active')
39
+ .arg('snap.microk8s.daemon-kubelite');
40
+ const result = await builder.exec();
41
+ return result.exitCode === 0 && result.stdout.trim() === 'active';
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * 啟動 MicroK8s
49
+ */
50
+ async start(instanceName) {
51
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
52
+ this.logger.debug('[MicroK8s] Starting MicroK8s', { instanceName });
53
+ const env = this.envFactory.getForInstance(instanceName);
54
+ const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
55
+ .arg('start');
56
+ await builder.exec();
57
+ }
58
+ /**
59
+ * 停止 MicroK8s
60
+ */
61
+ async stop(instanceName) {
62
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
63
+ this.logger.debug('[MicroK8s] Stopping MicroK8s', { instanceName });
64
+ const env = this.envFactory.getForInstance(instanceName);
65
+ const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
66
+ .arg('stop');
67
+ await builder.exec();
68
+ }
69
+ /**
70
+ * 等待 MicroK8s 停止
71
+ */
72
+ async waitForStop() {
73
+ await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.MICROK8S_STARTUP));
74
+ return true;
75
+ }
76
+ /**
77
+ * 等待 MicroK8s 就緒
78
+ */
79
+ async waitForReady(instanceName) {
80
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
81
+ this.logger.debug('[MicroK8s] Waiting for MicroK8s to be ready', { instanceName });
82
+ try {
83
+ const env = this.envFactory.getForInstance(instanceName);
84
+ const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
85
+ .arg('status')
86
+ .arg('--wait-ready');
87
+ const result = await builder.exec();
88
+ return result.exitCode === 0;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ /**
95
+ * 等待所有 Pods 就緒
96
+ */
97
+ async waitForPodsReady(instanceName, timeoutMs = index_1.TIMEOUTS.K8S_READY_TIMEOUT) {
98
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
99
+ const startTime = Date.now();
100
+ let lastCheckTime = 0;
101
+ const env = this.envFactory.getForInstance(instanceName);
102
+ while (Date.now() - startTime < timeoutMs) {
103
+ const now = Date.now();
104
+ if (now - lastCheckTime < index_1.TIMEOUTS.K8S_READY_CHECK_INTERVAL) {
105
+ await new Promise((resolve) => setTimeout(resolve, 5000));
106
+ continue;
107
+ }
108
+ lastCheckTime = now;
109
+ try {
110
+ const builder = env.kubectl('get')
111
+ .arg('pods')
112
+ .arg('--all-namespaces')
113
+ .arg('-o', 'json');
114
+ const { stdout, exitCode } = await builder.exec();
115
+ if (exitCode !== 0)
116
+ continue;
117
+ const pods = JSON.parse(stdout);
118
+ if (!pods.items || pods.items.length === 0) {
119
+ continue;
120
+ }
121
+ const allReady = pods.items.every((pod) => {
122
+ if (pod.status.phase === 'Succeeded') {
123
+ return true;
124
+ }
125
+ if (pod.status.phase === 'Failed') {
126
+ return false;
127
+ }
128
+ const isRunning = pod.status.phase === 'Running';
129
+ const readyCondition = pod.status.conditions?.find((c) => c.type === 'Ready');
130
+ const isReady = readyCondition && readyCondition.status === 'True';
131
+ return isRunning && isReady;
132
+ });
133
+ if (allReady) {
134
+ this.logger.debug('[MicroK8s] All pods are ready', { instanceName });
135
+ return true;
136
+ }
137
+ }
138
+ catch {
139
+ // Ignore and retry
140
+ }
141
+ }
142
+ this.logger.warn('[MicroK8s] Timeout waiting for pods to be ready', { instanceName, timeoutMs });
143
+ return false;
144
+ }
145
+ /**
146
+ * 重啟 containerd 服務
147
+ */
148
+ async restartContainerd(instanceName) {
149
+ (0, input_validator_util_1.requireValidInstanceName)(instanceName);
150
+ this.logger.debug('[MicroK8s] Restarting containerd', { instanceName });
151
+ const env = this.envFactory.getForInstance(instanceName);
152
+ // 停止
153
+ const restartBuilder = env.shell('sudo')
154
+ .arg('systemctl')
155
+ .arg('restart')
156
+ .arg('snap.microk8s.daemon-containerd');
157
+ await restartBuilder.exec();
158
+ await new Promise(resolve => setTimeout(resolve, 3000));
159
+ }
160
+ };
161
+ exports.MicroK8sService = MicroK8sService;
162
+ exports.MicroK8sService = MicroK8sService = __decorate([
163
+ (0, tsyringe_1.injectable)(),
164
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
165
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
166
+ __metadata("design:paramtypes", [Object, Object])
167
+ ], MicroK8sService);
168
+ //# sourceMappingURL=microk8s.service.js.map
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.RootfsManagerService = void 0;
49
+ const tsyringe_1 = require("tsyringe");
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const os = __importStar(require("os"));
53
+ const crypto = __importStar(require("crypto"));
54
+ const tokens_1 = require("../../../../di/tokens");
55
+ const errors_1 = require("../../../errors");
56
+ const index_1 = require("../../../../constants/index");
57
+ /**
58
+ * Rootfs 管理相關常數
59
+ */
60
+ const CACHE_DIR = path.join(os.homedir(), index_1.WINDOWS_PATHS.USER_CONFIG_DIR);
61
+ const ROOTFS_FILENAME = 'ubuntu-22.04-root.tar.xz';
62
+ const ROOTFS_URL = 'https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64-root.tar.xz';
63
+ const SHA256SUMS_URL = 'https://cloud-images.ubuntu.com/releases/jammy/release/SHA256SUMS';
64
+ /**
65
+ * Ubuntu Rootfs 管理服務
66
+ */
67
+ let RootfsManagerService = class RootfsManagerService {
68
+ /**
69
+ * 建立新的 RootfsManager 實例
70
+ * @param httpClient - 用於下載檔案的 HTTP 客戶端 (依賴注入)
71
+ * @param appLogger - 應用程式日誌記錄器
72
+ * @param cliProgress - CLI 進度條服務
73
+ */
74
+ constructor(httpClient, appLogger, cliProgress) {
75
+ this.appLogger = appLogger;
76
+ this.cliProgress = cliProgress;
77
+ this.httpClient = httpClient;
78
+ this.cacheDir = CACHE_DIR;
79
+ this.cachePath = path.join(this.cacheDir, ROOTFS_FILENAME);
80
+ this.rootfsUrl = ROOTFS_URL;
81
+ this.sha256SumsUrl = SHA256SUMS_URL;
82
+ }
83
+ /**
84
+ * 確保快取目錄存在
85
+ * 若不存在則建立目錄
86
+ */
87
+ ensureCacheDirectory() {
88
+ if (!fs.existsSync(this.cacheDir)) {
89
+ fs.mkdirSync(this.cacheDir, { recursive: true });
90
+ }
91
+ }
92
+ /**
93
+ * 檢查快取 rootfs 是否存在且有效
94
+ * @returns 快取檢查結果
95
+ */
96
+ checkCache() {
97
+ // 檢查檔案是否存在
98
+ if (!fs.existsSync(this.cachePath)) {
99
+ return {
100
+ exists: false,
101
+ path: this.cachePath,
102
+ valid: false,
103
+ reason: 'Cache file does not exist',
104
+ };
105
+ }
106
+ // 取得檔案狀態
107
+ const stats = fs.statSync(this.cachePath);
108
+ // 基本驗證 - 檔案應大於 100MB (rootfs 約 400MB)
109
+ if (stats.size < index_1.FILE_SIZE.MIN_ROOTFS_SIZE) {
110
+ return {
111
+ exists: true,
112
+ path: this.cachePath,
113
+ valid: false,
114
+ size: stats.size,
115
+ reason: 'Cache file is too small, may be corrupted',
116
+ };
117
+ }
118
+ return {
119
+ exists: true,
120
+ path: this.cachePath,
121
+ valid: true,
122
+ size: stats.size,
123
+ };
124
+ }
125
+ /**
126
+ * 計算檔案的 SHA256 雜湊值
127
+ * @param filePath - 檔案路徑
128
+ * @returns 解析為 hex 編碼 SHA256 雜湊值的 Promise
129
+ */
130
+ calculateSHA256(filePath) {
131
+ return new Promise((resolve, reject) => {
132
+ const hash = crypto.createHash('sha256');
133
+ const stream = fs.createReadStream(filePath);
134
+ stream.on('data', (data) => hash.update(data));
135
+ stream.on('end', () => resolve(hash.digest('hex')));
136
+ stream.on('error', (error) => reject(error));
137
+ });
138
+ }
139
+ /**
140
+ * 下載並解析 SHA256SUMS 檔案
141
+ * @returns 檔案名稱對應 SHA256 雜湊值的 Map
142
+ */
143
+ async downloadSHA256Sums() {
144
+ try {
145
+ this.appLogger?.debug('Downloading SHA256SUMS file', { url: this.sha256SumsUrl });
146
+ const responseText = await this.httpClient.fetchText(this.sha256SumsUrl);
147
+ const sums = new Map();
148
+ const lines = responseText.split('\n');
149
+ for (const line of lines) {
150
+ const trimmed = line.trim();
151
+ if (!trimmed)
152
+ continue;
153
+ // 格式: <hash> *<filename> 或 <hash> <filename>
154
+ const match = trimmed.match(/^([a-f0-9]{64})\s+\*?(.+)$/);
155
+ if (match) {
156
+ const [, hash, filename] = match;
157
+ sums.set(filename, hash);
158
+ }
159
+ }
160
+ return sums;
161
+ }
162
+ catch (error) {
163
+ this.appLogger?.error(error instanceof Error ? error : new Error(String(error)), {
164
+ operation: 'downloadSHA256Sums',
165
+ url: this.sha256SumsUrl,
166
+ });
167
+ throw new errors_1.AppError('Failed to download SHA256SUMS file', {
168
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
169
+ solution: 'Check your internet connection and try again',
170
+ cause: error instanceof Error ? error : new Error(String(error)),
171
+ context: { operation: 'downloadSHA256Sums', resource: this.sha256SumsUrl }
172
+ });
173
+ }
174
+ }
175
+ /**
176
+ * 驗證快取檔案是否符合預期的 SHA256 雜湊值
177
+ * @param expectedHash - 預期的 SHA256 雜湊值
178
+ * @returns 若雜湊值相符則回傳 true,否則回傳 false
179
+ */
180
+ async verifyCachedFile(expectedHash) {
181
+ if (!fs.existsSync(this.cachePath)) {
182
+ return false;
183
+ }
184
+ const actualHash = await this.calculateSHA256(this.cachePath);
185
+ return actualHash.toLowerCase() === expectedHash.toLowerCase();
186
+ }
187
+ /**
188
+ * 取得 rootfs 檔案的預期 SHA256 雜湊值
189
+ * @returns 預期的 SHA256 雜湊值
190
+ */
191
+ async getExpectedHash() {
192
+ const sums = await this.downloadSHA256Sums();
193
+ const rootfsFilename = path.basename(this.rootfsUrl);
194
+ const hash = sums.get(rootfsFilename);
195
+ if (!hash) {
196
+ throw new Error(`SHA256 hash not found for ${rootfsFilename}`);
197
+ }
198
+ return hash;
199
+ }
200
+ /**
201
+ * 下載 rootfs 並顯示進度
202
+ * @param onProgress - 可選的進度回調函式
203
+ * @returns 下載結果
204
+ */
205
+ async downloadRootfs(onProgress) {
206
+ // 確保快取目錄存在
207
+ this.ensureCacheDirectory();
208
+ // 建立臨時檔案路徑
209
+ const tempPath = `${this.cachePath}.tmp`;
210
+ try {
211
+ this.appLogger?.debug('Starting rootfs download', {
212
+ url: this.rootfsUrl,
213
+ tempPath,
214
+ });
215
+ // 使用注入的 HTTP 客戶端下載
216
+ await this.httpClient.downloadFile(this.rootfsUrl, tempPath, onProgress);
217
+ // 驗證下載的檔案
218
+ const expectedHash = await this.getExpectedHash();
219
+ const actualHash = await this.calculateSHA256(tempPath);
220
+ const verified = actualHash.toLowerCase() === expectedHash.toLowerCase();
221
+ if (!verified) {
222
+ // 清理無效檔案
223
+ fs.unlinkSync(tempPath);
224
+ throw new Error('SHA256 verification failed - downloaded file may be corrupted');
225
+ }
226
+ // 移動臨時檔案到最終位置
227
+ if (fs.existsSync(this.cachePath)) {
228
+ fs.unlinkSync(this.cachePath);
229
+ }
230
+ fs.renameSync(tempPath, this.cachePath);
231
+ // 取得檔案大小
232
+ const stats = fs.statSync(this.cachePath);
233
+ this.appLogger?.debug('Rootfs download completed and verified', {
234
+ path: this.cachePath,
235
+ size: stats.size,
236
+ });
237
+ return {
238
+ success: true,
239
+ path: this.cachePath,
240
+ size: stats.size,
241
+ verified: true,
242
+ };
243
+ }
244
+ catch (error) {
245
+ // 發生錯誤時清理臨時檔案
246
+ if (fs.existsSync(tempPath)) {
247
+ fs.unlinkSync(tempPath);
248
+ }
249
+ this.appLogger?.error(error instanceof Error ? error : new Error(String(error)), {
250
+ operation: 'downloadRootfs',
251
+ url: this.rootfsUrl,
252
+ });
253
+ throw new errors_1.AppError('Failed to download Ubuntu rootfs', {
254
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
255
+ solution: 'Check your internet connection and try again. The rootfs file will be downloaded from Ubuntu cloud images.',
256
+ cause: error instanceof Error ? error : new Error(String(error)),
257
+ context: { operation: 'downloadAndVerifyRootfs', resource: this.rootfsUrl }
258
+ });
259
+ }
260
+ }
261
+ /**
262
+ * 下載 rootfs 並顯示內建進度條
263
+ * @returns 下載結果
264
+ */
265
+ async downloadWithProgress() {
266
+ let progressBar;
267
+ try {
268
+ const result = await this.downloadRootfs((loaded, total) => {
269
+ if (!progressBar && this.cliProgress) {
270
+ progressBar = this.cliProgress.createDownloadProgress('Downloading Ubuntu rootfs', total);
271
+ }
272
+ progressBar?.update(loaded);
273
+ });
274
+ progressBar?.stop();
275
+ return result;
276
+ }
277
+ catch (error) {
278
+ progressBar?.stop();
279
+ throw error;
280
+ }
281
+ }
282
+ /**
283
+ * 取得或下載 rootfs
284
+ * 僅在快取不存在或無效時下載
285
+ * @param progress - 進度通知回調
286
+ * @returns rootfs 檔案路徑
287
+ */
288
+ async getOrDownloadRootfs(output) {
289
+ // 檢查快取
290
+ const cacheCheck = this.checkCache();
291
+ if (cacheCheck.exists && cacheCheck.valid) {
292
+ // 驗證雜湊值
293
+ output?.info('Checking cached rootfs...').flush();
294
+ const expectedHash = await this.getExpectedHash();
295
+ const isValid = await this.verifyCachedFile(expectedHash);
296
+ if (isValid) {
297
+ output?.success('Using cached rootfs').flush();
298
+ return this.cachePath;
299
+ }
300
+ else {
301
+ output?.warning('! Cached rootfs checksum mismatch, re-downloading...').flush();
302
+ }
303
+ }
304
+ // 下載
305
+ output?.info('Downloading Ubuntu rootfs...').flush();
306
+ output?.item('arrow', `Source: ${this.rootfsUrl}`).flush();
307
+ output?.item('arrow', `Destination: ${this.cachePath}`).flush();
308
+ const result = await this.downloadWithProgress();
309
+ output?.success('SHA256 checksum verified').flush();
310
+ output?.success('Ubuntu rootfs cached successfully').flush();
311
+ return result.path;
312
+ }
313
+ /**
314
+ * 取得快取路徑
315
+ * @returns 快取 rootfs 檔案路徑
316
+ */
317
+ getCachePath() {
318
+ return this.cachePath;
319
+ }
320
+ /**
321
+ * 取得快取目錄
322
+ * @returns 快取目錄路徑
323
+ */
324
+ getCacheDirectory() {
325
+ return this.cacheDir;
326
+ }
327
+ };
328
+ exports.RootfsManagerService = RootfsManagerService;
329
+ exports.RootfsManagerService = RootfsManagerService = __decorate([
330
+ (0, tsyringe_1.injectable)(),
331
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IHttpClient)),
332
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
333
+ __param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.IProgress)),
334
+ __metadata("design:paramtypes", [Object, Object, Object])
335
+ ], RootfsManagerService);
336
+ //# sourceMappingURL=rootfs-manager.service.js.map