@co0ontty/wand 1.6.2 → 1.9.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/config.js +40 -0
- package/dist/git-worktree.d.ts +28 -0
- package/dist/git-worktree.js +287 -0
- package/dist/message-parser.d.ts +1 -1
- package/dist/message-parser.js +275 -1
- package/dist/process-manager.d.ts +6 -3
- package/dist/process-manager.js +135 -81
- package/dist/pty-text-utils.js +79 -29
- package/dist/server-session-routes.js +192 -7
- package/dist/server.js +38 -1
- package/dist/session-logger.d.ts +2 -0
- package/dist/session-logger.js +23 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +258 -58
- package/dist/structured-session-manager.d.ts +5 -0
- package/dist/structured-session-manager.js +107 -43
- package/dist/types.d.ts +65 -0
- package/dist/web-ui/content/scripts.js +2251 -500
- package/dist/web-ui/content/styles.css +1154 -119
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import express from "express";
|
|
2
2
|
import { parseMessages } from "./message-parser.js";
|
|
3
3
|
import { SessionInputError } from "./process-manager.js";
|
|
4
|
+
import { checkSessionWorktreeMergeability, cleanupSessionWorktree, getWorktreeMergeErrorCode, mergeSessionWorktree, WorktreeMergeError } from "./git-worktree.js";
|
|
4
5
|
export function getErrorMessage(error, fallback) {
|
|
5
6
|
return error instanceof Error ? error.message : fallback;
|
|
6
7
|
}
|
|
@@ -70,6 +71,66 @@ function listAllSessions(processes, structured) {
|
|
|
70
71
|
return [...structured.list(), ...processes.list()]
|
|
71
72
|
.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
72
73
|
}
|
|
74
|
+
function requireWorktreeSession(snapshot) {
|
|
75
|
+
if (!snapshot) {
|
|
76
|
+
throw new Error("未找到该会话。");
|
|
77
|
+
}
|
|
78
|
+
if (!snapshot.worktreeEnabled || !snapshot.worktree?.branch || !snapshot.worktree?.path) {
|
|
79
|
+
throw new Error("该会话未启用 worktree 模式。 ");
|
|
80
|
+
}
|
|
81
|
+
return snapshot;
|
|
82
|
+
}
|
|
83
|
+
function buildWorktreeMergeInfo(current, status, info) {
|
|
84
|
+
return {
|
|
85
|
+
...(current.worktreeMergeInfo ?? null),
|
|
86
|
+
...(info ?? null),
|
|
87
|
+
lastError: info?.lastError,
|
|
88
|
+
conflict: info?.conflict,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function saveWorktreeMergeState(storage, current, status, info) {
|
|
92
|
+
const mergedInfo = buildWorktreeMergeInfo(current, status, info);
|
|
93
|
+
const updated = {
|
|
94
|
+
...current,
|
|
95
|
+
worktreeMergeStatus: status,
|
|
96
|
+
worktreeMergeInfo: mergedInfo,
|
|
97
|
+
};
|
|
98
|
+
storage.saveSessionMetadata(updated);
|
|
99
|
+
return updated;
|
|
100
|
+
}
|
|
101
|
+
function getWorktreeMergeResponseStatus(error) {
|
|
102
|
+
const code = getWorktreeMergeErrorCode(error);
|
|
103
|
+
if (!code) {
|
|
104
|
+
return 400;
|
|
105
|
+
}
|
|
106
|
+
if (code === "WORKTREE_MERGE_CONFLICT") {
|
|
107
|
+
return 409;
|
|
108
|
+
}
|
|
109
|
+
return 400;
|
|
110
|
+
}
|
|
111
|
+
function getWorktreeMergePayload(error, fallback) {
|
|
112
|
+
if (error instanceof WorktreeMergeError) {
|
|
113
|
+
return {
|
|
114
|
+
error: error.message,
|
|
115
|
+
errorCode: error.code,
|
|
116
|
+
result: error.result ?? null,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
error: getErrorMessage(error, fallback),
|
|
121
|
+
errorCode: getWorktreeMergeErrorCode(error) ?? null,
|
|
122
|
+
result: null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function getLatestSessionSnapshot(processes, structured, storage, id) {
|
|
126
|
+
return getSessionById(processes, structured, id) ?? storage.getSession(id);
|
|
127
|
+
}
|
|
128
|
+
function canMergeSession(snapshot) {
|
|
129
|
+
return Boolean(snapshot.worktreeEnabled && snapshot.worktree?.branch && snapshot.worktree?.path);
|
|
130
|
+
}
|
|
131
|
+
function isMergeActionAllowed(snapshot) {
|
|
132
|
+
return snapshot.status !== "running";
|
|
133
|
+
}
|
|
73
134
|
export function registerSessionRoutes(app, processes, structured, storage, defaultMode) {
|
|
74
135
|
app.get("/api/sessions", (_req, res) => {
|
|
75
136
|
const all = listAllSessions(processes, structured);
|
|
@@ -78,13 +139,18 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
78
139
|
});
|
|
79
140
|
app.post("/api/structured-sessions", express.json(), async (req, res) => {
|
|
80
141
|
const body = req.body;
|
|
81
|
-
console.log("[WAND] POST /api/structured-sessions body:", JSON.stringify({ cwd: body.cwd, mode: body.mode, runner: body.runner, hasPrompt: !!body.prompt }));
|
|
142
|
+
console.log("[WAND] POST /api/structured-sessions body:", JSON.stringify({ cwd: body.cwd, mode: body.mode, runner: body.runner, provider: body.provider, worktreeEnabled: body.worktreeEnabled === true, hasPrompt: !!body.prompt }));
|
|
82
143
|
try {
|
|
144
|
+
if (body.provider && body.provider !== "claude") {
|
|
145
|
+
res.status(400).json({ error: "结构化会话当前仅支持 Claude provider。" });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
83
148
|
const snapshot = structured.createSession({
|
|
84
149
|
cwd: body.cwd?.trim() || process.cwd(),
|
|
85
150
|
mode: normalizeMode(body.mode, defaultMode),
|
|
86
151
|
prompt: body.prompt,
|
|
87
152
|
runner: body.runner ?? "claude-cli-print",
|
|
153
|
+
worktreeEnabled: body.worktreeEnabled === true,
|
|
88
154
|
});
|
|
89
155
|
console.log("[WAND] structured session created:", JSON.stringify({ id: snapshot.id, sessionKind: snapshot.sessionKind, runner: snapshot.runner, status: snapshot.status }));
|
|
90
156
|
res.status(201).json(snapshot);
|
|
@@ -112,6 +178,98 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
112
178
|
res.status(400).json({ error: getErrorMessage(error, "无法发送结构化消息。") });
|
|
113
179
|
}
|
|
114
180
|
});
|
|
181
|
+
app.post("/api/sessions/:id/worktree/merge/check", (req, res) => {
|
|
182
|
+
try {
|
|
183
|
+
const current = requireWorktreeSession(getLatestSessionSnapshot(processes, structured, storage, req.params.id));
|
|
184
|
+
if (!isMergeActionAllowed(current)) {
|
|
185
|
+
res.status(409).json({ error: "会话仍在运行,请结束后再合并。", errorCode: "SESSION_STILL_RUNNING" });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const checking = saveWorktreeMergeState(storage, current, "checking", {
|
|
189
|
+
...(current.worktreeMergeInfo ?? null),
|
|
190
|
+
targetBranch: current.worktreeMergeInfo?.targetBranch,
|
|
191
|
+
lastError: undefined,
|
|
192
|
+
conflict: false,
|
|
193
|
+
});
|
|
194
|
+
const result = checkSessionWorktreeMergeability({
|
|
195
|
+
worktree: checking.worktree,
|
|
196
|
+
targetBranch: current.worktreeMergeInfo?.targetBranch,
|
|
197
|
+
});
|
|
198
|
+
const nextStatus = result.ok ? "ready" : "failed";
|
|
199
|
+
const updated = saveWorktreeMergeState(storage, checking, nextStatus, {
|
|
200
|
+
targetBranch: result.targetBranch,
|
|
201
|
+
conflict: result.hasConflicts,
|
|
202
|
+
lastError: result.ok ? undefined : result.reason,
|
|
203
|
+
});
|
|
204
|
+
res.json({ session: updated, result });
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
res.status(getWorktreeMergeResponseStatus(error)).json(getWorktreeMergePayload(error, "无法检查 worktree 合并状态。"));
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
app.post("/api/sessions/:id/worktree/merge", express.json(), (req, res) => {
|
|
211
|
+
try {
|
|
212
|
+
const current = requireWorktreeSession(getLatestSessionSnapshot(processes, structured, storage, req.params.id));
|
|
213
|
+
if (!isMergeActionAllowed(current)) {
|
|
214
|
+
res.status(409).json({ error: "会话仍在运行,请结束后再合并。", errorCode: "SESSION_STILL_RUNNING" });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const merging = saveWorktreeMergeState(storage, current, "merging", {
|
|
218
|
+
...(current.worktreeMergeInfo ?? null),
|
|
219
|
+
lastError: undefined,
|
|
220
|
+
conflict: false,
|
|
221
|
+
});
|
|
222
|
+
const result = mergeSessionWorktree({
|
|
223
|
+
worktree: merging.worktree,
|
|
224
|
+
targetBranch: current.worktreeMergeInfo?.targetBranch,
|
|
225
|
+
});
|
|
226
|
+
const updated = saveWorktreeMergeState(storage, merging, "merged", {
|
|
227
|
+
targetBranch: result.targetBranch,
|
|
228
|
+
mergedAt: result.mergedAt,
|
|
229
|
+
mergeCommit: result.mergeCommit,
|
|
230
|
+
cleanupDone: result.cleanupDone,
|
|
231
|
+
lastError: undefined,
|
|
232
|
+
conflict: false,
|
|
233
|
+
});
|
|
234
|
+
res.json({ session: updated, result });
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
const current = getLatestSessionSnapshot(processes, structured, storage, req.params.id);
|
|
238
|
+
if (current && canMergeSession(current)) {
|
|
239
|
+
const payload = getWorktreeMergePayload(error, "无法合并 worktree。");
|
|
240
|
+
saveWorktreeMergeState(storage, current, "failed", {
|
|
241
|
+
...(current.worktreeMergeInfo ?? null),
|
|
242
|
+
targetBranch: payload.result?.targetBranch ?? current.worktreeMergeInfo?.targetBranch,
|
|
243
|
+
mergedAt: payload.result?.mergedAt,
|
|
244
|
+
mergeCommit: payload.result?.mergeCommit,
|
|
245
|
+
cleanupDone: payload.result?.cleanupDone,
|
|
246
|
+
lastError: payload.error,
|
|
247
|
+
conflict: payload.result?.conflict === true,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
res.status(getWorktreeMergeResponseStatus(error)).json(getWorktreeMergePayload(error, "无法合并 worktree。"));
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
app.post("/api/sessions/:id/worktree/cleanup", (req, res) => {
|
|
254
|
+
try {
|
|
255
|
+
const current = requireWorktreeSession(getLatestSessionSnapshot(processes, structured, storage, req.params.id));
|
|
256
|
+
if (current.worktreeMergeStatus !== "merged" || current.worktreeMergeInfo?.cleanupDone !== false) {
|
|
257
|
+
res.status(400).json({ error: "当前 worktree 无需补偿清理。", errorCode: "WORKTREE_CLEANUP_NOT_NEEDED" });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
cleanupSessionWorktree({ worktree: current.worktree });
|
|
261
|
+
const updated = saveWorktreeMergeState(storage, current, "merged", {
|
|
262
|
+
...(current.worktreeMergeInfo ?? null),
|
|
263
|
+
cleanupDone: true,
|
|
264
|
+
lastError: undefined,
|
|
265
|
+
conflict: false,
|
|
266
|
+
});
|
|
267
|
+
res.json({ session: updated, ok: true });
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
res.status(getWorktreeMergeResponseStatus(error)).json(getWorktreeMergePayload(error, "无法清理 worktree。"));
|
|
271
|
+
}
|
|
272
|
+
});
|
|
115
273
|
app.post("/api/sessions/batch-delete", express.json(), (req, res) => {
|
|
116
274
|
const sessionIds = Array.isArray(req.body?.sessionIds)
|
|
117
275
|
? req.body.sessionIds.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
@@ -148,17 +306,21 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
148
306
|
res.status(404).json({ error: "未找到该会话,可能已被删除。" });
|
|
149
307
|
return;
|
|
150
308
|
}
|
|
309
|
+
const transcriptOutput = (snapshot.sessionKind ?? "pty") === "pty"
|
|
310
|
+
? processes.getPtyTranscript(snapshot.id) ?? snapshot.output
|
|
311
|
+
: snapshot.output;
|
|
151
312
|
if (req.query.format === "chat") {
|
|
152
313
|
const allowFallback = (snapshot.sessionKind ?? "pty") === "pty";
|
|
314
|
+
const fallbackOutput = allowFallback ? transcriptOutput : "";
|
|
153
315
|
const messages = snapshot.messages && snapshot.messages.length > 0
|
|
154
316
|
? snapshot.messages
|
|
155
317
|
: allowFallback
|
|
156
|
-
? parseMessages(snapshot.
|
|
318
|
+
? parseMessages(fallbackOutput, snapshot.command)
|
|
157
319
|
: [];
|
|
158
|
-
res.json({ ...snapshot, messages });
|
|
320
|
+
res.json({ ...snapshot, output: transcriptOutput, messages });
|
|
159
321
|
}
|
|
160
322
|
else {
|
|
161
|
-
res.json(snapshot);
|
|
323
|
+
res.json({ ...snapshot, output: transcriptOutput });
|
|
162
324
|
}
|
|
163
325
|
});
|
|
164
326
|
app.post("/api/sessions/:id/resume", (req, res) => {
|
|
@@ -176,6 +338,10 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
176
338
|
res.status(400).json({ error: "结构化会话不支持 Claude CLI resume。" });
|
|
177
339
|
return;
|
|
178
340
|
}
|
|
341
|
+
if (existingSession.provider && existingSession.provider !== "claude") {
|
|
342
|
+
res.status(400).json({ error: "只有 Claude provider 支持恢复功能。" });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
179
345
|
const claudeSessionId = existingSession.claudeSessionId;
|
|
180
346
|
if (!claudeSessionId) {
|
|
181
347
|
res.status(400).json({ error: "此会话没有 Claude 会话 ID,无法恢复。" });
|
|
@@ -207,6 +373,10 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
207
373
|
}
|
|
208
374
|
const existingSession = storage.getLatestSessionByClaudeSessionId(claudeSessionId);
|
|
209
375
|
if (existingSession) {
|
|
376
|
+
if (existingSession.provider && existingSession.provider !== "claude") {
|
|
377
|
+
res.status(400).json({ error: "只有 Claude provider 支持按 Claude Session ID 恢复。" });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
210
380
|
const command = existingSession.command.trim();
|
|
211
381
|
if ((existingSession.sessionKind ?? "pty") !== "pty") {
|
|
212
382
|
res.status(400).json({ error: "结构化会话不支持按 Claude Session ID 恢复。" });
|
|
@@ -284,7 +454,12 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
284
454
|
app.post("/api/sessions/:id/approve-permission", (req, res) => {
|
|
285
455
|
try {
|
|
286
456
|
if (structured.get(req.params.id)) {
|
|
287
|
-
res.
|
|
457
|
+
res.status(400).json({ error: "结构化会话不需要终端权限操作。" });
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const snapshot = processes.get(req.params.id);
|
|
461
|
+
if (snapshot?.provider === "codex") {
|
|
462
|
+
res.status(400).json({ error: "Codex provider 不支持权限批准操作。" });
|
|
288
463
|
return;
|
|
289
464
|
}
|
|
290
465
|
res.json(processes.approvePermission(req.params.id));
|
|
@@ -296,7 +471,12 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
296
471
|
app.post("/api/sessions/:id/deny-permission", (req, res) => {
|
|
297
472
|
try {
|
|
298
473
|
if (structured.get(req.params.id)) {
|
|
299
|
-
res.
|
|
474
|
+
res.status(400).json({ error: "结构化会话不需要终端权限操作。" });
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const snapshot = processes.get(req.params.id);
|
|
478
|
+
if (snapshot?.provider === "codex") {
|
|
479
|
+
res.status(400).json({ error: "Codex provider 不支持权限拒绝操作。" });
|
|
300
480
|
return;
|
|
301
481
|
}
|
|
302
482
|
res.json(processes.denyPermission(req.params.id));
|
|
@@ -308,7 +488,12 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
308
488
|
app.post("/api/sessions/:id/toggle-auto-approve", (req, res) => {
|
|
309
489
|
try {
|
|
310
490
|
if (structured.get(req.params.id)) {
|
|
311
|
-
res.
|
|
491
|
+
res.status(400).json({ error: "结构化会话不需要切换终端自动批准。" });
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const snapshot = processes.get(req.params.id);
|
|
495
|
+
if (snapshot?.provider === "codex") {
|
|
496
|
+
res.status(400).json({ error: "Codex provider 不支持自动批准切换。" });
|
|
312
497
|
return;
|
|
313
498
|
}
|
|
314
499
|
res.json(processes.toggleAutoApprove(req.params.id));
|
package/dist/server.js
CHANGED
|
@@ -519,6 +519,40 @@ export async function startServer(config, configPath) {
|
|
|
519
519
|
changed = true;
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
|
+
if (body.uiPreferences && typeof body.uiPreferences === "object") {
|
|
523
|
+
const defaultPanelStateInput = body.uiPreferences.defaultPanelState;
|
|
524
|
+
if (defaultPanelStateInput && typeof defaultPanelStateInput === "object") {
|
|
525
|
+
const nextDefaultPanelState = {
|
|
526
|
+
...config.uiPreferences?.defaultPanelState,
|
|
527
|
+
};
|
|
528
|
+
let panelChanged = false;
|
|
529
|
+
const panelFields = [
|
|
530
|
+
"sessionsDrawerOpen",
|
|
531
|
+
"filePanelOpen",
|
|
532
|
+
"shortcutsExpanded",
|
|
533
|
+
"claudeHistoryExpanded",
|
|
534
|
+
"chatMessageExpanded",
|
|
535
|
+
"structuredThinkingExpanded",
|
|
536
|
+
"structuredToolGroupExpanded",
|
|
537
|
+
"structuredInlineToolExpanded",
|
|
538
|
+
"structuredTerminalExpanded",
|
|
539
|
+
"structuredToolCardExpanded"
|
|
540
|
+
];
|
|
541
|
+
for (const field of panelFields) {
|
|
542
|
+
if (field in defaultPanelStateInput && typeof defaultPanelStateInput[field] === "boolean") {
|
|
543
|
+
nextDefaultPanelState[field] = defaultPanelStateInput[field];
|
|
544
|
+
panelChanged = true;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (panelChanged) {
|
|
548
|
+
config.uiPreferences = {
|
|
549
|
+
...config.uiPreferences,
|
|
550
|
+
defaultPanelState: nextDefaultPanelState,
|
|
551
|
+
};
|
|
552
|
+
changed = true;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
522
556
|
if (!changed) {
|
|
523
557
|
res.status(400).json({ error: "没有可更新的配置字段。" });
|
|
524
558
|
return;
|
|
@@ -858,7 +892,10 @@ export async function startServer(config, configPath) {
|
|
|
858
892
|
}
|
|
859
893
|
const initialInput = body.initialInput?.trim();
|
|
860
894
|
try {
|
|
861
|
-
const snapshot = processes.start(body.command, body.cwd, normalizeMode(body.mode, config.defaultMode), initialInput || undefined
|
|
895
|
+
const snapshot = processes.start(body.command, body.cwd, normalizeMode(body.mode, config.defaultMode), initialInput || undefined, {
|
|
896
|
+
worktreeEnabled: body.worktreeEnabled === true,
|
|
897
|
+
provider: body.provider,
|
|
898
|
+
});
|
|
862
899
|
res.status(201).json(snapshot);
|
|
863
900
|
}
|
|
864
901
|
catch (error) {
|
package/dist/session-logger.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export declare class SessionLogger {
|
|
|
44
44
|
private rotatePtyLog;
|
|
45
45
|
/** Append raw PTY output chunk */
|
|
46
46
|
appendPtyOutput(sessionId: string, chunk: string): void;
|
|
47
|
+
/** Read the full PTY transcript including rotated logs, oldest first. */
|
|
48
|
+
readPtyOutput(sessionId: string): string | null;
|
|
47
49
|
/** Append a native mode NDJSON event */
|
|
48
50
|
appendStreamEvent(sessionId: string, event: unknown): void;
|
|
49
51
|
/** Save the current structured messages snapshot */
|
package/dist/session-logger.js
CHANGED
|
@@ -89,6 +89,29 @@ export class SessionLogger {
|
|
|
89
89
|
// Non-critical — don't let logging failures affect main flow
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
/** Read the full PTY transcript including rotated logs, oldest first. */
|
|
93
|
+
readPtyOutput(sessionId) {
|
|
94
|
+
try {
|
|
95
|
+
const dir = this.ensureDir(sessionId);
|
|
96
|
+
const parts = [];
|
|
97
|
+
for (let index = PTY_LOG_MAX_ROTATIONS; index >= 1; index -= 1) {
|
|
98
|
+
const rotatedPath = path.join(dir, `pty-output.log.${index}`);
|
|
99
|
+
if (existsSync(rotatedPath)) {
|
|
100
|
+
parts.push(readFileSync(rotatedPath, "utf8"));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const currentPath = path.join(dir, "pty-output.log");
|
|
104
|
+
if (existsSync(currentPath)) {
|
|
105
|
+
parts.push(readFileSync(currentPath, "utf8"));
|
|
106
|
+
}
|
|
107
|
+
if (parts.length === 0)
|
|
108
|
+
return null;
|
|
109
|
+
return parts.join("");
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
92
115
|
/** Append a native mode NDJSON event */
|
|
93
116
|
appendStreamEvent(sessionId, event) {
|
|
94
117
|
try {
|
package/dist/storage.d.ts
CHANGED
|
@@ -37,5 +37,6 @@ export declare class WandStorage {
|
|
|
37
37
|
getLatestSessionByClaudeSessionId(claudeSessionId: string): SessionSnapshot | null;
|
|
38
38
|
loadSessions(): SessionSnapshot[];
|
|
39
39
|
private mapSessionRow;
|
|
40
|
+
updateSessionWorktreeMergeState(id: string, status: SessionSnapshot["worktreeMergeStatus"], info: SessionSnapshot["worktreeMergeInfo"]): SessionSnapshot | null;
|
|
40
41
|
deleteSession(id: string): void;
|
|
41
42
|
}
|