@acosmi/sdk-ts 2.0.1 → 2.2.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 +34 -0
- package/README.md +296 -52
- package/dist/browser/index.mjs +462 -8
- package/dist/browser/index.mjs.map +1 -1
- package/dist/index.mjs +462 -8
- package/dist/index.mjs.map +1 -1
- package/dist/node/adapters/anthropic.cjs.map +1 -1
- package/dist/node/adapters/anthropic.d.cts +1 -1
- package/dist/node/adapters/anthropic.d.ts +1 -1
- package/dist/node/adapters/anthropic.mjs.map +1 -1
- package/dist/node/adapters/openai.cjs.map +1 -1
- package/dist/node/adapters/openai.d.cts +2 -2
- package/dist/node/adapters/openai.d.ts +2 -2
- package/dist/node/adapters/openai.mjs.map +1 -1
- package/dist/node/{index-C9X5Yuyk.d.cts → index-CI7FM3xe.d.cts} +43 -1
- package/dist/node/{index-C9X5Yuyk.d.ts → index-CI7FM3xe.d.ts} +43 -1
- package/dist/node/index.cjs +480 -7
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +474 -8
- package/dist/node/index.d.ts +474 -8
- package/dist/node/index.mjs +462 -8
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/{openai-BqKAhbSc.d.ts → openai-oeY7dGNW.d.ts} +1 -1
- package/dist/node/{openai-C_UKadSX.d.cts → openai-xKHChcmG.d.cts} +1 -1
- package/docs//345/274/200/345/217/221/344/270/216/345/217/221/345/270/203/346/211/213/345/206/214.md +115 -20
- package/package.json +1 -1
package/dist/node/index.cjs
CHANGED
|
@@ -1012,6 +1012,21 @@ function apiResponseBusinessError(r) {
|
|
|
1012
1012
|
|
|
1013
1013
|
// src/auth/auth.ts
|
|
1014
1014
|
var authTimeoutMs = 3e4;
|
|
1015
|
+
var OAuthTokenEndpointError = class extends Error {
|
|
1016
|
+
status;
|
|
1017
|
+
oauthError;
|
|
1018
|
+
errorDescription;
|
|
1019
|
+
constructor(status, oauthError, errorDescription) {
|
|
1020
|
+
super(`token: HTTP ${status}: ${errorDescription || oauthError}`);
|
|
1021
|
+
this.name = "OAuthTokenEndpointError";
|
|
1022
|
+
this.status = status;
|
|
1023
|
+
this.oauthError = oauthError;
|
|
1024
|
+
this.errorDescription = errorDescription;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
function isInvalidGrantError(err) {
|
|
1028
|
+
return err instanceof OAuthTokenEndpointError && err.oauthError === "invalid_grant";
|
|
1029
|
+
}
|
|
1015
1030
|
async function discoverWithProfile(serverURL, profile, signal) {
|
|
1016
1031
|
let parsed;
|
|
1017
1032
|
try {
|
|
@@ -1388,7 +1403,9 @@ async function postToken(endpoint, data, signal) {
|
|
|
1388
1403
|
errBody = await resp.json();
|
|
1389
1404
|
} catch {
|
|
1390
1405
|
}
|
|
1391
|
-
|
|
1406
|
+
const oauthError = typeof errBody.error === "string" ? errBody.error : "";
|
|
1407
|
+
const errorDescription = typeof errBody.error_description === "string" ? errBody.error_description : "";
|
|
1408
|
+
throw new OAuthTokenEndpointError(resp.status, oauthError, errorDescription);
|
|
1392
1409
|
}
|
|
1393
1410
|
try {
|
|
1394
1411
|
return await resp.json();
|
|
@@ -1998,6 +2015,53 @@ var FilterStatusFallbackTkdistSkew = "fallback-tkdist-deployment-skew";
|
|
|
1998
2015
|
var FilterStatusFallbackNoBuckets = "fallback-no-buckets";
|
|
1999
2016
|
var FilterStatusFallbackMissingUser = "fallback-missing-userid";
|
|
2000
2017
|
var FilterStatusUnknown = "";
|
|
2018
|
+
var DEFAULT_GATEWAY_BASE_URL = "https://acosmi.com";
|
|
2019
|
+
function normalizeGatewayBaseURL(input) {
|
|
2020
|
+
if (typeof input !== "string") {
|
|
2021
|
+
throw new TypeError(
|
|
2022
|
+
"Acosmi Gateway URL must be a string (see docs/audit/sdk-remote-control-contract-2026-05-27.md \xA72)"
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
const trimmed = input.trim();
|
|
2026
|
+
if (trimmed.length === 0) {
|
|
2027
|
+
throw new Error("Acosmi Gateway URL is empty");
|
|
2028
|
+
}
|
|
2029
|
+
let parsed;
|
|
2030
|
+
try {
|
|
2031
|
+
parsed = new URL(trimmed);
|
|
2032
|
+
} catch {
|
|
2033
|
+
throw new Error(`Acosmi Gateway URL is not a valid URL: ${trimmed}`);
|
|
2034
|
+
}
|
|
2035
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2036
|
+
throw new Error(
|
|
2037
|
+
`Acosmi Gateway URL only allows http/https, got ${parsed.protocol} (${trimmed}). CrabCode --sdk-url (ws/wss) is the RemoteIO session channel, not a SDK gateway URL \u2014 see docs/audit/sdk-remote-control-contract-2026-05-27.md \xA71.`
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
if (!parsed.host) {
|
|
2041
|
+
throw new Error(`Acosmi Gateway URL has empty host: ${trimmed}`);
|
|
2042
|
+
}
|
|
2043
|
+
if (parsed.search.length > 0 || parsed.hash.length > 0) {
|
|
2044
|
+
throw new Error(`Acosmi Gateway URL must not contain query or hash: ${trimmed}`);
|
|
2045
|
+
}
|
|
2046
|
+
const path = parsed.pathname.replace(/\/+$/, "");
|
|
2047
|
+
return path ? `${parsed.origin}${path}` : parsed.origin;
|
|
2048
|
+
}
|
|
2049
|
+
function pickAndNormalizeGatewayURL(cfg) {
|
|
2050
|
+
const inputs = [];
|
|
2051
|
+
if (cfg.serverURL !== void 0) inputs.push(["serverURL", cfg.serverURL]);
|
|
2052
|
+
if (cfg.baseURL !== void 0) inputs.push(["baseURL", cfg.baseURL]);
|
|
2053
|
+
if (cfg.baseUrl !== void 0) inputs.push(["baseUrl", cfg.baseUrl]);
|
|
2054
|
+
if (inputs.length === 0) return null;
|
|
2055
|
+
const normalized = inputs.map(([n, v]) => [n, normalizeGatewayBaseURL(v)]);
|
|
2056
|
+
for (let i = 1; i < normalized.length; i++) {
|
|
2057
|
+
if (normalized[i][1] !== normalized[0][1]) {
|
|
2058
|
+
throw new Error(
|
|
2059
|
+
`Acosmi Gateway URL conflict: Config.${normalized[0][0]}=${normalized[0][1]} vs Config.${normalized[i][0]}=${normalized[i][1]} \u2014 pass only one field.`
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
return normalized[0][1];
|
|
2064
|
+
}
|
|
2001
2065
|
var ErrOAuthCORSBlocked = "oauth_cors_blocked";
|
|
2002
2066
|
var ErrRefreshProxyFailed = "refresh_proxy_failed";
|
|
2003
2067
|
var ErrTokenExpired = "token_expired";
|
|
@@ -2055,7 +2119,8 @@ var Client = class _Client {
|
|
|
2055
2119
|
/** 串行化锁 (替代 Go sync.Mutex) */
|
|
2056
2120
|
coefMu = Promise.resolve();
|
|
2057
2121
|
constructor(cfg = {}) {
|
|
2058
|
-
|
|
2122
|
+
const picked = pickAndNormalizeGatewayURL(cfg);
|
|
2123
|
+
this.serverURL = picked ?? DEFAULT_GATEWAY_BASE_URL;
|
|
2059
2124
|
this.complianceBaseURL = cfg.complianceBaseURL ? cfg.complianceBaseURL.replace(/\/+$/, "") : null;
|
|
2060
2125
|
this.oauthMetadataProfile = cfg.oauthMetadataProfile ?? "desktop";
|
|
2061
2126
|
this.browserRefreshMode = cfg.browserRefreshMode ?? "direct";
|
|
@@ -2090,6 +2155,20 @@ var Client = class _Client {
|
|
|
2090
2155
|
isAuthorized() {
|
|
2091
2156
|
return this.tokens != null;
|
|
2092
2157
|
}
|
|
2158
|
+
/**
|
|
2159
|
+
* 返回归一化后的 Acosmi Gateway URL (= `serverURL` 字段). readonly helper.
|
|
2160
|
+
*
|
|
2161
|
+
* Phase 0 §2 红线:
|
|
2162
|
+
* - 不要 mutate `client.serverURL` 字段实现 base 切换; 用 per-base Client 实例.
|
|
2163
|
+
* - 该值仅供日志/排查/上层缓存键使用, 不重新 normalize 它 (构造期已 normalize).
|
|
2164
|
+
*/
|
|
2165
|
+
getServerURL() {
|
|
2166
|
+
return this.serverURL;
|
|
2167
|
+
}
|
|
2168
|
+
/** `getServerURL()` 的 alias — Phase 0 §2 baseURL 与 serverURL 同义. */
|
|
2169
|
+
getBaseURL() {
|
|
2170
|
+
return this.serverURL;
|
|
2171
|
+
}
|
|
2093
2172
|
/** 当前 token 信息 (用于 CLI whoami 显示) */
|
|
2094
2173
|
getTokenSet() {
|
|
2095
2174
|
return this.tokens;
|
|
@@ -2356,6 +2435,10 @@ var Client = class _Client {
|
|
|
2356
2435
|
);
|
|
2357
2436
|
} catch (e) {
|
|
2358
2437
|
const message = e instanceof Error ? e.message : String(e);
|
|
2438
|
+
if (isInvalidGrantError(e)) {
|
|
2439
|
+
await this.clearInvalidRefreshToken();
|
|
2440
|
+
throw new Error(`refresh token invalid; local tokens cleared: ${message}`);
|
|
2441
|
+
}
|
|
2359
2442
|
if (isLikelyBrowserOAuthCORSError(message)) {
|
|
2360
2443
|
throw new Error(`${ErrOAuthCORSBlocked}: refresh token: ${message}`);
|
|
2361
2444
|
}
|
|
@@ -2390,11 +2473,18 @@ var Client = class _Client {
|
|
|
2390
2473
|
}
|
|
2391
2474
|
if (!resp.ok) {
|
|
2392
2475
|
let message = "";
|
|
2476
|
+
let oauthError = "";
|
|
2393
2477
|
try {
|
|
2394
2478
|
const body2 = await resp.json();
|
|
2395
2479
|
if (typeof body2.error === "string") message = body2.error;
|
|
2480
|
+
if (typeof body2.error === "string") oauthError = body2.error;
|
|
2481
|
+
if (typeof body2.error_description === "string") message = body2.error_description;
|
|
2396
2482
|
} catch {
|
|
2397
2483
|
}
|
|
2484
|
+
if (oauthError === "invalid_grant") {
|
|
2485
|
+
await this.clearInvalidRefreshToken();
|
|
2486
|
+
throw new Error(`${ErrRefreshProxyFailed}: refresh token invalid; local tokens cleared`);
|
|
2487
|
+
}
|
|
2398
2488
|
throw new Error(`${ErrRefreshProxyFailed}: HTTP ${resp.status}: ${message}`);
|
|
2399
2489
|
}
|
|
2400
2490
|
let body;
|
|
@@ -2421,6 +2511,20 @@ var Client = class _Client {
|
|
|
2421
2511
|
);
|
|
2422
2512
|
}
|
|
2423
2513
|
}
|
|
2514
|
+
async clearInvalidRefreshToken() {
|
|
2515
|
+
this.tokens = null;
|
|
2516
|
+
this.meta = null;
|
|
2517
|
+
this.loginInFlight = false;
|
|
2518
|
+
this.tokenReady = newDeferred();
|
|
2519
|
+
this.tokenReadyResolved = false;
|
|
2520
|
+
try {
|
|
2521
|
+
await this.store.clear();
|
|
2522
|
+
} catch (e) {
|
|
2523
|
+
console.warn(
|
|
2524
|
+
`[acosmi-sdk] warning: clear invalid token failed: ${e instanceof Error ? e.message : String(e)}`
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2424
2528
|
/** 互斥锁 helper (替代 Go sync.Mutex) */
|
|
2425
2529
|
withMu(fn) {
|
|
2426
2530
|
const next = this.mu.then(fn, fn);
|
|
@@ -2626,6 +2730,60 @@ var Client = class _Client {
|
|
|
2626
2730
|
ctl.dispose();
|
|
2627
2731
|
}
|
|
2628
2732
|
}
|
|
2733
|
+
// ===========================================================================
|
|
2734
|
+
// 媒体生成 (v1.3+) — 图片 / 视频生成托管模型 (与 chat 同网关)
|
|
2735
|
+
//
|
|
2736
|
+
// 仅对 capabilities.supports_image_generation / supports_video_generation 的模型有效。
|
|
2737
|
+
// 网关不算钱; 用量由网关上报营销系统结算。
|
|
2738
|
+
// ===========================================================================
|
|
2739
|
+
/** 解析 nexus-v4 {code,message,data} 信封, code!=0 抛 BusinessError, 返回 data。 */
|
|
2740
|
+
unwrapAPIResponse(result) {
|
|
2741
|
+
const env = JSON.parse(new TextDecoder().decode(result));
|
|
2742
|
+
const bizErr = apiResponseBusinessError(env);
|
|
2743
|
+
if (bizErr) throw bizErr;
|
|
2744
|
+
return env.data;
|
|
2745
|
+
}
|
|
2746
|
+
/**
|
|
2747
|
+
* 图片生成 (同步)。POST /managed-models/:id/images/generations
|
|
2748
|
+
*
|
|
2749
|
+
* @param modelID 图片生成托管模型 ID (capabilities.supports_image_generation=true)
|
|
2750
|
+
*/
|
|
2751
|
+
async generateImage(modelID, req, signal) {
|
|
2752
|
+
const endpoint = `/managed-models/${encodeURIComponent(modelID)}/images/generations`;
|
|
2753
|
+
const { result } = await this.doJSONFullRaw(
|
|
2754
|
+
"POST",
|
|
2755
|
+
endpoint,
|
|
2756
|
+
req,
|
|
2757
|
+
signal,
|
|
2758
|
+
CHAT_REQUEST_TIMEOUT_MS
|
|
2759
|
+
);
|
|
2760
|
+
return this.unwrapAPIResponse(result);
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* 创建视频生成任务 (异步)。POST /managed-models/:id/videos/generations
|
|
2764
|
+
* 返回 taskId; 用 pollVideoTask() 轮询直到 status=completed。
|
|
2765
|
+
* 上报真物理量 (视频秒数) 需在 pollVideoTask 时回传 req.duration。
|
|
2766
|
+
*
|
|
2767
|
+
* @param modelID 视频生成托管模型 ID (capabilities.supports_video_generation=true)
|
|
2768
|
+
*/
|
|
2769
|
+
async generateVideo(modelID, req, signal) {
|
|
2770
|
+
const endpoint = `/managed-models/${encodeURIComponent(modelID)}/videos/generations`;
|
|
2771
|
+
const { result } = await this.doJSONFullRaw("POST", endpoint, req, signal);
|
|
2772
|
+
return this.unwrapAPIResponse(result);
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* 轮询视频任务状态。GET /managed-models/:id/videos/tasks/:taskId
|
|
2776
|
+
*
|
|
2777
|
+
* @param durationSeconds 创建时的时长 (秒), 透传给网关在 completed 时上报用量。
|
|
2778
|
+
*/
|
|
2779
|
+
async pollVideoTask(modelID, taskID, durationSeconds, signal) {
|
|
2780
|
+
let endpoint = `/managed-models/${encodeURIComponent(modelID)}/videos/tasks/${encodeURIComponent(taskID)}`;
|
|
2781
|
+
if (durationSeconds != null && durationSeconds > 0) {
|
|
2782
|
+
endpoint += `?duration=${encodeURIComponent(String(durationSeconds))}`;
|
|
2783
|
+
}
|
|
2784
|
+
const { result } = await this.doJSONFullRaw("GET", endpoint, null, signal);
|
|
2785
|
+
return this.unwrapAPIResponse(result);
|
|
2786
|
+
}
|
|
2629
2787
|
/**
|
|
2630
2788
|
* Anthropic 原生格式同步聊天
|
|
2631
2789
|
* v0.5.0: 根据 provider 自动路由
|
|
@@ -2970,11 +3128,11 @@ var Client = class _Client {
|
|
|
2970
3128
|
}
|
|
2971
3129
|
}
|
|
2972
3130
|
/** doJSONFull 的 raw bytes 变体 (chat 用, 不立即 JSON.parse) */
|
|
2973
|
-
async doJSONFullRaw(method, path, body, signal) {
|
|
2974
|
-
return this.doJSONFullRawInternal(method, path, body, signal, false);
|
|
3131
|
+
async doJSONFullRaw(method, path, body, signal, timeoutMs = 3e4) {
|
|
3132
|
+
return this.doJSONFullRawInternal(method, path, body, signal, false, timeoutMs);
|
|
2975
3133
|
}
|
|
2976
|
-
async doJSONFullRawInternal(method, path, body, signal, retried) {
|
|
2977
|
-
const ctl = withRequestTimeout(
|
|
3134
|
+
async doJSONFullRawInternal(method, path, body, signal, retried, timeoutMs = 3e4) {
|
|
3135
|
+
const ctl = withRequestTimeout(timeoutMs, signal);
|
|
2978
3136
|
try {
|
|
2979
3137
|
const token = await this.ensureToken(ctl.signal);
|
|
2980
3138
|
let bodyStr = null;
|
|
@@ -3002,7 +3160,7 @@ var Client = class _Client {
|
|
|
3002
3160
|
`unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
|
|
3003
3161
|
);
|
|
3004
3162
|
}
|
|
3005
|
-
return this.doJSONFullRawInternal(method, path, body, signal, true);
|
|
3163
|
+
return this.doJSONFullRawInternal(method, path, body, signal, true, timeoutMs);
|
|
3006
3164
|
}
|
|
3007
3165
|
if (resp.status < 200 || resp.status >= 300) {
|
|
3008
3166
|
const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
|
|
@@ -3619,6 +3777,10 @@ function complianceErrorToRetryAdvice(info) {
|
|
|
3619
3777
|
var ScopeAI = "ai";
|
|
3620
3778
|
var ScopeSkills = "skills";
|
|
3621
3779
|
var ScopeAccount = "account";
|
|
3780
|
+
var ScopeRemoteControl = "remote_control";
|
|
3781
|
+
var ScopeRemoteControlAgentRun = "remote_control:agent-run";
|
|
3782
|
+
var ScopeRemoteControlSessionControl = "remote_control:session-control";
|
|
3783
|
+
var ScopeRemoteControlPermissionResponse = "remote_control:permission-response";
|
|
3622
3784
|
var ScopeModels = "models";
|
|
3623
3785
|
var ScopeModelsChat = "models:chat";
|
|
3624
3786
|
var ScopeEntitlements = "entitlements";
|
|
@@ -3641,6 +3803,9 @@ function commerceScopes() {
|
|
|
3641
3803
|
function skillScopes() {
|
|
3642
3804
|
return [ScopeSkills];
|
|
3643
3805
|
}
|
|
3806
|
+
function remoteControlScopes() {
|
|
3807
|
+
return [ScopeRemoteControl];
|
|
3808
|
+
}
|
|
3644
3809
|
|
|
3645
3810
|
// src/models/index.ts
|
|
3646
3811
|
init_types();
|
|
@@ -4486,6 +4651,136 @@ var AgentRunStreamError = class extends Error {
|
|
|
4486
4651
|
}
|
|
4487
4652
|
};
|
|
4488
4653
|
|
|
4654
|
+
// src/agent-runs/remote-control.ts
|
|
4655
|
+
function asRecord(v) {
|
|
4656
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return null;
|
|
4657
|
+
return v;
|
|
4658
|
+
}
|
|
4659
|
+
function str(rec, key) {
|
|
4660
|
+
const v = rec[key];
|
|
4661
|
+
return typeof v === "string" ? v : void 0;
|
|
4662
|
+
}
|
|
4663
|
+
function num(rec, key) {
|
|
4664
|
+
const v = rec[key];
|
|
4665
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
4666
|
+
}
|
|
4667
|
+
function bool(rec, key) {
|
|
4668
|
+
const v = rec[key];
|
|
4669
|
+
return typeof v === "boolean" ? v : void 0;
|
|
4670
|
+
}
|
|
4671
|
+
function parseRemoteControlEvent(raw) {
|
|
4672
|
+
const rec = asRecord(raw);
|
|
4673
|
+
if (!rec) return null;
|
|
4674
|
+
const type = str(rec, "type");
|
|
4675
|
+
if (!type) return null;
|
|
4676
|
+
switch (type) {
|
|
4677
|
+
case "text_delta": {
|
|
4678
|
+
const index = num(rec, "index");
|
|
4679
|
+
const text = str(rec, "text");
|
|
4680
|
+
if (typeof index !== "number" || typeof text !== "string") return null;
|
|
4681
|
+
return { type: "text_delta", index, text };
|
|
4682
|
+
}
|
|
4683
|
+
case "reasoning_delta": {
|
|
4684
|
+
const index = num(rec, "index");
|
|
4685
|
+
const text = str(rec, "text");
|
|
4686
|
+
if (typeof index !== "number" || typeof text !== "string") return null;
|
|
4687
|
+
return { type: "reasoning_delta", index, text };
|
|
4688
|
+
}
|
|
4689
|
+
case "tool_call": {
|
|
4690
|
+
const toolCallId = str(rec, "tool_call_id") ?? str(rec, "toolCallId");
|
|
4691
|
+
const name = str(rec, "name");
|
|
4692
|
+
if (!toolCallId || !name) return null;
|
|
4693
|
+
return {
|
|
4694
|
+
type: "tool_call",
|
|
4695
|
+
toolCallId,
|
|
4696
|
+
name,
|
|
4697
|
+
input: rec["input"],
|
|
4698
|
+
source: str(rec, "source")
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
case "tool_result": {
|
|
4702
|
+
const toolCallId = str(rec, "tool_call_id") ?? str(rec, "toolCallId");
|
|
4703
|
+
const ok = bool(rec, "ok");
|
|
4704
|
+
if (!toolCallId || typeof ok !== "boolean") return null;
|
|
4705
|
+
return {
|
|
4706
|
+
type: "tool_result",
|
|
4707
|
+
toolCallId,
|
|
4708
|
+
ok,
|
|
4709
|
+
output: rec["output"],
|
|
4710
|
+
error: str(rec, "error")
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
case "permission_request": {
|
|
4714
|
+
const requestId = str(rec, "request_id") ?? str(rec, "requestId");
|
|
4715
|
+
const kind = str(rec, "kind");
|
|
4716
|
+
if (!requestId || !kind) return null;
|
|
4717
|
+
return {
|
|
4718
|
+
type: "permission_request",
|
|
4719
|
+
requestId,
|
|
4720
|
+
kind,
|
|
4721
|
+
payload: rec["payload"],
|
|
4722
|
+
deadlineMs: num(rec, "deadline_ms") ?? num(rec, "deadlineMs")
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
case "permission_result": {
|
|
4726
|
+
const requestId = str(rec, "request_id") ?? str(rec, "requestId");
|
|
4727
|
+
const decision = str(rec, "decision");
|
|
4728
|
+
if (!requestId || !decision) return null;
|
|
4729
|
+
return {
|
|
4730
|
+
type: "permission_result",
|
|
4731
|
+
requestId,
|
|
4732
|
+
decision,
|
|
4733
|
+
actor: str(rec, "actor"),
|
|
4734
|
+
decidedAt: str(rec, "decided_at") ?? str(rec, "decidedAt")
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
case "usage": {
|
|
4738
|
+
return {
|
|
4739
|
+
type: "usage",
|
|
4740
|
+
inputTokens: num(rec, "input_tokens") ?? num(rec, "inputTokens"),
|
|
4741
|
+
outputTokens: num(rec, "output_tokens") ?? num(rec, "outputTokens"),
|
|
4742
|
+
cacheRead: num(rec, "cache_read") ?? num(rec, "cacheRead"),
|
|
4743
|
+
cacheCreate: num(rec, "cache_create") ?? num(rec, "cacheCreate"),
|
|
4744
|
+
exact: bool(rec, "exact")
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
case "settle": {
|
|
4748
|
+
const status = str(rec, "status");
|
|
4749
|
+
if (!status) return null;
|
|
4750
|
+
return { type: "settle", status, billed: bool(rec, "billed") };
|
|
4751
|
+
}
|
|
4752
|
+
case "status": {
|
|
4753
|
+
const phase = str(rec, "phase");
|
|
4754
|
+
if (!phase) return null;
|
|
4755
|
+
return { type: "status", phase, message: str(rec, "message") };
|
|
4756
|
+
}
|
|
4757
|
+
case "error": {
|
|
4758
|
+
const code = str(rec, "code");
|
|
4759
|
+
const message = str(rec, "message");
|
|
4760
|
+
if (!code || !message) return null;
|
|
4761
|
+
return {
|
|
4762
|
+
type: "error",
|
|
4763
|
+
code,
|
|
4764
|
+
message,
|
|
4765
|
+
retryable: bool(rec, "retryable"),
|
|
4766
|
+
kind: str(rec, "kind")
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4769
|
+
case "done": {
|
|
4770
|
+
const reason = str(rec, "reason");
|
|
4771
|
+
const runId = str(rec, "run_id") ?? str(rec, "runId");
|
|
4772
|
+
const finalStatus = str(rec, "final_status") ?? str(rec, "finalStatus");
|
|
4773
|
+
if (!reason || !runId || !finalStatus) return null;
|
|
4774
|
+
return { type: "done", reason, runId, finalStatus };
|
|
4775
|
+
}
|
|
4776
|
+
default:
|
|
4777
|
+
return null;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
function isTerminalRemoteEvent(ev) {
|
|
4781
|
+
return ev.type === "done" || ev.type === "settle";
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4489
4784
|
// src/agent-runs/client.ts
|
|
4490
4785
|
var agentRunsByClient = /* @__PURE__ */ new WeakMap();
|
|
4491
4786
|
Object.defineProperty(Client.prototype, "agentRuns", {
|
|
@@ -4538,6 +4833,62 @@ var AgentRunsClient = class {
|
|
|
4538
4833
|
{ retryOn401: false }
|
|
4539
4834
|
).then(fromWireRun);
|
|
4540
4835
|
}
|
|
4836
|
+
/**
|
|
4837
|
+
* Create a CrabCode remote-control agent run (contract §3, ADR-2 + ADR-5).
|
|
4838
|
+
*
|
|
4839
|
+
* Equivalent to `create()` with `runtime: 'crabcode_remote'` and the required
|
|
4840
|
+
* `runner` + `adapter` fields set. Per-session policies (permission/workspace)
|
|
4841
|
+
* are forwarded to the gateway, which is the only side allowed to enforce
|
|
4842
|
+
* them (contract §6). The SDK never enforces remote permissions client-side.
|
|
4843
|
+
*
|
|
4844
|
+
* The corresponding stream uses `streamRemoteControl(runId)`, NOT `stream()`,
|
|
4845
|
+
* because the event union is different (contract §4).
|
|
4846
|
+
*/
|
|
4847
|
+
async createRemoteRun(req, signal) {
|
|
4848
|
+
if (req.runtime !== "crabcode_remote") {
|
|
4849
|
+
throw new Error('createRemoteRun: runtime must be "crabcode_remote"');
|
|
4850
|
+
}
|
|
4851
|
+
if (!req.runner) throw new Error("createRemoteRun: runner is required");
|
|
4852
|
+
if (!req.adapter) throw new Error("createRemoteRun: adapter is required");
|
|
4853
|
+
return this.create(req, signal);
|
|
4854
|
+
}
|
|
4855
|
+
/**
|
|
4856
|
+
* Stream remote-control events for a CrabCode remote run (contract §4).
|
|
4857
|
+
*
|
|
4858
|
+
* Yields the canonical 11-event union: text_delta / reasoning_delta /
|
|
4859
|
+
* tool_call / tool_result / permission_request / permission_result /
|
|
4860
|
+
* usage / settle / status / error / done.
|
|
4861
|
+
*
|
|
4862
|
+
* Iteration ends naturally when a terminal event (`done` or `settle`) is
|
|
4863
|
+
* observed. Per contract §4, `error` alone is non-terminal — terminal errors
|
|
4864
|
+
* are carried by `done.reason` / `done.final_status`.
|
|
4865
|
+
*
|
|
4866
|
+
* Unknown event types and malformed frames are silently skipped (warn-only),
|
|
4867
|
+
* matching `parseRemoteControlEvent`'s null-return contract.
|
|
4868
|
+
*/
|
|
4869
|
+
streamRemoteControl(runId, signal) {
|
|
4870
|
+
return {
|
|
4871
|
+
[Symbol.asyncIterator]: () => this.streamRemoteControlGen(runId, signal)
|
|
4872
|
+
};
|
|
4873
|
+
}
|
|
4874
|
+
async *streamRemoteControlGen(runId, signal) {
|
|
4875
|
+
const resp = await this.requestRaw(
|
|
4876
|
+
"GET",
|
|
4877
|
+
`/agent-runs/${encodeURIComponent(runId)}/stream`,
|
|
4878
|
+
null,
|
|
4879
|
+
signal,
|
|
4880
|
+
{ retryOn401: true, accept: "text/event-stream" }
|
|
4881
|
+
);
|
|
4882
|
+
if (!resp.body) {
|
|
4883
|
+
throw new Error("remote-control stream: empty response body");
|
|
4884
|
+
}
|
|
4885
|
+
for await (const rawEvent of readAgentRunSSEFrames(resp.body)) {
|
|
4886
|
+
const ev = parseRemoteControlEvent(rawEvent);
|
|
4887
|
+
if (!ev) continue;
|
|
4888
|
+
yield ev;
|
|
4889
|
+
if (isTerminalRemoteEvent(ev)) return;
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4541
4892
|
listArtifacts(runId, signal) {
|
|
4542
4893
|
return this.requestAPI(
|
|
4543
4894
|
"GET",
|
|
@@ -4697,6 +5048,24 @@ var AgentRunsClient = class {
|
|
|
4697
5048
|
return resp;
|
|
4698
5049
|
}
|
|
4699
5050
|
};
|
|
5051
|
+
function toWirePermissionPolicy(p) {
|
|
5052
|
+
return {
|
|
5053
|
+
shell_allowed: p.shellAllowed,
|
|
5054
|
+
shell_deny_list: p.shellDenyList,
|
|
5055
|
+
network_allowed: p.networkAllowed,
|
|
5056
|
+
write_allowed: p.writeAllowed,
|
|
5057
|
+
approval_timeout_ms: p.approvalTimeoutMs,
|
|
5058
|
+
required_actors: p.requiredActors
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
function toWireWorkspacePolicy(p) {
|
|
5062
|
+
return {
|
|
5063
|
+
read_only: p.readOnly,
|
|
5064
|
+
allowed_paths: p.allowedPaths,
|
|
5065
|
+
denied_paths: p.deniedPaths,
|
|
5066
|
+
max_bytes: p.maxBytes
|
|
5067
|
+
};
|
|
5068
|
+
}
|
|
4700
5069
|
function toWireCreateRequest(req) {
|
|
4701
5070
|
return {
|
|
4702
5071
|
app_id: req.appId,
|
|
@@ -4714,6 +5083,11 @@ function toWireCreateRequest(req) {
|
|
|
4714
5083
|
max_bytes: req.localContextPolicy.maxBytes,
|
|
4715
5084
|
allowed_tools: req.localContextPolicy.allowedTools
|
|
4716
5085
|
} : void 0,
|
|
5086
|
+
runtime: req.runtime,
|
|
5087
|
+
runner: req.runner,
|
|
5088
|
+
adapter: req.adapter,
|
|
5089
|
+
permission_policy: req.permissionPolicy ? toWirePermissionPolicy(req.permissionPolicy) : void 0,
|
|
5090
|
+
workspace_policy: req.workspacePolicy ? toWireWorkspacePolicy(req.workspacePolicy) : void 0,
|
|
4717
5091
|
artifact_policy: req.artifactPolicy ? {
|
|
4718
5092
|
enabled: req.artifactPolicy.enabled,
|
|
4719
5093
|
max_files: req.artifactPolicy.maxFiles
|
|
@@ -4759,6 +5133,46 @@ function fromWireArtifact(resp) {
|
|
|
4759
5133
|
metadata: resp.metadata
|
|
4760
5134
|
};
|
|
4761
5135
|
}
|
|
5136
|
+
async function* readAgentRunSSEFrames(body) {
|
|
5137
|
+
let eventName = "";
|
|
5138
|
+
let dataLines = [];
|
|
5139
|
+
const flush = () => {
|
|
5140
|
+
if (dataLines.length === 0) return null;
|
|
5141
|
+
const data = dataLines.join("\n");
|
|
5142
|
+
dataLines = [];
|
|
5143
|
+
if (data === "[DONE]") return null;
|
|
5144
|
+
try {
|
|
5145
|
+
const parsed = JSON.parse(data);
|
|
5146
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5147
|
+
const obj = parsed;
|
|
5148
|
+
if (!("type" in obj) && eventName) {
|
|
5149
|
+
obj.type = eventName;
|
|
5150
|
+
}
|
|
5151
|
+
return obj;
|
|
5152
|
+
}
|
|
5153
|
+
} catch {
|
|
5154
|
+
}
|
|
5155
|
+
return null;
|
|
5156
|
+
};
|
|
5157
|
+
for await (const line of iterSSELines(body)) {
|
|
5158
|
+
if (line === "") {
|
|
5159
|
+
const frame2 = flush();
|
|
5160
|
+
eventName = "";
|
|
5161
|
+
if (frame2) yield frame2;
|
|
5162
|
+
continue;
|
|
5163
|
+
}
|
|
5164
|
+
if (line.startsWith(":")) continue;
|
|
5165
|
+
if (line.startsWith("event:")) {
|
|
5166
|
+
eventName = line.slice("event:".length).trim();
|
|
5167
|
+
continue;
|
|
5168
|
+
}
|
|
5169
|
+
if (line.startsWith("data:")) {
|
|
5170
|
+
dataLines.push(line.slice("data:".length).trimStart());
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
const frame = flush();
|
|
5174
|
+
if (frame) yield frame;
|
|
5175
|
+
}
|
|
4762
5176
|
async function* readAgentRunEvents(body) {
|
|
4763
5177
|
let eventName = "";
|
|
4764
5178
|
let dataLines = [];
|
|
@@ -6148,6 +6562,11 @@ Client.prototype.listUserSubscriptions = async function(signal) {
|
|
|
6148
6562
|
);
|
|
6149
6563
|
return Array.isArray(resp.data) ? resp.data : [];
|
|
6150
6564
|
};
|
|
6565
|
+
Client.prototype.getPlanByCode = async function(planCode, signal) {
|
|
6566
|
+
if (!planCode) return null;
|
|
6567
|
+
const plans = await this.listPlans(void 0, signal);
|
|
6568
|
+
return plans.find((p) => p.planCode === planCode) ?? null;
|
|
6569
|
+
};
|
|
6151
6570
|
|
|
6152
6571
|
// src/pricing/client.ts
|
|
6153
6572
|
Client.prototype.getPricingConfig = async function(key, signal) {
|
|
@@ -6521,6 +6940,44 @@ Client.prototype.listMyCorporateTransfers = async function(signal) {
|
|
|
6521
6940
|
return resp.data ?? [];
|
|
6522
6941
|
};
|
|
6523
6942
|
|
|
6943
|
+
// src/chatbridge/types.ts
|
|
6944
|
+
var ALL_PLATFORMS = [
|
|
6945
|
+
"feishu",
|
|
6946
|
+
"wecom",
|
|
6947
|
+
"dingtalk",
|
|
6948
|
+
"slack",
|
|
6949
|
+
"teams",
|
|
6950
|
+
"telegram",
|
|
6951
|
+
"whatsapp"
|
|
6952
|
+
];
|
|
6953
|
+
var ALL_REGIONS = ["cn", "intl"];
|
|
6954
|
+
var ALL_INTEGRATION_STATUS = [
|
|
6955
|
+
"pending",
|
|
6956
|
+
"active",
|
|
6957
|
+
"suspended",
|
|
6958
|
+
"revoked"
|
|
6959
|
+
];
|
|
6960
|
+
function isPlatform(v) {
|
|
6961
|
+
return typeof v === "string" && ALL_PLATFORMS.includes(v);
|
|
6962
|
+
}
|
|
6963
|
+
function isRegion(v) {
|
|
6964
|
+
return typeof v === "string" && ALL_REGIONS.includes(v);
|
|
6965
|
+
}
|
|
6966
|
+
function isIntegrationStatus(v) {
|
|
6967
|
+
return typeof v === "string" && ALL_INTEGRATION_STATUS.includes(v);
|
|
6968
|
+
}
|
|
6969
|
+
function isChannelInboundEvent(v) {
|
|
6970
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
6971
|
+
const r = v;
|
|
6972
|
+
return isPlatform(r.platform) && typeof r.threadHash === "string" && r.threadHash.length > 0 && typeof r.content === "string";
|
|
6973
|
+
}
|
|
6974
|
+
function asCredentialRef(s) {
|
|
6975
|
+
return s;
|
|
6976
|
+
}
|
|
6977
|
+
|
|
6978
|
+
exports.ALL_INTEGRATION_STATUS = ALL_INTEGRATION_STATUS;
|
|
6979
|
+
exports.ALL_PLATFORMS = ALL_PLATFORMS;
|
|
6980
|
+
exports.ALL_REGIONS = ALL_REGIONS;
|
|
6524
6981
|
exports.AgentRunStreamError = AgentRunStreamError;
|
|
6525
6982
|
exports.AgentRunsClient = AgentRunsClient;
|
|
6526
6983
|
exports.AudienceEnum = AudienceEnum;
|
|
@@ -6528,6 +6985,7 @@ exports.BillingModeEnum = BillingModeEnum;
|
|
|
6528
6985
|
exports.Client = Client;
|
|
6529
6986
|
exports.ComplianceClient = ComplianceClient;
|
|
6530
6987
|
exports.CompliancePollError = CompliancePollError;
|
|
6988
|
+
exports.DEFAULT_GATEWAY_BASE_URL = DEFAULT_GATEWAY_BASE_URL;
|
|
6531
6989
|
exports.DefaultRetryPolicy = DefaultRetryPolicy;
|
|
6532
6990
|
exports.ErrAuthDenied = ErrAuthDenied;
|
|
6533
6991
|
exports.ErrBillingCallbackCannotCommit = ErrBillingCallbackCannotCommit;
|
|
@@ -6571,6 +7029,7 @@ exports.FilterStatusUnknown = FilterStatusUnknown;
|
|
|
6571
7029
|
exports.IdempotencyKeyHeader = IdempotencyKeyHeader;
|
|
6572
7030
|
exports.InMemoryTokenStore = InMemoryTokenStore;
|
|
6573
7031
|
exports.LocalStorageTokenStore = LocalStorageTokenStore;
|
|
7032
|
+
exports.OAuthTokenEndpointError = OAuthTokenEndpointError;
|
|
6574
7033
|
exports.ProductFamilyEnum = ProductFamilyEnum;
|
|
6575
7034
|
exports.RETRY_ADVICE_REASONS = RETRY_ADVICE_REASONS;
|
|
6576
7035
|
exports.RegionScopeEnum = RegionScopeEnum;
|
|
@@ -6595,6 +7054,10 @@ exports.ScopeEntitlements = ScopeEntitlements;
|
|
|
6595
7054
|
exports.ScopeModels = ScopeModels;
|
|
6596
7055
|
exports.ScopeModelsChat = ScopeModelsChat;
|
|
6597
7056
|
exports.ScopeProfile = ScopeProfile;
|
|
7057
|
+
exports.ScopeRemoteControl = ScopeRemoteControl;
|
|
7058
|
+
exports.ScopeRemoteControlAgentRun = ScopeRemoteControlAgentRun;
|
|
7059
|
+
exports.ScopeRemoteControlPermissionResponse = ScopeRemoteControlPermissionResponse;
|
|
7060
|
+
exports.ScopeRemoteControlSessionControl = ScopeRemoteControlSessionControl;
|
|
6598
7061
|
exports.ScopeSkillStore = ScopeSkillStore;
|
|
6599
7062
|
exports.ScopeSkills = ScopeSkills;
|
|
6600
7063
|
exports.ScopeTokenPackages = ScopeTokenPackages;
|
|
@@ -6608,6 +7071,7 @@ exports.anthropicResponseThinkingContent = anthropicResponseThinkingContent;
|
|
|
6608
7071
|
exports.anthropicResponseToolUseBlocks = anthropicResponseToolUseBlocks;
|
|
6609
7072
|
exports.apiResponseBusinessError = apiResponseBusinessError;
|
|
6610
7073
|
exports.apiResponseGetMessage = apiResponseGetMessage;
|
|
7074
|
+
exports.asCredentialRef = asCredentialRef;
|
|
6611
7075
|
exports.authorize = authorize;
|
|
6612
7076
|
exports.bucketInfoIsCommercial = bucketInfoIsCommercial;
|
|
6613
7077
|
exports.bucketRowIsCommercial = bucketRowIsCommercial;
|
|
@@ -6634,10 +7098,16 @@ exports.generateState = generateState;
|
|
|
6634
7098
|
exports.getAdapter = getAdapter;
|
|
6635
7099
|
exports.getAdapterForModel = getAdapterForModel;
|
|
6636
7100
|
exports.isBillingConfirmable = isBillingConfirmable;
|
|
7101
|
+
exports.isChannelInboundEvent = isChannelInboundEvent;
|
|
6637
7102
|
exports.isComplianceBusinessError = isComplianceBusinessError;
|
|
6638
7103
|
exports.isComplianceTerminalError = isComplianceTerminalError;
|
|
7104
|
+
exports.isIntegrationStatus = isIntegrationStatus;
|
|
7105
|
+
exports.isInvalidGrantError = isInvalidGrantError;
|
|
7106
|
+
exports.isPlatform = isPlatform;
|
|
7107
|
+
exports.isRegion = isRegion;
|
|
6639
7108
|
exports.isSSECommentLine = isSSECommentLine;
|
|
6640
7109
|
exports.isSSLError = isSSLError;
|
|
7110
|
+
exports.isTerminalRemoteEvent = isTerminalRemoteEvent;
|
|
6641
7111
|
exports.maxEndUserIdLength = maxEndUserIdLength;
|
|
6642
7112
|
exports.modelScopes = modelScopes;
|
|
6643
7113
|
exports.modelSupportsImageInput = modelSupportsImageInput;
|
|
@@ -6646,12 +7116,15 @@ exports.newFileTokenStore = newFileTokenStore;
|
|
|
6646
7116
|
exports.newThinkingConfig = newThinkingConfig;
|
|
6647
7117
|
exports.newTokenSet = newTokenSet;
|
|
6648
7118
|
exports.newWebSearchTool = newWebSearchTool;
|
|
7119
|
+
exports.normalizeGatewayBaseURL = normalizeGatewayBaseURL;
|
|
6649
7120
|
exports.parseNotificationEvent = parseNotificationEvent;
|
|
7121
|
+
exports.parseRemoteControlEvent = parseRemoteControlEvent;
|
|
6650
7122
|
exports.parseSettlement = parseSettlement;
|
|
6651
7123
|
exports.parseSourcesEvent = parseSourcesEvent;
|
|
6652
7124
|
exports.refreshToken = refreshToken;
|
|
6653
7125
|
exports.register = register;
|
|
6654
7126
|
exports.registerWebOAuthClient = registerWebOAuthClient;
|
|
7127
|
+
exports.remoteControlScopes = remoteControlScopes;
|
|
6655
7128
|
exports.retryReasonForComplianceKey = retryReasonForComplianceKey;
|
|
6656
7129
|
exports.retryReasonForOAuthError = retryReasonForOAuthError;
|
|
6657
7130
|
exports.revokeToken = revokeToken;
|