@co0ontty/wand 4.66.0 → 4.67.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.
- package/dist/build-info.json +3 -3
- package/dist/git-worktree.js +10 -1
- package/dist/server-session-routes.js +2 -1
- package/dist/server-settings-routes.js +29 -23
- package/dist/structured-pi-adapter.d.ts +6 -1
- package/dist/structured-pi-adapter.js +12 -2
- package/dist/structured-session-manager.js +7 -1
- package/dist/wand-task-sync.js +28 -4
- package/dist/web-ui/content/scripts.js +3572 -3569
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +2 -2
- package/dist/web-ui/embedded-assets.js +2 -2
- package/package.json +3 -2
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-09-
|
|
4
|
-
"version": "4.
|
|
2
|
+
"commit": "19d226c7839da00873a245da7e252395f095d730",
|
|
3
|
+
"builtAt": "2026-09-16T23:41:08.180Z",
|
|
4
|
+
"version": "4.67.0",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
package/dist/git-worktree.js
CHANGED
|
@@ -409,7 +409,16 @@ export async function mergeSessionWorktreeAsync(options) {
|
|
|
409
409
|
}
|
|
410
410
|
export function prepareSessionWorktree(options) {
|
|
411
411
|
const resolvedCwd = path.resolve(options.cwd);
|
|
412
|
-
|
|
412
|
+
let repoRoot;
|
|
413
|
+
try {
|
|
414
|
+
repoRoot = runGit(["rev-parse", "--show-toplevel"], resolvedCwd);
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
if (/not a git repository/i.test(getGitErrorMessage(error))) {
|
|
418
|
+
throw new Error("当前目录不是 Git 仓库。请选择 Git 仓库,或关闭“独立 worktree 隔离”后重试。");
|
|
419
|
+
}
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
413
422
|
if (!repoRoot || !existsSync(repoRoot)) {
|
|
414
423
|
throw new Error("当前目录不在 git 仓库中,无法启用 worktree 模式。");
|
|
415
424
|
}
|
|
@@ -401,7 +401,8 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
401
401
|
}
|
|
402
402
|
try {
|
|
403
403
|
const tree = currentDirectoryTree();
|
|
404
|
-
|
|
404
|
+
const hasWorkspace = storage.listWorkspaces().some((workspace) => workspace.kind !== "global" && normalizeSessionDirectory(workspace.cwd) === directoryPath);
|
|
405
|
+
if (!directoryTreeContainsPath(tree.roots, directoryPath) && !hasWorkspace) {
|
|
405
406
|
res.status(404).json({ error: "未找到该会话目录。" });
|
|
406
407
|
return;
|
|
407
408
|
}
|
|
@@ -105,30 +105,36 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
105
105
|
});
|
|
106
106
|
}));
|
|
107
107
|
app.get("/api/settings/env-preview", (req, res, next) => {
|
|
108
|
-
if (req.query.reveal === "1")
|
|
109
|
-
return requireAdmin(req, res, next);
|
|
110
|
-
next();
|
|
111
|
-
}, (req, res) => {
|
|
112
|
-
const inheritEnv = config.inheritEnv !== false;
|
|
113
|
-
const env = buildChildEnv(inheritEnv, {
|
|
114
|
-
WAND_MODE: "<runtime>",
|
|
115
|
-
WAND_AUTO_CONFIRM: "<runtime>",
|
|
116
|
-
WAND_AUTO_EDIT: "<runtime>",
|
|
117
|
-
});
|
|
118
108
|
const reveal = req.query.reveal === "1" || req.query.reveal === "true";
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
109
|
+
if (reveal) {
|
|
110
|
+
return requireAdmin(req, res, (error) => {
|
|
111
|
+
if (error)
|
|
112
|
+
return next(error);
|
|
113
|
+
sendEnvironmentPreview(true);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
sendEnvironmentPreview(false);
|
|
117
|
+
function sendEnvironmentPreview(reveal) {
|
|
118
|
+
const inheritEnv = config.inheritEnv !== false;
|
|
119
|
+
const env = buildChildEnv(inheritEnv, {
|
|
120
|
+
WAND_MODE: "<runtime>",
|
|
121
|
+
WAND_AUTO_CONFIRM: "<runtime>",
|
|
122
|
+
WAND_AUTO_EDIT: "<runtime>",
|
|
123
|
+
});
|
|
124
|
+
const sensitivePattern = /(KEY|TOKEN|SECRET|PASSWORD|AUTH|CREDENTIAL|COOKIE|SESSION)/i;
|
|
125
|
+
const entries = Object.keys(env).sort().map((name) => {
|
|
126
|
+
const raw = env[name] ?? "";
|
|
127
|
+
const sensitive = sensitivePattern.test(name);
|
|
128
|
+
const placeholder = raw.startsWith("<") && raw.endsWith(">");
|
|
129
|
+
return {
|
|
130
|
+
name,
|
|
131
|
+
value: sensitive && !reveal && !placeholder ? "***" : raw,
|
|
132
|
+
length: raw.length,
|
|
133
|
+
sensitive,
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
res.json({ inheritEnv, total: entries.length, reveal, entries });
|
|
137
|
+
}
|
|
132
138
|
});
|
|
133
139
|
app.post("/api/settings/system-ai/import", requireAdmin, (_req, res) => {
|
|
134
140
|
const imported = discoverCliSystemAiConfigs();
|
|
@@ -2,12 +2,17 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import type { StructuredExecHost } from "./structured-exec-host.js";
|
|
3
3
|
import type { StructuredRunnerAdapter, StructuredRunnerContext, StructuredRunnerExecution, StructuredRunnerObserver, StructuredRunnerTurnState } from "./structured-runner.js";
|
|
4
4
|
import type { SessionSnapshot } from "./types.js";
|
|
5
|
+
type PiTurnState = StructuredRunnerTurnState & {
|
|
6
|
+
pendingSessionId?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function isMissingPiSession(error: string | null | undefined, sessionId: string | null | undefined): boolean;
|
|
5
9
|
export declare function buildPiArgs(session: SessionSnapshot, prompt: string): string[];
|
|
6
10
|
export declare function piToolName(name: string): string;
|
|
7
|
-
export declare function applyPiEvent(state:
|
|
11
|
+
export declare function applyPiEvent(state: PiTurnState, event: Record<string, unknown>): string | null;
|
|
8
12
|
export declare class PiRunner implements StructuredRunnerAdapter {
|
|
9
13
|
private readonly spawnProcess;
|
|
10
14
|
private readonly execHost?;
|
|
11
15
|
constructor(spawnProcess?: typeof spawn, execHost?: StructuredExecHost | undefined);
|
|
12
16
|
start(context: StructuredRunnerContext, observer: StructuredRunnerObserver): StructuredRunnerExecution;
|
|
13
17
|
}
|
|
18
|
+
export {};
|
|
@@ -2,6 +2,9 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
3
3
|
import { asRecord } from "./structured-content.js";
|
|
4
4
|
import { thinkingEffortToPiLevel } from "./structured-provider-common.js";
|
|
5
|
+
export function isMissingPiSession(error, sessionId) {
|
|
6
|
+
return !!sessionId && !!error?.includes(`No session found matching '${sessionId}'`);
|
|
7
|
+
}
|
|
5
8
|
function textContent(value) {
|
|
6
9
|
if (typeof value === "string")
|
|
7
10
|
return value;
|
|
@@ -88,8 +91,10 @@ function applyPiAssistantMessage(state, message) {
|
|
|
88
91
|
return null;
|
|
89
92
|
}
|
|
90
93
|
export function applyPiEvent(state, event) {
|
|
94
|
+
// Pi announces the ID before it writes a session file. The file is only
|
|
95
|
+
// created when an assistant message is saved, so this ID is not resumable yet.
|
|
91
96
|
if (event.type === "session" && typeof event.id === "string")
|
|
92
|
-
state.
|
|
97
|
+
state.pendingSessionId = event.id;
|
|
93
98
|
if (event.type === "message_update") {
|
|
94
99
|
const update = asRecord(event.assistantMessageEvent);
|
|
95
100
|
const delta = typeof update?.delta === "string" ? update.delta : "";
|
|
@@ -136,13 +141,18 @@ export function applyPiEvent(state, event) {
|
|
|
136
141
|
}
|
|
137
142
|
if (event.type === "message_end" || event.type === "turn_end") {
|
|
138
143
|
const message = asRecord(event.message);
|
|
139
|
-
if (message?.role === "assistant")
|
|
144
|
+
if (message?.role === "assistant") {
|
|
145
|
+
if (state.pendingSessionId)
|
|
146
|
+
state.sessionId = state.pendingSessionId;
|
|
140
147
|
return applyPiAssistantMessage(state, message);
|
|
148
|
+
}
|
|
141
149
|
}
|
|
142
150
|
if (event.type === "agent_end" && Array.isArray(event.messages)) {
|
|
143
151
|
for (const raw of event.messages) {
|
|
144
152
|
const message = asRecord(raw);
|
|
145
153
|
if (message?.role === "assistant") {
|
|
154
|
+
if (state.pendingSessionId)
|
|
155
|
+
state.sessionId = state.pendingSessionId;
|
|
146
156
|
const error = applyPiAssistantMessage(state, message);
|
|
147
157
|
if (error)
|
|
148
158
|
return error;
|
|
@@ -19,7 +19,7 @@ import { captureTaskMeta, extractClaudeAssistantMessage, extractClaudeModelName,
|
|
|
19
19
|
import { OpenCodeRunner, applyOpenCodeEvent } from "./structured-opencode-adapter.js";
|
|
20
20
|
import { GrokRunner, applyGrokEvent } from "./structured-grok-adapter.js";
|
|
21
21
|
import { QoderRunner } from "./structured-qoder-adapter.js";
|
|
22
|
-
import { PiRunner, applyPiEvent } from "./structured-pi-adapter.js";
|
|
22
|
+
import { PiRunner, applyPiEvent, isMissingPiSession } from "./structured-pi-adapter.js";
|
|
23
23
|
import { structuredRunId, } from "./structured-exec-host.js";
|
|
24
24
|
import { defaultStructuredRunner, defaultStructuredState, isStructuredRunnerForProvider, normalizeThinkingEffort, resolveStructuredRunner, } from "./structured-provider-common.js";
|
|
25
25
|
import { enrichStructuredMessages, WAND_PROTOCOL_VERSION } from "./structured-client-protocol.js";
|
|
@@ -1137,6 +1137,9 @@ export class StructuredSessionManager {
|
|
|
1137
1137
|
content: [{ type: "text", text: prompt }],
|
|
1138
1138
|
};
|
|
1139
1139
|
const requestId = randomUUID();
|
|
1140
|
+
if (session.provider === "pi" && isMissingPiSession(session.structuredState?.lastError, session.claudeSessionId)) {
|
|
1141
|
+
session = { ...session, claudeSessionId: null };
|
|
1142
|
+
}
|
|
1140
1143
|
const updated = {
|
|
1141
1144
|
...session,
|
|
1142
1145
|
status: "running",
|
|
@@ -2408,6 +2411,9 @@ export class StructuredSessionManager {
|
|
|
2408
2411
|
primary: result.primaryError,
|
|
2409
2412
|
});
|
|
2410
2413
|
const failed = this.finishStructuredFailure(current, typeof result.exitCode === "number" ? result.exitCode : 1, errorText, result.state);
|
|
2414
|
+
if (provider === "pi" && isMissingPiSession(result.stderr, current.claudeSessionId)) {
|
|
2415
|
+
failed.claudeSessionId = null;
|
|
2416
|
+
}
|
|
2411
2417
|
this.sessions.set(sessionId, failed);
|
|
2412
2418
|
this.saveAuthoritativeSession(failed);
|
|
2413
2419
|
this.emitStructuredSnapshot(failed);
|
package/dist/wand-task-sync.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { provisionalTaskTitleFromDescription } from "./task-title.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isSessionProvider } from "./session-provider.js";
|
|
3
|
+
import { isClosedWandTaskStatus, normalizeWandTaskAgentMode } from "./task-types.js";
|
|
3
4
|
const UNNAMED_TASK_NAME = "未命名任务";
|
|
4
5
|
function boardDescriptionFor(task, workspace) {
|
|
5
6
|
const lines = [`项目:${workspace.name}`];
|
|
@@ -98,6 +99,17 @@ function promoteUngroupedCard(storage, card, patch) {
|
|
|
98
99
|
workspaceTaskId: patch.workspaceTaskId !== undefined ? patch.workspaceTaskId : card.workspaceTaskId,
|
|
99
100
|
}) ?? card;
|
|
100
101
|
}
|
|
102
|
+
function agentFromSession(session) {
|
|
103
|
+
if (!isSessionProvider(session.provider))
|
|
104
|
+
return null;
|
|
105
|
+
const effort = session.thinkingEffort;
|
|
106
|
+
return {
|
|
107
|
+
provider: session.provider,
|
|
108
|
+
model: session.selectedModel || session.structuredState?.model || "default",
|
|
109
|
+
thinkingEffort: effort === "standard" || effort === "deep" || effort === "max" ? effort : "off",
|
|
110
|
+
mode: normalizeWandTaskAgentMode(session.provider, session.mode),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
101
113
|
/** 侧栏工作任务与看板卡片对账:已关联则复用,同项目同名未关联则挂上,否则新建。 */
|
|
102
114
|
export function ensureBoardTaskForWorkspaceTask(storage, task, workspace) {
|
|
103
115
|
const linked = storage.getWandTaskByWorkspaceTaskId(task.id);
|
|
@@ -119,7 +131,7 @@ export function ensureBoardTaskForWorkspaceTask(storage, task, workspace) {
|
|
|
119
131
|
workspaceTaskId: task.id,
|
|
120
132
|
title: task.name,
|
|
121
133
|
description: boardDescriptionFor(task, workspace),
|
|
122
|
-
status: task.status === "done" ? "done" : "
|
|
134
|
+
status: task.status === "done" ? "done" : "doing",
|
|
123
135
|
milestoneId: task.milestoneId ?? null,
|
|
124
136
|
});
|
|
125
137
|
}
|
|
@@ -132,9 +144,21 @@ export function syncUngroupedSessionsToBoard(storage) {
|
|
|
132
144
|
const bound = new Set(storage.listBoundWandTaskSessionIds());
|
|
133
145
|
for (const workspace of storage.listWorkspaces()) {
|
|
134
146
|
for (const task of storage.listWorkspaceTasks(workspace.id)) {
|
|
135
|
-
if (!isUnnamedWorkspaceTaskName(task.name))
|
|
136
|
-
continue;
|
|
137
147
|
const sessions = storage.listSessionsByWorkspaceTask(task.id);
|
|
148
|
+
if (!isUnnamedWorkspaceTaskName(task.name)) {
|
|
149
|
+
let card = ensureBoardTaskForWorkspaceTask(storage, task, workspace);
|
|
150
|
+
if (card && !card.agent) {
|
|
151
|
+
const agent = sessions.map(agentFromSession).find((value) => value !== null);
|
|
152
|
+
if (agent)
|
|
153
|
+
card = storage.updateWandTask(card.id, { agent }) ?? card;
|
|
154
|
+
}
|
|
155
|
+
if (card) {
|
|
156
|
+
bindSessions(storage, card.id, sessions.map((session) => session.id));
|
|
157
|
+
for (const session of sessions)
|
|
158
|
+
bound.add(session.id);
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
138
162
|
const best = pickBestSession(sessions);
|
|
139
163
|
const title = best ? boardTitleFromSession(best) : "";
|
|
140
164
|
if (!best || !title)
|