@uofx/cli 1.0.2 → 1.1.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 (240) hide show
  1. package/README.md +159 -3
  2. package/THIRD-PARTY-NOTICES.txt +84 -2
  3. package/dist/application/dtos/config/index.js +22 -0
  4. package/dist/application/dtos/config/request/index.js +19 -0
  5. package/dist/application/dtos/config/request/set-config.request.dto.js +3 -0
  6. package/dist/application/dtos/config/response/get-config.response.dto.js +8 -0
  7. package/dist/application/dtos/config/response/index.js +21 -0
  8. package/dist/application/dtos/dev/index.js +24 -0
  9. package/dist/application/dtos/dev/request/generate-component.request.dto.js +3 -0
  10. package/dist/application/dtos/dev/request/index.js +25 -0
  11. package/dist/application/dtos/dev/request/install-template.request.dto.js +3 -0
  12. package/dist/application/dtos/dev/request/new-project.request.dto.js +3 -0
  13. package/dist/application/dtos/dev/request/publish-plugin.request.dto.js +3 -0
  14. package/dist/application/dtos/dev/request/uninstall-template.request.dto.js +3 -0
  15. package/dist/application/dtos/dev/response/generate-component.response.dto.js +3 -0
  16. package/dist/application/dtos/dev/response/index.js +24 -0
  17. package/dist/application/dtos/dev/response/new-project.response.dto.js +3 -0
  18. package/dist/application/dtos/dev/response/publish-plugin.response.dto.js +3 -0
  19. package/dist/application/dtos/dev/response/template-info.response.dto.js +3 -0
  20. package/dist/application/dtos/env/index.js +24 -0
  21. package/dist/application/dtos/{request → env/request}/index.js +1 -3
  22. package/dist/application/dtos/env/request/list-running-instances.request.dto.js +3 -0
  23. package/dist/application/dtos/{response → env/response}/index.js +1 -1
  24. package/dist/application/dtos/env/response/list-running-instances.response.dto.js +3 -0
  25. package/dist/application/dtos/index.js +9 -2
  26. package/dist/application/dtos/logs/index.js +22 -0
  27. package/dist/application/dtos/logs/request/index.js +18 -0
  28. package/dist/application/dtos/logs/response/index.js +18 -0
  29. package/dist/application/index.js +1 -0
  30. package/dist/application/services/env/environment-validator.service.js +120 -0
  31. package/dist/application/services/env/index.js +11 -0
  32. package/dist/application/services/env/instance-cleanup.service.js +79 -0
  33. package/dist/application/services/index.js +21 -0
  34. package/dist/application/use-cases/config/get-all-config.use-case.js +54 -0
  35. package/dist/application/use-cases/config/get-config.use-case.js +7 -17
  36. package/dist/application/use-cases/config/index.js +23 -0
  37. package/dist/application/use-cases/config/set-config.use-case.js +49 -3
  38. package/dist/application/use-cases/dev/generate-component.use-case.js +138 -0
  39. package/dist/application/use-cases/dev/index.js +28 -0
  40. package/dist/application/use-cases/dev/install-template.use-case.js +60 -0
  41. package/dist/application/use-cases/dev/list-templates.use-case.js +45 -0
  42. package/dist/application/use-cases/dev/new-project.use-case.js +130 -0
  43. package/dist/application/use-cases/dev/publish-plugin.use-case.js +243 -0
  44. package/dist/application/use-cases/dev/uninstall-template.use-case.js +50 -0
  45. package/dist/application/use-cases/{instance → env}/delete-instance.use-case.js +2 -2
  46. package/dist/application/use-cases/{credentials → env}/get-credentials.use-case.js +1 -1
  47. package/dist/application/use-cases/{instance → env}/index.js +11 -0
  48. package/dist/application/use-cases/{instance → env}/install-instance.use-case.js +25 -136
  49. package/dist/application/use-cases/env/list-running-instances.use-case.js +47 -0
  50. package/dist/application/use-cases/{setup → env}/setup-environment.use-case.js +35 -4
  51. package/dist/application/use-cases/{instance → env}/start-instance.use-case.js +53 -41
  52. package/dist/application/use-cases/index.js +9 -6
  53. package/dist/application/use-cases/logs/index.js +21 -0
  54. package/dist/cli.js +175 -26
  55. package/dist/constants/defaults.js +8 -1
  56. package/dist/constants/oci-artifacts.js +4 -0
  57. package/dist/constants/paths.js +0 -4
  58. package/dist/constants/timeouts.js +5 -0
  59. package/dist/di/modules/application.module.js +13 -44
  60. package/dist/di/modules/config.module.js +19 -0
  61. package/dist/di/modules/dev.module.js +45 -0
  62. package/dist/di/modules/env.module.js +42 -0
  63. package/dist/di/modules/index.js +30 -0
  64. package/dist/di/modules/infrastructure.module.js +65 -82
  65. package/dist/di/modules/logs.module.js +15 -0
  66. package/dist/di/modules/presentation.module.js +18 -7
  67. package/dist/di/tokens.js +42 -30
  68. package/dist/domain/entities/credentials-resolver.entity.js +4 -1
  69. package/dist/domain/entities/credentials.entity.js +2 -5
  70. package/dist/domain/entities/delete-instance-validation.entity.js +4 -1
  71. package/dist/domain/entities/deployment-parameters.entity.js +11 -8
  72. package/dist/domain/entities/index.js +5 -1
  73. package/dist/domain/entities/instance-list-aggregator.entity.js +2 -2
  74. package/dist/domain/entities/instance-metadata.entity.js +2 -3
  75. package/dist/domain/entities/instance.entity.js +2 -1
  76. package/dist/domain/entities/log-filter.entity.js +6 -6
  77. package/dist/domain/entities/template-info.entity.js +63 -0
  78. package/dist/{infrastructure/utils/error-formatter.util.js → domain/errors/error-utils.js} +1 -1
  79. package/dist/domain/errors/index.js +19 -0
  80. package/dist/domain/index.js +6 -0
  81. package/dist/domain/interfaces/index.js +21 -0
  82. package/dist/domain/ports/dotnet-publisher.port.js +9 -0
  83. package/dist/{infrastructure/http/interfaces/http-client.interface.js → domain/ports/file-system.port.js} +1 -1
  84. package/dist/domain/ports/http-client.port.js +3 -0
  85. package/dist/domain/ports/index.js +10 -0
  86. package/dist/domain/ports/logger.port.js +3 -3
  87. package/dist/domain/ports/progress.port.js +10 -0
  88. package/dist/domain/ports/project-scaffolder.port.js +9 -0
  89. package/dist/domain/ports/schematics-runner.port.js +9 -0
  90. package/dist/domain/ports/template-manager.port.js +9 -0
  91. package/dist/domain/ports/wsl-setup.port.js +3 -0
  92. package/dist/domain/types/index.js +1 -0
  93. package/dist/domain/value-objects/acr-credentials.value-object.js +13 -1
  94. package/dist/domain/value-objects/chart-version.value-object.js +20 -3
  95. package/dist/domain/value-objects/config-log-level.value-object.js +2 -1
  96. package/dist/domain/value-objects/index.js +3 -1
  97. package/dist/domain/value-objects/instance-name.value-object.js +6 -3
  98. package/dist/domain/value-objects/jwt-key.value-object.js +27 -5
  99. package/dist/domain/value-objects/mssql-password.value-object.js +27 -5
  100. package/dist/domain/value-objects/output-path.value-object.js +85 -0
  101. package/dist/domain/value-objects/rsa-key-pair.value-object.js +29 -3
  102. package/dist/index.js +6 -1
  103. package/dist/infrastructure/config/{app-config.service.js → app-config.adapter.js} +29 -17
  104. package/dist/infrastructure/config/{config-validator.js → config-validator.util.js} +1 -1
  105. package/dist/infrastructure/config/index.js +14 -0
  106. package/dist/infrastructure/config/interfaces/index.js +3 -0
  107. package/dist/infrastructure/config/{crypto.service.js → services/crypto.service.js} +4 -1
  108. package/dist/infrastructure/config/services/index.js +9 -0
  109. package/dist/infrastructure/deployment/deployment.adapter.js +5 -5
  110. package/dist/infrastructure/deployment/index.js +9 -0
  111. package/dist/infrastructure/deployment/interfaces/index.js +3 -0
  112. package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +18 -7
  113. package/dist/infrastructure/deployment/services/app-manager.service.js +3 -3
  114. package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +13 -5
  115. package/dist/infrastructure/deployment/services/helm-registry.service.js +10 -3
  116. package/dist/infrastructure/deployment/services/index.js +35 -0
  117. package/dist/infrastructure/deployment/services/infra-manager.service.js +19 -15
  118. package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +24 -6
  119. package/dist/infrastructure/deployment/services/mssql-database-init.service.js +75 -11
  120. package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +17 -5
  121. package/dist/infrastructure/deployment/services/mssql-storage.service.js +5 -1
  122. package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +7 -3
  123. package/dist/infrastructure/deployment/services/oci-artifact.service.js +8 -3
  124. package/dist/infrastructure/deployment/services/secret-manager.service.js +14 -5
  125. package/dist/infrastructure/deployment/services/service-manager.service.js +1 -1
  126. package/dist/infrastructure/deployment/services/version-compatibility.service.js +9 -5
  127. package/dist/infrastructure/dotnet/dotnet-publisher.adapter.js +143 -0
  128. package/dist/infrastructure/dotnet/index.js +9 -0
  129. package/dist/infrastructure/environment/index.js +11 -0
  130. package/dist/infrastructure/environment/interfaces/index.js +3 -0
  131. package/dist/infrastructure/{platform-detector.js → environment/platform-detector.util.js} +1 -1
  132. package/dist/infrastructure/environment/services/hardware-info.service.js +8 -8
  133. package/dist/infrastructure/environment/services/index.js +11 -0
  134. package/dist/infrastructure/errors/{error-handler.js → error-handler.adapter.js} +17 -17
  135. package/dist/infrastructure/errors/index.js +5 -21
  136. package/dist/infrastructure/execution/builders/base-command.builder.js +32 -11
  137. package/dist/infrastructure/execution/builders/index.js +26 -0
  138. package/dist/infrastructure/execution/builders/wsl-command.builder.js +39 -1
  139. package/dist/infrastructure/execution/command-builder.util.js +94 -0
  140. package/dist/infrastructure/execution/environments/index.js +23 -0
  141. package/dist/infrastructure/execution/environments/wsl-execution.environment.js +22 -7
  142. package/dist/infrastructure/execution/index.js +1 -2
  143. package/dist/infrastructure/execution/services/command-executor.service.js +389 -0
  144. package/dist/infrastructure/execution/services/index.js +8 -0
  145. package/dist/infrastructure/execution/{script-executor.service.js → services/script-executor.service.js} +12 -15
  146. package/dist/infrastructure/filesystem/filesystem.adapter.js +86 -0
  147. package/dist/infrastructure/filesystem/index.js +23 -0
  148. package/dist/infrastructure/http/{http-client.service.js → http-client.adapter.js} +20 -13
  149. package/dist/infrastructure/http/index.js +1 -1
  150. package/dist/infrastructure/interceptors/decorators/index.js +23 -0
  151. package/dist/infrastructure/interceptors/index.js +2 -2
  152. package/dist/infrastructure/interceptors/interceptor.factory.js +3 -3
  153. package/dist/infrastructure/interceptors/interfaces/index.js +3 -0
  154. package/dist/infrastructure/interceptors/services/index.js +6 -0
  155. package/dist/infrastructure/interceptors/{logging.interceptor.js → services/logging-interceptor.service.js} +4 -4
  156. package/dist/infrastructure/logger/services/file-log-reader.repository.js +1 -1
  157. package/dist/infrastructure/logger/services/file-log-writer.repository.js +1 -1
  158. package/dist/infrastructure/persistence/instance-metadata.adapter.js +2 -1
  159. package/dist/infrastructure/persistence/services/file-system-config.repository.js +10 -4
  160. package/dist/infrastructure/persistence/services/file-system-instance.repository.js +2 -2
  161. package/dist/infrastructure/platforms/index.js +18 -0
  162. package/dist/infrastructure/platforms/windows/index.js +13 -0
  163. package/dist/infrastructure/platforms/windows/interfaces/index.js +3 -0
  164. package/dist/infrastructure/platforms/windows/parsers/index.js +9 -0
  165. package/dist/infrastructure/platforms/windows/services/index.js +33 -0
  166. package/dist/infrastructure/platforms/windows/services/microk8s.service.js +28 -10
  167. package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +7 -3
  168. package/dist/infrastructure/platforms/windows/services/windows-features.service.js +22 -8
  169. package/dist/infrastructure/platforms/windows/services/windows-info.service.js +10 -6
  170. package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +15 -6
  171. package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +12 -12
  172. package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +3 -3
  173. package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +76 -22
  174. package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +20 -15
  175. package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +3 -3
  176. package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +8 -3
  177. package/dist/infrastructure/platforms/windows/wsl-setup.adapter.js +100 -0
  178. package/dist/infrastructure/schematics/index.js +11 -0
  179. package/dist/infrastructure/schematics/project-scaffolder.adapter.js +314 -0
  180. package/dist/infrastructure/schematics/schematics-runner.adapter.js +175 -0
  181. package/dist/infrastructure/template/index.js +25 -0
  182. package/dist/infrastructure/template/interfaces/index.js +22 -0
  183. package/dist/infrastructure/template/interfaces/template-downloader.interface.js +6 -0
  184. package/dist/infrastructure/template/interfaces/template-registry.interface.js +6 -0
  185. package/dist/infrastructure/template/services/index.js +24 -0
  186. package/dist/infrastructure/template/services/template-downloader.service.js +319 -0
  187. package/dist/infrastructure/template/services/template-registry.service.js +175 -0
  188. package/dist/infrastructure/template/template-manager.adapter.js +196 -0
  189. package/dist/infrastructure/utils/index.js +23 -0
  190. package/dist/infrastructure/utils/input-validator.util.js +7 -6
  191. package/dist/presentation/controllers/base.controller.js +36 -0
  192. package/dist/presentation/controllers/{config.controller.js → config/config.controller.js} +17 -62
  193. package/dist/presentation/controllers/config/index.js +21 -0
  194. package/dist/presentation/controllers/dev/dev.controller.js +202 -0
  195. package/dist/presentation/controllers/dev/index.js +23 -0
  196. package/dist/presentation/controllers/dev/template.controller.js +158 -0
  197. package/dist/presentation/controllers/{credentials.controller.js → env/credentials.controller.js} +8 -14
  198. package/dist/presentation/controllers/env/index.js +23 -0
  199. package/dist/presentation/controllers/{instance.controller.js → env/instance.controller.js} +35 -92
  200. package/dist/presentation/controllers/{setup.controller.js → env/setup.controller.js} +33 -66
  201. package/dist/presentation/controllers/index.js +9 -5
  202. package/dist/presentation/controllers/logs/index.js +21 -0
  203. package/dist/presentation/controllers/{logs.controller.js → logs/logs.controller.js} +8 -14
  204. package/dist/presentation/interfaces/index.js +21 -0
  205. package/dist/presentation/prompts/acr-credentials.prompt.js +37 -9
  206. package/dist/presentation/ui/constants/index.js +23 -0
  207. package/dist/presentation/ui/interaction.service.js +4 -4
  208. package/dist/presentation/ui/interfaces/cli-progress.interface.js +0 -6
  209. package/dist/presentation/ui/interfaces/index.js +6 -0
  210. package/package.json +6 -1
  211. package/dist/application/dtos/request/set-config.request.dto.js +0 -16
  212. package/dist/infrastructure/errors/error-handler.interface.js +0 -3
  213. package/dist/infrastructure/execution/command-builder.js +0 -252
  214. package/dist/infrastructure/execution/command-executor.service.js +0 -230
  215. /package/dist/application/dtos/{request → config/request}/get-config.request.dto.js +0 -0
  216. /package/dist/application/dtos/{request → env/request}/delete-instance.request.dto.js +0 -0
  217. /package/dist/application/dtos/{request → env/request}/get-credentials.request.dto.js +0 -0
  218. /package/dist/application/dtos/{request → env/request}/install-instance.request.dto.js +0 -0
  219. /package/dist/application/dtos/{request → env/request}/list-charts.request.dto.js +0 -0
  220. /package/dist/application/dtos/{request → env/request}/setup-environment.request.dto.js +0 -0
  221. /package/dist/application/dtos/{request → env/request}/start-instance.request.dto.js +0 -0
  222. /package/dist/application/dtos/{request → env/request}/stop-instance.request.dto.js +0 -0
  223. /package/dist/application/dtos/{response → env/response}/credentials.response.dto.js +0 -0
  224. /package/dist/application/dtos/{response → env/response}/delete-instance.response.dto.js +0 -0
  225. /package/dist/application/dtos/{response → env/response}/install-instance.response.dto.js +0 -0
  226. /package/dist/application/dtos/{response → env/response}/instance-list.response.dto.js +0 -0
  227. /package/dist/application/dtos/{response → env/response}/instance-status.response.dto.js +0 -0
  228. /package/dist/application/dtos/{response → env/response}/setup-result.response.dto.js +0 -0
  229. /package/dist/application/dtos/{response → env/response}/start-instance.response.dto.js +0 -0
  230. /package/dist/application/dtos/{response → env/response}/stop-instance.response.dto.js +0 -0
  231. /package/dist/application/dtos/{request → logs/request}/show-logs.request.dto.js +0 -0
  232. /package/dist/application/dtos/{response → logs/response}/show-logs.response.dto.js +0 -0
  233. /package/dist/application/use-cases/{instance → env}/list-charts.use-case.js +0 -0
  234. /package/dist/application/use-cases/{instance → env}/list-instances.use-case.js +0 -0
  235. /package/dist/application/use-cases/{instance → env}/stop-instance.use-case.js +0 -0
  236. /package/dist/{infrastructure → domain}/errors/app-error.js +0 -0
  237. /package/dist/{infrastructure → domain}/errors/exit-codes.js +0 -0
  238. /package/dist/infrastructure/config/{app-config.interface.js → interfaces/app-config.interface.js} +0 -0
  239. /package/dist/{domain → infrastructure/interceptors}/decorators/sensitive.decorator.js +0 -0
  240. /package/dist/infrastructure/interceptors/{interceptor.interface.js → interfaces/interceptor.interface.js} +0 -0
