@co0ontty/wand 4.39.0 → 4.40.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/process-manager.d.ts +4 -0
- package/dist/process-manager.js +4 -0
- package/dist/server-file-routes.js +119 -1
- package/dist/server-session-routes.js +2 -0
- package/dist/server-workspace-routes.d.ts +16 -0
- package/dist/server-workspace-routes.js +373 -0
- package/dist/server.js +6 -0
- package/dist/storage.d.ts +41 -1
- package/dist/storage.js +261 -4
- package/dist/structured-session-manager.d.ts +4 -0
- package/dist/structured-session-manager.js +2 -0
- package/dist/types.d.ts +82 -0
- package/dist/web-ui/content/scripts.js +494 -75
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/dist/ws-broadcast.d.ts +1 -0
- package/dist/ws-broadcast.js +43 -27
- package/package.json +1 -1
package/dist/storage.js
CHANGED
|
@@ -255,12 +255,65 @@ function mapWorktreeMergeFields(row) {
|
|
|
255
255
|
}
|
|
256
256
|
function sessionSelectFields() {
|
|
257
257
|
return `id, session_source, automation_id, provider, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, pty_output_seq, archived, archived_at, claude_session_id, messages, queued_messages, queued_message_skills, structured_state
|
|
258
|
-
, resumed_from_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info, title, description, session_options`;
|
|
258
|
+
, resumed_from_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info, title, description, session_options, workspace_id, workspace_task_id`;
|
|
259
|
+
}
|
|
260
|
+
function mapWorkspaceRow(row) {
|
|
261
|
+
return {
|
|
262
|
+
id: row.id,
|
|
263
|
+
name: row.name,
|
|
264
|
+
cwd: row.cwd,
|
|
265
|
+
defaultProvider: (row.default_provider ?? undefined),
|
|
266
|
+
layout: row.layout_json ? safeJsonParse(row.layout_json) ?? null : null,
|
|
267
|
+
createdAt: row.created_at,
|
|
268
|
+
lastOpenedAt: row.last_opened_at,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function mapWorkspaceTaskWorktree(raw) {
|
|
272
|
+
const parsed = safeJsonParse(raw);
|
|
273
|
+
if (!parsed || typeof parsed.path !== "string" || typeof parsed.branch !== "string")
|
|
274
|
+
return null;
|
|
275
|
+
return parsed;
|
|
276
|
+
}
|
|
277
|
+
function firstLayoutTabId(node) {
|
|
278
|
+
if (node.type === "pane")
|
|
279
|
+
return node.tabs[node.active]?.id ?? node.tabs[0]?.id;
|
|
280
|
+
return firstLayoutTabId(node.children[0]) ?? firstLayoutTabId(node.children[1]);
|
|
281
|
+
}
|
|
282
|
+
/** 读取旧版单棵分屏树时就地包成一个工作窗口,避免升级后丢失布局。 */
|
|
283
|
+
function mapWorkspaceTaskLayout(raw) {
|
|
284
|
+
const parsed = safeJsonParse(raw);
|
|
285
|
+
if (!parsed || typeof parsed !== "object")
|
|
286
|
+
return null;
|
|
287
|
+
const record = parsed;
|
|
288
|
+
if (record.type === "windows" && Array.isArray(record.windows)) {
|
|
289
|
+
return parsed;
|
|
290
|
+
}
|
|
291
|
+
if (record.type === "pane" || record.type === "split") {
|
|
292
|
+
const legacy = parsed;
|
|
293
|
+
return {
|
|
294
|
+
type: "windows",
|
|
295
|
+
windows: [{ id: "window-legacy", layout: legacy, activeTabId: firstLayoutTabId(legacy) }],
|
|
296
|
+
activeWindowId: "window-legacy",
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
function mapWorkspaceTaskRow(row) {
|
|
302
|
+
return {
|
|
303
|
+
id: row.id,
|
|
304
|
+
workspaceId: row.workspace_id,
|
|
305
|
+
name: row.name,
|
|
306
|
+
worktree: mapWorkspaceTaskWorktree(row.worktree_json),
|
|
307
|
+
layout: mapWorkspaceTaskLayout(row.layout_json),
|
|
308
|
+
status: (row.status === "done" ? "done" : "active"),
|
|
309
|
+
createdAt: row.created_at,
|
|
310
|
+
lastOpenedAt: row.last_opened_at,
|
|
311
|
+
};
|
|
259
312
|
}
|
|
260
313
|
function sessionPersistFields() {
|
|
261
314
|
return `id, session_source, automation_id, command, cwd, mode, status, exit_code, started_at, ended_at, output, pty_output_seq
|
|
262
315
|
, archived, archived_at, claude_session_id, provider, session_kind, runner, messages, queued_messages, queued_message_skills, structured_state
|
|
263
|
-
, resumed_from_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info, title, description, session_options`;
|
|
316
|
+
, resumed_from_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info, title, description, session_options, workspace_id, workspace_task_id`;
|
|
264
317
|
}
|
|
265
318
|
function sessionPersistAssignments() {
|
|
266
319
|
return `session_source = excluded.session_source,
|
|
@@ -292,7 +345,9 @@ function sessionPersistAssignments() {
|
|
|
292
345
|
worktree_merge_info = excluded.worktree_merge_info,
|
|
293
346
|
title = excluded.title,
|
|
294
347
|
description = excluded.description,
|
|
295
|
-
session_options = excluded.session_options
|
|
348
|
+
session_options = excluded.session_options,
|
|
349
|
+
workspace_id = excluded.workspace_id,
|
|
350
|
+
workspace_task_id = excluded.workspace_task_id`;
|
|
296
351
|
}
|
|
297
352
|
function sessionRuntimeMetadataAssignments() {
|
|
298
353
|
return `session_source = ?, automation_id = ?,
|
|
@@ -337,6 +392,8 @@ function sessionPersistValues(snapshot) {
|
|
|
337
392
|
snapshot.title ?? null,
|
|
338
393
|
snapshot.description ?? null,
|
|
339
394
|
serializeSessionOptions(snapshot),
|
|
395
|
+
snapshot.workspaceId ?? null,
|
|
396
|
+
snapshot.workspaceTaskId ?? null,
|
|
340
397
|
];
|
|
341
398
|
}
|
|
342
399
|
function sessionRuntimeMetadataValues(snapshot) {
|
|
@@ -404,6 +461,8 @@ function mapSessionCore(row) {
|
|
|
404
461
|
worktree: parseWorktreeInfo(row.worktree_info) ?? null,
|
|
405
462
|
title: row.title ?? undefined,
|
|
406
463
|
description: row.description ?? undefined,
|
|
464
|
+
workspaceId: row.workspace_id ?? undefined,
|
|
465
|
+
workspaceTaskId: row.workspace_task_id ?? undefined,
|
|
407
466
|
...mapWorktreeMergeFields(row),
|
|
408
467
|
...sessionOptions,
|
|
409
468
|
...(Object.prototype.hasOwnProperty.call(sessionOptions, "pendingEscalation")
|
|
@@ -566,6 +625,34 @@ const INIT_SQL = `
|
|
|
566
625
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_mission_attempts_session ON mission_attempts(session_id) WHERE session_id IS NOT NULL;
|
|
567
626
|
CREATE INDEX IF NOT EXISTS idx_mission_comments_attempt ON mission_review_comments(attempt_id, status);
|
|
568
627
|
CREATE INDEX IF NOT EXISTS idx_agent_activity_state ON agent_activity(state, updated_at);
|
|
628
|
+
|
|
629
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
630
|
+
id TEXT PRIMARY KEY,
|
|
631
|
+
name TEXT NOT NULL,
|
|
632
|
+
cwd TEXT NOT NULL,
|
|
633
|
+
default_provider TEXT,
|
|
634
|
+
layout_json TEXT,
|
|
635
|
+
created_at TEXT NOT NULL,
|
|
636
|
+
last_opened_at TEXT
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
CREATE INDEX IF NOT EXISTS idx_workspaces_cwd ON workspaces(cwd);
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_workspaces_last_opened ON workspaces(last_opened_at);
|
|
641
|
+
|
|
642
|
+
CREATE TABLE IF NOT EXISTS workspace_tasks (
|
|
643
|
+
id TEXT PRIMARY KEY,
|
|
644
|
+
workspace_id TEXT NOT NULL,
|
|
645
|
+
name TEXT NOT NULL,
|
|
646
|
+
worktree_json TEXT,
|
|
647
|
+
layout_json TEXT,
|
|
648
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
649
|
+
created_at TEXT NOT NULL,
|
|
650
|
+
last_opened_at TEXT,
|
|
651
|
+
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
CREATE INDEX IF NOT EXISTS idx_workspace_tasks_workspace ON workspace_tasks(workspace_id);
|
|
655
|
+
CREATE INDEX IF NOT EXISTS idx_workspace_tasks_last_opened ON workspace_tasks(last_opened_at);
|
|
569
656
|
`;
|
|
570
657
|
export function ensureDatabaseFile(dbPath) {
|
|
571
658
|
const dir = path.dirname(dbPath);
|
|
@@ -689,6 +776,174 @@ export class WandStorage {
|
|
|
689
776
|
updated_at = excluded.updated_at`)
|
|
690
777
|
.run(normalizedPath, normalizedName, nowIso());
|
|
691
778
|
}
|
|
779
|
+
// ============ Workspaces(多标签 / 分屏项目)============
|
|
780
|
+
listWorkspaces() {
|
|
781
|
+
const rows = this.db
|
|
782
|
+
.prepare("SELECT id, name, cwd, default_provider, layout_json, created_at, last_opened_at FROM workspaces ORDER BY COALESCE(last_opened_at, created_at) DESC")
|
|
783
|
+
.all();
|
|
784
|
+
return rows.map(mapWorkspaceRow);
|
|
785
|
+
}
|
|
786
|
+
getWorkspace(id) {
|
|
787
|
+
const row = this.db
|
|
788
|
+
.prepare("SELECT id, name, cwd, default_provider, layout_json, created_at, last_opened_at FROM workspaces WHERE id = ?")
|
|
789
|
+
.get(id);
|
|
790
|
+
return row ? mapWorkspaceRow(row) : null;
|
|
791
|
+
}
|
|
792
|
+
createWorkspace(input) {
|
|
793
|
+
const id = crypto.randomUUID();
|
|
794
|
+
const createdAt = nowIso();
|
|
795
|
+
this.db
|
|
796
|
+
.prepare(`INSERT INTO workspaces (id, name, cwd, default_provider, layout_json, created_at, last_opened_at)
|
|
797
|
+
VALUES (?, ?, ?, ?, NULL, ?, NULL)`)
|
|
798
|
+
.run(id, input.name, input.cwd, input.defaultProvider ?? null, createdAt);
|
|
799
|
+
return {
|
|
800
|
+
id,
|
|
801
|
+
name: input.name,
|
|
802
|
+
cwd: input.cwd,
|
|
803
|
+
defaultProvider: input.defaultProvider,
|
|
804
|
+
layout: null,
|
|
805
|
+
createdAt,
|
|
806
|
+
lastOpenedAt: null,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
updateWorkspace(id, patch) {
|
|
810
|
+
const assignments = [];
|
|
811
|
+
const values = [];
|
|
812
|
+
if (patch.name !== undefined) {
|
|
813
|
+
assignments.push("name = ?");
|
|
814
|
+
values.push(patch.name);
|
|
815
|
+
}
|
|
816
|
+
if (patch.cwd !== undefined) {
|
|
817
|
+
assignments.push("cwd = ?");
|
|
818
|
+
values.push(patch.cwd);
|
|
819
|
+
}
|
|
820
|
+
if (patch.defaultProvider !== undefined) {
|
|
821
|
+
assignments.push("default_provider = ?");
|
|
822
|
+
values.push(patch.defaultProvider ?? null);
|
|
823
|
+
}
|
|
824
|
+
if (assignments.length === 0)
|
|
825
|
+
return;
|
|
826
|
+
this.db.prepare(`UPDATE workspaces SET ${assignments.join(", ")} WHERE id = ?`).run(...values, id);
|
|
827
|
+
}
|
|
828
|
+
saveWorkspaceLayout(id, layout) {
|
|
829
|
+
this.db
|
|
830
|
+
.prepare("UPDATE workspaces SET layout_json = ? WHERE id = ?")
|
|
831
|
+
.run(layout ? JSON.stringify(layout) : null, id);
|
|
832
|
+
}
|
|
833
|
+
touchWorkspace(id) {
|
|
834
|
+
this.db.prepare("UPDATE workspaces SET last_opened_at = ? WHERE id = ?").run(nowIso(), id);
|
|
835
|
+
}
|
|
836
|
+
deleteWorkspace(id, options = {}) {
|
|
837
|
+
if (options.cascade) {
|
|
838
|
+
this.db.prepare(`DELETE FROM command_sessions
|
|
839
|
+
WHERE workspace_id = ?
|
|
840
|
+
OR workspace_task_id IN (SELECT id FROM workspace_tasks WHERE workspace_id = ?)`).run(id, id);
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
// 解绑:保留会话,同时清空 workspace 与即将级联删除的 task 归属。
|
|
844
|
+
this.db.prepare(`UPDATE command_sessions
|
|
845
|
+
SET workspace_id = NULL, workspace_task_id = NULL
|
|
846
|
+
WHERE workspace_id = ?
|
|
847
|
+
OR workspace_task_id IN (SELECT id FROM workspace_tasks WHERE workspace_id = ?)`).run(id, id);
|
|
848
|
+
}
|
|
849
|
+
this.db.prepare("DELETE FROM workspaces WHERE id = ?").run(id);
|
|
850
|
+
}
|
|
851
|
+
listSessionsByWorkspace(workspaceId) {
|
|
852
|
+
const rows = this.db
|
|
853
|
+
.prepare(`${sessionRowQuery("SELECT")}
|
|
854
|
+
FROM command_sessions
|
|
855
|
+
WHERE workspace_id = ?
|
|
856
|
+
ORDER BY started_at DESC`)
|
|
857
|
+
.all(workspaceId);
|
|
858
|
+
return rows.map((row) => this.mapSessionRow(row));
|
|
859
|
+
}
|
|
860
|
+
/** 显式更新某会话的工作空间归属(用于创建时绑定)。 */
|
|
861
|
+
setSessionWorkspaceId(sessionId, workspaceId) {
|
|
862
|
+
this.db.prepare("UPDATE command_sessions SET workspace_id = ? WHERE id = ?").run(workspaceId, sessionId);
|
|
863
|
+
}
|
|
864
|
+
// ── Workspace tasks(任务 = 命名 + 独立 worktree + 一组标签)──
|
|
865
|
+
listWorkspaceTasks(workspaceId) {
|
|
866
|
+
const rows = this.db
|
|
867
|
+
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, created_at, last_opened_at
|
|
868
|
+
FROM workspace_tasks WHERE workspace_id = ?
|
|
869
|
+
ORDER BY COALESCE(last_opened_at, created_at) DESC`)
|
|
870
|
+
.all(workspaceId);
|
|
871
|
+
return rows.map(mapWorkspaceTaskRow);
|
|
872
|
+
}
|
|
873
|
+
getWorkspaceTask(id) {
|
|
874
|
+
const row = this.db
|
|
875
|
+
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, created_at, last_opened_at
|
|
876
|
+
FROM workspace_tasks WHERE id = ?`)
|
|
877
|
+
.get(id);
|
|
878
|
+
return row ? mapWorkspaceTaskRow(row) : null;
|
|
879
|
+
}
|
|
880
|
+
createWorkspaceTask(input) {
|
|
881
|
+
const id = crypto.randomUUID();
|
|
882
|
+
const createdAt = nowIso();
|
|
883
|
+
const status = input.status ?? "active";
|
|
884
|
+
this.db
|
|
885
|
+
.prepare(`INSERT INTO workspace_tasks (id, workspace_id, name, worktree_json, layout_json, status, created_at, last_opened_at)
|
|
886
|
+
VALUES (?, ?, ?, ?, NULL, ?, ?, NULL)`)
|
|
887
|
+
.run(id, input.workspaceId, input.name, input.worktree ? JSON.stringify(input.worktree) : null, status, createdAt);
|
|
888
|
+
return {
|
|
889
|
+
id,
|
|
890
|
+
workspaceId: input.workspaceId,
|
|
891
|
+
name: input.name,
|
|
892
|
+
worktree: input.worktree ?? null,
|
|
893
|
+
layout: null,
|
|
894
|
+
status,
|
|
895
|
+
createdAt,
|
|
896
|
+
lastOpenedAt: null,
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
updateWorkspaceTask(id, patch) {
|
|
900
|
+
const assignments = [];
|
|
901
|
+
const values = [];
|
|
902
|
+
if (patch.name !== undefined) {
|
|
903
|
+
assignments.push("name = ?");
|
|
904
|
+
values.push(patch.name);
|
|
905
|
+
}
|
|
906
|
+
if (patch.status !== undefined) {
|
|
907
|
+
assignments.push("status = ?");
|
|
908
|
+
values.push(patch.status);
|
|
909
|
+
}
|
|
910
|
+
if (patch.worktree !== undefined) {
|
|
911
|
+
assignments.push("worktree_json = ?");
|
|
912
|
+
values.push(patch.worktree ? JSON.stringify(patch.worktree) : null);
|
|
913
|
+
}
|
|
914
|
+
if (assignments.length === 0)
|
|
915
|
+
return;
|
|
916
|
+
this.db.prepare(`UPDATE workspace_tasks SET ${assignments.join(", ")} WHERE id = ?`).run(...values, id);
|
|
917
|
+
}
|
|
918
|
+
saveWorkspaceTaskLayout(id, layout) {
|
|
919
|
+
this.db
|
|
920
|
+
.prepare("UPDATE workspace_tasks SET layout_json = ? WHERE id = ?")
|
|
921
|
+
.run(layout ? JSON.stringify(layout) : null, id);
|
|
922
|
+
}
|
|
923
|
+
touchWorkspaceTask(id) {
|
|
924
|
+
this.db.prepare("UPDATE workspace_tasks SET last_opened_at = ? WHERE id = ?").run(nowIso(), id);
|
|
925
|
+
}
|
|
926
|
+
deleteWorkspaceTask(id, options = {}) {
|
|
927
|
+
if (options.cascade) {
|
|
928
|
+
this.db.prepare("DELETE FROM command_sessions WHERE workspace_task_id = ?").run(id);
|
|
929
|
+
}
|
|
930
|
+
else {
|
|
931
|
+
this.db.prepare("UPDATE command_sessions SET workspace_task_id = NULL WHERE workspace_task_id = ?").run(id);
|
|
932
|
+
}
|
|
933
|
+
this.db.prepare("DELETE FROM workspace_tasks WHERE id = ?").run(id);
|
|
934
|
+
}
|
|
935
|
+
listSessionsByWorkspaceTask(taskId) {
|
|
936
|
+
const rows = this.db
|
|
937
|
+
.prepare(`${sessionRowQuery("SELECT")}
|
|
938
|
+
FROM command_sessions
|
|
939
|
+
WHERE workspace_task_id = ?
|
|
940
|
+
ORDER BY started_at DESC`)
|
|
941
|
+
.all(taskId);
|
|
942
|
+
return rows.map((row) => this.mapSessionRow(row));
|
|
943
|
+
}
|
|
944
|
+
setSessionWorkspaceTaskId(sessionId, taskId) {
|
|
945
|
+
this.db.prepare("UPDATE command_sessions SET workspace_task_id = ? WHERE id = ?").run(taskId, sessionId);
|
|
946
|
+
}
|
|
692
947
|
/** Get password from database */
|
|
693
948
|
getPassword() {
|
|
694
949
|
return this.getConfigValue("password");
|
|
@@ -954,7 +1209,7 @@ export class WandStorage {
|
|
|
954
1209
|
this.db
|
|
955
1210
|
.prepare(`INSERT INTO command_sessions (
|
|
956
1211
|
${sessionPersistFields()}
|
|
957
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1212
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
958
1213
|
ON CONFLICT(id) DO UPDATE SET
|
|
959
1214
|
${sessionPersistAssignments()}`)
|
|
960
1215
|
.run(...sessionPersistValues(snapshot));
|
|
@@ -1148,6 +1403,8 @@ const SCHEMA_MIGRATIONS = [
|
|
|
1148
1403
|
["description", "ALTER TABLE command_sessions ADD COLUMN description TEXT"],
|
|
1149
1404
|
["pty_output_seq", "ALTER TABLE command_sessions ADD COLUMN pty_output_seq INTEGER NOT NULL DEFAULT 0"],
|
|
1150
1405
|
["session_options", `ALTER TABLE command_sessions ADD COLUMN session_options TEXT NOT NULL DEFAULT '{"schemaVersion":1}'`],
|
|
1406
|
+
["workspace_id", "ALTER TABLE command_sessions ADD COLUMN workspace_id TEXT"],
|
|
1407
|
+
["workspace_task_id", "ALTER TABLE command_sessions ADD COLUMN workspace_task_id TEXT"],
|
|
1151
1408
|
];
|
|
1152
1409
|
const AUTH_SESSION_MIGRATIONS = [
|
|
1153
1410
|
["kind", "ALTER TABLE auth_sessions ADD COLUMN kind TEXT NOT NULL DEFAULT 'browser-admin'"],
|
|
@@ -26,6 +26,10 @@ interface CreateStructuredSessionOptions {
|
|
|
26
26
|
thinkingEffort?: SessionSnapshot["thinkingEffort"];
|
|
27
27
|
sessionSource?: SessionSource;
|
|
28
28
|
automationId?: string;
|
|
29
|
+
/** 所属工作空间 ID(多标签 / 分屏项目)。 */
|
|
30
|
+
workspaceId?: string;
|
|
31
|
+
/** 所属工作空间任务 ID(任务 = 独立 worktree + 一组标签)。 */
|
|
32
|
+
workspaceTaskId?: string;
|
|
29
33
|
/**
|
|
30
34
|
* 恢复用的初始会话 id:
|
|
31
35
|
* - Codex:历史 thread id,首条消息即 `codex exec ... resume <id>` 续接。
|
|
@@ -545,6 +545,8 @@ export class StructuredSessionManager {
|
|
|
545
545
|
sessionKind: "structured",
|
|
546
546
|
sessionSource: options.sessionSource ?? "interactive",
|
|
547
547
|
automationId: options.automationId,
|
|
548
|
+
workspaceId: options.workspaceId,
|
|
549
|
+
workspaceTaskId: options.workspaceTaskId,
|
|
548
550
|
provider,
|
|
549
551
|
runner,
|
|
550
552
|
command: provider === "codex"
|
package/dist/types.d.ts
CHANGED
|
@@ -324,6 +324,10 @@ export interface CommandRequest {
|
|
|
324
324
|
rows?: number;
|
|
325
325
|
/** 思考深度。null/缺省 视为 off(不启用思考)。 */
|
|
326
326
|
thinkingEffort?: ThinkingEffort | null;
|
|
327
|
+
/** 创建会话时绑定到的工作空间 ID(多标签 / 分屏项目)。 */
|
|
328
|
+
workspaceId?: string;
|
|
329
|
+
/** 创建会话时绑定到的工作空间任务 ID(任务 = 独立 worktree + 一组标签)。 */
|
|
330
|
+
workspaceTaskId?: string;
|
|
327
331
|
}
|
|
328
332
|
export interface InputRequest {
|
|
329
333
|
input?: string;
|
|
@@ -490,6 +494,10 @@ export interface SessionSnapshot {
|
|
|
490
494
|
sessionSource?: SessionSource;
|
|
491
495
|
/** 自动化创建会话时关联的自动化任务 ID。 */
|
|
492
496
|
automationId?: string;
|
|
497
|
+
/** 所属工作空间 ID(多标签 / 分屏项目)。会话在该工作空间窗口内作为一个标签。 */
|
|
498
|
+
workspaceId?: string;
|
|
499
|
+
/** 所属工作空间任务 ID;任务独占一个 worktree,其下所有会话共享该 worktree 目录。 */
|
|
500
|
+
workspaceTaskId?: string;
|
|
493
501
|
sessionKind?: SessionKind;
|
|
494
502
|
provider?: SessionProvider;
|
|
495
503
|
/** True while the provider CLI owns the PTY; false after it returns to the persistent shell. */
|
|
@@ -574,6 +582,80 @@ export interface SessionSnapshot {
|
|
|
574
582
|
/** Internal shell-wrapper marker needed to keep parsing a daemon-owned PTY after reattach. */
|
|
575
583
|
ptyLaunchMarkerToken?: string | null;
|
|
576
584
|
}
|
|
585
|
+
/** 工作空间默认 IDE;新建 Agent 标签时缺省回落到该 provider。 */
|
|
586
|
+
export type WorkspaceDefaultProvider = SessionProvider;
|
|
587
|
+
/** 工作空间内一个标签页:会话(IDE/终端)/ 编辑器 / 预览。 */
|
|
588
|
+
export type PaneTab = {
|
|
589
|
+
id: string;
|
|
590
|
+
kind: "session";
|
|
591
|
+
sessionId: string;
|
|
592
|
+
} | {
|
|
593
|
+
id: string;
|
|
594
|
+
kind: "editor";
|
|
595
|
+
path: string;
|
|
596
|
+
} | {
|
|
597
|
+
id: string;
|
|
598
|
+
kind: "preview";
|
|
599
|
+
path: string;
|
|
600
|
+
};
|
|
601
|
+
/**
|
|
602
|
+
* 标签 / 分屏布局树。叶子是 tabset(一组标签),内部节点是二分屏。
|
|
603
|
+
* `split.ratio` 为左/上子节点占比(0~1),由前端 allotment 回写持久化。
|
|
604
|
+
*/
|
|
605
|
+
export type LayoutNode = {
|
|
606
|
+
type: "pane";
|
|
607
|
+
tabs: PaneTab[];
|
|
608
|
+
active: number;
|
|
609
|
+
} | {
|
|
610
|
+
type: "split";
|
|
611
|
+
dir: "h" | "v";
|
|
612
|
+
ratio: number;
|
|
613
|
+
children: [LayoutNode, LayoutNode];
|
|
614
|
+
};
|
|
615
|
+
/** 顶部一个工作窗口 Tab;内部可以包含一棵终端分屏树。 */
|
|
616
|
+
export interface WorkWindowLayout {
|
|
617
|
+
id: string;
|
|
618
|
+
layout: LayoutNode;
|
|
619
|
+
activeTabId?: string;
|
|
620
|
+
}
|
|
621
|
+
/** 任务级窗口集合:顶部 Tab 与 windows 一一对应。 */
|
|
622
|
+
export interface TaskWindowLayout {
|
|
623
|
+
type: "windows";
|
|
624
|
+
windows: WorkWindowLayout[];
|
|
625
|
+
activeWindowId: string | null;
|
|
626
|
+
}
|
|
627
|
+
/** 项目 / 工作空间。锚定一个目录(如 wand 仓库),内含多个并行「任务」。 */
|
|
628
|
+
export interface Workspace {
|
|
629
|
+
id: string;
|
|
630
|
+
name: string;
|
|
631
|
+
cwd: string;
|
|
632
|
+
defaultProvider?: WorkspaceDefaultProvider;
|
|
633
|
+
/**
|
|
634
|
+
* 工作空间级的布局占位(保留字段)。标签 / 分屏布局实际挂在 Task 上
|
|
635
|
+
* (见 WorkspaceTask.layout),因为每个任务独占一个 worktree 与一组标签。
|
|
636
|
+
*/
|
|
637
|
+
layout: LayoutNode | null;
|
|
638
|
+
createdAt: string;
|
|
639
|
+
lastOpenedAt: string | null;
|
|
640
|
+
}
|
|
641
|
+
/** 任务所属 worktree 信息(复用 WorktreeInfo);非 git 目录时为 null(退化为直接在项目目录运行)。 */
|
|
642
|
+
export type WorkspaceTaskWorktree = WorktreeInfo;
|
|
643
|
+
export type WorkspaceTaskStatus = "active" | "done";
|
|
644
|
+
/**
|
|
645
|
+
* 工作空间内的一个「任务」:命名、独立 worktree 隔离、自带一组标签(LayoutNode)。
|
|
646
|
+
* 一个工作空间下可有多个任务,任务在侧栏列表里单独展示;每个任务的会话共享该任务的 worktree。
|
|
647
|
+
*/
|
|
648
|
+
export interface WorkspaceTask {
|
|
649
|
+
id: string;
|
|
650
|
+
workspaceId: string;
|
|
651
|
+
name: string;
|
|
652
|
+
worktree: WorkspaceTaskWorktree | null;
|
|
653
|
+
/** 该任务的工作窗口 Tabs;每个窗口内部可含一棵分屏树。 */
|
|
654
|
+
layout: TaskWindowLayout | null;
|
|
655
|
+
status: WorkspaceTaskStatus;
|
|
656
|
+
createdAt: string;
|
|
657
|
+
lastOpenedAt: string | null;
|
|
658
|
+
}
|
|
577
659
|
/** Unified event type emitted by ClaudePtyBridge for WebSocket broadcast */
|
|
578
660
|
export type SessionEventType = "output.raw" | "output.chat" | "chat.turn" | "permission.prompt" | "permission.resolved" | "session.id" | "task" | "ended";
|
|
579
661
|
export interface SessionEvent {
|