@pawlogic/dl 0.37.1 → 0.38.1
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 +109 -34
- package/dl.mjs +1 -1
- package/ilands.mjs +1 -1
- package/package.json +1 -1
package/dl-npm-update.sh
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# dl 沙箱内自更新 (npm 版本) —— 替代 dl-selfupdate.sh 的 GCS 路径。
|
|
3
3
|
#
|
|
4
|
-
# wrapper 在 exec dl
|
|
5
|
-
#
|
|
6
|
-
# (agent 失败重试天然吃到新版)。
|
|
4
|
+
# wrapper 在 exec dl 之前同步调一次本脚本。更新失败不阻止继续调用已有 bundle。
|
|
5
|
+
# --cli 绕过 TTL,输出 outcome 给 self-update 命令;安装与恢复仍使用同一把锁。
|
|
7
6
|
#
|
|
8
7
|
# dist-tag 来源,优先级从高到低:
|
|
9
8
|
# 1. PI_DL_DIST_TAG —— hono-pi-e2b 每条命令注入 (e2b-operations.ts),反映
|
|
@@ -37,6 +36,11 @@ STAMP=/tmp/dl-npm-checkedat
|
|
|
37
36
|
LOCK=/tmp/dl-npm-update.lock
|
|
38
37
|
TTL=300
|
|
39
38
|
PACKAGE="@pawlogic/dl"
|
|
39
|
+
# 独立下发恢复脚本时,拒绝被尚未包含修复的 npm 包降级。
|
|
40
|
+
UPDATER_REVISION=2
|
|
41
|
+
CLI=false
|
|
42
|
+
[ "${1:-}" = "--cli" ] && CLI=true
|
|
43
|
+
RECOVERED=false
|
|
40
44
|
|
|
41
45
|
# 注入值优先,回落到 wrapper 烤死的值(理由见文件头)。
|
|
42
46
|
#
|
|
@@ -65,6 +69,17 @@ report_event() {
|
|
|
65
69
|
_outcome="$1"
|
|
66
70
|
_dur="${2:-null}"
|
|
67
71
|
_errfile="${3:-}"
|
|
72
|
+
_code=""
|
|
73
|
+
_syscall=""
|
|
74
|
+
if [ -n "$_errfile" ] && [ -f "$_errfile" ]; then
|
|
75
|
+
_code=$(awk '/^npm (error|ERR!) code [A-Z0-9_]+$/ {print $4; exit}' "$_errfile")
|
|
76
|
+
_syscall=$(awk '/^npm (error|ERR!) syscall [a-z]+$/ {print $4; exit}' "$_errfile")
|
|
77
|
+
fi
|
|
78
|
+
if [ "$CLI" = true ]; then
|
|
79
|
+
printf '%s\n' "$_outcome"
|
|
80
|
+
printf 'code=%s syscall=%s recovered=%s\n' "$_code" "$_syscall" "$RECOVERED" >&2
|
|
81
|
+
return 0
|
|
82
|
+
fi
|
|
68
83
|
[ -z "${CALLBACK_SERVER_URL:-}" ] && return 0
|
|
69
84
|
[ -z "${DL_PROXY_TOKEN:-}" ] && return 0
|
|
70
85
|
command -v curl >/dev/null 2>&1 || return 0
|
|
@@ -118,9 +133,9 @@ report_event() {
|
|
|
118
133
|
curl -fsS -m 2 -X POST "${CALLBACK_SERVER_URL%/}/sandbox-rpc/dl/selfupdate-event" \
|
|
119
134
|
-H 'content-type: application/json' \
|
|
120
135
|
-H "authorization: Bearer ${DL_PROXY_TOKEN}" \
|
|
121
|
-
-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}" \
|
|
136
|
+
-d "{\"trigger\":\"wrapper\",\"dist_tag\":\"$_tag\",\"outcome\":\"$_outcome\",\"duration_ms\":$_dur,\"installed_version\":\"$_ver\",\"build_short_sha\":null,\"error_tail\":$_err_json,\"error_code\":\"$_code\",\"error_syscall\":\"$_syscall\",\"recovered\":$RECOVERED,\"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}" \
|
|
122
137
|
>/dev/null 2>&1 || true
|
|
123
|
-
) &
|
|
138
|
+
) 9>&- &
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
# 采样 TTL hit 上报: 10 选 1。awk 比 $RANDOM 更便携 (busybox sh 默认没 $RANDOM)。
|
|
@@ -144,33 +159,72 @@ fi
|
|
|
144
159
|
# 整个 turn 都还在用另一条 lane 的 dl —— 自愈就不"立刻"了。tag 跟本次解析出
|
|
145
160
|
# 来的不一致直接跳过 TTL 重装。老格式(只有 epoch)解出来的 tag 是空串,同样
|
|
146
161
|
# 判成不一致,升级后第一次调用自然会重装一遍。
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
check_ttl() {
|
|
163
|
+
now=$(date +%s 2>/dev/null) || exit 0
|
|
164
|
+
last=0
|
|
165
|
+
last_tag=""
|
|
166
|
+
if [ -f "$STAMP" ]; then
|
|
167
|
+
_stamp_line=$(head -n1 "$STAMP" 2>/dev/null || echo "")
|
|
168
|
+
last="${_stamp_line%% *}"
|
|
169
|
+
case "$_stamp_line" in
|
|
170
|
+
*' '*) last_tag="${_stamp_line#* }" ;;
|
|
171
|
+
*) last_tag="" ;;
|
|
172
|
+
esac
|
|
173
|
+
case "$last" in
|
|
174
|
+
''|*[!0-9]*) last=0 ;;
|
|
175
|
+
esac
|
|
176
|
+
fi
|
|
177
|
+
if [ "$CLI" = false ] && [ -f /home/user/.local/lib/node_modules/@pawlogic/dl/dl.mjs ] \
|
|
178
|
+
&& [ -f /home/user/.local/lib/node_modules/@pawlogic/dl/ilands.mjs ] && [ "$last_tag" = "$TAG" ]; then
|
|
179
|
+
delta=$((now - last))
|
|
180
|
+
if [ "$delta" -ge 0 ] && [ "$delta" -lt "$TTL" ]; then
|
|
181
|
+
if should_sample_ttl; then
|
|
182
|
+
report_event ttl_hit null
|
|
183
|
+
fi
|
|
184
|
+
exit 0
|
|
166
185
|
fi
|
|
167
|
-
exit 0
|
|
168
186
|
fi
|
|
169
|
-
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
check_ttl
|
|
170
190
|
|
|
171
191
|
# 单飞锁:并发 dl 调用同时进来时只让一个跑 npm install,其他直接退出。
|
|
172
192
|
# flock 不一定有(busybox sh) → mkdir 兜底原子锁。
|
|
193
|
+
install_package() {
|
|
194
|
+
# 在持锁的 shell 内限制 npm 耗时,避免 CLI 超时只杀 shell 却留下安装子进程。
|
|
195
|
+
HOME=/home/user timeout -k 5s 30s npm install -g --prefix=/home/user/.local \
|
|
196
|
+
--no-audit --no-fund --no-update-notifier --force \
|
|
197
|
+
"$PACKAGE@$TAG" >/dev/null 2>"$_errlog"
|
|
198
|
+
_install_status=$?
|
|
199
|
+
case "$_install_status" in
|
|
200
|
+
124|137) printf 'npm error code ETIMEDOUT\n' >> "$_errlog" ;;
|
|
201
|
+
esac
|
|
202
|
+
return "$_install_status"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# 只备份本次 ENOTEMPTY 明确指向的 dl 临时目录,不扫描/删除其它包或工作文件。
|
|
206
|
+
recover_retired_directory() {
|
|
207
|
+
node - "$_errlog" <<'NODE'
|
|
208
|
+
const fs = require('node:fs');
|
|
209
|
+
const path = require('node:path');
|
|
210
|
+
try {
|
|
211
|
+
const log = fs.readFileSync(process.argv[2], 'utf8');
|
|
212
|
+
const field = name => log.match(new RegExp('^npm (?:error|ERR!) ' + name + ' (.+)$', 'm'))?.[1];
|
|
213
|
+
const scope = '/home/user/.local/lib/node_modules/@pawlogic';
|
|
214
|
+
const dest = field('dest');
|
|
215
|
+
if (field('code') !== 'ENOTEMPTY' || field('syscall') !== 'rename'
|
|
216
|
+
|| field('path') !== scope + '/dl' || !dest
|
|
217
|
+
|| path.dirname(dest) !== scope || !/^\.dl-[A-Za-z0-9]+$/.test(path.basename(dest))
|
|
218
|
+
|| fs.realpathSync(scope) !== scope || !fs.lstatSync(dest).isDirectory()) process.exit(1);
|
|
219
|
+
const backup = fs.mkdtempSync('/home/user/.local/lib/dl-update-backup-');
|
|
220
|
+
fs.renameSync(dest, path.join(backup, path.basename(dest)));
|
|
221
|
+
} catch { process.exit(1); }
|
|
222
|
+
NODE
|
|
223
|
+
}
|
|
224
|
+
|
|
173
225
|
run_install() {
|
|
226
|
+
# 首次安装等待锁期间,另一个入口可能已经装好并写入 TTL。
|
|
227
|
+
check_ttl
|
|
174
228
|
_t0=$(date +%s 2>/dev/null || echo 0)
|
|
175
229
|
# stderr 写到独立文件 npm_fail 分支带末尾给观测端;stdout 仍丢掉。
|
|
176
230
|
_errlog=$(mktemp 2>/dev/null || echo "/tmp/dl-npm-err.$$")
|
|
@@ -187,12 +241,23 @@ run_install() {
|
|
|
187
241
|
# --force to overwrite"); --force 允许 npm 覆盖。Honeycomb 上 staging
|
|
188
242
|
# dl-selfupdate dataset 看到的所有 npm_fail outcome (#741 接通后) 都是这条,
|
|
189
243
|
# 加 --force 就清零。
|
|
190
|
-
if
|
|
191
|
-
--no-audit --no-fund --no-update-notifier --force \
|
|
192
|
-
"$PACKAGE@$TAG" >/dev/null 2>"$_errlog"; then
|
|
244
|
+
if install_package; then
|
|
193
245
|
_outcome=ok
|
|
194
246
|
else
|
|
195
247
|
_outcome=npm_fail
|
|
248
|
+
if recover_retired_directory; then
|
|
249
|
+
if install_package; then
|
|
250
|
+
_outcome=ok
|
|
251
|
+
RECOVERED=true
|
|
252
|
+
fi
|
|
253
|
+
fi
|
|
254
|
+
fi
|
|
255
|
+
# npm exit 0 还不足以证明两个入口完整,缺失时不记为成功。
|
|
256
|
+
if [ "$_outcome" = ok ] && { [ ! -s /home/user/.local/lib/node_modules/@pawlogic/dl/dl.mjs ] \
|
|
257
|
+
|| [ ! -s /home/user/.local/lib/node_modules/@pawlogic/dl/ilands.mjs ]; }; then
|
|
258
|
+
_outcome=npm_fail
|
|
259
|
+
RECOVERED=false
|
|
260
|
+
printf 'npm error code INCOMPLETE_INSTALL\n' >> "$_errlog"
|
|
196
261
|
fi
|
|
197
262
|
_t1=$(date +%s 2>/dev/null || echo 0)
|
|
198
263
|
_dur_ms=$(( (_t1 - _t0) * 1000 ))
|
|
@@ -207,11 +272,15 @@ run_install() {
|
|
|
207
272
|
# dl-npm-update.sh), npm install 完成后覆盖一份到 wrapper 调用的固定路径。
|
|
208
273
|
# 1 代 lag: 本次仍跑老逻辑, 新逻辑下次 dl 调用生效 —— 比"等 e2b template republish
|
|
209
274
|
# + 老 snapshot 自然下线"快好几个量级, snapshot resume 的沙箱也能跟上。
|
|
210
|
-
#
|
|
211
|
-
|
|
275
|
+
# 原子替换,避免覆盖当前 shell 仍在读取的 inode;失败时不从旧包回滚恢复脚本。
|
|
276
|
+
_packaged_updater=/home/user/.local/lib/node_modules/@pawlogic/dl/dl-npm-update.sh
|
|
277
|
+
_packaged_revision=$(awk -F= '/^UPDATER_REVISION=[0-9]+$/ {print $2; exit}' "$_packaged_updater" 2>/dev/null || true)
|
|
278
|
+
if [ "$_outcome" = ok ] && [ "${_packaged_revision:-0}" -ge "$UPDATER_REVISION" ]; then
|
|
279
|
+
_refresh=$(mktemp /home/user/.local/lib/dl-npm-update.XXXXXX)
|
|
212
280
|
cp /home/user/.local/lib/node_modules/@pawlogic/dl/dl-npm-update.sh \
|
|
213
|
-
|
|
214
|
-
|
|
281
|
+
"$_refresh" 2>/dev/null \
|
|
282
|
+
&& chmod +x "$_refresh" 2>/dev/null \
|
|
283
|
+
&& mv -f "$_refresh" /home/user/.local/lib/dl-npm-update.sh 2>/dev/null || true
|
|
215
284
|
fi
|
|
216
285
|
echo "$now $TAG" > "$STAMP"
|
|
217
286
|
if [ "$_outcome" = "npm_fail" ]; then
|
|
@@ -224,7 +293,13 @@ run_install() {
|
|
|
224
293
|
|
|
225
294
|
if command -v flock >/dev/null 2>&1; then
|
|
226
295
|
(
|
|
227
|
-
|
|
296
|
+
# 首次安装允许等另一入口完成,已有 bundle 的日常调用仍直接跳过。
|
|
297
|
+
_lock_wait=0
|
|
298
|
+
if [ ! -f /home/user/.local/lib/node_modules/@pawlogic/dl/dl.mjs ] \
|
|
299
|
+
|| [ ! -f /home/user/.local/lib/node_modules/@pawlogic/dl/ilands.mjs ]; then
|
|
300
|
+
_lock_wait=30
|
|
301
|
+
fi
|
|
302
|
+
if ! flock -w "$_lock_wait" 9; then
|
|
228
303
|
report_event lock_skip null
|
|
229
304
|
exit 0
|
|
230
305
|
fi
|