@uofx/cli 1.0.1 → 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 +54 -24
  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} +36 -33
  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
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ /**
3
+ * Template Downloader
4
+ *
5
+ * 負責從遠端下載樣板 ZIP 並解壓縮
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ 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;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ };
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __metadata = (this && this.__metadata) || function (k, v) {
47
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
48
+ };
49
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
50
+ return function (target, key) { decorator(target, key, paramIndex); }
51
+ };
52
+ var __importDefault = (this && this.__importDefault) || function (mod) {
53
+ return (mod && mod.__esModule) ? mod : { "default": mod };
54
+ };
55
+ Object.defineProperty(exports, "__esModule", { value: true });
56
+ exports.TemplateDownloader = void 0;
57
+ const tsyringe_1 = require("tsyringe");
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const https = __importStar(require("https"));
61
+ const http = __importStar(require("http"));
62
+ const adm_zip_1 = __importDefault(require("adm-zip"));
63
+ const tokens_1 = require("../../../di/tokens");
64
+ const errors_1 = require("../../../domain/errors");
65
+ let TemplateDownloader = class TemplateDownloader {
66
+ constructor(logger) {
67
+ this.logger = logger;
68
+ }
69
+ async downloadAndExtract(url, destDir, templateName) {
70
+ // 驗證 URL 必須為 https:// 協議,防止 file:// 等危險協議
71
+ this.validateUrl(url);
72
+ const tempZipPath = path.join(destDir, `${templateName}.zip`);
73
+ try {
74
+ this.logger.debug(`Downloading template from ${url}`);
75
+ // 確保目標目錄存在
76
+ if (!fs.existsSync(destDir)) {
77
+ fs.mkdirSync(destDir, { recursive: true });
78
+ }
79
+ // 下載 ZIP 檔案
80
+ await this.downloadFile(url, tempZipPath);
81
+ // 解壓縮
82
+ const result = await this.extractFromLocal(tempZipPath, destDir, templateName);
83
+ // 刪除暫存 ZIP
84
+ if (fs.existsSync(tempZipPath)) {
85
+ fs.unlinkSync(tempZipPath);
86
+ }
87
+ return result;
88
+ }
89
+ catch (error) {
90
+ // 清理暫存檔案
91
+ if (fs.existsSync(tempZipPath)) {
92
+ fs.unlinkSync(tempZipPath);
93
+ }
94
+ const errorMessage = error instanceof Error ? error.message : String(error);
95
+ this.logger.error(new Error(`Failed to download template: ${errorMessage}`));
96
+ return {
97
+ success: false,
98
+ error: errorMessage,
99
+ };
100
+ }
101
+ }
102
+ async extractFromLocal(zipPath, destDir, templateName) {
103
+ try {
104
+ if (!fs.existsSync(zipPath)) {
105
+ return {
106
+ success: false,
107
+ error: `ZIP file not found: ${zipPath}`,
108
+ };
109
+ }
110
+ const extractPath = path.join(destDir, templateName);
111
+ // 如果目標目錄已存在,先刪除
112
+ if (fs.existsSync(extractPath)) {
113
+ fs.rmSync(extractPath, { recursive: true, force: true });
114
+ }
115
+ this.logger.debug(`Extracting template to ${extractPath}`);
116
+ const zip = new adm_zip_1.default(zipPath);
117
+ // ZIP Slip 防護:檢查所有 entry 路徑是否逃逸出目標目錄
118
+ const resolvedExtractPath = path.resolve(extractPath);
119
+ for (const entry of zip.getEntries()) {
120
+ const entryPath = path.resolve(extractPath, entry.entryName);
121
+ if (!entryPath.startsWith(resolvedExtractPath + path.sep) && entryPath !== resolvedExtractPath) {
122
+ throw new errors_1.AppError(`ZIP entry "${entry.entryName}" attempts to escape extraction directory (ZIP Slip attack)`, { exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR });
123
+ }
124
+ }
125
+ zip.extractAllTo(extractPath, true);
126
+ // 處理 ZIP 內可能有單一根目錄的情況
127
+ const extractedContents = fs.readdirSync(extractPath);
128
+ if (extractedContents.length === 1) {
129
+ const innerDir = path.join(extractPath, extractedContents[0]);
130
+ if (fs.statSync(innerDir).isDirectory()) {
131
+ // 將內層目錄的內容移到外層
132
+ const innerContents = fs.readdirSync(innerDir);
133
+ for (const item of innerContents) {
134
+ const srcPath = path.join(innerDir, item);
135
+ const destPath = path.join(extractPath, item);
136
+ fs.renameSync(srcPath, destPath);
137
+ }
138
+ fs.rmdirSync(innerDir);
139
+ }
140
+ }
141
+ // 建立 project 目錄並移動檔案
142
+ const projectDir = path.join(extractPath, 'project');
143
+ fs.mkdirSync(projectDir, { recursive: true });
144
+ // 將解壓的內容(除了 template.json 和 project 本身)移到 project/
145
+ const contentsToMove = fs.readdirSync(extractPath);
146
+ for (const item of contentsToMove) {
147
+ if (item === 'project' || item === 'template.json')
148
+ continue;
149
+ const srcPath = path.join(extractPath, item);
150
+ const destPath = path.join(projectDir, item);
151
+ fs.renameSync(srcPath, destPath);
152
+ }
153
+ this.logger.debug(`Template extracted successfully to ${extractPath}`);
154
+ return {
155
+ success: true,
156
+ extractedPath: extractPath,
157
+ };
158
+ }
159
+ catch (error) {
160
+ const errorMessage = error instanceof Error ? error.message : String(error);
161
+ this.logger.error(new Error(`Failed to extract template: ${errorMessage}`));
162
+ return {
163
+ success: false,
164
+ error: errorMessage,
165
+ };
166
+ }
167
+ }
168
+ async getGitHubArchiveUrl(repo, version) {
169
+ try {
170
+ let tag = version;
171
+ // 若未指定版本,取得最新的 tag
172
+ if (!tag) {
173
+ this.logger.debug('Getting latest tag from GitHub...');
174
+ const apiUrl = `https://api.github.com/repos/${repo}/tags`;
175
+ const tags = await this.fetchJson(apiUrl);
176
+ if (!tags || tags.length === 0) {
177
+ throw new errors_1.AppError(`No tags found in repository ${repo}`, {
178
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
179
+ });
180
+ }
181
+ tag = tags[0].name;
182
+ this.logger.debug(`Latest tag: ${tag}`);
183
+ }
184
+ // 組合 source archive URL
185
+ // 格式: https://github.com/{owner}/{repo}/archive/refs/tags/{tag}.zip
186
+ const archiveUrl = `https://github.com/${repo}/archive/refs/tags/${tag}.zip`;
187
+ return {
188
+ url: archiveUrl,
189
+ tag: tag,
190
+ };
191
+ }
192
+ catch (error) {
193
+ const errorMessage = error instanceof Error ? error.message : String(error);
194
+ throw new errors_1.AppError(`Failed to get GitHub archive URL: ${errorMessage}`, {
195
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
196
+ });
197
+ }
198
+ }
199
+ /**
200
+ * 驗證 URL 必須使用安全協議
201
+ * @param url - 要驗證的 URL
202
+ * @throws 若 URL 非 https:// 協議
203
+ */
204
+ validateUrl(url) {
205
+ try {
206
+ const parsedUrl = new URL(url);
207
+ if (parsedUrl.protocol !== 'https:') {
208
+ throw new errors_1.AppError(`Unsupported URL protocol: "${parsedUrl.protocol}". Only HTTPS is allowed.`, { exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR });
209
+ }
210
+ }
211
+ catch (error) {
212
+ if (error instanceof errors_1.AppError)
213
+ throw error;
214
+ throw new errors_1.AppError(`Invalid URL format: "${url}"`, { exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR });
215
+ }
216
+ }
217
+ /**
218
+ * 下載檔案
219
+ */
220
+ downloadFile(url, destPath) {
221
+ return new Promise((resolve, reject) => {
222
+ const protocol = url.startsWith('https') ? https : http;
223
+ const file = fs.createWriteStream(destPath);
224
+ const request = protocol.get(url, { headers: { 'User-Agent': 'uofx-cli' } }, (response) => {
225
+ // 處理重定向
226
+ // 處理重定向
227
+ if (response.statusCode === 301 || response.statusCode === 302) {
228
+ const redirectUrl = response.headers.location;
229
+ if (redirectUrl) {
230
+ request.destroy();
231
+ file.close();
232
+ fs.unlinkSync(destPath);
233
+ this.downloadFile(redirectUrl, destPath).then(resolve).catch(reject);
234
+ return;
235
+ }
236
+ }
237
+ if (response.statusCode !== 200) {
238
+ request.destroy();
239
+ file.close();
240
+ fs.unlinkSync(destPath);
241
+ reject(new errors_1.AppError(`HTTP ${response.statusCode}: ${response.statusMessage}`, {
242
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
243
+ }));
244
+ return;
245
+ }
246
+ response.pipe(file);
247
+ file.on('finish', () => {
248
+ request.destroy();
249
+ file.close();
250
+ resolve();
251
+ });
252
+ });
253
+ request.on('error', (error) => {
254
+ request.destroy();
255
+ file.close();
256
+ if (fs.existsSync(destPath)) {
257
+ fs.unlinkSync(destPath);
258
+ }
259
+ reject(error);
260
+ });
261
+ file.on('error', (error) => {
262
+ request.destroy();
263
+ file.close();
264
+ if (fs.existsSync(destPath)) {
265
+ fs.unlinkSync(destPath);
266
+ }
267
+ reject(error);
268
+ });
269
+ });
270
+ }
271
+ /**
272
+ * 取得 JSON 回應
273
+ */
274
+ fetchJson(url) {
275
+ return new Promise((resolve, reject) => {
276
+ const protocol = url.startsWith('https') ? https : http;
277
+ const request = protocol.get(url, {
278
+ headers: {
279
+ 'User-Agent': 'uofx-cli',
280
+ Accept: 'application/vnd.github.v3+json',
281
+ },
282
+ }, (response) => {
283
+ if (response.statusCode !== 200) {
284
+ request.destroy();
285
+ reject(new errors_1.AppError(`HTTP ${response.statusCode}: ${response.statusMessage}`, {
286
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
287
+ }));
288
+ return;
289
+ }
290
+ let data = '';
291
+ response.on('data', (chunk) => {
292
+ data += chunk;
293
+ });
294
+ response.on('end', () => {
295
+ request.destroy();
296
+ try {
297
+ resolve(JSON.parse(data));
298
+ }
299
+ catch (error) {
300
+ reject(new errors_1.AppError('Failed to parse JSON response', {
301
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
302
+ }));
303
+ }
304
+ });
305
+ });
306
+ request.on('error', (error) => {
307
+ request.destroy();
308
+ reject(error);
309
+ });
310
+ });
311
+ }
312
+ };
313
+ exports.TemplateDownloader = TemplateDownloader;
314
+ exports.TemplateDownloader = TemplateDownloader = __decorate([
315
+ (0, tsyringe_1.injectable)(),
316
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
317
+ __metadata("design:paramtypes", [Object])
318
+ ], TemplateDownloader);
319
+ //# sourceMappingURL=template-downloader.service.js.map
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ /**
3
+ * Template Registry
4
+ *
5
+ * 負責管理已安裝樣板的註冊表
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ 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;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ };
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __metadata = (this && this.__metadata) || function (k, v) {
47
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
48
+ };
49
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
50
+ return function (target, key) { decorator(target, key, paramIndex); }
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.TemplateRegistry = void 0;
54
+ const tsyringe_1 = require("tsyringe");
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ const tokens_1 = require("../../../di/tokens");
58
+ const template_info_entity_1 = require("../../../domain/entities/template-info.entity");
59
+ let TemplateRegistry = class TemplateRegistry {
60
+ constructor(logger, appConfig) {
61
+ this.logger = logger;
62
+ this.appConfig = appConfig;
63
+ this.registryFileName = 'templates.json';
64
+ this.templatesCache = new Map();
65
+ this.registryData = { version: '1.0.0', templates: [] };
66
+ this.load();
67
+ }
68
+ getAll() {
69
+ return Array.from(this.templatesCache.values());
70
+ }
71
+ get(name) {
72
+ return this.templatesCache.get(name);
73
+ }
74
+ register(templatePath, name, version, sourceUrl) {
75
+ const templateInfo = template_info_entity_1.TemplateInfo.create(name, version, new Date(), templatePath, sourceUrl);
76
+ // 更新註冊表
77
+ const existingIndex = this.registryData.templates.findIndex((t) => t.name === templateInfo.name);
78
+ const entry = {
79
+ name: templateInfo.name,
80
+ version: templateInfo.version,
81
+ installedAt: new Date().toISOString(),
82
+ path: templatePath,
83
+ sourceUrl,
84
+ };
85
+ if (existingIndex >= 0) {
86
+ this.registryData.templates[existingIndex] = entry;
87
+ }
88
+ else {
89
+ this.registryData.templates.push(entry);
90
+ }
91
+ // 更新快取
92
+ this.templatesCache.set(templateInfo.name, templateInfo);
93
+ // 儲存註冊表
94
+ this.save();
95
+ this.logger.debug(`Template "${templateInfo.name}" registered at ${templatePath}`);
96
+ return templateInfo;
97
+ }
98
+ unregister(name) {
99
+ const index = this.registryData.templates.findIndex((t) => t.name === name);
100
+ if (index < 0) {
101
+ return false;
102
+ }
103
+ this.registryData.templates.splice(index, 1);
104
+ this.templatesCache.delete(name);
105
+ this.save();
106
+ this.logger.debug(`Template "${name}" unregistered`);
107
+ return true;
108
+ }
109
+ isRegistered(name) {
110
+ return this.templatesCache.has(name);
111
+ }
112
+ getTemplateCacheDir() {
113
+ const userConfigDir = this.appConfig.getUserConfigDir();
114
+ return path.join(userConfigDir, 'templates');
115
+ }
116
+ save() {
117
+ const registryPath = this.getRegistryPath();
118
+ const dir = path.dirname(registryPath);
119
+ if (!fs.existsSync(dir)) {
120
+ fs.mkdirSync(dir, { recursive: true });
121
+ }
122
+ fs.writeFileSync(registryPath, JSON.stringify(this.registryData, null, 2), 'utf-8');
123
+ }
124
+ reload() {
125
+ this.load();
126
+ }
127
+ /**
128
+ * 取得註冊表檔案路徑
129
+ */
130
+ getRegistryPath() {
131
+ return path.join(this.getTemplateCacheDir(), this.registryFileName);
132
+ }
133
+ /**
134
+ * 載入註冊表
135
+ */
136
+ load() {
137
+ const registryPath = this.getRegistryPath();
138
+ if (!fs.existsSync(registryPath)) {
139
+ this.registryData = { version: '1.0.0', templates: [] };
140
+ this.templatesCache.clear();
141
+ return;
142
+ }
143
+ try {
144
+ this.registryData = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
145
+ this.templatesCache.clear();
146
+ // 從註冊表項目直接建立 TemplateInfo
147
+ for (const entry of this.registryData.templates) {
148
+ try {
149
+ if (!fs.existsSync(entry.path)) {
150
+ this.logger.warn(`Template "${entry.name}" not found at ${entry.path}, skipping`);
151
+ continue;
152
+ }
153
+ const templateInfo = template_info_entity_1.TemplateInfo.create(entry.name, entry.version, new Date(entry.installedAt), entry.path, entry.sourceUrl);
154
+ this.templatesCache.set(templateInfo.name, templateInfo);
155
+ }
156
+ catch (error) {
157
+ this.logger.warn(`Failed to load template "${entry.name}": ${error}`);
158
+ }
159
+ }
160
+ }
161
+ catch (error) {
162
+ this.logger.error(new Error(`Failed to load template registry: ${error}`));
163
+ this.registryData = { version: '1.0.0', templates: [] };
164
+ this.templatesCache.clear();
165
+ }
166
+ }
167
+ };
168
+ exports.TemplateRegistry = TemplateRegistry;
169
+ exports.TemplateRegistry = TemplateRegistry = __decorate([
170
+ (0, tsyringe_1.injectable)(),
171
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
172
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IAppConfigPort)),
173
+ __metadata("design:paramtypes", [Object, Object])
174
+ ], TemplateRegistry);
175
+ //# sourceMappingURL=template-registry.service.js.map
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * Template Manager Adapter
4
+ *
5
+ * 實作 ITemplateManagerPort 介面
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ 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;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ };
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __metadata = (this && this.__metadata) || function (k, v) {
47
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
48
+ };
49
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
50
+ return function (target, key) { decorator(target, key, paramIndex); }
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.TemplateManagerAdapter = void 0;
54
+ const tsyringe_1 = require("tsyringe");
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ const tokens_1 = require("../../di/tokens");
58
+ /**
59
+ * 包含 sample 程式碼的樣板 repo(預設)
60
+ */
61
+ const TEMPLATE_REPO_WITH_SAMPLE = 'UOfficeForceX/UOFX-Plugin-Training';
62
+ /**
63
+ * 不含 sample 程式碼的基礎樣板 repo
64
+ */
65
+ const TEMPLATE_REPO_BASE = 'UOfficeForceX/UOFX-Plugin-Base';
66
+ let TemplateManagerAdapter = class TemplateManagerAdapter {
67
+ constructor(logger, downloader, registry) {
68
+ this.logger = logger;
69
+ this.downloader = downloader;
70
+ this.registry = registry;
71
+ }
72
+ async install(options, output) {
73
+ try {
74
+ const cacheDir = this.registry.getTemplateCacheDir();
75
+ // 確保快取目錄存在
76
+ if (!fs.existsSync(cacheDir)) {
77
+ fs.mkdirSync(cacheDir, { recursive: true });
78
+ }
79
+ let downloadUrl;
80
+ let templateName;
81
+ let versionTag; // 儲存實際版本號(供 registry 使用)
82
+ // [Step 1] Resolving template version
83
+ output?.section('[1/4] Resolving template version').flush();
84
+ if (options.url) {
85
+ output?.item('arrow', 'Using custom URL...').flush();
86
+ downloadUrl = options.url;
87
+ templateName = options.name || 'custom';
88
+ versionTag = templateName;
89
+ }
90
+ else {
91
+ output?.item('arrow', 'Fetching latest version from GitHub...').flush();
92
+ this.logger.debug('Getting archive URL from GitHub...');
93
+ const repo = (options.withSample ?? true) ? TEMPLATE_REPO_WITH_SAMPLE : TEMPLATE_REPO_BASE;
94
+ const archiveResult = await this.downloader.getGitHubArchiveUrl(repo);
95
+ downloadUrl = archiveResult.url;
96
+ templateName = options.name || archiveResult.tag; // 優先使用 options.name(Bug 修正)
97
+ versionTag = archiveResult.tag; // 版本號始終來自 tag
98
+ }
99
+ output?.success(`Using version: ${templateName}`).flush();
100
+ // 檢查是否已安裝
101
+ if (this.registry.isRegistered(templateName)) {
102
+ const existingTemplate = this.registry.get(templateName);
103
+ return {
104
+ success: false,
105
+ template: existingTemplate,
106
+ message: `Template "${templateName}" is already installed.`,
107
+ };
108
+ }
109
+ // [Step 2] Downloading template
110
+ output?.newline().section('[2/4] Downloading template').flush();
111
+ output?.item('arrow', `Downloading from: ${downloadUrl}`).flush();
112
+ this.logger.debug(`Downloading template from ${downloadUrl}`);
113
+ // [Step 3] Extracting template
114
+ output?.newline().section('[3/4] Extracting template').flush();
115
+ output
116
+ ?.item('arrow', `Extracting to: ${path.join(cacheDir, templateName)}`)
117
+ .flush();
118
+ // 下載並解壓縮
119
+ const result = await this.downloader.downloadAndExtract(downloadUrl, cacheDir, templateName);
120
+ if (!result.success || !result.extractedPath) {
121
+ return {
122
+ success: false,
123
+ message: result.error || 'Failed to download template',
124
+ };
125
+ }
126
+ output?.item('arrow', 'Creating project directory structure...').flush();
127
+ output?.success('Template extracted').flush();
128
+ // [Step 4] Registering template
129
+ output?.newline().section('[4/4] Registering template').flush();
130
+ output?.item('arrow', 'Updating template registry...').flush();
131
+ // 註冊樣板
132
+ const version = versionTag.replace(/^v/, ''); // 去掉 v 前綴
133
+ const templateInfo = this.registry.register(result.extractedPath, templateName, version, downloadUrl);
134
+ output?.success('Template registered').flush();
135
+ return {
136
+ success: true,
137
+ template: templateInfo,
138
+ message: `Template "${templateInfo.name}" installed successfully.`,
139
+ };
140
+ }
141
+ catch (error) {
142
+ const errorMessage = error instanceof Error ? error.message : String(error);
143
+ this.logger.error(new Error(`Failed to install template: ${errorMessage}`));
144
+ return {
145
+ success: false,
146
+ message: errorMessage,
147
+ };
148
+ }
149
+ }
150
+ async list() {
151
+ return this.registry.getAll();
152
+ }
153
+ async get(name) {
154
+ return this.registry.get(name);
155
+ }
156
+ async uninstall(name) {
157
+ const template = this.registry.get(name);
158
+ if (!template) {
159
+ this.logger.warn(`Template "${name}" is not installed`);
160
+ return false;
161
+ }
162
+ try {
163
+ // 刪除樣板目錄
164
+ if (fs.existsSync(template.path)) {
165
+ fs.rmSync(template.path, { recursive: true, force: true });
166
+ }
167
+ // 從註冊表移除
168
+ this.registry.unregister(name);
169
+ this.logger.debug(`Template "${name}" uninstalled successfully`);
170
+ return true;
171
+ }
172
+ catch (error) {
173
+ const errorMessage = error instanceof Error ? error.message : String(error);
174
+ this.logger.error(new Error(`Failed to uninstall template: ${errorMessage}`));
175
+ return false;
176
+ }
177
+ }
178
+ async isInstalled(name) {
179
+ return this.registry.isRegistered(name);
180
+ }
181
+ getTemplateCacheDir() {
182
+ return this.registry.getTemplateCacheDir();
183
+ }
184
+ getTemplatePath(name) {
185
+ return path.join(this.getTemplateCacheDir(), name);
186
+ }
187
+ };
188
+ exports.TemplateManagerAdapter = TemplateManagerAdapter;
189
+ exports.TemplateManagerAdapter = TemplateManagerAdapter = __decorate([
190
+ (0, tsyringe_1.injectable)(),
191
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
192
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.TemplateDownloader)),
193
+ __param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.TemplateRegistry)),
194
+ __metadata("design:paramtypes", [Object, Object, Object])
195
+ ], TemplateManagerAdapter);
196
+ //# sourceMappingURL=template-manager.adapter.js.map