@@ -50,9 +50,9 @@ const tsyringe_1 = require("tsyringe");
50
50
  const fs = __importStar(require("fs"));
51
51
  const os = __importStar(require("os"));
52
52
  const tokens_1 = require("../../../../di/tokens");
53
- const command_builder_1 = require("../../../execution/command-builder");
53
+ const command_builder_util_1 = require("../../../execution/command-builder.util");
54
54
  const index_1 = require("../../../../constants/index");
55
- const errors_1 = require("../../../errors");
55
+ const errors_1 = require("../../../../domain/errors");
56
56
  const input_validator_util_1 = require("../../../utils/input-validator.util");
57
57
  /**
58
58
  * WSL 實例生命週期服務
@@ -161,10 +161,10 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
161
161
  async deleteInstance(instanceName) {
162
162
  (0, input_validator_util_1.requireValidInstanceName)(instanceName);
163
163
  this.appLogger?.info('Deleting WSL instance', { instanceName });
164
- const builder = command_builder_1.CommandBuilder.create('wsl')
164
+ const builder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
165
165
  .arg('--unregister')
166
166
  .arg(instanceName);
167
- await builder.exec(this.commandExecutor);
167
+ await builder.exec();
168
168
  }
169
169
  /**
170
170
  * 匯入 WSL 實例
@@ -174,12 +174,12 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
174
174
  if (!fs.existsSync(installPath)) {
175
175
  fs.mkdirSync(installPath, { recursive: true });
176
176
  }
177
- const builder = command_builder_1.CommandBuilder.create('wsl')
177
+ const builder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
178
178
  .arg('--import')
179
179
  .arg(instanceName)
180
180
  .arg(installPath)
181
181
  .arg(rootfsPath);
182
- await builder.exec(this.commandExecutor, () => { }, (stderr) => {
182
+ await builder.exec(() => { }, (stderr) => {
183
183
  if (stderr.trim()) {
184
184
  output?.error(`${stderr.trim()}`).flush();
185
185
  }
@@ -195,21 +195,19 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
195
195
  output?.item('arrow', 'Initializing D-Bus session...').flush();
196
196
  await this.initDBusSession(instanceName, output);
197
197
  await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.WSL_STARTUP_DELAY));
198
+ // 偵測並修補舊實例(disable wait-online + enable-linger)
199
+ await this.ensureBootPatch(instanceName, output);
198
200
  output?.item('arrow', 'Starting MicroK8s services...').flush();
199
201
  try {
202
+ await this.microK8sService.cleanStaleState(instanceName);
200
203
  await this.microK8sService.start(instanceName);
201
- await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.WSL_FULL_STARTUP));
202
- await this.microK8sService.restartContainerd(instanceName);
203
204
  }
204
205
  catch (error) {
205
206
  // MicroK8s start 失敗不應阻止整個啟動流程
206
207
  this.appLogger?.warn('MicroK8s start warning, continuing...', { error });
207
208
  }
208
- // output?.item('arrow', 'Waiting for MicroK8s to initialize...').flush();
209
- // await new Promise((resolve) => setTimeout(resolve, TIMEOUTS.WSL_FULL_STARTUP));
210
209
  output?.item('arrow', 'Waiting for MicroK8s to be ready...').flush();
211
210
  try {
212
- // await this.microK8sService.restartContainerd(instanceName);
213
211
  const isReady = await this.microK8sService.waitForReady(instanceName);
214
212
  if (!isReady) {
215
213
  throw new errors_1.AppError('MicroK8s did not become ready within timeout', { exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR, context: { instanceName } });
@@ -251,14 +249,14 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
251
249
  }
252
250
  output?.item('arrow', 'Shutting down WSL instance...').flush();
253
251
  try {
254
- const terminateBuilder = command_builder_1.CommandBuilder.create('wsl')
252
+ const terminateBuilder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
255
253
  .arg('--terminate')
256
254
  .arg(instanceName);
257
- await terminateBuilder.exec(this.commandExecutor);
255
+ await terminateBuilder.exec();
258
256
  await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.WSL_STARTUP_DELAY));
259
257
  const status = await this.inspectionService.getInstanceStatus(instanceName);
260
258
  if (status.running) {
261
- await terminateBuilder.exec(this.commandExecutor);
259
+ await terminateBuilder.exec();
262
260
  await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.WSL_STARTUP_DELAY));
263
261
  const retryStatus = await this.inspectionService.getInstanceStatus(instanceName);
264
262
  if (retryStatus.running) {
@@ -284,19 +282,72 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
284
282
  async initDBusSession(fullInstanceName, output) {
285
283
  (0, input_validator_util_1.requireValidInstanceName)(fullInstanceName);
286
284
  try {
287
- const builder = command_builder_1.CommandBuilder.create('wsl')
285
+ const builder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
288
286
  .arg('-d')
289
287
  .arg(fullInstanceName)
290
288
  .arg('--exec')
291
289
  .arg('dbus-launch')
292
290
  .arg('true');
293
- await builder.exec(this.commandExecutor);
291
+ await builder.exec();
294
292
  }
295
293
  catch (error) {
296
294
  this.appLogger?.warn('D-Bus initialization warning', { error });
297
295
  output?.warning(`D-Bus initialization warning: ${error}`).flush();
298
296
  }
299
297
  }
298
+ /**
299
+ * 偵測並修補舊版 WSL 實例的開機問題
300
+ *
301
+ * 舊實例缺少以下設定,會導致 stop → start 後 microk8s 啟動失敗:
302
+ * - systemd-networkd-wait-online.service 在 WSL 中無作用但阻塞開機 2 分鐘
303
+ * - 缺少 loginctl enable-linger,user@1000.service 不會在開機時啟動,
304
+ * /run/user/1000 (XDG_RUNTIME_DIR) 無法建立,snap/microk8s 啟動失敗
305
+ *
306
+ * 以 /var/lib/systemd/linger/ubuntu 作為「已補丁」標記,幂等執行。
307
+ */
308
+ async ensureBootPatch(instanceName, output) {
309
+ try {
310
+ // 檢查是否已補丁(linger 檔存在即為已補丁)
311
+ const checkBuilder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
312
+ .arg('-d')
313
+ .arg(instanceName)
314
+ .arg('--exec')
315
+ .arg('test')
316
+ .arg('-f')
317
+ .arg('/var/lib/systemd/linger/ubuntu');
318
+ const checkResult = await checkBuilder.exec();
319
+ if (checkResult.exitCode === 0) {
320
+ return; // 已補丁,直接返回
321
+ }
322
+ // 未補丁,執行修補
323
+ this.appLogger?.info('Applying WSL boot patch for instance', { instanceName });
324
+ output?.item('arrow', 'Applying boot patch for legacy instance...').flush();
325
+ const patchCommands = [
326
+ // 停用並立即停止 systemd-networkd-wait-online(解除當前開機阻塞)
327
+ ['sudo', 'systemctl', 'disable', '--now', 'systemd-networkd-wait-online.service'],
328
+ // 啟用 linger(觸發 user@1000.service 啟動,建立 /run/user/1000)
329
+ ['sudo', 'loginctl', 'enable-linger', 'ubuntu'],
330
+ // 清理舊版 logind.conf(不再需要)
331
+ ['sudo', 'rm', '-f', '/etc/systemd/logind.conf.d/99-uofx.conf'],
332
+ ];
333
+ for (const cmd of patchCommands) {
334
+ const builder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
335
+ .arg('-d')
336
+ .arg(instanceName)
337
+ .arg('--exec');
338
+ for (const a of cmd) {
339
+ builder.arg(a);
340
+ }
341
+ await builder.exec();
342
+ }
343
+ // 等待 systemd 完成 boot chain(disable --now 解除阻塞後需要時間收斂)
344
+ await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.WSL_STARTUP_DELAY * 2));
345
+ output?.success('Boot patch applied').flush();
346
+ }
347
+ catch (error) {
348
+ this.appLogger?.warn('Failed to apply boot patch', { error });
349
+ }
350
+ }
300
351
  /**
301
352
  * 等待 Kubernetes Pods 就緒
302
353
  */
