@jingyi0605/codingns 0.9.0 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/dist/public/assets/{AdaptiveButlerPage-B17QiMyT.js → AdaptiveButlerPage-kkJDsnCO.js} +2 -2
  2. package/dist/public/assets/{App-CFBwDUNA.js → App-DrNI9lWA.js} +6 -6
  3. package/dist/public/assets/{BootstrapPage-W5wU3BPh.js → BootstrapPage-QgVH5Mps.js} +1 -1
  4. package/dist/public/assets/{ConversationPage-DQLX1bUh.js → ConversationPage-DVk8VfIj.js} +1 -1
  5. package/dist/public/assets/{DesktopDetachPreviewPage-DTPeuAW-.js → DesktopDetachPreviewPage-BhfP0TpH.js} +1 -1
  6. package/dist/public/assets/{DesktopModal-6ii53_Y9.js → DesktopModal-DRmDrv0S.js} +1 -1
  7. package/dist/public/assets/DesktopWindowPage-DNbJXnSs.js +2 -0
  8. package/dist/public/assets/FileContextPanel---fLO4ve.js +1 -0
  9. package/dist/public/assets/GitSidebar-sXUE0TqT.js +6 -0
  10. package/dist/public/assets/MobileCreateSessionSheet-BftZ5pvb.js +1 -0
  11. package/dist/public/assets/{MobileSheet-opTWyRe1.js → MobileSheet-nw5SCa3N.js} +1 -1
  12. package/dist/public/assets/{MobileTopHeaderFrame-BbNON3Y4.js → MobileTopHeaderFrame-DH_D02Wy.js} +1 -1
  13. package/dist/public/assets/{MobileWorkspaceSwitcherHeader-BZEzPeMj.js → MobileWorkspaceSwitcherHeader-2K406G5p.js} +1 -1
  14. package/dist/public/assets/{PluginAccessOverview-mQDmAljp.js → PluginAccessOverview-BVJihw3D.js} +1 -1
  15. package/dist/public/assets/{PluginContainerPage-CcxUJpM4.js → PluginContainerPage-CR4vStvr.js} +1 -1
  16. package/dist/public/assets/{PluginDetailPage-D5--ACIt.js → PluginDetailPage-CrMX0Mnm.js} +1 -1
  17. package/dist/public/assets/{PluginsListPage-D_oJxYXT.js → PluginsListPage-FtIL71Yg.js} +1 -1
  18. package/dist/public/assets/{RelayConnectEntryPage-DROxpnkv.js → RelayConnectEntryPage-Bt1apX53.js} +1 -1
  19. package/dist/public/assets/{ServerSettingsModal-CUUOPqSe.js → ServerSettingsModal-D-guzPrI.js} +1 -1
  20. package/dist/public/assets/{SessionIndexPage-C2Jxh6Gp.js → SessionIndexPage-CX2FppcJ.js} +1 -1
  21. package/dist/public/assets/SettingsPage-BI2Olcvr.js +2 -0
  22. package/dist/public/assets/TerminalManagerPanel-B5MKGPy-.js +1 -0
  23. package/dist/public/assets/{TerminalPage-CwWyFDj8.js → TerminalPage-C2dTNGHK.js} +1 -1
  24. package/dist/public/assets/{TerminalRuntimeFallbackModal-CSVVbO8r.js → TerminalRuntimeFallbackModal-DAqOxFD8.js} +1 -1
  25. package/dist/public/assets/{ToolFilesPage-QBEY8oCf.js → ToolFilesPage-IsNwyE6T.js} +1 -1
  26. package/dist/public/assets/{ToolGitPage-BKoZ2l9v.js → ToolGitPage-BK1JBERN.js} +1 -1
  27. package/dist/public/assets/{ToolProcessesPage-BOH0ib4G.js → ToolProcessesPage-DwTYUQCK.js} +1 -1
  28. package/dist/public/assets/{ToolsHomePage-BcMZ3BCQ.js → ToolsHomePage-BLOy7lPg.js} +1 -1
  29. package/dist/public/assets/{WorkbenchLandingPage-B5zoppEl.js → WorkbenchLandingPage-CqZKR6EA.js} +1 -1
  30. package/dist/public/assets/WorkbenchLayout-CJHQtwuL.js +1022 -0
  31. package/dist/public/assets/{WorkbenchModal-NGmPgqaE.js → WorkbenchModal-BM-OeW-b.js} +1 -1
  32. package/dist/public/assets/WorkbenchShellRoute-2bKI6Q9k.js +1 -0
  33. package/dist/public/assets/WorkbenchShellRoute-BjuZD101.css +1 -0
  34. package/dist/public/assets/WorkspaceDebugDetailPage-BMsEN5iG.js +1 -0
  35. package/dist/public/assets/WorkspaceDetailPage-5H9Gosx2.js +1 -0
  36. package/dist/public/assets/WorkspaceHomePage-DQiXKgiP.js +1 -0
  37. package/dist/public/assets/{client-runtime-manager-DXbI9K1K.js → client-runtime-manager-CgPJq21V.js} +1 -1
  38. package/dist/public/assets/index-BARqMVSw.css +1 -0
  39. package/dist/public/assets/index-BUoNjVrY.js +50 -0
  40. package/dist/public/assets/{login-direct-candidate-resolver-DkKyFtQJ.js → login-direct-candidate-resolver-CGaxAXV8.js} +1 -1
  41. package/dist/public/assets/{plugin-permission-copy-CzN269Bk.js → plugin-permission-copy-BR9gWy8b.js} +1 -1
  42. package/dist/public/assets/{plugins-api-Bv9DHpLF.js → plugins-api-CdCsrG2e.js} +1 -1
  43. package/dist/public/assets/{preferences-service-D2ISL2Zz.js → preferences-service-lOhnlxzP.js} +1 -1
  44. package/dist/public/assets/{relay-entry-Bg0OisQy.js → relay-entry-CQpxTS8y.js} +1 -1
  45. package/dist/public/assets/{terminal-runtime-meta-C8t-CIDF.js → terminal-runtime-meta-oteTx66X.js} +1 -1
  46. package/dist/public/assets/useRegisteredDebugTemplates-Bu2ykZ6s.js +1 -0
  47. package/dist/public/assets/workbench-navigation-DlgXuFW2.js +1 -0
  48. package/dist/public/index.html +2 -2
  49. package/dist/server/config/env.d.ts +1 -0
  50. package/dist/server/config/env.js +3 -0
  51. package/dist/server/config/env.js.map +1 -1
  52. package/dist/server/modules/affairs-indexer/core/src/parser/parser-skip-repository.js.map +1 -1
  53. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-write-repository.js.map +1 -1
  54. package/dist/server/modules/affairs-indexer/core/src/sqlite/detect-catalog-schema.js +2 -2
  55. package/dist/server/modules/affairs-indexer/core/src/sqlite/detect-catalog-schema.js.map +1 -1
  56. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.d.ts +20 -3
  57. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.js +3 -3
  58. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.js.map +1 -1
  59. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.d.ts +32 -0
  60. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js +93 -0
  61. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js.map +1 -0
  62. package/dist/server/modules/butler/assistant-sandbox-service.d.ts +69 -0
  63. package/dist/server/modules/butler/assistant-sandbox-service.js +399 -0
  64. package/dist/server/modules/butler/assistant-sandbox-service.js.map +1 -0
  65. package/dist/server/modules/butler/butler-follow-up-service.d.ts +3 -0
  66. package/dist/server/modules/butler/butler-follow-up-service.js +11 -1
  67. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  68. package/dist/server/modules/butler/butler-inbox-service.d.ts +3 -0
  69. package/dist/server/modules/butler/butler-inbox-service.js +14 -2
  70. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -1
  71. package/dist/server/modules/butler/butler-session-service.d.ts +3 -0
  72. package/dist/server/modules/butler/butler-session-service.js +18 -0
  73. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  74. package/dist/server/modules/channels/wechat-claw-client.d.ts +51 -0
  75. package/dist/server/modules/channels/wechat-claw-client.js +245 -0
  76. package/dist/server/modules/channels/wechat-claw-client.js.map +1 -0
  77. package/dist/server/modules/file/file-controller.d.ts +11 -2
  78. package/dist/server/modules/file/file-controller.js +404 -17
  79. package/dist/server/modules/file/file-controller.js.map +1 -1
  80. package/dist/server/modules/file/file-search-service.js +200 -12
  81. package/dist/server/modules/file/file-search-service.js.map +1 -1
  82. package/dist/server/modules/file/runtime/codingns-workspace-bridge.js +18 -5
  83. package/dist/server/modules/file/workspace-file-bridge-service.d.ts +9 -0
  84. package/dist/server/modules/file/workspace-file-bridge-service.js +3 -0
  85. package/dist/server/modules/file/workspace-file-bridge-service.js.map +1 -1
  86. package/dist/server/modules/file/workspace-file-bridge-watch-service.d.ts +9 -0
  87. package/dist/server/modules/file/workspace-file-bridge-watch-service.js +28 -0
  88. package/dist/server/modules/file/workspace-file-bridge-watch-service.js.map +1 -1
  89. package/dist/server/modules/tasks/task-types.d.ts +1 -0
  90. package/dist/server/modules/tasks/task-types.js +1 -0
  91. package/dist/server/modules/tasks/task-types.js.map +1 -1
  92. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.d.ts +1 -1
  93. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js +22 -5
  94. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js.map +1 -1
  95. package/dist/server/modules/workbench/workbench-controller.d.ts +5 -0
  96. package/dist/server/modules/workbench/workbench-controller.js +20 -0
  97. package/dist/server/modules/workbench/workbench-controller.js.map +1 -1
  98. package/dist/server/modules/workbench/workbench-service.d.ts +6 -6
  99. package/dist/server/modules/workbench/workbench-service.js +33 -36
  100. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  101. package/dist/server/modules/workspace/affairs-library-controller.d.ts +8 -0
  102. package/dist/server/modules/workspace/affairs-library-controller.js +11 -0
  103. package/dist/server/modules/workspace/affairs-library-controller.js.map +1 -1
  104. package/dist/server/modules/workspace/affairs-library-preview-link-service.d.ts +6 -0
  105. package/dist/server/modules/workspace/affairs-library-preview-link-service.js +12 -2
  106. package/dist/server/modules/workspace/affairs-library-preview-link-service.js.map +1 -1
  107. package/dist/server/modules/workspace/affairs-library-service.d.ts +9 -0
  108. package/dist/server/modules/workspace/affairs-library-service.js +182 -42
  109. package/dist/server/modules/workspace/affairs-library-service.js.map +1 -1
  110. package/dist/server/modules/workspace/affairs-lightweight-session-controller.d.ts +8 -0
  111. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js +55 -8
  112. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js.map +1 -1
  113. package/dist/server/modules/workspace/affairs-lightweight-session-service.d.ts +13 -0
  114. package/dist/server/modules/workspace/affairs-lightweight-session-service.js +167 -21
  115. package/dist/server/modules/workspace/affairs-lightweight-session-service.js.map +1 -1
  116. package/dist/server/modules/workspace/affairs-tag-controller.d.ts +3 -0
  117. package/dist/server/modules/workspace/affairs-tag-controller.js +5 -0
  118. package/dist/server/modules/workspace/affairs-tag-controller.js.map +1 -1
  119. package/dist/server/modules/workspace/affairs-tag-service.d.ts +22 -1
  120. package/dist/server/modules/workspace/affairs-tag-service.js +41 -2
  121. package/dist/server/modules/workspace/affairs-tag-service.js.map +1 -1
  122. package/dist/server/modules/workspace/teable-api-client.d.ts +118 -0
  123. package/dist/server/modules/workspace/teable-api-client.js +142 -0
  124. package/dist/server/modules/workspace/teable-api-client.js.map +1 -0
  125. package/dist/server/modules/workspace/teable-catalog-controller.d.ts +18 -0
  126. package/dist/server/modules/workspace/teable-catalog-controller.js +17 -0
  127. package/dist/server/modules/workspace/teable-catalog-controller.js.map +1 -0
  128. package/dist/server/modules/workspace/teable-catalog-service.d.ts +36 -0
  129. package/dist/server/modules/workspace/teable-catalog-service.js +124 -0
  130. package/dist/server/modules/workspace/teable-catalog-service.js.map +1 -0
  131. package/dist/server/modules/workspace/teable-credential-service.d.ts +8 -0
  132. package/dist/server/modules/workspace/teable-credential-service.js +37 -0
  133. package/dist/server/modules/workspace/teable-credential-service.js.map +1 -0
  134. package/dist/server/modules/workspace/teable-field-mapping-controller.d.ts +25 -0
  135. package/dist/server/modules/workspace/teable-field-mapping-controller.js +31 -0
  136. package/dist/server/modules/workspace/teable-field-mapping-controller.js.map +1 -0
  137. package/dist/server/modules/workspace/teable-field-mapping-service.d.ts +38 -0
  138. package/dist/server/modules/workspace/teable-field-mapping-service.js +215 -0
  139. package/dist/server/modules/workspace/teable-field-mapping-service.js.map +1 -0
  140. package/dist/server/modules/workspace/teable-global-binding-controller.d.ts +22 -0
  141. package/dist/server/modules/workspace/teable-global-binding-controller.js +25 -0
  142. package/dist/server/modules/workspace/teable-global-binding-controller.js.map +1 -0
  143. package/dist/server/modules/workspace/teable-global-binding-service.d.ts +35 -0
  144. package/dist/server/modules/workspace/teable-global-binding-service.js +151 -0
  145. package/dist/server/modules/workspace/teable-global-binding-service.js.map +1 -0
  146. package/dist/server/modules/workspace/teable-mirror-sync-controller.d.ts +29 -0
  147. package/dist/server/modules/workspace/teable-mirror-sync-controller.js +50 -0
  148. package/dist/server/modules/workspace/teable-mirror-sync-controller.js.map +1 -0
  149. package/dist/server/modules/workspace/teable-mirror-sync-service.d.ts +157 -0
  150. package/dist/server/modules/workspace/teable-mirror-sync-service.js +917 -0
  151. package/dist/server/modules/workspace/teable-mirror-sync-service.js.map +1 -0
  152. package/dist/server/modules/workspace/teable-runtime-controller.d.ts +58 -0
  153. package/dist/server/modules/workspace/teable-runtime-controller.js +60 -0
  154. package/dist/server/modules/workspace/teable-runtime-controller.js.map +1 -0
  155. package/dist/server/modules/workspace/teable-runtime-service.d.ts +96 -0
  156. package/dist/server/modules/workspace/teable-runtime-service.js +362 -0
  157. package/dist/server/modules/workspace/teable-runtime-service.js.map +1 -0
  158. package/dist/server/modules/workspace/teable-workbench-sync-config-controller.d.ts +22 -0
  159. package/dist/server/modules/workspace/teable-workbench-sync-config-controller.js +20 -0
  160. package/dist/server/modules/workspace/teable-workbench-sync-config-controller.js.map +1 -0
  161. package/dist/server/modules/workspace/teable-workbench-sync-config-service.d.ts +22 -0
  162. package/dist/server/modules/workspace/teable-workbench-sync-config-service.js +159 -0
  163. package/dist/server/modules/workspace/teable-workbench-sync-config-service.js.map +1 -0
  164. package/dist/server/routes/affairs.d.ts +9 -1
  165. package/dist/server/routes/affairs.js +120 -1
  166. package/dist/server/routes/affairs.js.map +1 -1
  167. package/dist/server/routes/workbench.js +15 -0
  168. package/dist/server/routes/workbench.js.map +1 -1
  169. package/dist/server/routes/workspaces.js +51 -41
  170. package/dist/server/routes/workspaces.js.map +1 -1
  171. package/dist/server/server/create-server.d.ts +14 -0
  172. package/dist/server/server/create-server.js +73 -7
  173. package/dist/server/server/create-server.js.map +1 -1
  174. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.d.ts +18 -0
  175. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js +191 -0
  176. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js.map +1 -0
  177. package/dist/server/storage/repositories/user-affairs-library-setting-repository.js +8 -5
  178. package/dist/server/storage/repositories/user-affairs-library-setting-repository.js.map +1 -1
  179. package/dist/server/storage/repositories/user-teable-credential-repository.d.ts +9 -0
  180. package/dist/server/storage/repositories/user-teable-credential-repository.js +45 -0
  181. package/dist/server/storage/repositories/user-teable-credential-repository.js.map +1 -0
  182. package/dist/server/storage/repositories/user-teable-field-mapping-repository.d.ts +10 -0
  183. package/dist/server/storage/repositories/user-teable-field-mapping-repository.js +69 -0
  184. package/dist/server/storage/repositories/user-teable-field-mapping-repository.js.map +1 -0
  185. package/dist/server/storage/repositories/user-teable-global-setting-repository.d.ts +8 -0
  186. package/dist/server/storage/repositories/user-teable-global-setting-repository.js +52 -0
  187. package/dist/server/storage/repositories/user-teable-global-setting-repository.js.map +1 -0
  188. package/dist/server/storage/repositories/user-teable-mirror-record-mapping-repository.d.ts +9 -0
  189. package/dist/server/storage/repositories/user-teable-mirror-record-mapping-repository.js +66 -0
  190. package/dist/server/storage/repositories/user-teable-mirror-record-mapping-repository.js.map +1 -0
  191. package/dist/server/storage/repositories/user-teable-mirror-table-binding-repository.d.ts +9 -0
  192. package/dist/server/storage/repositories/user-teable-mirror-table-binding-repository.js +67 -0
  193. package/dist/server/storage/repositories/user-teable-mirror-table-binding-repository.js.map +1 -0
  194. package/dist/server/storage/repositories/user-teable-sync-log-repository.d.ts +14 -0
  195. package/dist/server/storage/repositories/user-teable-sync-log-repository.js +97 -0
  196. package/dist/server/storage/repositories/user-teable-sync-log-repository.js.map +1 -0
  197. package/dist/server/storage/repositories/user-teable-workbench-sync-config-repository.d.ts +8 -0
  198. package/dist/server/storage/repositories/user-teable-workbench-sync-config-repository.js +55 -0
  199. package/dist/server/storage/repositories/user-teable-workbench-sync-config-repository.js.map +1 -0
  200. package/dist/server/storage/sqlite/client.js +404 -1
  201. package/dist/server/storage/sqlite/client.js.map +1 -1
  202. package/dist/server/storage/sqlite/schema.sql +167 -1
  203. package/dist/server/types/domain.d.ts +106 -0
  204. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +22 -3
  205. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +29 -2
  206. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -1
  207. package/node_modules/@codingns/session-sync-core/package.json +3 -1
  208. package/package.json +1 -1
  209. package/dist/public/assets/DesktopWindowPage-D0blSuKd.js +0 -2
  210. package/dist/public/assets/FileContextPanel-BrKO8Xt6.js +0 -1
  211. package/dist/public/assets/GitSidebar-BdwiDtOr.js +0 -6
  212. package/dist/public/assets/MobileCreateSessionSheet-Cx_dBiBb.js +0 -1
  213. package/dist/public/assets/SettingsPage-BlAZCHsy.js +0 -2
  214. package/dist/public/assets/TerminalManagerPanel-CjzbiWjl.js +0 -1
  215. package/dist/public/assets/WorkbenchLayout-CikJBS62.js +0 -1019
  216. package/dist/public/assets/WorkbenchShellRoute-BbbSOiZw.js +0 -1
  217. package/dist/public/assets/WorkbenchShellRoute-DT3VMjWD.css +0 -1
  218. package/dist/public/assets/WorkspaceDebugDetailPage-CVivdPx5.js +0 -1
  219. package/dist/public/assets/WorkspaceDetailPage-DgOSjscR.js +0 -1
  220. package/dist/public/assets/WorkspaceHomePage-HPa7M_Vh.js +0 -1
  221. package/dist/public/assets/index-BxJPQpFM.css +0 -1
  222. package/dist/public/assets/index-CeXGOT_T.js +0 -50
  223. package/dist/public/assets/useRegisteredDebugTemplates-Bol3NVfN.js +0 -1
  224. package/dist/public/assets/workbench-navigation-B7IjRQd8.js +0 -1
