aws-runtime-bridge 1.9.78 → 1.9.80

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 (596) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +0 -1
  2. package/dist/adapter/AcodeSdkAdapter.js +11 -1986
  3. package/dist/adapter/AdapterRegistry.d.ts +0 -1
  4. package/dist/adapter/AdapterRegistry.js +2 -100
  5. package/dist/adapter/ClaudeSdkAdapter.d.ts +0 -1
  6. package/dist/adapter/ClaudeSdkAdapter.js +4 -1300
  7. package/dist/adapter/CodexSdkAdapter.d.ts +0 -1
  8. package/dist/adapter/CodexSdkAdapter.js +4 -836
  9. package/dist/adapter/OpencodeSdkAdapter.d.ts +0 -1
  10. package/dist/adapter/OpencodeSdkAdapter.js +4 -879
  11. package/dist/adapter/SdkProviderSpi.d.ts +0 -1
  12. package/dist/adapter/SdkProviderSpi.js +2 -55
  13. package/dist/adapter/acode-tool-names.d.ts +0 -1
  14. package/dist/adapter/acode-tool-names.js +2 -37
  15. package/dist/adapter/idle-poll-loop.d.ts +0 -1
  16. package/dist/adapter/idle-poll-loop.js +2 -59
  17. package/dist/adapter/index.d.ts +0 -1
  18. package/dist/adapter/index.js +2 -11
  19. package/dist/adapter/instance-ai-config-resolver.d.ts +0 -1
  20. package/dist/adapter/instance-ai-config-resolver.js +2 -46
  21. package/dist/adapter/types.d.ts +0 -1
  22. package/dist/adapter/types.js +2 -443
  23. package/dist/adapters/cc-switch/common.d.ts +0 -1
  24. package/dist/adapters/cc-switch/common.js +2 -61
  25. package/dist/adapters/cc-switch/index.d.ts +0 -1
  26. package/dist/adapters/cc-switch/index.js +2 -57
  27. package/dist/adapters/cc-switch/mcp-acode.d.ts +0 -1
  28. package/dist/adapters/cc-switch/mcp-acode.js +2 -71
  29. package/dist/adapters/cc-switch/mcp-claude.d.ts +0 -1
  30. package/dist/adapters/cc-switch/mcp-claude.js +2 -89
  31. package/dist/adapters/cc-switch/mcp-claudecode.d.ts +0 -1
  32. package/dist/adapters/cc-switch/mcp-claudecode.js +2 -77
  33. package/dist/adapters/cc-switch/mcp-codex.d.ts +0 -1
  34. package/dist/adapters/cc-switch/mcp-codex.js +11 -249
  35. package/dist/adapters/cc-switch/mcp-opencode.d.ts +0 -1
  36. package/dist/adapters/cc-switch/mcp-opencode.js +2 -97
  37. package/dist/adapters/cc-switch/mcp-placeholder.d.ts +0 -1
  38. package/dist/adapters/cc-switch/mcp-placeholder.js +2 -19
  39. package/dist/adapters/cc-switch/skill-acode.d.ts +0 -1
  40. package/dist/adapters/cc-switch/skill-acode.js +2 -12
  41. package/dist/adapters/cc-switch/skill-claude.d.ts +0 -1
  42. package/dist/adapters/cc-switch/skill-claude.js +2 -11
  43. package/dist/adapters/cc-switch/skill-claudecode.d.ts +0 -1
  44. package/dist/adapters/cc-switch/skill-claudecode.js +2 -11
  45. package/dist/adapters/cc-switch/skill-codex.d.ts +0 -1
  46. package/dist/adapters/cc-switch/skill-codex.js +2 -6
  47. package/dist/adapters/cc-switch/skill-directory.d.ts +0 -1
  48. package/dist/adapters/cc-switch/skill-directory.js +3 -137
  49. package/dist/adapters/cc-switch/skill-opencode.d.ts +0 -1
  50. package/dist/adapters/cc-switch/skill-opencode.js +2 -11
  51. package/dist/adapters/cc-switch/skill-placeholder.d.ts +0 -1
  52. package/dist/adapters/cc-switch/skill-placeholder.js +2 -19
  53. package/dist/config.d.ts +0 -1
  54. package/dist/config.js +2 -290
  55. package/dist/index.d.ts +0 -1
  56. package/dist/index.js +2 -573
  57. package/dist/middleware/auth.d.ts +0 -1
  58. package/dist/middleware/auth.js +2 -98
  59. package/dist/routes/acode-todos.d.ts +0 -1
  60. package/dist/routes/acode-todos.js +2 -75
  61. package/dist/routes/ai-sources.d.ts +0 -1
  62. package/dist/routes/ai-sources.js +2 -132
  63. package/dist/routes/dashboard.d.ts +0 -1
  64. package/dist/routes/dashboard.js +182 -649
  65. package/dist/routes/events.d.ts +0 -1
  66. package/dist/routes/events.js +2 -22
  67. package/dist/routes/file-browser.d.ts +0 -1
  68. package/dist/routes/file-browser.js +2 -781
  69. package/dist/routes/file-diff-rollback.d.ts +0 -1
  70. package/dist/routes/file-diff-rollback.js +2 -301
  71. package/dist/routes/file-snapshot.d.ts +0 -1
  72. package/dist/routes/file-snapshot.js +2 -156
  73. package/dist/routes/git.d.ts +0 -1
  74. package/dist/routes/git.js +28 -1900
  75. package/dist/routes/instance.d.ts +0 -1
  76. package/dist/routes/instance.js +3 -748
  77. package/dist/routes/mcp.d.ts +0 -1
  78. package/dist/routes/mcp.js +2 -196
  79. package/dist/routes/processes.d.ts +0 -1
  80. package/dist/routes/processes.js +2 -252
  81. package/dist/routes/properties.d.ts +0 -1
  82. package/dist/routes/properties.js +2 -72
  83. package/dist/routes/pty.d.ts +0 -1
  84. package/dist/routes/pty.js +6 -560
  85. package/dist/routes/runtime-binding.d.ts +0 -1
  86. package/dist/routes/runtime-binding.js +2 -263
  87. package/dist/routes/sessions.d.ts +0 -1
  88. package/dist/routes/sessions.js +2 -758
  89. package/dist/routes/skills.d.ts +0 -1
  90. package/dist/routes/skills.js +2 -187
  91. package/dist/routes/sub-agents.d.ts +0 -1
  92. package/dist/routes/sub-agents.js +20 -279
  93. package/dist/routes/system-metrics.d.ts +0 -1
  94. package/dist/routes/system-metrics.js +2 -20
  95. package/dist/routes/task-context.d.ts +0 -1
  96. package/dist/routes/task-context.js +2 -78
  97. package/dist/routes/terminal.d.ts +0 -1
  98. package/dist/routes/terminal.js +54 -2882
  99. package/dist/routes/yml.d.ts +0 -1
  100. package/dist/routes/yml.js +2 -223
  101. package/dist/services/acode-package.d.ts +0 -1
  102. package/dist/services/acode-package.js +2 -111
  103. package/dist/services/agent-event-store.d.ts +0 -1
  104. package/dist/services/agent-event-store.js +2 -81
  105. package/dist/services/agent-process-manager.d.ts +0 -1
  106. package/dist/services/agent-process-manager.js +2 -796
  107. package/dist/services/ai-source-apply.d.ts +0 -1
  108. package/dist/services/ai-source-apply.js +2 -83
  109. package/dist/services/auto-register.d.ts +0 -1
  110. package/dist/services/auto-register.js +2 -1053
  111. package/dist/services/aws-client-agent-mcp.d.ts +0 -1
  112. package/dist/services/aws-client-agent-mcp.js +3 -337
  113. package/dist/services/bridge-package-publish-hooks.d.ts +0 -1
  114. package/dist/services/bridge-package-publish-hooks.js +2 -115
  115. package/dist/services/cc-switch-sdk.d.ts +0 -1
  116. package/dist/services/cc-switch-sdk.js +2 -135
  117. package/dist/services/cli-commands.d.ts +6 -1
  118. package/dist/services/cli-commands.js +6 -645
  119. package/dist/services/event-delta-pusher.d.ts +0 -1
  120. package/dist/services/event-delta-pusher.js +2 -95
  121. package/dist/services/fileSnapshotStore.d.ts +0 -1
  122. package/dist/services/fileSnapshotStore.js +2 -500
  123. package/dist/services/instance-ai-config-service.d.ts +0 -1
  124. package/dist/services/instance-ai-config-service.js +2 -174
  125. package/dist/services/instance-init-service.d.ts +0 -1
  126. package/dist/services/instance-init-service.js +2 -193
  127. package/dist/services/instance-service.d.ts +0 -1
  128. package/dist/services/instance-service.js +2 -237
  129. package/dist/services/instance-state.d.ts +0 -1
  130. package/dist/services/instance-state.js +2 -94
  131. package/dist/services/legacy-path-migration.d.ts +0 -1
  132. package/dist/services/legacy-path-migration.js +2 -66
  133. package/dist/services/lifecycle-state.d.ts +0 -1
  134. package/dist/services/lifecycle-state.js +3 -110
  135. package/dist/services/mcp-launch-binding-queue.d.ts +0 -1
  136. package/dist/services/mcp-launch-binding-queue.js +2 -175
  137. package/dist/services/orphan-monitor.d.ts +0 -1
  138. package/dist/services/orphan-monitor.js +2 -382
  139. package/dist/services/panel-auth.d.ts +0 -1
  140. package/dist/services/panel-auth.js +2 -168
  141. package/dist/services/process-detector.d.ts +0 -1
  142. package/dist/services/process-detector.js +18 -1019
  143. package/dist/services/process-registry.d.ts +0 -1
  144. package/dist/services/process-registry.js +2 -375
  145. package/dist/services/ring-buffer.d.ts +0 -1
  146. package/dist/services/ring-buffer.js +2 -103
  147. package/dist/services/runtime-binding.d.ts +0 -1
  148. package/dist/services/runtime-binding.js +6 -363
  149. package/dist/services/runtime-lifecycle-policy.d.ts +0 -1
  150. package/dist/services/runtime-lifecycle-policy.js +2 -35
  151. package/dist/services/session-output.d.ts +0 -1
  152. package/dist/services/session-output.js +5 -539
  153. package/dist/services/startup-config-wizard.d.ts +0 -1
  154. package/dist/services/startup-config-wizard.js +14 -258
  155. package/dist/services/sub-agent-manager-instance.d.ts +0 -1
  156. package/dist/services/sub-agent-manager-instance.js +2 -12
  157. package/dist/services/system-metrics.d.ts +0 -1
  158. package/dist/services/system-metrics.js +2 -252
  159. package/dist/services/terminal-persistence.d.ts +0 -1
  160. package/dist/services/terminal-persistence.js +2 -176
  161. package/dist/services/tool-installer.d.ts +0 -1
  162. package/dist/services/tool-installer.js +5 -751
  163. package/dist/services/tunnel-client.d.ts +0 -1
  164. package/dist/services/tunnel-client.js +2 -402
  165. package/dist/services/windows-service-manager.d.ts +3 -2
  166. package/dist/services/windows-service-manager.js +2 -54
  167. package/dist/services/workspace-files.d.ts +0 -1
  168. package/dist/services/workspace-files.js +2 -1275
  169. package/dist/services/workspace-watch.d.ts +0 -1
  170. package/dist/services/workspace-watch.js +2 -123
  171. package/dist/services/workspaceTaskChangeWatcher.d.ts +0 -1
  172. package/dist/services/workspaceTaskChangeWatcher.js +2 -483
  173. package/dist/types.d.ts +0 -1
  174. package/dist/types.js +1 -1
  175. package/dist/utils/file-utils.d.ts +0 -1
  176. package/dist/utils/file-utils.js +3 -208
  177. package/dist/utils/instance-ai-config-debug-logger.d.ts +0 -1
  178. package/dist/utils/instance-ai-config-debug-logger.js +4 -63
  179. package/dist/utils/logger.d.ts +0 -1
  180. package/dist/utils/logger.js +2 -154
  181. package/dist/utils/mcp-utils.d.ts +0 -1
  182. package/dist/utils/mcp-utils.js +2 -187
  183. package/dist/utils/path-utils.d.ts +0 -1
  184. package/dist/utils/path-utils.js +2 -44
  185. package/dist/utils/sdk-package-loader.d.ts +0 -1
  186. package/dist/utils/sdk-package-loader.js +2 -78
  187. package/dist/utils/validation.d.ts +0 -1
  188. package/dist/utils/validation.js +2 -53
  189. package/dist/utils/yaml-utils.d.ts +0 -1
  190. package/dist/utils/yaml-utils.js +2 -312
  191. package/node_modules/@cc-switch/sdk/dist/constants.d.ts +0 -1
  192. package/node_modules/@cc-switch/sdk/dist/constants.js +2 -63
  193. package/node_modules/@cc-switch/sdk/dist/errors.d.ts +0 -1
  194. package/node_modules/@cc-switch/sdk/dist/errors.js +2 -8
  195. package/node_modules/@cc-switch/sdk/dist/index.d.ts +0 -1
  196. package/node_modules/@cc-switch/sdk/dist/index.js +2 -9
  197. package/node_modules/@cc-switch/sdk/dist/schemas.d.ts +0 -1
  198. package/node_modules/@cc-switch/sdk/dist/schemas.js +2 -38
  199. package/node_modules/@cc-switch/sdk/dist/sdk.d.ts +0 -1
  200. package/node_modules/@cc-switch/sdk/dist/sdk.js +2 -470
  201. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +0 -1
  202. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +4 -307
  203. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts +0 -1
  204. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.js +2 -37
  205. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts +0 -1
  206. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.js +2 -9
  207. package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts +0 -1
  208. package/node_modules/@cc-switch/sdk/dist/services/skill-model.js +2 -41
  209. package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts +0 -1
  210. package/node_modules/@cc-switch/sdk/dist/services/skill-service.js +2 -9
  211. package/node_modules/@cc-switch/sdk/dist/state.d.ts +0 -1
  212. package/node_modules/@cc-switch/sdk/dist/state.js +2 -19
  213. package/node_modules/@cc-switch/sdk/dist/types.d.ts +0 -1
  214. package/node_modules/@cc-switch/sdk/dist/types.js +1 -1
  215. package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts +0 -1
  216. package/node_modules/@cc-switch/sdk/dist/utils/fs.js +3 -114
  217. package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts +0 -1
  218. package/node_modules/@cc-switch/sdk/dist/utils/id.js +2 -12
  219. package/node_modules/@cc-switch/sdk/package.json +1 -1
  220. package/package/acode/dist/ask-user-question-tool.d.ts +0 -1
  221. package/package/acode/dist/ask-user-question-tool.js +4 -233
  222. package/package/acode/dist/built-in-file-tools.d.ts +0 -1
  223. package/package/acode/dist/built-in-file-tools.js +4 -966
  224. package/package/acode/dist/built-in-symbols-tool.d.ts +0 -1
  225. package/package/acode/dist/built-in-symbols-tool.js +2 -117
  226. package/package/acode/dist/builtins/commandArtifacts.d.ts +0 -1
  227. package/package/acode/dist/builtins/commandArtifacts.js +9 -97
  228. package/package/acode/dist/builtins/commandOutputEvents.d.ts +0 -1
  229. package/package/acode/dist/builtins/commandOutputEvents.js +2 -56
  230. package/package/acode/dist/builtins/shellCommandPreparation.d.ts +0 -1
  231. package/package/acode/dist/builtins/shellCommandPreparation.js +5 -44
  232. package/package/acode/dist/builtins/shellCommandResult.d.ts +0 -1
  233. package/package/acode/dist/builtins/shellCommandResult.js +2 -71
  234. package/package/acode/dist/builtins/shellCommandRunner.d.ts +0 -1
  235. package/package/acode/dist/builtins/shellCommandRunner.js +2 -115
  236. package/package/acode/dist/builtins/shellOutputDecoder.d.ts +0 -1
  237. package/package/acode/dist/builtins/shellOutputDecoder.js +2 -94
  238. package/package/acode/dist/cli.d.ts +0 -1
  239. package/package/acode/dist/cli.js +4 -38
  240. package/package/acode/dist/commands/commandLoader.d.ts +0 -1
  241. package/package/acode/dist/commands/commandLoader.js +2 -33
  242. package/package/acode/dist/commands/commandParser.d.ts +0 -1
  243. package/package/acode/dist/commands/commandParser.js +3 -77
  244. package/package/acode/dist/commands/commandRegistry.d.ts +0 -1
  245. package/package/acode/dist/commands/commandRegistry.js +2 -30
  246. package/package/acode/dist/compaction.d.ts +0 -1
  247. package/package/acode/dist/compaction.js +7 -919
  248. package/package/acode/dist/config.d.ts +0 -1
  249. package/package/acode/dist/config.js +5 -105
  250. package/package/acode/dist/fs-utils.d.ts +0 -1
  251. package/package/acode/dist/fs-utils.js +2 -79
  252. package/package/acode/dist/hooks/hookBus.d.ts +0 -1
  253. package/package/acode/dist/hooks/hookBus.js +2 -40
  254. package/package/acode/dist/index.d.ts +0 -1
  255. package/package/acode/dist/index.js +2 -19
  256. package/package/acode/dist/mcpToolNames.d.ts +0 -1
  257. package/package/acode/dist/mcpToolNames.js +2 -17
  258. package/package/acode/dist/parsers/ast-cache.d.ts +0 -1
  259. package/package/acode/dist/parsers/ast-cache.js +2 -76
  260. package/package/acode/dist/parsers/language-detector.d.ts +0 -1
  261. package/package/acode/dist/parsers/language-detector.js +2 -44
  262. package/package/acode/dist/parsers/language-queries.d.ts +0 -1
  263. package/package/acode/dist/parsers/language-queries.js +12 -602
  264. package/package/acode/dist/parsers/symbol-extractor.d.ts +0 -1
  265. package/package/acode/dist/parsers/symbol-extractor.js +3 -164
  266. package/package/acode/dist/parsers/tree-sitter-loader.d.ts +0 -1
  267. package/package/acode/dist/parsers/tree-sitter-loader.js +2 -64
  268. package/package/acode/dist/parsers/types.d.ts +0 -1
  269. package/package/acode/dist/parsers/types.js +1 -7
  270. package/package/acode/dist/runtime.d.ts +0 -1
  271. package/package/acode/dist/runtime.js +86 -3771
  272. package/package/acode/dist/skill-tool.d.ts +0 -1
  273. package/package/acode/dist/skill-tool.js +5 -174
  274. package/package/acode/dist/sub-agent-manager.d.ts +0 -1
  275. package/package/acode/dist/sub-agent-manager.js +2 -1159
  276. package/package/acode/dist/sub-agent-store.d.ts +0 -1
  277. package/package/acode/dist/sub-agent-store.js +4 -236
  278. package/package/acode/dist/sub-agent-tools.d.ts +0 -1
  279. package/package/acode/dist/sub-agent-tools.js +15 -369
  280. package/package/acode/dist/sub-agent-types.d.ts +0 -1
  281. package/package/acode/dist/sub-agent-types.js +1 -8
  282. package/package/acode/dist/todo-store.d.ts +0 -1
  283. package/package/acode/dist/todo-store.js +2 -73
  284. package/package/acode/dist/todo-tool.d.ts +0 -1
  285. package/package/acode/dist/todo-tool.js +5 -121
  286. package/package/acode/dist/types.d.ts +0 -1
  287. package/package/acode/dist/types.js +1 -1
  288. package/package/acode/package.json +2 -2
  289. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +0 -1
  290. package/package/aws-client-agent-mcp/dist/agent-client.js +2 -890
  291. package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +0 -1
  292. package/package/aws-client-agent-mcp/dist/agent-client.test.js +0 -1
  293. package/package/aws-client-agent-mcp/dist/config.d.ts +0 -1
  294. package/package/aws-client-agent-mcp/dist/config.js +2 -243
  295. package/package/aws-client-agent-mcp/dist/config.test.d.ts +0 -1
  296. package/package/aws-client-agent-mcp/dist/config.test.js +0 -1
  297. package/package/aws-client-agent-mcp/dist/constants.d.ts +0 -1
  298. package/package/aws-client-agent-mcp/dist/constants.js +2 -24
  299. package/package/aws-client-agent-mcp/dist/context-manager.d.ts +0 -1
  300. package/package/aws-client-agent-mcp/dist/context-manager.js +2 -477
  301. package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +0 -1
  302. package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -1
  303. package/package/aws-client-agent-mcp/dist/http-client.d.ts +0 -1
  304. package/package/aws-client-agent-mcp/dist/http-client.js +2 -446
  305. package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +0 -1
  306. package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -1
  307. package/package/aws-client-agent-mcp/dist/index.d.ts +0 -1
  308. package/package/aws-client-agent-mcp/dist/index.js +2 -34
  309. package/package/aws-client-agent-mcp/dist/logger.d.ts +0 -1
  310. package/package/aws-client-agent-mcp/dist/logger.js +3 -178
  311. package/package/aws-client-agent-mcp/dist/logger.test.d.ts +0 -1
  312. package/package/aws-client-agent-mcp/dist/logger.test.js +0 -1
  313. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -1
  314. package/package/aws-client-agent-mcp/dist/mcp-server.js +38 -2709
  315. package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +0 -1
  316. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +0 -1
  317. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +0 -1
  318. package/package/aws-client-agent-mcp/dist/mcp-tools.js +22 -562
  319. package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +0 -1
  320. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +0 -1
  321. package/package/aws-client-agent-mcp/dist/memory-store.d.ts +0 -1
  322. package/package/aws-client-agent-mcp/dist/memory-store.js +4 -826
  323. package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +0 -1
  324. package/package/aws-client-agent-mcp/dist/memory-store.test.js +0 -1
  325. package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +0 -1
  326. package/package/aws-client-agent-mcp/dist/memory-tools.js +31 -294
  327. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +0 -1
  328. package/package/aws-client-agent-mcp/dist/message-buffer.js +2 -288
  329. package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +0 -1
  330. package/package/aws-client-agent-mcp/dist/message-buffer.test.js +0 -1
  331. package/package/aws-client-agent-mcp/dist/messageContent.d.ts +0 -1
  332. package/package/aws-client-agent-mcp/dist/messageContent.js +2 -125
  333. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +0 -1
  334. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +2 -103
  335. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +0 -1
  336. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +0 -1
  337. package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +0 -1
  338. package/package/aws-client-agent-mcp/dist/server-memory-store.js +2 -96
  339. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +0 -1
  340. package/package/aws-client-agent-mcp/dist/status-reporter.js +3 -327
  341. package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +0 -1
  342. package/package/aws-client-agent-mcp/dist/status-reporter.test.js +0 -1
  343. package/package/aws-client-agent-mcp/dist/types.d.ts +0 -1
  344. package/package/aws-client-agent-mcp/dist/types.js +2 -9
  345. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -1
  346. package/package/aws-client-agent-mcp/dist/websocket-client.js +2 -677
  347. package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +0 -1
  348. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +0 -1
  349. package/package/aws-client-agent-mcp/package.json +1 -1
  350. package/package/cc-switch-sdk/dist/constants.d.ts +0 -1
  351. package/package/cc-switch-sdk/dist/constants.js +2 -63
  352. package/package/cc-switch-sdk/dist/errors.d.ts +0 -1
  353. package/package/cc-switch-sdk/dist/errors.js +2 -8
  354. package/package/cc-switch-sdk/dist/index.d.ts +0 -1
  355. package/package/cc-switch-sdk/dist/index.js +2 -9
  356. package/package/cc-switch-sdk/dist/schemas.d.ts +0 -1
  357. package/package/cc-switch-sdk/dist/schemas.js +2 -38
  358. package/package/cc-switch-sdk/dist/sdk.d.ts +0 -1
  359. package/package/cc-switch-sdk/dist/sdk.js +2 -470
  360. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +0 -1
  361. package/package/cc-switch-sdk/dist/services/ai-config-service.js +4 -307
  362. package/package/cc-switch-sdk/dist/services/mcp-model.d.ts +0 -1
  363. package/package/cc-switch-sdk/dist/services/mcp-model.js +2 -37
  364. package/package/cc-switch-sdk/dist/services/mcp-service.d.ts +0 -1
  365. package/package/cc-switch-sdk/dist/services/mcp-service.js +2 -9
  366. package/package/cc-switch-sdk/dist/services/skill-model.d.ts +0 -1
  367. package/package/cc-switch-sdk/dist/services/skill-model.js +2 -41
  368. package/package/cc-switch-sdk/dist/services/skill-service.d.ts +0 -1
  369. package/package/cc-switch-sdk/dist/services/skill-service.js +2 -9
  370. package/package/cc-switch-sdk/dist/state.d.ts +0 -1
  371. package/package/cc-switch-sdk/dist/state.js +2 -19
  372. package/package/cc-switch-sdk/dist/types.d.ts +0 -1
  373. package/package/cc-switch-sdk/dist/types.js +1 -1
  374. package/package/cc-switch-sdk/dist/utils/fs.d.ts +0 -1
  375. package/package/cc-switch-sdk/dist/utils/fs.js +3 -114
  376. package/package/cc-switch-sdk/dist/utils/id.d.ts +0 -1
  377. package/package/cc-switch-sdk/dist/utils/id.js +2 -12
  378. package/package/cc-switch-sdk/package.json +1 -1
  379. package/package.json +4 -2
  380. package/dist/adapter/AcodeSdkAdapter.d.ts.map +0 -1
  381. package/dist/adapter/AdapterRegistry.d.ts.map +0 -1
  382. package/dist/adapter/ClaudeSdkAdapter.d.ts.map +0 -1
  383. package/dist/adapter/CodexSdkAdapter.d.ts.map +0 -1
  384. package/dist/adapter/OpencodeSdkAdapter.d.ts.map +0 -1
  385. package/dist/adapter/SdkProviderSpi.d.ts.map +0 -1
  386. package/dist/adapter/acode-tool-names.d.ts.map +0 -1
  387. package/dist/adapter/idle-poll-loop.d.ts.map +0 -1
  388. package/dist/adapter/index.d.ts.map +0 -1
  389. package/dist/adapter/instance-ai-config-resolver.d.ts.map +0 -1
  390. package/dist/adapter/types.d.ts.map +0 -1
  391. package/dist/adapters/cc-switch/common.d.ts.map +0 -1
  392. package/dist/adapters/cc-switch/index.d.ts.map +0 -1
  393. package/dist/adapters/cc-switch/mcp-acode.d.ts.map +0 -1
  394. package/dist/adapters/cc-switch/mcp-claude.d.ts.map +0 -1
  395. package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +0 -1
  396. package/dist/adapters/cc-switch/mcp-codex.d.ts.map +0 -1
  397. package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +0 -1
  398. package/dist/adapters/cc-switch/mcp-placeholder.d.ts.map +0 -1
  399. package/dist/adapters/cc-switch/skill-acode.d.ts.map +0 -1
  400. package/dist/adapters/cc-switch/skill-claude.d.ts.map +0 -1
  401. package/dist/adapters/cc-switch/skill-claudecode.d.ts.map +0 -1
  402. package/dist/adapters/cc-switch/skill-codex.d.ts.map +0 -1
  403. package/dist/adapters/cc-switch/skill-directory.d.ts.map +0 -1
  404. package/dist/adapters/cc-switch/skill-opencode.d.ts.map +0 -1
  405. package/dist/adapters/cc-switch/skill-placeholder.d.ts.map +0 -1
  406. package/dist/config.d.ts.map +0 -1
  407. package/dist/index.d.ts.map +0 -1
  408. package/dist/middleware/auth.d.ts.map +0 -1
  409. package/dist/routes/acode-todos.d.ts.map +0 -1
  410. package/dist/routes/ai-sources.d.ts.map +0 -1
  411. package/dist/routes/dashboard.d.ts.map +0 -1
  412. package/dist/routes/events.d.ts.map +0 -1
  413. package/dist/routes/file-browser.d.ts.map +0 -1
  414. package/dist/routes/file-diff-rollback.d.ts.map +0 -1
  415. package/dist/routes/file-snapshot.d.ts.map +0 -1
  416. package/dist/routes/git.d.ts.map +0 -1
  417. package/dist/routes/instance.d.ts.map +0 -1
  418. package/dist/routes/mcp.d.ts.map +0 -1
  419. package/dist/routes/processes.d.ts.map +0 -1
  420. package/dist/routes/properties.d.ts.map +0 -1
  421. package/dist/routes/pty.d.ts.map +0 -1
  422. package/dist/routes/runtime-binding.d.ts.map +0 -1
  423. package/dist/routes/sessions.d.ts.map +0 -1
  424. package/dist/routes/skills.d.ts.map +0 -1
  425. package/dist/routes/sub-agents.d.ts.map +0 -1
  426. package/dist/routes/system-metrics.d.ts.map +0 -1
  427. package/dist/routes/task-context.d.ts.map +0 -1
  428. package/dist/routes/terminal.d.ts.map +0 -1
  429. package/dist/routes/yml.d.ts.map +0 -1
  430. package/dist/services/acode-package.d.ts.map +0 -1
  431. package/dist/services/agent-event-store.d.ts.map +0 -1
  432. package/dist/services/agent-process-manager.d.ts.map +0 -1
  433. package/dist/services/ai-source-apply.d.ts.map +0 -1
  434. package/dist/services/auto-register.d.ts.map +0 -1
  435. package/dist/services/aws-client-agent-mcp.d.ts.map +0 -1
  436. package/dist/services/bridge-package-publish-hooks.d.ts.map +0 -1
  437. package/dist/services/cc-switch-sdk.d.ts.map +0 -1
  438. package/dist/services/cli-commands.d.ts.map +0 -1
  439. package/dist/services/event-delta-pusher.d.ts.map +0 -1
  440. package/dist/services/fileSnapshotStore.d.ts.map +0 -1
  441. package/dist/services/instance-ai-config-service.d.ts.map +0 -1
  442. package/dist/services/instance-init-service.d.ts.map +0 -1
  443. package/dist/services/instance-service.d.ts.map +0 -1
  444. package/dist/services/instance-state.d.ts.map +0 -1
  445. package/dist/services/legacy-path-migration.d.ts.map +0 -1
  446. package/dist/services/lifecycle-state.d.ts.map +0 -1
  447. package/dist/services/mcp-launch-binding-queue.d.ts.map +0 -1
  448. package/dist/services/orphan-monitor.d.ts.map +0 -1
  449. package/dist/services/panel-auth.d.ts.map +0 -1
  450. package/dist/services/process-detector.d.ts.map +0 -1
  451. package/dist/services/process-registry.d.ts.map +0 -1
  452. package/dist/services/ring-buffer.d.ts.map +0 -1
  453. package/dist/services/runtime-binding.d.ts.map +0 -1
  454. package/dist/services/runtime-lifecycle-policy.d.ts.map +0 -1
  455. package/dist/services/session-output.d.ts.map +0 -1
  456. package/dist/services/startup-config-wizard.d.ts.map +0 -1
  457. package/dist/services/sub-agent-manager-instance.d.ts.map +0 -1
  458. package/dist/services/system-metrics.d.ts.map +0 -1
  459. package/dist/services/terminal-persistence.d.ts.map +0 -1
  460. package/dist/services/tool-installer.d.ts.map +0 -1
  461. package/dist/services/tunnel-client.d.ts.map +0 -1
  462. package/dist/services/windows-service-manager.d.ts.map +0 -1
  463. package/dist/services/workspace-files.d.ts.map +0 -1
  464. package/dist/services/workspace-watch.d.ts.map +0 -1
  465. package/dist/services/workspaceTaskChangeWatcher.d.ts.map +0 -1
  466. package/dist/types.d.ts.map +0 -1
  467. package/dist/utils/file-utils.d.ts.map +0 -1
  468. package/dist/utils/instance-ai-config-debug-logger.d.ts.map +0 -1
  469. package/dist/utils/logger.d.ts.map +0 -1
  470. package/dist/utils/mcp-utils.d.ts.map +0 -1
  471. package/dist/utils/path-utils.d.ts.map +0 -1
  472. package/dist/utils/sdk-package-loader.d.ts.map +0 -1
  473. package/dist/utils/validation.d.ts.map +0 -1
  474. package/dist/utils/yaml-utils.d.ts.map +0 -1
  475. package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +0 -1
  476. package/node_modules/@cc-switch/sdk/dist/errors.d.ts.map +0 -1
  477. package/node_modules/@cc-switch/sdk/dist/index.d.ts.map +0 -1
  478. package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +0 -1
  479. package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +0 -1
  480. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +0 -1
  481. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts.map +0 -1
  482. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts.map +0 -1
  483. package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts.map +0 -1
  484. package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts.map +0 -1
  485. package/node_modules/@cc-switch/sdk/dist/state.d.ts.map +0 -1
  486. package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +0 -1
  487. package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts.map +0 -1
  488. package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts.map +0 -1
  489. package/package/acode/dist/ask-user-question-tool.d.ts.map +0 -1
  490. package/package/acode/dist/built-in-file-tools.d.ts.map +0 -1
  491. package/package/acode/dist/built-in-symbols-tool.d.ts.map +0 -1
  492. package/package/acode/dist/builtins/commandArtifacts.d.ts.map +0 -1
  493. package/package/acode/dist/builtins/commandOutputEvents.d.ts.map +0 -1
  494. package/package/acode/dist/builtins/shellCommandPreparation.d.ts.map +0 -1
  495. package/package/acode/dist/builtins/shellCommandResult.d.ts.map +0 -1
  496. package/package/acode/dist/builtins/shellCommandRunner.d.ts.map +0 -1
  497. package/package/acode/dist/builtins/shellOutputDecoder.d.ts.map +0 -1
  498. package/package/acode/dist/cli.d.ts.map +0 -1
  499. package/package/acode/dist/commands/commandLoader.d.ts.map +0 -1
  500. package/package/acode/dist/commands/commandParser.d.ts.map +0 -1
  501. package/package/acode/dist/commands/commandRegistry.d.ts.map +0 -1
  502. package/package/acode/dist/compaction.d.ts.map +0 -1
  503. package/package/acode/dist/config.d.ts.map +0 -1
  504. package/package/acode/dist/fs-utils.d.ts.map +0 -1
  505. package/package/acode/dist/hooks/hookBus.d.ts.map +0 -1
  506. package/package/acode/dist/index.d.ts.map +0 -1
  507. package/package/acode/dist/mcpToolNames.d.ts.map +0 -1
  508. package/package/acode/dist/parsers/ast-cache.d.ts.map +0 -1
  509. package/package/acode/dist/parsers/language-detector.d.ts.map +0 -1
  510. package/package/acode/dist/parsers/language-queries.d.ts.map +0 -1
  511. package/package/acode/dist/parsers/symbol-extractor.d.ts.map +0 -1
  512. package/package/acode/dist/parsers/tree-sitter-loader.d.ts.map +0 -1
  513. package/package/acode/dist/parsers/types.d.ts.map +0 -1
  514. package/package/acode/dist/runtime.d.ts.map +0 -1
  515. package/package/acode/dist/skill-tool.d.ts.map +0 -1
  516. package/package/acode/dist/sub-agent-manager.d.ts.map +0 -1
  517. package/package/acode/dist/sub-agent-store.d.ts.map +0 -1
  518. package/package/acode/dist/sub-agent-tools.d.ts.map +0 -1
  519. package/package/acode/dist/sub-agent-types.d.ts.map +0 -1
  520. package/package/acode/dist/todo-store.d.ts.map +0 -1
  521. package/package/acode/dist/todo-tool.d.ts.map +0 -1
  522. package/package/acode/dist/types.d.ts.map +0 -1
  523. package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +0 -1
  524. package/package/aws-client-agent-mcp/dist/agent-client.js.map +0 -1
  525. package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts.map +0 -1
  526. package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +0 -1
  527. package/package/aws-client-agent-mcp/dist/config.d.ts.map +0 -1
  528. package/package/aws-client-agent-mcp/dist/config.js.map +0 -1
  529. package/package/aws-client-agent-mcp/dist/config.test.d.ts.map +0 -1
  530. package/package/aws-client-agent-mcp/dist/config.test.js.map +0 -1
  531. package/package/aws-client-agent-mcp/dist/constants.d.ts.map +0 -1
  532. package/package/aws-client-agent-mcp/dist/constants.js.map +0 -1
  533. package/package/aws-client-agent-mcp/dist/context-manager.d.ts.map +0 -1
  534. package/package/aws-client-agent-mcp/dist/context-manager.js.map +0 -1
  535. package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts.map +0 -1
  536. package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +0 -1
  537. package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +0 -1
  538. package/package/aws-client-agent-mcp/dist/http-client.js.map +0 -1
  539. package/package/aws-client-agent-mcp/dist/http-client.test.d.ts.map +0 -1
  540. package/package/aws-client-agent-mcp/dist/http-client.test.js.map +0 -1
  541. package/package/aws-client-agent-mcp/dist/index.d.ts.map +0 -1
  542. package/package/aws-client-agent-mcp/dist/index.js.map +0 -1
  543. package/package/aws-client-agent-mcp/dist/logger.d.ts.map +0 -1
  544. package/package/aws-client-agent-mcp/dist/logger.js.map +0 -1
  545. package/package/aws-client-agent-mcp/dist/logger.test.d.ts.map +0 -1
  546. package/package/aws-client-agent-mcp/dist/logger.test.js.map +0 -1
  547. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +0 -1
  548. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +0 -1
  549. package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts.map +0 -1
  550. package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +0 -1
  551. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +0 -1
  552. package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +0 -1
  553. package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts.map +0 -1
  554. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js.map +0 -1
  555. package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +0 -1
  556. package/package/aws-client-agent-mcp/dist/memory-store.js.map +0 -1
  557. package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts.map +0 -1
  558. package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +0 -1
  559. package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +0 -1
  560. package/package/aws-client-agent-mcp/dist/memory-tools.js.map +0 -1
  561. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +0 -1
  562. package/package/aws-client-agent-mcp/dist/message-buffer.js.map +0 -1
  563. package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts.map +0 -1
  564. package/package/aws-client-agent-mcp/dist/message-buffer.test.js.map +0 -1
  565. package/package/aws-client-agent-mcp/dist/messageContent.d.ts.map +0 -1
  566. package/package/aws-client-agent-mcp/dist/messageContent.js.map +0 -1
  567. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +0 -1
  568. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +0 -1
  569. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts.map +0 -1
  570. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +0 -1
  571. package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +0 -1
  572. package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +0 -1
  573. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +0 -1
  574. package/package/aws-client-agent-mcp/dist/status-reporter.js.map +0 -1
  575. package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts.map +0 -1
  576. package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +0 -1
  577. package/package/aws-client-agent-mcp/dist/types.d.ts.map +0 -1
  578. package/package/aws-client-agent-mcp/dist/types.js.map +0 -1
  579. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +0 -1
  580. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +0 -1
  581. package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts.map +0 -1
  582. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +0 -1
  583. package/package/cc-switch-sdk/dist/constants.d.ts.map +0 -1
  584. package/package/cc-switch-sdk/dist/errors.d.ts.map +0 -1
  585. package/package/cc-switch-sdk/dist/index.d.ts.map +0 -1
  586. package/package/cc-switch-sdk/dist/schemas.d.ts.map +0 -1
  587. package/package/cc-switch-sdk/dist/sdk.d.ts.map +0 -1
  588. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +0 -1
  589. package/package/cc-switch-sdk/dist/services/mcp-model.d.ts.map +0 -1
  590. package/package/cc-switch-sdk/dist/services/mcp-service.d.ts.map +0 -1
  591. package/package/cc-switch-sdk/dist/services/skill-model.d.ts.map +0 -1
  592. package/package/cc-switch-sdk/dist/services/skill-service.d.ts.map +0 -1
  593. package/package/cc-switch-sdk/dist/state.d.ts.map +0 -1
  594. package/package/cc-switch-sdk/dist/types.d.ts.map +0 -1
  595. package/package/cc-switch-sdk/dist/utils/fs.d.ts.map +0 -1
  596. package/package/cc-switch-sdk/dist/utils/id.d.ts.map +0 -1
