@huaweidevtools/aishell-dsh-credentials-store 0.0.1-beta.1 → 0.0.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +62 -14
- 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-CsXaUaRH.d.ts +211 -0
- package/lib/index-CsXaUaRH.d.ts.map +1 -0
- package/lib/index.d.ts +3 -208
- package/lib/index.js +26 -28
- 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 +22 -0
- package/lib/types/backends/memory.d.ts.map +1 -0
- package/lib/types/backends/memory.js +35 -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 +73 -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 +21 -8
- package/lib/index.d.ts.map +0 -1
|
@@ -0,0 +1,211 @@
|
|
|
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
|
+
|
|
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
|
+
* Explicit directory for the fingerprint encrypted-file backend. When set,
|
|
165
|
+
* overrides the default `$DSH_HOME/credentials` resolution. Callers rarely
|
|
166
|
+
* need this — the host's `DSH_HOME` already places credentials alongside
|
|
167
|
+
* its other data.
|
|
168
|
+
*/
|
|
169
|
+
readonly credentialsDir?: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Read/write a named credential. Backends are synchronous and per-call — no
|
|
173
|
+
* cache, so a credential changed from outside is visible on the next `get`.
|
|
174
|
+
* Absent credentials read as `undefined` / `has === false`; a backend that
|
|
175
|
+
* cannot serve (e.g. no D-Bus secret service) degrades the same way rather
|
|
176
|
+
* than throwing on read.
|
|
177
|
+
*/
|
|
178
|
+
interface CredentialsStore {
|
|
179
|
+
/** Read one credential; `undefined` when absent or backend unavailable. */
|
|
180
|
+
get(account: string): string | undefined;
|
|
181
|
+
/** Store one credential. Rejects an empty value; throws on backend errors. */
|
|
182
|
+
set(account: string, value: string): void;
|
|
183
|
+
/** Remove one credential; absent is a no-op. Throws on backend errors. */
|
|
184
|
+
delete(account: string): void;
|
|
185
|
+
/** Whether a credential is currently stored; `false` when absent or backend unavailable. */
|
|
186
|
+
has(account: string): boolean;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Probe backends in priority order and return the first that answers.
|
|
190
|
+
*
|
|
191
|
+
* Priority: OS keyring (durable, cross-process) → machine-fingerprint
|
|
192
|
+
* encrypted file (durable, same-machine) → process memory (volatile,
|
|
193
|
+
* last resort). A backend that cannot initialize — no D-Bus secret service
|
|
194
|
+
* on a headless Linux box, keychain locked, etc. — is skipped, not fatal.
|
|
195
|
+
*
|
|
196
|
+
* A keyring that passes the construction probe but is later found unwritable
|
|
197
|
+
* (GNOME login keyring locked: reads come back null, writes fail — the probe
|
|
198
|
+
* cannot see this) is wrapped in a {@link FailoverStore}: its first failed
|
|
199
|
+
* write fails the whole process over to the fingerprint backend, so saving
|
|
200
|
+
* credentials works instead of erroring. The wrapper also records which
|
|
201
|
+
* accounts the keyring must not answer for, so a credential saved while the
|
|
202
|
+
* collection was locked is not shadowed by the keyring's stale copy once it
|
|
203
|
+
* unlocks.
|
|
204
|
+
*
|
|
205
|
+
* @returns the highest-priority backend that could initialize.
|
|
206
|
+
*/
|
|
207
|
+
declare function createCredentialsStore(config?: CredentialsStoreConfig): CredentialsStore;
|
|
208
|
+
|
|
209
|
+
//#endregion
|
|
210
|
+
export { CredentialsStore, CredentialsStoreConfig, FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
|
|
211
|
+
//# sourceMappingURL=index-CsXaUaRH.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CsXaUaRH.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;;AErCzC;;;;AAAsD;;;;;ACqFtD;cH5Ba,cAAA,YAA0B;;;;wBAKhB;;;;;;;;;;;;;;;AALvB;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;;;;;;;cCvEnB,aAAA,YAAyB;;wBAGf;;;;;;;;;AFsDvB;;;UG4BiB,oBAAA;;;;;;;iCAOgB;;;;;;;;;;;ACnFjC;;;;;;cDqGa,aAAA,YAAyB;;ECpFrB,iBAAA,cAAgB;;;;uBD2FV,2BAA2B;;;;;;;;;;;;;;;;AC7DlD;;;;AAA6F;;;;;;;;AJvClB;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;;;;;ACvE3D;;;;AAAsD;;;;;ACqFtD;;;;;;;iBC7BgB,sBAAA,UAA+B,yBAA8B"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,208 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
getPassword: () => string | null;
|
|
5
|
-
setPassword: (value: string) => void;
|
|
6
|
-
deletePassword: () => void;
|
|
7
|
-
};
|
|
8
|
-
type KeyringModule = {
|
|
9
|
-
Entry: new (service: string, account: string) => KeyringEntry;
|
|
10
|
-
};
|
|
11
|
-
/** @internal Test hook: inject a mock keyring module (vi.mock cannot intercept createRequire). */
|
|
12
|
-
declare function __setKeyringModule(mock: KeyringModule | undefined): void;
|
|
13
|
-
/**
|
|
14
|
-
* OS-keyring {@link CredentialsStore}. Constructed with a shared service name;
|
|
15
|
-
* every entry is stored under `new Entry(service, account)`.
|
|
16
|
-
*
|
|
17
|
-
* Construction probes the keyring once — if the native module cannot be loaded
|
|
18
|
-
* (no prebuilt binding for this platform) or cannot reach a secret service it
|
|
19
|
-
* throws, and {@link createCredentialsStore} falls back to the next backend.
|
|
20
|
-
*/
|
|
21
|
-
declare class KeyringBackend implements CredentialsStore {
|
|
22
|
-
private readonly serviceName;
|
|
23
|
-
private readonly Entry;
|
|
24
|
-
constructor(config?: CredentialsStoreConfig);
|
|
25
|
-
get(account: string): string | undefined;
|
|
26
|
-
set(account: string, value: string): void;
|
|
27
|
-
delete(account: string): void;
|
|
28
|
-
has(account: string): boolean;
|
|
29
|
-
} //#endregion
|
|
30
|
-
//#region src/backends/fingerprint.d.ts
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Encrypted-file {@link CredentialsStore}. Each account is one entry in a
|
|
34
|
-
* single JSON map, encrypted as one file under the resolved credentials
|
|
35
|
-
* directory (`$DSH_HOME/credentials/credentials.enc` by default).
|
|
36
|
-
*
|
|
37
|
-
* Construction probes the machine ID + crypto — if `node-machine-id` cannot
|
|
38
|
-
* resolve a UUID, construction throws and the factory falls back to
|
|
39
|
-
* {@link MemoryBackend}.
|
|
40
|
-
*/
|
|
41
|
-
declare class FingerprintBackend implements CredentialsStore {
|
|
42
|
-
private readonly filePath;
|
|
43
|
-
private readonly fileDir;
|
|
44
|
-
constructor(config?: CredentialsStoreConfig);
|
|
45
|
-
/**
|
|
46
|
-
* Load + decrypt the file into a fresh map. Not cached — credentials are
|
|
47
|
-
* read, used, and dropped so plaintext does not linger in memory between
|
|
48
|
-
* operations. The file I/O + scrypt decrypt per call is acceptable: this is
|
|
49
|
-
* not a hot path (once per chat request, not per token).
|
|
50
|
-
*/
|
|
51
|
-
private load;
|
|
52
|
-
/** Encrypt + write the map to disk with owner-only permissions. */
|
|
53
|
-
private persist;
|
|
54
|
-
get(account: string): string | undefined;
|
|
55
|
-
set(account: string, value: string): void;
|
|
56
|
-
delete(account: string): void;
|
|
57
|
-
has(account: string): boolean;
|
|
58
|
-
} //#endregion
|
|
59
|
-
//#region src/backends/memory.d.ts
|
|
60
|
-
|
|
61
|
-
//# sourceMappingURL=fingerprint.d.ts.map
|
|
62
|
-
/** Process-scoped {@link CredentialsStore}; entries vanish on exit. */
|
|
63
|
-
declare class MemoryBackend implements CredentialsStore {
|
|
64
|
-
private readonly entries;
|
|
65
|
-
constructor(_config?: CredentialsStoreConfig);
|
|
66
|
-
get(account: string): string | undefined;
|
|
67
|
-
set(account: string, value: string): void;
|
|
68
|
-
delete(account: string): void;
|
|
69
|
-
has(account: string): boolean;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
//#endregion
|
|
73
|
-
//#region src/failover.d.ts
|
|
74
|
-
//# sourceMappingURL=memory.d.ts.map
|
|
75
|
-
|
|
76
|
-
/** Options for {@link FailoverStore}. */
|
|
77
|
-
interface FailoverStoreOptions {
|
|
78
|
-
/**
|
|
79
|
-
* Builds the store that takes over when the primary fails a write. Called
|
|
80
|
-
* at most once, lazily, on first need — a read miss or the first failed
|
|
81
|
-
* write — so a healthy keyring never pays for constructing it. Must not
|
|
82
|
-
* throw: the factory's closure ends in MemoryBackend.
|
|
83
|
-
*/
|
|
84
|
-
readonly createFallback: () => CredentialsStore;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* A {@link CredentialsStore} that delegates to a primary until the primary
|
|
88
|
-
* fails a write, then fails over — for the life of this store — to a
|
|
89
|
-
* fallback supplied by {@link FailoverStoreOptions.createFallback}.
|
|
90
|
-
*
|
|
91
|
-
* Stickiness is per instance, not per process: each plugin that consumes
|
|
92
|
-
* credentials builds its own store, so one instance failing over does not stop
|
|
93
|
-
* another from trying the primary. That is intended — the flag only exists to
|
|
94
|
-
* avoid re-hitting a primary already known to be unwritable from *this* store,
|
|
95
|
-
* and a second instance's attempt is one probe rather than a policy decision.
|
|
96
|
-
* Correctness across instances comes from the persisted record, not the flag.
|
|
97
|
-
*
|
|
98
|
-
* Constructed by {@link createCredentialsStore} around a KeyringBackend whose
|
|
99
|
-
* construction probe passed; not meant for direct use by upper layers.
|
|
100
|
-
*/
|
|
101
|
-
declare class FailoverStore implements CredentialsStore {
|
|
102
|
-
private readonly primary;
|
|
103
|
-
private readonly createFallback;
|
|
104
|
-
private fallback;
|
|
105
|
-
/** Set by the first failed primary write; routes writes thereafter. */
|
|
106
|
-
private failedOver;
|
|
107
|
-
constructor(primary: CredentialsStore, options: FailoverStoreOptions);
|
|
108
|
-
/** The lazily built fallback; constructed at most once per store. */
|
|
109
|
-
private get fallbackStore();
|
|
110
|
-
/**
|
|
111
|
-
* Drop the tombstone for `account`, if any.
|
|
112
|
-
*
|
|
113
|
-
* Paired with the value purge on every path that hands authority back to the
|
|
114
|
-
* primary. Best-effort: a tombstone that outlives its purpose costs one extra
|
|
115
|
-
* fallback lookup, which must never fail a write the keyring has accepted.
|
|
116
|
-
*/
|
|
117
|
-
private clearTombstone;
|
|
118
|
-
get(account: string): string | undefined;
|
|
119
|
-
has(account: string): boolean;
|
|
120
|
-
set(account: string, value: string): void;
|
|
121
|
-
delete(account: string): void;
|
|
122
|
-
/**
|
|
123
|
-
* Record the deletion, or report its failure.
|
|
124
|
-
*
|
|
125
|
-
* Unlike the best-effort cleanups above, a tombstone that cannot be written
|
|
126
|
-
* means the delete silently did not happen and the credential will read back
|
|
127
|
-
* later. That is a failed clear, so it throws rather than being swallowed.
|
|
128
|
-
*/
|
|
129
|
-
private writeTombstone;
|
|
130
|
-
} //#endregion
|
|
131
|
-
//#region src/index.d.ts
|
|
132
|
-
|
|
133
|
-
//# sourceMappingURL=failover.d.ts.map
|
|
134
|
-
/**
|
|
135
|
-
* Credential storage abstraction for aishell DSH plugins.
|
|
136
|
-
*
|
|
137
|
-
* Upper layers (aishell-llm's adapter, aishell-credentials' host half)
|
|
138
|
-
* call {@link createCredentialsStore} and get a {@link CredentialsStore} —
|
|
139
|
-
* they never learn which backend is serving. The factory probes backends in
|
|
140
|
-
* priority order (OS keyring first, process memory last) and returns the
|
|
141
|
-
* first that answers. New backends (fingerprint + encrypted file, etc.) slot
|
|
142
|
-
* into the chain without touching callers. A keyring that answers the probe
|
|
143
|
-
* but later rejects writes fails over to the next backend on first failure.
|
|
144
|
-
*
|
|
145
|
-
* This is a tool library, not a DSH plugin — no `dsh.client`, no
|
|
146
|
-
* `cordis.patch.yml`, no `apply`. DSH's own `ctx.credentials` is untouched;
|
|
147
|
-
* user-configured models and `.credentials.yaml` keep working.
|
|
148
|
-
*
|
|
149
|
-
* @module @huaweidevtools/aishell-dsh-credentials-store
|
|
150
|
-
*/
|
|
151
|
-
/** Options shared by every backend / the factory. */
|
|
152
|
-
interface CredentialsStoreConfig {
|
|
153
|
-
/**
|
|
154
|
-
* Backend-scoped identity. For the OS-keyring backend this is the keyring
|
|
155
|
-
* service name; every entry is stored under `new Entry(service, account)`.
|
|
156
|
-
* Defaults to `'aishell-dsh'`.
|
|
157
|
-
*/
|
|
158
|
-
readonly service?: string;
|
|
159
|
-
/**
|
|
160
|
-
* Explicit directory for the fingerprint encrypted-file backend. When set,
|
|
161
|
-
* overrides the default `$DSH_HOME/credentials` resolution. Callers rarely
|
|
162
|
-
* need this — the host's `DSH_HOME` already places credentials alongside
|
|
163
|
-
* its other data.
|
|
164
|
-
*/
|
|
165
|
-
readonly credentialsDir?: string;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Read/write a named credential. Backends are synchronous and per-call — no
|
|
169
|
-
* cache, so a credential changed from outside is visible on the next `get`.
|
|
170
|
-
* Absent credentials read as `undefined` / `has === false`; a backend that
|
|
171
|
-
* cannot serve (e.g. no D-Bus secret service) degrades the same way rather
|
|
172
|
-
* than throwing on read.
|
|
173
|
-
*/
|
|
174
|
-
interface CredentialsStore {
|
|
175
|
-
/** Read one credential; `undefined` when absent or backend unavailable. */
|
|
176
|
-
get(account: string): string | undefined;
|
|
177
|
-
/** Store one credential. Rejects an empty value; throws on backend errors. */
|
|
178
|
-
set(account: string, value: string): void;
|
|
179
|
-
/** Remove one credential; absent is a no-op. Throws on backend errors. */
|
|
180
|
-
delete(account: string): void;
|
|
181
|
-
/** Whether a credential is currently stored; `false` when absent or backend unavailable. */
|
|
182
|
-
has(account: string): boolean;
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Probe backends in priority order and return the first that answers.
|
|
186
|
-
*
|
|
187
|
-
* Priority: OS keyring (durable, cross-process) → machine-fingerprint
|
|
188
|
-
* encrypted file (durable, same-machine) → process memory (volatile,
|
|
189
|
-
* last resort). A backend that cannot initialize — no D-Bus secret service
|
|
190
|
-
* on a headless Linux box, keychain locked, etc. — is skipped, not fatal.
|
|
191
|
-
*
|
|
192
|
-
* A keyring that passes the construction probe but is later found unwritable
|
|
193
|
-
* (GNOME login keyring locked: reads come back null, writes fail — the probe
|
|
194
|
-
* cannot see this) is wrapped in a {@link FailoverStore}: its first failed
|
|
195
|
-
* write fails the whole process over to the fingerprint backend, so saving
|
|
196
|
-
* credentials works instead of erroring. The wrapper also records which
|
|
197
|
-
* accounts the keyring must not answer for, so a credential saved while the
|
|
198
|
-
* collection was locked is not shadowed by the keyring's stale copy once it
|
|
199
|
-
* unlocks.
|
|
200
|
-
*
|
|
201
|
-
* @param config - optional service name override.
|
|
202
|
-
* @returns the highest-priority backend that could initialize.
|
|
203
|
-
*/
|
|
204
|
-
declare function createCredentialsStore(config?: CredentialsStoreConfig): CredentialsStore;
|
|
205
|
-
|
|
206
|
-
//#endregion
|
|
207
|
-
export { CredentialsStore, CredentialsStoreConfig, FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
|
|
208
|
-
//# 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, __setKeyringModule, createCredentialsStore } from "./index-CsXaUaRH.js";
|
|
3
|
+
export { CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE, CredentialsStore, CredentialsStoreConfig, DEFAULT_SCOPE, FailoverStore, FingerprintBackend, KEYRING_SERVICE, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
|
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
|
|
|
@@ -18,21 +19,25 @@ function loadKeyring() {
|
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* OS-keyring {@link CredentialsStore}. Constructed with a shared service name;
|
|
21
|
-
* every entry is stored under `new Entry(service, account
|
|
22
|
+
* every entry is stored under `new Entry(service, account, { linux: { store:
|
|
23
|
+
* 'secret-service' } })`.
|
|
22
24
|
*
|
|
23
25
|
* Construction probes the keyring once — if the native module cannot be loaded
|
|
24
|
-
* (no prebuilt binding for this platform) or
|
|
25
|
-
*
|
|
26
|
+
* (no prebuilt binding for this platform), or the Linux Secret Service is
|
|
27
|
+
* unavailable, it throws and {@link createCredentialsStore} falls back to the
|
|
28
|
+
* next backend. Without the pin the absent-service case does not throw; see
|
|
29
|
+
* the module note.
|
|
26
30
|
*/
|
|
27
31
|
var KeyringBackend = class {
|
|
28
|
-
constructor(
|
|
29
|
-
this.serviceName =
|
|
32
|
+
constructor(_config = {}) {
|
|
33
|
+
this.serviceName = KEYRING_SERVICE;
|
|
34
|
+
this.options = { linux: { store: "secret-service" } };
|
|
30
35
|
this.Entry = loadKeyring().Entry;
|
|
31
|
-
new this.Entry(this.serviceName, "__probe__").getPassword();
|
|
36
|
+
new this.Entry(this.serviceName, "__probe__", this.options).getPassword();
|
|
32
37
|
}
|
|
33
38
|
get(account) {
|
|
34
39
|
try {
|
|
35
|
-
const value = new this.Entry(this.serviceName, account).getPassword();
|
|
40
|
+
const value = new this.Entry(this.serviceName, account, this.options).getPassword();
|
|
36
41
|
return value ?? void 0;
|
|
37
42
|
} catch {
|
|
38
43
|
return void 0;
|
|
@@ -40,18 +45,18 @@ var KeyringBackend = class {
|
|
|
40
45
|
}
|
|
41
46
|
set(account, value) {
|
|
42
47
|
if (value.length === 0) throw new Error("credential value must be non-empty");
|
|
43
|
-
new this.Entry(this.serviceName, account).setPassword(value);
|
|
48
|
+
new this.Entry(this.serviceName, account, this.options).setPassword(value);
|
|
44
49
|
}
|
|
45
50
|
delete(account) {
|
|
46
51
|
try {
|
|
47
|
-
new this.Entry(this.serviceName, account).deletePassword();
|
|
52
|
+
new this.Entry(this.serviceName, account, this.options).deletePassword();
|
|
48
53
|
} catch (error) {
|
|
49
54
|
if (!String(error).includes("not found") && !String(error).includes("no entry")) throw error;
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
has(account) {
|
|
53
58
|
try {
|
|
54
|
-
return new this.Entry(this.serviceName, account).getPassword() !== null;
|
|
59
|
+
return new this.Entry(this.serviceName, account, this.options).getPassword() !== null;
|
|
55
60
|
} catch {
|
|
56
61
|
return false;
|
|
57
62
|
}
|
|
@@ -98,26 +103,20 @@ function fingerprintDecrypt(data) {
|
|
|
98
103
|
/**
|
|
99
104
|
* Resolve the directory holding the encrypted credentials file.
|
|
100
105
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* dsh CLI) → a standalone `~/.aishell-dsh` fallback for headless / library
|
|
105
|
-
* 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.
|
|
106
109
|
*
|
|
107
|
-
*
|
|
108
|
-
* directory
|
|
110
|
+
* Builds are separated by the *file name* — see `../scope.ts` — not by the
|
|
111
|
+
* directory.
|
|
109
112
|
*/
|
|
110
113
|
function resolveCredentialsDir(config) {
|
|
111
|
-
|
|
112
|
-
const dshHome = process.env.DSH_HOME;
|
|
113
|
-
if (dshHome !== void 0 && dshHome.trim() !== "") return join(dshHome, "credentials");
|
|
114
|
-
const dirName = config.service ?? "aishell-dsh";
|
|
115
|
-
return join(homedir(), `.${dirName}`);
|
|
114
|
+
return config.credentialsDir ?? credentialsDir();
|
|
116
115
|
}
|
|
117
116
|
/**
|
|
118
117
|
* Encrypted-file {@link CredentialsStore}. Each account is one entry in a
|
|
119
118
|
* single JSON map, encrypted as one file under the resolved credentials
|
|
120
|
-
* directory (
|
|
119
|
+
* directory, named by the build's credential scope (see `../scope.ts`).
|
|
121
120
|
*
|
|
122
121
|
* Construction probes the machine ID + crypto — if `node-machine-id` cannot
|
|
123
122
|
* resolve a UUID, construction throws and the factory falls back to
|
|
@@ -126,7 +125,7 @@ function resolveCredentialsDir(config) {
|
|
|
126
125
|
var FingerprintBackend = class {
|
|
127
126
|
constructor(config = {}) {
|
|
128
127
|
this.fileDir = resolveCredentialsDir(config);
|
|
129
|
-
this.filePath = join(this.fileDir,
|
|
128
|
+
this.filePath = join(this.fileDir, CREDENTIALS_FILE_NAME);
|
|
130
129
|
deriveKey();
|
|
131
130
|
}
|
|
132
131
|
/**
|
|
@@ -330,7 +329,6 @@ var FailoverStore = class {
|
|
|
330
329
|
* collection was locked is not shadowed by the keyring's stale copy once it
|
|
331
330
|
* unlocks.
|
|
332
331
|
*
|
|
333
|
-
* @param config - optional service name override.
|
|
334
332
|
* @returns the highest-priority backend that could initialize.
|
|
335
333
|
*/
|
|
336
334
|
function createCredentialsStore(config = {}) {
|
|
@@ -353,5 +351,5 @@ function createFallbackStore(config) {
|
|
|
353
351
|
}
|
|
354
352
|
|
|
355
353
|
//#endregion
|
|
356
|
-
export { FailoverStore, FingerprintBackend, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
|
|
354
|
+
export { CREDENTIALS_FILE_NAME, CREDENTIAL_SCOPE, DEFAULT_SCOPE, FailoverStore, FingerprintBackend, KEYRING_SERVICE, KeyringBackend, MemoryBackend, __setKeyringModule, createCredentialsStore };
|
|
357
355
|
//# sourceMappingURL=index.js.map
|