@co0ontty/wand 1.9.0 → 1.14.2
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/README.md +48 -12
- package/dist/config.d.ts +2 -1
- package/dist/config.js +51 -40
- package/dist/message-truncator.d.ts +16 -0
- package/dist/message-truncator.js +76 -0
- package/dist/process-manager.d.ts +4 -0
- package/dist/process-manager.js +74 -21
- package/dist/resume-policy.d.ts +0 -77
- package/dist/resume-policy.js +0 -162
- package/dist/server-session-routes.js +29 -1
- package/dist/server.js +302 -45
- package/dist/storage.js +38 -112
- package/dist/structured-session-manager.d.ts +2 -0
- package/dist/structured-session-manager.js +10 -0
- package/dist/types.d.ts +27 -16
- package/dist/web-ui/content/scripts.js +1587 -780
- package/dist/web-ui/content/styles.css +677 -734
- package/dist/web-ui/scripts.js +3 -6
- package/dist/ws-broadcast.d.ts +3 -2
- package/dist/ws-broadcast.js +8 -2
- package/package.json +1 -1
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
|
}
|
|
@@ -16,6 +16,8 @@ export declare class StructuredSessionManager {
|
|
|
16
16
|
constructor(storage: WandStorage, config: WandConfig);
|
|
17
17
|
setEventEmitter(emitEvent: (event: ProcessEvent) => void): void;
|
|
18
18
|
list(): SessionSnapshot[];
|
|
19
|
+
/** Return lightweight snapshots for the session list (no output/messages). */
|
|
20
|
+
listSlim(): SessionSnapshot[];
|
|
19
21
|
get(id: string): SessionSnapshot | null;
|
|
20
22
|
createSession(options: CreateStructuredSessionOptions): SessionSnapshot;
|
|
21
23
|
sendMessage(id: string, input: string): Promise<SessionSnapshot>;
|
|
@@ -80,6 +80,16 @@ export class StructuredSessionManager {
|
|
|
80
80
|
.map(withSummary)
|
|
81
81
|
.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
82
82
|
}
|
|
83
|
+
/** Return lightweight snapshots for the session list (no output/messages). */
|
|
84
|
+
listSlim() {
|
|
85
|
+
return Array.from(this.sessions.values())
|
|
86
|
+
.map((s) => {
|
|
87
|
+
const enriched = withSummary(s);
|
|
88
|
+
const { output: _o, messages: _m, ...slim } = enriched;
|
|
89
|
+
return { ...slim, output: "" };
|
|
90
|
+
})
|
|
91
|
+
.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
92
|
+
}
|
|
83
93
|
get(id) {
|
|
84
94
|
const s = this.sessions.get(id);
|
|
85
95
|
return s ? withSummary(s) : null;
|
package/dist/types.d.ts
CHANGED
|
@@ -47,20 +47,22 @@ export interface StructuredChatPersonaConfig {
|
|
|
47
47
|
user?: StructuredChatPersonaRoleConfig;
|
|
48
48
|
assistant?: StructuredChatPersonaRoleConfig;
|
|
49
49
|
}
|
|
50
|
-
export interface
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
export interface
|
|
63
|
-
|
|
50
|
+
export interface CardExpandDefaults {
|
|
51
|
+
/** Edit/Write/MultiEdit diff cards (default: false) */
|
|
52
|
+
editCards?: boolean;
|
|
53
|
+
/** Read/Glob/Grep/WebFetch/WebSearch inline tools (default: false) */
|
|
54
|
+
inlineTools?: boolean;
|
|
55
|
+
/** Bash terminal output (default: false) */
|
|
56
|
+
terminal?: boolean;
|
|
57
|
+
/** Thinking blocks (default: false) */
|
|
58
|
+
thinking?: boolean;
|
|
59
|
+
/** Tool groups (default: false) */
|
|
60
|
+
toolGroup?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface AndroidApkConfig {
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
apkDir?: string;
|
|
65
|
+
currentApkFile?: string;
|
|
64
66
|
}
|
|
65
67
|
export interface WandConfig {
|
|
66
68
|
host: string;
|
|
@@ -79,8 +81,11 @@ export interface WandConfig {
|
|
|
79
81
|
shortcutLogMaxBytes?: number;
|
|
80
82
|
/** Preferred response language for Claude (e.g. "中文", "English"). Empty string means no override. */
|
|
81
83
|
language?: string;
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
+
/** Per-instance secret for app connection code encryption. Auto-generated on first run. */
|
|
85
|
+
appSecret?: string;
|
|
86
|
+
android?: AndroidApkConfig;
|
|
87
|
+
/** Default expand/collapse state for card types in structured chat view */
|
|
88
|
+
cardDefaults?: CardExpandDefaults;
|
|
84
89
|
}
|
|
85
90
|
interface WorktreeInfo {
|
|
86
91
|
branch: string;
|
|
@@ -184,6 +189,10 @@ export interface ToolResultBlock {
|
|
|
184
189
|
[key: string]: unknown;
|
|
185
190
|
}>;
|
|
186
191
|
is_error?: boolean;
|
|
192
|
+
/** When true, content has been truncated for transport. Client should fetch full content via API. */
|
|
193
|
+
_truncated?: boolean;
|
|
194
|
+
/** Original content size in bytes, provided when truncated. */
|
|
195
|
+
_originalSize?: number;
|
|
187
196
|
}
|
|
188
197
|
export type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock;
|
|
189
198
|
export interface ConversationTurn {
|
|
@@ -264,6 +273,8 @@ export interface SessionSnapshot {
|
|
|
264
273
|
};
|
|
265
274
|
/** 会话摘要:从首条用户消息或当前任务提取 */
|
|
266
275
|
summary?: string;
|
|
276
|
+
/** 当前正在执行的任务标题(用于会话列表展示) */
|
|
277
|
+
currentTaskTitle?: string;
|
|
267
278
|
}
|
|
268
279
|
export type SessionLifecycleState = "initializing" | "running" | "idle" | "thinking" | "waiting-input" | "archived";
|
|
269
280
|
export interface SessionLifecycle {
|