@co0ontty/wand 1.66.2 → 1.67.0-beta.gbdf490e
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 +4 -4
- package/dist/process-manager.d.ts +8 -0
- package/dist/process-manager.js +19 -0
- package/dist/server-session-routes.js +32 -0
- package/dist/server.js +53 -2
- package/dist/structured-session-manager.d.ts +7 -0
- package/dist/structured-session-manager.js +23 -0
- package/dist/web-ui/content/scripts.js +34 -34
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-06-
|
|
4
|
-
"version": "1.
|
|
5
|
-
"channel": "
|
|
2
|
+
"commit": "bdf490ea0abbcd603f15e39745919fe6fb50f866",
|
|
3
|
+
"builtAt": "2026-06-15T22:11:38.527Z",
|
|
4
|
+
"version": "1.67.0-beta.gbdf490e",
|
|
5
|
+
"channel": "beta"
|
|
6
6
|
}
|
|
@@ -97,6 +97,14 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
97
97
|
* sends a chat-view message (see sendInput → applyThinkingEffortToPrompt).
|
|
98
98
|
*/
|
|
99
99
|
setSessionThinkingEffort(id: string, effort: SessionSnapshot["thinkingEffort"]): SessionSnapshot;
|
|
100
|
+
/**
|
|
101
|
+
* Switch the execution mode of a PTY session mid-flight. The already-launched
|
|
102
|
+
* claude/codex process keeps its original CLI flags, but wand's own permission
|
|
103
|
+
* auto-approval (shouldAutoApprovePermissions / escalation handling) reads
|
|
104
|
+
* record.mode, so this changes the permission posture for subsequent prompts.
|
|
105
|
+
* Mirrors setSessionModel/setSessionThinkingEffort.
|
|
106
|
+
*/
|
|
107
|
+
setSessionMode(id: string, mode: ExecutionMode): SessionSnapshot;
|
|
100
108
|
sendInput(id: string, input: string, view?: "chat" | "terminal", shortcutKey?: string): SessionSnapshot;
|
|
101
109
|
/** Emit a task event for a session, debounced to avoid flooding */
|
|
102
110
|
private emitTask;
|
package/dist/process-manager.js
CHANGED
|
@@ -1232,6 +1232,25 @@ export class ProcessManager extends EventEmitter {
|
|
|
1232
1232
|
this.emitEvent({ type: "status", sessionId: id, data: { thinkingEffort: normalized } });
|
|
1233
1233
|
return this.snapshot(record);
|
|
1234
1234
|
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Switch the execution mode of a PTY session mid-flight. The already-launched
|
|
1237
|
+
* claude/codex process keeps its original CLI flags, but wand's own permission
|
|
1238
|
+
* auto-approval (shouldAutoApprovePermissions / escalation handling) reads
|
|
1239
|
+
* record.mode, so this changes the permission posture for subsequent prompts.
|
|
1240
|
+
* Mirrors setSessionModel/setSessionThinkingEffort.
|
|
1241
|
+
*/
|
|
1242
|
+
setSessionMode(id, mode) {
|
|
1243
|
+
const record = this.mustGet(id);
|
|
1244
|
+
record.mode = mode;
|
|
1245
|
+
record.autoApprovePermissions = this.shouldAutoApprovePermissions(record.command, mode, record.provider ?? "claude");
|
|
1246
|
+
this.persist(record);
|
|
1247
|
+
this.emitEvent({
|
|
1248
|
+
type: "status",
|
|
1249
|
+
sessionId: id,
|
|
1250
|
+
data: { mode, autoApprovePermissions: record.autoApprovePermissions },
|
|
1251
|
+
});
|
|
1252
|
+
return this.snapshot(record);
|
|
1253
|
+
}
|
|
1235
1254
|
sendInput(id, input, view, shortcutKey) {
|
|
1236
1255
|
const record = this.mustGet(id);
|
|
1237
1256
|
if (record.status !== "running") {
|
|
@@ -224,6 +224,38 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
224
224
|
res.status(400).json({ error: getErrorMessage(error, "切换思考深度失败。") });
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
|
+
// 执行模式切换:与 /model、/thinking-effort 路由对称。结构化会话立即影响下一条
|
|
228
|
+
// prompt(权限策略 / 系统提示 / CLI flag 都按 session.mode 逐轮重新派生);PTY 会话
|
|
229
|
+
// 仅更新 wand 自身的权限自动放行判定,已启动的 claude 进程命令行 flag 不变。codex 锁 full-access。
|
|
230
|
+
app.post("/api/sessions/:id/mode", express.json(), (req, res) => {
|
|
231
|
+
const body = req.body;
|
|
232
|
+
const raw = typeof body?.mode === "string" ? body.mode.trim() : "";
|
|
233
|
+
if (!raw) {
|
|
234
|
+
res.status(400).json({ error: "缺少 mode。" });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const mode = normalizeMode(raw, "managed");
|
|
238
|
+
const id = req.params.id;
|
|
239
|
+
try {
|
|
240
|
+
const structuredSnapshot = structured.get(id);
|
|
241
|
+
if (structuredSnapshot) {
|
|
242
|
+
const provider = structuredSnapshot.provider ?? "claude";
|
|
243
|
+
const effective = provider === "codex" ? "full-access" : mode;
|
|
244
|
+
res.json(structured.setSessionMode(id, effective));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const ptySnapshot = processes.get(id);
|
|
248
|
+
if (!ptySnapshot) {
|
|
249
|
+
res.status(404).json({ error: "未找到该会话。" });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const effective = (ptySnapshot.provider ?? "claude") === "codex" ? "full-access" : mode;
|
|
253
|
+
res.json(processes.setSessionMode(id, effective));
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
res.status(400).json({ error: getErrorMessage(error, "切换模式失败。") });
|
|
257
|
+
}
|
|
258
|
+
});
|
|
227
259
|
app.get("/api/structured-sessions/:id/messages", (req, res) => {
|
|
228
260
|
const snapshot = structured.get(req.params.id);
|
|
229
261
|
if (!snapshot) {
|
package/dist/server.js
CHANGED
|
@@ -7,6 +7,7 @@ import { lstat, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from
|
|
|
7
7
|
import { createServer as createHttpServer } from "node:http";
|
|
8
8
|
import { createServer as createHttpsServer } from "node:https";
|
|
9
9
|
import { exec, spawn } from "node:child_process";
|
|
10
|
+
import os from "node:os";
|
|
10
11
|
import { promisify } from "node:util";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import process from "node:process";
|
|
@@ -476,6 +477,56 @@ function normalizePublicOrigin(value) {
|
|
|
476
477
|
return undefined;
|
|
477
478
|
}
|
|
478
479
|
}
|
|
480
|
+
function isPrivateIpv4(address) {
|
|
481
|
+
if (address.startsWith("10.") || address.startsWith("192.168."))
|
|
482
|
+
return true;
|
|
483
|
+
const match = address.match(/^172\.(\d+)\./);
|
|
484
|
+
return match ? Number(match[1]) >= 16 && Number(match[1]) <= 31 : false;
|
|
485
|
+
}
|
|
486
|
+
function preferredLanIpv4() {
|
|
487
|
+
const candidates = [];
|
|
488
|
+
for (const [name, entries] of Object.entries(os.networkInterfaces())) {
|
|
489
|
+
for (const entry of entries ?? []) {
|
|
490
|
+
const family = entry.family;
|
|
491
|
+
if (entry.internal || (family !== "IPv4" && family !== 4))
|
|
492
|
+
continue;
|
|
493
|
+
let score = isPrivateIpv4(entry.address) ? 10 : 0;
|
|
494
|
+
if (/^(en|eth|wlan)\d+$/i.test(name))
|
|
495
|
+
score += 10;
|
|
496
|
+
candidates.push({ address: entry.address, score });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
candidates.sort((a, b) => b.score - a.score || a.address.localeCompare(b.address));
|
|
500
|
+
return candidates[0]?.address;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* App 连接码用于另一台设备。设置页若从 localhost 打开,直接编码浏览器 origin
|
|
504
|
+
* 会让客户端连接它自己;监听 0.0.0.0 时自动换成本机优先 LAN IPv4。
|
|
505
|
+
*/
|
|
506
|
+
function resolveAppConnectOrigin(origin, config) {
|
|
507
|
+
if (config.host !== "0.0.0.0")
|
|
508
|
+
return origin;
|
|
509
|
+
try {
|
|
510
|
+
const parsed = new URL(origin);
|
|
511
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
512
|
+
const isLocalOnly = hostname === "localhost"
|
|
513
|
+
|| hostname === "127.0.0.1"
|
|
514
|
+
|| hostname === "[::1]"
|
|
515
|
+
|| hostname === "::1"
|
|
516
|
+
|| hostname === "0.0.0.0"
|
|
517
|
+
|| hostname === "[::]";
|
|
518
|
+
if (!isLocalOnly)
|
|
519
|
+
return parsed.origin;
|
|
520
|
+
const lanIp = preferredLanIpv4();
|
|
521
|
+
if (!lanIp)
|
|
522
|
+
return parsed.origin;
|
|
523
|
+
parsed.hostname = lanIp;
|
|
524
|
+
return parsed.origin;
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
return origin;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
479
530
|
function decodeConnectCode(code) {
|
|
480
531
|
try {
|
|
481
532
|
const decoded = Buffer.from(code, "base64").toString("utf8");
|
|
@@ -1372,11 +1423,11 @@ export async function startServer(config, configPath) {
|
|
|
1372
1423
|
const protocol = getPublicRequestProtocol(req, useHttps ? "https" : "http");
|
|
1373
1424
|
const host = getPublicRequestHost(req, config);
|
|
1374
1425
|
const browserOrigin = normalizePublicOrigin(firstQueryStringValue(req.query.origin));
|
|
1375
|
-
const serverUrl = browserOrigin ?? `${protocol}://${host}
|
|
1426
|
+
const serverUrl = resolveAppConnectOrigin(browserOrigin ?? `${protocol}://${host}`, config);
|
|
1376
1427
|
const appSecret = config.appSecret ?? "";
|
|
1377
1428
|
const token = generateAppToken(effectivePassword, appSecret);
|
|
1378
1429
|
const code = encodeConnectCode(serverUrl, token);
|
|
1379
|
-
res.json({ code });
|
|
1430
|
+
res.json({ code, url: serverUrl });
|
|
1380
1431
|
});
|
|
1381
1432
|
app.post("/api/settings/config", async (req, res) => {
|
|
1382
1433
|
const body = req.body;
|
|
@@ -119,6 +119,13 @@ export declare class StructuredSessionManager {
|
|
|
119
119
|
* prepends magic words, codex runner overrides `model_reasoning_effort`).
|
|
120
120
|
*/
|
|
121
121
|
setSessionThinkingEffort(sessionId: string, effort: SessionSnapshot["thinkingEffort"]): SessionSnapshot;
|
|
122
|
+
/**
|
|
123
|
+
* Switch the execution mode of a structured session mid-flight. Takes effect on
|
|
124
|
+
* the next message/query — permission policy, append-system-prompt and CLI flags
|
|
125
|
+
* are all re-derived from session.mode per turn. Mirrors setSessionModel; also
|
|
126
|
+
* re-syncs autoApprovePermissions so the permission posture matches the new mode.
|
|
127
|
+
*/
|
|
128
|
+
setSessionMode(sessionId: string, mode: ExecutionMode): SessionSnapshot;
|
|
122
129
|
/** Toggle auto-approve for the session. */
|
|
123
130
|
toggleAutoApprove(sessionId: string): SessionSnapshot;
|
|
124
131
|
/** Resolve a specific escalation by requestId. */
|
|
@@ -904,6 +904,29 @@ export class StructuredSessionManager {
|
|
|
904
904
|
});
|
|
905
905
|
return updated;
|
|
906
906
|
}
|
|
907
|
+
/**
|
|
908
|
+
* Switch the execution mode of a structured session mid-flight. Takes effect on
|
|
909
|
+
* the next message/query — permission policy, append-system-prompt and CLI flags
|
|
910
|
+
* are all re-derived from session.mode per turn. Mirrors setSessionModel; also
|
|
911
|
+
* re-syncs autoApprovePermissions so the permission posture matches the new mode.
|
|
912
|
+
*/
|
|
913
|
+
setSessionMode(sessionId, mode) {
|
|
914
|
+
const session = this.requireSession(sessionId);
|
|
915
|
+
const autoApprove = shouldAutoApproveForMode(mode);
|
|
916
|
+
const updated = {
|
|
917
|
+
...session,
|
|
918
|
+
mode,
|
|
919
|
+
autoApprovePermissions: autoApprove,
|
|
920
|
+
};
|
|
921
|
+
this.sessions.set(sessionId, updated);
|
|
922
|
+
this.storage.saveSession(updated);
|
|
923
|
+
this.emit({
|
|
924
|
+
type: "status",
|
|
925
|
+
sessionId,
|
|
926
|
+
data: { sessionKind: "structured", mode, autoApprovePermissions: autoApprove },
|
|
927
|
+
});
|
|
928
|
+
return updated;
|
|
929
|
+
}
|
|
907
930
|
/** Toggle auto-approve for the session. */
|
|
908
931
|
toggleAutoApprove(sessionId) {
|
|
909
932
|
const session = this.requireSession(sessionId);
|