dsh-vault 1.2.0-rc.2 → 1.2.0-rc.3

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.
Files changed (3) hide show
  1. package/lib/client.js +61 -61
  2. package/lib/index.js +174 -1
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -17,80 +17,80 @@ window.__ModuleLoader__.load({
17
17
  document.head.appendChild(tag);
18
18
  }
19
19
  var VaultSection_module_css_default = {
20
- "backupButton": "t_j5va_backupButton",
21
- "footer": "t_j5va_footer",
22
- "saveButton": "t_j5va_saveButton",
23
- "emptyHint": "t_j5va_emptyHint",
24
- "status": "t_j5va_status",
25
- "kindIcon": "t_j5va_kindIcon",
26
- "searchBox": "t_j5va_searchBox",
27
- "intro": "t_j5va_intro",
28
- "detailItem": "t_j5va_detailItem",
29
- "policyField": "t_j5va_policyField",
30
- "field": "t_j5va_field",
31
- "pwFair": "t_j5va_pwFair",
32
- "identity": "t_j5va_identity",
33
- "checkField": "t_j5va_checkField",
34
- "pwLabel": "t_j5va_pwLabel",
35
- "dupGroup": "t_j5va_dupGroup",
36
- "modeReadonly": "t_j5va_modeReadonly",
37
- "badge": "t_j5va_badge",
38
- "dupNames": "t_j5va_dupNames",
39
- "badgeWarn": "t_j5va_badgeWarn",
40
- "auditRow": "t_j5va_auditRow",
41
- "genOptRow": "t_j5va_genOptRow",
20
+ "sortButton": "t_j5va_sortButton",
21
+ "reportLine": "t_j5va_reportLine",
22
+ "clearButton": "t_j5va_clearButton",
42
23
  "rowDue": "t_j5va_rowDue",
43
- "deleteButton": "t_j5va_deleteButton",
24
+ "dangerButton": "t_j5va_dangerButton",
44
25
  "detailBox": "t_j5va_detailBox",
26
+ "scoreRing": "t_j5va_scoreRing",
45
27
  "emptyBox": "t_j5va_emptyBox",
28
+ "totpRing": "t_j5va_totpRing",
29
+ "pwFair": "t_j5va_pwFair",
30
+ "policyBar": "t_j5va_policyBar",
31
+ "footer": "t_j5va_footer",
32
+ "editor": "t_j5va_editor",
33
+ "healthBar": "t_j5va_healthBar",
34
+ "lockedBanner": "t_j5va_lockedBanner",
35
+ "kindIcon": "t_j5va_kindIcon",
36
+ "pwLabel": "t_j5va_pwLabel",
46
37
  "rowActions": "t_j5va_rowActions",
47
- "kindFilter": "t_j5va_kindFilter",
38
+ "badge": "t_j5va_badge",
39
+ "editorActions": "t_j5va_editorActions",
40
+ "retryButton": "t_j5va_retryButton",
41
+ "rowMain": "t_j5va_rowMain",
48
42
  "reportTitle": "t_j5va_reportTitle",
49
- "empty": "t_j5va_empty",
50
- "pwWeak": "t_j5va_pwWeak",
43
+ "secretField": "t_j5va_secretField",
51
44
  "copied": "t_j5va_copied",
52
- "scoreRing": "t_j5va_scoreRing",
53
- "dangerButton": "t_j5va_dangerButton",
54
- "pwBar": "t_j5va_pwBar",
55
- "list": "t_j5va_list",
56
- "error": "t_j5va_error",
57
- "sortButton": "t_j5va_sortButton",
58
- "editorBody": "t_j5va_editorBody",
59
- "trashButton": "t_j5va_trashButton",
60
- "retryButton": "t_j5va_retryButton",
61
- "title": "t_j5va_title",
62
45
  "badgeOk": "t_j5va_badgeOk",
63
- "dupMerge": "t_j5va_dupMerge",
64
- "reportLine": "t_j5va_reportLine",
65
- "totp": "t_j5va_totp",
66
- "reportSub": "t_j5va_reportSub",
67
- "secretField": "t_j5va_secretField",
68
- "genOpts": "t_j5va_genOpts",
69
- "dueBadge": "t_j5va_dueBadge",
70
- "rowMain": "t_j5va_rowMain",
71
- "revealButton": "t_j5va_revealButton",
72
- "pinStar": "t_j5va_pinStar",
73
- "rowExpired": "t_j5va_rowExpired",
74
- "pwMeter": "t_j5va_pwMeter",
75
- "lockedBanner": "t_j5va_lockedBanner",
76
46
  "modeAuto": "t_j5va_modeAuto",
77
- "clearButton": "t_j5va_clearButton",
47
+ "addButton": "t_j5va_addButton",
78
48
  "badgeDanger": "t_j5va_badgeDanger",
79
- "modeAsk": "t_j5va_modeAsk",
80
- "editorActions": "t_j5va_editorActions",
81
- "policyBar": "t_j5va_policyBar",
49
+ "status": "t_j5va_status",
50
+ "reportBox": "t_j5va_reportBox",
51
+ "pinStar": "t_j5va_pinStar",
52
+ "dupMerge": "t_j5va_dupMerge",
82
53
  "pwStrong": "t_j5va_pwStrong",
83
- "healthBar": "t_j5va_healthBar",
84
- "totpRing": "t_j5va_totpRing",
85
- "section": "t_j5va_section",
54
+ "auditRow": "t_j5va_auditRow",
55
+ "auditIssues": "t_j5va_auditIssues",
56
+ "dupNames": "t_j5va_dupNames",
57
+ "modeAsk": "t_j5va_modeAsk",
58
+ "saveButton": "t_j5va_saveButton",
59
+ "detailItem": "t_j5va_detailItem",
60
+ "pwWeak": "t_j5va_pwWeak",
61
+ "trashButton": "t_j5va_trashButton",
62
+ "identity": "t_j5va_identity",
86
63
  "toolbar": "t_j5va_toolbar",
87
- "srOnly": "t_j5va_srOnly",
64
+ "kindFilter": "t_j5va_kindFilter",
65
+ "badgeWarn": "t_j5va_badgeWarn",
66
+ "searchBox": "t_j5va_searchBox",
67
+ "genOpts": "t_j5va_genOpts",
68
+ "pwBar": "t_j5va_pwBar",
69
+ "intro": "t_j5va_intro",
70
+ "totp": "t_j5va_totp",
71
+ "dupGroup": "t_j5va_dupGroup",
72
+ "reportSub": "t_j5va_reportSub",
73
+ "pwMeter": "t_j5va_pwMeter",
88
74
  "highBadge": "t_j5va_highBadge",
75
+ "section": "t_j5va_section",
76
+ "checkField": "t_j5va_checkField",
77
+ "error": "t_j5va_error",
78
+ "field": "t_j5va_field",
79
+ "empty": "t_j5va_empty",
80
+ "title": "t_j5va_title",
81
+ "rowExpired": "t_j5va_rowExpired",
82
+ "modeReadonly": "t_j5va_modeReadonly",
83
+ "emptyHint": "t_j5va_emptyHint",
89
84
  "row": "t_j5va_row",
90
- "reportBox": "t_j5va_reportBox",
91
- "editor": "t_j5va_editor",
92
- "auditIssues": "t_j5va_auditIssues",
93
- "addButton": "t_j5va_addButton"
85
+ "dueBadge": "t_j5va_dueBadge",
86
+ "deleteButton": "t_j5va_deleteButton",
87
+ "revealButton": "t_j5va_revealButton",
88
+ "backupButton": "t_j5va_backupButton",
89
+ "genOptRow": "t_j5va_genOptRow",
90
+ "srOnly": "t_j5va_srOnly",
91
+ "list": "t_j5va_list",
92
+ "policyField": "t_j5va_policyField",
93
+ "editorBody": "t_j5va_editorBody"
94
94
  };
95
95
  //#endregion
96
96
  //#region src/client/VaultSection.tsx
package/lib/index.js CHANGED
@@ -1574,7 +1574,36 @@ export async function apply(ctx, config) {
1574
1574
  xml += `<username>${x(e.username ?? e.email)}</username>\n`;
1575
1575
  xml += `<password>${x(e.password)}</password>\n`;
1576
1576
  xml += `<url>${x(e.url)}</url>\n`;
1577
- xml += `<notes>${x(e.notes)}</notes>\n</entry>\n`;
1577
+ xml += `<notes>${x(e.notes)}</notes>\n`;
1578
+ if (e.otpSecret !== undefined) {
1579
+ const uri = e.otpSecret.startsWith('otpauth://') ? e.otpSecret : `otpauth://totp/${encodeURIComponent(e.title)}?secret=${e.otpSecret}`;
1580
+ xml += `<otp><name>dsh-vault</name><otpauth>${x(uri)}</otpauth></otp>\n`;
1581
+ }
1582
+ const strings = [];
1583
+ if (e.host !== undefined)
1584
+ strings.push(['host', e.host]);
1585
+ if (e.port !== undefined)
1586
+ strings.push(['port', e.port]);
1587
+ if (e.apiKey !== undefined)
1588
+ strings.push(['apiKey', e.apiKey]);
1589
+ if (e.secret !== undefined)
1590
+ strings.push(['secret', e.secret]);
1591
+ if (e.accessToken !== undefined)
1592
+ strings.push(['accessToken', e.accessToken]);
1593
+ if (e.refreshToken !== undefined)
1594
+ strings.push(['refreshToken', e.refreshToken]);
1595
+ if (e.privateKey !== undefined)
1596
+ strings.push(['privateKey', e.privateKey]);
1597
+ if (e.expiresAt !== undefined)
1598
+ strings.push(['expiresAt', e.expiresAt]);
1599
+ if (e.rotationDays !== undefined)
1600
+ strings.push(['rotationDays', e.rotationDays]);
1601
+ for (const [k, v] of Object.entries(e.fields ?? {}))
1602
+ strings.push([k, v]);
1603
+ for (const [k, v] of strings) {
1604
+ xml += `<String><Key>${x(k)}</Key><Value>${x(v)}</Value></String>\n`;
1605
+ }
1606
+ xml += `</entry>\n`;
1578
1607
  }
1579
1608
  xml += `</group>\n`;
1580
1609
  }
