latchkey 2.5.1 → 2.5.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.
@@ -2,8 +2,12 @@
2
2
  * System keychain integration for secure password storage.
3
3
  * Uses @napi-rs/keyring for cross-platform support (macOS Keychain, Windows Credential Manager,
4
4
  * Linux Secret Service via keyutils/kernel keyring).
5
+ *
6
+ * All operations have a timeout to prevent hanging when the keyring is locked.
5
7
  */
6
- import { Entry } from '@napi-rs/keyring';
8
+ import { AsyncEntry } from '@napi-rs/keyring';
9
+ import { platform } from 'node:os';
10
+ const KEYRING_TIMEOUT_MILLISECONDS = 30_000;
7
11
  export class KeychainError extends Error {
8
12
  constructor(message) {
9
13
  super(message);
@@ -16,37 +20,64 @@ export class KeychainNotAvailableError extends KeychainError {
16
20
  this.name = 'KeychainNotAvailableError';
17
21
  }
18
22
  }
23
+ export class KeychainTimeoutError extends KeychainError {
24
+ constructor() {
25
+ const linuxHint = platform() === 'linux'
26
+ ? '\n\nOn Linux, you can try:\n' +
27
+ ' - Unlocking your keyring: run a GUI app that accesses it, or use `gnome-keyring-daemon --unlock`\n' +
28
+ ' - Bypassing the keyring entirely by setting LATCHKEY_ENCRYPTION_KEY:\n' +
29
+ ' export LATCHKEY_ENCRYPTION_KEY="$(openssl rand -base64 32)"\n' +
30
+ ' Add this to your shell profile to persist it across sessions.\n' +
31
+ ' You may need to first delete any existing .enc files in your LATCHKEY_DIRECTORY\n' +
32
+ ' (~/.latchkey by default) to reset any previously stored credentials.'
33
+ : '';
34
+ const timeoutSeconds = String(KEYRING_TIMEOUT_MILLISECONDS / 1000);
35
+ super(`Could not access the system keyring within ${timeoutSeconds} seconds — it may be locked or unavailable.${linuxHint}`);
36
+ this.name = 'KeychainTimeoutError';
37
+ }
38
+ }
19
39
  /**
20
- * Get a keyring entry.
40
+ * Get an async keyring entry.
21
41
  */
22
42
  function getEntry(serviceName, accountName) {
23
- return new Entry(serviceName, accountName);
43
+ return new AsyncEntry(serviceName, accountName);
44
+ }
45
+ function isTimeoutError(error) {
46
+ return error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError');
24
47
  }
25
48
  /**
26
49
  * Store a password in the system keychain.
50
+ * Throws KeychainTimeoutError if the keychain does not respond in time.
27
51
  * Throws KeychainNotAvailableError if the keychain is not accessible.
28
52
  */
29
- export function storeInKeychain(serviceName, accountName, password) {
53
+ export async function storeInKeychain(serviceName, accountName, password) {
30
54
  try {
31
55
  const entry = getEntry(serviceName, accountName);
32
- entry.setPassword(password);
56
+ await entry.setPassword(password, AbortSignal.timeout(KEYRING_TIMEOUT_MILLISECONDS));
33
57
  }
34
58
  catch (error) {
59
+ if (isTimeoutError(error)) {
60
+ throw new KeychainTimeoutError();
61
+ }
35
62
  throw new KeychainNotAvailableError(`Failed to store password in keychain: ${error instanceof Error ? error.message : String(error)}`);
36
63
  }
37
64
  }
38
65
  /**
39
66
  * Retrieve a password from the system keychain.
40
67
  * Returns null if the password is not found.
68
+ * Throws KeychainTimeoutError if the keychain does not respond in time.
41
69
  * Throws KeychainNotAvailableError if the keychain is not accessible.
42
70
  */
43
- export function retrieveFromKeychain(serviceName, accountName) {
71
+ export async function retrieveFromKeychain(serviceName, accountName) {
44
72
  try {
45
73
  const entry = getEntry(serviceName, accountName);
46
- const password = entry.getPassword();
74
+ const password = await entry.getPassword(AbortSignal.timeout(KEYRING_TIMEOUT_MILLISECONDS));
47
75
  return password ?? null;
48
76
  }
49
77
  catch (error) {
78
+ if (isTimeoutError(error)) {
79
+ throw new KeychainTimeoutError();
80
+ }
50
81
  const errorMessage = error instanceof Error ? error.message : String(error);
51
82
  // Check if it's a "not found" error
52
83
  if (errorMessage.includes('not found') ||
@@ -60,15 +91,19 @@ export function retrieveFromKeychain(serviceName, accountName) {
60
91
  /**
61
92
  * Delete a password from the system keychain.
62
93
  * Returns true if deleted, false if not found.
94
+ * Throws KeychainTimeoutError if the keychain does not respond in time.
63
95
  * Throws KeychainNotAvailableError if the keychain is not accessible.
64
96
  */
65
- export function deleteFromKeychain(serviceName, accountName) {
97
+ export async function deleteFromKeychain(serviceName, accountName) {
66
98
  try {
67
99
  const entry = getEntry(serviceName, accountName);
68
- entry.deletePassword();
100
+ await entry.deletePassword(AbortSignal.timeout(KEYRING_TIMEOUT_MILLISECONDS));
69
101
  return true;
70
102
  }
71
103
  catch (error) {
104
+ if (isTimeoutError(error)) {
105
+ throw new KeychainTimeoutError();
106
+ }
72
107
  const errorMessage = error instanceof Error ? error.message : String(error);
73
108
  if (errorMessage.includes('not found') ||
74
109
  errorMessage.includes('No password') ||
@@ -1 +1 @@
1
- {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/keychain.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,aAAa;IAC1D,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,WAAmB,EAAE,WAAmB;IACxD,OAAO,IAAI,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,WAAmB,EAAE,QAAgB;IACxF,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,yBAAyB,CACjC,yCAAyC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAClG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB,EAAE,WAAmB;IAC3E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QACrC,OAAO,QAAQ,IAAI,IAAI,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,oCAAoC;QACpC,IACE,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC;YAClC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;YACpC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EACrC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,8CAA8C,YAAY,EAAE,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,WAAmB;IACzE,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,IACE,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC;YAClC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;YACpC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EACrC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,4CAA4C,YAAY,EAAE,CAAC,CAAC;IAClG,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAE5C,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,aAAa;IAC1D,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IACrD;QACE,MAAM,SAAS,GACb,QAAQ,EAAE,KAAK,OAAO;YACpB,CAAC,CAAC,8BAA8B;gBAC9B,sGAAsG;gBACtG,0EAA0E;gBAC1E,qEAAqE;gBACrE,qEAAqE;gBACrE,uFAAuF;gBACvF,0EAA0E;YAC5E,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,cAAc,GAAG,MAAM,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAAC;QACnE,KAAK,CACH,8CAA8C,cAAc,8CAA8C,SAAS,EAAE,CACtH,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,WAAmB,EAAE,WAAmB;IACxD,OAAO,IAAI,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AAClG,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAAmB,EACnB,WAAmB,EACnB,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,oBAAoB,EAAE,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,yCAAyC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAClG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,WAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC;QAC5F,OAAO,QAAQ,IAAI,IAAI,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,oBAAoB,EAAE,CAAC;QACnC,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,oCAAoC;QACpC,IACE,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC;YAClC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;YACpC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EACrC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,8CAA8C,YAAY,EAAE,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,WAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,oBAAoB,EAAE,CAAC;QACnC,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,IACE,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC;YAClC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;YACpC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EACrC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,4CAA4C,YAAY,EAAE,CAAC,CAAC;IAClG,CAAC;AACH,CAAC"}
@@ -11,10 +11,10 @@ describe('ApiCredentialStore', () => {
11
11
  let tempDir;
12
12
  let storePath;
13
13
  let encryptedStorage;
14
- beforeEach(() => {
14
+ beforeEach(async () => {
15
15
  tempDir = mkdtempSync(join(tmpdir(), 'latchkey-test-'));
16
16
  storePath = join(tempDir, 'credentials.json');
17
- encryptedStorage = new EncryptedStorage({ encryptionKeyOverride: generateKey() });
17
+ encryptedStorage = await EncryptedStorage.create({ encryptionKeyOverride: generateKey() });
18
18
  });
19
19
  afterEach(() => {
20
20
  rmSync(tempDir, { recursive: true, force: true });
@@ -1 +1 @@
1
- {"version":3,"file":"apiCredentialStore.test.js","sourceRoot":"","sources":["../../tests/apiCredentialStore.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apiCredentialStore.test.js","sourceRoot":"","sources":["../../tests/apiCredentialStore.test.ts"],"names":[],"mappings":"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"}
@@ -21,12 +21,12 @@ import { deleteRegisteredService, loadRegisteredServices, saveRegisteredService,
21
21
  import { loadRegisteredServicesIntoRegistry } from '../src/registry.js';
22
22
  // Use a fixed test key for deterministic test behavior (32 bytes = 256 bits, base64 encoded)
23
23
  const TEST_ENCRYPTION_KEY = 'dGVzdGtleXRlc3RrZXl0ZXN0a2V5dGVzdGtleXRlc3Q=';
24
- function writeSecureFile(path, content) {
25
- const storage = new EncryptedStorage({ encryptionKeyOverride: TEST_ENCRYPTION_KEY });
24
+ async function writeSecureFile(path, content) {
25
+ const storage = await EncryptedStorage.create({ encryptionKeyOverride: TEST_ENCRYPTION_KEY });
26
26
  storage.writeFile(path, content);
27
27
  }
28
- function readSecureFile(path) {
29
- const storage = new EncryptedStorage({ encryptionKeyOverride: TEST_ENCRYPTION_KEY });
28
+ async function readSecureFile(path) {
29
+ const storage = await EncryptedStorage.create({ encryptionKeyOverride: TEST_ENCRYPTION_KEY });
30
30
  return storage.readFile(path);
31
31
  }
32
32
  function getCliPath() {
@@ -352,7 +352,7 @@ describe('CLI commands with dependency injection', () => {
352
352
  });
353
353
  it('should include services with stored credentials when using --viable', async () => {
354
354
  const storePath = join(tempDir, 'credentials.json');
355
- writeSecureFile(storePath, JSON.stringify({
355
+ await writeSecureFile(storePath, JSON.stringify({
356
356
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
357
357
  }));
358
358
  const deps = createMockDependencies();
@@ -363,7 +363,7 @@ describe('CLI commands with dependency injection', () => {
363
363
  });
364
364
  it('should include services with browser auth when using --viable', async () => {
365
365
  const storePath = join(tempDir, 'credentials.json');
366
- writeSecureFile(storePath, '{}');
366
+ await writeSecureFile(storePath, '{}');
367
367
  // The default mock slack service has getSession defined, so it supports browser auth
368
368
  // Ensure a graphical environment is available so browser auth is considered viable
369
369
  const originalDisplay = process.env.DISPLAY;
@@ -386,7 +386,7 @@ describe('CLI commands with dependency injection', () => {
386
386
  });
387
387
  it('should exclude services without credentials or browser auth when using --viable', async () => {
388
388
  const storePath = join(tempDir, 'credentials.json');
389
- writeSecureFile(storePath, '{}');
389
+ await writeSecureFile(storePath, '{}');
390
390
  const noLoginService = {
391
391
  name: 'nologin',
392
392
  displayName: 'No Login Service',
@@ -412,7 +412,7 @@ describe('CLI commands with dependency injection', () => {
412
412
  });
413
413
  it('should exclude browser-capable services when browser is disabled and no credentials with --viable', async () => {
414
414
  const storePath = join(tempDir, 'credentials.json');
415
- writeSecureFile(storePath, '{}');
415
+ await writeSecureFile(storePath, '{}');
416
416
  const deps = createMockDependencies({
417
417
  config: createMockConfig({ browserDisabled: true }),
418
418
  });
@@ -423,7 +423,7 @@ describe('CLI commands with dependency injection', () => {
423
423
  });
424
424
  it('should exclude browser-capable services when no graphical environment and no credentials with --viable', async () => {
425
425
  const storePath = join(tempDir, 'credentials.json');
426
- writeSecureFile(storePath, '{}');
426
+ await writeSecureFile(storePath, '{}');
427
427
  const originalPlatform = process.platform;
428
428
  const originalDisplay = process.env.DISPLAY;
429
429
  const originalWayland = process.env.WAYLAND_DISPLAY;
@@ -455,7 +455,7 @@ describe('CLI commands with dependency injection', () => {
455
455
  });
456
456
  it('should include services with credentials even when no graphical environment with --viable', async () => {
457
457
  const storePath = join(tempDir, 'credentials.json');
458
- writeSecureFile(storePath, JSON.stringify({
458
+ await writeSecureFile(storePath, JSON.stringify({
459
459
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
460
460
  }));
461
461
  const originalPlatform = process.platform;
@@ -489,7 +489,7 @@ describe('CLI commands with dependency injection', () => {
489
489
  });
490
490
  it('should include services with credentials even when browser is disabled with --viable', async () => {
491
491
  const storePath = join(tempDir, 'credentials.json');
492
- writeSecureFile(storePath, JSON.stringify({
492
+ await writeSecureFile(storePath, JSON.stringify({
493
493
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
494
494
  }));
495
495
  const deps = createMockDependencies({
@@ -502,7 +502,7 @@ describe('CLI commands with dependency injection', () => {
502
502
  });
503
503
  it('should combine --builtin and --viable filters', async () => {
504
504
  const storePath = join(tempDir, 'credentials.json');
505
- writeSecureFile(storePath, JSON.stringify({
505
+ await writeSecureFile(storePath, JSON.stringify({
506
506
  'my-gitlab': {
507
507
  objectType: 'rawCurl',
508
508
  curlArguments: ['-H', 'PRIVATE-TOKEN: token'],
@@ -536,7 +536,7 @@ describe('CLI commands with dependency injection', () => {
536
536
  describe('services info command', () => {
537
537
  it('should show login options, credentials status, and developer notes', async () => {
538
538
  const storePath = join(tempDir, 'credentials.json');
539
- writeSecureFile(storePath, '{}');
539
+ await writeSecureFile(storePath, '{}');
540
540
  const deps = createMockDependencies();
541
541
  await runCommand(['services', 'info', 'slack'], deps);
542
542
  expect(logs).toHaveLength(1);
@@ -550,7 +550,7 @@ describe('CLI commands with dependency injection', () => {
550
550
  });
551
551
  it('should show auth set only for services without browser login', async () => {
552
552
  const storePath = join(tempDir, 'credentials.json');
553
- writeSecureFile(storePath, '{}');
553
+ await writeSecureFile(storePath, '{}');
554
554
  const noLoginService = {
555
555
  name: 'nologin',
556
556
  displayName: 'No Login Service',
@@ -575,7 +575,7 @@ describe('CLI commands with dependency injection', () => {
575
575
  });
576
576
  it('should not list browser in authOptions when LATCHKEY_DISABLE_BROWSER is in effect', async () => {
577
577
  const storePath = join(tempDir, 'credentials.json');
578
- writeSecureFile(storePath, '{}');
578
+ await writeSecureFile(storePath, '{}');
579
579
  const deps = createMockDependencies({
580
580
  config: createMockConfig({ browserDisabled: true }),
581
581
  });
@@ -585,7 +585,7 @@ describe('CLI commands with dependency injection', () => {
585
585
  });
586
586
  it('should show valid credentials status when credentials are valid', async () => {
587
587
  const storePath = join(tempDir, 'credentials.json');
588
- writeSecureFile(storePath, JSON.stringify({
588
+ await writeSecureFile(storePath, JSON.stringify({
589
589
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
590
590
  }));
591
591
  const deps = createMockDependencies();
@@ -601,7 +601,7 @@ describe('CLI commands with dependency injection', () => {
601
601
  });
602
602
  it('should show type as registered for registered services', async () => {
603
603
  const storePath = join(tempDir, 'credentials.json');
604
- writeSecureFile(storePath, '{}');
604
+ await writeSecureFile(storePath, '{}');
605
605
  const registeredService = new RegisteredService('my-gitlab', 'https://gitlab.example.com');
606
606
  const deps = createMockDependencies();
607
607
  deps.registry.addService(registeredService);
@@ -613,12 +613,12 @@ describe('CLI commands with dependency injection', () => {
613
613
  describe('clear command', () => {
614
614
  it('should delete credentials for a service', async () => {
615
615
  const storePath = join(tempDir, 'credentials.json');
616
- writeSecureFile(storePath, JSON.stringify({
616
+ await writeSecureFile(storePath, JSON.stringify({
617
617
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
618
618
  }));
619
619
  const deps = createMockDependencies();
620
620
  await runCommand(['auth', 'clear', 'slack'], deps);
621
- const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
621
+ const storedData = JSON.parse((await readSecureFile(storePath)) ?? '{}');
622
622
  expect(storedData.slack).toBeUndefined();
623
623
  });
624
624
  it('should return error for unknown service', async () => {
@@ -629,13 +629,13 @@ describe('CLI commands with dependency injection', () => {
629
629
  });
630
630
  it('should preserve other services when clearing one', async () => {
631
631
  const storePath = join(tempDir, 'credentials.json');
632
- writeSecureFile(storePath, JSON.stringify({
632
+ await writeSecureFile(storePath, JSON.stringify({
633
633
  slack: { objectType: 'slack', token: 'slack-token', dCookie: 'slack-cookie' },
634
634
  discord: { objectType: 'authorizationBare', token: 'discord-token' },
635
635
  }));
636
636
  const deps = createMockDependencies();
637
637
  await runCommand(['auth', 'clear', 'slack'], deps);
638
- const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
638
+ const storedData = JSON.parse((await readSecureFile(storePath)) ?? '{}');
639
639
  expect(storedData.slack).toBeUndefined();
640
640
  expect(storedData.discord).toBeDefined();
641
641
  expect(storedData.discord?.token).toBe('discord-token');
@@ -643,8 +643,8 @@ describe('CLI commands with dependency injection', () => {
643
643
  it('should delete both store and browser state with -y flag', async () => {
644
644
  const storePath = join(tempDir, 'credentials.json');
645
645
  const browserStatePath = join(tempDir, 'browser_state.json');
646
- writeSecureFile(storePath, JSON.stringify({ slack: { objectType: 'slack', token: 'test', dCookie: 'test' } }));
647
- writeSecureFile(browserStatePath, '{}');
646
+ await writeSecureFile(storePath, JSON.stringify({ slack: { objectType: 'slack', token: 'test', dCookie: 'test' } }));
647
+ await writeSecureFile(browserStatePath, '{}');
648
648
  const deps = createMockDependencies();
649
649
  await runCommand(['auth', 'clear', '-y'], deps);
650
650
  expect(existsSync(storePath)).toBe(false);
@@ -654,7 +654,7 @@ describe('CLI commands with dependency injection', () => {
654
654
  describe('auth list command', () => {
655
655
  it('should list stored credentials with their status', async () => {
656
656
  const storePath = join(tempDir, 'credentials.json');
657
- writeSecureFile(storePath, JSON.stringify({
657
+ await writeSecureFile(storePath, JSON.stringify({
658
658
  slack: { objectType: 'slack', token: 'test-token', dCookie: 'test-cookie' },
659
659
  }));
660
660
  const deps = createMockDependencies();
@@ -668,7 +668,7 @@ describe('CLI commands with dependency injection', () => {
668
668
  });
669
669
  it('should output empty object when no credentials are stored', async () => {
670
670
  const storePath = join(tempDir, 'credentials.json');
671
- writeSecureFile(storePath, '{}');
671
+ await writeSecureFile(storePath, '{}');
672
672
  const deps = createMockDependencies();
673
673
  await runCommand(['auth', 'list'], deps);
674
674
  expect(logs).toHaveLength(1);
@@ -677,7 +677,7 @@ describe('CLI commands with dependency injection', () => {
677
677
  });
678
678
  it('should treat unknown services as valid', async () => {
679
679
  const storePath = join(tempDir, 'credentials.json');
680
- writeSecureFile(storePath, JSON.stringify({
680
+ await writeSecureFile(storePath, JSON.stringify({
681
681
  unknown: { objectType: 'rawCurl', curlArguments: ['-H', 'X-Token: secret'] },
682
682
  }));
683
683
  const deps = createMockDependencies();
@@ -693,11 +693,11 @@ describe('CLI commands with dependency injection', () => {
693
693
  describe('auth set command', () => {
694
694
  it('should store raw curl credentials', async () => {
695
695
  const storePath = join(tempDir, 'credentials.json');
696
- writeSecureFile(storePath, '{}');
696
+ await writeSecureFile(storePath, '{}');
697
697
  const deps = createMockDependencies();
698
698
  await runCommand(['auth', 'set', 'slack', '-H', 'X-Token: secret', '-H', 'X-Other: value'], deps);
699
699
  expect(logs).toContain('Credentials stored.');
700
- const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
700
+ const storedData = JSON.parse((await readSecureFile(storePath)) ?? '{}');
701
701
  expect(storedData.slack).toEqual({
702
702
  objectType: 'rawCurl',
703
703
  curlArguments: ['-H', 'X-Token: secret', '-H', 'X-Other: value'],
@@ -720,13 +720,13 @@ describe('CLI commands with dependency injection', () => {
720
720
  });
721
721
  it('should overwrite existing credentials', async () => {
722
722
  const storePath = join(tempDir, 'credentials.json');
723
- writeSecureFile(storePath, JSON.stringify({
723
+ await writeSecureFile(storePath, JSON.stringify({
724
724
  slack: { objectType: 'slack', token: 'old-token', dCookie: 'old-cookie' },
725
725
  }));
726
726
  const deps = createMockDependencies();
727
727
  await runCommand(['auth', 'set', 'slack', '-H', 'X-Token: new-secret'], deps);
728
728
  expect(logs).toContain('Credentials stored.');
729
- const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
729
+ const storedData = JSON.parse((await readSecureFile(storePath)) ?? '{}');
730
730
  expect(storedData.slack).toEqual({
731
731
  objectType: 'rawCurl',
732
732
  curlArguments: ['-H', 'X-Token: new-secret'],
@@ -736,13 +736,13 @@ describe('CLI commands with dependency injection', () => {
736
736
  describe('auth set-nocurl command', () => {
737
737
  it('should store telegram bot credentials', async () => {
738
738
  const storePath = join(tempDir, 'credentials.json');
739
- writeSecureFile(storePath, '{}');
739
+ await writeSecureFile(storePath, '{}');
740
740
  const deps = createMockDependencies({
741
741
  registry: new Registry([TELEGRAM]),
742
742
  });
743
743
  await runCommand(['auth', 'set-nocurl', 'telegram', '123456:ABC-DEF'], deps);
744
744
  expect(logs).toContain('Credentials stored.');
745
- const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
745
+ const storedData = JSON.parse((await readSecureFile(storePath)) ?? '{}');
746
746
  expect(storedData.telegram).toEqual({
747
747
  objectType: 'telegramBot',
748
748
  token: '123456:ABC-DEF',
@@ -776,7 +776,7 @@ describe('CLI commands with dependency injection', () => {
776
776
  describe('curl command', () => {
777
777
  it('should pass arguments to subprocess', async () => {
778
778
  const storePath = join(tempDir, 'credentials.json');
779
- writeSecureFile(storePath, JSON.stringify({
779
+ await writeSecureFile(storePath, JSON.stringify({
780
780
  slack: { objectType: 'slack', token: 'stored-token', dCookie: 'stored-cookie' },
781
781
  }));
782
782
  const deps = createMockDependencies();
@@ -792,7 +792,7 @@ describe('CLI commands with dependency injection', () => {
792
792
  });
793
793
  it('should pass raw curl credentials to subprocess', async () => {
794
794
  const storePath = join(tempDir, 'credentials.json');
795
- writeSecureFile(storePath, JSON.stringify({
795
+ await writeSecureFile(storePath, JSON.stringify({
796
796
  slack: { objectType: 'rawCurl', curlArguments: ['-H', 'X-Custom: header'] },
797
797
  }));
798
798
  const deps = createMockDependencies();
@@ -802,7 +802,7 @@ describe('CLI commands with dependency injection', () => {
802
802
  });
803
803
  it('should pass multiple arguments correctly', async () => {
804
804
  const storePath = join(tempDir, 'credentials.json');
805
- writeSecureFile(storePath, JSON.stringify({
805
+ await writeSecureFile(storePath, JSON.stringify({
806
806
  slack: { objectType: 'slack', token: 'stored-token', dCookie: 'stored-cookie' },
807
807
  }));
808
808
  const deps = createMockDependencies();
@@ -824,7 +824,7 @@ describe('CLI commands with dependency injection', () => {
824
824
  });
825
825
  it('should return subprocess exit code', async () => {
826
826
  const storePath = join(tempDir, 'credentials.json');
827
- writeSecureFile(storePath, JSON.stringify({
827
+ await writeSecureFile(storePath, JSON.stringify({
828
828
  slack: { objectType: 'slack', token: 'stored-token', dCookie: 'stored-cookie' },
829
829
  }));
830
830
  const deps = createMockDependencies({
@@ -845,7 +845,7 @@ describe('CLI commands with dependency injection', () => {
845
845
  });
846
846
  it('should read credentials from store and not call login', async () => {
847
847
  const storePath = join(tempDir, 'credentials.json');
848
- writeSecureFile(storePath, JSON.stringify({
848
+ await writeSecureFile(storePath, JSON.stringify({
849
849
  slack: { objectType: 'slack', token: 'stored-token', dCookie: 'stored-cookie' },
850
850
  }));
851
851
  const mockLogin = vi.fn();
@@ -874,14 +874,14 @@ describe('CLI commands with dependency injection', () => {
874
874
  });
875
875
  it('should return error when no credentials in store', async () => {
876
876
  const storePath = join(tempDir, 'credentials.json');
877
- writeSecureFile(storePath, '{}');
877
+ await writeSecureFile(storePath, '{}');
878
878
  const deps = createMockDependencies();
879
879
  await runCommand(['curl', 'https://slack.com/api/test'], deps);
880
880
  expect(exitCode).toBe(1);
881
881
  });
882
882
  it('should inject telegram bot token into URL path', async () => {
883
883
  const storePath = join(tempDir, 'credentials.json');
884
- writeSecureFile(storePath, JSON.stringify({
884
+ await writeSecureFile(storePath, JSON.stringify({
885
885
  telegram: { objectType: 'telegramBot', token: '123456:ABC-DEF' },
886
886
  }));
887
887
  const deps = createMockDependencies({
@@ -893,7 +893,7 @@ describe('CLI commands with dependency injection', () => {
893
893
  });
894
894
  it('should work when service does not have getSession but credentials exist', async () => {
895
895
  const storePath = join(tempDir, 'credentials.json');
896
- writeSecureFile(storePath, JSON.stringify({
896
+ await writeSecureFile(storePath, JSON.stringify({
897
897
  nologin: { objectType: 'rawCurl', curlArguments: ['-H', 'X-API-Key: secret'] },
898
898
  }));
899
899
  const noLoginService = {
@@ -946,7 +946,7 @@ describe('CLI commands with dependency injection', () => {
946
946
  });
947
947
  it('should return error when no graphical environment is available', async () => {
948
948
  const storePath = join(tempDir, 'credentials.json');
949
- writeSecureFile(storePath, '{}');
949
+ await writeSecureFile(storePath, '{}');
950
950
  const originalPlatform = process.platform;
951
951
  const originalDisplay = process.env.DISPLAY;
952
952
  const originalWayland = process.env.WAYLAND_DISPLAY;
@@ -1111,7 +1111,7 @@ describe('CLI commands with dependency injection', () => {
1111
1111
  });
1112
1112
  it('should not expose browser auth without --login-url', async () => {
1113
1113
  const storePath = join(tempDir, 'credentials.json');
1114
- writeSecureFile(storePath, '{}');
1114
+ await writeSecureFile(storePath, '{}');
1115
1115
  const deps = createMockDependencies({
1116
1116
  registry: new Registry([GITLAB]),
1117
1117
  });
@@ -1214,7 +1214,7 @@ describe('CLI commands with dependency injection', () => {
1214
1214
  ], deps);
1215
1215
  // Now store credentials for it
1216
1216
  const storePath = join(tempDir, 'credentials.json');
1217
- writeSecureFile(storePath, '{}');
1217
+ await writeSecureFile(storePath, '{}');
1218
1218
  logs = [];
1219
1219
  exitCode = null;
1220
1220
  await runCommand(['auth', 'set', 'my-gitlab', '-H', 'PRIVATE-TOKEN: my-secret-token'], deps);
@@ -1246,7 +1246,7 @@ describe('CLI commands with dependency injection', () => {
1246
1246
  });
1247
1247
  it('should not expose browser auth for service without family', async () => {
1248
1248
  const storePath = join(tempDir, 'credentials.json');
1249
- writeSecureFile(storePath, '{}');
1249
+ await writeSecureFile(storePath, '{}');
1250
1250
  const deps = createMockDependencies({
1251
1251
  registry: new Registry([GITLAB]),
1252
1252
  });
@@ -1265,7 +1265,7 @@ describe('CLI commands with dependency injection', () => {
1265
1265
  await runCommand(['services', 'register', 'my-api', '--base-api-url', 'https://api.example.com/'], deps);
1266
1266
  // Store credentials
1267
1267
  const storePath = join(tempDir, 'credentials.json');
1268
- writeSecureFile(storePath, JSON.stringify({
1268
+ await writeSecureFile(storePath, JSON.stringify({
1269
1269
  'my-api': {
1270
1270
  objectType: 'rawCurl',
1271
1271
  curlArguments: ['-H', 'Authorization: Bearer my-token'],
@@ -1319,7 +1319,7 @@ describe('CLI commands with dependency injection', () => {
1319
1319
  ], deps);
1320
1320
  // Store credentials
1321
1321
  const storePath = join(tempDir, 'credentials.json');
1322
- writeSecureFile(storePath, JSON.stringify({
1322
+ await writeSecureFile(storePath, JSON.stringify({
1323
1323
  'my-gitlab': {
1324
1324
  objectType: 'rawCurl',
1325
1325
  curlArguments: ['-H', 'PRIVATE-TOKEN: my-secret-token'],
@@ -1402,7 +1402,7 @@ describe('CLI commands with dependency injection', () => {
1402
1402
  ], deps);
1403
1403
  // Store credentials for it
1404
1404
  const storePath = join(tempDir, 'credentials.json');
1405
- writeSecureFile(storePath, JSON.stringify({
1405
+ await writeSecureFile(storePath, JSON.stringify({
1406
1406
  'my-gitlab': {
1407
1407
  objectType: 'rawCurl',
1408
1408
  curlArguments: ['-H', 'PRIVATE-TOKEN: my-secret-token'],
@@ -1435,7 +1435,7 @@ describe('CLI commands with dependency injection', () => {
1435
1435
  ], deps);
1436
1436
  // Store and then clear credentials
1437
1437
  const storePath = join(tempDir, 'credentials.json');
1438
- writeSecureFile(storePath, JSON.stringify({
1438
+ await writeSecureFile(storePath, JSON.stringify({
1439
1439
  'my-gitlab': {
1440
1440
  objectType: 'rawCurl',
1441
1441
  curlArguments: ['-H', 'PRIVATE-TOKEN: my-secret-token'],