@co0ontty/wand 2.4.2 → 2.4.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/dist/build-info.json +3 -3
- package/dist/process-manager.d.ts +1 -0
- package/dist/process-manager.js +86 -12
- package/dist/server.js +66 -23
- package/dist/web-ui/content/scripts.js +1 -1
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-07-
|
|
4
|
-
"version": "2.4.
|
|
2
|
+
"commit": "d0058288d0bce20e25fd06fd06f62de794d06790",
|
|
3
|
+
"builtAt": "2026-07-07T00:10:50.544Z",
|
|
4
|
+
"version": "2.4.3",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -85,6 +85,7 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
85
85
|
hasCodexSessionFile(threadId: string): boolean;
|
|
86
86
|
deleteCodexHistoryFiles(threadIds: string[]): number;
|
|
87
87
|
private captureCodexSessionId;
|
|
88
|
+
private captureClaudeSessionId;
|
|
88
89
|
get(id: string): SessionSnapshot | null;
|
|
89
90
|
getPtyTranscript(id: string): string | null;
|
|
90
91
|
/**
|
package/dist/process-manager.js
CHANGED
|
@@ -44,6 +44,7 @@ function readClaudeProjectSessionDetails(filePath, id) {
|
|
|
44
44
|
const fileSessionIds = new Set();
|
|
45
45
|
let hasAssistant = false;
|
|
46
46
|
let hasUser = false;
|
|
47
|
+
let firstUserAtMs = null;
|
|
47
48
|
for (const line of lines) {
|
|
48
49
|
try {
|
|
49
50
|
const parsed = JSON.parse(line);
|
|
@@ -52,6 +53,11 @@ function readClaudeProjectSessionDetails(filePath, id) {
|
|
|
52
53
|
}
|
|
53
54
|
if (parsed.type === "user" || parsed.message?.role === "user") {
|
|
54
55
|
hasUser = true;
|
|
56
|
+
if (firstUserAtMs === null && parsed.timestamp) {
|
|
57
|
+
const parsedTime = Date.parse(parsed.timestamp);
|
|
58
|
+
if (Number.isFinite(parsedTime))
|
|
59
|
+
firstUserAtMs = parsedTime;
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
if (parsed.type === "assistant" || parsed.message?.role === "assistant") {
|
|
57
63
|
hasAssistant = true;
|
|
@@ -75,7 +81,8 @@ function readClaudeProjectSessionDetails(filePath, id) {
|
|
|
75
81
|
id,
|
|
76
82
|
filePath,
|
|
77
83
|
mtimeMs: stats.mtimeMs,
|
|
78
|
-
hasConversation: hasUser && hasAssistant && lines.length >= REAL_CONVERSATION_MIN_LINES
|
|
84
|
+
hasConversation: hasUser && hasAssistant && lines.length >= REAL_CONVERSATION_MIN_LINES,
|
|
85
|
+
firstUserAtMs,
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
88
|
catch {
|
|
@@ -150,6 +157,30 @@ function selectClaudeProjectSessionForRecord(record) {
|
|
|
150
157
|
function getLatestClaudeProjectSessionId(record) {
|
|
151
158
|
return selectClaudeProjectSessionForRecord(record)?.id ?? null;
|
|
152
159
|
}
|
|
160
|
+
function selectClaudeProjectSessionForTimeWindow(record) {
|
|
161
|
+
const startedAtMs = parseTimeMs(record.startedAt);
|
|
162
|
+
if (startedAtMs === null)
|
|
163
|
+
return null;
|
|
164
|
+
const endedAtMs = parseTimeMs(record.endedAt) ?? Date.now();
|
|
165
|
+
const windowStart = startedAtMs - START_TIME_SKEW_MS;
|
|
166
|
+
const windowEnd = endedAtMs + START_TIME_SKEW_MS;
|
|
167
|
+
const fallbackWindowEnd = endedAtMs + DISCOVERY_RECENT_WINDOW_MS;
|
|
168
|
+
const candidates = listClaudeProjectSessionCandidates(record.cwd)
|
|
169
|
+
.map((candidate) => readClaudeProjectSessionDetails(candidate.filePath, candidate.id))
|
|
170
|
+
.filter((candidate) => Boolean(candidate?.hasConversation))
|
|
171
|
+
.filter((candidate) => {
|
|
172
|
+
if (candidate.firstUserAtMs !== null) {
|
|
173
|
+
return candidate.firstUserAtMs >= windowStart && candidate.firstUserAtMs <= windowEnd;
|
|
174
|
+
}
|
|
175
|
+
return candidate.mtimeMs >= windowStart && candidate.mtimeMs <= fallbackWindowEnd;
|
|
176
|
+
})
|
|
177
|
+
.sort((a, b) => {
|
|
178
|
+
const aTime = a.firstUserAtMs ?? a.mtimeMs;
|
|
179
|
+
const bTime = b.firstUserAtMs ?? b.mtimeMs;
|
|
180
|
+
return Math.abs(aTime - startedAtMs) - Math.abs(bTime - startedAtMs);
|
|
181
|
+
});
|
|
182
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
183
|
+
}
|
|
153
184
|
function listRecentClaudeProjectSessionIds(cwd, startedAt) {
|
|
154
185
|
return listClaudeProjectSessionCandidates(cwd)
|
|
155
186
|
.filter((candidate) => hasRecentProjectActivity(candidate, startedAt))
|
|
@@ -504,6 +535,12 @@ function recoverCodexSessionIdFromHistory(snapshot) {
|
|
|
504
535
|
}
|
|
505
536
|
return getCodexResumeCommandSessionId(snapshot.command) ?? selectCodexSessionForTimeWindow(snapshot)?.claudeSessionId ?? null;
|
|
506
537
|
}
|
|
538
|
+
function recoverClaudeSessionIdFromHistory(snapshot) {
|
|
539
|
+
if (snapshot.provider !== "claude" || snapshot.claudeSessionId) {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
return getResumeCommandSessionId(snapshot.command) ?? selectClaudeProjectSessionForTimeWindow(snapshot)?.id ?? null;
|
|
543
|
+
}
|
|
507
544
|
/** Delete every rollout file belonging to the given codex thread ids. */
|
|
508
545
|
function deleteCodexRolloutFiles(threadIds) {
|
|
509
546
|
if (threadIds.size === 0)
|
|
@@ -643,13 +680,19 @@ export class ProcessManager extends EventEmitter {
|
|
|
643
680
|
? getCodexResumeCommandSessionId(snapshot.command)
|
|
644
681
|
: null;
|
|
645
682
|
const orphanEndedAt = snapshot.status === "running" ? new Date().toISOString() : null;
|
|
646
|
-
const sessionIdFromHistory =
|
|
647
|
-
?
|
|
683
|
+
const sessionIdFromHistory = isClaudeCmd
|
|
684
|
+
? recoverClaudeSessionIdFromHistory({
|
|
648
685
|
...snapshot,
|
|
649
|
-
provider: "
|
|
686
|
+
provider: "claude",
|
|
650
687
|
endedAt: snapshot.endedAt ?? orphanEndedAt,
|
|
651
688
|
})
|
|
652
|
-
:
|
|
689
|
+
: isCodexCmd
|
|
690
|
+
? recoverCodexSessionIdFromHistory({
|
|
691
|
+
...snapshot,
|
|
692
|
+
provider: "codex",
|
|
693
|
+
endedAt: snapshot.endedAt ?? orphanEndedAt,
|
|
694
|
+
})
|
|
695
|
+
: null;
|
|
653
696
|
const restoredSessionId = resumeCommandSessionId ?? snapshot.claudeSessionId ?? sessionIdFromHistory;
|
|
654
697
|
// Sessions restored from storage have ptyProcess: null — the old server's PTY
|
|
655
698
|
// belongs to a dead process. Mark running sessions as exited so the UI
|
|
@@ -664,8 +707,9 @@ export class ProcessManager extends EventEmitter {
|
|
|
664
707
|
messages: recoveredMessages.length > 0 ? recoveredMessages : snapshot.messages,
|
|
665
708
|
};
|
|
666
709
|
this.storage.saveSession(updated);
|
|
667
|
-
if (
|
|
668
|
-
|
|
710
|
+
if (restoredSessionId && restoredSessionId !== snapshot.claudeSessionId) {
|
|
711
|
+
const label = isCodexCmd ? "Codex thread" : "Claude session";
|
|
712
|
+
process.stderr.write(`[wand] Recovered ${label} ID for orphan PTY ${snapshot.id}: ${restoredSessionId}\n`);
|
|
669
713
|
}
|
|
670
714
|
this.sessions.set(snapshot.id, {
|
|
671
715
|
...updated,
|
|
@@ -712,9 +756,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
712
756
|
: snapshot;
|
|
713
757
|
if (updated !== snapshot) {
|
|
714
758
|
this.storage.saveSessionMetadata(updated);
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
759
|
+
const label = isCodexCmd ? "Codex thread" : "Claude session";
|
|
760
|
+
process.stderr.write(`[wand] Recovered ${label} ID for saved PTY ${snapshot.id}: ${restoredSessionId}\n`);
|
|
718
761
|
}
|
|
719
762
|
this.sessions.set(snapshot.id, {
|
|
720
763
|
...updated,
|
|
@@ -995,6 +1038,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
995
1038
|
}
|
|
996
1039
|
current.pendingEscalation = null;
|
|
997
1040
|
current.ptyPermissionBlocked = false;
|
|
1041
|
+
this.captureClaudeSessionId(current, { allowTimeWindowFallback: true });
|
|
998
1042
|
this.captureCodexSessionId(current, { allowTimeWindowFallback: true });
|
|
999
1043
|
current.status = current.stopRequested ? "stopped" : exitCode === 0 ? "exited" : "failed";
|
|
1000
1044
|
current.exitCode = current.stopRequested ? null : exitCode;
|
|
@@ -1268,6 +1312,34 @@ export class ProcessManager extends EventEmitter {
|
|
|
1268
1312
|
process.stderr.write(`[wand] Captured Codex thread ID: ${threadId}\n`);
|
|
1269
1313
|
return true;
|
|
1270
1314
|
}
|
|
1315
|
+
captureClaudeSessionId(record, options) {
|
|
1316
|
+
if (record.provider !== "claude" || record.claudeSessionId) {
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
record.messages = snapshotMessages(record);
|
|
1320
|
+
const discoveredSessionId = record.knownClaudeProjectMtimes
|
|
1321
|
+
? getLatestClaudeProjectSessionId({
|
|
1322
|
+
cwd: record.cwd,
|
|
1323
|
+
startedAt: record.startedAt,
|
|
1324
|
+
knownClaudeProjectMtimes: record.knownClaudeProjectMtimes,
|
|
1325
|
+
messages: record.messages,
|
|
1326
|
+
})
|
|
1327
|
+
: null;
|
|
1328
|
+
const fallbackSessionId = discoveredSessionId
|
|
1329
|
+
? null
|
|
1330
|
+
: options?.allowTimeWindowFallback
|
|
1331
|
+
? selectClaudeProjectSessionForTimeWindow(record)?.id ?? null
|
|
1332
|
+
: null;
|
|
1333
|
+
const sessionId = discoveredSessionId ?? fallbackSessionId;
|
|
1334
|
+
if (!sessionId) {
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
record.claudeSessionId = sessionId;
|
|
1338
|
+
record.knownClaudeProjectMtimes?.set(sessionId, Date.now());
|
|
1339
|
+
this.claudeHistoryCache = null;
|
|
1340
|
+
process.stderr.write(`[wand] Captured Claude session ID: ${sessionId}\n`);
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1271
1343
|
get(id) {
|
|
1272
1344
|
const record = this.sessions.get(id);
|
|
1273
1345
|
if (!record) {
|
|
@@ -1458,12 +1530,14 @@ export class ProcessManager extends EventEmitter {
|
|
|
1458
1530
|
record.exitCode = null;
|
|
1459
1531
|
record.endedAt = new Date().toISOString();
|
|
1460
1532
|
record.ptyProcess = null;
|
|
1533
|
+
// Update lifecycle before dropping the bridge so Claude project-session
|
|
1534
|
+
// discovery can still inspect the latest parsed turns.
|
|
1535
|
+
this.captureClaudeSessionId(record, { allowTimeWindowFallback: true });
|
|
1536
|
+
this.captureCodexSessionId(record, { allowTimeWindowFallback: true });
|
|
1461
1537
|
if (record.ptyBridge) {
|
|
1462
1538
|
record.ptyBridge.removeAllListeners();
|
|
1463
1539
|
record.ptyBridge = null;
|
|
1464
1540
|
}
|
|
1465
|
-
// Update lifecycle
|
|
1466
|
-
this.captureCodexSessionId(record, { allowTimeWindowFallback: true });
|
|
1467
1541
|
this.persist(record);
|
|
1468
1542
|
return this.snapshot(record);
|
|
1469
1543
|
}
|
package/dist/server.js
CHANGED
|
@@ -142,14 +142,58 @@ async function fetchGitHubLatestApk(forceRefresh = false) {
|
|
|
142
142
|
function parseApkChannel(value) {
|
|
143
143
|
return value === "beta" ? "beta" : "stable";
|
|
144
144
|
}
|
|
145
|
+
function asRecord(value) {
|
|
146
|
+
return value && typeof value === "object" ? value : null;
|
|
147
|
+
}
|
|
148
|
+
async function refreshDistributionConfig(configPath, config) {
|
|
149
|
+
let raw;
|
|
150
|
+
try {
|
|
151
|
+
raw = JSON.parse(await readFile(configPath, "utf8"));
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const android = asRecord(raw.android);
|
|
157
|
+
if (android) {
|
|
158
|
+
config.android = { ...(config.android ?? {}) };
|
|
159
|
+
if (typeof android.enabled === "boolean")
|
|
160
|
+
config.android.enabled = android.enabled;
|
|
161
|
+
if (Object.prototype.hasOwnProperty.call(android, "apkDir")) {
|
|
162
|
+
config.android.apkDir = typeof android.apkDir === "string" && android.apkDir.trim()
|
|
163
|
+
? android.apkDir.trim()
|
|
164
|
+
: "android";
|
|
165
|
+
}
|
|
166
|
+
if (Object.prototype.hasOwnProperty.call(android, "currentApkFile")) {
|
|
167
|
+
config.android.currentApkFile = typeof android.currentApkFile === "string"
|
|
168
|
+
? android.currentApkFile.trim()
|
|
169
|
+
: "";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const macos = asRecord(raw.macos);
|
|
173
|
+
if (macos) {
|
|
174
|
+
config.macos = { ...(config.macos ?? {}) };
|
|
175
|
+
if (typeof macos.enabled === "boolean")
|
|
176
|
+
config.macos.enabled = macos.enabled;
|
|
177
|
+
if (Object.prototype.hasOwnProperty.call(macos, "dmgDir")) {
|
|
178
|
+
config.macos.dmgDir = typeof macos.dmgDir === "string" && macos.dmgDir.trim()
|
|
179
|
+
? macos.dmgDir.trim()
|
|
180
|
+
: "macos";
|
|
181
|
+
}
|
|
182
|
+
if (Object.prototype.hasOwnProperty.call(macos, "currentDmgFile")) {
|
|
183
|
+
config.macos.currentDmgFile = typeof macos.currentDmgFile === "string"
|
|
184
|
+
? macos.currentDmgFile.trim()
|
|
185
|
+
: "";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
145
189
|
/** 版本号带 prerelease 后缀(如 -debug.06121811)即视为 beta 构建。 */
|
|
146
190
|
function isPrereleaseApkVersion(version) {
|
|
147
191
|
return !!version && version.includes("-");
|
|
148
192
|
}
|
|
149
|
-
async function resolveLatestApkVersion(configDir, config, channel) {
|
|
193
|
+
async function resolveLatestApkVersion(configDir, config, channel, configPath) {
|
|
150
194
|
// local 与 github 两个来源都看,按安装序取真正更新的那个(持平偏向 local:同源下载更快)。
|
|
151
195
|
// 旧逻辑是「local 存在就一票否决」——本地目录留着旧包时,会把线上新版压住不提示。
|
|
152
|
-
const localApk = await resolveAndroidApkAsset(configDir, config, channel);
|
|
196
|
+
const localApk = await resolveAndroidApkAsset(configDir, config, channel, configPath);
|
|
153
197
|
const local = localApk && localApk.version
|
|
154
198
|
? {
|
|
155
199
|
version: localApk.version,
|
|
@@ -212,8 +256,8 @@ async function fetchGitHubLatestDmg(forceRefresh = false) {
|
|
|
212
256
|
return cachedGitHubDmg ?? null;
|
|
213
257
|
}
|
|
214
258
|
}
|
|
215
|
-
async function resolveLatestDmgVersion(configDir, config) {
|
|
216
|
-
const localDmg = await resolveMacosDmgAsset(configDir, config);
|
|
259
|
+
async function resolveLatestDmgVersion(configDir, config, configPath) {
|
|
260
|
+
const localDmg = await resolveMacosDmgAsset(configDir, config, configPath);
|
|
217
261
|
if (localDmg && localDmg.version) {
|
|
218
262
|
return {
|
|
219
263
|
version: localDmg.version,
|
|
@@ -585,13 +629,18 @@ function resolveAndroidApkDir(configDir, config) {
|
|
|
585
629
|
function extractAndroidApkVersion(fileName) {
|
|
586
630
|
return extractSemver(fileName.replace(/\.apk$/i, ""));
|
|
587
631
|
}
|
|
588
|
-
async function resolveAndroidApkAsset(configDir, config, channel = "beta") {
|
|
632
|
+
async function resolveAndroidApkAsset(configDir, config, channel = "beta", configPath) {
|
|
633
|
+
if (configPath)
|
|
634
|
+
await refreshDistributionConfig(configPath, config);
|
|
589
635
|
if (config.android?.enabled !== true)
|
|
590
636
|
return null;
|
|
591
637
|
const apkDir = resolveAndroidApkDir(configDir, config);
|
|
592
638
|
await mkdir(apkDir, { recursive: true });
|
|
593
639
|
const configuredFile = config.android?.currentApkFile?.trim();
|
|
594
|
-
|
|
640
|
+
// Beta is the local development channel: every check should pick the newest
|
|
641
|
+
// APK in apkDir, so dropping a new debug build into the directory is enough.
|
|
642
|
+
// currentApkFile remains a stable/manual pin and backward-compatible fallback.
|
|
643
|
+
if (configuredFile && channel !== "beta") {
|
|
595
644
|
const filePath = path.join(apkDir, path.basename(configuredFile));
|
|
596
645
|
try {
|
|
597
646
|
const fileStat = await stat(filePath);
|
|
@@ -676,7 +725,9 @@ function resolveMacosDmgDir(configDir, config) {
|
|
|
676
725
|
function extractMacosDmgVersion(fileName) {
|
|
677
726
|
return extractSemver(fileName.replace(/\.dmg$/i, ""));
|
|
678
727
|
}
|
|
679
|
-
async function resolveMacosDmgAsset(configDir, config) {
|
|
728
|
+
async function resolveMacosDmgAsset(configDir, config, configPath) {
|
|
729
|
+
if (configPath)
|
|
730
|
+
await refreshDistributionConfig(configPath, config);
|
|
680
731
|
if (config.macos?.enabled !== true)
|
|
681
732
|
return null;
|
|
682
733
|
const dmgDir = resolveMacosDmgDir(configDir, config);
|
|
@@ -1214,7 +1265,7 @@ export async function startServer(config, configPath) {
|
|
|
1214
1265
|
}
|
|
1215
1266
|
// 更新通道:beta 包含 -debug.* 构建,stable(默认,含不传参的老客户端)只推正式版。
|
|
1216
1267
|
const channel = parseApkChannel(req.query.channel);
|
|
1217
|
-
const latest = await resolveLatestApkVersion(configDir, config, channel);
|
|
1268
|
+
const latest = await resolveLatestApkVersion(configDir, config, channel, configPath);
|
|
1218
1269
|
if (!latest) {
|
|
1219
1270
|
res.json({ updateAvailable: false, currentVersion, latestVersion: null, downloadUrl: null, source: null, channel });
|
|
1220
1271
|
return;
|
|
@@ -1235,15 +1286,11 @@ export async function startServer(config, configPath) {
|
|
|
1235
1286
|
});
|
|
1236
1287
|
});
|
|
1237
1288
|
app.get("/android/download", async (req, res) => {
|
|
1238
|
-
if (config.android?.enabled !== true) {
|
|
1239
|
-
res.status(404).json({ error: "Android APK 下载未启用。" });
|
|
1240
|
-
return;
|
|
1241
|
-
}
|
|
1242
1289
|
// 更新弹窗的下载链接由 /api/android-apk-update 按通道生成(始终带 ?channel=)。
|
|
1243
1290
|
// 裸 /android/download(网页下载页、二维码落地页)不带参时默认 beta ——
|
|
1244
1291
|
// 保持「下载页拿到的就是目录里真正最新的包」的旧行为。
|
|
1245
1292
|
const channel = req.query.channel === "stable" ? "stable" : "beta";
|
|
1246
|
-
const androidApk = await resolveAndroidApkAsset(configDir, config, channel);
|
|
1293
|
+
const androidApk = await resolveAndroidApkAsset(configDir, config, channel, configPath);
|
|
1247
1294
|
if (!androidApk) {
|
|
1248
1295
|
res.status(404).json({ error: "当前没有可下载的 APK 文件。" });
|
|
1249
1296
|
return;
|
|
@@ -1263,7 +1310,7 @@ export async function startServer(config, configPath) {
|
|
|
1263
1310
|
res.status(400).json({ error: "Missing currentVersion query parameter." });
|
|
1264
1311
|
return;
|
|
1265
1312
|
}
|
|
1266
|
-
const latest = await resolveLatestDmgVersion(configDir, config);
|
|
1313
|
+
const latest = await resolveLatestDmgVersion(configDir, config, configPath);
|
|
1267
1314
|
if (!latest) {
|
|
1268
1315
|
res.json({ updateAvailable: false, currentVersion, latestVersion: null, downloadUrl: null, source: null });
|
|
1269
1316
|
return;
|
|
@@ -1280,11 +1327,7 @@ export async function startServer(config, configPath) {
|
|
|
1280
1327
|
});
|
|
1281
1328
|
});
|
|
1282
1329
|
app.get("/macos/download", async (req, res) => {
|
|
1283
|
-
|
|
1284
|
-
res.status(404).json({ error: "macOS DMG 下载未启用。" });
|
|
1285
|
-
return;
|
|
1286
|
-
}
|
|
1287
|
-
const macosDmg = await resolveMacosDmgAsset(configDir, config);
|
|
1330
|
+
const macosDmg = await resolveMacosDmgAsset(configDir, config, configPath);
|
|
1288
1331
|
if (!macosDmg) {
|
|
1289
1332
|
res.status(404).json({ error: "当前没有可下载的 DMG 文件。" });
|
|
1290
1333
|
return;
|
|
@@ -1453,7 +1496,7 @@ export async function startServer(config, configPath) {
|
|
|
1453
1496
|
};
|
|
1454
1497
|
const { password: _pw, ...safeConfig } = config;
|
|
1455
1498
|
const defaultModels = getProviderDefaultModels(config);
|
|
1456
|
-
const localApk = await resolveAndroidApkAsset(configDir, config);
|
|
1499
|
+
const localApk = await resolveAndroidApkAsset(configDir, config, "beta", configPath);
|
|
1457
1500
|
const ghApk = await fetchGitHubLatestApk();
|
|
1458
1501
|
const apkDir = resolveAndroidApkDir(configDir, config);
|
|
1459
1502
|
// Backward-compatible: pick best available for hasApk/version/downloadUrl
|
|
@@ -1462,7 +1505,7 @@ export async function startServer(config, configPath) {
|
|
|
1462
1505
|
: ghApk
|
|
1463
1506
|
? { hasApk: true, fileName: ghApk.fileName, version: ghApk.version, size: ghApk.size, updatedAt: null, downloadUrl: ghApk.downloadUrl, source: "github" }
|
|
1464
1507
|
: null;
|
|
1465
|
-
const localDmg = await resolveMacosDmgAsset(configDir, config);
|
|
1508
|
+
const localDmg = await resolveMacosDmgAsset(configDir, config, configPath);
|
|
1466
1509
|
const ghDmg = await fetchGitHubLatestDmg();
|
|
1467
1510
|
const dmgDir = resolveMacosDmgDir(configDir, config);
|
|
1468
1511
|
const resolvedDmg = localDmg
|
|
@@ -1525,7 +1568,7 @@ export async function startServer(config, configPath) {
|
|
|
1525
1568
|
});
|
|
1526
1569
|
});
|
|
1527
1570
|
app.get("/api/android-apk", async (_req, res) => {
|
|
1528
|
-
const localApk = await resolveAndroidApkAsset(configDir, config);
|
|
1571
|
+
const localApk = await resolveAndroidApkAsset(configDir, config, "beta", configPath);
|
|
1529
1572
|
const ghApk = await fetchGitHubLatestApk();
|
|
1530
1573
|
const apkDir = resolveAndroidApkDir(configDir, config);
|
|
1531
1574
|
const resolvedApk = localApk
|
|
@@ -1548,7 +1591,7 @@ export async function startServer(config, configPath) {
|
|
|
1548
1591
|
});
|
|
1549
1592
|
});
|
|
1550
1593
|
app.get("/api/macos-dmg", async (_req, res) => {
|
|
1551
|
-
const localDmg = await resolveMacosDmgAsset(configDir, config);
|
|
1594
|
+
const localDmg = await resolveMacosDmgAsset(configDir, config, configPath);
|
|
1552
1595
|
const ghDmg = await fetchGitHubLatestDmg();
|
|
1553
1596
|
const dmgDir = resolveMacosDmgDir(configDir, config);
|
|
1554
1597
|
const resolvedDmg = localDmg
|
|
@@ -49,4 +49,4 @@
|
|
|
49
49
|
`+t.join(`
|
|
50
50
|
`)+`]
|
|
51
51
|
|
|
52
|
-
`}function Ol(e){if(!r.terminalInteractive||!e||document.documentElement.classList.contains("is-wand-embed-terminal"))return!1;var t=e.value||"";return t?(Et(t,"interactive_text").catch(function(){}),e.value="",jt(e),ot("",!0),!0):!1}function Fg(e){var t=e.clipboardData&&e.clipboardData.items;if(t&&!r.terminalInteractive){for(var n=0;n<t.length;n++)if(t[n].type.indexOf("image/")===0){e.preventDefault();var i=t[n].getAsFile();i&&hu(i);return}}var a=e.clipboardData&&e.clipboardData.getData("text");if(a){if(e.preventDefault(),r.terminalInteractive){Et(a,"paste").catch(function(){});return}var u=document.getElementById("input-box");if(u){var s=u.selectionStart||0,o=u.selectionEnd||0,l=u.value,c=l.slice(0,s)+a+l.slice(o);u.value=c,ot(c)}}}function ph(e){Et(e),ot(wg()+e)}function wg(){if(r.selectedId){if(r.drafts[r.selectedId]!==void 0)return r.drafts[r.selectedId];try{var e=localStorage.getItem("wand-draft-"+r.selectedId);if(e)return e}catch{}}return""}function ot(e,t){if(r.selectedId){r.drafts[r.selectedId]=e;try{localStorage.setItem("wand-draft-"+r.selectedId,e)}catch{}if(!t){var n=document.getElementById("input-box");n&&(n.value=e)}}}var bu=!1;function Cg(){if(!bu){var e=document.getElementById("input-box"),t=document.getElementById("prompt-optimize-btn"),n=document.querySelector(".input-composer");if(e){var i=(e.value||"").trim();if(!i){typeof A=="function"&&A("\u8BF7\u5148\u8F93\u5165\u8981\u4F18\u5316\u7684\u5185\u5BB9\u3002","info"),e.focus();return}bu=!0,t&&(t.classList.add("is-loading"),t.disabled=!0,t.setAttribute("title","\u6B63\u5728\u4F18\u5316\u2026")),n&&n.classList.add("is-optimizing"),e.setAttribute("aria-busy","true");var a=e.readOnly;e.readOnly=!0;var u={text:i};r&&r.selectedId&&(u.sessionId=r.selectedId),fetch("/api/optimize-prompt",{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}).then(function(s){return s.json().then(function(o){return{ok:s.ok,data:o}})}).then(function(s){if(!s.ok)throw new Error(s.data&&s.data.error||"\u63D0\u793A\u8BCD\u4F18\u5316\u5931\u8D25\u3002");var o=s.data&&s.data.optimized||"";if(!o)throw new Error("Claude \u8FD4\u56DE\u4E3A\u7A7A\u3002");Bg(e,o)}).catch(function(s){typeof A=="function"&&A(s&&s.message||"\u63D0\u793A\u8BCD\u4F18\u5316\u5931\u8D25\u3002","error"),t&&(t.classList.remove("is-loading"),t.classList.add("is-shake"),setTimeout(function(){t&&t.classList.remove("is-shake")},400))}).finally(function(){bu=!1,t&&(t.classList.remove("is-loading"),t.disabled=!1,t.setAttribute("title","\u63D0\u793A\u8BCD\u4F18\u5316\uFF08AI\uFF09")),n&&n.classList.remove("is-optimizing"),e.removeAttribute("aria-busy"),e.readOnly=a})}}}function Bg(e,t){if(!e)return;var n=Array.from(t),i=n.length;if(i===0){e.value="",ot("",!0),jt(e);return}var a=Math.min(700,Math.max(220,i*8)),u=Math.min(i,60),s=Math.ceil(i/u),o=a/u,l=0;e.value="",jt(e);function c(){if(l=Math.min(i,l+s),e.value=n.slice(0,l).join(""),jt(e),l<i)setTimeout(c,o);else{ot(t,!0);try{e.setSelectionRange(t.length,t.length)}catch{}}}c()}function ji(e){var t=document.querySelector(".input-composer");if(t){var n=e||document.getElementById("input-box"),i=!!(n&&n.value&&n.value.length>0);t.classList.toggle("has-text",i)}}r._statusBarTimerId=null,r._statusBarStartTime=0;var jr=null,Ri=0;function Eu(e){if(!e)return{active:!1};if(e.archived)return{active:!1};var t=!!e.permissionBlocked,n=!!(ue(e)&&e.structuredState&&e.structuredState.inFlight),i=!ue(e)&&e.status==="running";return{active:n||i||t,inFlight:n,ptyRunning:i,permissionBlocked:t}}function Dg(e){var t=Math.max(0,Math.floor(e/1e3));if(t<60)return t+"s";var n=Math.floor(t/60),i=t%60;if(n<60)return n+"m"+(i?" "+i+"s":"");var a=Math.floor(n/60),u=n%60;return a+"h"+(u?" "+u+"m":"")}function Fu(e){var t=Eu(e),n=document.querySelector(".main-header-row"),i=n?n.querySelector(".session-status-pill"):null,a=document.querySelector(".chat-messages");if(n&&(n.classList.toggle("is-running",t.active),n.classList.toggle("is-permission-blocked",t.permissionBlocked)),i){var u=i.querySelector(".session-status-elapsed");if(t.inFlight){Ri||(Ri=r._statusBarStartTime>0?r._statusBarStartTime:Date.now());var s=Dg(Date.now()-Ri);u||(u=document.createElement("span"),u.className="session-status-elapsed",i.appendChild(u)),u.textContent=s}else Ri=0,u&&u.remove()}t.active?jr||(jr=setInterval(function(){var o=r.sessions.find(function(l){return l.id===r.selectedId});Fu(o)},1e3)):jr&&(clearInterval(jr),jr=null)}function wu(e,t){Fu(t);var n=document.querySelector(".composer-top-row"),i=document.querySelector(".structured-status-bar"),a=document.querySelector(".input-composer");if(!t||!ue(t)){i&&i.remove(),a&&a.classList.remove("in-flight"),clearInterval(r._statusBarTimerId),r._statusBarTimerId=null;return}var u=t.structuredState&&t.structuredState.inFlight;if(u){if(r._statusBarTimerId||(r._statusBarStartTime=Date.now()),a&&a.classList.add("in-flight"),!i&&n){var s=document.createElement("div");s.className="structured-status-bar",s.innerHTML='<span class="status-bar-dot"></span><span class="status-bar-label">\u56DE\u590D\u4E2D</span><span class="status-bar-timer">0.0s</span>',n.appendChild(s),i=s}else if(i&&i.classList.contains("completed")){i.classList.remove("completed"),i.style.animation="none",i.querySelector(".status-bar-label").textContent="\u56DE\u590D\u4E2D";var o=i.querySelector(".status-bar-dot");o&&(o.style.display=""),r._statusBarStartTime=Date.now()}r._statusBarTimerId||(r._statusBarTimerId=setInterval(function(){var c=document.querySelector(".structured-status-bar:not(.completed)");if(!c){clearInterval(r._statusBarTimerId),r._statusBarTimerId=null;return}var f=((Date.now()-r._statusBarStartTime)/1e3).toFixed(1),d=c.querySelector(".status-bar-timer");d&&(d.textContent=f+"s")},100))}else if(clearInterval(r._statusBarTimerId),r._statusBarTimerId=null,a&&a.classList.remove("in-flight"),i&&!i.classList.contains("completed")){var l=r._statusBarStartTime?((Date.now()-r._statusBarStartTime)/1e3).toFixed(1):"0.0";i.classList.add("completed"),i.querySelector(".status-bar-label").textContent="\u5B8C\u6210",i.querySelector(".status-bar-timer").textContent=l+"s";var o=i.querySelector(".status-bar-dot");o&&(o.style.display="none"),r._statusBarStartTime=0,setTimeout(function(){i.parentNode&&i.remove()},3e3)}}function p(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function lt(e,t,n){var i=String(e||"");return'<span class="'+t+' tail-marquee-path" title="'+p(i)+'"'+(n||"")+'><span class="tail-marquee-path-inner">'+p(i)+"</span></span>"}var kg=/\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|heic|heif)$/i;function Cu(e){if(typeof e!="string")return!1;var t=e.trim().split(/[?#]/)[0];return kg.test(t)}function ar(e){if(e){var t=function(){try{var n=e.firstElementChild&&e.firstElementChild.classList&&e.firstElementChild.classList.contains("tail-marquee-path-inner")?e.firstElementChild:null;if(n){var i=Math.max(0,n.scrollWidth-e.clientWidth);e.classList.toggle("is-overflowing",i>1),e.style.setProperty("--tail-marquee-shift",i+"px");var a=Math.max(4.8,i/18);e.style.setProperty("--tail-marquee-duration",Math.max(6.8,a/.68)+"s");return}e.scrollWidth>e.clientWidth&&(e.scrollLeft=e.scrollWidth)}catch{}};t(),typeof requestAnimationFrame=="function"&&requestAnimationFrame(t)}}function Jt(e){var t=e||document;!t||typeof t.querySelectorAll!="function"||t.querySelectorAll(".tail-marquee-path").forEach(function(n){ar(n)})}function Bu(e,t){if(e){var n=String(t||""),i=e.querySelector&&e.querySelector(".tail-marquee-path-inner");i?i.textContent=n:e.textContent=n,e.setAttribute&&e.setAttribute("title",n),ar(e)}}function Ag(e){if(e){var t=function(){try{if(typeof e.setSelectionRange=="function"&&e.value!=null){var n=e.value.length;try{e.setSelectionRange(n,n)}catch{}}e.scrollWidth>e.clientWidth&&(e.scrollLeft=e.scrollWidth)}catch{}};t(),typeof requestAnimationFrame=="function"&&requestAnimationFrame(t)}}(function(){try{var e=navigator&&navigator.userAgent||"";/WandApp\//.test(e)&&document.documentElement.classList.add("is-wand-app"),/WandPlatform\/iOS/.test(e)&&document.documentElement.classList.add("is-wand-ios")}catch{}try{const t=new URL(window.location.href).searchParams;t.get("embed")==="terminal"&&(document.documentElement.classList.add("is-wand-embed-terminal"),t.get("nativeInput")==="1"&&document.documentElement.classList.add("is-wand-native-input"))}catch{}try{window.__wandNativeBackHooked=!0}catch{}})()})();
|
|
52
|
+
`}function Ol(e){if(!r.terminalInteractive||!e||document.documentElement.classList.contains("is-wand-embed-terminal"))return!1;var t=e.value||"";return t?(Et(t,"interactive_text").catch(function(){}),e.value="",jt(e),ot("",!0),!0):!1}function Fg(e){var t=e.clipboardData&&e.clipboardData.items;if(t&&!r.terminalInteractive){for(var n=0;n<t.length;n++)if(t[n].type.indexOf("image/")===0){e.preventDefault();var i=t[n].getAsFile();i&&hu(i);return}}var a=e.clipboardData&&e.clipboardData.getData("text");if(a){if(e.preventDefault(),r.terminalInteractive){Et(a,"paste").catch(function(){});return}var u=document.getElementById("input-box");if(u){var s=u.selectionStart||0,o=u.selectionEnd||0,l=u.value,c=l.slice(0,s)+a+l.slice(o);u.value=c,ot(c)}}}function ph(e){Et(e),ot(wg()+e)}function wg(){if(r.selectedId){if(r.drafts[r.selectedId]!==void 0)return r.drafts[r.selectedId];try{var e=localStorage.getItem("wand-draft-"+r.selectedId);if(e)return e}catch{}}return""}function ot(e,t){if(r.selectedId){r.drafts[r.selectedId]=e;try{localStorage.setItem("wand-draft-"+r.selectedId,e)}catch{}if(!t){var n=document.getElementById("input-box");n&&(n.value=e)}}}var bu=!1;function Cg(){if(!bu){var e=document.getElementById("input-box"),t=document.getElementById("prompt-optimize-btn"),n=document.querySelector(".input-composer");if(e){var i=(e.value||"").trim();if(!i){typeof A=="function"&&A("\u8BF7\u5148\u8F93\u5165\u8981\u4F18\u5316\u7684\u5185\u5BB9\u3002","info"),e.focus();return}bu=!0,t&&(t.classList.add("is-loading"),t.disabled=!0,t.setAttribute("title","\u6B63\u5728\u4F18\u5316\u2026")),n&&n.classList.add("is-optimizing"),e.setAttribute("aria-busy","true");var a=e.readOnly;e.readOnly=!0;var u={text:i};r&&r.selectedId&&(u.sessionId=r.selectedId),fetch("/api/optimize-prompt",{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}).then(function(s){return s.json().then(function(o){return{ok:s.ok,data:o}})}).then(function(s){if(!s.ok)throw new Error(s.data&&s.data.error||"\u63D0\u793A\u8BCD\u4F18\u5316\u5931\u8D25\u3002");var o=s.data&&s.data.optimized||"";if(!o)throw new Error("Claude \u8FD4\u56DE\u4E3A\u7A7A\u3002");Bg(e,o)}).catch(function(s){typeof A=="function"&&A(s&&s.message||"\u63D0\u793A\u8BCD\u4F18\u5316\u5931\u8D25\u3002","error"),t&&(t.classList.remove("is-loading"),t.classList.add("is-shake"),setTimeout(function(){t&&t.classList.remove("is-shake")},400))}).finally(function(){bu=!1,t&&(t.classList.remove("is-loading"),t.disabled=!1,t.setAttribute("title","\u63D0\u793A\u8BCD\u4F18\u5316\uFF08AI\uFF09")),n&&n.classList.remove("is-optimizing"),e.removeAttribute("aria-busy"),e.readOnly=a})}}}function Bg(e,t){if(!e)return;var n=Array.from(t),i=n.length;if(i===0){e.value="",ot("",!0),jt(e);return}var a=Math.min(700,Math.max(220,i*8)),u=Math.min(i,60),s=Math.ceil(i/u),o=a/u,l=0;e.value="",jt(e);function c(){if(l=Math.min(i,l+s),e.value=n.slice(0,l).join(""),jt(e),l<i)setTimeout(c,o);else{ot(t,!0);try{e.setSelectionRange(t.length,t.length)}catch{}}}c()}function ji(e){var t=document.querySelector(".input-composer");if(t){var n=e||document.getElementById("input-box"),i=!!(n&&n.value&&n.value.length>0);t.classList.toggle("has-text",i)}}r._statusBarTimerId=null,r._statusBarStartTime=0;var jr=null,Ri=0;function Eu(e){if(!e)return{active:!1};if(e.archived)return{active:!1};var t=!!e.permissionBlocked,n=!!(ue(e)&&e.structuredState&&e.structuredState.inFlight),i=!ue(e)&&e.status==="running";return{active:n||i||t,inFlight:n,ptyRunning:i,permissionBlocked:t}}function Dg(e){var t=Math.max(0,Math.floor(e/1e3));if(t<60)return t+"s";var n=Math.floor(t/60),i=t%60;if(n<60)return n+"m"+(i?" "+i+"s":"");var a=Math.floor(n/60),u=n%60;return a+"h"+(u?" "+u+"m":"")}function Fu(e){var t=Eu(e),n=document.querySelector(".main-header-row"),i=n?n.querySelector(".session-status-pill"):null,a=document.querySelector(".chat-messages");if(n&&(n.classList.toggle("is-running",t.active),n.classList.toggle("is-permission-blocked",t.permissionBlocked)),i){var u=i.querySelector(".session-status-elapsed");if(t.inFlight){Ri||(Ri=r._statusBarStartTime>0?r._statusBarStartTime:Date.now());var s=Dg(Date.now()-Ri);u||(u=document.createElement("span"),u.className="session-status-elapsed",i.appendChild(u)),u.textContent=s}else Ri=0,u&&u.remove()}t.active?jr||(jr=setInterval(function(){var o=r.sessions.find(function(l){return l.id===r.selectedId});Fu(o)},1e3)):jr&&(clearInterval(jr),jr=null)}function wu(e,t){Fu(t);var n=document.querySelector(".composer-top-row"),i=document.querySelector(".structured-status-bar"),a=document.querySelector(".input-composer");if(!t||!ue(t)){i&&i.remove(),a&&a.classList.remove("in-flight"),clearInterval(r._statusBarTimerId),r._statusBarTimerId=null;return}var u=t.structuredState&&t.structuredState.inFlight;if(u){if(r._statusBarTimerId||(r._statusBarStartTime=Date.now()),a&&a.classList.add("in-flight"),!i&&n){var s=document.createElement("div");s.className="structured-status-bar",s.innerHTML='<span class="status-bar-dot"></span><span class="status-bar-label">\u56DE\u590D\u4E2D</span><span class="status-bar-timer">0.0s</span>',n.appendChild(s),i=s}else if(i&&i.classList.contains("completed")){i.classList.remove("completed"),i.style.animation="none",i.querySelector(".status-bar-label").textContent="\u56DE\u590D\u4E2D";var o=i.querySelector(".status-bar-dot");o&&(o.style.display=""),r._statusBarStartTime=Date.now()}r._statusBarTimerId||(r._statusBarTimerId=setInterval(function(){var c=document.querySelector(".structured-status-bar:not(.completed)");if(!c){clearInterval(r._statusBarTimerId),r._statusBarTimerId=null;return}var f=((Date.now()-r._statusBarStartTime)/1e3).toFixed(1),d=c.querySelector(".status-bar-timer");d&&(d.textContent=f+"s")},100))}else if(clearInterval(r._statusBarTimerId),r._statusBarTimerId=null,a&&a.classList.remove("in-flight"),i&&!i.classList.contains("completed")){var l=r._statusBarStartTime?((Date.now()-r._statusBarStartTime)/1e3).toFixed(1):"0.0";i.classList.add("completed"),i.querySelector(".status-bar-label").textContent="\u5B8C\u6210",i.querySelector(".status-bar-timer").textContent=l+"s";var o=i.querySelector(".status-bar-dot");o&&(o.style.display="none"),r._statusBarStartTime=0,setTimeout(function(){i.parentNode&&i.remove()},3e3)}}function p(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function lt(e,t,n){var i=String(e||"");return'<span class="'+t+' tail-marquee-path" title="'+p(i)+'"'+(n||"")+'><span class="tail-marquee-path-inner">'+p(i)+"</span></span>"}var kg=/\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|heic|heif)$/i;function Cu(e){if(typeof e!="string")return!1;var t=e.trim().split(/[?#]/)[0];return kg.test(t)}function ar(e){if(e){var t=function(){try{var n=e.firstElementChild&&e.firstElementChild.classList&&e.firstElementChild.classList.contains("tail-marquee-path-inner")?e.firstElementChild:null;if(n){var i=Math.max(0,n.scrollWidth-e.clientWidth);e.classList.toggle("is-overflowing",i>1),e.style.setProperty("--tail-marquee-shift",i+"px");var a=Math.max(4.8,i/18);e.style.setProperty("--tail-marquee-duration",Math.max(6.8,a/.68)+"s");return}e.scrollWidth>e.clientWidth&&(e.scrollLeft=e.scrollWidth)}catch{}};t(),typeof requestAnimationFrame=="function"&&requestAnimationFrame(t)}}function Jt(e){var t=e||document;!t||typeof t.querySelectorAll!="function"||t.querySelectorAll(".tail-marquee-path").forEach(function(n){ar(n)})}function Bu(e,t){if(e){var n=String(t||""),i=e.querySelector&&e.querySelector(".tail-marquee-path-inner");i?i.textContent=n:e.textContent=n,e.setAttribute&&e.setAttribute("title",n),ar(e)}}function Ag(e){if(e){var t=function(){try{if(typeof e.setSelectionRange=="function"&&e.value!=null){var n=e.value.length;try{e.setSelectionRange(n,n)}catch{}}e.scrollWidth>e.clientWidth&&(e.scrollLeft=e.scrollWidth)}catch{}};t(),typeof requestAnimationFrame=="function"&&requestAnimationFrame(t)}}(function(){try{var e=navigator&&navigator.userAgent||"";/WandApp\//.test(e)&&document.documentElement.classList.add("is-wand-app"),/WandPlatform\/iOS/.test(e)&&document.documentElement.classList.add("is-wand-ios"),/WandPlatform\/Android/.test(e)&&document.documentElement.classList.add("is-wand-android")}catch{}try{const t=new URL(window.location.href).searchParams;t.get("embed")==="terminal"&&(document.documentElement.classList.add("is-wand-embed-terminal"),t.get("nativeInput")==="1"&&document.documentElement.classList.add("is-wand-native-input"))}catch{}try{window.__wandNativeBackHooked=!0}catch{}})()})();
|