@@ -1638,6 +1667,8 @@ export async function apply(ctx, config) {
1638
1667
  url: header.indexOf('url'),
1639
1668
  username: header.indexOf('username'),
1640
1669
  password: header.indexOf('password'),
1670
+ otpauth: header.indexOf('otpauth'),
1671
+ notes: header.indexOf('notes'),
1641
1672
  };
1642
1673
  if (idx.name < 0 || idx.password < 0)
1643
1674
  return { added: 0, skipped: rows.length - 1, updated: 0 };
@@ -1660,6 +1691,8 @@ export async function apply(ctx, config) {
1660
1691
  ...(idx.url >= 0 && idx.url < row.length && row[idx.url] ? { url: row[idx.url] } : {}),
1661
1692
  ...(idx.username >= 0 && idx.username < row.length && row[idx.username] ? { username: row[idx.username] } : {}),
1662
1693
  ...(idx.password >= 0 && idx.password < row.length && row[idx.password] ? { password: row[idx.password] } : {}),
1694
+ ...(idx.otpauth >= 0 && idx.otpauth < row.length && row[idx.otpauth] ? { otpSecret: row[idx.otpauth] } : {}),
1695
+ ...(idx.notes >= 0 && idx.notes < row.length && row[idx.notes] ? { notes: row[idx.notes] } : {}),
1663
1696
  };