@@ -1,1275 +1,2 @@
1
- /**
2
- * 工作区文件服务
3
- * 负责在工作区范围内安全地浏览、读取与保存文件。
4
- */
5
- import { createReadStream, createWriteStream, promises as fs } from 'node:fs';
6
- import path from 'node:path';
7
- import { Readable } from 'node:stream';
8
- import { pipeline } from 'node:stream/promises';
9
- import { TextDecoder } from 'node:util';
10
- import { createGunzip } from 'node:zlib';
11
- import * as tar from 'tar';
12
- import unzipper from 'unzipper';
13
- const { ZipArchive } = await import('archiver');
14
- const ARCHIVE_LIMITS = {
15
- maxEntries: 10_000,
16
- maxFileBytes: 512 * 1024 * 1024,
17
- maxTotalBytes: 2 * 1024 * 1024 * 1024,
18
- maxPathDepth: 64,
19
- maxPathLength: 4096,
20
- };
21
- const GBK_DECODER = new TextDecoder('gbk');
22
- function toDisplayPath(targetPath) {
23
- return String(targetPath || '').replace(/\\/g, '/');
24
- }
25
- /**
26
- * 规范化并校验工作区路径。
27
- */
28
- function resolveWorkspaceRoot(workspacePath) {
29
- const normalizedWorkspacePath = path.resolve(String(workspacePath || '').trim());
30
- if (!normalizedWorkspacePath) {
31
- throw new Error('workspacePath is required');
32
- }
33
- return normalizedWorkspacePath;
34
- }
35
- async function resolveWorkspaceRootRealpath(workspacePath) {
36
- const workspaceRoot = resolveWorkspaceRoot(workspacePath);
37
- const realWorkspaceRoot = await fs.realpath(workspaceRoot);
38
- const stat = await fs.stat(realWorkspaceRoot);
39
- if (!stat.isDirectory()) {
40
- throw new Error(`Workspace path is not a directory: ${workspaceRoot}`);
41
- }
42
- return realWorkspaceRoot;
43
- }
44
- async function resolveWorkspaceRoots(workspacePath) {
45
- const requestedRoot = resolveWorkspaceRoot(workspacePath);
46
- return {
47
- requestedRoot,
48
- workspaceRoot: await resolveWorkspaceRootRealpath(workspacePath),
49
- };
50
- }
51
- /**
52
- * 判断目标路径是否位于工作区内。
53
- */
54
- function ensureInsideWorkspace(workspaceRoot, targetPath) {
55
- const resolvedTargetPath = path.resolve(targetPath);
56
- const relativePath = path.relative(workspaceRoot, resolvedTargetPath);
57
- const isOutside = relativePath.startsWith('..') || path.isAbsolute(relativePath);
58
- if (isOutside) {
59
- throw new Error(`Target path is outside workspace: ${resolvedTargetPath}`);
60
- }
61
- return resolvedTargetPath;
62
- }
63
- async function ensureRealPathInsideWorkspace(workspaceRoot, targetPath) {
64
- const realTargetPath = await fs.realpath(targetPath);
65
- return ensureInsideWorkspace(workspaceRoot, realTargetPath);
66
- }
67
- async function ensureWritableParentInsideWorkspace(workspaceRoot, targetPath) {
68
- const realParentPath = await fs.realpath(path.dirname(targetPath));
69
- ensureInsideWorkspace(workspaceRoot, realParentPath);
70
- try {
71
- const targetLinkStat = await fs.lstat(targetPath);
72
- if (targetLinkStat.isSymbolicLink()) {
73
- throw new Error(`Refusing to write through symbolic link: ${targetPath}`);
74
- }
75
- }
76
- catch (error) {
77
- const errorCode = typeof error === 'object' && error !== null ? Reflect.get(error, 'code') : undefined;
78
- if (errorCode !== 'ENOENT') {
79
- throw error;
80
- }
81
- }
82
- }
83
- function resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, requestedPath) {
84
- const resolvedRequestedPath = path.resolve(requestedPath);
85
- const requestedRelativePath = path.relative(requestedRoot, resolvedRequestedPath);
86
- if (requestedRelativePath.startsWith('..') || path.isAbsolute(requestedRelativePath)) {
87
- throw new Error(`Target path is outside workspace: ${resolvedRequestedPath}`);
88
- }
89
- return ensureInsideWorkspace(workspaceRoot, path.join(workspaceRoot, requestedRelativePath));
90
- }
91
- /**
92
- * 解析目录目标路径,空路径时回退到工作区根目录。
93
- */
94
- async function resolveDirectoryTarget({ workspacePath, targetPath = '' }) {
95
- const { requestedRoot, workspaceRoot } = await resolveWorkspaceRoots(workspacePath);
96
- const requestedPath = String(targetPath || '').trim();
97
- const lexicalTargetPath = requestedPath
98
- ? resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, requestedPath)
99
- : workspaceRoot;
100
- const resolvedTargetPath = await ensureRealPathInsideWorkspace(workspaceRoot, lexicalTargetPath);
101
- return { workspaceRoot, resolvedTargetPath };
102
- }
103
- /**
104
- * 校验工作区内的条目名称,避免跨目录操作与非法名称。
105
- */
106
- function normalizeEntryName(rawName) {
107
- const entryName = String(rawName || '').trim();
108
- if (!entryName) {
109
- throw new Error('entryName is required');
110
- }
111
- if (entryName === '.' || entryName === '..') {
112
- throw new Error('entryName is invalid');
113
- }
114
- if (entryName.includes('/') || entryName.includes('\\') || path.basename(entryName) !== entryName) {
115
- throw new Error(`entryName must be a plain file or directory name: ${entryName}`);
116
- }
117
- return entryName;
118
- }
119
- /**
120
- * 修正 multipart 文件名在部分代理链路中被 latin1 解读造成的 UTF-8 中文乱码。
121
- */
122
- function decodePossiblyLatin1Utf8FileName(rawName) {
123
- const fileName = String(rawName || '');
124
- if (!/[\u00c0-\u00ff]/.test(fileName)) {
125
- return fileName;
126
- }
127
- const decodedFileName = Buffer.from(fileName, 'latin1').toString('utf8');
128
- if (!decodedFileName || decodedFileName.includes('\uFFFD')) {
129
- return fileName;
130
- }
131
- return decodedFileName;
132
- }
133
- /**
134
- * 将上传文件名规范化为单级文件名,避免浏览器提交的路径穿透目录。
135
- */
136
- function normalizeUploadedFileName(rawName) {
137
- const decodedName = decodePossiblyLatin1Utf8FileName(rawName);
138
- return normalizeEntryName(path.basename(decodedName.replace(/\\/g, '/')));
139
- }
140
- /**
141
- * 将浏览器传入的上传相对路径拆成安全路径片段,保留文件夹层级但拒绝穿透路径。
142
- */
143
- function normalizeUploadedRelativePath(rawPath, fallbackName) {
144
- if (!rawPath) {
145
- return normalizeUploadedFileName(fallbackName);
146
- }
147
- const decodedPath = decodePossiblyLatin1Utf8FileName(String(rawPath || fallbackName || '')).replace(/\\/g, '/');
148
- const segments = decodedPath.split('/').filter(Boolean).map((segment) => normalizeEntryName(segment));
149
- if (segments.length === 0) {
150
- return normalizeUploadedFileName(fallbackName);
151
- }
152
- return path.join(...segments);
153
- }
154
- function formatModeOctal(mode) {
155
- return (mode & 0o7777).toString(8).padStart(4, '0');
156
- }
157
- function parsePermissionMode(rawMode) {
158
- const modeText = String(rawMode || '').trim();
159
- if (!/^[0-7]{3,4}$/.test(modeText)) {
160
- throw new Error('mode must be a 3 or 4 digit octal string');
161
- }
162
- return Number.parseInt(modeText, 8);
163
- }
164
- function permissionTriplet(mode, readBit, writeBit, executeBit, specialBit, specialExecute, specialNoExecute) {
165
- const read = (mode & readBit) !== 0 ? 'r' : '-';
166
- const write = (mode & writeBit) !== 0 ? 'w' : '-';
167
- const hasExecute = (mode & executeBit) !== 0;
168
- const hasSpecial = (mode & specialBit) !== 0;
169
- const execute = hasSpecial ? (hasExecute ? specialExecute : specialNoExecute) : (hasExecute ? 'x' : '-');
170
- return `${read}${write}${execute}`;
171
- }
172
- function formatPermissions(mode) {
173
- return [
174
- permissionTriplet(mode, 0o400, 0o200, 0o100, 0o4000, 's', 'S'),
175
- permissionTriplet(mode, 0o040, 0o020, 0o010, 0o2000, 's', 'S'),
176
- permissionTriplet(mode, 0o004, 0o002, 0o001, 0o1000, 't', 'T'),
177
- ].join('');
178
- }
179
- function readPngDimensions(buffer) {
180
- if (buffer.length < 24 || buffer.toString('hex', 0, 8) !== '89504e470d0a1a0a') {
181
- return undefined;
182
- }
183
- return {
184
- format: 'png',
185
- width: buffer.readUInt32BE(16),
186
- height: buffer.readUInt32BE(20),
187
- };
188
- }
189
- function readGifDimensions(buffer) {
190
- if (buffer.length < 10 || (buffer.toString('ascii', 0, 6) !== 'GIF87a' && buffer.toString('ascii', 0, 6) !== 'GIF89a')) {
191
- return undefined;
192
- }
193
- return {
194
- format: 'gif',
195
- width: buffer.readUInt16LE(6),
196
- height: buffer.readUInt16LE(8),
197
- };
198
- }
199
- function readJpegDimensions(buffer) {
200
- if (buffer.length < 4 || buffer[0] !== 0xff || buffer[1] !== 0xd8) {
201
- return undefined;
202
- }
203
- let offset = 2;
204
- while (offset + 9 < buffer.length) {
205
- if (buffer[offset] !== 0xff) {
206
- offset += 1;
207
- continue;
208
- }
209
- const marker = buffer[offset + 1];
210
- const segmentLength = buffer.readUInt16BE(offset + 2);
211
- if (segmentLength < 2) {
212
- return undefined;
213
- }
214
- const isStartOfFrame = marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
215
- if (isStartOfFrame && offset + 8 < buffer.length) {
216
- return {
217
- format: 'jpeg',
218
- height: buffer.readUInt16BE(offset + 5),
219
- width: buffer.readUInt16BE(offset + 7),
220
- };
221
- }
222
- offset += 2 + segmentLength;
223
- }
224
- return undefined;
225
- }
226
- function readWebpDimensions(buffer) {
227
- if (buffer.length < 30 || buffer.toString('ascii', 0, 4) !== 'RIFF' || buffer.toString('ascii', 8, 12) !== 'WEBP') {
228
- return undefined;
229
- }
230
- const chunkType = buffer.toString('ascii', 12, 16);
231
- if (chunkType === 'VP8X' && buffer.length >= 30) {
232
- return {
233
- format: 'webp',
234
- width: 1 + buffer.readUIntLE(24, 3),
235
- height: 1 + buffer.readUIntLE(27, 3),
236
- };
237
- }
238
- if (chunkType === 'VP8 ' && buffer.length >= 30) {
239
- return {
240
- format: 'webp',
241
- width: buffer.readUInt16LE(26) & 0x3fff,
242
- height: buffer.readUInt16LE(28) & 0x3fff,
243
- };
244
- }
245
- if (chunkType === 'VP8L' && buffer.length >= 25) {
246
- const bits = buffer.readUInt32LE(21);
247
- return {
248
- format: 'webp',
249
- width: (bits & 0x3fff) + 1,
250
- height: ((bits >> 14) & 0x3fff) + 1,
251
- };
252
- }
253
- return undefined;
254
- }
255
- async function collectChmodTargets(workspaceRoot, targetPath, recursive) {
256
- const linkStat = await fs.lstat(targetPath);
257
- if (linkStat.isSymbolicLink()) {
258
- throw new Error(`Refusing to change permissions on symbolic link: ${targetPath}`);
259
- }
260
- const stat = await fs.stat(targetPath);
261
- if (!recursive || !stat.isDirectory()) {
262
- return [targetPath];
263
- }
264
- const children = await fs.readdir(targetPath);
265
- const nestedTargets = await Promise.all(children.map((childName) => {
266
- const childPath = ensureInsideWorkspace(workspaceRoot, path.join(targetPath, childName));
267
- return collectChmodTargets(workspaceRoot, childPath, true);
268
- }));
269
- return [targetPath, ...nestedTargets.flat()];
270
- }
271
- async function ensureSafeChmodTarget(workspaceRoot, targetPath) {
272
- const linkStat = await fs.lstat(targetPath);
273
- if (linkStat.isSymbolicLink()) {
274
- throw new Error(`Refusing to change permissions on symbolic link: ${targetPath}`);
275
- }
276
- await ensureRealPathInsideWorkspace(workspaceRoot, targetPath);
277
- }
278
- async function readImageProperties(filePath) {
279
- const header = await fs.open(filePath, 'r');
280
- try {
281
- const buffer = Buffer.alloc(512);
282
- const { bytesRead } = await header.read(buffer, 0, buffer.length, 0);
283
- const imageHeader = buffer.subarray(0, bytesRead);
284
- return readPngDimensions(imageHeader)
285
- || readGifDimensions(imageHeader)
286
- || readJpegDimensions(imageHeader)
287
- || readWebpDimensions(imageHeader);
288
- }
289
- finally {
290
- await header.close();
291
- }
292
- }
293
- /**
294
- * 解析并校验工作区内条目的目标路径。
295
- */
296
- async function resolveEntryTarget({ workspacePath, targetPath }, options = {}) {
297
- const { requestedRoot, workspaceRoot } = await resolveWorkspaceRoots(workspacePath);
298
- const normalizedTargetPath = String(targetPath || '').trim();
299
- if (!normalizedTargetPath) {
300
- throw new Error('targetPath is required');
301
- }
302
- const lexicalTargetPath = resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, normalizedTargetPath);
303
- const resolvedTargetPath = await ensureRealPathInsideWorkspace(workspaceRoot, lexicalTargetPath);
304
- if (!options.allowWorkspaceRoot && resolvedTargetPath === workspaceRoot) {
305
- throw new Error('Workspace root cannot be modified');
306
- }
307
- return { workspaceRoot, resolvedTargetPath };
308
- }
309
- async function resolveWritableDirectory(params) {
310
- const { requestedRoot, workspaceRoot } = await resolveWorkspaceRoots(params.workspacePath);
311
- const requestedPath = String(params.targetPath || '').trim();
312
- if (!requestedPath) {
313
- return { workspaceRoot, resolvedTargetPath: workspaceRoot };
314
- }
315
- const lexicalTargetPath = resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, requestedPath);
316
- if (await pathExists(lexicalTargetPath)) {
317
- return {
318
- workspaceRoot,
319
- resolvedTargetPath: await ensureRealPathInsideWorkspace(workspaceRoot, lexicalTargetPath),
320
- };
321
- }
322
- const realParentPath = await fs.realpath(path.dirname(lexicalTargetPath));
323
- ensureInsideWorkspace(workspaceRoot, realParentPath);
324
- return { workspaceRoot, resolvedTargetPath: lexicalTargetPath };
325
- }
326
- function archiveKind(filePath) {
327
- const lowerName = filePath.toLowerCase();
328
- if (lowerName.endsWith('.zip'))
329
- return 'zip';
330
- if (lowerName.endsWith('.tar.gz') || lowerName.endsWith('.tgz'))
331
- return 'tgz';
332
- if (lowerName.endsWith('.tar'))
333
- return 'tar';
334
- if (lowerName.endsWith('.gz'))
335
- return 'gz';
336
- return null;
337
- }
338
- function stripArchiveExtension(fileName) {
339
- return fileName.replace(/\.tar\.gz$/i, '').replace(/\.tgz$/i, '').replace(/\.zip$/i, '').replace(/\.tar$/i, '').replace(/\.gz$/i, '');
340
- }
341
- function resolveArchiveUploadOutputPath(uploadTargetDirectory, archiveFileName) {
342
- const outputDirectoryName = normalizeEntryName(stripArchiveExtension(archiveFileName) || 'extracted');
343
- return path.join(uploadTargetDirectory, outputDirectoryName);
344
- }
345
- function ensureArchiveEntryInside(targetRoot, entryPath) {
346
- const normalizedEntryPath = String(entryPath || '').replace(/\\/g, '/');
347
- const pathSegments = normalizedEntryPath.split('/').filter(Boolean);
348
- if (!normalizedEntryPath || normalizedEntryPath.startsWith('/') || /^[A-Za-z]:/.test(normalizedEntryPath)) {
349
- throw new Error(`Archive entry path is invalid: ${entryPath}`);
350
- }
351
- if (normalizedEntryPath.length > ARCHIVE_LIMITS.maxPathLength || pathSegments.length > ARCHIVE_LIMITS.maxPathDepth) {
352
- throw new Error(`Archive entry path is too deep or too long: ${entryPath}`);
353
- }
354
- return ensureInsideWorkspace(targetRoot, path.join(targetRoot, normalizedEntryPath));
355
- }
356
- function decodeZipEntryPath(entry) {
357
- const pathBuffer = entry.props?.pathBuffer;
358
- if (!pathBuffer || entry.props?.flags?.isUnicode) {
359
- return entry.path;
360
- }
361
- const decodedPath = GBK_DECODER.decode(pathBuffer);
362
- if (!decodedPath || decodedPath.includes('\uFFFD')) {
363
- return entry.path;
364
- }
365
- return decodedPath;
366
- }
367
- async function drainZipEntry(entry) {
368
- const drainedStream = entry.autodrain?.();
369
- if (!drainedStream) {
370
- return;
371
- }
372
- await new Promise((resolve, reject) => {
373
- drainedStream.on('close', resolve);
374
- drainedStream.on('end', resolve);
375
- drainedStream.on('finish', resolve);
376
- drainedStream.on('error', reject);
377
- });
378
- }
379
- function trackArchiveEntry(meter) {
380
- meter.entries += 1;
381
- if (meter.entries > ARCHIVE_LIMITS.maxEntries) {
382
- throw new Error(`Archive contains too many entries. Limit: ${ARCHIVE_LIMITS.maxEntries}`);
383
- }
384
- }
385
- function trackArchiveBytes(meter, bytes) {
386
- meter.totalBytes += bytes;
387
- if (bytes > ARCHIVE_LIMITS.maxFileBytes) {
388
- throw new Error(`Archive entry exceeds max size. Limit: ${ARCHIVE_LIMITS.maxFileBytes} bytes`);
389
- }
390
- if (meter.totalBytes > ARCHIVE_LIMITS.maxTotalBytes) {
391
- throw new Error(`Archive extraction exceeds total size. Limit: ${ARCHIVE_LIMITS.maxTotalBytes} bytes`);
392
- }
393
- }
394
- async function pipelineWithArchiveLimits(input, destinationPath, meter) {
395
- let fileBytes = 0;
396
- input.on('data', (chunk) => {
397
- const chunkBytes = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
398
- fileBytes += chunkBytes;
399
- if (fileBytes > ARCHIVE_LIMITS.maxFileBytes || meter.totalBytes + fileBytes > ARCHIVE_LIMITS.maxTotalBytes) {
400
- if (input instanceof Readable) {
401
- input.destroy(new Error('Archive extraction size limit exceeded'));
402
- }
403
- }
404
- });
405
- await pipeline(input, createWriteStream(destinationPath));
406
- trackArchiveBytes(meter, fileBytes);
407
- }
408
- async function pathExists(targetPath) {
409
- try {
410
- await fs.access(targetPath);
411
- return true;
412
- }
413
- catch (error) {
414
- const errorCode = typeof error === 'object' && error !== null ? Reflect.get(error, 'code') : undefined;
415
- if (errorCode === 'ENOENT') {
416
- return false;
417
- }
418
- throw error;
419
- }
420
- }
421
- /**
422
- * 解析文件目标路径,并确保文件位于工作区中。
423
- */
424
- async function resolveFileTarget({ workspacePath, filePath }) {
425
- const { requestedRoot, workspaceRoot } = await resolveWorkspaceRoots(workspacePath);
426
- const normalizedFilePath = String(filePath || '').trim();
427
- if (!normalizedFilePath) {
428
- throw new Error('filePath is required');
429
- }
430
- return {
431
- workspaceRoot,
432
- resolvedFilePath: resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, normalizedFilePath)
433
- };
434
- }
435
- /**
436
- * 列出工作区目录内容。
437
- */
438
- export async function listWorkspaceDirectory(params) {
439
- const { workspaceRoot, resolvedTargetPath } = await resolveDirectoryTarget(params);
440
- const stat = await fs.stat(resolvedTargetPath);
441
- if (!stat.isDirectory()) {
442
- throw new Error(`Path is not a directory: ${resolvedTargetPath}`);
443
- }
444
- const entries = await fs.readdir(resolvedTargetPath, { withFileTypes: true });
445
- const items = await Promise.all(entries.map(async (entry) => {
446
- const entryPath = path.join(resolvedTargetPath, entry.name);
447
- const entrySize = entry.isFile() ? (await fs.stat(entryPath)).size : undefined;
448
- return {
449
- name: entry.name,
450
- path: toDisplayPath(entryPath),
451
- isDirectory: entry.isDirectory(),
452
- size: entrySize
453
- };
454
- }));
455
- return {
456
- currentPath: toDisplayPath(resolvedTargetPath),
457
- workspacePath: toDisplayPath(workspaceRoot),
458
- items
459
- };
460
- }
461
- /**
462
- * 读取工作区中的文本文件。
463
- */
464
- export async function readWorkspaceFile(params) {
465
- const { workspaceRoot, resolvedFilePath: lexicalFilePath } = await resolveFileTarget(params);
466
- const resolvedFilePath = await ensureRealPathInsideWorkspace(workspaceRoot, lexicalFilePath);
467
- const stat = await fs.stat(resolvedFilePath);
468
- if (!stat.isFile()) {
469
- throw new Error(`Path is not a file: ${resolvedFilePath}`);
470
- }
471
- const content = await fs.readFile(resolvedFilePath, 'utf-8');
472
- return {
473
- workspacePath: toDisplayPath(workspaceRoot),
474
- filePath: toDisplayPath(resolvedFilePath),
475
- content
476
- };
477
- }
478
- /**
479
- * 目录大小计算上限,避免在工作区体量异常时长时间阻塞属性查询。
480
- */
481
- const DIRECTORY_SIZE_LIMITS = {
482
- maxEntries: 100_000,
483
- maxDepth: 32,
484
- };
485
- /**
486
- * 递归累加目录内所有普通文件的总大小。
487
- * 跳过符号链接以避免跟随循环,访问失败的单个条目直接忽略。
488
- */
489
- async function calculateDirectorySize(workspaceRoot, directoryPath) {
490
- let total = 0;
491
- let entriesProcessed = 0;
492
- async function walk(dirPath, depth) {
493
- if (depth >= DIRECTORY_SIZE_LIMITS.maxDepth) {
494
- return;
495
- }
496
- let entries;
497
- try {
498
- entries = await fs.readdir(dirPath, { withFileTypes: true });
499
- }
500
- catch {
501
- return;
502
- }
503
- for (const entry of entries) {
504
- entriesProcessed += 1;
505
- if (entriesProcessed > DIRECTORY_SIZE_LIMITS.maxEntries) {
506
- return;
507
- }
508
- if (entry.isSymbolicLink()) {
509
- continue;
510
- }
511
- const entryPath = ensureInsideWorkspace(workspaceRoot, path.join(dirPath, entry.name));
512
- try {
513
- if (entry.isDirectory()) {
514
- await walk(entryPath, depth + 1);
515
- }
516
- else if (entry.isFile()) {
517
- const stat = await fs.stat(entryPath);
518
- total += stat.size;
519
- }
520
- }
521
- catch {
522
- // 单个条目失败不影响整体统计
523
- }
524
- }
525
- }
526
- await walk(directoryPath, 0);
527
- return total;
528
- }
529
- /**
530
- * 读取工作区条目的属性信息。
531
- * 主流程:解析安全路径 -> 读取 stat/lstat -> 文件额外尝试解析图片尺寸。
532
- * 对于目录,stat.size 只是目录项元数据大小,需要递归统计子文件总和。
533
- */
534
- export async function getWorkspaceEntryProperties(params) {
535
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params, { allowWorkspaceRoot: true });
536
- const [stat, linkStat] = await Promise.all([
537
- fs.stat(resolvedTargetPath),
538
- fs.lstat(resolvedTargetPath),
539
- ]);
540
- const isDirectory = stat.isDirectory();
541
- const image = stat.isFile() ? await readImageProperties(resolvedTargetPath) : undefined;
542
- const size = isDirectory
543
- ? await calculateDirectorySize(workspaceRoot, resolvedTargetPath)
544
- : stat.size;
545
- return {
546
- workspacePath: toDisplayPath(workspaceRoot),
547
- targetPath: toDisplayPath(resolvedTargetPath),
548
- name: path.basename(resolvedTargetPath),
549
- isDirectory: stat.isDirectory(),
550
- isFile: stat.isFile(),
551
- isSymbolicLink: linkStat.isSymbolicLink(),
552
- size,
553
- mode: stat.mode,
554
- modeOctal: formatModeOctal(stat.mode),
555
- permissions: formatPermissions(stat.mode),
556
- uid: stat.uid,
557
- gid: stat.gid,
558
- atimeMs: stat.atimeMs,
559
- mtimeMs: stat.mtimeMs,
560
- ctimeMs: stat.ctimeMs,
561
- birthtimeMs: stat.birthtimeMs,
562
- ...(image ? { image } : {}),
563
- };
564
- }
565
- /**
566
- * 修改工作区条目权限。
567
- * 主流程:校验八进制权限 -> 解析安全路径 -> 非递归只改目标,递归目录先收集所有非符号链接子项后逐一 chmod。
568
- */
569
- export async function chmodWorkspaceEntry(params) {
570
- const mode = parsePermissionMode(params.mode);
571
- const recursive = Boolean(params.recursive);
572
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params);
573
- const targets = await collectChmodTargets(workspaceRoot, resolvedTargetPath, recursive);
574
- for (const targetPath of targets) {
575
- await ensureSafeChmodTarget(workspaceRoot, targetPath);
576
- await fs.chmod(targetPath, mode);
577
- }
578
- return {
579
- ok: true,
580
- workspacePath: toDisplayPath(workspaceRoot),
581
- targetPath: toDisplayPath(resolvedTargetPath),
582
- mode,
583
- modeOctal: formatModeOctal(mode),
584
- recursive,
585
- changedCount: targets.length,
586
- };
587
- }
588
- /**
589
- * 返回工作区内 Word 文档的预览元数据;docx 由前端下载原文件后渲染,旧版 doc 明确提示不支持。
590
- */
591
- export async function previewWorkspaceDocument(params) {
592
- const { workspaceRoot, resolvedFilePath: lexicalFilePath } = await resolveFileTarget(params);
593
- const resolvedFilePath = await ensureRealPathInsideWorkspace(workspaceRoot, lexicalFilePath);
594
- const stat = await fs.stat(resolvedFilePath);
595
- if (!stat.isFile()) {
596
- throw new Error(`Path is not a file: ${resolvedFilePath}`);
597
- }
598
- const extension = path.extname(resolvedFilePath).toLowerCase();
599
- if (extension === '.doc') {
600
- return {
601
- workspacePath: toDisplayPath(workspaceRoot),
602
- filePath: toDisplayPath(resolvedFilePath),
603
- kind: 'unsupported',
604
- content: '',
605
- message: '暂不支持旧版 .doc 格式预览,请另存为 .docx 后再打开。',
606
- size: stat.size,
607
- mtimeMs: stat.mtimeMs,
608
- };
609
- }
610
- if (extension !== '.docx') {
611
- throw new Error('Document preview only supports .docx files');
612
- }
613
- return {
614
- workspacePath: toDisplayPath(workspaceRoot),
615
- filePath: toDisplayPath(resolvedFilePath),
616
- kind: 'html',
617
- content: '',
618
- size: stat.size,
619
- mtimeMs: stat.mtimeMs,
620
- };
621
- }
622
- /**
623
- * 将文本内容保存到工作区文件中。
624
- */
625
- export async function writeWorkspaceFile(params) {
626
- const { workspaceRoot, resolvedFilePath } = await resolveFileTarget(params);
627
- await fs.mkdir(path.dirname(resolvedFilePath), { recursive: true });
628
- await ensureWritableParentInsideWorkspace(workspaceRoot, resolvedFilePath);
629
- await fs.writeFile(resolvedFilePath, String(params.content ?? ''), 'utf-8');
630
- return {
631
- ok: true,
632
- workspacePath: toDisplayPath(workspaceRoot),
633
- filePath: toDisplayPath(resolvedFilePath)
634
- };
635
- }
636
- /**
637
- * 在指定工作区目录下创建文件或目录。
638
- */
639
- export async function createWorkspaceEntry(params) {
640
- const { workspaceRoot, resolvedTargetPath } = await resolveDirectoryTarget(params);
641
- const entryName = normalizeEntryName(params.entryName);
642
- const targetPath = ensureInsideWorkspace(workspaceRoot, path.join(resolvedTargetPath, entryName));
643
- const parentStat = await fs.stat(resolvedTargetPath);
644
- if (!parentStat.isDirectory()) {
645
- throw new Error(`Path is not a directory: ${resolvedTargetPath}`);
646
- }
647
- try {
648
- await fs.access(targetPath);
649
- throw new Error(`Path already exists: ${targetPath}`);
650
- }
651
- catch (error) {
652
- const errorCode = typeof error === 'object' && error !== null ? Reflect.get(error, 'code') : undefined;
653
- if (errorCode !== 'ENOENT') {
654
- throw error;
655
- }
656
- }
657
- if (params.isDirectory) {
658
- await fs.mkdir(targetPath, { recursive: false });
659
- }
660
- else {
661
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
662
- await ensureWritableParentInsideWorkspace(workspaceRoot, targetPath);
663
- await fs.writeFile(targetPath, '', 'utf-8');
664
- }
665
- return {
666
- ok: true,
667
- workspacePath: toDisplayPath(workspaceRoot),
668
- targetPath: toDisplayPath(targetPath),
669
- isDirectory: params.isDirectory
670
- };
671
- }
672
- /**
673
- * 在工作区内重命名文件或目录,保持目标仍位于原父目录中。
674
- */
675
- export async function renameWorkspaceEntry(params) {
676
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params);
677
- const newName = normalizeEntryName(params.newName);
678
- const renamedPath = ensureInsideWorkspace(workspaceRoot, path.join(path.dirname(resolvedTargetPath), newName));
679
- if (renamedPath === resolvedTargetPath) {
680
- return {
681
- ok: true,
682
- workspacePath: toDisplayPath(workspaceRoot),
683
- sourcePath: toDisplayPath(resolvedTargetPath),
684
- targetPath: toDisplayPath(renamedPath)
685
- };
686
- }
687
- await fs.stat(resolvedTargetPath);
688
- try {
689
- await fs.access(renamedPath);
690
- throw new Error(`Path already exists: ${renamedPath}`);
691
- }
692
- catch (error) {
693
- const errorCode = typeof error === 'object' && error !== null ? Reflect.get(error, 'code') : undefined;
694
- if (errorCode !== 'ENOENT') {
695
- throw error;
696
- }
697
- }
698
- await fs.rename(resolvedTargetPath, renamedPath);
699
- return {
700
- ok: true,
701
- workspacePath: toDisplayPath(workspaceRoot),
702
- sourcePath: toDisplayPath(resolvedTargetPath),
703
- targetPath: toDisplayPath(renamedPath)
704
- };
705
- }
706
- /**
707
- * 将工作区内文件或目录移动到另一个工作区目录,保持原文件名不变。
708
- */
709
- export async function moveWorkspaceEntry(params) {
710
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params);
711
- const destination = await resolveDirectoryTarget({
712
- workspacePath: params.workspacePath,
713
- targetPath: params.destinationPath,
714
- });
715
- const destinationStat = await fs.stat(destination.resolvedTargetPath);
716
- if (!destinationStat.isDirectory()) {
717
- throw new Error(`Destination is not a directory: ${destination.resolvedTargetPath}`);
718
- }
719
- const targetStat = await fs.stat(resolvedTargetPath);
720
- if (targetStat.isDirectory()) {
721
- const relativeDestination = path.relative(resolvedTargetPath, destination.resolvedTargetPath);
722
- if (relativeDestination === '' || (!relativeDestination.startsWith('..') && !path.isAbsolute(relativeDestination))) {
723
- throw new Error('Cannot move a directory into itself or its descendant');
724
- }
725
- }
726
- const movedPath = ensureInsideWorkspace(workspaceRoot, path.join(destination.resolvedTargetPath, path.basename(resolvedTargetPath)));
727
- if (movedPath === resolvedTargetPath) {
728
- return {
729
- ok: true,
730
- workspacePath: toDisplayPath(workspaceRoot),
731
- sourcePath: toDisplayPath(resolvedTargetPath),
732
- targetPath: toDisplayPath(movedPath),
733
- destinationPath: toDisplayPath(destination.resolvedTargetPath),
734
- };
735
- }
736
- try {
737
- await fs.access(movedPath);
738
- throw new Error(`Path already exists: ${movedPath}`);
739
- }
740
- catch (error) {
741
- const errorCode = typeof error === 'object' && error !== null ? Reflect.get(error, 'code') : undefined;
742
- if (errorCode !== 'ENOENT') {
743
- throw error;
744
- }
745
- }
746
- await fs.rename(resolvedTargetPath, movedPath);
747
- return {
748
- ok: true,
749
- workspacePath: toDisplayPath(workspaceRoot),
750
- sourcePath: toDisplayPath(resolvedTargetPath),
751
- targetPath: toDisplayPath(movedPath),
752
- destinationPath: toDisplayPath(destination.resolvedTargetPath),
753
- };
754
- }
755
- /**
756
- * 删除工作区内的文件或目录,并阻止误删工作区根目录。
757
- */
758
- export async function deleteWorkspaceEntry(params) {
759
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params);
760
- const targetStat = await fs.stat(resolvedTargetPath);
761
- if (targetStat.isDirectory()) {
762
- await fs.rm(resolvedTargetPath, { recursive: true, force: false });
763
- }
764
- else {
765
- await fs.unlink(resolvedTargetPath);
766
- }
767
- return {
768
- ok: true,
769
- workspacePath: toDisplayPath(workspaceRoot),
770
- targetPath: toDisplayPath(resolvedTargetPath)
771
- };
772
- }
773
- /**
774
- * 上传文件到工作区指定目录,并可在上传后自动解压常见归档。
775
- */
776
- export async function uploadWorkspaceFiles(params) {
777
- if (!Array.isArray(params.files) || params.files.length === 0) {
778
- throw new Error('files are required');
779
- }
780
- const { workspaceRoot, resolvedTargetPath } = await resolveWritableDirectory(params);
781
- const targetStat = await fs.stat(resolvedTargetPath);
782
- if (!targetStat.isDirectory()) {
783
- throw new Error(`Path is not a directory: ${resolvedTargetPath}`);
784
- }
785
- const files = [];
786
- for (const file of params.files) {
787
- const fileName = normalizeUploadedFileName(file.originalname);
788
- const uploadRelativePath = normalizeUploadedRelativePath(file.relativePath, file.originalname);
789
- const destinationPath = ensureInsideWorkspace(workspaceRoot, path.join(resolvedTargetPath, uploadRelativePath));
790
- await fs.mkdir(path.dirname(destinationPath), { recursive: true });
791
- await ensureWritableParentInsideWorkspace(workspaceRoot, destinationPath);
792
- await fs.copyFile(file.path, destinationPath);
793
- let extracted;
794
- if (params.extractArchives && archiveKind(destinationPath)) {
795
- extracted = await extractWorkspaceArchive({
796
- workspacePath: workspaceRoot,
797
- archivePath: destinationPath,
798
- outputPath: resolveArchiveUploadOutputPath(resolvedTargetPath, fileName)
799
- });
800
- }
801
- files.push({
802
- fileName,
803
- targetPath: toDisplayPath(destinationPath),
804
- size: file.size,
805
- extracted
806
- });
807
- }
808
- return {
809
- ok: true,
810
- workspacePath: toDisplayPath(workspaceRoot),
811
- targetPath: toDisplayPath(resolvedTargetPath),
812
- files
813
- };
814
- }
815
- /**
816
- * 解析工作区下载目标;目录由路由层打包为 zip,文件直接流式下载。
817
- */
818
- export async function resolveWorkspaceDownloadTarget(params) {
819
- const { workspaceRoot, resolvedTargetPath } = await resolveEntryTarget(params);
820
- const targetStat = await fs.stat(resolvedTargetPath);
821
- const baseName = path.basename(resolvedTargetPath);
822
- return {
823
- workspacePath: toDisplayPath(workspaceRoot),
824
- targetPath: toDisplayPath(resolvedTargetPath),
825
- resolvedTargetPath,
826
- fileName: targetStat.isDirectory() ? `${baseName || 'workspace'}.zip` : baseName,
827
- contentType: targetStat.isDirectory() ? 'application/zip' : 'application/octet-stream',
828
- isDirectory: targetStat.isDirectory()
829
- };
830
- }
831
- /**
832
- * 将目录内容以 zip 格式写入输出流,避免在磁盘生成临时包。
833
- */
834
- export async function streamWorkspaceDirectoryZip(directoryPath, output) {
835
- const archive = new ZipArchive({ zlib: { level: 9 } });
836
- const completion = new Promise((resolve, reject) => {
837
- output.on('close', resolve);
838
- output.on('finish', resolve);
839
- archive.on('error', reject);
840
- output.on('error', reject);
841
- });
842
- archive.pipe(output);
843
- archive.directory(directoryPath, false);
844
- await archive.finalize();
845
- await completion;
846
- }
847
- async function extractZipArchive(archivePath, outputRoot, meter) {
848
- let extractedEntries = 0;
849
- let extractionError;
850
- const sourceStream = createReadStream(archivePath);
851
- const parsedStream = sourceStream.pipe(unzipper.Parse({ forceStream: true }));
852
- try {
853
- for await (const entry of parsedStream) {
854
- if (extractionError) {
855
- await drainZipEntry(entry);
856
- continue;
857
- }
858
- const entryPath = decodeZipEntryPath(entry);
859
- try {
860
- const destinationPath = ensureArchiveEntryInside(outputRoot, entryPath);
861
- trackArchiveEntry(meter);
862
- if (entry.type === 'Directory') {
863
- await fs.mkdir(destinationPath, { recursive: true });
864
- continue;
865
- }
866
- if (entry.type !== 'File') {
867
- throw new Error(`Unsupported archive entry type: ${entryPath}`);
868
- }
869
- await fs.mkdir(path.dirname(destinationPath), { recursive: true });
870
- await ensureWritableParentInsideWorkspace(outputRoot, destinationPath);
871
- await pipelineWithArchiveLimits(entry, destinationPath, meter);
872
- extractedEntries += 1;
873
- }
874
- catch (error) {
875
- extractionError = error;
876
- await drainZipEntry(entry);
877
- }
878
- }
879
- }
880
- finally {
881
- sourceStream.destroy();
882
- parsedStream.destroy();
883
- }
884
- if (extractionError) {
885
- throw extractionError;
886
- }
887
- return extractedEntries;
888
- }
889
- async function extractTarArchive(archivePath, outputRoot, gzip, meter) {
890
- let extractedEntries = 0;
891
- let extractionError;
892
- /**
893
- * 校验 tar 条目并通过 filter 返回值控制是否解压。
894
- * tar 的 filter 回调位于流解析链路中,直接抛错会变成未处理异常;这里先记录错误,待流结束后统一 reject。
895
- */
896
- const shouldExtractTarEntry = (entryPath, entry) => {
897
- if (extractionError) {
898
- return false;
899
- }
900
- try {
901
- ensureArchiveEntryInside(outputRoot, entryPath);
902
- trackArchiveEntry(meter);
903
- const entryType = String(entry.type || '').toLowerCase();
904
- if (entryType === 'symboliclink' || entryType === 'link') {
905
- throw new Error(`Archive links are not supported: ${entryPath}`);
906
- }
907
- if (entryType !== 'file' && entryType !== 'oldfile' && entryType !== 'contiguousfile' && entryType !== 'directory') {
908
- throw new Error(`Unsupported archive entry type: ${entryPath}`);
909
- }
910
- if (typeof entry.size === 'number') {
911
- trackArchiveBytes(meter, entry.size);
912
- }
913
- if (entryType !== 'directory') {
914
- extractedEntries += 1;
915
- }
916
- return true;
917
- }
918
- catch (error) {
919
- extractionError = error;
920
- return false;
921
- }
922
- };
923
- await tar.x({
924
- file: archivePath,
925
- cwd: outputRoot,
926
- gzip,
927
- strict: true,
928
- filter: (entryPath, entry) => {
929
- return shouldExtractTarEntry(entryPath, entry);
930
- }
931
- });
932
- if (extractionError) {
933
- throw extractionError;
934
- }
935
- return extractedEntries;
936
- }
937
- async function extractGzipFile(archivePath, outputRoot, meter) {
938
- const outputName = stripArchiveExtension(path.basename(archivePath));
939
- const destinationPath = ensureArchiveEntryInside(outputRoot, outputName || 'decompressed');
940
- trackArchiveEntry(meter);
941
- await fs.mkdir(path.dirname(destinationPath), { recursive: true });
942
- await ensureWritableParentInsideWorkspace(outputRoot, destinationPath);
943
- await pipelineWithArchiveLimits(createReadStream(archivePath).pipe(createGunzip()), destinationPath, meter);
944
- return 1;
945
- }
946
- /**
947
- * 解压工作区中的归档文件到指定目录,支持 zip、tar、tar.gz/tgz 与单文件 gz。
948
- */
949
- export async function extractWorkspaceArchive(params) {
950
- const { requestedRoot, workspaceRoot } = await resolveWorkspaceRoots(params.workspacePath);
951
- const { resolvedTargetPath: outputRoot } = await resolveWritableDirectory({
952
- workspacePath: params.workspacePath,
953
- targetPath: params.outputPath || ''
954
- });
955
- const lexicalArchivePath = resolveRequestedPathInsideWorkspace(requestedRoot, workspaceRoot, String(params.archivePath || '').trim());
956
- const archivePath = await ensureRealPathInsideWorkspace(workspaceRoot, lexicalArchivePath);
957
- const archiveStat = await fs.stat(archivePath);
958
- if (!archiveStat.isFile()) {
959
- throw new Error(`Archive path is not a file: ${archivePath}`);
960
- }
961
- if (!(await pathExists(outputRoot))) {
962
- await fs.mkdir(outputRoot, { recursive: true });
963
- }
964
- const outputStat = await fs.stat(outputRoot);
965
- if (!outputStat.isDirectory()) {
966
- throw new Error(`Output path is not a directory: ${outputRoot}`);
967
- }
968
- const kind = archiveKind(archivePath);
969
- if (!kind) {
970
- throw new Error('Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz');
971
- }
972
- let extractedEntries = 0;
973
- const meter = { entries: 0, totalBytes: 0 };
974
- if (kind === 'zip') {
975
- extractedEntries = await extractZipArchive(archivePath, outputRoot, meter);
976
- }
977
- else if (kind === 'tar' || kind === 'tgz') {
978
- extractedEntries = await extractTarArchive(archivePath, outputRoot, kind === 'tgz', meter);
979
- }
980
- else {
981
- extractedEntries = await extractGzipFile(archivePath, outputRoot, meter);
982
- }
983
- return {
984
- ok: true,
985
- workspacePath: toDisplayPath(workspaceRoot),
986
- archivePath: toDisplayPath(archivePath),
987
- targetPath: toDisplayPath(outputRoot),
988
- extractedEntries
989
- };
990
- }
991
- /**
992
- * 展开简单花括号模式,如 *.{ts,tsx} -> *.ts, *.tsx。
993
- */
994
- function expandBracePattern(pattern) {
995
- const match = pattern.match(/^(.*?)\{([^}]+)\}(.*)$/);
996
- if (!match) {
997
- return [pattern];
998
- }
999
- const [, prefix, options, suffix] = match;
1000
- return options.split(',').flatMap((option) => expandBracePattern(prefix + option.trim() + suffix));
1001
- }
1002
- /**
1003
- * 判断文件名是否匹配简单 glob 掩码(支持 *、?、** 与 {a,b,c})。
1004
- */
1005
- function matchesFileMask(fileName, mask) {
1006
- const normalizedMask = String(mask || '').trim();
1007
- if (!normalizedMask || normalizedMask === '*' || normalizedMask === '*.*') {
1008
- return true;
1009
- }
1010
- const normalizedFileName = fileName.toLowerCase();
1011
- const patterns = expandBracePattern(normalizedMask.toLowerCase());
1012
- return patterns.some((pattern) => {
1013
- if (pattern === '*')
1014
- return true;
1015
- const regexSource = pattern
1016
- .replace(/\./g, '\\.')
1017
- .replace(/\*\*/g, '<<GLOBSTAR>>')
1018
- .replace(/\*/g, '[^/]*')
1019
- .replace(/\?/g, '[^/]')
1020
- .replace(/<<GLOBSTAR>>/g, '.*');
1021
- const regex = new RegExp(`^${regexSource}$`);
1022
- return regex.test(normalizedFileName);
1023
- });
1024
- }
1025
- /**
1026
- * 跳过明显不适合文本搜索/替换的目录。
1027
- */
1028
- function isSkippedSearchDirectory(name) {
1029
- return name === '.git' || name === 'node_modules' || name === '.svn' || name === '.hg';
1030
- }
1031
- const SEARCH_MAX_FILE_BYTES = 10 * 1024 * 1024;
1032
- const SEARCH_DEFAULT_MAX_RESULTS = 1000;
1033
- /**
1034
- * 在工作区范围内搜索文件内容。
1035
- * 主流程:安全解析工作区根 -> 递归遍历目录 -> 按文件掩码过滤 -> 逐行匹配关键词 -> 返回匹配列表。
1036
- */
1037
- export async function searchWorkspaceFiles(params) {
1038
- const { workspaceRoot } = await resolveWorkspaceRoots(params.workspacePath);
1039
- const keyword = String(params.keyword || '').trim();
1040
- if (!keyword) {
1041
- throw new Error('keyword is required');
1042
- }
1043
- const ignoreCase = Boolean(params.ignoreCase);
1044
- const fileMask = String(params.fileMask || '').trim();
1045
- const maxResults = typeof params.maxResults === 'number' && params.maxResults > 0
1046
- ? params.maxResults
1047
- : SEARCH_DEFAULT_MAX_RESULTS;
1048
- const searchKeyword = ignoreCase ? keyword.toLowerCase() : keyword;
1049
- const mode = params.mode === 'filename' ? 'filename' : 'content';
1050
- const isFilenameMode = mode === 'filename';
1051
- const matches = [];
1052
- let totalFiles = 0;
1053
- let truncated = false;
1054
- async function walk(directoryPath) {
1055
- if (truncated)
1056
- return;
1057
- let entries;
1058
- try {
1059
- entries = await fs.readdir(directoryPath, { withFileTypes: true });
1060
- }
1061
- catch {
1062
- return;
1063
- }
1064
- for (const entry of entries) {
1065
- if (truncated)
1066
- return;
1067
- const entryPath = ensureInsideWorkspace(workspaceRoot, path.join(directoryPath, entry.name));
1068
- if (entry.isSymbolicLink())
1069
- continue;
1070
- if (entry.isDirectory()) {
1071
- if (isSkippedSearchDirectory(entry.name))
1072
- continue;
1073
- await walk(entryPath);
1074
- continue;
1075
- }
1076
- if (!entry.isFile())
1077
- continue;
1078
- if (fileMask && !matchesFileMask(entry.name, fileMask))
1079
- continue;
1080
- totalFiles += 1;
1081
- let stat;
1082
- try {
1083
- stat = await fs.stat(entryPath);
1084
- }
1085
- catch {
1086
- continue;
1087
- }
1088
- if (stat.size > SEARCH_MAX_FILE_BYTES)
1089
- continue;
1090
- if (isFilenameMode) {
1091
- const searchName = ignoreCase ? entry.name.toLowerCase() : entry.name;
1092
- const matchIndex = searchName.indexOf(searchKeyword);
1093
- if (matchIndex !== -1) {
1094
- matches.push({
1095
- filePath: toDisplayPath(entryPath),
1096
- lineNumber: 0,
1097
- lineContent: entry.name,
1098
- matchStart: matchIndex,
1099
- matchEnd: matchIndex + keyword.length
1100
- });
1101
- if (matches.length >= maxResults) {
1102
- truncated = true;
1103
- return;
1104
- }
1105
- }
1106
- continue;
1107
- }
1108
- let content;
1109
- try {
1110
- content = await fs.readFile(entryPath, 'utf-8');
1111
- }
1112
- catch {
1113
- continue;
1114
- }
1115
- const lines = content.split(/\r?\n/);
1116
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
1117
- const line = lines[lineIndex];
1118
- const searchLine = ignoreCase ? line.toLowerCase() : line;
1119
- let searchIndex = 0;
1120
- while (true) {
1121
- const matchIndex = searchLine.indexOf(searchKeyword, searchIndex);
1122
- if (matchIndex === -1)
1123
- break;
1124
- matches.push({
1125
- filePath: toDisplayPath(entryPath),
1126
- lineNumber: lineIndex + 1,
1127
- lineContent: line,
1128
- matchStart: matchIndex,
1129
- matchEnd: matchIndex + keyword.length
1130
- });
1131
- if (matches.length >= maxResults) {
1132
- truncated = true;
1133
- return;
1134
- }
1135
- searchIndex = matchIndex + 1;
1136
- }
1137
- }
1138
- }
1139
- }
1140
- await walk(workspaceRoot);
1141
- return {
1142
- workspacePath: toDisplayPath(workspaceRoot),
1143
- keyword,
1144
- totalFiles,
1145
- totalMatches: matches.length,
1146
- matches,
1147
- truncated
1148
- };
1149
- }
1150
- /**
1151
- * 在单个文件内执行安全替换,只写入确实有变化的内容。
1152
- */
1153
- async function replaceInFile(workspaceRoot, filePath, keyword, replacement, ignoreCase) {
1154
- let content;
1155
- try {
1156
- content = await fs.readFile(filePath, 'utf-8');
1157
- }
1158
- catch {
1159
- return { changed: false, count: 0 };
1160
- }
1161
- const originalContent = content;
1162
- let count = 0;
1163
- if (ignoreCase) {
1164
- const lowerKeyword = keyword.toLowerCase();
1165
- let index = 0;
1166
- while (true) {
1167
- const lowerContent = content.toLowerCase();
1168
- const matchIndex = lowerContent.indexOf(lowerKeyword, index);
1169
- if (matchIndex === -1)
1170
- break;
1171
- content = content.slice(0, matchIndex) + replacement + content.slice(matchIndex + keyword.length);
1172
- count += 1;
1173
- const step = replacement.length || 1;
1174
- index = matchIndex + step;
1175
- }
1176
- }
1177
- else {
1178
- let index = 0;
1179
- while (true) {
1180
- const matchIndex = content.indexOf(keyword, index);
1181
- if (matchIndex === -1)
1182
- break;
1183
- content = content.slice(0, matchIndex) + replacement + content.slice(matchIndex + keyword.length);
1184
- count += 1;
1185
- const step = replacement.length || 1;
1186
- index = matchIndex + step;
1187
- }
1188
- }
1189
- if (content === originalContent) {
1190
- return { changed: false, count: 0 };
1191
- }
1192
- await ensureWritableParentInsideWorkspace(workspaceRoot, filePath);
1193
- await fs.writeFile(filePath, content, 'utf-8');
1194
- return { changed: true, count };
1195
- }
1196
- /**
1197
- * 在工作区范围内批量替换文件内容。
1198
- * 可指定 filePath 只替换单个文件;未指定时递归替换所有匹配掩码的文件。
1199
- */
1200
- export async function replaceWorkspaceFiles(params) {
1201
- const { workspaceRoot } = await resolveWorkspaceRoots(params.workspacePath);
1202
- const keyword = String(params.keyword || '').trim();
1203
- if (!keyword) {
1204
- throw new Error('keyword is required');
1205
- }
1206
- const replacement = String(params.replacement ?? '');
1207
- const ignoreCase = Boolean(params.ignoreCase);
1208
- const fileMask = String(params.fileMask || '').trim();
1209
- const singleFilePath = String(params.filePath || '').trim();
1210
- let changedFiles = 0;
1211
- let changedMatches = 0;
1212
- if (singleFilePath) {
1213
- const { resolvedFilePath } = await resolveFileTarget({
1214
- workspacePath: params.workspacePath,
1215
- filePath: singleFilePath
1216
- });
1217
- const result = await replaceInFile(workspaceRoot, resolvedFilePath, keyword, replacement, ignoreCase);
1218
- if (result.changed) {
1219
- changedFiles = 1;
1220
- changedMatches = result.count;
1221
- }
1222
- return {
1223
- workspacePath: toDisplayPath(workspaceRoot),
1224
- keyword,
1225
- replacement,
1226
- changedFiles,
1227
- changedMatches
1228
- };
1229
- }
1230
- async function walk(directoryPath) {
1231
- let entries;
1232
- try {
1233
- entries = await fs.readdir(directoryPath, { withFileTypes: true });
1234
- }
1235
- catch {
1236
- return;
1237
- }
1238
- for (const entry of entries) {
1239
- const entryPath = ensureInsideWorkspace(workspaceRoot, path.join(directoryPath, entry.name));
1240
- if (entry.isSymbolicLink())
1241
- continue;
1242
- if (entry.isDirectory()) {
1243
- if (isSkippedSearchDirectory(entry.name))
1244
- continue;
1245
- await walk(entryPath);
1246
- continue;
1247
- }
1248
- if (!entry.isFile())
1249
- continue;
1250
- if (fileMask && !matchesFileMask(entry.name, fileMask))
1251
- continue;
1252
- try {
1253
- const stat = await fs.stat(entryPath);
1254
- if (stat.size > SEARCH_MAX_FILE_BYTES)
1255
- continue;
1256
- }
1257
- catch {
1258
- continue;
1259
- }
1260
- const result = await replaceInFile(workspaceRoot, entryPath, keyword, replacement, ignoreCase);
1261
- if (result.changed) {
1262
- changedFiles += 1;
1263
- changedMatches += result.count;
1264
- }
1265
- }
1266
- }
1267
- await walk(workspaceRoot);
1268
- return {
1269
- workspacePath: toDisplayPath(workspaceRoot),
1270
- keyword,
1271
- replacement,
1272
- changedFiles,
1273
- changedMatches
1274
- };
1275
- }
1
+ import{createReadStream as U,createWriteStream as ot,promises as o}from"node:fs";import d from"node:path";import{Readable as st}from"node:stream";import{pipeline as ct}from"node:stream/promises";import{TextDecoder as lt}from"node:util";import{createGunzip as ht}from"node:zlib";import*as ut from"tar";import dt from"unzipper";const{ZipArchive:wt}=await import("archiver"),y={maxEntries:1e4,maxFileBytes:512*1024*1024,maxTotalBytes:2*1024*1024*1024,maxPathDepth:64,maxPathLength:4096},ft=new lt("gbk");function h(t){return String(t||"").replace(/\\/g,"/")}function j(t){const e=d.resolve(String(t||"").trim());if(!e)throw new Error("workspacePath is required");return e}async function pt(t){const e=j(t),r=await o.realpath(e);if(!(await o.stat(r)).isDirectory())throw new Error(`Workspace path is not a directory: ${e}`);return r}async function k(t){return{requestedRoot:j(t),workspaceRoot:await pt(t)}}function p(t,e){const r=d.resolve(e),i=d.relative(t,r);if(i.startsWith("..")||d.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function x(t,e){const r=await o.realpath(e);return p(t,r)}async function v(t,e){const r=await o.realpath(d.dirname(e));p(t,r);try{if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${e}`)}catch(i){if((typeof i=="object"&&i!==null?Reflect.get(i,"code"):void 0)!=="ENOENT")throw i}}function z(t,e,r){const i=d.resolve(r),a=d.relative(t,i);if(a.startsWith("..")||d.isAbsolute(a))throw new Error(`Target path is outside workspace: ${i}`);return p(e,d.join(e,a))}async function $({workspacePath:t,targetPath:e=""}){const{requestedRoot:r,workspaceRoot:i}=await k(t),a=String(e||"").trim(),n=a?z(r,i,a):i,s=await x(i,n);return{workspaceRoot:i,resolvedTargetPath:s}}function D(t){const e=String(t||"").trim();if(!e)throw new Error("entryName is required");if(e==="."||e==="..")throw new Error("entryName is invalid");if(e.includes("/")||e.includes("\\")||d.basename(e)!==e)throw new Error(`entryName must be a plain file or directory name: ${e}`);return e}function q(t){const e=String(t||"");if(!/[\u00c0-\u00ff]/.test(e))return e;const r=Buffer.from(e,"latin1").toString("utf8");return!r||r.includes("\uFFFD")?e:r}function B(t){const e=q(t);return D(d.basename(e.replace(/\\/g,"/")))}function mt(t,e){if(!t)return B(e);const i=q(String(t||e||"")).replace(/\\/g,"/").split("/").filter(Boolean).map(a=>D(a));return i.length===0?B(e):d.join(...i)}function _(t){return(t&4095).toString(8).padStart(4,"0")}function gt(t){const e=String(t||"").trim();if(!/^[0-7]{3,4}$/.test(e))throw new Error("mode must be a 3 or 4 digit octal string");return Number.parseInt(e,8)}function C(t,e,r,i,a,n,s){const c=t&e?"r":"-",u=t&r?"w":"-",l=(t&i)!==0,w=(t&a)!==0?l?n:s:l?"x":"-";return`${c}${u}${w}`}function yt(t){return[C(t,256,128,64,2048,"s","S"),C(t,32,16,8,1024,"s","S"),C(t,4,2,1,512,"t","T")].join("")}function Pt(t){if(!(t.length<24||t.toString("hex",0,8)!=="89504e470d0a1a0a"))return{format:"png",width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function kt(t){if(!(t.length<10||t.toString("ascii",0,6)!=="GIF87a"&&t.toString("ascii",0,6)!=="GIF89a"))return{format:"gif",width:t.readUInt16LE(6),height:t.readUInt16LE(8)}}function xt(t){if(t.length<4||t[0]!==255||t[1]!==216)return;let e=2;for(;e+9<t.length;){if(t[e]!==255){e+=1;continue}const r=t[e+1],i=t.readUInt16BE(e+2);if(i<2)return;if(r>=192&&r<=207&&![196,200,204].includes(r)&&e+8<t.length)return{format:"jpeg",height:t.readUInt16BE(e+5),width:t.readUInt16BE(e+7)};e+=2+i}}function Et(t){if(t.length<30||t.toString("ascii",0,4)!=="RIFF"||t.toString("ascii",8,12)!=="WEBP")return;const e=t.toString("ascii",12,16);if(e==="VP8X"&&t.length>=30)return{format:"webp",width:1+t.readUIntLE(24,3),height:1+t.readUIntLE(27,3)};if(e==="VP8 "&&t.length>=30)return{format:"webp",width:t.readUInt16LE(26)&16383,height:t.readUInt16LE(28)&16383};if(e==="VP8L"&&t.length>=25){const r=t.readUInt32LE(21);return{format:"webp",width:(r&16383)+1,height:(r>>14&16383)+1}}}async function G(t,e,r){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);const a=await o.stat(e);if(!r||!a.isDirectory())return[e];const n=await o.readdir(e),s=await Promise.all(n.map(c=>{const u=p(t,d.join(e,c));return G(t,u,!0)}));return[e,...s.flat()]}async function vt(t,e){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);await x(t,e)}async function St(t){const e=await o.open(t,"r");try{const r=Buffer.alloc(512),{bytesRead:i}=await e.read(r,0,r.length,0),a=r.subarray(0,i);return Pt(a)||kt(a)||xt(a)||Et(a)}finally{await e.close()}}async function S({workspacePath:t,targetPath:e},r={}){const{requestedRoot:i,workspaceRoot:a}=await k(t),n=String(e||"").trim();if(!n)throw new Error("targetPath is required");const s=z(i,a,n),c=await x(a,s);if(!r.allowWorkspaceRoot&&c===a)throw new Error("Workspace root cannot be modified");return{workspaceRoot:a,resolvedTargetPath:c}}async function Z(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),i=String(t.targetPath||"").trim();if(!i)return{workspaceRoot:r,resolvedTargetPath:r};const a=z(e,r,i);if(await J(a))return{workspaceRoot:r,resolvedTargetPath:await x(r,a)};const n=await o.realpath(d.dirname(a));return p(r,n),{workspaceRoot:r,resolvedTargetPath:a}}function H(t){const e=t.toLowerCase();return e.endsWith(".zip")?"zip":e.endsWith(".tar.gz")||e.endsWith(".tgz")?"tgz":e.endsWith(".tar")?"tar":e.endsWith(".gz")?"gz":null}function K(t){return t.replace(/\.tar\.gz$/i,"").replace(/\.tgz$/i,"").replace(/\.zip$/i,"").replace(/\.tar$/i,"").replace(/\.gz$/i,"")}function Ft(t,e){const r=D(K(e)||"extracted");return d.join(t,r)}function I(t,e){const r=String(e||"").replace(/\\/g,"/"),i=r.split("/").filter(Boolean);if(!r||r.startsWith("/")||/^[A-Za-z]:/.test(r))throw new Error(`Archive entry path is invalid: ${e}`);if(r.length>y.maxPathLength||i.length>y.maxPathDepth)throw new Error(`Archive entry path is too deep or too long: ${e}`);return p(t,d.join(t,r))}function Tt(t){const e=t.props?.pathBuffer;if(!e||t.props?.flags?.isUnicode)return t.path;const r=ft.decode(e);return!r||r.includes("\uFFFD")?t.path:r}async function V(t){const e=t.autodrain?.();e&&await new Promise((r,i)=>{e.on("close",r),e.on("end",r),e.on("finish",r),e.on("error",i)})}function A(t){if(t.entries+=1,t.entries>y.maxEntries)throw new Error(`Archive contains too many entries. Limit: ${y.maxEntries}`)}function X(t,e){if(t.totalBytes+=e,e>y.maxFileBytes)throw new Error(`Archive entry exceeds max size. Limit: ${y.maxFileBytes} bytes`);if(t.totalBytes>y.maxTotalBytes)throw new Error(`Archive extraction exceeds total size. Limit: ${y.maxTotalBytes} bytes`)}async function Y(t,e,r){let i=0;t.on("data",a=>{const n=Buffer.isBuffer(a)?a.length:Buffer.byteLength(a);i+=n,(i>y.maxFileBytes||r.totalBytes+i>y.maxTotalBytes)&&t instanceof st&&t.destroy(new Error("Archive extraction size limit exceeded"))}),await ct(t,ot(e)),X(r,i)}async function J(t){try{return await o.access(t),!0}catch(e){if((typeof e=="object"&&e!==null?Reflect.get(e,"code"):void 0)==="ENOENT")return!1;throw e}}async function W({workspacePath:t,filePath:e}){const{requestedRoot:r,workspaceRoot:i}=await k(t),a=String(e||"").trim();if(!a)throw new Error("filePath is required");return{workspaceRoot:i,resolvedFilePath:z(r,i,a)}}async function Ot(t){const{workspaceRoot:e,resolvedTargetPath:r}=await $(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=await o.readdir(r,{withFileTypes:!0}),n=await Promise.all(a.map(async s=>{const c=d.join(r,s.name),u=s.isFile()?(await o.stat(c)).size:void 0;return{name:s.name,path:h(c),isDirectory:s.isDirectory(),size:u}}));return{currentPath:h(r),workspacePath:h(e),items:n}}async function Ut(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r);if(!(await o.stat(i)).isFile())throw new Error(`Path is not a file: ${i}`);const n=await o.readFile(i,"utf-8");return{workspacePath:h(e),filePath:h(i),content:n}}const Q={maxEntries:1e5,maxDepth:32};async function Rt(t,e){let r=0,i=0;async function a(n,s){if(s>=Q.maxDepth)return;let c;try{c=await o.readdir(n,{withFileTypes:!0})}catch{return}for(const u of c){if(i+=1,i>Q.maxEntries)return;if(u.isSymbolicLink())continue;const l=p(t,d.join(n,u.name));try{if(u.isDirectory())await a(l,s+1);else if(u.isFile()){const f=await o.stat(l);r+=f.size}}catch{}}}return await a(e,0),r}async function jt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t,{allowWorkspaceRoot:!0}),[i,a]=await Promise.all([o.stat(r),o.lstat(r)]),n=i.isDirectory(),s=i.isFile()?await St(r):void 0,c=n?await Rt(e,r):i.size;return{workspacePath:h(e),targetPath:h(r),name:d.basename(r),isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:a.isSymbolicLink(),size:c,mode:i.mode,modeOctal:_(i.mode),permissions:yt(i.mode),uid:i.uid,gid:i.gid,atimeMs:i.atimeMs,mtimeMs:i.mtimeMs,ctimeMs:i.ctimeMs,birthtimeMs:i.birthtimeMs,...s?{image:s}:{}}}async function qt(t){const e=gt(t.mode),r=!!t.recursive,{workspaceRoot:i,resolvedTargetPath:a}=await S(t),n=await G(i,a,r);for(const s of n)await vt(i,s),await o.chmod(s,e);return{ok:!0,workspacePath:h(i),targetPath:h(a),mode:e,modeOctal:_(e),recursive:r,changedCount:n.length}}async function _t(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r),a=await o.stat(i);if(!a.isFile())throw new Error(`Path is not a file: ${i}`);const n=d.extname(i).toLowerCase();if(n===".doc")return{workspacePath:h(e),filePath:h(i),kind:"unsupported",content:"",message:"\u6682\u4E0D\u652F\u6301\u65E7\u7248 .doc \u683C\u5F0F\u9884\u89C8\uFF0C\u8BF7\u53E6\u5B58\u4E3A .docx \u540E\u518D\u6253\u5F00\u3002",size:a.size,mtimeMs:a.mtimeMs};if(n!==".docx")throw new Error("Document preview only supports .docx files");return{workspacePath:h(e),filePath:h(i),kind:"html",content:"",size:a.size,mtimeMs:a.mtimeMs}}async function Gt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t);return await o.mkdir(d.dirname(r),{recursive:!0}),await v(e,r),await o.writeFile(r,String(t.content??""),"utf-8"),{ok:!0,workspacePath:h(e),filePath:h(r)}}async function Zt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await $(t),i=D(t.entryName),a=p(e,d.join(r,i));if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)!=="ENOENT")throw s}return t.isDirectory?await o.mkdir(a,{recursive:!1}):(await o.mkdir(d.dirname(a),{recursive:!0}),await v(e,a),await o.writeFile(a,"","utf-8")),{ok:!0,workspacePath:h(e),targetPath:h(a),isDirectory:t.isDirectory}}async function Ht(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=D(t.newName),a=p(e,d.join(d.dirname(r),i));if(a===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)};await o.stat(r);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(n){if((typeof n=="object"&&n!==null?Reflect.get(n,"code"):void 0)!=="ENOENT")throw n}return await o.rename(r,a),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)}}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await $({workspacePath:t.workspacePath,targetPath:t.destinationPath});if(!(await o.stat(i.resolvedTargetPath)).isDirectory())throw new Error(`Destination is not a directory: ${i.resolvedTargetPath}`);if((await o.stat(r)).isDirectory()){const c=d.relative(r,i.resolvedTargetPath);if(c===""||!c.startsWith("..")&&!d.isAbsolute(c))throw new Error("Cannot move a directory into itself or its descendant")}const s=p(e,d.join(i.resolvedTargetPath,d.basename(r)));if(s===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)};try{throw await o.access(s),new Error(`Path already exists: ${s}`)}catch(c){if((typeof c=="object"&&c!==null?Reflect.get(c,"code"):void 0)!=="ENOENT")throw c}return await o.rename(r,s),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)}}async function Vt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t);return(await o.stat(r)).isDirectory()?await o.rm(r,{recursive:!0,force:!1}):await o.unlink(r),{ok:!0,workspacePath:h(e),targetPath:h(r)}}async function Xt(t){if(!Array.isArray(t.files)||t.files.length===0)throw new Error("files are required");const{workspaceRoot:e,resolvedTargetPath:r}=await Z(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=[];for(const n of t.files){const s=B(n.originalname),c=mt(n.relativePath,n.originalname),u=p(e,d.join(r,c));await o.mkdir(d.dirname(u),{recursive:!0}),await v(e,u),await o.copyFile(n.path,u);let l;t.extractArchives&&H(u)&&(l=await Wt({workspacePath:e,archivePath:u,outputPath:Ft(r,s)})),a.push({fileName:s,targetPath:h(u),size:n.size,extracted:l})}return{ok:!0,workspacePath:h(e),targetPath:h(r),files:a}}async function Yt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await o.stat(r),a=d.basename(r);return{workspacePath:h(e),targetPath:h(r),resolvedTargetPath:r,fileName:i.isDirectory()?`${a||"workspace"}.zip`:a,contentType:i.isDirectory()?"application/zip":"application/octet-stream",isDirectory:i.isDirectory()}}async function Jt(t,e){const r=new wt({zlib:{level:9}}),i=new Promise((a,n)=>{e.on("close",a),e.on("finish",a),r.on("error",n),e.on("error",n)});r.pipe(e),r.directory(t,!1),await r.finalize(),await i}async function zt(t,e,r){let i=0,a;const n=U(t),s=n.pipe(dt.Parse({forceStream:!0}));try{for await(const c of s){if(a){await V(c);continue}const u=Tt(c);try{const l=I(e,u);if(A(r),c.type==="Directory"){await o.mkdir(l,{recursive:!0});continue}if(c.type!=="File")throw new Error(`Unsupported archive entry type: ${u}`);await o.mkdir(d.dirname(l),{recursive:!0}),await v(e,l),await Y(c,l,r),i+=1}catch(l){a=l,await V(c)}}}finally{n.destroy(),s.destroy()}if(a)throw a;return i}async function Dt(t,e,r,i){let a=0,n;const s=(c,u)=>{if(n)return!1;try{I(e,c),A(i);const l=String(u.type||"").toLowerCase();if(l==="symboliclink"||l==="link")throw new Error(`Archive links are not supported: ${c}`);if(l!=="file"&&l!=="oldfile"&&l!=="contiguousfile"&&l!=="directory")throw new Error(`Unsupported archive entry type: ${c}`);return typeof u.size=="number"&&X(i,u.size),l!=="directory"&&(a+=1),!0}catch(l){return n=l,!1}};if(await ut.x({file:t,cwd:e,gzip:r,strict:!0,filter:(c,u)=>s(c,u)}),n)throw n;return a}async function Lt(t,e,r){const i=K(d.basename(t)),a=I(e,i||"decompressed");return A(r),await o.mkdir(d.dirname(a),{recursive:!0}),await v(e,a),await Y(U(t).pipe(ht()),a,r),1}async function Wt(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),{resolvedTargetPath:i}=await Z({workspacePath:t.workspacePath,targetPath:t.outputPath||""}),a=z(e,r,String(t.archivePath||"").trim()),n=await x(r,a);if(!(await o.stat(n)).isFile())throw new Error(`Archive path is not a file: ${n}`);if(await J(i)||await o.mkdir(i,{recursive:!0}),!(await o.stat(i)).isDirectory())throw new Error(`Output path is not a directory: ${i}`);const u=H(n);if(!u)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let l=0;const f={entries:0,totalBytes:0};return u==="zip"?l=await zt(n,i,f):u==="tar"||u==="tgz"?l=await Dt(n,i,u==="tgz",f):l=await Lt(n,i,f),{ok:!0,workspacePath:h(r),archivePath:h(n),targetPath:h(i),extractedEntries:l}}function tt(t){const e=t.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!e)return[t];const[,r,i,a]=e;return i.split(",").flatMap(n=>tt(r+n.trim()+a))}function et(t,e){const r=String(e||"").trim();if(!r||r==="*"||r==="*.*")return!0;const i=t.toLowerCase();return tt(r.toLowerCase()).some(n=>{if(n==="*")return!0;const s=n.replace(/\./g,"\\.").replace(/\*\*/g,"<<GLOBSTAR>>").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]").replace(/<<GLOBSTAR>>/g,".*");return new RegExp(`^${s}$`).test(i)})}function rt(t){return t===".git"||t==="node_modules"||t===".svn"||t===".hg"}const it=10*1024*1024,$t=1e3;async function Qt(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=!!t.ignoreCase,a=String(t.fileMask||"").trim(),n=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:$t,s=i?r.toLowerCase():r,u=(t.mode==="filename"?"filename":"content")==="filename",l=[];let f=0,w=!1;async function m(E){if(w)return;let F;try{F=await o.readdir(E,{withFileTypes:!0})}catch{return}for(const g of F){if(w)return;const T=p(e,d.join(E,g.name));if(g.isSymbolicLink())continue;if(g.isDirectory()){if(rt(g.name))continue;await m(T);continue}if(!g.isFile()||a&&!et(g.name,a))continue;f+=1;let N;try{N=await o.stat(T)}catch{continue}if(N.size>it)continue;if(u){const P=(i?g.name.toLowerCase():g.name).indexOf(s);if(P!==-1&&(l.push({filePath:h(T),lineNumber:0,lineContent:g.name,matchStart:P,matchEnd:P+r.length}),l.length>=n)){w=!0;return}continue}let M;try{M=await o.readFile(T,"utf-8")}catch{continue}const b=M.split(/\r?\n/);for(let R=0;R<b.length;R+=1){const P=b[R],nt=i?P.toLowerCase():P;let O=0;for(;;){const L=nt.indexOf(s,O);if(L===-1)break;if(l.push({filePath:h(T),lineNumber:R+1,lineContent:P,matchStart:L,matchEnd:L+r.length}),l.length>=n){w=!0;return}O=L+1}}}}return await m(e),{workspacePath:h(e),keyword:r,totalFiles:f,totalMatches:l.length,matches:l,truncated:w}}async function at(t,e,r,i,a){let n;try{n=await o.readFile(e,"utf-8")}catch{return{changed:!1,count:0}}const s=n;let c=0;if(a){const u=r.toLowerCase();let l=0;for(;;){const w=n.toLowerCase().indexOf(u,l);if(w===-1)break;n=n.slice(0,w)+i+n.slice(w+r.length),c+=1;const m=i.length||1;l=w+m}}else{let u=0;for(;;){const l=n.indexOf(r,u);if(l===-1)break;n=n.slice(0,l)+i+n.slice(l+r.length),c+=1;const f=i.length||1;u=l+f}}return n===s?{changed:!1,count:0}:(await v(t,e),await o.writeFile(e,n,"utf-8"),{changed:!0,count:c})}async function te(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=String(t.replacement??""),a=!!t.ignoreCase,n=String(t.fileMask||"").trim(),s=String(t.filePath||"").trim();let c=0,u=0;if(s){const{resolvedFilePath:f}=await W({workspacePath:t.workspacePath,filePath:s}),w=await at(e,f,r,i,a);return w.changed&&(c=1,u=w.count),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}async function l(f){let w;try{w=await o.readdir(f,{withFileTypes:!0})}catch{return}for(const m of w){const E=p(e,d.join(f,m.name));if(m.isSymbolicLink())continue;if(m.isDirectory()){if(rt(m.name))continue;await l(E);continue}if(!m.isFile()||n&&!et(m.name,n))continue;try{if((await o.stat(E)).size>it)continue}catch{continue}const F=await at(e,E,r,i,a);F.changed&&(c+=1,u+=F.count)}}return await l(e),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}export{qt as chmodWorkspaceEntry,Zt as createWorkspaceEntry,Vt as deleteWorkspaceEntry,Wt as extractWorkspaceArchive,jt as getWorkspaceEntryProperties,Ot as listWorkspaceDirectory,Kt as moveWorkspaceEntry,_t as previewWorkspaceDocument,Ut as readWorkspaceFile,Ht as renameWorkspaceEntry,te as replaceWorkspaceFiles,Yt as resolveWorkspaceDownloadTarget,Qt as searchWorkspaceFiles,Jt as streamWorkspaceDirectoryZip,Xt as uploadWorkspaceFiles,Gt as writeWorkspaceFile};
2
+