@co0ontty/wand 4.57.0 → 4.58.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/ensure-node-pty-helper.d.ts +0 -1
- package/dist/ensure-node-pty-helper.js +0 -5
- package/dist/git-quick-commit.js +18 -19
- package/dist/github-connector.d.ts +11 -5
- package/dist/github-connector.js +105 -33
- package/dist/missions.d.ts +1 -1
- package/dist/missions.js +17 -4
- package/dist/server-file-routes.js +43 -2
- package/dist/server-github-routes.js +50 -53
- package/dist/server-mission-routes.js +11 -10
- package/dist/server-task-routes.d.ts +14 -2
- package/dist/server-task-routes.js +248 -84
- package/dist/server-workspace-routes.js +90 -16
- package/dist/server.js +2 -20
- package/dist/storage.d.ts +23 -6
- package/dist/storage.js +130 -53
- package/dist/structured-exec-host.d.ts +13 -2
- package/dist/structured-exec-host.js +71 -36
- package/dist/structured-exec-pump.d.ts +1 -1
- package/dist/structured-exec-pump.js +14 -8
- package/dist/task-types.d.ts +15 -0
- package/dist/terminal-daemon-client.d.ts +2 -0
- package/dist/terminal-daemon-client.js +7 -1
- package/dist/terminal-daemon-server.js +17 -18
- package/dist/tui/ipc-server.js +3 -1
- package/dist/types.d.ts +1 -0
- package/dist/wand-task-sync.d.ts +9 -0
- package/dist/wand-task-sync.js +50 -0
- package/dist/web-ui/content/scripts.js +85 -85
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/ws-broadcast.js +5 -1
- package/package.json +2 -3
- package/dist/taskboard-bridge.d.ts +0 -11
- package/dist/taskboard-bridge.js +0 -371
- package/vendor/codex-taskboard/LICENSE +0 -201
- package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
- package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
- package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
- package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
- package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
- package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
- package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
- package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
- package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
- package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
- package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
- package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
- package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
- package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
- package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
- package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
- package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
- package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
- package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
- package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
- package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
- package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
- package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
- package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
- package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
- package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
- package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
- package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
- package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
- package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
- package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
- package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
- package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
- package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
- package/vendor/codex-taskboard/dist/web/index.html +0 -20
- package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
- package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
- package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
- package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
- package/vendor/codex-taskboard/server/app.mjs +0 -3211
- package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
- package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
- package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
- package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
- package/vendor/codex-taskboard/server/database.mjs +0 -2845
- package/vendor/codex-taskboard/server/index.mjs +0 -41
- package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
- package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
- package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
- package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
- package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
- package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
- package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
- package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
- package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
- package/vendor/codex-taskboard/shared/domain.mjs +0 -34
- package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
- package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
- package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
- package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
- package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
- package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
- package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
- package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
- package/vendor/codex-taskboard/skills/manage-taskboard/references/cli.md +0 -211
package/dist/server.js
CHANGED
|
@@ -23,7 +23,6 @@ import { StructuredSessionManager } from "./structured-session-manager.js";
|
|
|
23
23
|
import { recordRecentPath, registerFileRoutes } from "./server-file-routes.js";
|
|
24
24
|
import { registerSettingsRoutes } from "./server-settings-routes.js";
|
|
25
25
|
import { registerGithubRoutes } from "./server-github-routes.js";
|
|
26
|
-
import { startTaskboardBridge } from "./taskboard-bridge.js";
|
|
27
26
|
import { registerTaskRoutes } from "./server-task-routes.js";
|
|
28
27
|
import { getGithubConnectorStatus } from "./github-connector.js";
|
|
29
28
|
import { registerMissionRoutes } from "./server-mission-routes.js";
|
|
@@ -558,20 +557,6 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
558
557
|
const requireSessions = buildRequireScope("sessions");
|
|
559
558
|
const requireFiles = buildRequireScope("files");
|
|
560
559
|
const requirePasswordVault = buildRequireScope("password-vault");
|
|
561
|
-
const taskboardBridge = await startTaskboardBridge(configDir, storage, sessionRegistry, structuredSessions, config.defaultCwd);
|
|
562
|
-
app.use("/taskboard", (req, res, next) => {
|
|
563
|
-
// Taskboard's immutable Vite assets are safe to serve without the Wand
|
|
564
|
-
// session cookie. This matters behind HTTPS reverse proxies where iframe
|
|
565
|
-
// subresource requests may not carry the parent authentication cookie.
|
|
566
|
-
const staticAsset = req.path.startsWith("/assets/")
|
|
567
|
-
|| /^\/[^/]+\.(?:css|js|map|svg|png|jpg|jpeg|webp|ico)$/.test(req.path);
|
|
568
|
-
if (staticAsset) {
|
|
569
|
-
taskboardBridge.handler(req, res, next);
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
requireAuth(req, res, next);
|
|
573
|
-
});
|
|
574
|
-
app.use("/taskboard", taskboardBridge.handler);
|
|
575
560
|
// Route-specific parsers must run before the global parser. Once body-parser
|
|
576
561
|
// has consumed a request, a later express.json() cannot tighten or widen it.
|
|
577
562
|
app.use("/api/optimize-prompt", express.json({ limit: "256kb" }));
|
|
@@ -984,7 +969,8 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
984
969
|
},
|
|
985
970
|
});
|
|
986
971
|
registerGithubRoutes(app, { storage, requireAdmin, sessions: sessionRegistry });
|
|
987
|
-
|
|
972
|
+
// 任务管理与 Missions 一样只需登录:原生 connected-app 也要能列/建/派发。
|
|
973
|
+
registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, config });
|
|
988
974
|
registerAdminUpdateRoutes(app, {
|
|
989
975
|
storage,
|
|
990
976
|
config,
|
|
@@ -1586,10 +1572,6 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1586
1572
|
wss.close();
|
|
1587
1573
|
}
|
|
1588
1574
|
catch { /* ignore */ }
|
|
1589
|
-
try {
|
|
1590
|
-
await taskboardBridge.close();
|
|
1591
|
-
}
|
|
1592
|
-
catch { /* best-effort shutdown */ }
|
|
1593
1575
|
try {
|
|
1594
1576
|
await serverClosed;
|
|
1595
1577
|
}
|
package/dist/storage.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ export interface ConnectorMeta {
|
|
|
8
8
|
connectedAt: string | null;
|
|
9
9
|
updatedAt: string;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* `wand_tasks.agent_json` 的读侧校验。历史行 / 手工写坏的值都退化成 null,
|
|
13
|
+
* 前端据此显示「未指定 CLI 工具」,而不是渲染出半个派发配置。
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseWandTaskAgent(raw: unknown): import("./task-types.js").WandTaskAgent | null;
|
|
11
16
|
type AuthPrincipalKind = "browser-admin" | "connected-app";
|
|
12
17
|
export type AuthScope = "admin" | "sessions" | "files" | "password-vault" | "session-preferences";
|
|
13
18
|
export interface AuthPrincipal {
|
|
@@ -93,30 +98,41 @@ export declare class WandStorage {
|
|
|
93
98
|
status?: WorkspaceTaskStatus;
|
|
94
99
|
worktree?: WorkspaceTaskWorktree | null;
|
|
95
100
|
}): void;
|
|
96
|
-
saveWorkspaceTaskLayout(id: string, layout: TaskWindowLayout | null):
|
|
101
|
+
saveWorkspaceTaskLayout(id: string, layout: TaskWindowLayout | null, expectedRevision?: number): {
|
|
102
|
+
ok: true;
|
|
103
|
+
layoutRevision: number;
|
|
104
|
+
} | {
|
|
105
|
+
ok: false;
|
|
106
|
+
conflict: true;
|
|
107
|
+
layoutRevision: number;
|
|
108
|
+
layout: TaskWindowLayout | null;
|
|
109
|
+
};
|
|
97
110
|
touchWorkspaceTask(id: string): void;
|
|
98
111
|
deleteWorkspaceTask(id: string, options?: {
|
|
99
112
|
cascade?: boolean;
|
|
100
113
|
}): void;
|
|
114
|
+
tasksAggregateFingerprint(): string;
|
|
101
115
|
listWandTasks(workspaceId?: string | null): import("./task-types.js").WandTask[];
|
|
116
|
+
private mapWandTaskRow;
|
|
102
117
|
getWandTask(id: string): import("./task-types.js").WandTask | null;
|
|
118
|
+
getWandTaskByWorkspaceTaskId(workspaceTaskId: string): import("./task-types.js").WandTask | null;
|
|
119
|
+
findUnlinkedWandTask(workspaceId: string, title: string): import("./task-types.js").WandTask | null;
|
|
103
120
|
createWandTask(input: {
|
|
104
121
|
workspaceId?: string | null;
|
|
122
|
+
workspaceTaskId?: string | null;
|
|
105
123
|
title: string;
|
|
106
124
|
description?: string;
|
|
107
125
|
status?: import("./task-types.js").WandTaskStatus;
|
|
108
126
|
priority?: import("./task-types.js").WandTaskPriority;
|
|
109
127
|
labels?: string[];
|
|
110
128
|
dueDate?: string | null;
|
|
129
|
+
agent?: import("./task-types.js").WandTaskAgent | null;
|
|
111
130
|
}): import("./task-types.js").WandTask;
|
|
112
|
-
updateWandTask(id: string, patch: Partial<Pick<import("./task-types.js").WandTask, "workspaceId" | "title" | "description" | "status" | "priority" | "labels" | "dueDate" | "sortOrder">>): import("./task-types.js").WandTask | null;
|
|
131
|
+
updateWandTask(id: string, patch: Partial<Pick<import("./task-types.js").WandTask, "workspaceId" | "workspaceTaskId" | "title" | "description" | "status" | "priority" | "labels" | "dueDate" | "sortOrder" | "agent">>): import("./task-types.js").WandTask | null;
|
|
113
132
|
deleteWandTask(id: string): void;
|
|
114
133
|
listWandTaskSessionIds(taskId: string): string[];
|
|
115
134
|
bindWandTaskSession(taskId: string, sessionId: string): void;
|
|
116
135
|
unbindWandTaskSession(taskId: string, sessionId: string): void;
|
|
117
|
-
bindTaskboardTaskSession(taskId: string, sessionId: string): void;
|
|
118
|
-
listTaskboardTaskSessions(taskId: string): string[];
|
|
119
|
-
unbindTaskboardTaskSession(taskId: string, sessionId: string): void;
|
|
120
136
|
listGithubIssueBindings(owner: string, repo: string, issueNumber: number): Array<{
|
|
121
137
|
sessionId: string;
|
|
122
138
|
boundAt: string;
|
|
@@ -135,7 +151,8 @@ export declare class WandStorage {
|
|
|
135
151
|
getAppSecret(): string | null;
|
|
136
152
|
/** Persist appSecret in database (DB is the authoritative source after first migration) */
|
|
137
153
|
setAppSecret(value: string): void;
|
|
138
|
-
|
|
154
|
+
/** Encrypt an at-rest secret with the vault key; plaintext when no key exists. */
|
|
155
|
+
private encryptStoredSecret;
|
|
139
156
|
private decryptPasswordItem;
|
|
140
157
|
ensureDefaultPasswordVault(): void;
|
|
141
158
|
listPasswordVaults(): PasswordVault[];
|
package/dist/storage.js
CHANGED
|
@@ -15,6 +15,25 @@ function safeJsonParse(raw) {
|
|
|
15
15
|
return undefined;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* `wand_tasks.agent_json` 的读侧校验。历史行 / 手工写坏的值都退化成 null,
|
|
20
|
+
* 前端据此显示「未指定 CLI 工具」,而不是渲染出半个派发配置。
|
|
21
|
+
*/
|
|
22
|
+
export function parseWandTaskAgent(raw) {
|
|
23
|
+
const value = typeof raw === "string" ? safeJsonParse(raw) : undefined;
|
|
24
|
+
if (!value || typeof value !== "object")
|
|
25
|
+
return null;
|
|
26
|
+
const provider = value.provider;
|
|
27
|
+
const model = value.model;
|
|
28
|
+
const thinkingEffort = value.thinkingEffort;
|
|
29
|
+
if (provider !== "claude" && provider !== "codex" && provider !== "opencode" && provider !== "grok" && provider !== "qoder" && provider !== "pi")
|
|
30
|
+
return null;
|
|
31
|
+
if (typeof model !== "string" || !model.trim() || model.trim().length > 128)
|
|
32
|
+
return null;
|
|
33
|
+
if (thinkingEffort !== "off" && thinkingEffort !== "standard" && thinkingEffort !== "deep" && thinkingEffort !== "max")
|
|
34
|
+
return null;
|
|
35
|
+
return { provider, model: model.trim(), thinkingEffort };
|
|
36
|
+
}
|
|
18
37
|
const SESSION_OPTIONS_SCHEMA_VERSION = 1;
|
|
19
38
|
function isRecord(value) {
|
|
20
39
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -315,6 +334,7 @@ function mapWorkspaceTaskRow(row) {
|
|
|
315
334
|
status: (row.status === "done" ? "done" : "active"),
|
|
316
335
|
createdAt: row.created_at,
|
|
317
336
|
lastOpenedAt: row.last_opened_at,
|
|
337
|
+
layoutRevision: Number(row.layout_revision ?? 0) || 0,
|
|
318
338
|
};
|
|
319
339
|
}
|
|
320
340
|
function sessionPersistFields() {
|
|
@@ -381,8 +401,8 @@ function sessionPersistValues(snapshot) {
|
|
|
381
401
|
snapshot.output,
|
|
382
402
|
snapshot.ptyOutputSeq ?? 0,
|
|
383
403
|
snapshot.archived ? 1 : 0,
|
|
384
|
-
snapshot.archivedAt,
|
|
385
|
-
snapshot.claudeSessionId,
|
|
404
|
+
snapshot.archivedAt ?? null,
|
|
405
|
+
snapshot.claudeSessionId ?? null,
|
|
386
406
|
snapshot.provider ?? null,
|
|
387
407
|
snapshot.sessionKind ?? "pty",
|
|
388
408
|
snapshot.runner ?? null,
|
|
@@ -415,8 +435,8 @@ function sessionRuntimeMetadataValues(snapshot) {
|
|
|
415
435
|
snapshot.startedAt,
|
|
416
436
|
snapshot.endedAt,
|
|
417
437
|
snapshot.archived ? 1 : 0,
|
|
418
|
-
snapshot.archivedAt,
|
|
419
|
-
snapshot.claudeSessionId,
|
|
438
|
+
snapshot.archivedAt ?? null,
|
|
439
|
+
snapshot.claudeSessionId ?? null,
|
|
420
440
|
snapshot.provider ?? null,
|
|
421
441
|
snapshot.sessionKind ?? "pty",
|
|
422
442
|
snapshot.runner ?? null,
|
|
@@ -668,6 +688,7 @@ const INIT_SQL = `
|
|
|
668
688
|
cwd TEXT,
|
|
669
689
|
created_at TEXT NOT NULL,
|
|
670
690
|
last_opened_at TEXT,
|
|
691
|
+
layout_revision INTEGER NOT NULL DEFAULT 0,
|
|
671
692
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
672
693
|
);
|
|
673
694
|
|
|
@@ -696,6 +717,8 @@ const INIT_SQL = `
|
|
|
696
717
|
labels_json TEXT NOT NULL DEFAULT '[]',
|
|
697
718
|
due_date TEXT,
|
|
698
719
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
720
|
+
agent_json TEXT,
|
|
721
|
+
workspace_task_id TEXT,
|
|
699
722
|
created_at TEXT NOT NULL,
|
|
700
723
|
updated_at TEXT NOT NULL,
|
|
701
724
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
|
|
@@ -710,14 +733,6 @@ const INIT_SQL = `
|
|
|
710
733
|
FOREIGN KEY (session_id) REFERENCES command_sessions(id) ON DELETE CASCADE
|
|
711
734
|
);
|
|
712
735
|
CREATE INDEX IF NOT EXISTS idx_wand_task_sessions_session ON wand_task_sessions(session_id);
|
|
713
|
-
CREATE TABLE IF NOT EXISTS taskboard_session_bindings (
|
|
714
|
-
task_id TEXT NOT NULL,
|
|
715
|
-
session_id TEXT NOT NULL,
|
|
716
|
-
bound_at TEXT NOT NULL,
|
|
717
|
-
PRIMARY KEY (task_id, session_id),
|
|
718
|
-
FOREIGN KEY (session_id) REFERENCES command_sessions(id) ON DELETE CASCADE
|
|
719
|
-
);
|
|
720
|
-
CREATE INDEX IF NOT EXISTS idx_taskboard_session_bindings_session ON taskboard_session_bindings(session_id);
|
|
721
736
|
`;
|
|
722
737
|
function ensureWandTaskSchema(db) {
|
|
723
738
|
const columns = db.prepare("PRAGMA table_info(wand_tasks)").all();
|
|
@@ -726,7 +741,16 @@ function ensureWandTaskSchema(db) {
|
|
|
726
741
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN identifier TEXT");
|
|
727
742
|
if (columns.length > 0 && !names.has("due_date"))
|
|
728
743
|
db.exec("ALTER TABLE wand_tasks ADD COLUMN due_date TEXT");
|
|
744
|
+
// 任务的默认派发配置(CLI 工具 / 模型 / 思考深度)。只加列,历史行保持 NULL。
|
|
745
|
+
if (columns.length > 0 && !names.has("agent_json"))
|
|
746
|
+
db.exec("ALTER TABLE wand_tasks ADD COLUMN agent_json TEXT");
|
|
747
|
+
if (columns.length > 0 && !names.has("workspace_task_id"))
|
|
748
|
+
db.exec("ALTER TABLE wand_tasks ADD COLUMN workspace_task_id TEXT");
|
|
729
749
|
if (columns.length > 0) {
|
|
750
|
+
// Index creation must wait until the column exists on legacy databases.
|
|
751
|
+
// INIT_SQL cannot create it: CREATE TABLE IF NOT EXISTS is a no-op on old
|
|
752
|
+
// wand_tasks, and CREATE INDEX would then fail with "no such column".
|
|
753
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_wand_tasks_workspace_task ON wand_tasks(workspace_task_id)");
|
|
730
754
|
const rows = db.prepare("SELECT id FROM wand_tasks WHERE identifier IS NULL OR identifier = '' ORDER BY created_at, id").all();
|
|
731
755
|
const update = db.prepare("UPDATE wand_tasks SET identifier = ? WHERE id = ?");
|
|
732
756
|
rows.forEach((row, index) => update.run(`TASK-${index + 1}`, row.id));
|
|
@@ -743,6 +767,9 @@ function ensureWorkspaceSchema(db) {
|
|
|
743
767
|
if (taskColumns.length > 0 && !taskNames.has("cwd")) {
|
|
744
768
|
db.exec("ALTER TABLE workspace_tasks ADD COLUMN cwd TEXT");
|
|
745
769
|
}
|
|
770
|
+
if (taskColumns.length > 0 && !taskNames.has("layout_revision")) {
|
|
771
|
+
db.exec("ALTER TABLE workspace_tasks ADD COLUMN layout_revision INTEGER NOT NULL DEFAULT 0");
|
|
772
|
+
}
|
|
746
773
|
}
|
|
747
774
|
function ensureConnectorSchema(db) {
|
|
748
775
|
// The table is created by INIT_SQL for new databases; this repairs an
|
|
@@ -771,6 +798,7 @@ export function ensureDatabaseFile(dbPath) {
|
|
|
771
798
|
ensureAuthSessionSchema(db);
|
|
772
799
|
ensureCommandSessionSchema(db);
|
|
773
800
|
ensureWorkspaceSchema(db);
|
|
801
|
+
ensureWandTaskSchema(db);
|
|
774
802
|
ensureConnectorSchema(db);
|
|
775
803
|
{
|
|
776
804
|
const missionColumns = db.prepare("PRAGMA table_info(missions)").all();
|
|
@@ -902,7 +930,7 @@ export class WandStorage {
|
|
|
902
930
|
// ============ Workspaces(多标签 / 分屏项目)============
|
|
903
931
|
listWorkspaces() {
|
|
904
932
|
const rows = this.db
|
|
905
|
-
.prepare("SELECT id, name, cwd, kind, default_provider, layout_json, created_at, last_opened_at FROM workspaces ORDER BY created_at
|
|
933
|
+
.prepare("SELECT id, name, cwd, kind, default_provider, layout_json, created_at, last_opened_at FROM workspaces ORDER BY created_at DESC, id DESC")
|
|
906
934
|
.all();
|
|
907
935
|
return rows.map(mapWorkspaceRow);
|
|
908
936
|
}
|
|
@@ -1026,15 +1054,15 @@ export class WandStorage {
|
|
|
1026
1054
|
// ── Workspace tasks(任务 = 命名 + 独立 worktree + 一组标签)──
|
|
1027
1055
|
listWorkspaceTasks(workspaceId) {
|
|
1028
1056
|
const rows = this.db
|
|
1029
|
-
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at
|
|
1057
|
+
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at, layout_revision
|
|
1030
1058
|
FROM workspace_tasks WHERE workspace_id = ?
|
|
1031
|
-
ORDER BY
|
|
1059
|
+
ORDER BY created_at DESC, id DESC`)
|
|
1032
1060
|
.all(workspaceId);
|
|
1033
1061
|
return rows.map(mapWorkspaceTaskRow);
|
|
1034
1062
|
}
|
|
1035
1063
|
getWorkspaceTask(id) {
|
|
1036
1064
|
const row = this.db
|
|
1037
|
-
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at
|
|
1065
|
+
.prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at, layout_revision
|
|
1038
1066
|
FROM workspace_tasks WHERE id = ?`)
|
|
1039
1067
|
.get(id);
|
|
1040
1068
|
return row ? mapWorkspaceTaskRow(row) : null;
|
|
@@ -1079,10 +1107,19 @@ export class WandStorage {
|
|
|
1079
1107
|
return;
|
|
1080
1108
|
this.db.prepare(`UPDATE workspace_tasks SET ${assignments.join(", ")} WHERE id = ?`).run(...values, id);
|
|
1081
1109
|
}
|
|
1082
|
-
saveWorkspaceTaskLayout(id, layout) {
|
|
1110
|
+
saveWorkspaceTaskLayout(id, layout, expectedRevision) {
|
|
1111
|
+
const current = this.getWorkspaceTask(id);
|
|
1112
|
+
if (!current)
|
|
1113
|
+
throw new Error("未找到该任务。");
|
|
1114
|
+
const currentRevision = current.layoutRevision ?? 0;
|
|
1115
|
+
if (expectedRevision !== undefined && expectedRevision !== currentRevision) {
|
|
1116
|
+
return { ok: false, conflict: true, layoutRevision: currentRevision, layout: current.layout };
|
|
1117
|
+
}
|
|
1118
|
+
const nextRevision = currentRevision + 1;
|
|
1083
1119
|
this.db
|
|
1084
|
-
.prepare("UPDATE workspace_tasks SET layout_json = ? WHERE id = ?")
|
|
1085
|
-
.run(layout ? JSON.stringify(layout) : null, id);
|
|
1120
|
+
.prepare("UPDATE workspace_tasks SET layout_json = ?, layout_revision = ? WHERE id = ?")
|
|
1121
|
+
.run(layout ? JSON.stringify(layout) : null, nextRevision, id);
|
|
1122
|
+
return { ok: true, layoutRevision: nextRevision };
|
|
1086
1123
|
}
|
|
1087
1124
|
touchWorkspaceTask(id) {
|
|
1088
1125
|
this.db.prepare("UPDATE workspace_tasks SET last_opened_at = ? WHERE id = ?").run(nowIso(), id);
|
|
@@ -1096,14 +1133,36 @@ export class WandStorage {
|
|
|
1096
1133
|
}
|
|
1097
1134
|
this.db.prepare("DELETE FROM workspace_tasks WHERE id = ?").run(id);
|
|
1098
1135
|
}
|
|
1136
|
+
tasksAggregateFingerprint() {
|
|
1137
|
+
const sessions = this.db.prepare(`SELECT COUNT(*) AS count,
|
|
1138
|
+
COALESCE(MAX(started_at), '') AS started,
|
|
1139
|
+
COALESCE(MAX(ended_at), '') AS ended,
|
|
1140
|
+
COALESCE(MAX(title), '') AS title
|
|
1141
|
+
FROM command_sessions`).get();
|
|
1142
|
+
const tasks = this.db.prepare(`SELECT COUNT(*) AS count,
|
|
1143
|
+
COALESCE(MAX(created_at), '') AS created,
|
|
1144
|
+
COALESCE(MAX(last_opened_at), '') AS opened,
|
|
1145
|
+
COALESCE(MAX(layout_revision), 0) AS revision
|
|
1146
|
+
FROM workspace_tasks`).get();
|
|
1147
|
+
const workspaces = this.db.prepare(`SELECT COUNT(*) AS count, COALESCE(MAX(last_opened_at), '') AS opened FROM workspaces`).get();
|
|
1148
|
+
return JSON.stringify({
|
|
1149
|
+
sessions,
|
|
1150
|
+
tasks,
|
|
1151
|
+
workspaces,
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1099
1154
|
listWandTasks(workspaceId) {
|
|
1100
|
-
const rows = this.db.prepare(`SELECT id, identifier, workspace_id, title, description, status, priority, labels_json, due_date, sort_order, created_at, updated_at
|
|
1155
|
+
const rows = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
|
|
1101
1156
|
FROM wand_tasks ${workspaceId === undefined ? "" : "WHERE workspace_id IS ?"}
|
|
1102
1157
|
ORDER BY status, sort_order, updated_at DESC`).all(...(workspaceId === undefined ? [] : [workspaceId]));
|
|
1103
|
-
return rows.map((row) => (
|
|
1158
|
+
return rows.map((row) => this.mapWandTaskRow(row));
|
|
1159
|
+
}
|
|
1160
|
+
mapWandTaskRow(row) {
|
|
1161
|
+
return {
|
|
1104
1162
|
id: String(row.id),
|
|
1105
1163
|
identifier: typeof row.identifier === "string" && row.identifier ? row.identifier : `TASK-${String(row.id).slice(0, 6)}`,
|
|
1106
1164
|
workspaceId: typeof row.workspace_id === "string" ? row.workspace_id : null,
|
|
1165
|
+
workspaceTaskId: typeof row.workspace_task_id === "string" && row.workspace_task_id ? row.workspace_task_id : null,
|
|
1107
1166
|
title: String(row.title),
|
|
1108
1167
|
description: String(row.description ?? ""),
|
|
1109
1168
|
status: row.status === "doing" || row.status === "done" ? row.status : "todo",
|
|
@@ -1111,13 +1170,26 @@ export class WandStorage {
|
|
|
1111
1170
|
labels: (() => { const parsed = safeJsonParse(String(row.labels_json ?? "[]")); return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : []; })(),
|
|
1112
1171
|
dueDate: typeof row.due_date === "string" ? row.due_date : null,
|
|
1113
1172
|
sortOrder: Number(row.sort_order) || 0,
|
|
1173
|
+
agent: parseWandTaskAgent(row.agent_json),
|
|
1114
1174
|
createdAt: String(row.created_at),
|
|
1115
1175
|
updatedAt: String(row.updated_at),
|
|
1116
|
-
}
|
|
1176
|
+
};
|
|
1117
1177
|
}
|
|
1118
1178
|
getWandTask(id) {
|
|
1119
1179
|
return this.listWandTasks().find((task) => task.id === id) ?? null;
|
|
1120
1180
|
}
|
|
1181
|
+
getWandTaskByWorkspaceTaskId(workspaceTaskId) {
|
|
1182
|
+
const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
|
|
1183
|
+
FROM wand_tasks WHERE workspace_task_id = ? ORDER BY updated_at DESC LIMIT 1`).get(workspaceTaskId);
|
|
1184
|
+
return row ? this.mapWandTaskRow(row) : null;
|
|
1185
|
+
}
|
|
1186
|
+
findUnlinkedWandTask(workspaceId, title) {
|
|
1187
|
+
const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
|
|
1188
|
+
FROM wand_tasks
|
|
1189
|
+
WHERE workspace_id IS ? AND title = ? AND (workspace_task_id IS NULL OR workspace_task_id = '')
|
|
1190
|
+
ORDER BY updated_at DESC LIMIT 1`).get(workspaceId, title);
|
|
1191
|
+
return row ? this.mapWandTaskRow(row) : null;
|
|
1192
|
+
}
|
|
1121
1193
|
createWandTask(input) {
|
|
1122
1194
|
const id = crypto.randomUUID();
|
|
1123
1195
|
const now = nowIso();
|
|
@@ -1126,7 +1198,7 @@ export class WandStorage {
|
|
|
1126
1198
|
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);
|
|
1127
1199
|
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();
|
|
1128
1200
|
const identifier = `TASK-${(numberRow?.value ?? 0) + 1}`;
|
|
1129
|
-
this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, title, description, status, priority, labels_json, due_date, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.title, input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, (max?.value ?? -1) + 1, now, now);
|
|
1201
|
+
this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.title, input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, (max?.value ?? -1) + 1, input.agent ? JSON.stringify(input.agent) : null, now, now);
|
|
1130
1202
|
return this.getWandTask(id);
|
|
1131
1203
|
}
|
|
1132
1204
|
updateWandTask(id, patch) {
|
|
@@ -1134,12 +1206,12 @@ export class WandStorage {
|
|
|
1134
1206
|
if (!current)
|
|
1135
1207
|
return null;
|
|
1136
1208
|
const next = { ...current, ...patch, updatedAt: nowIso() };
|
|
1137
|
-
this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, title = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, sort_order = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.title, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.sortOrder, next.updatedAt, id);
|
|
1209
|
+
this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, workspace_task_id = ?, title = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, sort_order = ?, agent_json = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.workspaceTaskId, next.title, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.updatedAt, id);
|
|
1138
1210
|
return next;
|
|
1139
1211
|
}
|
|
1140
1212
|
deleteWandTask(id) { this.db.prepare("DELETE FROM wand_tasks WHERE id = ?").run(id); }
|
|
1141
1213
|
listWandTaskSessionIds(taskId) {
|
|
1142
|
-
const rows = this.db.prepare("SELECT session_id FROM wand_task_sessions WHERE task_id = ? ORDER BY
|
|
1214
|
+
const rows = this.db.prepare("SELECT session_id FROM wand_task_sessions WHERE task_id = ? ORDER BY rowid ASC, session_id ASC").all(taskId);
|
|
1143
1215
|
return rows.map((row) => row.session_id);
|
|
1144
1216
|
}
|
|
1145
1217
|
bindWandTaskSession(taskId, sessionId) {
|
|
@@ -1150,18 +1222,6 @@ export class WandStorage {
|
|
|
1150
1222
|
this.db.prepare("INSERT OR IGNORE INTO wand_task_sessions (task_id, session_id, bound_at) VALUES (?, ?, ?)").run(taskId, sessionId, nowIso());
|
|
1151
1223
|
}
|
|
1152
1224
|
unbindWandTaskSession(taskId, sessionId) { this.db.prepare("DELETE FROM wand_task_sessions WHERE task_id = ? AND session_id = ?").run(taskId, sessionId); }
|
|
1153
|
-
bindTaskboardTaskSession(taskId, sessionId) {
|
|
1154
|
-
if (!this.getSession(sessionId))
|
|
1155
|
-
throw new Error("未找到该会话。");
|
|
1156
|
-
this.db.prepare("INSERT OR IGNORE INTO taskboard_session_bindings (task_id, session_id, bound_at) VALUES (?, ?, ?)").run(taskId, sessionId, nowIso());
|
|
1157
|
-
}
|
|
1158
|
-
listTaskboardTaskSessions(taskId) {
|
|
1159
|
-
const rows = this.db.prepare("SELECT session_id FROM taskboard_session_bindings WHERE task_id = ? ORDER BY rowid ASC").all(taskId);
|
|
1160
|
-
return rows.map((row) => row.session_id);
|
|
1161
|
-
}
|
|
1162
|
-
unbindTaskboardTaskSession(taskId, sessionId) {
|
|
1163
|
-
this.db.prepare("DELETE FROM taskboard_session_bindings WHERE task_id = ? AND session_id = ?").run(taskId, sessionId);
|
|
1164
|
-
}
|
|
1165
1225
|
listGithubIssueBindings(owner, repo, issueNumber) {
|
|
1166
1226
|
const rows = this.db.prepare(`SELECT session_id, bound_at FROM github_issue_bindings
|
|
1167
1227
|
WHERE owner = ? AND repo = ? AND issue_number = ? ORDER BY bound_at ASC`).all(owner, repo, issueNumber);
|
|
@@ -1208,14 +1268,23 @@ export class WandStorage {
|
|
|
1208
1268
|
setAppSecret(value) {
|
|
1209
1269
|
this.setConfigValue("appSecret", value);
|
|
1210
1270
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1271
|
+
/** Encrypt an at-rest secret with the vault key; plaintext when no key exists. */
|
|
1272
|
+
encryptStoredSecret(value) {
|
|
1273
|
+
if (!value)
|
|
1213
1274
|
return null;
|
|
1214
1275
|
const secret = this.getAppSecret();
|
|
1215
|
-
return secret ? encryptVaultSecret(
|
|
1276
|
+
return secret ? encryptVaultSecret(value, secret) : value;
|
|
1216
1277
|
}
|
|
1217
|
-
decryptPasswordItem(item) {
|
|
1218
|
-
|
|
1278
|
+
decryptPasswordItem(item, raw) {
|
|
1279
|
+
const secret = this.getAppSecret();
|
|
1280
|
+
const notes = decryptVaultSecret(raw.notes ?? undefined, secret);
|
|
1281
|
+
const fieldsJson = decryptVaultSecret(raw.fields, secret) ?? raw.fields;
|
|
1282
|
+
return {
|
|
1283
|
+
...item,
|
|
1284
|
+
password: decryptVaultSecret(raw.password ?? undefined, secret),
|
|
1285
|
+
notes,
|
|
1286
|
+
fields: decodePasswordFields(fieldsJson),
|
|
1287
|
+
};
|
|
1219
1288
|
}
|
|
1220
1289
|
// ============ Browser Extension Password Vault Methods ============
|
|
1221
1290
|
ensureDefaultPasswordVault() {
|
|
@@ -1260,7 +1329,7 @@ export class WandStorage {
|
|
|
1260
1329
|
const limit = typeof filter.limit === "number" && Number.isFinite(filter.limit)
|
|
1261
1330
|
? Math.max(1, Math.min(200, Math.floor(filter.limit)))
|
|
1262
1331
|
: 100;
|
|
1263
|
-
return rows.map((row) => this.decryptPasswordItem(mapPasswordItemRow(row)))
|
|
1332
|
+
return rows.map((row) => this.decryptPasswordItem(mapPasswordItemRow(row), row))
|
|
1264
1333
|
.filter((item) => itemMatchesFilter(item, filter)).slice(0, limit);
|
|
1265
1334
|
}
|
|
1266
1335
|
getPasswordItem(id) {
|
|
@@ -1270,7 +1339,7 @@ export class WandStorage {
|
|
|
1270
1339
|
FROM password_items
|
|
1271
1340
|
WHERE id = ? AND archived = 0`)
|
|
1272
1341
|
.get(id);
|
|
1273
|
-
return row ? this.decryptPasswordItem(mapPasswordItemRow(row)) : null;
|
|
1342
|
+
return row ? this.decryptPasswordItem(mapPasswordItemRow(row), row) : null;
|
|
1274
1343
|
}
|
|
1275
1344
|
createPasswordItem(input) {
|
|
1276
1345
|
this.ensureDefaultPasswordVault();
|
|
@@ -1286,7 +1355,7 @@ export class WandStorage {
|
|
|
1286
1355
|
id, vault_id, type, title, username, password, urls, notes, fields, tags, favorite, archived,
|
|
1287
1356
|
created_at, updated_at, last_used_at, password_updated_at
|
|
1288
1357
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?)`)
|
|
1289
|
-
.run(id, vaultId, normalized.type, normalized.title, normalized.username ?? null, this.
|
|
1358
|
+
.run(id, vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredSecret(normalized.password), JSON.stringify(normalized.urls), this.encryptStoredSecret(normalized.notes), this.encryptStoredSecret(JSON.stringify(normalized.fields)) ?? "{}", JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, now, passwordUpdatedAt ?? null);
|
|
1290
1359
|
return this.getPasswordItem(id);
|
|
1291
1360
|
}
|
|
1292
1361
|
updatePasswordItem(id, input) {
|
|
@@ -1317,7 +1386,7 @@ export class WandStorage {
|
|
|
1317
1386
|
SET vault_id = ?, type = ?, title = ?, username = ?, password = ?, urls = ?, notes = ?,
|
|
1318
1387
|
fields = ?, tags = ?, favorite = ?, updated_at = ?, password_updated_at = ?
|
|
1319
1388
|
WHERE id = ? AND archived = 0`)
|
|
1320
|
-
.run(vaultId, normalized.type, normalized.title, normalized.username ?? null, this.
|
|
1389
|
+
.run(vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredSecret(normalized.password), JSON.stringify(normalized.urls), this.encryptStoredSecret(normalized.notes), this.encryptStoredSecret(JSON.stringify(normalized.fields)) ?? "{}", JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, passwordUpdatedAt, id);
|
|
1321
1390
|
return this.getPasswordItem(id);
|
|
1322
1391
|
}
|
|
1323
1392
|
touchPasswordItem(id) {
|
|
@@ -1382,10 +1451,7 @@ export class WandStorage {
|
|
|
1382
1451
|
return this.db.prepare("DELETE FROM connectors WHERE provider = ?").run(provider).changes > 0;
|
|
1383
1452
|
}
|
|
1384
1453
|
encryptConnectorToken(token) {
|
|
1385
|
-
|
|
1386
|
-
return "";
|
|
1387
|
-
const secret = this.getAppSecret();
|
|
1388
|
-
return secret ? encryptVaultSecret(token, secret) : token;
|
|
1454
|
+
return this.encryptStoredSecret(token) ?? "";
|
|
1389
1455
|
}
|
|
1390
1456
|
// ============ Auth Session Methods ============
|
|
1391
1457
|
saveAuthSession(token, expiresAt, principal = { kind: "browser-admin", scopes: ["admin"] }) {
|
|
@@ -1670,6 +1736,20 @@ function mapPasswordVaultRow(row) {
|
|
|
1670
1736
|
updatedAt: row.updated_at,
|
|
1671
1737
|
};
|
|
1672
1738
|
}
|
|
1739
|
+
/** Parse the (possibly encrypted) `fields` JSON column into a string map. */
|
|
1740
|
+
function decodePasswordFields(json) {
|
|
1741
|
+
if (!json)
|
|
1742
|
+
return {};
|
|
1743
|
+
try {
|
|
1744
|
+
const parsed = JSON.parse(json);
|
|
1745
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1746
|
+
return {};
|
|
1747
|
+
return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
|
|
1748
|
+
}
|
|
1749
|
+
catch {
|
|
1750
|
+
return {};
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1673
1753
|
function mapPasswordItemRow(row) {
|
|
1674
1754
|
return {
|
|
1675
1755
|
id: row.id,
|
|
@@ -1677,10 +1757,7 @@ function mapPasswordItemRow(row) {
|
|
|
1677
1757
|
type: row.type,
|
|
1678
1758
|
title: row.title,
|
|
1679
1759
|
username: row.username ?? undefined,
|
|
1680
|
-
password: row.password ?? undefined,
|
|
1681
1760
|
urls: safeJsonParse(row.urls)?.filter((item) => typeof item === "string") ?? [],
|
|
1682
|
-
notes: row.notes ?? undefined,
|
|
1683
|
-
fields: safeJsonParse(row.fields) ?? {},
|
|
1684
1761
|
tags: safeJsonParse(row.tags)?.filter((item) => typeof item === "string") ?? [],
|
|
1685
1762
|
favorite: Boolean(row.favorite),
|
|
1686
1763
|
createdAt: row.created_at,
|
|
@@ -60,6 +60,17 @@ export interface StructuredExecHost {
|
|
|
60
60
|
listRuns(): Promise<StructuredRunState[]>;
|
|
61
61
|
forgetRun(runId: string): void;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Stateful UTF-8 decoder for structured CLI stdout/stderr.
|
|
65
|
+
* Incomplete sequences are held across chunks. end() discards a trailing
|
|
66
|
+
* incomplete sequence instead of emitting U+FFFD replacement characters.
|
|
67
|
+
* Call this at the byte-stream seam, before line splitting or log append.
|
|
68
|
+
*/
|
|
69
|
+
export interface Utf8TextDecoder {
|
|
70
|
+
write(chunk: Buffer | string): string;
|
|
71
|
+
end(): string;
|
|
72
|
+
}
|
|
73
|
+
export declare function createUtf8TextDecoder(): Utf8TextDecoder;
|
|
63
74
|
/** Resolve a stable daemon-side key for a session's active structured run. */
|
|
64
75
|
export declare function structuredRunId(sessionId: string): string;
|
|
65
76
|
/** Legacy/local adapter and deterministic test adapter for the structured seam. */
|
|
@@ -67,8 +78,8 @@ export declare class InProcessStructuredExecHost implements StructuredExecHost {
|
|
|
67
78
|
readonly persistent = false;
|
|
68
79
|
private readonly processes;
|
|
69
80
|
spawnStructured(request: StructuredSpawnRequest): Promise<StructuredExecProcess>;
|
|
70
|
-
attachRun(): Promise<StructuredRunState | null>;
|
|
71
|
-
adoptRun(): Promise<StructuredExecProcess | null>;
|
|
81
|
+
attachRun(_runId: string): Promise<StructuredRunState | null>;
|
|
82
|
+
adoptRun(_runId: string): Promise<StructuredExecProcess | null>;
|
|
72
83
|
listRuns(): Promise<StructuredRunState[]>;
|
|
73
84
|
forgetRun(runId: string): void;
|
|
74
85
|
}
|