1664
1697
  const existing = s.list().find(e => e.title === name);
1665
1698
  if (existing && !args.overwrite) {
@@ -2338,6 +2371,146 @@ export async function apply(ctx, config) {
2338
2371
  return { path: file, count: entries.length };
2339
2372
  },
2340
2373
  }));
2374
+ // ── vault_import_bitwarden: import a Bitwarden JSON export ─────────────────
2375
+ ctx.tools.register(defineTool({
2376
+ name: 'vault_import_bitwarden',
2377
+ description: 'Import a Bitwarden (or Vaultwarden) JSON export: maps each item into a vault entry '
2378
+ + '(title, username/password, totp, notes, url, favorite, custom fields). Same-title entries are '
2379
+ + 'skipped unless overwrite is set. Returns added/skipped/updated counts.',
2380
+ parameters: {
2381
+ path: { type: 'string', required: true, description: 'Absolute path of the Bitwarden .json export.' },
2382
+ overwrite: { type: 'boolean', description: 'Update existing entries with the same title (default false).' },
2383
+ },
2384
+ output: { schema: { type: 'object', additionalProperties: false, properties: { added: { type: 'integer', required: true }, skipped: { type: 'integer', required: true }, updated: { type: 'integer', required: true } } }, render: (_a, v) => [{ type: 'text', text: `imported ${v.added}, updated ${v.updated}, skipped ${v.skipped}` }] },
2385
+ async execute(args) {
2386
+ assertWritable('vault_import_bitwarden');
2387
+ const s = await guardStore();
2388
+ const raw = JSON.parse(await readFile(args.path, 'utf8'));
2389
+ const items = Array.isArray(raw.items) ? raw.items : [];
2390
+ let added = 0;
2391
+ let skipped = 0;
2392
+ let updated = 0;
2393
+ for (const item of items) {
2394
+ const title = (item.name ?? '').trim();
2395
+ if (!title) {
2396
+ skipped++;
2397
+ continue;
2398
+ }
2399
+ const patch = {};
2400
+ const login = item.login;
2401
+ if (login?.username !== undefined)
2402
+ patch.username = login.username;
2403
+ if (login?.password !== undefined)
2404
+ patch.password = login.password;
2405
+ if (login?.totp !== undefined)
2406
+ patch.otpSecret = login.totp;
2407
+ const uri = login?.uris?.find(u => u.uri)?.uri;
2408
+ if (uri !== undefined)
2409
+ patch.url = uri;
2410
+ if (item.notes !== undefined && item.notes !== null)
2411
+ patch.notes = item.notes;
2412
+ if (item.favorite === true)
2413
+ patch.favorite = true;
2414
+ const fields = {};
2415
+ for (const f of item.fields ?? []) {
2416
+ if (f.name !== undefined && f.value !== undefined)
2417
+ fields[f.name] = f.value;
2418
+ }
2419
+ // Promote known custom fields back onto the entry model.
2420
+ for (const key of ['host', 'port', 'apiKey', 'secret', 'accessToken', 'refreshToken', 'privateKey', 'expiresAt', 'rotationDays']) {
2421
+ if (fields[key] !== undefined) {
2422
+ ;
2423
+ patch[key] = key === 'port' || key === 'expiresAt' || key === 'rotationDays' ? Number(fields[key]) : fields[key];
2424
+ delete fields[key];
2425
+ }
2426
+ }
2427
+ if (Object.keys(fields).length > 0)
2428
+ patch.fields = fields;
2429
+ const existing = s.list().find(e => e.title === title);
2430
+ if (existing && !args.overwrite) {
2431
+ skipped++;
2432
+ continue;
2433
+ }
2434
+ if (existing && args.overwrite) {
2435
+ await s.update(existing.id, patch);
2436
+ updated++;
2437
+ continue;
2438
+ }
2439
+ await s.add({ title, ...patch });
2440
+ added++;
2441
+ }
2442
+ return { added, skipped, updated };
2443
+ },
2444
+ }));
2445
+ // ── vault_export_bitwarden: Bitwarden-compatible JSON export ───────────────
2446
+ ctx.tools.register(defineTool({
2447
+ name: 'vault_export_bitwarden',
2448
+ description: 'Export entries in the Bitwarden JSON format (encrypted:false) so they can be '
2449
+ + 'imported into Bitwarden, Vaultwarden, or other tools that accept that format. Contains '
2450
+ + 'plaintext secrets — write it to a protected file. Returns the output path and count.',
2451
+ parameters: { path: { type: 'string', required: true, description: 'Absolute output .json path.' } },
2452
+ output: { schema: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', required: true }, count: { type: 'integer', required: true } } }, render: (_a, v) => [{ type: 'text', text: `exported ${v.count} entries to ${v.path}` }] },
2453
+ async execute(args) {
2454
+ assertWritable('vault_export_bitwarden');
2455
+ const s = await guardStore();
2456
+ const items = [];
2457
+ for (const e of s.list()) {
2458
+ const uris = e.url !== undefined ? [{ match: null, uri: e.url }] : null;
2459
+ const login = {};
2460
+ if (e.username !== undefined)
2461
+ login.username = e.username;
2462
+ if (e.email !== undefined && login.username === undefined)
2463
+ login.username = e.email;
2464
+ if (e.password !== undefined)
2465
+ login.password = e.password;
2466
+ if (e.otpSecret !== undefined)
2467
+ login.totp = e.otpSecret;
2468
+ if (uris !== null)
2469
+ login.uris = uris;
2470
+ const fields = [];
2471
+ for (const [k, v] of Object.entries(e.fields ?? {})) {
2472
+ if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
2473
+ fields.push({ name: k, value: String(v), type: 0 });
2474
+ }
2475
+ }
2476
+ if (e.host !== undefined)
2477
+ fields.push({ name: 'host', value: e.host, type: 0 });
2478
+ if (e.port !== undefined)
2479
+ fields.push({ name: 'port', value: String(e.port), type: 0 });
2480
+ if (e.apiKey !== undefined)
2481
+ fields.push({ name: 'apiKey', value: e.apiKey, type: 0 });
2482
+ if (e.secret !== undefined)
2483
+ fields.push({ name: 'secret', value: e.secret, type: 0 });
2484
+ if (e.accessToken !== undefined)
2485
+ fields.push({ name: 'accessToken', value: e.accessToken, type: 0 });
2486
+ if (e.refreshToken !== undefined)
2487
+ fields.push({ name: 'refreshToken', value: e.refreshToken, type: 0 });
2488
+ if (e.privateKey !== undefined)
2489
+ fields.push({ name: 'privateKey', value: e.privateKey, type: 0 });
2490
+ if (e.rotationDays !== undefined)
2491
+ fields.push({ name: 'rotationDays', value: String(e.rotationDays), type: 0 });
2492
+ if (e.expiresAt !== undefined)
2493
+ fields.push({ name: 'expiresAt', value: String(e.expiresAt), type: 0 });
2494
+ items.push({
2495
+ id: e.id,
2496
+ organizationId: null,
2497
+ folderId: null,
2498
+ type: 1,
2499
+ reprompt: 0,
2500
+ name: e.title,
2501
+ notes: e.notes ?? null,
2502
+ favorite: e.favorite === true,
2503
+ login: Object.keys(login).length > 0 ? login : null,
2504
+ fields: fields.length > 0 ? fields : null,
2505
+ collectionIds: null,
2506
+ });
2507
+ }
2508
+ const doc = { encrypted: false, folders: [], items };
2509
+ await mkdir(dirname(args.path), { recursive: true, mode: 0o700 });
2510
+ await writeFile(args.path, JSON.stringify(doc, null, 2), { mode: 0o600 });
2511
+ return { path: args.path, count: items.length };
2512
+ },
2513
+ }));
2341
2514
  // ── vault_backup: one-shot backup with a timestamped filename ───────────────
2342
2515
  ctx.tools.register(defineTool({
2343
2516
  name: 'vault_backup',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vault",
3
- "version": "1.2.0-rc.2",
3
+ "version": "1.2.0-rc.3",
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",