evolcore 0.0.1 → 0.0.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 (379) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION-0.5.0.md +378 -0
  4. package/README.md +318 -14
  5. package/ROLE_ACCESS_CONTROL.md +174 -0
  6. package/assets/.env.template +4 -0
  7. package/bin/ec-safe-output.js +161 -0
  8. package/bin/ec.js +29 -0
  9. package/dist/agents/baseagent.js +163 -0
  10. package/dist/agents/claude-runner.js +2565 -0
  11. package/dist/agents/codex-app-server-client.js +448 -0
  12. package/dist/agents/codex-runner.js +2682 -0
  13. package/dist/agents/gemini-runner.js +666 -0
  14. package/dist/agents/runner-types.js +75 -0
  15. package/dist/aun/aid/agentmd.js +216 -0
  16. package/dist/aun/aid/client.js +132 -0
  17. package/dist/aun/aid/control-aid.js +91 -0
  18. package/dist/aun/aid/identity.js +518 -0
  19. package/dist/aun/aid/index.js +4 -0
  20. package/dist/aun/aid/store.js +74 -0
  21. package/dist/aun/aid/types.js +1 -0
  22. package/dist/aun/aid/validation.js +21 -0
  23. package/dist/aun/group-identity.js +10 -0
  24. package/dist/aun/msg/group-index.js +6 -0
  25. package/dist/aun/msg/group.js +1231 -0
  26. package/dist/aun/msg/history.js +123 -0
  27. package/dist/aun/msg/index.js +5 -0
  28. package/dist/aun/msg/p2p.js +393 -0
  29. package/dist/aun/msg/payload-type.js +27 -0
  30. package/dist/aun/msg/upload.js +137 -0
  31. package/dist/aun/outbox.js +168 -0
  32. package/dist/aun/rpc/caller.js +42 -0
  33. package/dist/aun/rpc/connection.js +25 -0
  34. package/dist/aun/rpc/index.js +2 -0
  35. package/dist/aun/service-proxy.js +225 -0
  36. package/dist/aun/storage/download.js +29 -0
  37. package/dist/aun/storage/index.js +3 -0
  38. package/dist/aun/storage/manage.js +10 -0
  39. package/dist/aun/storage/upload.js +68 -0
  40. package/dist/channels/aun.js +4164 -0
  41. package/dist/channels/contact-bind-code.js +134 -0
  42. package/dist/channels/daemon.js +422 -0
  43. package/dist/channels/dingtalk.js +1479 -0
  44. package/dist/channels/feishu.js +1865 -0
  45. package/dist/channels/qqbot.js +409 -0
  46. package/dist/channels/wechat.js +817 -0
  47. package/dist/channels/wecom-card.js +101 -0
  48. package/dist/channels/wecom-onboarding.js +82 -0
  49. package/dist/channels/wecom-state.js +191 -0
  50. package/dist/channels/wecom.js +1157 -0
  51. package/dist/cli/agent-command.js +642 -0
  52. package/dist/cli/agent.js +1059 -0
  53. package/dist/cli/aun-commands.js +2003 -0
  54. package/dist/cli/bench.js +1228 -0
  55. package/dist/cli/cli-argv.js +66 -0
  56. package/dist/cli/code-stats.js +329 -0
  57. package/dist/cli/command-log.js +82 -0
  58. package/dist/cli/config-selector.js +69 -0
  59. package/dist/cli/config.js +261 -0
  60. package/dist/cli/contact.js +71 -0
  61. package/dist/cli/ctl-command.js +62 -0
  62. package/dist/cli/daemon-commands.js +2750 -0
  63. package/dist/cli/fs-command.js +1447 -0
  64. package/dist/cli/handoff-command.js +302 -0
  65. package/dist/cli/help.js +35 -0
  66. package/dist/cli/index.js +374 -0
  67. package/dist/cli/init-channel.js +1372 -0
  68. package/dist/cli/init.js +590 -0
  69. package/dist/cli/link-rules.js +240 -0
  70. package/dist/cli/model.js +591 -0
  71. package/dist/cli/net-check.js +723 -0
  72. package/dist/cli/queue-command.js +150 -0
  73. package/dist/cli/raw-key-input.js +25 -0
  74. package/dist/cli/response.js +344 -0
  75. package/dist/cli/restart-monitor.js +480 -0
  76. package/dist/cli/stats.js +609 -0
  77. package/dist/cli/task-context.js +80 -0
  78. package/dist/cli/trigger-command.js +545 -0
  79. package/dist/cli/version.js +93 -0
  80. package/dist/cli/watch-logs.js +33 -0
  81. package/dist/cli/watch-msg.js +673 -0
  82. package/dist/config/boot-log.js +266 -0
  83. package/dist/config/builtin-role-templates.js +42 -0
  84. package/dist/config/builtin-roles.js +91 -0
  85. package/dist/config/config-batch-get.js +11 -0
  86. package/dist/config/config-field-policy.js +261 -0
  87. package/dist/config/config-manager.js +1120 -0
  88. package/dist/config/config-operation-service.js +384 -0
  89. package/dist/config/contact-alias.js +68 -0
  90. package/dist/config/contact-book-store.js +454 -0
  91. package/dist/config/contact-book-v2-startup.js +35 -0
  92. package/dist/config/contact-book.js +224 -0
  93. package/dist/config/contact-operation-service.js +110 -0
  94. package/dist/config/gateway-config.js +858 -0
  95. package/dist/config/lifecycle.js +17 -0
  96. package/dist/config/mention-mode.js +27 -0
  97. package/dist/config/merge.js +161 -0
  98. package/dist/config/owner-policy.js +4 -0
  99. package/dist/config/peer-role-resolver.js +218 -0
  100. package/dist/config/resolved-config-op.js +483 -0
  101. package/dist/config/role-config-v4-startup.js +32 -0
  102. package/dist/config/role-config-v5-startup.js +27 -0
  103. package/dist/config/role-ranks.js +18 -0
  104. package/dist/config/role-schema.js +105 -0
  105. package/dist/config/role-service.js +156 -0
  106. package/dist/config/role-store.js +215 -0
  107. package/dist/config/roles.js +64 -0
  108. package/dist/config/schema-registry.js +154 -0
  109. package/dist/config/snapshot.js +598 -0
  110. package/dist/config-store.js +501 -0
  111. package/dist/core/auth/agent-delegation.js +111 -0
  112. package/dist/core/auth/auth-gateway.js +166 -0
  113. package/dist/core/auth/authenticated-actor.js +6 -0
  114. package/dist/core/auth/authorization-audit.js +119 -0
  115. package/dist/core/auth/operation-authorizer.js +720 -0
  116. package/dist/core/auth/operation-catalog.js +731 -0
  117. package/dist/core/baseagent-loader.js +54 -0
  118. package/dist/core/bootstrap-service.js +175 -0
  119. package/dist/core/capability/capability-manager.js +316 -0
  120. package/dist/core/capability/providers/claude-capability-provider.js +176 -0
  121. package/dist/core/capability/providers/codex-capability-provider.js +148 -0
  122. package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
  123. package/dist/core/capability/types.js +27 -0
  124. package/dist/core/causation/audit.js +103 -0
  125. package/dist/core/causation/aun-association.js +111 -0
  126. package/dist/core/causation/context.js +93 -0
  127. package/dist/core/causation/index.js +4 -0
  128. package/dist/core/causation/types.js +2 -0
  129. package/dist/core/channel-loader.js +277 -0
  130. package/dist/core/command/agent-control.js +616 -0
  131. package/dist/core/command/cli-intent-parser.js +225 -0
  132. package/dist/core/command/command-handler.js +1733 -0
  133. package/dist/core/command/connect-menu.js +374 -0
  134. package/dist/core/command/menu-handler.js +3452 -0
  135. package/dist/core/command/menu-protocol.js +247 -0
  136. package/dist/core/command/role-menu.js +1623 -0
  137. package/dist/core/command/slash-gate.js +148 -0
  138. package/dist/core/command/slash-handler.js +3066 -0
  139. package/dist/core/daemon-file-cache.js +222 -0
  140. package/dist/core/event-bus.js +32 -0
  141. package/dist/core/event-catalog.js +810 -0
  142. package/dist/core/evolagent-registry.js +545 -0
  143. package/dist/core/evolagent.js +342 -0
  144. package/dist/core/handoff/dispatcher.js +229 -0
  145. package/dist/core/handoff/mutex.js +46 -0
  146. package/dist/core/handoff/runtime.js +324 -0
  147. package/dist/core/handoff/store.js +537 -0
  148. package/dist/core/handoff/types.js +12 -0
  149. package/dist/core/inference/text-inference.js +173 -0
  150. package/dist/core/interaction-registration.js +10 -0
  151. package/dist/core/interaction-router.js +278 -0
  152. package/dist/core/message/create-status.js +67 -0
  153. package/dist/core/message/im-renderer.js +659 -0
  154. package/dist/core/message/items-formatter.js +76 -0
  155. package/dist/core/message/logical-queue-bridge.js +123 -0
  156. package/dist/core/message/message-bridge.js +874 -0
  157. package/dist/core/message/message-cache.js +56 -0
  158. package/dist/core/message/message-log.js +339 -0
  159. package/dist/core/message/message-processor.js +4 -0
  160. package/dist/core/message/message-queue.js +1446 -0
  161. package/dist/core/message/message-utils.js +76 -0
  162. package/dist/core/message/peer-mode.js +105 -0
  163. package/dist/core/message/pending-hints.js +232 -0
  164. package/dist/core/message/response-depth.js +33 -0
  165. package/dist/core/message/response-engine.js +4020 -0
  166. package/dist/core/message/response-snapshot.js +83 -0
  167. package/dist/core/message/send-receipt.js +24 -0
  168. package/dist/core/message/stream-debouncer.js +139 -0
  169. package/dist/core/message/stream-idle-monitor.js +124 -0
  170. package/dist/core/model/config-scope.js +162 -0
  171. package/dist/core/model/field-scope.js +78 -0
  172. package/dist/core/model/model-catalog.js +227 -0
  173. package/dist/core/model/model-diagnostics.js +182 -0
  174. package/dist/core/model/model-permission.js +90 -0
  175. package/dist/core/permission/approval-gateway.js +1017 -0
  176. package/dist/core/permission/ec-command-parser.js +347 -0
  177. package/dist/core/permission/execution-sandbox.js +16 -0
  178. package/dist/core/permission/index.js +6 -0
  179. package/dist/core/permission/mode.js +24 -0
  180. package/dist/core/permission/sandbox-runtime.js +265 -0
  181. package/dist/core/permission/tool-policy.js +1019 -0
  182. package/dist/core/permission/unix-socket-policy.js +99 -0
  183. package/dist/core/protected-paths.js +332 -0
  184. package/dist/core/relation/peer-identity.js +222 -0
  185. package/dist/core/relation/peer-key.js +1 -0
  186. package/dist/core/role/runtime-policy.js +141 -0
  187. package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
  188. package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
  189. package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
  190. package/dist/core/session/session-file-adapter.js +7 -0
  191. package/dist/core/session/session-file-health.js +45 -0
  192. package/dist/core/session/session-fs-store.js +273 -0
  193. package/dist/core/session/session-key.js +24 -0
  194. package/dist/core/session/session-manager.js +1643 -0
  195. package/dist/core/session/session-mapper.js +100 -0
  196. package/dist/core/session/session-renew.js +314 -0
  197. package/dist/core/session/session-title.js +128 -0
  198. package/dist/core/session/session-turn-coordinator.js +205 -0
  199. package/dist/core/session/session-turns.js +67 -0
  200. package/dist/core/system-channels.js +29 -0
  201. package/dist/eck/baseagent-caps.js +18 -0
  202. package/dist/eck/detect.js +47 -0
  203. package/dist/eck/group-rules-sync.js +345 -0
  204. package/dist/eck/init.js +77 -0
  205. package/dist/eck/kit-renderer.js +359 -0
  206. package/dist/eck/manifest-engine.js +446 -0
  207. package/dist/eck/message-renderer.js +199 -0
  208. package/dist/eck/rules-loader.js +28 -0
  209. package/dist/index.js +2926 -4
  210. package/dist/ipc.js +777 -0
  211. package/dist/paths.js +262 -0
  212. package/dist/product.js +18 -0
  213. package/dist/response-system/context-builder.js +71 -0
  214. package/dist/response-system/coordinator.js +117 -0
  215. package/dist/response-system/decision-executor.js +86 -0
  216. package/dist/response-system/engines/v1/index.js +21 -0
  217. package/dist/response-system/engines/v1/interactive-flow.js +27 -0
  218. package/dist/response-system/engines/v1/proactive-flow.js +137 -0
  219. package/dist/response-system/engines/v1/types.js +1 -0
  220. package/dist/response-system/extensions.js +41 -0
  221. package/dist/response-system/index.js +6 -0
  222. package/dist/response-system/modes/index.js +7 -0
  223. package/dist/response-system/modes/single-session/index.js +72 -0
  224. package/dist/response-system/queues/fifo-queue.js +44 -0
  225. package/dist/response-system/queues/index.js +6 -0
  226. package/dist/response-system/queues/lifo-queue.js +42 -0
  227. package/dist/response-system/queues/priority-queue.js +63 -0
  228. package/dist/response-system/registry.js +97 -0
  229. package/dist/response-system/resolver.js +37 -0
  230. package/dist/response-system/selector.js +23 -0
  231. package/dist/response-system/types.js +7 -0
  232. package/dist/stats/billing.js +163 -0
  233. package/dist/stats/budget.js +93 -0
  234. package/dist/stats/db.js +403 -0
  235. package/dist/stats/eck-vars.js +89 -0
  236. package/dist/stats/index.js +11 -0
  237. package/dist/stats/normalizer.js +80 -0
  238. package/dist/stats/price-resolver.js +138 -0
  239. package/dist/stats/query.js +763 -0
  240. package/dist/stats/role-budget.js +168 -0
  241. package/dist/stats/writer.js +151 -0
  242. package/dist/trigger/anomaly-store.js +258 -0
  243. package/dist/trigger/audit.js +152 -0
  244. package/dist/trigger/event-source.js +119 -0
  245. package/dist/trigger/feedback.js +685 -0
  246. package/dist/trigger/history.js +290 -0
  247. package/dist/trigger/manager.js +294 -0
  248. package/dist/trigger/parser.js +595 -0
  249. package/dist/trigger/patch.js +155 -0
  250. package/dist/trigger/scheduler.js +1602 -0
  251. package/dist/trigger/script-executor.js +155 -0
  252. package/dist/trigger/state.js +145 -0
  253. package/dist/trigger/types.js +1 -0
  254. package/dist/trigger/validation.js +634 -0
  255. package/dist/types.js +12 -0
  256. package/dist/utils/aid-bind.js +313 -0
  257. package/dist/utils/atomic-write.js +95 -0
  258. package/dist/utils/avatar-upload.js +123 -0
  259. package/dist/utils/cross-platform.js +297 -0
  260. package/dist/utils/ecweb-utils.js +73 -0
  261. package/dist/utils/error-dict.json +153 -0
  262. package/dist/utils/error-utils.js +349 -0
  263. package/dist/utils/instance-registry.js +444 -0
  264. package/dist/utils/locale.js +21 -0
  265. package/dist/utils/log-writer.js +270 -0
  266. package/dist/utils/logger.js +89 -0
  267. package/dist/utils/markdown-to-plain-text.js +20 -0
  268. package/dist/utils/media-cache.js +274 -0
  269. package/dist/utils/model-prices.jsonl +20 -0
  270. package/dist/utils/npm-ops.js +210 -0
  271. package/dist/utils/process-introspect.js +133 -0
  272. package/dist/utils/process-tree-stats.js +271 -0
  273. package/dist/utils/project-path.js +74 -0
  274. package/dist/utils/restart-safety.js +31 -0
  275. package/dist/utils/stats.js +410 -0
  276. package/dist/utils/system-memory.js +62 -0
  277. package/dist/utils/tool-summary.js +284 -0
  278. package/dist/utils/welcome.js +268 -0
  279. package/kits/docs/GUIDE.md +20 -0
  280. package/kits/docs/INDEX.md +66 -0
  281. package/kits/docs/aun/CHEATSHEET.md +19 -0
  282. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  283. package/kits/docs/channels/aun.md +65 -0
  284. package/kits/docs/channels/feishu.md +56 -0
  285. package/kits/docs/context-assembly.md +366 -0
  286. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  287. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  288. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  289. package/kits/docs/eck_templates/runtime.template.md +19 -0
  290. package/kits/docs/evolcore/INDEX.md +68 -0
  291. package/kits/docs/evolcore/agent.md +77 -0
  292. package/kits/docs/evolcore/aid.md +52 -0
  293. package/kits/docs/evolcore/config.md +149 -0
  294. package/kits/docs/evolcore/contact.md +57 -0
  295. package/kits/docs/evolcore/ctl.md +46 -0
  296. package/kits/docs/evolcore/event.md +216 -0
  297. package/kits/docs/evolcore/fs-architecture.md +1215 -0
  298. package/kits/docs/evolcore/fs.md +110 -0
  299. package/kits/docs/evolcore/group-fs.md +17 -0
  300. package/kits/docs/evolcore/group-rules.md +226 -0
  301. package/kits/docs/evolcore/group.md +150 -0
  302. package/kits/docs/evolcore/model.md +50 -0
  303. package/kits/docs/evolcore/msg.md +136 -0
  304. package/kits/docs/evolcore/response.md +75 -0
  305. package/kits/docs/evolcore/rpc.md +37 -0
  306. package/kits/docs/evolcore/self-summary.md +29 -0
  307. package/kits/docs/evolcore/stats.md +83 -0
  308. package/kits/docs/evolcore/storage.md +50 -0
  309. package/kits/docs/evolcore/trigger.md +539 -0
  310. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
  311. package/kits/docs/identity/PATH_OPS.md +16 -0
  312. package/kits/docs/identity/ROLE_DETAIL.md +23 -0
  313. package/kits/docs/identity/identity-tools.md +26 -0
  314. package/kits/docs/path-registry.md +43 -0
  315. package/kits/docs/prompt-loading-architecture.md +266 -0
  316. package/kits/docs/venues/aun-group.md +45 -0
  317. package/kits/docs/venues/aun-private.md +10 -0
  318. package/kits/docs/venues/client-desktop.md +10 -0
  319. package/kits/docs/venues/client-mobile.md +10 -0
  320. package/kits/docs/venues/feishu-group.md +13 -0
  321. package/kits/docs/venues/feishu-private.md +9 -0
  322. package/kits/docs/venues/group.md +25 -0
  323. package/kits/docs/venues/private.md +10 -0
  324. package/kits/eck_manifest.auxiliary.json +43 -0
  325. package/kits/eck_manifest.json +203 -0
  326. package/kits/eck_message_manifest.json +63 -0
  327. package/kits/migrations/README-role-config-v4.md +32 -0
  328. package/kits/migrations/migrate-contact-book-v2.mjs +747 -0
  329. package/kits/migrations/migrate-role-config-v4.mjs +623 -0
  330. package/kits/migrations/migrate-role-config-v5.mjs +346 -0
  331. package/kits/migrations/rename-config-file.mjs +99 -0
  332. package/kits/rules/01-overview.md +142 -0
  333. package/kits/rules/02-navigation.md +76 -0
  334. package/kits/rules/03-identity.md +34 -0
  335. package/kits/rules/04-relation.md +59 -0
  336. package/kits/rules/05-venue.md +44 -0
  337. package/kits/rules/06-channel.md +59 -0
  338. package/kits/schemas/_meta.json +32 -0
  339. package/kits/schemas/agent-config.schema.1.json +177 -0
  340. package/kits/schemas/agent-config.schema.2.json +239 -0
  341. package/kits/schemas/agent-config.schema.3.json +119 -0
  342. package/kits/schemas/agent-config.schema.4.json +208 -0
  343. package/kits/schemas/agent-config.schema.5.json +326 -0
  344. package/kits/schemas/agent-config.schema.6.json +322 -0
  345. package/kits/schemas/contact-book.schema.1.json +36 -0
  346. package/kits/schemas/contact-book.schema.2.json +43 -0
  347. package/kits/schemas/daemon.schema.1.json +90 -0
  348. package/kits/schemas/defaults.schema.1.json +81 -0
  349. package/kits/schemas/menu-exec-schema-commands.md +208 -0
  350. package/kits/schemas/migrations/README.md +28 -0
  351. package/kits/schemas/relation-config.schema.1.json +158 -0
  352. package/kits/schemas/relation-config.schema.2.json +73 -0
  353. package/kits/schemas/relation-config.schema.3.json +50 -0
  354. package/kits/schemas/relation-config.schema.4.json +47 -0
  355. package/kits/schemas/relation-config.schema.5.json +47 -0
  356. package/kits/schemas/role-config.schema.1.json +201 -0
  357. package/kits/schemas/role-registry.schema.1.json +35 -0
  358. package/kits/schemas/single-session.schema.1.json +31 -0
  359. package/kits/templates/bootstrap-welcome.md +15 -0
  360. package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
  361. package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
  362. package/kits/templates/message-fragments/inject-default.md +2 -0
  363. package/kits/templates/message-fragments/item.md +2 -0
  364. package/kits/templates/roles/admin.json +9 -0
  365. package/kits/templates/roles/member.json +41 -0
  366. package/kits/templates/roles/owner.json +9 -0
  367. package/kits/templates/roles/visitor.json +40 -0
  368. package/kits/templates/system-fragments/baseagent.md +14 -0
  369. package/kits/templates/system-fragments/bootstrap.md +16 -0
  370. package/kits/templates/system-fragments/channel.md +48 -0
  371. package/kits/templates/system-fragments/commands.md +28 -0
  372. package/kits/templates/system-fragments/identity.md +11 -0
  373. package/kits/templates/system-fragments/relation.md +19 -0
  374. package/kits/templates/system-fragments/session.md +53 -0
  375. package/kits/templates/system-fragments/venue.md +31 -0
  376. package/package.json +50 -15
  377. package/dist/index.d.ts +0 -7
  378. package/dist/index.d.ts.map +0 -1
  379. package/dist/index.js.map +0 -1
