@acosmi/sdk-ts 2.18.0 → 2.19.1
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/CHANGELOG.md +28 -0
- package/README.md +30 -2
- package/dist/browser/index.mjs +1228 -171
- package/dist/browser/index.mjs.map +1 -1
- package/dist/index.mjs +1228 -171
- package/dist/index.mjs.map +1 -1
- package/dist/node/adapters/anthropic.cjs.map +1 -1
- package/dist/node/adapters/anthropic.mjs.map +1 -1
- package/dist/node/adapters/openai.cjs +110 -45
- package/dist/node/adapters/openai.cjs.map +1 -1
- package/dist/node/adapters/openai.d.cts +1 -1
- package/dist/node/adapters/openai.d.ts +1 -1
- package/dist/node/adapters/openai.mjs +110 -45
- package/dist/node/adapters/openai.mjs.map +1 -1
- package/dist/node/index.cjs +1230 -170
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +376 -153
- package/dist/node/index.d.ts +376 -153
- package/dist/node/index.mjs +1228 -171
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/{openai-Cyvi8g6B.d.ts → openai-BbCQOMNY.d.ts} +28 -1
- package/dist/node/{openai-DR_KhEdM.d.cts → openai-CMrvATF_.d.cts} +28 -1
- package/package.json +1 -1
package/dist/node/index.cjs
CHANGED
|
@@ -149,6 +149,55 @@ var init_types = __esm({
|
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
// src/shared/errors.ts
|
|
152
|
+
function nullableID(value) {
|
|
153
|
+
return value === null || typeof value === "string";
|
|
154
|
+
}
|
|
155
|
+
function decodeGatewayErrorContract(value) {
|
|
156
|
+
if (value == null || typeof value !== "object") return null;
|
|
157
|
+
const v = value;
|
|
158
|
+
const version = v.errorContractVersion;
|
|
159
|
+
const domain = v.faultDomain;
|
|
160
|
+
const disposition = v.requestDisposition;
|
|
161
|
+
const errorCode = v.errorCode;
|
|
162
|
+
if (version !== 1 || typeof domain !== "string" || !gatewayFaultDomains.includes(domain)) return null;
|
|
163
|
+
if (typeof errorCode !== "string" || errorCode.length === 0) return null;
|
|
164
|
+
if (disposition !== "not_accepted" && disposition !== "accepted" && disposition !== "unknown") return null;
|
|
165
|
+
if (!nullableID(v.transportRequestId) || !nullableID(v.consumeRequestId) || !nullableID(v.providerRequestId)) return null;
|
|
166
|
+
if (typeof v.retryable !== "boolean") return null;
|
|
167
|
+
return {
|
|
168
|
+
errorContractVersion: 1,
|
|
169
|
+
faultDomain: domain,
|
|
170
|
+
errorCode,
|
|
171
|
+
transportRequestId: v.transportRequestId,
|
|
172
|
+
consumeRequestId: v.consumeRequestId,
|
|
173
|
+
providerRequestId: v.providerRequestId,
|
|
174
|
+
requestDisposition: disposition,
|
|
175
|
+
retryable: v.retryable
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function readGatewayErrorContract(error) {
|
|
179
|
+
const direct = decodeGatewayErrorContract(error);
|
|
180
|
+
if (direct) return direct;
|
|
181
|
+
if (error == null || typeof error !== "object") return null;
|
|
182
|
+
const e = error;
|
|
183
|
+
const axios = decodeGatewayErrorContract(e.response?.data);
|
|
184
|
+
if (axios) return axios;
|
|
185
|
+
if (typeof e.body === "string") {
|
|
186
|
+
try {
|
|
187
|
+
return decodeGatewayErrorContract(JSON.parse(e.body));
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
function isUserAccessTokenRejected(error) {
|
|
195
|
+
if (error == null || typeof error !== "object") return false;
|
|
196
|
+
const e = error;
|
|
197
|
+
const status = e.statusCode ?? e.response?.status;
|
|
198
|
+
const contract = readGatewayErrorContract(error);
|
|
199
|
+
return status === 401 && contract?.faultDomain === "user_auth" && contract.errorCode === "USER_ACCESS_TOKEN_INVALID" && contract.requestDisposition === "not_accepted";
|
|
200
|
+
}
|
|
152
201
|
function isWindowLimitError(err) {
|
|
153
202
|
if (!(err instanceof exports.HTTPError)) return false;
|
|
154
203
|
if (err.errorCode === windowLimitErrorCode) return true;
|
|
@@ -176,7 +225,7 @@ function getWindowLimitStreamDetails(err) {
|
|
|
176
225
|
windowOverridable: typeof e.windowOverridable === "boolean" ? e.windowOverridable : void 0
|
|
177
226
|
};
|
|
178
227
|
}
|
|
179
|
-
exports.RateLimitError = void 0; exports.BusinessError = void 0; exports.OrderTerminalError = void 0; exports.ModelNotFoundError = void 0; exports.HTTPError = void 0; exports.NetworkError = void 0; exports.StreamError = void 0; var windowLimitErrorCode, windowLimitStreamCode;
|
|
228
|
+
exports.RateLimitError = void 0; exports.BusinessError = void 0; exports.OrderTerminalError = void 0; exports.ModelNotFoundError = void 0; exports.HTTPError = void 0; exports.NetworkError = void 0; exports.StreamError = void 0; var gatewayFaultDomains, windowLimitErrorCode, windowLimitStreamCode;
|
|
180
229
|
var init_errors = __esm({
|
|
181
230
|
"src/shared/errors.ts"() {
|
|
182
231
|
exports.RateLimitError = class extends Error {
|
|
@@ -235,6 +284,13 @@ var init_errors = __esm({
|
|
|
235
284
|
* false/缺失 = 周窗/显式禁止/灰度关 (硬等待, 无豁免路径, 老网关不返回时为 undefined)。
|
|
236
285
|
*/
|
|
237
286
|
windowOverridable;
|
|
287
|
+
errorContractVersion;
|
|
288
|
+
faultDomain;
|
|
289
|
+
requestDisposition;
|
|
290
|
+
transportRequestId;
|
|
291
|
+
consumeRequestId;
|
|
292
|
+
providerRequestId;
|
|
293
|
+
retryable;
|
|
238
294
|
constructor(statusCode, opts = {}) {
|
|
239
295
|
let msg;
|
|
240
296
|
if (opts.type) {
|
|
@@ -256,6 +312,13 @@ var init_errors = __esm({
|
|
|
256
312
|
this.windowKind = opts.windowKind;
|
|
257
313
|
this.windowResetAt = opts.windowResetAt;
|
|
258
314
|
this.windowOverridable = opts.windowOverridable;
|
|
315
|
+
this.errorContractVersion = opts.errorContractVersion;
|
|
316
|
+
this.faultDomain = opts.faultDomain;
|
|
317
|
+
this.requestDisposition = opts.requestDisposition;
|
|
318
|
+
this.transportRequestId = opts.transportRequestId;
|
|
319
|
+
this.consumeRequestId = opts.consumeRequestId;
|
|
320
|
+
this.providerRequestId = opts.providerRequestId;
|
|
321
|
+
this.retryable = opts.retryable === true && opts.requestDisposition === "not_accepted";
|
|
259
322
|
}
|
|
260
323
|
};
|
|
261
324
|
exports.NetworkError = class extends Error {
|
|
@@ -266,6 +329,7 @@ var init_errors = __esm({
|
|
|
266
329
|
cause;
|
|
267
330
|
timeout;
|
|
268
331
|
eof;
|
|
332
|
+
requestDisposition = "unknown";
|
|
269
333
|
constructor(op, url, cause, opts = {}) {
|
|
270
334
|
const causeMsg = cause instanceof Error ? cause.message : cause != null ? String(cause) : "network error";
|
|
271
335
|
super(`${op} ${url}: ${causeMsg}`);
|
|
@@ -286,6 +350,8 @@ var init_errors = __esm({
|
|
|
286
350
|
exports.StreamError = class extends Error {
|
|
287
351
|
/** 例: "empty_response" / "rate_limit" / "overloaded" / "" */
|
|
288
352
|
code;
|
|
353
|
+
/** D23 gateway machine code; mirrors the legacy `code` field. */
|
|
354
|
+
errorCode;
|
|
289
355
|
/** 例: "provider" / "settlement" */
|
|
290
356
|
stage;
|
|
291
357
|
/** 用户友好提示 (中文); 历史字段, 与 rawError 区分 */
|
|
@@ -294,23 +360,47 @@ var init_errors = __esm({
|
|
|
294
360
|
rawError;
|
|
295
361
|
/** 客户端是否值得重试 */
|
|
296
362
|
retryable;
|
|
363
|
+
errorContractVersion;
|
|
364
|
+
faultDomain;
|
|
365
|
+
requestDisposition;
|
|
366
|
+
transportRequestId;
|
|
367
|
+
consumeRequestId;
|
|
368
|
+
providerRequestId;
|
|
297
369
|
constructor(opts = {}) {
|
|
298
370
|
const code = opts.code ?? "";
|
|
299
371
|
const stage = opts.stage ?? "";
|
|
300
372
|
const userMessage = opts.message ?? "";
|
|
301
373
|
const rawError = opts.rawError ?? "";
|
|
302
|
-
const retryable = opts.retryable
|
|
374
|
+
const retryable = opts.retryable === true && opts.requestDisposition === "not_accepted";
|
|
303
375
|
const body = rawError !== "" ? rawError : userMessage;
|
|
304
376
|
const msg = stage !== "" ? `stream failed: ${stage}: ${body}` : `stream failed: ${body}`;
|
|
305
377
|
super(msg);
|
|
306
378
|
this.name = "StreamError";
|
|
307
379
|
this.code = code;
|
|
380
|
+
this.errorCode = code;
|
|
308
381
|
this.stage = stage;
|
|
309
382
|
this.userMessage = userMessage;
|
|
310
383
|
this.rawError = rawError;
|
|
311
384
|
this.retryable = retryable;
|
|
385
|
+
this.errorContractVersion = opts.errorContractVersion;
|
|
386
|
+
this.faultDomain = opts.faultDomain;
|
|
387
|
+
this.requestDisposition = opts.requestDisposition;
|
|
388
|
+
this.transportRequestId = opts.transportRequestId;
|
|
389
|
+
this.consumeRequestId = opts.consumeRequestId;
|
|
390
|
+
this.providerRequestId = opts.providerRequestId;
|
|
312
391
|
}
|
|
313
392
|
};
|
|
393
|
+
gatewayFaultDomains = [
|
|
394
|
+
"user_auth",
|
|
395
|
+
"caller_credentials",
|
|
396
|
+
"account_quota",
|
|
397
|
+
"account_permission",
|
|
398
|
+
"provider",
|
|
399
|
+
"gateway",
|
|
400
|
+
"transport",
|
|
401
|
+
"stream_ticket",
|
|
402
|
+
"protocol"
|
|
403
|
+
];
|
|
314
404
|
windowLimitErrorCode = "WINDOW_LIMIT_EXCEEDED";
|
|
315
405
|
windowLimitStreamCode = "window_limit_exceeded";
|
|
316
406
|
}
|
|
@@ -893,16 +983,90 @@ var init_openai = __esm({
|
|
|
893
983
|
* 可能已被 text/tool 推进的 this.blockIndex (否则 content_block_stop 索引错配)。 */
|
|
894
984
|
thinkingBlockIndex = 0;
|
|
895
985
|
textStarted = false;
|
|
896
|
-
/** OpenAI tool_call
|
|
986
|
+
/** OpenAI tool_call 键 → Anthropic block index。键正常是 `tc.index`;上游省略
|
|
987
|
+
* index 时退化为 `id:<tool_call_id>`,两者都没有时沿用上一个键(见
|
|
988
|
+
* {@link resolveToolKey})。 */
|
|
897
989
|
toolBlockIndex = /* @__PURE__ */ new Map();
|
|
898
990
|
blockIndex = 0;
|
|
991
|
+
/** 每个 tool block 已发出的 `partial_json` 累积,用于识别「每片重发全量参数」
|
|
992
|
+
* 的上游(见 tool_calls 分支的累计判别)。 */
|
|
993
|
+
toolArgsAccum = /* @__PURE__ */ new Map();
|
|
994
|
+
/** 上一次解析出的 tool 键,供缺 index 且缺 id 的后续增量沿用。 */
|
|
995
|
+
lastToolKey = null;
|
|
996
|
+
/** 已发出 message_delta/message_stop,避免 finish_reason 与 `[DONE]` 各收一次。 */
|
|
997
|
+
messageClosed = false;
|
|
998
|
+
/**
|
|
999
|
+
* 解析一条 tool_call delta 归属的块键。
|
|
1000
|
+
*
|
|
1001
|
+
* OpenAI 流式规范里 `index` 是必填,但兼容实现常有省略。此前这里直接用
|
|
1002
|
+
* `tc.index` 做 Map 键:两个都省略 index 的 tool_call 会共用键 `undefined`,
|
|
1003
|
+
* 于是只开一个块、两段参数拼进同一条 `partial_json` 流,产出 `{…}{…}` 这种
|
|
1004
|
+
* 必然非法的 JSON。这里按「index → id → 沿用上一个」三级降级,让至少一种
|
|
1005
|
+
* 稳定标识生效。
|
|
1006
|
+
*/
|
|
1007
|
+
resolveToolKey(tc) {
|
|
1008
|
+
if (typeof tc.index === "number" && Number.isFinite(tc.index)) {
|
|
1009
|
+
this.lastToolKey = tc.index;
|
|
1010
|
+
return tc.index;
|
|
1011
|
+
}
|
|
1012
|
+
if (typeof tc.id === "string" && tc.id !== "") {
|
|
1013
|
+
const key = `id:${tc.id}`;
|
|
1014
|
+
this.lastToolKey = key;
|
|
1015
|
+
return key;
|
|
1016
|
+
}
|
|
1017
|
+
if (this.lastToolKey !== null) return this.lastToolKey;
|
|
1018
|
+
this.lastToolKey = 0;
|
|
1019
|
+
return 0;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* 关闭仍打开的 text / thinking / tool 块,并收口 message。
|
|
1023
|
+
*
|
|
1024
|
+
* 由 `finish_reason` 分支与 `[DONE]` 分支共用:上游断流或只发 `[DONE]` 而不发
|
|
1025
|
+
* `finish_reason` 时,此前一个 `content_block_stop` 都不会发,下游拿到的是一个
|
|
1026
|
+
* 永不闭合的 tool_use 块。
|
|
1027
|
+
*/
|
|
1028
|
+
closeOpenBlocks(events, stopReason) {
|
|
1029
|
+
if (this.messageClosed) return;
|
|
1030
|
+
this.messageClosed = true;
|
|
1031
|
+
if (this.textStarted) {
|
|
1032
|
+
events.push({
|
|
1033
|
+
event: "content_block_stop",
|
|
1034
|
+
data: JSON.stringify({ type: "content_block_stop", index: this.blockIndex })
|
|
1035
|
+
});
|
|
1036
|
+
this.textStarted = false;
|
|
1037
|
+
} else if (this.thinkingStarted && !this.thinkingStopped) {
|
|
1038
|
+
this.thinkingStopped = true;
|
|
1039
|
+
events.push({
|
|
1040
|
+
event: "content_block_stop",
|
|
1041
|
+
data: JSON.stringify({ type: "content_block_stop", index: this.thinkingBlockIndex })
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
for (const idx of this.toolBlockIndex.values()) {
|
|
1045
|
+
events.push({
|
|
1046
|
+
event: "content_block_stop",
|
|
1047
|
+
data: JSON.stringify({ type: "content_block_stop", index: idx })
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
events.push({
|
|
1051
|
+
event: "message_delta",
|
|
1052
|
+
data: JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason } })
|
|
1053
|
+
});
|
|
1054
|
+
events.push({
|
|
1055
|
+
event: "message_stop",
|
|
1056
|
+
data: JSON.stringify({ type: "message_stop" })
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
899
1059
|
/**
|
|
900
1060
|
* 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
|
|
901
1061
|
* 返回 { events, done }
|
|
902
1062
|
*/
|
|
903
1063
|
convert(data) {
|
|
904
1064
|
if (data === "[DONE]") {
|
|
905
|
-
|
|
1065
|
+
const events2 = [];
|
|
1066
|
+
if (this.messageStarted) {
|
|
1067
|
+
this.closeOpenBlocks(events2, "end_turn");
|
|
1068
|
+
}
|
|
1069
|
+
return { events: events2, done: true };
|
|
906
1070
|
}
|
|
907
1071
|
let chunk;
|
|
908
1072
|
try {
|
|
@@ -974,7 +1138,9 @@ var init_openai = __esm({
|
|
|
974
1138
|
events.push({ event: "content_block_delta", data: deltaJSON });
|
|
975
1139
|
}
|
|
976
1140
|
for (const tc of choice.delta.tool_calls ?? []) {
|
|
977
|
-
|
|
1141
|
+
const fn = tc.function;
|
|
1142
|
+
const toolKey = this.resolveToolKey(tc);
|
|
1143
|
+
if (!this.toolBlockIndex.has(toolKey)) {
|
|
978
1144
|
if (this.thinkingStarted && !this.thinkingStopped) {
|
|
979
1145
|
this.thinkingStopped = true;
|
|
980
1146
|
const stopJSON = JSON.stringify({
|
|
@@ -993,56 +1159,46 @@ var init_openai = __esm({
|
|
|
993
1159
|
this.blockIndex++;
|
|
994
1160
|
this.textStarted = false;
|
|
995
1161
|
}
|
|
996
|
-
this.toolBlockIndex.set(
|
|
1162
|
+
this.toolBlockIndex.set(toolKey, this.blockIndex);
|
|
997
1163
|
const blockJSON = JSON.stringify({
|
|
998
1164
|
type: "content_block_start",
|
|
999
1165
|
index: this.blockIndex,
|
|
1000
1166
|
content_block: {
|
|
1001
1167
|
type: "tool_use",
|
|
1002
1168
|
id: tc.id,
|
|
1003
|
-
name:
|
|
1169
|
+
name: fn?.name,
|
|
1004
1170
|
input: {}
|
|
1005
1171
|
}
|
|
1006
1172
|
});
|
|
1007
1173
|
events.push({ event: "content_block_start", data: blockJSON });
|
|
1008
1174
|
this.blockIndex++;
|
|
1009
1175
|
}
|
|
1010
|
-
if (
|
|
1011
|
-
const
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1176
|
+
if (fn?.arguments !== void 0 && fn.arguments !== null && fn.arguments !== "") {
|
|
1177
|
+
const rawArgs = typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments);
|
|
1178
|
+
const accum = this.toolArgsAccum.get(toolKey) ?? "";
|
|
1179
|
+
let emit = rawArgs;
|
|
1180
|
+
if (accum !== "" && rawArgs.length > accum.length && rawArgs.startsWith(accum)) {
|
|
1181
|
+
emit = rawArgs.slice(accum.length);
|
|
1182
|
+
this.toolArgsAccum.set(toolKey, rawArgs);
|
|
1183
|
+
} else {
|
|
1184
|
+
this.toolArgsAccum.set(toolKey, accum + rawArgs);
|
|
1185
|
+
}
|
|
1186
|
+
if (emit !== "") {
|
|
1187
|
+
const idx = this.toolBlockIndex.get(toolKey);
|
|
1188
|
+
const deltaJSON = JSON.stringify({
|
|
1189
|
+
type: "content_block_delta",
|
|
1190
|
+
index: idx,
|
|
1191
|
+
delta: {
|
|
1192
|
+
type: "input_json_delta",
|
|
1193
|
+
partial_json: emit
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
events.push({ event: "content_block_delta", data: deltaJSON });
|
|
1197
|
+
}
|
|
1021
1198
|
}
|
|
1022
1199
|
}
|
|
1023
1200
|
if (choice.finish_reason != null && choice.finish_reason !== "") {
|
|
1024
|
-
|
|
1025
|
-
const stopJSON2 = JSON.stringify({
|
|
1026
|
-
type: "content_block_stop",
|
|
1027
|
-
index: this.blockIndex
|
|
1028
|
-
});
|
|
1029
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
1030
|
-
} else if (this.thinkingStarted && !this.thinkingStopped) {
|
|
1031
|
-
this.thinkingStopped = true;
|
|
1032
|
-
const stopJSON2 = JSON.stringify({
|
|
1033
|
-
type: "content_block_stop",
|
|
1034
|
-
index: this.thinkingBlockIndex
|
|
1035
|
-
});
|
|
1036
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
1037
|
-
}
|
|
1038
|
-
for (const idx of this.toolBlockIndex.values()) {
|
|
1039
|
-
const stopJSON2 = JSON.stringify({
|
|
1040
|
-
type: "content_block_stop",
|
|
1041
|
-
index: idx
|
|
1042
|
-
});
|
|
1043
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
1044
|
-
}
|
|
1045
|
-
let stopReason = "end_turn";
|
|
1201
|
+
let stopReason;
|
|
1046
1202
|
switch (choice.finish_reason) {
|
|
1047
1203
|
case "tool_calls":
|
|
1048
1204
|
stopReason = "tool_use";
|
|
@@ -1050,14 +1206,13 @@ var init_openai = __esm({
|
|
|
1050
1206
|
case "length":
|
|
1051
1207
|
stopReason = "max_tokens";
|
|
1052
1208
|
break;
|
|
1209
|
+
case "stop":
|
|
1210
|
+
stopReason = "end_turn";
|
|
1211
|
+
break;
|
|
1212
|
+
default:
|
|
1213
|
+
stopReason = choice.finish_reason;
|
|
1053
1214
|
}
|
|
1054
|
-
|
|
1055
|
-
type: "message_delta",
|
|
1056
|
-
delta: { stop_reason: stopReason }
|
|
1057
|
-
});
|
|
1058
|
-
events.push({ event: "message_delta", data: deltaJSON });
|
|
1059
|
-
const stopJSON = JSON.stringify({ type: "message_stop" });
|
|
1060
|
-
events.push({ event: "message_stop", data: stopJSON });
|
|
1215
|
+
this.closeOpenBlocks(events, stopReason);
|
|
1061
1216
|
}
|
|
1062
1217
|
return { events, done: false };
|
|
1063
1218
|
}
|
|
@@ -1118,6 +1273,560 @@ var init_adapters = __esm({
|
|
|
1118
1273
|
}
|
|
1119
1274
|
});
|
|
1120
1275
|
|
|
1276
|
+
// src/core/credentials.ts
|
|
1277
|
+
var uuid = () => globalThis.crypto.randomUUID();
|
|
1278
|
+
var pause = () => new Promise((resolve) => setTimeout(resolve, 25));
|
|
1279
|
+
var abort = (signal) => {
|
|
1280
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
1281
|
+
};
|
|
1282
|
+
var sameRequestOwner = (snapshot, expected) => snapshot.storeInstanceId === expected.storeInstanceId && snapshot.authSessionId === expected.authSessionId && snapshot.principal?.issuer === expected.principal?.issuer && snapshot.principal?.subject === expected.principal?.subject && snapshot.principal?.organizationId === expected.principal?.organizationId;
|
|
1283
|
+
var CredentialLifecycle = class {
|
|
1284
|
+
constructor(store, protocol) {
|
|
1285
|
+
this.store = store;
|
|
1286
|
+
this.protocol = protocol;
|
|
1287
|
+
}
|
|
1288
|
+
store;
|
|
1289
|
+
protocol;
|
|
1290
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1291
|
+
notificationKey = "";
|
|
1292
|
+
observations = /* @__PURE__ */ new Map();
|
|
1293
|
+
read(signal) {
|
|
1294
|
+
return this.store.readSnapshot(signal);
|
|
1295
|
+
}
|
|
1296
|
+
subscribe(listener) {
|
|
1297
|
+
this.listeners.add(listener);
|
|
1298
|
+
return () => {
|
|
1299
|
+
this.listeners.delete(listener);
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
async notify(snapshot) {
|
|
1303
|
+
let current;
|
|
1304
|
+
try {
|
|
1305
|
+
current = await this.read();
|
|
1306
|
+
} catch {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (current.storeInstanceId !== snapshot.storeInstanceId || current.revision !== snapshot.revision)
|
|
1310
|
+
return;
|
|
1311
|
+
const projection = {
|
|
1312
|
+
storeInstanceId: snapshot.storeInstanceId,
|
|
1313
|
+
authorityConfig: snapshot.authorityConfig && {
|
|
1314
|
+
serverURL: snapshot.authorityConfig.serverURL,
|
|
1315
|
+
issuer: snapshot.authorityConfig.issuer,
|
|
1316
|
+
oauthProfile: "desktop",
|
|
1317
|
+
authContractVersion: 2,
|
|
1318
|
+
errorContractVersion: 1
|
|
1319
|
+
},
|
|
1320
|
+
revision: snapshot.revision,
|
|
1321
|
+
authSessionId: snapshot.authSessionId,
|
|
1322
|
+
credentialState: snapshot.credentialState,
|
|
1323
|
+
reason: snapshot.reason,
|
|
1324
|
+
principal: snapshot.principal && {
|
|
1325
|
+
issuer: snapshot.principal.issuer,
|
|
1326
|
+
subject: snapshot.principal.subject,
|
|
1327
|
+
organizationId: snapshot.principal.organizationId
|
|
1328
|
+
},
|
|
1329
|
+
refreshOperation: snapshot.refreshOperation && {
|
|
1330
|
+
operationId: snapshot.refreshOperation.operationId,
|
|
1331
|
+
sessionId: snapshot.refreshOperation.sessionId,
|
|
1332
|
+
baseRevision: snapshot.refreshOperation.baseRevision,
|
|
1333
|
+
phase: snapshot.refreshOperation.phase,
|
|
1334
|
+
returnState: snapshot.refreshOperation.returnState,
|
|
1335
|
+
startedAt: snapshot.refreshOperation.startedAt,
|
|
1336
|
+
dispatchedAt: snapshot.refreshOperation.dispatchedAt,
|
|
1337
|
+
deadlineAt: snapshot.refreshOperation.deadlineAt
|
|
1338
|
+
},
|
|
1339
|
+
loginAttempt: snapshot.loginAttempt && {
|
|
1340
|
+
attemptId: snapshot.loginAttempt.attemptId,
|
|
1341
|
+
baseSessionId: snapshot.loginAttempt.baseSessionId,
|
|
1342
|
+
startedAt: snapshot.loginAttempt.startedAt
|
|
1343
|
+
},
|
|
1344
|
+
lastMutation: snapshot.lastMutation && {
|
|
1345
|
+
mutationId: snapshot.lastMutation.mutationId,
|
|
1346
|
+
operationId: snapshot.lastMutation.operationId,
|
|
1347
|
+
resultRevision: snapshot.lastMutation.resultRevision
|
|
1348
|
+
},
|
|
1349
|
+
lastLoginAttemptId: snapshot.lastLoginAttemptId,
|
|
1350
|
+
verifiedIdentity: snapshot.verifiedIdentity && {
|
|
1351
|
+
authSessionId: snapshot.verifiedIdentity.authSessionId,
|
|
1352
|
+
principal: {
|
|
1353
|
+
issuer: snapshot.verifiedIdentity.principal.issuer,
|
|
1354
|
+
subject: snapshot.verifiedIdentity.principal.subject,
|
|
1355
|
+
organizationId: snapshot.verifiedIdentity.principal.organizationId
|
|
1356
|
+
},
|
|
1357
|
+
displayName: snapshot.verifiedIdentity.displayName,
|
|
1358
|
+
avatarUrl: snapshot.verifiedIdentity.avatarUrl,
|
|
1359
|
+
email: snapshot.verifiedIdentity.email,
|
|
1360
|
+
imageUrl: snapshot.verifiedIdentity.imageUrl,
|
|
1361
|
+
accountCreatedAt: snapshot.verifiedIdentity.accountCreatedAt,
|
|
1362
|
+
requiresPhoneBinding: snapshot.verifiedIdentity.requiresPhoneBinding,
|
|
1363
|
+
hasExtraUsageEnabled: snapshot.verifiedIdentity.hasExtraUsageEnabled,
|
|
1364
|
+
billingType: snapshot.verifiedIdentity.billingType,
|
|
1365
|
+
subscriptionCreatedAt: snapshot.verifiedIdentity.subscriptionCreatedAt,
|
|
1366
|
+
rateLimitTier: snapshot.verifiedIdentity.rateLimitTier,
|
|
1367
|
+
organizationName: snapshot.verifiedIdentity.organizationName,
|
|
1368
|
+
verifiedAt: snapshot.verifiedIdentity.verifiedAt
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
const key = JSON.stringify({ ...projection, revision: void 0, lastMutation: void 0 });
|
|
1372
|
+
if (key === this.notificationKey) return;
|
|
1373
|
+
this.notificationKey = key;
|
|
1374
|
+
for (const listener of this.listeners) {
|
|
1375
|
+
try {
|
|
1376
|
+
listener(structuredClone(projection));
|
|
1377
|
+
} catch {
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
async reconcile(signal) {
|
|
1382
|
+
const s = await this.read(signal);
|
|
1383
|
+
await this.notify(s);
|
|
1384
|
+
return s;
|
|
1385
|
+
}
|
|
1386
|
+
cas(s, changes, mutationId = uuid(), signal) {
|
|
1387
|
+
return this.store.compareAndSwap(
|
|
1388
|
+
{
|
|
1389
|
+
storeInstanceId: s.storeInstanceId,
|
|
1390
|
+
revision: s.revision,
|
|
1391
|
+
authSessionId: s.authSessionId,
|
|
1392
|
+
state: s.credentialState,
|
|
1393
|
+
operationId: s.refreshOperation?.operationId ?? s.loginAttempt?.attemptId ?? null
|
|
1394
|
+
},
|
|
1395
|
+
{ ...s, ...changes },
|
|
1396
|
+
mutationId,
|
|
1397
|
+
signal
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
async commit(s, changes, signal) {
|
|
1401
|
+
const r = await this.cas(s, changes, uuid(), signal);
|
|
1402
|
+
if (r.status === "storage_error") throw new Error("storage_unavailable");
|
|
1403
|
+
if (r.status === "committed") await this.notify(r.snapshot);
|
|
1404
|
+
return r;
|
|
1405
|
+
}
|
|
1406
|
+
async metadata(s, signal) {
|
|
1407
|
+
const m = await this.protocol.metadata(signal);
|
|
1408
|
+
if (m.crabcode_auth_contract_version !== 2 || m.gateway_error_contract_version !== 1 || !m.issuer)
|
|
1409
|
+
throw new Error("auth_contract_unsupported");
|
|
1410
|
+
if (s.authorityConfig && (s.authorityConfig.serverURL !== this.protocol.serverURL || s.authorityConfig.issuer !== m.issuer || s.authorityConfig.oauthProfile !== "desktop" || s.authorityConfig.authContractVersion !== 2 || s.authorityConfig.errorContractVersion !== 1))
|
|
1411
|
+
throw new Error("auth_contract_unsupported");
|
|
1412
|
+
return m;
|
|
1413
|
+
}
|
|
1414
|
+
async reserveLogin(signal) {
|
|
1415
|
+
const attemptId = uuid();
|
|
1416
|
+
let reserved;
|
|
1417
|
+
for (; ; ) {
|
|
1418
|
+
abort(signal);
|
|
1419
|
+
const s = await this.read(signal);
|
|
1420
|
+
if (s.credentialState !== "signed_out") throw new Error("local_logout_required");
|
|
1421
|
+
const r = await this.commit(s, { loginAttempt: { attemptId, baseSessionId: s.authSessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString() } }, signal);
|
|
1422
|
+
if (r.status === "committed") {
|
|
1423
|
+
reserved = r.snapshot;
|
|
1424
|
+
break;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
const m = await this.metadata(reserved, signal);
|
|
1429
|
+
for (; ; ) {
|
|
1430
|
+
abort(signal);
|
|
1431
|
+
const current = await this.read(signal);
|
|
1432
|
+
if (current.storeInstanceId !== reserved.storeInstanceId || current.loginAttempt?.attemptId !== attemptId) throw new Error("superseded");
|
|
1433
|
+
if (current.authorityConfig) return { attemptId, metadata: m };
|
|
1434
|
+
const r = await this.commit(current, { authorityConfig: { serverURL: this.protocol.serverURL, issuer: m.issuer, oauthProfile: "desktop", authContractVersion: 2, errorContractVersion: 1 } }, signal);
|
|
1435
|
+
if (r.status === "committed") return { attemptId, metadata: m };
|
|
1436
|
+
}
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
await this.cancelLogin(attemptId);
|
|
1439
|
+
throw error;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
async installLogin(attemptId, tokens, signal) {
|
|
1443
|
+
this.validateTokens(tokens);
|
|
1444
|
+
for (; ; ) {
|
|
1445
|
+
abort(signal);
|
|
1446
|
+
const s = await this.read(signal);
|
|
1447
|
+
if (s.loginAttempt?.attemptId !== attemptId || s.loginAttempt.baseSessionId !== s.authSessionId)
|
|
1448
|
+
throw new Error("superseded");
|
|
1449
|
+
const r = await this.commit(
|
|
1450
|
+
s,
|
|
1451
|
+
{
|
|
1452
|
+
authSessionId: uuid(),
|
|
1453
|
+
tokenSet: tokens,
|
|
1454
|
+
credentialState: "pending_identity",
|
|
1455
|
+
principal: null,
|
|
1456
|
+
verifiedIdentity: null,
|
|
1457
|
+
loginAttempt: null,
|
|
1458
|
+
lastLoginAttemptId: attemptId,
|
|
1459
|
+
reason: "identity_unavailable"
|
|
1460
|
+
},
|
|
1461
|
+
signal
|
|
1462
|
+
);
|
|
1463
|
+
if (r.status === "committed") return this.bindIdentity(r.snapshot.authSessionId, signal);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
async assertLogin(attemptId, signal) {
|
|
1467
|
+
abort(signal);
|
|
1468
|
+
const s = await this.read(signal);
|
|
1469
|
+
if (s.loginAttempt?.attemptId !== attemptId) throw new Error("superseded");
|
|
1470
|
+
}
|
|
1471
|
+
async cancelLogin(attemptId) {
|
|
1472
|
+
const s = await this.read();
|
|
1473
|
+
if (s.loginAttempt?.attemptId === attemptId) await this.commit(s, { loginAttempt: null });
|
|
1474
|
+
}
|
|
1475
|
+
validateTokens(t) {
|
|
1476
|
+
if (!t.access_token?.trim() || !t.refresh_token?.trim() || !t.client_id?.trim() || t.server_url !== this.protocol.serverURL || !Number.isFinite(Date.parse(t.expires_at)) || Date.parse(t.expires_at) <= Date.now())
|
|
1477
|
+
throw new Error("invalid_response");
|
|
1478
|
+
}
|
|
1479
|
+
usable(s) {
|
|
1480
|
+
return !!s.tokenSet && Date.parse(s.tokenSet.expires_at) > Date.now() + 3e5;
|
|
1481
|
+
}
|
|
1482
|
+
async bindIdentity(sessionId, signal) {
|
|
1483
|
+
const s = await this.ensureSnapshot(signal, void 0, true);
|
|
1484
|
+
if (s.authSessionId !== sessionId) throw new Error("superseded");
|
|
1485
|
+
if (s.credentialState === "ready") return s;
|
|
1486
|
+
await this.metadata(s, signal);
|
|
1487
|
+
const beforeProfile = await this.read(signal);
|
|
1488
|
+
if (beforeProfile.storeInstanceId !== s.storeInstanceId || beforeProfile.authSessionId !== sessionId || beforeProfile.revision !== s.revision || beforeProfile.credentialState !== "pending_identity")
|
|
1489
|
+
throw new Error("superseded");
|
|
1490
|
+
let identity;
|
|
1491
|
+
try {
|
|
1492
|
+
identity = await this.protocol.profile(s.tokenSet, signal);
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
const message = error instanceof Error ? error.message : "";
|
|
1495
|
+
if (message === "invalid_scope" || message === "auth_contract_unsupported")
|
|
1496
|
+
await this.commit(s, { credentialState: "configuration_error", reason: message });
|
|
1497
|
+
throw error;
|
|
1498
|
+
}
|
|
1499
|
+
abort(signal);
|
|
1500
|
+
const current = await this.read(signal);
|
|
1501
|
+
if (current.storeInstanceId !== s.storeInstanceId || current.authSessionId !== sessionId || current.revision !== s.revision)
|
|
1502
|
+
throw new Error("superseded");
|
|
1503
|
+
if (!identity.subject) throw new Error("invalid_response");
|
|
1504
|
+
const principal = {
|
|
1505
|
+
issuer: s.authorityConfig.issuer,
|
|
1506
|
+
subject: identity.subject,
|
|
1507
|
+
organizationId: identity.organizationId
|
|
1508
|
+
};
|
|
1509
|
+
const r = await this.commit(
|
|
1510
|
+
s,
|
|
1511
|
+
{
|
|
1512
|
+
principal,
|
|
1513
|
+
verifiedIdentity: {
|
|
1514
|
+
authSessionId: sessionId,
|
|
1515
|
+
principal,
|
|
1516
|
+
displayName: identity.displayName,
|
|
1517
|
+
avatarUrl: identity.avatarUrl,
|
|
1518
|
+
email: identity.email,
|
|
1519
|
+
imageUrl: identity.imageUrl,
|
|
1520
|
+
accountCreatedAt: identity.accountCreatedAt,
|
|
1521
|
+
requiresPhoneBinding: identity.requiresPhoneBinding,
|
|
1522
|
+
hasExtraUsageEnabled: identity.hasExtraUsageEnabled,
|
|
1523
|
+
billingType: identity.billingType,
|
|
1524
|
+
subscriptionCreatedAt: identity.subscriptionCreatedAt,
|
|
1525
|
+
rateLimitTier: identity.rateLimitTier,
|
|
1526
|
+
organizationName: identity.organizationName,
|
|
1527
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1528
|
+
},
|
|
1529
|
+
credentialState: "ready",
|
|
1530
|
+
reason: null
|
|
1531
|
+
},
|
|
1532
|
+
signal
|
|
1533
|
+
);
|
|
1534
|
+
if (r.status !== "committed") throw new Error("superseded");
|
|
1535
|
+
return r.snapshot;
|
|
1536
|
+
}
|
|
1537
|
+
async ensure(signal, rejected, expected) {
|
|
1538
|
+
const snapshot = await this.ensureSnapshot(signal, rejected, false, expected);
|
|
1539
|
+
this.observations.set(snapshot.tokenSet.access_token, snapshot);
|
|
1540
|
+
if (this.observations.size > 128)
|
|
1541
|
+
this.observations.delete(this.observations.keys().next().value);
|
|
1542
|
+
return snapshot.tokenSet.access_token;
|
|
1543
|
+
}
|
|
1544
|
+
async forceRefresh(signal, rejectedToken, expected) {
|
|
1545
|
+
const rejected = rejectedToken ? this.observations.get(rejectedToken) : await this.read(signal);
|
|
1546
|
+
if (rejectedToken && !rejected) throw new Error("credential_observation_unavailable");
|
|
1547
|
+
return this.ensure(signal, rejected, expected);
|
|
1548
|
+
}
|
|
1549
|
+
async ensureSnapshot(signal, rejected, pending = false, expected) {
|
|
1550
|
+
let session;
|
|
1551
|
+
let instance;
|
|
1552
|
+
let initialRevision;
|
|
1553
|
+
let refreshed = false;
|
|
1554
|
+
let initialAccess;
|
|
1555
|
+
for (; ; ) {
|
|
1556
|
+
abort(signal);
|
|
1557
|
+
const s = await this.read(signal);
|
|
1558
|
+
if (expected && !sameRequestOwner(s, expected)) throw new Error("superseded");
|
|
1559
|
+
if (s.authorityConfig && s.authorityConfig.serverURL !== this.protocol.serverURL)
|
|
1560
|
+
throw new Error("auth_contract_unsupported");
|
|
1561
|
+
if (session === void 0) {
|
|
1562
|
+
session = s.authSessionId;
|
|
1563
|
+
instance = s.storeInstanceId;
|
|
1564
|
+
initialRevision = s.revision;
|
|
1565
|
+
initialAccess = s.tokenSet?.access_token;
|
|
1566
|
+
}
|
|
1567
|
+
if (s.storeInstanceId !== instance || s.authSessionId !== session || rejected && (s.authSessionId !== rejected.authSessionId || s.storeInstanceId !== rejected.storeInstanceId))
|
|
1568
|
+
throw new Error("superseded");
|
|
1569
|
+
if (s.credentialState === "refresh_dispatched") {
|
|
1570
|
+
if (Date.now() >= Date.parse(s.refreshOperation.deadlineAt))
|
|
1571
|
+
await this.commit(s, {
|
|
1572
|
+
credentialState: "reauth_required",
|
|
1573
|
+
tokenSet: null,
|
|
1574
|
+
refreshOperation: null,
|
|
1575
|
+
reason: "refresh_outcome_unknown"
|
|
1576
|
+
});
|
|
1577
|
+
else await pause();
|
|
1578
|
+
continue;
|
|
1579
|
+
}
|
|
1580
|
+
if (s.credentialState === "refresh_reserved") {
|
|
1581
|
+
if (Date.now() >= Date.parse(s.refreshOperation.startedAt) + 3e4)
|
|
1582
|
+
await this.commit(s, {
|
|
1583
|
+
credentialState: s.refreshOperation.returnState,
|
|
1584
|
+
refreshOperation: null
|
|
1585
|
+
});
|
|
1586
|
+
else await pause();
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
if (s.credentialState !== "ready" && !(pending && s.credentialState === "pending_identity"))
|
|
1590
|
+
throw new Error(`credential_unavailable:${s.credentialState}:${s.reason ?? ""}`);
|
|
1591
|
+
const live = !!s.tokenSet && Date.parse(s.tokenSet.expires_at) > Date.now();
|
|
1592
|
+
if (live && (refreshed || s.revision !== (rejected?.revision ?? initialRevision) && (rejected !== void 0 || s.tokenSet.access_token !== initialAccess)))
|
|
1593
|
+
return s;
|
|
1594
|
+
if (this.usable(s) && !rejected) return s;
|
|
1595
|
+
const metadata = await this.metadata(s, signal);
|
|
1596
|
+
abort(signal);
|
|
1597
|
+
const operationId = uuid();
|
|
1598
|
+
const r = await this.commit(
|
|
1599
|
+
s,
|
|
1600
|
+
{
|
|
1601
|
+
credentialState: "refresh_reserved",
|
|
1602
|
+
refreshOperation: {
|
|
1603
|
+
operationId,
|
|
1604
|
+
sessionId: s.authSessionId,
|
|
1605
|
+
baseRevision: s.revision,
|
|
1606
|
+
phase: "reserved",
|
|
1607
|
+
returnState: s.credentialState,
|
|
1608
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1609
|
+
dispatchedAt: null,
|
|
1610
|
+
deadlineAt: null
|
|
1611
|
+
}
|
|
1612
|
+
},
|
|
1613
|
+
signal
|
|
1614
|
+
);
|
|
1615
|
+
if (r.status !== "committed") continue;
|
|
1616
|
+
const owner = this.refresh(r.snapshot, metadata);
|
|
1617
|
+
await this.wait(owner, signal);
|
|
1618
|
+
refreshed = true;
|
|
1619
|
+
rejected = void 0;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
wait(promise, signal) {
|
|
1623
|
+
if (!signal) return promise;
|
|
1624
|
+
return new Promise((resolve, reject) => {
|
|
1625
|
+
const onAbort = () => {
|
|
1626
|
+
signal.removeEventListener("abort", onAbort);
|
|
1627
|
+
reject(new Error("aborted"));
|
|
1628
|
+
};
|
|
1629
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1630
|
+
promise.then(
|
|
1631
|
+
(value) => {
|
|
1632
|
+
signal.removeEventListener("abort", onAbort);
|
|
1633
|
+
resolve(value);
|
|
1634
|
+
},
|
|
1635
|
+
(error) => {
|
|
1636
|
+
signal.removeEventListener("abort", onAbort);
|
|
1637
|
+
reject(error);
|
|
1638
|
+
}
|
|
1639
|
+
);
|
|
1640
|
+
if (signal.aborted) onAbort();
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
async refresh(reserved, metadata) {
|
|
1644
|
+
const dispatchedAt = Date.now();
|
|
1645
|
+
const r = await this.commit(reserved, {
|
|
1646
|
+
credentialState: "refresh_dispatched",
|
|
1647
|
+
refreshOperation: {
|
|
1648
|
+
...reserved.refreshOperation,
|
|
1649
|
+
phase: "dispatched",
|
|
1650
|
+
dispatchedAt: new Date(dispatchedAt).toISOString(),
|
|
1651
|
+
deadlineAt: new Date(dispatchedAt + 3e4).toISOString()
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
if (r.status !== "committed") return;
|
|
1655
|
+
const s = r.snapshot;
|
|
1656
|
+
const ctl = new AbortController();
|
|
1657
|
+
const timeout = setTimeout(() => ctl.abort(), 3e4);
|
|
1658
|
+
let tokens;
|
|
1659
|
+
try {
|
|
1660
|
+
const result = await this.wait(
|
|
1661
|
+
(async () => {
|
|
1662
|
+
const response = await this.protocol.fetch(metadata.token_endpoint, {
|
|
1663
|
+
method: "POST",
|
|
1664
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1665
|
+
body: new URLSearchParams({
|
|
1666
|
+
grant_type: "refresh_token",
|
|
1667
|
+
client_id: s.tokenSet.client_id,
|
|
1668
|
+
refresh_token: s.tokenSet.refresh_token
|
|
1669
|
+
}),
|
|
1670
|
+
signal: ctl.signal
|
|
1671
|
+
});
|
|
1672
|
+
const body2 = await response.json();
|
|
1673
|
+
return { response, body: body2 };
|
|
1674
|
+
})(),
|
|
1675
|
+
ctl.signal
|
|
1676
|
+
);
|
|
1677
|
+
if (!result.response.ok) {
|
|
1678
|
+
const code = result.body.error;
|
|
1679
|
+
let reason = "refresh_outcome_unknown";
|
|
1680
|
+
let state = "reauth_required";
|
|
1681
|
+
if (code === "invalid_grant") reason = code;
|
|
1682
|
+
else if (["invalid_client", "invalid_scope", "unsupported_grant_type"].includes(code)) {
|
|
1683
|
+
reason = code;
|
|
1684
|
+
state = "configuration_error";
|
|
1685
|
+
} else if (result.body.rotationOutcome === "not_committed" && ["temporarily_unavailable", "server_error"].includes(code)) {
|
|
1686
|
+
await this.commit(s, {
|
|
1687
|
+
credentialState: s.refreshOperation.returnState,
|
|
1688
|
+
refreshOperation: null
|
|
1689
|
+
});
|
|
1690
|
+
throw new Error("refresh_temporarily_unavailable");
|
|
1691
|
+
}
|
|
1692
|
+
await this.commit(s, {
|
|
1693
|
+
credentialState: state,
|
|
1694
|
+
tokenSet: null,
|
|
1695
|
+
refreshOperation: null,
|
|
1696
|
+
reason
|
|
1697
|
+
});
|
|
1698
|
+
throw new Error(reason);
|
|
1699
|
+
}
|
|
1700
|
+
const body = result.body;
|
|
1701
|
+
if (!Number.isFinite(body.expires_in) || body.expires_in <= 0)
|
|
1702
|
+
throw new Error("invalid_response");
|
|
1703
|
+
tokens = {
|
|
1704
|
+
...s.tokenSet,
|
|
1705
|
+
access_token: body.access_token,
|
|
1706
|
+
refresh_token: body.refresh_token,
|
|
1707
|
+
expires_at: new Date(Date.now() + body.expires_in * 1e3).toISOString(),
|
|
1708
|
+
scope: body.scope ?? s.tokenSet.scope
|
|
1709
|
+
};
|
|
1710
|
+
this.validateTokens(tokens);
|
|
1711
|
+
} catch (error) {
|
|
1712
|
+
await this.commit(s, {
|
|
1713
|
+
credentialState: "reauth_required",
|
|
1714
|
+
tokenSet: null,
|
|
1715
|
+
refreshOperation: null,
|
|
1716
|
+
reason: "refresh_outcome_unknown"
|
|
1717
|
+
});
|
|
1718
|
+
throw error;
|
|
1719
|
+
} finally {
|
|
1720
|
+
clearTimeout(timeout);
|
|
1721
|
+
}
|
|
1722
|
+
const started = performance.now(), mutationId = uuid();
|
|
1723
|
+
for (; ; ) {
|
|
1724
|
+
if (Date.now() >= Date.parse(s.refreshOperation.deadlineAt)) {
|
|
1725
|
+
await this.commit(s, {
|
|
1726
|
+
credentialState: "reauth_required",
|
|
1727
|
+
tokenSet: null,
|
|
1728
|
+
refreshOperation: null,
|
|
1729
|
+
reason: "refresh_outcome_unknown"
|
|
1730
|
+
});
|
|
1731
|
+
void this.revoke(tokens, metadata);
|
|
1732
|
+
throw new Error("refresh_outcome_unknown");
|
|
1733
|
+
}
|
|
1734
|
+
const result = await this.cas(
|
|
1735
|
+
s,
|
|
1736
|
+
{
|
|
1737
|
+
tokenSet: tokens,
|
|
1738
|
+
credentialState: s.refreshOperation.returnState,
|
|
1739
|
+
refreshOperation: null
|
|
1740
|
+
},
|
|
1741
|
+
mutationId
|
|
1742
|
+
);
|
|
1743
|
+
if (result.status === "committed") {
|
|
1744
|
+
await this.notify(result.snapshot);
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
if (result.status === "superseded") {
|
|
1748
|
+
void this.revoke(tokens, metadata);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (performance.now() - started >= 2e3) throw new Error("credential_persist_failed");
|
|
1752
|
+
await pause();
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
async revoke(tokens, metadata, authority) {
|
|
1756
|
+
const ctl = new AbortController();
|
|
1757
|
+
const timer = setTimeout(() => ctl.abort(), 3e4);
|
|
1758
|
+
try {
|
|
1759
|
+
const m = metadata ?? await this.wait(authority ? this.metadata(authority, ctl.signal) : this.protocol.metadata(ctl.signal), ctl.signal);
|
|
1760
|
+
if (!m.revocation_endpoint) return "unsupported";
|
|
1761
|
+
const response = await this.wait(this.protocol.fetch(m.revocation_endpoint, {
|
|
1762
|
+
method: "POST",
|
|
1763
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1764
|
+
body: new URLSearchParams({ token: tokens.refresh_token, token_type_hint: "refresh_token" }),
|
|
1765
|
+
signal: ctl.signal
|
|
1766
|
+
}), ctl.signal);
|
|
1767
|
+
return response.ok ? "confirmed" : "failed";
|
|
1768
|
+
} catch {
|
|
1769
|
+
return "failed";
|
|
1770
|
+
} finally {
|
|
1771
|
+
clearTimeout(timer);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async logout(signal, expected) {
|
|
1775
|
+
let s = await this.read(signal);
|
|
1776
|
+
if (expected && (s.storeInstanceId !== expected.storeInstanceId || s.authSessionId !== expected.authSessionId)) {
|
|
1777
|
+
return {
|
|
1778
|
+
logoutOperationId: uuid(),
|
|
1779
|
+
expectedAuthSessionId: expected.authSessionId,
|
|
1780
|
+
storeInstanceId: expected.storeInstanceId,
|
|
1781
|
+
revision: s.revision,
|
|
1782
|
+
status: "superseded"
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
const session = s.authSessionId, instance = s.storeInstanceId, started = performance.now(), logoutOperationId = uuid();
|
|
1786
|
+
const receipt = () => ({
|
|
1787
|
+
logoutOperationId,
|
|
1788
|
+
expectedAuthSessionId: session,
|
|
1789
|
+
storeInstanceId: instance,
|
|
1790
|
+
revision: s.revision
|
|
1791
|
+
});
|
|
1792
|
+
for (; ; ) {
|
|
1793
|
+
abort(signal);
|
|
1794
|
+
if (s.storeInstanceId !== instance || s.authSessionId !== session)
|
|
1795
|
+
return { ...receipt(), status: "superseded" };
|
|
1796
|
+
if (s.credentialState === "signed_out" && !s.loginAttempt)
|
|
1797
|
+
return { ...receipt(), status: "already_signed_out" };
|
|
1798
|
+
const r = await this.cas(
|
|
1799
|
+
s,
|
|
1800
|
+
{
|
|
1801
|
+
credentialState: "signed_out",
|
|
1802
|
+
authSessionId: null,
|
|
1803
|
+
principal: null,
|
|
1804
|
+
tokenSet: null,
|
|
1805
|
+
refreshOperation: null,
|
|
1806
|
+
loginAttempt: null,
|
|
1807
|
+
lastLoginAttemptId: null,
|
|
1808
|
+
verifiedIdentity: null,
|
|
1809
|
+
reason: null
|
|
1810
|
+
},
|
|
1811
|
+
logoutOperationId,
|
|
1812
|
+
signal
|
|
1813
|
+
);
|
|
1814
|
+
if (r.status === "committed") {
|
|
1815
|
+
await this.notify(r.snapshot);
|
|
1816
|
+
return {
|
|
1817
|
+
...receipt(),
|
|
1818
|
+
revision: r.snapshot.revision,
|
|
1819
|
+
status: "committed",
|
|
1820
|
+
revocation: s.tokenSet ? this.revoke(s.tokenSet, void 0, s) : Promise.resolve("unsupported")
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
if (performance.now() - started >= 2e3) throw new Error("storage_busy");
|
|
1824
|
+
await pause();
|
|
1825
|
+
s = await this.read(signal);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1121
1830
|
// src/core/client.ts
|
|
1122
1831
|
init_types();
|
|
1123
1832
|
|
|
@@ -1557,33 +2266,35 @@ async function revokeToken(meta, token, signal, fetchImpl = globalThis.fetch) {
|
|
|
1557
2266
|
}
|
|
1558
2267
|
async function postToken(endpoint, data, signal, fetchImpl = globalThis.fetch) {
|
|
1559
2268
|
const ctl = withTimeout(authTimeoutMs, signal);
|
|
1560
|
-
let resp;
|
|
1561
2269
|
try {
|
|
1562
|
-
resp
|
|
1563
|
-
method: "POST",
|
|
1564
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1565
|
-
body: data,
|
|
1566
|
-
signal: ctl.signal
|
|
1567
|
-
});
|
|
1568
|
-
} catch (e) {
|
|
1569
|
-
throw new Error(`token request: ${e instanceof Error ? e.message : String(e)}`);
|
|
1570
|
-
} finally {
|
|
1571
|
-
ctl.dispose();
|
|
1572
|
-
}
|
|
1573
|
-
if (!resp.ok) {
|
|
1574
|
-
let errBody = {};
|
|
2270
|
+
let resp;
|
|
1575
2271
|
try {
|
|
1576
|
-
|
|
1577
|
-
|
|
2272
|
+
resp = await fetchImpl(endpoint, {
|
|
2273
|
+
method: "POST",
|
|
2274
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
2275
|
+
body: data,
|
|
2276
|
+
signal: ctl.signal
|
|
2277
|
+
});
|
|
2278
|
+
} catch (e) {
|
|
2279
|
+
throw new Error(`token request: ${e instanceof Error ? e.message : String(e)}`);
|
|
1578
2280
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2281
|
+
if (!resp.ok) {
|
|
2282
|
+
let errBody = {};
|
|
2283
|
+
try {
|
|
2284
|
+
errBody = await resp.json();
|
|
2285
|
+
} catch {
|
|
2286
|
+
}
|
|
2287
|
+
const oauthError = typeof errBody.error === "string" ? errBody.error : "";
|
|
2288
|
+
const errorDescription = typeof errBody.error_description === "string" ? errBody.error_description : "";
|
|
2289
|
+
throw new OAuthTokenEndpointError(resp.status, oauthError, errorDescription);
|
|
2290
|
+
}
|
|
2291
|
+
try {
|
|
2292
|
+
return await resp.json();
|
|
2293
|
+
} catch (e) {
|
|
2294
|
+
throw new Error(`token: decode: ${e instanceof Error ? e.message : String(e)}`);
|
|
2295
|
+
}
|
|
2296
|
+
} finally {
|
|
2297
|
+
ctl.dispose();
|
|
1587
2298
|
}
|
|
1588
2299
|
}
|
|
1589
2300
|
function newTokenSet(resp, clientID, serverURL) {
|
|
@@ -2066,10 +2777,31 @@ function parseHTTPErrorWithHeader(statusCode, body, header) {
|
|
|
2066
2777
|
} else if (typeof top.message === "string") {
|
|
2067
2778
|
message = top.message;
|
|
2068
2779
|
}
|
|
2780
|
+
const contractSource = errObj && typeof errObj === "object" ? errObj : top;
|
|
2069
2781
|
if (typeof top.errorCode === "string") errorCode = top.errorCode;
|
|
2782
|
+
else if (typeof contractSource.errorCode === "string") errorCode = contractSource.errorCode;
|
|
2070
2783
|
if (top.windowKind === "FIVE_HOUR" || top.windowKind === "WEEKLY") windowKind = top.windowKind;
|
|
2071
2784
|
if (typeof top.windowResetAt === "string") windowResetAt = top.windowResetAt;
|
|
2072
2785
|
if (typeof top.windowOverridable === "boolean") windowOverridable = top.windowOverridable;
|
|
2786
|
+
const disposition = contractSource.requestDisposition === "not_accepted" || contractSource.requestDisposition === "accepted" || contractSource.requestDisposition === "unknown" ? contractSource.requestDisposition : void 0;
|
|
2787
|
+
const domains = ["user_auth", "caller_credentials", "account_quota", "account_permission", "provider", "gateway", "transport", "stream_ticket", "protocol"];
|
|
2788
|
+
return new exports.HTTPError(statusCode, {
|
|
2789
|
+
type,
|
|
2790
|
+
message,
|
|
2791
|
+
retryAfter,
|
|
2792
|
+
body: bodyStr,
|
|
2793
|
+
errorCode,
|
|
2794
|
+
windowKind,
|
|
2795
|
+
windowResetAt,
|
|
2796
|
+
windowOverridable,
|
|
2797
|
+
errorContractVersion: contractSource.errorContractVersion === 1 ? 1 : void 0,
|
|
2798
|
+
faultDomain: typeof contractSource.faultDomain === "string" && domains.includes(contractSource.faultDomain) ? contractSource.faultDomain : void 0,
|
|
2799
|
+
requestDisposition: disposition,
|
|
2800
|
+
transportRequestId: typeof contractSource.transportRequestId === "string" ? contractSource.transportRequestId : null,
|
|
2801
|
+
consumeRequestId: typeof contractSource.consumeRequestId === "string" ? contractSource.consumeRequestId : null,
|
|
2802
|
+
providerRequestId: typeof contractSource.providerRequestId === "string" ? contractSource.providerRequestId : null,
|
|
2803
|
+
retryable: contractSource.retryable === true
|
|
2804
|
+
});
|
|
2073
2805
|
}
|
|
2074
2806
|
} catch {
|
|
2075
2807
|
}
|
|
@@ -2131,7 +2863,19 @@ function parseStreamError(data) {
|
|
|
2131
2863
|
if (code === "" && errObj.type) code = errObj.type;
|
|
2132
2864
|
}
|
|
2133
2865
|
}
|
|
2134
|
-
return new exports.StreamError({
|
|
2866
|
+
return new exports.StreamError({
|
|
2867
|
+
code,
|
|
2868
|
+
stage,
|
|
2869
|
+
message,
|
|
2870
|
+
rawError,
|
|
2871
|
+
retryable,
|
|
2872
|
+
errorContractVersion: payload.errorContractVersion,
|
|
2873
|
+
faultDomain: payload.faultDomain,
|
|
2874
|
+
requestDisposition: payload.requestDisposition,
|
|
2875
|
+
transportRequestId: payload.transportRequestId,
|
|
2876
|
+
consumeRequestId: payload.consumeRequestId,
|
|
2877
|
+
providerRequestId: payload.providerRequestId
|
|
2878
|
+
});
|
|
2135
2879
|
}
|
|
2136
2880
|
function isOrderSuccess(status) {
|
|
2137
2881
|
switch (status) {
|
|
@@ -2319,6 +3063,22 @@ function notifyUpstreamActivity(cb) {
|
|
|
2319
3063
|
} catch {
|
|
2320
3064
|
}
|
|
2321
3065
|
}
|
|
3066
|
+
var GATEWAY_REQUEST_ID_HEADER = "X-Acosmi-Request-Id";
|
|
3067
|
+
function readGatewayRequestID(headers) {
|
|
3068
|
+
const raw = headers.get(GATEWAY_REQUEST_ID_HEADER);
|
|
3069
|
+
if (!raw) return void 0;
|
|
3070
|
+
const trimmed = raw.trim();
|
|
3071
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
3072
|
+
}
|
|
3073
|
+
function notifyGatewayRequestID(cb, headers) {
|
|
3074
|
+
if (!cb) return;
|
|
3075
|
+
const id = readGatewayRequestID(headers);
|
|
3076
|
+
if (id === void 0) return;
|
|
3077
|
+
try {
|
|
3078
|
+
cb(id);
|
|
3079
|
+
} catch {
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
2322
3082
|
var DEFAULT_API_TIMEOUT_MS = 6e4;
|
|
2323
3083
|
function newDeferred() {
|
|
2324
3084
|
let resolve;
|
|
@@ -2349,6 +3109,13 @@ var Client = class _Client {
|
|
|
2349
3109
|
tokens = null;
|
|
2350
3110
|
/** token 持久化 */
|
|
2351
3111
|
store;
|
|
3112
|
+
credentialMode;
|
|
3113
|
+
versionedCredentialStore;
|
|
3114
|
+
accessTokenProvider;
|
|
3115
|
+
beforeCredentialInstall;
|
|
3116
|
+
credentialAuthority;
|
|
3117
|
+
credentialRequestOwner;
|
|
3118
|
+
lifecycle = null;
|
|
2352
3119
|
/** fetch 实现 (默认 globalThis.fetch) */
|
|
2353
3120
|
fetchImpl;
|
|
2354
3121
|
/** 互斥锁 (TS 用 Promise chain 替代 sync.Mutex) */
|
|
@@ -2379,6 +3146,7 @@ var Client = class _Client {
|
|
|
2379
3146
|
/** V29 系数缓存 (TTL 8s, listCoefficients 内部用) */
|
|
2380
3147
|
coefCacheData = null;
|
|
2381
3148
|
coefCacheTimeMs = 0;
|
|
3149
|
+
credentialOwnerKey = null;
|
|
2382
3150
|
/** 串行化锁 (替代 Go sync.Mutex) */
|
|
2383
3151
|
coefMu = Promise.resolve();
|
|
2384
3152
|
constructor(cfg = {}) {
|
|
@@ -2389,8 +3157,92 @@ var Client = class _Client {
|
|
|
2389
3157
|
this.oauthMetadataProfile = cfg.oauthMetadataProfile ?? "desktop";
|
|
2390
3158
|
this.browserRefreshMode = cfg.browserRefreshMode ?? "direct";
|
|
2391
3159
|
this.refreshProxyURL = cfg.refreshProxyURL ?? null;
|
|
2392
|
-
this.
|
|
3160
|
+
this.credentialMode = cfg.credentialMode ?? "legacy";
|
|
3161
|
+
this.credentialAuthority = new URL(this.serverURL).origin;
|
|
3162
|
+
if (this.credentialMode === "versioned" && !cfg.versionedCredentialStore) {
|
|
3163
|
+
throw new Error("versionedCredentialStore is required for credentialMode=versioned");
|
|
3164
|
+
}
|
|
3165
|
+
if (cfg.credentialRequestOwner && this.credentialMode !== "versioned") {
|
|
3166
|
+
throw new Error("credentialRequestOwner requires credentialMode=versioned");
|
|
3167
|
+
}
|
|
3168
|
+
if (this.credentialMode === "legacy" && cfg.versionedCredentialStore) {
|
|
3169
|
+
throw new Error("credentialMode=versioned is required when versionedCredentialStore is provided");
|
|
3170
|
+
}
|
|
3171
|
+
if (this.credentialMode === "external" && cfg.versionedCredentialStore) {
|
|
3172
|
+
throw new Error("versionedCredentialStore is incompatible with credentialMode=external");
|
|
3173
|
+
}
|
|
3174
|
+
if (this.credentialMode !== "external" && cfg.accessTokenProvider) {
|
|
3175
|
+
throw new Error("accessTokenProvider requires credentialMode=external");
|
|
3176
|
+
}
|
|
3177
|
+
if (this.credentialMode === "external" && !cfg.accessTokenProvider) {
|
|
3178
|
+
throw new Error("accessTokenProvider is required for credentialMode=external");
|
|
3179
|
+
}
|
|
3180
|
+
if (this.credentialMode !== "versioned" && cfg.beforeCredentialInstall) {
|
|
3181
|
+
throw new Error("beforeCredentialInstall requires credentialMode=versioned");
|
|
3182
|
+
}
|
|
3183
|
+
if (this.credentialMode !== "legacy") {
|
|
3184
|
+
const authority = new URL(this.serverURL).origin;
|
|
3185
|
+
for (const [name, override] of [["apiBaseURL", this.apiBaseURL], ["complianceBaseURL", this.complianceBaseURL]]) {
|
|
3186
|
+
if (override && new URL(override).origin !== authority) {
|
|
3187
|
+
throw new Error(`${name} must use the credential authority ${authority}`);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
this.accessTokenProvider = cfg.accessTokenProvider;
|
|
3192
|
+
this.beforeCredentialInstall = cfg.beforeCredentialInstall;
|
|
3193
|
+
this.credentialRequestOwner = cfg.credentialRequestOwner ? {
|
|
3194
|
+
storeInstanceId: cfg.credentialRequestOwner.storeInstanceId,
|
|
3195
|
+
authSessionId: cfg.credentialRequestOwner.authSessionId,
|
|
3196
|
+
principal: cfg.credentialRequestOwner.principal ? {
|
|
3197
|
+
issuer: cfg.credentialRequestOwner.principal.issuer,
|
|
3198
|
+
subject: cfg.credentialRequestOwner.principal.subject,
|
|
3199
|
+
organizationId: cfg.credentialRequestOwner.principal.organizationId
|
|
3200
|
+
} : null
|
|
3201
|
+
} : null;
|
|
2393
3202
|
this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
3203
|
+
this.versionedCredentialStore = cfg.versionedCredentialStore ?? null;
|
|
3204
|
+
if (this.versionedCredentialStore) this.lifecycle = new CredentialLifecycle(this.versionedCredentialStore, {
|
|
3205
|
+
serverURL: this.serverURL,
|
|
3206
|
+
fetch: this.fetchImpl,
|
|
3207
|
+
metadata: (signal) => discoverWithProfile(this.serverURL, "desktop", signal, this.fetchImpl),
|
|
3208
|
+
profile: async (tokens, signal) => {
|
|
3209
|
+
const gatewayRoot = this.serverURL.replace(/\/api\/v4$/, "");
|
|
3210
|
+
const profileURL = `${gatewayRoot}/api/oauth/profile`;
|
|
3211
|
+
this.assertCredentialURL(profileURL);
|
|
3212
|
+
const response = await this.fetchImpl(profileURL, { headers: { Authorization: `Bearer ${tokens.access_token}` }, signal });
|
|
3213
|
+
if (!response.ok) {
|
|
3214
|
+
const bodyBytes = response.body ? await readLimited(response.body, maxErrorBodySize) : new Uint8Array();
|
|
3215
|
+
const failure = parseHTTPErrorWithHeader(response.status, bodyBytes, response.headers);
|
|
3216
|
+
const contract = readGatewayErrorContract(failure);
|
|
3217
|
+
const code = contract?.errorCode ?? null;
|
|
3218
|
+
if (code === "INVALID_SCOPE") throw new Error("invalid_scope");
|
|
3219
|
+
if (code === "AUTH_CONTRACT_UNSUPPORTED") throw new Error("auth_contract_unsupported");
|
|
3220
|
+
if (code === "ACCOUNT_NOT_FOUND") throw new Error("account_permission");
|
|
3221
|
+
throw new Error("identity_unavailable");
|
|
3222
|
+
}
|
|
3223
|
+
const body = await response.json();
|
|
3224
|
+
const subject = body.account?.uuid;
|
|
3225
|
+
if (typeof subject !== "string" || !subject) throw new Error("invalid_response");
|
|
3226
|
+
const account = body.account;
|
|
3227
|
+
const organization = body.organization;
|
|
3228
|
+
return {
|
|
3229
|
+
subject,
|
|
3230
|
+
organizationId: organization?.uuid || null,
|
|
3231
|
+
...typeof account.display_name === "string" ? { displayName: account.display_name } : {},
|
|
3232
|
+
...[account.avatar_url, account.picture, account.image, body.avatar_url, body.picture, body.image].find((value) => typeof value === "string") !== void 0 ? { avatarUrl: [account.avatar_url, account.picture, account.image, body.avatar_url, body.picture, body.image].find((value) => typeof value === "string") } : {},
|
|
3233
|
+
...typeof account.email === "string" ? { email: account.email } : {},
|
|
3234
|
+
...typeof account.image_url === "string" ? { imageUrl: account.image_url } : {},
|
|
3235
|
+
...typeof account.created_at === "string" ? { accountCreatedAt: account.created_at } : {},
|
|
3236
|
+
...typeof account.requires_phone_binding === "boolean" ? { requiresPhoneBinding: account.requires_phone_binding } : {},
|
|
3237
|
+
...typeof organization?.has_extra_usage_enabled === "boolean" ? { hasExtraUsageEnabled: organization.has_extra_usage_enabled } : {},
|
|
3238
|
+
...typeof organization?.billing_type === "string" ? { billingType: organization.billing_type } : {},
|
|
3239
|
+
...typeof organization?.subscription_created_at === "string" ? { subscriptionCreatedAt: organization.subscription_created_at } : {},
|
|
3240
|
+
...typeof organization?.rate_limit_tier === "string" ? { rateLimitTier: organization.rate_limit_tier } : {},
|
|
3241
|
+
...typeof organization?.name === "string" ? { organizationName: organization.name } : {}
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
this.store = cfg.store ?? defaultTokenStore();
|
|
2394
3246
|
this.retryPolicy = effectivePolicy(cfg.retryPolicy ?? null);
|
|
2395
3247
|
}
|
|
2396
3248
|
/**
|
|
@@ -2399,6 +3251,11 @@ var Client = class _Client {
|
|
|
2399
3251
|
*/
|
|
2400
3252
|
static async create(cfg = {}) {
|
|
2401
3253
|
const c = new _Client(cfg);
|
|
3254
|
+
if (c.credentialMode === "external") return c;
|
|
3255
|
+
if (c.credentialMode === "versioned") {
|
|
3256
|
+
await c.reconcileCredentials();
|
|
3257
|
+
return c;
|
|
3258
|
+
}
|
|
2402
3259
|
try {
|
|
2403
3260
|
const tokens = await c.store.load();
|
|
2404
3261
|
if (tokens) {
|
|
@@ -2412,6 +3269,63 @@ var Client = class _Client {
|
|
|
2412
3269
|
}
|
|
2413
3270
|
return c;
|
|
2414
3271
|
}
|
|
3272
|
+
/** Read the durable authority. Available only in explicit versioned mode. */
|
|
3273
|
+
async getCredentialSnapshot(signal) {
|
|
3274
|
+
if (!this.versionedCredentialStore) {
|
|
3275
|
+
throw new Error("getCredentialSnapshot requires credentialMode=versioned");
|
|
3276
|
+
}
|
|
3277
|
+
return this.versionedCredentialStore.readSnapshot(signal);
|
|
3278
|
+
}
|
|
3279
|
+
/** Reconcile memory from durable state; storage errors never clear confirmed memory. */
|
|
3280
|
+
async reconcileCredentials(signal) {
|
|
3281
|
+
const snapshot = await this.lifecycle.reconcile(signal);
|
|
3282
|
+
this.adoptCredentialOwner(snapshot);
|
|
3283
|
+
this.tokens = snapshot.credentialState === "ready" ? snapshot.tokenSet : null;
|
|
3284
|
+
return snapshot;
|
|
3285
|
+
}
|
|
3286
|
+
ownerKey(snapshot) {
|
|
3287
|
+
return `${snapshot.storeInstanceId}\0${snapshot.authSessionId ?? ""}\0${snapshot.principal?.issuer ?? ""}\0${snapshot.principal?.subject ?? ""}\0${snapshot.principal?.organizationId ?? ""}`;
|
|
3288
|
+
}
|
|
3289
|
+
adoptCredentialOwner(snapshot) {
|
|
3290
|
+
const next = this.ownerKey(snapshot);
|
|
3291
|
+
if (this.credentialOwnerKey !== null && this.credentialOwnerKey !== next) {
|
|
3292
|
+
this.modelCache = [];
|
|
3293
|
+
this.modelCacheTimeMs = 0;
|
|
3294
|
+
this.coefCacheData = null;
|
|
3295
|
+
this.coefCacheTimeMs = 0;
|
|
3296
|
+
}
|
|
3297
|
+
this.credentialOwnerKey = next;
|
|
3298
|
+
}
|
|
3299
|
+
async ensureCredential(signal) {
|
|
3300
|
+
if (!this.lifecycle) return this.ensureToken(signal);
|
|
3301
|
+
const token = await this.lifecycle.ensure(signal, void 0, this.credentialRequestOwner ?? void 0);
|
|
3302
|
+
const snapshot = await this.lifecycle.read(signal);
|
|
3303
|
+
this.adoptCredentialOwner(snapshot);
|
|
3304
|
+
this.tokens = snapshot.credentialState === "ready" ? snapshot.tokenSet : null;
|
|
3305
|
+
return token;
|
|
3306
|
+
}
|
|
3307
|
+
subscribeCredentialState(listener) {
|
|
3308
|
+
if (!this.lifecycle) throw new Error("subscribeCredentialState requires credentialMode=versioned");
|
|
3309
|
+
return this.lifecycle.subscribe(listener);
|
|
3310
|
+
}
|
|
3311
|
+
async retryCredentialIdentity(signal) {
|
|
3312
|
+
const snapshot = await this.getCredentialSnapshot(signal);
|
|
3313
|
+
if (!snapshot.authSessionId) throw new Error("not authorized");
|
|
3314
|
+
const ready = await this.lifecycle.bindIdentity(snapshot.authSessionId, signal);
|
|
3315
|
+
this.adoptCredentialOwner(ready);
|
|
3316
|
+
this.tokens = ready.credentialState === "ready" ? ready.tokenSet : null;
|
|
3317
|
+
return ready;
|
|
3318
|
+
}
|
|
3319
|
+
async logoutCredential(signal, expected) {
|
|
3320
|
+
if (!this.lifecycle) throw new Error("logoutCredential requires credentialMode=versioned");
|
|
3321
|
+
const result = await this.lifecycle.logout(signal, expected);
|
|
3322
|
+
if (result.status === "committed" || result.status === "already_signed_out") {
|
|
3323
|
+
const current = await this.lifecycle.read(signal);
|
|
3324
|
+
this.adoptCredentialOwner(current);
|
|
3325
|
+
this.tokens = null;
|
|
3326
|
+
}
|
|
3327
|
+
return result;
|
|
3328
|
+
}
|
|
2415
3329
|
// ===========================================================================
|
|
2416
3330
|
// 授权生命周期
|
|
2417
3331
|
// ===========================================================================
|
|
@@ -2462,6 +3376,44 @@ var Client = class _Client {
|
|
|
2462
3376
|
return this.loginInternal(appName, scopes, { handler, ...opts }, signal);
|
|
2463
3377
|
}
|
|
2464
3378
|
async loginInternal(appName, scopes, opts, signal) {
|
|
3379
|
+
if (this.lifecycle) {
|
|
3380
|
+
const attempt = await this.lifecycle.reserveLogin(signal);
|
|
3381
|
+
let installHookRejected = false;
|
|
3382
|
+
try {
|
|
3383
|
+
const registration = await register(attempt.metadata, appName, signal, this.fetchImpl);
|
|
3384
|
+
await this.lifecycle.assertLogin(attempt.attemptId, signal);
|
|
3385
|
+
const authorization = await authorize(attempt.metadata, registration.client_id, scopes, { ...opts, handler: opts?.handler ?? void 0, signal });
|
|
3386
|
+
await this.lifecycle.assertLogin(attempt.attemptId, signal);
|
|
3387
|
+
const response = await exchangeCode(attempt.metadata, registration.client_id, authorization.result.code, authorization.result.redirectURI, authorization.verifier, signal, this.fetchImpl);
|
|
3388
|
+
if (!Number.isFinite(response.expires_in) || response.expires_in <= 0) throw new Error("invalid_response");
|
|
3389
|
+
await this.lifecycle.assertLogin(attempt.attemptId, signal);
|
|
3390
|
+
try {
|
|
3391
|
+
await this.beforeCredentialInstall?.({
|
|
3392
|
+
accessToken: response.access_token,
|
|
3393
|
+
attemptId: attempt.attemptId,
|
|
3394
|
+
serverURL: this.serverURL,
|
|
3395
|
+
clientId: registration.client_id
|
|
3396
|
+
}, signal);
|
|
3397
|
+
} catch (error) {
|
|
3398
|
+
installHookRejected = true;
|
|
3399
|
+
throw error;
|
|
3400
|
+
}
|
|
3401
|
+
await this.lifecycle.assertLogin(attempt.attemptId, signal);
|
|
3402
|
+
const ready = await this.lifecycle.installLogin(attempt.attemptId, newTokenSet(response, registration.client_id, this.serverURL), signal);
|
|
3403
|
+
const current = await this.getCredentialSnapshot(signal);
|
|
3404
|
+
if (current.revision !== ready.revision || current.authSessionId !== ready.authSessionId) throw new Error("superseded");
|
|
3405
|
+
this.tokens = ready.tokenSet;
|
|
3406
|
+
opts?.handler?.({ type: EventComplete, attemptId: attempt.attemptId });
|
|
3407
|
+
} catch (error) {
|
|
3408
|
+
const current = await this.getCredentialSnapshot();
|
|
3409
|
+
if (current.loginAttempt?.attemptId === attempt.attemptId || current.lastLoginAttemptId === attempt.attemptId) {
|
|
3410
|
+
opts?.handler?.(installHookRejected ? { type: EventError, attemptId: attempt.attemptId, err_code: "credential_install_rejected", error: "credential_install_rejected" } : { type: EventError, attemptId: attempt.attemptId, error: "credential_login_failed" });
|
|
3411
|
+
}
|
|
3412
|
+
await this.lifecycle.cancelLogin(attempt.attemptId);
|
|
3413
|
+
throw error;
|
|
3414
|
+
}
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
2465
3417
|
const handler = opts?.handler ?? void 0;
|
|
2466
3418
|
const emit = (e) => {
|
|
2467
3419
|
if (handler) handler(e);
|
|
@@ -2570,6 +3522,11 @@ var Client = class _Client {
|
|
|
2570
3522
|
}
|
|
2571
3523
|
/** 吊销 token 并清除本地存储 */
|
|
2572
3524
|
async logout(signal) {
|
|
3525
|
+
if (this.lifecycle) {
|
|
3526
|
+
await this.logoutCredential(signal);
|
|
3527
|
+
await this.reconcileCredentials(signal);
|
|
3528
|
+
return;
|
|
3529
|
+
}
|
|
2573
3530
|
const tokens = this.tokens;
|
|
2574
3531
|
let meta = this.meta;
|
|
2575
3532
|
this.tokens = null;
|
|
@@ -2607,6 +3564,13 @@ var Client = class _Client {
|
|
|
2607
3564
|
* 避免应用启动期 "login + 多个 API 调用" 并发场景下 4+ 条 "not authorized" 误报.
|
|
2608
3565
|
*/
|
|
2609
3566
|
async ensureToken(signal) {
|
|
3567
|
+
if (this.credentialMode === "external") {
|
|
3568
|
+
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3569
|
+
const token = await this.accessTokenProvider(signal);
|
|
3570
|
+
if (typeof token !== "string" || token.length === 0) throw new Error("external access token unavailable");
|
|
3571
|
+
return token;
|
|
3572
|
+
}
|
|
3573
|
+
if (this.lifecycle) return this.lifecycle.ensure(signal, void 0, this.credentialRequestOwner ?? void 0);
|
|
2610
3574
|
let tokens = this.tokens;
|
|
2611
3575
|
const ready = this.tokenReady.promise;
|
|
2612
3576
|
const inFlight = this.loginInFlight;
|
|
@@ -2654,7 +3618,14 @@ var Client = class _Client {
|
|
|
2654
3618
|
);
|
|
2655
3619
|
}
|
|
2656
3620
|
/** 强制刷新 token (用于 401 重试) */
|
|
2657
|
-
async forceRefresh(signal) {
|
|
3621
|
+
async forceRefresh(signal, rejectedToken) {
|
|
3622
|
+
if (this.credentialMode === "external") throw new Error("external credentials cannot be refreshed by the SDK");
|
|
3623
|
+
if (this.lifecycle) {
|
|
3624
|
+
await this.lifecycle.forceRefresh(signal, rejectedToken, this.credentialRequestOwner ?? void 0);
|
|
3625
|
+
const snapshot = await this.lifecycle.read(signal);
|
|
3626
|
+
this.tokens = snapshot.credentialState === "ready" ? snapshot.tokenSet : null;
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
2658
3629
|
return this.withMu(
|
|
2659
3630
|
() => this.storeWithLock(async () => {
|
|
2660
3631
|
await this.syncFromDisk();
|
|
@@ -2849,6 +3820,7 @@ var Client = class _Client {
|
|
|
2849
3820
|
*/
|
|
2850
3821
|
async listModelsWithStatus(signal, opts) {
|
|
2851
3822
|
const includeLocked = opts?.includeLocked === true;
|
|
3823
|
+
const requestOwner = this.lifecycle ? this.ownerKey(await this.getCredentialSnapshot(signal)) : null;
|
|
2852
3824
|
const path = includeLocked ? "/managed-models?picker=1" : "/managed-models";
|
|
2853
3825
|
const { result, headers } = await this.doJSONFull(
|
|
2854
3826
|
"GET",
|
|
@@ -2857,6 +3829,11 @@ var Client = class _Client {
|
|
|
2857
3829
|
signal
|
|
2858
3830
|
);
|
|
2859
3831
|
const normalized = normalizeInputModalities(result.data);
|
|
3832
|
+
if (requestOwner !== null) {
|
|
3833
|
+
const current = await this.getCredentialSnapshot(signal);
|
|
3834
|
+
if (this.ownerKey(current) !== requestOwner) throw new Error("superseded");
|
|
3835
|
+
this.adoptCredentialOwner(current);
|
|
3836
|
+
}
|
|
2860
3837
|
if (!includeLocked) {
|
|
2861
3838
|
this.modelCache = normalized;
|
|
2862
3839
|
this.modelCacheTimeMs = Date.now();
|
|
@@ -3026,7 +4003,7 @@ var Client = class _Client {
|
|
|
3026
4003
|
* 响应的 tokenRemaining / callRemaining 字段来自服务端 Header, 反映结算后余额
|
|
3027
4004
|
* v0.5.0: 根据 provider 自动路由到 /anthropic 或 /chat 端点
|
|
3028
4005
|
*/
|
|
3029
|
-
async chat(modelID, req, signal) {
|
|
4006
|
+
async chat(modelID, req, signal, onGatewayRequestID) {
|
|
3030
4007
|
const r = { ...req, stream: false };
|
|
3031
4008
|
const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
|
|
3032
4009
|
try {
|
|
@@ -3039,6 +4016,7 @@ var Client = class _Client {
|
|
|
3039
4016
|
ctl.signal,
|
|
3040
4017
|
CHAT_REQUEST_TIMEOUT_MS
|
|
3041
4018
|
);
|
|
4019
|
+
notifyGatewayRequestID(onGatewayRequestID, headers);
|
|
3042
4020
|
const resp = adapter.parseResponse(result);
|
|
3043
4021
|
const v1 = headers.get("X-Token-Remaining");
|
|
3044
4022
|
if (v1) {
|
|
@@ -3159,28 +4137,29 @@ var Client = class _Client {
|
|
|
3159
4137
|
* Anthropic → chatMessagesAnthropic (现有路径, POST /anthropic)
|
|
3160
4138
|
* 其他厂商 → chatMessagesOpenAI (POST /chat, 响应转换为 AnthropicResponse)
|
|
3161
4139
|
*/
|
|
3162
|
-
async chatMessages(modelID, req, signal) {
|
|
4140
|
+
async chatMessages(modelID, req, signal, onGatewayRequestID) {
|
|
3163
4141
|
const m = await this.ensureModelCached(modelID, signal);
|
|
3164
4142
|
const adapter = getAdapterForModel(m);
|
|
3165
4143
|
if (adapter.format() === 0 /* Anthropic */) {
|
|
3166
|
-
return this.chatMessagesAnthropic(modelID, req, adapter, signal);
|
|
4144
|
+
return this.chatMessagesAnthropic(modelID, req, adapter, signal, onGatewayRequestID);
|
|
3167
4145
|
}
|
|
3168
|
-
return this.chatMessagesOpenAI(modelID, req, adapter, signal);
|
|
4146
|
+
return this.chatMessagesOpenAI(modelID, req, adapter, signal, onGatewayRequestID);
|
|
3169
4147
|
}
|
|
3170
|
-
async chatMessagesAnthropic(modelID, req, adapter, signal) {
|
|
4148
|
+
async chatMessagesAnthropic(modelID, req, adapter, signal, onGatewayRequestID) {
|
|
3171
4149
|
const r = { ...req, stream: false };
|
|
3172
4150
|
const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
|
|
3173
4151
|
try {
|
|
3174
4152
|
const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
|
|
3175
4153
|
const body = adapter.buildRequestBody(caps, r);
|
|
3176
4154
|
const data = JSON.stringify(body);
|
|
3177
|
-
const { result } = await this.doJSONFullRaw(
|
|
4155
|
+
const { result, headers } = await this.doJSONFullRaw(
|
|
3178
4156
|
"POST",
|
|
3179
4157
|
`/managed-models/${encodeURIComponent(modelID)}/anthropic`,
|
|
3180
4158
|
data,
|
|
3181
4159
|
ctl.signal,
|
|
3182
4160
|
CHAT_REQUEST_TIMEOUT_MS
|
|
3183
4161
|
);
|
|
4162
|
+
notifyGatewayRequestID(onGatewayRequestID, headers);
|
|
3184
4163
|
const rawStr = new TextDecoder().decode(result);
|
|
3185
4164
|
try {
|
|
3186
4165
|
const wrapper = JSON.parse(rawStr);
|
|
@@ -3205,7 +4184,7 @@ var Client = class _Client {
|
|
|
3205
4184
|
ctl.dispose();
|
|
3206
4185
|
}
|
|
3207
4186
|
}
|
|
3208
|
-
async chatMessagesOpenAI(modelID, req, adapter, signal) {
|
|
4187
|
+
async chatMessagesOpenAI(modelID, req, adapter, signal, onGatewayRequestID) {
|
|
3209
4188
|
const r = { ...req, stream: false };
|
|
3210
4189
|
const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
|
|
3211
4190
|
try {
|
|
@@ -3213,13 +4192,14 @@ var Client = class _Client {
|
|
|
3213
4192
|
const body = adapter.buildRequestBody(caps, r);
|
|
3214
4193
|
const data = JSON.stringify(body);
|
|
3215
4194
|
const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
|
|
3216
|
-
const { result } = await this.doJSONFullRaw(
|
|
4195
|
+
const { result, headers } = await this.doJSONFullRaw(
|
|
3217
4196
|
"POST",
|
|
3218
4197
|
endpoint,
|
|
3219
4198
|
data,
|
|
3220
4199
|
ctl.signal,
|
|
3221
4200
|
CHAT_REQUEST_TIMEOUT_MS
|
|
3222
4201
|
);
|
|
4202
|
+
notifyGatewayRequestID(onGatewayRequestID, headers);
|
|
3223
4203
|
const { parseOpenAIResponseToAnthropic: parseOpenAIResponseToAnthropic2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
3224
4204
|
return parseOpenAIResponseToAnthropic2(result);
|
|
3225
4205
|
} finally {
|
|
@@ -3231,10 +4211,11 @@ var Client = class _Client {
|
|
|
3231
4211
|
* v0.5.0: 根据 adapter 路由端点
|
|
3232
4212
|
*
|
|
3233
4213
|
* @param onUpstreamActivity 见 {@link UpstreamActivityCallback}
|
|
4214
|
+
* @param onGatewayRequestID 见 {@link GatewayRequestIDCallback}
|
|
3234
4215
|
*/
|
|
3235
|
-
chatStream(modelID, req, signal, onUpstreamActivity) {
|
|
4216
|
+
chatStream(modelID, req, signal, onUpstreamActivity, onGatewayRequestID) {
|
|
3236
4217
|
return {
|
|
3237
|
-
[Symbol.asyncIterator]: () => this.chatStreamGen(modelID, req, signal, false, onUpstreamActivity)
|
|
4218
|
+
[Symbol.asyncIterator]: () => this.chatStreamGen(modelID, req, signal, false, onUpstreamActivity, onGatewayRequestID)
|
|
3238
4219
|
};
|
|
3239
4220
|
}
|
|
3240
4221
|
/**
|
|
@@ -3243,13 +4224,14 @@ var Client = class _Client {
|
|
|
3243
4224
|
* 无 started/settled/failed 自定义事件, 无 data: [DONE], message_stop 为自然结束
|
|
3244
4225
|
*
|
|
3245
4226
|
* @param onUpstreamActivity 见 {@link UpstreamActivityCallback}
|
|
4227
|
+
* @param onGatewayRequestID 见 {@link GatewayRequestIDCallback}
|
|
3246
4228
|
*/
|
|
3247
|
-
chatMessagesStream(modelID, req, signal, onUpstreamActivity) {
|
|
4229
|
+
chatMessagesStream(modelID, req, signal, onUpstreamActivity, onGatewayRequestID) {
|
|
3248
4230
|
return {
|
|
3249
|
-
[Symbol.asyncIterator]: () => this.chatMessagesStreamGen(modelID, req, signal, false, onUpstreamActivity)
|
|
4231
|
+
[Symbol.asyncIterator]: () => this.chatMessagesStreamGen(modelID, req, signal, false, onUpstreamActivity, onGatewayRequestID)
|
|
3250
4232
|
};
|
|
3251
4233
|
}
|
|
3252
|
-
async *chatStreamGen(modelID, req, signal, retried, onUpstreamActivity) {
|
|
4234
|
+
async *chatStreamGen(modelID, req, signal, retried, onUpstreamActivity, onGatewayRequestID) {
|
|
3253
4235
|
const r = { ...req, stream: true };
|
|
3254
4236
|
const { body, adapter } = await this.buildChatRequest(modelID, r, signal);
|
|
3255
4237
|
const token = await this.ensureToken(signal);
|
|
@@ -3271,20 +4253,20 @@ var Client = class _Client {
|
|
|
3271
4253
|
throw classifyTransport("POST " + endpoint, url, e);
|
|
3272
4254
|
}
|
|
3273
4255
|
if (resp.status === 401 && !retried) {
|
|
4256
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
4257
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
4258
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
3274
4259
|
try {
|
|
3275
|
-
await
|
|
3276
|
-
} catch {
|
|
3277
|
-
}
|
|
3278
|
-
try {
|
|
3279
|
-
await this.forceRefresh(signal);
|
|
4260
|
+
await this.forceRefresh(signal, token);
|
|
3280
4261
|
} catch (refreshErr) {
|
|
3281
4262
|
throw new Error(
|
|
3282
4263
|
`stream: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
3283
4264
|
);
|
|
3284
4265
|
}
|
|
3285
|
-
yield* this.chatStreamGen(modelID, req, signal, true, onUpstreamActivity);
|
|
4266
|
+
yield* this.chatStreamGen(modelID, req, signal, true, onUpstreamActivity, onGatewayRequestID);
|
|
3286
4267
|
return;
|
|
3287
4268
|
}
|
|
4269
|
+
notifyGatewayRequestID(onGatewayRequestID, resp.headers);
|
|
3288
4270
|
if (!resp.ok) {
|
|
3289
4271
|
const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
|
|
3290
4272
|
throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
@@ -3319,7 +4301,7 @@ var Client = class _Client {
|
|
|
3319
4301
|
}
|
|
3320
4302
|
}
|
|
3321
4303
|
}
|
|
3322
|
-
async *chatMessagesStreamGen(modelID, req, signal, retried, onUpstreamActivity) {
|
|
4304
|
+
async *chatMessagesStreamGen(modelID, req, signal, retried, onUpstreamActivity, onGatewayRequestID) {
|
|
3323
4305
|
const r = { ...req, stream: true };
|
|
3324
4306
|
const { body, adapter } = await this.buildChatRequest(modelID, r, signal);
|
|
3325
4307
|
const token = await this.ensureToken(signal);
|
|
@@ -3341,20 +4323,20 @@ var Client = class _Client {
|
|
|
3341
4323
|
throw classifyTransport("POST " + endpoint, url, e);
|
|
3342
4324
|
}
|
|
3343
4325
|
if (resp.status === 401 && !retried) {
|
|
4326
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
4327
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
4328
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
3344
4329
|
try {
|
|
3345
|
-
await
|
|
3346
|
-
} catch {
|
|
3347
|
-
}
|
|
3348
|
-
try {
|
|
3349
|
-
await this.forceRefresh(signal);
|
|
4330
|
+
await this.forceRefresh(signal, token);
|
|
3350
4331
|
} catch (refreshErr) {
|
|
3351
4332
|
throw new Error(
|
|
3352
4333
|
`messages stream: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
3353
4334
|
);
|
|
3354
4335
|
}
|
|
3355
|
-
yield* this.chatMessagesStreamGen(modelID, req, signal, true, onUpstreamActivity);
|
|
4336
|
+
yield* this.chatMessagesStreamGen(modelID, req, signal, true, onUpstreamActivity, onGatewayRequestID);
|
|
3356
4337
|
return;
|
|
3357
4338
|
}
|
|
4339
|
+
notifyGatewayRequestID(onGatewayRequestID, resp.headers);
|
|
3358
4340
|
if (!resp.ok) {
|
|
3359
4341
|
const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
|
|
3360
4342
|
throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
@@ -3435,7 +4417,9 @@ var Client = class _Client {
|
|
|
3435
4417
|
if (!base.endsWith("/api/v4")) {
|
|
3436
4418
|
base += "/api/v4";
|
|
3437
4419
|
}
|
|
3438
|
-
|
|
4420
|
+
const url = base + path;
|
|
4421
|
+
this.assertCredentialURL(url);
|
|
4422
|
+
return url;
|
|
3439
4423
|
}
|
|
3440
4424
|
/**
|
|
3441
4425
|
* Compliance API URL 拼接。
|
|
@@ -3448,7 +4432,14 @@ var Client = class _Client {
|
|
|
3448
4432
|
*/
|
|
3449
4433
|
complianceURL(path) {
|
|
3450
4434
|
const base = this.complianceBaseURL ?? this.serverURL + "/admin-api";
|
|
3451
|
-
|
|
4435
|
+
const url = base + path;
|
|
4436
|
+
this.assertCredentialURL(url);
|
|
4437
|
+
return url;
|
|
4438
|
+
}
|
|
4439
|
+
assertCredentialURL(url) {
|
|
4440
|
+
if (this.credentialMode !== "legacy" && new URL(url).origin !== this.credentialAuthority) {
|
|
4441
|
+
throw new Error("credential request authority changed");
|
|
4442
|
+
}
|
|
3452
4443
|
}
|
|
3453
4444
|
/** GET/POST/... 通用 JSON 调用 (返回 result 已 typed) */
|
|
3454
4445
|
async doJSON(method, path, body, signal) {
|
|
@@ -3477,12 +4468,11 @@ var Client = class _Client {
|
|
|
3477
4468
|
ctl.signal
|
|
3478
4469
|
);
|
|
3479
4470
|
if (resp.status === 401 && !retried) {
|
|
4471
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
4472
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
4473
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
3480
4474
|
try {
|
|
3481
|
-
await
|
|
3482
|
-
} catch {
|
|
3483
|
-
}
|
|
3484
|
-
try {
|
|
3485
|
-
await this.forceRefresh(ctl.signal);
|
|
4475
|
+
await this.forceRefresh(ctl.signal, token);
|
|
3486
4476
|
} catch (refreshErr) {
|
|
3487
4477
|
throw new Error(
|
|
3488
4478
|
`unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
@@ -3547,12 +4537,11 @@ var Client = class _Client {
|
|
|
3547
4537
|
ctl.signal
|
|
3548
4538
|
);
|
|
3549
4539
|
if (resp.status === 401 && !retried) {
|
|
4540
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
4541
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
4542
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
3550
4543
|
try {
|
|
3551
|
-
await
|
|
3552
|
-
} catch {
|
|
3553
|
-
}
|
|
3554
|
-
try {
|
|
3555
|
-
await this.forceRefresh(ctl.signal);
|
|
4544
|
+
await this.forceRefresh(ctl.signal, token);
|
|
3556
4545
|
} catch (refreshErr) {
|
|
3557
4546
|
throw new Error(
|
|
3558
4547
|
`unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
@@ -3627,6 +4616,7 @@ var Client = class _Client {
|
|
|
3627
4616
|
* 6 处原始 fetch() 全部走此 helper
|
|
3628
4617
|
*/
|
|
3629
4618
|
async doRequest(req, signal) {
|
|
4619
|
+
if (new Headers(req.headers).has("Authorization")) this.assertCredentialURL(req.url);
|
|
3630
4620
|
try {
|
|
3631
4621
|
return await this.fetchImpl(req.url, {
|
|
3632
4622
|
method: req.method,
|
|
@@ -4624,12 +5614,11 @@ async function uploadSkillInternal(c, zipData, scope, intent, retried, signal) {
|
|
|
4624
5614
|
throw classifyTransport("POST /skill-store/upload", url, e);
|
|
4625
5615
|
}
|
|
4626
5616
|
if (resp.status === 401 && !retried) {
|
|
5617
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
5618
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
5619
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
4627
5620
|
try {
|
|
4628
|
-
await
|
|
4629
|
-
} catch {
|
|
4630
|
-
}
|
|
4631
|
-
try {
|
|
4632
|
-
await c.forceRefresh(ctl.signal);
|
|
5621
|
+
await c.forceRefresh(ctl.signal, token);
|
|
4633
5622
|
} catch (refreshErr) {
|
|
4634
5623
|
throw new Error(
|
|
4635
5624
|
`upload: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
@@ -4800,9 +5789,7 @@ Client.prototype.updateNotificationPreference = async function(typeCode, pref, s
|
|
|
4800
5789
|
|
|
4801
5790
|
// src/notifications/ws.ts
|
|
4802
5791
|
Client.prototype.connect = async function(cfg, signal) {
|
|
4803
|
-
|
|
4804
|
-
await this.disconnect();
|
|
4805
|
-
}
|
|
5792
|
+
const oldDisconnect = this.ws ? this.disconnect() : Promise.resolve();
|
|
4806
5793
|
const noop = () => {
|
|
4807
5794
|
};
|
|
4808
5795
|
const filledCfg = {
|
|
@@ -4818,21 +5805,31 @@ Client.prototype.connect = async function(cfg, signal) {
|
|
|
4818
5805
|
const done = new Promise((r) => {
|
|
4819
5806
|
resolveDone = r;
|
|
4820
5807
|
});
|
|
4821
|
-
const
|
|
5808
|
+
const abort2 = new AbortController();
|
|
4822
5809
|
if (signal) {
|
|
4823
|
-
if (signal.aborted)
|
|
4824
|
-
else signal.addEventListener("abort", () =>
|
|
5810
|
+
if (signal.aborted) abort2.abort();
|
|
5811
|
+
else signal.addEventListener("abort", () => abort2.abort());
|
|
4825
5812
|
}
|
|
4826
5813
|
const ws = {
|
|
4827
5814
|
conn: null,
|
|
4828
5815
|
cfg: filledCfg,
|
|
4829
|
-
abort,
|
|
5816
|
+
abort: abort2,
|
|
4830
5817
|
done,
|
|
4831
5818
|
doneResolve: resolveDone,
|
|
4832
|
-
connected: false
|
|
5819
|
+
connected: false,
|
|
5820
|
+
owner: null
|
|
4833
5821
|
};
|
|
4834
|
-
await wsConnectOnce(this, ws);
|
|
4835
5822
|
this.ws = ws;
|
|
5823
|
+
try {
|
|
5824
|
+
await oldDisconnect;
|
|
5825
|
+
await assertCurrent(this, ws);
|
|
5826
|
+
await wsConnectOnce(this, ws);
|
|
5827
|
+
} catch (error) {
|
|
5828
|
+
ws.abort.abort();
|
|
5829
|
+
if (this.ws === ws) this.ws = null;
|
|
5830
|
+
ws.doneResolve();
|
|
5831
|
+
throw error;
|
|
5832
|
+
}
|
|
4836
5833
|
void wsLoop(this, ws);
|
|
4837
5834
|
};
|
|
4838
5835
|
Client.prototype.disconnect = async function() {
|
|
@@ -4870,15 +5867,47 @@ function getWebSocketCtor() {
|
|
|
4870
5867
|
}
|
|
4871
5868
|
return WSCtor;
|
|
4872
5869
|
}
|
|
5870
|
+
function sameOwner(a, b) {
|
|
5871
|
+
return a?.storeInstanceId === b?.storeInstanceId && a?.authSessionId === b?.authSessionId && a?.principal?.issuer === b?.principal?.issuer && a?.principal?.subject === b?.principal?.subject && a?.principal?.organizationId === b?.principal?.organizationId;
|
|
5872
|
+
}
|
|
5873
|
+
async function readOwner(c, signal) {
|
|
5874
|
+
if (c.credentialMode !== "versioned") return null;
|
|
5875
|
+
const snapshot = await c.getCredentialSnapshot(signal);
|
|
5876
|
+
return {
|
|
5877
|
+
storeInstanceId: snapshot.storeInstanceId,
|
|
5878
|
+
authSessionId: snapshot.authSessionId,
|
|
5879
|
+
principal: snapshot.principal
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
async function assertCurrent(c, ws) {
|
|
5883
|
+
if (ws.abort.signal.aborted || c.ws !== ws) throw new Error("websocket connection superseded");
|
|
5884
|
+
if (ws.owner !== null && !sameOwner(ws.owner, await readOwner(c, ws.abort.signal))) {
|
|
5885
|
+
ws.abort.abort();
|
|
5886
|
+
throw new Error("websocket credential owner changed");
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
4873
5889
|
async function wsConnectOnce(c, ws) {
|
|
5890
|
+
const observedOwner = await readOwner(c, ws.abort.signal);
|
|
5891
|
+
if (ws.owner === null) ws.owner = observedOwner;
|
|
5892
|
+
else if (!sameOwner(ws.owner, observedOwner)) throw new Error("websocket credential owner changed");
|
|
5893
|
+
await assertCurrent(c, ws);
|
|
4874
5894
|
const url = wsURL(c);
|
|
4875
5895
|
const WSCtor = getWebSocketCtor();
|
|
4876
|
-
const
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
5896
|
+
const token = await c.ensureToken(ws.abort.signal);
|
|
5897
|
+
await assertCurrent(c, ws);
|
|
5898
|
+
const ticketURL = c.apiURL("/ws/stream-ticket");
|
|
5899
|
+
await assertCurrent(c, ws);
|
|
5900
|
+
const ticketHTTP = await c.doRequest({
|
|
5901
|
+
method: "POST",
|
|
5902
|
+
url: ticketURL,
|
|
5903
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5904
|
+
}, ws.abort.signal);
|
|
5905
|
+
if (!ticketHTTP.ok) {
|
|
5906
|
+
const body = ticketHTTP.body ? await readLimited(ticketHTTP.body, maxErrorBodySize) : new Uint8Array();
|
|
5907
|
+
throw parseHTTPErrorWithHeader(ticketHTTP.status, body, ticketHTTP.headers);
|
|
5908
|
+
}
|
|
5909
|
+
const ticketResp = await ticketHTTP.json();
|
|
5910
|
+
await assertCurrent(c, ws);
|
|
4882
5911
|
const ticket = ticketResp.data.ticket;
|
|
4883
5912
|
const u = new URL(url);
|
|
4884
5913
|
u.searchParams.set("ticket", ticket);
|
|
@@ -4899,11 +5928,28 @@ async function wsConnectOnce(c, ws) {
|
|
|
4899
5928
|
reject(new Error("dial: handshake timeout"));
|
|
4900
5929
|
}
|
|
4901
5930
|
}, 3e4);
|
|
5931
|
+
const abortHandshake = () => {
|
|
5932
|
+
clearTimeout(handshakeTimer);
|
|
5933
|
+
try {
|
|
5934
|
+
conn.close();
|
|
5935
|
+
} catch {
|
|
5936
|
+
}
|
|
5937
|
+
reject(new Error("websocket connection aborted"));
|
|
5938
|
+
};
|
|
5939
|
+
ws.abort.signal.addEventListener("abort", abortHandshake, { once: true });
|
|
4902
5940
|
conn.addEventListener("open", () => {
|
|
5941
|
+
if (ws.abort.signal.aborted || c.ws !== ws) {
|
|
5942
|
+
try {
|
|
5943
|
+
conn.close();
|
|
5944
|
+
} catch {
|
|
5945
|
+
}
|
|
5946
|
+
return;
|
|
5947
|
+
}
|
|
4903
5948
|
opened = true;
|
|
4904
5949
|
});
|
|
4905
5950
|
conn.addEventListener("error", (e) => {
|
|
4906
5951
|
clearTimeout(handshakeTimer);
|
|
5952
|
+
ws.abort.signal.removeEventListener("abort", abortHandshake);
|
|
4907
5953
|
reject(new Error(`dial: ${e.message ?? "connection error"}`));
|
|
4908
5954
|
});
|
|
4909
5955
|
conn.addEventListener("message", (e) => {
|
|
@@ -4912,6 +5958,7 @@ async function wsConnectOnce(c, ws) {
|
|
|
4912
5958
|
const welcome = JSON.parse(msg);
|
|
4913
5959
|
if (welcome.type !== "welcome") {
|
|
4914
5960
|
clearTimeout(handshakeTimer);
|
|
5961
|
+
ws.abort.signal.removeEventListener("abort", abortHandshake);
|
|
4915
5962
|
try {
|
|
4916
5963
|
conn.close();
|
|
4917
5964
|
} catch {
|
|
@@ -4919,31 +5966,34 @@ async function wsConnectOnce(c, ws) {
|
|
|
4919
5966
|
reject(new Error(`unexpected first message: ${welcome.type}`));
|
|
4920
5967
|
return;
|
|
4921
5968
|
}
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
JSON.stringify({
|
|
4929
|
-
type: "subscribe",
|
|
4930
|
-
topics: ws.cfg.topics
|
|
4931
|
-
})
|
|
4932
|
-
);
|
|
4933
|
-
} catch (sendErr) {
|
|
4934
|
-
ws.conn = null;
|
|
4935
|
-
ws.connected = false;
|
|
5969
|
+
void assertCurrent(c, ws).then(() => {
|
|
5970
|
+
clearTimeout(handshakeTimer);
|
|
5971
|
+
ws.abort.signal.removeEventListener("abort", abortHandshake);
|
|
5972
|
+
ws.conn = conn;
|
|
5973
|
+
ws.connected = true;
|
|
5974
|
+
if (ws.cfg.topics.length > 0) {
|
|
4936
5975
|
try {
|
|
4937
|
-
conn.
|
|
4938
|
-
|
|
5976
|
+
conn.send(
|
|
5977
|
+
JSON.stringify({
|
|
5978
|
+
type: "subscribe",
|
|
5979
|
+
topics: ws.cfg.topics
|
|
5980
|
+
})
|
|
5981
|
+
);
|
|
5982
|
+
} catch (sendErr) {
|
|
5983
|
+
ws.conn = null;
|
|
5984
|
+
ws.connected = false;
|
|
5985
|
+
try {
|
|
5986
|
+
conn.close();
|
|
5987
|
+
} catch {
|
|
5988
|
+
}
|
|
5989
|
+
reject(new Error(`send subscribe: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}`));
|
|
5990
|
+
return;
|
|
4939
5991
|
}
|
|
4940
|
-
reject(new Error(`send subscribe: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}`));
|
|
4941
|
-
return;
|
|
4942
5992
|
}
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
5993
|
+
ws.cfg.onConnect();
|
|
5994
|
+
console.log(`[acosmi-sdk] websocket connected, connId=${welcome.connId ?? ""}`);
|
|
5995
|
+
resolve();
|
|
5996
|
+
}).catch(reject);
|
|
4947
5997
|
} catch (parseErr) {
|
|
4948
5998
|
clearTimeout(handshakeTimer);
|
|
4949
5999
|
try {
|
|
@@ -4958,7 +6008,7 @@ async function wsConnectOnce(c, ws) {
|
|
|
4958
6008
|
async function wsLoop(c, ws) {
|
|
4959
6009
|
try {
|
|
4960
6010
|
while (true) {
|
|
4961
|
-
await wsReadLoop(ws);
|
|
6011
|
+
await wsReadLoop(c, ws);
|
|
4962
6012
|
if (ws.abort.signal.aborted) return;
|
|
4963
6013
|
if (ws.conn) {
|
|
4964
6014
|
try {
|
|
@@ -4989,7 +6039,7 @@ async function wsLoop(c, ws) {
|
|
|
4989
6039
|
ws.doneResolve();
|
|
4990
6040
|
}
|
|
4991
6041
|
}
|
|
4992
|
-
async function wsReadLoop(ws) {
|
|
6042
|
+
async function wsReadLoop(c, ws) {
|
|
4993
6043
|
const conn = ws.conn;
|
|
4994
6044
|
if (!conn) return;
|
|
4995
6045
|
return new Promise((resolve) => {
|
|
@@ -4997,10 +6047,17 @@ async function wsReadLoop(ws) {
|
|
|
4997
6047
|
try {
|
|
4998
6048
|
const data = e.data;
|
|
4999
6049
|
const event = JSON.parse(data);
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
6050
|
+
void assertCurrent(c, ws).then(() => {
|
|
6051
|
+
try {
|
|
6052
|
+
ws.cfg.onEvent(event);
|
|
6053
|
+
} catch {
|
|
6054
|
+
}
|
|
6055
|
+
}).catch(() => {
|
|
6056
|
+
try {
|
|
6057
|
+
conn.close();
|
|
6058
|
+
} catch {
|
|
6059
|
+
}
|
|
6060
|
+
});
|
|
5004
6061
|
} catch {
|
|
5005
6062
|
}
|
|
5006
6063
|
};
|
|
@@ -5213,6 +6270,7 @@ function isTerminalRemoteEvent(ev) {
|
|
|
5213
6270
|
}
|
|
5214
6271
|
|
|
5215
6272
|
// src/agent-runs/client.ts
|
|
6273
|
+
init_errors();
|
|
5216
6274
|
var agentRunsByClient = /* @__PURE__ */ new WeakMap();
|
|
5217
6275
|
Object.defineProperty(Client.prototype, "agentRuns", {
|
|
5218
6276
|
configurable: true,
|
|
@@ -5592,11 +6650,10 @@ var AgentRunsClient = class {
|
|
|
5592
6650
|
}
|
|
5593
6651
|
const resp = await this.client.doRequest({ method, url, headers, body: bodyStr }, signal);
|
|
5594
6652
|
if (resp.status === 401 && opts.retryOn401 && !retried) {
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
await this.client.forceRefresh(signal);
|
|
6653
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
6654
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
6655
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
6656
|
+
await this.client.forceRefresh(signal, token);
|
|
5600
6657
|
return this.requestRawInner(method, path, body, signal, opts, true);
|
|
5601
6658
|
}
|
|
5602
6659
|
if (resp.status < 200 || resp.status >= 300) {
|
|
@@ -6223,6 +7280,7 @@ function isComplianceBusinessError(err) {
|
|
|
6223
7280
|
}
|
|
6224
7281
|
|
|
6225
7282
|
// src/compliance/client.ts
|
|
7283
|
+
init_errors();
|
|
6226
7284
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
6227
7285
|
Object.defineProperty(Client.prototype, "compliance", {
|
|
6228
7286
|
configurable: true,
|
|
@@ -7039,11 +8097,10 @@ var ComplianceClient = class {
|
|
|
7039
8097
|
}
|
|
7040
8098
|
const resp = await this.client.doRequest({ method, url, headers, body: bodyStr }, signal);
|
|
7041
8099
|
if (resp.status === 401 && opts.retryOn401 && !retried) {
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
await this.client.forceRefresh(signal);
|
|
8100
|
+
const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
|
|
8101
|
+
const authError = parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
|
|
8102
|
+
if (!isUserAccessTokenRejected(authError)) throw authError;
|
|
8103
|
+
await this.client.forceRefresh(signal, token);
|
|
7047
8104
|
return this.executeJsonInner(method, path, body, signal, opts, true);
|
|
7048
8105
|
}
|
|
7049
8106
|
if (resp.status < 200 || resp.status >= 300) {
|
|
@@ -7871,6 +8928,7 @@ exports.FilterStatusFallbackTkdistSkew = FilterStatusFallbackTkdistSkew;
|
|
|
7871
8928
|
exports.FilterStatusInternalBypass = FilterStatusInternalBypass;
|
|
7872
8929
|
exports.FilterStatusOK = FilterStatusOK;
|
|
7873
8930
|
exports.FilterStatusUnknown = FilterStatusUnknown;
|
|
8931
|
+
exports.GATEWAY_REQUEST_ID_HEADER = GATEWAY_REQUEST_ID_HEADER;
|
|
7874
8932
|
exports.IdempotencyKeyHeader = IdempotencyKeyHeader;
|
|
7875
8933
|
exports.InMemoryTokenStore = InMemoryTokenStore;
|
|
7876
8934
|
exports.LocalStorageTokenStore = LocalStorageTokenStore;
|
|
@@ -7963,6 +9021,7 @@ exports.isRegion = isRegion;
|
|
|
7963
9021
|
exports.isSSECommentLine = isSSECommentLine;
|
|
7964
9022
|
exports.isSSLError = isSSLError;
|
|
7965
9023
|
exports.isTerminalRemoteEvent = isTerminalRemoteEvent;
|
|
9024
|
+
exports.isUserAccessTokenRejected = isUserAccessTokenRejected;
|
|
7966
9025
|
exports.isValidTokenSet = isValidTokenSet;
|
|
7967
9026
|
exports.isWindowLimitError = isWindowLimitError;
|
|
7968
9027
|
exports.isWindowLimitStreamError = isWindowLimitStreamError;
|
|
@@ -7980,6 +9039,7 @@ exports.parseNotificationEvent = parseNotificationEvent;
|
|
|
7980
9039
|
exports.parseRemoteControlEvent = parseRemoteControlEvent;
|
|
7981
9040
|
exports.parseSettlement = parseSettlement;
|
|
7982
9041
|
exports.parseSourcesEvent = parseSourcesEvent;
|
|
9042
|
+
exports.readGatewayErrorContract = readGatewayErrorContract;
|
|
7983
9043
|
exports.refreshToken = refreshToken;
|
|
7984
9044
|
exports.register = register;
|
|
7985
9045
|
exports.registerWebOAuthClient = registerWebOAuthClient;
|