@logictan/dsh-config-manager 0.1.60

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 (429) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/cordis.patch.yml +21 -0
  4. package/lib/adapters/agent-instructions.d.ts +13 -0
  5. package/lib/adapters/agent-instructions.js +46 -0
  6. package/lib/adapters/agent-presets.d.ts +13 -0
  7. package/lib/adapters/agent-presets.js +14 -0
  8. package/lib/adapters/credentials.d.ts +37 -0
  9. package/lib/adapters/credentials.js +122 -0
  10. package/lib/adapters/file-collection.d.ts +15 -0
  11. package/lib/adapters/file-collection.js +127 -0
  12. package/lib/adapters/index.d.ts +57 -0
  13. package/lib/adapters/index.js +55 -0
  14. package/lib/adapters/link-report.d.ts +16 -0
  15. package/lib/adapters/link-report.js +47 -0
  16. package/lib/adapters/mcp.d.ts +28 -0
  17. package/lib/adapters/mcp.js +178 -0
  18. package/lib/adapters/plugin-files.d.ts +18 -0
  19. package/lib/adapters/plugin-files.js +142 -0
  20. package/lib/adapters/plugins.d.ts +41 -0
  21. package/lib/adapters/plugins.js +603 -0
  22. package/lib/adapters/pnpm-workspace.d.ts +26 -0
  23. package/lib/adapters/pnpm-workspace.js +147 -0
  24. package/lib/adapters/prompts.d.ts +30 -0
  25. package/lib/adapters/prompts.js +231 -0
  26. package/lib/adapters/providers.d.ts +34 -0
  27. package/lib/adapters/providers.js +155 -0
  28. package/lib/adapters/self.d.ts +17 -0
  29. package/lib/adapters/self.js +71 -0
  30. package/lib/adapters/sessions.d.ts +14 -0
  31. package/lib/adapters/sessions.js +15 -0
  32. package/lib/adapters/settings.d.ts +28 -0
  33. package/lib/adapters/settings.js +165 -0
  34. package/lib/adapters/skills.d.ts +13 -0
  35. package/lib/adapters/skills.js +14 -0
  36. package/lib/adapters/test-helpers.d.ts +125 -0
  37. package/lib/adapters/test-helpers.js +236 -0
  38. package/lib/adapters/ui.d.ts +27 -0
  39. package/lib/adapters/ui.js +89 -0
  40. package/lib/adapters/workspaces.d.ts +13 -0
  41. package/lib/adapters/workspaces.js +106 -0
  42. package/lib/client.d.ts +1026 -0
  43. package/lib/client.js +10047 -0
  44. package/lib/core/analyzer.d.ts +99 -0
  45. package/lib/core/analyzer.js +934 -0
  46. package/lib/core/backup.d.ts +138 -0
  47. package/lib/core/backup.js +513 -0
  48. package/lib/core/boot-rescue.d.ts +146 -0
  49. package/lib/core/boot-rescue.js +618 -0
  50. package/lib/core/cache-cleaner.d.ts +31 -0
  51. package/lib/core/cache-cleaner.js +95 -0
  52. package/lib/core/config-lifecycle.d.ts +180 -0
  53. package/lib/core/config-lifecycle.js +481 -0
  54. package/lib/core/config-snapshot.d.ts +135 -0
  55. package/lib/core/config-snapshot.js +424 -0
  56. package/lib/core/config-state.d.ts +70 -0
  57. package/lib/core/config-state.js +177 -0
  58. package/lib/core/consult-source.d.ts +58 -0
  59. package/lib/core/consult-source.js +260 -0
  60. package/lib/core/crash-report.d.ts +84 -0
  61. package/lib/core/crash-report.js +317 -0
  62. package/lib/core/exporter.d.ts +68 -0
  63. package/lib/core/exporter.js +375 -0
  64. package/lib/core/ghost-sweep.d.ts +19 -0
  65. package/lib/core/ghost-sweep.js +52 -0
  66. package/lib/core/importer.d.ts +60 -0
  67. package/lib/core/importer.js +29 -0
  68. package/lib/core/index.d.ts +14 -0
  69. package/lib/core/index.js +14 -0
  70. package/lib/core/journal.d.ts +197 -0
  71. package/lib/core/journal.js +418 -0
  72. package/lib/core/local-plugin-host.d.ts +35 -0
  73. package/lib/core/local-plugin-host.js +120 -0
  74. package/lib/core/local-plugin-pack.d.ts +135 -0
  75. package/lib/core/local-plugin-pack.js +278 -0
  76. package/lib/core/messages.d.ts +310 -0
  77. package/lib/core/messages.js +621 -0
  78. package/lib/core/migration-consult.d.ts +117 -0
  79. package/lib/core/migration-consult.js +363 -0
  80. package/lib/core/migration-history.d.ts +153 -0
  81. package/lib/core/migration-history.js +411 -0
  82. package/lib/core/model-tools.d.ts +50 -0
  83. package/lib/core/model-tools.js +148 -0
  84. package/lib/core/msg-types.d.ts +8 -0
  85. package/lib/core/msg-types.js +6 -0
  86. package/lib/core/phase3-child-crash.d.ts +1 -0
  87. package/lib/core/phase3-child-crash.js +87 -0
  88. package/lib/core/phase3-host.d.ts +138 -0
  89. package/lib/core/phase3-host.js +359 -0
  90. package/lib/core/phase3-prod-child.d.ts +1 -0
  91. package/lib/core/phase3-prod-child.js +35 -0
  92. package/lib/core/phase4-crash-child.d.ts +1 -0
  93. package/lib/core/phase4-crash-child.js +103 -0
  94. package/lib/core/plugin-cli.d.ts +150 -0
  95. package/lib/core/plugin-cli.js +497 -0
  96. package/lib/core/reconcile.d.ts +120 -0
  97. package/lib/core/reconcile.js +389 -0
  98. package/lib/core/recovery-orchestrator.d.ts +94 -0
  99. package/lib/core/recovery-orchestrator.js +327 -0
  100. package/lib/core/restore.d.ts +162 -0
  101. package/lib/core/restore.js +703 -0
  102. package/lib/core/rollback.d.ts +12 -0
  103. package/lib/core/rollback.js +145 -0
  104. package/lib/core/run-registry.d.ts +85 -0
  105. package/lib/core/run-registry.js +157 -0
  106. package/lib/core/startup-barrier.d.ts +53 -0
  107. package/lib/core/startup-barrier.js +77 -0
  108. package/lib/core/types.d.ts +515 -0
  109. package/lib/core/types.js +27 -0
  110. package/lib/core/undo.d.ts +73 -0
  111. package/lib/core/undo.js +87 -0
  112. package/lib/core/validator.d.ts +18 -0
  113. package/lib/core/validator.js +77 -0
  114. package/lib/core/verify-recovery.d.ts +21 -0
  115. package/lib/core/verify-recovery.js +256 -0
  116. package/lib/core/watcher.d.ts +101 -0
  117. package/lib/core/watcher.js +219 -0
  118. package/lib/index.d.ts +238 -0
  119. package/lib/index.js +2764 -0
  120. package/lib/migrations/index.d.ts +29 -0
  121. package/lib/migrations/index.js +56 -0
  122. package/lib/migrations/v1-to-v2.d.ts +9 -0
  123. package/lib/migrations/v1-to-v2.js +10 -0
  124. package/lib/profiles/index.d.ts +4 -0
  125. package/lib/profiles/index.js +5 -0
  126. package/lib/profiles/profile-manager.d.ts +106 -0
  127. package/lib/profiles/profile-manager.js +543 -0
  128. package/lib/schema/config.d.ts +28 -0
  129. package/lib/schema/config.js +165 -0
  130. package/lib/schema/index.d.ts +24 -0
  131. package/lib/schema/index.js +26 -0
  132. package/lib/schema/manifest.d.ts +28 -0
  133. package/lib/schema/manifest.js +109 -0
  134. package/lib/schema/tombstones.d.ts +46 -0
  135. package/lib/schema/tombstones.js +64 -0
  136. package/lib/schema/types.d.ts +232 -0
  137. package/lib/schema/types.js +8 -0
  138. package/lib/schema/versions.d.ts +27 -0
  139. package/lib/schema/versions.js +50 -0
  140. package/lib/security/encryption.d.ts +85 -0
  141. package/lib/security/encryption.js +279 -0
  142. package/lib/security/index.d.ts +16 -0
  143. package/lib/security/index.js +17 -0
  144. package/lib/security/integrity.d.ts +30 -0
  145. package/lib/security/integrity.js +78 -0
  146. package/lib/security/redaction.d.ts +26 -0
  147. package/lib/security/redaction.js +95 -0
  148. package/lib/security/secret-scanner.d.ts +86 -0
  149. package/lib/security/secret-scanner.js +404 -0
  150. package/lib/security/vault.d.ts +59 -0
  151. package/lib/security/vault.js +151 -0
  152. package/lib/security/zip-security.d.ts +27 -0
  153. package/lib/security/zip-security.js +154 -0
  154. package/lib/sync/autosync-config.d.ts +39 -0
  155. package/lib/sync/autosync-config.js +151 -0
  156. package/lib/sync/autosync-scheduler.d.ts +139 -0
  157. package/lib/sync/autosync-scheduler.js +564 -0
  158. package/lib/sync/fs.d.ts +18 -0
  159. package/lib/sync/fs.js +49 -0
  160. package/lib/sync/git/git-transport.d.ts +100 -0
  161. package/lib/sync/git/git-transport.js +437 -0
  162. package/lib/sync/github-auth.d.ts +131 -0
  163. package/lib/sync/github-auth.js +264 -0
  164. package/lib/sync/layout.d.ts +43 -0
  165. package/lib/sync/layout.js +203 -0
  166. package/lib/sync/snapshot-json.d.ts +9 -0
  167. package/lib/sync/snapshot-json.js +107 -0
  168. package/lib/sync/sync-config.d.ts +81 -0
  169. package/lib/sync/sync-config.js +281 -0
  170. package/lib/sync/sync-engine.d.ts +269 -0
  171. package/lib/sync/sync-engine.js +591 -0
  172. package/lib/sync/sync-history.d.ts +42 -0
  173. package/lib/sync/sync-history.js +78 -0
  174. package/lib/sync/sync-selection.d.ts +29 -0
  175. package/lib/sync/sync-selection.js +127 -0
  176. package/lib/sync/sync-session.d.ts +40 -0
  177. package/lib/sync/sync-session.js +54 -0
  178. package/lib/sync/sync-state.d.ts +37 -0
  179. package/lib/sync/sync-state.js +108 -0
  180. package/lib/sync/transport.d.ts +75 -0
  181. package/lib/sync/transport.js +55 -0
  182. package/lib/sync/ui-prefs.d.ts +34 -0
  183. package/lib/sync/ui-prefs.js +109 -0
  184. package/lib/sync/webdav/webdav-transport.d.ts +77 -0
  185. package/lib/sync/webdav/webdav-transport.js +451 -0
  186. package/lib/ui/errors.d.ts +19 -0
  187. package/lib/ui/errors.js +81 -0
  188. package/lib/ui/export-flow.d.ts +82 -0
  189. package/lib/ui/export-flow.js +98 -0
  190. package/lib/ui/i18n.d.ts +289 -0
  191. package/lib/ui/i18n.js +581 -0
  192. package/lib/ui/migration-consult-view.d.ts +91 -0
  193. package/lib/ui/migration-consult-view.js +115 -0
  194. package/lib/ui/progress.d.ts +38 -0
  195. package/lib/ui/progress.js +87 -0
  196. package/lib/ui/report.d.ts +23 -0
  197. package/lib/ui/report.js +153 -0
  198. package/lib/ui/test-helpers.d.ts +69 -0
  199. package/lib/ui/test-helpers.js +154 -0
  200. package/lib/ui/types.d.ts +257 -0
  201. package/lib/ui/types.js +21 -0
  202. package/lib/utils/atomic-write.d.ts +88 -0
  203. package/lib/utils/atomic-write.js +399 -0
  204. package/lib/utils/env-lock.d.ts +393 -0
  205. package/lib/utils/env-lock.js +930 -0
  206. package/lib/utils/hashing.d.ts +17 -0
  207. package/lib/utils/hashing.js +41 -0
  208. package/lib/utils/json.d.ts +24 -0
  209. package/lib/utils/json.js +76 -0
  210. package/lib/utils/logger.d.ts +39 -0
  211. package/lib/utils/logger.js +82 -0
  212. package/lib/utils/paths.d.ts +42 -0
  213. package/lib/utils/paths.js +189 -0
  214. package/lib/utils/proxy.d.ts +69 -0
  215. package/lib/utils/proxy.js +455 -0
  216. package/lib/utils/recursive-walk.d.ts +24 -0
  217. package/lib/utils/recursive-walk.js +134 -0
  218. package/lib/utils/zip.d.ts +54 -0
  219. package/lib/utils/zip.js +285 -0
  220. package/package.json +188 -0
  221. package/src/adapters/agent-instructions.ts +48 -0
  222. package/src/adapters/agent-presets.ts +14 -0
  223. package/src/adapters/credentials.test.ts +90 -0
  224. package/src/adapters/credentials.ts +143 -0
  225. package/src/adapters/file-collection.ts +137 -0
  226. package/src/adapters/files.test.ts +353 -0
  227. package/src/adapters/index.ts +94 -0
  228. package/src/adapters/link-report.test.ts +65 -0
  229. package/src/adapters/link-report.ts +61 -0
  230. package/src/adapters/mcp.test.ts +96 -0
  231. package/src/adapters/mcp.ts +185 -0
  232. package/src/adapters/plugin-files.ts +146 -0
  233. package/src/adapters/plugins.test.ts +326 -0
  234. package/src/adapters/plugins.ts +615 -0
  235. package/src/adapters/pnpm-workspace.test.ts +87 -0
  236. package/src/adapters/pnpm-workspace.ts +150 -0
  237. package/src/adapters/prompts.test.ts +110 -0
  238. package/src/adapters/prompts.ts +230 -0
  239. package/src/adapters/providers.test.ts +126 -0
  240. package/src/adapters/providers.ts +173 -0
  241. package/src/adapters/roundtrip.test.ts +190 -0
  242. package/src/adapters/self.test.ts +92 -0
  243. package/src/adapters/self.ts +75 -0
  244. package/src/adapters/sessions.ts +15 -0
  245. package/src/adapters/settings.test.ts +100 -0
  246. package/src/adapters/settings.ts +187 -0
  247. package/src/adapters/skills.ts +14 -0
  248. package/src/adapters/test-helpers.ts +248 -0
  249. package/src/adapters/ui.test.ts +57 -0
  250. package/src/adapters/ui.ts +108 -0
  251. package/src/adapters/workspaces.test.ts +106 -0
  252. package/src/adapters/workspaces.ts +109 -0
  253. package/src/client/ConfigManagerSection.tsx +90 -0
  254. package/src/client/api.ts +66 -0
  255. package/src/client/client-types.ts +18 -0
  256. package/src/client/common/ErrorBanner.tsx +73 -0
  257. package/src/client/common/Icon.tsx +129 -0
  258. package/src/client/common/Modal.tsx +173 -0
  259. package/src/client/common/ToastViewport.tsx +73 -0
  260. package/src/client/common/toast-store.test.ts +192 -0
  261. package/src/client/common/toast-store.ts +181 -0
  262. package/src/client/common/toast-types.ts +7 -0
  263. package/src/client/common/ui.tsx +342 -0
  264. package/src/client/config-manager.module.css +2971 -0
  265. package/src/client/css-modules.d.ts +8 -0
  266. package/src/client/index.ts +65 -0
  267. package/src/client/locales.ts +56 -0
  268. package/src/client/lucide-icons.d.ts +38 -0
  269. package/src/client/run-store.test.ts +223 -0
  270. package/src/client/run-store.ts +372 -0
  271. package/src/client/sync/SyncConfirmView.tsx +379 -0
  272. package/src/client/sync/SyncHistoryView.test.ts +40 -0
  273. package/src/client/sync/SyncHistoryView.tsx +220 -0
  274. package/src/client/sync/SyncSettingsView.tsx +1535 -0
  275. package/src/client/sync/history-model.test.ts +194 -0
  276. package/src/client/sync/history-model.ts +195 -0
  277. package/src/client/sync/sync-api.test.ts +585 -0
  278. package/src/client/sync/sync-api.ts +543 -0
  279. package/src/client/sync/sync-locales.ts +416 -0
  280. package/src/client/sync/sync-push-preview.test.ts +52 -0
  281. package/src/client/sync/sync-view-v2.test.ts +213 -0
  282. package/src/client/sync/sync-view.test.ts +487 -0
  283. package/src/client/sync/sync-view.ts +759 -0
  284. package/src/core/analyzer.ts +1068 -0
  285. package/src/core/backup.ts +605 -0
  286. package/src/core/boot-rescue.test.ts +847 -0
  287. package/src/core/boot-rescue.ts +737 -0
  288. package/src/core/cache-cleaner.test.ts +169 -0
  289. package/src/core/cache-cleaner.ts +121 -0
  290. package/src/core/config-lifecycle.test.ts +727 -0
  291. package/src/core/config-lifecycle.ts +573 -0
  292. package/src/core/config-snapshot.test.ts +369 -0
  293. package/src/core/config-snapshot.ts +522 -0
  294. package/src/core/config-state.test.ts +381 -0
  295. package/src/core/config-state.ts +207 -0
  296. package/src/core/consult-source.test.ts +199 -0
  297. package/src/core/consult-source.ts +293 -0
  298. package/src/core/crash-report.test.ts +380 -0
  299. package/src/core/crash-report.ts +369 -0
  300. package/src/core/exporter.ts +417 -0
  301. package/src/core/ghost-sweep.test.ts +94 -0
  302. package/src/core/ghost-sweep.ts +67 -0
  303. package/src/core/importer.ts +83 -0
  304. package/src/core/index.ts +56 -0
  305. package/src/core/journal.test.ts +257 -0
  306. package/src/core/journal.ts +518 -0
  307. package/src/core/local-plugin-host.ts +152 -0
  308. package/src/core/local-plugin-pack.test.ts +359 -0
  309. package/src/core/local-plugin-pack.ts +363 -0
  310. package/src/core/messages.test.ts +54 -0
  311. package/src/core/messages.ts +664 -0
  312. package/src/core/migration-consult.test.ts +315 -0
  313. package/src/core/migration-consult.ts +494 -0
  314. package/src/core/migration-history.test.ts +319 -0
  315. package/src/core/migration-history.ts +484 -0
  316. package/src/core/model-tools.test.ts +190 -0
  317. package/src/core/model-tools.ts +208 -0
  318. package/src/core/msg-types.ts +10 -0
  319. package/src/core/phase3-child-crash.ts +95 -0
  320. package/src/core/phase3-host.ts +411 -0
  321. package/src/core/phase3-p1.test.ts +212 -0
  322. package/src/core/phase3-p2b-fingerprint.test.ts +189 -0
  323. package/src/core/phase3-prod-child.ts +36 -0
  324. package/src/core/phase3-production-integration.test.ts +286 -0
  325. package/src/core/phase4-crash-child.ts +101 -0
  326. package/src/core/phase4-crash-injection.test.ts +166 -0
  327. package/src/core/plugin-cli.fs.test.ts +184 -0
  328. package/src/core/plugin-cli.test.ts +137 -0
  329. package/src/core/plugin-cli.ts +563 -0
  330. package/src/core/reconcile.test.ts +392 -0
  331. package/src/core/reconcile.ts +498 -0
  332. package/src/core/recovery-orchestrator.test.ts +145 -0
  333. package/src/core/recovery-orchestrator.ts +366 -0
  334. package/src/core/restore-manage.test.ts +94 -0
  335. package/src/core/restore.ts +849 -0
  336. package/src/core/rollback.ts +151 -0
  337. package/src/core/run-progress.test.ts +237 -0
  338. package/src/core/run-registry.test.ts +232 -0
  339. package/src/core/run-registry.ts +209 -0
  340. package/src/core/smoke.test.ts +842 -0
  341. package/src/core/startup-barrier.ts +99 -0
  342. package/src/core/types.ts +542 -0
  343. package/src/core/undo.ts +116 -0
  344. package/src/core/validator.ts +84 -0
  345. package/src/core/verify-recovery.ts +265 -0
  346. package/src/core/watcher.test.ts +326 -0
  347. package/src/core/watcher.ts +263 -0
  348. package/src/index.facade.test.ts +265 -0
  349. package/src/index.sync.test.ts +217 -0
  350. package/src/index.ts +2975 -0
  351. package/src/migrations/index.ts +71 -0
  352. package/src/migrations/v1-to-v2.ts +17 -0
  353. package/src/profiles/index.ts +8 -0
  354. package/src/profiles/profile-manager.test.ts +282 -0
  355. package/src/profiles/profile-manager.ts +630 -0
  356. package/src/schema/config.ts +165 -0
  357. package/src/schema/index.ts +71 -0
  358. package/src/schema/manifest-schema.test.ts +519 -0
  359. package/src/schema/manifest.ts +124 -0
  360. package/src/schema/tombstones.test.ts +131 -0
  361. package/src/schema/tombstones.ts +89 -0
  362. package/src/schema/types.ts +245 -0
  363. package/src/schema/versions.ts +55 -0
  364. package/src/security/encryption.ts +335 -0
  365. package/src/security/index.ts +16 -0
  366. package/src/security/integrity.ts +102 -0
  367. package/src/security/redaction.ts +101 -0
  368. package/src/security/secret-scanner.ts +445 -0
  369. package/src/security/security.test.ts +1215 -0
  370. package/src/security/vault.test.ts +164 -0
  371. package/src/security/vault.ts +198 -0
  372. package/src/security/zip-security.ts +180 -0
  373. package/src/sync/autosync-config.test.ts +147 -0
  374. package/src/sync/autosync-config.ts +179 -0
  375. package/src/sync/autosync-scheduler.test.ts +490 -0
  376. package/src/sync/autosync-scheduler.ts +646 -0
  377. package/src/sync/fs.ts +48 -0
  378. package/src/sync/git/git-transport.test.ts +673 -0
  379. package/src/sync/git/git-transport.ts +491 -0
  380. package/src/sync/github-auth.test.ts +280 -0
  381. package/src/sync/github-auth.ts +356 -0
  382. package/src/sync/layout.test.ts +270 -0
  383. package/src/sync/layout.ts +222 -0
  384. package/src/sync/snapshot-json.test.ts +119 -0
  385. package/src/sync/snapshot-json.ts +128 -0
  386. package/src/sync/sync-config.test.ts +310 -0
  387. package/src/sync/sync-config.ts +299 -0
  388. package/src/sync/sync-engine.test.ts +1074 -0
  389. package/src/sync/sync-engine.ts +754 -0
  390. package/src/sync/sync-history.test.ts +116 -0
  391. package/src/sync/sync-history.ts +120 -0
  392. package/src/sync/sync-selection.test.ts +186 -0
  393. package/src/sync/sync-selection.ts +143 -0
  394. package/src/sync/sync-session.test.ts +143 -0
  395. package/src/sync/sync-session.ts +77 -0
  396. package/src/sync/sync-state.test.ts +164 -0
  397. package/src/sync/sync-state.ts +131 -0
  398. package/src/sync/transport.test.ts +111 -0
  399. package/src/sync/transport.ts +118 -0
  400. package/src/sync/ui-prefs.test.ts +152 -0
  401. package/src/sync/ui-prefs.ts +135 -0
  402. package/src/sync/webdav/webdav-transport.test.ts +871 -0
  403. package/src/sync/webdav/webdav-transport.ts +535 -0
  404. package/src/ui/errors.test.ts +47 -0
  405. package/src/ui/errors.ts +100 -0
  406. package/src/ui/export-flow.test.ts +87 -0
  407. package/src/ui/export-flow.ts +156 -0
  408. package/src/ui/i18n.ts +590 -0
  409. package/src/ui/migration-consult-view.test.ts +151 -0
  410. package/src/ui/migration-consult-view.ts +178 -0
  411. package/src/ui/progress.test.ts +47 -0
  412. package/src/ui/progress.ts +102 -0
  413. package/src/ui/report.test.ts +101 -0
  414. package/src/ui/report.ts +146 -0
  415. package/src/ui/test-helpers.ts +184 -0
  416. package/src/ui/types.ts +325 -0
  417. package/src/utils/atomic-write.test.ts +312 -0
  418. package/src/utils/atomic-write.ts +422 -0
  419. package/src/utils/env-lock.test.ts +999 -0
  420. package/src/utils/env-lock.ts +1086 -0
  421. package/src/utils/hashing.ts +53 -0
  422. package/src/utils/json.ts +77 -0
  423. package/src/utils/logger.ts +114 -0
  424. package/src/utils/paths.ts +176 -0
  425. package/src/utils/proxy.test.ts +465 -0
  426. package/src/utils/proxy.ts +493 -0
  427. package/src/utils/recursive-walk.test.ts +148 -0
  428. package/src/utils/recursive-walk.ts +145 -0
  429. package/src/utils/zip.ts +332 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Phase 3 Reconciler 单测(node:test,零依赖)。
