@co0ontty/wand 4.68.1 → 4.69.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 +1 -0
- package/dist/process-manager.js +8 -0
- package/dist/server-task-routes.d.ts +14 -0
- package/dist/server-task-routes.js +90 -24
- package/dist/server-workspace-routes.d.ts +2 -1
- package/dist/server-workspace-routes.js +71 -26
- package/dist/server.js +1 -1
- package/dist/session-registry.d.ts +2 -0
- package/dist/session-registry.js +18 -3
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +44 -14
- package/dist/structured-session-manager.d.ts +1 -0
- package/dist/structured-session-manager.js +5 -0
- package/dist/task-types.d.ts +6 -1
- package/dist/wand-task-sync.d.ts +30 -9
- package/dist/wand-task-sync.js +198 -209
- package/dist/web-ui/content/scripts.js +88 -68
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +3 -0
- package/dist/web-ui/embedded-assets.js +2 -2
- package/package.json +1 -1
package/dist/storage.js
CHANGED
|
@@ -347,8 +347,8 @@ function sessionPersistAssignments() {
|
|
|
347
347
|
title = excluded.title,
|
|
348
348
|
description = excluded.description,
|
|
349
349
|
session_options = excluded.session_options,
|
|
350
|
-
workspace_id =
|
|
351
|
-
workspace_task_id =
|
|
350
|
+
workspace_id = command_sessions.workspace_id,
|
|
351
|
+
workspace_task_id = command_sessions.workspace_task_id`;
|
|
352
352
|
}
|
|
353
353
|
function sessionRuntimeMetadataAssignments() {
|
|
354
354
|
return `session_source = ?, automation_id = ?,
|
|
@@ -708,6 +708,7 @@ const INIT_SQL = `
|
|
|
708
708
|
agent_json TEXT,
|
|
709
709
|
workspace_task_id TEXT,
|
|
710
710
|
title_source TEXT,
|
|
711
|
+
auto_title_signature TEXT,
|
|
711
712
|
created_at TEXT NOT NULL,
|
|
712
713
|
updated_at TEXT NOT NULL,
|
|
713
714
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
|
|
@@ -735,9 +736,12 @@ function ensureWandTaskSchema(db) {
|
|
|
735
736
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN agent_json TEXT");
|
|
736
737
|
if (columns.length > 0 && !names.has("workspace_task_id"))
|
|
737
738
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN workspace_task_id TEXT");
|
|
738
|
-
// 标题来源:'user' = 用户手写,'auto' =
|
|
739
|
+
// 标题来源:'user' = 用户手写,'auto' = 由描述/会话内容自动生成。只加列,历史行保持 NULL(读取时视为 user)。
|
|
739
740
|
if (columns.length > 0 && !names.has("title_source"))
|
|
740
741
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN title_source TEXT");
|
|
742
|
+
// 自动标题最近一次的输入指纹:内容没变就不再重复总结(也避免自动标题来回震荡)。只加列。
|
|
743
|
+
if (columns.length > 0 && !names.has("auto_title_signature"))
|
|
744
|
+
db.exec("ALTER TABLE wand_tasks ADD COLUMN auto_title_signature TEXT");
|
|
741
745
|
// 里程碑:只加列,历史行保持 NULL;里程碑本体在 wand_milestones(INIT_SQL 建表)。
|
|
742
746
|
if (columns.length > 0 && !names.has("milestone_id"))
|
|
743
747
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN milestone_id TEXT");
|
|
@@ -1028,6 +1032,11 @@ export class WandStorage {
|
|
|
1028
1032
|
return rows.map((row) => this.mapSessionRow(row));
|
|
1029
1033
|
}
|
|
1030
1034
|
/** 显式更新某会话的工作空间归属(用于创建时绑定)。 */
|
|
1035
|
+
getSessionWorkspace(sessionId) {
|
|
1036
|
+
const row = this.db.prepare("SELECT workspace_id, workspace_task_id FROM command_sessions WHERE id = ?")
|
|
1037
|
+
.get(sessionId);
|
|
1038
|
+
return row ? { workspaceId: row.workspace_id ?? undefined, workspaceTaskId: row.workspace_task_id ?? undefined } : null;
|
|
1039
|
+
}
|
|
1031
1040
|
setSessionWorkspaceId(sessionId, workspaceId) {
|
|
1032
1041
|
this.db.prepare("UPDATE command_sessions SET workspace_id = ? WHERE id = ?").run(workspaceId, sessionId);
|
|
1033
1042
|
}
|
|
@@ -1097,6 +1106,10 @@ export class WandStorage {
|
|
|
1097
1106
|
updateWorkspaceTask(id, patch) {
|
|
1098
1107
|
const assignments = [];
|
|
1099
1108
|
const values = [];
|
|
1109
|
+
if (patch.workspaceId !== undefined) {
|
|
1110
|
+
assignments.push("workspace_id = ?");
|
|
1111
|
+
values.push(patch.workspaceId);
|
|
1112
|
+
}
|
|
1100
1113
|
if (patch.name !== undefined) {
|
|
1101
1114
|
assignments.push("name = ?");
|
|
1102
1115
|
values.push(patch.name);
|
|
@@ -1116,6 +1129,19 @@ export class WandStorage {
|
|
|
1116
1129
|
if (assignments.length === 0)
|
|
1117
1130
|
return;
|
|
1118
1131
|
this.db.prepare(`UPDATE workspace_tasks SET ${assignments.join(", ")} WHERE id = ?`).run(...values, id);
|
|
1132
|
+
const card = this.getWandTaskByWorkspaceTaskId(id);
|
|
1133
|
+
if (card) {
|
|
1134
|
+
// Reverse projection uses direct SQL, so these writers cannot recurse.
|
|
1135
|
+
this.updateWandTask(card.id, {
|
|
1136
|
+
...(patch.name !== undefined ? { title: patch.name, titleSource: "user" } : {}),
|
|
1137
|
+
...(patch.milestoneId !== undefined ? { milestoneId: patch.milestoneId } : {}),
|
|
1138
|
+
...(patch.status !== undefined ? {
|
|
1139
|
+
status: patch.status === "done"
|
|
1140
|
+
? card.status === "archived" ? "archived" : "done"
|
|
1141
|
+
: card.status === "todo" ? "todo" : "doing",
|
|
1142
|
+
} : {}),
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1119
1145
|
}
|
|
1120
1146
|
saveWorkspaceTaskLayout(id, layout, expectedRevision) {
|
|
1121
1147
|
const current = this.getWorkspaceTask(id);
|
|
@@ -1161,8 +1187,10 @@ export class WandStorage {
|
|
|
1161
1187
|
FROM (SELECT id, name, cwd FROM workspaces ORDER BY id)), '') AS workspaceNames,
|
|
1162
1188
|
COALESCE((SELECT GROUP_CONCAT(path || ':' || name || ':' || updated_at, '|')
|
|
1163
1189
|
FROM (SELECT path, name, updated_at FROM session_directory_names ORDER BY path)), '') AS directoryNames`).get();
|
|
1190
|
+
const membership = this.db.prepare("SELECT id, workspace_id, workspace_task_id FROM command_sessions ORDER BY id").all();
|
|
1164
1191
|
return JSON.stringify({
|
|
1165
1192
|
sessions,
|
|
1193
|
+
membership,
|
|
1166
1194
|
tasks,
|
|
1167
1195
|
workspaces,
|
|
1168
1196
|
names,
|
|
@@ -1170,7 +1198,7 @@ export class WandStorage {
|
|
|
1170
1198
|
}
|
|
1171
1199
|
/** GET /api/wand-tasks 的顺序:新创建在前。客户端按此顺序渲染,不再本地排序。 */
|
|
1172
1200
|
listWandTasks(workspaceId) {
|
|
1173
|
-
const rows = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, title_source, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at
|
|
1201
|
+
const rows = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, title_source, auto_title_signature, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at
|
|
1174
1202
|
FROM wand_tasks ${workspaceId === undefined ? "" : "WHERE workspace_id IS ?"}
|
|
1175
1203
|
ORDER BY created_at DESC, rowid DESC`).all(...(workspaceId === undefined ? [] : [workspaceId]));
|
|
1176
1204
|
return rows.map((row) => this.mapWandTaskRow(row));
|
|
@@ -1183,6 +1211,7 @@ export class WandStorage {
|
|
|
1183
1211
|
workspaceTaskId: typeof row.workspace_task_id === "string" && row.workspace_task_id ? row.workspace_task_id : null,
|
|
1184
1212
|
title: String(row.title),
|
|
1185
1213
|
titleSource: row.title_source === "auto" ? "auto" : "user",
|
|
1214
|
+
autoTitleSignature: typeof row.auto_title_signature === "string" && row.auto_title_signature ? row.auto_title_signature : null,
|
|
1186
1215
|
description: String(row.description ?? ""),
|
|
1187
1216
|
status: row.status === "doing" || row.status === "done" || row.status === "archived" ? row.status : "todo",
|
|
1188
1217
|
priority: row.priority === "low" || row.priority === "medium" || row.priority === "high" || row.priority === "urgent" ? row.priority : "none",
|
|
@@ -1199,17 +1228,10 @@ export class WandStorage {
|
|
|
1199
1228
|
return this.listWandTasks().find((task) => task.id === id) ?? null;
|
|
1200
1229
|
}
|
|
1201
1230
|
getWandTaskByWorkspaceTaskId(workspaceTaskId) {
|
|
1202
|
-
const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, title_source, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at
|
|
1231
|
+
const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, title_source, auto_title_signature, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at
|
|
1203
1232
|
FROM wand_tasks WHERE workspace_task_id = ? ORDER BY updated_at DESC LIMIT 1`).get(workspaceTaskId);
|
|
1204
1233
|
return row ? this.mapWandTaskRow(row) : null;
|
|
1205
1234
|
}
|
|
1206
|
-
findUnlinkedWandTask(workspaceId, title) {
|
|
1207
|
-
const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, title_source, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at
|
|
1208
|
-
FROM wand_tasks
|
|
1209
|
-
WHERE workspace_id IS ? AND title = ? AND (workspace_task_id IS NULL OR workspace_task_id = '')
|
|
1210
|
-
ORDER BY updated_at DESC LIMIT 1`).get(workspaceId, title);
|
|
1211
|
-
return row ? this.mapWandTaskRow(row) : null;
|
|
1212
|
-
}
|
|
1213
1235
|
createWandTask(input) {
|
|
1214
1236
|
const id = crypto.randomUUID();
|
|
1215
1237
|
const now = nowIso();
|
|
@@ -1218,7 +1240,7 @@ export class WandStorage {
|
|
|
1218
1240
|
const max = this.db.prepare("SELECT COALESCE(MAX(sort_order), -1) AS value FROM wand_tasks WHERE workspace_id IS ? AND status = ?").get(input.workspaceId ?? null, status);
|
|
1219
1241
|
const numberRow = this.db.prepare("SELECT COALESCE(MAX(CAST(substr(identifier, 6) AS INTEGER)), 0) AS value FROM wand_tasks WHERE identifier GLOB 'TASK-[0-9]*'").get();
|
|
1220
1242
|
const identifier = `TASK-${(numberRow?.value ?? 0) + 1}`;
|
|
1221
|
-
this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, workspace_task_id, title, title_source, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.title, input.titleSource ?? "user", input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, input.milestoneId ?? null, (max?.value ?? -1) + 1, input.agent ? JSON.stringify(input.agent) : null, now, now);
|
|
1243
|
+
this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, workspace_task_id, title, title_source, auto_title_signature, description, status, priority, labels_json, due_date, milestone_id, sort_order, agent_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.title, input.titleSource ?? "user", null, input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, input.milestoneId ?? null, (max?.value ?? -1) + 1, input.agent ? JSON.stringify(input.agent) : null, now, now);
|
|
1222
1244
|
return this.getWandTask(id);
|
|
1223
1245
|
}
|
|
1224
1246
|
updateWandTask(id, patch) {
|
|
@@ -1226,7 +1248,14 @@ export class WandStorage {
|
|
|
1226
1248
|
if (!current)
|
|
1227
1249
|
return null;
|
|
1228
1250
|
const next = { ...current, ...patch, updatedAt: nowIso() };
|
|
1229
|
-
this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, workspace_task_id = ?, title = ?, title_source = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, milestone_id = ?, sort_order = ?, agent_json = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.workspaceTaskId, next.title, next.titleSource, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.milestoneId ?? null, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.updatedAt, id);
|
|
1251
|
+
this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, workspace_task_id = ?, title = ?, title_source = ?, auto_title_signature = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, milestone_id = ?, sort_order = ?, agent_json = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.workspaceTaskId, next.title, next.titleSource, next.autoTitleSignature ?? null, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.milestoneId ?? null, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.updatedAt, id);
|
|
1252
|
+
if (next.workspaceTaskId) {
|
|
1253
|
+
const workspaceId = next.workspaceId ?? this.ensureGlobalWorkspace().id;
|
|
1254
|
+
this.db.prepare(`UPDATE workspace_tasks SET name = ?, status = ?, milestone_id = ?, workspace_id = ? WHERE id = ?`)
|
|
1255
|
+
.run(next.title, next.status === "done" || next.status === "archived" ? "done" : "active", next.milestoneId ?? null, workspaceId, next.workspaceTaskId);
|
|
1256
|
+
this.db.prepare("UPDATE command_sessions SET workspace_id = ? WHERE workspace_task_id = ?")
|
|
1257
|
+
.run(workspaceId, next.workspaceTaskId);
|
|
1258
|
+
}
|
|
1230
1259
|
return next;
|
|
1231
1260
|
}
|
|
1232
1261
|
deleteWandTask(id) { this.db.prepare("DELETE FROM wand_tasks WHERE id = ?").run(id); }
|
|
@@ -1284,6 +1313,7 @@ export class WandStorage {
|
|
|
1284
1313
|
throw new Error("未找到该任务。");
|
|
1285
1314
|
if (!this.getSession(sessionId))
|
|
1286
1315
|
throw new Error("未找到该会话。");
|
|
1316
|
+
this.db.prepare("DELETE FROM wand_task_sessions WHERE session_id = ? AND task_id != ?").run(sessionId, taskId);
|
|
1287
1317
|
this.db.prepare("INSERT OR IGNORE INTO wand_task_sessions (task_id, session_id, bound_at) VALUES (?, ?, ?)").run(taskId, sessionId, nowIso());
|
|
1288
1318
|
}
|
|
1289
1319
|
unbindWandTaskSession(taskId, sessionId) { this.db.prepare("DELETE FROM wand_task_sessions WHERE task_id = ? AND session_id = ?").run(taskId, sessionId); }
|
|
@@ -129,6 +129,7 @@ export declare class StructuredSessionManager {
|
|
|
129
129
|
/** Return lightweight snapshots for the session list (no output/messages). */
|
|
130
130
|
listSlim(): SessionSnapshot[];
|
|
131
131
|
get(id: string): SessionSnapshot | null;
|
|
132
|
+
setSessionWorkspace(id: string, membership: Pick<SessionSnapshot, "workspaceId" | "workspaceTaskId">): void;
|
|
132
133
|
setSessionTopic(id: string, title: string, description: string): SessionSnapshot;
|
|
133
134
|
private setSessionTopicGenerating;
|
|
134
135
|
/**
|
|
@@ -931,6 +931,11 @@ export class StructuredSessionManager {
|
|
|
931
931
|
const s = this.sessions.get(id);
|
|
932
932
|
return s ? withSummary(s) : null;
|
|
933
933
|
}
|
|
934
|
+
setSessionWorkspace(id, membership) {
|
|
935
|
+
const updated = { ...this.requireSession(id), ...membership };
|
|
936
|
+
this.sessions.set(id, updated);
|
|
937
|
+
this.emitStructuredSnapshot(updated);
|
|
938
|
+
}
|
|
934
939
|
setSessionTopic(id, title, description) {
|
|
935
940
|
const current = this.requireSession(id);
|
|
936
941
|
const updated = { ...current, title, description, summary: description };
|
package/dist/task-types.d.ts
CHANGED
|
@@ -58,8 +58,13 @@ export interface WandTask {
|
|
|
58
58
|
workspaceTaskId: string | null;
|
|
59
59
|
identifier: string;
|
|
60
60
|
title: string;
|
|
61
|
-
/** 'user' = 用户自己填的标题;'auto' =
|
|
61
|
+
/** 'user' = 用户自己填的标题;'auto' = 用户留空后按描述/会话内容自动生成。 */
|
|
62
62
|
titleSource: WandTaskTitleSource;
|
|
63
|
+
/**
|
|
64
|
+
* 最近一次自动命名用到的输入指纹(描述 + 所有会话摘要);内容没变就不再重复总结。
|
|
65
|
+
* 标题来源为 user 或从未自动命名过时为 null。
|
|
66
|
+
*/
|
|
67
|
+
autoTitleSignature: string | null;
|
|
63
68
|
description: string;
|
|
64
69
|
status: WandTaskStatus;
|
|
65
70
|
priority: WandTaskPriority;
|
package/dist/wand-task-sync.d.ts
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
import type { WandStorage } from "./storage.js";
|
|
2
|
-
import { type WandTask } from "./task-types.js";
|
|
2
|
+
import { type WandTask, type WandTaskTitleSource } from "./task-types.js";
|
|
3
3
|
import type { SessionSnapshot, Workspace, WorkspaceTask } from "./types.js";
|
|
4
|
+
/** 未命名任务的占位名;服务端新建时写入,自动命名成功后被真实标题覆盖。 */
|
|
5
|
+
export declare const UNNAMED_WORKSPACE_TASK_NAME = "\u672A\u547D\u540D\u4EFB\u52A1";
|
|
4
6
|
export declare function isUnnamedWorkspaceTaskName(name: string): boolean;
|
|
5
|
-
/**
|
|
7
|
+
/** Legacy title helper; prompts name sessions, not their containing task. */
|
|
6
8
|
export declare function boardTitleFromSession(session: SessionSnapshot): string;
|
|
7
|
-
/** 侧栏工作任务与看板卡片对账:已关联则复用,同项目同名未关联则挂上,否则新建。 */
|
|
8
|
-
export declare function ensureBoardTaskForWorkspaceTask(storage: WandStorage, task: WorkspaceTask, workspace: Workspace): WandTask | null;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* 自动命名只覆盖「还没有人为名字」的任务:标题来源是 auto,或仍是占位名的历史卡片
|
|
11
|
+
* (老版本把未命名任务写成了 titleSource=user)。用户在面板 / 侧栏改过名后此处为 false。
|
|
12
12
|
*/
|
|
13
|
+
export declare function isAutoNameableBoardTask(card: Pick<WandTask, "title" | "titleSource">): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* 自动命名的输入:任务描述(去掉「项目:/ 目录:/ 分支:」同步元信息)+ 每个会话的可读摘要。
|
|
16
|
+
* 为空表示这个任务还没有可命名的内容,调用方应保留占位标题。
|
|
17
|
+
*/
|
|
18
|
+
export declare function taskAutoNameSourceText(storage: WandStorage, card: WandTask): string;
|
|
19
|
+
/** 输入指纹:内容没变就没必要再总结一次,也避免自动标题与模型结果来回震荡。 */
|
|
20
|
+
export declare function taskAutoNameSignature(source: string): string;
|
|
21
|
+
/** Identity is the task id, never its title. Same-named tasks stay independent. */
|
|
22
|
+
export declare function ensureBoardTaskForWorkspaceTask(storage: WandStorage, task: WorkspaceTask, workspace: Workspace, options?: {
|
|
23
|
+
titleSource?: WandTaskTitleSource;
|
|
24
|
+
}): WandTask;
|
|
25
|
+
/** A board task always has a sidebar container, even before its first session. */
|
|
26
|
+
export declare function ensureWorkspaceTaskForBoardTask(storage: WandStorage, card: WandTask): WorkspaceTask;
|
|
27
|
+
/** Atomic exclusive ownership change. Never changes cwd, messages, or execution state. */
|
|
28
|
+
export declare function moveSessionToWorkspaceTask(storage: WandStorage, sessionId: string, taskId: string | null): void;
|
|
29
|
+
/** Reconcile legacy records by explicit ids. Unassigned sessions remain unassigned. */
|
|
30
|
+
export declare function syncSidebarTasksFromBoard(storage: WandStorage): void;
|
|
13
31
|
export declare function syncUngroupedSessionsToBoard(storage: WandStorage): void;
|
|
14
|
-
/**
|
|
32
|
+
/** Status and title projection is centralized in the storage writer. */
|
|
15
33
|
export declare function syncClosedBoardTask(storage: WandStorage, task: WandTask): void;
|
|
16
|
-
/** 看板归档:卡片进入归档目录,并同步把关联的侧栏工作任务标成 done(不删会话)。 */
|
|
17
34
|
export declare function archiveBoardTask(storage: WandStorage, id: string): WandTask | null;
|
|
18
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* 归档侧栏任务是软删除:终端继续运行、worktree 与布局都保留,只有看板卡片进入
|
|
37
|
+
* 归档文件夹、侧栏不再显示。恢复方式是看板里把卡片拖回任一列(或右键恢复)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function archiveWorkspaceTask(storage: WandStorage, task: WorkspaceTask): WandTask | null;
|
|
19
40
|
export declare function archiveBoardTaskForWorkspaceTask(storage: WandStorage, workspaceTaskId: string): void;
|