@icoretech/warden-mcp 0.1.16 → 0.1.18

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.md CHANGED
@@ -62,12 +62,14 @@ BW_BIN=/absolute/path/to/bw npx -y @icoretech/warden-mcp@latest
62
62
  `warden-mcp` intentionally bundles a vetted `@bitwarden/cli` version instead of
63
63
  blindly following the newest upstream CLI on every release. New `bw` releases
64
64
  can change login and unlock behavior in ways that break automation, so `bw`
65
- upgrades should be smoke-tested against real Vaultwarden and Bitwarden flows
66
- before bumping the bundled version.
65
+ upgrades should be smoke-tested against real Vaultwarden flows before bumping
66
+ the bundled version. Official Bitwarden compatibility is intended, but it is
67
+ not continuously proven in CI without a real Bitwarden tenant.
67
68
 
68
- This repository's compose smoke now exercises both username/password auth and
69
- user API-key auth against a real local Vaultwarden so `@bitwarden/cli` bumps do
70
- not rely on unit coverage alone.
69
+ This repository's compose smoke now exercises both direct `bw` auth flows and
70
+ the MCP/SDK layers with username/password auth plus user API-key auth against a
71
+ real local Vaultwarden, so `@bitwarden/cli` bumps do not rely on unit coverage
72
+ alone.
71
73
 
72
74
  ## Install And Run
73
75
 
@@ -456,7 +458,8 @@ Run integration tests:
456
458
  make test
