dsh-vault 0.7.6 → 0.7.7

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/lib/client.js CHANGED
@@ -17,50 +17,50 @@ window.__ModuleLoader__.load({
17
17
  document.head.appendChild(tag);
18
18
  }
19
19
  var VaultSection_module_css_default = {
20
- "reportTitle": "t_j5va_reportTitle",
21
- "footer": "t_j5va_footer",
20
+ "addButton": "t_j5va_addButton",
21
+ "modeReadonly": "t_j5va_modeReadonly",
22
22
  "trashButton": "t_j5va_trashButton",
23
- "kindFilter": "t_j5va_kindFilter",
24
- "emptyHint": "t_j5va_emptyHint",
25
- "reportLine": "t_j5va_reportLine",
26
- "detailBox": "t_j5va_detailBox",
27
- "status": "t_j5va_status",
23
+ "field": "t_j5va_field",
28
24
  "secretField": "t_j5va_secretField",
29
- "clearButton": "t_j5va_clearButton",
25
+ "highBadge": "t_j5va_highBadge",
26
+ "identity": "t_j5va_identity",
27
+ "detailBox": "t_j5va_detailBox",
28
+ "modeAsk": "t_j5va_modeAsk",
29
+ "reportTitle": "t_j5va_reportTitle",
30
+ "empty": "t_j5va_empty",
31
+ "dangerButton": "t_j5va_dangerButton",
32
+ "reportBox": "t_j5va_reportBox",
30
33
  "policyField": "t_j5va_policyField",
31
- "copied": "t_j5va_copied",
34
+ "rowActions": "t_j5va_rowActions",
35
+ "clearButton": "t_j5va_clearButton",
36
+ "title": "t_j5va_title",
37
+ "reportLine": "t_j5va_reportLine",
38
+ "saveButton": "t_j5va_saveButton",
39
+ "section": "t_j5va_section",
40
+ "list": "t_j5va_list",
32
41
  "rowMain": "t_j5va_rowMain",
33
- "detailItem": "t_j5va_detailItem",
34
- "totp": "t_j5va_totp",
42
+ "deleteButton": "t_j5va_deleteButton",
43
+ "editor": "t_j5va_editor",
35
44
  "revealButton": "t_j5va_revealButton",
45
+ "footer": "t_j5va_footer",
46
+ "toolbar": "t_j5va_toolbar",
47
+ "searchBox": "t_j5va_searchBox",
48
+ "copied": "t_j5va_copied",
49
+ "totp": "t_j5va_totp",
36
50
  "error": "t_j5va_error",
37
- "emptyBox": "t_j5va_emptyBox",
38
- "modeAsk": "t_j5va_modeAsk",
39
51
  "editorActions": "t_j5va_editorActions",
40
- "list": "t_j5va_list",
41
- "reportBox": "t_j5va_reportBox",
42
- "deleteButton": "t_j5va_deleteButton",
43
- "rowActions": "t_j5va_rowActions",
44
- "toolbar": "t_j5va_toolbar",
45
- "empty": "t_j5va_empty",
46
- "row": "t_j5va_row",
47
- "modeReadonly": "t_j5va_modeReadonly",
52
+ "kindFilter": "t_j5va_kindFilter",
53
+ "detailItem": "t_j5va_detailItem",
48
54
  "kindIcon": "t_j5va_kindIcon",
49
55
  "pinStar": "t_j5va_pinStar",
50
- "dangerButton": "t_j5va_dangerButton",
51
- "saveButton": "t_j5va_saveButton",
52
- "field": "t_j5va_field",
53
- "highBadge": "t_j5va_highBadge",
54
- "addButton": "t_j5va_addButton",
55
56
  "modeAuto": "t_j5va_modeAuto",
56
- "identity": "t_j5va_identity",
57
- "editor": "t_j5va_editor",
57
+ "row": "t_j5va_row",
58
58
  "policyBar": "t_j5va_policyBar",
59
59
  "intro": "t_j5va_intro",
60
+ "status": "t_j5va_status",
61
+ "emptyBox": "t_j5va_emptyBox",
60
62
  "editorBody": "t_j5va_editorBody",
61
- "section": "t_j5va_section",
62
- "title": "t_j5va_title",
63
- "searchBox": "t_j5va_searchBox"
63
+ "emptyHint": "t_j5va_emptyHint"
64
64
  };
65
65
  //#endregion
66
66
  //#region src/client/VaultSection.tsx
package/lib/index.js CHANGED
@@ -390,6 +390,7 @@ export async function apply(ctx, config) {
390
390
  icon: { type: 'string', description: 'Optional emoji/icon shown in the UI (e.g. "🚀").' },
391
391
  color: { type: 'string', description: 'Optional accent color (e.g. "red", "#ff0000").' },
392
392
  favorite: { type: 'boolean', description: 'Pin/unpin the entry (favorites rank first in search).' },
393
+ resetRotation: { type: 'boolean', description: 'Reset the rotation timer now (sets updatedAt to now).' },
393
394
  otpSecret: { type: 'string', description: 'New TOTP secret.' },
394
395
  url: { type: 'string', description: 'New URL.' },
395
396
  notes: { type: 'string', description: 'New notes.' },
@@ -426,7 +427,10 @@ export async function apply(ctx, config) {
426
427
  patch[key] = key === 'fields' ? cleanFieldsValue(value) ?? {} : value;
427
428
  }
428
429
  }
429
- const updated = await s.update(args.id, patch);
430
+ const updated = args.resetRotation === true
431
+ ? await s.update(args.id, patch).then(async (u) => { if (u)
432
+ await s.markUsed(args.id); return u; })
433
+ : await s.update(args.id, patch);
430
434
  if (!updated)
431
435
  return { found: false };
432
436
  emitAudit('write', 'vault_update', updated.id, updated.title);
@@ -1149,6 +1153,46 @@ export async function apply(ctx, config) {
1149
1153
  return { id: entry.id, title: entry.title };
1150
1154
  },
1151
1155
  }));
