dsh-vault 0.8.9 → 0.9.1

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/README-zh.md CHANGED
@@ -46,7 +46,7 @@ dsh-vault 是一个面向 DeepSeek Harness 的安全加密插件:把你在使
46
46
  | `vault_compare` | 逐字段比较两个条目(`onlyA`/`onlyB`/`differ`/`equal`)——只返回字段名,绝不返回密钥值 |
47
47
  | `vault_rename` | 一次调用即可重命名条目(`vault_update` 的快捷方式) |
48
48
  | `vault_delete` | 软删除条目(移入回收站,磁盘上仍加密保留) |
49
- | `vault_restore` / `vault_purge` | 从回收站恢复 / 从磁盘永久移除 |
49
+ | `vault_restore` / `vault_purge` / `vault_restore_recent` | 从回收站恢复 / 永久删除 / 撤销最近一次删除 |
50
50
  | `vault_lock` / `vault_unlock` | 显式锁定保险库(清空内存密钥)/ 重新解锁 |
51
51
  | `vault_totp` | 为存储的 otpSecret(或直接传入的 Base32/otpauth URI)生成当前 6 位动态验证码 |
52
52
  | `vault_generate_password` | 生成强随机密码(长度/字符集/分组可选)**或易记口令短语**(`passphrase: true`,EFF 词表,`words`/`separator`/`wordDigits`) |
@@ -63,6 +63,9 @@ dsh-vault 是一个面向 DeepSeek Harness 的安全加密插件:把你在使
63
63
  | `vault_quick_add` | 快速录入(标题 + 一个密钥),支持 tags/notes |
64
64
  | `vault_expiry` | 设置/清除过期(`expiresAt: 0` 移除) |
65
65
  | `vault_stats` | 概览计数,含 `trashCount`(不含密钥) |
66
+ | `vault_verify` | 校验单个条目或审计全部条目(`all: true`):按 kind 检查完整性、端口/过期合理性(不含密钥) |
67
+ | `vault_duplicates` | 查找重复分组:`mode` = `both`(默认)/ `title` / `content`(不含密钥) |
68
+ | `vault_report` | 可打印清单,含到期/轮换列与统计页脚(不含密钥) |
66
69
  | `vault_export` / `vault_import` | 整库加密备份/迁移(独立导出密码) |
67
70
  | `vault_fill` | 按 host/URL/用户名/标题匹配条目并返回其凭据 |
68
71
  | `vault_env` | 把标记 env 的条目(tags 含 `env`)渲染为 `KEY=VALUE` 行 |
@@ -153,6 +156,7 @@ tarball 自带预构建 `lib/` 产物,无需构建或 allowBuilds。
153
156
  | `autoCapture` | `false`(默认)。设为 `true` 时,系统提示词会指导模型识别对话中出现的凭据,并**按用户偏好**用 `vault_add` 提供保存。 |
154
157
  | `lockTimeoutSeconds` | 自动锁库:空闲超过该秒数后自动重新锁定(清空内存密钥),之后每次读写需 `vault_unlock`。`0`/缺省为禁用。 |
155
158
  | `exportPasswordEnv` | 存放 `vault_export`/`vault_import` 导出密码的环境变量名(绝不能作为模型参数传入)。 |
159
+ | `backupRetention` | 保留多少个加密备份(默认 10);`vault_backup` 自动清理更旧的副本。 |
156
160
 
157
161
  示例:
158
162
 
package/README.md CHANGED
@@ -48,7 +48,7 @@ Each record has a `title`, an optional `kind`, and any combination of fields:
48
48
  | `vault_compare` | Compare two entries field by field (`onlyA`/`onlyB`/`differ`/`equal`) — field names only, never secret values |
49
49
  | `vault_rename` | Rename an entry in one call (shortcut for `vault_update`) |
50
50
  | `vault_delete` | Soft-delete an entry (moves it to the trash, still encrypted on disk) |
51
- | `vault_restore` / `vault_purge` | Bring a trashed entry back / permanently remove it from disk |
51
+ | `vault_restore` / `vault_purge` / `vault_restore_recent` | Bring a trashed entry back / purge it / undo the last delete |
52
52
  | `vault_lock` / `vault_unlock` | Explicitly lock the vault (wipe the in-memory key) / re-unlock it |
53
53
  | `vault_totp` | Generate the current 6-digit code for a stored otpSecret (or a bare Base32 / otpauth URI) |
54
54
  | `vault_generate_password` | Generate a strong random password (length/classes/grouping) **or a memorable passphrase** (`passphrase: true`, EFF-style word list, `words`/`separator`/`wordDigits`) |
@@ -65,6 +65,9 @@ Each record has a `title`, an optional `kind`, and any combination of fields:
65
65
  | `vault_quick_add` | Fast capture (title + one secret) with optional tags/notes |
66
66
  | `vault_expiry` | Set/clear expiry (`expiresAt: 0` removes it) |
67
67
  | `vault_stats` | Overview counts incl. `trashCount` (no secrets) |
68
+ | `vault_verify` | Verify one entry or audit every entry (`all: true`) for per-kind completeness, port/expiry sanity (no secrets) |
69
+ | `vault_duplicates` | Find duplicate groups: `mode` = `both` (default) / `title` / `content` (no secrets) |
70
+ | `vault_report` | Printable inventory with expiry/rotation columns and a stats footer (no secrets) |
68
71
  | `vault_export` / `vault_import` | Portable encrypted backup/migration of the whole vault (separate export password) |
69
72
  | `vault_fill` | Find the entry matching a host/URL/username/title and return its credentials |
70
73
  | `vault_env` | Render env-flagged entries (tags contain `env`) as `KEY=VALUE` lines |
@@ -155,6 +158,7 @@ The tarball ships prebuilt `lib/` artifacts, so no build step or `allowBuilds` i
155
158
  | `autoCapture` | `false` (default). When `true`, the system prompt instructs the model to detect credentials shared in conversation and — per user preference — offer to save them with `vault_add`. |
156
159
  | `lockTimeoutSeconds` | Auto-lock: after this many seconds of inactivity the vault re-locks (key wiped) and every read/write requires `vault_unlock`. `0`/absent disables. |
157
160
  | `exportPasswordEnv` | Environment variable holding the export/import password for `vault_export`/`vault_import` (never pass it as a model argument). |
161
+ | `backupRetention` | How many encrypted backups to keep (default 10); `vault_backup` prunes older copies. |
158
162
 
159
163
  Example:
160
164
 
package/lib/client.js CHANGED
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  //#region \0dsh-css:/Users/zhipeng/Desktop/work/dsh test/dsh-vault/src/client/VaultSection.module.css.mjs
10
- const css = ".t_j5va_section{flex-direction:column;gap:12px;padding:16px 0;display:flex}.t_j5va_intro{opacity:.75;margin:0;font-size:13px}.t_j5va_toolbar{align-items:center;gap:8px;display:flex}.t_j5va_searchBox{flex:1;display:flex}.t_j5va_searchBox input{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;flex:1;padding:6px 10px;font-size:13px}.t_j5va_addButton,.t_j5va_saveButton{background:var(--color-accent,#4f8cff);color:#fff;cursor:pointer;border:1px solid #0000;border-radius:6px;padding:6px 12px;font-size:13px}.t_j5va_addButton:disabled,.t_j5va_saveButton:disabled,.t_j5va_rowActions button:disabled{opacity:.5;cursor:not-allowed}.t_j5va_list{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}.t_j5va_row{border:1px solid var(--color-border,#333);border-radius:8px;justify-content:space-between;align-items:center;gap:12px;padding:8px 10px;display:flex}.t_j5va_rowMain{flex-direction:column;gap:2px;min-width:0;display:flex}.t_j5va_title{font-size:14px;font-weight:600}.t_j5va_identity{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.t_j5va_totp{font-family:var(--font-mono,monospace);color:var(--color-accent,#4f8cff);font-size:13px}.t_j5va_rowActions{flex-shrink:0;align-items:center;gap:6px;display:flex}.t_j5va_rowActions button{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 8px;font-size:12px}.t_j5va_deleteButton{color:#e5484d}.t_j5va_copied{color:#30a46c;font-size:12px}.t_j5va_empty,.t_j5va_status,.t_j5va_error{opacity:.8;font-size:13px}.t_j5va_modeReadonly,.t_j5va_modeAsk,.t_j5va_modeAuto{border-radius:999px;width:fit-content;margin:0;padding:4px 10px;font-size:12px;display:inline-block}.t_j5va_modeReadonly{color:#e5484d;border:1px solid #e5484d80}.t_j5va_modeAsk{color:#f5a524;border:1px solid #f5a52480}.t_j5va_modeAuto{color:#30a46c;border:1px solid #30a46c80}.t_j5va_policyBar{flex-direction:column;align-items:flex-start;gap:6px;display:flex}.t_j5va_policyField{align-items:center;gap:8px;font-size:13px;display:flex}.t_j5va_policyField select{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:4px 8px;font-size:13px}.t_j5va_emptyBox{flex-direction:column;gap:4px;display:flex}.t_j5va_emptyHint{opacity:.65;margin:0;font-size:12px}.t_j5va_error{color:#e5484d}.t_j5va_editor{border:1px solid var(--color-border,#444);border-radius:8px;padding:12px}.t_j5va_editorBody{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px 12px;display:grid}.t_j5va_checkField{align-items:center;gap:8px;font-size:13px;display:inline-flex}.t_j5va_field{flex-direction:column;gap:4px;font-size:12px;display:flex}.t_j5va_field input,.t_j5va_field select{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:5px 8px;font-size:13px}.t_j5va_editorActions{justify-content:flex-end;gap:8px;margin-top:12px;display:flex}.t_j5va_editorActions button{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:6px 12px;font-size:13px}.t_j5va_reportBox{border:1px solid #f5a52466;border-radius:8px;flex-direction:column;gap:2px;padding:8px 10px;font-size:12px;display:flex}.t_j5va_reportTitle{margin:0;font-weight:600}.t_j5va_reportLine{opacity:.8;margin:0}.t_j5va_reportSub{color:var(--dsh-color-text-2,#666);margin:2px 0 0 10px;font-size:12px}.t_j5va_trashButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;align-self:flex-start;padding:4px 8px;font-size:12px}.t_j5va_secretField{gap:4px;display:flex}.t_j5va_secretField input{flex:1;min-width:0}.t_j5va_revealButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:0 8px;font-size:12px}.t_j5va_kindFilter{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:6px 8px;font-size:13px}.t_j5va_highBadge{color:#e5484d;vertical-align:middle;background:#e5484d26;border-radius:4px;margin-left:6px;padding:1px 6px;font-size:10px;font-weight:600}.t_j5va_pinStar{color:#f5a524;margin-right:4px}.t_j5va_clearButton{color:inherit;cursor:pointer;opacity:.6;background:0 0;border:none;padding:0 8px;font-size:16px}.t_j5va_field textarea{border:1px solid var(--color-border,#444);color:inherit;resize:vertical;background:0 0;border-radius:6px;padding:5px 8px;font-family:inherit;font-size:13px}.t_j5va_row{transition:background .12s}.t_j5va_row:hover{background:var(--color-hover,#80808014)}.t_j5va_healthBar{flex-wrap:wrap;gap:6px;margin:0 0 10px;display:flex}.t_j5va_badge{border:1px solid var(--dsh-color-border,#ddd);background:var(--dsh-color-surface-2,#f7f7f8);color:var(--dsh-color-text-2,#555);border-radius:999px;padding:4px 10px;font-size:12px;line-height:1}.t_j5va_backupButton{border:1px solid var(--dsh-color-border,#ccc);background:var(--dsh-color-surface-2,#f7f7f8);cursor:pointer;border-radius:999px;padding:4px 10px;font-size:12px;line-height:1}.t_j5va_badgeOk{color:#2e9e5b;border-color:#2e9e5b}.t_j5va_badgeWarn{color:#b57f1a;border-color:#c98a1b}.t_j5va_rowDue{border-left:3px solid #c98a1b}.t_j5va_rowExpired{border-left:3px solid #cf3d3d}.t_j5va_dueBadge{border-radius:999px;margin-left:6px;padding:3px 8px;font-size:11px;font-weight:600;line-height:1}.t_j5va_badgeDanger{color:#c03a3a;border-color:#cf3d3d}.t_j5va_footer{opacity:.6;margin:0;font-size:12px}.t_j5va_kindIcon{margin-right:4px;font-size:12px}.t_j5va_dangerButton{color:#e5484d;cursor:pointer;background:0 0;border:1px solid #e5484d80;border-radius:6px;align-self:flex-start;padding:4px 8px;font-size:12px}.t_j5va_rowMain{cursor:pointer}.t_j5va_detailBox{background:var(--color-hover,#8080800f);border-radius:6px;flex-wrap:wrap;grid-column:1/-1;gap:4px 12px;padding:6px 8px;font-size:12px;display:flex}.t_j5va_detailItem{opacity:.85}.t_j5va_sortButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:6px 10px;font-size:13px}";
10
+ const css = ".t_j5va_section{flex-direction:column;gap:12px;padding:16px 0;display:flex}.t_j5va_intro{opacity:.75;margin:0;font-size:13px}.t_j5va_toolbar{align-items:center;gap:8px;display:flex}.t_j5va_searchBox{flex:1;display:flex}.t_j5va_searchBox input{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;flex:1;padding:6px 10px;font-size:13px}.t_j5va_addButton,.t_j5va_saveButton{background:var(--color-accent,#4f8cff);color:#fff;cursor:pointer;border:1px solid #0000;border-radius:6px;padding:6px 12px;font-size:13px}.t_j5va_addButton:disabled,.t_j5va_saveButton:disabled,.t_j5va_rowActions button:disabled{opacity:.5;cursor:not-allowed}.t_j5va_list{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}.t_j5va_row{border:1px solid var(--color-border,#333);border-radius:8px;justify-content:space-between;align-items:center;gap:12px;padding:8px 10px;display:flex}.t_j5va_rowMain{flex-direction:column;gap:2px;min-width:0;display:flex}.t_j5va_title{font-size:14px;font-weight:600}.t_j5va_identity{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.t_j5va_totp{font-family:var(--font-mono,monospace);color:var(--color-accent,#4f8cff);font-size:13px}.t_j5va_totpRing{vertical-align:-2px;margin-right:4px;display:inline-block}.t_j5va_rowActions{flex-shrink:0;align-items:center;gap:6px;display:flex}.t_j5va_rowActions button{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 8px;font-size:12px}.t_j5va_deleteButton{color:#e5484d}.t_j5va_copied{color:#30a46c;font-size:12px}.t_j5va_empty,.t_j5va_status,.t_j5va_error{opacity:.8;font-size:13px}.t_j5va_modeReadonly,.t_j5va_modeAsk,.t_j5va_modeAuto{border-radius:999px;width:fit-content;margin:0;padding:4px 10px;font-size:12px;display:inline-block}.t_j5va_modeReadonly{color:#e5484d;border:1px solid #e5484d80}.t_j5va_modeAsk{color:#f5a524;border:1px solid #f5a52480}.t_j5va_modeAuto{color:#30a46c;border:1px solid #30a46c80}.t_j5va_policyBar{flex-direction:column;align-items:flex-start;gap:6px;display:flex}.t_j5va_policyField{align-items:center;gap:8px;font-size:13px;display:flex}.t_j5va_policyField select{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:4px 8px;font-size:13px}.t_j5va_emptyBox{flex-direction:column;gap:4px;display:flex}.t_j5va_emptyHint{opacity:.65;margin:0;font-size:12px}.t_j5va_error{color:#e5484d}.t_j5va_editor{border:1px solid var(--color-border,#444);border-radius:8px;padding:12px}.t_j5va_editorBody{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px 12px;display:grid}.t_j5va_checkField{align-items:center;gap:8px;font-size:13px;display:inline-flex}.t_j5va_field{flex-direction:column;gap:4px;font-size:12px;display:flex}.t_j5va_field input,.t_j5va_field select{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:5px 8px;font-size:13px}.t_j5va_editorActions{justify-content:flex-end;gap:8px;margin-top:12px;display:flex}.t_j5va_editorActions button{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:6px 12px;font-size:13px}.t_j5va_reportBox{border:1px solid #f5a52466;border-radius:8px;flex-direction:column;gap:2px;padding:8px 10px;font-size:12px;display:flex}.t_j5va_reportTitle{margin:0;font-weight:600}.t_j5va_reportLine{opacity:.8;margin:0}.t_j5va_reportSub{color:var(--dsh-color-text-2,#666);margin:2px 0 0 10px;font-size:12px}.t_j5va_trashButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;align-self:flex-start;padding:4px 8px;font-size:12px}.t_j5va_secretField{gap:4px;display:flex}.t_j5va_secretField input{flex:1;min-width:0}.t_j5va_revealButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:0 8px;font-size:12px}.t_j5va_kindFilter{border:1px solid var(--color-border,#444);color:inherit;background:0 0;border-radius:6px;padding:6px 8px;font-size:13px}.t_j5va_highBadge{color:#e5484d;vertical-align:middle;background:#e5484d26;border-radius:4px;margin-left:6px;padding:1px 6px;font-size:10px;font-weight:600}.t_j5va_pinStar{color:#f5a524;margin-right:4px}.t_j5va_clearButton{color:inherit;cursor:pointer;opacity:.6;background:0 0;border:none;padding:0 8px;font-size:16px}.t_j5va_field textarea{border:1px solid var(--color-border,#444);color:inherit;resize:vertical;background:0 0;border-radius:6px;padding:5px 8px;font-family:inherit;font-size:13px}.t_j5va_row{transition:background .12s}.t_j5va_row:hover{background:var(--color-hover,#80808014)}.t_j5va_healthBar{flex-wrap:wrap;gap:6px;margin:0 0 10px;display:flex}.t_j5va_badge{border:1px solid var(--dsh-color-border,#ddd);background:var(--dsh-color-surface-2,#f7f7f8);color:var(--dsh-color-text-2,#555);border-radius:999px;padding:4px 10px;font-size:12px;line-height:1}.t_j5va_backupButton{border:1px solid var(--dsh-color-border,#ccc);background:var(--dsh-color-surface-2,#f7f7f8);cursor:pointer;border-radius:999px;padding:4px 10px;font-size:12px;line-height:1}.t_j5va_badgeOk{color:#2e9e5b;border-color:#2e9e5b}.t_j5va_badgeWarn{color:#b57f1a;border-color:#c98a1b}.t_j5va_rowDue{border-left:3px solid #c98a1b}.t_j5va_rowExpired{border-left:3px solid #cf3d3d}.t_j5va_dueBadge{border-radius:999px;margin-left:6px;padding:3px 8px;font-size:11px;font-weight:600;line-height:1}.t_j5va_badgeDanger{color:#c03a3a;border-color:#cf3d3d}.t_j5va_footer{opacity:.6;margin:0;font-size:12px}.t_j5va_kindIcon{margin-right:4px;font-size:12px}.t_j5va_dangerButton{color:#e5484d;cursor:pointer;background:0 0;border:1px solid #e5484d80;border-radius:6px;align-self:flex-start;padding:4px 8px;font-size:12px}.t_j5va_rowMain{cursor:pointer}.t_j5va_detailBox{background:var(--color-hover,#8080800f);border-radius:6px;flex-wrap:wrap;grid-column:1/-1;gap:4px 12px;padding:6px 8px;font-size:12px;display:flex}.t_j5va_detailItem{opacity:.85}.t_j5va_sortButton{border:1px solid var(--color-border,#444);color:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:6px 10px;font-size:13px}";
11
11
  const tagId = "dsh-vault/VaultSection.module.css";
