dsh-session-toolbox 0.1.8 → 0.1.10

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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。版本号从 `0.1` 起;DSH 宿主接口(`workspaceRegistry` / `sessionPersistence` / `webServer` 服务面与 `~/.dsh/sessions` 布局)在 0.x 期间按 `0.2.0-rc.2` 基线验证,每次官方升级会重验并在此记录。
6
6
 
7
+ ## 0.1.10
8
+
9
+ - **修复已发送的截图在切回会话后被重复插回输入框**。此前判断「已发送」靠附件栏从有到无的跳变:切回会话时附件栏本来就是空的,这个跳变永远不会发生,于是每次切回都把旧图重新粘贴一遍。
10
+ 现在记录**明确的「已发送」标记**(`sentAt`),恢复时跳过该标记的记录;触发点有三处——输入框挂载期间附件卡片消失、按回车发送(含快速切走)、以及恢复成功后标记 `seeded` 避免重复派发。新增回归测试:已发送的草稿在切回后不再被恢复。
11
+
12
+ ## 0.1.9
13
+
14
+ - **修复右键菜单里的迁移对话框没有项目选择**:0.1.8 只给设置页的迁移表单加了项目下拉,而会话行 `⋯` → 「切换到其它项目…」打开的是 `MoveDialog`,那里仍只能手填绝对路径。现在对话框与面板一致:显示当前项目、提供项目下拉(标注会话数与「当前项目」),并可继续手填路径。
15
+
7
16
  ## 0.1.8
8
17
 
9
18
  - **回收站改名并明确语义**:会话的「删除」在 DSH 里本质就是官方归档,插件只是额外记录了入池时的项目归属、列表位置与置顶状态,因此可以还原到原位。界面统一改为「归档池」:菜单项「归档到归档池」、面板标题「归档池」、列名「入池时间」,并在面板里说明它同时出现在官方归档视图中。
package/cordis.patch.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  - insert:
2
- - id: session-toolbox-v1-0.1.8
3
- name: ./lib/host-v0.1.8.js
2
+ - id: session-toolbox-v1-0.1.10
3
+ name: ./lib/host-v0.1.10.js
package/lib/client.js CHANGED
@@ -39,7 +39,7 @@ var import_react = require("react");
39
39
  // package.json
