@acosmi/sdk-ts 2.16.0 → 2.17.0
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 +14 -0
- package/dist/browser/index.mjs +30 -4
- package/dist/browser/index.mjs.map +1 -1
- package/dist/index.mjs +30 -4
- package/dist/index.mjs.map +1 -1
- package/dist/node/index.cjs +31 -3
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +17 -3
- package/dist/node/index.d.ts +17 -3
- package/dist/node/index.mjs +30 -4
- package/dist/node/index.mjs.map +1 -1
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to `@acosmi/sdk-ts` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.17.0] - 2026-08-15 — 桌面 loopback OAuth state 全路径闸 + 端口确定性关闭
|
|
9
|
+
|
|
10
|
+
**回环监听在整个登录期间对本机任意进程开放,state 是唯一的门。** 2.16.0 及更早版本的桌面 loopback `authorize` 不生成也不校验 OAuth `state`(`af2daa0b` 已在源码补上核心校验,但从未发版);且校验只覆盖"带 code"的回调 —— 携带 OAuth error 的回调**绕过 state 直接把登录结算成"用户已拒绝"**,本机恶意进程不猜任何秘密就能打断/塑形一次等待中的登录;重复 `state` 参数取首值即可蒙混(`?code=攻击者的码&state=<正确值>&state=x` 在旧实现下**成功登录到攻击者会话**)。本版把 state 校验提到 `/callback` 所有形态之前,并收紧为"恰好一个"。
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`/callback` 全形态 state 先行校验** —— 成功回调、OAuth error 回调、畸形回调一律先验 state 再消费 code / 结算 denied。缺失、重复(含重复的正确值)、错值均以稳定错误码 `state_mismatch` 拒绝本次登录、返回安全失败页并关闭 listener;错误信息只描述形态,不回显 code / state / token / 完整 callback query。
|
|
15
|
+
- **"恰好一个 state"** —— `searchParams.getAll('state').length === 1` 且严格等值;重复参数不再因 `get()` 取首值而通过。
|
|
16
|
+
- **端口确定性关闭** —— `finally` 里在 `server.close()` 后补 `closeIdleConnections?.()`:Node 18 上 `close()` 不关浏览器残留的 idle keep-alive 连接,端口会滞留到对端松手;现在每条终止路径(成功 / 各类 state 失败 / 用户拒绝 / 超时 / 取消 / 浏览器打开失败)都以端口完全关闭收尾。
|
|
17
|
+
|
|
18
|
+
### 回归闸门
|
|
19
|
+
|
|
20
|
+
- `test/auth/desktop-loopback-state.test.ts` —— 十路终止矩阵:成功、缺失 state、错误 state、重复 state(异值与同值)、恶意先到回调后合法回调不能复活、用户拒绝(正确 state + OAuth error → `auth_denied`)、超时、取消(pre-aborted signal)、浏览器打开失败后手动回调仍可完成;每条路径断言**只结算一次**(事件流恰一条 error / 结果不可翻转)且**新连接最终被拒**(listener teardown)。对 2.16.0 实测:4 条缺口用例全红(其中"重复 state 首值正确"在旧实现下以攻击者 code 成功 resolve),6 条保行为用例双版本皆绿。
|
|
21
|
+
|
|
8
22
|
## [2.16.0] - 2026-08-06 — chat 超时预算真正下传 + 流式活性回调
|
|
9
23
|
|
|
10
24
|
**一个预算只有在被传下去时才存在。** `chat` / `chatMessagesAnthropic` / `chatMessagesOpenAI` 三条推理链路都用 `withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS)` 建了 11 分钟的外层预算,却只把 `ctl.signal` 传给 `doJSONFullRaw` —— 后者的 `timeoutMs` 形参有 **30 秒**默认值,会**另建**一个计时器并恒先于外层触发。于是 v1.6.0 那次"调整为 11min 以容纳 DeepSeek 保活窗口"的改动,**一天都没生效过**,而它上方的注释还在声称它已生效。
|
package/dist/browser/index.mjs
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
5
10
|
};
|
|
6
11
|
var __export = (target, all) => {
|
|
7
12
|
for (var name in all)
|
|
@@ -1322,6 +1327,7 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1322
1327
|
};
|
|
1323
1328
|
const verifier = await generateCodeVerifier();
|
|
1324
1329
|
const challenge = await codeChallenge(verifier);
|
|
1330
|
+
const state = await generateState();
|
|
1325
1331
|
const http = await import('http');
|
|
1326
1332
|
const server = http.createServer();
|
|
1327
1333
|
await new Promise((resolve, reject) => {
|
|
@@ -1337,6 +1343,8 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1337
1343
|
codeResolver = resolve;
|
|
1338
1344
|
codeRejecter = reject;
|
|
1339
1345
|
});
|
|
1346
|
+
codePromise.catch(() => {
|
|
1347
|
+
});
|
|
1340
1348
|
server.on("request", (req, res) => {
|
|
1341
1349
|
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
1342
1350
|
if (url.pathname !== "/callback") {
|
|
@@ -1344,6 +1352,16 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1344
1352
|
res.end();
|
|
1345
1353
|
return;
|
|
1346
1354
|
}
|
|
1355
|
+
const states = url.searchParams.getAll("state");
|
|
1356
|
+
const stateFailure = states.length === 0 ? "callback missing state" : states.length > 1 ? "callback carried multiple state values" : states[0] !== state ? "callback state does not match pending state" : null;
|
|
1357
|
+
if (stateFailure !== null) {
|
|
1358
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1359
|
+
res.end(
|
|
1360
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>\u6388\u6743\u5931\u8D25</title></head><body style="font-family:system-ui,sans-serif;text-align:center;padding:60px 20px"><h2>\u6388\u6743\u5931\u8D25</h2><p>\u56DE\u8C03\u6821\u9A8C\u672A\u901A\u8FC7, \u5DF2\u4E2D\u6B62\u767B\u5F55\u3002</p><p style="color:#888;font-size:14px">\u53EF\u4EE5\u5173\u95ED\u6B64\u7A97\u53E3\u3002</p></body></html>`
|
|
1361
|
+
);
|
|
1362
|
+
codeRejecter(new Error(`authorize: ${ErrStateMismatch}: ${stateFailure} (possible CSRF)`));
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1347
1365
|
const code = url.searchParams.get("code");
|
|
1348
1366
|
if (!code) {
|
|
1349
1367
|
const errMsg = url.searchParams.get("error_description") || url.searchParams.get("error") || "";
|
|
@@ -1374,6 +1392,7 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1374
1392
|
authURL.searchParams.set("response_type", "code");
|
|
1375
1393
|
authURL.searchParams.set("code_challenge", challenge);
|
|
1376
1394
|
authURL.searchParams.set("code_challenge_method", "S256");
|
|
1395
|
+
authURL.searchParams.set("state", state);
|
|
1377
1396
|
if (scopes.length > 0) {
|
|
1378
1397
|
authURL.searchParams.set("scope", scopes.join(" "));
|
|
1379
1398
|
}
|
|
@@ -1411,7 +1430,9 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1411
1430
|
return { result: { code, redirectURI }, verifier };
|
|
1412
1431
|
} catch (e) {
|
|
1413
1432
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1414
|
-
if (msg.includes(
|
|
1433
|
+
if (msg.includes(ErrStateMismatch)) {
|
|
1434
|
+
emit({ type: EventError, err_code: ErrStateMismatch, error: msg });
|
|
1435
|
+
} else if (msg.includes("denied")) {
|
|
1415
1436
|
emit({ type: EventError, err_code: ErrAuthDenied, error: msg });
|
|
1416
1437
|
} else if (msg.includes("timed out")) {
|
|
1417
1438
|
emit({ type: EventError, err_code: ErrTimeout, error: msg });
|
|
@@ -1422,6 +1443,7 @@ async function authorize(meta, clientID, scopes, opts = {}) {
|
|
|
1422
1443
|
} finally {
|
|
1423
1444
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
1424
1445
|
server.close();
|
|
1446
|
+
server.closeIdleConnections?.();
|
|
1425
1447
|
}
|
|
1426
1448
|
}
|
|
1427
1449
|
function htmlEscape(s) {
|
|
@@ -4169,6 +4191,7 @@ var ScopeChatBridge = "chat_bridge";
|
|
|
4169
4191
|
var ScopeChatBridgeRead = "chat_bridge:read";
|
|
4170
4192
|
var ScopeChatBridgeWrite = "chat_bridge:write";
|
|
4171
4193
|
var ScopeChatBridgeRotate = "chat_bridge:rotate";
|
|
4194
|
+
var ScopeAgentAccessManage = "agent_access:manage";
|
|
4172
4195
|
var ScopeModels = "models";
|
|
4173
4196
|
var ScopeModelsChat = "models:chat";
|
|
4174
4197
|
var ScopeEntitlements = "entitlements";
|
|
@@ -4197,6 +4220,9 @@ function remoteControlScopes() {
|
|
|
4197
4220
|
function chatBridgeScopes() {
|
|
4198
4221
|
return [ScopeChatBridge];
|
|
4199
4222
|
}
|
|
4223
|
+
function agentAccessScopes() {
|
|
4224
|
+
return [ScopeAgentAccessManage];
|
|
4225
|
+
}
|
|
4200
4226
|
|
|
4201
4227
|
// src/models/index.ts
|
|
4202
4228
|
init_types();
|
|
@@ -7786,6 +7812,6 @@ function brandCredential(c) {
|
|
|
7786
7812
|
return c;
|
|
7787
7813
|
}
|
|
7788
7814
|
|
|
7789
|
-
export { AGENT_RUN_META_TITLE, AGENT_RUN_META_WORKSPACE, ALL_INTEGRATION_STATUS, ALL_PLATFORMS, ALL_REGIONS, AgentRunStreamError, AgentRunsClient, AnthropicAdapter, AudienceEnum, BillingModeEnum, BucketClassCommercial, BucketClassGeneric, BusinessError, CHAT_REQUEST_TIMEOUT_MS, ChatBridgeClient, Client, ComplianceClient, CompliancePollError, CrabCodeByokClient, DEFAULT_API_TIMEOUT_MS, DEFAULT_GATEWAY_BASE_URL, LocalStorageTokenStore as DefaultBrowserTokenStore, DefaultRetryPolicy, ErrAuthDenied, ErrBillingCallbackCannotCommit, ErrBillingCommitRequiresLocalVerify, ErrBillingS2sForbidden, ErrBrowserOpen, ErrComplianceStepUpRequired, ErrDiscovery, ErrEnvelopeGateClosed, ErrOAuthCORSBlocked, ErrProviderNotConfigured, ErrProviderUnknownNoRetry, ErrRefreshProxyFailed, ErrRegistration, ErrSSLProxy, ErrSealApprovalContractHashMismatch, ErrSealApprovalExpired, ErrSealApprovalLocationMismatch, ErrSealApprovalNonceUsed, ErrSealApprovalNotApproved, ErrSealApprovalSealMismatch, ErrSealApprovalTransactorMismatch, ErrSealUseAlreadyConsumed, ErrStateMismatch, ErrTimeout, ErrTokenExchange, ErrTokenExpired, EventAuthURL, EventComplete, EventError, FileTokenStore, FilterStatusAdminBypass, FilterStatusDisabledByFlag, FilterStatusFallbackMissingUser, FilterStatusFallbackNoBuckets, FilterStatusFallbackTkdistError, FilterStatusFallbackTkdistSkew, FilterStatusInternalBypass, FilterStatusOK, FilterStatusUnknown, HTTPError, IdempotencyKeyHeader, InMemoryTokenStore, LocalStorageTokenStore, ModelNotFoundError, NetworkError, OAuthTokenEndpointError, OpenAIAdapter, OrderTerminalError, ProductFamilyEnum, ProviderFormat, RETRY_ADVICE_REASONS, RateLimitError, RegionScopeEnum, ScopeAI, ScopeAccount, ScopeChatBridge, ScopeChatBridgeRead, ScopeChatBridgeRotate, ScopeChatBridgeWrite, ScopeComplianceContractSigningRead, ScopeComplianceContractSigningWrite, ScopeComplianceContractTemplateRead, ScopeComplianceContractTemplateWrite, ScopeComplianceEvidenceRead, ScopeComplianceEvidenceWrite, ScopeComplianceReportsPublish, ScopeComplianceReportsRead, ScopeComplianceReportsWrite, ScopeComplianceSealApprovalApprove, ScopeComplianceSealApprovalRequest, ScopeComplianceSealManage, ScopeComplianceSealUseExecute, ScopeComplianceTimestampIssue, ScopeComplianceTimestampVerify, ScopeEntitlements, ScopeModels, ScopeModelsChat, ScopeProfile, ScopeRemoteControl, ScopeRemoteControlAgentRun, ScopeRemoteControlPermissionResponse, ScopeRemoteControlSessionControl, ScopeSkillStore, ScopeSkills, ScopeTokenPackages, ScopeTools, ScopeToolsExecute, ScopeWallet, ScopeWalletReadonly, ServerToolTypeWebSearch, StreamError, ThinkingHigh, ThinkingHighMinMaxTokens, ThinkingMax, ThinkingMaxFallbackMaxTokens, ThinkingOff, allScopes, anthropicResponseTextContent, anthropicResponseThinkingContent, anthropicResponseToolUseBlocks, apiResponseBusinessError, apiResponseGetMessage, asCredentialRef, authorize, bucketInfoIsCommercial, bucketRowIsCommercial, buildBetas, chatBridgeScopes, classifyComplianceError, classifySourcesEvent, commerceScopes, completeWebAuthorizationRequest, complianceErrorToRetryAdvice, complianceScopes, computeBackoff, createWebAuthorizationRequest, defaultRetryable, defaultSafeToRetry, discover, discoverWebOAuthMetadata, discoverWithProfile, effectivePolicy, exchangeCode, extractAnthropicBlockMeta, fileLockDefaults, findDesktopVisualUnderstandingModel, findFirstModelByInputModality, generateState, getAdapter, getAdapterForModel, getWindowLimitStreamDetails, isBillingConfirmable, isChannelInboundEvent, isComplianceBusinessError, isComplianceTerminalError, isContinuableWindowLimitError, isIntegrationStatus, isInvalidGrantError, isPlatform, isRegion, isSSECommentLine, isSSLError, isTerminalRemoteEvent, isValidTokenSet, isWindowLimitError, isWindowLimitStreamError, maxEndUserIdLength, modelScopes, modelSupportsImageInput, modelSupportsInputModality, newFileTokenStore, newThinkingConfig, newTokenSet, newWebSearchTool, normalizeGatewayBaseURL, normalizeOverrideBaseURL, parseNotificationEvent, parseRemoteControlEvent, parseSettlement, parseSourcesEvent, refreshToken, register, registerWebOAuthClient, remoteControlScopes, retryReasonForComplianceKey, retryReasonForOAuthError, revokeToken, sanitize_exports as sanitize, skillScopes, tokenSetIsExpired, uniqueMerge, validateEndUserId };
|
|
7815
|
+
export { AGENT_RUN_META_TITLE, AGENT_RUN_META_WORKSPACE, ALL_INTEGRATION_STATUS, ALL_PLATFORMS, ALL_REGIONS, AgentRunStreamError, AgentRunsClient, AnthropicAdapter, AudienceEnum, BillingModeEnum, BucketClassCommercial, BucketClassGeneric, BusinessError, CHAT_REQUEST_TIMEOUT_MS, ChatBridgeClient, Client, ComplianceClient, CompliancePollError, CrabCodeByokClient, DEFAULT_API_TIMEOUT_MS, DEFAULT_GATEWAY_BASE_URL, LocalStorageTokenStore as DefaultBrowserTokenStore, DefaultRetryPolicy, ErrAuthDenied, ErrBillingCallbackCannotCommit, ErrBillingCommitRequiresLocalVerify, ErrBillingS2sForbidden, ErrBrowserOpen, ErrComplianceStepUpRequired, ErrDiscovery, ErrEnvelopeGateClosed, ErrOAuthCORSBlocked, ErrProviderNotConfigured, ErrProviderUnknownNoRetry, ErrRefreshProxyFailed, ErrRegistration, ErrSSLProxy, ErrSealApprovalContractHashMismatch, ErrSealApprovalExpired, ErrSealApprovalLocationMismatch, ErrSealApprovalNonceUsed, ErrSealApprovalNotApproved, ErrSealApprovalSealMismatch, ErrSealApprovalTransactorMismatch, ErrSealUseAlreadyConsumed, ErrStateMismatch, ErrTimeout, ErrTokenExchange, ErrTokenExpired, EventAuthURL, EventComplete, EventError, FileTokenStore, FilterStatusAdminBypass, FilterStatusDisabledByFlag, FilterStatusFallbackMissingUser, FilterStatusFallbackNoBuckets, FilterStatusFallbackTkdistError, FilterStatusFallbackTkdistSkew, FilterStatusInternalBypass, FilterStatusOK, FilterStatusUnknown, HTTPError, IdempotencyKeyHeader, InMemoryTokenStore, LocalStorageTokenStore, ModelNotFoundError, NetworkError, OAuthTokenEndpointError, OpenAIAdapter, OrderTerminalError, ProductFamilyEnum, ProviderFormat, RETRY_ADVICE_REASONS, RateLimitError, RegionScopeEnum, ScopeAI, ScopeAccount, ScopeAgentAccessManage, ScopeChatBridge, ScopeChatBridgeRead, ScopeChatBridgeRotate, ScopeChatBridgeWrite, ScopeComplianceContractSigningRead, ScopeComplianceContractSigningWrite, ScopeComplianceContractTemplateRead, ScopeComplianceContractTemplateWrite, ScopeComplianceEvidenceRead, ScopeComplianceEvidenceWrite, ScopeComplianceReportsPublish, ScopeComplianceReportsRead, ScopeComplianceReportsWrite, ScopeComplianceSealApprovalApprove, ScopeComplianceSealApprovalRequest, ScopeComplianceSealManage, ScopeComplianceSealUseExecute, ScopeComplianceTimestampIssue, ScopeComplianceTimestampVerify, ScopeEntitlements, ScopeModels, ScopeModelsChat, ScopeProfile, ScopeRemoteControl, ScopeRemoteControlAgentRun, ScopeRemoteControlPermissionResponse, ScopeRemoteControlSessionControl, ScopeSkillStore, ScopeSkills, ScopeTokenPackages, ScopeTools, ScopeToolsExecute, ScopeWallet, ScopeWalletReadonly, ServerToolTypeWebSearch, StreamError, ThinkingHigh, ThinkingHighMinMaxTokens, ThinkingMax, ThinkingMaxFallbackMaxTokens, ThinkingOff, agentAccessScopes, allScopes, anthropicResponseTextContent, anthropicResponseThinkingContent, anthropicResponseToolUseBlocks, apiResponseBusinessError, apiResponseGetMessage, asCredentialRef, authorize, bucketInfoIsCommercial, bucketRowIsCommercial, buildBetas, chatBridgeScopes, classifyComplianceError, classifySourcesEvent, commerceScopes, completeWebAuthorizationRequest, complianceErrorToRetryAdvice, complianceScopes, computeBackoff, createWebAuthorizationRequest, defaultRetryable, defaultSafeToRetry, discover, discoverWebOAuthMetadata, discoverWithProfile, effectivePolicy, exchangeCode, extractAnthropicBlockMeta, fileLockDefaults, findDesktopVisualUnderstandingModel, findFirstModelByInputModality, generateState, getAdapter, getAdapterForModel, getWindowLimitStreamDetails, isBillingConfirmable, isChannelInboundEvent, isComplianceBusinessError, isComplianceTerminalError, isContinuableWindowLimitError, isIntegrationStatus, isInvalidGrantError, isPlatform, isRegion, isSSECommentLine, isSSLError, isTerminalRemoteEvent, isValidTokenSet, isWindowLimitError, isWindowLimitStreamError, maxEndUserIdLength, modelScopes, modelSupportsImageInput, modelSupportsInputModality, newFileTokenStore, newThinkingConfig, newTokenSet, newWebSearchTool, normalizeGatewayBaseURL, normalizeOverrideBaseURL, parseNotificationEvent, parseRemoteControlEvent, parseSettlement, parseSourcesEvent, refreshToken, register, registerWebOAuthClient, remoteControlScopes, retryReasonForComplianceKey, retryReasonForOAuthError, revokeToken, sanitize_exports as sanitize, skillScopes, tokenSetIsExpired, uniqueMerge, validateEndUserId };
|
|
7790
7816
|
//# sourceMappingURL=index.mjs.map
|
|
7791
7817
|
//# sourceMappingURL=index.mjs.map
|