@@ -357,11 +408,14 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
357
408
  */
358
409
  async getDirectorySize(dirPath) {
359
410
  try {
360
- const builder = command_builder_1.CommandBuilder.create('powershell')
411
+ // 使用 -EncodedCommand 防止 PowerShell 子表達式注入
412
+ const psCommand = `(Get-ChildItem -Path '${dirPath.replace(/'/g, "''")}' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum`;
413
+ const encodedCommand = Buffer.from(psCommand, 'utf16le').toString('base64');
414
+ const builder = command_builder_util_1.CommandBuilder.create('powershell', this.commandExecutor)
361
415
  .arg('-NoProfile')
362
- .arg('-Command')
363
- .arg(`(Get-ChildItem -Path '${dirPath.replace(/'/g, "''")}' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum`);
364
- const result = await builder.exec(this.commandExecutor);
416
+ .arg('-EncodedCommand')
417
+ .arg(encodedCommand);
418
+ const result = await builder.exec();
365
419
  if (result.exitCode !== 0)
366
420
  return 0;
367
421
  const size = parseInt(result.stdout.trim(), 10);
@@ -411,9 +465,9 @@ let WslInstanceLifecycleService = class WslInstanceLifecycleService {
411
465
  const homeDir = os.homedir();
412
466
  const driveLetter = homeDir.charAt(0);
413
467
  try {
414
- const builder = command_builder_1.CommandBuilder.create('powershell')
468
+ const builder = command_builder_util_1.CommandBuilder.create('powershell', this.commandExecutor)
415
469
  .powershell(`(Get-PSDrive ${driveLetter}).Free`);
416
- const result = await builder.exec(this.commandExecutor);
470
+ const result = await builder.exec();
417
471
  const freeSpaceBytes = parseInt(result.stdout.trim(), 10);
418
472
  if (isNaN(freeSpaceBytes)) {
419
473
  return true;
@@ -15,9 +15,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.WslUpdaterService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../../di/tokens");
18
- const errors_1 = require("../../../errors");
19
- const command_builder_1 = require("../../../execution/command-builder");
20
- const error_formatter_util_1 = require("../../../utils/error-formatter.util");
18
+ const errors_1 = require("../../../../domain/errors");
19
+ const command_builder_util_1 = require("../../../execution/command-builder.util");
20
+ const errors_2 = require("../../../../domain/errors");
21
21
  /**
22
22
  * WSL 更新服務
23
23
  *
@@ -108,9 +108,9 @@ let WslUpdaterService = class WslUpdaterService {
108
108
  */
109
109
  async checkAdmin() {
110
110
  try {
111
- const builder = command_builder_1.CommandBuilder.create('powershell')
111
+ const builder = command_builder_util_1.CommandBuilder.create('powershell', this.commandExecutor)
112
112
  .powershell("([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')");
113
- const result = await builder.exec(this.commandExecutor);
113
+ const result = await builder.exec();
114
114
  return result.stdout.trim() === 'True';
115
115
  }
116
116
  catch {
@@ -148,14 +148,14 @@ let WslUpdaterService = class WslUpdaterService {
148
148
  // install: 使用 wsl --install --no-distribution(首次安裝)
149
149
  // update: 使用 wsl --update(更新現有安裝)
150
150
  const builder = action === 'install'
151
- ? command_builder_1.CommandBuilder.create('wsl')
151
+ ? command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
152
152
  .arg('--install')
153
153
  .arg('--no-distribution')
154
154
  .inheritStdio()
155
- : command_builder_1.CommandBuilder.create('wsl')
155
+ : command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
156
156
  .wslCmd('--update')
157
157
  .inheritStdio();
158
- const result = await builder.exec(this.commandExecutor);
158
+ const result = await builder.exec();
159
159
  const cmdOutput = result.stdout + result.stderr;
160
160
  if (result.exitCode !== 0) {
161
161
  // 即使命令 "失敗",也可能已經成功但需要重新啟動
@@ -164,7 +164,9 @@ let WslUpdaterService = class WslUpdaterService {
164
164
  output?.success(`WSL ${actionDescription} completed (restart required).`).flush();
165
165
  return { needsRestart: true };
166
166
  }
167
- throw new Error('Command failed');
167
+ throw new errors_1.AppError('Command failed', {
168
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
169
+ });
168
170
  }
169
171
  // 根據輸出檢查是否需要重新啟動
170
172
  const needsRestart = this.checkNeedsRestart(cmdOutput);
@@ -179,7 +181,7 @@ let WslUpdaterService = class WslUpdaterService {
179
181
  }
180
182
  catch (error) {
181
183
  // 檢查錯誤訊息中是否包含重啟提示
182
- const errorMessage = (0, error_formatter_util_1.formatErrorMessage)(error);
184
+ const errorMessage = (0, errors_2.formatErrorMessage)(error);
183
185
  if (this.checkNeedsRestart(errorMessage)) {
184
186
  output?.success(`WSL ${actionDescription} completed (restart required).`).flush();
185
187
  return { needsRestart: true };
@@ -227,12 +229,15 @@ let WslUpdaterService = class WslUpdaterService {
227
229
  try {
228
230
  output?.info('Setting WSL 2 as default version...').flush();
229
231
  this.appLogger?.debug('Running wsl --set-default-version 2');
230
- const builder = command_builder_1.CommandBuilder.create('wsl')
232
+ const builder = command_builder_util_1.CommandBuilder.create('wsl', this.commandExecutor)
231
233
  .arg('--set-default-version')
232
234
  .arg('2');
233
- const result = await builder.exec(this.commandExecutor, (data) => output?.info(`${data.trim()}`).flush(), (data) => output?.info(`${data.trim()}`).flush());
235
+ const result = await builder.exec((data) => output?.info(`${data.trim()}`).flush(), (data) => output?.info(`${data.trim()}`).flush());
234
236
  if (result.exitCode !== 0) {
235
- throw new Error(result.stderr || 'Command failed');
237
+ this.appLogger?.debug('Failed to set default WSL version', { stderr: result.stderr });
238
+ throw new errors_1.AppError('Failed to set default WSL version', {
239
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
240
+ });
236
241
  }
237
242
  output?.success('WSL 2 set as default version').flush();
238
243
  }
@@ -269,7 +274,7 @@ let WslUpdaterService = class WslUpdaterService {
269
274
  lines.push(' wsl --set-default-version 2');
270
275
  }
271
276
  lines.push('');
272
- lines.push("Then run 'uofx setup' again");
277
+ lines.push("Then run 'uofx env setup' again");
273
278
  lines.push('');
274
279
  lines.push('For more information: https://docs.microsoft.com/en-us/windows/wsl/');
275
280
  return lines.join('\n');
@@ -284,7 +289,7 @@ let WslUpdaterService = class WslUpdaterService {
284
289
  output?.info(this.formatSetupCancelledError(requirement)).flush();
285
290
  throw new errors_1.AppError('Setup cancelled: WSL 2 is required for Kubernetes', {
286
291
  exitCode: errors_1.EXIT_CODES.BUSINESS_ERROR,
287
- solution: 'Install WSL 2 to continue. Run: uofx setup',
292
+ solution: 'Install WSL 2 to continue. Run: uofx env setup',
288
293
  });
289
294
  }
290
295
  };
@@ -51,7 +51,7 @@ const fs = __importStar(require("fs"));
51
51
  const path = __importStar(require("path"));
52
52
  const os = __importStar(require("os"));
53
53
  const tokens_1 = require("../../../../di/tokens");
54
- const command_builder_1 = require("../../../execution/command-builder");
54
+ const command_builder_util_1 = require("../../../execution/command-builder.util");
55
55
  /**
56
56
  * WSL 配置檔案解析服務
57
57
  *
@@ -174,11 +174,11 @@ let WslConfigParserService = class WslConfigParserService {
174
174
  */
175
175
  async getWslCurrentMemoryUsageGB() {
176
176
  try {
177
- const builder = command_builder_1.CommandBuilder.create('powershell')
177
+ const builder = command_builder_util_1.CommandBuilder.create('powershell', this.commandExecutor)
178
178
  .arg('-NoProfile')
179
179
  .arg('-Command')
180
180
  .arg('(Get-Process vmmemWSL -ErrorAction SilentlyContinue).WorkingSet64');
181
- const result = await builder.exec(this.commandExecutor);
181
+ const result = await builder.exec();
182
182
  const bytes = parseInt(result.stdout.trim(), 10);
183
183
  if (isNaN(bytes))
184
184
  return 0;
@@ -16,6 +16,7 @@ exports.WslInstanceManagerAdapter = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const instance_status_entity_1 = require("../../../domain/entities/instance-status.entity");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * WSL Instance Manager Adapter
21
22
  *
@@ -126,13 +127,17 @@ let WslInstanceManagerAdapter = class WslInstanceManagerAdapter {
126
127
  async requireExists(name) {
127
128
  const exists = await this.exists(name);
128
129
  if (!exists) {
129
- throw new Error(`Instance "${name.toString()}" not found`);
130
+ throw new errors_1.AppError(`Instance "${name.toString()}" not found`, {
131
+ exitCode: errors_1.EXIT_CODES.BUSINESS_ERROR,
132
+ });
130
133
  }
131
134
  }
132
135
  async requireNotExists(name) {
133
136
  const exists = await this.exists(name);
134
137
  if (exists) {
135
- throw new Error(`Instance "${name.toString()}" already exists`);
138
+ throw new errors_1.AppError(`Instance "${name.toString()}" already exists`, {
139
+ exitCode: errors_1.EXIT_CODES.BUSINESS_ERROR,
140
+ });
136
141
  }
137
142
  }
138
143
  };
@@ -142,7 +147,7 @@ exports.WslInstanceManagerAdapter = WslInstanceManagerAdapter = __decorate([
142
147
  __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IWslInstanceNaming)),
143
148
  __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IWslInstanceLifecycle)),
144
149
  __param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IWslInstanceInspection)),
145
- __param(3, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.WslConfigService)),
150
+ __param(3, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IWslConfig)),
146
151
  __param(4, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.ScriptExecutorService)),
147
152
  __param(5, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
148
153
  __metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object])
@@ -0,0 +1,100 @@
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.WslSetupAdapter = void 0;
16
+ const tsyringe_1 = require("tsyringe");
17
+ const tokens_1 = require("../../../di/tokens");
18
+ /**
19
+ * WSL 設定 Adapter
20
+ *
21
+ * 實作 IWslSetupPort,封裝 WSL 狀態檢查與互動式設定流程
22
+ * 協調 WslInfoService 與 WslUpdaterService 完成 WSL 相關操作
23
+ */
24
+ let WslSetupAdapter = class WslSetupAdapter {
25
+ constructor(wslInfo, wslUpdater) {
26
+ this.wslInfo = wslInfo;
27
+ this.wslUpdater = wslUpdater;
28
+ }
29
+ /**
30
+ * 取得 WSL 設定狀態
31
+ * @returns WSL 設定狀態
32
+ */
33
+ async getWslStatus() {
34
+ const versionInfo = await this.wslInfo.getWslVersionInfo();
35
+ if (!versionInfo.installed) {
36
+ return {
37
+ isInstalled: false,
38
+ needsUpdate: false,
39
+ needsDefaultVersion: false,
40
+ };
41
+ }
42
+ const updateReq = this.wslInfo.checkWslUpdateRequirement(versionInfo);
43
+ return {
44
+ isInstalled: true,
45
+ needsUpdate: updateReq.needsUpdate,
46
+ needsDefaultVersion: updateReq.needsDefaultVersion,
47
+ currentVersion: updateReq.currentVersion,
48
+ currentDefaultVersion: updateReq.currentDefaultVersion,
49
+ };
50
+ }
51
+ /**
52
+ * 互動式 WSL 設定
53
+ * @param status - WSL 設定狀態
54
+ * @param output - 可選的輸出介面
55
+ * @returns WSL 設定結果
56
+ */
57
+ async promptAndSetupWsl(status, output) {
58
+ // WSL 未安裝 → 詢問是否安裝
59
+ if (!status.isInstalled) {
60
+ const shouldInstall = await this.wslUpdater.promptWslInstall(output);
61
+ if (!shouldInstall) {
62
+ return { action: 'cancelled', needsRestart: false };
63
+ }
64
+ const { needsRestart } = await this.wslUpdater.installWsl('install', output);
65
+ return { action: 'installed', needsRestart };
66
+ }
67
+ // WSL 已安裝但需要更新或設定預設版本
68
+ if (status.needsUpdate || status.needsDefaultVersion) {
69
+ const versionInfo = await this.wslInfo.getWslVersionInfo();
70
+ const updateReq = this.wslInfo.checkWslUpdateRequirement(versionInfo);
71
+ const shouldSetup = await this.wslUpdater.promptWslSetup(updateReq, output);
72
+ if (!shouldSetup) {
73
+ // 使用者取消 — handleSetupCancelled 會拋出例外
74
+ this.wslUpdater.handleSetupCancelled(updateReq, output);
75
+ return { action: 'cancelled', needsRestart: false };
76
+ }
77
+ // 執行更新
78
+ if (status.needsUpdate) {
79
+ const { needsRestart } = await this.wslUpdater.installWsl('update', output);
80
+ if (needsRestart) {
81
+ return { action: 'updated', needsRestart: true };
82
+ }
83
+ }
84
+ // 設定預設版本
85
+ if (status.needsDefaultVersion) {
86
+ await this.wslUpdater.setDefaultVersion(output);
87
+ }
88
+ return { action: 'updated', needsRestart: false };
89
+ }
90
+ return { action: 'none', needsRestart: false };
91
+ }
92
+ };
93
+ exports.WslSetupAdapter = WslSetupAdapter;
94
+ exports.WslSetupAdapter = WslSetupAdapter = __decorate([
95
+ (0, tsyringe_1.injectable)(),
96
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.WslInfoService)),
97
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.WslUpdaterService)),
98
+ __metadata("design:paramtypes", [Object, Object])
99
+ ], WslSetupAdapter);
100
+ //# sourceMappingURL=wsl-setup.adapter.js.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchematicsRunnerAdapter = exports.ProjectScaffolderAdapter = void 0;
4
+ /**
5
+ * Infrastructure Schematics 模組匯出
6
+ */
7
+ var project_scaffolder_adapter_1 = require("./project-scaffolder.adapter");
8
+ Object.defineProperty(exports, "ProjectScaffolderAdapter", { enumerable: true, get: function () { return project_scaffolder_adapter_1.ProjectScaffolderAdapter; } });
9
+ var schematics_runner_adapter_1 = require("./schematics-runner.adapter");
10
+ Object.defineProperty(exports, "SchematicsRunnerAdapter", { enumerable: true, get: function () { return schematics_runner_adapter_1.SchematicsRunnerAdapter; } });
11
+ //# sourceMappingURL=index.js.map