@winspan/claude-forge 8.53.2 → 8.54.4

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 (394) hide show
  1. package/DEVELOPMENT.md +290 -221
  2. package/README.md +50 -8
  3. package/dist/cli/commands/skills.d.ts.map +1 -1
  4. package/dist/cli/commands/skills.js +7 -3
  5. package/dist/cli/commands/skills.js.map +1 -1
  6. package/dist/cli/init/hook-manager.d.ts +1 -1
  7. package/dist/cli/init/hook-manager.d.ts.map +1 -1
  8. package/dist/cli/init/hook-manager.js +1 -0
  9. package/dist/cli/init/hook-manager.js.map +1 -1
  10. package/dist/core/storage/events.d.ts.map +1 -1
  11. package/dist/core/storage/events.js +0 -1
  12. package/dist/core/storage/events.js.map +1 -1
  13. package/dist/core/storage/maintenance.d.ts +25 -3
  14. package/dist/core/storage/maintenance.d.ts.map +1 -1
  15. package/dist/core/storage/maintenance.js +33 -4
  16. package/dist/core/storage/maintenance.js.map +1 -1
  17. package/dist/core/storage/routing.d.ts +4 -0
  18. package/dist/core/storage/routing.d.ts.map +1 -1
  19. package/dist/core/storage/routing.js +10 -4
  20. package/dist/core/storage/routing.js.map +1 -1
  21. package/dist/core/storage/sessions.d.ts +17 -0
  22. package/dist/core/storage/sessions.d.ts.map +1 -1
  23. package/dist/core/storage/sessions.js +64 -0
  24. package/dist/core/storage/sessions.js.map +1 -1
  25. package/dist/core/storage/skills.d.ts +4 -0
  26. package/dist/core/storage/skills.d.ts.map +1 -1
  27. package/dist/core/storage/skills.js +10 -2
  28. package/dist/core/storage/skills.js.map +1 -1
  29. package/dist/core/storage/sqlite.d.ts +5 -0
  30. package/dist/core/storage/sqlite.d.ts.map +1 -1
  31. package/dist/core/storage/sqlite.js +6 -0
  32. package/dist/core/storage/sqlite.js.map +1 -1
  33. package/dist/core/storage/tasks.d.ts.map +1 -1
  34. package/dist/core/storage/tasks.js +2 -0
  35. package/dist/core/storage/tasks.js.map +1 -1
  36. package/dist/core/types.d.ts +7 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/daemon/index.d.ts.map +1 -1
  39. package/dist/daemon/index.js +19 -4
  40. package/dist/daemon/index.js.map +1 -1
  41. package/dist/skills/official/official-openspec.md +89 -0
  42. package/dist/skills/official-skills.d.ts.map +1 -1
  43. package/dist/skills/official-skills.js +1 -0
  44. package/dist/skills/official-skills.js.map +1 -1
  45. package/dist/skills/registry.d.ts.map +1 -1
  46. package/dist/skills/registry.js +13 -2
  47. package/dist/skills/registry.js.map +1 -1
  48. package/dist/skills/semantic-matcher.d.ts +2 -2
  49. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  50. package/dist/skills/semantic-matcher.js +14 -19
  51. package/dist/skills/semantic-matcher.js.map +1 -1
  52. package/dist/skills/upgrade-engine.d.ts +3 -1
  53. package/dist/skills/upgrade-engine.d.ts.map +1 -1
  54. package/dist/skills/upgrade-engine.js +25 -14
  55. package/dist/skills/upgrade-engine.js.map +1 -1
  56. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  57. package/dist/web/analytics/weekly-report.js +21 -29
  58. package/dist/web/analytics/weekly-report.js.map +1 -1
  59. package/dist/web/routes/patch.d.ts.map +1 -1
  60. package/dist/web/routes/patch.js +32 -2
  61. package/dist/web/routes/patch.js.map +1 -1
  62. package/dist/web/routes/sessions.d.ts.map +1 -1
  63. package/dist/web/routes/sessions.js +9 -7
  64. package/dist/web/routes/sessions.js.map +1 -1
  65. package/dist/web/routes/trace.d.ts.map +1 -1
  66. package/dist/web/routes/trace.js +2 -3
  67. package/dist/web/routes/trace.js.map +1 -1
  68. package/dist/web/server.d.ts.map +1 -1
  69. package/dist/web/server.js +3 -2
  70. package/dist/web/server.js.map +1 -1
  71. package/package.json +12 -2
  72. package/scripts/postinstall.cjs +21 -0
  73. package/.claude/CLAUDE.md +0 -17
  74. package/.eslintrc.js +0 -23
  75. package/.prettierrc +0 -8
  76. package/ARCHITECTURE_ISSUES.md +0 -249
  77. package/CLAUDE.md +0 -265
  78. package/CLAUDE.md.backup +0 -488
  79. package/docs/concurrent-agents.md +0 -129
  80. package/docs/design/architecture-review-20260516.md +0 -232
  81. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  82. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  83. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  84. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  85. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  86. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  87. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  88. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  89. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  90. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  91. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  92. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  93. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  94. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  95. package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
  96. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  97. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  98. package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
  99. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  100. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  101. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  102. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  103. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  104. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  105. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  106. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  107. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  108. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  109. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  110. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  111. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  112. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  113. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  114. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  115. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  116. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  117. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  118. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  119. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  120. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  121. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  122. package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
  123. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  124. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  125. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  126. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  127. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  128. package/docs/reviews/claudemd-template-sync.md +0 -54
  129. package/docs/reviews/task-title-summary.md +0 -92
  130. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  131. package/docs/reviews/tasks-filter-pagination.md +0 -80
  132. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  133. package/docs/ruflo-learning-strategy.md +0 -322
  134. package/docs/skills-deduplication-analysis.md +0 -83
  135. package/docs/skills-multiformat-support.md +0 -177
  136. package/docs/skills-third-party.md +0 -183
  137. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  138. package/forge +0 -321
  139. package/playwright.config.ts +0 -40
  140. package/scripts/demo-v2.ts +0 -91
  141. package/scripts/dev-daemon.sh +0 -232
  142. package/scripts/dev-web.ts +0 -109
  143. package/scripts/e2e-mcp-link.ts +0 -423
  144. package/scripts/e2e-methodology-quality.ts +0 -253
  145. package/scripts/e2e-routing.ts +0 -456
  146. package/scripts/e2e-user-methodology.ts +0 -326
  147. package/scripts/e2e-web-workflows.ts +0 -299
  148. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  149. package/scripts/regenerate-execution-docs.ts +0 -116
  150. package/scripts/sync-agent-skills.ts +0 -193
  151. package/scripts/test-hook.sh +0 -71
  152. package/scripts/verify-skill-loading.ts +0 -62
  153. package/src/claudemd/claudemd-generator.ts +0 -568
  154. package/src/claudemd/convention-extractor.ts +0 -69
  155. package/src/claudemd/index.ts +0 -35
  156. package/src/claudemd/persona-manager.ts +0 -88
  157. package/src/claudemd/resume-manager.ts +0 -236
  158. package/src/claudemd/tech-detector.ts +0 -220
  159. package/src/claudemd/templates/swarm-protocol.md +0 -222
  160. package/src/cli/commands/claudemd.ts +0 -84
  161. package/src/cli/commands/config.ts +0 -46
  162. package/src/cli/commands/daemon.ts +0 -310
  163. package/src/cli/commands/executions.ts +0 -115
  164. package/src/cli/commands/init.ts +0 -204
  165. package/src/cli/commands/logs.ts +0 -181
  166. package/src/cli/commands/mcp.ts +0 -242
  167. package/src/cli/commands/menu.ts +0 -357
  168. package/src/cli/commands/skills.ts +0 -328
  169. package/src/cli/commands/stats.ts +0 -73
  170. package/src/cli/commands/status.ts +0 -69
  171. package/src/cli/commands/template.ts +0 -77
  172. package/src/cli/commands/trace.ts +0 -148
  173. package/src/cli/index.ts +0 -42
  174. package/src/cli/init/hook-manager.ts +0 -132
  175. package/src/core/ai/provider.ts +0 -308
  176. package/src/core/ai/types.ts +0 -51
  177. package/src/core/config.ts +0 -124
  178. package/src/core/constants.ts +0 -67
  179. package/src/core/event-fields.ts +0 -32
  180. package/src/core/queue/index.ts +0 -192
  181. package/src/core/storage/base.ts +0 -302
  182. package/src/core/storage/events.ts +0 -434
  183. package/src/core/storage/injections.ts +0 -78
  184. package/src/core/storage/maintenance.ts +0 -59
  185. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  186. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  187. package/src/core/storage/performance-indexes.sql +0 -23
  188. package/src/core/storage/routing.ts +0 -322
  189. package/src/core/storage/rows.ts +0 -112
  190. package/src/core/storage/schema.sql +0 -224
  191. package/src/core/storage/sessions.ts +0 -168
  192. package/src/core/storage/skills.ts +0 -233
  193. package/src/core/storage/sqlite.ts +0 -293
  194. package/src/core/storage/tasks.ts +0 -318
  195. package/src/core/storage/token-usage.ts +0 -93
  196. package/src/core/types.ts +0 -181
  197. package/src/core/utils/error-handler.ts +0 -257
  198. package/src/core/utils/forge-resume-block.ts +0 -74
  199. package/src/core/utils/format.ts +0 -69
  200. package/src/core/utils/git.ts +0 -23
  201. package/src/core/utils/logger.ts +0 -134
  202. package/src/core/utils/lru-cache.ts +0 -54
  203. package/src/core/utils/path.ts +0 -19
  204. package/src/core/utils/session.ts +0 -26
  205. package/src/core/utils/time.ts +0 -37
  206. package/src/core/utils/token-tracker.ts +0 -97
  207. package/src/daemon/event-parser.ts +0 -36
  208. package/src/daemon/handlers/history-exporter.ts +0 -117
  209. package/src/daemon/handlers/post-tool-use.ts +0 -54
  210. package/src/daemon/handlers/stop.ts +0 -208
  211. package/src/daemon/handlers/user-prompt.ts +0 -178
  212. package/src/daemon/hook-sync.ts +0 -91
  213. package/src/daemon/index.ts +0 -312
  214. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  215. package/src/daemon/launchd-installer.ts +0 -260
  216. package/src/daemon/lifecycle.ts +0 -128
  217. package/src/daemon/router.ts +0 -40
  218. package/src/daemon/server.ts +0 -196
  219. package/src/daemon/services/task-segmenter.ts +0 -112
  220. package/src/daemon/skill-sync.ts +0 -88
  221. package/src/hooks/hook-lib.sh +0 -118
  222. package/src/hooks/notification.sh +0 -35
  223. package/src/hooks/post-tool-use.sh +0 -61
  224. package/src/hooks/pre-tool-use.sh +0 -63
  225. package/src/hooks/stop.sh +0 -43
  226. package/src/hooks/user-prompt-submit.sh +0 -69
  227. package/src/mcp/server.ts +0 -322
  228. package/src/skills/index.ts +0 -2
  229. package/src/skills/invocation-guard.ts +0 -177
  230. package/src/skills/matcher.ts +0 -148
  231. package/src/skills/official/code-simplifier.md +0 -52
  232. package/src/skills/official/find-skills.md +0 -142
  233. package/src/skills/official/official-api-design.md +0 -30
  234. package/src/skills/official/official-architecture-decision.md +0 -41
  235. package/src/skills/official/official-bmad.md +0 -118
  236. package/src/skills/official/official-db-schema-design.md +0 -34
  237. package/src/skills/official/official-debug.md +0 -25
  238. package/src/skills/official/official-doc-driven.md +0 -31
  239. package/src/skills/official/official-harness-engineering.md +0 -108
  240. package/src/skills/official/official-performance-optimization.md +0 -30
  241. package/src/skills/official/official-pr-review.md +0 -35
  242. package/src/skills/official/official-release-checklist.md +0 -30
  243. package/src/skills/official/official-security-hardening.md +0 -32
  244. package/src/skills/official/official-spec-driven-design.md +0 -31
  245. package/src/skills/official/planning-with-files.md +0 -241
  246. package/src/skills/official/ui-ux-pro-max.md +0 -105
  247. package/src/skills/official/webapp-testing.md +0 -96
  248. package/src/skills/official-skills.ts +0 -89
  249. package/src/skills/registry.ts +0 -355
  250. package/src/skills/semantic-matcher.ts +0 -234
  251. package/src/skills/tools/pipeline-suggest.ts +0 -226
  252. package/src/skills/tools/skill-invoke.ts +0 -168
  253. package/src/skills/tools/skill-list.ts +0 -59
  254. package/src/skills/upgrade-engine.ts +0 -541
  255. package/src/skills/upgrade-prompt.ts +0 -84
  256. package/src/templates/go.yaml +0 -53
  257. package/src/templates/python.yaml +0 -59
  258. package/src/templates/react.yaml +0 -55
  259. package/src/templates/template-manager.ts +0 -170
  260. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  261. package/src/web/analytics/drift-detector.ts +0 -219
  262. package/src/web/analytics/weekly-report.ts +0 -431
  263. package/src/web/auth-middleware.ts +0 -54
  264. package/src/web/routes/_helpers.ts +0 -34
  265. package/src/web/routes/ai.ts +0 -204
  266. package/src/web/routes/auth.ts +0 -22
  267. package/src/web/routes/drift.ts +0 -25
  268. package/src/web/routes/error-handler.ts +0 -120
  269. package/src/web/routes/events.ts +0 -47
  270. package/src/web/routes/insights.ts +0 -43
  271. package/src/web/routes/patch.ts +0 -117
  272. package/src/web/routes/reports.ts +0 -34
  273. package/src/web/routes/rules.ts +0 -76
  274. package/src/web/routes/sessions.ts +0 -250
  275. package/src/web/routes/skill-stats.ts +0 -92
  276. package/src/web/routes/skills.ts +0 -350
  277. package/src/web/routes/static.ts +0 -67
  278. package/src/web/routes/stats.ts +0 -50
  279. package/src/web/routes/status.ts +0 -30
  280. package/src/web/routes/tasks.ts +0 -193
  281. package/src/web/routes/token-usage.ts +0 -20
  282. package/src/web/routes/trace.ts +0 -126
  283. package/src/web/routes/types.ts +0 -57
  284. package/src/web/server.ts +0 -134
  285. package/src/web/ssrf-guard.ts +0 -112
  286. package/src/web/static/index.html +0 -3251
  287. package/src/web/static/vendor/chart.umd.min.js +0 -20
  288. package/tests/e2e/dashboard.spec.ts +0 -205
  289. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  290. package/tests/helpers/mock-ai.ts +0 -92
  291. package/tests/helpers/mock-storage.ts +0 -159
  292. package/tests/integration/claudemd-generator.test.ts +0 -90
  293. package/tests/integration/queue-replay.integration.test.ts +0 -193
  294. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  295. package/tests/integration/web-analytics.integration.test.ts +0 -133
  296. package/tests/integration/web-stats.integration.test.ts +0 -135
  297. package/tests/integration/web-trace.integration.test.ts +0 -175
  298. package/tests/performance/database.benchmark.ts +0 -161
  299. package/tests/semantic-matcher.test.ts +0 -99
  300. package/tests/skill-matcher.test.ts +0 -110
  301. package/tests/unit/ai-provider-retry.test.ts +0 -194
  302. package/tests/unit/ai-provider-vision.test.ts +0 -224
  303. package/tests/unit/claudemd-generator.test.ts +0 -68
  304. package/tests/unit/cli-mcp.test.ts +0 -141
  305. package/tests/unit/core/forge-paths.test.ts +0 -99
  306. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  307. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  308. package/tests/unit/daemon/skill-sync.test.ts +0 -75
  309. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  310. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  311. package/tests/unit/event-fields.test.ts +0 -88
  312. package/tests/unit/event-parser.test.ts +0 -55
  313. package/tests/unit/handlers.test.ts +0 -171
  314. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  315. package/tests/unit/invocation-guard.test.ts +0 -125
  316. package/tests/unit/queue.test.ts +0 -272
  317. package/tests/unit/router.test.ts +0 -138
  318. package/tests/unit/security.test.ts +0 -128
  319. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  320. package/tests/unit/skill-registry.test.ts +0 -94
  321. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  322. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  323. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  324. package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
  325. package/tests/unit/skills/upgrade-engine.test.ts +0 -401
  326. package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
  327. package/tests/unit/socket-server.test.ts +0 -183
  328. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  329. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  330. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  331. package/tests/unit/storage/routing.test.ts +0 -117
  332. package/tests/unit/storage/schema-missing.test.ts +0 -81
  333. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  334. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  335. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  336. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  337. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  338. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  339. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  340. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  341. package/tests/unit/storage.test.ts +0 -172
  342. package/tests/unit/token-usage.test.ts +0 -144
  343. package/tests/unit/type-guards.test.ts +0 -201
  344. package/tests/unit/utils/format.test.ts +0 -189
  345. package/tests/unit/utils/session.test.ts +0 -89
  346. package/tests/unit/utils/time.test.ts +0 -112
  347. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  348. package/tests/unit/web/routes-auth.test.ts +0 -93
  349. package/tests/unit/web/routes-events.test.ts +0 -101
  350. package/tests/unit/web/routes-rules.test.ts +0 -182
  351. package/tests/unit/web/routes-sessions.test.ts +0 -181
  352. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  353. package/tests/unit/web/routes-stats.test.ts +0 -92
  354. package/tests/unit/web/routes-tasks.test.ts +0 -385
  355. package/tests/unit/web/task-title-contract.test.ts +0 -210
  356. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  357. package/tsconfig.json +0 -22
  358. package/vitest.config.ts +0 -21
  359. package/vitest.integration.config.ts +0 -16
  360. package/web/CLAUDE.md +0 -20
  361. package/web/index.html +0 -13
  362. package/web/package-lock.json +0 -4854
  363. package/web/package.json +0 -35
  364. package/web/postcss.config.js +0 -6
  365. package/web/src/App.tsx +0 -110
  366. package/web/src/components/CodeBlock.tsx +0 -31
  367. package/web/src/components/Confirm.tsx +0 -96
  368. package/web/src/components/Drawer.tsx +0 -60
  369. package/web/src/components/Layout.tsx +0 -145
  370. package/web/src/components/MarkdownRenderer.tsx +0 -77
  371. package/web/src/components/SearchInput.tsx +0 -31
  372. package/web/src/components/SessionDetailContent.tsx +0 -157
  373. package/web/src/components/Toast.tsx +0 -92
  374. package/web/src/index.css +0 -19
  375. package/web/src/main.tsx +0 -31
  376. package/web/src/pages/AIConfig.tsx +0 -233
  377. package/web/src/pages/Dashboard.tsx +0 -572
  378. package/web/src/pages/Events.tsx +0 -271
  379. package/web/src/pages/Reports.tsx +0 -428
  380. package/web/src/pages/SessionDetail.tsx +0 -162
  381. package/web/src/pages/Sessions.tsx +0 -205
  382. package/web/src/pages/Skills.tsx +0 -180
  383. package/web/src/pages/TaskDetail.tsx +0 -515
  384. package/web/src/pages/Tasks.tsx +0 -415
  385. package/web/src/utils/auth.ts +0 -59
  386. package/web/src/utils/export.ts +0 -54
  387. package/web/src/utils/navigation.ts +0 -25
  388. package/web/src/utils/task-title.ts +0 -49
  389. package/web/src/utils/time.ts +0 -13
  390. package/web/tailwind.config.js +0 -11
  391. package/web/tsconfig.json +0 -21
  392. package/web/tsconfig.node.json +0 -10
  393. package/web/vite.config.ts +0 -76
  394. package/winspan-claude-forge-8.43.0.tgz +0 -0
