@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.
Files changed (74) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +62 -14
  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-CsXaUaRH.d.ts +211 -0
  8. package/lib/index-CsXaUaRH.d.ts.map +1 -0
  9. package/lib/index.d.ts +3 -208
  10. package/lib/index.js +26 -28
  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 +22 -0
  42. package/lib/types/backends/memory.d.ts.map +1 -0
  43. package/lib/types/backends/memory.js +35 -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 +73 -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 +21 -8
  74. package/lib/index.d.ts.map +0 -1
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Credential storage abstraction for aishell DSH plugins.
3
+ *
4
+ * Upper layers (aishell-llm's adapter, aishell-credentials' host half)
5
+ * call {@link createCredentialsStore} and get a {@link CredentialsStore} —
6
+ * they never learn which backend is serving. The factory probes backends in
7
+ * priority order (OS keyring first, process memory last) and returns the
8
+ * first that answers. New backends (fingerprint + encrypted file, etc.) slot
9
+ * into the chain without touching callers. A keyring that answers the probe
10
+ * but later rejects writes fails over to the next backend on first failure.
11
+ *
12
+ * This is a tool library, not a DSH plugin — no `dsh.client`, no
13
+ * `cordis.patch.yml`, no `apply`. DSH's own `ctx.credentials` is untouched;
14
+ * user-configured models and `.credentials.yaml` keep working.
15
+ *
16
+ * @module @huaweidevtools/aishell-dsh-credentials-store
17
+ */
18
+ import { KeyringBackend } from "./backends/keyring.js";
19
+ import { FingerprintBackend } from "./backends/fingerprint.js";
20
+ import { MemoryBackend } from "./backends/memory.js";
21
+ import { FailoverStore } from "./failover.js";
22
+ /**
23
+ * Probe backends in priority order and return the first that answers.
24
+ *
25
+ * Priority: OS keyring (durable, cross-process) → machine-fingerprint
26
+ * encrypted file (durable, same-machine) → process memory (volatile,
27
+ * last resort). A backend that cannot initialize — no D-Bus secret service
28
+ * on a headless Linux box, keychain locked, etc. — is skipped, not fatal.
29
+ *
30
+ * A keyring that passes the construction probe but is later found unwritable
31
+ * (GNOME login keyring locked: reads come back null, writes fail — the probe
32
+ * cannot see this) is wrapped in a {@link FailoverStore}: its first failed
33
+ * write fails the whole process over to the fingerprint backend, so saving
34
+ * credentials works instead of erroring. The wrapper also records which
35
+ * accounts the keyring must not answer for, so a credential saved while the
36
+ * collection was locked is not shadowed by the keyring's stale copy once it
37
+ * unlocks.
38
+ *
39
+ * @returns the highest-priority backend that could initialize.
40
+ */
41
+ export function createCredentialsStore(config = {}) {
42
+ const keyring = (() => {
43
+ try {
44
+ return new KeyringBackend(config);
45
+ }
46
+ catch {
47
+ // OS keyring unavailable — try the fingerprint file backend.
48
+ return undefined;
49
+ }
50
+ })();
51
+ if (keyring !== undefined) {
52
+ // FingerprintBackend construction probes machine-id (deriveKey); if that
53
+ // fails, MemoryBackend closes the chain — createFallback never throws.
54
+ return new FailoverStore(keyring, { createFallback: () => createFallbackStore(config) });
55
+ }
56
+ return createFallbackStore(config);
57
+ }
58
+ /** Probe the durable-file and memory backends behind the keyring. */
59
+ function createFallbackStore(config) {
60
+ try {
61
+ return new FingerprintBackend(config);
62
+ }
63
+ catch {
64
+ // Machine fingerprint unavailable (no machine ID) — fall back to memory.
65
+ }
66
+ return new MemoryBackend(config);
67
+ }
68
+ export { CREDENTIAL_SCOPE, CREDENTIALS_FILE_NAME, DEFAULT_SCOPE, KEYRING_SERVICE } from "./scope.js";
69
+ export { KeyringBackend, __setKeyringModule } from "./backends/keyring.js";
70
+ export { FingerprintBackend } from "./backends/fingerprint.js";
71
+ export { MemoryBackend } from "./backends/memory.js";
72
+ export { FailoverStore } from "./failover.js";
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AA+B7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAiC,EAAE;IACxE,MAAM,OAAO,GAAiC,CAAC,GAAG,EAAE;QAClD,IAAI,CAAC;YACH,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAA;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;YAC7D,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC,CAAC,EAAE,CAAA;IACJ,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,yEAAyE;QACzE,uEAAuE;QACvE,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC;AAED,qEAAqE;AACrE,SAAS,mBAAmB,CAAC,MAA8B;IACzD,IAAI,CAAC;QACH,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACpG,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Where per-host state lives. One definition, shared.
3
+ *
4
+ * ## Why this lives in the store package
5
+ *
6
+ * `DSH_HOME` is the host's data directory, and two things in this repo derive
7
+ * from it: the encrypted credentials file here, and the terraform cache in
8
+ * `aishell-deploy`. Defining it twice would let the two drift — the same
9
+ * failure shape the credential-scope constant was created to end.
10
+ *
11
+ * The direction is forced by the dependency graph: `aishell-deploy` already
12
+ * depends on this package, so a definition here is reachable from there, while
13
+ * the reverse would be a cycle. This module is the lower of the two, so it is
14
+ * where the definition belongs.
15
+ *
16
+ * @module @huaweidevtools/aishell-dsh-credentials-store/paths
17
+ */
18
+ /**
19
+ * The fallback data directory when the host sets no `DSH_HOME`.
20
+ *
21
+ * `~/.aishell` — the same directory the desktop app uses, so a library or
22
+ * headless run shares one location with a real install rather than inventing a
23
+ * second home directory.
24
+ */
25
+ export declare const FALLBACK_HOME_NAME = ".aishell";
26
+ /**
27
+ * The host's data directory.
28
+ *
29
+ * `DSH_HOME` when the host set it — the desktop app and the CLI both do, so
30
+ * this lands where the rest of that host's data lives — otherwise
31
+ * `~/.aishell`.
32
+ *
33
+ * Read per call, never captured: a host that sets `DSH_HOME` after this module
34
+ * is first imported must still be honoured.
35
+ *
36
+ * @returns the base directory; not created here.
37
+ */
38
+ export declare function dshHome(): string;
39
+ /**
40
+ * The directory holding the encrypted credentials file.
41
+ *
42
+ * `<DSH_HOME>/credentials`. Builds are separated by the *file name* (the
43
+ * credential scope — `plugins.credentials.enc` by default), not by a second
44
+ * directory, so two builds sit side by side here without reading each other's
45
+ * secrets.
46
+ *
47
+ * @returns the directory; not created here.
48
+ */
49
+ export declare function credentialsDir(): string;
50
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,aAAa,CAAA;AAE5C;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAIhC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Where per-host state lives. One definition, shared.
3
+ *
4
+ * ## Why this lives in the store package
5
+ *
6
+ * `DSH_HOME` is the host's data directory, and two things in this repo derive
7
+ * from it: the encrypted credentials file here, and the terraform cache in
8
+ * `aishell-deploy`. Defining it twice would let the two drift — the same
9
+ * failure shape the credential-scope constant was created to end.
10
+ *
11
+ * The direction is forced by the dependency graph: `aishell-deploy` already
12
+ * depends on this package, so a definition here is reachable from there, while
13
+ * the reverse would be a cycle. This module is the lower of the two, so it is
14
+ * where the definition belongs.
15
+ *
16
+ * @module @huaweidevtools/aishell-dsh-credentials-store/paths
17
+ */
18
+ import { homedir } from 'node:os';
19
+ import { join } from 'node:path';
20
+ /**
21
+ * The fallback data directory when the host sets no `DSH_HOME`.
22
+ *
23
+ * `~/.aishell` — the same directory the desktop app uses, so a library or
24
+ * headless run shares one location with a real install rather than inventing a
25
+ * second home directory.
26
+ */
27
+ export const FALLBACK_HOME_NAME = '.aishell';
28
+ /**
29
+ * The host's data directory.
30
+ *
31
+ * `DSH_HOME` when the host set it — the desktop app and the CLI both do, so
32
+ * this lands where the rest of that host's data lives — otherwise
33
+ * `~/.aishell`.
34
+ *
35
+ * Read per call, never captured: a host that sets `DSH_HOME` after this module
36
+ * is first imported must still be honoured.
37
+ *
38
+ * @returns the base directory; not created here.
39
+ */
40
+ export function dshHome() {
41
+ const configured = process.env.DSH_HOME;
42
+ if (configured !== undefined && configured.trim() !== '')
43
+ return configured;
44
+ return join(homedir(), FALLBACK_HOME_NAME);
45
+ }
46
+ /**
47
+ * The directory holding the encrypted credentials file.
48
+ *
49
+ * `<DSH_HOME>/credentials`. Builds are separated by the *file name* (the
50
+ * credential scope — `plugins.credentials.enc` by default), not by a second
51
+ * directory, so two builds sit side by side here without reading each other's
52
+ * secrets.
53
+ *
54
+ * @returns the directory; not created here.
55
+ */
56
+ export function credentialsDir() {
57
+ return join(dshHome(), 'credentials');
58
+ }
59
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAE5C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAA;IACvC,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,UAAU,CAAA;IAC3E,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAA;AACvC,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Which credential namespace this build uses.
3
+ *
4
+ * ## The knob
5
+ *
6
+ * A build-time constant, not a runtime setting:
7
+ *
8
+ * ```sh
9
+ * AISHELL_CREDENTIAL_SCOPE=staging pnpm run build
10
+ * ```
11
+ *
12
+ * Default `plugins`. It decides two things, and it decides both at once —
13
+ * which is the point:
14
+ *
15
+ * | Used as | Default |
16
+ * |---|---|
17
+ * | keyring service name | `aishell-dsh-plugins` |
18
+ * | encrypted file name | `plugins.credentials.enc` |
19
+ *
20
+ * ## Why it is not a plugin config option
21
+ *
22
+ * It was one, briefly: `keyringService` sat on four plugins' `Config`. Only one
23
+ * of the four writes and all four read, so a deployment that set it on a reader
24
+ * but not the writer stored credentials in one namespace and looked for them in
25
+ * another — the form said "saved" while every tool said "not authenticated".
26
+ * A knob whose only observable effect is that failure is not worth having.
27
+ *
28
+ * A constant cannot be set on one side only. Two deployments that must not see
29
+ * each other's credentials (a staging profile beside a production one) differ
30
+ * by *building* differently, which is the honest place for a persistence
31
+ * boundary.
32
+ *
33
+ * ## Why there is no migration
34
+ *
35
+ * The default changed from `aishell-dsh` to `aishell-dsh-plugins` before the
36
+ * first release, so no install ever wrote the old namespace. Nothing reads it,
37
+ * and adding a migration path would mean keeping the old name alive forever for
38
+ * a state that never existed.
39
+ *
40
+ * @module @huaweidevtools/aishell-dsh-credentials-store/scope
41
+ */
42
+ /**
43
+ * The default scope.
44
+ *
45
+ * Named for the repository, not for a feature: the credential store is shared
46
+ * by every plugin in this set, so it is not `llm`'s or `e2b`'s.
47
+ */
48
+ export declare const DEFAULT_SCOPE = "plugins";
49
+ /** The scope this build uses. */
50
+ export declare const CREDENTIAL_SCOPE: string;
51
+ /**
52
+ * The keyring service every entry of this build lives under.
53
+ *
54
+ * Part of the persistence contract: renaming it orphans every stored entry
55
+ * while looking like a harmless constant change.
56
+ */
57
+ export declare const KEYRING_SERVICE: string;
58
+ /**
59
+ * The encrypted file name for this build.
60
+ *
61
+ * Scoped the same way as the keyring service, so two builds on one machine
62
+ * cannot collide in either backend.
63
+ */
64
+ export declare const CREDENTIALS_FILE_NAME: string;
65
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,YAAY,CAAA;AAkDtC,iCAAiC;AACjC,eAAO,MAAM,gBAAgB,QAAiB,CAAA;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAAoC,CAAA;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAwC,CAAA"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Which credential namespace this build uses.
3
+ *
4
+ * ## The knob
5
+ *
6
+ * A build-time constant, not a runtime setting:
7
+ *
8
+ * ```sh
9
+ * AISHELL_CREDENTIAL_SCOPE=staging pnpm run build
10
+ * ```
11
+ *
12
+ * Default `plugins`. It decides two things, and it decides both at once —
13
+ * which is the point:
14
+ *
15
+ * | Used as | Default |
16
+ * |---|---|
17
+ * | keyring service name | `aishell-dsh-plugins` |
18
+ * | encrypted file name | `plugins.credentials.enc` |
19
+ *
20
+ * ## Why it is not a plugin config option
21
+ *
22
+ * It was one, briefly: `keyringService` sat on four plugins' `Config`. Only one
23
+ * of the four writes and all four read, so a deployment that set it on a reader
24
+ * but not the writer stored credentials in one namespace and looked for them in
25
+ * another — the form said "saved" while every tool said "not authenticated".
26
+ * A knob whose only observable effect is that failure is not worth having.
27
+ *
28
+ * A constant cannot be set on one side only. Two deployments that must not see
29
+ * each other's credentials (a staging profile beside a production one) differ
30
+ * by *building* differently, which is the honest place for a persistence
31
+ * boundary.
32
+ *
33
+ * ## Why there is no migration
34
+ *
35
+ * The default changed from `aishell-dsh` to `aishell-dsh-plugins` before the
36
+ * first release, so no install ever wrote the old namespace. Nothing reads it,
37
+ * and adding a migration path would mean keeping the old name alive forever for
38
+ * a state that never existed.
39
+ *
40
+ * @module @huaweidevtools/aishell-dsh-credentials-store/scope
41
+ */
42
+ /**
43
+ * The default scope.
44
+ *
45
+ * Named for the repository, not for a feature: the credential store is shared
46
+ * by every plugin in this set, so it is not `llm`'s or `e2b`'s.
47
+ */
48
+ export const DEFAULT_SCOPE = 'plugins';
49
+ /**
50
+ * Accepted scope shape: a lowercase identifier.
51
+ *
52
+ * The value reaches a keyring service name and a file name, so it is validated
53
+ * as a strict identifier rather than merely "not obviously a path": no
54
+ * separators, no dots, no leading dash. Assuming a value is benign because the
55
+ * caller is trusted is how path traversal gets in.
56
+ */
57
+ const SCOPE_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
58
+ /**
59
+ * Resolve the scope for this build.
60
+ *
61
+ * Fails loudly on a malformed value rather than falling back to the default:
62
+ * a typo would otherwise silently share the namespace with every unset build,
63
+ * which is exactly the confusion scoping exists to avoid.
64
+ *
65
+ * @returns the validated scope.
66
+ * @throws when the injected value is not a valid scope.
67
+ */
68
+ function resolveScope() {
69
+ // `typeof` rather than a direct read: an undeclared identifier throws, and a
70
+ // source run legitimately has no injected value.
71
+ const injected = typeof __AISHELL_CREDENTIAL_SCOPE__ === 'string'
72
+ ? __AISHELL_CREDENTIAL_SCOPE__
73
+ : undefined;
74
+ if (injected === undefined || injected === '')
75
+ return DEFAULT_SCOPE;
76
+ const normalized = injected.trim().toLowerCase();
77
+ if (normalized.length === 0)
78
+ return DEFAULT_SCOPE;
79
+ if (!SCOPE_PATTERN.test(normalized)) {
80
+ throw new Error(`AISHELL_CREDENTIAL_SCOPE=${JSON.stringify(injected)} is not a valid credential scope: `
81
+ + `expected 1-64 characters of [a-z0-9_-], starting with a letter or digit `
82
+ + '(for example "plugins" or "staging").');
83
+ }
84
+ return normalized;
85
+ }
86
+ /** The scope this build uses. */
87
+ export const CREDENTIAL_SCOPE = resolveScope();
88
+ /**
89
+ * The keyring service every entry of this build lives under.
90
+ *
91
+ * Part of the persistence contract: renaming it orphans every stored entry
92
+ * while looking like a harmless constant change.
93
+ */
94
+ export const KEYRING_SERVICE = `aishell-dsh-${CREDENTIAL_SCOPE}`;
95
+ /**
96
+ * The encrypted file name for this build.
97
+ *
98
+ * Scoped the same way as the keyring service, so two builds on one machine
99
+ * cannot collide in either backend.
100
+ */
101
+ export const CREDENTIALS_FILE_NAME = `${CREDENTIAL_SCOPE}.credentials.enc`;
102
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAA;AAWtC;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,4BAA4B,CAAA;AAElD;;;;;;;;;GASG;AACH,SAAS,YAAY;IACnB,6EAA6E;IAC7E,iDAAiD;IACjD,MAAM,QAAQ,GAAG,OAAO,4BAA4B,KAAK,QAAQ;QAC/D,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,SAAS,CAAA;IACb,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,aAAa,CAAA;IACnE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAChD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,aAAa,CAAA;IACjD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,oCAAoC;cACtF,0EAA0E;cAC1E,uCAAuC,CAC1C,CAAA;IACH,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,iCAAiC;AACjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,EAAE,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,gBAAgB,EAAE,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,gBAAgB,kBAAkB,CAAA"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Huawei Cloud SDK-HMAC-SHA256 request signing.
3
+ *
4
+ * Signs an HTTP request with AK/SK (and an optional temporary SecurityToken)
5
+ * using the same algorithm as the official Huawei Cloud SDK. Produces the
6
+ * `Authorization`, `x-sdk-date`, and `x-security-token` headers to attach
7
+ * to a `fetch` call.
8
+ *
9
+ * @module @huaweidevtools/aishell-dsh-credentials-store/signer
10
+ */
11
+ /** Credentials for signing. */
12
+ export interface SignCredentials {
13
+ readonly ak: string;
14
+ readonly sk: string;
15
+ readonly securityToken?: string;
16
+ }
17
+ /** The headers produced by {@link sign}. */
18
+ export interface SignedHeaders {
19
+ readonly host: string;
20
+ readonly 'x-sdk-date': string;
21
+ readonly Authorization: string;
22
+ readonly 'x-security-token'?: string;
23
+ }
24
+ /**
25
+ * Sign an HTTP request with SDK-HMAC-SHA256.
26
+ *
27
+ * @param method - HTTP method (GET, POST, ...).
28
+ * @param host - Host component of the request URL (e.g. `openai.aishell.openbuildlab.com`).
29
+ * @param path - Path component of the request URL (e.g. `/chat/completions`).
30
+ * @param query - Query parameters as a key-value map.
31
+ * @param body - Request body (stringified for hashing; empty string for GET).
32
+ * @param credentials - AK/SK and optional SecurityToken.
33
+ * @returns The headers to attach to the fetch call.
34
+ */
35
+ export declare function sign(method: string, host: string, path: string, query: Record<string, string>, body: string, credentials: SignCredentials): SignedHeaders;
36
+ //# sourceMappingURL=signer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CACrC;AAiCD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,eAAe,GAC3B,aAAa,CAoCf"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Huawei Cloud SDK-HMAC-SHA256 request signing.
3
+ *
4
+ * Signs an HTTP request with AK/SK (and an optional temporary SecurityToken)
5
+ * using the same algorithm as the official Huawei Cloud SDK. Produces the
6
+ * `Authorization`, `x-sdk-date`, and `x-security-token` headers to attach
7
+ * to a `fetch` call.
8
+ *
9
+ * @module @huaweidevtools/aishell-dsh-credentials-store/signer
10
+ */
11
+ import crypto from 'node:crypto';
12
+ function sha256Hex(data) {
13
+ return crypto.createHash('sha256').update(data).digest('hex');
14
+ }
15
+ function hmacSha256(key, data) {
16
+ return crypto.createHmac('sha256', key).update(data).digest('hex');
17
+ }
18
+ /** RFC 3986 URL-encode a string (unreserved chars stay literal). */
19
+ function urlEncode(str) {
20
+ const hex = (c) => '%' + (c < 16 ? '0' : '') + c.toString(16).toUpperCase();
21
+ const noEscape = new Set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~'.split(''));
22
+ let out = '';
23
+ for (const ch of str) {
24
+ const c = ch.codePointAt(0);
25
+ if (c < 0x80) {
26
+ out += noEscape.has(ch) ? ch : hex(c);
27
+ }
28
+ else {
29
+ out += encodeURIComponent(ch);
30
+ }
31
+ }
32
+ return out;
33
+ }
34
+ /** ISO 8601 timestamp in Huawei Cloud format: `20260912T064552Z`. */
35
+ function sdkTimestamp() {
36
+ // toISOString() returns "2026-09-12T06:45:52.123Z"; strip separators and
37
+ // milliseconds but keep the trailing Z — do NOT append another one.
38
+ return new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
39
+ }
40
+ /**
41
+ * Sign an HTTP request with SDK-HMAC-SHA256.
42
+ *
43
+ * @param method - HTTP method (GET, POST, ...).
44
+ * @param host - Host component of the request URL (e.g. `openai.aishell.openbuildlab.com`).
45
+ * @param path - Path component of the request URL (e.g. `/chat/completions`).
46
+ * @param query - Query parameters as a key-value map.
47
+ * @param body - Request body (stringified for hashing; empty string for GET).
48
+ * @param credentials - AK/SK and optional SecurityToken.
49
+ * @returns The headers to attach to the fetch call.
50
+ */
51
+ export function sign(method, host, path, query, body, credentials) {
52
+ const ts = sdkTimestamp();
53
+ // Canonical query string (sorted keys)
54
+ const cqs = Object.entries(query)
55
+ .sort(([a], [b]) => a.localeCompare(b))
56
+ .map(([k, v]) => `${urlEncode(k)}=${urlEncode(v)}`)
57
+ .join('&');
58
+ // Canonical URI: path segments URL-encoded, joined with /, trailing /
59
+ const curi = '/' + path.split('/').filter(Boolean).map(urlEncode).join('/') + '/';
60
+ // Signed headers
61
+ const hasToken = credentials.securityToken !== undefined;
62
+ const signedHeaders = hasToken ? 'host;x-sdk-date;x-security-token' : 'host;x-sdk-date';
63
+ const canonicalHeaders = hasToken
64
+ ? `host:${host}\nx-sdk-date:${ts}\nx-security-token:${credentials.securityToken}\n`
65
+ : `host:${host}\nx-sdk-date:${ts}\n`;
66
+ // Payload hash
67
+ const payloadHash = sha256Hex(body);
68
+ // Canonical request
69
+ const canonicalRequest = [method, curi, cqs, canonicalHeaders, signedHeaders, payloadHash].join('\n');
70
+ // String to sign
71
+ const stringToSign = `SDK-HMAC-SHA256\n${ts}\n${sha256Hex(canonicalRequest)}`;
72
+ const signature = hmacSha256(credentials.sk, stringToSign);
73
+ const headers = {
74
+ host,
75
+ 'x-sdk-date': ts,
76
+ Authorization: `SDK-HMAC-SHA256 Access=${credentials.ak}, SignedHeaders=${signedHeaders}, Signature=${signature}`,
77
+ ...(hasToken ? { 'x-security-token': credentials.securityToken } : {}),
78
+ };
79
+ return headers;
80
+ }
81
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Huawei Cloud IAM credential verification — one implementation, shared.
3
+ *
4
+ * ## Why it moved here
5
+ *
6
+ * Two packages verified credentials independently: `aishell-credentials`' Host
7
+ * route, with `GET /v3/auth/domains` (it only wanted the account name), and
8
+ * the copied ops tool `auth`, with `GET /v3/projects` (it also wanted the
9
+ * account id and the project list). Same question — "are these credentials
10
+ * valid?" — two endpoints, two error-message vocabularies, two chances to
11
+ * disagree about what a 403 means.
12
+ *
13
+ * This module is the one answer. It is storage-adjacent rather than
14
+ * credential-form-specific, so it lives with the store and the signer, and
15
+ * every caller gets the same verdict in the same shape.
16
+ *
17
+ * ## Why `/v3/projects` is the endpoint
18
+ *
19
+ * It is a superset of the domains call:
20
+ *
21
+ * - A 200 proves the AK/SK (and STS token) work, signed exactly the way every
22
+ * other call is.
23
+ * - Each project carries `domain_id`, which **is** the account id (Huawei
24
+ * Cloud's "domain" and "account" are the same object).
25
+ * - Each project's `name` is a region name, so a caller that needs a
26
+ * `project_id` for a region-scoped API has it without a second request.
27
+ *
28
+ * The account *name* is the one thing projects does not carry, so it is read
29
+ * from `/v3/auth/domains` **best-effort** — some AK/SK scopes do not grant it,
30
+ * and a missing name is diagnostic-only. A failure there never fails
31
+ * verification.
32
+ *
33
+ * ## Region semantics, which surprise people
34
+ *
35
+ * IAM is a global service. The region in the URL only routes to a regional
36
+ * replica; it does not scope the result. So a wrong region still answers 200
37
+ * with the same account, and the real mismatch surfaces at the first
38
+ * region-scoped call. Verification therefore validates the credentials and
39
+ * **not** the region — conflating them would report "invalid credentials" for
40
+ * a typo'd region, sending the person to check the wrong thing.
41
+ *
42
+ * ## Result shape
43
+ *
44
+ * A discriminated union rather than a throw: a rejected credential is an
45
+ * expected outcome of asking someone for credentials, not an exception. The
46
+ * caller decides whether to show, log or retry it.
47
+ *
48
+ * @module @huaweidevtools/aishell-dsh-credentials-store/verify
49
+ */
50
+ import { type SignCredentials } from './signer.ts';
51
+ /** One project visible to the credentials; its `name` is a region name. */
52
+ export interface VerifiedProject {
53
+ /** The `project_id` a region-scoped API takes. */
54
+ readonly id: string;
55
+ /** The project name — the region name, e.g. `cn-north-4`. */
56
+ readonly name: string;
57
+ readonly description?: string;
58
+ }
59
+ /** The account the credentials belong to. */
60
+ export interface VerifiedAccount {
61
+ /** The account id, equal to IAM's `domain_id`. */
62
+ readonly accountId: string;
63
+ /** The account (domain) name; falls back to {@link accountId} when unreadable. */
64
+ readonly name: string;
65
+ /**
66
+ * Every project visible to these credentials.
67
+ *
68
+ * More than one project may share a region name (enterprise projects), which
69
+ * is why this is not reduced to a region→project map: picking one would be a
70
+ * deployment decision this module has no basis for.
71
+ */
72
+ readonly projects: readonly VerifiedProject[];
73
+ }
74
+ /** A successful verification. */
75
+ export interface VerifyOk {
76
+ readonly ok: true;
77
+ readonly account: VerifiedAccount;
78
+ /** The region the call was routed through; see the module note — not verified. */
79
+ readonly region: string;
80
+ /** Whether the credentials were temporary (STS). */
81
+ readonly sts: boolean;
82
+ }
83
+ /** A failed verification. */
84
+ export interface VerifyFailed {
85
+ readonly ok: false;
86
+ /** HTTP status from IAM, or `0` for a network/signing failure. */
87
+ readonly status: number;
88
+ /** A sentence the person or model can act on. */
89
+ readonly message: string;
90
+ }
91
+ /** The outcome of one verification. */
92
+ export type VerifyResult = VerifyOk | VerifyFailed;
93
+ /** Options for {@link verifyCredentials}. */
94
+ export interface VerifyOptions {
95
+ /** Region used to route the call. Does not scope the result; see the module note. */
96
+ readonly region: string;
97
+ /** Fetch implementation; defaults to the global. Injected so tests need no network. */
98
+ readonly fetchImpl?: typeof fetch;
99
+ /** Per-request timeout in milliseconds; defaults to 15 s. */
100
+ readonly timeoutMs?: number;
101
+ }
102
+ /**
103
+ * Verify a credential pair against Huawei Cloud IAM.
104
+ *
105
+ * The pair is signed exactly as every other request is, so a 200 proves the
106
+ * same thing a successful tool call would prove — no separate verification
107
+ * scheme that could drift from the one that actually runs.
108
+ *
109
+ * @param credentials - the AK/SK (and optional STS token) to verify.
110
+ * @param options - region and transport details.
111
+ * @returns the account on success, or a failure naming what IAM said.
112
+ */
113
+ export declare function verifyCredentials(credentials: SignCredentials, options: VerifyOptions): Promise<VerifyResult>;
114
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAAQ,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AAExD,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;CAC9C;AAED,iCAAiC;AACjC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,oDAAoD;IACpD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;CACtB;AAED,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;IAClB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,uCAAuC;AACvC,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAA;AAElD,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AA8CD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAqDvB"}