12
12
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
13
13
  const tag = document.createElement("style");
@@ -17,62 +17,63 @@ window.__ModuleLoader__.load({
17
17
  document.head.appendChild(tag);
18
18
  }
19
19
  var VaultSection_module_css_default = {
20
- "badge": "t_j5va_badge",
21
- "rowDue": "t_j5va_rowDue",
22
- "healthBar": "t_j5va_healthBar",
20
+ "intro": "t_j5va_intro",
23
21
  "rowActions": "t_j5va_rowActions",
24
- "row": "t_j5va_row",
25
- "totp": "t_j5va_totp",
26
- "rowMain": "t_j5va_rowMain",
22
+ "clearButton": "t_j5va_clearButton",
23
+ "policyField": "t_j5va_policyField",
24
+ "footer": "t_j5va_footer",
25
+ "modeReadonly": "t_j5va_modeReadonly",
26
+ "kindFilter": "t_j5va_kindFilter",
27
+ "badgeDanger": "t_j5va_badgeDanger",
27
28
  "error": "t_j5va_error",
28
- "policyBar": "t_j5va_policyBar",
29
- "saveButton": "t_j5va_saveButton",
30
29
  "checkField": "t_j5va_checkField",
31
- "reportLine": "t_j5va_reportLine",
32
- "editor": "t_j5va_editor",
33
- "editorBody": "t_j5va_editorBody",
34
- "kindFilter": "t_j5va_kindFilter",
35
- "detailBox": "t_j5va_detailBox",
30
+ "reportTitle": "t_j5va_reportTitle",
36
31
  "title": "t_j5va_title",
32
+ "backupButton": "t_j5va_backupButton",
33
+ "dueBadge": "t_j5va_dueBadge",
34
+ "emptyHint": "t_j5va_emptyHint",
35
+ "editor": "t_j5va_editor",
36
+ "modeAsk": "t_j5va_modeAsk",
37
+ "searchBox": "t_j5va_searchBox",
38
+ "list": "t_j5va_list",
37
39
  "trashButton": "t_j5va_trashButton",
40
+ "reportSub": "t_j5va_reportSub",
41
+ "revealButton": "t_j5va_revealButton",
42
+ "badgeOk": "t_j5va_badgeOk",
43
+ "copied": "t_j5va_copied",
44
+ "rowExpired": "t_j5va_rowExpired",
38
45
  "empty": "t_j5va_empty",
39
- "pinStar": "t_j5va_pinStar",
40
- "clearButton": "t_j5va_clearButton",
41
- "modeAsk": "t_j5va_modeAsk",
42
- "deleteButton": "t_j5va_deleteButton",
43
- "toolbar": "t_j5va_toolbar",
44
- "emptyBox": "t_j5va_emptyBox",
46
+ "healthBar": "t_j5va_healthBar",
47
+ "sortButton": "t_j5va_sortButton",
48
+ "badge": "t_j5va_badge",
49
+ "identity": "t_j5va_identity",
45
50
  "addButton": "t_j5va_addButton",
46
- "copied": "t_j5va_copied",
47
- "modeReadonly": "t_j5va_modeReadonly",
48
- "reportTitle": "t_j5va_reportTitle",
49
- "revealButton": "t_j5va_revealButton",
50
- "badgeDanger": "t_j5va_badgeDanger",
51
- "footer": "t_j5va_footer",
52
51
  "modeAuto": "t_j5va_modeAuto",
53
- "reportSub": "t_j5va_reportSub",
54
- "dangerButton": "t_j5va_dangerButton",
55
- "detailItem": "t_j5va_detailItem",
56
- "identity": "t_j5va_identity",
57
- "sortButton": "t_j5va_sortButton",
58
- "field": "t_j5va_field",
59
- "reportBox": "t_j5va_reportBox",
60
- "dueBadge": "t_j5va_dueBadge",
61
- "searchBox": "t_j5va_searchBox",
62
- "emptyHint": "t_j5va_emptyHint",
63
- "editorActions": "t_j5va_editorActions",
64
- "list": "t_j5va_list",
52
+ "status": "t_j5va_status",
65
53
  "section": "t_j5va_section",
66
- "policyField": "t_j5va_policyField",
67
- "secretField": "t_j5va_secretField",
54
+ "emptyBox": "t_j5va_emptyBox",
55
+ "editorActions": "t_j5va_editorActions",
56
+ "detailBox": "t_j5va_detailBox",
57
+ "toolbar": "t_j5va_toolbar",
58
+ "totp": "t_j5va_totp",
59
+ "rowMain": "t_j5va_rowMain",
68
60
  "highBadge": "t_j5va_highBadge",
69
- "intro": "t_j5va_intro",
70
- "status": "t_j5va_status",
71
- "backupButton": "t_j5va_backupButton",
72
- "badgeOk": "t_j5va_badgeOk",
61
+ "reportLine": "t_j5va_reportLine",
62
+ "kindIcon": "t_j5va_kindIcon",
63
+ "editorBody": "t_j5va_editorBody",
64
+ "secretField": "t_j5va_secretField",
65
+ "rowDue": "t_j5va_rowDue",
66
+ "detailItem": "t_j5va_detailItem",
73
67
  "badgeWarn": "t_j5va_badgeWarn",
74
- "rowExpired": "t_j5va_rowExpired",
75
- "kindIcon": "t_j5va_kindIcon"
68
+ "deleteButton": "t_j5va_deleteButton",
69
+ "totpRing": "t_j5va_totpRing",
70
+ "policyBar": "t_j5va_policyBar",
71
+ "dangerButton": "t_j5va_dangerButton",
72
+ "field": "t_j5va_field",
73
+ "saveButton": "t_j5va_saveButton",
74
+ "reportBox": "t_j5va_reportBox",
75
+ "row": "t_j5va_row",
76
+ "pinStar": "t_j5va_pinStar"
76
77
  };
77
78
  //#endregion
78
79
  //#region src/client/VaultSection.tsx
@@ -191,7 +192,7 @@ window.__ModuleLoader__.load({
191
192
  }
192
193
  /** Render the Vault settings section. */
193
194
  function VaultSection(props) {
194
- const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp } = props;
195
+ const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, duplicates, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp } = props;
195
196
  const searchId = (0, react.useId)();
196
197
  const [query, setQuery] = (0, react.useState)("");
197
198
  const [state, setState] = (0, react.useState)({ status: "loading" });
@@ -200,7 +201,8 @@ window.__ModuleLoader__.load({
200
201
  const [busy, setBusy] = (0, react.useState)(false);
201
202
  const [message, setMessage] = (0, react.useState)(null);
202
203
  const [copiedId, setCopiedId] = (0, react.useState)(null);
203
- const [codeMap, setCodeMap] = (0, react.useState)({});
204
+ const [totpMap, setTotpMap] = (0, react.useState)({});
205
+ const [nowTick, setNowTick] = (0, react.useState)(Date.now());
204
206
  const [tagsDraft, setTagsDraft] = (0, react.useState)("");
205
207
  const [fieldsDraft, setFieldsDraft] = (0, react.useState)("");
206
208
  const [revealed, setRevealed] = (0, react.useState)({});
@@ -217,6 +219,7 @@ window.__ModuleLoader__.load({
217
219
  const [vaultStats, setVaultStats] = (0, react.useState)(null);
218
220
  const [backupInfo, setBackupInfo] = (0, react.useState)(null);
219
221
  const [recentEntries, setRecentEntries] = (0, react.useState)([]);
222
+ const [dupGroups, setDupGroups] = (0, react.useState)(0);
220
223
  const readonly = policy?.accessMode === "readonly";
221
224
  /** Run an encrypted backup now and refresh the backup-age badge. */
222
225
  async function backupNow() {
@@ -272,6 +275,10 @@ window.__ModuleLoader__.load({
272
275
  window.clearTimeout(timer);
273
276
  };
274
277
  }, [refresh]);
278
+ (0, react.useEffect)(() => {
279
+ const timer = window.setInterval(() => setNowTick(Date.now()), 1e3);
280
+ return () => window.clearInterval(timer);
281
+ }, []);
275
282
  (0, react.useEffect)(() => {
276
283
  let current = true;
277
284
  const load = () => {
@@ -280,8 +287,10 @@ window.__ModuleLoader__.load({
280
287
  backupStatus().catch(() => null),
281
288
  rotation().catch(() => []),
282
289
  health().catch(() => null),
283
- recent().catch(() => [])
284
- ]).then(([st, bk, rot, hl, rc]) => {
290
+ recent().catch(() => []),
291
+ duplicates().catch(() => null)
292
+ ]).then(([st, bk, rot, hl, rc, dp]) => {
293
+ if (dp !== null && typeof dp === "object" && dp.groups !== void 0) setDupGroups(dp.groups);
285
294
  if (!current) return;
286
295
  if (st !== null) setVaultStats(st);
287
296
  if (bk !== null) setBackupInfo(bk);
@@ -314,7 +323,8 @@ window.__ModuleLoader__.load({
314
323
  backupStatus,
315
324
  rotation,
316
325
  health,
317
- recent
326
+ recent,
327
+ duplicates
318
328
  ]);
319
329
  /** Open the editor for a new entry. */
320
330
  function startCreate() {
@@ -458,14 +468,20 @@ window.__ModuleLoader__.load({
458
468
  setBusy(true);
459
469
  try {
460
470
  const result = await totp(id);
461
- setCodeMap((previous) => ({
471
+ setTotpMap((previous) => ({
462
472
  ...previous,
463
- [id]: `${result.code} (${result.secondsRemaining}s)`
473
+ [id]: {
474
+ code: result.code,
475
+ until: Date.now() + result.secondsRemaining * 1e3
476
+ }
464
477
  }));
465
478
  } catch {
466
- setCodeMap((previous) => ({
479
+ setTotpMap((previous) => ({
467
480
  ...previous,
468
- [id]: t("error")
481
+ [id]: {
482
+ code: t("error"),
483
+ until: 0
484
+ }
469
485
  }));
470
486
  } finally {
471
487
  setBusy(false);
@@ -712,22 +728,28 @@ window.__ModuleLoader__.load({
712
728
  item.due === "expired" ? ` (${t("dueExpired")})` : item.due === "soon" ? ` (${t("dueExpiring")} ${item.daysLeft ?? 0}d)` : ` (${t("dueNow")})`
713
729
  ]
714
730
  }, i))] }),
715
- report.weak.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
731
+ report.weak.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
716
732
  className: VaultSection_module_css_default.reportLine,
717
733
  children: [
718
734
  t("reportWeak"),
719
735
  ": ",
720
736
  report.weak.length
721
737
  ]
722
- }),
723
- report.reused.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
738
+ }), report.weak.slice(0, 5).map((item, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
739
+ className: VaultSection_module_css_default.reportSub,
740
+ children: ["· ", String(item.title ?? "?")]
741
+ }, i))] }),
742
+ report.reused.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
724
743
  className: VaultSection_module_css_default.reportLine,
725
744
  children: [
726
745
  t("reportReused"),
727
746
  ": ",
728
747
  report.reused.length
729
748
  ]
730
- })
749
+ }), report.reused.slice(0, 3).map((group, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
750
+ className: VaultSection_module_css_default.reportSub,
751
+ children: ["· ", (group.entries ?? []).slice(0, 3).map((e) => String(e.title ?? "?")).join(" / ")]
752
+ }, i))] })
731
753
  ]
732
754
  }),
733
755
  state.status === "ready" && (state.entries.length > 0 || trashEntries.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -802,7 +824,10 @@ window.__ModuleLoader__.load({
802
824
  state.status === "ready" && state.entries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
803
825
  className: VaultSection_module_css_default.list,
804
826
  children: state.entries.filter((entry) => (kindFilter === "" || entry.kind === kindFilter) && (tagFilter === "" || (entry.tags ?? []).includes(tagFilter))).sort((a, b) => sortBy === "alpha" ? a.title.localeCompare(b.title) : 0).map((entry) => {
805
- const code = codeMap[entry.id];
827
+ const totpInfo = totpMap[entry.id];
828
+ const remaining = totpInfo !== void 0 && totpInfo.until > 0 ? Math.max(0, Math.ceil((totpInfo.until - nowTick) / 1e3)) : void 0;
829
+ const frac = remaining !== void 0 ? remaining / 30 : 0;
830
+ const code = totpInfo?.code;
806
831
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
807
832
  className: `${VaultSection_module_css_default.row}${dueMap[entry.id] !== void 0 ? ` ${dueMap[entry.id].due === "expired" ? VaultSection_module_css_default.rowExpired : VaultSection_module_css_default.rowDue}` : ""}`,
808
833
  children: [
@@ -812,10 +837,14 @@ window.__ModuleLoader__.load({
812
837
  children: [
813
838
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
814
839
  className: VaultSection_module_css_default.title,
840
+ style: entry.color !== void 0 && entry.color !== "" ? {
841
+ borderLeft: `3px solid ${entry.color}`,
842
+ paddingLeft: 6
843
+ } : void 0,
815
844
  children: [
816
845
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
817
846
  className: VaultSection_module_css_default.kindIcon,
818
- children: kindIcon(entry.kind)
847
+ children: entry.icon ?? kindIcon(entry.kind)
819
848
  }),
820
849
  entry.favorite && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
821
850
  className: VaultSection_module_css_default.pinStar,
@@ -837,9 +866,37 @@ window.__ModuleLoader__.load({
837
866
  className: VaultSection_module_css_default.identity,
838
867
  children: identityLine(entry)
839
868
  }),
840
- code !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
869
+ code !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
841
870
  className: VaultSection_module_css_default.totp,
842
- children: code
871
+ children: [
872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
873
+ className: VaultSection_module_css_default.totpRing,
874
+ width: "16",
875
+ height: "16",
876
+ viewBox: "0 0 16 16",
877
+ "aria-hidden": "true",
878
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
879
+ cx: "8",
880
+ cy: "8",
881
+ r: "6.5",
882
+ fill: "none",
883
+ stroke: "var(--dsh-color-border, #ddd)",
884
+ strokeWidth: "2"
885
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
886
+ cx: "8",
887
+ cy: "8",
888
+ r: "6.5",
889
+ fill: "none",
890
+ stroke: remaining !== void 0 && remaining <= 5 ? "#cf3d3d" : "#2e9e5b",
891
+ strokeWidth: "2",
892
+ strokeLinecap: "round",
893
+ strokeDasharray: `${(frac * 2 * Math.PI * 6.5).toFixed(1)} ${(2 * Math.PI * 6.5).toFixed(1)}`,
894
+ transform: "rotate(-90 8 8)"
895
+ })]
896
+ }),
897
+ code,
898
+ remaining !== void 0 ? ` (${remaining}s)` : ""
899
+ ]
843
900
  })
844
901
  ]
845
902
  }),
@@ -887,7 +944,7 @@ window.__ModuleLoader__.load({
887
944
  }),
888
945
  code !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
889
946
  type: "button",
890
- onClick: () => void copyValue(entry.id, code.split(" ")[0]),
947
+ onClick: () => void copyValue(entry.id, code),
891
948
  disabled: busy,
892
949
  children: t("copyCode")
893
950
  }),
@@ -970,6 +1027,14 @@ window.__ModuleLoader__.load({
970
1027
  report.reused.length
971
1028
  ]
972
1029
  }),
1030
+ dupGroups > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1031
+ className: `${VaultSection_module_css_default.badge} ${VaultSection_module_css_default.badgeWarn}`,
1032
+ children: [
1033
+ t("dupGroups"),
1034
+ ": ",
1035
+ dupGroups
1036
+ ]
1037
+ }),
973
1038
  report !== null && report.rotation.length === 0 && report.weak.length === 0 && report.reused.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
974
1039
  className: `${VaultSection_module_css_default.badge} ${VaultSection_module_css_default.badgeOk}`,
975
1040
  children: t("healthOk")
