@co0ontty/wand 1.9.0 → 1.10.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 +0 -40
- package/dist/resume-policy.d.ts +0 -77
- package/dist/resume-policy.js +0 -162
- package/dist/server.js +27 -35
- package/dist/storage.js +38 -112
- package/dist/types.d.ts +0 -17
- package/dist/web-ui/content/scripts.js +817 -725
- package/dist/web-ui/content/styles.css +568 -758
- package/dist/web-ui/scripts.js +3 -6
- package/package.json +1 -1
package/dist/config.js
CHANGED
|
@@ -16,20 +16,6 @@ export const defaultConfig = () => ({
|
|
|
16
16
|
allowedCommandPrefixes: [],
|
|
17
17
|
shortcutLogMaxBytes: 10 * 1024 * 1024,
|
|
18
18
|
language: "",
|
|
19
|
-
uiPreferences: {
|
|
20
|
-
defaultPanelState: {
|
|
21
|
-
sessionsDrawerOpen: false,
|
|
22
|
-
filePanelOpen: false,
|
|
23
|
-
shortcutsExpanded: false,
|
|
24
|
-
claudeHistoryExpanded: true,
|
|
25
|
-
chatMessageExpanded: true,
|
|
26
|
-
structuredThinkingExpanded: true,
|
|
27
|
-
structuredToolGroupExpanded: false,
|
|
28
|
-
structuredInlineToolExpanded: false,
|
|
29
|
-
structuredTerminalExpanded: false,
|
|
30
|
-
structuredToolCardExpanded: false,
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
19
|
commandPresets: [
|
|
34
20
|
{
|
|
35
21
|
label: "Claude",
|
|
@@ -115,26 +101,8 @@ function normalizeStructuredChatPersona(input) {
|
|
|
115
101
|
return undefined;
|
|
116
102
|
return { user, assistant };
|
|
117
103
|
}
|
|
118
|
-
function normalizeDefaultPanelState(input) {
|
|
119
|
-
if (!input || typeof input !== "object")
|
|
120
|
-
return undefined;
|
|
121
|
-
const state = input;
|
|
122
|
-
return {
|
|
123
|
-
sessionsDrawerOpen: typeof state.sessionsDrawerOpen === "boolean" ? state.sessionsDrawerOpen : undefined,
|
|
124
|
-
filePanelOpen: typeof state.filePanelOpen === "boolean" ? state.filePanelOpen : undefined,
|
|
125
|
-
shortcutsExpanded: typeof state.shortcutsExpanded === "boolean" ? state.shortcutsExpanded : undefined,
|
|
126
|
-
claudeHistoryExpanded: typeof state.claudeHistoryExpanded === "boolean" ? state.claudeHistoryExpanded : undefined,
|
|
127
|
-
chatMessageExpanded: typeof state.chatMessageExpanded === "boolean" ? state.chatMessageExpanded : undefined,
|
|
128
|
-
structuredThinkingExpanded: typeof state.structuredThinkingExpanded === "boolean" ? state.structuredThinkingExpanded : undefined,
|
|
129
|
-
structuredToolGroupExpanded: typeof state.structuredToolGroupExpanded === "boolean" ? state.structuredToolGroupExpanded : undefined,
|
|
130
|
-
structuredInlineToolExpanded: typeof state.structuredInlineToolExpanded === "boolean" ? state.structuredInlineToolExpanded : undefined,
|
|
131
|
-
structuredTerminalExpanded: typeof state.structuredTerminalExpanded === "boolean" ? state.structuredTerminalExpanded : undefined,
|
|
132
|
-
structuredToolCardExpanded: typeof state.structuredToolCardExpanded === "boolean" ? state.structuredToolCardExpanded : undefined,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
104
|
function mergeWithDefaults(input) {
|
|
136
105
|
const defaults = defaultConfig();
|
|
137
|
-
const normalizedPanelState = normalizeDefaultPanelState(input.uiPreferences?.defaultPanelState);
|
|
138
106
|
return {
|
|
139
107
|
...defaults,
|
|
140
108
|
...input,
|
|
@@ -164,14 +132,6 @@ function mergeWithDefaults(input) {
|
|
|
164
132
|
: defaults.commandPresets,
|
|
165
133
|
structuredChatPersona: normalizeStructuredChatPersona(input.structuredChatPersona),
|
|
166
134
|
language: typeof input.language === "string" ? input.language.trim() : defaults.language,
|
|
167
|
-
uiPreferences: {
|
|
168
|
-
...defaults.uiPreferences,
|
|
169
|
-
...(input.uiPreferences || {}),
|
|
170
|
-
defaultPanelState: {
|
|
171
|
-
...defaults.uiPreferences?.defaultPanelState,
|
|
172
|
-
...(normalizedPanelState || {}),
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
135
|
};
|
|
176
136
|
}
|
|
177
137
|
export function isExecutionMode(value) {
|
package/dist/resume-policy.d.ts
CHANGED
|
@@ -1,80 +1,3 @@
|
|
|
1
1
|
import { ConversationTurn } from "./types.js";
|
|
2
2
|
export declare function hasRealConversationMessages(messages: ConversationTurn[] | undefined): boolean;
|
|
3
|
-
export declare function hasRuntimeConversationSignal(messages: ConversationTurn[] | undefined): boolean;
|
|
4
|
-
export declare function hasStoredConversationHistory(messages: ConversationTurn[] | undefined): boolean;
|
|
5
|
-
export declare function shouldBindClaudeSessionId(record: {
|
|
6
|
-
messages: ConversationTurn[] | undefined;
|
|
7
|
-
}): boolean;
|
|
8
|
-
export declare function shouldAllowResume(record: {
|
|
9
|
-
claudeSessionId: string | null | undefined;
|
|
10
|
-
messages: ConversationTurn[] | undefined;
|
|
11
|
-
}): boolean;
|
|
12
|
-
export declare function shouldBackfillFromStoredHistory(record: {
|
|
13
|
-
messages: ConversationTurn[] | undefined;
|
|
14
|
-
}): boolean;
|
|
15
|
-
export declare function shouldDisplayResumeAction(messages: ConversationTurn[] | undefined): boolean;
|
|
16
|
-
export declare function shouldAutoResumeMessages(messages: ConversationTurn[] | undefined): boolean;
|
|
17
|
-
export declare function shouldBackfillMessages(messages: ConversationTurn[] | undefined): boolean;
|
|
18
|
-
export declare function shouldPromoteProjectSessionId(record: {
|
|
19
|
-
messages: ConversationTurn[] | undefined;
|
|
20
|
-
}): boolean;
|
|
21
|
-
export declare function shouldPromoteStoredSessionId(record: {
|
|
22
|
-
messages: ConversationTurn[] | undefined;
|
|
23
|
-
}): boolean;
|
|
24
|
-
export declare function shouldPromoteUiSessionId(messages: ConversationTurn[] | undefined): boolean;
|
|
25
|
-
export declare function shouldPromoteResumeSessionId(messages: ConversationTurn[] | undefined): boolean;
|
|
26
|
-
export declare function hasBindableConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
27
|
-
export declare function hasBackfillableConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
28
|
-
export declare function hasUiConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
29
|
-
export declare function hasResumeConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
30
|
-
export declare function isRuntimeConversationReady(messages: ConversationTurn[] | undefined): boolean;
|
|
31
|
-
export declare function isStoredConversationReady(messages: ConversationTurn[] | undefined): boolean;
|
|
32
|
-
export declare function isResumeConversationReady(messages: ConversationTurn[] | undefined): boolean;
|
|
33
|
-
export declare function shouldBindFromRuntimeMessages(record: {
|
|
34
|
-
messages: ConversationTurn[] | undefined;
|
|
35
|
-
}): boolean;
|
|
36
|
-
export declare function shouldAllowUiResume(messages: ConversationTurn[] | undefined): boolean;
|
|
37
|
-
export declare function shouldPromoteResumeAction(record: {
|
|
38
|
-
claudeSessionId: string | null | undefined;
|
|
39
|
-
messages: ConversationTurn[] | undefined;
|
|
40
|
-
}): boolean;
|
|
41
|
-
export declare function shouldBackfillClaudeSessionIdFromDisk(record: {
|
|
42
|
-
messages: ConversationTurn[] | undefined;
|
|
43
|
-
}): boolean;
|
|
44
|
-
export declare function shouldUseProjectCandidate(record: {
|
|
45
|
-
messages: ConversationTurn[] | undefined;
|
|
46
|
-
}): boolean;
|
|
47
|
-
export declare function shouldResumeProjectCandidate(record: {
|
|
48
|
-
claudeSessionId: string | null | undefined;
|
|
49
|
-
messages: ConversationTurn[] | undefined;
|
|
50
|
-
}): boolean;
|
|
51
|
-
export declare function shouldBackfillProjectCandidate(record: {
|
|
52
|
-
messages: ConversationTurn[] | undefined;
|
|
53
|
-
}): boolean;
|
|
54
|
-
export declare function hasMinimumRuntimeConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
55
|
-
export declare function hasMinimumStoredConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
56
|
-
export declare function hasMinimumResumeConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
57
|
-
export declare function hasMinimumBackfillConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
58
|
-
export declare function hasProjectConversationSignal(messages: ConversationTurn[] | undefined): boolean;
|
|
59
|
-
export declare function hasStoredProjectConversationSignal(messages: ConversationTurn[] | undefined): boolean;
|
|
60
|
-
export declare function hasUiProjectConversationSignal(messages: ConversationTurn[] | undefined): boolean;
|
|
61
|
-
export declare function hasResumeProjectConversationSignal(messages: ConversationTurn[] | undefined): boolean;
|
|
62
|
-
export declare function canBindFromProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
63
|
-
export declare function canBackfillFromProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
64
|
-
export declare function canShowUiProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
65
|
-
export declare function canResumeProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
66
|
-
export declare function shouldUseRuntimeProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
67
|
-
export declare function shouldUseStoredProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
68
|
-
export declare function shouldUseUiProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
69
|
-
export declare function shouldUseResumeProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
70
|
-
export declare function hasProjectConversationForBinding(messages: ConversationTurn[] | undefined): boolean;
|
|
71
|
-
export declare function hasProjectConversationForBackfill(messages: ConversationTurn[] | undefined): boolean;
|
|
72
|
-
export declare function hasProjectConversationForUi(messages: ConversationTurn[] | undefined): boolean;
|
|
73
|
-
export declare function hasProjectConversationForResume(messages: ConversationTurn[] | undefined): boolean;
|
|
74
|
-
export declare function isBindableProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
75
|
-
export declare function isBackfillableProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
76
|
-
export declare function isUiProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
77
|
-
export declare function isResumeProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
78
|
-
export declare function hasLiveProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
79
|
-
export declare function hasStoredProjectConversation(messages: ConversationTurn[] | undefined): boolean;
|
|
80
3
|
export declare function getResumeCommandSessionId(command: string): string | null;
|
package/dist/resume-policy.js
CHANGED
|
@@ -10,168 +10,6 @@ export function hasRealConversationMessages(messages) {
|
|
|
10
10
|
&& turn.content.some((block) => block.type === "text" && block.text.trim().length > 0));
|
|
11
11
|
return hasUser && hasAssistant;
|
|
12
12
|
}
|
|
13
|
-
export function hasRuntimeConversationSignal(messages) {
|
|
14
|
-
if (!messages || messages.length === 0) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
const hasUser = messages.some((turn) => turn.role === "user"
|
|
18
|
-
&& turn.content.some((block) => block.type === "text" && block.text.trim().length > 0));
|
|
19
|
-
const hasAssistant = messages.some((turn) => turn.role === "assistant");
|
|
20
|
-
return hasUser && hasAssistant;
|
|
21
|
-
}
|
|
22
|
-
export function hasStoredConversationHistory(messages) {
|
|
23
|
-
return hasRealConversationMessages(messages);
|
|
24
|
-
}
|
|
25
|
-
export function shouldBindClaudeSessionId(record) {
|
|
26
|
-
return hasRuntimeConversationSignal(record.messages);
|
|
27
|
-
}
|
|
28
|
-
export function shouldAllowResume(record) {
|
|
29
|
-
return Boolean(record.claudeSessionId) && hasStoredConversationHistory(record.messages);
|
|
30
|
-
}
|
|
31
|
-
export function shouldBackfillFromStoredHistory(record) {
|
|
32
|
-
return hasStoredConversationHistory(record.messages);
|
|
33
|
-
}
|
|
34
|
-
export function shouldDisplayResumeAction(messages) {
|
|
35
|
-
return hasStoredConversationHistory(messages);
|
|
36
|
-
}
|
|
37
|
-
export function shouldAutoResumeMessages(messages) {
|
|
38
|
-
return hasStoredConversationHistory(messages);
|
|
39
|
-
}
|
|
40
|
-
export function shouldBackfillMessages(messages) {
|
|
41
|
-
return hasStoredConversationHistory(messages);
|
|
42
|
-
}
|
|
43
|
-
export function shouldPromoteProjectSessionId(record) {
|
|
44
|
-
return shouldBindClaudeSessionId(record);
|
|
45
|
-
}
|
|
46
|
-
export function shouldPromoteStoredSessionId(record) {
|
|
47
|
-
return shouldBackfillMessages(record.messages);
|
|
48
|
-
}
|
|
49
|
-
export function shouldPromoteUiSessionId(messages) {
|
|
50
|
-
return shouldDisplayResumeAction(messages);
|
|
51
|
-
}
|
|
52
|
-
export function shouldPromoteResumeSessionId(messages) {
|
|
53
|
-
return shouldAutoResumeMessages(messages);
|
|
54
|
-
}
|
|
55
|
-
export function hasBindableConversation(messages) {
|
|
56
|
-
return shouldBindFromRuntimeMessages({ messages: messages ?? [] });
|
|
57
|
-
}
|
|
58
|
-
export function hasBackfillableConversation(messages) {
|
|
59
|
-
return shouldBackfillMessages(messages);
|
|
60
|
-
}
|
|
61
|
-
export function hasUiConversation(messages) {
|
|
62
|
-
return shouldPromoteUiSessionId(messages);
|
|
63
|
-
}
|
|
64
|
-
export function hasResumeConversation(messages) {
|
|
65
|
-
return shouldPromoteResumeSessionId(messages);
|
|
66
|
-
}
|
|
67
|
-
export function isRuntimeConversationReady(messages) {
|
|
68
|
-
return hasBindableConversation(messages);
|
|
69
|
-
}
|
|
70
|
-
export function isStoredConversationReady(messages) {
|
|
71
|
-
return hasBackfillableConversation(messages);
|
|
72
|
-
}
|
|
73
|
-
export function isResumeConversationReady(messages) {
|
|
74
|
-
return hasResumeConversation(messages);
|
|
75
|
-
}
|
|
76
|
-
export function shouldBindFromRuntimeMessages(record) {
|
|
77
|
-
return isRuntimeConversationReady(record.messages);
|
|
78
|
-
}
|
|
79
|
-
export function shouldAllowUiResume(messages) {
|
|
80
|
-
return hasUiConversation(messages);
|
|
81
|
-
}
|
|
82
|
-
export function shouldPromoteResumeAction(record) {
|
|
83
|
-
return shouldAllowResume(record);
|
|
84
|
-
}
|
|
85
|
-
export function shouldBackfillClaudeSessionIdFromDisk(record) {
|
|
86
|
-
return isStoredConversationReady(record.messages);
|
|
87
|
-
}
|
|
88
|
-
export function shouldUseProjectCandidate(record) {
|
|
89
|
-
return shouldBindFromRuntimeMessages(record);
|
|
90
|
-
}
|
|
91
|
-
export function shouldResumeProjectCandidate(record) {
|
|
92
|
-
return shouldPromoteResumeAction(record);
|
|
93
|
-
}
|
|
94
|
-
export function shouldBackfillProjectCandidate(record) {
|
|
95
|
-
return shouldBackfillClaudeSessionIdFromDisk(record);
|
|
96
|
-
}
|
|
97
|
-
export function hasMinimumRuntimeConversation(messages) {
|
|
98
|
-
return shouldBindFromRuntimeMessages({ messages: messages ?? [] });
|
|
99
|
-
}
|
|
100
|
-
export function hasMinimumStoredConversation(messages) {
|
|
101
|
-
return shouldAllowUiResume(messages);
|
|
102
|
-
}
|
|
103
|
-
export function hasMinimumResumeConversation(messages) {
|
|
104
|
-
return isResumeConversationReady(messages);
|
|
105
|
-
}
|
|
106
|
-
export function hasMinimumBackfillConversation(messages) {
|
|
107
|
-
return isStoredConversationReady(messages);
|
|
108
|
-
}
|
|
109
|
-
export function hasProjectConversationSignal(messages) {
|
|
110
|
-
return hasMinimumRuntimeConversation(messages);
|
|
111
|
-
}
|
|
112
|
-
export function hasStoredProjectConversationSignal(messages) {
|
|
113
|
-
return hasMinimumBackfillConversation(messages);
|
|
114
|
-
}
|
|
115
|
-
export function hasUiProjectConversationSignal(messages) {
|
|
116
|
-
return hasMinimumStoredConversation(messages);
|
|
117
|
-
}
|
|
118
|
-
export function hasResumeProjectConversationSignal(messages) {
|
|
119
|
-
return hasMinimumResumeConversation(messages);
|
|
120
|
-
}
|
|
121
|
-
export function canBindFromProjectConversation(messages) {
|
|
122
|
-
return hasProjectConversationSignal(messages);
|
|
123
|
-
}
|
|
124
|
-
export function canBackfillFromProjectConversation(messages) {
|
|
125
|
-
return hasStoredProjectConversationSignal(messages);
|
|
126
|
-
}
|
|
127
|
-
export function canShowUiProjectConversation(messages) {
|
|
128
|
-
return hasUiProjectConversationSignal(messages);
|
|
129
|
-
}
|
|
130
|
-
export function canResumeProjectConversation(messages) {
|
|
131
|
-
return hasResumeProjectConversationSignal(messages);
|
|
132
|
-
}
|
|
133
|
-
export function shouldUseRuntimeProjectConversation(messages) {
|
|
134
|
-
return canBindFromProjectConversation(messages);
|
|
135
|
-
}
|
|
136
|
-
export function shouldUseStoredProjectConversation(messages) {
|
|
137
|
-
return canBackfillFromProjectConversation(messages);
|
|
138
|
-
}
|
|
139
|
-
export function shouldUseUiProjectConversation(messages) {
|
|
140
|
-
return canShowUiProjectConversation(messages);
|
|
141
|
-
}
|
|
142
|
-
export function shouldUseResumeProjectConversation(messages) {
|
|
143
|
-
return canResumeProjectConversation(messages);
|
|
144
|
-
}
|
|
145
|
-
export function hasProjectConversationForBinding(messages) {
|
|
146
|
-
return shouldUseRuntimeProjectConversation(messages);
|
|
147
|
-
}
|
|
148
|
-
export function hasProjectConversationForBackfill(messages) {
|
|
149
|
-
return shouldUseStoredProjectConversation(messages);
|
|
150
|
-
}
|
|
151
|
-
export function hasProjectConversationForUi(messages) {
|
|
152
|
-
return shouldUseUiProjectConversation(messages);
|
|
153
|
-
}
|
|
154
|
-
export function hasProjectConversationForResume(messages) {
|
|
155
|
-
return shouldUseResumeProjectConversation(messages);
|
|
156
|
-
}
|
|
157
|
-
export function isBindableProjectConversation(messages) {
|
|
158
|
-
return hasProjectConversationForBinding(messages);
|
|
159
|
-
}
|
|
160
|
-
export function isBackfillableProjectConversation(messages) {
|
|
161
|
-
return hasProjectConversationForBackfill(messages);
|
|
162
|
-
}
|
|
163
|
-
export function isUiProjectConversation(messages) {
|
|
164
|
-
return hasProjectConversationForUi(messages);
|
|
165
|
-
}
|
|
166
|
-
export function isResumeProjectConversation(messages) {
|
|
167
|
-
return hasProjectConversationForResume(messages);
|
|
168
|
-
}
|
|
169
|
-
export function hasLiveProjectConversation(messages) {
|
|
170
|
-
return isBindableProjectConversation(messages);
|
|
171
|
-
}
|
|
172
|
-
export function hasStoredProjectConversation(messages) {
|
|
173
|
-
return isBackfillableProjectConversation(messages);
|
|
174
|
-
}
|
|
175
13
|
export function getResumeCommandSessionId(command) {
|
|
176
14
|
const match = RESUME_COMMAND_ID_PATTERN.exec(command);
|
|
177
15
|
return match?.[1] ?? null;
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readdir, readFile, stat } from "node:fs/promises";
|
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createServer as createHttpServer } from "node:http";
|
|
5
5
|
import { createServer as createHttpsServer } from "node:https";
|
|
6
|
-
import { exec } from "node:child_process";
|
|
6
|
+
import { exec, spawn } from "node:child_process";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import process from "node:process";
|
|
@@ -478,6 +478,8 @@ export async function startServer(config, configPath) {
|
|
|
478
478
|
repoUrl: PKG_REPO_URL,
|
|
479
479
|
config: safeConfig,
|
|
480
480
|
hasCert: existsSync(certPaths.keyPath) && existsSync(certPaths.certPath),
|
|
481
|
+
updateAvailable: cachedUpdateInfo?.updateAvailable ?? false,
|
|
482
|
+
latestVersion: cachedUpdateInfo?.latest ?? null,
|
|
481
483
|
});
|
|
482
484
|
});
|
|
483
485
|
app.post("/api/settings/config", async (req, res) => {
|
|
@@ -519,40 +521,6 @@ export async function startServer(config, configPath) {
|
|
|
519
521
|
changed = true;
|
|
520
522
|
}
|
|
521
523
|
}
|
|
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
|
-
}
|
|
556
524
|
if (!changed) {
|
|
557
525
|
res.status(400).json({ error: "没有可更新的配置字段。" });
|
|
558
526
|
return;
|
|
@@ -919,6 +887,30 @@ export async function startServer(config, configPath) {
|
|
|
919
887
|
structuredSessions.setEventEmitter((event) => {
|
|
920
888
|
wsManager.emitEvent(event);
|
|
921
889
|
});
|
|
890
|
+
// ── Restart endpoint (needs server + wss in scope) ──
|
|
891
|
+
app.post("/api/restart", async (_req, res) => {
|
|
892
|
+
res.json({ ok: true, message: "服务正在重启..." });
|
|
893
|
+
wsManager.emitEvent({
|
|
894
|
+
type: "notification",
|
|
895
|
+
sessionId: "__system__",
|
|
896
|
+
data: { kind: "restart" },
|
|
897
|
+
});
|
|
898
|
+
setTimeout(() => {
|
|
899
|
+
// Close all WebSocket connections first
|
|
900
|
+
wss.clients.forEach((client) => client.close());
|
|
901
|
+
server.close(() => {
|
|
902
|
+
spawn(process.execPath, process.argv.slice(1), {
|
|
903
|
+
detached: true,
|
|
904
|
+
stdio: "inherit",
|
|
905
|
+
cwd: process.cwd(),
|
|
906
|
+
env: process.env,
|
|
907
|
+
}).unref();
|
|
908
|
+
process.exit(0);
|
|
909
|
+
});
|
|
910
|
+
// Force exit after 5s if graceful shutdown stalls
|
|
911
|
+
setTimeout(() => process.exit(0), 5000);
|
|
912
|
+
}, 600);
|
|
913
|
+
});
|
|
922
914
|
await new Promise((resolve, reject) => {
|
|
923
915
|
server.listen(config.port, config.host, () => {
|
|
924
916
|
const listenAddr = config.host === "0.0.0.0" ? "0.0.0.0 (所有接口)" : config.host;
|
package/dist/storage.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { DatabaseSync } from "node:sqlite";
|
|
4
|
-
function
|
|
5
|
-
if (!raw)
|
|
4
|
+
function safeJsonParse(raw) {
|
|
5
|
+
if (!raw)
|
|
6
6
|
return undefined;
|
|
7
|
-
}
|
|
8
7
|
try {
|
|
9
8
|
return JSON.parse(raw);
|
|
10
9
|
}
|
|
@@ -13,27 +12,8 @@ function parseStoredMessages(raw) {
|
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
function parseQueuedMessages(raw) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
const parsed = JSON.parse(raw);
|
|
21
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : undefined;
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function parseStructuredState(raw) {
|
|
28
|
-
if (!raw) {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
return JSON.parse(raw);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
15
|
+
const parsed = safeJsonParse(raw);
|
|
16
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : undefined;
|
|
37
17
|
}
|
|
38
18
|
function inferSessionProvider(row) {
|
|
39
19
|
if (row.provider === "claude" || row.provider === "codex") {
|
|
@@ -45,37 +25,14 @@ function inferSessionProvider(row) {
|
|
|
45
25
|
return /^claude\b/.test(row.command.trim()) ? "claude" : undefined;
|
|
46
26
|
}
|
|
47
27
|
function parseWorktreeInfo(raw) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const parsed = JSON.parse(raw);
|
|
53
|
-
if (parsed
|
|
54
|
-
&& typeof parsed === "object"
|
|
55
|
-
&& typeof parsed.branch === "string"
|
|
56
|
-
&& typeof parsed.path === "string") {
|
|
57
|
-
return parsed;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return undefined;
|
|
28
|
+
const parsed = safeJsonParse(raw);
|
|
29
|
+
if (parsed && typeof parsed.branch === "string" && typeof parsed.path === "string") {
|
|
30
|
+
return { branch: parsed.branch, path: parsed.path };
|
|
62
31
|
}
|
|
63
32
|
return undefined;
|
|
64
33
|
}
|
|
65
34
|
function parseWorktreeMergeInfo(raw) {
|
|
66
|
-
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const parsed = JSON.parse(raw);
|
|
71
|
-
if (parsed && typeof parsed === "object") {
|
|
72
|
-
return parsed;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
return undefined;
|
|
35
|
+
return safeJsonParse(raw);
|
|
79
36
|
}
|
|
80
37
|
function serializeWorktreeMergeInfo(info) {
|
|
81
38
|
return info ? JSON.stringify(info) : null;
|
|
@@ -89,15 +46,6 @@ function normalizeWorktreeMergeStatus(raw) {
|
|
|
89
46
|
}
|
|
90
47
|
return undefined;
|
|
91
48
|
}
|
|
92
|
-
function getWorktreeMergeStatusValue(snapshot) {
|
|
93
|
-
return snapshot.worktreeMergeStatus ?? null;
|
|
94
|
-
}
|
|
95
|
-
function getWorktreeMergeInfoValue(snapshot) {
|
|
96
|
-
return serializeWorktreeMergeInfo(snapshot.worktreeMergeInfo);
|
|
97
|
-
}
|
|
98
|
-
function getWorktreeInfoValue(snapshot) {
|
|
99
|
-
return serializeWorktreeInfo(snapshot.worktree);
|
|
100
|
-
}
|
|
101
49
|
function mapWorktreeMergeFields(row) {
|
|
102
50
|
return {
|
|
103
51
|
worktreeMergeStatus: normalizeWorktreeMergeStatus(row.worktree_merge_status),
|
|
@@ -171,9 +119,9 @@ function sessionPersistValues(snapshot) {
|
|
|
171
119
|
snapshot.resumedToSessionId ?? null,
|
|
172
120
|
snapshot.autoRecovered ? 1 : 0,
|
|
173
121
|
snapshot.worktreeEnabled ? 1 : 0,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
122
|
+
serializeWorktreeInfo(snapshot.worktree),
|
|
123
|
+
snapshot.worktreeMergeStatus ?? null,
|
|
124
|
+
serializeWorktreeMergeInfo(snapshot.worktreeMergeInfo),
|
|
177
125
|
];
|
|
178
126
|
}
|
|
179
127
|
function sessionMetadataValues(snapshot) {
|
|
@@ -197,9 +145,9 @@ function sessionMetadataValues(snapshot) {
|
|
|
197
145
|
snapshot.resumedToSessionId ?? null,
|
|
198
146
|
snapshot.autoRecovered ? 1 : 0,
|
|
199
147
|
snapshot.worktreeEnabled ? 1 : 0,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
148
|
+
serializeWorktreeInfo(snapshot.worktree),
|
|
149
|
+
snapshot.worktreeMergeStatus ?? null,
|
|
150
|
+
serializeWorktreeMergeInfo(snapshot.worktreeMergeInfo),
|
|
203
151
|
snapshot.id,
|
|
204
152
|
];
|
|
205
153
|
}
|
|
@@ -221,9 +169,9 @@ function mapSessionCore(row) {
|
|
|
221
169
|
archived: Boolean(row.archived),
|
|
222
170
|
archivedAt: row.archived_at,
|
|
223
171
|
claudeSessionId: row.claude_session_id,
|
|
224
|
-
messages:
|
|
172
|
+
messages: safeJsonParse(row.messages),
|
|
225
173
|
queuedMessages: parseQueuedMessages(row.queued_messages),
|
|
226
|
-
structuredState:
|
|
174
|
+
structuredState: safeJsonParse(row.structured_state),
|
|
227
175
|
resumedFromSessionId: row.resumed_from_session_id ?? undefined,
|
|
228
176
|
resumedToSessionId: row.resumed_to_session_id ?? undefined,
|
|
229
177
|
autoRecovered: Boolean(row.auto_recovered),
|
|
@@ -430,52 +378,30 @@ export class WandStorage {
|
|
|
430
378
|
this.db.prepare("DELETE FROM command_sessions WHERE id = ?").run(id);
|
|
431
379
|
}
|
|
432
380
|
}
|
|
381
|
+
const SCHEMA_MIGRATIONS = [
|
|
382
|
+
["archived", "ALTER TABLE command_sessions ADD COLUMN archived INTEGER NOT NULL DEFAULT 0"],
|
|
383
|
+
["archived_at", "ALTER TABLE command_sessions ADD COLUMN archived_at TEXT"],
|
|
384
|
+
["claude_session_id", "ALTER TABLE command_sessions ADD COLUMN claude_session_id TEXT"],
|
|
385
|
+
["provider", "ALTER TABLE command_sessions ADD COLUMN provider TEXT"],
|
|
386
|
+
["session_kind", "ALTER TABLE command_sessions ADD COLUMN session_kind TEXT NOT NULL DEFAULT 'pty'"],
|
|
387
|
+
["runner", "ALTER TABLE command_sessions ADD COLUMN runner TEXT"],
|
|
388
|
+
["messages", "ALTER TABLE command_sessions ADD COLUMN messages TEXT"],
|
|
389
|
+
["queued_messages", "ALTER TABLE command_sessions ADD COLUMN queued_messages TEXT"],
|
|
390
|
+
["structured_state", "ALTER TABLE command_sessions ADD COLUMN structured_state TEXT"],
|
|
391
|
+
["resumed_from_session_id", "ALTER TABLE command_sessions ADD COLUMN resumed_from_session_id TEXT"],
|
|
392
|
+
["resumed_to_session_id", "ALTER TABLE command_sessions ADD COLUMN resumed_to_session_id TEXT"],
|
|
393
|
+
["auto_recovered", "ALTER TABLE command_sessions ADD COLUMN auto_recovered INTEGER NOT NULL DEFAULT 0"],
|
|
394
|
+
["worktree_enabled", "ALTER TABLE command_sessions ADD COLUMN worktree_enabled INTEGER NOT NULL DEFAULT 0"],
|
|
395
|
+
["worktree_info", "ALTER TABLE command_sessions ADD COLUMN worktree_info TEXT"],
|
|
396
|
+
["worktree_merge_status", "ALTER TABLE command_sessions ADD COLUMN worktree_merge_status TEXT"],
|
|
397
|
+
["worktree_merge_info", "ALTER TABLE command_sessions ADD COLUMN worktree_merge_info TEXT"],
|
|
398
|
+
];
|
|
433
399
|
function ensureCommandSessionSchema(db) {
|
|
434
400
|
const columns = db.prepare("PRAGMA table_info(command_sessions)").all();
|
|
435
401
|
const names = new Set(columns.map((column) => column.name));
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN archived_at TEXT");
|
|
441
|
-
}
|
|
442
|
-
if (!names.has("claude_session_id")) {
|
|
443
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN claude_session_id TEXT");
|
|
444
|
-
}
|
|
445
|
-
if (!names.has("provider")) {
|
|
446
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN provider TEXT");
|
|
447
|
-
}
|
|
448
|
-
if (!names.has("session_kind")) {
|
|
449
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN session_kind TEXT NOT NULL DEFAULT 'pty'");
|
|
450
|
-
}
|
|
451
|
-
if (!names.has("runner")) {
|
|
452
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN runner TEXT");
|
|
453
|
-
}
|
|
454
|
-
if (!names.has("messages")) {
|
|
455
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN messages TEXT");
|
|
456
|
-
}
|
|
457
|
-
if (!names.has("queued_messages")) {
|
|
458
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN queued_messages TEXT");
|
|
459
|
-
}
|
|
460
|
-
if (!names.has("structured_state")) {
|
|
461
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN structured_state TEXT");
|
|
462
|
-
}
|
|
463
|
-
if (!names.has("resumed_from_session_id")) {
|
|
464
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN resumed_from_session_id TEXT");
|
|
465
|
-
}
|
|
466
|
-
if (!names.has("resumed_to_session_id")) {
|
|
467
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN resumed_to_session_id TEXT");
|
|
468
|
-
}
|
|
469
|
-
if (!names.has("worktree_enabled")) {
|
|
470
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_enabled INTEGER NOT NULL DEFAULT 0");
|
|
471
|
-
}
|
|
472
|
-
if (!names.has("worktree_info")) {
|
|
473
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_info TEXT");
|
|
474
|
-
}
|
|
475
|
-
if (!names.has("worktree_merge_status")) {
|
|
476
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_merge_status TEXT");
|
|
477
|
-
}
|
|
478
|
-
if (!names.has("worktree_merge_info")) {
|
|
479
|
-
db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_merge_info TEXT");
|
|
402
|
+
for (const [column, sql] of SCHEMA_MIGRATIONS) {
|
|
403
|
+
if (!names.has(column)) {
|
|
404
|
+
db.exec(sql);
|
|
405
|
+
}
|
|
480
406
|
}
|
|
481
407
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -47,21 +47,6 @@ export interface StructuredChatPersonaConfig {
|
|
|
47
47
|
user?: StructuredChatPersonaRoleConfig;
|
|
48
48
|
assistant?: StructuredChatPersonaRoleConfig;
|
|
49
49
|
}
|
|
50
|
-
export interface DefaultPanelStateConfig {
|
|
51
|
-
sessionsDrawerOpen?: boolean;
|
|
52
|
-
filePanelOpen?: boolean;
|
|
53
|
-
shortcutsExpanded?: boolean;
|
|
54
|
-
claudeHistoryExpanded?: boolean;
|
|
55
|
-
chatMessageExpanded?: boolean;
|
|
56
|
-
structuredThinkingExpanded?: boolean;
|
|
57
|
-
structuredToolGroupExpanded?: boolean;
|
|
58
|
-
structuredInlineToolExpanded?: boolean;
|
|
59
|
-
structuredTerminalExpanded?: boolean;
|
|
60
|
-
structuredToolCardExpanded?: boolean;
|
|
61
|
-
}
|
|
62
|
-
export interface UiPreferencesConfig {
|
|
63
|
-
defaultPanelState?: DefaultPanelStateConfig;
|
|
64
|
-
}
|
|
65
50
|
export interface WandConfig {
|
|
66
51
|
host: string;
|
|
67
52
|
port: number;
|
|
@@ -79,8 +64,6 @@ export interface WandConfig {
|
|
|
79
64
|
shortcutLogMaxBytes?: number;
|
|
80
65
|
/** Preferred response language for Claude (e.g. "中文", "English"). Empty string means no override. */
|
|
81
66
|
language?: string;
|
|
82
|
-
/** UI defaults used for initializing panel open/expanded state. */
|
|
83
|
-
uiPreferences?: UiPreferencesConfig;
|
|
84
67
|
}
|
|
85
68
|
interface WorktreeInfo {
|
|
86
69
|
branch: string;
|