@pawlogic/dl 0.4.0-staging.7f170e8 → 0.4.0-staging.80830b7

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/dl-npm-update.sh CHANGED
@@ -16,9 +16,10 @@
16
16
  # 次,不加缓存白白打满 npm registry。
17
17
  #
18
18
  # 观测: 每条分支结束 fire-and-forget POST 一条事件到 callback-server
19
- # $CALLBACK_SERVER_URL/internal/dl-selfupdate-event,backend Prometheus
20
- # counter + 结构化日志。TTL 命中频繁(每个沙箱 5min 内 N 次), 按 1/10 采样上报;
19
+ # $CALLBACK_SERVER_URL/sandbox-rpc/dl/selfupdate-event,backend 仅转发到 Honeycomb,
20
+ # schema 校验由本脚本端负责。TTL 命中频繁(每个沙箱 5min 内 N 次), 按 1/10 采样上报;
21
21
  # 其余分支(install / no_tag / no_npm / lock_skip)全量上报。
22
+ # 鉴权: 复用 $DL_PROXY_TOKEN (沙箱 env, hono-pi-e2b 注入), Authorization Bearer。
22
23
  # 上报失败一律 swallow,绝不让观测链阻塞自更新本身。
23
24
 
24
25
  set -u
@@ -29,11 +30,15 @@ TTL=300
29
30
  PACKAGE="@pawlogic/dl"
30
31
 
31
32
  # ── 观测上报: fire-and-forget JSON POST,失败 swallow ────────────────────────
32
- # args: outcome duration_ms
33
+ # args: outcome duration_ms [error_tail_file]
34
+ # error_tail_file 仅 npm_fail 分支传入,读末尾 ~200 字节带上去 —— 之前 npm stderr
35
+ # 全 swallow,根本看不到为什么失败,fleet 升级率 0.24% 都查不到根因。
33
36
  report_event() {
34
37
  _outcome="$1"
35
38
  _dur="${2:-null}"
39
+ _errfile="${3:-}"
36
40
  [ -z "${CALLBACK_SERVER_URL:-}" ] && return 0
41
+ [ -z "${DL_PROXY_TOKEN:-}" ] && return 0
37
42
  command -v curl >/dev/null 2>&1 || return 0
38
43
  _tag="${DL_NPM_DIST_TAG:-unknown}"
39
44
  case "$_tag" in
@@ -45,11 +50,47 @@ report_event() {
45
50
  if [ -f "$_verfile" ] && command -v node >/dev/null 2>&1; then
46
51
  _ver=$(node -e 'try{console.log(require("'"$_verfile"'").version||"")}catch(e){}' 2>/dev/null || echo "")
47
52
  fi
53
+ # 上下文维度: 全部从 hono-pi-e2b 注入的 PI_* env 里读 (e2b-operations.ts:582-627),
54
+ # 加上 E2B sandbox hostname (= sandbox ID)。校验只允许 [A-Za-z0-9_-], 防奇怪字符
55
+ # 破坏 JSON 后续没办法 escape; 不合法 / 空一律 null。
56
+ #
57
+ # 维度用途:
58
+ # - project_id per-project 去重 (COUNT_DISTINCT) 是 fleet 升级率口径
59
+ # - mode dramaland / ilands / ... 按产品线切升级率
60
+ # - user_tier starter / pro / ... 看付费用户升级率差异
61
+ # - user_id 单用户排障 (高基数)
62
+ # - prompt_id LLM turn 级聚合 (高基数)
63
+ # - tool_call_id 单次工具调用关联到自更新 outcome (高基数)
64
+ # - sandbox_id 单沙箱排障; E2B 内 hostname 就是 sandbox ID
65
+ _json_or_null() {
66
+ case "${1:-}" in
67
+ "") echo "null" ;;
68
+ *[!A-Za-z0-9_-]*) echo "null" ;;
69
+ *) printf '"%s"' "$1" ;;
70
+ esac
71
+ }
72
+ _proj_json=$(_json_or_null "${PI_PROJECT_ID:-}")
73
+ _mode_json=$(_json_or_null "${PI_MODE:-}")
74
+ _tier_json=$(_json_or_null "${PI_USER_TIER:-}")
75
+ _user_json=$(_json_or_null "${PI_USER_ID:-}")
76
+ _prompt_json=$(_json_or_null "${PI_PROMPT_ID:-}")
77
+ _toolcall_json=$(_json_or_null "${PI_TOOL_CALL_ID:-}")
78
+ _sandbox_json=$(_json_or_null "${HOSTNAME:-$(hostname 2>/dev/null || echo)}")
79
+ # error_tail: 末尾 200 字节, JSON 不安全字符 (\ " 控制符 / 换行 / tab) 一律压成
80
+ # 单空格; 长度上限挡住 LB 滥用,字符替换让 JSON 不需要复杂 escape。
81
+ _err_json="null"
82
+ if [ -n "$_errfile" ] && [ -f "$_errfile" ]; then
83
+ _err_raw=$(tail -c 200 "$_errfile" 2>/dev/null | tr '\n\r\t\\"' ' ' | tr -d '\000-\037')
84
+ if [ -n "$_err_raw" ]; then
85
+ _err_json="\"$_err_raw\""
86
+ fi
87
+ fi
48
88
  # 后台 curl, -m 2 上限 2s; 不解析响应; stderr/stdout 全丢。
