@jingyi0605/codingns 0.2.5 → 0.3.5

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 (227) hide show
  1. package/bin/codingns.mjs +278 -2
  2. package/dist/public/assets/{TerminalPage-BkjqU9NG.js → TerminalPage-CgrfstRm.js} +19 -19
  3. package/dist/public/assets/index-Cek6u0b9.css +1 -0
  4. package/dist/public/assets/index-THHY79si.js +122 -0
  5. package/dist/public/index.html +2 -2
  6. package/dist/server/config/env.d.ts +4 -0
  7. package/dist/server/config/env.js +67 -3
  8. package/dist/server/config/env.js.map +1 -1
  9. package/dist/server/modules/auth/auth-service.d.ts +18 -1
  10. package/dist/server/modules/auth/auth-service.js +168 -7
  11. package/dist/server/modules/auth/auth-service.js.map +1 -1
  12. package/dist/server/modules/butler/butler-codex-model-policy.d.ts +1 -0
  13. package/dist/server/modules/butler/butler-codex-model-policy.js +36 -0
  14. package/dist/server/modules/butler/butler-codex-model-policy.js.map +1 -0
  15. package/dist/server/modules/butler/butler-control-session-service.d.ts +16 -2
  16. package/dist/server/modules/butler/butler-control-session-service.js +58 -210
  17. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  18. package/dist/server/modules/butler/butler-controller.d.ts +17 -0
  19. package/dist/server/modules/butler/butler-controller.js +20 -1
  20. package/dist/server/modules/butler/butler-controller.js.map +1 -1
  21. package/dist/server/modules/butler/butler-follow-up-service.js +7 -3
  22. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  23. package/dist/server/modules/butler/butler-inbox-analysis-service.d.ts +36 -0
  24. package/dist/server/modules/butler/butler-inbox-analysis-service.js +375 -0
  25. package/dist/server/modules/butler/butler-inbox-analysis-service.js.map +1 -0
  26. package/dist/server/modules/butler/butler-inbox-instruction-adapter.d.ts +23 -0
  27. package/dist/server/modules/butler/butler-inbox-instruction-adapter.js +96 -0
  28. package/dist/server/modules/butler/butler-inbox-instruction-adapter.js.map +1 -0
  29. package/dist/server/modules/butler/butler-inbox-service.d.ts +39 -2
  30. package/dist/server/modules/butler/butler-inbox-service.js +392 -2
  31. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -1
  32. package/dist/server/modules/butler/butler-session-service.d.ts +9 -0
  33. package/dist/server/modules/butler/butler-session-service.js +277 -68
  34. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  35. package/dist/server/modules/butler/butler-session-summary-service.d.ts +1 -0
  36. package/dist/server/modules/butler/butler-session-summary-service.js +48 -23
  37. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  38. package/dist/server/modules/butler/butler-workspace-context.d.ts +13 -0
  39. package/dist/server/modules/butler/butler-workspace-context.js +223 -0
  40. package/dist/server/modules/butler/butler-workspace-context.js.map +1 -0
  41. package/dist/server/modules/client/client-controller.d.ts +6 -0
  42. package/dist/server/modules/client/client-controller.js +30 -8
  43. package/dist/server/modules/client/client-controller.js.map +1 -1
  44. package/dist/server/modules/client/client-service.d.ts +22 -10
  45. package/dist/server/modules/client/client-service.js +77 -100
  46. package/dist/server/modules/client/client-service.js.map +1 -1
  47. package/dist/server/modules/client/npm-global-package-service.d.ts +21 -0
  48. package/dist/server/modules/client/npm-global-package-service.js +210 -0
  49. package/dist/server/modules/client/npm-global-package-service.js.map +1 -0
  50. package/dist/server/modules/client/service-update-task-service.d.ts +15 -0
  51. package/dist/server/modules/client/service-update-task-service.js +147 -0
  52. package/dist/server/modules/client/service-update-task-service.js.map +1 -0
  53. package/dist/server/modules/client/service-update-types.d.ts +30 -0
  54. package/dist/server/modules/client/service-update-types.js +2 -0
  55. package/dist/server/modules/client/service-update-types.js.map +1 -0
  56. package/dist/server/modules/debug-target/debug-target-service.d.ts +5 -2
  57. package/dist/server/modules/debug-target/debug-target-service.js +170 -10
  58. package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
  59. package/dist/server/modules/file/file-constants.d.ts +1 -0
  60. package/dist/server/modules/file/file-constants.js +1 -0
  61. package/dist/server/modules/file/file-constants.js.map +1 -1
  62. package/dist/server/modules/file/file-controller.js +12 -3
  63. package/dist/server/modules/file/file-controller.js.map +1 -1
  64. package/dist/server/modules/file/file-preview-link-service.js +6 -37
  65. package/dist/server/modules/file/file-preview-link-service.js.map +1 -1
  66. package/dist/server/modules/file/file-preview-service.d.ts +6 -12
  67. package/dist/server/modules/file/file-preview-service.js +114 -28
  68. package/dist/server/modules/file/file-preview-service.js.map +1 -1
  69. package/dist/server/modules/file/file-preview-types.d.ts +37 -0
  70. package/dist/server/modules/file/file-preview-types.js +84 -0
  71. package/dist/server/modules/file/file-preview-types.js.map +1 -0
  72. package/dist/server/modules/git/git-controller.d.ts +6 -0
  73. package/dist/server/modules/git/git-controller.js +13 -0
  74. package/dist/server/modules/git/git-controller.js.map +1 -1
  75. package/dist/server/modules/git/git-read-service.d.ts +2 -1
  76. package/dist/server/modules/git/git-read-service.js +100 -0
  77. package/dist/server/modules/git/git-read-service.js.map +1 -1
  78. package/dist/server/modules/git/types.d.ts +23 -0
  79. package/dist/server/modules/model-switch/cc-switch-adapter.d.ts +36 -0
  80. package/dist/server/modules/model-switch/cc-switch-adapter.js +317 -0
  81. package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -0
  82. package/dist/server/modules/model-switch/model-switch-controller.d.ts +11 -0
  83. package/dist/server/modules/model-switch/model-switch-controller.js +30 -0
  84. package/dist/server/modules/model-switch/model-switch-controller.js.map +1 -0
  85. package/dist/server/modules/model-switch/model-switch-service.d.ts +16 -0
  86. package/dist/server/modules/model-switch/model-switch-service.js +29 -0
  87. package/dist/server/modules/model-switch/model-switch-service.js.map +1 -0
  88. package/dist/server/modules/preferences/profile-service.d.ts +1 -0
  89. package/dist/server/modules/preferences/profile-service.js +9 -0
  90. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  91. package/dist/server/modules/sessions/codex-app-server-helper-process.js +3 -0
  92. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  93. package/dist/server/modules/sessions/session-activity-authority-service.d.ts +3 -1
  94. package/dist/server/modules/sessions/session-activity-authority-service.js +16 -1
  95. package/dist/server/modules/sessions/session-activity-authority-service.js.map +1 -1
  96. package/dist/server/modules/sessions/session-activity-inspector.d.ts +1 -1
  97. package/dist/server/modules/sessions/session-activity-inspector.js +52 -9
  98. package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
  99. package/dist/server/modules/sessions/session-controller.d.ts +3 -3
  100. package/dist/server/modules/sessions/session-controller.js +3 -3
  101. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  102. package/dist/server/modules/sessions/session-history-service.d.ts +11 -2
  103. package/dist/server/modules/sessions/session-history-service.js +373 -78
  104. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  105. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +9 -4
  106. package/dist/server/modules/sessions/session-live-runtime-service.js +59 -11
  107. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  108. package/dist/server/modules/sessions/session-message-attachment-service.d.ts +8 -8
  109. package/dist/server/modules/sessions/session-message-attachment-service.js +25 -34
  110. package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
  111. package/dist/server/modules/sessions/session-provider-error-mapper.js +7 -0
  112. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  113. package/dist/server/modules/skills/skill-controller.d.ts +23 -0
  114. package/dist/server/modules/skills/skill-controller.js +35 -0
  115. package/dist/server/modules/skills/skill-controller.js.map +1 -0
  116. package/dist/server/modules/skills/skill-manager-service.d.ts +86 -0
  117. package/dist/server/modules/skills/skill-manager-service.js +557 -0
  118. package/dist/server/modules/skills/skill-manager-service.js.map +1 -0
  119. package/dist/server/modules/skills/skill-reconciler.d.ts +21 -0
  120. package/dist/server/modules/skills/skill-reconciler.js +99 -0
  121. package/dist/server/modules/skills/skill-reconciler.js.map +1 -0
  122. package/dist/server/modules/skills/skill-sync-planner.d.ts +8 -0
  123. package/dist/server/modules/skills/skill-sync-planner.js +20 -0
  124. package/dist/server/modules/skills/skill-sync-planner.js.map +1 -0
  125. package/dist/server/modules/skills/skill-target-adapter.d.ts +34 -0
  126. package/dist/server/modules/skills/skill-target-adapter.js +65 -0
  127. package/dist/server/modules/skills/skill-target-adapter.js.map +1 -0
  128. package/dist/server/modules/tailscale/tailscale-controller.d.ts +15 -0
  129. package/dist/server/modules/tailscale/tailscale-controller.js +33 -0
  130. package/dist/server/modules/tailscale/tailscale-controller.js.map +1 -0
  131. package/dist/server/modules/tailscale/tailscale-helper-client.d.ts +41 -0
  132. package/dist/server/modules/tailscale/tailscale-helper-client.js +135 -0
  133. package/dist/server/modules/tailscale/tailscale-helper-client.js.map +1 -0
  134. package/dist/server/modules/tailscale/tailscale-helper-process.d.ts +1 -0
  135. package/dist/server/modules/tailscale/tailscale-helper-process.js +327 -0
  136. package/dist/server/modules/tailscale/tailscale-helper-process.js.map +1 -0
  137. package/dist/server/modules/tailscale/tailscale-manager.d.ts +41 -0
  138. package/dist/server/modules/tailscale/tailscale-manager.js +263 -0
  139. package/dist/server/modules/tailscale/tailscale-manager.js.map +1 -0
  140. package/dist/server/modules/tailscale/tailscale-service.d.ts +43 -0
  141. package/dist/server/modules/tailscale/tailscale-service.js +201 -0
  142. package/dist/server/modules/tailscale/tailscale-service.js.map +1 -0
  143. package/dist/server/modules/tasks/task-lane-executors.js +3 -0
  144. package/dist/server/modules/tasks/task-lane-executors.js.map +1 -1
  145. package/dist/server/modules/tasks/task-types.d.ts +2 -0
  146. package/dist/server/modules/tasks/task-types.js +3 -1
  147. package/dist/server/modules/tasks/task-types.js.map +1 -1
  148. package/dist/server/modules/terminal/command-template-service.js +10 -1
  149. package/dist/server/modules/terminal/command-template-service.js.map +1 -1
  150. package/dist/server/modules/terminal/template-port-runtime.d.ts +12 -1
  151. package/dist/server/modules/terminal/template-port-runtime.js +189 -17
  152. package/dist/server/modules/terminal/template-port-runtime.js.map +1 -1
  153. package/dist/server/modules/terminal/terminal-service.js +30 -3
  154. package/dist/server/modules/terminal/terminal-service.js.map +1 -1
  155. package/dist/server/modules/workspace/workspace-code-composition.js +95 -2
  156. package/dist/server/modules/workspace/workspace-code-composition.js.map +1 -1
  157. package/dist/server/routes/butler.js +4 -0
  158. package/dist/server/routes/butler.js.map +1 -1
  159. package/dist/server/routes/client.js +2 -0
  160. package/dist/server/routes/client.js.map +1 -1
  161. package/dist/server/routes/git.js +1 -0
  162. package/dist/server/routes/git.js.map +1 -1
  163. package/dist/server/routes/sessions.js +1 -1
  164. package/dist/server/routes/sessions.js.map +1 -1
  165. package/dist/server/routes/skills.d.ts +3 -0
  166. package/dist/server/routes/skills.js +7 -0
  167. package/dist/server/routes/skills.js.map +1 -0
  168. package/dist/server/routes/system.d.ts +4 -0
  169. package/dist/server/routes/system.js +11 -0
  170. package/dist/server/routes/system.js.map +1 -0
  171. package/dist/server/server/create-server.d.ts +16 -0
  172. package/dist/server/server/create-server.js +91 -8
  173. package/dist/server/server/create-server.js.map +1 -1
  174. package/dist/server/shared/errors/app-error.d.ts +2 -0
  175. package/dist/server/shared/errors/app-error.js +2 -0
  176. package/dist/server/shared/errors/app-error.js.map +1 -1
  177. package/dist/server/shared/http/error-handler.d.ts +2 -1
  178. package/dist/server/shared/http/error-handler.js +3 -2
  179. package/dist/server/shared/http/error-handler.js.map +1 -1
  180. package/dist/server/shared/utils/command-availability.d.ts +1 -0
  181. package/dist/server/shared/utils/command-availability.js +26 -3
  182. package/dist/server/shared/utils/command-availability.js.map +1 -1
  183. package/dist/server/storage/repositories/auth-login-attempt-repository.d.ts +9 -0
  184. package/dist/server/storage/repositories/auth-login-attempt-repository.js +59 -0
  185. package/dist/server/storage/repositories/auth-login-attempt-repository.js.map +1 -0
  186. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +3 -0
  187. package/dist/server/storage/repositories/butler-control-session-repository.js +80 -4
  188. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -1
  189. package/dist/server/storage/repositories/butler-inbox-item-repository.js +54 -3
  190. package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -1
  191. package/dist/server/storage/repositories/instance-tailscale-repository.d.ts +10 -0
  192. package/dist/server/storage/repositories/instance-tailscale-repository.js +112 -0
  193. package/dist/server/storage/repositories/instance-tailscale-repository.js.map +1 -0
  194. package/dist/server/storage/repositories/managed-skill-repository.d.ts +11 -0
  195. package/dist/server/storage/repositories/managed-skill-repository.js +102 -0
  196. package/dist/server/storage/repositories/managed-skill-repository.js.map +1 -0
  197. package/dist/server/storage/repositories/skill-target-binding-repository.d.ts +10 -0
  198. package/dist/server/storage/repositories/skill-target-binding-repository.js +77 -0
  199. package/dist/server/storage/repositories/skill-target-binding-repository.js.map +1 -0
  200. package/dist/server/storage/repositories/terminal-instance-repository.js +1 -1
  201. package/dist/server/storage/repositories/terminal-instance-repository.js.map +1 -1
  202. package/dist/server/storage/repositories/user-preference-profile-repository.js +6 -3
  203. package/dist/server/storage/repositories/user-preference-profile-repository.js.map +1 -1
  204. package/dist/server/storage/sqlite/client.js +137 -0
  205. package/dist/server/storage/sqlite/client.js.map +1 -1
  206. package/dist/server/storage/sqlite/schema.sql +84 -1
  207. package/dist/server/types/domain.d.ts +109 -1
  208. package/dist/server/ws/ws-server.js +4 -4
  209. package/dist/server/ws/ws-server.js.map +1 -1
  210. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +23 -0
  211. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +162 -0
  212. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
  213. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +1 -0
  214. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +89 -33
  215. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  216. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +18 -2
  217. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  218. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +3 -1
  219. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +238 -53
  220. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  221. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +1 -0
  222. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +1 -1
  223. package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +6 -2
  224. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
  225. package/package.json +1 -1
  226. package/dist/public/assets/index-C6U8-9jg.css +0 -1
  227. package/dist/public/assets/index-CKSumuV2.js +0 -109