40
40
  var package_default = {
41
41
  name: "dsh-session-toolbox",
42
- version: "0.1.8",
42
+ version: "0.1.10",
43
43
  repository: {
44
44
  type: "git",
45
45
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
@@ -240,7 +240,7 @@ function createVault(options = {}) {
240
240
  const probe = await fetchImpl(endpoint, { cache: "no-store" });
241
241
  hostKey = (await probe.json()).key ?? "";
242
242
  }
243
- const records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name: name2, size, hash, seeded, createdAt }) => ({ key, sessionId, attachmentId, name: name2, size, hash, seeded, createdAt }));
243
+ const records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name: name2, size, hash, seeded, createdAt, sentAt }) => ({ key, sessionId, attachmentId, name: name2, size, hash, seeded, createdAt, sentAt: sentAt ?? 0 }));
244
244
  await fetchImpl(endpoint, {
245
245
  method: "POST",
246
246
  cache: "no-store",
@@ -288,6 +288,7 @@ function createVault(options = {}) {
288
288
  size: item.size,
289
289
  hash,
290
290
  createdAt: now(),
291
+ sentAt: 0,
291
292
  seeded: false
292
293
  };
293
294
  record.blob = item;
@@ -321,7 +322,7 @@ function createVault(options = {}) {
321
322
  const rail = attachmentRail(editor);
322
323
  if (rail === void 0) return false;
323
324
  if (rail.length > 0) return true;
324
- const pending = (await allRecords()).filter((record) => record.sessionId === sessionId);
325
+ const pending = (await allRecords()).filter((record) => record.sessionId === sessionId && !record.sentAt);
325
326
  if (pending.length === 0) return true;
326
327
  const files = [];
327
328
  for (const record of pending) {
@@ -349,9 +350,24 @@ function createVault(options = {}) {
349
350
  const accepted = event.defaultPrevented === true;
350
351
  if (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);
351
352
  else log("warn", "synthetic paste was not accepted; vault stays idle for this session");
353
+ if (accepted) for (const record of pending) await putRecord({ ...record, seeded: true });
352
354
  changed();
353
355
  return accepted;
354
356
  }
357
+ async function retireSession(sessionId, reason) {
358
+ let retired = 0;
359
+ for (const record of await allRecords()) {
360
+ if (record.sessionId !== sessionId || record.sentAt) continue;
361
+ await putRecord({ ...record, sentAt: now() });
362
+ retired += 1;
363
+ }
364
+ if (retired > 0) {
365
+ notice(reason === "sent" ? `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u968F\u6D88\u606F\u53D1\u9001\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D` : `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u6E05\u7A7A\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`);
366
+ void syncHost();
367
+ }
368
+ changed();
369
+ return retired;
370
+ }
355
371
  function watchSession(sessionId, editor) {
356
372
  let rail = attachmentRail(editor)?.length ?? 0;
357
373
  const scope = editor.closest?.("[data-conversation-content]") ?? editor;
@@ -359,9 +375,7 @@ function createVault(options = {}) {
359
375
  const next = attachmentRail(editor)?.length ?? rail;
360
376
  if (next === rail) return;
361
377
  if (rail > 0 && next === 0 && scope?.isConnected === true) {
362
- for (const record of await allRecords()) if (record.sessionId === sessionId) await dropRecord(record.key);
363
- notice("\u672C\u4F1A\u8BDD\u8349\u7A3F\u56FE\u7247\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u540C\u6B65\u79FB\u9664");
364
- void syncHost();
378
+ await retireSession(sessionId, "sent");
365
379
  }
366
380
  rail = next;
367
381
  changed();
@@ -370,7 +384,17 @@ function createVault(options = {}) {
370
384
  };
371
385
  const observer = new MutationObserver(() => void check());
372
386
  observer.observe(scope, { subtree: true, childList: true });
373
- return () => observer.disconnect();
387
+ const onKeyDown = (event) => {
388
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
389
+ const target = event.target;
390
+ if (target?.closest?.("[data-conversation-session]") === null || target?.closest?.("[data-conversation-session]") === void 0) return;
391
+ if ((attachmentRail(editor)?.length ?? 0) > 0) void retireSession(sessionId, "sent");
392
+ };
393
+ document.addEventListener("keydown", onKeyDown, true);
394
+ return () => {
395
+ observer.disconnect();
396
+ document.removeEventListener("keydown", onKeyDown, true);
397
+ };
374
398
  }
375
399
  function watchMounts() {
376
400
  let boundSession = "", dispose = () => {
@@ -452,6 +476,8 @@ function createVault(options = {}) {
452
476
  },
453
477
  sweep,
454
478
  seedSession,
479
+ /** Retire a session's unsent drafts (called when its images were sent). */
480
+ retire: async (sessionId = currentSession(), reason = "sent") => retireSession(sessionId, reason),
455
481
  intake,
456
482
  watchMounts
457
483
  };
@@ -826,6 +852,8 @@ function MoveDialog({ sessionId, close }) {
826
852
  const [createTarget, setCreateTarget] = (0, import_react.useState)(false);
827
853
  const [error, setError] = (0, import_react.useState)("");
828
854
  const [busy, setBusy2] = (0, import_react.useState)(false);
855
+ const [projects, setProjects] = (0, import_react.useState)([]);
856
+ const [fromProject, setFromProject] = (0, import_react.useState)(null);
829
857
  (0, import_react.useEffect)(() => {
830
858
  const dialog = ref.current;
831
859
  dialog?.showModal();
@@ -836,6 +864,21 @@ function MoveDialog({ sessionId, close }) {
836
864
  }
837
865
  };
838
866
  }, []);
867
+ (0, import_react.useEffect)(() => {
868
+ let alive = true;
869
+ void loadState().then((next) => {
870
+ if (alive) setProjects(next?.projects ?? []);
871
+ }).catch(() => {
872
+ });
873
+ void callHost({ type: "project.of", sessionId }).then((result) => {
874
+ if (alive) setFromProject(result?.project ?? null);
875
+ }).catch(() => {
876
+ if (alive) setFromProject(null);
877
+ });
878
+ return () => {
879
+ alive = false;
880
+ };
881
+ }, [sessionId]);
839
882
  const submit = async () => {
840
883
  setBusy2(true);
841
884
  setError("");
@@ -860,7 +903,33 @@ function MoveDialog({ sessionId, close }) {
860
903
  ] }),
861
904
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body", children: [
862
905
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "muted mono", children: sessionId }),
863
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { autoFocus: true, className: "grow mono", placeholder: "\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84", value: toCwd, onChange: (e) => setToCwd(e.target.value) }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "muted", children: [
907
+ "\u5F53\u524D\u9879\u76EE\uFF1A",
908
+ fromProject === null ? "\uFF08\u672A\u627E\u5230\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09" : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: fromProject.title }),
910
+ " ",
911
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mono", children: fromProject.path })
912
+ ] })
913
+ ] }),
914
+ projects.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
915
+ "select",
916
+ {
917
+ className: "grow",
918
+ value: projects.some((p) => p.path === toCwd) ? toCwd : "",
919
+ onChange: (e) => setToCwd(e.target.value),
920
+ children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026" }),
922
+ projects.map((project) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: project.path, disabled: fromProject?.path === project.path, children: [
923
+ project.title,
924
+ "\uFF08",
925
+ project.sessions,
926
+ " \u4E2A\u4F1A\u8BDD\uFF09",
927
+ fromProject?.path === project.path ? " \u2014 \u5F53\u524D\u9879\u76EE" : ""
928
+ ] }, project.id))
929
+ ]
930
+ }
931
+ ),
932
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "grow mono", placeholder: "\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84", value: toCwd, onChange: (e) => setToCwd(e.target.value) }),
864
933
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "row", children: [
865
934
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: createTarget, onChange: (e) => setCreateTarget(e.target.checked) }),
866
935
  " \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA"
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client.jsx", "../package.json", "../src/draft-vault.js"],
4
- "sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u5F52\u6863\u5230\u5F52\u6863\u6C60' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Archive' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u5F52\u6863\u6C60 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\t// Known projects, so the target can be picked by name instead of typed as a path.\n\tconst projects = state?.projects ?? [];\n\t// Which project the typed session currently belongs to (computed host-side).\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => {\n\t\tif (!sessionId) { setFromProject(null); return; }\n\t\tlet alive = true;\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\tonChange={e => { setToCwd(e.target.value); setPlan(null); }}>\n\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t)}\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t{sessionId && (\n\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\u8BE5\u4F1A\u8BDD\uFF0C\u6216\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u5F52\u6863\u6C60</h3>\n\t\t\t<p className=\"muted\">\n\t\t\t\tDSH \u6CA1\u6709\u72EC\u7ACB\u7684\u300C\u5220\u9664\u4F1A\u8BDD\u300D\u2014\u2014\u4F1A\u8BDD\u53EA\u80FD<b>\u5F52\u6863</b>\u3002\u8FD9\u91CC\u5C31\u662F\u5B98\u65B9\u7684\u5F52\u6863\u6C60\uFF1A\u5165\u6C60\u7684\u4F1A\u8BDD\u4ECD\u7136\u662F\u5B98\u65B9\u5F52\u6863\u72B6\u6001\uFF08\u4FA7\u680F\u7684\u5F52\u6863\u89C6\u56FE\u91CC\u540C\u6837\u80FD\u770B\u5230\uFF09\uFF0C\u672C\u63D2\u4EF6\u989D\u5916\u8BB0\u5F55\u4E86\u5B83\u5165\u6C60\u65F6\u7684<b>\u9879\u76EE\u5F52\u5C5E\u3001\u5217\u8868\u4F4D\u7F6E\u4E0E\u7F6E\u9876\u72B6\u6001</b>\uFF0C\u56E0\u6B64\u53EF\u4EE5\u4E00\u952E\u8FD8\u539F\u5230\u539F\u4F4D\u3002\n\t\t\t\t\u5F52\u6863\u9879\u76EE\u5219\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u53EA\u79FB\u9664\u9879\u76EE\u6CE8\u518C\u8868\u9879\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5165\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002\n\t\t\t</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u6C60\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\u5230\u539F\u4F4D').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u79FB\u51FA{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u5F52\u6863\uFF1F\uFF08\u4F1A\u8BDD\u5C06\u8FDB\u5165\u5F52\u6863\u6C60\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u5F52\u6863\u5230\u5F52\u6863\u6C60'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry { await callHost({ type: 'move', sessionId, toCwd, createTarget }); close(); }\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<input autoFocus className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.8\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\tfunction attachmentRail(editor) {\n\t\t// Official structure: attachment cards are imgs with blob: srcs sitting\n\t\t// above the contenteditable root inside the composer bar subtree.\n\t\tconst bar = editor?.closest?.('[data-composer-bar], [data-conversation-region=\"composer\"]') ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (bar?.querySelectorAll === undefined) return undefined;\n\t\treturn [...bar.querySelectorAll('img')].filter(img => img.getAttribute?.('src')?.startsWith('blob:'));\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Sent or manually removed while mounted \u2192 backups retire (\u00A7 FR3).\n\t\t\t\tfor (const record of await allRecords()) if (record.sessionId === sessionId) await dropRecord(record.key);\n\t\t\t\tnotice('\u672C\u4F1A\u8BDD\u8349\u7A3F\u56FE\u7247\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u540C\u6B65\u79FB\u9664');\n\t\t\t\tvoid syncHost();\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\t\treturn () => observer.disconnect();\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u5F52\u6863\u5230\u5F52\u6863\u6C60' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Archive' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u5F52\u6863\u6C60 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\t// Known projects, so the target can be picked by name instead of typed as a path.\n\tconst projects = state?.projects ?? [];\n\t// Which project the typed session currently belongs to (computed host-side).\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => {\n\t\tif (!sessionId) { setFromProject(null); return; }\n\t\tlet alive = true;\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\tonChange={e => { setToCwd(e.target.value); setPlan(null); }}>\n\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t)}\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t{sessionId && (\n\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\u8BE5\u4F1A\u8BDD\uFF0C\u6216\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u5F52\u6863\u6C60</h3>\n\t\t\t<p className=\"muted\">\n\t\t\t\tDSH \u6CA1\u6709\u72EC\u7ACB\u7684\u300C\u5220\u9664\u4F1A\u8BDD\u300D\u2014\u2014\u4F1A\u8BDD\u53EA\u80FD<b>\u5F52\u6863</b>\u3002\u8FD9\u91CC\u5C31\u662F\u5B98\u65B9\u7684\u5F52\u6863\u6C60\uFF1A\u5165\u6C60\u7684\u4F1A\u8BDD\u4ECD\u7136\u662F\u5B98\u65B9\u5F52\u6863\u72B6\u6001\uFF08\u4FA7\u680F\u7684\u5F52\u6863\u89C6\u56FE\u91CC\u540C\u6837\u80FD\u770B\u5230\uFF09\uFF0C\u672C\u63D2\u4EF6\u989D\u5916\u8BB0\u5F55\u4E86\u5B83\u5165\u6C60\u65F6\u7684<b>\u9879\u76EE\u5F52\u5C5E\u3001\u5217\u8868\u4F4D\u7F6E\u4E0E\u7F6E\u9876\u72B6\u6001</b>\uFF0C\u56E0\u6B64\u53EF\u4EE5\u4E00\u952E\u8FD8\u539F\u5230\u539F\u4F4D\u3002\n\t\t\t\t\u5F52\u6863\u9879\u76EE\u5219\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u53EA\u79FB\u9664\u9879\u76EE\u6CE8\u518C\u8868\u9879\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5165\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002\n\t\t\t</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u6C60\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\u5230\u539F\u4F4D').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u79FB\u51FA{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u5F52\u6863\uFF1F\uFF08\u4F1A\u8BDD\u5C06\u8FDB\u5165\u5F52\u6863\u6C60\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u5F52\u6863\u5230\u5F52\u6863\u6C60'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\t// Same project picker as the panel: the dialog is what the context menu opens,\n\t// so it must not fall back to typing a path by hand.\n\tconst [projects, setProjects] = useState([]);\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tuseEffect(() => {\n\t\tlet alive = true;\n\t\tvoid loadState().then(next => { if (alive) setProjects(next?.projects ?? []); }).catch(() => {});\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry { await callHost({ type: 'move', sessionId, toCwd, createTarget }); close(); }\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t\t</p>\n\t\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\t\tonChange={e => setToCwd(e.target.value)}>\n\t\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t)}\n\t\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.10\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt: sentAt ?? 0 }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), sentAt: 0, seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\tfunction attachmentRail(editor) {\n\t\t// Official structure: attachment cards are imgs with blob: srcs sitting\n\t\t// above the contenteditable root inside the composer bar subtree.\n\t\tconst bar = editor?.closest?.('[data-composer-bar], [data-conversation-region=\"composer\"]') ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (bar?.querySelectorAll === undefined) return undefined;\n\t\treturn [...bar.querySelectorAll('img')].filter(img => img.getAttribute?.('src')?.startsWith('blob:'));\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\t// Never resurrect images that already travelled with a sent message: that is\n\t\t// what duplicated screenshots whenever a session was switched back to.\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId && !record.sentAt);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\t// A restore hands the images back to the official composer; they now live\n\t\t// there (and will be retired when sent), so keep a single copy in the vault.\n\t\tif (accepted) for (const record of pending) await putRecord({ ...record, seeded: true });\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\t/**\n\t * Mark a session's backed-up drafts as already sent, so they are never\n\t * replayed into the composer again.\n\t *\n\t * Inferring \"sent\" from the attachment rail reaching zero used to miss the\n\t * common case: the rail is already empty by the time the session mounts\n\t * again, so the transition never happened while we were watching and the\n\t * old images were restored on every switch. Observing the composer while it\n\t * is mounted (below) plus this explicit retirement keeps the vault honest.\n\t */\n\tasync function retireSession(sessionId, reason) {\n\t\tlet retired = 0;\n\t\tfor (const record of await allRecords()) {\n\t\t\tif (record.sessionId !== sessionId || record.sentAt) continue;\n\t\t\tawait putRecord({ ...record, sentAt: now() });\n\t\t\tretired += 1;\n\t\t}\n\t\tif (retired > 0) {\n\t\t\tnotice(reason === 'sent'\n\t\t\t\t? `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u968F\u6D88\u606F\u53D1\u9001\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`\n\t\t\t\t: `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u6E05\u7A7A\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`);\n\t\t\tvoid syncHost();\n\t\t}\n\t\tchanged();\n\t\treturn retired;\n\t}\n\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Cards disappeared while the composer was mounted: the message was\n\t\t\t\t// sent (or the user removed them), so retire the backups for good.\n\t\t\t\tawait retireSession(sessionId, 'sent');\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\n\t\t// Belt and braces: listen for the send gesture itself, so a fast unmount\n\t\t// (enter then immediately switch away) still retires the drafts.\n\t\tconst onKeyDown = event => {\n\t\t\tif (event.key !== 'Enter' || event.shiftKey || event.isComposing) return;\n\t\t\tconst target = event.target;\n\t\t\tif (target?.closest?.('[data-conversation-session]') === null || target?.closest?.('[data-conversation-session]') === undefined) return;\n\t\t\tif ((attachmentRail(editor)?.length ?? 0) > 0) void retireSession(sessionId, 'sent');\n\t\t};\n\t\tdocument.addEventListener('keydown', onKeyDown, true);\n\t\treturn () => { observer.disconnect(); document.removeEventListener('keydown', onKeyDown, true); };\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\t/** Retire a session's unsent drafts (called when its images were sent). */\n\t\tretire: async (sessionId = currentSession(), reason = 'sent') => retireSession(sessionId, reason),\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["store", "name", "setBusy"]
7
7
  }
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.8",
23
+ version: "0.1.10",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
package/lib/host.js CHANGED
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.8",
23
+ version: "0.1.10",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
package/lib/index.js CHANGED
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.8",
23
+ version: "0.1.10",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
package/lib/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.mjs", "../package.json"],
4
- "sourcesContent": ["/**\n * dsh-session-toolbox host plugin: draft vault manifest mirror (\u00A74 FR1), session\n * move (\u00A710) and trash bin (\u00A711) for DeepSeek Harness. Every official surface\n * is reached through `ctx.get(...)` service names probed at runtime; a missing\n * capability degrades that feature to a visible \"unavailable\" state \u2014 never a\n * crash, never a patch (NFR3 / \u00A710 MN1). The plugin declares no hard `inject`\n * dependencies so it can never block host boot.\n */\nimport { mkdir, realpath, stat } from 'node:fs/promises';\nimport { appendFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport z from '@deepseek-ai/schemastery';\nimport { resolveDshHome } from '@deepseek-ai/dsh-home-paths';\nimport { guardJournal, guardLedger, guardManifest, guardSettings, JsonDocument } from './store.mjs';\nimport { MoveEngine } from './move.mjs';\nimport { TrashEngine } from './trash.mjs';\nimport { apiHandler, dispatch } from './api.mjs';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\n\nexport const name = 'dsh-session-toolbox';\nexport const inject = [];\n// schemastery has no `.optional()`; empty defaults mean \"unset\" (apply() treats '' as absent).\nexport const Config = z.object({\n\tconfigFile: z.string().description('Directory for session-toolbox data (defaults to <DSH_HOME>/session-toolbox)').default(''),\n\tdshHome: z.string().description('Override the DSH home whose sessions/ storage the tool manages (advanced)').default(''),\n});\n\nexport function defaultKitRoot() {\n\treturn join(resolveDshHome(), 'session-toolbox');\n}\n\nexport async function apply(ctx, options = {}) {\n\ttry {\n\t\treturn await applyInner(ctx, options);\n\t} catch (error) {\n\t\t// A failing third-party plugin must never take the whole app down.\n\t\t// Record the real stack so the failure is diagnosable, then degrade.\n\t\trecordStartupFailure(error);\n\t\tconst hint = /Cannot find package|ERR_MODULE_NOT_FOUND/.test(String(error?.message ?? error))\n\t\t\t? '\uFF08\u4F9D\u8D56\u7F3A\u5931\uFF1A\u8BF7\u4ECE\u63D2\u4EF6\u9875\u5378\u8F7D\u540E\u91CD\u65B0\u5B89\u88C5\uFF0C\u8BA9\u5305\u7BA1\u7406\u5668\u91CD\u65B0\u62C9\u53D6 peer \u4F9D\u8D56\uFF09'\n\t\t\t: '';\n\t\tctx.logger?.warn?.(`session-toolbox: disabled after startup error${hint} \u2014 ${error?.message ?? error}`);\n\t}\n}\n\n/** Absolute path of the startup-failure log, next to the plugin's other state. */\nfunction startupFailureLog() {\n\ttry {\n\t\treturn join(resolve(resolveDshHome()), 'session-toolbox', 'startup-error.log');\n\t} catch { return ''; }\n}\n\n/** Append a startup failure (with stack) so a failed activation stays diagnosable. */\nfunction recordStartupFailure(error) {\n\tconst file = startupFailureLog();\n\tif (file === '') return;\n\ttry {\n\t\tconst detail = `${new Date().toISOString()}\\n${error?.stack ?? String(error)}\\n\\n`;\n\t\tappendFileSync(file, detail, { mode: 0o600 });\n\t} catch { /* diagnostics must never throw */ }\n}\n\nasync function applyInner(ctx, options = {}) {\n\tconst dshHome = resolve(options.dshHome || resolveDshHome());\n\tconst root = resolve(options.configFile || join(dshHome, 'session-toolbox'));\n\tawait mkdir(root, { recursive: true, mode: 0o700 });\n\tconst capabilities = {\n\t\tworkspaceRegistry: ctx.get('workspaceRegistry') !== undefined,\n\t\tsessions: ctx.get('sessions') !== undefined,\n\t\tsessionPersistence: ctx.get('sessionPersistence') !== undefined,\n\t\twebServer: false, // flips true when the optional webServer inject fires\n\t};\n\tconst settingsDoc = new JsonDocument(root, 'session-toolbox.json', { version: 1 }, guardSettings);\n\tconst journalDoc = new JsonDocument(root, 'journal.json', { version: 1, active: null }, guardJournal);\n\tconst ledgerDoc = new JsonDocument(root, 'trash.json', { version: 1, items: [] }, guardLedger);\n\tconst manifestDoc = new JsonDocument(root, 'vault-manifest.json', { version: 1, records: [] }, guardManifest);\n\n\tlet settingsCache = await settingsDoc.read().catch(() => ({ trash: {}, draftVault: {} }));\n\tlet journalCache = null;\n\ttry { journalCache = (await journalDoc.read()).active; }\n\tcatch (error) { ctx.logger?.warn?.(`session-toolbox: journal unreadable \u2014 ${error.message}`); }\n\tif (journalCache !== null) ctx.logger?.warn?.(`session-toolbox: unresolved move journal for ${journalCache.sessionId}; complete or roll it back from the panel or CLI`);\n\n\tconst move = new MoveEngine({\n\t\troot,\n\t\tsessionsRoot: join(dshHome, 'sessions'),\n\t\tprojcacheRoot: join(dshHome, 'storages', 'session_projcache', 'sessions'),\n\t\tjournal: journalDoc,\n\t\tcapabilities: hostCapabilities(ctx),\n\t});\n\tconst trash = new TrashEngine({\n\t\tadapter: registryAdapter(ctx),\n\t\tsessions: () => (ctx.get('sessions')?.list() ?? []).map(session => session.header?.id).filter(id => typeof id === 'string'),\n\t\tsessionsRoot: join(dshHome, 'sessions'),\n\t\tprojcacheRoot: join(dshHome, 'storages', 'session_projcache', 'sessions'),\n\t\tkitRoot: root,\n\t\tledger: ledgerDoc,\n\t\tvault: { dropSession: sessionId => manifestDoc.update(current => ({ ...current, records: current.records.filter(record => record.sessionId !== sessionId) })) },\n\t\tjournal: () => journalCache,\n\t\tsettings: () => settingsCache.trash ?? {},\n\t});\n\n\t/** All known projects (id/path/title/session count) for the move picker. */\n\tconst listProjects = ctx => {\n\t\tconst registry = ctx.get('workspaceRegistry');\n\t\tif (registry === undefined) return [];\n\t\treturn registry.list().map(row => ({\n\t\t\tid: row.id,\n\t\t\tpath: row.path,\n\t\t\ttitle: row.title ?? row.path,\n\t\t\tsessions: (row.sessionIds ?? []).length,\n\t\t})).sort((a, b) => a.title.localeCompare(b.title));\n\t};\n\n\tconst kit = {\n\t\tsettings: settingsDoc,\n\t\tmanifest: manifestDoc,\n\t\tjournal: journalDoc,\n\t\tmove,\n\t\ttrash,\n\t\tupdateSettings: async patch => {\n\t\t\tconst next = await settingsDoc.update(current => deepMerge(current, patch));\n\t\t\tsettingsCache = next;\n\t\t\treturn next;\n\t\t},\n\t\tvaultSync: async records => {\n\t\t\tconst manifest = await manifestDoc.update(() => ({\n\t\t\t\tversion: 1,\n\t\t\t\trecords: (Array.isArray(records) ? records : []).filter(record => typeof record.sessionId === 'string' && typeof record.attachmentId === 'string')\n\t\t\t\t\t.slice(-2000)\n\t\t\t\t\t.map(record => ({ sessionId: record.sessionId, attachmentId: record.attachmentId,\n\t\t\t\t\t\tname: String(record.name ?? ''), size: Number(record.size) || 0, hash: String(record.hash ?? ''),\n\t\t\t\t\t\tcreatedAt: Number(record.createdAt) || Date.now() })),\n\t\t\t}));\n\t\t\treturn manifest;\n\t\t},\n\t\tstate: async () => {\n\t\t\tconst settings = await settingsDoc.read();\n\t\t\tsettingsCache = settings;\n\t\t\tconst journal = (await journalDoc.read().catch(() => ({ active: journalCache }))).active;\n\t\t\tjournalCache = journal;\n\t\t\tconst manifest = await manifestDoc.read();\n\t\t\treturn {\n\t\t\t\tpluginVersion,\n\t\t\t\tcapabilities,\n\t\t\t\tsettings,\n\t\t\t\tjournal,\n\t\t\t\tprojects: listProjects(ctx),\n\t\t\t\ttrash: await trash.items(),\n\t\t\t\t// Bytes live in browser IndexedDB; the manifest mirrors their metadata only.\n\t\t\t\tvault: { records: manifest.records.length, bytes: manifest.records.reduce((total, record) => total + (record.size ?? 0), 0) },\n\t\t\t\trecovery: journal === null ? null : `\u672A\u5B8C\u6210\u8FC1\u79FB\uFF1A${journal.sessionId} \u2192 ${journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 ${journal.done.join('/') || '\u65E0'}\uFF09`,\n\t\t\t};\n\t\t},\n\t\t/** The project a session currently belongs to (null when unknown). */\n\t\tprojectOf: async sessionId => {\n\t\t\tif (sessionId === '') return null;\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry === undefined) return null;\n\t\t\tfor (const row of registry.list()) {\n\t\t\t\tif ((row.sessionIds ?? []).includes(sessionId)) {\n\t\t\t\t\treturn { id: row.id, path: row.path, title: row.title ?? row.path };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\taction: input => dispatch(kit, input),\n\t};\n\n\tctx.inject?.(['webServer'], webCtx => {\n\t\tcapabilities.webServer = true;\n\t\twebCtx.effect(() => webCtx.webServer.register({ kind: 'exact', path: '/.well-known/dsh-session-toolbox', handler: apiHandler(kit) }));\n\t});\n\n\t// Expose the kit in-process (cordis plugins must return nothing; the service\n\t// is the surface other plugins or future official hooks can reach).\n\tctx.provide('sessionToolbox', kit);\n\n\tctx.effect(() => () => {});\n\tctx.logger?.info?.('dsh-session-toolbox ready', JSON.stringify({ version: pluginVersion, capabilities }));\n}\n\n/**\n * Host capabilities for the move engine (\u00A710 MR5). Services are fetched lazily\n * through `ctx.get`; a missing one raises a stable error the UI can show.\n */\nexport function hostCapabilities(ctx) {\n\treturn {\n\t\tsessionLive: sessionId => {\n\t\t\tconst sessions = ctx.get('sessions');\n\t\t\treturn sessions ? sessions.list().some(session => session.header?.id === sessionId) : false;\n\t\t},\n\t\tlistHeaders: async () => {\n\t\t\tconst persistence = ctx.get('sessionPersistence');\n\t\t\tif (persistence?.list !== undefined) return (await persistence.list()).map(snapshot => snapshot.header);\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry?.listStoredHeaders !== undefined) return await registry.listStoredHeaders();\n\t\t\tthrow new Error('no session-persistence service in this DSH build');\n\t\t},\n\t\t/** Drop stale header entries so `attachSession` validates the NEW on-disk cwd (\u00A710 MR4). */\n\t\trefreshHeaderCache: async () => {\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (typeof registry?.replaceHeaderIndex !== 'function') return;\n\t\t\tawait registry.replaceHeaderIndex(await registry.listStoredHeaders());\n\t\t},\n\t\t/** Ledger step (\u00A710 MR2 step 2): source detach \u2192 target create \u2192 attach. */\n\t\tmoveLedger: async (sessionId, fromCwd, toCwd) => {\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry === undefined) throw new Error('workspaceRegistry unavailable in this DSH build');\n\t\t\tconst canonicalFrom = fromCwd === undefined ? undefined : await realpath(fromCwd).catch(() => fromCwd);\n\t\t\tconst source = canonicalFrom === undefined ? undefined : await registry.resolveByPath(canonicalFrom);\n\t\t\tif (source !== undefined) await source.detachSession(sessionId);\n\t\t\tlet target = await registry.resolveByPath(toCwd);\n\t\t\tif (target === undefined) {\n\t\t\t\tif (!(await stat(toCwd).then(s => s.isDirectory(), () => false))) throw new Error(`target directory ${toCwd} does not exist`);\n\t\t\t\ttarget = await registry.create(toCwd);\n\t\t\t}\n\t\t\tawait target.attachSession(sessionId);\n\t\t},\n\t\tresyncIndex: async () => {\n\t\t\t// \u00A710.8-3: the sqlite query index reconciles against persistence lazily on\n\t\t\t// every query path; there is nothing explicit to call in this build.\n\t\t\treturn ctx.get('sessionQuery') !== undefined ? 'lazy' : 'unavailable';\n\t\t},\n\t};\n}\n\n/**\n * The narrow adapter the trash engine runs every official effect through\n * (\u00A711 TB5). Workspace deletion keeps session logs (\u00A711.1-2); bootstrap only\n * re-claims non-archived unaccounted sessions \u2014 which is why trashing a project\n * archives its sessions first, and restoring re-registers + attaches +\n * unarchives in order.\n */\nexport function registryAdapter(ctx) {\n\tconst registry = () => {\n\t\tconst value = ctx.get('workspaceRegistry');\n\t\tif (value === undefined) throw Object.assign(new Error('workspaceRegistry unavailable'), { code: 'no-capability' });\n\t\treturn value;\n\t};\n\treturn {\n\t\tsessionKnown: async sessionId => {\n\t\t\tconst rows = registry();\n\t\t\treturn typeof rows.sessionKnown === 'function' ? await rows.sessionKnown(sessionId) : false;\n\t\t},\n\t\tarchivedSet: async () => new Set(registry().archivedSessionIds ?? []),\n\t\tpinnedSet: async () => new Set(registry().pinnedSessionIds ?? []),\n\t\tlocateSession: async sessionId => {\n\t\t\tfor (const entity of registry().list()) {\n\t\t\t\tconst ids = entity.sessionIds ?? [];\n\t\t\t\tif (!ids.includes(sessionId)) continue;\n\t\t\t\tconst at = ids.indexOf(sessionId);\n\t\t\t\treturn { workspaceId: entity.id, beforeId: at + 1 < ids.length ? ids[at + 1] : null, title: null };\n\t\t\t}\n\t\t\treturn { workspaceId: null, beforeId: null, title: null };\n\t\t},\n\t\tworkspaceSnapshot: async workspaceId => {\n\t\t\tconst rows = registry();\n\t\t\tconst entity = rows.get(workspaceId);\n\t\t\tif (entity === undefined) return undefined;\n\t\t\tconst ids = entity.sessionIds ?? [];\n\t\t\tconst archived = new Set(rows.archivedSessionIds ?? []);\n\t\t\tconst pinned = new Set(rows.pinnedSessionIds ?? []);\n\t\t\tconst order = rows.list().map(row => row.id);\n\t\t\tconst at = order.indexOf(entity.id);\n\t\t\treturn {\n\t\t\t\tworkspaceId: entity.id, path: entity.path, title: entity.title,\n\t\t\t\tbeforeId: at > 0 ? order[at - 1] : null,\n\t\t\t\tsessions: ids.map((sessionId, index) => ({ sessionId,\n\t\t\t\t\tpreviouslyArchived: archived.has(sessionId),\n\t\t\t\t\tpreviouslyPinned: pinned.has(sessionId),\n\t\t\t\t\tbeforeId: index + 1 < ids.length ? ids[index + 1] : null })),\n\t\t\t};\n\t\t},\n\t\tarchiveSession: (sessionId, options) => registry().archiveSession(sessionId, options),\n\t\tunarchiveSession: sessionId => registry().unarchiveSession(sessionId),\n\t\tpinSession: sessionId => registry().pinSession(sessionId),\n\t\tunpinSession: sessionId => registry().unpinSession(sessionId),\n\t\tdeleteWorkspace: workspaceId => registry().delete(workspaceId),\n\t\trepairSlot: async (sessionId, workspaceId, beforeId) => {\n\t\t\tconst entity = registry().get(workspaceId);\n\t\t\tif (entity === undefined) return;\n\t\t\tif (!entity.sessionIds?.includes(sessionId)) await entity.attachSession(sessionId);\n\t\t\tif (beforeId !== null && entity.sessionIds?.includes(beforeId)) await entity.insertSessionBefore(sessionId, beforeId);\n\t\t},\n\t\trestoreWorkspace: async snapshot => {\n\t\t\tconst rows = registry();\n\t\t\tlet entity = rows.list().find(row => row.path === snapshot.path);\n\t\t\tif (entity === undefined) {\n\t\t\t\tawait mkdir(snapshot.path, { recursive: true }).catch(() => {});\n\t\t\t\tentity = await rows.create(snapshot.path, snapshot.title);\n\t\t\t}\n\t\t\tfor (const row of [...snapshot.sessions].reverse()) await entity.attachSession(row.sessionId).catch(() => {});\n\t\t\tif (snapshot.beforeId !== null && entity.id !== snapshot.beforeId) await rows.insertBefore(entity.id, snapshot.beforeId).catch(() => {});\n\t\t\treturn entity;\n\t\t},\n\t\tpurgeSession: async () => ctx.get('sessionPersistence')?.delete === undefined ? 'unavailable' : 'present',\n\t};\n}\n\nfunction deepMerge(base, patch) {\n\tconst next = { ...base };\n\tfor (const [key, value] of Object.entries(patch ?? {})) {\n\t\tnext[key] = value !== null && typeof value === 'object' && !Array.isArray(value) ? deepMerge(base[key] ?? {}, value) : value;\n\t}\n\treturn next;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.8\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n"],
4
+ "sourcesContent": ["/**\n * dsh-session-toolbox host plugin: draft vault manifest mirror (\u00A74 FR1), session\n * move (\u00A710) and trash bin (\u00A711) for DeepSeek Harness. Every official surface\n * is reached through `ctx.get(...)` service names probed at runtime; a missing\n * capability degrades that feature to a visible \"unavailable\" state \u2014 never a\n * crash, never a patch (NFR3 / \u00A710 MN1). The plugin declares no hard `inject`\n * dependencies so it can never block host boot.\n */\nimport { mkdir, realpath, stat } from 'node:fs/promises';\nimport { appendFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport z from '@deepseek-ai/schemastery';\nimport { resolveDshHome } from '@deepseek-ai/dsh-home-paths';\nimport { guardJournal, guardLedger, guardManifest, guardSettings, JsonDocument } from './store.mjs';\nimport { MoveEngine } from './move.mjs';\nimport { TrashEngine } from './trash.mjs';\nimport { apiHandler, dispatch } from './api.mjs';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\n\nexport const name = 'dsh-session-toolbox';\nexport const inject = [];\n// schemastery has no `.optional()`; empty defaults mean \"unset\" (apply() treats '' as absent).\nexport const Config = z.object({\n\tconfigFile: z.string().description('Directory for session-toolbox data (defaults to <DSH_HOME>/session-toolbox)').default(''),\n\tdshHome: z.string().description('Override the DSH home whose sessions/ storage the tool manages (advanced)').default(''),\n});\n\nexport function defaultKitRoot() {\n\treturn join(resolveDshHome(), 'session-toolbox');\n}\n\nexport async function apply(ctx, options = {}) {\n\ttry {\n\t\treturn await applyInner(ctx, options);\n\t} catch (error) {\n\t\t// A failing third-party plugin must never take the whole app down.\n\t\t// Record the real stack so the failure is diagnosable, then degrade.\n\t\trecordStartupFailure(error);\n\t\tconst hint = /Cannot find package|ERR_MODULE_NOT_FOUND/.test(String(error?.message ?? error))\n\t\t\t? '\uFF08\u4F9D\u8D56\u7F3A\u5931\uFF1A\u8BF7\u4ECE\u63D2\u4EF6\u9875\u5378\u8F7D\u540E\u91CD\u65B0\u5B89\u88C5\uFF0C\u8BA9\u5305\u7BA1\u7406\u5668\u91CD\u65B0\u62C9\u53D6 peer \u4F9D\u8D56\uFF09'\n\t\t\t: '';\n\t\tctx.logger?.warn?.(`session-toolbox: disabled after startup error${hint} \u2014 ${error?.message ?? error}`);\n\t}\n}\n\n/** Absolute path of the startup-failure log, next to the plugin's other state. */\nfunction startupFailureLog() {\n\ttry {\n\t\treturn join(resolve(resolveDshHome()), 'session-toolbox', 'startup-error.log');\n\t} catch { return ''; }\n}\n\n/** Append a startup failure (with stack) so a failed activation stays diagnosable. */\nfunction recordStartupFailure(error) {\n\tconst file = startupFailureLog();\n\tif (file === '') return;\n\ttry {\n\t\tconst detail = `${new Date().toISOString()}\\n${error?.stack ?? String(error)}\\n\\n`;\n\t\tappendFileSync(file, detail, { mode: 0o600 });\n\t} catch { /* diagnostics must never throw */ }\n}\n\nasync function applyInner(ctx, options = {}) {\n\tconst dshHome = resolve(options.dshHome || resolveDshHome());\n\tconst root = resolve(options.configFile || join(dshHome, 'session-toolbox'));\n\tawait mkdir(root, { recursive: true, mode: 0o700 });\n\tconst capabilities = {\n\t\tworkspaceRegistry: ctx.get('workspaceRegistry') !== undefined,\n\t\tsessions: ctx.get('sessions') !== undefined,\n\t\tsessionPersistence: ctx.get('sessionPersistence') !== undefined,\n\t\twebServer: false, // flips true when the optional webServer inject fires\n\t};\n\tconst settingsDoc = new JsonDocument(root, 'session-toolbox.json', { version: 1 }, guardSettings);\n\tconst journalDoc = new JsonDocument(root, 'journal.json', { version: 1, active: null }, guardJournal);\n\tconst ledgerDoc = new JsonDocument(root, 'trash.json', { version: 1, items: [] }, guardLedger);\n\tconst manifestDoc = new JsonDocument(root, 'vault-manifest.json', { version: 1, records: [] }, guardManifest);\n\n\tlet settingsCache = await settingsDoc.read().catch(() => ({ trash: {}, draftVault: {} }));\n\tlet journalCache = null;\n\ttry { journalCache = (await journalDoc.read()).active; }\n\tcatch (error) { ctx.logger?.warn?.(`session-toolbox: journal unreadable \u2014 ${error.message}`); }\n\tif (journalCache !== null) ctx.logger?.warn?.(`session-toolbox: unresolved move journal for ${journalCache.sessionId}; complete or roll it back from the panel or CLI`);\n\n\tconst move = new MoveEngine({\n\t\troot,\n\t\tsessionsRoot: join(dshHome, 'sessions'),\n\t\tprojcacheRoot: join(dshHome, 'storages', 'session_projcache', 'sessions'),\n\t\tjournal: journalDoc,\n\t\tcapabilities: hostCapabilities(ctx),\n\t});\n\tconst trash = new TrashEngine({\n\t\tadapter: registryAdapter(ctx),\n\t\tsessions: () => (ctx.get('sessions')?.list() ?? []).map(session => session.header?.id).filter(id => typeof id === 'string'),\n\t\tsessionsRoot: join(dshHome, 'sessions'),\n\t\tprojcacheRoot: join(dshHome, 'storages', 'session_projcache', 'sessions'),\n\t\tkitRoot: root,\n\t\tledger: ledgerDoc,\n\t\tvault: { dropSession: sessionId => manifestDoc.update(current => ({ ...current, records: current.records.filter(record => record.sessionId !== sessionId) })) },\n\t\tjournal: () => journalCache,\n\t\tsettings: () => settingsCache.trash ?? {},\n\t});\n\n\t/** All known projects (id/path/title/session count) for the move picker. */\n\tconst listProjects = ctx => {\n\t\tconst registry = ctx.get('workspaceRegistry');\n\t\tif (registry === undefined) return [];\n\t\treturn registry.list().map(row => ({\n\t\t\tid: row.id,\n\t\t\tpath: row.path,\n\t\t\ttitle: row.title ?? row.path,\n\t\t\tsessions: (row.sessionIds ?? []).length,\n\t\t})).sort((a, b) => a.title.localeCompare(b.title));\n\t};\n\n\tconst kit = {\n\t\tsettings: settingsDoc,\n\t\tmanifest: manifestDoc,\n\t\tjournal: journalDoc,\n\t\tmove,\n\t\ttrash,\n\t\tupdateSettings: async patch => {\n\t\t\tconst next = await settingsDoc.update(current => deepMerge(current, patch));\n\t\t\tsettingsCache = next;\n\t\t\treturn next;\n\t\t},\n\t\tvaultSync: async records => {\n\t\t\tconst manifest = await manifestDoc.update(() => ({\n\t\t\t\tversion: 1,\n\t\t\t\trecords: (Array.isArray(records) ? records : []).filter(record => typeof record.sessionId === 'string' && typeof record.attachmentId === 'string')\n\t\t\t\t\t.slice(-2000)\n\t\t\t\t\t.map(record => ({ sessionId: record.sessionId, attachmentId: record.attachmentId,\n\t\t\t\t\t\tname: String(record.name ?? ''), size: Number(record.size) || 0, hash: String(record.hash ?? ''),\n\t\t\t\t\t\tcreatedAt: Number(record.createdAt) || Date.now() })),\n\t\t\t}));\n\t\t\treturn manifest;\n\t\t},\n\t\tstate: async () => {\n\t\t\tconst settings = await settingsDoc.read();\n\t\t\tsettingsCache = settings;\n\t\t\tconst journal = (await journalDoc.read().catch(() => ({ active: journalCache }))).active;\n\t\t\tjournalCache = journal;\n\t\t\tconst manifest = await manifestDoc.read();\n\t\t\treturn {\n\t\t\t\tpluginVersion,\n\t\t\t\tcapabilities,\n\t\t\t\tsettings,\n\t\t\t\tjournal,\n\t\t\t\tprojects: listProjects(ctx),\n\t\t\t\ttrash: await trash.items(),\n\t\t\t\t// Bytes live in browser IndexedDB; the manifest mirrors their metadata only.\n\t\t\t\tvault: { records: manifest.records.length, bytes: manifest.records.reduce((total, record) => total + (record.size ?? 0), 0) },\n\t\t\t\trecovery: journal === null ? null : `\u672A\u5B8C\u6210\u8FC1\u79FB\uFF1A${journal.sessionId} \u2192 ${journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 ${journal.done.join('/') || '\u65E0'}\uFF09`,\n\t\t\t};\n\t\t},\n\t\t/** The project a session currently belongs to (null when unknown). */\n\t\tprojectOf: async sessionId => {\n\t\t\tif (sessionId === '') return null;\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry === undefined) return null;\n\t\t\tfor (const row of registry.list()) {\n\t\t\t\tif ((row.sessionIds ?? []).includes(sessionId)) {\n\t\t\t\t\treturn { id: row.id, path: row.path, title: row.title ?? row.path };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\taction: input => dispatch(kit, input),\n\t};\n\n\tctx.inject?.(['webServer'], webCtx => {\n\t\tcapabilities.webServer = true;\n\t\twebCtx.effect(() => webCtx.webServer.register({ kind: 'exact', path: '/.well-known/dsh-session-toolbox', handler: apiHandler(kit) }));\n\t});\n\n\t// Expose the kit in-process (cordis plugins must return nothing; the service\n\t// is the surface other plugins or future official hooks can reach).\n\tctx.provide('sessionToolbox', kit);\n\n\tctx.effect(() => () => {});\n\tctx.logger?.info?.('dsh-session-toolbox ready', JSON.stringify({ version: pluginVersion, capabilities }));\n}\n\n/**\n * Host capabilities for the move engine (\u00A710 MR5). Services are fetched lazily\n * through `ctx.get`; a missing one raises a stable error the UI can show.\n */\nexport function hostCapabilities(ctx) {\n\treturn {\n\t\tsessionLive: sessionId => {\n\t\t\tconst sessions = ctx.get('sessions');\n\t\t\treturn sessions ? sessions.list().some(session => session.header?.id === sessionId) : false;\n\t\t},\n\t\tlistHeaders: async () => {\n\t\t\tconst persistence = ctx.get('sessionPersistence');\n\t\t\tif (persistence?.list !== undefined) return (await persistence.list()).map(snapshot => snapshot.header);\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry?.listStoredHeaders !== undefined) return await registry.listStoredHeaders();\n\t\t\tthrow new Error('no session-persistence service in this DSH build');\n\t\t},\n\t\t/** Drop stale header entries so `attachSession` validates the NEW on-disk cwd (\u00A710 MR4). */\n\t\trefreshHeaderCache: async () => {\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (typeof registry?.replaceHeaderIndex !== 'function') return;\n\t\t\tawait registry.replaceHeaderIndex(await registry.listStoredHeaders());\n\t\t},\n\t\t/** Ledger step (\u00A710 MR2 step 2): source detach \u2192 target create \u2192 attach. */\n\t\tmoveLedger: async (sessionId, fromCwd, toCwd) => {\n\t\t\tconst registry = ctx.get('workspaceRegistry');\n\t\t\tif (registry === undefined) throw new Error('workspaceRegistry unavailable in this DSH build');\n\t\t\tconst canonicalFrom = fromCwd === undefined ? undefined : await realpath(fromCwd).catch(() => fromCwd);\n\t\t\tconst source = canonicalFrom === undefined ? undefined : await registry.resolveByPath(canonicalFrom);\n\t\t\tif (source !== undefined) await source.detachSession(sessionId);\n\t\t\tlet target = await registry.resolveByPath(toCwd);\n\t\t\tif (target === undefined) {\n\t\t\t\tif (!(await stat(toCwd).then(s => s.isDirectory(), () => false))) throw new Error(`target directory ${toCwd} does not exist`);\n\t\t\t\ttarget = await registry.create(toCwd);\n\t\t\t}\n\t\t\tawait target.attachSession(sessionId);\n\t\t},\n\t\tresyncIndex: async () => {\n\t\t\t// \u00A710.8-3: the sqlite query index reconciles against persistence lazily on\n\t\t\t// every query path; there is nothing explicit to call in this build.\n\t\t\treturn ctx.get('sessionQuery') !== undefined ? 'lazy' : 'unavailable';\n\t\t},\n\t};\n}\n\n/**\n * The narrow adapter the trash engine runs every official effect through\n * (\u00A711 TB5). Workspace deletion keeps session logs (\u00A711.1-2); bootstrap only\n * re-claims non-archived unaccounted sessions \u2014 which is why trashing a project\n * archives its sessions first, and restoring re-registers + attaches +\n * unarchives in order.\n */\nexport function registryAdapter(ctx) {\n\tconst registry = () => {\n\t\tconst value = ctx.get('workspaceRegistry');\n\t\tif (value === undefined) throw Object.assign(new Error('workspaceRegistry unavailable'), { code: 'no-capability' });\n\t\treturn value;\n\t};\n\treturn {\n\t\tsessionKnown: async sessionId => {\n\t\t\tconst rows = registry();\n\t\t\treturn typeof rows.sessionKnown === 'function' ? await rows.sessionKnown(sessionId) : false;\n\t\t},\n\t\tarchivedSet: async () => new Set(registry().archivedSessionIds ?? []),\n\t\tpinnedSet: async () => new Set(registry().pinnedSessionIds ?? []),\n\t\tlocateSession: async sessionId => {\n\t\t\tfor (const entity of registry().list()) {\n\t\t\t\tconst ids = entity.sessionIds ?? [];\n\t\t\t\tif (!ids.includes(sessionId)) continue;\n\t\t\t\tconst at = ids.indexOf(sessionId);\n\t\t\t\treturn { workspaceId: entity.id, beforeId: at + 1 < ids.length ? ids[at + 1] : null, title: null };\n\t\t\t}\n\t\t\treturn { workspaceId: null, beforeId: null, title: null };\n\t\t},\n\t\tworkspaceSnapshot: async workspaceId => {\n\t\t\tconst rows = registry();\n\t\t\tconst entity = rows.get(workspaceId);\n\t\t\tif (entity === undefined) return undefined;\n\t\t\tconst ids = entity.sessionIds ?? [];\n\t\t\tconst archived = new Set(rows.archivedSessionIds ?? []);\n\t\t\tconst pinned = new Set(rows.pinnedSessionIds ?? []);\n\t\t\tconst order = rows.list().map(row => row.id);\n\t\t\tconst at = order.indexOf(entity.id);\n\t\t\treturn {\n\t\t\t\tworkspaceId: entity.id, path: entity.path, title: entity.title,\n\t\t\t\tbeforeId: at > 0 ? order[at - 1] : null,\n\t\t\t\tsessions: ids.map((sessionId, index) => ({ sessionId,\n\t\t\t\t\tpreviouslyArchived: archived.has(sessionId),\n\t\t\t\t\tpreviouslyPinned: pinned.has(sessionId),\n\t\t\t\t\tbeforeId: index + 1 < ids.length ? ids[index + 1] : null })),\n\t\t\t};\n\t\t},\n\t\tarchiveSession: (sessionId, options) => registry().archiveSession(sessionId, options),\n\t\tunarchiveSession: sessionId => registry().unarchiveSession(sessionId),\n\t\tpinSession: sessionId => registry().pinSession(sessionId),\n\t\tunpinSession: sessionId => registry().unpinSession(sessionId),\n\t\tdeleteWorkspace: workspaceId => registry().delete(workspaceId),\n\t\trepairSlot: async (sessionId, workspaceId, beforeId) => {\n\t\t\tconst entity = registry().get(workspaceId);\n\t\t\tif (entity === undefined) return;\n\t\t\tif (!entity.sessionIds?.includes(sessionId)) await entity.attachSession(sessionId);\n\t\t\tif (beforeId !== null && entity.sessionIds?.includes(beforeId)) await entity.insertSessionBefore(sessionId, beforeId);\n\t\t},\n\t\trestoreWorkspace: async snapshot => {\n\t\t\tconst rows = registry();\n\t\t\tlet entity = rows.list().find(row => row.path === snapshot.path);\n\t\t\tif (entity === undefined) {\n\t\t\t\tawait mkdir(snapshot.path, { recursive: true }).catch(() => {});\n\t\t\t\tentity = await rows.create(snapshot.path, snapshot.title);\n\t\t\t}\n\t\t\tfor (const row of [...snapshot.sessions].reverse()) await entity.attachSession(row.sessionId).catch(() => {});\n\t\t\tif (snapshot.beforeId !== null && entity.id !== snapshot.beforeId) await rows.insertBefore(entity.id, snapshot.beforeId).catch(() => {});\n\t\t\treturn entity;\n\t\t},\n\t\tpurgeSession: async () => ctx.get('sessionPersistence')?.delete === undefined ? 'unavailable' : 'present',\n\t};\n}\n\nfunction deepMerge(base, patch) {\n\tconst next = { ...base };\n\tfor (const [key, value] of Object.entries(patch ?? {})) {\n\t\tnext[key] = value !== null && typeof value === 'object' && !Array.isArray(value) ? deepMerge(base[key] ?? {}, value) : value;\n\t}\n\treturn next;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.10\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["ctx"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-session-toolbox",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/AndroidSix/dsh-session-toolbox.git"