49
89
  (
50
- curl -fsS -m 2 -X POST "${CALLBACK_SERVER_URL%/}/internal/dl-selfupdate-event" \
90
+ curl -fsS -m 2 -X POST "${CALLBACK_SERVER_URL%/}/sandbox-rpc/dl/selfupdate-event" \
51
91
  -H 'content-type: application/json' \
52
- -d "{\"trigger\":\"wrapper\",\"dist_tag\":\"$_tag\",\"outcome\":\"$_outcome\",\"duration_ms\":$_dur,\"installed_version\":\"$_ver\",\"build_short_sha\":null}" \
92
+ -H "authorization: Bearer ${DL_PROXY_TOKEN}" \
93
+ -d "{\"trigger\":\"wrapper\",\"dist_tag\":\"$_tag\",\"outcome\":\"$_outcome\",\"duration_ms\":$_dur,\"installed_version\":\"$_ver\",\"build_short_sha\":null,\"error_tail\":$_err_json,\"project_id\":$_proj_json,\"mode\":$_mode_json,\"user_tier\":$_tier_json,\"user_id\":$_user_json,\"prompt_id\":$_prompt_json,\"tool_call_id\":$_toolcall_json,\"sandbox_id\":$_sandbox_json}" \
53
94
  >/dev/null 2>&1 || true
54
95
  ) &
55
96
  }
@@ -89,7 +130,16 @@ fi
89
130
  # flock 不一定有(busybox sh) → mkdir 兜底原子锁。
90
131
  run_install() {
91
132
  _t0=$(date +%s 2>/dev/null || echo 0)
92
- if npm install -g --silent --no-audit --no-fund --no-update-notifier "$PACKAGE@$DL_NPM_DIST_TAG" >/dev/null 2>&1; then
133
+ # stderr 写到独立文件 npm_fail 分支带末尾给观测端;stdout 仍丢掉。
134
+ _errlog=$(mktemp 2>/dev/null || echo "/tmp/dl-npm-err.$$")
135
+ # 必须带 --prefix=/home/user/.local 跟 HOME=/home/user,跟 sandbox-template
136
+ # 首次安装那一段对齐 (packages/sandbox-template/scripts/publish-e2b-template.ts
137
+ # 里 wrapper 写法)。沙箱进程 owner 是 user,无 /usr/lib/node_modules 写权限;
138
+ # 不带 prefix → npm 走默认全局路径 → EACCES 1 秒内挂掉, stderr 又被吞 → fleet
139
+ # 自更新 100% 失败但完全黑盒。这是 PR #729 漏掉 prefix 留下的 bug。
140
+ if HOME=/home/user npm install -g --prefix=/home/user/.local \
141
+ --no-audit --no-fund --no-update-notifier \
142
+ "$PACKAGE@$DL_NPM_DIST_TAG" >/dev/null 2>"$_errlog"; then
93
143
  _outcome=ok
94
144
  else
95
145
  _outcome=npm_fail
@@ -114,7 +164,12 @@ run_install() {
114
164
  chmod +x /home/user/.local/lib/dl-npm-update.sh 2>/dev/null || true
115
165
  fi
116
166
  echo "$now" > "$STAMP"
117
- report_event "$_outcome" "$_dur_ms"
167
+ if [ "$_outcome" = "npm_fail" ]; then
168
+ report_event "$_outcome" "$_dur_ms" "$_errlog"
169
+ else
170
+ report_event "$_outcome" "$_dur_ms"
171
+ fi
172
+ rm -f "$_errlog" 2>/dev/null || true
118
173
  }
119
174
 
120
175
  if command -v flock >/dev/null 2>&1; then