dsh-session-toolbox 0.1.11 → 0.1.12

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,12 @@
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.12
8
+
9
+ - **迁移/还原后自动刷新会话位置**(不重启、不影响其它会话):宿主在 `index` 阶段重新读取会话头(`replaceHeaderIndex`)使新位置立即生效;客户端在迁移/还原完成后通过窗口事件通知面板并重新拉取状态。侧栏列表是服务端驱动的快照,因此界面会给出一句明确提示——刷新侧栏即见,而不是静默消失。
10
+ - **账本写入前校验文件真实位置**:`ledger` 阶段会读取新位置的日志并确认 `header.cwd === toCwd`,不匹配就以 `publish-missing` 失败,避免「注册表说在 B、字节还在 A」的孤立会话(此前正是这种半完成状态让一个会话从侧栏消失)。
11
+ - **迁移失败自动回滚**:`commit` 之后的阶段一旦抛错,立即按逆序回滚到底,不再留下半完成状态;仅当回滚本身也失败时才保留 journal 作为救援指针。
12
+
7
13
  ## 0.1.11
8
14
 
9
15
  - **修复「切换会话到其它项目」总是报 `session … is live`**:此前的判定是「`sessions.list()` 里是否包含该会话」,但那返回的是**宿主已加载的全部会话**,只要文件打开过就算「运行中」,几乎任何会话都无法迁移。现改用官方的 `workspace/session-activity` 事件:只有出现 `{kind:'turn'}`(真正有一轮在跑,含等待审批/回答)才阻止迁移。
package/cordis.patch.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  - insert:
2
- - id: session-toolbox-v1-0.1.11
3
- name: ./lib/host-v0.1.11.js
2
+ - id: session-toolbox-v1-0.1.12
3
+ name: ./lib/host-v0.1.12.js
@@ -400,6 +400,14 @@ var MoveEngine = class {
400
400
  await this.phaseIndex(entry);
401
401
  } catch (error) {
402
402
  error.journal = await this.readJournal();
403
+ if (error.journal !== null && error.journal.done?.includes("commit")) {
404
+ try {
405
+ await this.rollback();
406
+ error.rolledBack = true;
407
+ } catch (rollbackError) {
408
+ error.rollbackError = rollbackError.message;
409
+ }
410
+ }
403
411
  throw error;
404
412
  }