1156
+ // ── vault_search_advanced: multi-criteria filter ────────────────────────────
1157
+ ctx.tools.register(defineTool({
1158
+ name: 'vault_search_advanced',
1159
+ description: 'Search with multiple optional criteria: title substring, username/email substring, '
1160
+ + 'kind, tag, and created-after (epoch millis). All provided criteria must match (AND). Returns secret-free summaries.',
1161
+ parameters: {
1162
+ title: { type: 'string', description: 'Title substring.' },
1163
+ username: { type: 'string', description: 'Username/email substring.' },
1164
+ kind: { type: 'string', enum: ['login', 'ssh', 'api-key', 'secret', 'oauth', 'custom'], description: 'Entry kind.' },
1165
+ tag: { type: 'string', description: 'Exact tag.' },
1166
+ createdAfter: { type: 'integer', description: 'Only entries created after this epoch millis.' },
1167
+ limit: { type: 'number', description: 'Max results (default 20).' },
1168
+ },
1169
+ output: { schema: { type: 'object', additionalProperties: false, properties: { results: { type: 'array', required: true, items: { type: 'json' } } } }, render: (_a, v) => [{ type: 'text', text: JSON.stringify(v.results) }] },
1170
+ async execute(args) {
1171
+ const s = await guardStore();
1172
+ return { results: s.advancedSearch({
1173
+ ...(args.title !== undefined ? { title: args.title } : {}),
1174
+ ...(args.username !== undefined ? { username: args.username } : {}),
1175
+ ...(args.kind !== undefined ? { kind: args.kind } : {}),
1176
+ ...(args.tag !== undefined ? { tag: args.tag } : {}),
1177
+ ...(args.createdAfter !== undefined ? { createdAfter: args.createdAfter } : {}),
1178
+ ...(args.limit !== undefined ? { limit: validateLimit(args.limit, 'vault_search_advanced') } : {}),
1179
+ }) };
1180
+ },
1181
+ }));
1182
+ // ── vault_count: lightweight entry count ────────────────────────────────────
1183
+ ctx.tools.register(defineTool({
1184
+ name: 'vault_count',
1185
+ description: 'Return the number of active entries (optionally filtered by kind). Lightweight '
1186
+ + 'alternative to vault_stats when you only need a count.',
1187
+ parameters: { kind: { type: 'string', enum: ['login', 'ssh', 'api-key', 'secret', 'oauth', 'custom'], description: 'Count only this kind.' } },
1188
+ output: { schema: { type: 'object', additionalProperties: false, properties: { count: { type: 'integer', required: true } } }, render: (_a, v) => [{ type: 'text', text: `${v.count} entries` }] },
1189
+ async execute(args) {
1190
+ const s = await guardStore();
1191
+ const kind = args.kind;
1192
+ const count = kind === undefined ? s.list().length : s.list().filter(e => (e.kind ?? 'login') === kind).length;
1193
+ return { count };
1194
+ },
1195
+ }));
1152
1196
  // ── vault_rotation: expiry / rotation report ───────────────────────────────
1153
1197
  ctx.tools.register(defineTool({
1154
1198
  name: 'vault_rotation',
package/lib/store.js CHANGED
@@ -360,6 +360,29 @@ export class VaultStore {
360
360
  }
361
361
  return report;
362
362
  }
363
+ /** Advanced multi-criteria search: title/username/kind/tag text + created-after. */
364
+ advancedSearch(criteria) {
365
+ const limit = criteria.limit ?? 20;
366
+ const t = criteria.title?.toLowerCase();
367
+ const u = criteria.username?.toLowerCase();
368
+ const results = [];
369
+ for (const entry of this.list()) {
370
+ if (results.length >= limit)
371
+ break;
372
+ if (t !== undefined && !entry.title.toLowerCase().includes(t))
373
+ continue;
374
+ if (u !== undefined && !(entry.username ?? entry.email ?? '').toLowerCase().includes(u))
375
+ continue;
376
+ if (criteria.kind !== undefined && (entry.kind ?? 'login') !== criteria.kind)
377
+ continue;
378
+ if (criteria.tag !== undefined && !(entry.tags ?? []).includes(criteria.tag))
379
+ continue;
380
+ if (criteria.createdAfter !== undefined && entry.createdAt < criteria.createdAfter)
381
+ continue;
382
+ results.push(toSummary(entry));
383
+ }
384
+ return results;
385
+ }
363
386
  /** Vault overview statistics (1Password-style): counts by kind, TOTP,
364
387
  * high-sensitivity, expired entries. No secrets. */
365
388
  stats() {
@@ -512,7 +535,10 @@ export class VaultStore {
512
535
  throw new Error('vault: export password must not be empty');
513
536
  const parsed = JSON.parse(blob);
514
537
  if (!parsed.kdf || !Array.isArray(parsed.entries)) {
515
- throw new Error('vault: invalid export document');
538
+ throw new Error('vault: invalid export document — expected { kdf, entries[] } from vault_export');
539
+ }
540
+ if (parsed.entries.length === 0) {
541
+ return 0;
516
542
  }
517
543
  const exportKey = await deriveKey(exportPassword, parsed.kdf);
518
544
  let added = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vault",
3
- "version": "0.7.6",
3
+ "version": "0.7.7",
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",