@huaweidevtools/aishell-dsh-credentials-store 0.0.7 → 0.0.9

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 (74) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +49 -10
  3. package/lib/accounts.d.ts +187 -0
  4. package/lib/accounts.d.ts.map +1 -0
  5. package/lib/accounts.js +298 -0
  6. package/lib/accounts.js.map +1 -0
  7. package/lib/index-JlyJE1E8.d.ts +219 -0
  8. package/lib/index-JlyJE1E8.d.ts.map +1 -0
  9. package/lib/index.d.ts +3 -218
  10. package/lib/index.js +55 -21
  11. package/lib/index.js.map +1 -1
  12. package/lib/paths-DtFRzop6.js +46 -0
  13. package/lib/paths-DtFRzop6.js.map +1 -0
  14. package/lib/paths.d.ts +56 -0
  15. package/lib/paths.d.ts.map +1 -0
  16. package/lib/paths.js +3 -0
  17. package/lib/scope-CFetnbG3.d.ts +71 -0
  18. package/lib/scope-CFetnbG3.d.ts.map +1 -0
  19. package/lib/scope-QmrHZaZt.js +96 -0
  20. package/lib/scope-QmrHZaZt.js.map +1 -0
  21. package/lib/scope.d.ts +2 -0
  22. package/lib/scope.js +3 -0
  23. package/lib/signer-BPR0UAF0.js +66 -0
  24. package/lib/signer-BPR0UAF0.js.map +1 -0
  25. package/lib/signer-Et0DIH6o.d.ts +40 -0
  26. package/lib/signer-Et0DIH6o.d.ts.map +1 -0
  27. package/lib/signer.d.ts +2 -40
  28. package/lib/signer.js +2 -65
  29. package/lib/types/accounts.d.ts +231 -0
  30. package/lib/types/accounts.d.ts.map +1 -0
  31. package/lib/types/accounts.js +384 -0
  32. package/lib/types/accounts.js.map +1 -0
  33. package/lib/types/backends/fingerprint.d.ts +41 -0
  34. package/lib/types/backends/fingerprint.d.ts.map +1 -0
  35. package/lib/types/backends/fingerprint.js +135 -0
  36. package/lib/types/backends/fingerprint.js.map +1 -0
  37. package/lib/types/backends/keyring.d.ts +69 -0
  38. package/lib/types/backends/keyring.d.ts.map +1 -0
  39. package/lib/types/backends/keyring.js +107 -0
  40. package/lib/types/backends/keyring.js.map +1 -0
  41. package/lib/types/backends/memory.d.ts +45 -0
  42. package/lib/types/backends/memory.d.ts.map +1 -0
  43. package/lib/types/backends/memory.js +89 -0
  44. package/lib/types/backends/memory.js.map +1 -0
  45. package/lib/types/failover.d.ts +136 -0
  46. package/lib/types/failover.d.ts.map +1 -0
  47. package/lib/types/failover.js +242 -0
  48. package/lib/types/failover.js.map +1 -0
  49. package/lib/types/index.d.ts +70 -0
  50. package/lib/types/index.d.ts.map +1 -0
  51. package/lib/types/index.js +78 -0
  52. package/lib/types/index.js.map +1 -0
  53. package/lib/types/paths.d.ts +50 -0
  54. package/lib/types/paths.d.ts.map +1 -0
  55. package/lib/types/paths.js +59 -0
  56. package/lib/types/paths.js.map +1 -0
  57. package/lib/types/scope.d.ts +65 -0
  58. package/lib/types/scope.d.ts.map +1 -0
  59. package/lib/types/scope.js +102 -0
  60. package/lib/types/scope.js.map +1 -0
  61. package/lib/types/signer.d.ts +36 -0
  62. package/lib/types/signer.d.ts.map +1 -0
  63. package/lib/types/signer.js +81 -0
  64. package/lib/types/signer.js.map +1 -0
  65. package/lib/types/verify.d.ts +114 -0
  66. package/lib/types/verify.d.ts.map +1 -0
  67. package/lib/types/verify.js +240 -0
  68. package/lib/types/verify.js.map +1 -0
  69. package/lib/verify.d.ts +72 -0
  70. package/lib/verify.d.ts.map +1 -0
  71. package/lib/verify.js +172 -0
  72. package/lib/verify.js.map +1 -0
  73. package/package.json +20 -7
  74. package/lib/index.d.ts.map +0 -1
