@co0ontty/wand 4.84.0-beta.ga87dbb0 → 4.85.0-beta.gcbbaae1
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/server-session-routes.js +25 -0
- package/dist/storage.d.ts +2 -0
- package/dist/storage.js +14 -0
- package/dist/structured-session-manager.d.ts +2 -0
- package/dist/structured-session-manager.js +26 -0
- package/dist/web-ui/content/scripts.js +234 -156
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +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-10-
|
|
4
|
-
"version": "4.
|
|
2
|
+
"commit": "cbbaae1e68608d5fcd347e91db13b65ce2e9af0f",
|
|
3
|
+
"builtAt": "2026-10-08T01:55:10.335Z",
|
|
4
|
+
"version": "4.85.0-beta.gcbbaae1",
|
|
5
5
|
"channel": "beta"
|
|
6
6
|
}
|
|
@@ -555,6 +555,31 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
555
555
|
sendRouteError(res, error, "无法启动结构化会话。");
|
|
556
556
|
}
|
|
557
557
|
}));
|
|
558
|
+
app.post("/api/sessions/:id/directory", (req, res) => {
|
|
559
|
+
const cwd = req.body?.cwd;
|
|
560
|
+
if (typeof cwd !== "string" || !cwd.trim()) {
|
|
561
|
+
res.status(400).json({ error: "请选择有效的运行目录。" });
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const id = req.params.id;
|
|
565
|
+
const owner = sessions.ownerOf(id);
|
|
566
|
+
if (!owner) {
|
|
567
|
+
res.status(404).json({ error: "未找到该会话。" });
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (owner !== "structured") {
|
|
571
|
+
res.status(400).json({ error: "只有新建空白结构化对话可以更换运行目录。" });
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
const updated = structured.setSessionDirectory(id, cwd);
|
|
576
|
+
onSessionCreated?.(updated.cwd);
|
|
577
|
+
res.json(sessionResponseDTO(updated, req));
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
sendRouteError(res, error, "切换运行目录失败。");
|
|
581
|
+
}
|
|
582
|
+
});
|
|
558
583
|
app.post("/api/sessions/:id/provider", (req, res) => {
|
|
559
584
|
const provider = req.body?.provider;
|
|
560
585
|
if (!isSessionProvider(provider)) {
|
package/dist/storage.d.ts
CHANGED
|
@@ -169,6 +169,8 @@ export declare class WandStorage {
|
|
|
169
169
|
listSessionsByWorkspace(workspaceId: string): SessionSnapshot[];
|
|
170
170
|
/** 显式更新某会话的工作空间归属(用于创建时绑定)。 */
|
|
171
171
|
getSessionWorkspace(sessionId: string): Pick<SessionSnapshot, "workspaceId" | "workspaceTaskId"> | null;
|
|
172
|
+
/** 空白独立会话的目录与项目归属一次落库;创建新项目失败也随事务回滚。 */
|
|
173
|
+
updateBlankSessionDirectory(sessionId: string, cwd: string): string;
|
|
172
174
|
setSessionWorkspaceId(sessionId: string, workspaceId: string | null): void;
|
|
173
175
|
/** Normal workspace counts exclude archived sessions and sessions in archived tasks. */
|
|
174
176
|
countSessionsByWorkspace(options?: {
|
package/dist/storage.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
6
|
import { GLOBAL_WORKSPACE_ID } from "./types.js";
|
|
7
7
|
import { normalizeSessionDirectory } from "./session-directory-tree.js";
|
|
8
|
+
import { ensureWorkspaceForCwd } from "./workspace-binding.js";
|
|
8
9
|
import { defaultPiCliSessionSettings, defaultPiSessionSettings, patchPiSessionSettings } from "./pi-session-settings.js";
|
|
9
10
|
import { parseHarnessExtensionState } from "./core-extension-host.js";
|
|
10
11
|
import { inferProviderFromCommand, inferProviderFromRunner, isSessionProvider, SESSION_PROVIDERS } from "./session-provider.js";
|
|
@@ -1694,6 +1695,19 @@ export class WandStorage {
|
|
|
1694
1695
|
.get(sessionId);
|
|
1695
1696
|
return row ? { workspaceId: row.workspace_id ?? undefined, workspaceTaskId: row.workspace_task_id ?? undefined } : null;
|
|
1696
1697
|
}
|
|
1698
|
+
/** 空白独立会话的目录与项目归属一次落库;创建新项目失败也随事务回滚。 */
|
|
1699
|
+
updateBlankSessionDirectory(sessionId, cwd) {
|
|
1700
|
+
return this.transaction(() => {
|
|
1701
|
+
const session = this.getSession(sessionId);
|
|
1702
|
+
if (!session || session.workspaceTaskId || session.worktreeEnabled || session.worktree) {
|
|
1703
|
+
throw new Error("任务或工作树会话必须保留原运行目录。");
|
|
1704
|
+
}
|
|
1705
|
+
const workspace = ensureWorkspaceForCwd(this, cwd);
|
|
1706
|
+
this.db.prepare("UPDATE command_sessions SET cwd = ?, workspace_id = ? WHERE id = ?")
|
|
1707
|
+
.run(cwd, workspace.id, sessionId);
|
|
1708
|
+
return workspace.id;
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1697
1711
|
setSessionWorkspaceId(sessionId, workspaceId) {
|
|
1698
1712
|
const binding = this.getSessionWorkspace(sessionId);
|
|
1699
1713
|
const task = binding?.workspaceTaskId ? this.getWorkspaceTask(binding.workspaceTaskId) : null;
|
|
@@ -225,6 +225,8 @@ export declare class StructuredSessionManager {
|
|
|
225
225
|
promoteQueuedMessage(sessionId: string, index: number, expectedText?: string, idempotencyKey?: string): Promise<SessionSnapshot>;
|
|
226
226
|
/** Clear all queued messages. No-op when queue is already empty. */
|
|
227
227
|
clearQueuedMessages(sessionId: string): SessionSnapshot;
|
|
228
|
+
/** 仅空白独立对话能改目录:不建替代会话、不迁移任务/worktree 或已接受的输入。 */
|
|
229
|
+
setSessionDirectory(sessionId: string, cwd: string): SessionSnapshot;
|
|
228
230
|
/** 新建空白对话可原位换 CLI;接受过输入、恢复会话和自动化不跨 provider 搬历史。 */
|
|
229
231
|
setSessionProvider(sessionId: string, provider: SessionProvider): SessionSnapshot;
|
|
230
232
|
getPiSettings(sessionId: string): {
|
|
@@ -2037,6 +2037,32 @@ export class StructuredSessionManager {
|
|
|
2037
2037
|
this.emitStructuredSnapshot(updated);
|
|
2038
2038
|
return updated;
|
|
2039
2039
|
}
|
|
2040
|
+
/** 仅空白独立对话能改目录:不建替代会话、不迁移任务/worktree 或已接受的输入。 */
|
|
2041
|
+
setSessionDirectory(sessionId, cwd) {
|
|
2042
|
+
if (typeof cwd !== "string" || !cwd.trim())
|
|
2043
|
+
throw new Error("请选择有效的运行目录。");
|
|
2044
|
+
const session = this.requireSession(sessionId);
|
|
2045
|
+
if (session.status !== "idle" || session.archived || session.structuredState?.inFlight ||
|
|
2046
|
+
this.pendingRunnerExecutions.has(sessionId) || session.claudeSessionId ||
|
|
2047
|
+
session.resumedFromSessionId || session.autoRecovered ||
|
|
2048
|
+
(session.messages?.length ?? 0) > 0 || (session.queuedMessages?.length ?? 0) > 0 ||
|
|
2049
|
+
session.automationId || (session.sessionSource && session.sessionSource !== "interactive") ||
|
|
2050
|
+
this.relayHandlerFor(session)) {
|
|
2051
|
+
throw new Error("只有尚未发送消息的新建空白对话可以更换运行目录。");
|
|
2052
|
+
}
|
|
2053
|
+
if (session.workspaceTaskId || session.worktreeEnabled || session.worktree) {
|
|
2054
|
+
throw new Error("任务或工作树会话必须保留原运行目录。");
|
|
2055
|
+
}
|
|
2056
|
+
const resolved = resolveSessionCwd(cwd, null);
|
|
2057
|
+
const workspaceId = this.storage.updateBlankSessionDirectory(sessionId, resolved);
|
|
2058
|
+
const updated = { ...session, cwd: resolved, workspaceId };
|
|
2059
|
+
this.sessions.set(sessionId, updated);
|
|
2060
|
+
this.emit({
|
|
2061
|
+
type: "status", sessionId,
|
|
2062
|
+
data: { sessionKind: "structured", cwd: resolved, workspaceId },
|
|
2063
|
+
});
|
|
2064
|
+
return updated;
|
|
2065
|
+
}
|
|
2040
2066
|
/** 新建空白对话可原位换 CLI;接受过输入、恢复会话和自动化不跨 provider 搬历史。 */
|
|
2041
2067
|
setSessionProvider(sessionId, provider) {
|
|
2042
2068
|
const session = this.requireSession(sessionId);
|