@co0ontty/wand 1.41.3 → 1.42.0

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.
@@ -0,0 +1,238 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { chmodSync, existsSync, mkdtempSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import { detectInstalledScope } from "./tui/commands.js";
7
+ function shellQuote(value) {
8
+ return `'${value.replace(/'/g, `'\\''`)}'`;
9
+ }
10
+ function shellArray(values) {
11
+ return values.map((value) => shellQuote(value)).join(" ");
12
+ }
13
+ function boolShell(value) {
14
+ return value ? "1" : "0";
15
+ }
16
+ function resolveBashPath() {
17
+ return existsSync("/bin/bash") ? "/bin/bash" : "bash";
18
+ }
19
+ function detectServiceScope() {
20
+ try {
21
+ return detectInstalledScope();
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ function buildHelperScript(opts, logPath, serviceScope) {
28
+ const isSystemdManaged = !!process.env.INVOCATION_ID;
29
+ const isLaunchdManaged = !!process.env.LAUNCHD_SOCKET || !!process.env.XPC_SERVICE_NAME;
30
+ const shouldStartService = serviceScope !== null;
31
+ const shouldRestartStandalone = !shouldStartService;
32
+ const npmCache = opts.env.npm_config_cache || path.join(os.homedir(), ".npm");
33
+ const timeoutSec = Math.max(30, Math.ceil((opts.timeoutMs ?? 300_000) / 1000));
34
+ const cliArgArray = opts.cliArgs;
35
+ return `#!/usr/bin/env bash
36
+ set -u
37
+ LOG=${shellQuote(logPath)}
38
+ exec >>"$LOG" 2>&1
39
+ echo "[wand-update] started at $(date -Is)"
40
+ echo "[wand-update] parent pid: ${opts.parentPid}"
41
+ echo "[wand-update] install spec: ${opts.installSpec}"
42
+
43
+ export PATH=${shellQuote(opts.env.PATH || process.env.PATH || "")}
44
+ export HOME=${shellQuote(opts.env.HOME || os.homedir())}
45
+ export npm_config_cache=${shellQuote(npmCache)}
46
+ CONFIG_PATH=${shellQuote(opts.configPath)}
47
+ INSTALL_SPEC=${shellQuote(opts.installSpec)}
48
+ PARENT_PID=${opts.parentPid}
49
+ SERVICE_SCOPE=${shellQuote(serviceScope ?? "")}
50
+ SHOULD_START_SERVICE=${boolShell(shouldStartService)}
51
+ SHOULD_RESTART_STANDALONE=${boolShell(shouldRestartStandalone)}
52
+ TIMEOUT_SEC=${timeoutSec}
53
+ CLI_ARGS=(${shellArray(cliArgArray)})
54
+
55
+ run() {
56
+ echo "+ $*"
57
+ "$@"
58
+ }
59
+
60
+ run_best_effort() {
61
+ echo "+ $*"
62
+ "$@" || true
63
+ }
64
+
65
+ wait_for_parent_exit() {
66
+ local pid="$1"
67
+ local i=0
68
+ while kill -0 "$pid" 2>/dev/null; do
69
+ i=$((i + 1))
70
+ if [ "$i" -ge 120 ]; then
71
+ echo "[wand-update] parent still alive after 60s; continuing"
72
+ return 0
73
+ fi
74
+ sleep 0.5
75
+ done
76
+ }
77
+
78
+ clean_leftovers() {
79
+ local npm_root
80
+ npm_root="$(npm root -g 2>/dev/null || true)"
81
+ if [ -n "$npm_root" ] && [ -d "$npm_root/@co0ontty" ]; then
82
+ find "$npm_root/@co0ontty" -maxdepth 1 -name ".wand-*" -type d -print -exec rm -rf {} + 2>/dev/null || true
83
+ fi
84
+ }
85
+
86
+ npm_install_wand() {
87
+ if command -v timeout >/dev/null 2>&1; then
88
+ run timeout "$TIMEOUT_SEC" npm install -g "$INSTALL_SPEC"
89
+ else
90
+ run npm install -g "$INSTALL_SPEC"
91
+ fi
92
+ }
93
+
94
+ npm_install_wand_force() {
95
+ if command -v timeout >/dev/null 2>&1; then
96
+ run timeout "$TIMEOUT_SEC" npm install -g --force "$INSTALL_SPEC"
97
+ else
98
+ run npm install -g --force "$INSTALL_SPEC"
99
+ fi
100
+ }
101
+
102
+ restart_or_start_service() {
103
+ if [ "$SHOULD_START_SERVICE" = "1" ]; then
104
+ if [ "$SERVICE_SCOPE" = "user" ]; then
105
+ run_best_effort wand service:install --user -c "$CONFIG_PATH"
106
+ run_best_effort systemctl --user restart wand.service
107
+ else
108
+ run_best_effort wand service:install -c "$CONFIG_PATH"
109
+ run_best_effort systemctl restart wand.service
110
+ fi
111
+ return
112
+ fi
113
+
114
+ if [ "$SHOULD_RESTART_STANDALONE" = "1" ]; then
115
+ echo "[wand-update] starting standalone process"
116
+ cd ${shellQuote(opts.cwd)}
117
+ local global_cli
118
+ global_cli="$(npm root -g 2>/dev/null)/@co0ontty/wand/dist/cli.js"
119
+ if [ -f "$global_cli" ]; then
120
+ nohup ${shellQuote(process.execPath)} "$global_cli" "$\{CLI_ARGS[@]}" >>"$LOG" 2>&1 &
121
+ else
122
+ nohup wand "$\{CLI_ARGS[@]}" >>"$LOG" 2>&1 &
123
+ fi
124
+ fi
125
+ }
126
+
127
+ main() {
128
+ if [ "$SERVICE_SCOPE" = "user" ]; then
129
+ run_best_effort systemctl --user stop wand.service
130
+ elif [ "$SERVICE_SCOPE" = "system" ]; then
131
+ run_best_effort systemctl stop wand.service
132
+ fi
133
+
134
+ wait_for_parent_exit "$PARENT_PID"
135
+ clean_leftovers
136
+
137
+ echo "[wand-update] installing"
138
+ if ! npm_install_wand; then
139
+ echo "[wand-update] first install failed; retrying with uninstall + force install"
140
+ clean_leftovers
141
+ run_best_effort npm uninstall -g @co0ontty/wand
142
+ clean_leftovers
143
+ npm_install_wand_force
144
+ fi
145
+
146
+ run wand init -c "$CONFIG_PATH"
147
+ restart_or_start_service
148
+ echo "[wand-update] completed at $(date -Is)"
149
+ }
150
+
151
+ main
152
+ `;
153
+ }
154
+ function spawnDetached(scriptPath, _logPath, env, serviceScope) {
155
+ const baseEnv = { ...env };
156
+ const trySpawn = (cmd, args, method) => {
157
+ try {
158
+ const child = spawn(cmd, args, {
159
+ detached: true,
160
+ stdio: "ignore",
161
+ env: baseEnv,
162
+ });
163
+ child.unref();
164
+ return { pid: child.pid, method };
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ };
170
+ if (process.platform === "linux") {
171
+ const unitName = `wand-update-${process.pid}-${Date.now()}`;
172
+ const bashPath = resolveBashPath();
173
+ const runTransient = (args, method) => {
174
+ const res = spawnSync("systemd-run", args, {
175
+ encoding: "utf8",
176
+ timeout: 5000,
177
+ env: baseEnv,
178
+ stdio: "ignore",
179
+ });
180
+ return res.status === 0 ? { method } : null;
181
+ };
182
+ const userService = runTransient(["--user", `--unit=${unitName}`, "--quiet", "--collect", bashPath, scriptPath], "systemd-run --user") ??
183
+ runTransient(["--user", `--unit=${unitName}`, "--quiet", bashPath, scriptPath], "systemd-run --user");
184
+ if (userService)
185
+ return userService;
186
+ const systemService = runTransient([`--unit=${unitName}`, "--quiet", "--collect", bashPath, scriptPath], "systemd-run") ??
187
+ runTransient([`--unit=${unitName}`, "--quiet", bashPath, scriptPath], "systemd-run");
188
+ if (systemService)
189
+ return systemService;
190
+ if (serviceScope) {
191
+ return null;
192
+ }
193
+ const setsid = spawnSync("setsid", ["--version"], { encoding: "utf8", timeout: 2000 });
194
+ if (setsid.status === 0) {
195
+ const result = trySpawn("setsid", [bashPath, scriptPath], "setsid");
196
+ if (result)
197
+ return result;
198
+ }
199
+ }
200
+ if (process.platform === "darwin") {
201
+ const result = trySpawn("nohup", [resolveBashPath(), scriptPath], "nohup");
202
+ if (result)
203
+ return result;
204
+ }
205
+ return trySpawn(resolveBashPath(), [scriptPath], "bash detached");
206
+ }
207
+ export function startDetachedUpdateHelper(opts) {
208
+ const dir = mkdtempSync(path.join(os.tmpdir(), "wand-update-"));
209
+ const scriptPath = path.join(dir, "update.sh");
210
+ const logPath = path.join(dir, "update.log");
211
+ const serviceScope = detectServiceScope();
212
+ writeFileSync(scriptPath, buildHelperScript(opts, logPath, serviceScope), { encoding: "utf8", mode: 0o700 });
213
+ chmodSync(scriptPath, 0o700);
214
+ const spawned = spawnDetached(scriptPath, logPath, opts.env, serviceScope);
215
+ if (!spawned) {
216
+ return {
217
+ started: false,
218
+ scriptPath,
219
+ logPath,
220
+ message: "无法启动独立更新 helper。",
221
+ };
222
+ }
223
+ return {
224
+ started: true,
225
+ scriptPath,
226
+ logPath,
227
+ pid: spawned.pid,
228
+ message: `独立更新 helper 已启动 (${spawned.method})。日志: ${logPath}`,
229
+ };
230
+ }
231
+ export function canUseDetachedUpdateHelper() {
232
+ if (process.platform === "win32")
233
+ return false;
234
+ if (existsSync("/bin/bash"))
235
+ return true;
236
+ const res = spawnSync("bash", ["--version"], { encoding: "utf8", timeout: 2000 });
237
+ return res.status === 0;
238
+ }
@@ -8888,7 +8888,7 @@
8888
8888
  // 标签直接用 Claude CLI 原生 magic word:think / think hard / ultrathink。
8889
8889
  // 这样用户一眼能对上官方文档里的思考强度档位,PTY 模式下也是这几个词被注入到 prompt 前缀。
8890
8890
  var THINKING_LEVELS = [
8891
- { id: "off", label: "off", hint: "不启用思考(CLI 无前缀;SDK 关闭 thinking;Codex --reasoning-effort minimal)" },
8891
+ { id: "off", label: "off", hint: "不启用思考(CLI 无前缀;SDK 关闭 thinking;Codex minimal)" },
8892
8892
  { id: "standard", label: "think", hint: "Claude CLI: think · SDK budget 4096 · Codex low" },
8893
8893
  { id: "deep", label: "think hard", hint: "Claude CLI: think hard · SDK budget 16000 · Codex medium" },
8894
8894
  { id: "max", label: "ultrathink", hint: "Claude CLI: ultrathink · SDK budget 31999 · Codex high" }
@@ -11322,7 +11322,9 @@
11322
11322
  msgEl.classList.remove("hidden");
11323
11323
  }
11324
11324
  updateBtn.classList.add("hidden");
11325
- if (data.restartRequired !== false) {
11325
+ if (data.detachedUpdate) {
11326
+ showRestartOverlay();
11327
+ } else if (data.restartRequired !== false) {
11326
11328
  performRestart(null, msgEl);
11327
11329
  } else {
11328
11330
  // \u670d\u52a1\u7aef\u660e\u786e\u8868\u793a\u4e0d\u9700\u8981\u91cd\u542f\uff0c\u4fdd\u7559\u624b\u52a8\u91cd\u542f\u6309\u94ae
@@ -22089,6 +22091,10 @@
22089
22091
  setSubtitle((data.message || "\u66f4\u65b0\u5b8c\u6210") + "\uff0c\u6b63\u5728\u91cd\u542f\u670d\u52a1\u2026");
22090
22092
  setStatus("");
22091
22093
  if (actionLabel) actionLabel.textContent = "\u6b63\u5728\u91cd\u542f\u2026";
22094
+ if (data.detachedUpdate) {
22095
+ showRestartOverlay();
22096
+ return;
22097
+ }
22092
22098
  if (data.restartRequired === false) {
22093
22099
  setProgress(false);
22094
22100
  card.classList.remove("is-busy");
@@ -22174,7 +22180,7 @@
22174
22180
  document.body.appendChild(overlay);
22175
22181
 
22176
22182
  var attempts = 0;
22177
- var maxAttempts = 20; // 20 * 2s = 40s
22183
+ var maxAttempts = 180; // 180 * 2s = 6min; beta git installs can be slow
22178
22184
  var timer = setInterval(function() {
22179
22185
  attempts++;
22180
22186
  fetch("/api/config", { credentials: "same-origin" })
@@ -0,0 +1,23 @@
1
+ export declare const EMBEDDED_WEB_ASSET_VERSION = "ed9dad22c425";
2
+ export declare const EMBEDDED_WEB_ASSETS: {
3
+ readonly scriptsJs: string;
4
+ readonly stylesCss: string;
5
+ readonly vendor: {
6
+ readonly "/vendor/wterm/wterm.bundle.js": {
7
+ readonly content: string;
8
+ readonly contentType: "application/javascript";
9
+ readonly hash: "5c8595b1";
10
+ };
11
+ readonly "/vendor/wterm/terminal.css": {
12
+ readonly content: string;
13
+ readonly contentType: "text/css; charset=utf-8";
14
+ readonly hash: "e6459118";
15
+ };
16
+ readonly "/vendor/qrcode/qrcode.bundle.js": {
17
+ readonly content: string;
18
+ readonly contentType: "application/javascript";
19
+ readonly hash: "8be76aad";
20
+ };
21
+ };
22
+ };
23
+ export type EmbeddedVendorAssetPath = keyof typeof EMBEDDED_WEB_ASSETS.vendor;