@huaweidevtools/aishell-dsh-credentials-store 0.0.15 → 0.0.16
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/lib/accounts.d.ts +31 -20
- package/lib/accounts.d.ts.map +1 -1
- package/lib/accounts.js +57 -24
- package/lib/accounts.js.map +1 -1
- package/lib/{index-JlyJE1E8.d.ts → index-Cdsu1kDC.d.ts} +87 -12
- package/lib/index-Cdsu1kDC.d.ts.map +1 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.js +165 -29
- package/lib/index.js.map +1 -1
- package/lib/{paths-DtFRzop6.js → paths-C0wxA5LO.js} +3 -3
- package/lib/paths-C0wxA5LO.js.map +1 -0
- package/lib/paths.d.ts +10 -9
- package/lib/paths.d.ts.map +1 -1
- package/lib/paths.js +1 -1
- package/lib/{scope-CFetnbG3.d.ts → scope-C7w2eTvE.d.ts} +12 -12
- package/lib/{scope-CFetnbG3.d.ts.map → scope-C7w2eTvE.d.ts.map} +1 -1
- package/lib/{scope-QmrHZaZt.js → scope-DJcK27Uc.js} +12 -12
- package/lib/scope-DJcK27Uc.js.map +1 -0
- package/lib/scope.d.ts +1 -1
- package/lib/scope.js +1 -1
- package/lib/signer-Bx88x7dI.d.ts +72 -0
- package/lib/signer-Bx88x7dI.d.ts.map +1 -0
- package/lib/{signer-BPR0UAF0.js → signer-CVOjpi6y.js} +39 -3
- package/lib/signer-CVOjpi6y.js.map +1 -0
- package/lib/signer.d.ts +2 -2
- package/lib/signer.js +2 -2
- package/lib/types/accounts.d.ts +61 -39
- package/lib/types/accounts.d.ts.map +1 -1
- package/lib/types/accounts.js +108 -52
- package/lib/types/accounts.js.map +1 -1
- package/lib/types/backends/fingerprint.d.ts +78 -0
- package/lib/types/backends/fingerprint.d.ts.map +1 -1
- package/lib/types/backends/fingerprint.js +155 -8
- package/lib/types/backends/fingerprint.js.map +1 -1
- package/lib/types/backends/keyring.d.ts +7 -7
- package/lib/types/backends/keyring.d.ts.map +1 -1
- package/lib/types/backends/keyring.js +15 -9
- package/lib/types/backends/keyring.js.map +1 -1
- package/lib/types/backends/memory.d.ts +4 -17
- package/lib/types/backends/memory.d.ts.map +1 -1
- package/lib/types/backends/memory.js +21 -32
- package/lib/types/backends/memory.js.map +1 -1
- package/lib/types/failover.d.ts +16 -18
- package/lib/types/failover.d.ts.map +1 -1
- package/lib/types/failover.js +19 -21
- package/lib/types/failover.js.map +1 -1
- package/lib/types/index.d.ts +4 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +4 -4
- package/lib/types/index.js.map +1 -1
- package/lib/types/paths.d.ts +10 -9
- package/lib/types/paths.d.ts.map +1 -1
- package/lib/types/paths.js +10 -9
- package/lib/types/paths.js.map +1 -1
- package/lib/types/scope.d.ts +11 -11
- package/lib/types/scope.js +11 -11
- package/lib/types/signer.d.ts +32 -0
- package/lib/types/signer.d.ts.map +1 -1
- package/lib/types/signer.js +40 -5
- package/lib/types/signer.js.map +1 -1
- package/lib/types/verify.d.ts +7 -11
- package/lib/types/verify.d.ts.map +1 -1
- package/lib/types/verify.js +27 -11
- package/lib/types/verify.js.map +1 -1
- package/lib/verify.d.ts +1 -1
- package/lib/verify.d.ts.map +1 -1
- package/lib/verify.js +11 -1
- package/lib/verify.js.map +1 -1
- package/package.json +1 -1
- package/lib/index-JlyJE1E8.d.ts.map +0 -1
- package/lib/paths-DtFRzop6.js.map +0 -1
- package/lib/scope-QmrHZaZt.js.map +0 -1
- package/lib/signer-BPR0UAF0.js.map +0 -1
- package/lib/signer-Et0DIH6o.d.ts +0 -40
- package/lib/signer-Et0DIH6o.d.ts.map +0 -1
package/lib/types/scope.js
CHANGED
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
* | keyring service name | `aishell-dsh-plugins` |
|
|
18
18
|
* | encrypted file name | `plugins.credentials.enc` |
|
|
19
19
|
*
|
|
20
|
-
* ## Why it is
|
|
20
|
+
* ## Why it is a build-time constant
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* The value has to be the same on both sides of a credential: the writer that
|
|
23
|
+
* stores it and the reader that looks it up. A runtime setting can be applied
|
|
24
|
+
* to one plugin and not another, and the result is a form that says "saved"
|
|
25
|
+
* beside tools that say "not authenticated" — a failure whose only symptom is
|
|
26
|
+
* the one thing the setting exists to control.
|
|
27
27
|
*
|
|
28
28
|
* A constant cannot be set on one side only. Two deployments that must not see
|
|
29
29
|
* each other's credentials (a staging profile beside a production one) differ
|
|
30
30
|
* by *building* differently, which is the honest place for a persistence
|
|
31
31
|
* boundary.
|
|
32
32
|
*
|
|
33
|
-
* ## Why
|
|
33
|
+
* ## Why the default namespace carries no migration path
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* a
|
|
35
|
+
* `plugins` is the first and only scope, so a build that sets nothing lands on
|
|
36
|
+
* it and a build that sets something has deliberately moved to a namespace of
|
|
37
|
+
* its own. There are no two names to reconcile, so there is no migration path
|
|
38
|
+
* to write — and none is needed until a release renames this default.
|
|
39
39
|
*
|
|
40
40
|
* @module @huaweidevtools/aishell-dsh-credentials-store/scope
|
|
41
41
|
*/
|
package/lib/types/signer.d.ts
CHANGED
|
@@ -21,6 +21,38 @@ export interface SignedHeaders {
|
|
|
21
21
|
readonly Authorization: string;
|
|
22
22
|
readonly 'x-security-token'?: string;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Code-unit comparison — the order the signing specification requires.
|
|
26
|
+
*
|
|
27
|
+
* Exported because it is the part of signing that is checkable without a
|
|
28
|
+
* signature: everything downstream of it is a hash, so a test that compares two
|
|
29
|
+
* signatures cannot tell a correct ordering from a consistently wrong one. See
|
|
30
|
+
* `tests/format.spec.ts`.
|
|
31
|
+
*
|
|
32
|
+
* @param a - one string.
|
|
33
|
+
* @param b - the other.
|
|
34
|
+
* @returns negative, zero or positive in code-unit order.
|
|
35
|
+
*/
|
|
36
|
+
export declare function compareCodeUnits(a: string, b: string): number;
|
|
37
|
+
/**
|
|
38
|
+
* Build the canonical query string, sorted by parameter name.
|
|
39
|
+
*
|
|
40
|
+
* ## Why byte order and not `localeCompare`
|
|
41
|
+
*
|
|
42
|
+
* The specification sorts by code point. Locale-aware collation disagrees with
|
|
43
|
+
* it on mixed case: `Zebra` sorts *before* `apple` by code unit and *after* it
|
|
44
|
+
* under the default locale. The signed string would then differ from the one
|
|
45
|
+
* the gateway rebuilds from the same URL, and the request fails to authenticate
|
|
46
|
+
* with nothing in the error naming the sort.
|
|
47
|
+
*
|
|
48
|
+
* Every caller in this repo passes lowercase ASCII parameter names today, which
|
|
49
|
+
* is why this survived; `openapi_request` takes a URL from the model, so mixed
|
|
50
|
+
* case reaches this path from outside.
|
|
51
|
+
*
|
|
52
|
+
* @param query - the query parameters.
|
|
53
|
+
* @returns the canonical string, `k=v&k=v`, with both sides URL-encoded.
|
|
54
|
+
*/
|
|
55
|
+
export declare function canonicalQuery(query: Record<string, string>): string;
|
|
24
56
|
/**
|
|
25
57
|
* Sign an HTTP request with SDK-HMAC-SHA256.
|
|
26
58
|
*
|
|
@@ -1 +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,
|
|
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;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAKpE;AAED;;;;;;;;;;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,CA+Bf"}
|
package/lib/types/signer.js
CHANGED
|
@@ -37,6 +37,45 @@ function sdkTimestamp() {
|
|
|
37
37
|
// milliseconds but keep the trailing Z — do NOT append another one.
|
|
38
38
|
return new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Code-unit comparison — the order the signing specification requires.
|
|
42
|
+
*
|
|
43
|
+
* Exported because it is the part of signing that is checkable without a
|
|
44
|
+
* signature: everything downstream of it is a hash, so a test that compares two
|
|
45
|
+
* signatures cannot tell a correct ordering from a consistently wrong one. See
|
|
46
|
+
* `tests/format.spec.ts`.
|
|
47
|
+
*
|
|
48
|
+
* @param a - one string.
|
|
49
|
+
* @param b - the other.
|
|
50
|
+
* @returns negative, zero or positive in code-unit order.
|
|
51
|
+
*/
|
|
52
|
+
export function compareCodeUnits(a, b) {
|
|
53
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build the canonical query string, sorted by parameter name.
|
|
57
|
+
*
|
|
58
|
+
* ## Why byte order and not `localeCompare`
|
|
59
|
+
*
|
|
60
|
+
* The specification sorts by code point. Locale-aware collation disagrees with
|
|
61
|
+
* it on mixed case: `Zebra` sorts *before* `apple` by code unit and *after* it
|
|
62
|
+
* under the default locale. The signed string would then differ from the one
|
|
63
|
+
* the gateway rebuilds from the same URL, and the request fails to authenticate
|
|
64
|
+
* with nothing in the error naming the sort.
|
|
65
|
+
*
|
|
66
|
+
* Every caller in this repo passes lowercase ASCII parameter names today, which
|
|
67
|
+
* is why this survived; `openapi_request` takes a URL from the model, so mixed
|
|
68
|
+
* case reaches this path from outside.
|
|
69
|
+
*
|
|
70
|
+
* @param query - the query parameters.
|
|
71
|
+
* @returns the canonical string, `k=v&k=v`, with both sides URL-encoded.
|
|
72
|
+
*/
|
|
73
|
+
export function canonicalQuery(query) {
|
|
74
|
+
return Object.entries(query)
|
|
75
|
+
.sort(([a], [b]) => compareCodeUnits(a, b))
|
|
76
|
+
.map(([k, v]) => `${urlEncode(k)}=${urlEncode(v)}`)
|
|
77
|
+
.join('&');
|
|
78
|
+
}
|
|
40
79
|
/**
|
|
41
80
|
* Sign an HTTP request with SDK-HMAC-SHA256.
|
|
42
81
|
*
|
|
@@ -50,11 +89,7 @@ function sdkTimestamp() {
|
|
|
50
89
|
*/
|
|
51
90
|
export function sign(method, host, path, query, body, credentials) {
|
|
52
91
|
const ts = sdkTimestamp();
|
|
53
|
-
|
|
54
|
-
const cqs = Object.entries(query)
|
|
55
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
56
|
-
.map(([k, v]) => `${urlEncode(k)}=${urlEncode(v)}`)
|
|
57
|
-
.join('&');
|
|
92
|
+
const cqs = canonicalQuery(query);
|
|
58
93
|
// Canonical URI: path segments URL-encoded, joined with /, trailing /
|
|
59
94
|
const curi = '/' + path.split('/').filter(Boolean).map(urlEncode).join('/') + '/';
|
|
60
95
|
// Signed headers
|
package/lib/types/signer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAA;AAiBhC,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC;AAED,SAAS,UAAU,CAAC,GAAW,EAAE,IAAY;IAC3C,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACpE,CAAC;AAED,oEAAoE;AACpE,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;IAC3F,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,oEAAoE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IACxG,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAE,CAAA;QAC5B,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;YACb,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACvC,CAAC;aAAM,CAAC;YACN,GAAG,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,qEAAqE;AACrE,SAAS,YAAY;IACnB,yEAAyE;IACzE,oEAAoE;IACpE,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;AAC3E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"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"}
|
package/lib/types/verify.d.ts
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Huawei Cloud IAM credential verification — one implementation, shared.
|
|
3
3
|
*
|
|
4
|
-
* ## Why
|
|
4
|
+
* ## Why one implementation
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
6
|
+
* Several packages need to answer "are these credentials valid?": the
|
|
7
|
+
* credential form before it saves, and any tool about to sign with them. Each
|
|
8
|
+
* answer has to agree with every other, or a credential passes one check and
|
|
9
|
+
* fails the next with a message that points at the wrong thing. One
|
|
10
|
+
* implementation, shaped for storage rather than for the form, gives every
|
|
11
|
+
* caller the same verdict in the same shape.
|
|
16
12
|
*
|
|
17
13
|
* ## Why `/v3/projects` is the endpoint
|
|
18
14
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;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;AAmDD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAuEvB"}
|
package/lib/types/verify.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Huawei Cloud IAM credential verification — one implementation, shared.
|
|
3
3
|
*
|
|
4
|
-
* ## Why
|
|
4
|
+
* ## Why one implementation
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
6
|
+
* Several packages need to answer "are these credentials valid?": the
|
|
7
|
+
* credential form before it saves, and any tool about to sign with them. Each
|
|
8
|
+
* answer has to agree with every other, or a credential passes one check and
|
|
9
|
+
* fails the next with a message that points at the wrong thing. One
|
|
10
|
+
* implementation, shaped for storage rather than for the form, gives every
|
|
11
|
+
* caller the same verdict in the same shape.
|
|
16
12
|
*
|
|
17
13
|
* ## Why `/v3/projects` is the endpoint
|
|
18
14
|
*
|
|
@@ -50,6 +46,10 @@
|
|
|
50
46
|
import { sign } from "./signer.js";
|
|
51
47
|
/** Default per-request timeout; IAM answers in well under a second when healthy. */
|
|
52
48
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
49
|
+
/** Narrow a possibly-absent value for `filter`. */
|
|
50
|
+
function defined(value) {
|
|
51
|
+
return value !== undefined;
|
|
52
|
+
}
|
|
53
53
|
/** IAM hostname for a region (regional replica of a global service). */
|
|
54
54
|
function iamUrl(region) {
|
|
55
55
|
const host = /^\d+\.\d+\.\d+\.\d+$/.test(region) || region === 'global'
|
|
@@ -134,6 +134,22 @@ export async function verifyCredentials(credentials, options) {
|
|
|
134
134
|
if (accountId === undefined) {
|
|
135
135
|
return { ok: false, status, message: 'Huawei Cloud IAM returned no domain_id, so the account is unknown.' };
|
|
136
136
|
}
|
|
137
|
+
// Every project carries the domain it belongs to, and they must agree: one
|
|
138
|
+
// Set of credentials is one account. A body mixing two would mean an
|
|
139
|
+
// aggregating proxy or a partial failover answered, and taking `first` then
|
|
140
|
+
// would silently report an account chosen by response order. The projects
|
|
141
|
+
// would also be unusable — `project_id` values from two accounts cannot be
|
|
142
|
+
// mixed in one deployment — so this is a failure, not a warning.
|
|
143
|
+
const domains = new Set(parsedProjects.map(project => project.domainId).filter(defined));
|
|
144
|
+
if (domains.size > 1) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
status,
|
|
148
|
+
message: 'Huawei Cloud IAM returned projects belonging to more than one account '
|
|
149
|
+
+ `(${[...domains].join(', ')}). These credentials cannot be used together; `
|
|
150
|
+
+ 'sign in with an account-scoped AK/SK.',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
137
153
|
const name = await readAccountName(base, accountId, credentials, options) ?? accountId;
|
|
138
154
|
return {
|
|
139
155
|
ok: true,
|
package/lib/types/verify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"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"}
|
package/lib/verify.d.ts
CHANGED
package/lib/verify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","names":[],"sources":["../../src/verify.ts"],"sourcesContent":null,"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"verify.d.ts","names":[],"sources":["../../src/verify.ts"],"sourcesContent":null,"mappings":";;;;;;UAiDiB,eAAA;;;;;;;;UASA,eAAA;;;;;;;;;;;;8BAYa;;;UAIb,QAAA;;oBAEG;;;;;;;UAQH,YAAA;;;;;;;;KASL,YAAA,GAAe,WAAW;;UAGrB,aAAA;;;;8BAIa;;;;;;;;;;;;;;;iBAiER,iBAAA,cACP,0BACJ,gBACR,QAAQ"}
|
package/lib/verify.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { sign } from "./signer-
|
|
1
|
+
import { sign } from "./signer-CVOjpi6y.js";
|
|
2
2
|
|
|
3
3
|
//#region src/verify.ts
|
|
4
4
|
/** Default per-request timeout; IAM answers in well under a second when healthy. */
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 15e3;
|
|
6
|
+
/** Narrow a possibly-absent value for `filter`. */
|
|
7
|
+
function defined(value) {
|
|
8
|
+
return value !== void 0;
|
|
9
|
+
}
|
|
6
10
|
/** IAM hostname for a region (regional replica of a global service). */
|
|
7
11
|
function iamUrl(region) {
|
|
8
12
|
const host = /^\d+\.\d+\.\d+\.\d+$/.test(region) || region === "global" ? "iam.myhuaweicloud.com" : `iam.${region}.myhuaweicloud.com`;
|
|
@@ -83,6 +87,12 @@ async function verifyCredentials(credentials, options) {
|
|
|
83
87
|
status,
|
|
84
88
|
message: "Huawei Cloud IAM returned no domain_id, so the account is unknown."
|
|
85
89
|
};
|
|
90
|
+
const domains = new Set(parsedProjects.map((project) => project.domainId).filter(defined));
|
|
91
|
+
if (domains.size > 1) return {
|
|
92
|
+
ok: false,
|
|
93
|
+
status,
|
|
94
|
+
message: `Huawei Cloud IAM returned projects belonging to more than one account (${[...domains].join(", ")}). These credentials cannot be used together; sign in with an account-scoped AK/SK.`
|
|
95
|
+
};
|
|
86
96
|
const name = await readAccountName(base, accountId, credentials, options) ?? accountId;
|
|
87
97
|
return {
|
|
88
98
|
ok: true,
|
package/lib/verify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","names":["region: string","base: string","path: string","credentials: SignCredentials","options: VerifyOptions","status: number","body: string","parsed: unknown","projects: Array<VerifiedProject & { readonly domainId?: string }>","accountId: string"],"sources":["../src/verify.ts"],"sourcesContent":["/**\n * Huawei Cloud IAM credential verification — one implementation, shared.\n *\n * ## Why it moved here\n *\n * Two packages verified credentials independently: `aishell-credentials`' Host\n * route, with `GET /v3/auth/domains` (it only wanted the account name), and\n * the copied ops tool `auth`, with `GET /v3/projects` (it also wanted the\n * account id and the project list). Same question — \"are these credentials\n * valid?\" — two endpoints, two error-message vocabularies, two chances to\n * disagree about what a 403 means.\n *\n * This module is the one answer. It is storage-adjacent rather than\n * credential-form-specific, so it lives with the store and the signer, and\n * every caller gets the same verdict in the same shape.\n *\n * ## Why `/v3/projects` is the endpoint\n *\n * It is a superset of the domains call:\n *\n * - A 200 proves the AK/SK (and STS token) work, signed exactly the way every\n * other call is.\n * - Each project carries `domain_id`, which **is** the account id (Huawei\n * Cloud's \"domain\" and \"account\" are the same object).\n * - Each project's `name` is a region name, so a caller that needs a\n * `project_id` for a region-scoped API has it without a second request.\n *\n * The account *name* is the one thing projects does not carry, so it is read\n * from `/v3/auth/domains` **best-effort** — some AK/SK scopes do not grant it,\n * and a missing name is diagnostic-only. A failure there never fails\n * verification.\n *\n * ## Region semantics, which surprise people\n *\n * IAM is a global service. The region in the URL only routes to a regional\n * replica; it does not scope the result. So a wrong region still answers 200\n * with the same account, and the real mismatch surfaces at the first\n * region-scoped call. Verification therefore validates the credentials and\n * **not** the region — conflating them would report \"invalid credentials\" for\n * a typo'd region, sending the person to check the wrong thing.\n *\n * ## Result shape\n *\n * A discriminated union rather than a throw: a rejected credential is an\n * expected outcome of asking someone for credentials, not an exception. The\n * caller decides whether to show, log or retry it.\n *\n * @module @huaweidevtools/aishell-dsh-credentials-store/verify\n */\n\nimport { sign, type SignCredentials } from './signer.ts'\n\n/** One project visible to the credentials; its `name` is a region name. */\nexport interface VerifiedProject {\n /** The `project_id` a region-scoped API takes. */\n readonly id: string\n /** The project name — the region name, e.g. `cn-north-4`. */\n readonly name: string\n readonly description?: string\n}\n\n/** The account the credentials belong to. */\nexport interface VerifiedAccount {\n /** The account id, equal to IAM's `domain_id`. */\n readonly accountId: string\n /** The account (domain) name; falls back to {@link accountId} when unreadable. */\n readonly name: string\n /**\n * Every project visible to these credentials.\n *\n * More than one project may share a region name (enterprise projects), which\n * is why this is not reduced to a region→project map: picking one would be a\n * deployment decision this module has no basis for.\n */\n readonly projects: readonly VerifiedProject[]\n}\n\n/** A successful verification. */\nexport interface VerifyOk {\n readonly ok: true\n readonly account: VerifiedAccount\n /** The region the call was routed through; see the module note — not verified. */\n readonly region: string\n /** Whether the credentials were temporary (STS). */\n readonly sts: boolean\n}\n\n/** A failed verification. */\nexport interface VerifyFailed {\n readonly ok: false\n /** HTTP status from IAM, or `0` for a network/signing failure. */\n readonly status: number\n /** A sentence the person or model can act on. */\n readonly message: string\n}\n\n/** The outcome of one verification. */\nexport type VerifyResult = VerifyOk | VerifyFailed\n\n/** Options for {@link verifyCredentials}. */\nexport interface VerifyOptions {\n /** Region used to route the call. Does not scope the result; see the module note. */\n readonly region: string\n /** Fetch implementation; defaults to the global. Injected so tests need no network. */\n readonly fetchImpl?: typeof fetch\n /** Per-request timeout in milliseconds; defaults to 15 s. */\n readonly timeoutMs?: number\n}\n\n/** Default per-request timeout; IAM answers in well under a second when healthy. */\nconst DEFAULT_TIMEOUT_MS = 15_000\n\n/** IAM hostname for a region (regional replica of a global service). */\nfunction iamUrl(region: string): string {\n const host = /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(region) || region === 'global'\n ? 'iam.myhuaweicloud.com'\n : `iam.${region}.myhuaweicloud.com`\n return `https://${host}`\n}\n\n/**\n * Sign and GET one IAM path, returning the status and body.\n *\n * @param base - IAM origin.\n * @param path - the request path.\n * @param credentials - the pair to sign with.\n * @param options - fetch implementation and timeout.\n */\nasync function signedGet(\n base: string,\n path: string,\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<{ status: number, body: string }> {\n const fetchImpl = options.fetchImpl ?? fetch\n const url = new URL(`${base}${path}`)\n const headers = sign('GET', url.host, url.pathname, {}, '', credentials)\n const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n const response = await fetchImpl(url, {\n method: 'GET',\n headers: {\n host: headers.host,\n 'x-sdk-date': headers['x-sdk-date'],\n Authorization: headers.Authorization,\n ...(headers['x-security-token'] === undefined\n ? {}\n : { 'x-security-token': headers['x-security-token'] }),\n },\n signal,\n })\n return { status: response.status, body: await response.text() }\n}\n\n/**\n * Verify a credential pair against Huawei Cloud IAM.\n *\n * The pair is signed exactly as every other request is, so a 200 proves the\n * same thing a successful tool call would prove — no separate verification\n * scheme that could drift from the one that actually runs.\n *\n * @param credentials - the AK/SK (and optional STS token) to verify.\n * @param options - region and transport details.\n * @returns the account on success, or a failure naming what IAM said.\n */\nexport async function verifyCredentials(\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<VerifyResult> {\n const base = iamUrl(options.region)\n\n let status: number\n let body: string\n try {\n ({ status, body } = await signedGet(base, '/v3/projects', credentials, options))\n } catch (error) {\n return {\n ok: false,\n status: 0,\n message: `Could not reach Huawei Cloud IAM: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n\n if (status !== 200) {\n return { ok: false, status, message: describeIamFailure(status, body) }\n }\n\n const parsedProjects = parseProjects(body)\n if (parsedProjects === undefined) {\n return { ok: false, status, message: 'Huawei Cloud IAM answered with a body that is not JSON.' }\n }\n\n const first = parsedProjects[0]\n if (first === undefined) {\n // Authenticated but sees no projects. IAM is global, so this is not a\n // region problem: the AK/SK lacks IAM read scope, or belongs to a\n // sub-account whose parent has not granted project membership. Terraform\n // needs a project, so this is a failure rather than an empty success.\n return {\n ok: false,\n status,\n message:\n 'The credentials are valid but no project is visible to them. Confirm the AK/SK may '\n + 'read IAM, or ask the main account to grant project membership. '\n + '(This is unrelated to the region — IAM is a global service.)',\n }\n }\n\n const accountId = first.domainId\n if (accountId === undefined) {\n return { ok: false, status, message: 'Huawei Cloud IAM returned no domain_id, so the account is unknown.' }\n }\n\n const name = await readAccountName(base, accountId, credentials, options) ?? accountId\n\n return {\n ok: true,\n account: { accountId, name, projects: parsedProjects },\n region: options.region,\n sts: credentials.securityToken !== undefined,\n }\n}\n\n/**\n * Parse `/v3/projects` into the project list.\n *\n * @returns the projects (possibly empty), or `undefined` when the body is not\n * JSON at all — a different failure from \"valid JSON, no projects\".\n */\nfunction parseProjects(body: string): Array<VerifiedProject & { readonly domainId?: string }> | undefined {\n let parsed: unknown\n try {\n parsed = JSON.parse(body)\n } catch {\n return undefined\n }\n const raw = (parsed as { projects?: unknown } | null)?.projects\n if (!Array.isArray(raw)) return []\n\n const projects: Array<VerifiedProject & { readonly domainId?: string }> = []\n for (const entry of raw) {\n const project = entry as Record<string, unknown> | null\n if (project === null || typeof project !== 'object') continue\n if (typeof project.id !== 'string' || project.id === '') continue\n projects.push({\n id: project.id,\n name: typeof project.name === 'string' ? project.name : '',\n ...(typeof project.description === 'string' && project.description !== ''\n ? { description: project.description }\n : {}),\n // Kept beside the public fields because the caller derives the account id\n // from it without a second pass over the raw body.\n ...(typeof project.domain_id === 'string' && project.domain_id !== ''\n ? { domainId: project.domain_id }\n : {}),\n })\n }\n return projects\n}\n\n/**\n * Read the account (domain) name, best-effort.\n *\n * @returns the name, or `undefined` — never an error. Some AK/SK scopes do not\n * grant `/v3/auth/domains`, and the account name is diagnostic only.\n */\nasync function readAccountName(\n base: string,\n accountId: string,\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<string | undefined> {\n let status: number\n let body: string\n try {\n ({ status, body } = await signedGet(base, '/v3/auth/domains', credentials, options))\n } catch {\n return undefined\n }\n if (status !== 200) return undefined\n try {\n const parsed = JSON.parse(body) as { domains?: Array<{ id?: unknown, name?: unknown }> }\n const match = parsed.domains?.find(domain => domain.id === accountId)\n return typeof match?.name === 'string' && match.name !== '' ? match.name : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Render an IAM rejection into one actionable sentence.\n *\n * @param status - the HTTP status.\n * @param body - the response body, which may carry Huawei's own error text.\n */\nfunction describeIamFailure(status: number, body: string): string {\n try {\n const parsed = JSON.parse(body) as { error_msg?: unknown, error_code?: unknown, message?: unknown }\n const detail = typeof parsed.error_msg === 'string'\n ? parsed.error_msg\n : typeof parsed.message === 'string' ? parsed.message : undefined\n if (detail !== undefined) {\n const code = typeof parsed.error_code === 'string' ? `, ${parsed.error_code}` : ''\n return `Huawei Cloud rejected the credentials (HTTP ${status}${code}): ${detail}`\n }\n } catch {\n // Non-JSON body — an ingress page or a proxy error; nothing to read.\n }\n if (status === 401) {\n return 'Huawei Cloud rejected the credentials (HTTP 401): the Access Key, Secret Key, '\n + 'or Security Token was refused.'\n }\n if (status === 403) {\n return 'The credentials are valid but may not read IAM (HTTP 403). '\n + 'Confirm the Access Key has IAM read permission.'\n }\n return `Huawei Cloud IAM failed with HTTP ${status}: ${body.slice(0, 200)}`\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"verify.js","names":["value: T | undefined","region: string","base: string","path: string","credentials: SignCredentials","options: VerifyOptions","status: number","body: string","parsed: unknown","projects: Array<VerifiedProject & { readonly domainId?: string }>","accountId: string"],"sources":["../src/verify.ts"],"sourcesContent":["/**\n * Huawei Cloud IAM credential verification — one implementation, shared.\n *\n * ## Why one implementation\n *\n * Several packages need to answer \"are these credentials valid?\": the\n * credential form before it saves, and any tool about to sign with them. Each\n * answer has to agree with every other, or a credential passes one check and\n * fails the next with a message that points at the wrong thing. One\n * implementation, shaped for storage rather than for the form, gives every\n * caller the same verdict in the same shape.\n *\n * ## Why `/v3/projects` is the endpoint\n *\n * It is a superset of the domains call:\n *\n * - A 200 proves the AK/SK (and STS token) work, signed exactly the way every\n * other call is.\n * - Each project carries `domain_id`, which **is** the account id (Huawei\n * Cloud's \"domain\" and \"account\" are the same object).\n * - Each project's `name` is a region name, so a caller that needs a\n * `project_id` for a region-scoped API has it without a second request.\n *\n * The account *name* is the one thing projects does not carry, so it is read\n * from `/v3/auth/domains` **best-effort** — some AK/SK scopes do not grant it,\n * and a missing name is diagnostic-only. A failure there never fails\n * verification.\n *\n * ## Region semantics, which surprise people\n *\n * IAM is a global service. The region in the URL only routes to a regional\n * replica; it does not scope the result. So a wrong region still answers 200\n * with the same account, and the real mismatch surfaces at the first\n * region-scoped call. Verification therefore validates the credentials and\n * **not** the region — conflating them would report \"invalid credentials\" for\n * a typo'd region, sending the person to check the wrong thing.\n *\n * ## Result shape\n *\n * A discriminated union rather than a throw: a rejected credential is an\n * expected outcome of asking someone for credentials, not an exception. The\n * caller decides whether to show, log or retry it.\n *\n * @module @huaweidevtools/aishell-dsh-credentials-store/verify\n */\n\nimport { sign, type SignCredentials } from './signer.ts'\n\n/** One project visible to the credentials; its `name` is a region name. */\nexport interface VerifiedProject {\n /** The `project_id` a region-scoped API takes. */\n readonly id: string\n /** The project name — the region name, e.g. `cn-north-4`. */\n readonly name: string\n readonly description?: string\n}\n\n/** The account the credentials belong to. */\nexport interface VerifiedAccount {\n /** The account id, equal to IAM's `domain_id`. */\n readonly accountId: string\n /** The account (domain) name; falls back to {@link accountId} when unreadable. */\n readonly name: string\n /**\n * Every project visible to these credentials.\n *\n * More than one project may share a region name (enterprise projects), which\n * is why this is not reduced to a region→project map: picking one would be a\n * deployment decision this module has no basis for.\n */\n readonly projects: readonly VerifiedProject[]\n}\n\n/** A successful verification. */\nexport interface VerifyOk {\n readonly ok: true\n readonly account: VerifiedAccount\n /** The region the call was routed through; see the module note — not verified. */\n readonly region: string\n /** Whether the credentials were temporary (STS). */\n readonly sts: boolean\n}\n\n/** A failed verification. */\nexport interface VerifyFailed {\n readonly ok: false\n /** HTTP status from IAM, or `0` for a network/signing failure. */\n readonly status: number\n /** A sentence the person or model can act on. */\n readonly message: string\n}\n\n/** The outcome of one verification. */\nexport type VerifyResult = VerifyOk | VerifyFailed\n\n/** Options for {@link verifyCredentials}. */\nexport interface VerifyOptions {\n /** Region used to route the call. Does not scope the result; see the module note. */\n readonly region: string\n /** Fetch implementation; defaults to the global. Injected so tests need no network. */\n readonly fetchImpl?: typeof fetch\n /** Per-request timeout in milliseconds; defaults to 15 s. */\n readonly timeoutMs?: number\n}\n\n/** Default per-request timeout; IAM answers in well under a second when healthy. */\nconst DEFAULT_TIMEOUT_MS = 15_000\n\n/** Narrow a possibly-absent value for `filter`. */\nfunction defined<T>(value: T | undefined): value is T {\n return value !== undefined\n}\n\n/** IAM hostname for a region (regional replica of a global service). */\nfunction iamUrl(region: string): string {\n const host = /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(region) || region === 'global'\n ? 'iam.myhuaweicloud.com'\n : `iam.${region}.myhuaweicloud.com`\n return `https://${host}`\n}\n\n/**\n * Sign and GET one IAM path, returning the status and body.\n *\n * @param base - IAM origin.\n * @param path - the request path.\n * @param credentials - the pair to sign with.\n * @param options - fetch implementation and timeout.\n */\nasync function signedGet(\n base: string,\n path: string,\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<{ status: number, body: string }> {\n const fetchImpl = options.fetchImpl ?? fetch\n const url = new URL(`${base}${path}`)\n const headers = sign('GET', url.host, url.pathname, {}, '', credentials)\n const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n const response = await fetchImpl(url, {\n method: 'GET',\n headers: {\n host: headers.host,\n 'x-sdk-date': headers['x-sdk-date'],\n Authorization: headers.Authorization,\n ...(headers['x-security-token'] === undefined\n ? {}\n : { 'x-security-token': headers['x-security-token'] }),\n },\n signal,\n })\n return { status: response.status, body: await response.text() }\n}\n\n/**\n * Verify a credential pair against Huawei Cloud IAM.\n *\n * The pair is signed exactly as every other request is, so a 200 proves the\n * same thing a successful tool call would prove — no separate verification\n * scheme that could drift from the one that actually runs.\n *\n * @param credentials - the AK/SK (and optional STS token) to verify.\n * @param options - region and transport details.\n * @returns the account on success, or a failure naming what IAM said.\n */\nexport async function verifyCredentials(\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<VerifyResult> {\n const base = iamUrl(options.region)\n\n let status: number\n let body: string\n try {\n ({ status, body } = await signedGet(base, '/v3/projects', credentials, options))\n } catch (error) {\n return {\n ok: false,\n status: 0,\n message: `Could not reach Huawei Cloud IAM: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n\n if (status !== 200) {\n return { ok: false, status, message: describeIamFailure(status, body) }\n }\n\n const parsedProjects = parseProjects(body)\n if (parsedProjects === undefined) {\n return { ok: false, status, message: 'Huawei Cloud IAM answered with a body that is not JSON.' }\n }\n\n const first = parsedProjects[0]\n if (first === undefined) {\n // Authenticated but sees no projects. IAM is global, so this is not a\n // region problem: the AK/SK lacks IAM read scope, or belongs to a\n // sub-account whose parent has not granted project membership. Terraform\n // needs a project, so this is a failure rather than an empty success.\n return {\n ok: false,\n status,\n message:\n 'The credentials are valid but no project is visible to them. Confirm the AK/SK may '\n + 'read IAM, or ask the main account to grant project membership. '\n + '(This is unrelated to the region — IAM is a global service.)',\n }\n }\n\n const accountId = first.domainId\n if (accountId === undefined) {\n return { ok: false, status, message: 'Huawei Cloud IAM returned no domain_id, so the account is unknown.' }\n }\n\n // Every project carries the domain it belongs to, and they must agree: one\n // Set of credentials is one account. A body mixing two would mean an\n // aggregating proxy or a partial failover answered, and taking `first` then\n // would silently report an account chosen by response order. The projects\n // would also be unusable — `project_id` values from two accounts cannot be\n // mixed in one deployment — so this is a failure, not a warning.\n const domains = new Set(parsedProjects.map(project => project.domainId).filter(defined))\n if (domains.size > 1) {\n return {\n ok: false,\n status,\n message:\n 'Huawei Cloud IAM returned projects belonging to more than one account '\n + `(${[...domains].join(', ')}). These credentials cannot be used together; `\n + 'sign in with an account-scoped AK/SK.',\n }\n }\n\n const name = await readAccountName(base, accountId, credentials, options) ?? accountId\n\n return {\n ok: true,\n account: { accountId, name, projects: parsedProjects },\n region: options.region,\n sts: credentials.securityToken !== undefined,\n }\n}\n\n/**\n * Parse `/v3/projects` into the project list.\n *\n * @returns the projects (possibly empty), or `undefined` when the body is not\n * JSON at all — a different failure from \"valid JSON, no projects\".\n */\nfunction parseProjects(body: string): Array<VerifiedProject & { readonly domainId?: string }> | undefined {\n let parsed: unknown\n try {\n parsed = JSON.parse(body)\n } catch {\n return undefined\n }\n const raw = (parsed as { projects?: unknown } | null)?.projects\n if (!Array.isArray(raw)) return []\n\n const projects: Array<VerifiedProject & { readonly domainId?: string }> = []\n for (const entry of raw) {\n const project = entry as Record<string, unknown> | null\n if (project === null || typeof project !== 'object') continue\n if (typeof project.id !== 'string' || project.id === '') continue\n projects.push({\n id: project.id,\n name: typeof project.name === 'string' ? project.name : '',\n ...(typeof project.description === 'string' && project.description !== ''\n ? { description: project.description }\n : {}),\n // Kept beside the public fields because the caller derives the account id\n // from it without a second pass over the raw body.\n ...(typeof project.domain_id === 'string' && project.domain_id !== ''\n ? { domainId: project.domain_id }\n : {}),\n })\n }\n return projects\n}\n\n/**\n * Read the account (domain) name, best-effort.\n *\n * @returns the name, or `undefined` — never an error. Some AK/SK scopes do not\n * grant `/v3/auth/domains`, and the account name is diagnostic only.\n */\nasync function readAccountName(\n base: string,\n accountId: string,\n credentials: SignCredentials,\n options: VerifyOptions,\n): Promise<string | undefined> {\n let status: number\n let body: string\n try {\n ({ status, body } = await signedGet(base, '/v3/auth/domains', credentials, options))\n } catch {\n return undefined\n }\n if (status !== 200) return undefined\n try {\n const parsed = JSON.parse(body) as { domains?: Array<{ id?: unknown, name?: unknown }> }\n const match = parsed.domains?.find(domain => domain.id === accountId)\n return typeof match?.name === 'string' && match.name !== '' ? match.name : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Render an IAM rejection into one actionable sentence.\n *\n * @param status - the HTTP status.\n * @param body - the response body, which may carry Huawei's own error text.\n */\nfunction describeIamFailure(status: number, body: string): string {\n try {\n const parsed = JSON.parse(body) as { error_msg?: unknown, error_code?: unknown, message?: unknown }\n const detail = typeof parsed.error_msg === 'string'\n ? parsed.error_msg\n : typeof parsed.message === 'string' ? parsed.message : undefined\n if (detail !== undefined) {\n const code = typeof parsed.error_code === 'string' ? `, ${parsed.error_code}` : ''\n return `Huawei Cloud rejected the credentials (HTTP ${status}${code}): ${detail}`\n }\n } catch {\n // Non-JSON body — an ingress page or a proxy error; nothing to read.\n }\n if (status === 401) {\n return 'Huawei Cloud rejected the credentials (HTTP 401): the Access Key, Secret Key, '\n + 'or Security Token was refused.'\n }\n if (status === 403) {\n return 'The credentials are valid but may not read IAM (HTTP 403). '\n + 'Confirm the Access Key has IAM read permission.'\n }\n return `Huawei Cloud IAM failed with HTTP ${status}: ${body.slice(0, 200)}`\n}"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paths-DtFRzop6.js","names":[],"sources":["../src/paths.ts"],"sourcesContent":["/**\n * Where per-host state lives. One definition, shared.\n *\n * ## Why this lives in the store package\n *\n * `DSH_HOME` is the host's data directory, and two things in this repo derive\n * from it: the encrypted credentials file here, and the terraform cache in\n * `aishell-deploy`. Defining it twice would let the two drift — the same\n * failure shape the credential-scope constant was created to end.\n *\n * The direction is forced by the dependency graph: `aishell-deploy` already\n * depends on this package, so a definition here is reachable from there, while\n * the reverse would be a cycle. This module is the lower of the two, so it is\n * where the definition belongs.\n *\n * @module @huaweidevtools/aishell-dsh-credentials-store/paths\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\n/**\n * The fallback data directory when the host sets no `DSH_HOME`.\n *\n * `~/.aishell` — the same directory the desktop app uses, so a library or\n * headless run shares one location with a real install rather than inventing a\n * second home directory.\n */\nexport const FALLBACK_HOME_NAME = '.aishell'\n\n/**\n * The host's data directory.\n *\n * `DSH_HOME` when the host set it — the desktop app and the CLI both do, so\n * this lands where the rest of that host's data lives — otherwise\n * `~/.aishell`.\n *\n * Read per call, never captured: a host that sets `DSH_HOME` after this module\n * is first imported must still be honoured.\n *\n * @returns the base directory; not created here.\n */\nexport function dshHome(): string {\n const configured = process.env.DSH_HOME\n if (configured !== undefined && configured.trim() !== '') return configured\n return join(homedir(), FALLBACK_HOME_NAME)\n}\n\n/**\n * The directory holding the encrypted credentials file.\n *\n * `<DSH_HOME>/credentials`. Builds are separated by the *file name* (the\n * credential scope — `plugins.credentials.enc` by default), not by a second\n * directory, so two builds sit side by side here without reading each other's\n * secrets.\n *\n * @returns the directory; not created here.\n */\nexport function credentialsDir(): string {\n return join(dshHome(), 'credentials')\n}"],"mappings":";;;;;;;;;;;AA4BA,MAAa,qBAAqB;;;;;;;;;;;;;AAclC,SAAgB,UAAkB;CAChC,MAAM,aAAa,QAAQ,IAAI;AAC/B,KAAI,yBAA4B,WAAW,MAAM,KAAK,GAAI,QAAO;AACjE,QAAO,KAAK,SAAS,EAAE,mBAAmB;AAC3C;;;;;;;;;;;AAYD,SAAgB,iBAAyB;AACvC,QAAO,KAAK,SAAS,EAAE,cAAc;AACtC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scope-QmrHZaZt.js","names":[],"sources":["../src/scope.ts"],"sourcesContent":["/**\n * Which credential namespace this build uses.\n *\n * ## The knob\n *\n * A build-time constant, not a runtime setting:\n *\n * ```sh\n * AISHELL_CREDENTIAL_SCOPE=staging pnpm run build\n * ```\n *\n * Default `plugins`. It decides two things, and it decides both at once —\n * which is the point:\n *\n * | Used as | Default |\n * |---|---|\n * | keyring service name | `aishell-dsh-plugins` |\n * | encrypted file name | `plugins.credentials.enc` |\n *\n * ## Why it is not a plugin config option\n *\n * It was one, briefly: `keyringService` sat on four plugins' `Config`. Only one\n * of the four writes and all four read, so a deployment that set it on a reader\n * but not the writer stored credentials in one namespace and looked for them in\n * another — the form said \"saved\" while every tool said \"not authenticated\".\n * A knob whose only observable effect is that failure is not worth having.\n *\n * A constant cannot be set on one side only. Two deployments that must not see\n * each other's credentials (a staging profile beside a production one) differ\n * by *building* differently, which is the honest place for a persistence\n * boundary.\n *\n * ## Why there is no migration\n *\n * The default changed from `aishell-dsh` to `aishell-dsh-plugins` before the\n * first release, so no install ever wrote the old namespace. Nothing reads it,\n * and adding a migration path would mean keeping the old name alive forever for\n * a state that never existed.\n *\n * @module @huaweidevtools/aishell-dsh-credentials-store/scope\n */\n\n/**\n * The default scope.\n *\n * Named for the repository, not for a feature: the credential store is shared\n * by every plugin in this set, so it is not `llm`'s or `e2b`'s.\n */\nexport const DEFAULT_SCOPE = 'plugins'\n\n/**\n * Injected at build time; see `tsdown.config.ts`.\n *\n * Declared as possibly-absent because a source run (the test suite, which\n * resolves `src/` directly) never passes through the bundler. The `typeof`\n * guard below handles that without a `ReferenceError`.\n */\ndeclare const __AISHELL_CREDENTIAL_SCOPE__: string | undefined\n\n/**\n * Accepted scope shape: a lowercase identifier.\n *\n * The value reaches a keyring service name and a file name, so it is validated\n * as a strict identifier rather than merely \"not obviously a path\": no\n * separators, no dots, no leading dash. Assuming a value is benign because the\n * caller is trusted is how path traversal gets in.\n */\nconst SCOPE_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/\n\n/**\n * Resolve the scope for this build.\n *\n * Fails loudly on a malformed value rather than falling back to the default:\n * a typo would otherwise silently share the namespace with every unset build,\n * which is exactly the confusion scoping exists to avoid.\n *\n * @returns the validated scope.\n * @throws when the injected value is not a valid scope.\n */\nfunction resolveScope(): string {\n // `typeof` rather than a direct read: an undeclared identifier throws, and a\n // source run legitimately has no injected value.\n const injected = typeof __AISHELL_CREDENTIAL_SCOPE__ === 'string'\n ? __AISHELL_CREDENTIAL_SCOPE__\n : undefined\n if (injected === undefined || injected === '') return DEFAULT_SCOPE\n const normalized = injected.trim().toLowerCase()\n if (normalized.length === 0) return DEFAULT_SCOPE\n if (!SCOPE_PATTERN.test(normalized)) {\n throw new Error(\n `AISHELL_CREDENTIAL_SCOPE=${JSON.stringify(injected)} is not a valid credential scope: `\n + `expected 1-64 characters of [a-z0-9_-], starting with a letter or digit `\n + '(for example \"plugins\" or \"staging\").',\n )\n }\n return normalized\n}\n\n/** The scope this build uses. */\nexport const CREDENTIAL_SCOPE = resolveScope()\n\n/**\n * The keyring service every entry of this build lives under.\n *\n * Part of the persistence contract: renaming it orphans every stored entry\n * while looking like a harmless constant change.\n */\nexport const KEYRING_SERVICE = `aishell-dsh-${CREDENTIAL_SCOPE}`\n\n/**\n * The encrypted file name for this build.\n *\n * Scoped the same way as the keyring service, so two builds on one machine\n * cannot collide in either backend.\n */\nexport const CREDENTIALS_FILE_NAME = `${CREDENTIAL_SCOPE}.credentials.enc`"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,MAAa,gBAAgB;;;;;;;;;AAmB7B,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,eAAuB;CAG9B,MAAM;AAGN,KAAI,uBAA0B,aAAa,GAAI,QAAO;CACtD,MAAM,aAAa,SAAS,MAAM,CAAC,aAAa;AAChD,KAAI,WAAW,WAAW,EAAG,QAAO;AACpC,MAAK,cAAc,KAAK,WAAW,CACjC,OAAM,IAAI,OACP,2BAA2B,KAAK,UAAU,SAAS,CAAC;AAKzD,QAAO;AACR;;AAGD,MAAa,mBAAmB,cAAc;;;;;;;AAQ9C,MAAa,mBAAmB,cAAc,iBAAiB;;;;;;;AAQ/D,MAAa,yBAAyB,EAAE,iBAAiB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signer-BPR0UAF0.js","names":["data: string","key: string","str: string","c: number","method: string","host: string","path: string","query: Record<string, string>","body: string","credentials: SignCredentials","headers: SignedHeaders"],"sources":["../src/signer.ts"],"sourcesContent":["/**\n * Huawei Cloud SDK-HMAC-SHA256 request signing.\n *\n * Signs an HTTP request with AK/SK (and an optional temporary SecurityToken)\n * using the same algorithm as the official Huawei Cloud SDK. Produces the\n * `Authorization`, `x-sdk-date`, and `x-security-token` headers to attach\n * to a `fetch` call.\n *\n * @module @huaweidevtools/aishell-dsh-credentials-store/signer\n */\n\nimport crypto from 'node:crypto'\n\n/** Credentials for signing. */\nexport interface SignCredentials {\n readonly ak: string\n readonly sk: string\n readonly securityToken?: string\n}\n\n/** The headers produced by {@link sign}. */\nexport interface SignedHeaders {\n readonly host: string\n readonly 'x-sdk-date': string\n readonly Authorization: string\n readonly 'x-security-token'?: string\n}\n\nfunction sha256Hex(data: string): string {\n return crypto.createHash('sha256').update(data).digest('hex')\n}\n\nfunction hmacSha256(key: string, data: string): string {\n return crypto.createHmac('sha256', key).update(data).digest('hex')\n}\n\n/** RFC 3986 URL-encode a string (unreserved chars stay literal). */\nfunction urlEncode(str: string): string {\n const hex = (c: number): string => '%' + (c < 16 ? '0' : '') + c.toString(16).toUpperCase()\n const noEscape = new Set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~'.split(''))\n let out = ''\n for (const ch of str) {\n const c = ch.codePointAt(0)!\n if (c < 0x80) {\n out += noEscape.has(ch) ? ch : hex(c)\n } else {\n out += encodeURIComponent(ch)\n }\n }\n return out\n}\n\n/** ISO 8601 timestamp in Huawei Cloud format: `20260912T064552Z`. */\nfunction sdkTimestamp(): string {\n // toISOString() returns \"2026-09-12T06:45:52.123Z\"; strip separators and\n // milliseconds but keep the trailing Z — do NOT append another one.\n return new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d+/, '')\n}\n\n/**\n * Sign an HTTP request with SDK-HMAC-SHA256.\n *\n * @param method - HTTP method (GET, POST, ...).\n * @param host - Host component of the request URL (e.g. `openai.aishell.openbuildlab.com`).\n * @param path - Path component of the request URL (e.g. `/chat/completions`).\n * @param query - Query parameters as a key-value map.\n * @param body - Request body (stringified for hashing; empty string for GET).\n * @param credentials - AK/SK and optional SecurityToken.\n * @returns The headers to attach to the fetch call.\n */\nexport function sign(\n method: string,\n host: string,\n path: string,\n query: Record<string, string>,\n body: string,\n credentials: SignCredentials,\n): SignedHeaders {\n const ts = sdkTimestamp()\n\n // Canonical query string (sorted keys)\n const cqs = Object.entries(query)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${urlEncode(k)}=${urlEncode(v)}`)\n .join('&')\n\n // Canonical URI: path segments URL-encoded, joined with /, trailing /\n const curi = '/' + path.split('/').filter(Boolean).map(urlEncode).join('/') + '/'\n\n // Signed headers\n const hasToken = credentials.securityToken !== undefined\n const signedHeaders = hasToken ? 'host;x-sdk-date;x-security-token' : 'host;x-sdk-date'\n const canonicalHeaders = hasToken\n ? `host:${host}\\nx-sdk-date:${ts}\\nx-security-token:${credentials.securityToken}\\n`\n : `host:${host}\\nx-sdk-date:${ts}\\n`\n\n // Payload hash\n const payloadHash = sha256Hex(body)\n\n // Canonical request\n const canonicalRequest = [method, curi, cqs, canonicalHeaders, signedHeaders, payloadHash].join('\\n')\n\n // String to sign\n const stringToSign = `SDK-HMAC-SHA256\\n${ts}\\n${sha256Hex(canonicalRequest)}`\n const signature = hmacSha256(credentials.sk, stringToSign)\n\n const headers: SignedHeaders = {\n host,\n 'x-sdk-date': ts,\n Authorization: `SDK-HMAC-SHA256 Access=${credentials.ak}, SignedHeaders=${signedHeaders}, Signature=${signature}`,\n ...(hasToken ? { 'x-security-token': credentials.securityToken } : {}),\n }\n return headers\n}\n"],"mappings":";;;AA4BA,SAAS,UAAUA,MAAsB;AACvC,QAAO,OAAO,WAAW,SAAS,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM;AAC9D;AAED,SAAS,WAAWC,KAAaD,MAAsB;AACrD,QAAO,OAAO,WAAW,UAAU,IAAI,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM;AACnE;;AAGD,SAAS,UAAUE,KAAqB;CACtC,MAAM,MAAM,CAACC,MAAsB,OAAO,IAAI,KAAK,MAAM,MAAM,EAAE,SAAS,GAAG,CAAC,aAAa;CAC3F,MAAM,WAAW,IAAI,IAAI,qEAAqE,MAAM,GAAG;CACvG,IAAI,MAAM;AACV,MAAK,MAAM,MAAM,KAAK;EACpB,MAAM,IAAI,GAAG,YAAY,EAAE;AAC3B,MAAI,IAAI,IACN,QAAO,SAAS,IAAI,GAAG,GAAG,KAAK,IAAI,EAAE;MAErC,QAAO,mBAAmB,GAAG;CAEhC;AACD,QAAO;AACR;;AAGD,SAAS,eAAuB;AAG9B,QAAO,IAAI,OAAO,aAAa,CAAC,QAAQ,SAAS,GAAG,CAAC,QAAQ,SAAS,GAAG;AAC1E;;;;;;;;;;;;AAaD,SAAgB,KACdC,QACAC,MACAC,MACAC,OACAC,MACAC,aACe;CACf,MAAM,KAAK,cAAc;CAGzB,MAAM,MAAM,OAAO,QAAQ,MAAM,CAC9B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CACtC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,CAClD,KAAK,IAAI;CAGZ,MAAM,OAAO,MAAM,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,IAAI,UAAU,CAAC,KAAK,IAAI,GAAG;CAG9E,MAAM,WAAW,YAAY;CAC7B,MAAM,gBAAgB,WAAW,qCAAqC;CACtE,MAAM,mBAAmB,YACpB,OAAO,KAAK,eAAe,GAAG,qBAAqB,YAAY,cAAc,OAC7E,OAAO,KAAK,eAAe,GAAG;CAGnC,MAAM,cAAc,UAAU,KAAK;CAGnC,MAAM,mBAAmB;EAAC;EAAQ;EAAM;EAAK;EAAkB;EAAe;CAAY,EAAC,KAAK,KAAK;CAGrG,MAAM,gBAAgB,mBAAmB,GAAG,IAAI,UAAU,iBAAiB,CAAC;CAC5E,MAAM,YAAY,WAAW,YAAY,IAAI,aAAa;CAE1D,MAAMC,UAAyB;EAC7B;EACA,cAAc;EACd,gBAAgB,yBAAyB,YAAY,GAAG,kBAAkB,cAAc,cAAc,UAAU;EAChH,GAAI,WAAW,EAAE,oBAAoB,YAAY,cAAe,IAAG,CAAE;CACtE;AACD,QAAO;AACR"}
|
package/lib/signer-Et0DIH6o.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
//#region src/signer.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Huawei Cloud SDK-HMAC-SHA256 request signing.
|
|
4
|
-
*
|
|
5
|
-
* Signs an HTTP request with AK/SK (and an optional temporary SecurityToken)
|
|
6
|
-
* using the same algorithm as the official Huawei Cloud SDK. Produces the
|
|
7
|
-
* `Authorization`, `x-sdk-date`, and `x-security-token` headers to attach
|
|
8
|
-
* to a `fetch` call.
|
|
9
|
-
*
|
|
10
|
-
* @module @huaweidevtools/aishell-dsh-credentials-store/signer
|
|
11
|
-
*/
|
|
12
|
-
/** Credentials for signing. */
|
|
13
|
-
interface SignCredentials {
|
|
14
|
-
readonly ak: string;
|
|
15
|
-
readonly sk: string;
|
|
16
|
-
readonly securityToken?: string;
|
|
17
|
-
}
|
|
18
|
-
/** The headers produced by {@link sign}. */
|
|
19
|
-
interface SignedHeaders {
|
|
20
|
-
readonly host: string;
|
|
21
|
-
readonly 'x-sdk-date': string;
|
|
22
|
-
readonly Authorization: string;
|
|
23
|
-
readonly 'x-security-token'?: string;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Sign an HTTP request with SDK-HMAC-SHA256.
|
|
27
|
-
*
|
|
28
|
-
* @param method - HTTP method (GET, POST, ...).
|
|
29
|
-
* @param host - Host component of the request URL (e.g. `openai.aishell.openbuildlab.com`).
|
|
30
|
-
* @param path - Path component of the request URL (e.g. `/chat/completions`).
|
|
31
|
-
* @param query - Query parameters as a key-value map.
|
|
32
|
-
* @param body - Request body (stringified for hashing; empty string for GET).
|
|
33
|
-
* @param credentials - AK/SK and optional SecurityToken.
|
|
34
|
-
* @returns The headers to attach to the fetch call.
|
|
35
|
-
*/
|
|
36
|
-
declare function sign(method: string, host: string, path: string, query: Record<string, string>, body: string, credentials: SignCredentials): SignedHeaders; //#endregion
|
|
37
|
-
|
|
38
|
-
//# sourceMappingURL=signer.d.ts.map
|
|
39
|
-
export { SignCredentials, SignedHeaders, sign as sign$1 };
|
|
40
|
-
//# sourceMappingURL=signer-Et0DIH6o.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signer-Et0DIH6o.d.ts","names":[],"sources":["../../src/signer.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;UAciB,eAAA;EAAA,SAAA,EAAA,EAAA,MAAe;;EAOf,SAAA,aAAa,CAAA,EAAA,MAAA;;;UAAb,aAAA;;;;;;;;;AAiDjB;;;;;AAOgB;;;iBAPA,IAAA,oDAIP,mDAEM,kBACZ"}
|