@@ -0,0 +1,297 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { execFileSync, execFile, spawn, spawnSync } from 'child_process';
4
+ import { promisify } from 'util';
5
+ import fs from 'fs';
6
+ const execFileAsync = promisify(execFile);
7
+ export const isWindows = process.platform === 'win32';
8
+ const ENCODE_PATH_MAX = 200;
9
+ function encodePathHash(s) {
10
+ let h = 0;
11
+ for (let i = 0; i < s.length; i++)
12
+ h = (h << 5) - h + s.charCodeAt(i) | 0;
13
+ return Math.abs(h).toString(36);
14
+ }
15
+ /**
16
+ * Encode project path as directory name (Claude SDK convention).
17
+ * Mirrors the SDK's F0(L_(path)) logic:
18
+ * 1. path.resolve → realpath (if exists) → Unicode NFC
19
+ * 2. replace every non-alphanumeric character with '-'
20
+ * 3. truncate to 200 chars + hash suffix for long paths
21
+ * This must stay in sync with the SDK so evolcore can locate session files.
22
+ */
23
+ export function encodePath(projectPath) {
24
+ const resolved = path.resolve(projectPath);
25
+ let real = resolved;
26
+ try {
27
+ real = fs.realpathSync(resolved);
28
+ }
29
+ catch { }
30
+ const nfc = real.normalize('NFC');
31
+ const encoded = nfc.replace(/[^a-zA-Z0-9]/g, '-');
32
+ if (encoded.length <= ENCODE_PATH_MAX)
33
+ return encoded;
34
+ return `${encoded.slice(0, ENCODE_PATH_MAX)}-${encodePathHash(nfc)}`;
35
+ }
36
+ /**
37
+ * Cross-platform process liveness check.
38
+ */
39
+ export function isProcessRunning(pid) {
40
+ try {
41
+ process.kill(pid, 0);
42
+ return true;
43
+ }
44
+ catch (e) {
45
+ // ESRCH = process not found; EPERM = exists but no permission
46
+ return e.code === 'EPERM';
47
+ }
48
+ }
49
+ /**
50
+ * Cross-platform process termination.
51
+ */
52
+ export function killProcess(pid, force = false) {
53
+ if (isWindows && force) {
54
+ try {
55
+ spawnSync('taskkill', ['/PID', String(pid), '/F'], { windowsHide: true });
56
+ }
57
+ catch { }
58
+ }
59
+ else {
60
+ try {
61
+ process.kill(pid, force ? 'SIGKILL' : 'SIGTERM');
62
+ }
63
+ catch { }
64
+ }
65
+ }
66
+ /**
67
+ * Cross-platform process search by command line pattern.
68
+ * Returns list of matching PIDs.
69
+ */
70
+ export function findProcesses(pattern) {
71
+ try {
72
+ if (isWindows) {
73
+ const result = spawnSync('wmic', ['process', 'where', `CommandLine like '%${pattern}%'`, 'get', 'ProcessId'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
74
+ const output = result.stdout || '';
75
+ return output.split('\n')
76
+ .map(line => parseInt(line.trim(), 10))
77
+ .filter(pid => !isNaN(pid) && pid !== process.pid);
78
+ }
79
+ else {
80
+ const output = execFileSync('pgrep', ['-f', pattern], { encoding: 'utf-8' }).trim();
81
+ return output ? output.split('\n').map(Number).filter(pid => pid !== process.pid) : [];
82
+ }
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ /**
89
+ * Cross-platform: find PIDs listening on a TCP port.
90
+ * Used to clean up stale/orphaned listeners (e.g. manually-spawned ecweb
91
+ * that never registered a pid file) before binding the port again.
92
+ */
93
+ export function findProcessByPort(port) {
94
+ const pids = new Set();
95
+ try {
96
+ if (isWindows) {
97
+ const result = spawnSync('netstat', ['-ano', '-p', 'TCP'], { encoding: 'utf-8', windowsHide: true });
98
+ const output = result.stdout || '';
99
+ for (const line of output.split('\n')) {
100
+ // 形如: TCP 0.0.0.0:42705 0.0.0.0:0 LISTENING 23004
101
+ if (!/LISTENING/i.test(line))
102
+ continue;
103
+ const m = line.match(/:(\d+)\s+\S+\s+LISTENING\s+(\d+)/i);
104
+ if (m && Number(m[1]) === port) {
105
+ const pid = Number(m[2]);
106
+ if (pid && pid !== process.pid)
107
+ pids.add(pid);
108
+ }
109
+ }
110
+ }
111
+ else {
112
+ const output = execFileSync('lsof', ['-ti', `tcp:${port}`, '-sTCP:LISTEN'], { encoding: 'utf-8' }).trim();
113
+ for (const line of output.split('\n')) {
114
+ const pid = parseInt(line.trim(), 10);
115
+ if (pid && pid !== process.pid)
116
+ pids.add(pid);
117
+ }
118
+ }
119
+ }
120
+ catch { /* netstat/lsof missing or no match */ }
121
+ return [...pids];
122
+ }
123
+ export function getProcessInfo(pid) {
124
+ try {
125
+ if (isWindows) {
126
+ const result = spawnSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'WorkingSetSize,CreationDate'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
127
+ const output = result.stdout || '';
128
+ const lines = output.trim().split('\n').filter(l => l.trim());
129
+ if (lines.length >= 2) {
130
+ const parts = lines[1].trim().split(/\s+/);
131
+ const memKB = parts[1] ? Math.round(parseInt(parts[1], 10) / 1024) : undefined;
132
+ return { memory: memKB ? `${memKB}` : undefined };
133
+ }
134
+ }
135
+ else {
136
+ const etimes = execFileSync('ps', ['-p', String(pid), '-o', 'etimes='], { encoding: 'utf-8' }).trim();
137
+ const cpu = execFileSync('ps', ['-p', String(pid), '-o', '%cpu='], { encoding: 'utf-8' }).trim();
138
+ const mem = execFileSync('ps', ['-p', String(pid), '-o', 'rss='], { encoding: 'utf-8' }).trim();
139
+ const uptime = formatUptime(parseInt(etimes, 10));
140
+ return { uptime, cpu, memory: mem };
141
+ }
142
+ }
143
+ catch { }
144
+ return {};
145
+ }
146
+ function formatUptime(totalSeconds) {
147
+ if (isNaN(totalSeconds) || totalSeconds < 0)
148
+ return 'unknown';
149
+ const days = Math.floor(totalSeconds / 86400);
150
+ const hours = Math.floor((totalSeconds % 86400) / 3600);
151
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
152
+ const seconds = totalSeconds % 60;
153
+ const parts = [];
154
+ if (days > 0)
155
+ parts.push(`${days}d`);
156
+ if (hours > 0)
157
+ parts.push(`${hours}h`);
158
+ if (minutes > 0)
159
+ parts.push(`${minutes}m`);
160
+ if (parts.length === 0)
161
+ parts.push(`${seconds}s`);
162
+ return parts.join(' ');
163
+ }
164
+ /**
165
+ * Cross-platform command existence check.
166
+ */
167
+ const _commandExistsCache = new Map();
168
+ export function commandExists(cmd) {
169
+ const cached = _commandExistsCache.get(cmd);
170
+ if (cached !== undefined)
171
+ return cached;
172
+ let exists = false;
173
+ try {
174
+ if (isWindows) {
175
+ const r = spawnSync('where', [cmd], { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
176
+ exists = r.status === 0;
177
+ }
178
+ else {
179
+ execFileSync('which', [cmd], { encoding: 'utf-8', stdio: 'pipe' });
180
+ exists = true;
181
+ }
182
+ }
183
+ catch {
184
+ exists = false;
185
+ }
186
+ _commandExistsCache.set(cmd, exists);
187
+ return exists;
188
+ }
189
+ /**
190
+ * 解析命令的真实可执行文件绝对路径。
191
+ * Windows: `where` 会列出全部同名文件(npm 全局 bin 同时生成无后缀 sh 包装、.cmd、.ps1),
192
+ * 其中无后缀的那个是 Unix sh 脚本,Windows 无法直接 spawn(ENOENT)。
193
+ * 因此优先选 PATHEXT 可执行后缀(.cmd/.exe/.bat/.com),都没有才退回首行。
194
+ * 失败返回 null。不缓存——刚安装的命令需要重新探测。
195
+ */
196
+ export function resolveCommandPath(cmd) {
197
+ try {
198
+ if (isWindows) {
199
+ const r = spawnSync('where', [cmd], { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
200
+ if (r.status !== 0 || !r.stdout)
201
+ return null;
202
+ const candidates = r.stdout.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
203
+ if (candidates.length === 0)
204
+ return null;
205
+ const execExts = ['.cmd', '.exe', '.bat', '.com'];
206
+ const runnable = candidates.find(p => execExts.some(ext => p.toLowerCase().endsWith(ext)));
207
+ return runnable || candidates[0];
208
+ }
209
+ else {
210
+ const out = execFileSync('which', [cmd], { encoding: 'utf-8', stdio: 'pipe' }).trim();
211
+ return out || null;
212
+ }
213
+ }
214
+ catch {
215
+ return null;
216
+ }
217
+ }
218
+ /**
219
+ * Cross-platform live log tailing (replaces tail -f).
220
+ * Returns an abort function.
221
+ */
222
+ export function tailFile(filePath) {
223
+ if (!isWindows) {
224
+ // Unix: use tail -f (more efficient)
225
+ const child = spawn('tail', ['-f', filePath], { stdio: 'inherit' });
226
+ child.on('exit', (code) => process.exit(code || 0));
227
+ return { abort: () => child.kill() };
228
+ }
229
+ // Windows: Node.js-based implementation using stat polling
230
+ // (fs.watch / ReadDirectoryChangesW is unreliable for cross-process appends)
231
+ // Output last 20 lines of existing content
232
+ const content = fs.readFileSync(filePath, 'utf-8');
233
+ const lines = content.split('\n');
234
+ const lastLines = lines.slice(-20);
235
+ process.stdout.write(lastLines.join('\n'));
236
+ let position = fs.statSync(filePath).size;
237
+ const listener = () => {
238
+ try {
239
+ const stat = fs.statSync(filePath);
240
+ if (stat.size < position) {
241
+ // File was truncated (log rotation) — reset and re-read from start
242
+ position = 0;
243
+ }
244
+ if (stat.size > position) {
245
+ const fd = fs.openSync(filePath, 'r');
246
+ const buffer = Buffer.alloc(stat.size - position);
247
+ fs.readSync(fd, buffer, 0, buffer.length, position);
248
+ fs.closeSync(fd);
249
+ process.stdout.write(buffer.toString('utf-8'));
250
+ position = stat.size;
251
+ }
252
+ }
253
+ catch {
254
+ // File may be briefly unavailable during rotation — ignore and retry next tick
255
+ }
256
+ };
257
+ fs.watchFile(filePath, { interval: 500, persistent: true }, listener);
258
+ return { abort: () => fs.unwatchFile(filePath, listener) };
259
+ }
260
+ /**
261
+ * Resolve file path from import.meta.url (cross-platform safe).
262
+ * Replaces unsafe `new URL('.', import.meta.url).pathname` usage.
263
+ */
264
+ export function dirFromImportMeta(importMetaUrl) {
265
+ return path.dirname(fileURLToPath(importMetaUrl));
266
+ }
267
+ /**
268
+ * Check if current file is the main entry script (cross-platform safe).
269
+ * Replaces unsafe `import.meta.url === \`file://\${process.argv[1]}\`` check.
270
+ */
271
+ export function isMainScript(importMetaUrl) {
272
+ const argv1 = process.argv[1];
273
+ if (!argv1)
274
+ return false;
275
+ try {
276
+ const selfPath = fileURLToPath(importMetaUrl);
277
+ const argvPath = fs.realpathSync(argv1);
278
+ return selfPath === argvPath || fs.realpathSync(selfPath) === argvPath;
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ }
284
+ /**
285
+ * Register graceful shutdown signal handlers (cross-platform safe).
286
+ */
287
+ export function onShutdown(callback) {
288
+ process.on('SIGINT', callback);
289
+ // SIGTERM is not fully supported on Windows, but Node.js can still emit it
290
+ // in some scenarios (e.g., process managers), so register it anyway
291
+ process.on('SIGTERM', callback);
292
+ if (isWindows) {
293
+ // On Windows, also handle SIGHUP for graceful shutdown
294
+ // when the process is terminated via Task Manager or similar
295
+ process.on('SIGHUP', callback);
296
+ }
297
+ }
@@ -0,0 +1,73 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { resolveGlobalPkg } from './npm-ops.js';
4
+ import { isWindows as platformIsWindows, resolveCommandPath } from './cross-platform.js';
5
+ import { WEB_CLI_BIN, WEB_PACKAGE_NAME } from '../product.js';
6
+ function readEcwebPackageEntry(pkgJsonPath) {
7
+ try {
8
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
9
+ const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.[WEB_CLI_BIN];
10
+ if (!bin)
11
+ return null;
12
+ const entry = path.resolve(path.dirname(pkgJsonPath), bin);
13
+ return fs.existsSync(entry) ? entry : null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ function packageJsonBesideNpmShim(commandPath, isWindows) {
20
+ if (!isWindows || !/\.(cmd|bat)$/i.test(commandPath))
21
+ return null;
22
+ const pkgJsonPath = path.join(path.dirname(commandPath), 'node_modules', WEB_PACKAGE_NAME, 'package.json');
23
+ return fs.existsSync(pkgJsonPath) ? pkgJsonPath : null;
24
+ }
25
+ /**
26
+ * Resolve how to launch ecweb as a real background process.
27
+ *
28
+ * On Windows, npm bins are usually .cmd shims. Launching that shim through a
29
+ * shell can create a visible console window; launching the package's JS entry
30
+ * through the current Node executable avoids that wrapper entirely.
31
+ */
32
+ export function resolveEcwebLaunchCommand(ecwebArgs, opts = {}) {
33
+ const nodePath = opts.nodePath ?? process.execPath;
34
+ const installed = opts.installedPkg !== undefined ? opts.installedPkg : resolveGlobalPkg(WEB_PACKAGE_NAME);
35
+ let entry = installed?.path ? readEcwebPackageEntry(installed.path) : null;
36
+ if (entry) {
37
+ return { command: nodePath, args: [entry, ...ecwebArgs], entry, source: 'package-bin' };
38
+ }
39
+ const commandPath = opts.commandPath !== undefined ? opts.commandPath : resolveCommandPath(WEB_CLI_BIN);
40
+ if (!commandPath)
41
+ return null;
42
+ const shimPkgJson = packageJsonBesideNpmShim(commandPath, opts.isWindows ?? platformIsWindows);
43
+ entry = shimPkgJson ? readEcwebPackageEntry(shimPkgJson) : null;
44
+ if (entry) {
45
+ return { command: nodePath, args: [entry, ...ecwebArgs], entry, source: 'package-bin' };
46
+ }
47
+ // Windows: .cmd/.bat 文件需要通过 cmd.exe 执行
48
+ const isWindows = opts.isWindows ?? platformIsWindows;
49
+ if (isWindows && (commandPath.endsWith('.cmd') || commandPath.endsWith('.bat'))) {
50
+ return { command: 'cmd.exe', args: ['/c', commandPath, ...ecwebArgs], source: 'command' };
51
+ }
52
+ return { command: commandPath, args: ecwebArgs, source: 'command' };
53
+ }
54
+ /**
55
+ * ECWeb 配对码取码 helper(共享给 daemon 和 CLI)。
56
+ *
57
+ * 配对码是 ecweb 进程自己生成并持有的内部状态。daemon/CLI 通过 ecweb 的
58
+ * localhost-only HTTP 接口 GET /api/pair-code 取当前码(远程访问被 ecweb 403 拒绝)。
59
+ */
60
+ /** 经 localhost 拉取 ecweb 当前配对码(仅本机可取)。失败返回 null。 */
61
+ export async function fetchEcwebPairCode(port) {
62
+ try {
63
+ const resp = await fetch(`http://127.0.0.1:${port}/api/pair-code`, {
64
+ signal: AbortSignal.timeout(2000),
65
+ });
66
+ if (!resp.ok)
67
+ return null;
68
+ return await resp.json();
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
@@ -0,0 +1,153 @@
1
+ {
2
+ "rules": [
3
+ {
4
+ "id": "context-too-long-cn",
5
+ "match": "上下文过长",
6
+ "action": "stop",
7
+ "type": "context_too_long",
8
+ "message": "⚠️ 上下文过长,请手动输入 /compact 压缩上下文"
9
+ },
10
+ {
11
+ "id": "context-too-long-en",
12
+ "match": "context too long",
13
+ "action": "stop",
14
+ "type": "context_too_long",
15
+ "message": "⚠️ 上下文过长,请手动输入 /compact 压缩上下文"
16
+ },
17
+ {
18
+ "id": "prompt-too-long",
19
+ "match": "prompt is too long",
20
+ "action": "stop",
21
+ "type": "context_too_long",
22
+ "message": "⚠️ 输入过长,请精简提问或使用 /compact 压缩上下文"
23
+ },
24
+ {
25
+ "id": "invalid-api-key",
26
+ "match": "invalid api key",
27
+ "action": "stop",
28
+ "type": "auth_error",
29
+ "message": "❌ API Key 无效,请检查密钥配置。使用 /status 查看当前配置"
30
+ },
31
+ {
32
+ "id": "key-not-found",
33
+ "match": "key_not_found",
34
+ "action": "stop",
35
+ "type": "auth_error",
36
+ "message": "❌ API Key 未找到,请检查密钥配置"
37
+ },
38
+ {
39
+ "id": "api-key-verifier-too-many-connections",
40
+ "match": "error 1040 (08004): too many connections",
41
+ "action": "retry",
42
+ "type": "api_error",
43
+ "message": "⚠️ API 服务繁忙,请稍后重试"
44
+ },
45
+ {
46
+ "id": "credit-query-timeout",
47
+ "match": "积分查询超时",
48
+ "action": "retry",
49
+ "type": "api_error",
50
+ "message": "⚠️ 积分查询超时,请稍后重试"
51
+ },
52
+ {
53
+ "id": "capacity-pool-switching-cn",
54
+ "match": "算力池切换",
55
+ "action": "retry",
56
+ "type": "api_error",
57
+ "message": "⚠️ 当前算力池切换中,请稍后重试"
58
+ },
59
+ {
60
+ "id": "selected-model-at-capacity",
61
+ "match": "selected model is at capacity",
62
+ "action": "retry",
63
+ "type": "api_error",
64
+ "message": "⚠️ 当前模型繁忙,请稍后重试"
65
+ },
66
+ {
67
+ "id": "api-error-400",
68
+ "match": "api error: 400",
69
+ "action": "stop",
70
+ "type": "api_error",
71
+ "message": "❌ 请求格式错误,请检查输入内容"
72
+ },
73
+ {
74
+ "id": "model-not-supported-403",
75
+ "match": "api error: 403 不支持该模型",
76
+ "action": "stop",
77
+ "type": "model_unavailable",
78
+ "message": "❌ 当前模型不受支持,请正确配置模型后重试"
79
+ },
80
+ {
81
+ "id": "api-error-403",
82
+ "match": "api error: 403",
83
+ "action": "retry",
84
+ "type": "api_error",
85
+ "message": "⚠️ API 访问受限,请稍后重试"
86
+ },
87
+ {
88
+ "id": "api-error-429",
89
+ "match": "api error: 429",
90
+ "action": "retry",
91
+ "type": "api_error",
92
+ "message": "⚠️ 请求过于频繁,请稍后重试"
93
+ },
94
+ {
95
+ "id": "api-error-500",
96
+ "match": "api error: 500",
97
+ "action": "retry",
98
+ "type": "api_error",
99
+ "message": "❌ API 服务暂时不可用,请稍后重试"
100
+ },
101
+ {
102
+ "id": "api-error-502",
103
+ "match": "api error: 502",
104
+ "action": "retry",
105
+ "type": "api_error"
106
+ },
107
+ {
108
+ "id": "api-error-503",
109
+ "match": "api error: 503",
110
+ "action": "retry",
111
+ "type": "api_error"
112
+ },
113
+ {
114
+ "id": "api-error-504",
115
+ "match": "api error: 504",
116
+ "action": "retry",
117
+ "type": "api_error"
118
+ },
119
+ {
120
+ "id": "not-valid-json",
121
+ "match": "is not valid json",
122
+ "action": "retry",
123
+ "type": "api_error",
124
+ "message": "⚠️ API 返回异常响应,请稍后重试"
125
+ },
126
+ {
127
+ "id": "json-parse-error",
128
+ "match": "json parse error",
129
+ "action": "retry",
130
+ "type": "api_error",
131
+ "message": "⚠️ API 返回 JSON 解析异常,请稍后重试"
132
+ },
133
+ {
134
+ "id": "feishu-permission",
135
+ "match": "im:resource",
136
+ "action": "stop",
137
+ "type": "unknown",
138
+ "message": "❌ 权限不足,请联系管理员配置应用权限"
139
+ },
140
+ {
141
+ "id": "stream-error",
142
+ "match": "stream",
143
+ "action": "retry",
144
+ "type": "stream_error"
145
+ },
146
+ {
147
+ "id": "request-aborted",
148
+ "match": "request was aborted",
149
+ "action": "ignore",
150
+ "type": "stream_error"
151
+ }
152
+ ]
153
+ }