457
459
  ```
458
460
 
459
- `make test` now runs both compose-backed auth paths:
461
+ `make test` now runs both compose-backed auth paths and verifies them at the
462
+ raw CLI plus MCP/SDK layers:
460
463
 
461
464
  - user/password login from `.env.test`
462
465
  - api-key login from `tmp/vaultwarden-bootstrap/apikey.env`, generated by the bootstrap step and kept out of git via `tmp/`
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import { existsSync } from 'node:fs';
5
+ import { createRequire } from 'node:module';
6
+ import { dirname, resolve } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const require = createRequire(import.meta.url);
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const rootDir = resolve(__dirname, '..');
12
+ const cliPackageDir = resolve(__dirname, '../node_modules/@bitwarden/cli');
13
+
14
+ if (!existsSync(cliPackageDir)) {
15
+ process.exit(0);
16
+ }
17
+
18
+ const patchPackageEntrypoint = require.resolve('patch-package/dist/index.js');
19
+ const result = spawnSync(process.execPath, [patchPackageEntrypoint], {
20
+ cwd: rootDir,
21
+ stdio: 'inherit',
22
+ });
23
+
24
+ if (result.error) {
25
+ console.error(
26
+ `[warden-mcp] failed to execute patch-package: ${result.error.message}`,
27
+ );
28
+ process.exit(1);
29
+ }
30
+
31
+ process.exit(result.status ?? 1);
package/bin/warden-mcp.js CHANGED
@@ -2,50 +2,21 @@
2
2
 
3
3
  // bin/warden-mcp.js — CLI entry for @icoretech/warden-mcp
4
4
 
5
- import { spawnSync } from 'node:child_process';
6
- import { accessSync, constants, existsSync, readFileSync } from 'node:fs';
7
- import { createRequire } from 'node:module';
5
+ import { existsSync } from 'node:fs';
8
6
  import { dirname, resolve } from 'node:path';
9
7
  import { fileURLToPath } from 'node:url';
10
8
 
11
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
10
 
13
- // Resolve bw binary: optional dep → system PATH
14
- if (!process.env.BW_BIN) {
15
- try {
16
- const require = createRequire(import.meta.url);
17
- const { resolveBundledBwCandidate } = await import(
18
- resolve(__dirname, '../dist/bw/resolveBwBin.js')
19
- );
20
- const pkgManifest = require.resolve('@bitwarden/cli/package.json');
21
- const pkgJson = JSON.parse(readFileSync(pkgManifest, 'utf8'));
22
- const candidate = resolveBundledBwCandidate(pkgManifest, pkgJson.bin);
23
- if (existsSync(candidate)) {
24
- try {
25
- accessSync(candidate, constants.X_OK);
26
- process.env.BW_BIN = candidate;
27
- } catch {
28
- // Not executable — fall through to system bw
29
- }
30
- }
31
- } catch {
32
- // @bitwarden/cli optional dep not installed — fall through to system bw
33
- }
34
- }
35
-
36
- // Verify bw is available (either from optional dep or system PATH)
37
- if (!process.env.BW_BIN) {
38
- const probe = spawnSync('bw', ['--version'], { encoding: 'utf8' });
39
- if (probe.error) {
40
- console.error(
41
- '[warden-mcp] ERROR: bw CLI not found.\n' +
42
- 'Install it with: npm install -g @bitwarden/cli\n' +
43
- 'Or set the BW_BIN environment variable to the path of the bw binary.',
44
- );
45
- process.exit(1);
46
- }
47
- // System bw is available — bwCli.ts will find it via PATH
11
+ const startupPath = resolve(__dirname, '../dist/startup/bwStartup.js');
12
+ if (!existsSync(startupPath)) {
13
+ console.error(
14
+ '[warden-mcp] ERROR: dist/startup/bwStartup.js not found. Run `npm run build` first.',
15
+ );
16
+ process.exit(1);
48
17
  }
18
+ const { prepareBwStartup } = await import(startupPath);
19
+ prepareBwStartup(process.env);
49
20
 
50
21
  // Delegate to the compiled server entry, forwarding all arguments.
51
22
  const serverPath = resolve(__dirname, '../dist/server.js');
package/dist/bw/bwCli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/bw/bwCli.ts
2
2
  import { spawn } from 'node:child_process';
3
+ import { resolveBundledBwBin } from './resolveBwBin.js';
3
4
  export class BwCliError extends Error {
4
5
  exitCode;
5
6
  stdout;
@@ -13,7 +14,7 @@ export class BwCliError extends Error {
13
14
  }
14
15
  }
15
16
  export async function runBw(args, opts = {}) {
16
- const bwBin = process.env.BW_BIN ?? 'bw';
17
+ const bwBin = process.env.BW_BIN ?? resolveBundledBwBin() ?? 'bw';
17
18
  // Ensure the CLI never blocks waiting for a prompt (e.g. master password).
18
19
  // This is critical for running as an MCP server / in test automation.
19
20
  const injectNoInteraction = opts.noInteraction ?? true;
@@ -80,7 +81,14 @@ export async function runBw(args, opts = {}) {
80
81
  }, timeoutMs);
81
82
  });
82
83
  const completed = new Promise((resolve, reject) => {
83
- child.on('error', reject);
84
+ child.on('error', (error) => {
85
+ const safeCmd = `${bwBin} ${safeRenderedArgs(finalArgs)}`;
86
+ if (error.code === 'ENOENT') {
87
+ reject(new Error(`bw CLI not available for ${safeCmd}. Install @bitwarden/cli or set BW_BIN to a valid bw binary.`));
88
+ return;
89
+ }
90
+ reject(new Error(`Failed to start ${safeCmd}: ${error.message}`));
91
+ });
84
92
  child.on('close', (code) => {
85
93
  if (timeout)
86
94
  clearTimeout(timeout);
@@ -3,6 +3,11 @@ import { rm } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { runBw } from './bwCli.js';
5
5
  import { Mutex } from './mutex.js';
6
+ const POST_LOGIN_UNLOCK_RETRY_ATTEMPTS = 20;
7
+ const POST_LOGIN_UNLOCK_RETRY_DELAY_MS = 2_000;
8
+ function sleep(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms));
10
+ }
6
11
  function requiredEnv(name) {
7
12
  const v = process.env[name];
8
13
  if (!v) {
@@ -145,6 +150,19 @@ export class BwSessionManager {
145
150
  env: this.baseEnv(opts.env),
146
151
  });
147
152
  }
153
+ async resetCliProfile() {
154
+ this.session = null;
155
+ this.templateItem = null;
156
+ this.configuredHost = null;
157
+ await runBw(['logout'], { env: this.baseEnv(), timeoutMs: 30_000 }).catch(() => { });
158
+ const home = this.homeDir;
159
+ await rm(join(home, '.config', 'Bitwarden CLI', 'data.json'), {
160
+ force: true,
161
+ }).catch(() => { });
162
+ await rm(join(home, '.config', 'Bitwarden CLI', 'config.json'), {
163
+ force: true,
164
+ }).catch(() => { });
165
+ }
148
166
  async ensureUnlockedInternal() {
149
167
  // Ensure server config points to BW_HOST.
150
168
  await this.ensureServerConfigured();
@@ -188,7 +206,7 @@ export class BwSessionManager {
188
206
  timeoutMs: 60_000,
189
207
  noInteraction: false,
190
208
  });
191
- return stdout.trim();
209
+ return { completed: true, session: stdout.trim() };
192
210
  }
193
211
  const { stdout } = await runBw([
194
212
  'login',
@@ -197,19 +215,47 @@ export class BwSessionManager {
197
215
  'BW_PASSWORD',
198
216
  '--raw',
199
217
  ], { env: unlockEnv, timeoutMs: 60_000, noInteraction: false });
200
- return stdout.trim();
218
+ return { completed: true, session: stdout.trim() };
201
219
  }
202
220
  catch {
203
- return '';
221
+ return { completed: false, session: '' };
204
222
  }
205
223
  };
206
- // Prefer unlocking first (works when already logged in). If it yields an empty
207
- // stdout on exit=0 (observed in some bw builds), fall back to login --raw.
208
- let session = await tryUnlock();
209
- if (!session)
210
- session = await tryLoginRaw();
211
- if (!session)
212
- session = await tryUnlock();
224
+ const retryUnlockAfterLogin = async () => {
225
+ for (let attempt = 0; attempt < POST_LOGIN_UNLOCK_RETRY_ATTEMPTS; attempt += 1) {
226
+ const session = await tryUnlock();
227
+ if (session)
228
+ return session;
229
+ if (attempt < POST_LOGIN_UNLOCK_RETRY_ATTEMPTS - 1) {
230
+ await sleep(POST_LOGIN_UNLOCK_RETRY_DELAY_MS);
231
+ }
232
+ }
233
+ return '';
234
+ };
235
+ const obtainSession = async () => {
236
+ // Prefer unlocking first (works when already logged in). If it yields an
237
+ // empty stdout on exit=0 (observed in some bw builds), fall back to
238
+ // login --raw.
239
+ let session = await tryUnlock();
240
+ if (!session) {
241
+ const login = await tryLoginRaw();
242
+ if (login.session) {
243
+ session = login.session;
244
+ }
245
+ else if (login.completed) {
246
+ session = await retryUnlockAfterLogin();
247
+ }
248
+ }
249
+ if (!session)
250
+ session = await tryUnlock();
251
+ return session;
252
+ };
253
+ let session = await obtainSession();
254
+ if (!session) {
255
+ await this.resetCliProfile();
256
+ await this.ensureServerConfigured();
257
+ session = await obtainSession();
258
+ }
213
259
  if (!session)
214
260
  throw new Error('bw login/unlock returned an empty session');
215
261
  this.session = session;
@@ -231,13 +277,7 @@ export class BwSessionManager {
231
277
  catch {
232
278
  // If the CLI data is corrupt/out-of-sync, wiping config is the fastest recovery.
233
279
  }
234
- const home = this.homeDir;
235
- await rm(join(home, '.config', 'Bitwarden CLI', 'data.json'), {
236
- force: true,
237
- }).catch(() => { });
238
- await rm(join(home, '.config', 'Bitwarden CLI', 'config.json'), {
239
- force: true,
240
- }).catch(() => { });
280
+ await this.resetCliProfile();
241
281
  await runBw(['config', 'server', this.env.host], {
242
282
  env: this.baseEnv(),
243
283
  timeoutMs: 30_000,
@@ -1,3 +1,5 @@
1
+ import { accessSync, constants, readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
1
3
  import { dirname, join } from 'node:path';
2
4
  export function resolveBundledBwCandidate(pkgManifestPath, pkgBin) {
3
5
  const pkgDir = dirname(pkgManifestPath);
@@ -8,3 +10,15 @@ export function resolveBundledBwCandidate(pkgManifestPath, pkgBin) {
8
10
  : 'dist/bw';
9
11
  return join(pkgDir, binEntry);
10
12
  }
13
+ export function resolveBundledBwBin(resolvePackage = createRequire(import.meta.url).resolve) {
14
+ try {
15
+ const pkgManifestPath = resolvePackage('@bitwarden/cli/package.json');
16
+ const pkgJson = JSON.parse(readFileSync(pkgManifestPath, 'utf8'));
17
+ const candidate = resolveBundledBwCandidate(pkgManifestPath, pkgJson.bin);
18
+ accessSync(candidate, constants.X_OK);
19
+ return candidate;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
@@ -168,8 +168,85 @@ export class KeychainSdk {
168
168
  maybeRedact(value, reveal) {
169
169
  return (reveal ? value : redactItem(value));
170
170
  }
171
- valueResult(value, revealed) {
172
- return { value, revealed };
171
+ valueResult(value, revealed, extra) {
172
+ return { value, revealed, ...(extra ?? {}) };
173
+ }
174
+ extractLoginTotp(item) {
175
+ if (!item || typeof item !== 'object')
176
+ return null;
177
+ const login = item.login;
178
+ if (!login || typeof login !== 'object')
179
+ return null;
180
+ const totp = login.totp;
181
+ return typeof totp === 'string' && totp.trim().length > 0 ? totp : null;
182
+ }
183
+ computeTotpMetadata(rawTotp, nowMs = Date.now()) {
184
+ if (!rawTotp)
185
+ return { period: null, timeLeft: null };
186
+ let period = 30;
187
+ if (rawTotp.startsWith('otpauth://')) {
188
+ try {
189
+ const parsed = new URL(rawTotp);
190
+ const candidate = Number.parseInt(parsed.searchParams.get('period') ?? '', 10);
191
+ if (Number.isFinite(candidate) && candidate > 0) {
192
+ period = candidate;
193
+ }
194
+ }
195
+ catch {
196
+ return { period: null, timeLeft: null };
197
+ }
198
+ }
199
+ const elapsed = Math.floor(nowMs / 1000) % period;
200
+ return {
201
+ period,
202
+ timeLeft: elapsed === 0 ? period : period - elapsed,
203
+ };
204
+ }
205
+ candidateMatchesTerm(item, terms) {
206
+ const id = item.id;
207
+ const name = item.name;
208
+ const login = item.login && typeof item.login === 'object'
209
+ ? item.login
210
+ : null;
211
+ const username = login?.username;
212
+ return terms.some((term) => id === term || name === term || username === term);
213
+ }
214
+ async resolveTotpConfigForSession(session, term) {
215
+ const direct = await this.bw
216
+ .runForSession(session, ['get', 'item', term], { timeoutMs: 60_000 })
217
+ .then(({ stdout }) => this.parseBwJson(stdout))
218
+ .catch(() => null);
219
+ const directTotp = this.extractLoginTotp(direct);
220
+ if (directTotp)
221
+ return directTotp;
222
+ const tokens = term
223
+ .split('|')
224
+ .map((token) => token.trim())
225
+ .filter((token) => token.length > 0);
226
+ const terms = tokens.length ? tokens : [term];
227
+ const byId = new Map();
228
+ for (const searchTerm of terms) {
229
+ const { stdout } = await this.bw.runForSession(session, ['list', 'items', '--search', searchTerm], { timeoutMs: 120_000 });
230
+ const results = this.parseBwJson(stdout);
231
+ for (const raw of results) {
232
+ if (!raw || typeof raw !== 'object')
233
+ continue;
234
+ const item = raw;
235
+ if (item.type !== ITEM_TYPE.login)
236
+ continue;
237
+ const id = item.id;
238
+ if (typeof id === 'string' && id.length > 0)
239
+ byId.set(id, item);
240
+ }
241
+ }
242
+ const candidates = [...byId.values()];
243
+ const candidate = candidates.find((item) => this.candidateMatchesTerm(item, terms)) ??
244
+ candidates[0];
245
+ if (!candidate || typeof candidate.id !== 'string')
246
+ return null;
247
+ const { stdout } = await this.bw.runForSession(session, ['get', 'item', candidate.id], { timeoutMs: 60_000 });
248
+ const item = this.parseBwJson(stdout);
249
+ return this.extractLoginTotp(item);
173
250
  }
174
251
  parseBwJson(stdout) {
175
252
  try {
@@ -848,11 +925,20 @@ export class KeychainSdk {
848
925
  });
849
926
  }
850
927
  async getTotp(input, opts = {}) {
851
- if (!opts.reveal)
852
- return this.valueResult(null, false);
928
+ if (!opts.reveal) {
929
+ return {
930
+ ...this.valueResult(null, false),
931
+ period: null,
932
+ timeLeft: null,
933
+ };
934
+ }
853
935
  return this.bw.withSession(async (session) => {
854
936
  const { stdout } = await this.bw.runForSession(session, ['--raw', 'get', 'totp', input.term], { timeoutMs: 60_000 });
855
- return this.valueResult(stdout.trim(), true);
937
+ const rawTotp = await this.resolveTotpConfigForSession(session, input.term).catch(() => null);
938
+ return {
939
+ ...this.valueResult(stdout.trim(), true),
940
+ ...this.computeTotpMetadata(rawTotp),
941
+ };
856
942
  });
857
943
  }
858
944
  /** Always reveals — URIs are not considered secrets by Bitwarden. */
@@ -0,0 +1,24 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { resolveBundledBwBin } from '../bw/resolveBwBin.js';
3
+ export function prepareBwStartup(env = process.env, deps = {}) {
4
+ if (!env.BW_BIN) {
5
+ const resolveBundled = deps.resolveBundledBwBin ?? resolveBundledBwBin;
6
+ const candidate = resolveBundled();
7
+ if (candidate) {
8
+ env.BW_BIN = candidate;
9
+ return;
10
+ }
11
+ }
12
+ if (env.BW_BIN)
13
+ return;
14
+ const probe = (deps.probeSystemBw ?? spawnSync)('bw', ['--version'], {
15
+ encoding: 'utf8',
16
+ });
17
+ if (!probe.error)
18
+ return;
19
+ const warn = deps.warn ?? console.warn;
20
+ warn('[warden-mcp] WARNING: bw CLI not found.\n' +
21
+ 'Install it with: npm install -g @bitwarden/cli\n' +
22
+ 'Or set the BW_BIN environment variable to the path of the bw binary.\n' +
23
+ 'The server will start but tool calls will fail until bw is available.');
24
+ }
@@ -35,8 +35,8 @@ export function registerTools(server, deps) {
35
35
  function effectiveReveal(input) {
36
36
  return isNoReveal ? false : (input.reveal ?? false);
37
37
  }
38
- function toolResult(kind, value, revealed) {
39
- return { result: { kind, value, revealed } };
38
+ function toolResult(kind, value, revealed, extra) {
39
+ return { result: { kind, value, revealed, ...(extra ?? {}) } };
40
40
  }
41
41
  const uriMatchSchema = z.enum([
42
42
  'domain',
@@ -926,7 +926,10 @@ export function registerTools(server, deps) {
926
926
  const sdk = await deps.getSdk(extra.authInfo);
927
927
  const totp = await sdk.getTotp({ term: input.term }, { reveal: effectiveReveal(input) });
928
928
  return {
929
- structuredContent: toolResult('totp', totp.value, totp.revealed),
929
+ structuredContent: toolResult('totp', totp.value, totp.revealed, {
930
+ period: totp.period,
931
+ timeLeft: totp.timeLeft,
932
+ }),
930
933
  content: [{ type: 'text', text: 'OK' }],
931
934
  };
932
935
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@icoretech/warden-mcp",
4
- "version": "0.1.16",
4
+ "version": "0.1.18",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "description": "Vaultwarden/Bitwarden MCP server backed by Bitwarden CLI (bw).",
@@ -29,11 +29,13 @@
29
29
  "files": [
30
30
  "dist/",
31
31
  "bin/",
32
+ "patches/",
32
33
  "!dist/**/*.test.js",
33
34
  "!dist/integration/"
34
35
  ],
35
36
  "scripts": {
36
37
  "dev": "tsx watch --clear-screen=false src/server.ts",
38
+ "postinstall": "node bin/patch-bitwarden-cli.js",
37
39
  "build": "tsc -p .",
38
40
  "start": "node dist/server.js",
39
41
  "test": "npm run build && node --test \"dist/**/*.test.js\"",
@@ -45,10 +47,11 @@
45
47
  "dependencies": {
46
48
  "@modelcontextprotocol/sdk": "^1.27.1",
47
49
  "express": "^5.2.1",
50
+ "patch-package": "^8.0.1",
48
51
  "zod": "^4.3.6"
49
52
  },
50
53
  "optionalDependencies": {
51
- "@bitwarden/cli": "2026.1.0"
54
+ "@bitwarden/cli": "2026.2.0"
52
55
  },
53
56
  "devDependencies": {
54
57
  "@biomejs/biome": "^2.4.8",
@@ -0,0 +1,80 @@
1
+ diff --git a/node_modules/@bitwarden/cli/build/bw.js b/node_modules/@bitwarden/cli/build/bw.js
2
+ index 6b02f68..2085f5e 100755
3
+ --- a/node_modules/@bitwarden/cli/build/bw.js
4
+ +++ b/node_modules/@bitwarden/cli/build/bw.js
5
+ @@ -27093,7 +27093,17 @@ class AuthRequestLoginStrategy extends LoginStrategy {
6
+ }
7
+ setAccountCryptographicState(response, userId) {
8
+ return auth_request_login_strategy_awaiter(this, void 0, void 0, function* () {
9
+ - yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
10
+ + /* icoretech-vaultwarden-compat */
11
+ + if (response.accountKeysResponseModel) {
12
+ + yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
13
+ + }
14
+ + else if (response.privateKey) {
15
+ + yield this.accountCryptographicStateService.setAccountCryptographicState({
16
+ + V1: {
17
+ + private_key: response.privateKey,
18
+ + },
19
+ + }, userId);
20
+ + }
21
+ });
22
+ }
23
+ exportCache() {
24
+ @@ -27203,7 +27213,17 @@ class PasswordLoginStrategy extends LoginStrategy {
25
+ }
26
+ setAccountCryptographicState(response, userId) {
27
+ return password_login_strategy_awaiter(this, void 0, void 0, function* () {
28
+ - yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
29
+ + /* icoretech-vaultwarden-compat */
30
+ + if (response.accountKeysResponseModel) {
31
+ + yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
32
+ + }
33
+ + else if (response.privateKey) {
34
+ + yield this.accountCryptographicStateService.setAccountCryptographicState({
35
+ + V1: {
36
+ + private_key: response.privateKey,
37
+ + },
38
+ + }, userId);
39
+ + }
40
+ });
41
+ }
42
+ encryptionKeyMigrationRequired(response) {
43
+ @@ -28327,7 +28347,17 @@ class UserApiLoginStrategy extends LoginStrategy {
44
+ }
45
+ setAccountCryptographicState(response, userId) {
46
+ return user_api_login_strategy_awaiter(this, void 0, void 0, function* () {
47
+ - yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
48
+ + /* icoretech-vaultwarden-compat */
49
+ + if (response.accountKeysResponseModel) {
50
+ + yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
51
+ + }
52
+ + else if (response.privateKey) {
53
+ + yield this.accountCryptographicStateService.setAccountCryptographicState({
54
+ + V1: {
55
+ + private_key: response.privateKey,
56
+ + },
57
+ + }, userId);
58
+ + }
59
+ });
60
+ }
61
+ // Overridden to save client ID and secret to token service
62
+ @@ -28457,7 +28487,17 @@ class WebAuthnLoginStrategy extends LoginStrategy {
63
+ }
64
+ setAccountCryptographicState(response, userId) {
65
+ return webauthn_login_strategy_awaiter(this, void 0, void 0, function* () {
66
+ - yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
67
+ + /* icoretech-vaultwarden-compat */
68
+ + if (response.accountKeysResponseModel) {
69
+ + yield this.accountCryptographicStateService.setAccountCryptographicState(response.accountKeysResponseModel.toWrappedAccountCryptographicState(), userId);
70
+ + }
71
+ + else if (response.privateKey) {
72
+ + yield this.accountCryptographicStateService.setAccountCryptographicState({
73
+ + V1: {
74
+ + private_key: response.privateKey,
75
+ + },
76
+ + }, userId);
77
+ + }
78
+ });
79
+ }
80
+ exportCache() {