@@ -0,0 +1,147 @@
1
+ import { HOST_TASK_TYPES } from "../tasks/task-types.js";
2
+ import { AppError } from "../../shared/errors/app-error.js";
3
+ import { readHostPackageVersion } from "./client-service.js";
4
+ export class ServiceUpdateTaskService {
5
+ taskManager;
6
+ npmGlobalPackageService;
7
+ taskRecordById = new Map();
8
+ latestTaskIdByPackageName = new Map();
9
+ constructor(taskManager, npmGlobalPackageService) {
10
+ this.taskManager = taskManager;
11
+ this.npmGlobalPackageService = npmGlobalPackageService;
12
+ this.registerTask();
13
+ }
14
+ async installPackage(channel, packageName) {
15
+ const latestTask = this.getLatestTaskByPackageName(packageName);
16
+ if (latestTask?.restartRequired) {
17
+ throw new AppError({
18
+ statusCode: 409,
19
+ errorCode: "SERVICE_UPDATE_RESTART_REQUIRED",
20
+ detail: "新版本已经安装完成,重启 Host 后才会生效"
21
+ });
22
+ }
23
+ const target = await this.npmGlobalPackageService.resolveInstallTarget(channel, readHostPackageVersion(), packageName);
24
+ const handle = this.taskManager.enqueue(HOST_TASK_TYPES.serviceNpmGlobalUpdateInstall, {
25
+ key: packageName,
26
+ source: "client.service_update.install",
27
+ input: {
28
+ packageName: target.packageName,
29
+ channel: target.channel,
30
+ targetVersion: target.targetVersion,
31
+ distTag: target.distTag
32
+ }
33
+ });
34
+ this.taskRecordById.set(handle.taskId, {
35
+ packageName,
36
+ channel,
37
+ key: packageName,
38
+ targetVersion: target.targetVersion
39
+ });
40
+ this.latestTaskIdByPackageName.set(packageName, handle.taskId);
41
+ void handle.promise.catch(() => undefined);
42
+ return this.getTask(handle.taskId);
43
+ }
44
+ getTask(taskId) {
45
+ const record = this.taskRecordById.get(taskId);
46
+ if (!record) {
47
+ throw new AppError({
48
+ statusCode: 404,
49
+ errorCode: "SERVICE_UPDATE_TASK_NOT_FOUND",
50
+ detail: `未找到更新任务 ${taskId}`
51
+ });
52
+ }
53
+ const snapshot = this.taskManager.peek(HOST_TASK_TYPES.serviceNpmGlobalUpdateInstall, record.key);
54
+ if (!snapshot || snapshot.taskId !== taskId) {
55
+ throw new AppError({
56
+ statusCode: 404,
57
+ errorCode: "SERVICE_UPDATE_TASK_NOT_FOUND",
58
+ detail: `未找到更新任务 ${taskId}`
59
+ });
60
+ }
61
+ return this.toTaskDto(record, snapshot);
62
+ }
63
+ getLatestTaskByPackageName(packageName) {
64
+ const taskId = this.latestTaskIdByPackageName.get(packageName);
65
+ if (!taskId) {
66
+ return null;
67
+ }
68
+ try {
69
+ return this.getTask(taskId);
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ registerTask() {
76
+ if (this.taskManager.has(HOST_TASK_TYPES.serviceNpmGlobalUpdateInstall)) {
77
+ return;
78
+ }
79
+ this.taskManager.register({
80
+ taskType: HOST_TASK_TYPES.serviceNpmGlobalUpdateInstall,
81
+ executionLane: "external_process",
82
+ timeoutMs: 300_000,
83
+ concurrency: 1,
84
+ run: async (input, context) => {
85
+ await this.npmGlobalPackageService.installGlobalPackage({
86
+ packageName: input.packageName,
87
+ distTag: input.distTag,
88
+ signal: context.signal
89
+ });
90
+ }
91
+ });
92
+ }
93
+ toTaskDto(record, snapshot) {
94
+ return {
95
+ taskId: snapshot.taskId,
96
+ packageName: record.packageName,
97
+ channel: record.channel,
98
+ targetVersion: record.targetVersion,
99
+ status: snapshot.status,
100
+ startedAt: toIsoTime(snapshot.startedAt),
101
+ finishedAt: toIsoTime(snapshot.finishedAt),
102
+ errorMessage: snapshot.errorMessage ?? null,
103
+ restartRequired: snapshot.status === "succeeded"
104
+ && compareSemver(record.targetVersion, readHostPackageVersion()) > 0
105
+ };
106
+ }
107
+ }
108
+ function toIsoTime(timestamp) {
109
+ if (timestamp === null) {
110
+ return null;
111
+ }
112
+ return new Date(timestamp).toISOString();
113
+ }
114
+ function compareSemver(left, right) {
115
+ const leftMeta = parseSemver(left);
116
+ const rightMeta = parseSemver(right);
117
+ for (let index = 0; index < 3; index += 1) {
118
+ const diff = (leftMeta.numbers[index] ?? 0) - (rightMeta.numbers[index] ?? 0);
119
+ if (diff !== 0) {
120
+ return diff;
121
+ }
122
+ }
123
+ if (leftMeta.prerelease === rightMeta.prerelease) {
124
+ return 0;
125
+ }
126
+ if (!leftMeta.prerelease) {
127
+ return 1;
128
+ }
129
+ if (!rightMeta.prerelease) {
130
+ return -1;
131
+ }
132
+ return leftMeta.prerelease.localeCompare(rightMeta.prerelease);
133
+ }
134
+ function parseSemver(input) {
135
+ const normalized = input.trim().replace(/^v/i, "");
136
+ const [versionPart, prerelease = ""] = normalized.split("-", 2);
137
+ const rawNumbers = versionPart.split(".");
138
+ return {
139
+ numbers: [
140
+ Number.parseInt(rawNumbers[0] ?? "0", 10) || 0,
141
+ Number.parseInt(rawNumbers[1] ?? "0", 10) || 0,
142
+ Number.parseInt(rawNumbers[2] ?? "0", 10) || 0
143
+ ],
144
+ prerelease
145
+ };
146
+ }
147
+ //# sourceMappingURL=service-update-task-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-update-task-service.js","sourceRoot":"","sources":["../../../../src/modules/client/service-update-task-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { TaskStatus } from "../tasks/task-types.js";
2
+ export interface ServiceUpdateTaskDto {
3
+ taskId: string;
4
+ packageName: string;
5
+ channel: "stable" | "beta";
6
+ targetVersion: string | null;
7
+ status: TaskStatus;
8
+ startedAt: string | null;
9
+ finishedAt: string | null;
10
+ errorMessage: string | null;
11
+ restartRequired: boolean;
12
+ }
13
+ export interface ManagedServicePackageDto {
14
+ channel: "stable" | "beta";
15
+ packageName: string;
16
+ registryUrl: string;
17
+ packagePageUrl: string;
18
+ currentVersion: string;
19
+ latestVersion: string | null;
20
+ hasUpdate: boolean;
21
+ checkStatus: "ready" | "up_to_date" | "check_failed";
22
+ checkError: string | null;
23
+ restartRequired: boolean;
24
+ installTask: ServiceUpdateTaskDto | null;
25
+ }
26
+ export interface ServiceUpdateListDto {
27
+ channel: "stable" | "beta";
28
+ checkedAt: string;
29
+ packages: ManagedServicePackageDto[];
30
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=service-update-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-update-types.js","sourceRoot":"","sources":["../../../../src/modules/client/service-update-types.ts"],"names":[],"mappings":""}
@@ -7,7 +7,7 @@ import type { FrameworkAnalysisResultRepository } from "../../storage/repositori
7
7
  import type { PortLeaseRepository } from "../../storage/repositories/port-lease-repository.js";
8
8
  import type { RuntimeBindingRepository } from "../../storage/repositories/runtime-binding-repository.js";
9
9
  import type { WorkspaceWorktreeRepository } from "../../storage/repositories/workspace-worktree-repository.js";
10
- import type { AiFallbackEditRecord, DebugLaunchPlan, DebugRuntimeHistoryEnvelope, DebugRuntimeDetail, DebugRuntimeSession, DebugServiceSpec, DebugTargetProfile, FrameworkAnalysisResult, TerminalInstance, TerminalRuntimeType } from "../../types/domain.js";
10
+ import type { AiFallbackEditRecord, DebugLaunchPlan, DebugRuntimeHistoryEnvelope, DebugRuntimeDetail, DebugRuntimeSession, DebugServiceSpec, DebugTargetProfile, FrameworkAnalysisResult, TerminalCommandTemplate, TerminalInstance, TerminalRuntimeType } from "../../types/domain.js";
11
11
  import type { TerminalService } from "../terminal/terminal-service.js";
12
12
  import { TaskManager } from "../tasks/task-manager.js";
13
13
  import type { WorkspaceService } from "../workspace/workspace-service.js";
@@ -49,10 +49,13 @@ export declare class DebugTargetService {
49
49
  private readonly portLeaseRepository;
50
50
  private readonly runtimeBindingRepository;
51
51
  private readonly aiFallbackEditRepository;
52
+ private readonly terminalCommandTemplateRepository;
52
53
  private readonly terminalService;
53
54
  private readonly terminalInstanceRepository;
54
55
  private readonly taskManager;
55
- constructor(db: Database.Database, workspaceService: WorkspaceService, workspaceWorktreeRepository: Pick<WorkspaceWorktreeRepository, "findByWorkspaceId">, debugTargetRepository: DebugTargetRepository, debugServiceRepository: DebugServiceRepository, frameworkAnalysisResultRepository: FrameworkAnalysisResultRepository, debugRuntimeSessionRepository: DebugRuntimeSessionRepository, portLeaseRepository: PortLeaseRepository, runtimeBindingRepository: RuntimeBindingRepository, aiFallbackEditRepository: AiFallbackEditRepository, terminalService: Pick<TerminalService, "createTerminal" | "writeInput" | "closeTerminal" | "getTerminalOrThrow">, terminalInstanceRepository: {
56
+ constructor(db: Database.Database, workspaceService: WorkspaceService, workspaceWorktreeRepository: Pick<WorkspaceWorktreeRepository, "findByWorkspaceId">, debugTargetRepository: DebugTargetRepository, debugServiceRepository: DebugServiceRepository, frameworkAnalysisResultRepository: FrameworkAnalysisResultRepository, debugRuntimeSessionRepository: DebugRuntimeSessionRepository, portLeaseRepository: PortLeaseRepository, runtimeBindingRepository: RuntimeBindingRepository, aiFallbackEditRepository: AiFallbackEditRepository, terminalCommandTemplateRepository: {
57
+ listByWorkspace(workspaceId: string): TerminalCommandTemplate[];
58
+ }, terminalService: Pick<TerminalService, "createTerminal" | "writeInput" | "closeTerminal" | "getTerminalOrThrow">, terminalInstanceRepository: {
56
59
  findById(id: string): TerminalInstance | null;
57
60
  }, taskManager?: TaskManager);
58
61
  analyze(input: AnalyzeDebugTargetInput): AnalyzeDebugTargetResult;
@@ -20,10 +20,11 @@ export class DebugTargetService {
20
20
  portLeaseRepository;
21
21
  runtimeBindingRepository;
22
22
  aiFallbackEditRepository;
23
+ terminalCommandTemplateRepository;
23
24
  terminalService;
24
25
  terminalInstanceRepository;
25
26
  taskManager;
26
- constructor(db, workspaceService, workspaceWorktreeRepository, debugTargetRepository, debugServiceRepository, frameworkAnalysisResultRepository, debugRuntimeSessionRepository, portLeaseRepository, runtimeBindingRepository, aiFallbackEditRepository, terminalService, terminalInstanceRepository, taskManager = createTaskManager()) {
27
+ constructor(db, workspaceService, workspaceWorktreeRepository, debugTargetRepository, debugServiceRepository, frameworkAnalysisResultRepository, debugRuntimeSessionRepository, portLeaseRepository, runtimeBindingRepository, aiFallbackEditRepository, terminalCommandTemplateRepository, terminalService, terminalInstanceRepository, taskManager = createTaskManager()) {
27
28
  this.db = db;
28
29
  this.workspaceService = workspaceService;
29
30
  this.workspaceWorktreeRepository = workspaceWorktreeRepository;
@@ -34,6 +35,7 @@ export class DebugTargetService {
34
35
  this.portLeaseRepository = portLeaseRepository;
35
36
  this.runtimeBindingRepository = runtimeBindingRepository;
36
37
  this.aiFallbackEditRepository = aiFallbackEditRepository;
38
+ this.terminalCommandTemplateRepository = terminalCommandTemplateRepository;
37
39
  this.terminalService = terminalService;
38
40
  this.terminalInstanceRepository = terminalInstanceRepository;
39
41
  this.taskManager = taskManager;
@@ -44,7 +46,13 @@ export class DebugTargetService {
44
46
  const rootPath = this.resolveRootPath(workspace.path, input.rootPath);
45
47
  const sourceMeta = this.resolveSourceMeta(workspace.id);
46
48
  const timestamp = nowIso();
47
- const discoveredServices = discoverServiceCandidates(rootPath, input.commandHints);
49
+ const commandHints = resolveAnalyzeCommandHints({
50
+ workspaceId: workspace.id,
51
+ rootPath,
52
+ explicitCommandHints: input.commandHints,
53
+ terminalCommandTemplateRepository: this.terminalCommandTemplateRepository
54
+ });
55
+ const discoveredServices = discoverServiceCandidates(rootPath, commandHints);
48
56
  const analyzedServices = discoveredServices.map((candidate) => ({
49
57
  candidate,
50
58
  framework: pickFramework(collectFrameworkEvidence(candidate.cwd))
@@ -754,12 +762,20 @@ function discoverServiceCandidates(rootPath, commandHints) {
754
762
  const rootScripts = extractPackageScripts(rootPackageJson);
755
763
  const workspacePackages = discoverWorkspacePackages(rootPath, rootPackageJson);
756
764
  const candidatesFromRootScripts = discoverWorkspaceServiceCandidatesFromRootScripts(rootPath, workspacePackages, rootScripts);
757
- if (candidatesFromRootScripts.length > 0) {
758
- return applyCommandHintFallback(candidatesFromRootScripts, commandHints);
759
- }
760
765
  const candidatesFromWorkspacePackages = discoverWorkspaceServiceCandidatesFromPackages(rootPath, workspacePackages);
761
- if (candidatesFromWorkspacePackages.length > 0) {
762
- return applyCommandHintFallback(candidatesFromWorkspacePackages, commandHints);
766
+ const candidatesFromAppsPythonServices = discoverAppsPythonServiceCandidates(rootPath);
767
+ const discoveredByCwd = new Map();
768
+ for (const candidate of [
769
+ ...candidatesFromRootScripts,
770
+ ...candidatesFromWorkspacePackages,
771
+ ...candidatesFromAppsPythonServices
772
+ ]) {
773
+ if (!discoveredByCwd.has(candidate.cwd)) {
774
+ discoveredByCwd.set(candidate.cwd, candidate);
775
+ }
776
+ }
777
+ if (discoveredByCwd.size > 0) {
778
+ return applyCommandHintFallback(sortDiscoveredServiceCandidates([...discoveredByCwd.values()]), commandHints);
763
779
  }
764
780
  return applyCommandHintFallback([
765
781
  {
@@ -908,6 +924,37 @@ function discoverWorkspaceServiceCandidatesFromPackages(rootPath, workspacePacka
908
924
  }));
909
925
  return sortDiscoveredServiceCandidates(discovered);
910
926
  }
927
+ function discoverAppsPythonServiceCandidates(rootPath) {
928
+ const appsDir = path.join(rootPath, "apps");
929
+ if (!fs.existsSync(appsDir) || !fs.statSync(appsDir).isDirectory()) {
930
+ return [];
931
+ }
932
+ let appEntries = [];
933
+ try {
934
+ appEntries = fs.readdirSync(appsDir);
935
+ }
936
+ catch {
937
+ return [];
938
+ }
939
+ const discovered = [];
940
+ for (const entry of appEntries) {
941
+ const serviceDir = path.join(appsDir, entry);
942
+ if (!fs.existsSync(serviceDir) || !fs.statSync(serviceDir).isDirectory()) {
943
+ continue;
944
+ }
945
+ const framework = pickFramework(collectFrameworkEvidence(serviceDir));
946
+ if (!isPythonFramework(framework.primaryFramework)) {
947
+ continue;
948
+ }
949
+ discovered.push({
950
+ name: path.basename(serviceDir) || "service",
951
+ cwd: serviceDir,
952
+ commandHint: null,
953
+ roleHint: "backend"
954
+ });
955
+ }
956
+ return sortDiscoveredServiceCandidates(discovered);
957
+ }
911
958
  function hasRunnableDevScript(scripts) {
912
959
  return Object.keys(scripts).some((name) => name === "dev" || name.startsWith("dev:"));
913
960
  }
@@ -976,7 +1023,7 @@ function extractPackageName(packageJson) {
976
1023
  return typeof packageName === "string" && packageName.trim().length > 0 ? packageName.trim() : null;
977
1024
  }
978
1025
  function buildServiceRecord(target, candidate, framework, timestamp, frameworkAnalysisId) {
979
- const parsedCommand = parseCommandHint(candidate.commandHint ?? defaultCommandHintForFramework(framework.primaryFramework));
1026
+ const parsedCommand = parseCommandHint(candidate.commandHint ?? defaultCommandHintForFramework(framework.primaryFramework, candidate.cwd));
980
1027
  return {
981
1028
  id: createId(),
982
1029
  targetId: target.id,
@@ -1010,12 +1057,12 @@ function parseCommandHint(commandHint) {
1010
1057
  args: tokens.slice(1)
1011
1058
  };
1012
1059
  }
1013
- function defaultCommandHintForFramework(framework) {
1060
+ function defaultCommandHintForFramework(framework, rootPath) {
1014
1061
  switch (framework) {
1015
1062
  case "spring-boot":
1016
1063
  return "./mvnw spring-boot:run";
1017
1064
  case "uvicorn":
1018
- return "uvicorn main:app --reload";
1065
+ return `uvicorn ${inferUvicornAppSpec(rootPath)} --reload`;
1019
1066
  case "flask":
1020
1067
  return "flask run";
1021
1068
  case "django":
@@ -1057,6 +1104,35 @@ function resolveServiceRole(framework) {
1057
1104
  return "custom";
1058
1105
  }
1059
1106
  }
1107
+ function inferUvicornAppSpec(rootPath) {
1108
+ if (!rootPath) {
1109
+ return "main:app";
1110
+ }
1111
+ const candidates = [
1112
+ {
1113
+ relativePath: "app/main.py",
1114
+ moduleSpec: "app.main:app"
1115
+ },
1116
+ {
1117
+ relativePath: "main.py",
1118
+ moduleSpec: "main:app"
1119
+ },
1120
+ {
1121
+ relativePath: "src/main.py",
1122
+ moduleSpec: "src.main:app"
1123
+ },
1124
+ {
1125
+ relativePath: "app.py",
1126
+ moduleSpec: "app:app"
1127
+ }
1128
+ ];
1129
+ for (const candidate of candidates) {
1130
+ if (fs.existsSync(path.join(rootPath, candidate.relativePath))) {
1131
+ return candidate.moduleSpec;
1132
+ }
1133
+ }
1134
+ return "main:app";
1135
+ }
1060
1136
  function normalizeAdapterKind(framework) {
1061
1137
  const item = getFrameworkCompatibilityItem(framework);
1062
1138
  if (item.recommendedInjectionMode === "none") {
@@ -1366,6 +1442,90 @@ function safeListFiles(rootPath) {
1366
1442
  function pickFramework(result) {
1367
1443
  return result;
1368
1444
  }
1445
+ function isPythonFramework(framework) {
1446
+ return framework === "uvicorn" || framework === "flask" || framework === "django";
1447
+ }
1448
+ function resolveAnalyzeCommandHints(input) {
1449
+ const templateHints = collectTemplateCommandHints(input.terminalCommandTemplateRepository.listByWorkspace(input.workspaceId), input.rootPath);
1450
+ const explicitHints = (input.explicitCommandHints ?? []).map((item) => item.trim()).filter(Boolean);
1451
+ const mergedHints = [...templateHints, ...explicitHints];
1452
+ return mergedHints.length > 0 ? [...new Set(mergedHints)] : undefined;
1453
+ }
1454
+ function collectTemplateCommandHints(templates, rootPath) {
1455
+ return templates
1456
+ .filter((template) => isTemplateRelevantToRootPath(template, rootPath))
1457
+ .sort((left, right) => compareTemplatePriority(left, right, rootPath))
1458
+ .map((template) => buildTemplateCommandHint(template))
1459
+ .filter((item) => Boolean(item));
1460
+ }
1461
+ function isTemplateRelevantToRootPath(template, rootPath) {
1462
+ const templateCwd = path.resolve(template.cwd);
1463
+ const normalizedRootPath = path.resolve(rootPath);
1464
+ return isPathWithin(normalizedRootPath, templateCwd) || isPathWithin(templateCwd, normalizedRootPath);
1465
+ }
1466
+ function compareTemplatePriority(left, right, rootPath) {
1467
+ const backendDelta = Number(isLikelyBackendTemplate(right)) - Number(isLikelyBackendTemplate(left));
1468
+ if (backendDelta !== 0) {
1469
+ return backendDelta;
1470
+ }
1471
+ const sourceDelta = resolveTemplateSourcePriority(left) - resolveTemplateSourcePriority(right);
1472
+ if (sourceDelta !== 0) {
1473
+ return sourceDelta;
1474
+ }
1475
+ const distanceDelta = resolveTemplateDistance(left, rootPath) - resolveTemplateDistance(right, rootPath);
1476
+ if (distanceDelta !== 0) {
1477
+ return distanceDelta;
1478
+ }
1479
+ return left.cwd.localeCompare(right.cwd);
1480
+ }
1481
+ function resolveTemplateSourcePriority(template) {
1482
+ return template.sourceType === "debug_service" ? 1 : 0;
1483
+ }
1484
+ function resolveTemplateDistance(template, rootPath) {
1485
+ const templateCwd = path.resolve(template.cwd);
1486
+ const normalizedRootPath = path.resolve(rootPath);
1487
+ if (templateCwd === normalizedRootPath) {
1488
+ return 0;
1489
+ }
1490
+ if (isPathWithin(normalizedRootPath, templateCwd)) {
1491
+ return 1;
1492
+ }
1493
+ if (isPathWithin(templateCwd, normalizedRootPath)) {
1494
+ return 2;
1495
+ }
1496
+ return 3;
1497
+ }
1498
+ function buildTemplateCommandHint(template) {
1499
+ const command = template.command.trim();
1500
+ const args = template.args.map((item) => item.trim()).filter(Boolean);
1501
+ if (!command) {
1502
+ return null;
1503
+ }
1504
+ return [command, ...args].join(" ");
1505
+ }
1506
+ function isLikelyBackendTemplate(template) {
1507
+ const signal = [
1508
+ template.name,
1509
+ template.cwd,
1510
+ template.command,
1511
+ ...template.args
1512
+ ].join(" ").toLowerCase();
1513
+ return (signal.includes("backend")
1514
+ || signal.includes("server")
1515
+ || signal.includes("api")
1516
+ || signal.includes("host")
1517
+ || signal.includes("python")
1518
+ || signal.includes("uvicorn")
1519
+ || signal.includes("gunicorn")
1520
+ || signal.includes("flask")
1521
+ || signal.includes("django")
1522
+ || signal.includes("manage.py"));
1523
+ }
1524
+ function isPathWithin(parentPath, childPath) {
1525
+ const normalizedParent = withTrailingSeparator(path.resolve(parentPath));
1526
+ const normalizedChild = path.resolve(childPath);
1527
+ return normalizedChild === path.resolve(parentPath) || normalizedChild.startsWith(normalizedParent);
1528
+ }
1369
1529
  function selectFrameworkCandidate(candidates) {
1370
1530
  return Array.from(candidates.values())
1371
1531
  .sort((left, right) => {