@@ -1,260 +0,0 @@
1
- /**
2
- * macOS LaunchAgent integration for the Forge daemon.
3
- *
4
- * Renders the plist template, installs/uninstalls the LaunchAgent via launchctl,
5
- * and reports status. Intended to give the daemon supervisor-grade auto-restart
6
- * behavior on darwin without depending on systemd.
7
- */
8
-
9
- import fs from 'fs';
10
- import os from 'os';
11
- import path from 'path';
12
- import { execSync, execFileSync } from 'child_process';
13
- import { fileURLToPath } from 'url';
14
-
15
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
-
17
- export const LAUNCHD_LABEL = 'com.claude-forge.daemon';
18
- export const LAUNCH_AGENTS_DIR = path.join(os.homedir(), 'Library', 'LaunchAgents');
19
- export const PLIST_PATH = path.join(LAUNCH_AGENTS_DIR, `${LAUNCHD_LABEL}.plist`);
20
-
21
- export const TEMPLATE_PATH = path.resolve(
22
- __dirname,
23
- 'launchd',
24
- 'com.claude-forge.daemon.plist.template'
25
- );
26
-
27
- export function ensureDarwin(): void {
28
- if (process.platform !== 'darwin') {
29
- throw new Error(
30
- `LaunchAgent commands are only supported on macOS (current platform: ${process.platform}). ` +
31
- `Use './scripts/dev-daemon.sh' or 'cf daemon start' on other platforms.`
32
- );
33
- }
34
- }
35
-
36
- export function getUid(): number {
37
- if (typeof process.getuid === 'function') {
38
- return process.getuid();
39
- }
40
- // Fallback: shell out (process.getuid is undefined on Windows but always defined on darwin).
41
- return parseInt(execSync('id -u', { encoding: 'utf-8' }).trim(), 10);
42
- }
43
-
44
- export interface RenderOptions {
45
- /**
46
- * If true, points ProgramArguments at the local development build
47
- * (`<repo>/dist/daemon/index.js` resolved from this module's location).
48
- * Otherwise, resolves the daemon entry from the globally-installed package.
49
- */
50
- dev?: boolean;
51
- /**
52
- * Optional explicit path to the daemon entrypoint.
53
- * Takes precedence over `dev`.
54
- */
55
- daemonPath?: string;
56
- /**
57
- * Optional explicit node path. Defaults to `process.execPath`.
58
- */
59
- nodePath?: string;
60
- }
61
-
62
- /**
63
- * Resolve the path to `dist/daemon/index.js`.
64
- *
65
- * - In dev mode: resolves relative to this module (works in both src and dist).
66
- * - In production mode: prefers a globally-installed `claude-forge` binary,
67
- * falling back to the path resolved from this module.
68
- */
69
- export function resolveDaemonPath(opts: RenderOptions = {}): string {
70
- if (opts.daemonPath) {
71
- if (!fs.existsSync(opts.daemonPath)) {
72
- throw new Error(`Provided daemon path does not exist: ${opts.daemonPath}`);
73
- }
74
- return opts.daemonPath;
75
- }
76
-
77
- // From `dist/daemon/launchd-installer.js` -> `dist/daemon/index.js`
78
- // From `src/daemon/launchd-installer.ts` -> would resolve to src path; but we only ship dist.
79
- const localGuess = path.resolve(__dirname, 'index.js');
80
-
81
- if (opts.dev) {
82
- if (!fs.existsSync(localGuess)) {
83
- throw new Error(
84
- `Local daemon entry not found at ${localGuess}. Run 'npm run build' first.`
85
- );
86
- }
87
- return localGuess;
88
- }
89
-
90
- // Production mode: try to find global install via `which claude-forge`.
91
- try {
92
- const cliPath = execSync('command -v claude-forge', { encoding: 'utf-8' }).trim();
93
- if (cliPath) {
94
- const resolved = fs.realpathSync(cliPath);
95
- // The CLI lives at <pkg>/dist/cli/index.js; daemon at <pkg>/dist/daemon/index.js.
96
- const pkgRoot = path.resolve(path.dirname(resolved), '..');
97
- const candidate = path.join(pkgRoot, 'daemon', 'index.js');
98
- if (fs.existsSync(candidate)) {
99
- return candidate;
100
- }
101
- }
102
- } catch {
103
- // Ignore — fall through to local guess.
104
- }
105
-
106
- if (fs.existsSync(localGuess)) {
107
- return localGuess;
108
- }
109
-
110
- throw new Error(
111
- `Could not locate daemon entry. Install claude-forge globally (npm i -g @winspan/claude-forge) ` +
112
- `or run with --dev after 'npm run build'.`
113
- );
114
- }
115
-
116
- export function renderPlist(opts: RenderOptions = {}): string {
117
- if (!fs.existsSync(TEMPLATE_PATH)) {
118
- throw new Error(`plist template missing at ${TEMPLATE_PATH}. Did you run 'npm run build'?`);
119
- }
120
- const template = fs.readFileSync(TEMPLATE_PATH, 'utf-8');
121
- const nodePath = opts.nodePath ?? process.execPath;
122
- const daemonPath = resolveDaemonPath(opts);
123
- const home = os.homedir();
124
-
125
- return template
126
- .replace(/{{NODE_PATH}}/g, nodePath)
127
- .replace(/{{DAEMON_PATH}}/g, daemonPath)
128
- .replace(/{{HOME}}/g, home);
129
- }
130
-
131
- function runLaunchctl(args: string[]): { stdout: string; stderr: string; code: number } {
132
- try {
133
- const stdout = execFileSync('launchctl', args, {
134
- encoding: 'utf-8',
135
- stdio: ['ignore', 'pipe', 'pipe'],
136
- });
137
- return { stdout, stderr: '', code: 0 };
138
- } catch (err) {
139
- const e = err as NodeJS.ErrnoException & { stdout?: Buffer; stderr?: Buffer; status?: number };
140
- return {
141
- stdout: e.stdout?.toString() ?? '',
142
- stderr: e.stderr?.toString() ?? e.message ?? '',
143
- code: e.status ?? 1,
144
- };
145
- }
146
- }
147
-
148
- export interface InstallResult {
149
- plistPath: string;
150
- nodePath: string;
151
- daemonPath: string;
152
- reloaded: boolean;
153
- }
154
-
155
- export function install(opts: RenderOptions = {}): InstallResult {
156
- ensureDarwin();
157
- fs.mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });
158
-
159
- const uid = getUid();
160
- const target = `gui/${uid}/${LAUNCHD_LABEL}`;
161
-
162
- // Idempotent: bootout existing service before re-installing.
163
- let reloaded = false;
164
- if (fs.existsSync(PLIST_PATH)) {
165
- const r = runLaunchctl(['bootout', `gui/${uid}`, PLIST_PATH]);
166
- if (r.code === 0 || /No such process|Could not find specified service/.test(r.stderr)) {
167
- reloaded = true;
168
- } else {
169
- // Non-fatal; proceed and let bootstrap surface real errors.
170
- reloaded = true;
171
- }
172
- }
173
-
174
- const rendered = renderPlist(opts);
175
- fs.writeFileSync(PLIST_PATH, rendered, { mode: 0o644 });
176
-
177
- const bootstrap = runLaunchctl(['bootstrap', `gui/${uid}`, PLIST_PATH]);
178
- if (bootstrap.code !== 0) {
179
- throw new Error(
180
- `launchctl bootstrap failed (exit ${bootstrap.code}): ${bootstrap.stderr.trim() || bootstrap.stdout.trim()}\n` +
181
- `Service target: ${target}`
182
- );
183
- }
184
-
185
- // Best-effort kickstart so the daemon comes up immediately.
186
- runLaunchctl(['kickstart', '-k', target]);
187
-
188
- // Read back rendered values to report.
189
- const nodePath = opts.nodePath ?? process.execPath;
190
- const daemonPath = resolveDaemonPath(opts);
191
-
192
- return { plistPath: PLIST_PATH, nodePath, daemonPath, reloaded };
193
- }
194
-
195
- export interface UninstallResult {
196
- removedPlist: boolean;
197
- bootedOut: boolean;
198
- }
199
-
200
- export function uninstall(): UninstallResult {
201
- ensureDarwin();
202
- const uid = getUid();
203
-
204
- let bootedOut = false;
205
- if (fs.existsSync(PLIST_PATH)) {
206
- const r = runLaunchctl(['bootout', `gui/${uid}`, PLIST_PATH]);
207
- bootedOut =
208
- r.code === 0 || /No such process|Could not find specified service/.test(r.stderr);
209
- }
210
-
211
- let removedPlist = false;
212
- if (fs.existsSync(PLIST_PATH)) {
213
- fs.unlinkSync(PLIST_PATH);
214
- removedPlist = true;
215
- }
216
-
217
- return { bootedOut, removedPlist };
218
- }
219
-
220
- export interface LaunchdStatus {
221
- installed: boolean;
222
- loaded: boolean;
223
- pid: number | null;
224
- lastExitCode: number | null;
225
- state: string | null;
226
- raw: string;
227
- }
228
-
229
- export function status(): LaunchdStatus {
230
- ensureDarwin();
231
- const uid = getUid();
232
- const target = `gui/${uid}/${LAUNCHD_LABEL}`;
233
- const installed = fs.existsSync(PLIST_PATH);
234
-
235
- const r = runLaunchctl(['print', target]);
236
- if (r.code !== 0) {
237
- return {
238
- installed,
239
- loaded: false,
240
- pid: null,
241
- lastExitCode: null,
242
- state: null,
243
- raw: r.stderr.trim() || r.stdout.trim(),
244
- };
245
- }
246
-
247
- const out = r.stdout;
248
- const pidMatch = out.match(/^\s*pid\s*=\s*(\d+)/m);
249
- const stateMatch = out.match(/^\s*state\s*=\s*([\w-]+)/m);
250
- const exitMatch = out.match(/^\s*last exit code\s*=\s*(-?\d+)/mi);
251
-
252
- return {
253
- installed,
254
- loaded: true,
255
- pid: pidMatch ? parseInt(pidMatch[1], 10) : null,
256
- state: stateMatch ? stateMatch[1] : null,
257
- lastExitCode: exitMatch ? parseInt(exitMatch[1], 10) : null,
258
- raw: out,
259
- };
260
- }
@@ -1,128 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import crypto from 'crypto';
4
- import { execSync, execFileSync } from 'child_process';
5
- import { logger } from '../core/utils/logger.js';
6
- import { FORGE_PATHS } from '../core/constants.js';
7
-
8
- const FORGE_HOME = FORGE_PATHS.home();
9
- const TOKEN_FILE = FORGE_PATHS.daemonToken();
10
- const PID_FILE = FORGE_PATHS.daemonPid();
11
-
12
- export function writePidFile(): void {
13
- if (!fs.existsSync(FORGE_HOME)) {
14
- fs.mkdirSync(FORGE_HOME, { recursive: true });
15
- }
16
- fs.writeFileSync(PID_FILE, String(process.pid));
17
- }
18
-
19
- export function removePidFile(): void {
20
- if (fs.existsSync(PID_FILE)) {
21
- fs.unlinkSync(PID_FILE);
22
- }
23
- }
24
-
25
- export function readPid(): number | null {
26
- if (!fs.existsSync(PID_FILE)) {
27
- return null;
28
- }
29
-
30
- try {
31
- const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);
32
-
33
- // 检查进程是否存活
34
- try {
35
- process.kill(pid, 0);
36
- return pid;
37
- } catch {
38
- // 进程不存在,清理过期 PID 文件
39
- removePidFile();
40
- return null;
41
- }
42
- } catch {
43
- return null;
44
- }
45
- }
46
-
47
- export function isRunning(): boolean {
48
- return readPid() !== null;
49
- }
50
-
51
- export function getSocketPath(): string {
52
- return FORGE_PATHS.daemonSocket();
53
- }
54
-
55
- export function cleanSocket(): void {
56
- const socketPath = getSocketPath();
57
- if (fs.existsSync(socketPath)) {
58
- fs.unlinkSync(socketPath);
59
- }
60
- }
61
-
62
- /** daemon 启动时生成随机 auth token,写入 chmod 600 文件 */
63
- export function writeAuthToken(): string {
64
- const token = crypto.randomBytes(32).toString('hex');
65
- if (!fs.existsSync(FORGE_HOME)) {
66
- fs.mkdirSync(FORGE_HOME, { recursive: true });
67
- }
68
- fs.writeFileSync(TOKEN_FILE, token, { mode: 0o600 });
69
- return token;
70
- }
71
-
72
- /** 读取当前 auth token */
73
- export function readAuthToken(): string | null {
74
- if (!fs.existsSync(TOKEN_FILE)) return null;
75
- try {
76
- return fs.readFileSync(TOKEN_FILE, 'utf-8').trim();
77
- } catch {
78
- return null;
79
- }
80
- }
81
-
82
- /** daemon 关闭时清理 token 文件 */
83
- export function removeAuthToken(): void {
84
- if (fs.existsSync(TOKEN_FILE)) {
85
- fs.unlinkSync(TOKEN_FILE);
86
- }
87
- }
88
-
89
- /**
90
- * Sync MCP registration token after daemon restart.
91
- *
92
- * Problem: Each daemon start generates a new token, but the MCP registration
93
- * in Claude Code still has the old token → 401 → "Failed to connect".
94
- *
95
- * Solution: After writing the new token, re-register the MCP server with
96
- * the updated Bearer header. This is a best-effort operation — if Claude CLI
97
- * is not installed or registration fails, we log a warning and continue.
98
- */
99
- export function syncMcpToken(token: string, port: number): void {
100
- try {
101
- execSync('command -v claude', { stdio: 'ignore' });
102
- } catch {
103
- return; // Claude CLI not installed, skip
104
- }
105
-
106
- try {
107
- const checkOutput = execSync('claude mcp get claude-forge', {
108
- stdio: ['ignore', 'pipe', 'ignore'],
109
- encoding: 'utf-8',
110
- });
111
- if (!checkOutput.includes('claude-forge')) return; // Not registered
112
- } catch {
113
- return; // Not registered, skip
114
- }
115
-
116
- const mcpUrl = `http://127.0.0.1:${port}/mcp`;
117
- try {
118
- execFileSync('claude', ['mcp', 'remove', 'claude-forge', '-s', 'user'], { stdio: 'ignore' });
119
- execFileSync('claude', [
120
- 'mcp', 'add', '--transport', 'http', '-s', 'user',
121
- 'claude-forge', mcpUrl,
122
- '-H', `Authorization: Bearer ${token}`,
123
- ], { stdio: 'ignore' });
124
- logger.info('[Lifecycle] MCP token synced successfully');
125
- } catch (err) {
126
- logger.warn(`[Lifecycle] MCP token sync failed (non-fatal): ${err instanceof Error ? err.message : err}`);
127
- }
128
- }
@@ -1,40 +0,0 @@
1
- import type { ForgeEvent, HookResult } from '../core/types.js';
2
- import { isUserPromptSubmit, isPostToolUse, isStop } from '../core/types.js';
3
- import type { UserPromptHandler } from './handlers/user-prompt.js';
4
- import type { PostToolUseHandler } from './handlers/post-tool-use.js';
5
- import type { StopHandler } from './handlers/stop.js';
6
- import { logger } from '../core/utils/logger.js';
7
- import { formatError } from '../core/utils/format.js';
8
- import { truncateSessionId } from '../core/utils/session.js';
9
-
10
- export interface Handlers {
11
- UserPromptSubmit: UserPromptHandler;
12
- PostToolUse: PostToolUseHandler;
13
- Stop: StopHandler;
14
- }
15
-
16
- /**
17
- * Dispatch an event to the matching handler with hook-type narrowing.
18
- *
19
- * The narrow union (PreToolUseEvent / PostToolUseEvent / ...) is enforced at
20
- * the handler entry, not at storage / wire level — so handlers can rely on
21
- * required fields without `!` assertions.
22
- *
23
- * Error boundary: catches and logs handler errors to prevent daemon crashes.
24
- */
25
- export async function routeEvent(event: ForgeEvent, handlers: Handlers): Promise<HookResult | void> {
26
- try {
27
- if (isUserPromptSubmit(event)) return await handlers.UserPromptSubmit.handle(event);
28
- if (isPostToolUse(event)) return await handlers.PostToolUse.handle(event);
29
- if (isStop(event)) return await handlers.Stop.handle(event);
30
- // PreToolUse / Notification: handled inline in daemon/index.ts (no dedicated handler).
31
- } catch (err) {
32
- logger.error(
33
- `[Router] Handler error for ${event.hook_type}: ${formatError(err)} ` +
34
- `(session: ${truncateSessionId(event.session_id)})`
35
- );
36
-
37
- // Return safe default to prevent daemon crash
38
- return { allow: true };
39
- }
40
- }
@@ -1,196 +0,0 @@
1
- import net from 'net';
2
- import fs from 'fs';
3
- import { EventParser } from './event-parser.js';
4
- import { logger } from '../core/utils/logger.js';
5
- import { formatError, truncateString } from '../core/utils/format.js';
6
-
7
- export interface HookResponse {
8
- allow: boolean;
9
- reason?: string;
10
- additionalContext?: string;
11
- systemMessage?: string;
12
- hookSpecificOutput?: {
13
- hookEventName: string;
14
- permissionDecision: 'allow' | 'deny' | 'ask' | 'defer';
15
- permissionDecisionReason?: string;
16
- };
17
- }
18
-
19
- export type EventHandler = (
20
- event: import('../core/types.js').ForgeEvent
21
- ) => HookResponse | void | Promise<HookResponse | void>;
22
-
23
- export class SocketServer {
24
- private server: net.Server;
25
- private parser: EventParser;
26
- private handler: EventHandler;
27
- private authToken: string | null;
28
- private static readonly MAX_BUFFER_SIZE = 512 * 1024; // 512KB
29
-
30
- constructor(socketPath: string, handler: EventHandler, authToken?: string) {
31
- this.parser = new EventParser();
32
- this.handler = handler;
33
- this.authToken = authToken ?? null;
34
-
35
- this.server = net.createServer(this.handleConnection.bind(this));
36
- this.server.listen(socketPath, () => {
37
- // 限制 socket 文件仅当前用户可读写,防止同机其他用户注入伪造事件
38
- try {
39
- fs.chmodSync(socketPath, 0o600);
40
- } catch (err) {
41
- logger.warn(`[Socket] 设置权限失败:${formatError(err)}`);
42
- }
43
- logger.info(`Socket 服务器已监听:${socketPath}`);
44
- });
45
-
46
- this.server.on('error', (err) => {
47
- logger.error(`Socket 服务器错误:${formatError(err)}`);
48
- });
49
- }
50
-
51
- private handleConnection(socket: net.Socket): void {
52
- let buffer = '';
53
- let connectionTimeout: NodeJS.Timeout | null = null;
54
-
55
- // 设置 10 秒超时,防止连接挂起
56
- connectionTimeout = setTimeout(() => {
57
- if (buffer.length > 0) {
58
- logger.warn(`连接超时,数据不完整(${buffer.length} 字节)`);
59
- }
60
- socket.destroy();
61
- }, 10000);
62
-
63
- socket.on('data', async (chunk) => {
64
- buffer += chunk.toString();
65
-
66
- // 主动检查 buffer 大小,防止内存溢出
67
- if (buffer.length > SocketServer.MAX_BUFFER_SIZE) {
68
- logger.warn(`事件过大(${buffer.length} 字节),关闭连接`);
69
- buffer = '';
70
- if (connectionTimeout) clearTimeout(connectionTimeout);
71
- socket.destroy();
72
- return;
73
- }
74
-
75
- // 性能修复(L3):hook 端用 `echo` 发送 JSON,结尾自带换行符。
76
- // 只在 buffer 中遇到换行符时才尝试解析,避免大事件(接近 512KB)
77
- // 在分片到达时每个 chunk 都跑一次完整 JSON.parse 造成的 N² 行为。
78
- // 兼容:如果数据无换行(旧客户端、其他来源),等到 socket 关闭时
79
- // 由 'end' 事件做最后一次解析尝试。
80
- const newlineIdx = buffer.indexOf('\n');
81
- if (newlineIdx === -1) {
82
- // 尚未收到完整消息,继续等待
83
- return;
84
- }
85
-
86
- // 取换行前的完整消息;忽略行后可能存在的多余数据(hook 单连接一事件)
87
- const message = buffer.slice(0, newlineIdx);
88
- buffer = '';
89
-
90
- try {
91
- await this.processMessage(socket, message);
92
- } finally {
93
- if (connectionTimeout) clearTimeout(connectionTimeout);
94
- }
95
- });
96
-
97
- // 兜底:连接关闭时若 buffer 仍有内容(无换行结尾的旧客户端),尝试解析一次
98
- socket.on('end', async () => {
99
- const remaining = buffer.trim();
100
- buffer = '';
101
- if (remaining.length === 0) return;
102
- try {
103
- await this.processMessage(socket, remaining);
104
- } catch {
105
- // processMessage 内部已记录错误
106
- } finally {
107
- if (connectionTimeout) clearTimeout(connectionTimeout);
108
- }
109
- });
110
-
111
- socket.on('error', (err) => {
112
- logger.debug(`Socket 连接错误:${formatError(err)}`);
113
- if (connectionTimeout) clearTimeout(connectionTimeout);
114
- });
115
-
116
- socket.on('close', () => {
117
- if (connectionTimeout) clearTimeout(connectionTimeout);
118
- });
119
- }
120
-
121
- /**
122
- * 解析单条完整消息(一次性,无 N² 行为)并执行 handler。
123
- *
124
- * 注意:调用方需确保 message 是一条完整的 JSON 文本(已根据换行符切分)。
125
- */
126
- private async processMessage(socket: net.Socket, message: string): Promise<void> {
127
- try {
128
- // 认证检查:在 EventParser 之前提取 _auth 字段(zod 会剥离未知字段)
129
- if (this.authToken) {
130
- try {
131
- const raw = JSON.parse(message);
132
- if (raw._auth !== this.authToken) {
133
- logger.warn('[Socket] 认证失败,关闭连接');
134
- socket.destroy();
135
- return;
136
- }
137
- } catch {
138
- // JSON 解析失败,交给下面的 EventParser 处理
139
- }
140
- }
141
-
142
- const event = this.parser.parse(message);
143
- const result = await this.handler(event);
144
-
145
- // 双向通信:如果 handler 返回了结果,写回给 hook 脚本
146
- if (result) {
147
- // 清理无意义内容,避免 Claude Code 注入干扰上下文
148
- const cleaned = { ...result } as Record<string, unknown>;
149
- if (cleaned['additionalContext'] && !this.isValidContent(cleaned['additionalContext'] as string)) {
150
- delete cleaned['additionalContext'];
151
- }
152
- if (cleaned['systemMessage'] && !this.isValidContent(cleaned['systemMessage'] as string)) {
153
- delete cleaned['systemMessage'];
154
- }
155
-
156
- const payload = JSON.stringify(cleaned);
157
- socket.write(payload, () => socket.end());
158
- } else {
159
- socket.end();
160
- }
161
- } catch (err) {
162
- logger.error(`事件解析失败:${formatError(err)}`);
163
- logger.debug(`无效消息内容:${truncateString(message, 500)}`);
164
- socket.destroy();
165
- }
166
- }
167
-
168
- /**
169
- * 判断内容是否有意义,过滤空字符串、纯标点/符号等
170
- */
171
- private isValidContent(content: string): boolean {
172
- if (!content) return false;
173
- const trimmed = content.trim();
174
- if (trimmed.length === 0) return false;
175
-
176
- const stripped = trimmed
177
- .split('\n')
178
- .map(line => line.replace(/^>\s*/, '').trim())
179
- .join('\n')
180
- .trim();
181
-
182
- if (stripped.length === 0) return false;
183
- if (!/[\p{L}\p{N}]/u.test(stripped)) return false;
184
-
185
- return true;
186
- }
187
-
188
- close(): Promise<void> {
189
- return new Promise((resolve) => {
190
- this.server.close(() => {
191
- logger.info('Socket 服务器已关闭');
192
- resolve();
193
- });
194
- });
195
- }
196
- }