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.
- package/CHANGELOG.md +29 -0
- package/README.md +32 -28
- package/build/cli.mjs +121 -0
- package/build/extensions/proactive.js +47 -6
- package/build/src/commands.js +78 -2
- package/build/src/config.js +511 -4
- package/build/src/daemon-config.js +221 -0
- package/build/src/daemon-contact.js +103 -0
- package/build/src/daemon-host.js +84 -0
- package/build/src/daemon.js +193 -3
- package/build/src/health.js +84 -0
- package/build/src/lineage.js +41 -2
- package/build/src/notify.js +183 -0
- package/build/src/progress-eta.js +134 -0
- package/build/src/trigger.js +43 -0
- package/build/src/trust-loop.js +321 -0
- package/build/src/trust-metrics.js +236 -0
- package/build/src/trust-notify.js +73 -0
- package/build/src/trust-policy.js +58 -0
- package/build/src/trust-registry.js +323 -0
- package/daemon/launchd.plist +48 -0
- package/daemon/pi-proactive-daemon.service +29 -0
- package/daemon/run-daemon.sh +84 -0
- package/node_modules/typebox/build/compile/code.mjs +2 -1
- package/node_modules/typebox/build/compile/validator.mjs +1 -5
- package/node_modules/typebox/build/format/date_time.d.mts +1 -2
- package/node_modules/typebox/build/format/date_time.mjs +3 -12
- package/node_modules/typebox/build/format/duration.d.mts +2 -2
- package/node_modules/typebox/build/format/duration.mjs +2 -2
- package/node_modules/typebox/build/format/idn_email.d.mts +0 -1
- package/node_modules/typebox/build/format/idn_email.mjs +0 -1
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
- package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
- package/node_modules/typebox/build/format/ipv4.mjs +2 -2
- package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
- package/node_modules/typebox/build/format/ipv6.mjs +1 -1
- package/node_modules/typebox/build/format/iri.mjs +9 -16
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/guard/emit.mjs +2 -2
- package/node_modules/typebox/build/guard/guard.d.mts +5 -7
- package/node_modules/typebox/build/guard/guard.mjs +9 -14
- package/node_modules/typebox/build/guard/index.mjs +0 -6
- package/node_modules/typebox/build/guard/{unicode/unicode_segment.mjs → string.mjs} +19 -16
- package/node_modules/typebox/build/schema/build.mjs +5 -5
- package/node_modules/typebox/build/schema/check.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/_context.d.mts +1 -5
- package/node_modules/typebox/build/schema/engine/_context.mjs +3 -16
- package/node_modules/typebox/build/schema/engine/_functions.d.mts +2 -2
- package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/_reducer.d.mts +3 -3
- package/node_modules/typebox/build/schema/engine/_refine.d.mts +5 -5
- package/node_modules/typebox/build/schema/engine/_refine.mjs +9 -14
- package/node_modules/typebox/build/schema/engine/_stack.d.mts +20 -34
- package/node_modules/typebox/build/schema/engine/_stack.mjs +202 -143
- package/node_modules/typebox/build/schema/engine/_unique.d.mts +2 -4
- package/node_modules/typebox/build/schema/engine/_unique.mjs +3 -7
- package/node_modules/typebox/build/schema/engine/additionalItems.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +8 -5
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +32 -27
- package/node_modules/typebox/build/schema/engine/allOf.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/allOf.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/anyOf.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/anyOf.mjs +9 -4
- package/node_modules/typebox/build/schema/engine/boolean.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/boolean.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/const.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/const.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/contains.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/contains.mjs +9 -5
- package/node_modules/typebox/build/schema/engine/dependencies.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/dependencies.mjs +7 -5
- package/node_modules/typebox/build/schema/engine/dependentRequired.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/dependentRequired.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/dependentSchemas.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +1 -2
- package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +8 -12
- package/node_modules/typebox/build/schema/engine/enum.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/enum.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/exclusiveMaximum.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/exclusiveMaximum.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/exclusiveMinimum.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/exclusiveMinimum.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/format.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/format.mjs +8 -11
- package/node_modules/typebox/build/schema/engine/if.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/if.mjs +13 -5
- package/node_modules/typebox/build/schema/engine/index.d.mts +0 -1
- package/node_modules/typebox/build/schema/engine/index.mjs +0 -1
- package/node_modules/typebox/build/schema/engine/items.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/items.mjs +5 -8
- package/node_modules/typebox/build/schema/engine/maxContains.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +8 -4
- package/node_modules/typebox/build/schema/engine/maxItems.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/maxItems.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/maxLength.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/maxLength.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/maxProperties.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/maxProperties.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/maximum.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/maximum.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/minContains.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/minContains.mjs +9 -5
- package/node_modules/typebox/build/schema/engine/minItems.d.mts +5 -5
- package/node_modules/typebox/build/schema/engine/minItems.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/minLength.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/minLength.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/minProperties.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/minProperties.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/minimum.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/minimum.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/multipleOf.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/multipleOf.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/not.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/not.mjs +8 -4
- package/node_modules/typebox/build/schema/engine/oneOf.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +11 -6
- package/node_modules/typebox/build/schema/engine/pattern.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/pattern.mjs +7 -3
- package/node_modules/typebox/build/schema/engine/patternProperties.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +5 -6
- package/node_modules/typebox/build/schema/engine/prefixItems.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/prefixItems.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/properties.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/properties.mjs +3 -4
- package/node_modules/typebox/build/schema/engine/propertyNames.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +10 -7
- package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +8 -12
- package/node_modules/typebox/build/schema/engine/ref.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/ref.mjs +14 -18
- package/node_modules/typebox/build/schema/engine/required.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/required.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/schema.d.mts +7 -7
- package/node_modules/typebox/build/schema/engine/schema.mjs +138 -136
- package/node_modules/typebox/build/schema/engine/type.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/type.mjs +6 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedItems.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +9 -5
- package/node_modules/typebox/build/schema/engine/unevaluatedProperties.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +9 -5
- package/node_modules/typebox/build/schema/engine/uniqueItems.d.mts +4 -4
- package/node_modules/typebox/build/schema/engine/uniqueItems.mjs +6 -2
- package/node_modules/typebox/build/schema/errors.mjs +1 -1
- package/node_modules/typebox/build/schema/intern/intern.d.mts +9 -6
- package/node_modules/typebox/build/schema/intern/intern.mjs +56 -90
- package/node_modules/typebox/build/schema/parse.mjs +0 -3
- package/node_modules/typebox/build/schema/pointer/pointer.d.mts +6 -1
- package/node_modules/typebox/build/schema/pointer/pointer.mjs +64 -47
- package/node_modules/typebox/build/schema/resolve/resolve.d.mts +32 -6
- package/node_modules/typebox/build/schema/resolve/resolve.mjs +227 -231
- package/node_modules/typebox/build/schema/schema.d.mts +0 -1
- package/node_modules/typebox/build/schema/schema.mjs +0 -1
- package/node_modules/typebox/build/system/hashing/hash.mjs +34 -63
- package/node_modules/typebox/build/system/locale/en_US.mjs +1 -2
- package/node_modules/typebox/build/system/memory/assign.d.mts +8 -2
- package/node_modules/typebox/build/system/memory/assign.mjs +6 -13
- package/node_modules/typebox/build/system/memory/clone.d.mts +5 -4
- package/node_modules/typebox/build/system/memory/clone.mjs +96 -8
- package/node_modules/typebox/build/system/memory/create.d.mts +5 -1
- package/node_modules/typebox/build/system/memory/create.mjs +13 -18
- package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
- package/node_modules/typebox/build/system/memory/discard.mjs +8 -9
- package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
- package/node_modules/typebox/build/system/memory/update.d.mts +4 -1
- package/node_modules/typebox/build/system/memory/update.mjs +16 -43
- package/node_modules/typebox/build/system/settings/settings.d.mts +0 -11
- package/node_modules/typebox/build/system/settings/settings.mjs +0 -14
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +24 -2
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +29 -2
- package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +0 -1
- package/node_modules/typebox/build/type/engine/evaluate/index.mjs +0 -1
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +1 -5
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +1 -6
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +3 -1
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +4 -1
- package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +3 -1
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +4 -1
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +3 -1
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +4 -1
- package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
- package/node_modules/typebox/build/type/extends/inference.mjs +2 -15
- package/node_modules/typebox/build/type/extends/tuple.d.mts +1 -2
- package/node_modules/typebox/build/type/extends/tuple.mjs +1 -21
- package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
- package/node_modules/typebox/build/value/clone/clone.d.mts +3 -4
- package/node_modules/typebox/build/value/clone/clone.mjs +7 -5
- package/node_modules/typebox/build/value/parse/parse.mjs +4 -10
- package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +14 -25
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +84 -52
- package/package.json +11 -3
- package/skills/pi-proactive/SKILL.md +27 -24
- package/tools/cli.mjs +121 -0
- package/tools/daemon-install.sh +397 -0
- package/tools/daemon-uninstall.sh +226 -0
- package/node_modules/typebox/build/guard/unicode/unicode.d.mts +0 -6
- package/node_modules/typebox/build/guard/unicode/unicode.mjs +0 -35
- package/node_modules/typebox/build/schema/engine/_pathing.d.mts +0 -1
- package/node_modules/typebox/build/schema/engine/_pathing.mjs +0 -11
- package/node_modules/typebox/build/schema/meta/draft_2019_09.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_2019_09.mjs +0 -288
- package/node_modules/typebox/build/schema/meta/draft_2020_12.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_2020_12.mjs +0 -300
- package/node_modules/typebox/build/schema/meta/draft_3.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_3.mjs +0 -143
- package/node_modules/typebox/build/schema/meta/draft_4.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_4.mjs +0 -149
- package/node_modules/typebox/build/schema/meta/draft_6.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_6.mjs +0 -155
- package/node_modules/typebox/build/schema/meta/draft_7.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/draft_7.mjs +0 -172
- package/node_modules/typebox/build/schema/meta/index.d.mts +0 -2
- package/node_modules/typebox/build/schema/meta/index.mjs +0 -2
- package/node_modules/typebox/build/schema/meta/meta.d.mts +0 -9
- package/node_modules/typebox/build/schema/meta/meta.mjs +0 -15
- package/node_modules/typebox/build/schema/meta/schema.d.mts +0 -6
- package/node_modules/typebox/build/schema/meta/schema.mjs +0 -1
- package/node_modules/typebox/build/system/memory/deep.d.mts +0 -2
- package/node_modules/typebox/build/system/memory/deep.mjs +0 -108
- package/node_modules/typebox/build/system/memory/finalize.d.mts +0 -4
- package/node_modules/typebox/build/system/memory/finalize.mjs +0 -30
- package/node_modules/typebox/build/system/memory/keys.d.mts +0 -2
- package/node_modules/typebox/build/system/memory/keys.mjs +0 -8
- package/node_modules/typebox/build/system/settings/internal.d.mts +0 -2
- package/node_modules/typebox/build/system/settings/internal.mjs +0 -20
- package/node_modules/typebox/build/type/engine/call/index.d.mts +0 -1
- package/node_modules/typebox/build/type/engine/call/index.mjs +0 -1
- package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +0 -30
- package/node_modules/typebox/build/type/engine/evaluate/property.mjs +0 -29
- package/node_modules/typebox/build/type/engine/this/index.d.mts +0 -1
- package/node_modules/typebox/build/type/engine/this/index.mjs +0 -1
- package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +0 -2
- package/node_modules/typebox/build/value/repair/from_cyclic.mjs +0 -6
- /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;
|