405
413
  if (entry.deferredLedger === true) {
@@ -453,6 +461,15 @@ var MoveEngine = class {
453
461
  await this.#stage(entry, "commit");
454
462
  }
455
463
  async phaseLedger(entry) {
464
+ const published = join3(entry.newDir, entry.logName);
465
+ let landed = false;
466
+ try {
467
+ const { header } = readHeader(await readFile2(published));
468
+ landed = header.cwd === entry.toCwd;
469
+ } catch {
470
+ landed = false;
471
+ }
472
+ if (!landed) throw new MovePreflightError("publish-missing", `migrated log not found at ${published}; refusing to touch the ledger`);
456
473
  if (this.caps.moveLedger === void 0) {
457
474
  entry.deferredLedger = true;
458
475
  await this.#stage(entry, "ledger");
@@ -474,6 +491,8 @@ var MoveEngine = class {
474
491
  }
475
492
  async phaseIndex(entry) {
476
493
  await this.caps.resyncIndex?.();
494
+ await this.caps.refreshPlacement?.(entry.sessionId)?.catch?.(() => {
495
+ });
477
496
  await this.#stage(entry, "index");
478
497
  }
479
498
  /** Undo completed phases in reverse (§10 MR3). */
@@ -495,6 +514,8 @@ var MoveEngine = class {
495
514
  if (this.projcacheRoot !== void 0) await rm(join3(this.projcacheRoot, `${entry.sessionId}.json`), { force: true });
496
515
  await this.caps.resyncIndex?.().catch(() => {
497
516
  });
517
+ await this.caps.refreshPlacement?.(entry.sessionId)?.catch?.(() => {
518
+ });
498
519
  await this.#finish({ ...entry, retired: entry.done.includes("commit") ? void 0 : entry.retired });
499
520
  return { ok: true, rolledBack: entry.done };
500
521
  }
@@ -627,6 +648,9 @@ var TrashEngine = class {
627
648
  if (item === void 0) throw new TrashError("item-unknown", `no trash item ${itemId}`);
628
649
  if (item.kind === "session") await this.#restoreSession(adapter, item.snapshot);
629
650
  else await this.#restoreWorkspace(adapter, item.snapshot);
651
+ const affected = item.kind === "session" ? [item.snapshot.sessionId] : item.snapshot.sessions.map((row) => row.sessionId);
652
+ for (const sessionId of affected) await adapter.refreshPlacement?.(sessionId)?.catch?.(() => {
653
+ });
630
654
  state.items = state.items.filter((row) => row.id !== itemId);
631
655
  await this.ledger.write(state);
632
656
  return { ok: true };
@@ -827,4 +851,4 @@ export {
827
851
  apiHandler,
828
852
  dispatch
829
853
  };
830
- //# sourceMappingURL=chunk-QXW2NIKH.js.map
854
+ //# sourceMappingURL=chunk-EXE2TWBF.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/store.mjs", "../src/move.mjs", "../src/paths.mjs", "../src/session-log.mjs", "../src/trash.mjs", "../src/api.mjs"],
4
+ "sourcesContent": ["/**\n * Plugin-owned durable documents under `<DSH_HOME>/session-toolbox/`. All writes go\n * through `@deepseek-ai/dsh-atomic-write` (`writeFileAtomic` inside `withFileLock`)\n * with owner-only modes, mirroring the host's durability discipline. Corrupt\n * files are never auto-reset: they fail loud and surface in status.\n */\nimport { mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { writeFileAtomic, withFileLock } from '@deepseek-ai/dsh-atomic-write';\n\nexport const DEFAULT_SETTINGS = Object.freeze({\n\tversion: 1,\n\tdraftVault: { enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 },\n\ttrash: { ttlDays: 30, autoPurge: false },\n\tmove: { enabled: true },\n});\n\nconst OWNED = { mode: 0o600, dirMode: 0o700 };\n\nfunction mergeSettings(raw) {\n\treturn {\n\t\tversion: 1,\n\t\tdraftVault: { ...DEFAULT_SETTINGS.draftVault, ...(raw.draftVault ?? {}) },\n\t\ttrash: { ...DEFAULT_SETTINGS.trash, ...(raw.trash ?? {}) },\n\t\tmove: { ...DEFAULT_SETTINGS.move, ...(raw.move ?? {}) },\n\t};\n}const positiveInt = value => Number.isSafeInteger(value) && value > 0;\n\n/** One durable JSON document: locked read-modify-write + schema guard. */\nexport class JsonDocument {\n\tfile;\n\t#fallback;\n\t#guard;\n\tconstructor(root, name, fallback, guard) {\n\t\tthis.file = join(root, name);\n\t\tthis.#fallback = fallback;\n\t\tthis.#guard = guard;\n\t}\n\tasync read() {\n\t\tlet value;\n\t\ttry {\n\t\t\tvalue = JSON.parse(await readFile(this.file, 'utf8'));\n\t\t} catch (error) {\n\t\t\tif (error.code === 'ENOENT') value = structuredClone(this.#fallback);\n\t\t\telse if (error instanceof SyntaxError) throw new Error(`session-toolbox: corrupt JSON in ${this.file}: ${error.message}`, { cause: error });\n\t\t\telse throw error;\n\t\t}\n\t\tif (value === null || typeof value !== 'object' || Array.isArray(value)) throw new Error(`session-toolbox: ${this.file} is not a JSON object`);\n\t\treturn this.#guard === undefined ? value : this.#guard(value, this.file);\n\t}\n\tasync write(value) {\n\t\tthis.#guard?.(value, this.file);\n\t\t// The lock sibling is created before writeFileAtomic's own mkdir, so the\n\t\t// parent directory must already exist for withFileLock.\n\t\tawait mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n\t\tawait withFileLock(this.file, () => writeFileAtomic(this.file, JSON.stringify(value, null, 2), OWNED));\n\t\treturn value;\n\t}\n\t/** Serialized read-modify-write; `fn` returning undefined aborts without writing. */\n\tasync update(fn) {\n\t\tawait mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n\t\treturn withFileLock(this.file, async () => {\n\t\t\tconst current = await this.read();\n\t\t\tconst next = fn(current);\n\t\t\tif (next === undefined) return current;\n\t\t\tthis.#guard?.(next, this.file);\n\t\t\tawait writeFileAtomic(this.file, JSON.stringify(next, null, 2), OWNED);\n\t\t\treturn next;\n\t\t});\n\t}\n}\n\nexport function guardSettings(value, file) {\n\tif (value.version !== undefined && value.version !== 1) throw new Error(`session-toolbox: unsupported settings version in ${file}: ${String(value.version)}`);\n\tconst draft = value.draftVault ?? {};\n\tif (draft.enabled !== undefined && typeof draft.enabled !== 'boolean') throw new Error(`session-toolbox: settings.draftVault.enabled must be boolean (${file})`);\n\tif (draft.maxSizeBytes !== undefined && !positiveInt(draft.maxSizeBytes)) throw new Error(`session-toolbox: settings.draftVault.maxSizeBytes must be a positive safe integer (${file})`);\n\tif (draft.ttlDays !== undefined && !positiveInt(draft.ttlDays)) throw new Error(`session-toolbox: settings.draftVault.ttlDays must be a positive integer (${file})`);\n\tconst trash = value.trash ?? {};\n\tif (trash.ttlDays !== undefined && !positiveInt(trash.ttlDays)) throw new Error(`session-toolbox: settings.trash.ttlDays must be a positive integer (${file})`);\n\tif (trash.autoPurge !== undefined && typeof trash.autoPurge !== 'boolean') throw new Error(`session-toolbox: settings.trash.autoPurge must be boolean (${file})`);\n\treturn mergeSettings(value);\n}\n\nexport function guardJournal(value, file) {\n\tif (value.version !== 1) throw new Error(`session-toolbox: unsupported journal version in ${file}: ${String(value.version)}`);\n\tconst entry = value.active;\n\tif (entry === null || entry === undefined) return value;\n\tif (typeof entry.sessionId !== 'string' || typeof entry.fromCwd !== 'string' && entry.fromCwd !== null || typeof entry.toCwd !== 'string') {\n\t\tthrow new Error(`session-toolbox: malformed move journal (${file})`);\n\t}\n\tif (!Array.isArray(entry.done) || typeof entry.startedAt !== 'string') throw new Error(`session-toolbox: move journal lacks done/startedAt (${file})`);\n\treturn value;\n}\n\nexport function guardLedger(value, file) {\n\tif (value.version !== 1) throw new Error(`session-toolbox: unsupported ledger version in ${file}: ${String(value.version)}`);\n\tif (!Array.isArray(value.items)) throw new Error(`session-toolbox: trash ledger has no items array (${file})`);\n\tfor (const item of value.items) {\n\t\tif ((item.kind !== 'session' && item.kind !== 'workspace') || typeof item.id !== 'string') throw new Error(`session-toolbox: malformed trash item (${file})`);\n\t\tif (item.snapshot === null || typeof item.snapshot !== 'object') throw new Error(`session-toolbox: trash item lacks snapshot (${file})`);\n\t}\n\treturn value;\n}\n\nexport function guardManifest(value, file) {\n\tif (value.version !== 1) throw new Error(`session-toolbox: unsupported vault manifest version in ${file}: ${String(value.version)}`);\n\tif (!Array.isArray(value.records)) throw new Error(`session-toolbox: vault manifest has no records array (${file})`);\n\tfor (const record of value.records) {\n\t\tif (typeof record.sessionId !== 'string' || typeof record.attachmentId !== 'string') throw new Error(`session-toolbox: malformed vault record (${file})`);\n\t}\n\treturn value;\n}\n\nexport { mergeSettings, OWNED };\n", "/**\n * Session-to-project move (\u00A710). The engine is capability-injected: the host\n * wires it to `workspaceRegistry` / `sessionPersistence` / `sessionQuery`, the\n * CLI wires an offline ledger over `workspace.json`. Phases are journaled so an\n * interrupted move is detected on startup and rolled back or completed\n * deterministically (\u00A710 MR2/MR3, MN1).\n *\n * Critical-section ordering (\u00A710.6): backup \u2192 stage new artifact (header\n * rewritten + fsynced) \u2192 commit placement \u2192 ledger \u2192 caches. The store never\n * exposes a window where one id exists in two project directories, and the\n * ledger runs only after placement is consistent, so `attachSession`'s\n * header-cwd validation holds by construction.\n */\nimport { createHash } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { encodeSegment, findLogFile, projectKey, verifyEncoders } from './paths.mjs';\nimport { readHeader, rewriteHeaderFrame } from './session-log.mjs';\n\nexport const PHASES = ['backup', 'rewrite', 'commit', 'ledger', 'projcache', 'index'];\n\nexport class MovePreflightError extends Error {\n\tcode;\n\tdetails;\n\tconstructor(code, message, details = {}) {\n\t\tsuper(message);\n\t\tthis.name = 'MovePreflightError';\n\t\tthis.code = code;\n\t\tthis.details = details;\n\t}\n}\n\nconst sha256 = buffer => createHash('sha256').update(buffer).digest('hex');\n\nasync function fsyncFile(path) {\n\tconst handle = await open(path, constants.O_WRONLY);\n\ttry { await handle.sync(); } finally { await handle.close(); }\n}\n\nexport async function writeOwned(file, data, mode = 0o600) {\n\tawait mkdir(dirname(file), { recursive: true, mode: 0o700 });\n\tconst temp = `${file}.${process.pid}.${Date.now()}.tmp`;\n\tawait writeFile(temp, data, { mode, flag: 'wx' });\n\tawait rename(temp, file);\n\treturn file;\n}\n\n/**\n * Move a directory between the sessions store and the kit working areas.\n * `rename` is the atomic same-volume path; a cross-device layout (unusual, but\n * possible when DSH_HOME points at a separate mount) falls back to a\n * copy-then-remove so a move never dead-ends half-published.\n */\nexport async function relocate(source, target) {\n\tawait mkdir(dirname(target), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait rename(source, target);\n\t\treturn;\n\t} catch (error) {\n\t\tif (error.code !== 'EXDEV') throw error;\n\t}\n\tconst { cp } = await import('node:fs/promises');\n\tawait cp(source, target, { recursive: true, errorOnExist: true, force: false });\n\tawait rm(source, { recursive: true, force: true });\n}\n\n/**\n * @param deps - {root (kit dir), sessionsRoot, projcacheRoot, journal: JsonDocument,\n * capabilities: {sessionLive, listHeaders, moveLedger, resyncIndex, refreshHeaderCache}, now}\n */\nexport class MoveEngine {\n\tconstructor(deps) {\n\t\tthis.root = deps.root;\n\t\tthis.sessionsRoot = deps.sessionsRoot;\n\t\tthis.projcacheRoot = deps.projcacheRoot;\n\t\tthis.journal = deps.journal;\n\t\tthis.caps = deps.capabilities ?? {};\n\t\tthis.now = deps.now ?? (() => Date.now());\n\t}\n\n\tasync readJournal() { return (await this.journal.read()).active ?? null; }\n\tasync writeJournal(entry) { await this.journal.write({ version: 1, active: entry }); }\n\n\t/** List every journaled placement hazard for status surfaces. */\n\tasync recoverable() { return await this.readJournal() !== null; }\n\n\t/**\n\t * Fail-closed feasibility check (\u00A710 MR1). Returns the plan or throws\n\t * MovePreflightError with a stable code; never touches anything.\n\t */\n\tasync preflight({ sessionId, toCwd, createTarget = false }) {\n\t\tif (typeof sessionId !== 'string' || !sessionId.startsWith('session-')) throw new MovePreflightError('bad-request', 'sessionId must look like session-<uuid>');\n\t\tif (typeof toCwd !== 'string' || (!toCwd.startsWith('/') && !/^[A-Za-z]:[\\\\/]/.test(toCwd))) throw new MovePreflightError('bad-request', 'toCwd must be an absolute path');\n\t\tif (await this.readJournal() !== null) throw new MovePreflightError('journal-active', 'another move is journaled; complete or roll it back first');\n\t\tif (this.caps.sessionLive?.(sessionId)) throw new MovePreflightError('session-busy', `session ${sessionId} is live; stop its work before moving`);\n\t\tconst headers = await this.caps.listHeaders();\n\t\tconst header = headers.find(row => row.id === sessionId);\n\t\tif (header === undefined) throw new MovePreflightError('session-unknown', `no stored session ${sessionId}`);\n\t\tif (typeof header.version === 'number' && header.version < 4) throw new MovePreflightError('format-legacy', `stored log is format v${header.version}; open the session once in DSH so the host migrates it, then retry`);\n\n\t\t// Locate the authoritative directory by scanning project dirs \u2014 never trust a computed path alone.\n\t\tconst encoded = encodeSegment(sessionId);\n\t\tconst entries = await readdir(this.sessionsRoot, { withFileTypes: true }).catch(error => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t});\n\t\tconst projects = entries.filter(entry => entry.isDirectory() && !entry.name.startsWith('.')).map(entry => entry.name);\n\t\tconst found = [];\n\t\tfor (const project of projects) {\n\t\t\ttry { if ((await stat(join(this.sessionsRoot, project, encoded))).isDirectory()) found.push(project); } catch {}\n\t\t}\n\t\tif (found.length > 1) throw new MovePreflightError('duplicate-artifact', `session ${sessionId} lives in ${found.length} project directories; refusing to guess`);\n\t\tif (found.length === 0) throw new MovePreflightError('artifact-missing', `session ${sessionId} has no session directory under any project`);\n\t\tconst fromProject = found[0];\n\t\tconst logName = await findLogFile(join(this.sessionsRoot, fromProject, encoded));\n\t\tif (logName === undefined) throw new MovePreflightError('artifact-missing', `session directory for ${sessionId} holds no session log`);\n\t\tconst logPath = join(this.sessionsRoot, fromProject, encoded, logName);\n\t\t// \u00A710 MR1 belt-and-braces (CLI/offline path): a freshly-written log means an\n\t\t// appender is active; moving it now would orphan their writes.\n\t\tconst logStat = await stat(logPath);\n\t\tif (this.now() - logStat.mtimeMs < 10_000) throw new MovePreflightError('session-churning', `session log was written ${Math.round((this.now() - logStat.mtimeMs) / 1000)}s ago; make sure DSH has stopped using it, then retry`);\n\t\tif (header.cwd !== undefined && fromProject !== projectKey(header.cwd)) {\n\t\t\t// \u00A710 MN3: the layout changed in a way this build cannot follow.\n\t\t\tthrow new MovePreflightError('unknown-layout', `cwd '${header.cwd}' should live at '${projectKey(header.cwd)}' but the artifact was observed at '${fromProject}'`);\n\t\t}\n\t\tconst fromCwd = header.cwd;\n\t\tlet canonicalTo;\n\t\ttry { canonicalTo = await realpath(toCwd.replace(/[/\\\\]+$/, '') || toCwd); } catch { canonicalTo = undefined; }\n\t\tif (canonicalTo === undefined) {\n\t\t\tif (!createTarget) throw new MovePreflightError('target-missing', `target directory ${toCwd} does not exist (choose create to make it)`);\n\t\t\tawait mkdir(toCwd, { recursive: true });\n\t\t\tcanonicalTo = await realpath(toCwd);\n\t\t}\n\t\tif (fromCwd !== undefined && canonicalTo === fromCwd) throw new MovePreflightError('same-target', 'the session already belongs to that project');\n\t\tconst samples = headers.filter(row => row.cwd !== undefined).slice(0, 5)\n\t\t\t.map(row => ({ cwd: row.cwd, id: row.id, project: projectKey(row.cwd), dir: encodeSegment(row.id) }));\n\t\tif (samples.length > 0 && !verifyEncoders(samples, this.sessionsRoot)) {\n\t\t\tthrow new MovePreflightError('unknown-layout', 'project-key / segment encoders disagree with the shipped layout; refusing to write');\n\t\t}\n\t\treturn {\n\t\t\tsessionId, fromCwd: fromCwd ?? null, toCwd: canonicalTo,\n\t\t\tfromProject, toProject: projectKey(canonicalTo),\n\t\t\tdir: join(this.sessionsRoot, fromProject, encoded),\n\t\t\tnewDir: join(this.sessionsRoot, projectKey(canonicalTo), encoded),\n\t\t\tlogName,\n\t\t\tstartedAt: new Date(this.now()).toISOString(),\n\t\t};\n\t}\n\n\tasync #stage(entry, phase) {\n\t\tif (!entry.done.includes(phase)) { entry.done = [...entry.done, phase]; await this.writeJournal(entry); }\n\t}\n\n\t/** Execute the whole move; on failure the journal survives for rollback/complete. */\n\tasync move(plan) {\n\t\tconst entry = { ...plan, done: [], deferredLedger: false,\n\t\t\tbackup: join(this.root, 'backups', `${plan.sessionId}.orig`),\n\t\t\tstagedNew: join(this.root, 'backups', `${plan.sessionId}.new`),\n\t\t\tretired: join(this.root, 'pending-purge', `${plan.sessionId}__${this.now()}`) };\n\t\tawait this.writeJournal(entry);\n\t\ttry {\n\t\t\tawait this.phaseBackup(entry);\n\t\t\tawait this.phaseRewrite(entry);\n\t\t\tawait this.phaseCommit(entry);\n\t\t\tawait this.phaseLedger(entry);\n\t\t\tawait this.phaseProjcache(entry);\n\t\t\tawait this.phaseIndex(entry);\n\t\t} catch (error) {\n\t\t\terror.journal = await this.readJournal();\n\t\t\t// Never leave a half-applied move behind. Phases after `commit` are safe\n\t\t\t// to undo in reverse; if the rollback itself fails the journal stays as\n\t\t\t// the recovery pointer and the panel offers complete/rollback explicitly.\n\t\t\tif (error.journal !== null && error.journal.done?.includes('commit')) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.rollback();\n\t\t\t\t\terror.rolledBack = true;\n\t\t\t\t} catch (rollbackError) {\n\t\t\t\t\terror.rollbackError = rollbackError.message;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\t// A deferred ledger (same-process stale header cache) keeps the journal alive\n\t\t// as the retry marker: the artifact is already moved, and the next process \u2014\n\t\t// or move.complete \u2014 converges the accounting (\u00A710 MR3).\n\t\tif (entry.deferredLedger === true) {\n\t\t\tawait this.writeJournal(entry);\n\t\t\treturn { ok: true, deferred: true, sessionId: entry.sessionId, toCwd: entry.toCwd,\n\t\t\t\tnote: '\u6587\u4EF6\u5DF2\u8FC1\u79FB\uFF1BDSH \u8D26\u672C\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u6216 move.complete \u65F6\u6536\u655B' };\n\t\t}\n\t\tawait this.#finish(entry);\n\t\treturn { ok: true, sessionId: entry.sessionId, toCwd: entry.toCwd, deferredLedger: entry.deferredLedger === true };\n\t}\n\n\tasync #finish(entry) {\n\t\tawait this.writeJournal(null);\n\t\tawait rm(entry.backup, { force: true });\n\t\tawait rm(entry.stagedNew, { force: true });\n\t\tif (entry.retired !== undefined) await rm(entry.retired, { recursive: true, force: true });\n\t}\n\n\tasync phaseBackup(entry) {\n\t\tconst source = await readFile(join(entry.dir, entry.logName));\n\t\tawait writeOwned(entry.backup, source);\n\t\tentry.sourceHash = sha256(source);\n\t\tawait this.#stage(entry, 'backup');\n\t}\n\n\tasync phaseRewrite(entry) {\n\t\tconst source = await readFile(entry.backup);\n\t\tif (sha256(source) !== entry.sourceHash) throw new MovePreflightError('source-changed', 'the session log changed between backup and rewrite');\n\t\tconst current = readHeader(source);\n\t\tif (current.header.id !== entry.sessionId) throw new MovePreflightError('artifact-mismatch', `log header id '${current.header.id}' does not name ${entry.sessionId}`);\n\t\tconst rewritten = rewriteHeaderFrame(source, entry.toCwd);\n\t\treadHeader(rewritten); // parse + walk validation of our own output before staging\n\t\tif (sha256(rewritten) === entry.sourceHash) throw new MovePreflightError('rewrite-noop', 'header rewrite produced identical bytes');\n\t\tawait writeOwned(entry.stagedNew, rewritten);\n\t\tawait fsyncFile(entry.stagedNew);\n\t\tawait this.#stage(entry, 'rewrite');\n\t}\n\n\tasync phaseCommit(entry) {\n\t\t// Retire first, then publish: the id is never simultaneously discoverable in two project dirs.\n\t\tawait relocate(entry.dir, entry.retired);\n\t\tawait mkdir(entry.newDir, { recursive: true, mode: 0o700 });\n\t\tawait writeOwned(join(entry.newDir, entry.logName), await readFile(entry.stagedNew));\n\t\tawait fsyncFile(join(entry.newDir, entry.logName));\n\t\t// Carry non-log artifacts (session-local files); never resurrect a stale lock.\n\t\tfor (const name of await readdir(entry.retired).catch(() => [])) {\n\t\t\tif (name === entry.logName || name === 'session.lock') continue;\n\t\t\tawait rename(join(entry.retired, name), join(entry.newDir, name)).catch(() => {});\n\t\t}\n\t\tawait rm(join(entry.newDir, 'session.lock'), { force: true });\n\t\tawait this.#stage(entry, 'commit');\n\t}\n\n\tasync phaseLedger(entry) {\n\t\t// Guard the accounting step with the artifact's real location: if the log is\n\t\t// not where commit claimed to put it, recording the move in the ledger would\n\t\t// orphan the session (registry says project B, bytes still live in A). This\n\t\t// is exactly the half-applied state that stranded a session before.\n\t\tconst published = join(entry.newDir, entry.logName);\n\t\tlet landed = false;\n\t\ttry {\n\t\t\tconst { header } = readHeader(await readFile(published));\n\t\t\tlanded = header.cwd === entry.toCwd;\n\t\t} catch { landed = false; }\n\t\tif (!landed) throw new MovePreflightError('publish-missing', `migrated log not found at ${published}; refusing to touch the ledger`);\n\t\tif (this.caps.moveLedger === undefined) { entry.deferredLedger = true; await this.#stage(entry, 'ledger'); return; }\n\t\ttry {\n\t\t\tawait this.caps.refreshHeaderCache?.();\n\t\t\tawait this.caps.moveLedger(entry.sessionId, entry.fromCwd ?? undefined, entry.toCwd);\n\t\t\tentry.deferredLedger = false;\n\t\t} catch (error) {\n\t\t\t// The registry's header/pin caches can lag a same-process move. Leaving the\n\t\t\t// journal's ledger phase undone lets startup recovery re-run it in a fresh\n\t\t\t// process where the caches are cold (\u00A710 MR3/MR4).\n\t\t\tentry.deferredLedger = true;\n\t\t\tentry.ledgerError = String(error?.message ?? error);\n\t\t}\n\t\tawait this.#stage(entry, 'ledger');\n\t}\n\n\tasync phaseProjcache(entry) {\n\t\tif (this.projcacheRoot !== undefined) await rm(join(this.projcacheRoot, `${entry.sessionId}.json`), { force: true });\n\t\tawait this.#stage(entry, 'projcache');\n\t}\n\n\tasync phaseIndex(entry) {\n\t\tawait this.caps.resyncIndex?.();\n\t\t// Make the session's new placement visible without restarting DSH: re-read\n\t\t// the headers and let the caller refetch just this session's row.\n\t\tawait this.caps.refreshPlacement?.(entry.sessionId)?.catch?.(() => {});\n\t\tawait this.#stage(entry, 'index');\n\t}\n\n\t/** Undo completed phases in reverse (\u00A710 MR3). */\n\tasync rollback() {\n\t\tconst entry = await this.readJournal();\n\t\tif (entry === null) throw new MovePreflightError('no-journal', 'no journaled move to roll back');\n\t\tif (entry.done.includes('commit')) {\n\t\t\tawait rm(entry.newDir, { recursive: true, force: true });\n\t\t\tawait mkdir(dirname(entry.dir), { recursive: true, mode: 0o700 });\n\t\t\tif (entry.retired !== undefined) await relocate(entry.retired, entry.dir);\n\t\t\telse throw new MovePreflightError('journal-order', 'committed journal lost its retired directory');\n\t\t}\n\t\tif (entry.deferredLedger !== true && entry.done.includes('ledger') && this.caps.moveLedger !== undefined) {\n\t\t\tawait this.caps.moveLedger(entry.sessionId, entry.toCwd, entry.fromCwd ?? undefined).catch(() => {});\n\t\t\tawait this.caps.refreshHeaderCache?.().catch(() => {});\n\t\t}\n\t\tif (this.projcacheRoot !== undefined) await rm(join(this.projcacheRoot, `${entry.sessionId}.json`), { force: true });\n\t\tawait this.caps.resyncIndex?.().catch(() => {});\n\t\tawait this.caps.refreshPlacement?.(entry.sessionId)?.catch?.(() => {});\n\t\tawait this.#finish({ ...entry, retired: entry.done.includes('commit') ? undefined : entry.retired });\n\t\treturn { ok: true, rolledBack: entry.done };\n\t}\n\n\t/** Resume a journaled move from its first missing phase (post-crash forward roll). */\n\tasync complete() {\n\t\tconst entry = await this.readJournal();\n\t\tif (entry === null) throw new MovePreflightError('no-journal', 'no journaled move to complete');\n\t\tconst runs = { backup: e => this.phaseBackup(e), rewrite: e => this.phaseRewrite(e), commit: e => this.phaseCommit(e),\n\t\t\tledger: e => this.phaseLedger(e), projcache: e => this.phaseProjcache(e), index: e => this.phaseIndex(e) };\n\t\tfor (const phase of PHASES) {\n\t\t\tif (entry.done.includes(phase)) {\n\t\t\t\t// A deferred ledger from a previous attempt deserves another try in\n\t\t\t\t// this (fresh) process where the registry caches are cold.\n\t\t\t\tif (phase === 'ledger' && entry.deferredLedger === true) await runs.ledger(entry);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (phase !== 'backup' && !entry.done.includes('backup')) throw new MovePreflightError('journal-order', `journal skipped '${phase}' before completing 'backup'`);\n\t\t\tawait runs[phase](entry);\n\t\t}\n\t\t// Still-deferred accounting keeps the journal (and every backup) alive as\n\t\t// the convergence pointer; rollback remains available (\u00A710 MR3).\n\t\tif (entry.deferredLedger === true) {\n\t\t\tawait this.writeJournal(entry);\n\t\t\treturn { ok: true, deferred: true, sessionId: entry.sessionId, toCwd: entry.toCwd, ledgerError: entry.ledgerError,\n\t\t\t\tnote: '\u6587\u4EF6\u5DF2\u8FC1\u79FB\u4F46 DSH \u8D26\u672C\u4ECD\u672A\u6536\u655B\uFF1B\u91CD\u542F DSH \u540E\u518D\u6B21\u70B9\u5B8C\u6210\uFF0C\u6216\u9009\u62E9\u56DE\u6EDA' };\n\t\t}\n\t\tawait this.#finish(entry);\n\t\treturn { ok: true, sessionId: entry.sessionId, toCwd: entry.toCwd };\n\t}\n}\n", "/**\n * DSH session-storage path algebra. Mirrors `projectKey` / `encodeSegment` /\n * `projectDir` / `sessionDir` from `@deepseek-ai/dsh-session-persistence-jsonl`\n * (which does not export them). Every implementation is verified against the\n * live layout by `verifyEncoders` before any mutation runs, so a future official\n * change to the encoding rules fails closed (\u00A710 MN3) instead of corrupting data.\n */\nimport { readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/** The `undefined` cwd bucket: sessions whose stored header carries no cwd. */\nexport const NO_CWD_DIR = '_no-cwd';\n\n/** Encode one id/path segment: safe code units stay literal, everything else becomes `~XXXX`. */\nexport function encodeSegment(raw) {\n\tif (raw.length === 0) throw new Error('cannot encode an empty path segment');\n\tif (raw === '.') return '~002E';\n\tif (raw === '..') return '~002E~002E';\n\tlet out = '';\n\tfor (let i = 0; i < raw.length; i++) {\n\t\tconst code = raw.charCodeAt(i);\n\t\tconst ch = String.fromCharCode(code);\n\t\tif (ch !== '~' && /^[A-Za-z0-9._-]$/.test(ch)) out += ch;\n\t\telse out += '~' + code.toString(16).toUpperCase().padStart(4, '0');\n\t}\n\treturn out;\n}\n\n/**\n * Build the readable project directory key for a canonical cwd, exactly like the\n * official backend: separators collapse to one `-`, unsafe units escape as `~XXXX`,\n * the whole key is wrapped in `--\u2026--` and bounded to 251 readable characters.\n */\nexport function projectKey(cwd) {\n\tif (cwd.length === 0) throw new Error('cannot encode an empty project path');\n\tlet readable = '';\n\tlet separatorRun = false;\n\tfor (let i = 0; i < cwd.length; i++) {\n\t\tconst code = cwd.charCodeAt(i);\n\t\tconst ch = String.fromCharCode(code);\n\t\tif (ch === '/' || ch === '\\\\' || ch === ':') {\n\t\t\tif (!separatorRun) readable += '-';\n\t\t\tseparatorRun = true;\n\t\t} else if (ch !== '~' && /^[A-Za-z0-9._-]$/.test(ch)) {\n\t\t\treadable += ch;\n\t\t\tseparatorRun = false;\n\t\t} else {\n\t\t\treadable += '~' + code.toString(16).toUpperCase().padStart(4, '0');\n\t\t\tseparatorRun = false;\n\t\t}\n\t}\n\treturn `--${(readable.replace(/^-+/, '') || 'root').slice(0, 251)}--`;\n}\n\nexport function projectDir(root, cwd) {\n\treturn cwd === undefined ? join(root, NO_CWD_DIR) : join(root, projectKey(cwd));\n}\n\nexport function sessionDir(root, cwd, id) {\n\treturn join(projectDir(root, cwd), encodeSegment(id));\n}\n\nconst LOG_BASENAMES = ['session.jsonl', 'session.v1.jsonl', 'session.v2.jsonl', 'session.v3.jsonl', 'session.v4.jsonl',\n\t'session.jsonl.zstd', 'session.v1.jsonl.zstd', 'session.v2.jsonl.zstd', 'session.v3.jsonl.zstd', 'session.v4.jsonl.zstd'];\n\n/** Locate the authoritative session log inside one session directory (highest generation wins). */\nexport async function findLogFile(dir) {\n\tconst entries = await readdir(dir);\n\tconst present = LOG_BASENAMES.filter(name => entries.includes(name));\n\tif (present.length === 0) return undefined;\n\t// Compression-suffixed generations outrank the bare same-generation name; then highest version.\n\tconst zstd = present.filter(name => name.endsWith('.zstd'));\n\tconst pool = zstd.length > 0 ? zstd : present;\n\treturn pool.sort((left, right) => right.length - left.length || right.localeCompare(left))[0];\n}\n\n/** One candidate location for a session id, checked cheaply (directory stat only). */\nexport async function sessionDirExists(root, cwd, id) {\n\ttry { return (await stat(join(sessionDir(root, cwd, id)))).isDirectory(); } catch { return false; }\n}\n\n/**\n * Prove our local encoders agree with the shipped layout before anything writes:\n * for every provided {cwd, id, observedProjectDir, observedSessionDir} sample\n * (taken from headers the host itself enumerated), the directory derived by\n * projectKey/encodeSegment must match the observed path relative to the root.\n * @param samples - observed placement of stored sessions.\n * @param root - absolute sessions root; samples carry root-relative dirs.\n * @returns whether every sample reproduces.\n */\nexport function verifyEncoders(samples, root) {\n\tif (samples.length === 0) return false;\n\treturn samples.every(({ cwd, id, project, dir }) =>\n\t\tjoin(root, project, dir) === sessionDir(root, cwd, id) && join(root, project) === projectDir(root, cwd));\n}\n", "/**\n * Session log zstd artifact surgery. The shipped format is a concatenation of\n * independent zstd frames: frame 0 is the header line, every event is its own\n * frame (verified against live v4 logs and against `scanZstdFrames` in\n * `@deepseek-ai/dsh-session-persistence-jsonl`). Rewriting the header therefore\n * only rebuilds frame 0; every event frame is carried over byte-for-byte, which\n * keeps the operation cheap and reversible for arbitrarily large logs.\n */\nimport { constants, zstdCompressSync, zstdDecompressSync } from 'node:zlib';\n\nexport const ZSTD_MAGIC = 0xfd2fb528;\nconst SKIPPABLE_MAGIC_MIN = 0x184d2a50, SKIPPABLE_MAGIC_MAX = 0x184d2a5f;\n// The shipped writer puts an XXH64 content checksum on every frame; match it so\n// the rewritten header is indistinguishable in style from the original.\nconst CHECKSUM = { [constants.ZSTD_c_checksumFlag]: 1 };\n\n/**\n * Walk a frame sequence exactly like the official scanner (including skippable\n * frames). Throws on any malformed byte \u2014 the walk IS the post-write validation.\n * @param buffer - complete artifact bytes.\n * @returns ordered frames with byte ranges.\n */\nexport function scanFrames(buffer) {\n\tconst frames = [];\n\tlet offset = 0;\n\twhile (offset < buffer.length) {\n\t\tconst start = offset;\n\t\tif (buffer.length - offset < 4) throw new Error(`truncated zstd artifact: fewer than 4 bytes at ${offset}`);\n\t\tconst magic = buffer.readUInt32LE(offset);\n\t\tif (magic >= SKIPPABLE_MAGIC_MIN && magic <= SKIPPABLE_MAGIC_MAX) {\n\t\t\tconst size = buffer.readUInt32LE(offset + 4);\n\t\t\toffset += 8 + size;\n\t\t\tif (offset > buffer.length) throw new Error(`truncated skippable frame at ${start}`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (magic !== ZSTD_MAGIC) throw new Error(`invalid frame magic at byte ${offset}`);\n\t\toffset += 4;\n\t\tif (offset === buffer.length) throw new Error(`frame header missing at byte ${start}`);\n\t\tconst descriptor = buffer.readUInt8(offset);\n\t\toffset += 1;\n\t\tif ((descriptor & 24) !== 0) throw new Error(`reserved frame-header bit at byte ${offset - 1}`);\n\t\tconst contentSizeFlag = descriptor >>> 6;\n\t\tconst singleSegment = (descriptor & 32) !== 0;\n\t\tconst checksum = (descriptor & 4) !== 0;\n\t\tconst dictionaryFlag = descriptor & 3;\n\t\tconst dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;\n\t\tconst contentSizeBytes = contentSizeFlag === 0 ? (singleSegment ? 1 : 0) : 1 << contentSizeFlag;\n\t\tconst remaining = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;\n\t\tif (buffer.length - offset < remaining) throw new Error(`truncated frame header at byte ${start}`);\n\t\toffset += remaining;\n\t\tfor (;;) {\n\t\t\tif (buffer.length - offset < 3) throw new Error(`truncated block header at byte ${offset}`);\n\t\t\tconst blockHeader = buffer.readUIntLE(offset, 3);\n\t\t\toffset += 3;\n\t\t\tconst lastBlock = (blockHeader & 1) !== 0;\n\t\t\tconst blockType = blockHeader >>> 1 & 3;\n\t\t\tconst blockSize = blockHeader >>> 3;\n\t\t\tif (blockType === 3) throw new Error(`reserved block type at byte ${offset - 3}`);\n\t\t\tconst payloadBytes = blockType === 1 ? 1 : blockSize;\n\t\t\tif (buffer.length - offset < payloadBytes) throw new Error(`truncated block payload at byte ${offset}`);\n\t\t\toffset += payloadBytes;\n\t\t\tif (lastBlock) break;\n\t\t}\n\t\tif (checksum) {\n\t\t\tif (buffer.length - offset < 4) throw new Error(`truncated frame checksum at byte ${offset}`);\n\t\t\toffset += 4;\n\t\t}\n\t\tframes.push({ start, end: offset });\n\t}\n\treturn frames;\n}\n\nexport function decodeFrame(buffer, frame) {\n\treturn zstdDecompressSync(buffer.subarray(frame.start, frame.end));\n}\n\n/** Parse the header line of a session artifact (first non-skippable frame). */\nexport function readHeader(buffer) {\n\tconst frames = scanFrames(buffer);\n\tif (frames.length === 0) throw new Error('empty zstd artifact');\n\tconst text = decodeFrame(buffer, frames[0]).toString('utf8').trim();\n\tconst header = JSON.parse(text);\n\tif (header.type !== 'session' || typeof header.id !== 'string') throw new Error('malformed session header frame');\n\treturn { header, frames };\n}\n\n/**\n * Produce a complete artifact whose header frame carries a replaced cwd. The\n * header object keeps its original key order; only the `cwd` value changes\n * (`undefined` removes the field). All remaining frames are copied verbatim.\n * @param buffer - original artifact bytes.\n * @param nextCwd - canonical project directory for the moved session, or undefined.\n * @returns rewritten bytes.\n */\nexport function rewriteHeaderFrame(buffer, nextCwd) {\n\tconst { header, frames } = readHeader(buffer);\n\tconst updated = { ...header };\n\tif (nextCwd === undefined) delete updated.cwd;\n\telse updated.cwd = nextCwd;\n\tif (JSON.stringify(updated) === JSON.stringify(header)) return Buffer.from(buffer);\n\tconst frame0 = zstdCompressSync(Buffer.from(`${JSON.stringify(updated)}\\n`, 'utf8'), { params: CHECKSUM });\n\tscanFrames(frame0); // re-verify our own output before it ever touches the store\n\treturn Buffer.concat([frame0, buffer.subarray(frames[0].end)]);\n}\n", "/**\n * Trash bin for sessions and projects (\u00A711). Semantics pinned by the shipped\n * build: archiving is the reversible hide (slot kept, unarchive restores the\n * position), workspace deletion keeps every session log, and startup bootstrap\n * only re-claims sessions that are NOT archived \u2014 so \"archive all sessions +\n * delete the registration\" keeps a trashed project invisible until this ledger\n * restores it. All official effects run through a narrow registry adapter\n * (implemented in the host, faked in tests); the plugin-owned ledger snapshots\n * the placement, archive and pin state needed to restore them (\u00A711 TB1/TB5).\n *\n * `purge` is the hard-delete path the official surface has no command for\n * (\u00A711.1-1): it moves the session artifact into `pending-purge/` (same volume,\n * atomic placement, undoable until the last step), drops the projection-cache\n * record and vault manifest entries, and only then removes the ledger item.\n */\nimport { readdir, rm, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { encodeSegment } from './paths.mjs';\nimport { relocate } from './move.mjs';\n\nexport class TrashError extends Error {\n\tcode;\n\tconstructor(code, message) { super(message); this.name = 'TrashError'; this.code = code; }\n}\n\nconst DAY = 24 * 60 * 60 * 1000;\n\nexport class TrashEngine {\n\t/**\n\t * @param deps - {adapter: RegistryAdapter, sessions?: () => liveSessionIds,\n\t * sessionsRoot, projcacheRoot, kitRoot, ledger: JsonDocument, vault?,\n\t * journal?: () => activeJournalEntry|null, settings?: () => trashSettings, now}\n\t */\n\tconstructor(deps) {\n\t\tthis.deps = deps;\n\t\tthis.ledger = deps.ledger;\n\t\tthis.now = deps.now ?? (() => Date.now());\n\t}\n\n\tasync items() { return (await this.ledger.read()).items.map(publicItem); }\n\n\t#guardFree(id) {\n\t\tif (typeof this.deps.journal === 'function' && this.deps.journal() !== null) {\n\t\t\tthrow new TrashError('journal-active', 'a journaled move is unresolved; finish or roll it back first');\n\t\t}\n\t\tconst live = this.deps.sessions?.() ?? [];\n\t\tif (live.includes(id)) throw new TrashError('session-busy', `session ${id} is live; open it and stop its work first`);\n\t}\n\n\t#find(state, predicate) { return state.items.find(predicate); }\n\n\t/** \u00A711 TB3: trash one session (archive + ledger; already-archived sessions only enter the bin). */\n\tasync trashSession(sessionId, { force = false } = {}) {\n\t\tthis.#guardFree(sessionId);\n\t\tconst adapter = this.deps.adapter;\n\t\tconst state = await this.ledger.read();\n\t\tif (this.#find(state, item => item.kind === 'session' && item.id2 === sessionId)) throw new TrashError('already-trashed', 'this session is already in the trash bin');\n\t\tif (!await adapter.sessionKnown(sessionId)) throw new TrashError('session-unknown', `session ${sessionId} is unknown to the registry`);\n\t\tconst archived = await adapter.archivedSet();\n\t\tconst pinned = await adapter.pinnedSet();\n\t\tconst { workspaceId, beforeId, title } = await adapter.locateSession(sessionId);\n\t\tif (!archived.has(sessionId)) await adapter.archiveSession(sessionId, force ? { stopActivity: true } : {});\n\t\tconst item = {\n\t\t\tid: randomUUID(), kind: 'session', id2: sessionId, trashedAt: new Date(this.now()).toISOString(),\n\t\t\tsnapshot: { sessionId, workspaceId: workspaceId ?? null, beforeId: beforeId ?? null,\n\t\t\t\tpreviouslyArchived: archived.has(sessionId), previouslyPinned: pinned.has(sessionId), title: title ?? null },\n\t\t};\n\t\tstate.items.push(item);\n\t\tawait this.ledger.write(state);\n\t\treturn { ok: true, itemId: item.id };\n\t}\n\n\t/** \u00A711 TB4: trash a project (archive all its sessions, snapshot order/state, delete registration). */\n\tasync trashWorkspace(workspaceId, { force = false } = {}) {\n\t\tconst adapter = this.deps.adapter;\n\t\tconst state = await this.ledger.read();\n\t\tif (this.#find(state, item => item.kind === 'workspace' && item.id2 === workspaceId)) throw new TrashError('already-trashed', 'this project is already in the trash bin');\n\t\tconst snapshot = await adapter.workspaceSnapshot(workspaceId);\n\t\tif (snapshot === undefined) throw new TrashError('workspace-unknown', `workspace ${workspaceId} not found`);\n\t\tconst live = new Set(this.deps.sessions?.() ?? []);\n\t\tif (!force && snapshot.sessions.some(row => live.has(row.sessionId))) throw new TrashError('workspace-busy', 'the project has live sessions; use force to stop and trash them');\n\t\tfor (const row of snapshot.sessions) {\n\t\t\tif (row.previouslyArchived) continue;\n\t\t\tawait adapter.archiveSession(row.sessionId, live.has(row.sessionId) ? { stopActivity: true } : {});\n\t\t}\n\t\tconst item = { id: randomUUID(), kind: 'workspace', id2: workspaceId, trashedAt: new Date(this.now()).toISOString(), snapshot };\n\t\tstate.items.push(item);\n\t\tawait this.ledger.write(state);\n\t\tawait adapter.deleteWorkspace(workspaceId);\n\t\treturn { ok: true, itemId: item.id };\n\t}\n\n\t/** \u00A711 TB5 idempotent restore: converge to the snapshot; failures propagate so the item survives for retry. */\n\tasync restore(itemId) {\n\t\tconst adapter = this.deps.adapter;\n\t\tconst state = await this.ledger.read();\n\t\tconst item = this.#find(state, row => row.id === itemId);\n\t\tif (item === undefined) throw new TrashError('item-unknown', `no trash item ${itemId}`);\n\t\tif (item.kind === 'session') await this.#restoreSession(adapter, item.snapshot);\n\t\telse await this.#restoreWorkspace(adapter, item.snapshot);\n\t\t// Restoring changes where a session lives in the sidebar; refresh that one\n\t\t// placement so the row reappears without a DSH restart (other sessions\n\t\t// are untouched).\n\t\tconst affected = item.kind === 'session' ? [item.snapshot.sessionId]\n\t\t\t: item.snapshot.sessions.map(row => row.sessionId);\n\t\tfor (const sessionId of affected) await adapter.refreshPlacement?.(sessionId)?.catch?.(() => {});\n\t\tstate.items = state.items.filter(row => row.id !== itemId);\n\t\tawait this.ledger.write(state);\n\t\treturn { ok: true };\n\t}\n\n\tasync #restoreSession(adapter, snapshot) {\n\t\tconst { sessionId, workspaceId, beforeId, previouslyArchived, previouslyPinned } = snapshot;\n\t\tif (!previouslyArchived) await adapter.unarchiveSession(sessionId);\n\t\tif (workspaceId !== null) await adapter.repairSlot(sessionId, workspaceId, beforeId);\n\t\tif (previouslyPinned) await adapter.pinSession(sessionId);\n\t}\n\n\tasync #restoreWorkspace(adapter, snapshot) {\n\t\tawait adapter.restoreWorkspace(snapshot);\n\t\tfor (const row of snapshot.sessions) {\n\t\t\tif (!row.previouslyArchived) await adapter.unarchiveSession(row.sessionId);\n\t\t}\n\t\t// Pins are dropped by archiving (official rule); restore them for sessions\n\t\t// that were pinned before entering the bin.\n\t\tfor (const row of snapshot.sessions) {\n\t\t\tif (row.previouslyPinned && !row.previouslyArchived) await adapter.pinSession(row.sessionId).catch(() => {});\n\t\t}\n\t}\n\n\t/** \u00A711 TB6: hard-delete with staging so a crash never yields a half-deleted state. */\n\tasync purge(itemId) {\n\t\tconst adapter = this.deps.adapter;\n\t\tconst state = await this.ledger.read();\n\t\tconst item = this.#find(state, row => row.id === itemId);\n\t\tif (item === undefined) throw new TrashError('item-unknown', `no trash item ${itemId}`);\n\t\tconst sessionIds = item.kind === 'session' ? [item.snapshot.sessionId] : item.snapshot.sessions.map(row => row.sessionId);\n\t\tfor (const sessionId of sessionIds) this.#guardFree(sessionId);\n\t\tfor (const sessionId of sessionIds) {\n\t\t\tawait this.#stageArtifact(sessionId);\n\t\t\tif (adapter.purgeSession) await adapter.purgeSession(sessionId);\n\t\t}\n\t\tfor (const sessionId of sessionIds) {\n\t\t\tawait adapter.unpinSession(sessionId).catch(() => {});\n\t\t\tawait adapter.unarchiveSession(sessionId).catch(() => {});\n\t\t}\n\t\tif (item.kind === 'workspace') await adapter.deleteWorkspace(item.id2).catch(() => {});\n\t\tif (this.deps.vault?.dropSession) for (const sessionId of sessionIds) await this.deps.vault.dropSession(sessionId);\n\t\tstate.items = state.items.filter(row => row.id !== itemId);\n\t\tawait this.ledger.write(state);\n\t\treturn { ok: true, purged: sessionIds };\n\t}\n\n\tasync #stageArtifact(sessionId) {\n\t\tconst encoded = encodeSegment(sessionId);\n\t\tconst root = this.deps.sessionsRoot;\n\t\tlet found;\n\t\tfor (const project of await readdir(root).catch(() => [])) {\n\t\t\tif (project.startsWith('.')) continue;\n\t\t\ttry { if ((await stat(join(root, project, encoded))).isDirectory()) { found = join(root, project, encoded); break; } } catch {}\n\t\t}\n\t\tif (found === undefined) return; // already staged away (idempotent)\n\t\tconst staged = join(this.deps.kitRoot, 'pending-purge', `${sessionId}__${this.now()}`);\n\t\tawait relocate(found, staged);\n\t\tif (this.deps.projcacheRoot) await rm(join(this.deps.projcacheRoot, `${sessionId}.json`), { force: true });\n\t\t// Durable placement done; only now drop the real bytes.\n\t\tawait rm(staged, { recursive: true, force: true });\n\t}\n\n\t/** TTL sweep (\u00A711 T5). With autoPurge, expired items are hard-deleted; otherwise reported. */\n\tasync sweep({ now = this.now() } = {}) {\n\t\tconst state = await this.ledger.read();\n\t\tconst settings = this.deps.settings?.() ?? {};\n\t\tconst ttlDays = settings.ttlDays ?? 30;\n\t\tconst auto = settings.autoPurge === true;\n\t\tconst report = { expired: [], purged: [], skipped: [] };\n\t\tfor (const item of state.items) {\n\t\t\tconst trashedAt = Date.parse(item.trashedAt);\n\t\t\tif (!Number.isFinite(trashedAt) || now - trashedAt < ttlDays * DAY) continue;\n\t\t\treport.expired.push({ itemId: item.id, kind: item.kind, targetId: item.id2 });\n\t\t\tif (!auto) continue;\n\t\t\ttry { await this.purge(item.id); report.purged.push(item.id); }\n\t\t\tcatch (error) { report.skipped.push({ itemId: item.id, reason: String(error?.message ?? error) }); }\n\t\t}\n\t\treturn report;\n\t}\n}\n\nfunction publicItem(item) {\n\treturn { itemId: item.id, kind: item.kind, targetId: item.id2, trashedAt: item.trashedAt, snapshot: item.snapshot };\n}\n", "/**\n * Loopback management endpoint, same trust model as dsh-multi-router: exact\n * `/.well-known` route, host-header + socket must be loopback, Origin/\n * Sec-Fetch-Site cross-site rejected, writes need the per-process random key\n * the GET hands out (timing-safe compare). The handler is framework-free so the\n * host wires it into `ctx.webServer.register` and the CLI can call it directly.\n */\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\n\nexport const API_PATH = '/.well-known/dsh-session-toolbox';\nconst MAX_BODY = 4 * 1024 * 1024;\n\nclass ApiError extends Error {\n\tstatus;\n\tconstructor(status, message) { super(message); this.status = status; }\n}\n\nconst json = (res, status, value) => {\n\tres.writeHead(status, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });\n\tres.end(JSON.stringify(value));\n};\n\nconst hostIsLoopback = host => {\n\tconst name = String(host ?? '').split(':')[0];\n\treturn ['localhost', '127.0.0.1', '::1', '[::1]', ''].includes(name) || name.startsWith('127.');\n};\n\n/**\n * @param kit - {state(): snapshot, action(input): result, move, trash, settings, vaultManifest}\n */\nexport function apiHandler(kit) {\n\tconst key = randomBytes(32).toString('base64url');\n\treturn async (req, res) => {\n\t\ttry {\n\t\t\tconst remote = req.socket?.remoteAddress;\n\t\t\tif (!hostIsLoopback(req.headers.host) || (remote && !['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(remote))) throw new ApiError(403, '\u4EC5\u5141\u8BB8\u672C\u673A\u8BF7\u6C42');\n\t\t\tconst origin = req.headers.origin;\n\t\t\tif (origin && origin !== `http://${req.headers.host}` && origin !== `https://${req.headers.host}`) throw new ApiError(403, '\u6765\u6E90\u65E0\u6548');\n\t\t\tif (req.headers['sec-fetch-site'] === 'cross-site') throw new ApiError(403, '\u6765\u6E90\u65E0\u6548');\n\t\t\tif (req.method === 'GET') { json(res, 200, { ...await kit.state(), key }); return; }\n\t\t\tif (req.method !== 'POST') throw new ApiError(405, '\u65B9\u6CD5\u65E0\u6548');\n\t\t\tconst actual = Buffer.from(String(req.headers['x-session-toolbox-key'] ?? '')), expected = Buffer.from(key);\n\t\t\tif (actual.length !== expected.length || !timingSafeEqual(actual, expected)) throw new ApiError(401, '\u8BF7\u5237\u65B0\u9762\u677F\u540E\u91CD\u8BD5');\n\t\t\tif (!/^application\\/json(?:\\s*;|$)/i.test(String(req.headers['content-type'] ?? ''))) throw new ApiError(415, '\u9700\u8981 JSON');\n\t\t\tlet size = 0; const chunks = [];\n\t\t\tfor await (const chunk of req) { size += chunk.length; if (size > MAX_BODY) throw new ApiError(413, '\u8BF7\u6C42\u8FC7\u5927'); chunks.push(chunk); }\n\t\t\tlet input;\n\t\t\ttry { input = JSON.parse(Buffer.concat(chunks).toString('utf8')); } catch { throw new ApiError(400, 'JSON \u65E0\u6548'); }\n\t\t\tif (input === null || typeof input !== 'object' || typeof input.type !== 'string') throw new ApiError(400, '\u7F3A\u5C11\u64CD\u4F5C\u7C7B\u578B');\n\t\t\tjson(res, 200, { ...await kit.action(input), key });\n\t\t} catch (error) {\n\t\t\tjson(res, error instanceof ApiError ? error.status : 500, { error: error instanceof ApiError ? error.message : `\u64CD\u4F5C\u5931\u8D25\uFF1A${error?.message ?? error}` });\n\t\t}\n\t};\n}\n\n/** Shared dispatch so the host, the CLI and tests all speak one vocabulary. */\nexport async function dispatch(kit, input) {\n\tswitch (input.type) {\n\t\tcase 'preflight': return await kit.move.preflight({ sessionId: String(input.sessionId ?? ''), toCwd: String(input.toCwd ?? ''), createTarget: input.createTarget === true });\n\t\tcase 'move': return await kit.move.move(await kit.move.preflight({ sessionId: String(input.sessionId ?? ''), toCwd: String(input.toCwd ?? ''), createTarget: input.createTarget === true }));\n\t\tcase 'move.rollback': return await kit.move.rollback();\n\t\tcase 'move.complete': return await kit.move.complete();\n\t\tcase 'trash.list': return { items: await kit.trash.items() };\n\t\tcase 'trash.session': return await kit.trash.trashSession(String(input.sessionId ?? ''), { force: input.force === true });\n\t\tcase 'trash.workspace': return await kit.trash.trashWorkspace(String(input.workspaceId ?? ''), { force: input.force === true });\n\t\tcase 'trash.restore': return await kit.trash.restore(String(input.itemId ?? ''));\n\t\tcase 'trash.purge': return await kit.trash.purge(String(input.itemId ?? ''));\n\t\tcase 'trash.sweep': return await kit.trash.sweep();\n\t\tcase 'project.of': return { project: await kit.projectOf(String(input.sessionId ?? '')) };\n\t\tcase 'settings.update': return { settings: await kit.updateSettings(input.patch ?? {}) };\n\t\tcase 'vault.sync': return { records: (await kit.vaultSync(Array.isArray(input.records) ? input.records : [])).records.length };\n\t\tcase 'vault.sweep': {\n\t\t\tconst settings = await kit.settings.read();\n\t\t\tconst ttlMs = (settings.draftVault?.ttlDays ?? 7) * 24 * 60 * 60 * 1000;\n\t\t\tconst manifest = await kit.manifest.update(current => ({\n\t\t\t\tversion: 1,\n\t\t\t\trecords: current.records.filter(record => Date.now() - (record.createdAt ?? 0) <= ttlMs),\n\t\t\t}));\n\t\t\treturn { records: manifest.records.length };\n\t\t}\n\t\tdefault: throw new Error(`\u672A\u77E5\u7684\u64CD\u4F5C\u7C7B\u578B\uFF1A${input.type}`);\n\t}\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["constants", "mkdir", "readFile", "readdir", "stat", "dirname", "join", "join", "join", "constants", "mkdir", "dirname", "readdir", "stat", "join", "readFile", "readdir", "rm", "stat", "join", "item", "readdir", "stat", "join", "rm"]
7
+ }
package/lib/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  guardJournal,
8
8
  guardLedger,
9
9
  writeOwned
10
- } from "./chunk-QXW2NIKH.js";
10
+ } from "./chunk-EXE2TWBF.js";
11
11
 
12
12
  // src/cli.mjs
13
13
  import { readFile } from "node:fs/promises";
package/lib/client.js CHANGED
@@ -23,6 +23,7 @@ __export(client_exports, {
23
23
  KitPage: () => KitPage,
24
24
  MoveDialog: () => MoveDialog,
25
25
  MoveSessionMenuItem: () => MoveSessionMenuItem,
26
+ NOTICE_EVENT: () => NOTICE_EVENT,
26
27
  NS: () => NS,
27
28
  TrashSessionMenuItem: () => TrashSessionMenuItem,
28
29
  VaultBadge: () => VaultBadge,
@@ -39,7 +40,7 @@ var import_react = require("react");
39
40
  // package.json
40
41
  var package_default = {
41
42
  name: "dsh-session-toolbox",
42
- version: "0.1.11",
43
+ version: "0.1.12",
43
44
  repository: {
44
45
  type: "git",
45
46
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
@@ -494,6 +495,13 @@ var name = "dsh-session-toolbox-client";
494
495
  var inject = ["slots", "locale"];
495
496
  var NS = "dsh-session-toolbox";
496
497
  var ENDPOINT = `/.well-known/${NS}`;
498
+ var NOTICE_EVENT = "dsh-session-toolbox:notice";
499
+ function dispatchNotice(message) {
500
+ try {
501
+ window.dispatchEvent(new CustomEvent(NOTICE_EVENT, { detail: message }));
502
+ } catch {
503
+ }
504
+ }
497
505
  var vault;
498
506
  var directoryPicker;
499
507
  var store = { state: null, error: "", listeners: /* @__PURE__ */ new Set() };
@@ -593,6 +601,16 @@ function KitPage() {
593
601
  setFeedback(message);
594
602
  setTimeout(() => setFeedback(""), 8e3);
595
603
  };
604
+ (0, import_react.useEffect)(() => {
605
+ const onNotice = (event) => {
606
+ const message = typeof event?.detail === "string" ? event.detail : "";
607
+ if (message) say(message);
608
+ void loadState().catch(() => {
609
+ });
610
+ };
611
+ window.addEventListener(NOTICE_EVENT, onNotice);
612
+ return () => window.removeEventListener(NOTICE_EVENT, onNotice);
613
+ }, []);
596
614
  const act = async (input, ok) => {
597
615
  setBusy2(true);
598
616
  try {
@@ -887,8 +905,9 @@ function MoveDialog({ sessionId, close }) {
887
905
  setBusy2(true);
888
906
  setError("");
889
907
  try {
890
- await callHost({ type: "move", sessionId, toCwd, createTarget });
908
+ const result = await callHost({ type: "move", sessionId, toCwd, createTarget });
891
909
  close();
910
+ dispatchNotice(result?.deferred ? "\u6587\u4EF6\u5DF2\u8FC1\u79FB\uFF1BDSH \u8D26\u672C\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u6536\u655B\uFF0C\u5237\u65B0\u4FA7\u680F\u540E\u53EF\u89C1" : "\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5237\u65B0\u4FA7\u680F\u540E\u8BE5\u4F1A\u8BDD\u5C31\u4F1A\u51FA\u73B0\u5728\u76EE\u6807\u9879\u76EE\u4E0B");
892
911
  } catch (err) {
893
912
  setError(err.message);
894
913
  setBusy2(false);