@@ -1,9 +1,26 @@
1
- import { DatabaseSync } from "node:sqlite";
2
1
  export interface OpenDatabaseOptions {
3
2
  tempStore?: "FILE" | "MEMORY";
4
3
  }
4
+ export interface AffairsIndexerRunResult {
5
+ changes: number;
6
+ lastInsertRowid: number | bigint;
7
+ }
8
+ /**
9
+ * affairs-indexer 只需要同步 SQL 的最小接口。
10
+ * 不直接暴露 better-sqlite3 的完整 Statement 类型,避免它的绑定参数类型把运行时允许的多参数调用误报成错误。
11
+ */
12
+ export interface AffairsIndexerStatement {
13
+ run(...params: unknown[]): AffairsIndexerRunResult;
14
+ get(...params: unknown[]): unknown;
15
+ all(...params: unknown[]): unknown[];
16
+ }
17
+ export interface AffairsIndexerDatabase {
18
+ exec(sql: string): void;
19
+ prepare(sql: string): AffairsIndexerStatement;
20
+ close(): void;
21
+ }
5
22
  /**
6
23
  * 打开 SQLite 数据库。
7
- * 当前阶段统一使用 Node 内置 sqlite,同步接口足够支撑 CLI 初始化与检测。
24
+ * 这里统一走 better-sqlite3,避免 helper 子进程加载 Node 实验性的 node:sqlite
8
25
  */
