@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,219 @@
1
+ //#region src/backends/keyring.d.ts
2
+
3
+ type KeyringEntry = {
4
+ getPassword: () => string | null;
5
+ setPassword: (value: string) => void;
6
+ deletePassword: () => void;
7
+ };
8
+ /** Entry options bag; only the Linux store pin is used (see the module note). */
9
+ type KeyringEntryOptions = {
10
+ linux?: {
11
+ store?: 'secret-service' | 'keyutils';
12
+ };
13
+ };
14
+ type KeyringModule = {
15
+ Entry: new (service: string, account: string, options?: KeyringEntryOptions) => KeyringEntry;
16
+ };
17
+ /** @internal Test hook: inject a mock keyring module (vi.mock cannot intercept createRequire). */
18
+ declare function __setKeyringModule(mock: KeyringModule | undefined): void;
19
+ /**
20
+ * OS-keyring {@link CredentialsStore}. Constructed with a shared service name;
21
+ * every entry is stored under `new Entry(service, account, { linux: { store:
22
+ * 'secret-service' } })`.
23
+ *
24
+ * Construction probes the keyring once — if the native module cannot be loaded
25
+ * (no prebuilt binding for this platform), or the Linux Secret Service is
26
+ * unavailable, it throws and {@link createCredentialsStore} falls back to the
27
+ * next backend. Without the pin the absent-service case does not throw; see
28
+ * the module note.
29
+ */
30
+ declare class KeyringBackend implements CredentialsStore {
31
+ private readonly serviceName;
32
+ private readonly options;
33
+ private readonly Entry;
34
+ constructor(_config?: CredentialsStoreConfig);
35
+ get(account: string): string | undefined;
36
+ set(account: string, value: string): void;
37
+ delete(account: string): void;
38
+ has(account: string): boolean;
39
+ } //#endregion
40
+ //#region src/backends/fingerprint.d.ts
41
+ /**
42
+ * Encrypted-file {@link CredentialsStore}. Each account is one entry in a
43
+ * single JSON map, encrypted as one file under the resolved credentials
44
+ * directory, named by the build's credential scope (see `../scope.ts`).
45
+ *
46
+ * Construction probes the machine ID + crypto — if `node-machine-id` cannot
47
+ * resolve a UUID, construction throws and the factory falls back to
48
+ * {@link MemoryBackend}.
49
+ */
50
+ declare class FingerprintBackend implements CredentialsStore {
51
+ private readonly filePath;
52
+ private readonly fileDir;
53
+ constructor(config?: CredentialsStoreConfig);
54
+ /**
55
+ * Load + decrypt the file into a fresh map. Not cached — credentials are
56
+ * read, used, and dropped so plaintext does not linger in memory between
57
+ * operations. The file I/O + scrypt decrypt per call is acceptable: this is
58
+ * not a hot path (once per chat request, not per token).
59
+ */
60
+ private load;
61
+ /** Encrypt + write the map to disk with owner-only permissions. */
62
+ private persist;
63
+ get(account: string): string | undefined;
64
+ set(account: string, value: string): void;
65
+ delete(account: string): void;
66
+ has(account: string): boolean;
67
+ }
68
+
69
+ //#endregion
70
+ //#region src/backends/memory.d.ts
71
+ //# sourceMappingURL=fingerprint.d.ts.map
72
+ /** Process-scoped {@link CredentialsStore}; entries vanish on exit. */
73
+ declare class MemoryBackend implements CredentialsStore {
74
+ private readonly entries;
75
+ constructor(_config?: CredentialsStoreConfig);
76
+ get(account: string): string | undefined;
77
+ set(account: string, value: string): void;
78
+ delete(account: string): void;
79
+ has(account: string): boolean;
80
+ }
81
+ declare function sharedMemoryBackend(config?: CredentialsStoreConfig): MemoryBackend;
82
+ /**
83
+ * Drop the shared instance.
84
+ *
85
+ * For tests, which need each case to start from an empty store. Not called in
86
+ * production: discarding the instance would discard every credential in it.
87
+ */
88
+ declare function __resetSharedMemoryBackend(): void;
89
+
90
+ //#endregion
91
+ //#region src/failover.d.ts
92
+ //# sourceMappingURL=memory.d.ts.map
93
+
94
+ /** Options for {@link FailoverStore}. */
95
+ interface FailoverStoreOptions {
96
+ /**
97
+ * Builds the store that takes over when the primary fails a write. Called
98
+ * at most once, lazily, on first need — a read miss or the first failed
99
+ * write — so a healthy keyring never pays for constructing it. Must not
100
+ * throw: the factory's closure ends in MemoryBackend.
101
+ */
102
+ readonly createFallback: () => CredentialsStore;
103
+ }
104
+ /**
105
+ * A {@link CredentialsStore} that delegates to a primary until the primary
106
+ * fails a write, then fails over — for the life of this store — to a
107
+ * fallback supplied by {@link FailoverStoreOptions.createFallback}.
108
+ *
109
+ * Stickiness is per instance, not per process: each plugin that consumes
110
+ * credentials builds its own store, so one instance failing over does not stop
111
+ * another from trying the primary. That is intended — the flag only exists to
112
+ * avoid re-hitting a primary already known to be unwritable from *this* store,
113
+ * and a second instance's attempt is one probe rather than a policy decision.
114
+ * Correctness across instances comes from the persisted record, not the flag.
115
+ *
116
+ * Constructed by {@link createCredentialsStore} around a KeyringBackend whose
117
+ * construction probe passed; not meant for direct use by upper layers.
118
+ */
119
+ declare class FailoverStore implements CredentialsStore {
120
+ private readonly primary;
121
+ private readonly createFallback;
122
+ private fallback;
123
+ /** Set by the first failed primary write; routes writes thereafter. */
124
+ private failedOver;
125
+ constructor(primary: CredentialsStore, options: FailoverStoreOptions);
126
+ /** The lazily built fallback; constructed at most once per store. */
127
+ private get fallbackStore();
128
+ /**
129
+ * Drop the tombstone for `account`, if any.
130
+ *
131
+ * Paired with the value purge on every path that hands authority back to the
132
+ * primary. Best-effort: a tombstone that outlives its purpose costs one extra
133
+ * fallback lookup, which must never fail a write the keyring has accepted.
134
+ */
135
+ private clearTombstone;
136
+ get(account: string): string | undefined;
137
+ has(account: string): boolean;
138
+ set(account: string, value: string): void;
139
+ delete(account: string): void;
140
+ /**
141
+ * Record the deletion, or report its failure.
142
+ *
143
+ * Unlike the best-effort cleanups above, a tombstone that cannot be written
144
+ * means the delete silently did not happen and the credential will read back
145
+ * later. That is a failed clear, so it throws rather than being swallowed.
146
+ */
147
+ private writeTombstone;
148
+ } //#endregion
149
+ //#region src/index.d.ts
150
+
151
+ //# sourceMappingURL=failover.d.ts.map
152
+ /**
153
+ * Credential storage abstraction for aishell DSH plugins.
154
+ *
155
+ * Upper layers (aishell-llm's adapter, aishell-credentials' host half)
156
+ * call {@link createCredentialsStore} and get a {@link CredentialsStore} —
157
+ * they never learn which backend is serving. The factory probes backends in
158
+ * priority order (OS keyring first, process memory last) and returns the
159
+ * first that answers. New backends (fingerprint + encrypted file, etc.) slot
160
+ * into the chain without touching callers. A keyring that answers the probe
161
+ * but later rejects writes fails over to the next backend on first failure.
162
+ *
163
+ * This is a tool library, not a DSH plugin — no `dsh.client`, no
164
+ * `cordis.patch.yml`, no `apply`. DSH's own `ctx.credentials` is untouched;
165
+ * user-configured models and `.credentials.yaml` keep working.
166
+ *
167
+ * @module @huaweidevtools/aishell-dsh-credentials-store
168
+ */
169
+ /** Options shared by every backend / the factory. */
170
+ interface CredentialsStoreConfig {
171
+ /**
172
+ * Explicit directory for the fingerprint encrypted-file backend. When set,
173
+ * overrides the default `$DSH_HOME/credentials` resolution. Callers rarely
174
+ * need this — the host's `DSH_HOME` already places credentials alongside
175
+ * its other data.
176
+ */
177
+ readonly credentialsDir?: string;
178
+ }
179
+ /**
180
+ * Read/write a named credential. Backends are synchronous and per-call — no
181
+ * cache, so a credential changed from outside is visible on the next `get`.
182
+ * Absent credentials read as `undefined` / `has === false`; a backend that
183
+ * cannot serve (e.g. no D-Bus secret service) degrades the same way rather
184
+ * than throwing on read.
185
+ */
186
+ interface CredentialsStore {
187
+ /** Read one credential; `undefined` when absent or backend unavailable. */
188
+ get(account: string): string | undefined;
189
+ /** Store one credential. Rejects an empty value; throws on backend errors. */
190
+ set(account: string, value: string): void;
191
+ /** Remove one credential; absent is a no-op. Throws on backend errors. */
192
+ delete(account: string): void;
193
+ /** Whether a credential is currently stored; `false` when absent or backend unavailable. */
194
+ has(account: string): boolean;
195
+ }
196
+ /**
197
+ * Probe backends in priority order and return the first that answers.
198
+ *
199
+ * Priority: OS keyring (durable, cross-process) → machine-fingerprint
200
+ * encrypted file (durable, same-machine) → process memory (volatile,
201
+ * last resort). A backend that cannot initialize — no D-Bus secret service
202
+ * on a headless Linux box, keychain locked, etc. — is skipped, not fatal.
203
+ *
204
+ * A keyring that passes the construction probe but is later found unwritable
205
+ * (GNOME login keyring locked: reads come back null, writes fail — the probe
206
+ * cannot see this) is wrapped in a {@link FailoverStore}: its first failed
207
+ * write fails the whole process over to the fingerprint backend, so saving
208
+ * credentials works instead of erroring. The wrapper also records which
209
+ * accounts the keyring must not answer for, so a credential saved while the
210
+ * collection was locked is not shadowed by the keyring's stale copy once it
211
+ * unlocks.
212
+ *
213
+ * @returns the highest-priority backend that could initialize.
214
+ */
215
+ declare function createCredentialsStore(config?: CredentialsStoreConfig): CredentialsStore;
216
+
217
+ //#endregion
218
+ export { CredentialsStore, CredentialsStoreConfig, FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __resetSharedMemoryBackend, __setKeyringModule, createCredentialsStore, sharedMemoryBackend };
219
+ //# sourceMappingURL=index-JlyJE1E8.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-JlyJE1E8.d.ts","names":[],"sources":["../../src/backends/keyring.ts","../../src/backends/fingerprint.ts","../../src/backends/memory.ts","../../src/failover.ts","../../src/index.ts"],"sourcesContent":null,"mappings":";;KAuCK,YAAA;;;;;;KAMA,mBAAA;;ICyCQ,KAAA,CAAA,EAAA,gBAAmB,GAAA,UAAA;EAAA,CAAA;CAAA;KDxC3B,aAAA,GCwCsC;EAAgB,KAAA,EAAA,KAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EDvCD,mBCuCC,EAAA,GDvCuB,YCuCvB;;;iBDlC3C,kBAAA,OAAyB;;AEtBzC;;;;AAAsD;AAsDtD;;;;AAAuF;cFZ1E,cAAA,YAA0B;;;;wBAKhB;;EEkBP,GAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAA0B,CAAA,EAAA,IAAA;;;;;;;;;;;;;AFvB1C;AAA4B,cCcf,kBAAA,YAA8B,gBDdf,CAAA;EAAA,iBAKL,QAAA;EAA2B,iBALX,OAAA;EAAgB,WAAA,CAAA,MAAA,CAAA,ECkBjC,sBDlBiC;;;;;;;;;;;;;ECc1C,GAAA,CAAA,OAAA,EAAA,MAAA,CAAA,EAAmB,OAAA;;;;;;;cCxDnB,aAAA,YAAyB;;wBAGf;;;;;;iBAmDP,mBAAA,UAA4B,yBAA8B;;ADE1E;;;;AAA2D;iBCS3C,0BAAA,CAAA;;;;;;;UCKC,oBAAA;;;AAyBjB;;;;EAOsE,SAPhC,cAAA,EAAA,GAAA,GAlBL,gBAkBK;AAAgB;;;;;;;;;;;;;;;;cAAzC,aAAA,YAAyB;;;;;;ECrGrB,WAAA,CAAA,OAAA,ED4GM,gBC5GgB,EAAA,OAAA,ED4GW,oBC5GX;;;;;;;;AAiBvC;;;;;;;;;;;;;;;;;;;;AJT2E;AAO1D;AAMO;;;;AAEsE;;AAK9F;;;;;;;;;UI5BiB,sBAAA;;;AJgDjB;;;;EAAuD,SAAA,cAAA,CAAA,EAAA,MAAA;;;;;;;;;UI/BtC,gBAAA;;;;EH6CJ,GAAA,CAAA,OAAA,EAAA,MAAA,EAAmB,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAA;EAAA,MAIV,CAAA,OAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAA2B;EAJU,GAAA,CAAA,OAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;;;ACxD3D;;;;AAAsD;AAsDtD;;;;AAAuF;;;;;;;AAWvE,iBExBA,sBAAA,CFwB0B,MAAA,CAAA,EExBK,sBFwBL,CAAA,EExBmC,gBFwBnC"}
package/lib/index.d.ts CHANGED
@@ -1,218 +1,3 @@
1
- //#region src/backends/keyring.d.ts
2
-
3
- type KeyringEntry = {
4
- getPassword: () => string | null;
5
- setPassword: (value: string) => void;
6
- deletePassword: () => void;
7
- };
8
- /** Entry options bag; only the Linux store pin is used (see the module note). */
9
- type KeyringEntryOptions = {
10
- linux?: {
11
- store?: 'secret-service' | 'keyutils';
12
- };
13
- };
14
- type KeyringModule = {
15
- Entry: new (service: string, account: string, options?: KeyringEntryOptions) => KeyringEntry;
16
- };
17
- /** @internal Test hook: inject a mock keyring module (vi.mock cannot intercept createRequire). */
18
- declare function __setKeyringModule(mock: KeyringModule | undefined): void;
19
- /**
20
- * OS-keyring {@link CredentialsStore}. Constructed with a shared service name;
21
- * every entry is stored under `new Entry(service, account, { linux: { store:
22
- * 'secret-service' } })`.
23
- *
24
- * Construction probes the keyring once — if the native module cannot be loaded
25
- * (no prebuilt binding for this platform), or the Linux Secret Service is
26
- * unavailable, it throws and {@link createCredentialsStore} falls back to the
27
- * next backend. Without the pin the absent-service case does not throw; see
28
- * the module note.
29
- */
30
- declare class KeyringBackend implements CredentialsStore {
31
- private readonly serviceName;
32
- private readonly options;
33
- private readonly Entry;
34
- constructor(config?: CredentialsStoreConfig);
35
- get(account: string): string | undefined;
36
- set(account: string, value: string): void;
37
- delete(account: string): void;
38
- has(account: string): boolean;
39
- } //#endregion
40
- //#region src/backends/fingerprint.d.ts
41
- /**
42
- * Encrypted-file {@link CredentialsStore}. Each account is one entry in a
43
- * single JSON map, encrypted as one file under the resolved credentials
44
- * directory (`$DSH_HOME/credentials/credentials.enc` by default).
45
- *
46
- * Construction probes the machine ID + crypto — if `node-machine-id` cannot
47
- * resolve a UUID, construction throws and the factory falls back to
48
- * {@link MemoryBackend}.
49
- */
50
- declare class FingerprintBackend implements CredentialsStore {
51
- private readonly filePath;
52
- private readonly fileDir;
53
- constructor(config?: CredentialsStoreConfig);
54
- /**
55
- * Load + decrypt the file into a fresh map. Not cached — credentials are
56
- * read, used, and dropped so plaintext does not linger in memory between
57
- * operations. The file I/O + scrypt decrypt per call is acceptable: this is
58
- * not a hot path (once per chat request, not per token).
59
- */
60
- private load;
61
- /** Encrypt + write the map to disk with owner-only permissions. */
62
- private persist;
63
- get(account: string): string | undefined;
64
- set(account: string, value: string): void;
65
- delete(account: string): void;
66
- has(account: string): boolean;
67
- }
68
-
69
- //#endregion
70
- //#region src/backends/memory.d.ts
71
- //# sourceMappingURL=fingerprint.d.ts.map
72
- /** Process-scoped {@link CredentialsStore}; entries vanish on exit. */
73
- declare class MemoryBackend implements CredentialsStore {
74
- private readonly entries;
75
- constructor(_config?: CredentialsStoreConfig);
76
- get(account: string): string | undefined;
77
- set(account: string, value: string): void;
78
- delete(account: string): void;
79
- has(account: string): boolean;
80
- }
81
-
82
- //#endregion
83
- //#region src/failover.d.ts
84
- //# sourceMappingURL=memory.d.ts.map
85
-
86
- /** Options for {@link FailoverStore}. */
87
- interface FailoverStoreOptions {
88
- /**
89
- * Builds the store that takes over when the primary fails a write. Called
90
- * at most once, lazily, on first need — a read miss or the first failed
91
- * write — so a healthy keyring never pays for constructing it. Must not
92
- * throw: the factory's closure ends in MemoryBackend.
93
- */
94
- readonly createFallback: () => CredentialsStore;
95
- }
96
- /**
97
- * A {@link CredentialsStore} that delegates to a primary until the primary
98
- * fails a write, then fails over — for the life of this store — to a
99
- * fallback supplied by {@link FailoverStoreOptions.createFallback}.
100
- *
101
- * Stickiness is per instance, not per process: each plugin that consumes
102
- * credentials builds its own store, so one instance failing over does not stop
103
- * another from trying the primary. That is intended — the flag only exists to
104
- * avoid re-hitting a primary already known to be unwritable from *this* store,
105
- * and a second instance's attempt is one probe rather than a policy decision.
106
- * Correctness across instances comes from the persisted record, not the flag.
107
- *
108
- * Constructed by {@link createCredentialsStore} around a KeyringBackend whose
109
- * construction probe passed; not meant for direct use by upper layers.
110
- */
111
- declare class FailoverStore implements CredentialsStore {
112
- private readonly primary;
113
- private readonly createFallback;
114
- private fallback;
115
- /** Set by the first failed primary write; routes writes thereafter. */
116
- private failedOver;
117
- constructor(primary: CredentialsStore, options: FailoverStoreOptions);
118
- /** The lazily built fallback; constructed at most once per store. */
119
- private get fallbackStore();
120
- /**
121
- * Drop the tombstone for `account`, if any.
122
- *
123
- * Paired with the value purge on every path that hands authority back to the
124
- * primary. Best-effort: a tombstone that outlives its purpose costs one extra
125
- * fallback lookup, which must never fail a write the keyring has accepted.
126
- */
127
- private clearTombstone;
128
- get(account: string): string | undefined;
129
- has(account: string): boolean;
130
- set(account: string, value: string): void;
131
- delete(account: string): void;
132
- /**
133
- * Record the deletion, or report its failure.
134
- *
135
- * Unlike the best-effort cleanups above, a tombstone that cannot be written
136
- * means the delete silently did not happen and the credential will read back
137
- * later. That is a failed clear, so it throws rather than being swallowed.
138
- */
139
- private writeTombstone;
140
- } //#endregion
141
- //#region src/index.d.ts
142
-
143
- //# sourceMappingURL=failover.d.ts.map
144
- /**
145
- * Credential storage abstraction for aishell DSH plugins.
146
- *
147
- * Upper layers (aishell-llm's adapter, aishell-credentials' host half)
148
- * call {@link createCredentialsStore} and get a {@link CredentialsStore} —
149
- * they never learn which backend is serving. The factory probes backends in
150
- * priority order (OS keyring first, process memory last) and returns the
151
- * first that answers. New backends (fingerprint + encrypted file, etc.) slot
152
- * into the chain without touching callers. A keyring that answers the probe
153
- * but later rejects writes fails over to the next backend on first failure.
154
- *
155
- * This is a tool library, not a DSH plugin — no `dsh.client`, no
156
- * `cordis.patch.yml`, no `apply`. DSH's own `ctx.credentials` is untouched;
157
- * user-configured models and `.credentials.yaml` keep working.
158
- *
159
- * @module @huaweidevtools/aishell-dsh-credentials-store
160
- */
161
- /** Options shared by every backend / the factory. */
162
- interface CredentialsStoreConfig {
163
- /**
164
- * Backend-scoped identity. For the OS-keyring backend this is the keyring
165
- * service name; every entry is stored under `new Entry(service, account)`.
166
- * Defaults to `'aishell-dsh'`.
167
- */
168
- readonly service?: string;
169
- /**
170
- * Explicit directory for the fingerprint encrypted-file backend. When set,
171
- * overrides the default `$DSH_HOME/credentials` resolution. Callers rarely
172
- * need this — the host's `DSH_HOME` already places credentials alongside
173
- * its other data.
174
- */
175
- readonly credentialsDir?: string;
176
- }
177
- /**
178
- * Read/write a named credential. Backends are synchronous and per-call — no
179
- * cache, so a credential changed from outside is visible on the next `get`.
180
- * Absent credentials read as `undefined` / `has === false`; a backend that
181
- * cannot serve (e.g. no D-Bus secret service) degrades the same way rather
182
- * than throwing on read.
183
- */
184
- interface CredentialsStore {
185
- /** Read one credential; `undefined` when absent or backend unavailable. */
186
- get(account: string): string | undefined;
187
- /** Store one credential. Rejects an empty value; throws on backend errors. */
188
- set(account: string, value: string): void;
189
- /** Remove one credential; absent is a no-op. Throws on backend errors. */
190
- delete(account: string): void;
191
- /** Whether a credential is currently stored; `false` when absent or backend unavailable. */
192
- has(account: string): boolean;
193
- }
194
- /**
195
- * Probe backends in priority order and return the first that answers.
196
- *
197
- * Priority: OS keyring (durable, cross-process) → machine-fingerprint
198
- * encrypted file (durable, same-machine) → process memory (volatile,
199
- * last resort). A backend that cannot initialize — no D-Bus secret service
200
- * on a headless Linux box, keychain locked, etc. — is skipped, not fatal.
201
- *
202
- * A keyring that passes the construction probe but is later found unwritable
203
- * (GNOME login keyring locked: reads come back null, writes fail — the probe
204
- * cannot see this) is wrapped in a {@link FailoverStore}: its first failed
205
- * write fails the whole process over to the fingerprint backend, so saving
206
- * credentials works instead of erroring. The wrapper also records which
207
- * accounts the keyring must not answer for, so a credential saved while the
208
- * collection was locked is not shadowed by the keyring's stale copy once it
209
- * unlocks.
210
- *
211
- * @param config - optional service name override.
212
- * @returns the highest-priority backend that could initialize.
213
- */
214
- declare function createCredentialsStore(config?: CredentialsStoreConfig): CredentialsStore;
215
-
216
- //#endregion
217
- export { CredentialsStore, CredentialsStoreConfig, FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
218
- //# sourceMappingURL=index.d.ts.map
1
+ import { CREDENTIALS_FILE_NAME$1 as CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE$1 as CREDENTIAL_SCOPE, DEFAULT_SCOPE$1 as DEFAULT_SCOPE, KEYRING_SERVICE$1 as KEYRING_SERVICE } from "./scope-CFetnbG3.js";
2
+ import { CredentialsStore, CredentialsStoreConfig, FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __resetSharedMemoryBackend, __setKeyringModule, createCredentialsStore, sharedMemoryBackend } from "./index-JlyJE1E8.js";
3
+ export { CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE, CredentialsStore, CredentialsStoreConfig, DEFAULT_SCOPE, FailoverStore, FingerprintBackend, KEYRING_SERVICE, KeyringBackend, MemoryBackend, __resetSharedMemoryBackend, __setKeyringModule, createCredentialsStore, sharedMemoryBackend };
package/lib/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import { createRequire } from "node:module";
1
+ import { CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE, DEFAULT_SCOPE, KEYRING_SERVICE } from "./scope-QmrHZaZt.js";
2
+ import { credentialsDir } from "./paths-DtFRzop6.js";
2
3
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
4
+ import { createRequire } from "node:module";
3
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
- import { homedir } from "node:os";
5
6
  import { join } from "node:path";
6
7
  import machineIdModule from "node-machine-id";
7
8
 
@@ -28,8 +29,8 @@ function loadKeyring() {
28
29
  * the module note.
29
30
  */
30
31
  var KeyringBackend = class {
31
- constructor(config = {}) {
32
- this.serviceName = config.service ?? "aishell-dsh";
32
+ constructor(_config = {}) {
33
+ this.serviceName = KEYRING_SERVICE;
33
34
  this.options = { linux: { store: "secret-service" } };
34
35
  this.Entry = loadKeyring().Entry;
35
36
  new this.Entry(this.serviceName, "__probe__", this.options).getPassword();
@@ -102,26 +103,20 @@ function fingerprintDecrypt(data) {
102
103
  /**
103
104
  * Resolve the directory holding the encrypted credentials file.
104
105
  *
105
- * Priority: an explicit `config.credentialsDir` override → the host's
106
- * `DSH_HOME` (so credentials live alongside the rest of that host's data —
107
- * `~/.aishell/credentials` for the desktop app, `~/.dsh/credentials` for the
108
- * dsh CLI) → a standalone `~/.aishell-dsh` fallback for headless / library
109
- * use where no DSH host is involved.
106
+ * An explicit `config.credentialsDir` wins; otherwise `<DSH_HOME>/credentials`,
107
+ * resolved by the shared `paths` module so the terraform cache in
108
+ * `aishell-deploy` cannot drift to a different home.
110
109
  *
111
- * The `service` config still names the keyring service (and the fallback
112
- * directory name when neither `credentialsDir` nor `DSH_HOME` is set).
110
+ * Builds are separated by the *file name* — see `../scope.ts` — not by the
111
+ * directory.
113
112
  */
114
113
  function resolveCredentialsDir(config) {
115
- if (config.credentialsDir !== void 0) return config.credentialsDir;
116
- const dshHome = process.env.DSH_HOME;
117
- if (dshHome !== void 0 && dshHome.trim() !== "") return join(dshHome, "credentials");
118
- const dirName = config.service ?? "aishell-dsh";
119
- return join(homedir(), `.${dirName}`);
114
+ return config.credentialsDir ?? credentialsDir();
120
115
  }
121
116
  /**
122
117
  * Encrypted-file {@link CredentialsStore}. Each account is one entry in a
123
118
  * single JSON map, encrypted as one file under the resolved credentials
124
- * directory (`$DSH_HOME/credentials/credentials.enc` by default).
119
+ * directory, named by the build's credential scope (see `../scope.ts`).
125
120
  *
126
121
  * Construction probes the machine ID + crypto — if `node-machine-id` cannot
127
122
  * resolve a UUID, construction throws and the factory falls back to
@@ -130,7 +125,7 @@ function resolveCredentialsDir(config) {
130
125
  var FingerprintBackend = class {
131
126
  constructor(config = {}) {
132
127
  this.fileDir = resolveCredentialsDir(config);
133
- this.filePath = join(this.fileDir, "credentials.enc");
128
+ this.filePath = join(this.fileDir, CREDENTIALS_FILE_NAME);
134
129
  deriveKey();
135
130
  }
136
131
  /**
@@ -197,6 +192,46 @@ var MemoryBackend = class {
197
192
  return this.entries.has(account);
198
193
  }
199
194
  };
195
+ /**
196
+ * The one memory backend the factory hands to every caller in this process.
197
+ *
198
+ * ## Why a module-level instance and not a class singleton
199
+ *
200
+ * `createCredentialsStore()` is called once per plugin — four of them — and
201
+ * the memory backend is the only one of the three that is not shared by
202
+ * construction. The keyring is a daemon and the encrypted file is a path, so
203
+ * two callers reaching either are reading the same bytes; two `Map`s are not.
204
+ * Without this, a machine with no keyring would have one plugin storing a
205
+ * credential and another unable to see it.
206
+ *
207
+ * Held here rather than on the class so `new MemoryBackend()` stays
208
+ * independent, which the specs depend on.
209
+ *
210
+ * ## Why the lazily-created shape
211
+ *
212
+ * The instance is created on first use rather than at module load, so
213
+ * importing this module has no effect a test has to account for — the specs
214
+ * that swap the keyring module in and out must not leave a populated store
215
+ * behind.
216
+ *
217
+ * @param config - forwarded on first construction only; later callers reuse
218
+ * the instance, since the memory backend keeps nothing config decides.
219
+ * @returns the shared backend.
220
+ */
221
+ let shared;
222
+ function sharedMemoryBackend(config = {}) {
223
+ shared ??= new MemoryBackend(config);
224
+ return shared;
225
+ }
226
+ /**
227
+ * Drop the shared instance.
228
+ *
229
+ * For tests, which need each case to start from an empty store. Not called in
230
+ * production: discarding the instance would discard every credential in it.
231
+ */
232
+ function __resetSharedMemoryBackend() {
233
+ shared = void 0;
234
+ }
200
235
 
201
236
  //#endregion
202
237
  //#region src/failover.ts
@@ -334,7 +369,6 @@ var FailoverStore = class {
334
369
  * collection was locked is not shadowed by the keyring's stale copy once it
335
370
  * unlocks.
336
371
  *
337
- * @param config - optional service name override.
338
372
  * @returns the highest-priority backend that could initialize.
339
373
  */
340
374
  function createCredentialsStore(config = {}) {
@@ -353,9 +387,9 @@ function createFallbackStore(config) {
353
387
  try {
354
388
  return new FingerprintBackend(config);
355
389
  } catch {}
356
- return new MemoryBackend(config);
390
+ return sharedMemoryBackend(config);
357
391
  }
358
392
 
359
393
  //#endregion
360
- export { FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
394
+ export { CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE, DEFAULT_SCOPE, FailoverStore, FingerprintBackend, KEYRING_SERVICE, KeyringBackend, MemoryBackend, __resetSharedMemoryBackend, __setKeyringModule, createCredentialsStore, sharedMemoryBackend };
361
395
  //# sourceMappingURL=index.js.map