pi-proactive 0.2.0-beta.2 → 0.2.0-beta.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 (243) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +32 -28
  3. package/build/cli.mjs +121 -0
  4. package/build/extensions/proactive.js +47 -6
  5. package/build/src/commands.js +78 -2
  6. package/build/src/config.js +511 -4
  7. package/build/src/daemon-config.js +221 -0
  8. package/build/src/daemon-contact.js +103 -0
  9. package/build/src/daemon-host.js +84 -0
  10. package/build/src/daemon.js +193 -3
  11. package/build/src/health.js +84 -0
  12. package/build/src/lineage.js +41 -2
  13. package/build/src/notify.js +183 -0
  14. package/build/src/progress-eta.js +134 -0
  15. package/build/src/trigger.js +43 -0
  16. package/build/src/trust-loop.js +321 -0
  17. package/build/src/trust-metrics.js +236 -0
  18. package/build/src/trust-notify.js +73 -0
  19. package/build/src/trust-policy.js +58 -0
  20. package/build/src/trust-registry.js +323 -0
  21. package/daemon/launchd.plist +48 -0
  22. package/daemon/pi-proactive-daemon.service +29 -0
  23. package/daemon/run-daemon.sh +84 -0
  24. package/node_modules/typebox/build/compile/code.mjs +2 -1
  25. package/node_modules/typebox/build/compile/validator.mjs +1 -5
  26. package/node_modules/typebox/build/format/date_time.d.mts +1 -2
  27. package/node_modules/typebox/build/format/date_time.mjs +3 -12
  28. package/node_modules/typebox/build/format/duration.d.mts +2 -2
  29. package/node_modules/typebox/build/format/duration.mjs +2 -2
  30. package/node_modules/typebox/build/format/idn_email.d.mts +0 -1
  31. package/node_modules/typebox/build/format/idn_email.mjs +0 -1
  32. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
  33. package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
  34. package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
  35. package/node_modules/typebox/build/format/ipv4.mjs +2 -2
  36. package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
  37. package/node_modules/typebox/build/format/ipv6.mjs +1 -1
  38. package/node_modules/typebox/build/format/iri.mjs +9 -16
  39. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  40. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  41. package/node_modules/typebox/build/guard/emit.mjs +2 -2
  42. package/node_modules/typebox/build/guard/guard.d.mts +5 -7
  43. package/node_modules/typebox/build/guard/guard.mjs +9 -14
  44. package/node_modules/typebox/build/guard/index.mjs +0 -6
  45. package/node_modules/typebox/build/guard/{unicode/unicode_segment.mjs → string.mjs} +19 -16
  46. package/node_modules/typebox/build/schema/build.mjs +5 -5
  47. package/node_modules/typebox/build/schema/check.mjs +1 -1
  48. package/node_modules/typebox/build/schema/engine/_context.d.mts +1 -5
  49. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -16
  50. package/node_modules/typebox/build/schema/engine/_functions.d.mts +2 -2
  51. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -1
  52. package/node_modules/typebox/build/schema/engine/_reducer.d.mts +3 -3
  53. package/node_modules/typebox/build/schema/engine/_refine.d.mts +5 -5
  54. package/node_modules/typebox/build/schema/engine/_refine.mjs +9 -14
  55. package/node_modules/typebox/build/schema/engine/_stack.d.mts +20 -34
  56. package/node_modules/typebox/build/schema/engine/_stack.mjs +202 -143
  57. package/node_modules/typebox/build/schema/engine/_unique.d.mts +2 -4
  58. package/node_modules/typebox/build/schema/engine/_unique.mjs +3 -7
  59. package/node_modules/typebox/build/schema/engine/additionalItems.d.mts +4 -4
  60. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
  61. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +8 -5
  62. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +32 -27
  63. package/node_modules/typebox/build/schema/engine/allOf.d.mts +4 -4
  64. package/node_modules/typebox/build/schema/engine/allOf.mjs +1 -1
  65. package/node_modules/typebox/build/schema/engine/anyOf.d.mts +4 -4
  66. package/node_modules/typebox/build/schema/engine/anyOf.mjs +9 -4
  67. package/node_modules/typebox/build/schema/engine/boolean.d.mts +4 -4
  68. package/node_modules/typebox/build/schema/engine/boolean.mjs +6 -2
  69. package/node_modules/typebox/build/schema/engine/const.d.mts +4 -4
  70. package/node_modules/typebox/build/schema/engine/const.mjs +6 -2
  71. package/node_modules/typebox/build/schema/engine/contains.d.mts +4 -4
  72. package/node_modules/typebox/build/schema/engine/contains.mjs +9 -5
  73. package/node_modules/typebox/build/schema/engine/dependencies.d.mts +4 -4
  74. package/node_modules/typebox/build/schema/engine/dependencies.mjs +7 -5
  75. package/node_modules/typebox/build/schema/engine/dependentRequired.d.mts +4 -4
  76. package/node_modules/typebox/build/schema/engine/dependentRequired.mjs +6 -2
  77. package/node_modules/typebox/build/schema/engine/dependentSchemas.d.mts +4 -4
  78. package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +1 -2
  79. package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +4 -4
  80. package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +8 -12
  81. package/node_modules/typebox/build/schema/engine/enum.d.mts +4 -4
  82. package/node_modules/typebox/build/schema/engine/enum.mjs +6 -2
  83. package/node_modules/typebox/build/schema/engine/exclusiveMaximum.d.mts +4 -4
  84. package/node_modules/typebox/build/schema/engine/exclusiveMaximum.mjs +6 -2
  85. package/node_modules/typebox/build/schema/engine/exclusiveMinimum.d.mts +4 -4
  86. package/node_modules/typebox/build/schema/engine/exclusiveMinimum.mjs +6 -2
  87. package/node_modules/typebox/build/schema/engine/format.d.mts +4 -4
  88. package/node_modules/typebox/build/schema/engine/format.mjs +8 -11
  89. package/node_modules/typebox/build/schema/engine/if.d.mts +4 -4
  90. package/node_modules/typebox/build/schema/engine/if.mjs +13 -5
  91. package/node_modules/typebox/build/schema/engine/index.d.mts +0 -1
  92. package/node_modules/typebox/build/schema/engine/index.mjs +0 -1
  93. package/node_modules/typebox/build/schema/engine/items.d.mts +4 -4
  94. package/node_modules/typebox/build/schema/engine/items.mjs +5 -8
  95. package/node_modules/typebox/build/schema/engine/maxContains.d.mts +4 -4
  96. package/node_modules/typebox/build/schema/engine/maxContains.mjs +8 -4
  97. package/node_modules/typebox/build/schema/engine/maxItems.d.mts +4 -4
  98. package/node_modules/typebox/build/schema/engine/maxItems.mjs +6 -2
  99. package/node_modules/typebox/build/schema/engine/maxLength.d.mts +4 -4
  100. package/node_modules/typebox/build/schema/engine/maxLength.mjs +6 -2
  101. package/node_modules/typebox/build/schema/engine/maxProperties.d.mts +4 -4
  102. package/node_modules/typebox/build/schema/engine/maxProperties.mjs +6 -2
  103. package/node_modules/typebox/build/schema/engine/maximum.d.mts +4 -4
  104. package/node_modules/typebox/build/schema/engine/maximum.mjs +6 -2
  105. package/node_modules/typebox/build/schema/engine/minContains.d.mts +4 -4
  106. package/node_modules/typebox/build/schema/engine/minContains.mjs +9 -5
  107. package/node_modules/typebox/build/schema/engine/minItems.d.mts +5 -5
  108. package/node_modules/typebox/build/schema/engine/minItems.mjs +6 -2
  109. package/node_modules/typebox/build/schema/engine/minLength.d.mts +4 -4
  110. package/node_modules/typebox/build/schema/engine/minLength.mjs +6 -2
  111. package/node_modules/typebox/build/schema/engine/minProperties.d.mts +4 -4
  112. package/node_modules/typebox/build/schema/engine/minProperties.mjs +6 -2
  113. package/node_modules/typebox/build/schema/engine/minimum.d.mts +4 -4
  114. package/node_modules/typebox/build/schema/engine/minimum.mjs +6 -2
  115. package/node_modules/typebox/build/schema/engine/multipleOf.d.mts +4 -4
  116. package/node_modules/typebox/build/schema/engine/multipleOf.mjs +6 -2
  117. package/node_modules/typebox/build/schema/engine/not.d.mts +4 -4
  118. package/node_modules/typebox/build/schema/engine/not.mjs +8 -4
  119. package/node_modules/typebox/build/schema/engine/oneOf.d.mts +4 -4
  120. package/node_modules/typebox/build/schema/engine/oneOf.mjs +11 -6
  121. package/node_modules/typebox/build/schema/engine/pattern.d.mts +4 -4
  122. package/node_modules/typebox/build/schema/engine/pattern.mjs +7 -3
  123. package/node_modules/typebox/build/schema/engine/patternProperties.d.mts +4 -4
  124. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +5 -6
  125. package/node_modules/typebox/build/schema/engine/prefixItems.d.mts +4 -4
  126. package/node_modules/typebox/build/schema/engine/prefixItems.mjs +1 -1
  127. package/node_modules/typebox/build/schema/engine/properties.d.mts +4 -4
  128. package/node_modules/typebox/build/schema/engine/properties.mjs +3 -4
  129. package/node_modules/typebox/build/schema/engine/propertyNames.d.mts +4 -4
  130. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +10 -7
  131. package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +4 -4
  132. package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +8 -12
  133. package/node_modules/typebox/build/schema/engine/ref.d.mts +4 -4
  134. package/node_modules/typebox/build/schema/engine/ref.mjs +14 -18
  135. package/node_modules/typebox/build/schema/engine/required.d.mts +4 -4
  136. package/node_modules/typebox/build/schema/engine/required.mjs +6 -2
  137. package/node_modules/typebox/build/schema/engine/schema.d.mts +7 -7
  138. package/node_modules/typebox/build/schema/engine/schema.mjs +138 -136
  139. package/node_modules/typebox/build/schema/engine/type.d.mts +4 -4
  140. package/node_modules/typebox/build/schema/engine/type.mjs +6 -2
  141. package/node_modules/typebox/build/schema/engine/unevaluatedItems.d.mts +4 -4
  142. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +9 -5
  143. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.d.mts +4 -4
  144. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +9 -5
  145. package/node_modules/typebox/build/schema/engine/uniqueItems.d.mts +4 -4
  146. package/node_modules/typebox/build/schema/engine/uniqueItems.mjs +6 -2
  147. package/node_modules/typebox/build/schema/errors.mjs +1 -1
  148. package/node_modules/typebox/build/schema/intern/intern.d.mts +9 -6
  149. package/node_modules/typebox/build/schema/intern/intern.mjs +56 -90
  150. package/node_modules/typebox/build/schema/parse.mjs +0 -3
  151. package/node_modules/typebox/build/schema/pointer/pointer.d.mts +6 -1
  152. package/node_modules/typebox/build/schema/pointer/pointer.mjs +64 -47
  153. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +32 -6
  154. package/node_modules/typebox/build/schema/resolve/resolve.mjs +227 -231
  155. package/node_modules/typebox/build/schema/schema.d.mts +0 -1
  156. package/node_modules/typebox/build/schema/schema.mjs +0 -1
  157. package/node_modules/typebox/build/system/hashing/hash.mjs +34 -63
  158. package/node_modules/typebox/build/system/locale/en_US.mjs +1 -2
  159. package/node_modules/typebox/build/system/memory/assign.d.mts +8 -2
  160. package/node_modules/typebox/build/system/memory/assign.mjs +6 -13
  161. package/node_modules/typebox/build/system/memory/clone.d.mts +5 -4
  162. package/node_modules/typebox/build/system/memory/clone.mjs +96 -8
  163. package/node_modules/typebox/build/system/memory/create.d.mts +5 -1
  164. package/node_modules/typebox/build/system/memory/create.mjs +13 -18
  165. package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
  166. package/node_modules/typebox/build/system/memory/discard.mjs +8 -9
  167. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  168. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  169. package/node_modules/typebox/build/system/memory/update.d.mts +4 -1
  170. package/node_modules/typebox/build/system/memory/update.mjs +16 -43
  171. package/node_modules/typebox/build/system/settings/settings.d.mts +0 -11
  172. package/node_modules/typebox/build/system/settings/settings.mjs +0 -14
  173. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +24 -2
  174. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +29 -2
  175. package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +0 -1
  176. package/node_modules/typebox/build/type/engine/evaluate/index.mjs +0 -1
  177. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +1 -5
  178. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +1 -6
  179. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +3 -1
  180. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +4 -1
  181. package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
  182. package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
  183. package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
  184. package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
  185. package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +3 -1
  186. package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +4 -1
  187. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +3 -1
  188. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +4 -1
  189. package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
  190. package/node_modules/typebox/build/type/extends/inference.mjs +2 -15
  191. package/node_modules/typebox/build/type/extends/tuple.d.mts +1 -2
  192. package/node_modules/typebox/build/type/extends/tuple.mjs +1 -21
  193. package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
  194. package/node_modules/typebox/build/value/clone/clone.d.mts +3 -4
  195. package/node_modules/typebox/build/value/clone/clone.mjs +7 -5
  196. package/node_modules/typebox/build/value/parse/parse.mjs +4 -10
  197. package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +14 -25
  198. package/node_modules/typebox/package.json +1 -1
  199. package/node_modules/typebox/readme.md +84 -52
  200. package/package.json +11 -3
  201. package/skills/pi-proactive/SKILL.md +27 -24
  202. package/tools/cli.mjs +121 -0
  203. package/tools/daemon-install.sh +397 -0
  204. package/tools/daemon-uninstall.sh +226 -0
  205. package/node_modules/typebox/build/guard/unicode/unicode.d.mts +0 -6
  206. package/node_modules/typebox/build/guard/unicode/unicode.mjs +0 -35
  207. package/node_modules/typebox/build/schema/engine/_pathing.d.mts +0 -1
  208. package/node_modules/typebox/build/schema/engine/_pathing.mjs +0 -11
  209. package/node_modules/typebox/build/schema/meta/draft_2019_09.d.mts +0 -2
  210. package/node_modules/typebox/build/schema/meta/draft_2019_09.mjs +0 -288
  211. package/node_modules/typebox/build/schema/meta/draft_2020_12.d.mts +0 -2
  212. package/node_modules/typebox/build/schema/meta/draft_2020_12.mjs +0 -300
  213. package/node_modules/typebox/build/schema/meta/draft_3.d.mts +0 -2
  214. package/node_modules/typebox/build/schema/meta/draft_3.mjs +0 -143
  215. package/node_modules/typebox/build/schema/meta/draft_4.d.mts +0 -2
  216. package/node_modules/typebox/build/schema/meta/draft_4.mjs +0 -149
  217. package/node_modules/typebox/build/schema/meta/draft_6.d.mts +0 -2
  218. package/node_modules/typebox/build/schema/meta/draft_6.mjs +0 -155
  219. package/node_modules/typebox/build/schema/meta/draft_7.d.mts +0 -2
  220. package/node_modules/typebox/build/schema/meta/draft_7.mjs +0 -172
  221. package/node_modules/typebox/build/schema/meta/index.d.mts +0 -2
  222. package/node_modules/typebox/build/schema/meta/index.mjs +0 -2
  223. package/node_modules/typebox/build/schema/meta/meta.d.mts +0 -9
  224. package/node_modules/typebox/build/schema/meta/meta.mjs +0 -15
  225. package/node_modules/typebox/build/schema/meta/schema.d.mts +0 -6
  226. package/node_modules/typebox/build/schema/meta/schema.mjs +0 -1
  227. package/node_modules/typebox/build/system/memory/deep.d.mts +0 -2
  228. package/node_modules/typebox/build/system/memory/deep.mjs +0 -108
  229. package/node_modules/typebox/build/system/memory/finalize.d.mts +0 -4
  230. package/node_modules/typebox/build/system/memory/finalize.mjs +0 -30
  231. package/node_modules/typebox/build/system/memory/keys.d.mts +0 -2
  232. package/node_modules/typebox/build/system/memory/keys.mjs +0 -8
  233. package/node_modules/typebox/build/system/settings/internal.d.mts +0 -2
  234. package/node_modules/typebox/build/system/settings/internal.mjs +0 -20
  235. package/node_modules/typebox/build/type/engine/call/index.d.mts +0 -1
  236. package/node_modules/typebox/build/type/engine/call/index.mjs +0 -1
  237. package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +0 -30
  238. package/node_modules/typebox/build/type/engine/evaluate/property.mjs +0 -29
  239. package/node_modules/typebox/build/type/engine/this/index.d.mts +0 -1
  240. package/node_modules/typebox/build/type/engine/this/index.mjs +0 -1
  241. package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +0 -2
  242. package/node_modules/typebox/build/value/repair/from_cyclic.mjs +0 -6
  243. /package/node_modules/typebox/build/guard/{unicode/unicode_segment.d.mts → string.d.mts} +0 -0
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env bash
2
+
3
+
4
+ # pi-proactive 常驻助理:**一键安装**(用户只跑这一条命令 → 看到「已常驻」)。
5
+ #
6
+ # 用户要做的事:跑一条命令(或一条命令面指令)。
7
+ # 我方要做的事:检测平台与宿主 → 生成单元文件 → 设置权限 → 启动 → **验证真的起来了** → 回执。
8
+ # **用户不编辑任何单元文件 / 权限 / 开机自启**(那是我方的事)。
9
+ #
10
+ # 验证三证据(**缺一即失败,不谎报成功**):
11
+ # ① 进程在 —— 心跳文件里的 pid 活着(kill -0)
12
+ # ② 心跳在更新 —— 两次读取的 ts 前进
13
+ # ③ 宿主已加载 —— launchctl print / systemctl is-active / herdr pane 在
14
+ #
15
+ # 宿主(双宿主):herdr 可用 → 用 herdr workspace/session 承载(助理在其中**纯 headless**);
16
+ # 不可用 → 回落自带 launchd / systemd --user(**不绑死单一宿主**)。
17
+ #
18
+ # 成本硬约束:装上**不等于花钱** —— 配置默认 `enabled=false` + 干跑。
19
+ #
20
+ # 用法:
21
+ # bash pi-proactive/tools/daemon-install.sh
22
+ # bash pi-proactive/tools/daemon-install.sh --agent-dir <dir> # 指定 agent 根目录
23
+ # bash pi-proactive/tools/daemon-install.sh --agent-id <id> # 指定稳定身份
24
+ # bash pi-proactive/tools/daemon-install.sh --host auto|herdr|system
25
+ # bash pi-proactive/tools/daemon-install.sh --no-start # 只渲染配置,不启动(自测用)
26
+ # bash pi-proactive/tools/daemon-install.sh --label <label> # 指定 launchd 单元标签(默认 com.pi-proactive.daemon)
27
+ # bash pi-proactive/tools/daemon-install.sh --service-name <n> # 指定 systemd 单元名(默认 pi-proactive-daemon.service)
28
+ #
29
+ # 退出码:0=已常驻(验证通过);1=失败;2=环境不支持。
30
+ set -uo pipefail
31
+
32
+ PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
33
+ TEMPLATE_DIR="${PKG_ROOT}/daemon"
34
+ LABEL="${PI_PROACTIVE_DAEMON_LABEL:-com.pi-proactive.daemon}"
35
+ SERVICE_NAME="${PI_PROACTIVE_DAEMON_SERVICE:-pi-proactive-daemon.service}"
36
+ WORKSPACE_LABEL="pi-proactive-daemon"
37
+
38
+ AGENT_DIR="${PI_CODING_AGENT_DIR:-${HOME}/.pi/agent}"
39
+ AGENT_ID="${PI_PROACTIVE_AGENT_ID:-}"
40
+ HOST_PREF="auto"
41
+ NO_START=0
42
+ while [ $# -gt 0 ]; do
43
+ case "$1" in
44
+ --agent-dir) AGENT_DIR="$2"; shift 2 ;;
45
+ --agent-id) AGENT_ID="$2"; shift 2 ;;
46
+ --host) HOST_PREF="$2"; shift 2 ;;
47
+ --label) LABEL="$2"; shift 2 ;;
48
+ --service-name) SERVICE_NAME="$2"; shift 2 ;;
49
+ --no-start) NO_START=1; shift ;;
50
+ -h|--help) sed -n '2,28p' "$0"; exit 0 ;;
51
+ *) echo "未知参数:$1" >&2; exit 2 ;;
52
+ esac
53
+ done
54
+
55
+ NODE_BIN="${NODE_BIN:-$(command -v node || true)}"
56
+ if [ -z "${NODE_BIN}" ]; then
57
+ echo "找不到 node(常驻助理需要 Node ≥ 22.19)" >&2
58
+ exit 2
59
+ fi
60
+ if [ -z "${AGENT_ID}" ]; then
61
+ HOST="$(hostname 2>/dev/null || echo localhost)"
62
+ AGENT_ID="employee#${HOST}#pi-proactive-daemon"
63
+ fi
64
+
65
+ # 常驻入口按【运行环境】解析:两种布局都要能跑。
66
+ # · 开发态(仓库):src/*.ts 是真源 → 优先用它(build/ 是编译产物,可能陈旧 →
67
+ # 在仓库里跑陈旧产物 = 测的不是当前代码);
68
+ # · 产物态(npm 包):包里**没有** src/(files 白名单只有 build/)→ 自动落到 build/*.js。
69
+ # 候选清单与错误信息**同源** → 报错时说的就是真的试过的路径(不写死一份会漂移的清单)。
70
+ DAEMON_CANDIDATES="src/daemon.ts build/src/daemon.js"
71
+ DAEMON_TS=""
72
+ for c in ${DAEMON_CANDIDATES}; do
73
+ [ -f "${PKG_ROOT}/${c}" ] && { DAEMON_TS="${PKG_ROOT}/${c}"; break; }
74
+ done
75
+ [ -n "${DAEMON_TS}" ] || { echo "找不到常驻入口(试过 ${DAEMON_CANDIDATES// /、})" >&2; exit 2; }
76
+ DAEMON_HOST_CANDIDATES="src/daemon-host.ts build/src/daemon-host.js"
77
+ DAEMON_HOST_ENTRY=""
78
+ for c in ${DAEMON_HOST_CANDIDATES}; do
79
+ [ -f "${PKG_ROOT}/${c}" ] && { DAEMON_HOST_ENTRY="${PKG_ROOT}/${c}"; break; }
80
+ done
81
+ [ -n "${DAEMON_HOST_ENTRY}" ] || { echo "找不到宿主判定入口(试过 ${DAEMON_HOST_CANDIDATES// /、})" >&2; exit 2; }
82
+ LOG_DIR="${AGENT_DIR}/proactive/daemon"
83
+ CONFIG_FILE="${AGENT_DIR}/proactive/daemon.json"
84
+ DAEMON_DIR="${AGENT_DIR}/proactive/daemon"
85
+ HEARTBEAT_FILE="${DAEMON_DIR}/heartbeat.json"
86
+ HOST_STATE_FILE="${DAEMON_DIR}/host.json"
87
+ RUNNER_FILE="${DAEMON_DIR}/run-daemon.sh"
88
+
89
+
90
+ mkdir -p "${LOG_DIR}" "${AGENT_DIR}/proactive" || exit 1
91
+
92
+ # ---- 配置:不存在才写(**不覆盖**用户已有的配置;enabled 默认 false = 默认不花钱)----
93
+ if [ ! -f "${CONFIG_FILE}" ]; then
94
+ cat >"${CONFIG_FILE}" <<EOF
95
+ {
96
+ "version": "v1",
97
+ "enabled": false,
98
+ "agentId": "${AGENT_ID}",
99
+ "dryRun": true,
100
+ "budget": {
101
+ "maxActionsPerHour": 6,
102
+ "maxActionsPerDay": 40,
103
+ "maxTokenUnitsPerDay": 200000,
104
+ "maxLimitedActionsPerDay": 20,
105
+ "degradeToObserveWhenExhausted": true
106
+ },
107
+ "permissions": {
108
+ "defaultTier": "limited"
109
+ },
110
+ "tickIntervalMs": 30000,
111
+ "handoverHour": 8,
112
+ "staleSocketMs": 3600000,
113
+ "staleExecutionMs": 3600000,
114
+ "contact": {
115
+ "enabled": true,
116
+ "target": "auto",
117
+ "minIntervalMs": 3600000,
118
+ "maxPerDay": 8,
119
+ "deviceNotify": "fallback"
120
+ },
121
+ "host": {
122
+ "hostKind": "auto"
123
+ }
124
+ }
125
+ EOF
126
+ echo "已写值班配置(enabled=false · 默认干跑):${CONFIG_FILE}"
127
+ else
128
+ echo "值班配置已存在,保持不动:${CONFIG_FILE}"
129
+ fi
130
+
131
+ # ---- 宿主判定(herdr 优先 → 回落系统宿主)----
132
+ detect_host() {
133
+ "$NODE_BIN" --experimental-strip-types "${DAEMON_HOST_ENTRY}" --kind 2>/dev/null || echo ""
134
+ }
135
+ HOST_KIND="$(detect_host)"
136
+ HOST_REASON="$("$NODE_BIN" --experimental-strip-types "${DAEMON_HOST_ENTRY}" 2>/dev/null | sed -n 's/.*"reason":"\([^"]*\)".*/\1/p')"
137
+ case "$(uname -s)" in
138
+ Darwin) SYSTEM_KIND="launchd" ;;
139
+ Linux) SYSTEM_KIND="systemd" ;;
140
+ *) SYSTEM_KIND="" ;;
141
+ esac
142
+ if [ "${HOST_PREF}" = "system" ]; then
143
+ HOST_KIND="${SYSTEM_KIND}"
144
+ # 用户显式指定 → 回执里的理由必须**说实话**(不能还挂着探测结论「herdr 可用 → …」)
145
+ HOST_REASON="用户指定使用自带系统宿主(--host system)"
146
+ elif [ "${HOST_PREF}" = "herdr" ]; then
147
+ HOST_KIND="herdr"
148
+ HOST_REASON="用户指定使用 herdr 宿主(--host herdr)"
149
+ fi
150
+ case "${HOST_KIND}" in
151
+ herdr|launchd|systemd) : ;;
152
+ *) HOST_KIND="${SYSTEM_KIND}" ;;
153
+ esac
154
+
155
+ # ---- 渲染模板(占位符替换)----
156
+ render() {
157
+ sed -e "s|__NODE__|${NODE_BIN}|g" \
158
+ -e "s|__DAEMON_TS__|${DAEMON_TS}|g" \
159
+ -e "s|__AGENT_DIR__|${AGENT_DIR}|g" \
160
+ -e "s|__AGENT_ID__|${AGENT_ID}|g" \
161
+ -e "s|__PKG_ROOT__|${PKG_ROOT}|g" \
162
+ -e "s|__LOG_DIR__|${LOG_DIR}|g" \
163
+ -e "s|__LOG_DIR__|${LOG_DIR}|g" \
164
+ -e "s|__LABEL__|${LABEL}|g" \
165
+ -e "s|__RUNNER__|${RUNNER_FILE}|g" \
166
+ "$1"
167
+ }
168
+
169
+ # ---- 保活启动器(herdr 宿主用:pane 里跑这个 → 崩溃自动重来;systemd/launchd 自带自愈)----
170
+ render "${TEMPLATE_DIR}/run-daemon.sh" >"${RUNNER_FILE}" || exit 1
171
+ chmod 700 "${RUNNER_FILE}" 2>/dev/null || true
172
+
173
+ write_host_state() {
174
+ cat >"${HOST_STATE_FILE}" <<EOF
175
+ {
176
+ "hostKind": "$1",
177
+ "label": "${WORKSPACE_LABEL}",
178
+ "workspaceId": "${2:-}",
179
+ "paneId": "${3:-}",
180
+ "unitPath": "${4:-}",
181
+ "agentId": "${AGENT_ID}",
182
+ "installedAt": $(date +%s000)
183
+ }
184
+ EOF
185
+ chmod 600 "${HOST_STATE_FILE}" 2>/dev/null || true
186
+ }
187
+
188
+ # ---- 启动(按宿主分派)----
189
+ UNIT_PATH=""
190
+ UNIT_LOADED=0
191
+ WS_ID=""
192
+ PANE_ID=""
193
+ case "$(uname -s)" in
194
+ Darwin|Linux) : ;;
195
+ *)
196
+ echo "不支持的操作系统:$(uname -s)(本脚本只支持 macOS launchd 与 Linux systemd --user)" >&2
197
+ exit 2
198
+ ;;
199
+ esac
200
+
201
+ if [ "${HOST_KIND}" = "herdr" ]; then
202
+ # ---- herdr 宿主:workspace + pane(**纯 headless**:不抢焦点、不占用户终端)----
203
+ if ! command -v herdr >/dev/null 2>&1; then
204
+ echo "herdr 不可用 → 回落自带系统宿主" >&2
205
+ HOST_KIND="${SYSTEM_KIND}"
206
+ elif ! herdr status server 2>/dev/null | grep -q "status: running"; then
207
+ echo "herdr 服务端没在跑 → 回落自带系统宿主" >&2
208
+ HOST_KIND="${SYSTEM_KIND}"
209
+ fi
210
+ fi
211
+
212
+ # ---- 系统宿主单元(launchd / systemd --user):既是「宿主就是系统时的主角」,
213
+ # 也是「宿主是 herdr 时的兜底保活」(形态选型 §2.5:server 挂 → 助理可判定的降级)。
214
+ # 两种情况跑的都是同一个监督器脚本(run-daemon.sh)→ 靠**心跳 + 值班锁**保证只有一个值班进程。
215
+ SYSTEM_UNIT_PATH=""
216
+ render_system_unit() {
217
+ case "$(uname -s)" in
218
+ Darwin)
219
+ SYSTEM_UNIT_PATH="${HOME}/Library/LaunchAgents/${LABEL}.plist"
220
+ mkdir -p "$(dirname "${SYSTEM_UNIT_PATH}")" || return 1
221
+ render "${TEMPLATE_DIR}/launchd.plist" >"${SYSTEM_UNIT_PATH}" || return 1
222
+ chmod 600 "${SYSTEM_UNIT_PATH}" 2>/dev/null || true
223
+ ;;
224
+ Linux)
225
+ SYSTEM_UNIT_PATH="${HOME}/.config/systemd/user/${SERVICE_NAME}"
226
+ mkdir -p "$(dirname "${SYSTEM_UNIT_PATH}")" || return 1
227
+ render "${TEMPLATE_DIR}/pi-proactive-daemon.service" >"${SYSTEM_UNIT_PATH}" || return 1
228
+ ;;
229
+ esac
230
+ return 0
231
+ }
232
+
233
+ install_system_unit() {
234
+ render_system_unit || return 1
235
+ case "$(uname -s)" in
236
+ Darwin)
237
+ launchctl bootout "gui/$(id -u)/${LABEL}" >/dev/null 2>&1 || true
238
+ launchctl unload "${SYSTEM_UNIT_PATH}" >/dev/null 2>&1 || true
239
+ if launchctl bootstrap "gui/$(id -u)" "${SYSTEM_UNIT_PATH}" >/dev/null 2>&1; then
240
+ :
241
+ elif launchctl load -w "${SYSTEM_UNIT_PATH}" >/dev/null 2>&1; then
242
+ :
243
+ else
244
+ return 1
245
+ fi
246
+ ;;
247
+ Linux)
248
+ if ! command -v systemctl >/dev/null 2>&1; then
249
+ echo "找不到 systemctl(systemd --user 不可用)" >&2
250
+ return 2
251
+ fi
252
+ systemctl --user disable --now "${SERVICE_NAME}" >/dev/null 2>&1 || true
253
+ systemctl --user daemon-reload || return 1
254
+ systemctl --user enable --now "${SERVICE_NAME}" || return 1
255
+ ;;
256
+ esac
257
+ return 0
258
+ }
259
+
260
+ # ---- 只渲染不启动(--no-start):**不得**创建 workspace / 不得启动任何进程 ----
261
+ if [ "${NO_START}" -eq 1 ]; then
262
+ render_system_unit || exit 1
263
+ echo "已渲染配置(--no-start · 未启动):${SYSTEM_UNIT_PATH}"
264
+ exit 0
265
+ fi
266
+
267
+ if [ "${HOST_KIND}" = "herdr" ]; then
268
+ # 幂等:先关掉同名旧 workspace(没有就忽略)
269
+ OLD_WS="$(herdr workspace list 2>/dev/null | "$NODE_BIN" -e '
270
+ let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{
271
+ try{const j=JSON.parse(s);const ws=(j.result?.workspaces??[]).filter(w=>w.label===process.argv[1]);console.log(ws.map(w=>w.workspace_id).join(" "))}catch{console.log("")}})' "${WORKSPACE_LABEL}")"
272
+ for w in ${OLD_WS}; do herdr workspace close "$w" >/dev/null 2>&1 || true; done
273
+ WS_JSON="$(herdr workspace create --cwd "${PKG_ROOT}" --label "${WORKSPACE_LABEL}" --no-focus 2>/dev/null)"
274
+ WS_ID="$(printf '%s' "${WS_JSON}" | "$NODE_BIN" -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).result.workspace.workspace_id)}catch{console.log("")}})')"
275
+ PANE_ID="$(printf '%s' "${WS_JSON}" | "$NODE_BIN" -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).result.root_pane.pane_id)}catch{console.log("")}})')"
276
+ if [ -z "${WS_ID}" ]; then
277
+ echo "herdr workspace 创建失败 → 回落自带系统宿主" >&2
278
+ HOST_KIND="${SYSTEM_KIND}"
279
+ HOST_REASON="回落自带系统宿主(herdr workspace 创建失败)"
280
+ else
281
+ # pane 里跑监督器脚本(**纯 headless**:不抢焦点、不占用户终端、不进 TUI)
282
+ herdr pane run "${PANE_ID}" bash "${RUNNER_FILE}" >/dev/null 2>&1 || true
283
+ write_host_state herdr "${WS_ID}" "${PANE_ID}" ""
284
+ UNIT_PATH="herdr workspace ${WS_ID} / pane ${PANE_ID}"
285
+ fi
286
+ fi
287
+
288
+ # 系统单元:宿主是系统时它是主角;宿主是 herdr 时它是**兜底保活**(herdr 挂了它接管)。
289
+ if ! install_system_unit; then
290
+ echo "系统宿主单元安装失败(单元文件已写入:${SYSTEM_UNIT_PATH})" >&2
291
+ exit 1
292
+ fi
293
+ if [ "${HOST_KIND}" != "herdr" ]; then
294
+ write_host_state "${SYSTEM_KIND}" "" "" "${SYSTEM_UNIT_PATH}"
295
+ UNIT_PATH="${SYSTEM_UNIT_PATH}"
296
+ fi
297
+
298
+
299
+ # ---- 验证三证据(不谎报成功)----
300
+ hb_ts() {
301
+ "$NODE_BIN" -e '
302
+ const fs=require("fs");try{const j=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));console.log(String(j.ts??""))}catch{console.log("")}' "$1"
303
+ }
304
+ hb_pid() {
305
+ "$NODE_BIN" -e '
306
+ const fs=require("fs");try{const j=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));console.log(String(j.pid??""))}catch{console.log("")}' "$1"
307
+ }
308
+
309
+ WAITED=0
310
+ TS1=""
311
+ while [ "${WAITED}" -lt 120 ]; do # 最多 60s:launchd 冷启动 + 崩溃重启的 ThrottleInterval(10s) 都要覆盖
312
+ TS1="$(hb_ts "${HEARTBEAT_FILE}")"
313
+ if [ -n "${TS1}" ]; then break; fi
314
+ sleep 0.5
315
+ WAITED=$((WAITED + 1))
316
+ done
317
+ if [ -z "${TS1}" ]; then
318
+ echo "验证失败:没有看到心跳文件(${HEARTBEAT_FILE})→ 助理没起来" >&2
319
+ # 诊断:把值班进程自己的错误日志尾部打出来(否则失败无法归因)。
320
+ for f in "${LOG_DIR}/daemon.err.log" "${LOG_DIR}/daemon.out.log"; do
321
+ if [ -f "${f}" ] && [ -s "${f}" ]; then
322
+ echo "---- ${f}(尾部 20 行)----" >&2
323
+ tail -20 "${f}" >&2
324
+ fi
325
+ done
326
+ exit 1
327
+ fi
328
+ PID="$(hb_pid "${HEARTBEAT_FILE}")"
329
+ if [ -z "${PID}" ] || ! kill -0 "${PID}" 2>/dev/null; then
330
+ echo "验证失败:心跳里的进程不在(pid=${PID:-?})→ 助理没起来" >&2
331
+ exit 1
332
+ fi
333
+ TS2="$(hb_ts "${HEARTBEAT_FILE}")"
334
+ WAITED=0
335
+ while [ "${TS2}" = "${TS1}" ] && [ "${WAITED}" -lt 30 ]; do
336
+ sleep 0.5
337
+ TS2="$(hb_ts "${HEARTBEAT_FILE}")"
338
+ WAITED=$((WAITED + 1))
339
+ done
340
+ if [ "${TS2}" = "${TS1}" ]; then
341
+ echo "验证失败:心跳没有更新(ts 一直是 ${TS1})→ 助理没在跑" >&2
342
+ exit 1
343
+ fi
344
+ case "${HOST_KIND}" in
345
+ herdr)
346
+ if [ -z "${WS_ID}" ] || ! herdr pane list 2>/dev/null | grep -q "${PANE_ID}"; then
347
+ echo "验证失败:herdr pane 不在了(${PANE_ID})→ 助理没在跑" >&2
348
+ exit 1
349
+ fi
350
+ # 兜底保活单元也必须已加载(herdr 挂了就靠它接管)
351
+ case "${SYSTEM_KIND}" in
352
+ launchd)
353
+ if ! launchctl print "gui/$(id -u)/${LABEL}" >/dev/null 2>&1; then
354
+ echo "验证失败:兜底 LaunchAgent 没加载(${SYSTEM_UNIT_PATH})→ herdr 挂了就没人接管" >&2
355
+ exit 1
356
+ fi
357
+ ;;
358
+ systemd)
359
+ if ! systemctl --user is-active "${SERVICE_NAME}" >/dev/null 2>&1; then
360
+ echo "验证失败:兜底 systemd 单元没在跑(${SYSTEM_UNIT_PATH})→ herdr 挂了就没人接管" >&2
361
+ exit 1
362
+ fi
363
+ ;;
364
+ esac
365
+ UNIT_LOADED=1
366
+ ;;
367
+ launchd)
368
+ if ! launchctl print "gui/$(id -u)/${LABEL}" >/dev/null 2>&1; then
369
+ echo "验证失败:LaunchAgent 没加载(${UNIT_PATH})→ 助理没在跑" >&2
370
+ exit 1
371
+ fi
372
+ UNIT_LOADED=1
373
+ ;;
374
+ systemd)
375
+ if ! systemctl --user is-active "${SERVICE_NAME}" >/dev/null 2>&1; then
376
+ echo "验证失败:systemd 单元没在跑(${UNIT_PATH})→ 助理没在跑" >&2
377
+ exit 1
378
+ fi
379
+ UNIT_LOADED=1
380
+ ;;
381
+ esac
382
+ if [ "${UNIT_LOADED}" -ne 1 ]; then
383
+ echo "验证失败:宿主未确认加载 → 不谎报「已常驻」" >&2
384
+ exit 1
385
+ fi
386
+
387
+ echo "已常驻"
388
+ echo " · 主宿主:${HOST_KIND}${HOST_REASON:+(${HOST_REASON})}"
389
+ echo " · 单元:${UNIT_PATH}"
390
+ if [ "${HOST_KIND}" = "herdr" ]; then
391
+ echo " · 兜底宿主:${SYSTEM_KIND}(${SYSTEM_UNIT_PATH})→ herdr 挂了自动接管"
392
+ fi
393
+ echo " · 进程:pid=${PID} 在跑"
394
+ echo " · 心跳:${HEARTBEAT_FILE}(ts ${TS1} → ${TS2} 在前进)"
395
+ echo " · 想关掉:bash pi-proactive/tools/daemon-uninstall.sh"
396
+ echo "提醒:默认 enabled=false + 干跑 → 装上不会花钱;要真跑请开开关与预算上限。"
397
+ exit 0
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env bash
2
+
3
+
4
+ # pi-proactive 常驻助理:**一键卸载**(幂等 · 卸载后**零残留**)。
5
+ #
6
+ # 做四件事(顺序固定):
7
+ # ① 停(herdr workspace / launchd / systemd;再兜底杀掉残留进程)
8
+ # ② 删(单元文件 / 日志 / socket / 状态目录)
9
+ # ③ 校验(**逐项列出证据**;有残留即非零退出 —— 不谎报「卸载成功」)
10
+ #
11
+ # 保留(默认不删):`$agentDir/proactive/` 下的任务表 / 事件流 / 邮箱(**用户数据**,删不删由用户决定;
12
+ # `--purge-data` 才连它一起删)。
13
+ #
14
+ # 用法:
15
+ # bash pi-proactive/tools/daemon-uninstall.sh
16
+ # bash pi-proactive/tools/daemon-uninstall.sh --agent-dir <dir>
17
+ # bash pi-proactive/tools/daemon-uninstall.sh --no-start # 只删文件,不动服务管理器(自测用)
18
+ # bash pi-proactive/tools/daemon-uninstall.sh --label <label> # 指定 launchd 单元标签(必须与安装时一致)
19
+ # bash pi-proactive/tools/daemon-uninstall.sh --service-name <n># 指定 systemd 单元名(必须与安装时一致)
20
+ # bash pi-proactive/tools/daemon-uninstall.sh --purge-data # 连用户数据一起删
21
+ #
22
+ # 退出码:0=卸载完成且无残留;1=有残留;2=环境不支持。
23
+ set -uo pipefail
24
+
25
+ PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
26
+ LABEL="${PI_PROACTIVE_DAEMON_LABEL:-com.pi-proactive.daemon}"
27
+ SERVICE_NAME="${PI_PROACTIVE_DAEMON_SERVICE:-pi-proactive-daemon.service}"
28
+ WORKSPACE_LABEL="pi-proactive-daemon"
29
+
30
+ AGENT_DIR="${PI_CODING_AGENT_DIR:-${HOME}/.pi/agent}"
31
+ NO_START=0
32
+ PURGE_DATA=0
33
+ AGENT_ID="${PI_PROACTIVE_AGENT_ID:-}"
34
+ while [ $# -gt 0 ]; do
35
+ case "$1" in
36
+ --agent-dir) AGENT_DIR="$2"; shift 2 ;;
37
+ --agent-id) AGENT_ID="$2"; shift 2 ;;
38
+ --label) LABEL="$2"; shift 2 ;;
39
+ --service-name) SERVICE_NAME="$2"; shift 2 ;;
40
+ # --host 只为与 cli 的参数契约一致(卸载会把所有宿主都停掉 → 不区分宿主)
41
+ --host) if [ $# -ge 2 ]; then shift 2; else shift; fi ;;
42
+ --no-start) NO_START=1; shift ;;
43
+ --purge-data) PURGE_DATA=1; shift ;;
44
+ -h|--help) sed -n '2,20p' "$0"; exit 0 ;;
45
+ *) echo "未知参数:$1" >&2; exit 2 ;;
46
+ esac
47
+ done
48
+
49
+ NODE_BIN="${NODE_BIN:-$(command -v node || true)}"
50
+ DAEMON_DIR="${AGENT_DIR}/proactive/daemon"
51
+ LOG_DIR="${DAEMON_DIR}"
52
+ HOST_STATE_FILE="${DAEMON_DIR}/host.json"
53
+ HEARTBEAT_FILE="${DAEMON_DIR}/heartbeat.json"
54
+ RUNNER_FILE="${DAEMON_DIR}/run-daemon.sh"
55
+ PROACTIVE_DIR="${AGENT_DIR}/proactive"
56
+
57
+ # ---- 停 ----
58
+ KIND=""
59
+ AGENT_ID="${AGENT_ID:-}"
60
+ if [ -f "${HOST_STATE_FILE}" ]; then
61
+ KIND="$(sed -n 's/.*"hostKind"[ ]*:[ ]*"\([^"]*\)".*/\1/p' "${HOST_STATE_FILE}")"
62
+ [ -n "${AGENT_ID}" ] || AGENT_ID="$(sed -n 's/.*"agentId"[ ]*:[ ]*"\([^"]*\)".*/\1/p' "${HOST_STATE_FILE}")"
63
+ fi
64
+ OLD_PID=""
65
+ if [ -f "${HEARTBEAT_FILE}" ] && [ -n "${NODE_BIN}" ]; then
66
+ OLD_PID="$("$NODE_BIN" -e '
67
+ const fs=require("fs");try{const j=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));console.log(String(j.pid??""))}catch{console.log("")}' "${HEARTBEAT_FILE}")"
68
+ fi
69
+
70
+ if [ "${NO_START}" -ne 1 ]; then
71
+ # ---- 停(**不依赖 host.json**)----
72
+ # 为什么必须无条件停:host.json 可能缺失/损坏(用户手动删过状态目录、被别的工具清过)。
73
+ # 若「停」只按 host.json 里的 kind 走,卸载就只会删文件、**停不掉服务** →
74
+ # 留下「单元仍加载 + 进程还在」的孤儿,用户永远卸不干净
75
+ # (实测:只删 plist 后 `launchctl print` 仍成功 → 脚本自检报「残留」并 exit 1)。
76
+ WS_ID=""
77
+ case "$(uname -s)" in
78
+ Darwin)
79
+ launchctl bootout "gui/$(id -u)/${LABEL}" >/dev/null 2>&1 || true
80
+ launchctl unload "${HOME}/Library/LaunchAgents/${LABEL}.plist" >/dev/null 2>&1 || true
81
+ ;;
82
+ Linux)
83
+ if command -v systemctl >/dev/null 2>&1; then
84
+ systemctl --user disable --now "${SERVICE_NAME}" >/dev/null 2>&1 || true
85
+ systemctl --user reset-failed "${SERVICE_NAME}" >/dev/null 2>&1 || true
86
+ fi
87
+ ;;
88
+ esac
89
+ # herdr 宿主:按 label 关掉同名 workspace(纯 headless 容器,不影响用户自己的 pane)
90
+ if command -v herdr >/dev/null 2>&1 && [ -n "${NODE_BIN}" ]; then
91
+ WS_ID="$(sed -n 's/.*"workspaceId"[ ]*:[ ]*"\([^"]*\)".*/\1/p' "${HOST_STATE_FILE}" 2>/dev/null || true)"
92
+ if [ -z "${WS_ID}" ]; then
93
+ WS_ID="$(herdr workspace list 2>/dev/null | "${NODE_BIN}" -e '
94
+ let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{
95
+ try{const j=JSON.parse(s);const ws=(j.result?.workspaces??[]).filter(w=>w.label===process.argv[1]);console.log(ws.map(w=>w.workspace_id).join(" "))}catch{console.log("")}})' "${WORKSPACE_LABEL}")"
96
+ fi
97
+ for w in ${WS_ID}; do
98
+ herdr workspace close "$w" >/dev/null 2>&1 || true
99
+ done
100
+ fi
101
+ case "${KIND}" in
102
+ herdr) echo "已停(herdr workspace):${WS_ID:-无}" ;;
103
+ systemd) echo "已停(systemd --user):${SERVICE_NAME}" ;;
104
+ launchd) echo "已停(launchd):${LABEL}" ;;
105
+ *) echo "已停(按平台默认宿主;状态文件缺失 → 未能确认原宿主,仍按默认停了一遍)" ;;
106
+ esac
107
+ # ★监督器也要杀:监督器(run-daemon.sh)会**把值班进程再拉起来** →
108
+ # 只杀值班进程 = 没卸载干净(单元还在 → 几秒后又活了)。
109
+ if [ -f "${RUNNER_FILE}" ] || [ -n "${RUNNER_FILE}" ]; then
110
+ if pgrep -f "${RUNNER_FILE}" >/dev/null 2>&1; then
111
+ pkill -f "${RUNNER_FILE}" 2>/dev/null || true
112
+ sleep 1
113
+ pkill -9 -f "${RUNNER_FILE}" 2>/dev/null || true
114
+ echo "已结束残留监督器(${RUNNER_FILE})"
115
+ fi
116
+ fi
117
+ # 兜底:进程还在就杀掉(常驻进程不该留在用户机器上)。
118
+ if [ -n "${OLD_PID}" ] && kill -0 "${OLD_PID}" 2>/dev/null; then
119
+ kill "${OLD_PID}" 2>/dev/null || true
120
+ sleep 1
121
+ kill -9 "${OLD_PID}" 2>/dev/null || true
122
+ echo "已结束残留进程:pid=${OLD_PID}"
123
+ fi
124
+ fi
125
+
126
+ # ---- 删 ----
127
+ case "$(uname -s)" in
128
+ Darwin)
129
+ rm -f "${HOME}/Library/LaunchAgents/${LABEL}.plist"
130
+ ;;
131
+ Linux)
132
+ rm -f "${HOME}/.config/systemd/user/${SERVICE_NAME}"
133
+ if [ "${NO_START}" -ne 1 ] && command -v systemctl >/dev/null 2>&1; then
134
+ systemctl --user daemon-reload >/dev/null 2>&1 || true
135
+ fi
136
+ ;;
137
+ esac
138
+
139
+ # socket(常驻进程的端点;正常没有 → 顺手清掉)
140
+ # socket 端点路径:模块按【运行环境】解析(产物态 build/… 优先,开发态 src/… 回落)
141
+ SOCKET_MODULE=""
142
+ SOCKET_CANDIDATES="src/socket.ts build/src/socket.js"
143
+ for c in ${SOCKET_CANDIDATES}; do
144
+ [ -f "${PKG_ROOT}/${c}" ] && { SOCKET_MODULE="${PKG_ROOT}/${c}"; break; }
145
+ done
146
+ if [ -z "${SOCKET_MODULE}" ]; then
147
+ echo "警告:找不到 socket 模块(试过 ${SOCKET_CANDIDATES// /、})→ socket 端点文件可能残留" >&2
148
+ elif [ -n "${NODE_BIN}" ] && [ -n "${AGENT_ID}" ]; then
149
+ SOCK="$("$NODE_BIN" --experimental-strip-types -e '
150
+ import(process.argv[1]).then(m=>{console.log(m.socketPathFor(process.argv[2], process.argv[3]))}).catch(()=>console.log(""))' "file://${SOCKET_MODULE}" "${AGENT_ID}" "${AGENT_DIR}" 2>/dev/null || true)"
151
+ [ -n "${SOCK}" ] && rm -f "${SOCK}"
152
+ fi
153
+
154
+ # 日志 / 状态目录(心跳 / 联系清单 / 交班摘要 / 宿主状态 / 启动器)
155
+ rm -rf "${DAEMON_DIR}"
156
+
157
+ if [ "${PURGE_DATA}" -eq 1 ]; then
158
+ rm -rf "${PROACTIVE_DIR}"
159
+ echo "已删用户数据(--purge-data):${PROACTIVE_DIR}"
160
+ else
161
+ echo "保留(未删):${PROACTIVE_DIR} 下的任务表 / 事件流 / 邮箱(用户数据;--purge-data 才删)"
162
+ fi
163
+
164
+ # ---- 校验(逐项;有残留即非零退出)----
165
+ RESIDUE=0
166
+ check_gone() {
167
+ if [ -e "$1" ]; then
168
+ echo "残留:$2 仍在 $1" >&2
169
+ RESIDUE=1
170
+ else
171
+ echo "已清:$2"
172
+ fi
173
+ }
174
+ case "$(uname -s)" in
175
+ Darwin) check_gone "${HOME}/Library/LaunchAgents/${LABEL}.plist" "LaunchAgent 单元" ;;
176
+ Linux) check_gone "${HOME}/.config/systemd/user/${SERVICE_NAME}" "systemd 单元" ;;
177
+ esac
178
+ check_gone "${DAEMON_DIR}" "状态/日志目录"
179
+ if pgrep -f "${RUNNER_FILE}" >/dev/null 2>&1; then
180
+ echo "残留:监督器进程还在(${RUNNER_FILE})" >&2
181
+ RESIDUE=1
182
+ else
183
+ echo "已清:监督器进程"
184
+ fi
185
+ if [ -n "${OLD_PID}" ] && kill -0 "${OLD_PID}" 2>/dev/null; then
186
+ echo "残留:进程还在(pid=${OLD_PID})" >&2
187
+ RESIDUE=1
188
+ else
189
+ echo "已清:进程(pid=${OLD_PID:-无})"
190
+ fi
191
+ if [ "${NO_START}" -ne 1 ]; then
192
+ case "$(uname -s)" in
193
+ Darwin)
194
+ if launchctl print "gui/$(id -u)/${LABEL}" >/dev/null 2>&1; then
195
+ echo "残留:服务仍处于加载状态(launchctl print 成功)" >&2
196
+ RESIDUE=1
197
+ else
198
+ echo "已清:服务未加载"
199
+ fi
200
+ ;;
201
+ Linux)
202
+ if command -v systemctl >/dev/null 2>&1 && systemctl --user is-enabled "${SERVICE_NAME}" >/dev/null 2>&1; then
203
+ echo "残留:服务仍处于 enabled 状态" >&2
204
+ RESIDUE=1
205
+ else
206
+ echo "已清:服务未 enabled"
207
+ fi
208
+ ;;
209
+ esac
210
+ fi
211
+
212
+ if [ "${RESIDUE}" -ne 0 ]; then
213
+ echo "结果:存在残留(见上方)" >&2
214
+ echo "手工清理命令(逐条执行即可清干净):" >&2
215
+ case "$(uname -s)" in
216
+ Darwin) echo " launchctl bootout \"gui/$(id -u)/${LABEL}\" || true" >&2
217
+ echo " rm -f \"${HOME}/Library/LaunchAgents/${LABEL}.plist\"" >&2 ;;
218
+ Linux) echo " systemctl --user disable --now \"${SERVICE_NAME}\" || true" >&2
219
+ echo " rm -f \"${HOME}/.config/systemd/user/${SERVICE_NAME}\"" >&2 ;;
220
+ esac
221
+ echo " pkill -f \"${RUNNER_FILE}\" || true" >&2
222
+ echo " rm -rf \"${DAEMON_DIR}\"" >&2
223
+ exit 1
224
+ fi
225
+ echo "结果:卸载完成,无残留(exit 0)"
226
+ exit 0
@@ -1,6 +0,0 @@
1
- /** Returns the total number of Unicode code points in the string */
2
- export declare function CodePointCount(value: string): number;
3
- /** Returns true if the string length in Unicode code points is less than or equal to maxLength */
4
- export declare function IsMaxLength(value: string, maxLength: number): boolean;
5
- /** Returns true if the string length in Unicode code points is greater than or equal to minLength */
6
- export declare function IsMinLength(value: string, minLength: number): boolean;
@@ -1,35 +0,0 @@
1
- // ------------------------------------------------------------------
2
- // CodePointCount (10-bit Shift Branchless)
3
- //
4
- // Counts code points by enumerating UTF-16 sequences and incrementing
5
- // when not within a high/low pairing. Because surrogate blocks are
6
- // 1024 (2^10) wide and aligned, by shifting right by (0xA), we can
7
- // collapse each unit to a constant per block (high: 0x36, low: 0x37)
8
- // and shift again into a packed (0x3637) for comparison. Fetch calls
9
- // to charCodeAt(...) are kept to one call per iteration.
10
- //
11
- // ------------------------------------------------------------------
12
- /** Returns the total number of Unicode code points in the string */
13
- export function CodePointCount(value) {
14
- let result = 0, index = 0, prev = 0;
15
- while (index < value.length) {
16
- const next = value.charCodeAt(index++) >> 0xA; // shift (10-bits into high/low)
17
- result += +(((prev << 8) | next) !== 0x3637); // packed (or +!(prev === 0x36 && next === 0x37))
18
- prev = next;
19
- }
20
- return result;
21
- }
22
- // ------------------------------------------------------------------
23
- // IsMaxLength
24
- // ------------------------------------------------------------------
25
- /** Returns true if the string length in Unicode code points is less than or equal to maxLength */
26
- export function IsMaxLength(value, maxLength) {
27
- return value.length <= maxLength || (value.length <= (maxLength << 1) && CodePointCount(value) <= maxLength);
28
- }
29
- // ------------------------------------------------------------------
30
- // IsMinLength
31
- // ------------------------------------------------------------------
32
- /** Returns true if the string length in Unicode code points is greater than or equal to minLength */
33
- export function IsMinLength(value, minLength) {
34
- return value.length >= (minLength << 1) || (value.length >= minLength && CodePointCount(value) >= minLength);
35
- }
@@ -1 +0,0 @@
1
- export declare function EncodeFragment(fragment: string): string;