9
- export declare function openDatabase(dbPath: string, options?: OpenDatabaseOptions): DatabaseSync;
26
+ export declare function openDatabase(dbPath: string, options?: OpenDatabaseOptions): AffairsIndexerDatabase;
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { DatabaseSync } from "node:sqlite";
3
+ import Database from "../../../../../shared/runtime/better-sqlite3.js";
4
4
  /**
5
5
  * 打开 SQLite 数据库。
6
- * 当前阶段统一使用 Node 内置 sqlite,同步接口足够支撑 CLI 初始化与检测。
6
+ * 这里统一走 better-sqlite3,避免 helper 子进程加载 Node 实验性的 node:sqlite
7
7
  */
8
8
  export function openDatabase(dbPath, options = {}) {
9
9
  fs.mkdirSync(path.dirname(dbPath), { recursive: true });
10
- const db = new DatabaseSync(dbPath);
10
+ const db = new Database(dbPath);
11
11
  const tempStore = options.tempStore ?? "FILE";
12
12
  db.exec("PRAGMA journal_mode=WAL;");
13
13
  db.exec("PRAGMA synchronous=NORMAL;");
@@ -1 +1 @@
1
- {"version":3,"file":"open-database.js","sourceRoot":"","sources":["../../../../../../../src/modules/affairs-indexer/core/src/sqlite/open-database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,UAA+B,EAAE;IAEjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACpC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IACtC,EAAE,CAAC,IAAI,CAAC,qBAAqB,SAAS,GAAG,CAAC,CAAC;IAC3C,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACnC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"open-database.js","sourceRoot":"","sources":["../../../../../../../src/modules/affairs-indexer/core/src/sqlite/open-database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,iDAAiD,CAAC;AA2BvE;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,UAA+B,EAAE;IAEjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAsC,CAAC;IACrE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACpC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IACtC,EAAE,CAAC,IAAI,CAAC,qBAAqB,SAAS,GAAG,CAAC,CAAC;IAC3C,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACnC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type SchedulerMetrics } from "../tasks/scheduler-metrics.js";
2
+ import type { AssistantSandboxService } from "./assistant-sandbox-service.js";
3
+ interface AssistantSandboxCleanupSchedulerLogger {
4
+ error(message: string, detail?: unknown): void;
5
+ }
6
+ interface AssistantSandboxCleanupSchedulerOptions {
7
+ intervalMs?: number;
8
+ maxIntervalMs?: number;
9
+ now?: () => string;
10
+ logger?: AssistantSandboxCleanupSchedulerLogger;
11
+ schedulerMetrics?: SchedulerMetrics;
12
+ }
13
+ export declare class AssistantSandboxCleanupScheduler {
14
+ private readonly assistantSandboxService;
15
+ private readonly intervalMs;
16
+ private readonly maxIntervalMs;
17
+ private readonly now;
18
+ private readonly logger;
19
+ private readonly schedulerMetrics;
20
+ private timer;
21
+ private ticking;
22
+ private started;
23
+ private disposed;
24
+ private idleStreak;
25
+ constructor(assistantSandboxService: Pick<AssistantSandboxService, "runDueCleanup">, options?: AssistantSandboxCleanupSchedulerOptions);
26
+ start(): void;
27
+ dispose(): Promise<void>;
28
+ runOnce(): Promise<void>;
29
+ private scheduleNext;
30
+ private tick;
31
+ }
32
+ export {};
@@ -0,0 +1,93 @@
1
+ import { nowIso } from "../../shared/utils/time.js";
2
+ import { resolveAdaptiveSchedulerDelayMs } from "../tasks/scheduler-metrics.js";
3
+ const DEFAULT_INTERVAL_MS = 60_000;
4
+ export class AssistantSandboxCleanupScheduler {
5
+ assistantSandboxService;
6
+ intervalMs;
7
+ maxIntervalMs;
8
+ now;
9
+ logger;
10
+ schedulerMetrics;
11
+ timer = null;
12
+ ticking = false;
13
+ started = false;
14
+ disposed = false;
15
+ idleStreak = 0;
16
+ constructor(assistantSandboxService, options = {}) {
17
+ this.assistantSandboxService = assistantSandboxService;
18
+ this.intervalMs = Math.max(15_000, options.intervalMs ?? DEFAULT_INTERVAL_MS);
19
+ this.maxIntervalMs = Math.max(this.intervalMs, options.maxIntervalMs ?? this.intervalMs * 8);
20
+ this.now = options.now ?? nowIso;
21
+ this.logger = options.logger ?? console;
22
+ this.schedulerMetrics = options.schedulerMetrics ?? null;
23
+ }
24
+ start() {
25
+ if (this.timer || this.disposed) {
26
+ return;
27
+ }
28
+ this.started = true;
29
+ this.scheduleNext(0);
30
+ }
31
+ async dispose() {
32
+ this.started = false;
33
+ this.disposed = true;
34
+ if (this.timer) {
35
+ clearTimeout(this.timer);
36
+ this.timer = null;
37
+ }
38
+ while (this.ticking) {
39
+ await new Promise((resolve) => setTimeout(resolve, 10));
40
+ }
41
+ }
42
+ async runOnce() {
43
+ await this.tick(false);
44
+ }
45
+ scheduleNext(delayMs) {
46
+ this.timer = setTimeout(() => {
47
+ this.timer = null;
48
+ void this.tick();
49
+ }, delayMs);
50
+ this.timer.unref?.();
51
+ }
52
+ async tick(shouldScheduleNext = true) {
53
+ if (this.ticking) {
54
+ return;
55
+ }
56
+ this.ticking = true;
57
+ const tickStartedAt = Date.now();
58
+ const referenceAt = this.now();
59
+ let result = null;
60
+ let errorCount = 0;
61
+ try {
62
+ result = await this.assistantSandboxService.runDueCleanup(referenceAt);
63
+ }
64
+ catch (error) {
65
+ errorCount = 1;
66
+ this.logger.error("[assistant-sandbox-cleanup-scheduler] tick failed", {
67
+ error: error instanceof Error ? error.message : String(error),
68
+ referenceAt
69
+ });
70
+ }
71
+ finally {
72
+ this.ticking = false;
73
+ const idle = result?.idle ?? true;
74
+ const taskCount = result?.dueSandboxCount ?? 0;
75
+ this.idleStreak = idle ? this.idleStreak + 1 : 0;
76
+ const nextDelayMs = resolveAdaptiveSchedulerDelayMs(this.intervalMs, this.maxIntervalMs, this.idleStreak);
77
+ this.schedulerMetrics?.recordTick({
78
+ schedulerName: "assistant_sandbox_cleanup",
79
+ referenceAt,
80
+ durationMs: Date.now() - tickStartedAt,
81
+ taskCount,
82
+ idle,
83
+ errorCount,
84
+ nextDelayMs,
85
+ idleStreak: this.idleStreak
86
+ });
87
+ if (shouldScheduleNext && this.started && !this.disposed && this.timer === null) {
88
+ this.scheduleNext(nextDelayMs);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ //# sourceMappingURL=assistant-sandbox-cleanup-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistant-sandbox-cleanup-scheduler.js","sourceRoot":"","sources":["../../../../src/modules/butler/assistant-sandbox-cleanup-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import type { AssistantSandboxWorkspace, Workspace } from "../../types/domain.js";
2
+ import type { AssistantSandboxWorkspaceRepository } from "../../storage/repositories/assistant-sandbox-workspace-repository.js";
3
+ import type { ButlerProfileService } from "./butler-profile-service.js";
4
+ import type { ButlerProjectService } from "./butler-project-service.js";
5
+ import { type TaskManager } from "../tasks/task-manager.js";
6
+ import type { CloneWorkspaceInput, WorkspaceService } from "../workspace/workspace-service.js";
7
+ export interface AssistantSandboxWorkspaceView extends AssistantSandboxWorkspace {
8
+ workspace: Workspace | null;
9
+ }
10
+ export interface CreateAssistantSandboxInput {
11
+ userId: string;
12
+ controlSessionId?: string | null;
13
+ title?: string | null;
14
+ description?: string | null;
15
+ purpose?: string | null;
16
+ expiresAt?: string | null;
17
+ source: {
18
+ kind: "blank";
19
+ directoryName?: string | null;
20
+ } | {
21
+ kind: "clone";
22
+ repositoryUrl: string;
23
+ directoryName?: string | null;
24
+ auth?: CloneWorkspaceInput["auth"];
25
+ };
26
+ }
27
+ export interface PromoteAssistantSandboxInput {
28
+ mode?: "pin" | "project";
29
+ projectName?: string | null;
30
+ defaultProvider?: "codex" | "claude-code" | null;
31
+ }
32
+ export interface AssistantSandboxCleanupResult {
33
+ dueSandboxCount: number;
34
+ cleanedSandboxCount: number;
35
+ idle: boolean;
36
+ }
37
+ export declare class AssistantSandboxService {
38
+ private readonly repository;
39
+ private readonly butlerProfileService;
40
+ private readonly workspaceService;
41
+ private readonly butlerProjectService?;
42
+ private readonly taskManager;
43
+ constructor(repository: AssistantSandboxWorkspaceRepository, butlerProfileService: Pick<ButlerProfileService, "getProfile">, workspaceService: Pick<WorkspaceService, "importWorkspace" | "cloneWorkspace" | "removeWorkspace" | "getWorkspaceOrThrow">, butlerProjectService?: Pick<ButlerProjectService, "create"> | undefined, taskManager?: TaskManager);
44
+ listSandboxes(filters: {
45
+ userId: string;
46
+ controlSessionId?: string | null;
47
+ statuses?: Array<"active" | "archived" | "expired" | "orphaned" | "deleted">;
48
+ limit?: number;
49
+ }): AssistantSandboxWorkspaceView[];
50
+ getSandbox(sandboxId: string, userId: string): AssistantSandboxWorkspaceView;
51
+ createSandbox(input: CreateAssistantSandboxInput): Promise<AssistantSandboxWorkspaceView>;
52
+ promoteSandbox(sandboxId: string, userId: string, input?: PromoteAssistantSandboxInput): AssistantSandboxWorkspaceView;
53
+ markSandboxUsedByControlSession(sandboxId: string, userId: string, controlSessionId: string | null): AssistantSandboxWorkspaceView;
54
+ markSandboxOrphanedByWorkspaceId(workspaceId: string, userId: string): AssistantSandboxWorkspaceView | null;
55
+ expireSandbox(sandboxId: string, userId: string): AssistantSandboxWorkspaceView;
56
+ removeSandbox(sandboxId: string, userId: string): AssistantSandboxWorkspaceView;
57
+ runDueCleanup(referenceAt: string): Promise<AssistantSandboxCleanupResult>;
58
+ resolveWorkspaceId(sandboxId: string, userId: string): string;
59
+ private registerBackgroundTasks;
60
+ private runDueCleanupDirect;
61
+ private cleanupSandboxDirect;
62
+ private createBlankWorkspace;
63
+ private createCloneWorkspace;
64
+ private ensureSandboxRootPath;
65
+ private resolveSandboxRootPath;
66
+ private removeSandboxDirectoryIfSafe;
67
+ private requireSandbox;
68
+ private toView;
69
+ }
@@ -0,0 +1,399 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { AppError } from "../../shared/errors/app-error.js";
4
+ import { createId } from "../../shared/utils/id.js";
5
+ import { nowIso } from "../../shared/utils/time.js";
6
+ import { createTaskManager } from "../tasks/task-manager.js";
7
+ import { HOST_TASK_TYPES } from "../tasks/task-types.js";
8
+ const DEFAULT_SANDBOX_RETENTION_DAYS = 30;
9
+ const DEFAULT_SANDBOX_CLEANUP_LIMIT = 20;
10
+ export class AssistantSandboxService {
11
+ repository;
12
+ butlerProfileService;
13
+ workspaceService;
14
+ butlerProjectService;
15
+ taskManager;
16
+ constructor(repository, butlerProfileService, workspaceService, butlerProjectService, taskManager = createTaskManager()) {
17
+ this.repository = repository;
18
+ this.butlerProfileService = butlerProfileService;
19
+ this.workspaceService = workspaceService;
20
+ this.butlerProjectService = butlerProjectService;
21
+ this.taskManager = taskManager;
22
+ this.registerBackgroundTasks();
23
+ }
24
+ listSandboxes(filters) {
25
+ return this.repository
26
+ .list({
27
+ userId: filters.userId,
28
+ controlSessionId: filters.controlSessionId ?? null,
29
+ statuses: filters.statuses,
30
+ limit: filters.limit
31
+ })
32
+ .map((record) => this.toView(record));
33
+ }
34
+ getSandbox(sandboxId, userId) {
35
+ return this.toView(this.requireSandbox(sandboxId, userId));
36
+ }
37
+ async createSandbox(input) {
38
+ const timestamp = nowIso();
39
+ const sandboxRootPath = this.ensureSandboxRootPath();
40
+ const workspace = input.source.kind === "blank"
41
+ ? this.createBlankWorkspace(sandboxRootPath, input)
42
+ : await this.createCloneWorkspace(sandboxRootPath, input);
43
+ const record = this.repository.create({
44
+ id: createId(),
45
+ userId: input.userId,
46
+ workspaceId: workspace.id,
47
+ controlSessionId: normalizeNullableText(input.controlSessionId),
48
+ title: normalizeSandboxTitle(input.title, workspace.name),
49
+ description: normalizeNullableText(input.description),
50
+ sourceKind: input.source.kind,
51
+ sourceRef: input.source.kind === "clone"
52
+ ? normalizeNullableText(input.source.repositoryUrl)
53
+ : workspace.path,
54
+ visibility: "assistant_only",
55
+ status: "active",
56
+ purpose: normalizeNullableText(input.purpose),
57
+ expiresAt: normalizeNullableIsoTime(input.expiresAt, "expiresAt"),
58
+ promotedAt: null,
59
+ createdAt: timestamp,
60
+ updatedAt: timestamp
61
+ });
62
+ return this.toView(record);
63
+ }
64
+ promoteSandbox(sandboxId, userId, input = {}) {
65
+ const current = this.requireSandbox(sandboxId, userId);
66
+ const promotedAt = nowIso();
67
+ const updated = this.repository.update({
68
+ ...current,
69
+ controlSessionId: null,
70
+ visibility: "pinned",
71
+ status: "active",
72
+ expiresAt: null,
73
+ promotedAt: current.promotedAt ?? promotedAt,
74
+ updatedAt: promotedAt
75
+ });
76
+ if (input.mode === "project") {
77
+ if (!this.butlerProjectService) {
78
+ throw new AppError({
79
+ statusCode: 500,
80
+ errorCode: "ASSISTANT_SANDBOX_PROMOTE_UNAVAILABLE",
81
+ detail: "当前环境未启用沙箱晋升为项目的能力"
82
+ });
83
+ }
84
+ const workspace = this.workspaceService.getWorkspaceOrThrow(updated.workspaceId);
85
+ this.butlerProjectService.create({
86
+ workspaceId: workspace.id,
87
+ name: normalizeNullableText(input.projectName) ?? workspace.name,
88
+ repoRoot: workspace.repoRoot ?? workspace.path,
89
+ defaultProvider: input.defaultProvider ?? null,
90
+ config: {
91
+ createdFrom: "assistant_sandbox",
92
+ sandboxId: updated.id
93
+ }
94
+ });
95
+ }
96
+ return this.toView(updated);
97
+ }
98
+ markSandboxUsedByControlSession(sandboxId, userId, controlSessionId) {
99
+ const current = this.requireSandbox(sandboxId, userId);
100
+ const nextControlSessionId = normalizeNullableText(controlSessionId);
101
+ if (current.controlSessionId === nextControlSessionId) {
102
+ return this.toView(current);
103
+ }
104
+ const updated = this.repository.update({
105
+ ...current,
106
+ controlSessionId: nextControlSessionId,
107
+ updatedAt: nowIso()
108
+ });
109
+ return this.toView(updated);
110
+ }
111
+ markSandboxOrphanedByWorkspaceId(workspaceId, userId) {
112
+ const current = this.repository.findByWorkspaceId(workspaceId.trim());
113
+ if (!current || current.userId !== userId) {
114
+ return null;
115
+ }
116
+ if (current.status === "deleted") {
117
+ return this.toView(current);
118
+ }
119
+ const updatedAt = nowIso();
120
+ const nextStatus = isAutomaticCleanupProtected(current) ? current.status : "orphaned";
121
+ const updated = this.repository.update({
122
+ ...current,
123
+ controlSessionId: null,
124
+ status: nextStatus,
125
+ expiresAt: nextStatus === "orphaned" ? addDaysIso(updatedAt, DEFAULT_SANDBOX_RETENTION_DAYS) : null,
126
+ updatedAt
127
+ });
128
+ return this.toView(updated);
129
+ }
130
+ expireSandbox(sandboxId, userId) {
131
+ const current = this.requireSandbox(sandboxId, userId);
132
+ const updatedAt = nowIso();
133
+ const updated = this.repository.update({
134
+ ...current,
135
+ status: "expired",
136
+ updatedAt
137
+ });
138
+ return this.toView(updated);
139
+ }
140
+ removeSandbox(sandboxId, userId) {
141
+ const current = this.requireSandbox(sandboxId, userId);
142
+ const updatedAt = nowIso();
143
+ try {
144
+ this.workspaceService.removeWorkspace(current.workspaceId);
145
+ }
146
+ catch {
147
+ // 工作区可能已被清理或移除;这里仍然收口元数据,避免残留不可操作沙箱。
148
+ }
149
+ const updated = this.repository.update({
150
+ ...current,
151
+ status: "deleted",
152
+ updatedAt
153
+ });
154
+ return this.toView(updated);
155
+ }
156
+ async runDueCleanup(referenceAt) {
157
+ return await this.taskManager.enqueue(HOST_TASK_TYPES.assistantSandboxTick, {
158
+ key: "global",
159
+ source: "assistant_sandbox.run_due_cleanup",
160
+ input: {
161
+ referenceAt
162
+ }
163
+ }).promise;
164
+ }
165
+ resolveWorkspaceId(sandboxId, userId) {
166
+ const sandbox = this.requireSandbox(sandboxId, userId);
167
+ if (sandbox.status === "deleted") {
168
+ throw new AppError({
169
+ statusCode: 409,
170
+ errorCode: "ASSISTANT_SANDBOX_UNAVAILABLE",
171
+ detail: "该沙箱已经删除,不能再用于启动会话"
172
+ });
173
+ }
174
+ if (sandbox.status === "expired") {
175
+ throw new AppError({
176
+ statusCode: 409,
177
+ errorCode: "ASSISTANT_SANDBOX_UNAVAILABLE",
178
+ detail: "该沙箱已过期,不能再用于启动会话"
179
+ });
180
+ }
181
+ if (sandbox.status === "orphaned") {
182
+ throw new AppError({
183
+ statusCode: 409,
184
+ errorCode: "ASSISTANT_SANDBOX_UNAVAILABLE",
185
+ detail: "该沙箱已经孤立,不能再用于启动会话"
186
+ });
187
+ }
188
+ return sandbox.workspaceId;
189
+ }
190
+ registerBackgroundTasks() {
191
+ if (!this.taskManager.has(HOST_TASK_TYPES.assistantSandboxTick)) {
192
+ this.taskManager.register({
193
+ taskType: HOST_TASK_TYPES.assistantSandboxTick,
194
+ executionLane: "host_background",
195
+ timeoutMs: 10_000,
196
+ run: async ({ referenceAt }) => await this.runDueCleanupDirect(referenceAt)
197
+ });
198
+ }
199
+ if (!this.taskManager.has(HOST_TASK_TYPES.assistantSandboxCleanup)) {
200
+ this.taskManager.register({
201
+ taskType: HOST_TASK_TYPES.assistantSandboxCleanup,
202
+ executionLane: "host_background",
203
+ timeoutMs: 10_000,
204
+ run: async ({ sandboxId, referenceAt }) => {
205
+ return this.cleanupSandboxDirect(sandboxId, referenceAt);
206
+ }
207
+ });
208
+ }
209
+ }
210
+ async runDueCleanupDirect(referenceAt) {
211
+ const dueSandboxes = this.repository.listDueCleanup(referenceAt, DEFAULT_SANDBOX_CLEANUP_LIMIT);
212
+ const handles = dueSandboxes.map((sandbox) => this.taskManager.enqueue(HOST_TASK_TYPES.assistantSandboxCleanup, {
213
+ key: sandbox.id,
214
+ source: "assistant_sandbox.tick.cleanup",
215
+ input: {
216
+ sandboxId: sandbox.id,
217
+ referenceAt
218
+ }
219
+ }));
220
+ const results = await Promise.all(handles.map((handle) => handle.promise));
221
+ const cleanedSandboxCount = results.filter(Boolean).length;
222
+ return {
223
+ dueSandboxCount: dueSandboxes.length,
224
+ cleanedSandboxCount,
225
+ idle: dueSandboxes.length === 0
226
+ };
227
+ }
228
+ async cleanupSandboxDirect(sandboxId, referenceAt) {
229
+ const current = this.repository.findById(sandboxId);
230
+ if (!current || current.status !== "orphaned" || !current.expiresAt || current.expiresAt > referenceAt) {
231
+ return false;
232
+ }
233
+ if (isAutomaticCleanupProtected(current)) {
234
+ this.repository.update({
235
+ ...current,
236
+ status: "active",
237
+ expiresAt: null,
238
+ updatedAt: nowIso()
239
+ });
240
+ return false;
241
+ }
242
+ await this.removeSandboxDirectoryIfSafe(current);
243
+ this.removeSandbox(current.id, current.userId);
244
+ return true;
245
+ }
246
+ createBlankWorkspace(sandboxRootPath, input) {
247
+ if (input.source.kind !== "blank") {
248
+ throw new Error("invalid blank sandbox input");
249
+ }
250
+ const directoryName = normalizeSandboxDirectoryName(input.source.directoryName, input.title);
251
+ const sandboxPath = path.join(sandboxRootPath, directoryName);
252
+ if (fs.existsSync(sandboxPath)) {
253
+ throw new AppError({
254
+ statusCode: 409,
255
+ errorCode: "ASSISTANT_SANDBOX_EXISTS",
256
+ detail: "目标沙箱目录已存在,请换一个名称",
257
+ field: "directoryName"
258
+ });
259
+ }
260
+ fs.mkdirSync(sandboxPath, { recursive: true });
261
+ return this.workspaceService.importWorkspace(sandboxPath, normalizeNullableText(input.title) ?? directoryName);
262
+ }
263
+ async createCloneWorkspace(sandboxRootPath, input) {
264
+ if (input.source.kind !== "clone") {
265
+ throw new Error("invalid clone sandbox input");
266
+ }
267
+ return await this.workspaceService.cloneWorkspace({
268
+ repositoryUrl: input.source.repositoryUrl,
269
+ parentPath: sandboxRootPath,
270
+ directoryName: normalizeSandboxDirectoryName(input.source.directoryName, input.title, input.source.repositoryUrl),
271
+ name: normalizeNullableText(input.title) ?? undefined,
272
+ auth: input.source.auth
273
+ });
274
+ }
275
+ ensureSandboxRootPath() {
276
+ const sandboxRootPath = this.resolveSandboxRootPath();
277
+ fs.mkdirSync(sandboxRootPath, { recursive: true });
278
+ return sandboxRootPath;
279
+ }
280
+ resolveSandboxRootPath() {
281
+ const profile = this.butlerProfileService.getProfile();
282
+ if (!profile) {
283
+ throw new AppError({
284
+ statusCode: 409,
285
+ errorCode: "BUTLER_PROFILE_NOT_INITIALIZED",
286
+ detail: "代码助手尚未完成初始化,不能创建沙箱工作区"
287
+ });
288
+ }
289
+ const workspacePath = profile.workspacePath;
290
+ return path.join(path.resolve(workspacePath), "sandboxes");
291
+ }
292
+ async removeSandboxDirectoryIfSafe(record) {
293
+ const sandboxPath = this.toView(record).workspace?.path?.trim() || null;
294
+ if (!sandboxPath) {
295
+ return;
296
+ }
297
+ const resolvedSandboxPath = path.resolve(sandboxPath);
298
+ const sandboxRootPath = this.resolveSandboxRootPath();
299
+ if (!isPathInsideDirectory(resolvedSandboxPath, sandboxRootPath)) {
300
+ throw new AppError({
301
+ statusCode: 409,
302
+ errorCode: "ASSISTANT_SANDBOX_DELETE_PATH_UNSAFE",
303
+ detail: "沙箱目录不在允许清理的沙箱根目录内,已拒绝自动删除"
304
+ });
305
+ }
306
+ await fs.promises.rm(resolvedSandboxPath, {
307
+ recursive: true,
308
+ force: true
309
+ });
310
+ }
311
+ requireSandbox(sandboxId, userId) {
312
+ const sandbox = this.repository.findById(sandboxId.trim());
313
+ if (!sandbox || sandbox.userId !== userId) {
314
+ throw new AppError({
315
+ statusCode: 404,
316
+ errorCode: "ASSISTANT_SANDBOX_NOT_FOUND",
317
+ detail: "未找到对应的助手沙箱"
318
+ });
319
+ }
320
+ return sandbox;
321
+ }
322
+ toView(record) {
323
+ try {
324
+ return {
325
+ ...record,
326
+ workspace: this.workspaceService.getWorkspaceOrThrow(record.workspaceId)
327
+ };
328
+ }
329
+ catch {
330
+ return {
331
+ ...record,
332
+ workspace: null
333
+ };
334
+ }
335
+ }
336
+ }
337
+ function isAutomaticCleanupProtected(sandbox) {
338
+ return sandbox.visibility === "pinned" || sandbox.promotedAt !== null;
339
+ }
340
+ function normalizeNullableText(value) {
341
+ const normalized = value?.trim();
342
+ return normalized ? normalized : null;
343
+ }
344
+ function normalizeSandboxTitle(input, fallback) {
345
+ return normalizeNullableText(input) ?? fallback;
346
+ }
347
+ function normalizeSandboxDirectoryName(requestedName, title, repositoryUrl) {
348
+ const normalized = normalizeNullableText(requestedName)
349
+ ?? slugifyPathSegment(normalizeNullableText(title) ?? inferRepositoryName(repositoryUrl) ?? `sandbox-${createId().slice(0, 8)}`);
350
+ if (!normalized) {
351
+ throw new AppError({
352
+ statusCode: 400,
353
+ errorCode: "INVALID_INPUT",
354
+ detail: "沙箱目录名不能为空",
355
+ field: "directoryName"
356
+ });
357
+ }
358
+ return normalized;
359
+ }
360
+ function inferRepositoryName(repositoryUrl) {
361
+ const normalized = normalizeNullableText(repositoryUrl);
362
+ if (!normalized) {
363
+ return null;
364
+ }
365
+ const tail = normalized.split("/").pop() ?? "";
366
+ return tail.replace(/\.git$/i, "").trim() || null;
367
+ }
368
+ function slugifyPathSegment(value) {
369
+ const normalized = value
370
+ .trim()
371
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
372
+ .replace(/-+/g, "-")
373
+ .replace(/^-|-$/g, "");
374
+ return normalized.slice(0, 64);
375
+ }
376
+ function normalizeNullableIsoTime(value, field) {
377
+ const normalized = normalizeNullableText(value);
378
+ if (!normalized) {
379
+ return null;
380
+ }
381
+ const timestamp = Date.parse(normalized);
382
+ if (Number.isNaN(timestamp)) {
383
+ throw new AppError({
384
+ statusCode: 400,
385
+ errorCode: "INVALID_INPUT",
386
+ detail: `${field} 必须是合法的 ISO 时间`,
387
+ field
388
+ });
389
+ }
390
+ return new Date(timestamp).toISOString();
391
+ }
392
+ function addDaysIso(referenceAt, days) {
393
+ return new Date(new Date(referenceAt).getTime() + days * 24 * 60 * 60 * 1000).toISOString();
394
+ }
395
+ function isPathInsideDirectory(candidatePath, parentPath) {
396
+ const relative = path.relative(path.resolve(parentPath), path.resolve(candidatePath));
397
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
398
+ }
399
+ //# sourceMappingURL=assistant-sandbox-service.js.map