3
+ * 覆盖:corrupt→quarantine、env 不匹配、terminal 遗留→organized、all-done→recovered、
4
+ * no-apply→noop、external half-install→needs-attention、live owner 跳过、startup
5
+ * stale→RECOVERY_REQUIRED(不自动 recover)、executeRecovery 无确认无副作用、幂等。
6
+ */
7
+ import test from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+ import fs from 'node:fs/promises';
10
+ import fssync from 'node:fs';
11
+ import os from 'node:os';
12
+ import path from 'node:path';
13
+ import { JournalStore, createJournalEntry, type OperationJournal, type JournalStep } from './journal.ts';
14
+ import {
15
+ reconcileActive, executeRecovery, inspectStartup, recomputeRecoveryDecision,
16
+ type ReconcileProbeHooks, type ReconcileEnv,
17
+ } from './reconcile.ts';
18
+
19
+ function tmp(t: test.TestContext): string {
20
+ const dir = fssync.mkdtempSync(path.join(os.tmpdir(), 'rec-'));
21
+ t.after(() => fssync.rmSync(dir, { recursive: true, force: true }));
22
+ return dir;
23
+ }
24
+
25
+ const FP = 'fp-rec';
26
+ function mkStore(dir: string) { return new JournalStore({ transactionsDir: path.join(dir, 'transactions') }); }
27
+
28
+ function op(id: string, state?: OperationJournal['state']): OperationJournal {
29
+ const j = createJournalEntry('import-apply', { operationId: id, ownerInstanceId: 'o1', lockId: 'l1', packageVersion: '0.1.54', environmentFingerprint: FP }, '2026-01-01T00:00:00.000Z');
30
+ if (state !== undefined) j.state = state;
31
+ return j;
32
+ }
33
+
34
+ function fileStep(status: JournalStep['status']): JournalStep {
35
+ return { adapter: 'skills', ref: 'a.md', kind: 'Update', external: false, beforeFp: 'before', afterFp: 'after-a.md', status, appliedAt: null };
36
+ }
37
+ function extStep(status: JournalStep['status']): JournalStep {
38
+ return { adapter: 'plugins', ref: 'plugin:@x', kind: 'Install', external: true, beforeFp: null, afterFp: null, status, appliedAt: null };
39
+ }
40
+
41
+ function hooks(over: Partial<ReconcileProbeHooks> = {}): ReconcileProbeHooks {
42
+ return {
43
+ verifyStepFingerprint: async (s) => (s.status === 'done' ? 'after-match' : 'before-match'),
44
+ probeExternal: async () => 'not-installed',
45
+ snapshotExists: async () => true,
46
+ ...over,
47
+ };
48
+ }
49
+ const env: ReconcileEnv = { environmentFingerprint: FP, isLiveOwner: async () => false };
50
+
51
+ // ---------- 决策测试 ----------
52
+
53
+ test('corrupt journal → quarantine + SAFE MODE', async (t) => {
54
+ const dir = tmp(t);
55
+ const store = mkStore(dir);
56
+ await store.ensureDirs();
57
+ const badId = '00000000-0000-4000-8000-0000000000c1';
58
+ await fs.writeFile(path.join(dir, 'transactions', 'active', `${badId}.json`), 'not-json{{{');
59
+ const out = await reconcileActive(store, hooks(), env);
60
+ assert.equal(out.decisions[0]!.kind, 'corrupt');
61
+ assert.equal(out.safeModeRequired, true);
62
+ assert.equal(await store.readSafeMode(), true);
63
+ // 已隔离
64
+ assert.ok((await fs.readdir(path.join(dir, 'transactions', 'quarantine'))).length > 0);
65
+ assert.deepEqual(await store.scanActive(), []);
66
+ });
67
+
68
+ test('environment 不匹配 → env-mismatch / SAFE MODE(不恢复)', async (t) => {
69
+ const dir = tmp(t);
70
+ const store = mkStore(dir);
71
+ const id = '00000000-0000-4000-8000-0000000000c2';
72
+ await store.create({ ...op(id, 'APPLYING'), environmentFingerprint: 'OTHER-MACHINE' });
73
+ const out = await reconcileActive(store, hooks(), env);
74
+ assert.equal(out.decisions[0]!.kind, 'env-mismatch');
75
+ assert.equal(out.safeModeRequired, true);
76
+ });
77
+
78
+ test('terminal 遗留 active → organized(move 到 completed,无实质 recovery)', async (t) => {
79
+ const dir = tmp(t);
80
+ const store = mkStore(dir);
81
+ const id = '00000000-0000-4000-8000-0000000000c3';
82
+ await store.create(op(id, 'CREATED'));
83
+ await store.update(id, (j) => ({ ...j, state: 'COMMITTED' }));
84
+ const out = await reconcileActive(store, hooks(), env);
85
+ assert.equal(out.decisions[0]!.kind, 'organized');
86
+ assert.equal(out.safeModeRequired, false);
87
+ assert.deepEqual(await store.scanActive(), []);
88
+ });
89
+
90
+ test('所有 step 已 done(afterFp 确认)→ recovered(resume 不重做)', async (t) => {
91
+ const dir = tmp(t);
92
+ const store = mkStore(dir);
93
+ const id = '00000000-0000-4000-8000-0000000000c4';
94
+ const j = op(id, 'APPLYING');
95
+ j.plannedSteps = ['s1'];
96
+ j.steps = { s1: fileStep('done') };
97
+ await store.create(j);
98
+ const out = await reconcileActive(store, hooks(), env);
99
+ assert.equal(out.decisions[0]!.kind, 'recovered');
100
+ assert.equal((await store.load(id))?.state, 'RECOVERED');
101
+ assert.deepEqual(await store.scanActive(), []);
102
+ });
103
+
104
+ test('外部 half-installed → needs-attention / SAFE MODE(绝不自动 cleanup)', async (t) => {
105
+ const dir = tmp(t);
106
+ const store = mkStore(dir);
107
+ const id = '00000000-0000-4000-8000-0000000000c5';
108
+ const j = op(id, 'APPLYING');
109
+ j.plannedSteps = ['p1'];
110
+ j.steps = { p1: extStep('planned') };
111
+ await store.create(j);
112
+ const out = await reconcileActive(store, { verifyStepFingerprint: async () => 'before-match', probeExternal: async () => 'half-installed', snapshotExists: async () => true }, env);
113
+ assert.equal(out.decisions[0]!.kind, 'needs-attention');
114
+ assert.equal(out.safeModeRequired, true);
115
+ });
116
+
117
+ test('no step applied(全 beforeFp)→ noop(安全丢弃)', async (t) => {
118
+ const dir = tmp(t);
119
+ const store = mkStore(dir);
120
+ const id = '00000000-0000-4000-8000-0000000000c6';
121
+ const j = op(id, 'APPLYING');
122
+ j.plannedSteps = ['s1'];
123
+ j.steps = { s1: fileStep('planned') }; // verifyStepFingerprint 默认 before-match
124
+ await store.create(j);
125
+ const out = await reconcileActive(store, hooks(), env);
126
+ assert.equal(out.decisions[0]!.kind, 'noop');
127
+ assert.equal(out.safeModeRequired, false);
128
+ });
129
+
130
+ test('live owner → live 跳过(不 reconcile/quarantine/move)', async (t) => {
131
+ const dir = tmp(t);
132
+ const store = mkStore(dir);
133
+ const id = '00000000-0000-4000-8000-0000000000c7';
134
+ const j = op(id, 'APPLYING');
135
+ await store.create(j);
136
+ const out = await reconcileActive(store, hooks(), { environmentFingerprint: FP, isLiveOwner: async (j2) => j2.operationId === id });
137
+ assert.equal(out.decisions[0]!.kind, 'live');
138
+ assert.equal(out.safeModeRequired, false);
139
+ assert.ok((await store.loadActive(id)) !== null, 'live journal 不被 move/隔离');
140
+ });
141
+
142
+ test('startup:stale lock + incomplete → RECOVERY_REQUIRED / SAFE MODE,不自动 recover', async (t) => {
143
+ const dir = tmp(t);
144
+ const store = mkStore(dir);
145
+ const id = '00000000-0000-4000-8000-0000000000c8';
146
+ const j = op(id, 'APPLYING');
147
+ j.steps = { s1: extStep('planned') };
148
+ await store.create(j);
149
+ const insp = await inspectStartup(store, { verifyStepFingerprint: async () => 'before-match', probeExternal: async () => 'half-installed', snapshotExists: async () => true }, env, {}, 'STALE_LOCK_DETECTED');
150
+ assert.equal(insp.recoveryRequired, true);
151
+ assert.equal(insp.safeModeRequired, true);
152
+ assert.equal(await store.readSafeMode(), true);
153
+ // 不能自动 recoverStaleLock:断言 active journal 仍原样(未被删除/隔离/规整)
154
+ assert.ok((await store.loadActive(id)) !== null);
155
+ });
156
+
157
+ test('executeRecovery:rollback 无用户确认 → needs-confirmation(零副作用)', async (t) => {
158
+ const dir = tmp(t);
159
+ const store = mkStore(dir);
160
+ const id = '00000000-0000-4000-8000-0000000000c9';
161
+ const j = op(id, 'APPLYING'); j.snapshotId = 'snap-1'; await store.create(j);
162
+ let rollbackRan = false;
163
+ const r = await executeRecovery(store, { operationId: id, action: 'rollback', snapshotId: 'snap-1', performRollback: async () => { rollbackRan = true; return { full: true, failed: [] }; } }, false);
164
+ assert.equal(r, 'needs-confirmation');
165
+ assert.equal(rollbackRan, false, '无确认不得执行破坏性回滚');
166
+ });
167
+
168
+ test('executeRecovery:有确认 → RECOVERING(终态由 verify 原子完成,不自行 ROLLED_BACK)', async (t) => {
169
+ const dir = tmp(t);
170
+ const store = mkStore(dir);
171
+ const id = '00000000-0000-4000-8000-0000000000ca';
172
+ const j = op(id, 'NEEDS_ATTENTION'); j.snapshotId = 'snap-1'; await store.create(j);
173
+ let rollbackRan = false;
174
+ const r = await executeRecovery(store, { operationId: id, action: 'rollback', snapshotId: 'snap-1', performRollback: async () => { rollbackRan = true; return { full: true, failed: [] }; } }, true);
175
+ assert.equal(r, 'done');
176
+ assert.equal(rollbackRan, true);
177
+ // §7.1:recovery 全程保持 RECOVERING(不引入 ROLLING_BACK);终态迁移 + verification 写入
178
+ // 由 verify 路由以单次原子 journal update 完成。此处绝不自行转 ROLLED_BACK。
179
+ assert.equal((await store.load(id))?.state, 'RECOVERING');
180
+ assert.ok((await store.loadActive(id)) !== null, 'RECOVERING journal 仍 active(待 verify)');
181
+ });
182
+
183
+ // ---------- 幂等 ----------
184
+
185
+ test('reconcile 幂等:重复运行稳定,不重复副作用', async (t) => {
186
+ const dir = tmp(t);
187
+ const store = mkStore(dir);
188
+ const id = '00000000-0000-4000-8000-0000000000cb';
189
+ const j = op(id, 'APPLYING');
190
+ j.plannedSteps = ['s1']; j.steps = { s1: fileStep('done') };
191
+ await store.create(j);
192
+ await reconcileActive(store, hooks(), env);
193
+ await reconcileActive(store, hooks(), env);
194
+ await reconcileActive(store, hooks(), env);
195
+ // recovered 后已规整;再跑无 active
196
+ assert.deepEqual(await store.scanActive(), []);
197
+ assert.equal((await store.load(id))?.state, 'RECOVERED');
198
+ });
199
+
200
+ // ---------- F20:空 steps(opaque intent journal)不得判 RECOVERED ----------
201
+
202
+ test('F20:APPLYING + 空 steps + 无 snapshot → NEEDS_ATTENTION(非 RECOVERED)', async (t) => {
203
+ const dir = tmp(t);
204
+ const store = mkStore(dir);
205
+ const id = '00000000-0000-4000-8000-0000000000cc';
206
+ const j = op(id, 'APPLYING'); // 默认 plannedSteps=[] steps={} snapshotId=null
207
+ await store.create(j);
208
+ const out = await reconcileActive(store, hooks(), env);
209
+ assert.equal(out.decisions[0]!.kind, 'needs-attention', '空 steps + APPLYING 不得判 recovered/noop');
210
+ assert.equal(out.safeModeRequired, true);
211
+ assert.equal((await store.load(id))?.state, 'NEEDS_ATTENTION');
212
+ });
213
+
214
+ test('F20:APPLYING + 空 steps + trusted snapshot → rollback-recommended', async (t) => {
215
+ const dir = tmp(t);
216
+ const store = mkStore(dir);
217
+ const id = '00000000-0000-4000-8000-0000000000cd';
218
+ const j = op(id, 'APPLYING'); j.snapshotId = 'snap-1';
219
+ await store.create(j);
220
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
221
+ assert.equal(out.decisions[0]!.kind, 'rollback-recommended', '有 trusted snapshot 应推荐回滚');
222
+ assert.equal(out.safeModeRequired, true);
223
+ });
224
+
225
+ test('F20:CREATED + 空 steps → noop(mutation 未开始,安全)', async (t) => {
226
+ const dir = tmp(t);
227
+ const store = mkStore(dir);
228
+ const id = '00000000-0000-4000-8000-0000000000ce';
229
+ const j = op(id, 'CREATED'); // 默认空 steps
230
+ await store.create(j);
231
+ const out = await reconcileActive(store, hooks(), env);
232
+ assert.equal(out.decisions[0]!.kind, 'noop', 'CREATED + 空 steps 无 mutation,安全 noop');
233
+ assert.equal(out.safeModeRequired, false);
234
+ assert.equal((await store.load(id))?.state, 'RECOVERED');
235
+ });
236
+
237
+ // ---------- §6.5 recovery hard gate ----------
238
+
239
+ test('§6.5:RECOVERING + 无 recoveryVerification → needs-attention(绝不自动 RECOVERED)', async (t) => {
240
+ const dir = tmp(t);
241
+ const store = mkStore(dir);
242
+ const id = '00000000-0000-4000-8000-0000000000d1';
243
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
244
+ await store.create(j);
245
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
246
+ assert.equal(out.decisions[0]!.kind, 'needs-attention', 'RECOVERING + 待验证绝不自动 RECOVERED');
247
+ assert.equal(out.safeModeRequired, true);
248
+ assert.equal((await store.load(id))?.state, 'NEEDS_ATTENTION');
249
+ });
250
+
251
+ test('§6.5:RECOVERING + MISMATCH verification → needs-attention(不 COMMITTED)', async (t) => {
252
+ const dir = tmp(t);
253
+ const store = mkStore(dir);
254
+ const id = '00000000-0000-4000-8000-0000000000d2';
255
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
256
+ j.recoveryVerification = { verdict: 'MISMATCH', details: ['host file 残留'], manualHints: [], at: '2026-01-01T00:00:00.000Z' };
257
+ await store.create(j);
258
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
259
+ assert.equal(out.decisions[0]!.kind, 'needs-attention', 'verdict 非 MATCH/PARTIAL_MATCH → needs-attention');
260
+ assert.equal(out.safeModeRequired, true);
261
+ assert.equal((await store.load(id))?.state, 'NEEDS_ATTENTION');
262
+ });
263
+
264
+ test('§6.5:RECOVERING + MATCH verification → 不触发门控(可 proceed)', async (t) => {
265
+ const dir = tmp(t);
266
+ const store = mkStore(dir);
267
+ const id = '00000000-0000-4000-8000-0000000000d3';
268
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
269
+ j.recoveryVerification = { verdict: 'MATCH', details: ['all match'], manualHints: [], at: '2026-01-01T00:00:00.000Z' };
270
+ j.plannedSteps = ['s1']; j.steps = { s1: fileStep('done') };
271
+ await store.create(j);
272
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
273
+ assert.notEqual(out.decisions[0]!.kind, 'needs-attention', 'MATCH verification 不触发硬门控');
274
+ assert.equal(out.decisions[0]!.kind, 'recovered', '已验证 RECOVERING 可 proceed 到 terminal');
275
+ });
276
+
277
+ test('§6.5:RECOVERING + PARTIAL_MATCH verification → 不触发门控(可 proceed)', async (t) => {
278
+ const dir = tmp(t);
279
+ const store = mkStore(dir);
280
+ const id = '00000000-0000-4000-8000-0000000000d9';
281
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
282
+ j.recoveryVerification = { verdict: 'PARTIAL_MATCH', details: ['核心匹配,凭据不可验证'], manualHints: ['凭据需人工确认'], at: '2026-01-01T00:00:00.000Z' };
283
+ j.plannedSteps = ['s1']; j.steps = { s1: fileStep('done') };
284
+ await store.create(j);
285
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
286
+ assert.notEqual(out.decisions[0]!.kind, 'needs-attention', 'PARTIAL_MATCH verification 不触发硬门控');
287
+ assert.equal(out.decisions[0]!.kind, 'recovered', 'PARTIAL_MATCH 已验证可 proceed 到 terminal');
288
+ });
289
+
290
+ test('§6.5:RECOVERING + VERIFICATION_ERROR verification → needs-attention(不 COMMITTED)', async (t) => {
291
+ const dir = tmp(t);
292
+ const store = mkStore(dir);
293
+ const id = '00000000-0000-4000-8000-0000000000da';
294
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
295
+ j.recoveryVerification = { verdict: 'VERIFICATION_ERROR', details: ['快照重验失败'], manualHints: [], at: '2026-01-01T00:00:00.000Z' };
296
+ await store.create(j);
297
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
298
+ assert.equal(out.decisions[0]!.kind, 'needs-attention', 'VERIFICATION_ERROR → needs-attention(绝不自动 RECOVERED)');
299
+ assert.equal(out.safeModeRequired, true);
300
+ assert.equal((await store.load(id))?.state, 'NEEDS_ATTENTION');
301
+ });
302
+
303
+ test('§6.5:RECOVERING + entryDone 非空 → rollback-continue(门控之前,续跑中断回滚)', async (t) => {
304
+ const dir = tmp(t);
305
+ const store = mkStore(dir);
306
+ const id = '00000000-0000-4000-8000-0000000000d4';
307
+ const j = op(id, 'RECOVERING'); j.snapshotId = 'snap-1';
308
+ j.rollback.entryDone = { 0: true };
309
+ await store.create(j);
310
+ const out = await reconcileActive(store, hooks({ snapshotExists: async () => true }), env);
311
+ assert.equal(out.decisions[0]!.kind, 'rollback-continue', 'entryDone 非空 → 续跑中断回滚(不违反 VERIFIED)');
312
+ });
313
+
314
+ // ---------- recomputeRecoveryDecision(§5.4) ----------
315
+
316
+ test('recomputeRecoveryDecision:entryDone 非空 → rollback-continue', async (t) => {
317
+ const j = op('00000000-0000-4000-8000-0000000000d5', 'NEEDS_ATTENTION');
318
+ j.rollback.entryDone = { 0: true };
319
+ const d = await recomputeRecoveryDecision(j, { snapshotExists: async () => true });
320
+ assert.equal(d, 'rollback-continue');
321
+ });
322
+
323
+ test('recomputeRecoveryDecision:snapshotId 合法且存在 → rollback-recommended', async (t) => {
324
+ const j = op('00000000-0000-4000-8000-0000000000d6', 'NEEDS_ATTENTION');
325
+ j.snapshotId = 'snap-1';
326
+ const d = await recomputeRecoveryDecision(j, { snapshotExists: async () => true });
327
+ assert.equal(d, 'rollback-recommended');
328
+ });
329
+
330
+ test('recomputeRecoveryDecision:无 entryDone / 快照缺失 → needs-attention', async (t) => {
331
+ const j = op('00000000-0000-4000-8000-0000000000d7', 'NEEDS_ATTENTION');
332
+ j.snapshotId = 'snap-1';
333
+ const d = await recomputeRecoveryDecision(j, { snapshotExists: async () => false });
334
+ assert.equal(d, 'needs-attention');
335
+ });
336
+
337
+ test('recomputeRecoveryDecision:只读,不修改 journal / 不写 SAFE MODE', async (t) => {
338
+ const dir = tmp(t);
339
+ const store = mkStore(dir);
340
+ const id = '00000000-0000-4000-8000-0000000000d8';
341
+ const j = op(id, 'NEEDS_ATTENTION'); j.snapshotId = 'snap-1';
342
+ await store.create(j);
343
+ const before = await store.load(id);
344
+ await recomputeRecoveryDecision(before!, { snapshotExists: async () => true });
345
+ const after = await store.load(id);
346
+ assert.deepEqual(after, before, 'recomputeRecoveryDecision 不得修改 journal');
347
+ assert.equal(await store.readSafeMode(), false, '不得写 SAFE MODE');
348
+ });
349
+
350
+ // ---------- executeRecovery 引擎路由(§5.2) ----------
351
+
352
+ test('executeRecovery:rollback-recommended → 调 restore executor(不调 rollback executor)', async (t) => {
353
+ const dir = tmp(t);
354
+ const store = mkStore(dir);
355
+ const id = '00000000-0000-4000-8000-0000000000d9';
356
+ const j = op(id, 'NEEDS_ATTENTION'); j.snapshotId = 'snap-1'; await store.create(j);
357
+ let restoreRan = false; let rollbackRan = false;
358
+ const r = await executeRecovery(store, {
359
+ operationId: id, action: 'rollback', snapshotId: 'snap-1', decision: 'rollback-recommended',
360
+ performRestore: async () => { restoreRan = true; return { full: true, failed: [] }; },
361
+ performRollback: async () => { rollbackRan = true; return { full: true, failed: [] }; },
362
+ }, true);
363
+ assert.equal(r, 'done');
364
+ assert.equal(restoreRan, true, 'rollback-recommended 应调 restore executor');
365
+ assert.equal(rollbackRan, false, '不得混用 rollback executor');
366
+ assert.equal((await store.load(id))?.state, 'RECOVERING', 'NEEDS_ATTENTION → RECOVERING');
367
+ });
368
+
369
+ test('executeRecovery:rollback-continue(缺省)→ 调 rollback executor', async (t) => {
370
+ const dir = tmp(t);
371
+ const store = mkStore(dir);
372
+ const id = '00000000-0000-4000-8000-0000000000da';
373
+ const j = op(id, 'NEEDS_ATTENTION'); j.snapshotId = 'snap-1'; await store.create(j);
374
+ let rollbackRan = false;
375
+ const r = await executeRecovery(store, {
376
+ operationId: id, action: 'rollback', snapshotId: 'snap-1',
377
+ performRollback: async () => { rollbackRan = true; return { full: true, failed: [] }; },
378
+ }, true);
379
+ assert.equal(r, 'done');
380
+ assert.equal(rollbackRan, true, '缺省 decision 走 rollback executor');
381
+ assert.equal((await store.load(id))?.state, 'RECOVERING');
382
+ });
383
+
384
+ test('executeRecovery:rollback 无 executor → failed(不执行)', async (t) => {
385
+ const dir = tmp(t);
386
+ const store = mkStore(dir);
387
+ const id = '00000000-0000-4000-8000-0000000000db';
388
+ const j = op(id, 'NEEDS_ATTENTION'); j.snapshotId = 'snap-1'; await store.create(j);
389
+ const r = await executeRecovery(store, { operationId: id, action: 'rollback', snapshotId: 'snap-1' }, true);
390
+ assert.equal(r, 'failed');
391
+ assert.equal((await store.load(id))?.state, 'NEEDS_ATTENTION', '无 executor 不得迁移状态');
392
+ });