@@ -0,0 +1,107 @@
1
+ /**
2
+ * OS-keyring backend — the durable, cross-process credential store.
3
+ *
4
+ * Wraps `@napi-rs/keyring` (macOS Keychain / Linux Secret Service / Windows
5
+ * Credential Manager). Values persist across process restarts and reboots
6
+ * because the OS owns the store.
7
+ *
8
+ * ## Why Linux pins the Secret Service store
9
+ *
10
+ * On Linux the binding picks a store itself: Secret Service (gnome-keyring,
11
+ * KWallet, KeePassXC) first, then — silently — the kernel keyutils keyring.
12
+ * That fallback is wrong for credentials, and the binding's README says so:
13
+ * the kernel keyring is "completely in-memory and will not persist across
14
+ * reboots". A machine with no Secret Service (a container, CI, a systemd unit
15
+ * with no session bus) would therefore accept credentials, report them stored,
16
+ * and lose them at the next reboot — with `credentialsStored: true` left in
17
+ * the profile pointing at nothing.
18
+ *
19
+ * Pinning `{ linux: { store: 'secret-service' } }` makes an unavailable
20
+ * service throw from the constructor probe instead, so
21
+ * {@link createCredentialsStore} falls through to the machine-fingerprint
22
+ * encrypted file: durable, and honest about where the value lives. Requiring
23
+ * the store is the only way to get that throw — the default selection reports
24
+ * success either way.
25
+ *
26
+ * Native keyring errors are caught: `get`/`has` return `undefined`/`false` so
27
+ * callers degrade gracefully; `set`/`delete` throw a descriptive error so the
28
+ * caller knows the store is unavailable.
29
+ */
30
+ import { createRequire } from 'node:module';
31
+ import { KEYRING_SERVICE } from "../scope.js";
32
+ // Lazily resolved so a missing native binding (the known @napi-rs optional-deps
33
+ // bug, or a platform with no prebuilt binary) throws inside the constructor
34
+ // probe — where createCredentialsStore's try/catch catches it and falls back
35
+ // to FingerprintBackend — instead of crashing at module-load time.
36
+ const require = createRequire(import.meta.url);
37
+ let keyringModule;
38
+ /** @internal Test hook: inject a mock keyring module (vi.mock cannot intercept createRequire). */
39
+ export function __setKeyringModule(mock) {
40
+ keyringModule = mock;
41
+ }
42
+ function loadKeyring() {
43
+ if (keyringModule === undefined)
44
+ keyringModule = require('@napi-rs/keyring');
45
+ return keyringModule;
46
+ }
47
+ /**
48
+ * OS-keyring {@link CredentialsStore}. Constructed with a shared service name;
49
+ * every entry is stored under `new Entry(service, account, { linux: { store:
50
+ * 'secret-service' } })`.
51
+ *
52
+ * Construction probes the keyring once — if the native module cannot be loaded
53
+ * (no prebuilt binding for this platform), or the Linux Secret Service is
54
+ * unavailable, it throws and {@link createCredentialsStore} falls back to the
55
+ * next backend. Without the pin the absent-service case does not throw; see
56
+ * the module note.
57
+ */
58
+ export class KeyringBackend {
59
+ serviceName;
60
+ options;
61
+ Entry;
62
+ constructor(_config = {}) {
63
+ this.serviceName = KEYRING_SERVICE;
64
+ // Required, not preferred: an unavailable Secret Service must throw here so
65
+ // the factory can fall through to a durable backend, rather than silently
66
+ // landing in the kernel keyring's reboot-volatile store.
67
+ this.options = { linux: { store: 'secret-service' } };
68
+ this.Entry = loadKeyring().Entry;
69
+ // Probe: a getPassword on a throwaway account exercises the native path.
70
+ new this.Entry(this.serviceName, '__probe__', this.options).getPassword();
71
+ }
72
+ get(account) {
73
+ try {
74
+ const value = new this.Entry(this.serviceName, account, this.options).getPassword();
75
+ return value ?? undefined;
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ }
81
+ set(account, value) {
82
+ if (value.length === 0)
83
+ throw new Error('credential value must be non-empty');
84
+ new this.Entry(this.serviceName, account, this.options).setPassword(value);
85
+ }
86
+ delete(account) {
87
+ try {
88
+ new this.Entry(this.serviceName, account, this.options).deletePassword();
89
+ }
90
+ catch (error) {
91
+ // deletePassword on an absent entry may throw on some backends;
92
+ // treat that as a no-op rather than an error.
93
+ if (!String(error).includes('not found') && !String(error).includes('no entry')) {
94
+ throw error;
95
+ }
96
+ }
97
+ }
98
+ has(account) {
99
+ try {
100
+ return new this.Entry(this.serviceName, account, this.options).getPassword() !== null;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ }
107
+ //# sourceMappingURL=keyring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyring.js","sourceRoot":"","sources":["../../../src/backends/keyring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAG7C,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,mEAAmE;AACnE,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAW9C,IAAI,aAAwC,CAAA;AAE5C,kGAAkG;AAClG,MAAM,UAAU,kBAAkB,CAAC,IAA+B;IAChE,aAAa,GAAG,IAAI,CAAA;AACtB,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,aAAa,KAAK,SAAS;QAAE,aAAa,GAAG,OAAO,CAAC,kBAAkB,CAAkB,CAAA;IAC7F,OAAO,aAAa,CAAA;AACtB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,cAAc;IACR,WAAW,CAAQ;IACnB,OAAO,CAAqB;IAC5B,KAAK,CAAwB;IAE9C,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,WAAW,GAAG,eAAe,CAAA;QAClC,4EAA4E;QAC5E,0EAA0E;QAC1E,yDAAyD;QACzD,IAAI,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAA;QACrD,IAAI,CAAC,KAAK,GAAG,WAAW,EAAE,CAAC,KAAK,CAAA;QAChC,yEAAyE;QACzE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;IAC3E,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;YACnF,OAAO,KAAK,IAAI,SAAS,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,KAAa;QAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;QAC7E,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IAC5E,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,CAAA;QAC1E,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,gEAAgE;YAChE,8CAA8C;YAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChF,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,IAAI,CAAC;YACH,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAA;QACvF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * In-memory backend — the volatile last-resort credential store.
3
+ *
4
+ * Used when the OS keyring is unavailable (no secret service, keychain
5
+ * locked, headless box without D-Bus). Entries live in a process-scoped
6
+ * `Map` and are lost when the process exits — durable enough for a single
7
+ * DSH session, not for persistence.
8
+ *
9
+ * Callers never select this directly; {@link createCredentialsStore} falls
10
+ * back to it when {@link KeyringBackend} cannot initialize.
11
+ *
12
+ * ## Why the shared instance is not this class's business
13
+ *
14
+ * Each plugin that stores credentials calls `createCredentialsStore()` for
15
+ * itself — four of them — and every call constructs a fresh backend. That is
16
+ * harmless for the keyring and the encrypted file, which are process-external
17
+ * and therefore shared by construction, but a private `Map` is not: without
18
+ * the sharing below, the plugin that saved a credential and the plugin that
19
+ * read it would be looking at different maps, and the second would report
20
+ * "not authenticated" on a machine where the person had just authenticated.
21
+ *
22
+ * The sharing lives in {@link sharedMemoryBackend} rather than in this class
23
+ * so that `new MemoryBackend()` keeps producing an independent store. Tests
24
+ * rely on that — a spec constructing one expects it to start empty, and a
25
+ * singleton class would leak state between spec files.
26
+ */
27
+ import type { CredentialsStore, CredentialsStoreConfig } from '../index.ts';
28
+ /** Process-scoped {@link CredentialsStore}; entries vanish on exit. */
29
+ export declare class MemoryBackend implements CredentialsStore {
30
+ private readonly entries;
31
+ constructor(_config?: CredentialsStoreConfig);
32
+ get(account: string): string | undefined;
33
+ set(account: string, value: string): void;
34
+ delete(account: string): void;
35
+ has(account: string): boolean;
36
+ }
37
+ export declare function sharedMemoryBackend(config?: CredentialsStoreConfig): MemoryBackend;
38
+ /**
39
+ * Drop the shared instance.
40
+ *
41
+ * For tests, which need each case to start from an empty store. Not called in
42
+ * production: discarding the instance would discard every credential in it.
43
+ */
44
+ export declare function __resetSharedMemoryBackend(): void;
45
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/backends/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAE3E,uEAAuE;AACvE,qBAAa,aAAc,YAAW,gBAAgB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,OAAO,GAAE,sBAA2B;IAKhD,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIxC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI7B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CAG9B;AA8BD,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,sBAA2B,GAAG,aAAa,CAGtF;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * In-memory backend — the volatile last-resort credential store.
3
+ *
4
+ * Used when the OS keyring is unavailable (no secret service, keychain
5
+ * locked, headless box without D-Bus). Entries live in a process-scoped
6
+ * `Map` and are lost when the process exits — durable enough for a single
7
+ * DSH session, not for persistence.
8
+ *
9
+ * Callers never select this directly; {@link createCredentialsStore} falls
10
+ * back to it when {@link KeyringBackend} cannot initialize.
11
+ *
12
+ * ## Why the shared instance is not this class's business
13
+ *
14
+ * Each plugin that stores credentials calls `createCredentialsStore()` for
15
+ * itself — four of them — and every call constructs a fresh backend. That is
16
+ * harmless for the keyring and the encrypted file, which are process-external
17
+ * and therefore shared by construction, but a private `Map` is not: without
18
+ * the sharing below, the plugin that saved a credential and the plugin that
19
+ * read it would be looking at different maps, and the second would report
20
+ * "not authenticated" on a machine where the person had just authenticated.
21
+ *
22
+ * The sharing lives in {@link sharedMemoryBackend} rather than in this class
23
+ * so that `new MemoryBackend()` keeps producing an independent store. Tests
24
+ * rely on that — a spec constructing one expects it to start empty, and a
25
+ * singleton class would leak state between spec files.
26
+ */
27
+ /** Process-scoped {@link CredentialsStore}; entries vanish on exit. */
28
+ export class MemoryBackend {
29
+ entries = new Map();
30
+ constructor(_config = {}) {
31
+ // Service name is accepted for API symmetry with KeyringBackend but not
32
+ // used: the in-process Map is keyed by account alone.
33
+ }
34
+ get(account) {
35
+ return this.entries.get(account);
36
+ }
37
+ set(account, value) {
38
+ if (value.length === 0)
39
+ throw new Error('credential value must be non-empty');
40
+ this.entries.set(account, value);
41
+ }
42
+ delete(account) {
43
+ this.entries.delete(account);
44
+ }
45
+ has(account) {
46
+ return this.entries.has(account);
47
+ }
48
+ }
49
+ /**
50
+ * The one memory backend the factory hands to every caller in this process.
51
+ *
52
+ * ## Why a module-level instance and not a class singleton
53
+ *
54
+ * `createCredentialsStore()` is called once per plugin — four of them — and
55
+ * the memory backend is the only one of the three that is not shared by
56
+ * construction. The keyring is a daemon and the encrypted file is a path, so
57
+ * two callers reaching either are reading the same bytes; two `Map`s are not.
58
+ * Without this, a machine with no keyring would have one plugin storing a
59
+ * credential and another unable to see it.
60
+ *
61
+ * Held here rather than on the class so `new MemoryBackend()` stays
62
+ * independent, which the specs depend on.
63
+ *
64
+ * ## Why the lazily-created shape
65
+ *
66
+ * The instance is created on first use rather than at module load, so
67
+ * importing this module has no effect a test has to account for — the specs
68
+ * that swap the keyring module in and out must not leave a populated store
69
+ * behind.
70
+ *
71
+ * @param config - forwarded on first construction only; later callers reuse
72
+ * the instance, since the memory backend keeps nothing config decides.
73
+ * @returns the shared backend.
74
+ */
75
+ let shared;
76
+ export function sharedMemoryBackend(config = {}) {
77
+ shared ??= new MemoryBackend(config);
78
+ return shared;
79
+ }
80
+ /**
81
+ * Drop the shared instance.
82
+ *
83
+ * For tests, which need each case to start from an empty store. Not called in
84
+ * production: discarding the instance would discard every credential in it.
85
+ */
86
+ export function __resetSharedMemoryBackend() {
87
+ shared = undefined;
88
+ }
89
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/backends/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,uEAAuE;AACvE,MAAM,OAAO,aAAa;IACP,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEpD,YAAY,UAAkC,EAAE;QAC9C,wEAAwE;QACxE,sDAAsD;IACxD,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,KAAa;QAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;QAC7E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,IAAI,MAAiC,CAAA;AAErC,MAAM,UAAU,mBAAmB,CAAC,SAAiC,EAAE;IACrE,MAAM,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B;IACxC,MAAM,GAAG,SAAS,CAAA;AACpB,CAAC"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Write-failover wrapper — covers the failure class the construction-time
3
+ * probe chain cannot see.
4
+ *
5
+ * {@link createCredentialsStore} picks a backend by probing at construction
6
+ * time, but the highest-priority backend (the OS keyring) can pass that probe
7
+ * and still reject every real write: a locked GNOME keyring collection reads
8
+ * as `null` (the probe's getPassword does not throw) while setPassword fails.
9
+ * Measured on an isolated D-Bus session:
10
+ *
11
+ * - locked collection, no default alias — read 5 ms → null; write 5 ms →
12
+ * "Couldn't access platform storage: Secret Service: no result found"
13
+ * - locked collection, default alias set (the real-desktop shape) — read
14
+ * still null; write blocks on the system unlock prompter and rejects with
15
+ * "Secret Service: unlock prompt was dismissed" when canceled
16
+ *
17
+ * So the write path needs its own fallback chain, and it must be sticky: a
18
+ * locked keyring fails every write, so retrying it per call would surface the
19
+ * same error (or the same prompt) once per account a save writes. Deletes trip
20
+ * the same failover: a locked collection rejects them with the same error, and
21
+ * the save route (delete old set → write new set) deletes first — without
22
+ * this, a locked keyring would fail the save before the write failover ever
23
+ * engages.
24
+ *
25
+ * ## Which copy wins
26
+ *
27
+ * A locked collection answers `null` for entries that are really there, so the
28
+ * primary's copy of an account can be arbitrarily out of date while still
29
+ * reading as authoritative. Once it unlocks, that copy would outrank the value
30
+ * the user saved in the meantime — resurrecting a credential the user rotated
31
+ * or cleared, with every later signature using the superseded key.
32
+ *
33
+ * The two backends cannot be compared by recency (neither records one), so the
34
+ * fallback is treated as authoritative for any account it has an opinion
35
+ * about, and the primary answers only when the fallback has none:
36
+ *
37
+ * - **A value in the fallback wins.** It is there because a failover write
38
+ * put it there, and that write is by definition newer than whatever the
39
+ * primary holds. No bookkeeping is needed — the value shadows the primary
40
+ * simply by existing.
41
+ * - **A tombstone means deleted.** A clear whose primary delete failed has
42
+ * nothing to shadow with, so it writes an explicit marker; without one, the
43
+ * stale primary copy would read back as present.
44
+ * - **Otherwise the primary answers**, which is the ordinary healthy path.
45
+ *
46
+ * A successful primary write or delete clears both the fallback value and the
47
+ * tombstone, handing authority back to the keyring.
48
+ *
49
+ * Recording deletion this way — rather than marking accounts "stale" on every
50
+ * failover write — keeps the value and its shadow in one place. A mark set by a
51
+ * write would have to be cleared in lockstep with the value, and a mark left
52
+ * behind after its value was gone would make a perfect keyring credential read
53
+ * as *absent* rather than merely stale.
54
+ *
55
+ * ## Why the record is persisted, not held in memory
56
+ *
57
+ * The state it describes outlives the store: the failure is "the keyring is
58
+ * locked now", and the read that would resurrect the credential happens in a
59
+ * later session, after it has unlocked. It is therefore stored in the fallback
60
+ * backend itself — the encrypted file in practice, and merely the process in
61
+ * the memory-backed last resort, where the failover is no more durable either.
62
+ * Keys carry a NUL prefix, which no real credential account can collide with.
63
+ *
64
+ * ## Cost on the healthy path
65
+ *
66
+ * The fallback is consulted on every read (once for a value, once for a
67
+ * tombstone). On a machine whose keyring has never failed the fallback has
68
+ * written nothing, so its file does not exist and both lookups are
69
+ * sub-millisecond `existsSync` misses (measured 0.02-0.4 ms). Where a fallback
70
+ * file does exist, each lookup decrypts it — the read-through this replaced
71
+ * already paid one decrypt per miss.
72
+ *
73
+ * The fallback is built on the first read rather than the first failed write,
74
+ * which a machine with a working keyring pays once per store: ~97 ms, almost
75
+ * all of it the fingerprint backend's scrypt probe (measured). It is lazy and
76
+ * cached, so later reads are unaffected.
77
+ *
78
+ * @module
79
+ */
80
+ import type { CredentialsStore } from './index.ts';
81
+ /** Options for {@link FailoverStore}. */
82
+ export interface FailoverStoreOptions {
83
+ /**
84
+ * Builds the store that takes over when the primary fails a write. Called
85
+ * at most once, lazily, on first need — a read miss or the first failed
86
+ * write — so a healthy keyring never pays for constructing it. Must not
87
+ * throw: the factory's closure ends in MemoryBackend.
88
+ */
89
+ readonly createFallback: () => CredentialsStore;
90
+ }
91
+ /**
92
+ * A {@link CredentialsStore} that delegates to a primary until the primary
93
+ * fails a write, then fails over — for the life of this store — to a
94
+ * fallback supplied by {@link FailoverStoreOptions.createFallback}.
95
+ *
96
+ * Stickiness is per instance, not per process: each plugin that consumes
97
+ * credentials builds its own store, so one instance failing over does not stop
98
+ * another from trying the primary. That is intended — the flag only exists to
99
+ * avoid re-hitting a primary already known to be unwritable from *this* store,
100
+ * and a second instance's attempt is one probe rather than a policy decision.
101
+ * Correctness across instances comes from the persisted record, not the flag.
102
+ *
103
+ * Constructed by {@link createCredentialsStore} around a KeyringBackend whose
104
+ * construction probe passed; not meant for direct use by upper layers.
105
+ */
106
+ export declare class FailoverStore implements CredentialsStore {
107
+ private readonly primary;
108
+ private readonly createFallback;
109
+ private fallback;
110
+ /** Set by the first failed primary write; routes writes thereafter. */
111
+ private failedOver;
112
+ constructor(primary: CredentialsStore, options: FailoverStoreOptions);
113
+ /** The lazily built fallback; constructed at most once per store. */
114
+ private get fallbackStore();
115
+ /**
116
+ * Drop the tombstone for `account`, if any.
117
+ *
118
+ * Paired with the value purge on every path that hands authority back to the
119
+ * primary. Best-effort: a tombstone that outlives its purpose costs one extra
120
+ * fallback lookup, which must never fail a write the keyring has accepted.
121
+ */
122
+ private clearTombstone;
123
+ get(account: string): string | undefined;
124
+ has(account: string): boolean;
125
+ set(account: string, value: string): void;
126
+ delete(account: string): void;
127
+ /**
128
+ * Record the deletion, or report its failure.
129
+ *
130
+ * Unlike the best-effort cleanups above, a tombstone that cannot be written
131
+ * means the delete silently did not happen and the credential will read back
132
+ * later. That is a failed clear, so it throws rather than being swallowed.
133
+ */
134
+ private writeTombstone;
135
+ }
136
+ //# sourceMappingURL=failover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover.d.ts","sourceRoot":"","sources":["../../src/failover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAmBlD,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,gBAAgB,CAAA;CAChD;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,aAAc,YAAW,gBAAgB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,uEAAuE;IACvE,OAAO,CAAC,UAAU,CAAQ;gBAEd,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,oBAAoB;IAKpE,qEAAqE;IACrE,OAAO,KAAK,aAAa,GAGxB;IAED;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUxC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAM7B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAyCzC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IA8B7B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;CAGvB"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Write-failover wrapper — covers the failure class the construction-time
3
+ * probe chain cannot see.
4
+ *
5
+ * {@link createCredentialsStore} picks a backend by probing at construction
6
+ * time, but the highest-priority backend (the OS keyring) can pass that probe
7
+ * and still reject every real write: a locked GNOME keyring collection reads
8
+ * as `null` (the probe's getPassword does not throw) while setPassword fails.
9
+ * Measured on an isolated D-Bus session:
10
+ *
11
+ * - locked collection, no default alias — read 5 ms → null; write 5 ms →
12
+ * "Couldn't access platform storage: Secret Service: no result found"
13
+ * - locked collection, default alias set (the real-desktop shape) — read
14
+ * still null; write blocks on the system unlock prompter and rejects with
15
+ * "Secret Service: unlock prompt was dismissed" when canceled
16
+ *
17
+ * So the write path needs its own fallback chain, and it must be sticky: a
18
+ * locked keyring fails every write, so retrying it per call would surface the
19
+ * same error (or the same prompt) once per account a save writes. Deletes trip
20
+ * the same failover: a locked collection rejects them with the same error, and
21
+ * the save route (delete old set → write new set) deletes first — without
22
+ * this, a locked keyring would fail the save before the write failover ever
23
+ * engages.
24
+ *
25
+ * ## Which copy wins
26
+ *
27
+ * A locked collection answers `null` for entries that are really there, so the
28
+ * primary's copy of an account can be arbitrarily out of date while still
29
+ * reading as authoritative. Once it unlocks, that copy would outrank the value
30
+ * the user saved in the meantime — resurrecting a credential the user rotated
31
+ * or cleared, with every later signature using the superseded key.
32
+ *
33
+ * The two backends cannot be compared by recency (neither records one), so the
34
+ * fallback is treated as authoritative for any account it has an opinion
35
+ * about, and the primary answers only when the fallback has none:
36
+ *
37
+ * - **A value in the fallback wins.** It is there because a failover write
38
+ * put it there, and that write is by definition newer than whatever the
39
+ * primary holds. No bookkeeping is needed — the value shadows the primary
40
+ * simply by existing.
41
+ * - **A tombstone means deleted.** A clear whose primary delete failed has
42
+ * nothing to shadow with, so it writes an explicit marker; without one, the
43
+ * stale primary copy would read back as present.
44
+ * - **Otherwise the primary answers**, which is the ordinary healthy path.
45
+ *
46
+ * A successful primary write or delete clears both the fallback value and the
47
+ * tombstone, handing authority back to the keyring.
48
+ *
49
+ * Recording deletion this way — rather than marking accounts "stale" on every
50
+ * failover write — keeps the value and its shadow in one place. A mark set by a
51
+ * write would have to be cleared in lockstep with the value, and a mark left
52
+ * behind after its value was gone would make a perfect keyring credential read
53
+ * as *absent* rather than merely stale.
54
+ *
55
+ * ## Why the record is persisted, not held in memory
56
+ *
57
+ * The state it describes outlives the store: the failure is "the keyring is
58
+ * locked now", and the read that would resurrect the credential happens in a
59
+ * later session, after it has unlocked. It is therefore stored in the fallback
60
+ * backend itself — the encrypted file in practice, and merely the process in
61
+ * the memory-backed last resort, where the failover is no more durable either.
62
+ * Keys carry a NUL prefix, which no real credential account can collide with.
63
+ *
64
+ * ## Cost on the healthy path
65
+ *
66
+ * The fallback is consulted on every read (once for a value, once for a
67
+ * tombstone). On a machine whose keyring has never failed the fallback has
68
+ * written nothing, so its file does not exist and both lookups are
69
+ * sub-millisecond `existsSync` misses (measured 0.02-0.4 ms). Where a fallback
70
+ * file does exist, each lookup decrypts it — the read-through this replaced
71
+ * already paid one decrypt per miss.
72
+ *
73
+ * The fallback is built on the first read rather than the first failed write,
74
+ * which a machine with a working keyring pays once per store: ~97 ms, almost
75
+ * all of it the fingerprint backend's scrypt probe (measured). It is lazy and
76
+ * cached, so later reads are unaffected.
77
+ *
78
+ * @module
79
+ */
80
+ /**
81
+ * Account-name prefix marking an account as deleted while the primary was
82
+ * unreachable.
83
+ *
84
+ * The NUL byte cannot appear in a real account name (`HW_ACCESS_KEY`,
85
+ * `HW_SECRET_KEY`, `HW_SECURITY_TOKEN`), so a tombstone can never be confused
86
+ * with a credential. Tombstones are only ever written to the fallback, never
87
+ * to the keyring.
88
+ */
89
+ const TOMBSTONE_PREFIX = '\u0000aishell:tombstone:';
90
+ /** The fallback key recording that `account` was deleted. */
91
+ const tombstoneKey = (account) => TOMBSTONE_PREFIX + account;
92
+ /** Tombstone value; never read, only tested for presence. */
93
+ const TOMBSTONE_VALUE = '1';
94
+ /**
95
+ * A {@link CredentialsStore} that delegates to a primary until the primary
96
+ * fails a write, then fails over — for the life of this store — to a
97
+ * fallback supplied by {@link FailoverStoreOptions.createFallback}.
98
+ *
99
+ * Stickiness is per instance, not per process: each plugin that consumes
100
+ * credentials builds its own store, so one instance failing over does not stop
101
+ * another from trying the primary. That is intended — the flag only exists to
102
+ * avoid re-hitting a primary already known to be unwritable from *this* store,
103
+ * and a second instance's attempt is one probe rather than a policy decision.
104
+ * Correctness across instances comes from the persisted record, not the flag.
105
+ *
106
+ * Constructed by {@link createCredentialsStore} around a KeyringBackend whose
107
+ * construction probe passed; not meant for direct use by upper layers.
108
+ */
109
+ export class FailoverStore {
110
+ primary;
111
+ createFallback;
112
+ fallback;
113
+ /** Set by the first failed primary write; routes writes thereafter. */
114
+ failedOver = false;
115
+ constructor(primary, options) {
116
+ this.primary = primary;
117
+ this.createFallback = options.createFallback;
118
+ }
119
+ /** The lazily built fallback; constructed at most once per store. */
120
+ get fallbackStore() {
121
+ if (this.fallback === undefined)
122
+ this.fallback = this.createFallback();
123
+ return this.fallback;
124
+ }
125
+ /**
126
+ * Drop the tombstone for `account`, if any.
127
+ *
128
+ * Paired with the value purge on every path that hands authority back to the
129
+ * primary. Best-effort: a tombstone that outlives its purpose costs one extra
130
+ * fallback lookup, which must never fail a write the keyring has accepted.
131
+ */
132
+ clearTombstone(account) {
133
+ try {
134
+ this.fallbackStore.delete(tombstoneKey(account));
135
+ }
136
+ catch {
137
+ // Best-effort only.
138
+ }
139
+ }
140
+ get(account) {
141
+ // Fallback value first: it is authoritative for any account a failover
142
+ // write touched, and reading the primary first would let the keyring's
143
+ // out-of-date copy win the moment it unlocked.
144
+ const failedOverValue = this.fallbackStore.get(account);
145
+ if (failedOverValue !== undefined)
146
+ return failedOverValue;
147
+ if (this.fallbackStore.has(tombstoneKey(account)))
148
+ return undefined;
149
+ return this.primary.get(account);
150
+ }
151
+ has(account) {
152
+ if (this.fallbackStore.has(account))
153
+ return true;
154
+ if (this.fallbackStore.has(tombstoneKey(account)))
155
+ return false;
156
+ return this.primary.has(account);
157
+ }
158
+ set(account, value) {
159
+ if (value.length === 0)
160
+ throw new Error('credential value must be non-empty');
161
+ if (this.failedOver) {
162
+ // The primary has already proved it cannot be written, so the fallback
163
+ // is the only destination left.
164
+ this.fallbackStore.set(account, value);
165
+ this.clearTombstone(account);
166
+ return;
167
+ }
168
+ try {
169
+ this.primary.set(account, value);
170
+ }
171
+ catch (error) {
172
+ // The primary answered its construction probe but cannot take writes
173
+ // (locked collection, vanished secret service). Fail over — sticky, so
174
+ // the remaining accounts of this save do not each hit the same error (or
175
+ // the same unlock prompt) again — and put the value somewhere durable.
176
+ this.failedOver = true;
177
+ try {
178
+ this.fallbackStore.set(account, value);
179
+ this.clearTombstone(account);
180
+ }
181
+ catch (fallbackError) {
182
+ throw new Error('credential write failed over, but the fallback store rejected it too: '
183
+ + `${String(fallbackError)} (primary error: ${String(error)})`);
184
+ }
185
+ return;
186
+ }
187
+ // The keyring took the write, so it is authoritative for this account
188
+ // again: drop whatever an earlier failed-over session left in the fallback
189
+ // — a value, which would otherwise be read in preference to this write, or
190
+ // a tombstone, which would hide it. Best-effort: failing this cleanup must
191
+ // not fail a save that already succeeded.
192
+ try {
193
+ this.fallbackStore.delete(account);
194
+ }
195
+ catch {
196
+ // Best-effort purge only.
197
+ }
198
+ this.clearTombstone(account);
199
+ }
200
+ delete(account) {
201
+ // The value goes first: it is what a read would serve, and clearing it is
202
+ // required regardless of what the primary does.
203
+ try {
204
+ this.fallbackStore.delete(account);
205
+ }
206
+ catch {
207
+ // Best-effort; the tombstone below still masks the primary's copy.
208
+ }
209
+ if (this.failedOver) {
210
+ // The primary cannot be written, so its copy cannot be removed now. Leave
211
+ // a tombstone: with no value left in the fallback to shadow it, this
212
+ // marker is the only record that the account is gone, and a later read
213
+ // would otherwise serve the very credential being cleared.
214
+ this.writeTombstone(account);
215
+ return;
216
+ }
217
+ try {
218
+ this.primary.delete(account);
219
+ this.clearTombstone(account);
220
+ }
221
+ catch {
222
+ // A locked collection fails deletes as well as writes ("no result found"
223
+ // — wording KeyringBackend's absent-entry no-op filter does not match),
224
+ // and the save route deletes before it writes, so this must trip the
225
+ // same sticky failover a failed write would. The copy we could not remove
226
+ // is masked rather than left readable.
227
+ this.failedOver = true;
228
+ this.writeTombstone(account);
229
+ }
230
+ }
231
+ /**
232
+ * Record the deletion, or report its failure.
233
+ *
234
+ * Unlike the best-effort cleanups above, a tombstone that cannot be written
235
+ * means the delete silently did not happen and the credential will read back
236
+ * later. That is a failed clear, so it throws rather than being swallowed.
237
+ */
238
+ writeTombstone(account) {
239
+ this.fallbackStore.set(tombstoneKey(account), TOMBSTONE_VALUE);
240
+ }
241
+ }
242
+ //# sourceMappingURL=failover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover.js","sourceRoot":"","sources":["../../src/failover.ts"],"names":[],"mappings":"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"}