@@ -1171,6 +1236,7 @@ window.__ModuleLoader__.load({
1171
1236
  reportRotation: "expired / due for rotation",
1172
1237
  reportWeak: "weak passwords",
1173
1238
  reportReused: "credentials reused across entries",
1239
+ dupGroups: "duplicate groups",
1174
1240
  healthOk: "vault healthy",
1175
1241
  healthBackup: "backup",
1176
1242
  backupNow: "Backup now",
@@ -1270,6 +1336,7 @@ window.__ModuleLoader__.load({
1270
1336
  reportRotation: "已过期 / 待轮换",
1271
1337
  reportWeak: "弱密码",
1272
1338
  reportReused: "跨条目复用的凭据",
1339
+ dupGroups: "重复分组",
1273
1340
  healthOk: "凭据库健康",
1274
1341
  healthBackup: "备份",
1275
1342
  backupNow: "立即备份",
@@ -1394,6 +1461,7 @@ window.__ModuleLoader__.load({
1394
1461
  backupStatus: () => invoke("backupStatus"),
1395
1462
  backup: (maxBackups) => invoke("backup", { maxBackups: maxBackups ?? 10 }),
1396
1463
  health: () => invoke("health"),
1464
+ duplicates: () => invoke("duplicates"),
1397
1465
  restore: (id) => invoke("restore", { id }),
1398
1466
  undeleteAll: () => invoke("undeleteAll"),
1399
1467
  totp: (id) => invoke("totp", { id })
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["css"],"sources":["../src/client/VaultSection.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Vault settings section: list, search, add, edit, delete, and copy encrypted\n * credentials. All mutations go through the host VaultGateway remote; secrets\n * are shown only inside the edit form or a copy action, never in the list.\n */\n\nimport { useEffect, useId, useMemo, useState, type ReactNode } from 'react'\nimport type { PropsLocale, PropsRuntime, InjectFace, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { VaultLocaleKey } from './locales.ts'\nimport css from './VaultSection.module.css'\n\n/** Wire shapes shared with the host gateway (mirror of src/index.ts types). */\nexport interface VaultSummaryWire {\n id: string\n title: string\n sensitivity?: string\n kind?: string\n username?: string\n email?: string\n phone?: string\n host?: string\n port?: string\n url?: string\n tags?: string[]\n}\n\nexport interface VaultFullWire {\n id: string\n title: string\n kind?: string\n username?: string\n email?: string\n phone?: string\n password?: string\n host?: string\n port?: string\n privateKey?: string\n apiKey?: string\n secret?: string\n accessToken?: string\n refreshToken?: string\n expiresAt?: number\n rotationDays?: number\n sensitivity?: string\n favorite?: boolean\n otpSecret?: string\n url?: string\n notes?: string\n tags?: string[]\n icon?: string\n color?: string\n fields?: Record<string, unknown>\n}\n\n/** Patch shape accepted by vault.add / vault.update. */\nexport type VaultPatch = Partial<Omit<VaultFullWire, 'id'>>\n\n/** Registration-side business face supplied by the plugin entry. */\nexport interface VaultSectionInjected {\n t: TranslateNS<'settings.vault'>\n config: () => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n setAccessMode: (mode: 'readonly' | 'ask' | 'auto') => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n setAutoCapture: (enabled: boolean) => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n list: () => Promise<VaultSummaryWire[]>\n search: (query: string, limit?: number) => Promise<VaultSummaryWire[]>\n get: (id: string) => Promise<{ found: boolean; entry?: VaultFullWire }>\n add: (patch: VaultPatch & { title: string }) => Promise<VaultSummaryWire>\n update: (id: string, patch: VaultPatch) => Promise<{ found: boolean; entry?: VaultSummaryWire }>\n remove: (id: string) => Promise<{ deleted: boolean }>\n trash: () => Promise<VaultSummaryWire[]>\n rotation: () => Promise<unknown[]>\n history: () => Promise<unknown[]>\n stats: () => Promise<Record<string, unknown>>\n recent: () => Promise<unknown[]>\n backupStatus: () => Promise<{ daysSinceBackup: number; backups: number }>\n backup: (maxBackups?: number) => Promise<{ path: string; kept: number; pruned: number }>\n health: () => Promise<{ weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } }>\n restore: (id: string) => Promise<{ restored: boolean }>\n undeleteAll: () => Promise<{ restored: number }>\n totp: (id: string) => Promise<{ code: string; label?: string; secondsRemaining: number }>\n}\n\n/** Type-level alias so consumers can reference the wire shapes without values. */\nexport type VaultSectionTypes = {\n entries: VaultSummaryWire[]\n fullEntry: VaultFullWire\n summaryEntry: VaultSummaryWire\n config: { accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }\n accessModes: Array<'readonly' | 'ask' | 'auto'>\n}\n\n/** Full component props assembled by the Settings slot renderer. */\nexport type VaultSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'settings.vault'>\n & InjectFace<VaultSectionInjected>\n\n/** The editable form fields we render (subset of the full wire). */\ntype FormFields = {\n title?: string | undefined\n kind?: string | undefined\n username?: string | undefined\n email?: string | undefined\n phone?: string | undefined\n password?: string | undefined\n host?: string | undefined\n port?: string | undefined\n privateKey?: string | undefined\n apiKey?: string | undefined\n secret?: string | undefined\n accessToken?: string | undefined\n refreshToken?: string | undefined\n otpSecret?: string | undefined\n url?: string | undefined\n notes?: string | undefined\n icon?: string | undefined\n color?: string | undefined\n expiresAt?: number | undefined\n rotationDays?: number | undefined\n sensitivity?: string | undefined\n favorite?: boolean | undefined\n}\n\nconst FORM_FIELDS: Array<{ key: keyof FormFields; label: VaultLocaleKey }> = [\n { key: 'title', label: 'fieldTitle' },\n { key: 'kind', label: 'fieldKind' },\n { key: 'username', label: 'fieldUsername' },\n { key: 'email', label: 'fieldEmail' },\n { key: 'phone', label: 'fieldPhone' },\n { key: 'password', label: 'fieldPassword' },\n { key: 'host', label: 'fieldHost' },\n { key: 'port', label: 'fieldPort' },\n { key: 'apiKey', label: 'fieldApiKey' },\n { key: 'secret', label: 'fieldSecret' },\n { key: 'accessToken', label: 'fieldAccessToken' },\n { key: 'refreshToken', label: 'fieldRefreshToken' },\n { key: 'otpSecret', label: 'fieldOtpSecret' },\n { key: 'url', label: 'fieldUrl' },\n { key: 'notes', label: 'fieldNotes' },\n { key: 'expiresAt', label: 'fieldExpiresAt' },\n { key: 'rotationDays', label: 'fieldRotationDays' },\n { key: 'sensitivity', label: 'fieldSensitivity' },\n { key: 'favorite', label: 'fieldFavorite' },\n { key: 'icon', label: 'fieldIcon' },\n { key: 'color', label: 'fieldColor' },\n]\n\nconst KIND_KEYS: Record<string, VaultLocaleKey> = {\n login: 'kindLogin',\n ssh: 'kindSsh',\n 'api-key': 'kindApiKey',\n secret: 'kindSecret',\n oauth: 'kindOauth',\n custom: 'kindCustom',\n}\n\ntype ViewState =\n | { readonly status: 'loading' }\n | { readonly status: 'error' }\n | { readonly status: 'ready'; readonly entries: VaultSummaryWire[] }\n\ntype EditorState =\n | { readonly status: 'closed' }\n | { readonly status: 'creating' }\n | { readonly status: 'editing'; readonly entry: VaultFullWire }\n\nfunction emptyForm(): FormFields {\n return { title: '', kind: 'login', username: '', email: '', phone: '', password: '', host: '', port: '', icon: '', color: '' }\n}\n\n/** Render the Vault settings section. */\nexport function VaultSection(props: VaultSectionProps): ReactNode {\n const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp } = props\n const searchId = useId()\n const [query, setQuery] = useState('')\n const [state, setState] = useState<ViewState>({ status: 'loading' })\n const [editor, setEditor] = useState<EditorState>({ status: 'closed' })\n const [form, setForm] = useState<FormFields>(emptyForm())\n const [busy, setBusy] = useState(false)\n const [message, setMessage] = useState<string | null>(null)\n const [copiedId, setCopiedId] = useState<string | null>(null)\n const [codeMap, setCodeMap] = useState<Record<string, string>>({})\n const [tagsDraft, setTagsDraft] = useState('')\n const [fieldsDraft, setFieldsDraft] = useState('')\n const [revealed, setRevealed] = useState<Record<string, boolean>>({})\n const [kindFilter, setKindFilter] = useState('')\n const [tagFilter, setTagFilter] = useState('')\n const [expandedId, setExpandedId] = useState<string | null>(null)\n const [sortBy, setSortBy] = useState<'alpha' | 'recent'>('alpha')\n const [policy, setPolicy] = useState<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean } | null>(null)\n const [showTrash, setShowTrash] = useState(false)\n const [trashEntries, setTrashEntries] = useState<VaultSummaryWire[]>([])\n const [report, setReport] = useState<{ rotation: unknown[]; weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } | null } | null>(null)\n const [dueMap, setDueMap] = useState<Record<string, { due: string; daysLeft: number }>>({})\n const [recentEvents, setRecentEvents] = useState<Array<Record<string, unknown>>>([])\n const [vaultStats, setVaultStats] = useState<Record<string, unknown> | null>(null)\n const [backupInfo, setBackupInfo] = useState<{ daysSinceBackup: number; backups: number } | null>(null)\n const [recentEntries, setRecentEntries] = useState<Array<Record<string, unknown>>>([])\n\n const readonly = policy?.accessMode === 'readonly'\n\n /** Run an encrypted backup now and refresh the backup-age badge. */\n async function backupNow(): Promise<void> {\n setBusy(true)\n setMessage(null)\n try {\n const result = await backup(10)\n setBackupInfo({ daysSinceBackup: 0, backups: result.kept })\n setMessage(`${t('backupDone')} (${result.kept} kept, ${result.pruned} pruned)`)\n void refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n useEffect(() => {\n let current = true\n void config().then(\n value => { if (current) setPolicy(value) },\n () => { /* policy is informational; ignore failures */ },\n )\n return () => { current = false }\n }, [config])\n\n const refresh = useMemo(() => async () => {\n setState({ status: 'loading' })\n try {\n const entries = query.trim().length === 0\n ? await list()\n : await search(query.trim())\n setState({ status: 'ready', entries })\n } catch {\n setState({ status: 'error' })\n }\n }, [list, search, query])\n\n useEffect(() => {\n let current = true\n const timer = window.setTimeout(() => {\n void refresh().then(\n () => { /* state already set inside refresh */ },\n () => { if (current) setState({ status: 'error' }) },\n )\n }, 250)\n return () => { current = false; window.clearTimeout(timer) }\n // refresh is memoized on query (debounced); list/search are stable.\n }, [refresh])\n\n // Vault health & meta: load once on mount (stats, backup age, rotation,\n // weak/reused scan, recent activity) and refresh on window focus.\n useEffect(() => {\n let current = true\n const load = (): void => {\n void Promise.all([\n stats().catch(() => null),\n backupStatus().catch(() => null),\n rotation().catch(() => []),\n health().catch(() => null),\n recent().catch(() => []),\n ]).then(([st, bk, rot, hl, rc]) => {\n if (!current) return\n if (st !== null) setVaultStats(st as Record<string, unknown>)\n if (bk !== null) setBackupInfo(bk)\n setReport({ rotation: (rot ?? []) as unknown[], weak: ((hl?.weak ?? []) as unknown[]), reused: ((hl?.reused ?? []) as unknown[]), strength: (hl?.strength ?? null) as { weak: number; fair: number; strong: number } | null })\n const due: Record<string, { due: string; daysLeft: number }> = {}\n for (const item of (rot ?? []) as Array<{ id?: string; due?: string; daysLeft?: number }>) {\n if (item.id !== undefined && item.due !== undefined) due[item.id] = { due: item.due, daysLeft: item.daysLeft ?? 0 }\n }\n setDueMap(due)\n setRecentEntries((rc ?? []) as Array<Record<string, unknown>>)\n })\n }\n load()\n const onFocus = (): void => { load() }\n window.addEventListener('focus', onFocus)\n return () => { current = false; window.removeEventListener('focus', onFocus) }\n }, [stats, backupStatus, rotation, health, recent])\n\n /** Open the editor for a new entry. */\n function startCreate(): void {\n setForm(emptyForm())\n setTagsDraft('')\n setFieldsDraft('')\n setMessage(null)\n setEditor({ status: 'creating' })\n }\n\n /** Open the editor for an existing entry (fetches full secrets). */\n async function startEdit(id: string): Promise<void> {\n setBusy(true)\n setMessage(null)\n try {\n const result = await get(id)\n if (!result.found || result.entry === undefined) {\n setMessage(t('error'))\n return\n }\n const entry = result.entry\n setForm({\n title: entry.title ?? '',\n kind: entry.kind ?? 'login',\n username: entry.username ?? '',\n email: entry.email ?? '',\n phone: entry.phone ?? '',\n password: entry.password ?? '',\n host: entry.host ?? '',\n port: entry.port ?? '',\n privateKey: entry.privateKey ?? '',\n apiKey: entry.apiKey ?? '',\n secret: entry.secret ?? '',\n accessToken: entry.accessToken ?? '',\n refreshToken: entry.refreshToken ?? '',\n otpSecret: entry.otpSecret ?? '',\n url: entry.url ?? '',\n notes: entry.notes ?? '',\n icon: entry.icon ?? '',\n color: entry.color ?? '',\n expiresAt: entry.expiresAt,\n rotationDays: entry.rotationDays,\n sensitivity: entry.sensitivity,\n favorite: entry.favorite ?? false,\n })\n setTagsDraft((entry.tags ?? []).join(', '))\n setFieldsDraft(entry.fields !== undefined ? Object.entries(entry.fields).map(([k, v]) => `${k}=${String(v)}`).join('\\n') : '')\n setEditor({ status: 'editing', entry })\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Save the current form (create or update). */\n async function save(): Promise<void> {\n if (!(form.title ?? '').trim()) {\n setMessage(t('error'))\n return\n }\n setBusy(true)\n setMessage(null)\n try {\n const tags = tagsDraft.split(',').map(x => x.trim()).filter(x => x.length > 0)\n const fields: Record<string, string> = {}\n for (const line of fieldsDraft.split('\\n')) {\n const trimmed = line.trim()\n if (trimmed.length === 0) continue\n const eq = trimmed.indexOf('=')\n if (eq <= 0) continue\n fields[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim()\n }\n const patch: VaultPatch = {\n title: (form.title ?? '').trim(),\n ...(form.kind !== undefined ? { kind: form.kind } : {}),\n ...(form.username !== undefined ? { username: form.username } : {}),\n ...(form.email !== undefined ? { email: form.email } : {}),\n ...(form.phone !== undefined ? { phone: form.phone } : {}),\n ...(form.password !== undefined ? { password: form.password } : {}),\n ...(form.host !== undefined ? { host: form.host } : {}),\n ...(form.port !== undefined ? { port: form.port } : {}),\n ...(form.privateKey !== undefined ? { privateKey: form.privateKey } : {}),\n ...(form.apiKey !== undefined ? { apiKey: form.apiKey } : {}),\n ...(form.secret !== undefined ? { secret: form.secret } : {}),\n ...(form.accessToken !== undefined ? { accessToken: form.accessToken } : {}),\n ...(form.refreshToken !== undefined ? { refreshToken: form.refreshToken } : {}),\n ...(form.otpSecret !== undefined ? { otpSecret: form.otpSecret } : {}),\n ...(form.url !== undefined ? { url: form.url } : {}),\n ...(form.notes !== undefined ? { notes: form.notes } : {}),\n ...(form.icon !== undefined ? { icon: form.icon } : {}),\n ...(form.color !== undefined ? { color: form.color } : {}),\n ...(form.expiresAt !== undefined ? { expiresAt: form.expiresAt } : {}),\n ...(form.rotationDays !== undefined ? { rotationDays: form.rotationDays } : {}),\n ...(form.sensitivity !== undefined ? { sensitivity: form.sensitivity } : {}),\n ...(form.favorite !== undefined ? { favorite: form.favorite } : {}),\n // Always send tags: an empty array clears them, and the host store\n // treats empty string values as \"clear this field\" too.\n tags,\n fields,\n }\n if (editor.status === 'creating') {\n await add(patch as VaultPatch & { title: string })\n } else if (editor.status === 'editing') {\n await update(editor.entry.id, patch)\n }\n setEditor({ status: 'closed' })\n await refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Delete an entry after confirmation. */\n async function removeEntry(id: string): Promise<void> {\n if (!window.confirm(t('deleteConfirm'))) return\n setBusy(true)\n try {\n await remove(id)\n await refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Copy a field value to the clipboard and flash the row. */\n async function copyValue(id: string, value: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(value)\n } catch {\n // Clipboard may be blocked; fall back to a temporary textarea.\n const textarea = document.createElement('textarea')\n textarea.value = value\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n textarea.remove()\n }\n setCopiedId(id)\n window.setTimeout(() => setCopiedId(null), 1500)\n }\n\n /** Fetch and display a TOTP code for an entry with an otpSecret. */\n async function showTotp(id: string): Promise<void> {\n setBusy(true)\n try {\n const result = await totp(id)\n setCodeMap(previous => ({ ...previous, [id]: `${result.code} (${result.secondsRemaining}s)` }))\n } catch {\n setCodeMap(previous => ({ ...previous, [id]: t('error') }))\n } finally {\n setBusy(false)\n }\n }\n\n /** Field-set summary line for one entry (non-secret). */\n function kindIcon(kind?: string): string {\n switch (kind ?? 'login') {\n case 'ssh': return '🖥️'\n case 'api-key': return '🔑'\n case 'oauth': return '🔐'\n case 'secret': return '🤫'\n case 'custom': return '🧩'\n default: return '👤'\n }\n }\n\n function identityLine(entry: VaultSummaryWire): string {\n const parts = [\n entry.kind !== undefined ? t(KIND_KEYS[entry.kind] ?? 'kindCustom') : t('kindLogin'),\n entry.username,\n entry.email,\n entry.phone,\n entry.host !== undefined ? `${entry.host}${entry.port !== undefined ? `:${entry.port}` : ''}` : undefined,\n entry.url,\n ].filter((value): value is string => value !== undefined && value.length > 0)\n return parts.join(' · ')\n }\n\n return (\n <section className={css.section} aria-labelledby=\"vault-heading\">\n <h2 id=\"vault-heading\">{t('heading')}</h2>\n <p className={css.intro}>{t('intro')}</p>\n\n <div className={css.toolbar}>\n <label className={css.searchBox}>\n <span className=\"sr-only\">{t('searchPlaceholder')}</span>\n <input\n id={searchId}\n type=\"search\"\n placeholder={t('searchPlaceholder')}\n value={query}\n onChange={event => setQuery(event.target.value)}\n />\n {query.length > 0 && (\n <button type=\"button\" className={css.clearButton} onClick={() => setQuery('')} aria-label={t('clearSearch')}>×</button>\n )}\n </label>\n <select className={css.kindFilter} value={kindFilter} onChange={e => setKindFilter(e.target.value)} aria-label={t('fieldKind')}>\n <option value=\"\">{t('allKinds')}</option>\n {Object.entries(KIND_KEYS).map(([value, key]) => (\n <option key={value} value={value}>{t(key)}</option>\n ))}\n </select>\n <select className={css.kindFilter} value={tagFilter} onChange={e => setTagFilter(e.target.value)} aria-label={t('fieldTags')}>\n <option value=\"\">{t('allTags')}</option>\n {[...new Set(state.status === 'ready' ? state.entries.flatMap(e => e.tags ?? []) : [])].sort().map(tag => (\n <option key={tag} value={tag}>{tag}</option>\n ))}\n </select>\n <button\n type=\"button\"\n className={css.sortButton}\n onClick={() => setSortBy(sortBy === 'alpha' ? 'recent' : 'alpha')}\n >{sortBy === 'alpha' ? t('sortAlpha') : t('sortRecent')}</button>\n <button type=\"button\" className={css.addButton} onClick={startCreate} disabled={busy || readonly}>\n + {t('add')}\n </button>\n </div>\n\n {policy !== null && (\n <div className={css.policyBar}>\n <label className={css.policyField}>\n <span>{t('modeLabel')}</span>\n <select\n value={policy.accessMode}\n disabled={busy}\n onChange={event => {\n const next = event.target.value as 'readonly' | 'ask' | 'auto'\n setBusy(true)\n setMessage(null)\n void setAccessMode(next).then(\n value => { setPolicy(value); setBusy(false) },\n () => { setMessage(t('error')); setBusy(false) },\n )\n }}\n >\n <option value=\"readonly\">{t('modeReadonly')}</option>\n <option value=\"ask\">{t('modeAsk')}</option>\n <option value=\"auto\">{t('modeAuto')}</option>\n </select>\n </label>\n <label className={css.policyField}>\n <span>{t('autoCaptureLabel')}</span>\n <input\n type=\"checkbox\"\n checked={policy.autoCapture}\n disabled={busy}\n onChange={event => {\n const next = event.target.checked\n setBusy(true)\n setMessage(null)\n void setAutoCapture(next).then(\n value => { setPolicy(value); setBusy(false) },\n () => { setMessage(t('error')); setBusy(false) },\n )\n }}\n />\n </label>\n <p className={policy.accessMode === 'readonly' ? css.modeReadonly : policy.accessMode === 'ask' ? css.modeAsk : css.modeAuto}>\n {policy.accessMode === 'readonly'\n ? t('modeReadonlyHint')\n : policy.accessMode === 'ask'\n ? t('modeAskHint')\n : t('modeAutoHint')}\n {policy.autoCapture ? ` · ${t('autoCaptureOn')}` : ` · ${t('autoCaptureOff')}`}\n </p>\n </div>\n )}\n\n {message !== null && <p role=\"alert\" className={css.error}>{message}</p>}\n\n {state.status === 'loading' && <p className={css.status}>{t('loading')}</p>}\n {state.status === 'error' && <p role=\"alert\" className={css.error}>{t('error')}</p>}\n {state.status === 'ready' && state.entries.length === 0 && (\n <div className={css.emptyBox}>\n <p className={css.empty}>{t('empty')}</p>\n <p className={css.emptyHint}>{readonly ? t('emptyHintReadonly') : t('emptyHint')}</p>\n {!readonly && (\n <button type=\"button\" className={css.addButton} onClick={startCreate}>{t('quickAdd')}</button>\n )}\n </div>\n )}\n\n {recentEntries.length > 0 && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('recentlyAdded')}</p>\n {recentEntries.map((e, i) => (\n <p key={i} className={css.reportLine}>{String(e.title ?? '')}</p>\n ))}\n </div>\n )}\n\n {recentEvents.length > 0 && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('recentActivity')}</p>\n {recentEvents.map((ev, i) => (\n <p key={i} className={css.reportLine}>\n {String(ev.action ?? '')} · {String(ev.title ?? ev.id ?? '')}\n </p>\n ))}\n </div>\n )}\n\n {report !== null && (report.rotation.length > 0 || report.weak.length > 0 || report.reused.length > 0) && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('reportTitle')}</p>\n {report.rotation.length > 0 && (\n <>\n <p className={css.reportLine}>{t('reportRotation')}: {report.rotation.length}</p>\n {(report.rotation as Array<{ title?: string; due?: string; daysLeft?: number }>).slice(0, 8).map((item, i) => (\n <p key={i} className={css.reportSub}>\n · {String(item.title ?? '?')}{item.due === 'expired' ? ` (${t('dueExpired')})` : item.due === 'soon' ? ` (${t('dueExpiring')} ${item.daysLeft ?? 0}d)` : ` (${t('dueNow')})`}\n </p>\n ))}\n </>\n )}\n {report.weak.length > 0 && (\n <p className={css.reportLine}>{t('reportWeak')}: {report.weak.length}</p>\n )}\n {report.reused.length > 0 && (\n <p className={css.reportLine}>{t('reportReused')}: {report.reused.length}</p>\n )}\n </div>\n )}\n\n {state.status === 'ready' && (state.entries.length > 0 || trashEntries.length > 0) && (\n <button\n type=\"button\"\n className={css.trashButton}\n onClick={() => {\n if (!showTrash) void trash().then(setTrashEntries)\n setShowTrash(!showTrash)\n }}\n >{showTrash ? t('hideTrash') : t('showTrash')}{trashEntries.length > 0 ? ` (${trashEntries.length})` : ''}</button>\n )}\n\n {showTrash && (\n <ul className={css.list}>\n {trashEntries.map(entry => (\n <li key={entry.id} className={css.row}>\n <div className={css.rowMain}>\n <span className={css.title}>{entry.title}</span>\n <span className={css.identity}>{t('trashed')}</span>\n </div>\n <div className={css.rowActions}>\n <button\n type=\"button\"\n onClick={() => { void restore(entry.id).then(() => { void trash().then(setTrashEntries); void refresh() }) }}\n disabled={busy || readonly}\n >{t('restore')}</button>\n </div>\n </li>\n ))}\n {trashEntries.length === 0 && <p className={css.empty}>{t('trashEmpty')}</p>}\n </ul>\n )}\n {showTrash && trashEntries.length > 0 && (\n <>\n <button\n type=\"button\"\n className={css.trashButton}\n onClick={() => {\n setBusy(true)\n void undeleteAll().then(() => {\n void trash().then(setTrashEntries)\n void refresh()\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('restoreAll')}</button>\n <button\n type=\"button\"\n className={css.dangerButton}\n onClick={() => {\n if (!window.confirm(t('clearTrashConfirm'))) return\n setBusy(true)\n void Promise.all(trashEntries.map(e => remove(e.id))).then(() => {\n void trash().then(setTrashEntries)\n void refresh()\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('clearTrash')}</button>\n </>\n )}\n\n {state.status === 'ready' && state.entries.length > 0 && (\n <ul className={css.list}>\n {state.entries.filter(entry => (kindFilter === '' || entry.kind === kindFilter) && (tagFilter === '' || (entry.tags ?? []).includes(tagFilter))).sort((a, b) => sortBy === 'alpha' ? a.title.localeCompare(b.title) : 0).map(entry => {\n const code = codeMap[entry.id]\n return (\n <li key={entry.id} className={`${css.row}${dueMap[entry.id] !== undefined ? ` ${dueMap[entry.id]!.due === 'expired' ? css.rowExpired : css.rowDue}` : ''}`}>\n <div className={css.rowMain} onClick={() => setExpandedId(expandedId === entry.id ? null : entry.id)}>\n <span className={css.title}>\n <span className={css.kindIcon}>{kindIcon(entry.kind)}</span>\n {(entry as VaultSummaryWire & { favorite?: boolean }).favorite && (\n <span className={css.pinStar} title={t('pinned')}>★</span>\n )}\n {entry.title}\n {(entry as VaultSummaryWire & { sensitivity?: string }).sensitivity === 'high' && (\n <span className={css.highBadge}>{t('highSensitivity')}</span>\n )}\n {dueMap[entry.id] !== undefined && (\n <span className={`${css.dueBadge} ${dueMap[entry.id]!.due === 'expired' ? css.badgeDanger : css.badgeWarn}`}>\n {dueMap[entry.id]!.due === 'expired' ? t('dueExpired') : dueMap[entry.id]!.due === 'soon' ? `${t('dueExpiring')} ${dueMap[entry.id]!.daysLeft}d` : t('dueNow')}\n </span>\n )}\n </span>\n <span className={css.identity}>{identityLine(entry)}</span>\n {code !== undefined && <span className={css.totp}>{code}</span>}\n </div>\n {expandedId === entry.id && (\n <div className={css.detailBox}>\n {Object.entries(entry).filter(([k]) => !['id', 'title'].includes(k) && entry[k as keyof VaultSummaryWire] !== undefined).map(([k, v]) => (\n <span key={k} className={css.detailItem}>\n <strong>{k}</strong>: {Array.isArray(v) ? v.join(', ') : String(v)}\n </span>\n ))}\n </div>\n )}\n <div className={css.rowActions}>\n {copiedId === entry.id && <span className={css.copied}>{t('copied')}</span>}\n <button\n type=\"button\"\n onClick={() => void copyValue(entry.id, entry.username ?? entry.title)}\n disabled={busy}\n >{t('copy')}</button>\n <button\n type=\"button\"\n onClick={() => {\n setBusy(true)\n void get(entry.id).then(r => {\n if (r.found && r.entry?.password) void copyValue(entry.id, r.entry.password)\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('copyPassword')}</button>\n <button type=\"button\" onClick={() => void showTotp(entry.id)} disabled={busy}>{t('totp')}</button>\n {code !== undefined && (\n <button type=\"button\" onClick={() => void copyValue(entry.id, code.split(' ')[0]!)} disabled={busy}>{t('copyCode')}</button>\n )}\n <button type=\"button\" onClick={() => void startEdit(entry.id)} disabled={busy || readonly}>{t('edit')}</button>\n <button\n type=\"button\"\n className={css.deleteButton}\n onClick={() => void removeEntry(entry.id)}\n disabled={busy || readonly}\n >{t('delete')}</button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n\n {state.status === 'ready' && (\n <div className={css.healthBar}>\n {vaultStats !== null && typeof vaultStats.total === 'number' && (\n <span className={css.badge}>{t('entryCount')}: {String(vaultStats.total)}</span>\n )}\n {vaultStats !== null && typeof vaultStats.withTotp === 'number' && (\n <span className={css.badge}>TOTP: {String(vaultStats.withTotp)}</span>\n )}\n {vaultStats !== null && typeof vaultStats.highSensitivity === 'number' && (\n <span className={css.badge}>{t('highSensitivity')}: {String(vaultStats.highSensitivity)}</span>\n )}\n {report !== null && report.strength !== null && (\n <span className={`${css.badge} ${report.strength.weak > 0 ? css.badgeDanger : css.badgeOk}`}>\n {t('healthStrength')}: {report.strength.weak}W/{report.strength.fair}F/{report.strength.strong}S\n </span>\n )}\n {report !== null && report.rotation.length > 0 && (\n <span className={`${css.badge} ${css.badgeWarn}`}>{t('reportRotation')}: {report.rotation.length}</span>\n )}\n {report !== null && report.weak.length > 0 && (\n <span className={`${css.badge} ${css.badgeDanger}`}>{t('reportWeak')}: {report.weak.length}</span>\n )}\n {report !== null && report.reused.length > 0 && (\n <span className={`${css.badge} ${css.badgeDanger}`}>{t('reportReused')}: {report.reused.length}</span>\n )}\n {report !== null && report.rotation.length === 0 && report.weak.length === 0 && report.reused.length === 0 && (\n <span className={`${css.badge} ${css.badgeOk}`}>{t('healthOk')}</span>\n )}\n {backupInfo !== null && backupInfo.daysSinceBackup >= 0 && (\n <span className={`${css.badge} ${backupInfo.daysSinceBackup > 14 ? css.badgeDanger : backupInfo.daysSinceBackup > 7 ? css.badgeWarn : ''}`}>\n {t('healthBackup')}: {backupInfo.daysSinceBackup}d\n </span>\n )}\n <button type=\"button\" className={css.backupButton} onClick={() => void backupNow()} disabled={busy}>\n {t('backupNow')}\n </button>\n </div>\n )}\n {state.status === 'ready' && (\n <p className={css.footer}>\n {t('entryCount')}: {state.entries.length}\n {vaultStats !== null && typeof vaultStats.withTotp === 'number' && ` · TOTP: ${String(vaultStats.withTotp)}`}\n {vaultStats !== null && typeof vaultStats.highSensitivity === 'number' && ` · ${t('highSensitivity')}: ${String(vaultStats.highSensitivity)}`}\n {backupInfo !== null && backupInfo.daysSinceBackup >= 0 && ` · backup: ${backupInfo.daysSinceBackup}d ago`}\n {vaultStats !== null && typeof vaultStats.byTag === 'object' && vaultStats.byTag !== null\n && Object.keys(vaultStats.byTag as Record<string, unknown>).length > 0\n && ` · tags: ${Object.entries(vaultStats.byTag as Record<string, unknown>).map(([k, v]) => `${k}(${String(v)})`).join(' ')}`}\n </p>\n )}\n\n {editor.status !== 'closed' && (\n <div className={css.editor} role=\"dialog\" aria-label={editor.status === 'creating' ? t('add') : t('edit')}>\n <div className={css.editorBody}>\n {FORM_FIELDS.map(field => (\n <label key={field.key} className={css.field}>\n <span>{t(field.label)}</span>\n {field.key === 'kind' ? (\n <select\n value={form.kind ?? 'login'}\n onChange={event => setForm(previous => ({ ...previous, kind: event.target.value }))}\n >\n {Object.entries(KIND_KEYS).map(([value, key]) => (\n <option key={value} value={value}>{t(key)}</option>\n ))}\n </select>\n ) : field.key === 'title' ? (\n <input\n value={form.title ?? ''}\n onChange={event => setForm(previous => ({ ...previous, title: event.target.value }))}\n />\n ) : field.key === 'password' || field.key === 'otpSecret' || field.key === 'apiKey'\n || field.key === 'secret' || field.key === 'accessToken' || field.key === 'refreshToken'\n || field.key === 'privateKey' ? (\n <span className={css.secretField}>\n <input\n type={revealed[field.key] ? 'text' : 'password'}\n value={(form[field.key] as string | undefined) ?? ''}\n onChange={event => setForm(previous => ({ ...previous, [field.key]: event.target.value }))}\n />\n <button\n type=\"button\"\n className={css.revealButton}\n onClick={() => setRevealed(previous => ({ ...previous, [field.key]: !previous[field.key] }))}\n >{revealed[field.key] ? t('hide') : t('show')}</button>\n </span>\n ) : field.key === 'expiresAt' ? (\n <input\n type=\"datetime-local\"\n value={form.expiresAt !== undefined ? new Date(form.expiresAt).toISOString().slice(0, 16) : ''}\n onChange={event => {\n const raw = event.target.value\n const epoch = raw.length > 0 ? Date.parse(raw) : NaN\n setForm(previous => ({ ...previous, expiresAt: Number.isNaN(epoch) ? undefined : epoch }))\n }}\n />\n ) : field.key === 'rotationDays' ? (\n <input\n type=\"number\"\n min={0}\n max={3650}\n placeholder={t('rotationClearHint')}\n value={form.rotationDays ?? ''}\n onChange={event => {\n const raw = event.target.value\n setForm(previous => ({ ...previous, rotationDays: raw.length > 0 ? Number(raw) : undefined }))\n }}\n />\n ) : field.key === 'sensitivity' ? (\n <select\n value={form.sensitivity ?? 'normal'}\n onChange={event => setForm(previous => ({ ...previous, sensitivity: event.target.value === 'high' ? 'high' : 'normal' }))}\n >\n <option value=\"normal\">{t('sensitivityNormal')}</option>\n <option value=\"high\">{t('sensitivityHigh')}</option>\n </select>\n ) : field.key === 'favorite' ? (\n <label className={css.checkField}>\n <input\n type=\"checkbox\"\n checked={form.favorite ?? false}\n onChange={event => setForm(previous => ({ ...previous, favorite: event.target.checked }))}\n />\n {t('favoriteHint')}\n </label>\n ) : (\n <input\n type=\"text\"\n value={(form[field.key] as string | undefined) ?? ''}\n onChange={event => setForm(previous => ({ ...previous, [field.key]: event.target.value }))}\n />\n )}\n </label>\n ))}\n <label className={css.field}>\n <span>{t('fieldTags')}</span>\n <input\n value={tagsDraft}\n onChange={event => setTagsDraft(event.target.value)}\n placeholder=\"dev, prod\"\n />\n </label>\n <label className={css.field}>\n <span>{t('fieldCustom')}</span>\n <textarea\n value={fieldsDraft}\n onChange={event => setFieldsDraft(event.target.value)}\n rows={3}\n placeholder=\"region=us-east-1&#10;team=infra\"\n />\n </label>\n </div>\n <div className={css.editorActions}>\n <button type=\"button\" onClick={() => setEditor({ status: 'closed' })} disabled={busy}>{t('cancel')}</button>\n <button type=\"button\" className={css.saveButton} onClick={() => void save()} disabled={busy}>{t('save')}</button>\n </div>\n </div>\n )}\n </section>\n )\n}\n","/** Copy dictionaries for the Vault settings section. */\n\n/** English strings (the key-set source of truth for this pair). */\nexport const en = {\n nav: 'Vault',\n heading: 'Vault',\n intro: 'Encrypted credentials stored on this machine.',\n searchPlaceholder: 'Search title, username, email, host…',\n loading: 'Loading…',\n empty: 'No credentials yet. Add one to get started.',\n emptyHint: 'Tip: you can ask the assistant to save an API key or token you share in chat — with auto-capture on it offers to store it for you. Bulk importing? Ask for vault_import_csv with a CSV file path.',\n emptyHintReadonly: 'The vault is in readonly mode — credentials can be read but not added here. Switch accessMode to \"readwrite\" to enable writes.',\n modeLabel: 'Access mode',\n autoCaptureLabel: 'Auto-capture credentials from chat',\n modeReadonly: 'Read-only',\n modeAsk: 'Ask before writing',\n modeAuto: 'Automatic read-write',\n modeReadonlyHint: 'Read-only — mutations disabled',\n modeAskHint: 'Prompt before writing — every add/update/delete asks you',\n modeAutoHint: 'Automatic read-write — no per-call prompt',\n autoCaptureOn: 'auto-capture on',\n autoCaptureOff: 'auto-capture off',\n clearSearch: 'Clear search',\n entryCount: 'Entries',\n reportTitle: 'Health & rotation',\n recentActivity: 'Recent activity',\n recentlyAdded: 'Recently added',\n reportRotation: 'expired / due for rotation',\n reportWeak: 'weak passwords',\n reportReused: 'credentials reused across entries',\n healthOk: 'vault healthy',\n healthBackup: 'backup',\n backupNow: 'Backup now',\n backupDone: 'Backup created',\n healthStrength: 'strength',\n dueExpired: 'EXPIRED',\n dueExpiring: 'expires in',\n dueNow: 'rotation due',\n showTrash: 'Trash',\n hideTrash: 'Hide trash',\n trashed: 'in trash',\n trashEmpty: 'Trash is empty',\n restore: 'Restore',\n clearTrash: 'Empty trash',\n restoreAll: 'Restore all',\n clearTrashConfirm: 'Permanently delete ALL trashed entries? This cannot be undone.',\n show: 'Show',\n hide: 'Hide',\n allKinds: 'All kinds',\n allTags: 'All tags',\n sortAlpha: 'Sort: A–Z',\n sortRecent: 'Sort: recent',\n highSensitivity: 'HIGH',\n pinned: 'Pinned',\n add: 'Add credential',\n quickAdd: 'Quick add',\n cancel: 'Cancel',\n save: 'Save',\n edit: 'Edit',\n delete: 'Delete',\n deleteConfirm: 'Delete this entry? This cannot be undone.',\n copy: 'Copy',\n copyPassword: 'Copy password',\n copyCode: 'Copy code',\n copied: 'Copied',\n totp: 'Code',\n fieldTitle: 'Title',\n fieldKind: 'Kind',\n fieldUsername: 'Username',\n fieldEmail: 'Email',\n fieldPhone: 'Phone',\n fieldPassword: 'Password',\n fieldHost: 'Host',\n fieldPort: 'Port',\n fieldApiKey: 'API key',\n fieldSecret: 'Secret',\n fieldAccessToken: 'Access token',\n fieldRefreshToken: 'Refresh token',\n fieldOtpSecret: 'OTP secret',\n fieldUrl: 'URL',\n fieldNotes: 'Notes',\n fieldTags: 'Tags',\n fieldIcon: 'Icon (emoji)',\n fieldExpiresAt: 'Expires at',\n fieldRotationDays: 'Rotation (days, 0 = never)',\n fieldSensitivity: 'Sensitivity',\n fieldFavorite: 'Pinned',\n sensitivityNormal: 'normal',\n sensitivityHigh: 'high',\n favoriteHint: 'Show at top',\n rotationClearHint: '0 clears rotation',\n fieldColor: 'Color',\n fieldCustom: 'Custom fields (key=value per line)',\n error: 'Something went wrong.',\n kindLogin: 'Login',\n kindSsh: 'SSH',\n kindApiKey: 'API key',\n kindSecret: 'Secret',\n kindOauth: 'OAuth',\n kindCustom: 'Custom',\n} as const\n\nexport type VaultLocaleKey = keyof typeof en\n\n/** Chinese strings (same keys as English). */\nexport const zh: Record<VaultLocaleKey, string> = {\n nav: '凭据库',\n heading: 'Vault 凭据库',\n intro: '本机加密存储的凭据。',\n searchPlaceholder: '搜索标题、用户名、邮箱、主机…',\n loading: '加载中…',\n empty: '还没有凭据,添加一条开始使用。',\n emptyHint: '提示:你可以在对话中把 API Key 或 token 交给助手保存——开启自动捕获后,它会在你同意时帮你存入本库。批量导入?让助手调用 vault_import_csv 并给出 CSV 路径即可。',\n emptyHintReadonly: '当前为只读模式——只能读取凭据,不能在此添加。将 accessMode 改为 \"readwrite\" 即可启用写入。',\n modeLabel: '访问模式',\n autoCaptureLabel: '自动捕获对话中的凭据',\n modeReadonly: '只读',\n modeAsk: '写入前询问',\n modeAuto: '自动读写',\n modeReadonlyHint: '只读 —— 变更已禁用',\n modeAskHint: '写入前询问 —— 每次增/改/删都会征求你的确认',\n modeAutoHint: '自动读写 —— 无需逐次确认',\n autoCaptureOn: '自动捕获已开启',\n autoCaptureOff: '自动捕获已关闭',\n clearSearch: '清除搜索',\n entryCount: '条目数',\n reportTitle: '健康与轮换',\n recentActivity: '最近活动',\n recentlyAdded: '最近添加',\n reportRotation: '已过期 / 待轮换',\n reportWeak: '弱密码',\n reportReused: '跨条目复用的凭据',\n healthOk: '凭据库健康',\n healthBackup: '备份',\n backupNow: '立即备份',\n backupDone: '备份已创建',\n healthStrength: '强度',\n dueExpired: '已过期',\n dueExpiring: '将于',\n dueNow: '待轮换',\n showTrash: '回收站',\n hideTrash: '隐藏回收站',\n trashed: '已删除',\n trashEmpty: '回收站为空',\n restore: '恢复',\n clearTrash: '清空回收站',\n restoreAll: '全部恢复',\n clearTrashConfirm: '永久删除回收站中全部条目?此操作不可撤销。',\n show: '显示',\n hide: '隐藏',\n allKinds: '全部类型',\n allTags: '全部标签',\n sortAlpha: '排序:A–Z',\n sortRecent: '排序:最近',\n highSensitivity: '高敏',\n pinned: '已置顶',\n add: '新增凭据',\n quickAdd: '快速添加',\n cancel: '取消',\n save: '保存',\n edit: '编辑',\n delete: '删除',\n deleteConfirm: '确定删除该条目?此操作不可撤销。',\n copy: '复制',\n copyPassword: '复制密码',\n copyCode: '复制验证码',\n copied: '已复制',\n totp: '验证码',\n fieldTitle: '标题',\n fieldKind: '类型',\n fieldUsername: '用户名',\n fieldEmail: '邮箱',\n fieldPhone: '手机号',\n fieldPassword: '密码',\n fieldHost: '主机',\n fieldPort: '端口',\n fieldApiKey: 'API 密钥',\n fieldSecret: 'Secret',\n fieldAccessToken: '访问令牌',\n fieldRefreshToken: '刷新令牌',\n fieldOtpSecret: 'TOTP 密钥',\n fieldUrl: 'URL',\n fieldNotes: '备注',\n fieldTags: '标签',\n fieldIcon: '图标(emoji)',\n fieldExpiresAt: '过期时间',\n fieldRotationDays: '轮换周期(天,0=永不)',\n fieldSensitivity: '敏感级别',\n fieldFavorite: '置顶',\n sensitivityNormal: '普通',\n sensitivityHigh: '高敏',\n favoriteHint: '列表置顶显示',\n rotationClearHint: '0 清除轮换',\n fieldColor: '颜色',\n fieldCustom: '自定义字段(每行 key=value)',\n error: '操作失败。',\n kindLogin: '登录',\n kindSsh: 'SSH',\n kindApiKey: 'API 密钥',\n kindSecret: 'Secret',\n kindOauth: 'OAuth',\n kindCustom: '自定义',\n}\n","/**\n * dsh-vault browser half: a Vault settings page managing encrypted\n * credentials — list, search, add, edit, delete, and copy — through the host\n * VaultGateway remote over the /api RPC channel.\n *\n * @module dsh-vault/client\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { VaultSection, type VaultSectionInjected } from './VaultSection.tsx'\nimport type { VaultSectionTypes } from './VaultSection.tsx'\nimport { en, zh, type VaultLocaleKey } from './locales.ts'\n\nexport type { VaultSectionInjected, VaultSectionProps } from './VaultSection.tsx'\nexport type { VaultSectionTypes } from './VaultSection.tsx'\nexport type { VaultLocaleKey } from './locales.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Vault settings page copy. */\n 'settings.vault': VaultLocaleKey\n }\n}\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'settings.vault'\n\n/** Services required by the Settings registration and the RPC face. */\nexport const inject = ['slots', 'locale', 'connection']\n\n/**\n * Register the Vault settings section once the `settings.section` declaration\n * is on the ledger. The section talks to the host through the connection RPC\n * channel (endpoints `vault/*` served by the host VaultGateway remote).\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-vault: dictionaries')\n\n const t = ctx.locale.bind(NS)\n const connection = ctx.get('connection') as ConnectionHandle\n\n /** Invoke one host vault remote and normalize the RPC envelope. */\n async function invoke<T>(method: string, args: Record<string, unknown> = {}): Promise<T> {\n const result = await connection.rpc.call('/api', `vault/${method}`, { args })\n if (!result.ok) {\n throw new Error(`vault.${method} failed: ${result.error.code}: ${result.error.message}`)\n }\n return result.value as T\n }\n\n const injected = (): VaultSectionInjected => ({\n t,\n config: () => invoke<VaultSectionTypes['config']>('config'),\n setAccessMode: (mode) => invoke<VaultSectionTypes['config']>('setAccessMode', { mode }),\n setAutoCapture: (enabled) => invoke<VaultSectionTypes['config']>('setAutoCapture', { enabled }),\n list: () => invoke<{ entries: VaultSectionTypes['entries'] }>('list').then(r => r.entries),\n search: (query, limit) => invoke<{ entries: VaultSectionTypes['entries'] }>('search', { query, limit: limit ?? 50 }).then(r => r.entries),\n get: (id) => invoke<{ found: boolean; entry?: VaultSectionTypes['fullEntry'] }>('get', { id }),\n add: (patch) => invoke<VaultSectionTypes['summaryEntry']>('add', { patch }),\n update: (id, patch) => invoke<{ found: boolean; entry?: VaultSectionTypes['summaryEntry'] }>('update', { id, patch }),\n remove: (id) => invoke<{ deleted: boolean }>('delete', { id }),\n trash: () => invoke<{ entries: VaultSectionTypes['entries'] }>('trash').then(r => r.entries),\n rotation: () => invoke<{ entries: unknown[] }>('rotation').then(r => r.entries),\n history: () => invoke<{ events: unknown[] }>('history').then(r => r.events),\n stats: () => invoke<Record<string, unknown>>('stats'),\n recent: () => invoke<{ entries: unknown[] }>('recent').then(r => r.entries),\n backupStatus: () => invoke<{ daysSinceBackup: number; backups: number }>('backupStatus'),\n backup: (maxBackups) => invoke<{ path: string; kept: number; pruned: number }>('backup', { maxBackups: maxBackups ?? 10 }),\n health: () => invoke<{ weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } }>('health'),\n restore: (id) => invoke<{ restored: boolean }>('restore', { id }),\n undeleteAll: () => invoke<{ restored: number }>('undeleteAll'),\n totp: (id) => invoke<{ code: string; label?: string; secondsRemaining: number }>('totp', { id }),\n })\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'vault',\n order: 30,\n label: () => t('nav'),\n inject: injected,\n }, VaultSection))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["css"],"sources":["../src/client/VaultSection.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Vault settings section: list, search, add, edit, delete, and copy encrypted\n * credentials. All mutations go through the host VaultGateway remote; secrets\n * are shown only inside the edit form or a copy action, never in the list.\n */\n\nimport { useEffect, useId, useMemo, useState, type ReactNode } from 'react'\nimport type { PropsLocale, PropsRuntime, InjectFace, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { VaultLocaleKey } from './locales.ts'\nimport css from './VaultSection.module.css'\n\n/** Wire shapes shared with the host gateway (mirror of src/index.ts types). */\nexport interface VaultSummaryWire {\n id: string\n title: string\n sensitivity?: string\n kind?: string\n username?: string\n email?: string\n phone?: string\n host?: string\n port?: string\n url?: string\n tags?: string[]\n icon?: string\n color?: string\n}\n\nexport interface VaultFullWire {\n id: string\n title: string\n kind?: string\n username?: string\n email?: string\n phone?: string\n password?: string\n host?: string\n port?: string\n privateKey?: string\n apiKey?: string\n secret?: string\n accessToken?: string\n refreshToken?: string\n expiresAt?: number\n rotationDays?: number\n sensitivity?: string\n favorite?: boolean\n otpSecret?: string\n url?: string\n notes?: string\n tags?: string[]\n icon?: string\n color?: string\n fields?: Record<string, unknown>\n}\n\n/** Patch shape accepted by vault.add / vault.update. */\nexport type VaultPatch = Partial<Omit<VaultFullWire, 'id'>>\n\n/** Registration-side business face supplied by the plugin entry. */\nexport interface VaultSectionInjected {\n t: TranslateNS<'settings.vault'>\n config: () => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n setAccessMode: (mode: 'readonly' | 'ask' | 'auto') => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n setAutoCapture: (enabled: boolean) => Promise<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }>\n list: () => Promise<VaultSummaryWire[]>\n search: (query: string, limit?: number) => Promise<VaultSummaryWire[]>\n get: (id: string) => Promise<{ found: boolean; entry?: VaultFullWire }>\n add: (patch: VaultPatch & { title: string }) => Promise<VaultSummaryWire>\n update: (id: string, patch: VaultPatch) => Promise<{ found: boolean; entry?: VaultSummaryWire }>\n remove: (id: string) => Promise<{ deleted: boolean }>\n trash: () => Promise<VaultSummaryWire[]>\n rotation: () => Promise<unknown[]>\n history: () => Promise<unknown[]>\n stats: () => Promise<Record<string, unknown>>\n recent: () => Promise<unknown[]>\n backupStatus: () => Promise<{ daysSinceBackup: number; backups: number }>\n backup: (maxBackups?: number) => Promise<{ path: string; kept: number; pruned: number }>\n health: () => Promise<{ weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } }>\n duplicates: () => Promise<{ groups: number }>\n restore: (id: string) => Promise<{ restored: boolean }>\n undeleteAll: () => Promise<{ restored: number }>\n totp: (id: string) => Promise<{ code: string; label?: string; secondsRemaining: number }>\n}\n\n/** Type-level alias so consumers can reference the wire shapes without values. */\nexport type VaultSectionTypes = {\n entries: VaultSummaryWire[]\n fullEntry: VaultFullWire\n summaryEntry: VaultSummaryWire\n config: { accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean }\n accessModes: Array<'readonly' | 'ask' | 'auto'>\n}\n\n/** Full component props assembled by the Settings slot renderer. */\nexport type VaultSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'settings.vault'>\n & InjectFace<VaultSectionInjected>\n\n/** The editable form fields we render (subset of the full wire). */\ntype FormFields = {\n title?: string | undefined\n kind?: string | undefined\n username?: string | undefined\n email?: string | undefined\n phone?: string | undefined\n password?: string | undefined\n host?: string | undefined\n port?: string | undefined\n privateKey?: string | undefined\n apiKey?: string | undefined\n secret?: string | undefined\n accessToken?: string | undefined\n refreshToken?: string | undefined\n otpSecret?: string | undefined\n url?: string | undefined\n notes?: string | undefined\n icon?: string | undefined\n color?: string | undefined\n expiresAt?: number | undefined\n rotationDays?: number | undefined\n sensitivity?: string | undefined\n favorite?: boolean | undefined\n}\n\nconst FORM_FIELDS: Array<{ key: keyof FormFields; label: VaultLocaleKey }> = [\n { key: 'title', label: 'fieldTitle' },\n { key: 'kind', label: 'fieldKind' },\n { key: 'username', label: 'fieldUsername' },\n { key: 'email', label: 'fieldEmail' },\n { key: 'phone', label: 'fieldPhone' },\n { key: 'password', label: 'fieldPassword' },\n { key: 'host', label: 'fieldHost' },\n { key: 'port', label: 'fieldPort' },\n { key: 'apiKey', label: 'fieldApiKey' },\n { key: 'secret', label: 'fieldSecret' },\n { key: 'accessToken', label: 'fieldAccessToken' },\n { key: 'refreshToken', label: 'fieldRefreshToken' },\n { key: 'otpSecret', label: 'fieldOtpSecret' },\n { key: 'url', label: 'fieldUrl' },\n { key: 'notes', label: 'fieldNotes' },\n { key: 'expiresAt', label: 'fieldExpiresAt' },\n { key: 'rotationDays', label: 'fieldRotationDays' },\n { key: 'sensitivity', label: 'fieldSensitivity' },\n { key: 'favorite', label: 'fieldFavorite' },\n { key: 'icon', label: 'fieldIcon' },\n { key: 'color', label: 'fieldColor' },\n]\n\nconst KIND_KEYS: Record<string, VaultLocaleKey> = {\n login: 'kindLogin',\n ssh: 'kindSsh',\n 'api-key': 'kindApiKey',\n secret: 'kindSecret',\n oauth: 'kindOauth',\n custom: 'kindCustom',\n}\n\ntype ViewState =\n | { readonly status: 'loading' }\n | { readonly status: 'error' }\n | { readonly status: 'ready'; readonly entries: VaultSummaryWire[] }\n\ntype EditorState =\n | { readonly status: 'closed' }\n | { readonly status: 'creating' }\n | { readonly status: 'editing'; readonly entry: VaultFullWire }\n\nfunction emptyForm(): FormFields {\n return { title: '', kind: 'login', username: '', email: '', phone: '', password: '', host: '', port: '', icon: '', color: '' }\n}\n\n/** Render the Vault settings section. */\nexport function VaultSection(props: VaultSectionProps): ReactNode {\n const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, duplicates, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp } = props\n const searchId = useId()\n const [query, setQuery] = useState('')\n const [state, setState] = useState<ViewState>({ status: 'loading' })\n const [editor, setEditor] = useState<EditorState>({ status: 'closed' })\n const [form, setForm] = useState<FormFields>(emptyForm())\n const [busy, setBusy] = useState(false)\n const [message, setMessage] = useState<string | null>(null)\n const [copiedId, setCopiedId] = useState<string | null>(null)\n const [totpMap, setTotpMap] = useState<Record<string, { code: string; until: number }>>({})\n const [nowTick, setNowTick] = useState(Date.now())\n const [tagsDraft, setTagsDraft] = useState('')\n const [fieldsDraft, setFieldsDraft] = useState('')\n const [revealed, setRevealed] = useState<Record<string, boolean>>({})\n const [kindFilter, setKindFilter] = useState('')\n const [tagFilter, setTagFilter] = useState('')\n const [expandedId, setExpandedId] = useState<string | null>(null)\n const [sortBy, setSortBy] = useState<'alpha' | 'recent'>('alpha')\n const [policy, setPolicy] = useState<{ accessMode: 'readonly' | 'ask' | 'auto'; autoCapture: boolean } | null>(null)\n const [showTrash, setShowTrash] = useState(false)\n const [trashEntries, setTrashEntries] = useState<VaultSummaryWire[]>([])\n const [report, setReport] = useState<{ rotation: unknown[]; weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } | null } | null>(null)\n const [dueMap, setDueMap] = useState<Record<string, { due: string; daysLeft: number }>>({})\n const [recentEvents, setRecentEvents] = useState<Array<Record<string, unknown>>>([])\n const [vaultStats, setVaultStats] = useState<Record<string, unknown> | null>(null)\n const [backupInfo, setBackupInfo] = useState<{ daysSinceBackup: number; backups: number } | null>(null)\n const [recentEntries, setRecentEntries] = useState<Array<Record<string, unknown>>>([])\n const [dupGroups, setDupGroups] = useState<number>(0)\n\n const readonly = policy?.accessMode === 'readonly'\n\n /** Run an encrypted backup now and refresh the backup-age badge. */\n async function backupNow(): Promise<void> {\n setBusy(true)\n setMessage(null)\n try {\n const result = await backup(10)\n setBackupInfo({ daysSinceBackup: 0, backups: result.kept })\n setMessage(`${t('backupDone')} (${result.kept} kept, ${result.pruned} pruned)`)\n void refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n useEffect(() => {\n let current = true\n void config().then(\n value => { if (current) setPolicy(value) },\n () => { /* policy is informational; ignore failures */ },\n )\n return () => { current = false }\n }, [config])\n\n const refresh = useMemo(() => async () => {\n setState({ status: 'loading' })\n try {\n const entries = query.trim().length === 0\n ? await list()\n : await search(query.trim())\n setState({ status: 'ready', entries })\n } catch {\n setState({ status: 'error' })\n }\n }, [list, search, query])\n\n useEffect(() => {\n let current = true\n const timer = window.setTimeout(() => {\n void refresh().then(\n () => { /* state already set inside refresh */ },\n () => { if (current) setState({ status: 'error' }) },\n )\n }, 250)\n return () => { current = false; window.clearTimeout(timer) }\n // refresh is memoized on query (debounced); list/search are stable.\n }, [refresh])\n\n // TOTP countdown: tick every second so the progress ring stays live.\n useEffect(() => {\n const timer = window.setInterval(() => setNowTick(Date.now()), 1000)\n return () => window.clearInterval(timer)\n }, [])\n\n // Vault health & meta: load once on mount (stats, backup age, rotation,\n // weak/reused scan, recent activity) and refresh on window focus.\n useEffect(() => {\n let current = true\n const load = (): void => {\n void Promise.all([\n stats().catch(() => null),\n backupStatus().catch(() => null),\n rotation().catch(() => []),\n health().catch(() => null),\n recent().catch(() => []),\n duplicates().catch(() => null),\n ]).then(([st, bk, rot, hl, rc, dp]) => {\n if (dp !== null && typeof dp === 'object' && (dp as { groups?: number }).groups !== undefined) {\n setDupGroups((dp as { groups: number }).groups)\n }\n if (!current) return\n if (st !== null) setVaultStats(st as Record<string, unknown>)\n if (bk !== null) setBackupInfo(bk)\n setReport({ rotation: (rot ?? []) as unknown[], weak: ((hl?.weak ?? []) as unknown[]), reused: ((hl?.reused ?? []) as unknown[]), strength: (hl?.strength ?? null) as { weak: number; fair: number; strong: number } | null })\n const due: Record<string, { due: string; daysLeft: number }> = {}\n for (const item of (rot ?? []) as Array<{ id?: string; due?: string; daysLeft?: number }>) {\n if (item.id !== undefined && item.due !== undefined) due[item.id] = { due: item.due, daysLeft: item.daysLeft ?? 0 }\n }\n setDueMap(due)\n setRecentEntries((rc ?? []) as Array<Record<string, unknown>>)\n })\n }\n load()\n const onFocus = (): void => { load() }\n window.addEventListener('focus', onFocus)\n return () => { current = false; window.removeEventListener('focus', onFocus) }\n }, [stats, backupStatus, rotation, health, recent, duplicates])\n\n /** Open the editor for a new entry. */\n function startCreate(): void {\n setForm(emptyForm())\n setTagsDraft('')\n setFieldsDraft('')\n setMessage(null)\n setEditor({ status: 'creating' })\n }\n\n /** Open the editor for an existing entry (fetches full secrets). */\n async function startEdit(id: string): Promise<void> {\n setBusy(true)\n setMessage(null)\n try {\n const result = await get(id)\n if (!result.found || result.entry === undefined) {\n setMessage(t('error'))\n return\n }\n const entry = result.entry\n setForm({\n title: entry.title ?? '',\n kind: entry.kind ?? 'login',\n username: entry.username ?? '',\n email: entry.email ?? '',\n phone: entry.phone ?? '',\n password: entry.password ?? '',\n host: entry.host ?? '',\n port: entry.port ?? '',\n privateKey: entry.privateKey ?? '',\n apiKey: entry.apiKey ?? '',\n secret: entry.secret ?? '',\n accessToken: entry.accessToken ?? '',\n refreshToken: entry.refreshToken ?? '',\n otpSecret: entry.otpSecret ?? '',\n url: entry.url ?? '',\n notes: entry.notes ?? '',\n icon: entry.icon ?? '',\n color: entry.color ?? '',\n expiresAt: entry.expiresAt,\n rotationDays: entry.rotationDays,\n sensitivity: entry.sensitivity,\n favorite: entry.favorite ?? false,\n })\n setTagsDraft((entry.tags ?? []).join(', '))\n setFieldsDraft(entry.fields !== undefined ? Object.entries(entry.fields).map(([k, v]) => `${k}=${String(v)}`).join('\\n') : '')\n setEditor({ status: 'editing', entry })\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Save the current form (create or update). */\n async function save(): Promise<void> {\n if (!(form.title ?? '').trim()) {\n setMessage(t('error'))\n return\n }\n setBusy(true)\n setMessage(null)\n try {\n const tags = tagsDraft.split(',').map(x => x.trim()).filter(x => x.length > 0)\n const fields: Record<string, string> = {}\n for (const line of fieldsDraft.split('\\n')) {\n const trimmed = line.trim()\n if (trimmed.length === 0) continue\n const eq = trimmed.indexOf('=')\n if (eq <= 0) continue\n fields[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim()\n }\n const patch: VaultPatch = {\n title: (form.title ?? '').trim(),\n ...(form.kind !== undefined ? { kind: form.kind } : {}),\n ...(form.username !== undefined ? { username: form.username } : {}),\n ...(form.email !== undefined ? { email: form.email } : {}),\n ...(form.phone !== undefined ? { phone: form.phone } : {}),\n ...(form.password !== undefined ? { password: form.password } : {}),\n ...(form.host !== undefined ? { host: form.host } : {}),\n ...(form.port !== undefined ? { port: form.port } : {}),\n ...(form.privateKey !== undefined ? { privateKey: form.privateKey } : {}),\n ...(form.apiKey !== undefined ? { apiKey: form.apiKey } : {}),\n ...(form.secret !== undefined ? { secret: form.secret } : {}),\n ...(form.accessToken !== undefined ? { accessToken: form.accessToken } : {}),\n ...(form.refreshToken !== undefined ? { refreshToken: form.refreshToken } : {}),\n ...(form.otpSecret !== undefined ? { otpSecret: form.otpSecret } : {}),\n ...(form.url !== undefined ? { url: form.url } : {}),\n ...(form.notes !== undefined ? { notes: form.notes } : {}),\n ...(form.icon !== undefined ? { icon: form.icon } : {}),\n ...(form.color !== undefined ? { color: form.color } : {}),\n ...(form.expiresAt !== undefined ? { expiresAt: form.expiresAt } : {}),\n ...(form.rotationDays !== undefined ? { rotationDays: form.rotationDays } : {}),\n ...(form.sensitivity !== undefined ? { sensitivity: form.sensitivity } : {}),\n ...(form.favorite !== undefined ? { favorite: form.favorite } : {}),\n // Always send tags: an empty array clears them, and the host store\n // treats empty string values as \"clear this field\" too.\n tags,\n fields,\n }\n if (editor.status === 'creating') {\n await add(patch as VaultPatch & { title: string })\n } else if (editor.status === 'editing') {\n await update(editor.entry.id, patch)\n }\n setEditor({ status: 'closed' })\n await refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Delete an entry after confirmation. */\n async function removeEntry(id: string): Promise<void> {\n if (!window.confirm(t('deleteConfirm'))) return\n setBusy(true)\n try {\n await remove(id)\n await refresh()\n } catch {\n setMessage(t('error'))\n } finally {\n setBusy(false)\n }\n }\n\n /** Copy a field value to the clipboard and flash the row. */\n async function copyValue(id: string, value: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(value)\n } catch {\n // Clipboard may be blocked; fall back to a temporary textarea.\n const textarea = document.createElement('textarea')\n textarea.value = value\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n textarea.remove()\n }\n setCopiedId(id)\n window.setTimeout(() => setCopiedId(null), 1500)\n }\n\n /** Fetch and display a TOTP code for an entry with an otpSecret. */\n async function showTotp(id: string): Promise<void> {\n setBusy(true)\n try {\n const result = await totp(id)\n setTotpMap(previous => ({ ...previous, [id]: { code: result.code, until: Date.now() + result.secondsRemaining * 1000 } }))\n } catch {\n setTotpMap(previous => ({ ...previous, [id]: { code: t('error'), until: 0 } }))\n } finally {\n setBusy(false)\n }\n }\n\n /** Field-set summary line for one entry (non-secret). */\n function kindIcon(kind?: string): string {\n switch (kind ?? 'login') {\n case 'ssh': return '🖥️'\n case 'api-key': return '🔑'\n case 'oauth': return '🔐'\n case 'secret': return '🤫'\n case 'custom': return '🧩'\n default: return '👤'\n }\n }\n\n function identityLine(entry: VaultSummaryWire): string {\n const parts = [\n entry.kind !== undefined ? t(KIND_KEYS[entry.kind] ?? 'kindCustom') : t('kindLogin'),\n entry.username,\n entry.email,\n entry.phone,\n entry.host !== undefined ? `${entry.host}${entry.port !== undefined ? `:${entry.port}` : ''}` : undefined,\n entry.url,\n ].filter((value): value is string => value !== undefined && value.length > 0)\n return parts.join(' · ')\n }\n\n return (\n <section className={css.section} aria-labelledby=\"vault-heading\">\n <h2 id=\"vault-heading\">{t('heading')}</h2>\n <p className={css.intro}>{t('intro')}</p>\n\n <div className={css.toolbar}>\n <label className={css.searchBox}>\n <span className=\"sr-only\">{t('searchPlaceholder')}</span>\n <input\n id={searchId}\n type=\"search\"\n placeholder={t('searchPlaceholder')}\n value={query}\n onChange={event => setQuery(event.target.value)}\n />\n {query.length > 0 && (\n <button type=\"button\" className={css.clearButton} onClick={() => setQuery('')} aria-label={t('clearSearch')}>×</button>\n )}\n </label>\n <select className={css.kindFilter} value={kindFilter} onChange={e => setKindFilter(e.target.value)} aria-label={t('fieldKind')}>\n <option value=\"\">{t('allKinds')}</option>\n {Object.entries(KIND_KEYS).map(([value, key]) => (\n <option key={value} value={value}>{t(key)}</option>\n ))}\n </select>\n <select className={css.kindFilter} value={tagFilter} onChange={e => setTagFilter(e.target.value)} aria-label={t('fieldTags')}>\n <option value=\"\">{t('allTags')}</option>\n {[...new Set(state.status === 'ready' ? state.entries.flatMap(e => e.tags ?? []) : [])].sort().map(tag => (\n <option key={tag} value={tag}>{tag}</option>\n ))}\n </select>\n <button\n type=\"button\"\n className={css.sortButton}\n onClick={() => setSortBy(sortBy === 'alpha' ? 'recent' : 'alpha')}\n >{sortBy === 'alpha' ? t('sortAlpha') : t('sortRecent')}</button>\n <button type=\"button\" className={css.addButton} onClick={startCreate} disabled={busy || readonly}>\n + {t('add')}\n </button>\n </div>\n\n {policy !== null && (\n <div className={css.policyBar}>\n <label className={css.policyField}>\n <span>{t('modeLabel')}</span>\n <select\n value={policy.accessMode}\n disabled={busy}\n onChange={event => {\n const next = event.target.value as 'readonly' | 'ask' | 'auto'\n setBusy(true)\n setMessage(null)\n void setAccessMode(next).then(\n value => { setPolicy(value); setBusy(false) },\n () => { setMessage(t('error')); setBusy(false) },\n )\n }}\n >\n <option value=\"readonly\">{t('modeReadonly')}</option>\n <option value=\"ask\">{t('modeAsk')}</option>\n <option value=\"auto\">{t('modeAuto')}</option>\n </select>\n </label>\n <label className={css.policyField}>\n <span>{t('autoCaptureLabel')}</span>\n <input\n type=\"checkbox\"\n checked={policy.autoCapture}\n disabled={busy}\n onChange={event => {\n const next = event.target.checked\n setBusy(true)\n setMessage(null)\n void setAutoCapture(next).then(\n value => { setPolicy(value); setBusy(false) },\n () => { setMessage(t('error')); setBusy(false) },\n )\n }}\n />\n </label>\n <p className={policy.accessMode === 'readonly' ? css.modeReadonly : policy.accessMode === 'ask' ? css.modeAsk : css.modeAuto}>\n {policy.accessMode === 'readonly'\n ? t('modeReadonlyHint')\n : policy.accessMode === 'ask'\n ? t('modeAskHint')\n : t('modeAutoHint')}\n {policy.autoCapture ? ` · ${t('autoCaptureOn')}` : ` · ${t('autoCaptureOff')}`}\n </p>\n </div>\n )}\n\n {message !== null && <p role=\"alert\" className={css.error}>{message}</p>}\n\n {state.status === 'loading' && <p className={css.status}>{t('loading')}</p>}\n {state.status === 'error' && <p role=\"alert\" className={css.error}>{t('error')}</p>}\n {state.status === 'ready' && state.entries.length === 0 && (\n <div className={css.emptyBox}>\n <p className={css.empty}>{t('empty')}</p>\n <p className={css.emptyHint}>{readonly ? t('emptyHintReadonly') : t('emptyHint')}</p>\n {!readonly && (\n <button type=\"button\" className={css.addButton} onClick={startCreate}>{t('quickAdd')}</button>\n )}\n </div>\n )}\n\n {recentEntries.length > 0 && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('recentlyAdded')}</p>\n {recentEntries.map((e, i) => (\n <p key={i} className={css.reportLine}>{String(e.title ?? '')}</p>\n ))}\n </div>\n )}\n\n {recentEvents.length > 0 && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('recentActivity')}</p>\n {recentEvents.map((ev, i) => (\n <p key={i} className={css.reportLine}>\n {String(ev.action ?? '')} · {String(ev.title ?? ev.id ?? '')}\n </p>\n ))}\n </div>\n )}\n\n {report !== null && (report.rotation.length > 0 || report.weak.length > 0 || report.reused.length > 0) && (\n <div className={css.reportBox}>\n <p className={css.reportTitle}>{t('reportTitle')}</p>\n {report.rotation.length > 0 && (\n <>\n <p className={css.reportLine}>{t('reportRotation')}: {report.rotation.length}</p>\n {(report.rotation as Array<{ title?: string; due?: string; daysLeft?: number }>).slice(0, 8).map((item, i) => (\n <p key={i} className={css.reportSub}>\n · {String(item.title ?? '?')}{item.due === 'expired' ? ` (${t('dueExpired')})` : item.due === 'soon' ? ` (${t('dueExpiring')} ${item.daysLeft ?? 0}d)` : ` (${t('dueNow')})`}\n </p>\n ))}\n </>\n )}\n {report.weak.length > 0 && (\n <>\n <p className={css.reportLine}>{t('reportWeak')}: {report.weak.length}</p>\n {(report.weak as Array<{ title?: string }>).slice(0, 5).map((item, i) => (\n <p key={i} className={css.reportSub}>· {String(item.title ?? '?')}</p>\n ))}\n </>\n )}\n {report.reused.length > 0 && (\n <>\n <p className={css.reportLine}>{t('reportReused')}: {report.reused.length}</p>\n {(report.reused as Array<{ entries?: Array<{ title?: string }> }>).slice(0, 3).map((group, i) => (\n <p key={i} className={css.reportSub}>\n · {(group.entries ?? []).slice(0, 3).map(e => String(e.title ?? '?')).join(' / ')}\n </p>\n ))}\n </>\n )}\n </div>\n )}\n\n {state.status === 'ready' && (state.entries.length > 0 || trashEntries.length > 0) && (\n <button\n type=\"button\"\n className={css.trashButton}\n onClick={() => {\n if (!showTrash) void trash().then(setTrashEntries)\n setShowTrash(!showTrash)\n }}\n >{showTrash ? t('hideTrash') : t('showTrash')}{trashEntries.length > 0 ? ` (${trashEntries.length})` : ''}</button>\n )}\n\n {showTrash && (\n <ul className={css.list}>\n {trashEntries.map(entry => (\n <li key={entry.id} className={css.row}>\n <div className={css.rowMain}>\n <span className={css.title}>{entry.title}</span>\n <span className={css.identity}>{t('trashed')}</span>\n </div>\n <div className={css.rowActions}>\n <button\n type=\"button\"\n onClick={() => { void restore(entry.id).then(() => { void trash().then(setTrashEntries); void refresh() }) }}\n disabled={busy || readonly}\n >{t('restore')}</button>\n </div>\n </li>\n ))}\n {trashEntries.length === 0 && <p className={css.empty}>{t('trashEmpty')}</p>}\n </ul>\n )}\n {showTrash && trashEntries.length > 0 && (\n <>\n <button\n type=\"button\"\n className={css.trashButton}\n onClick={() => {\n setBusy(true)\n void undeleteAll().then(() => {\n void trash().then(setTrashEntries)\n void refresh()\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('restoreAll')}</button>\n <button\n type=\"button\"\n className={css.dangerButton}\n onClick={() => {\n if (!window.confirm(t('clearTrashConfirm'))) return\n setBusy(true)\n void Promise.all(trashEntries.map(e => remove(e.id))).then(() => {\n void trash().then(setTrashEntries)\n void refresh()\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('clearTrash')}</button>\n </>\n )}\n\n {state.status === 'ready' && state.entries.length > 0 && (\n <ul className={css.list}>\n {state.entries.filter(entry => (kindFilter === '' || entry.kind === kindFilter) && (tagFilter === '' || (entry.tags ?? []).includes(tagFilter))).sort((a, b) => sortBy === 'alpha' ? a.title.localeCompare(b.title) : 0).map(entry => {\n const totpInfo = totpMap[entry.id]\n const remaining = totpInfo !== undefined && totpInfo.until > 0 ? Math.max(0, Math.ceil((totpInfo.until - nowTick) / 1000)) : undefined\n const frac = remaining !== undefined ? remaining / 30 : 0\n const code = totpInfo?.code\n return (\n <li key={entry.id} className={`${css.row}${dueMap[entry.id] !== undefined ? ` ${dueMap[entry.id]!.due === 'expired' ? css.rowExpired : css.rowDue}` : ''}`}>\n <div className={css.rowMain} onClick={() => setExpandedId(expandedId === entry.id ? null : entry.id)}>\n <span className={css.title} style={entry.color !== undefined && entry.color !== '' ? { borderLeft: `3px solid ${entry.color}`, paddingLeft: 6 } : undefined}>\n <span className={css.kindIcon}>{entry.icon ?? kindIcon(entry.kind)}</span>\n {(entry as VaultSummaryWire & { favorite?: boolean }).favorite && (\n <span className={css.pinStar} title={t('pinned')}>★</span>\n )}\n {entry.title}\n {(entry as VaultSummaryWire & { sensitivity?: string }).sensitivity === 'high' && (\n <span className={css.highBadge}>{t('highSensitivity')}</span>\n )}\n {dueMap[entry.id] !== undefined && (\n <span className={`${css.dueBadge} ${dueMap[entry.id]!.due === 'expired' ? css.badgeDanger : css.badgeWarn}`}>\n {dueMap[entry.id]!.due === 'expired' ? t('dueExpired') : dueMap[entry.id]!.due === 'soon' ? `${t('dueExpiring')} ${dueMap[entry.id]!.daysLeft}d` : t('dueNow')}\n </span>\n )}\n </span>\n <span className={css.identity}>{identityLine(entry)}</span>\n {code !== undefined && (\n <span className={css.totp}>\n <svg className={css.totpRing} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" fill=\"none\" stroke=\"var(--dsh-color-border, #ddd)\" strokeWidth=\"2\" />\n <circle\n cx=\"8\" cy=\"8\" r=\"6.5\" fill=\"none\"\n stroke={remaining !== undefined && remaining <= 5 ? '#cf3d3d' : '#2e9e5b'}\n strokeWidth=\"2\" strokeLinecap=\"round\"\n strokeDasharray={`${(frac * 2 * Math.PI * 6.5).toFixed(1)} ${(2 * Math.PI * 6.5).toFixed(1)}`}\n transform=\"rotate(-90 8 8)\"\n />\n </svg>\n {code}{remaining !== undefined ? ` (${remaining}s)` : ''}\n </span>\n )}\n </div>\n {expandedId === entry.id && (\n <div className={css.detailBox}>\n {Object.entries(entry).filter(([k]) => !['id', 'title'].includes(k) && entry[k as keyof VaultSummaryWire] !== undefined).map(([k, v]) => (\n <span key={k} className={css.detailItem}>\n <strong>{k}</strong>: {Array.isArray(v) ? v.join(', ') : String(v)}\n </span>\n ))}\n </div>\n )}\n <div className={css.rowActions}>\n {copiedId === entry.id && <span className={css.copied}>{t('copied')}</span>}\n <button\n type=\"button\"\n onClick={() => void copyValue(entry.id, entry.username ?? entry.title)}\n disabled={busy}\n >{t('copy')}</button>\n <button\n type=\"button\"\n onClick={() => {\n setBusy(true)\n void get(entry.id).then(r => {\n if (r.found && r.entry?.password) void copyValue(entry.id, r.entry.password)\n setBusy(false)\n }, () => setBusy(false))\n }}\n disabled={busy || readonly}\n >{t('copyPassword')}</button>\n <button type=\"button\" onClick={() => void showTotp(entry.id)} disabled={busy}>{t('totp')}</button>\n {code !== undefined && (\n <button type=\"button\" onClick={() => void copyValue(entry.id, code)} disabled={busy}>{t('copyCode')}</button>\n )}\n <button type=\"button\" onClick={() => void startEdit(entry.id)} disabled={busy || readonly}>{t('edit')}</button>\n <button\n type=\"button\"\n className={css.deleteButton}\n onClick={() => void removeEntry(entry.id)}\n disabled={busy || readonly}\n >{t('delete')}</button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n\n {state.status === 'ready' && (\n <div className={css.healthBar}>\n {vaultStats !== null && typeof vaultStats.total === 'number' && (\n <span className={css.badge}>{t('entryCount')}: {String(vaultStats.total)}</span>\n )}\n {vaultStats !== null && typeof vaultStats.withTotp === 'number' && (\n <span className={css.badge}>TOTP: {String(vaultStats.withTotp)}</span>\n )}\n {vaultStats !== null && typeof vaultStats.highSensitivity === 'number' && (\n <span className={css.badge}>{t('highSensitivity')}: {String(vaultStats.highSensitivity)}</span>\n )}\n {report !== null && report.strength !== null && (\n <span className={`${css.badge} ${report.strength.weak > 0 ? css.badgeDanger : css.badgeOk}`}>\n {t('healthStrength')}: {report.strength.weak}W/{report.strength.fair}F/{report.strength.strong}S\n </span>\n )}\n {report !== null && report.rotation.length > 0 && (\n <span className={`${css.badge} ${css.badgeWarn}`}>{t('reportRotation')}: {report.rotation.length}</span>\n )}\n {report !== null && report.weak.length > 0 && (\n <span className={`${css.badge} ${css.badgeDanger}`}>{t('reportWeak')}: {report.weak.length}</span>\n )}\n {report !== null && report.reused.length > 0 && (\n <span className={`${css.badge} ${css.badgeDanger}`}>{t('reportReused')}: {report.reused.length}</span>\n )}\n {dupGroups > 0 && (\n <span className={`${css.badge} ${css.badgeWarn}`}>{t('dupGroups')}: {dupGroups}</span>\n )}\n {report !== null && report.rotation.length === 0 && report.weak.length === 0 && report.reused.length === 0 && (\n <span className={`${css.badge} ${css.badgeOk}`}>{t('healthOk')}</span>\n )}\n {backupInfo !== null && backupInfo.daysSinceBackup >= 0 && (\n <span className={`${css.badge} ${backupInfo.daysSinceBackup > 14 ? css.badgeDanger : backupInfo.daysSinceBackup > 7 ? css.badgeWarn : ''}`}>\n {t('healthBackup')}: {backupInfo.daysSinceBackup}d\n </span>\n )}\n <button type=\"button\" className={css.backupButton} onClick={() => void backupNow()} disabled={busy}>\n {t('backupNow')}\n </button>\n </div>\n )}\n {state.status === 'ready' && (\n <p className={css.footer}>\n {t('entryCount')}: {state.entries.length}\n {vaultStats !== null && typeof vaultStats.withTotp === 'number' && ` · TOTP: ${String(vaultStats.withTotp)}`}\n {vaultStats !== null && typeof vaultStats.highSensitivity === 'number' && ` · ${t('highSensitivity')}: ${String(vaultStats.highSensitivity)}`}\n {backupInfo !== null && backupInfo.daysSinceBackup >= 0 && ` · backup: ${backupInfo.daysSinceBackup}d ago`}\n {vaultStats !== null && typeof vaultStats.byTag === 'object' && vaultStats.byTag !== null\n && Object.keys(vaultStats.byTag as Record<string, unknown>).length > 0\n && ` · tags: ${Object.entries(vaultStats.byTag as Record<string, unknown>).map(([k, v]) => `${k}(${String(v)})`).join(' ')}`}\n </p>\n )}\n\n {editor.status !== 'closed' && (\n <div className={css.editor} role=\"dialog\" aria-label={editor.status === 'creating' ? t('add') : t('edit')}>\n <div className={css.editorBody}>\n {FORM_FIELDS.map(field => (\n <label key={field.key} className={css.field}>\n <span>{t(field.label)}</span>\n {field.key === 'kind' ? (\n <select\n value={form.kind ?? 'login'}\n onChange={event => setForm(previous => ({ ...previous, kind: event.target.value }))}\n >\n {Object.entries(KIND_KEYS).map(([value, key]) => (\n <option key={value} value={value}>{t(key)}</option>\n ))}\n </select>\n ) : field.key === 'title' ? (\n <input\n value={form.title ?? ''}\n onChange={event => setForm(previous => ({ ...previous, title: event.target.value }))}\n />\n ) : field.key === 'password' || field.key === 'otpSecret' || field.key === 'apiKey'\n || field.key === 'secret' || field.key === 'accessToken' || field.key === 'refreshToken'\n || field.key === 'privateKey' ? (\n <span className={css.secretField}>\n <input\n type={revealed[field.key] ? 'text' : 'password'}\n value={(form[field.key] as string | undefined) ?? ''}\n onChange={event => setForm(previous => ({ ...previous, [field.key]: event.target.value }))}\n />\n <button\n type=\"button\"\n className={css.revealButton}\n onClick={() => setRevealed(previous => ({ ...previous, [field.key]: !previous[field.key] }))}\n >{revealed[field.key] ? t('hide') : t('show')}</button>\n </span>\n ) : field.key === 'expiresAt' ? (\n <input\n type=\"datetime-local\"\n value={form.expiresAt !== undefined ? new Date(form.expiresAt).toISOString().slice(0, 16) : ''}\n onChange={event => {\n const raw = event.target.value\n const epoch = raw.length > 0 ? Date.parse(raw) : NaN\n setForm(previous => ({ ...previous, expiresAt: Number.isNaN(epoch) ? undefined : epoch }))\n }}\n />\n ) : field.key === 'rotationDays' ? (\n <input\n type=\"number\"\n min={0}\n max={3650}\n placeholder={t('rotationClearHint')}\n value={form.rotationDays ?? ''}\n onChange={event => {\n const raw = event.target.value\n setForm(previous => ({ ...previous, rotationDays: raw.length > 0 ? Number(raw) : undefined }))\n }}\n />\n ) : field.key === 'sensitivity' ? (\n <select\n value={form.sensitivity ?? 'normal'}\n onChange={event => setForm(previous => ({ ...previous, sensitivity: event.target.value === 'high' ? 'high' : 'normal' }))}\n >\n <option value=\"normal\">{t('sensitivityNormal')}</option>\n <option value=\"high\">{t('sensitivityHigh')}</option>\n </select>\n ) : field.key === 'favorite' ? (\n <label className={css.checkField}>\n <input\n type=\"checkbox\"\n checked={form.favorite ?? false}\n onChange={event => setForm(previous => ({ ...previous, favorite: event.target.checked }))}\n />\n {t('favoriteHint')}\n </label>\n ) : (\n <input\n type=\"text\"\n value={(form[field.key] as string | undefined) ?? ''}\n onChange={event => setForm(previous => ({ ...previous, [field.key]: event.target.value }))}\n />\n )}\n </label>\n ))}\n <label className={css.field}>\n <span>{t('fieldTags')}</span>\n <input\n value={tagsDraft}\n onChange={event => setTagsDraft(event.target.value)}\n placeholder=\"dev, prod\"\n />\n </label>\n <label className={css.field}>\n <span>{t('fieldCustom')}</span>\n <textarea\n value={fieldsDraft}\n onChange={event => setFieldsDraft(event.target.value)}\n rows={3}\n placeholder=\"region=us-east-1&#10;team=infra\"\n />\n </label>\n </div>\n <div className={css.editorActions}>\n <button type=\"button\" onClick={() => setEditor({ status: 'closed' })} disabled={busy}>{t('cancel')}</button>\n <button type=\"button\" className={css.saveButton} onClick={() => void save()} disabled={busy}>{t('save')}</button>\n </div>\n </div>\n )}\n </section>\n )\n}\n","/** Copy dictionaries for the Vault settings section. */\n\n/** English strings (the key-set source of truth for this pair). */\nexport const en = {\n nav: 'Vault',\n heading: 'Vault',\n intro: 'Encrypted credentials stored on this machine.',\n searchPlaceholder: 'Search title, username, email, host…',\n loading: 'Loading…',\n empty: 'No credentials yet. Add one to get started.',\n emptyHint: 'Tip: you can ask the assistant to save an API key or token you share in chat — with auto-capture on it offers to store it for you. Bulk importing? Ask for vault_import_csv with a CSV file path.',\n emptyHintReadonly: 'The vault is in readonly mode — credentials can be read but not added here. Switch accessMode to \"readwrite\" to enable writes.',\n modeLabel: 'Access mode',\n autoCaptureLabel: 'Auto-capture credentials from chat',\n modeReadonly: 'Read-only',\n modeAsk: 'Ask before writing',\n modeAuto: 'Automatic read-write',\n modeReadonlyHint: 'Read-only — mutations disabled',\n modeAskHint: 'Prompt before writing — every add/update/delete asks you',\n modeAutoHint: 'Automatic read-write — no per-call prompt',\n autoCaptureOn: 'auto-capture on',\n autoCaptureOff: 'auto-capture off',\n clearSearch: 'Clear search',\n entryCount: 'Entries',\n reportTitle: 'Health & rotation',\n recentActivity: 'Recent activity',\n recentlyAdded: 'Recently added',\n reportRotation: 'expired / due for rotation',\n reportWeak: 'weak passwords',\n reportReused: 'credentials reused across entries',\n dupGroups: 'duplicate groups',\n healthOk: 'vault healthy',\n healthBackup: 'backup',\n backupNow: 'Backup now',\n backupDone: 'Backup created',\n healthStrength: 'strength',\n dueExpired: 'EXPIRED',\n dueExpiring: 'expires in',\n dueNow: 'rotation due',\n showTrash: 'Trash',\n hideTrash: 'Hide trash',\n trashed: 'in trash',\n trashEmpty: 'Trash is empty',\n restore: 'Restore',\n clearTrash: 'Empty trash',\n restoreAll: 'Restore all',\n clearTrashConfirm: 'Permanently delete ALL trashed entries? This cannot be undone.',\n show: 'Show',\n hide: 'Hide',\n allKinds: 'All kinds',\n allTags: 'All tags',\n sortAlpha: 'Sort: A–Z',\n sortRecent: 'Sort: recent',\n highSensitivity: 'HIGH',\n pinned: 'Pinned',\n add: 'Add credential',\n quickAdd: 'Quick add',\n cancel: 'Cancel',\n save: 'Save',\n edit: 'Edit',\n delete: 'Delete',\n deleteConfirm: 'Delete this entry? This cannot be undone.',\n copy: 'Copy',\n copyPassword: 'Copy password',\n copyCode: 'Copy code',\n copied: 'Copied',\n totp: 'Code',\n fieldTitle: 'Title',\n fieldKind: 'Kind',\n fieldUsername: 'Username',\n fieldEmail: 'Email',\n fieldPhone: 'Phone',\n fieldPassword: 'Password',\n fieldHost: 'Host',\n fieldPort: 'Port',\n fieldApiKey: 'API key',\n fieldSecret: 'Secret',\n fieldAccessToken: 'Access token',\n fieldRefreshToken: 'Refresh token',\n fieldOtpSecret: 'OTP secret',\n fieldUrl: 'URL',\n fieldNotes: 'Notes',\n fieldTags: 'Tags',\n fieldIcon: 'Icon (emoji)',\n fieldExpiresAt: 'Expires at',\n fieldRotationDays: 'Rotation (days, 0 = never)',\n fieldSensitivity: 'Sensitivity',\n fieldFavorite: 'Pinned',\n sensitivityNormal: 'normal',\n sensitivityHigh: 'high',\n favoriteHint: 'Show at top',\n rotationClearHint: '0 clears rotation',\n fieldColor: 'Color',\n fieldCustom: 'Custom fields (key=value per line)',\n error: 'Something went wrong.',\n kindLogin: 'Login',\n kindSsh: 'SSH',\n kindApiKey: 'API key',\n kindSecret: 'Secret',\n kindOauth: 'OAuth',\n kindCustom: 'Custom',\n} as const\n\nexport type VaultLocaleKey = keyof typeof en\n\n/** Chinese strings (same keys as English). */\nexport const zh: Record<VaultLocaleKey, string> = {\n nav: '凭据库',\n heading: 'Vault 凭据库',\n intro: '本机加密存储的凭据。',\n searchPlaceholder: '搜索标题、用户名、邮箱、主机…',\n loading: '加载中…',\n empty: '还没有凭据,添加一条开始使用。',\n emptyHint: '提示:你可以在对话中把 API Key 或 token 交给助手保存——开启自动捕获后,它会在你同意时帮你存入本库。批量导入?让助手调用 vault_import_csv 并给出 CSV 路径即可。',\n emptyHintReadonly: '当前为只读模式——只能读取凭据,不能在此添加。将 accessMode 改为 \"readwrite\" 即可启用写入。',\n modeLabel: '访问模式',\n autoCaptureLabel: '自动捕获对话中的凭据',\n modeReadonly: '只读',\n modeAsk: '写入前询问',\n modeAuto: '自动读写',\n modeReadonlyHint: '只读 —— 变更已禁用',\n modeAskHint: '写入前询问 —— 每次增/改/删都会征求你的确认',\n modeAutoHint: '自动读写 —— 无需逐次确认',\n autoCaptureOn: '自动捕获已开启',\n autoCaptureOff: '自动捕获已关闭',\n clearSearch: '清除搜索',\n entryCount: '条目数',\n reportTitle: '健康与轮换',\n recentActivity: '最近活动',\n recentlyAdded: '最近添加',\n reportRotation: '已过期 / 待轮换',\n reportWeak: '弱密码',\n reportReused: '跨条目复用的凭据',\n dupGroups: '重复分组',\n healthOk: '凭据库健康',\n healthBackup: '备份',\n backupNow: '立即备份',\n backupDone: '备份已创建',\n healthStrength: '强度',\n dueExpired: '已过期',\n dueExpiring: '将于',\n dueNow: '待轮换',\n showTrash: '回收站',\n hideTrash: '隐藏回收站',\n trashed: '已删除',\n trashEmpty: '回收站为空',\n restore: '恢复',\n clearTrash: '清空回收站',\n restoreAll: '全部恢复',\n clearTrashConfirm: '永久删除回收站中全部条目?此操作不可撤销。',\n show: '显示',\n hide: '隐藏',\n allKinds: '全部类型',\n allTags: '全部标签',\n sortAlpha: '排序:A–Z',\n sortRecent: '排序:最近',\n highSensitivity: '高敏',\n pinned: '已置顶',\n add: '新增凭据',\n quickAdd: '快速添加',\n cancel: '取消',\n save: '保存',\n edit: '编辑',\n delete: '删除',\n deleteConfirm: '确定删除该条目?此操作不可撤销。',\n copy: '复制',\n copyPassword: '复制密码',\n copyCode: '复制验证码',\n copied: '已复制',\n totp: '验证码',\n fieldTitle: '标题',\n fieldKind: '类型',\n fieldUsername: '用户名',\n fieldEmail: '邮箱',\n fieldPhone: '手机号',\n fieldPassword: '密码',\n fieldHost: '主机',\n fieldPort: '端口',\n fieldApiKey: 'API 密钥',\n fieldSecret: 'Secret',\n fieldAccessToken: '访问令牌',\n fieldRefreshToken: '刷新令牌',\n fieldOtpSecret: 'TOTP 密钥',\n fieldUrl: 'URL',\n fieldNotes: '备注',\n fieldTags: '标签',\n fieldIcon: '图标(emoji)',\n fieldExpiresAt: '过期时间',\n fieldRotationDays: '轮换周期(天,0=永不)',\n fieldSensitivity: '敏感级别',\n fieldFavorite: '置顶',\n sensitivityNormal: '普通',\n sensitivityHigh: '高敏',\n favoriteHint: '列表置顶显示',\n rotationClearHint: '0 清除轮换',\n fieldColor: '颜色',\n fieldCustom: '自定义字段(每行 key=value)',\n error: '操作失败。',\n kindLogin: '登录',\n kindSsh: 'SSH',\n kindApiKey: 'API 密钥',\n kindSecret: 'Secret',\n kindOauth: 'OAuth',\n kindCustom: '自定义',\n}\n","/**\n * dsh-vault browser half: a Vault settings page managing encrypted\n * credentials — list, search, add, edit, delete, and copy — through the host\n * VaultGateway remote over the /api RPC channel.\n *\n * @module dsh-vault/client\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { VaultSection, type VaultSectionInjected } from './VaultSection.tsx'\nimport type { VaultSectionTypes } from './VaultSection.tsx'\nimport { en, zh, type VaultLocaleKey } from './locales.ts'\n\nexport type { VaultSectionInjected, VaultSectionProps } from './VaultSection.tsx'\nexport type { VaultSectionTypes } from './VaultSection.tsx'\nexport type { VaultLocaleKey } from './locales.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Vault settings page copy. */\n 'settings.vault': VaultLocaleKey\n }\n}\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'settings.vault'\n\n/** Services required by the Settings registration and the RPC face. */\nexport const inject = ['slots', 'locale', 'connection']\n\n/**\n * Register the Vault settings section once the `settings.section` declaration\n * is on the ledger. The section talks to the host through the connection RPC\n * channel (endpoints `vault/*` served by the host VaultGateway remote).\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-vault: dictionaries')\n\n const t = ctx.locale.bind(NS)\n const connection = ctx.get('connection') as ConnectionHandle\n\n /** Invoke one host vault remote and normalize the RPC envelope. */\n async function invoke<T>(method: string, args: Record<string, unknown> = {}): Promise<T> {\n const result = await connection.rpc.call('/api', `vault/${method}`, { args })\n if (!result.ok) {\n throw new Error(`vault.${method} failed: ${result.error.code}: ${result.error.message}`)\n }\n return result.value as T\n }\n\n const injected = (): VaultSectionInjected => ({\n t,\n config: () => invoke<VaultSectionTypes['config']>('config'),\n setAccessMode: (mode) => invoke<VaultSectionTypes['config']>('setAccessMode', { mode }),\n setAutoCapture: (enabled) => invoke<VaultSectionTypes['config']>('setAutoCapture', { enabled }),\n list: () => invoke<{ entries: VaultSectionTypes['entries'] }>('list').then(r => r.entries),\n search: (query, limit) => invoke<{ entries: VaultSectionTypes['entries'] }>('search', { query, limit: limit ?? 50 }).then(r => r.entries),\n get: (id) => invoke<{ found: boolean; entry?: VaultSectionTypes['fullEntry'] }>('get', { id }),\n add: (patch) => invoke<VaultSectionTypes['summaryEntry']>('add', { patch }),\n update: (id, patch) => invoke<{ found: boolean; entry?: VaultSectionTypes['summaryEntry'] }>('update', { id, patch }),\n remove: (id) => invoke<{ deleted: boolean }>('delete', { id }),\n trash: () => invoke<{ entries: VaultSectionTypes['entries'] }>('trash').then(r => r.entries),\n rotation: () => invoke<{ entries: unknown[] }>('rotation').then(r => r.entries),\n history: () => invoke<{ events: unknown[] }>('history').then(r => r.events),\n stats: () => invoke<Record<string, unknown>>('stats'),\n recent: () => invoke<{ entries: unknown[] }>('recent').then(r => r.entries),\n backupStatus: () => invoke<{ daysSinceBackup: number; backups: number }>('backupStatus'),\n backup: (maxBackups) => invoke<{ path: string; kept: number; pruned: number }>('backup', { maxBackups: maxBackups ?? 10 }),\n health: () => invoke<{ weak: unknown[]; reused: unknown[]; strength: { weak: number; fair: number; strong: number } }>('health'),\n duplicates: () => invoke<{ groups: number }>('duplicates'),\n restore: (id) => invoke<{ restored: boolean }>('restore', { id }),\n undeleteAll: () => invoke<{ restored: number }>('undeleteAll'),\n totp: (id) => invoke<{ code: string; label?: string; secondsRemaining: number }>('totp', { id }),\n })\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'vault',\n order: 30,\n label: () => t('nav'),\n inject: injected,\n }, VaultSection))\n}\n"],"mappings":"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"}
package/lib/index.js CHANGED
@@ -74,6 +74,7 @@ export const Config = Schema.object({
74
74
  autoCapture: Schema.boolean(),
75
75
  lockTimeoutSeconds: Schema.number(),
76
76
  exportPasswordEnv: Schema.string(),
77
+ backupRetention: Schema.number().description('How many encrypted backups to keep (default 10; vault_backup prunes older copies).'),
77
78
  });
78
79
  export async function apply(ctx, config) {
79
80
  const masterPassword = resolveMasterPassword(config);
@@ -814,12 +815,46 @@ export async function apply(ctx, config) {
814
815
  // ── vault_verify: integrity/completeness check of one entry ─────────────────
815
816
  ctx.tools.register(defineTool({
816
817
  name: 'vault_verify',
817
- description: 'Verify one entry for completeness and plausibility: required fields per kind, '
818
- + 'valid port/expiry, and that required secrets are present. No secrets in the report.',
819
- parameters: { id: { type: 'string', required: true, description: 'Entry id.' } },
820
- output: { schema: { type: 'object', additionalProperties: false, properties: { ok: { type: 'boolean', required: true }, issues: { type: 'array', required: true, items: { type: 'string' } } } }, render: (_a, v) => [{ type: 'text', text: v.ok ? 'entry looks complete' : `issues: ${v.issues.join('; ')}` }] },
818
+ description: 'Verify one entry (or every entry with all: true) for completeness and plausibility: '
819
+ + 'required fields per kind, valid port/expiry, and that required secrets are present. No secrets in the report.',
820
+ parameters: {
821
+ id: { type: 'string', description: 'Entry id to verify (omit when all is true).' },
822
+ all: { type: 'boolean', description: 'Verify every active entry and return a per-entry audit.' },
823
+ },
824
+ output: { schema: { type: 'object', additionalProperties: false, properties: { ok: { type: 'boolean', required: true }, issues: { type: 'array', required: true, items: { type: 'string' } }, audited: { type: 'integer' }, withIssues: { type: 'integer' }, perEntry: { type: 'array', items: { type: 'json' } } } }, render: (_a, v) => [{ type: 'text', text: v.audited !== undefined ? `audited ${v.audited} entries, ${v.withIssues} with issues` : (v.ok ? 'entry looks complete' : `issues: ${v.issues.join('; ')}`) }] },
821
825
  async execute(args) {
822
826
  const s = await guardStore();
827
+ if (args.all === true) {
828
+ const perEntry = [];
829
+ for (const e of s.list()) {
830
+ const issues = [];
831
+ if (e.port !== undefined && !/^\d{1,5}$/.test(String(e.port)))
832
+ issues.push('port is not numeric');
833
+ if (e.expiresAt !== undefined && e.expiresAt < Date.now())
834
+ issues.push('expired');
835
+ switch (e.kind ?? 'login') {
836
+ case 'ssh':
837
+ if (!e.host)
838
+ issues.push('ssh: missing host');
839
+ if (!e.password && !e.privateKey)
840
+ issues.push('ssh: missing password/privateKey');
841
+ break;
842
+ case 'api-key':
843
+ if (!e.apiKey && !e.secret)
844
+ issues.push('api-key: missing apiKey/secret');
845
+ break;
846
+ case 'oauth':
847
+ if (!e.accessToken)
848
+ issues.push('oauth: missing accessToken');
849
+ break;
850
+ }
851
+ perEntry.push({ id: e.id, title: e.title, ok: issues.length === 0, issues });
852
+ }
853
+ const withIssues = perEntry.filter(p => !p.ok).length;
854
+ return { ok: withIssues === 0, issues: [], audited: perEntry.length, withIssues, perEntry };
855
+ }
856
+ if (args.id === undefined)
857
+ throw new Error('vault_verify: provide id or set all: true');
823
858
  const entry = s.get(args.id);
824
859
  if (!entry)
825
860
  return { ok: false, issues: ['entry not found'] };
@@ -942,20 +977,44 @@ export async function apply(ctx, config) {
942
977
  },
943
978
  async execute() {
944
979
  const s = await guardStore();
980
+ const now = Date.now();
945
981
  const lines = [];
982
+ const byKind = {};
983
+ let withTotp = 0;
984
+ let withPrivateKey = 0;
985
+ let highSensitivity = 0;
986
+ let expired = 0;
987
+ let rotationDue = 0;
946
988
  for (const e of s.list()) {
989
+ const kind = e.kind ?? 'login';
990
+ byKind[kind] = (byKind[kind] ?? 0) + 1;
991
+ if (e.otpSecret !== undefined)
992
+ withTotp++;
993
+ if (e.privateKey !== undefined)
994
+ withPrivateKey++;
995
+ if (e.sensitivity === 'high')
996
+ highSensitivity++;
997
+ if (e.expiresAt !== undefined && e.expiresAt < now)
998
+ expired++;
999
+ if (e.rotationDays !== undefined && e.rotationDays > 0 && (e.updatedAt ?? e.createdAt) + e.rotationDays * 86_400_000 < now)
1000
+ rotationDue++;
947
1001
  const parts = [
948
1002
  e.favorite ? '★' : '·',
949
- `[${e.kind ?? 'login'}]`,
1003
+ `[${kind}]`,
950
1004
  e.title,
951
1005
  e.username ?? e.email ?? '',
952
1006
  e.host !== undefined ? `@${e.host}${e.port !== undefined ? `:${e.port}` : ''}` : '',
953
1007
  e.expiresAt !== undefined ? `exp ${new Date(e.expiresAt).toISOString().slice(0, 10)}` : '',
1008
+ e.rotationDays !== undefined && e.rotationDays > 0 ? `rot ${e.rotationDays}d` : '',
1009
+ e.otpSecret !== undefined ? 'totp' : '',
1010
+ e.sensitivity === 'high' ? 'high' : '',
954
1011
  ].filter(Boolean);
955
1012
  lines.push(parts.join(' '));
956
1013
  }
957
- const header = `dsh-vault inventory (${s.list().length} entries)\n${'-'.repeat(40)}`;
958
- return { report: lines.length > 0 ? header + '\n' + lines.join('\n') : header };
1014
+ const statsLine = `total ${s.list().length} | byKind ${JSON.stringify(byKind)} | totp ${withTotp} | privateKey ${withPrivateKey} | high ${highSensitivity} | expired ${expired} | rotationDue ${rotationDue}`;
1015
+ const header = `dsh-vault inventory\n${'-'.repeat(40)}`;
1016
+ const footer = `${'-'.repeat(40)}\n${statsLine}`;
1017
+ return { report: header + '\n' + lines.join('\n') + '\n' + footer };
959
1018
  },
960
1019
  }));
961
1020
  // ── vault_tags: tag inventory with counts ────────────────────────────────────
@@ -1078,11 +1137,11 @@ export async function apply(ctx, config) {
1078
1137
  // ── vault_duplicates: exact-title+kind duplicates ───────────────────────────
1079
1138
  ctx.tools.register(defineTool({
1080
1139
  name: 'vault_duplicates',
1081
- description: 'Find entries that are exact duplicates (same title + kind). Returns groups of '
1082
- + 'summaries (no secrets) so the caller can merge or delete them.',
1083
- parameters: {},
1140
+ description: 'Find duplicate entries: same title+kind (mode: title), same username+secret (mode: '
1141
+ + 'content), or both. Returns groups of summaries (no secrets) so the caller can merge or delete them.',
1142
+ parameters: { mode: { type: 'string', enum: ['title', 'content', 'both'], description: 'both (default): union of title and content groups; title: same title+kind only; content: same username+secret only.' } },
1084
1143
  output: { schema: { type: 'object', additionalProperties: false, properties: { groups: { type: 'array', required: true, items: { type: 'json' } } } }, render: (_a, v) => [{ type: 'text', text: `found ${v.groups.length} duplicate groups` }] },
1085
- async execute() {
1144
+ async execute(args) {
1086
1145
  const s = await guardStore();
1087
1146
  const byKey = new Map();
1088
1147
  const byContent = new Map();
@@ -1100,8 +1159,13 @@ export async function apply(ctx, config) {
1100
1159
  byContent.set(contentKey, cl);
1101
1160
  }
1102
1161
  }
1162
+ const mode = args.mode ?? 'both';
1103
1163
  const titleGroups = [...byKey.values()].filter(g => g.length > 1);
1104
1164
  const contentGroups = [...byContent.values()].filter(g => g.length > 1);
1165
+ if (mode === 'title')
1166
+ return { groups: titleGroups };
1167
+ if (mode === 'content')
1168
+ return { groups: contentGroups };
1105
1169
  return { groups: [...titleGroups, ...contentGroups] };
1106
1170
  },
1107
1171
  }));
@@ -1140,7 +1204,7 @@ export async function apply(ctx, config) {
1140
1204
  try {
1141
1205
  const entries = await readdir(dir);
1142
1206
  for (const entry of entries) {
1143
- const m = /^vault-backup-(\d+)\.json$/.exec(entry);
1207
+ const m = /^vault-backup-(\d+)(?:-[0-9a-f]{8})?\.json$/.exec(entry);
1144
1208
  if (m)
1145
1209
  backups.push(Number(m[1]));
1146
1210
  }
@@ -1840,6 +1904,20 @@ export async function apply(ctx, config) {
1840
1904
  return { restored: await s.restore(args.id) };
1841
1905
  },
1842
1906
  }));
1907
+ // ── vault_restore_recent: undo the last delete ─────────────────────────────
1908
+ ctx.tools.register(defineTool({
1909
+ name: 'vault_restore_recent',
1910
+ description: 'Undo the most recent delete: restore the trashed entry that was deleted last. '
1911
+ + 'Returns the restored summary or restored=false when the trash is empty.',
1912
+ parameters: {},
1913
+ output: { schema: { type: 'object', additionalProperties: false, properties: { restored: { type: 'boolean', required: true }, entry: { type: 'json' } } }, render: (_a, v) => [{ type: 'text', text: v.restored ? `restored: ${v.entry?.title ?? 'entry'}` : 'trash is empty' }] },
1914
+ async execute() {
1915
+ assertWritable('vault_restore_recent');
1916
+ const s = await guardStore();
1917
+ const entry = await s.restoreRecent();
1918
+ return entry === undefined ? { restored: false } : { restored: true, entry: toSummaryJson(entry) };
1919
+ },
1920
+ }));
1843
1921
  ctx.tools.register(defineTool({
1844
1922
  name: 'vault_purge',
1845
1923
  description: 'Permanently delete an entry (active or trashed). Cannot be undone — prefer vault_delete '
@@ -2037,11 +2115,11 @@ export async function apply(ctx, config) {
2037
2115
  const s = await guardStore();
2038
2116
  const source = resolveVaultPath(config);
2039
2117
  const dir = dirname(source);
2040
- const maxBackups = args.maxBackups === undefined ? 10 : args.maxBackups;
2118
+ const maxBackups = args.maxBackups === undefined ? (config.backupRetention ?? 10) : args.maxBackups;
2041
2119
  if (!Number.isInteger(maxBackups) || maxBackups < 1 || maxBackups > 100) {
2042
2120
  throw new Error('vault_backup: maxBackups must be an integer 1–100');
2043
2121
  }
2044
- const backup = join(dir, `vault-backup-${Date.now()}.json`);
2122
+ const backup = join(dir, `vault-backup-${Date.now()}-${randomUUID().slice(0, 8)}.json`);
2045
2123
  const raw = await readFile(source, 'utf8');
2046
2124
  await mkdir(dir, { recursive: true, mode: 0o700 });
2047
2125
  await writeFile(backup, raw, { mode: 0o600 });
@@ -2049,7 +2127,7 @@ export async function apply(ctx, config) {
2049
2127
  let backups = [];
2050
2128
  try {
2051
2129
  const names = await readdir(dir);
2052
- backups = names.filter(n => /^vault-backup-\d+\.json$/.test(n))
2130
+ backups = names.filter(n => /^vault-backup-\d+(?:-[0-9a-f]{8})?\.json$/.test(n))
2053
2131
  .map(n => join(dir, n))
2054
2132
  .sort((a, b) => b.localeCompare(a)); // newest first (timestamp in name)
2055
2133
  }
@@ -2269,6 +2347,7 @@ let VaultGateway = (() => {
2269
2347
  let _history_decorators;
2270
2348
  let _rotation_decorators;
2271
2349
  let _health_decorators;
2350
+ let _duplicates_decorators;
2272
2351
  let _get_decorators;
2273
2352
  let _search_decorators;
2274
2353
  let _add_decorators;
@@ -2292,6 +2371,7 @@ let VaultGateway = (() => {
2292
2371
  _history_decorators = [Remote('history')];
2293
2372
  _rotation_decorators = [Remote('rotation')];
2294
2373
  _health_decorators = [Remote('health')];
2374
+ _duplicates_decorators = [Remote('duplicates')];
2295
2375
  _get_decorators = [Remote('get')];
2296
2376
  _search_decorators = [Remote('search')];
2297
2377
  _add_decorators = [Remote('add')];
@@ -2312,6 +2392,7 @@ let VaultGateway = (() => {
2312
2392
  __esDecorate(this, null, _history_decorators, { kind: "method", name: "history", static: false, private: false, access: { has: obj => "history" in obj, get: obj => obj.history }, metadata: _metadata }, null, _instanceExtraInitializers);
2313
2393
  __esDecorate(this, null, _rotation_decorators, { kind: "method", name: "rotation", static: false, private: false, access: { has: obj => "rotation" in obj, get: obj => obj.rotation }, metadata: _metadata }, null, _instanceExtraInitializers);
2314
2394
  __esDecorate(this, null, _health_decorators, { kind: "method", name: "health", static: false, private: false, access: { has: obj => "health" in obj, get: obj => obj.health }, metadata: _metadata }, null, _instanceExtraInitializers);
2395
+ __esDecorate(this, null, _duplicates_decorators, { kind: "method", name: "duplicates", static: false, private: false, access: { has: obj => "duplicates" in obj, get: obj => obj.duplicates }, metadata: _metadata }, null, _instanceExtraInitializers);
2315
2396
  __esDecorate(this, null, _get_decorators, { kind: "method", name: "get", static: false, private: false, access: { has: obj => "get" in obj, get: obj => obj.get }, metadata: _metadata }, null, _instanceExtraInitializers);
2316
2397
  __esDecorate(this, null, _search_decorators, { kind: "method", name: "search", static: false, private: false, access: { has: obj => "search" in obj, get: obj => obj.search }, metadata: _metadata }, null, _instanceExtraInitializers);
2317
2398
  __esDecorate(this, null, _add_decorators, { kind: "method", name: "add", static: false, private: false, access: { has: obj => "add" in obj, get: obj => obj.add }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -2325,12 +2406,14 @@ let VaultGateway = (() => {
2325
2406
  vaultPath;
2326
2407
  vaultName;
2327
2408
  accessPolicy;
2409
+ backupRetention;
2328
2410
  constructor(ctx, config) {
2329
2411
  super(ctx, 'vault');
2330
2412
  this.masterPassword = config.masterPassword ?? resolveMasterPassword(config);
2331
2413
  this.vaultPath = config.path;
2332
2414
  this.vaultName = config.name;
2333
2415
  this.accessPolicy = config.accessPolicy ?? { mode: config.accessMode ?? 'ask', autoCapture: config.autoCapture ?? false };
2416
+ this.backupRetention = config.backupRetention ?? 10;
2334
2417
  }
2335
2418
  async ensureStore() {
2336
2419
  return sharedVaultStore(this.masterPassword, {
@@ -2401,20 +2484,21 @@ let VaultGateway = (() => {
2401
2484
  return { restored: await store.restore(id) };
2402
2485
  }
2403
2486
  /** Days since last backup + backup count (no secrets). */
2404
- async backup(maxBackups = 10) {
2487
+ async backup(maxBackups) {
2488
+ const max = maxBackups ?? this.backupRetention;
2405
2489
  const dir = dirname(this.vaultPath ?? defaultVaultPath(this.vaultName));
2406
2490
  const source = this.vaultPath ?? defaultVaultPath(this.vaultName);
2407
- const backup = join(dir, `vault-backup-${Date.now()}.json`);
2491
+ const backup = join(dir, `vault-backup-${Date.now()}-${randomUUID().slice(0, 8)}.json`);
2408
2492
  const raw = await readFile(source, 'utf8');
2409
2493
  await mkdir(dir, { recursive: true, mode: 0o700 });
2410
2494
  await writeFile(backup, raw, { mode: 0o600 });
2411
2495
  let total = 1;
2412
2496
  let pruned = 0;
2413
2497
  try {
2414
- const names = (await readdir(dir)).filter(n => /^vault-backup-\d+\.json$/.test(n))
2498
+ const names = (await readdir(dir)).filter(n => /^vault-backup-\d+(?:-[0-9a-f]{8})?\.json$/.test(n))
2415
2499
  .map(n => join(dir, n)).sort((a, b) => b.localeCompare(a));
2416
2500
  total = names.length;
2417
- for (const stale of names.filter(n => n !== backup).slice(maxBackups - 1)) {
2501
+ for (const stale of names.filter(n => n !== backup).slice(max - 1)) {
2418
2502
  try {
2419
2503
  await unlink(stale);
2420
2504
  pruned++;
@@ -2423,7 +2507,7 @@ let VaultGateway = (() => {
2423
2507
  }
2424
2508
  }
2425
2509
  catch { /* no dir yet */ }
2426
- return { path: backup, kept: Math.max(1, Math.min(total, maxBackups)), pruned };
2510
+ return { path: backup, kept: Math.max(1, Math.min(total, max)), pruned };
2427
2511
  }
2428
2512
  async backupStatus() {
2429
2513
  const dir = dirname(this.vaultPath ?? defaultVaultPath(this.vaultName));
@@ -2431,7 +2515,7 @@ let VaultGateway = (() => {
2431
2515
  try {
2432
2516
  const entries = await readdir(dir);
2433
2517
  for (const entry of entries) {
2434
- const m = /^vault-backup-(\d+)\.json$/.exec(entry);
2518
+ const m = /^vault-backup-(\d+)(?:-[0-9a-f]{8})?\.json$/.exec(entry);
2435
2519
  if (m)
2436
2520
  backups.push(Number(m[1]));
2437
2521
  }
@@ -2467,6 +2551,18 @@ let VaultGateway = (() => {
2467
2551
  const store = await this.ensureStore();
2468
2552
  return store.health();
2469
2553
  }
2554
+ /** Duplicate groups count for the UI health badge (title+kind matches). */
2555
+ async duplicates() {
2556
+ const store = await this.ensureStore();
2557
+ const byKey = new Map();
2558
+ for (const e of store.list()) {
2559
+ const key = `${e.title.toLowerCase()}::${e.kind ?? 'login'}`;
2560
+ const list = byKey.get(key) ?? [];
2561
+ list.push(e);
2562
+ byKey.set(key, list);
2563
+ }
2564
+ return { groups: [...byKey.values()].filter(g => g.length > 1).length };
2565
+ }
2470
2566
  /** Read one full entry (including secrets) by id. */
2471
2567
  async get(id) {
2472
2568
  const store = await this.ensureStore();
@@ -2539,6 +2635,8 @@ function toSummary(entry) {
2539
2635
  title: entry.title,
2540
2636
  ...(entry.sensitivity !== undefined ? { sensitivity: entry.sensitivity } : {}),
2541
2637
  ...(entry.favorite !== undefined ? { favorite: entry.favorite } : {}),
2638
+ ...(entry.icon !== undefined ? { icon: entry.icon } : {}),
2639
+ ...(entry.color !== undefined ? { color: entry.color } : {}),
2542
2640
  ...(entry.kind !== undefined ? { kind: entry.kind } : {}),
2543
2641
  ...(entry.username !== undefined ? { username: entry.username } : {}),
2544
2642
  ...(entry.email !== undefined ? { email: entry.email } : {}),
package/lib/store.js CHANGED
@@ -316,6 +316,18 @@ export class VaultStore {
316
316
  await this.persist();
317
317
  return true;
318
318
  }
319
+ /** Undo the most recent delete: restore the trashed entry deleted last.
320
+ * Returns the restored summary (or undefined when the trash is empty). */
321
+ async restoreRecent() {
322
+ const trashed = this.listTrash().sort((a, b) => ((b.deletedAt ?? 0) - (a.deletedAt ?? 0)) || b.id.localeCompare(a.id));
323
+ const latest = trashed[0];
324
+ if (!latest)
325
+ return undefined;
326
+ delete latest.deletedAt;
327
+ this.recordHistory('restore', latest.id, latest.title);
328
+ await this.persist();
329
+ return toSummary(latest);
330
+ }
319
331
  /** Permanently remove a trashed (or active) entry; returns true when it existed. */
320
332
  async purge(id) {
321
333
  const existed = this.entries.delete(id);
@@ -765,6 +777,8 @@ function toSummary(entry) {
765
777
  title: entry.title,
766
778
  ...(entry.sensitivity !== undefined ? { sensitivity: entry.sensitivity } : {}),
767
779
  ...(entry.favorite !== undefined ? { favorite: entry.favorite } : {}),
780
+ ...(entry.icon !== undefined ? { icon: entry.icon } : {}),
781
+ ...(entry.color !== undefined ? { color: entry.color } : {}),
768
782
  ...(entry.kind !== undefined ? { kind: entry.kind } : {}),
769
783
  ...(entry.username !== undefined ? { username: entry.username } : {}),
770
784
  ...(entry.email !== undefined ? { email: entry.email } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vault",
3
- "version": "0.8.9",
3
+ "version": "0.9.1",
4
4
  "description": "Encrypted credential vault for DeepSeek Harness: store and retrieve usernames, emails, phone numbers, passwords, TOTP secrets, SSH/API-key/OAuth developer credentials through model tools and a Settings UI page.",
5
5
  "type": "module",
6
6
  "license": "MIT",