@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.
- package/LICENSE +1 -1
- package/README.md +49 -10
- package/lib/accounts.d.ts +187 -0
- package/lib/accounts.d.ts.map +1 -0
- package/lib/accounts.js +298 -0
- package/lib/accounts.js.map +1 -0
- package/lib/index-JlyJE1E8.d.ts +219 -0
- package/lib/index-JlyJE1E8.d.ts.map +1 -0
- package/lib/index.d.ts +3 -218
- package/lib/index.js +55 -21
- package/lib/index.js.map +1 -1
- package/lib/paths-DtFRzop6.js +46 -0
- package/lib/paths-DtFRzop6.js.map +1 -0
- package/lib/paths.d.ts +56 -0
- package/lib/paths.d.ts.map +1 -0
- package/lib/paths.js +3 -0
- package/lib/scope-CFetnbG3.d.ts +71 -0
- package/lib/scope-CFetnbG3.d.ts.map +1 -0
- package/lib/scope-QmrHZaZt.js +96 -0
- package/lib/scope-QmrHZaZt.js.map +1 -0
- package/lib/scope.d.ts +2 -0
- package/lib/scope.js +3 -0
- package/lib/signer-BPR0UAF0.js +66 -0
- package/lib/signer-BPR0UAF0.js.map +1 -0
- package/lib/signer-Et0DIH6o.d.ts +40 -0
- package/lib/signer-Et0DIH6o.d.ts.map +1 -0
- package/lib/signer.d.ts +2 -40
- package/lib/signer.js +2 -65
- package/lib/types/accounts.d.ts +231 -0
- package/lib/types/accounts.d.ts.map +1 -0
- package/lib/types/accounts.js +384 -0
- package/lib/types/accounts.js.map +1 -0
- package/lib/types/backends/fingerprint.d.ts +41 -0
- package/lib/types/backends/fingerprint.d.ts.map +1 -0
- package/lib/types/backends/fingerprint.js +135 -0
- package/lib/types/backends/fingerprint.js.map +1 -0
- package/lib/types/backends/keyring.d.ts +69 -0
- package/lib/types/backends/keyring.d.ts.map +1 -0
- package/lib/types/backends/keyring.js +107 -0
- package/lib/types/backends/keyring.js.map +1 -0
- package/lib/types/backends/memory.d.ts +45 -0
- package/lib/types/backends/memory.d.ts.map +1 -0
- package/lib/types/backends/memory.js +89 -0
- package/lib/types/backends/memory.js.map +1 -0
- package/lib/types/failover.d.ts +136 -0
- package/lib/types/failover.d.ts.map +1 -0
- package/lib/types/failover.js +242 -0
- package/lib/types/failover.js.map +1 -0
- package/lib/types/index.d.ts +70 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +78 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/paths.d.ts +50 -0
- package/lib/types/paths.d.ts.map +1 -0
- package/lib/types/paths.js +59 -0
- package/lib/types/paths.js.map +1 -0
- package/lib/types/scope.d.ts +65 -0
- package/lib/types/scope.d.ts.map +1 -0
- package/lib/types/scope.js +102 -0
- package/lib/types/scope.js.map +1 -0
- package/lib/types/signer.d.ts +36 -0
- package/lib/types/signer.d.ts.map +1 -0
- package/lib/types/signer.js +81 -0
- package/lib/types/signer.js.map +1 -0
- package/lib/types/verify.d.ts +114 -0
- package/lib/types/verify.d.ts.map +1 -0
- package/lib/types/verify.js +240 -0
- package/lib/types/verify.js.map +1 -0
- package/lib/verify.d.ts +72 -0
- package/lib/verify.d.ts.map +1 -0
- package/lib/verify.js +172 -0
- package/lib/verify.js.map +1 -0
- package/package.json +20 -7
- 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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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 {
|
|
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(
|
|
32
|
-
this.serviceName =
|
|
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
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
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
|
-
*
|
|
112
|
-
* directory
|
|
110
|
+
* Builds are separated by the *file name* — see `../scope.ts` — not by the
|
|
111
|
+
* directory.
|
|
113
112
|
*/
|
|
114
113
|
function resolveCredentialsDir(config) {
|
|
115
|
-
|
|
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 (
|
|
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,
|
|
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
|
|
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
|