@winspan/claude-forge 8.53.2 → 8.54.3

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 (390) 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/registry.d.ts.map +1 -1
  42. package/dist/skills/registry.js +13 -2
  43. package/dist/skills/registry.js.map +1 -1
  44. package/dist/skills/semantic-matcher.d.ts +2 -2
  45. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  46. package/dist/skills/semantic-matcher.js +14 -19
  47. package/dist/skills/semantic-matcher.js.map +1 -1
  48. package/dist/skills/upgrade-engine.d.ts +3 -1
  49. package/dist/skills/upgrade-engine.d.ts.map +1 -1
  50. package/dist/skills/upgrade-engine.js +25 -14
  51. package/dist/skills/upgrade-engine.js.map +1 -1
  52. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  53. package/dist/web/analytics/weekly-report.js +21 -29
  54. package/dist/web/analytics/weekly-report.js.map +1 -1
  55. package/dist/web/routes/patch.d.ts.map +1 -1
  56. package/dist/web/routes/patch.js +32 -2
  57. package/dist/web/routes/patch.js.map +1 -1
  58. package/dist/web/routes/sessions.d.ts.map +1 -1
  59. package/dist/web/routes/sessions.js +9 -7
  60. package/dist/web/routes/sessions.js.map +1 -1
  61. package/dist/web/routes/trace.d.ts.map +1 -1
  62. package/dist/web/routes/trace.js +2 -3
  63. package/dist/web/routes/trace.js.map +1 -1
  64. package/dist/web/server.d.ts.map +1 -1
  65. package/dist/web/server.js +3 -2
  66. package/dist/web/server.js.map +1 -1
  67. package/package.json +12 -2
  68. package/scripts/postinstall.cjs +21 -0
  69. package/.claude/CLAUDE.md +0 -17
  70. package/.eslintrc.js +0 -23
  71. package/.prettierrc +0 -8
  72. package/ARCHITECTURE_ISSUES.md +0 -249
  73. package/CLAUDE.md +0 -265
  74. package/CLAUDE.md.backup +0 -488
  75. package/docs/concurrent-agents.md +0 -129
  76. package/docs/design/architecture-review-20260516.md +0 -232
  77. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  78. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  79. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  80. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  81. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  82. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  83. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  84. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  85. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  86. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  87. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  88. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  89. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  90. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  91. package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
  92. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  93. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  94. package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
  95. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  96. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  97. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  98. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  99. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  100. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  101. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  102. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  103. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  104. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  105. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  106. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  107. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  108. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  109. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  110. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  111. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  112. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  113. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  114. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  115. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  116. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  117. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  118. package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
  119. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  120. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  121. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  122. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  123. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  124. package/docs/reviews/claudemd-template-sync.md +0 -54
  125. package/docs/reviews/task-title-summary.md +0 -92
  126. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  127. package/docs/reviews/tasks-filter-pagination.md +0 -80
  128. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  129. package/docs/ruflo-learning-strategy.md +0 -322
  130. package/docs/skills-deduplication-analysis.md +0 -83
  131. package/docs/skills-multiformat-support.md +0 -177
  132. package/docs/skills-third-party.md +0 -183
  133. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  134. package/forge +0 -321
  135. package/playwright.config.ts +0 -40
  136. package/scripts/demo-v2.ts +0 -91
  137. package/scripts/dev-daemon.sh +0 -232
  138. package/scripts/dev-web.ts +0 -109
  139. package/scripts/e2e-mcp-link.ts +0 -423
  140. package/scripts/e2e-methodology-quality.ts +0 -253
  141. package/scripts/e2e-routing.ts +0 -456
  142. package/scripts/e2e-user-methodology.ts +0 -326
  143. package/scripts/e2e-web-workflows.ts +0 -299
  144. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  145. package/scripts/regenerate-execution-docs.ts +0 -116
  146. package/scripts/sync-agent-skills.ts +0 -193
  147. package/scripts/test-hook.sh +0 -71
  148. package/scripts/verify-skill-loading.ts +0 -62
  149. package/src/claudemd/claudemd-generator.ts +0 -568
  150. package/src/claudemd/convention-extractor.ts +0 -69
  151. package/src/claudemd/index.ts +0 -35
  152. package/src/claudemd/persona-manager.ts +0 -88
  153. package/src/claudemd/resume-manager.ts +0 -236
  154. package/src/claudemd/tech-detector.ts +0 -220
  155. package/src/claudemd/templates/swarm-protocol.md +0 -222
  156. package/src/cli/commands/claudemd.ts +0 -84
  157. package/src/cli/commands/config.ts +0 -46
  158. package/src/cli/commands/daemon.ts +0 -310
  159. package/src/cli/commands/executions.ts +0 -115
  160. package/src/cli/commands/init.ts +0 -204
  161. package/src/cli/commands/logs.ts +0 -181
  162. package/src/cli/commands/mcp.ts +0 -242
  163. package/src/cli/commands/menu.ts +0 -357
  164. package/src/cli/commands/skills.ts +0 -328
  165. package/src/cli/commands/stats.ts +0 -73
  166. package/src/cli/commands/status.ts +0 -69
  167. package/src/cli/commands/template.ts +0 -77
  168. package/src/cli/commands/trace.ts +0 -148
  169. package/src/cli/index.ts +0 -42
  170. package/src/cli/init/hook-manager.ts +0 -132
  171. package/src/core/ai/provider.ts +0 -308
  172. package/src/core/ai/types.ts +0 -51
  173. package/src/core/config.ts +0 -124
  174. package/src/core/constants.ts +0 -67
  175. package/src/core/event-fields.ts +0 -32
  176. package/src/core/queue/index.ts +0 -192
  177. package/src/core/storage/base.ts +0 -302
  178. package/src/core/storage/events.ts +0 -434
  179. package/src/core/storage/injections.ts +0 -78
  180. package/src/core/storage/maintenance.ts +0 -59
  181. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  182. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  183. package/src/core/storage/performance-indexes.sql +0 -23
  184. package/src/core/storage/routing.ts +0 -322
  185. package/src/core/storage/rows.ts +0 -112
  186. package/src/core/storage/schema.sql +0 -224
  187. package/src/core/storage/sessions.ts +0 -168
  188. package/src/core/storage/skills.ts +0 -233
  189. package/src/core/storage/sqlite.ts +0 -293
  190. package/src/core/storage/tasks.ts +0 -318
  191. package/src/core/storage/token-usage.ts +0 -93
  192. package/src/core/types.ts +0 -181
  193. package/src/core/utils/error-handler.ts +0 -257
  194. package/src/core/utils/forge-resume-block.ts +0 -74
  195. package/src/core/utils/format.ts +0 -69
  196. package/src/core/utils/git.ts +0 -23
  197. package/src/core/utils/logger.ts +0 -134
  198. package/src/core/utils/lru-cache.ts +0 -54
  199. package/src/core/utils/path.ts +0 -19
  200. package/src/core/utils/session.ts +0 -26
  201. package/src/core/utils/time.ts +0 -37
  202. package/src/core/utils/token-tracker.ts +0 -97
  203. package/src/daemon/event-parser.ts +0 -36
  204. package/src/daemon/handlers/history-exporter.ts +0 -117
  205. package/src/daemon/handlers/post-tool-use.ts +0 -54
  206. package/src/daemon/handlers/stop.ts +0 -208
  207. package/src/daemon/handlers/user-prompt.ts +0 -178
  208. package/src/daemon/hook-sync.ts +0 -91
  209. package/src/daemon/index.ts +0 -312
  210. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  211. package/src/daemon/launchd-installer.ts +0 -260
  212. package/src/daemon/lifecycle.ts +0 -128
  213. package/src/daemon/router.ts +0 -40
  214. package/src/daemon/server.ts +0 -196
  215. package/src/daemon/services/task-segmenter.ts +0 -112
  216. package/src/daemon/skill-sync.ts +0 -88
  217. package/src/hooks/hook-lib.sh +0 -118
  218. package/src/hooks/notification.sh +0 -35
  219. package/src/hooks/post-tool-use.sh +0 -61
  220. package/src/hooks/pre-tool-use.sh +0 -63
  221. package/src/hooks/stop.sh +0 -43
  222. package/src/hooks/user-prompt-submit.sh +0 -69
  223. package/src/mcp/server.ts +0 -322
  224. package/src/skills/index.ts +0 -2
  225. package/src/skills/invocation-guard.ts +0 -177
  226. package/src/skills/matcher.ts +0 -148
  227. package/src/skills/official/code-simplifier.md +0 -52
  228. package/src/skills/official/find-skills.md +0 -142
  229. package/src/skills/official/official-api-design.md +0 -30
  230. package/src/skills/official/official-architecture-decision.md +0 -41
  231. package/src/skills/official/official-bmad.md +0 -118
  232. package/src/skills/official/official-db-schema-design.md +0 -34
  233. package/src/skills/official/official-debug.md +0 -25
  234. package/src/skills/official/official-doc-driven.md +0 -31
  235. package/src/skills/official/official-harness-engineering.md +0 -108
  236. package/src/skills/official/official-performance-optimization.md +0 -30
  237. package/src/skills/official/official-pr-review.md +0 -35
  238. package/src/skills/official/official-release-checklist.md +0 -30
  239. package/src/skills/official/official-security-hardening.md +0 -32
  240. package/src/skills/official/official-spec-driven-design.md +0 -31
  241. package/src/skills/official/planning-with-files.md +0 -241
  242. package/src/skills/official/ui-ux-pro-max.md +0 -105
  243. package/src/skills/official/webapp-testing.md +0 -96
  244. package/src/skills/official-skills.ts +0 -89
  245. package/src/skills/registry.ts +0 -355
  246. package/src/skills/semantic-matcher.ts +0 -234
  247. package/src/skills/tools/pipeline-suggest.ts +0 -226
  248. package/src/skills/tools/skill-invoke.ts +0 -168
  249. package/src/skills/tools/skill-list.ts +0 -59
  250. package/src/skills/upgrade-engine.ts +0 -541
  251. package/src/skills/upgrade-prompt.ts +0 -84
  252. package/src/templates/go.yaml +0 -53
  253. package/src/templates/python.yaml +0 -59
  254. package/src/templates/react.yaml +0 -55
  255. package/src/templates/template-manager.ts +0 -170
  256. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  257. package/src/web/analytics/drift-detector.ts +0 -219
  258. package/src/web/analytics/weekly-report.ts +0 -431
  259. package/src/web/auth-middleware.ts +0 -54
  260. package/src/web/routes/_helpers.ts +0 -34
  261. package/src/web/routes/ai.ts +0 -204
  262. package/src/web/routes/auth.ts +0 -22
  263. package/src/web/routes/drift.ts +0 -25
  264. package/src/web/routes/error-handler.ts +0 -120
  265. package/src/web/routes/events.ts +0 -47
  266. package/src/web/routes/insights.ts +0 -43
  267. package/src/web/routes/patch.ts +0 -117
  268. package/src/web/routes/reports.ts +0 -34
  269. package/src/web/routes/rules.ts +0 -76
  270. package/src/web/routes/sessions.ts +0 -250
  271. package/src/web/routes/skill-stats.ts +0 -92
  272. package/src/web/routes/skills.ts +0 -350
  273. package/src/web/routes/static.ts +0 -67
  274. package/src/web/routes/stats.ts +0 -50
  275. package/src/web/routes/status.ts +0 -30
  276. package/src/web/routes/tasks.ts +0 -193
  277. package/src/web/routes/token-usage.ts +0 -20
  278. package/src/web/routes/trace.ts +0 -126
  279. package/src/web/routes/types.ts +0 -57
  280. package/src/web/server.ts +0 -134
  281. package/src/web/ssrf-guard.ts +0 -112
  282. package/src/web/static/index.html +0 -3251
  283. package/src/web/static/vendor/chart.umd.min.js +0 -20
  284. package/tests/e2e/dashboard.spec.ts +0 -205
  285. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  286. package/tests/helpers/mock-ai.ts +0 -92
  287. package/tests/helpers/mock-storage.ts +0 -159
  288. package/tests/integration/claudemd-generator.test.ts +0 -90
  289. package/tests/integration/queue-replay.integration.test.ts +0 -193
  290. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  291. package/tests/integration/web-analytics.integration.test.ts +0 -133
  292. package/tests/integration/web-stats.integration.test.ts +0 -135
  293. package/tests/integration/web-trace.integration.test.ts +0 -175
  294. package/tests/performance/database.benchmark.ts +0 -161
  295. package/tests/semantic-matcher.test.ts +0 -99
  296. package/tests/skill-matcher.test.ts +0 -110
  297. package/tests/unit/ai-provider-retry.test.ts +0 -194
  298. package/tests/unit/ai-provider-vision.test.ts +0 -224
  299. package/tests/unit/claudemd-generator.test.ts +0 -68
  300. package/tests/unit/cli-mcp.test.ts +0 -141
  301. package/tests/unit/core/forge-paths.test.ts +0 -99
  302. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  303. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  304. package/tests/unit/daemon/skill-sync.test.ts +0 -75
  305. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  306. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  307. package/tests/unit/event-fields.test.ts +0 -88
  308. package/tests/unit/event-parser.test.ts +0 -55
  309. package/tests/unit/handlers.test.ts +0 -171
  310. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  311. package/tests/unit/invocation-guard.test.ts +0 -125
  312. package/tests/unit/queue.test.ts +0 -272
  313. package/tests/unit/router.test.ts +0 -138
  314. package/tests/unit/security.test.ts +0 -128
  315. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  316. package/tests/unit/skill-registry.test.ts +0 -94
  317. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  318. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  319. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  320. package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
  321. package/tests/unit/skills/upgrade-engine.test.ts +0 -401
  322. package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
  323. package/tests/unit/socket-server.test.ts +0 -183
  324. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  325. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  326. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  327. package/tests/unit/storage/routing.test.ts +0 -117
  328. package/tests/unit/storage/schema-missing.test.ts +0 -81
  329. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  330. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  331. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  332. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  333. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  334. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  335. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  336. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  337. package/tests/unit/storage.test.ts +0 -172
  338. package/tests/unit/token-usage.test.ts +0 -144
  339. package/tests/unit/type-guards.test.ts +0 -201
  340. package/tests/unit/utils/format.test.ts +0 -189
  341. package/tests/unit/utils/session.test.ts +0 -89
  342. package/tests/unit/utils/time.test.ts +0 -112
  343. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  344. package/tests/unit/web/routes-auth.test.ts +0 -93
  345. package/tests/unit/web/routes-events.test.ts +0 -101
  346. package/tests/unit/web/routes-rules.test.ts +0 -182
  347. package/tests/unit/web/routes-sessions.test.ts +0 -181
  348. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  349. package/tests/unit/web/routes-stats.test.ts +0 -92
  350. package/tests/unit/web/routes-tasks.test.ts +0 -385
  351. package/tests/unit/web/task-title-contract.test.ts +0 -210
  352. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  353. package/tsconfig.json +0 -22
  354. package/vitest.config.ts +0 -21
  355. package/vitest.integration.config.ts +0 -16
  356. package/web/CLAUDE.md +0 -20
  357. package/web/index.html +0 -13
  358. package/web/package-lock.json +0 -4854
  359. package/web/package.json +0 -35
  360. package/web/postcss.config.js +0 -6
  361. package/web/src/App.tsx +0 -110
  362. package/web/src/components/CodeBlock.tsx +0 -31
  363. package/web/src/components/Confirm.tsx +0 -96
  364. package/web/src/components/Drawer.tsx +0 -60
  365. package/web/src/components/Layout.tsx +0 -145
  366. package/web/src/components/MarkdownRenderer.tsx +0 -77
  367. package/web/src/components/SearchInput.tsx +0 -31
  368. package/web/src/components/SessionDetailContent.tsx +0 -157
  369. package/web/src/components/Toast.tsx +0 -92
  370. package/web/src/index.css +0 -19
  371. package/web/src/main.tsx +0 -31
  372. package/web/src/pages/AIConfig.tsx +0 -233
  373. package/web/src/pages/Dashboard.tsx +0 -572
  374. package/web/src/pages/Events.tsx +0 -271
  375. package/web/src/pages/Reports.tsx +0 -428
  376. package/web/src/pages/SessionDetail.tsx +0 -162
  377. package/web/src/pages/Sessions.tsx +0 -205
  378. package/web/src/pages/Skills.tsx +0 -180
  379. package/web/src/pages/TaskDetail.tsx +0 -515
  380. package/web/src/pages/Tasks.tsx +0 -415
  381. package/web/src/utils/auth.ts +0 -59
  382. package/web/src/utils/export.ts +0 -54
  383. package/web/src/utils/navigation.ts +0 -25
  384. package/web/src/utils/task-title.ts +0 -49
  385. package/web/src/utils/time.ts +0 -13
  386. package/web/tailwind.config.js +0 -11
  387. package/web/tsconfig.json +0 -21
  388. package/web/tsconfig.node.json +0 -10
  389. package/web/vite.config.ts +0 -76
  390. 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
- }