@tencent-ai/cloud-agent-sdk 0.2.5 → 0.2.6
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/dist/index.cjs +963 -515
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -25
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +79 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +963 -515
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.6.tgz +0 -0
- package/package.json +2 -2
- package/dist/tencent-ai-cloud-agent-sdk-0.2.5.tgz +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1526,12 +1526,13 @@ var StreamableHttpClient = class {
|
|
|
1526
1526
|
* - Emit 'disconnected' event when connection becomes unhealthy so provider can clean up cache
|
|
1527
1527
|
*/
|
|
1528
1528
|
var CloudAgentConnection = class {
|
|
1529
|
-
constructor(agentId, config) {
|
|
1529
|
+
constructor(agentId, config, cwd = "/workspace") {
|
|
1530
1530
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1531
1531
|
this.onceListeners = /* @__PURE__ */ new Map();
|
|
1532
1532
|
this._isStreaming = false;
|
|
1533
1533
|
this.transport = "cloud";
|
|
1534
1534
|
this.agentId = agentId;
|
|
1535
|
+
this.cwd = cwd;
|
|
1535
1536
|
this.client = new StreamableHttpClient({
|
|
1536
1537
|
endpoint: config.endpoint,
|
|
1537
1538
|
authToken: config.authToken,
|
|
@@ -1687,13 +1688,13 @@ var CloudAgentConnection = class {
|
|
|
1687
1688
|
}
|
|
1688
1689
|
async createSession(params) {
|
|
1689
1690
|
return {
|
|
1690
|
-
...await this.client.loadSession(this.agentId,
|
|
1691
|
+
...await this.client.loadSession(this.agentId, this.cwd),
|
|
1691
1692
|
sessionId: this.agentId
|
|
1692
1693
|
};
|
|
1693
1694
|
}
|
|
1694
1695
|
async loadSession(params) {
|
|
1695
1696
|
if (!params.sessionId) throw new Error("sessionId is required for loadSession");
|
|
1696
|
-
return this.client.loadSession(params.sessionId,
|
|
1697
|
+
return this.client.loadSession(params.sessionId, this.cwd);
|
|
1697
1698
|
}
|
|
1698
1699
|
async setSessionMode(sessionId, modeId) {
|
|
1699
1700
|
return this.client.setSessionMode({
|
|
@@ -1852,7 +1853,6 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1852
1853
|
*/
|
|
1853
1854
|
static async connect(info) {
|
|
1854
1855
|
return new E2BFilesystem(await e2b.Sandbox.connect(info.sandboxId, {
|
|
1855
|
-
apiKey: info.apiKey,
|
|
1856
1856
|
domain: info.domain,
|
|
1857
1857
|
apiUrl: info.apiUrl,
|
|
1858
1858
|
requestTimeoutMs: info.requestTimeoutMs,
|
|
@@ -1896,6 +1896,49 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1896
1896
|
}
|
|
1897
1897
|
};
|
|
1898
1898
|
|
|
1899
|
+
//#endregion
|
|
1900
|
+
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
1901
|
+
/**
|
|
1902
|
+
* 并发执行任务,返回包含成功/失败状态的结果
|
|
1903
|
+
*
|
|
1904
|
+
* @param tasks - 任务函数数组
|
|
1905
|
+
* @param concurrency - 最大并发数,默认 5
|
|
1906
|
+
* @returns 所有任务的结果数组,包含状态信息
|
|
1907
|
+
*
|
|
1908
|
+
* @example
|
|
1909
|
+
* ```typescript
|
|
1910
|
+
* const results = await runWithConcurrencySettled(tasks, 3);
|
|
1911
|
+
* const successes = results.filter(r => r.status === 'fulfilled');
|
|
1912
|
+
* const failures = results.filter(r => r.status === 'rejected');
|
|
1913
|
+
* ```
|
|
1914
|
+
*/
|
|
1915
|
+
async function runWithConcurrencySettled(tasks, concurrency = 5) {
|
|
1916
|
+
if (tasks.length === 0) return [];
|
|
1917
|
+
const limit = Math.max(1, concurrency);
|
|
1918
|
+
const results = new Array(tasks.length);
|
|
1919
|
+
let currentIndex = 0;
|
|
1920
|
+
async function runNext() {
|
|
1921
|
+
while (currentIndex < tasks.length) {
|
|
1922
|
+
const index = currentIndex++;
|
|
1923
|
+
const task = tasks[index];
|
|
1924
|
+
try {
|
|
1925
|
+
results[index] = {
|
|
1926
|
+
status: "fulfilled",
|
|
1927
|
+
value: await task()
|
|
1928
|
+
};
|
|
1929
|
+
} catch (reason) {
|
|
1930
|
+
results[index] = {
|
|
1931
|
+
status: "rejected",
|
|
1932
|
+
reason
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const workers = Array(Math.min(limit, tasks.length)).fill(null).map(() => runNext());
|
|
1938
|
+
await Promise.all(workers);
|
|
1939
|
+
return results;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1899
1942
|
//#endregion
|
|
1900
1943
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cos-upload-service.ts
|
|
1901
1944
|
/**
|
|
@@ -1906,6 +1949,7 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1906
1949
|
* const service = new CosUploadService({
|
|
1907
1950
|
* request: (method, path, body) => cloudProvider.request(method, path, body),
|
|
1908
1951
|
* logger: console,
|
|
1952
|
+
* uploadConcurrency: 3,
|
|
1909
1953
|
* });
|
|
1910
1954
|
*
|
|
1911
1955
|
* const result = await service.uploadFile(file);
|
|
@@ -1919,6 +1963,7 @@ var CosUploadService = class {
|
|
|
1919
1963
|
this.request = options.request;
|
|
1920
1964
|
this.logger = options.logger;
|
|
1921
1965
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
1966
|
+
this.uploadConcurrency = options.uploadConcurrency ?? 3;
|
|
1922
1967
|
}
|
|
1923
1968
|
/**
|
|
1924
1969
|
* 生成唯一的 objectKey
|
|
@@ -1929,13 +1974,13 @@ var CosUploadService = class {
|
|
|
1929
1974
|
return `uploads/${Date.now()}-${Math.random().toString(36).substring(2, 10)}-${encodeURIComponent(filename)}`;
|
|
1930
1975
|
}
|
|
1931
1976
|
/**
|
|
1932
|
-
*
|
|
1977
|
+
* 批量获取预签名 URL
|
|
1933
1978
|
*
|
|
1934
|
-
* POST /
|
|
1979
|
+
* POST /conversations/presigned_url
|
|
1935
1980
|
*/
|
|
1936
|
-
async
|
|
1937
|
-
const response = await this.request("POST", "/
|
|
1938
|
-
if (!response.ok) throw new Error(`Failed to get presigned
|
|
1981
|
+
async getPresignedUrls(objectKeys) {
|
|
1982
|
+
const response = await this.request("POST", "/conversations/presigned_url", { object_keys: objectKeys });
|
|
1983
|
+
if (!response.ok) throw new Error(`Failed to get presigned URLs: ${response.statusText}`);
|
|
1939
1984
|
const apiResponse = await response.json();
|
|
1940
1985
|
if (!apiResponse.data) throw new Error("No data in presigned URL response");
|
|
1941
1986
|
return apiResponse.data;
|
|
@@ -1944,22 +1989,17 @@ var CosUploadService = class {
|
|
|
1944
1989
|
* 上传单个文件到 COS
|
|
1945
1990
|
*
|
|
1946
1991
|
* @param file - 要上传的文件
|
|
1947
|
-
* @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
|
|
1948
1992
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
1949
1993
|
*/
|
|
1950
|
-
async uploadFile(file
|
|
1994
|
+
async uploadFile(file) {
|
|
1951
1995
|
const filename = file.name;
|
|
1952
1996
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
1953
1997
|
try {
|
|
1954
1998
|
const objectKey = this.generateObjectKey(filename);
|
|
1955
1999
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
expire_seconds: expireSeconds
|
|
1960
|
-
});
|
|
1961
|
-
this.logger?.debug(`[CosUploadService] Got PUT presigned URL`);
|
|
1962
|
-
const uploadResponse = await this.fetchImpl(putPresigned.url, {
|
|
2000
|
+
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
2001
|
+
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
2002
|
+
const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
|
|
1963
2003
|
method: "PUT",
|
|
1964
2004
|
body: file,
|
|
1965
2005
|
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
@@ -1969,15 +2009,11 @@ var CosUploadService = class {
|
|
|
1969
2009
|
throw new Error(`COS upload failed: ${uploadResponse.status} ${errorText}`);
|
|
1970
2010
|
}
|
|
1971
2011
|
this.logger?.debug(`[CosUploadService] File uploaded to COS`);
|
|
1972
|
-
const getPresigned = await this.getPresignedUrl({
|
|
1973
|
-
object_key: objectKey,
|
|
1974
|
-
method: "GET",
|
|
1975
|
-
expire_seconds: expireSeconds
|
|
1976
|
-
});
|
|
1977
2012
|
this.logger?.info(`[CosUploadService] Upload success: ${filename}`);
|
|
1978
2013
|
return {
|
|
1979
2014
|
success: true,
|
|
1980
|
-
url:
|
|
2015
|
+
url: presignedItem.download_url,
|
|
2016
|
+
objectKey
|
|
1981
2017
|
};
|
|
1982
2018
|
} catch (error) {
|
|
1983
2019
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1991,509 +2027,272 @@ var CosUploadService = class {
|
|
|
1991
2027
|
/**
|
|
1992
2028
|
* 批量上传文件到 COS
|
|
1993
2029
|
*
|
|
2030
|
+
* 使用并发控制,限制同时上传的文件数量
|
|
2031
|
+
*
|
|
1994
2032
|
* @param files - 要上传的文件数组
|
|
1995
|
-
* @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
|
|
1996
2033
|
* @returns 所有文件的上传结果
|
|
1997
2034
|
*/
|
|
1998
|
-
async uploadFiles(files
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2035
|
+
async uploadFiles(files) {
|
|
2036
|
+
if (files.length === 0) return {
|
|
2037
|
+
success: true,
|
|
2038
|
+
urls: [],
|
|
2039
|
+
results: []
|
|
2040
|
+
};
|
|
2041
|
+
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
2042
|
+
try {
|
|
2043
|
+
const fileInfos = files.map((file) => ({
|
|
2044
|
+
file,
|
|
2045
|
+
objectKey: this.generateObjectKey(file.name)
|
|
2046
|
+
}));
|
|
2047
|
+
const objectKeys = fileInfos.map((info) => info.objectKey);
|
|
2048
|
+
const presignedResponse = await this.getPresignedUrls(objectKeys);
|
|
2049
|
+
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
2050
|
+
const presignedMap = /* @__PURE__ */ new Map();
|
|
2051
|
+
for (const item of presignedResponse.items) presignedMap.set(item.object_key, item);
|
|
2052
|
+
const results = (await runWithConcurrencySettled(fileInfos.map(({ file, objectKey }) => async () => {
|
|
2053
|
+
const presignedItem = presignedMap.get(objectKey);
|
|
2054
|
+
if (!presignedItem) return {
|
|
2055
|
+
success: false,
|
|
2056
|
+
error: `No presigned URL for ${file.name}`,
|
|
2057
|
+
objectKey
|
|
2058
|
+
};
|
|
2059
|
+
try {
|
|
2060
|
+
const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
|
|
2061
|
+
method: "PUT",
|
|
2062
|
+
body: file,
|
|
2063
|
+
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
2064
|
+
});
|
|
2065
|
+
if (!uploadResponse.ok) {
|
|
2066
|
+
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
2067
|
+
return {
|
|
2068
|
+
success: false,
|
|
2069
|
+
error: `COS upload failed: ${uploadResponse.status} ${errorText}`,
|
|
2070
|
+
objectKey
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
this.logger?.debug(`[CosUploadService] Uploaded: ${file.name}`);
|
|
2074
|
+
return {
|
|
2075
|
+
success: true,
|
|
2076
|
+
url: presignedItem.download_url,
|
|
2077
|
+
objectKey
|
|
2078
|
+
};
|
|
2079
|
+
} catch (error) {
|
|
2080
|
+
return {
|
|
2081
|
+
success: false,
|
|
2082
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
2083
|
+
objectKey
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
}), this.uploadConcurrency)).map((result, index) => {
|
|
2087
|
+
if (result.status === "fulfilled") return result.value;
|
|
2088
|
+
return {
|
|
2089
|
+
success: false,
|
|
2090
|
+
error: result.reason instanceof Error ? result.reason.message : "Unknown error",
|
|
2091
|
+
objectKey: fileInfos[index].objectKey
|
|
2092
|
+
};
|
|
2093
|
+
});
|
|
2094
|
+
const urls = results.filter((r) => r.success && r.url).map((r) => r.url);
|
|
2095
|
+
const failedResults = results.filter((r) => !r.success);
|
|
2096
|
+
if (failedResults.length > 0) {
|
|
2097
|
+
const failedErrors = failedResults.map((r) => r.error).join("; ");
|
|
2098
|
+
return {
|
|
2099
|
+
success: false,
|
|
2100
|
+
error: `${failedResults.length} file(s) failed: ${failedErrors}`,
|
|
2101
|
+
expireSeconds: presignedResponse.expire,
|
|
2102
|
+
results
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
|
|
2010
2106
|
return {
|
|
2011
|
-
success:
|
|
2012
|
-
|
|
2107
|
+
success: true,
|
|
2108
|
+
urls,
|
|
2109
|
+
expireSeconds: presignedResponse.expire,
|
|
2013
2110
|
results
|
|
2014
2111
|
};
|
|
2112
|
+
} catch (error) {
|
|
2113
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2114
|
+
this.logger?.error(`[CosUploadService] Batch upload failed`, error);
|
|
2115
|
+
return {
|
|
2116
|
+
success: false,
|
|
2117
|
+
error: errorMessage,
|
|
2118
|
+
results: files.map(() => ({
|
|
2119
|
+
success: false,
|
|
2120
|
+
error: errorMessage
|
|
2121
|
+
}))
|
|
2122
|
+
};
|
|
2015
2123
|
}
|
|
2016
|
-
this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
|
|
2017
|
-
return {
|
|
2018
|
-
success: true,
|
|
2019
|
-
urls,
|
|
2020
|
-
results
|
|
2021
|
-
};
|
|
2022
2124
|
}
|
|
2023
2125
|
};
|
|
2024
2126
|
|
|
2025
2127
|
//#endregion
|
|
2026
|
-
//#region ../agent-provider/src/
|
|
2027
|
-
/**
|
|
2028
|
-
* 套餐代码
|
|
2029
|
-
*/
|
|
2030
|
-
/**
|
|
2031
|
-
* TCACA_code_001_PqouKr6QWV CodeBuddy海外版免费包
|
|
2032
|
-
* TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
|
|
2033
|
-
* TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
|
|
2034
|
-
* TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
|
|
2035
|
-
* TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
|
|
2036
|
-
* TCACA_code_008_cfWoLwvjU4 赠送月包
|
|
2037
|
-
*/
|
|
2038
|
-
let CommodityCode = /* @__PURE__ */ function(CommodityCode) {
|
|
2039
|
-
CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
|
|
2040
|
-
CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
|
|
2041
|
-
CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
|
|
2042
|
-
CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
|
|
2043
|
-
CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
|
|
2044
|
-
CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
|
|
2045
|
-
CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
|
|
2046
|
-
return CommodityCode;
|
|
2047
|
-
}({});
|
|
2048
|
-
|
|
2049
|
-
//#endregion
|
|
2050
|
-
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
2128
|
+
//#region ../agent-provider/src/account/account-service.ts
|
|
2051
2129
|
/**
|
|
2052
|
-
*
|
|
2130
|
+
* AccountService 类
|
|
2053
2131
|
*
|
|
2054
|
-
*
|
|
2055
|
-
*/
|
|
2056
|
-
/** 获取当前域名的登录页面 URL */
|
|
2057
|
-
const getLoginUrl = () => `${window.location.origin}/login`;
|
|
2058
|
-
/** 获取当前域名的账号选择页面 URL */
|
|
2059
|
-
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
2060
|
-
/** localStorage 中存储选中账号 ID 的 key */
|
|
2061
|
-
const SELECTED_ACCOUNT_KEY = "CODEBUDDY_IDE_SELECTED_ACCOUNT_ID";
|
|
2062
|
-
/**
|
|
2063
|
-
* Backend Provider 实现类
|
|
2132
|
+
* 单例模式,管理全局账号状态
|
|
2064
2133
|
*/
|
|
2065
|
-
var
|
|
2066
|
-
constructor(
|
|
2067
|
-
this.
|
|
2068
|
-
this.
|
|
2134
|
+
var AccountService = class {
|
|
2135
|
+
constructor() {
|
|
2136
|
+
this.account = null;
|
|
2137
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
2138
|
+
this.initialized = false;
|
|
2139
|
+
this.initPromise = null;
|
|
2140
|
+
this.initResolve = null;
|
|
2141
|
+
this.initPromise = new Promise((resolve) => {
|
|
2142
|
+
this.initResolve = resolve;
|
|
2143
|
+
});
|
|
2069
2144
|
}
|
|
2070
2145
|
/**
|
|
2071
|
-
*
|
|
2072
|
-
*
|
|
2146
|
+
* 获取当前账号
|
|
2147
|
+
* @returns 当前账号,未登录或未加载时返回 null
|
|
2073
2148
|
*/
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
const sessions = new MockAgentProvider().getAllSessions();
|
|
2077
|
-
const mockTitles = {
|
|
2078
|
-
"1": "开发五子棋游戏",
|
|
2079
|
-
"2": "修复登录页面样式",
|
|
2080
|
-
"3": "API 接口优化"
|
|
2081
|
-
};
|
|
2082
|
-
const agents = sessions.map((session, index) => ({
|
|
2083
|
-
id: session.sessionId,
|
|
2084
|
-
name: mockTitles[session.sessionId] || `Agent ${session.sessionId}`,
|
|
2085
|
-
status: "RUNNING",
|
|
2086
|
-
visibility: "PRIVATE",
|
|
2087
|
-
createdAt: new Date(session.createdAt).toISOString(),
|
|
2088
|
-
summary: `Session created at ${new Date(session.createdAt).toLocaleString()}`,
|
|
2089
|
-
source: {
|
|
2090
|
-
provider: "github",
|
|
2091
|
-
ref: "refs/heads/main",
|
|
2092
|
-
repository: session.cwd
|
|
2093
|
-
},
|
|
2094
|
-
target: {
|
|
2095
|
-
autoCreatePr: false,
|
|
2096
|
-
branchName: "feature/mock",
|
|
2097
|
-
prUrl: void 0,
|
|
2098
|
-
url: void 0
|
|
2099
|
-
}
|
|
2100
|
-
}));
|
|
2101
|
-
return {
|
|
2102
|
-
agents,
|
|
2103
|
-
pagination: {
|
|
2104
|
-
hasNext: false,
|
|
2105
|
-
hasPrev: false,
|
|
2106
|
-
page: 1,
|
|
2107
|
-
size: agents.length,
|
|
2108
|
-
total: agents.length,
|
|
2109
|
-
totalPages: 1
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2149
|
+
getAccount() {
|
|
2150
|
+
return this.account;
|
|
2112
2151
|
}
|
|
2113
2152
|
/**
|
|
2114
|
-
*
|
|
2115
|
-
*
|
|
2116
|
-
*
|
|
2117
|
-
* 当前实现: 返回 Mock 数据
|
|
2153
|
+
* 设置账号
|
|
2154
|
+
* @param account 账号信息,登出时传 null
|
|
2118
2155
|
*/
|
|
2119
|
-
|
|
2120
|
-
const
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
supportsImages: false,
|
|
2128
|
-
disabledMultimodal: true,
|
|
2129
|
-
maxAllowedSize: 2e5,
|
|
2130
|
-
supportsReasoning: true,
|
|
2131
|
-
onlyReasoning: true,
|
|
2132
|
-
temperature: 1,
|
|
2133
|
-
reasoning: {
|
|
2134
|
-
effort: "medium",
|
|
2135
|
-
summary: "auto"
|
|
2136
|
-
},
|
|
2137
|
-
descriptionEn: "GLM-4.7 model, Well-rounded model for everyday use",
|
|
2138
|
-
descriptionZh: "GLM-4.7 大模型,能力均衡,适合日常使用"
|
|
2139
|
-
}, {
|
|
2140
|
-
id: "glm-4.7-flash",
|
|
2141
|
-
name: "GLM-4.7 Flash",
|
|
2142
|
-
vendor: "f",
|
|
2143
|
-
maxOutputTokens: 4e4,
|
|
2144
|
-
maxInputTokens: 128e3,
|
|
2145
|
-
supportsToolCall: true,
|
|
2146
|
-
supportsImages: false,
|
|
2147
|
-
disabledMultimodal: false,
|
|
2148
|
-
maxAllowedSize: 128e3,
|
|
2149
|
-
supportsReasoning: false,
|
|
2150
|
-
onlyReasoning: false,
|
|
2151
|
-
temperature: .7,
|
|
2152
|
-
reasoning: {
|
|
2153
|
-
effort: "low",
|
|
2154
|
-
summary: "never"
|
|
2155
|
-
},
|
|
2156
|
-
descriptionEn: "GLM-4.7 Flash, Fast and efficient model",
|
|
2157
|
-
descriptionZh: "GLM-4.7 Flash,快速高效的模型"
|
|
2158
|
-
}];
|
|
2159
|
-
console.log("[BackendProvider] getModels called for repository:", request.repository);
|
|
2160
|
-
return { models: mockModels };
|
|
2156
|
+
setAccount(account) {
|
|
2157
|
+
const prev = this.account;
|
|
2158
|
+
this.account = account;
|
|
2159
|
+
if (!this.initialized) {
|
|
2160
|
+
this.initialized = true;
|
|
2161
|
+
this.initResolve?.(account);
|
|
2162
|
+
}
|
|
2163
|
+
if (prev?.uid !== account?.uid) this.notifyListeners();
|
|
2161
2164
|
}
|
|
2162
2165
|
/**
|
|
2163
|
-
*
|
|
2164
|
-
* API 端点: GET /console/accounts (返回账号列表)
|
|
2165
|
-
*
|
|
2166
|
-
* 逻辑:
|
|
2167
|
-
* 1. 从 localStorage 读取 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID
|
|
2168
|
-
* 2. 根据 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID 找到对应账号
|
|
2169
|
-
* - personal 类型: 用 uid 匹配
|
|
2170
|
-
* - 其他类型: 用 enterpriseId 匹配
|
|
2171
|
-
* 3. 如果没有选中的账号,跳转到账号选择页面
|
|
2172
|
-
* 4. 获取套餐信息并合并到账号中
|
|
2166
|
+
* 清除账号(登出)
|
|
2173
2167
|
*/
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2168
|
+
clearAccount() {
|
|
2169
|
+
this.setAccount(null);
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* 订阅账号变化
|
|
2173
|
+
* @param callback 变化时的回调函数
|
|
2174
|
+
* @returns 取消订阅函数
|
|
2175
|
+
*/
|
|
2176
|
+
subscribe(callback) {
|
|
2177
|
+
this.listeners.add(callback);
|
|
2178
|
+
return () => {
|
|
2179
|
+
this.listeners.delete(callback);
|
|
2179
2180
|
};
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
});
|
|
2211
|
-
return {
|
|
2212
|
-
...selectedAccount,
|
|
2213
|
-
...plan,
|
|
2214
|
-
editionType
|
|
2215
|
-
};
|
|
2216
|
-
} catch (error) {
|
|
2217
|
-
return { ...selectedAccount };
|
|
2218
|
-
}
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* 等待首次账号加载完成
|
|
2184
|
+
* @returns Promise<Account | null>
|
|
2185
|
+
*/
|
|
2186
|
+
waitForInit() {
|
|
2187
|
+
if (this.initialized) return Promise.resolve(this.account);
|
|
2188
|
+
return this.initPromise;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* 是否已初始化
|
|
2192
|
+
*/
|
|
2193
|
+
isInitialized() {
|
|
2194
|
+
return this.initialized;
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* 是否已登录
|
|
2198
|
+
*/
|
|
2199
|
+
isLoggedIn() {
|
|
2200
|
+
return this.account !== null;
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* 通知所有订阅者
|
|
2204
|
+
*/
|
|
2205
|
+
notifyListeners() {
|
|
2206
|
+
this.listeners.forEach((callback) => {
|
|
2207
|
+
try {
|
|
2208
|
+
callback(this.account);
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
console.error("[AccountService] Listener error:", error);
|
|
2219
2211
|
}
|
|
2220
|
-
|
|
2221
|
-
selectedAccount = accounts[0];
|
|
2222
|
-
const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
|
|
2223
|
-
if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
|
|
2224
|
-
const plan = await this.getCurrentPlan();
|
|
2225
|
-
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
2226
|
-
console.log("account (auto-selected)", {
|
|
2227
|
-
...selectedAccount,
|
|
2228
|
-
...plan,
|
|
2229
|
-
editionType
|
|
2230
|
-
});
|
|
2231
|
-
return {
|
|
2232
|
-
...selectedAccount,
|
|
2233
|
-
...plan,
|
|
2234
|
-
editionType
|
|
2235
|
-
};
|
|
2236
|
-
}
|
|
2237
|
-
const redirectUrl = encodeURIComponent(window.location.href);
|
|
2238
|
-
window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
2239
|
-
return null;
|
|
2240
|
-
} catch (error) {
|
|
2241
|
-
console.error("[BackendProvider] getAccount failed:", error);
|
|
2242
|
-
return null;
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
/**
|
|
2246
|
-
* 获取当前套餐信息
|
|
2247
|
-
* 从计量计费接口获取用户的套餐信息
|
|
2248
|
-
* API: POST /billing/meter/get-user-resource
|
|
2249
|
-
*/
|
|
2250
|
-
async getCurrentPlan() {
|
|
2251
|
-
const defaultPlan = {
|
|
2252
|
-
isPro: false,
|
|
2253
|
-
expireAt: 0,
|
|
2254
|
-
renewFlag: 0,
|
|
2255
|
-
PackageCode: void 0,
|
|
2256
|
-
name: ""
|
|
2257
|
-
};
|
|
2258
|
-
try {
|
|
2259
|
-
const url = `${this.baseUrl}/billing/meter/get-user-resource`;
|
|
2260
|
-
const headers = {
|
|
2261
|
-
"Content-Type": "application/json",
|
|
2262
|
-
"Accept": "application/json"
|
|
2263
|
-
};
|
|
2264
|
-
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
2265
|
-
const now = /* @__PURE__ */ new Date();
|
|
2266
|
-
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
2267
|
-
const formatDate = (d) => {
|
|
2268
|
-
const pad = (n) => n.toString().padStart(2, "0");
|
|
2269
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
2270
|
-
};
|
|
2271
|
-
const body = {
|
|
2272
|
-
PageNumber: 1,
|
|
2273
|
-
PageSize: 100,
|
|
2274
|
-
ProductCode: "p_tcaca",
|
|
2275
|
-
Status: [0, 3],
|
|
2276
|
-
PackageEndTimeRangeBegin: formatDate(now),
|
|
2277
|
-
PackageEndTimeRangeEnd: formatDate(futureDate)
|
|
2278
|
-
};
|
|
2279
|
-
const response = await fetch(url, {
|
|
2280
|
-
method: "POST",
|
|
2281
|
-
headers,
|
|
2282
|
-
credentials: "include",
|
|
2283
|
-
body: JSON.stringify(body)
|
|
2284
|
-
});
|
|
2285
|
-
if (!response.ok) {
|
|
2286
|
-
console.warn("[BackendProvider] getCurrentPlan failed:", response.status);
|
|
2287
|
-
return defaultPlan;
|
|
2288
|
-
}
|
|
2289
|
-
const result = await response.json();
|
|
2290
|
-
const resources = result?.data?.Response?.Data?.Accounts || result?.data?.Accounts || [];
|
|
2291
|
-
if (!resources || resources.length === 0) return defaultPlan;
|
|
2292
|
-
const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon);
|
|
2293
|
-
const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift);
|
|
2294
|
-
const activePlan = proPlan || trialPlan;
|
|
2295
|
-
if (activePlan) return {
|
|
2296
|
-
isPro: !!proPlan,
|
|
2297
|
-
expireAt: activePlan.ExpiredTime || activePlan.CycleEndTime || 0,
|
|
2298
|
-
renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
|
|
2299
|
-
PackageCode: activePlan.PackageCode,
|
|
2300
|
-
name: activePlan.PackageName || ""
|
|
2301
|
-
};
|
|
2302
|
-
return defaultPlan;
|
|
2303
|
-
} catch (error) {
|
|
2304
|
-
console.error("[BackendProvider] getCurrentPlan error:", error);
|
|
2305
|
-
return defaultPlan;
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
/**
|
|
2309
|
-
* 根据账号类型和 Pro 状态计算版本展示类型
|
|
2310
|
-
* - personal + isPro = 'pro'
|
|
2311
|
-
* - personal + !isPro = 'free'
|
|
2312
|
-
* - ultimate = 'ultimate' (旗舰版/团队版)
|
|
2313
|
-
* - exclusive = 'exclusive' (专享版/企业版)
|
|
2314
|
-
*/
|
|
2315
|
-
getEditionDisplayType(type, isPro) {
|
|
2316
|
-
if (type === "personal") return isPro ? "pro" : "free";
|
|
2317
|
-
if (type === "ultimate") return "ultimate";
|
|
2318
|
-
if (type === "exclusive") return "exclusive";
|
|
2319
|
-
return "free";
|
|
2320
|
-
}
|
|
2321
|
-
/**
|
|
2322
|
-
* 触发登录流程
|
|
2323
|
-
* Web 环境: 跳转到登录页面
|
|
2324
|
-
*/
|
|
2325
|
-
async login() {
|
|
2326
|
-
const redirectUrl = encodeURIComponent(window.location.href);
|
|
2327
|
-
window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
2212
|
+
});
|
|
2328
2213
|
}
|
|
2329
2214
|
/**
|
|
2330
|
-
*
|
|
2331
|
-
* Web 环境: 跳转到登出页面或清除 cookie
|
|
2215
|
+
* 重置服务状态(仅用于测试)
|
|
2332
2216
|
*/
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
} catch (error) {
|
|
2341
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
2342
|
-
}
|
|
2343
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
2344
|
-
window.location.reload();
|
|
2217
|
+
_reset() {
|
|
2218
|
+
this.account = null;
|
|
2219
|
+
this.listeners.clear();
|
|
2220
|
+
this.initialized = false;
|
|
2221
|
+
this.initPromise = new Promise((resolve) => {
|
|
2222
|
+
this.initResolve = resolve;
|
|
2223
|
+
});
|
|
2345
2224
|
}
|
|
2346
2225
|
};
|
|
2347
2226
|
/**
|
|
2348
|
-
*
|
|
2227
|
+
* 导出单例实例
|
|
2349
2228
|
*/
|
|
2350
|
-
|
|
2351
|
-
return new BackendProvider(config);
|
|
2352
|
-
}
|
|
2229
|
+
const accountService = new AccountService();
|
|
2353
2230
|
|
|
2354
2231
|
//#endregion
|
|
2355
|
-
//#region ../agent-provider/src/
|
|
2356
|
-
/**
|
|
2357
|
-
* Backend 请求类型常量
|
|
2358
|
-
*/
|
|
2359
|
-
const BACKEND_REQUEST_TYPES = {
|
|
2360
|
-
GET_AGENTS: "backend:get-agents-request",
|
|
2361
|
-
GET_MODELS: "backend:get-models",
|
|
2362
|
-
LOGIN: "backend:login",
|
|
2363
|
-
LOGOUT: "backend:logout",
|
|
2364
|
-
GET_ACCOUNT: "backend:get-account"
|
|
2365
|
-
};
|
|
2232
|
+
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
|
|
2366
2233
|
/**
|
|
2367
|
-
*
|
|
2234
|
+
* Normalize a path by resolving `.` and `..` segments
|
|
2235
|
+
* This is a simplified version that works in browser environment
|
|
2368
2236
|
*/
|
|
2369
|
-
function
|
|
2370
|
-
|
|
2237
|
+
function normalizePath(path) {
|
|
2238
|
+
const segments = path.split("/");
|
|
2239
|
+
const result = [];
|
|
2240
|
+
for (const segment of segments) if (segment === "..") {
|
|
2241
|
+
if (result.length > 0 && result[result.length - 1] !== "") result.pop();
|
|
2242
|
+
} else if (segment !== "." && segment !== "") result.push(segment);
|
|
2243
|
+
return (path.startsWith("/") ? "/" : "") + result.join("/");
|
|
2371
2244
|
}
|
|
2372
2245
|
/**
|
|
2373
|
-
*
|
|
2246
|
+
* Resolve agent:/// URI to filesystem path
|
|
2374
2247
|
*
|
|
2375
|
-
*
|
|
2248
|
+
* 只处理 agent:// 协议的 URI,raw path 直接透传不做任何处理
|
|
2249
|
+
*
|
|
2250
|
+
* Supported formats:
|
|
2251
|
+
* - `agent:///workspace/{path}` → `/workspace/{path}`
|
|
2252
|
+
* - `agent:///plans/{path}` → `/root/.codebuddy/plans/{path}`
|
|
2253
|
+
* - `agent:///{path}` → `/{path}`
|
|
2254
|
+
* - Raw paths (e.g. `/foo/bar`) → 直接透传,不处理
|
|
2255
|
+
*
|
|
2256
|
+
* Security: Path traversal attacks are prevented by normalizing paths
|
|
2257
|
+
* and verifying they stay within expected boundaries.
|
|
2376
2258
|
*/
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
*/
|
|
2390
|
-
async sendBackendRequest(requestType, params) {
|
|
2391
|
-
const message = {
|
|
2392
|
-
type: "backend",
|
|
2393
|
-
requestId: generateRequestId(),
|
|
2394
|
-
params: {
|
|
2395
|
-
type: requestType,
|
|
2396
|
-
params
|
|
2397
|
-
}
|
|
2398
|
-
};
|
|
2399
|
-
this.log("Sending backend request:", message);
|
|
2400
|
-
const response = await this.channel.callMethod("__backend__", message, this.timeoutMs);
|
|
2401
|
-
this.log("Received response:", response);
|
|
2402
|
-
if (response?.error) throw new Error(response.error);
|
|
2403
|
-
return response?.data !== void 0 ? response.data : response;
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* 获取 Agent 列表
|
|
2407
|
-
* 通过 IWidgetChannel 发送请求到后端
|
|
2408
|
-
*/
|
|
2409
|
-
async getAgents(request = {}) {
|
|
2410
|
-
this.log("Getting agents with request:", request);
|
|
2411
|
-
try {
|
|
2412
|
-
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_AGENTS, request);
|
|
2413
|
-
} catch (error) {
|
|
2414
|
-
this.log("Get agents failed:", error);
|
|
2415
|
-
throw error;
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
/**
|
|
2419
|
-
* 获取可用模型列表
|
|
2420
|
-
* 通过 IWidgetChannel 发送请求到后端
|
|
2421
|
-
*/
|
|
2422
|
-
async getModels(request) {
|
|
2423
|
-
this.log("Getting models with request:", request);
|
|
2424
|
-
try {
|
|
2425
|
-
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_MODELS, request);
|
|
2426
|
-
} catch (error) {
|
|
2427
|
-
this.log("Get models failed:", error);
|
|
2428
|
-
throw error;
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
/**
|
|
2432
|
-
* 获取当前账号信息
|
|
2433
|
-
* IDE 环境: 通过 IPC 获取账号信息
|
|
2434
|
-
*/
|
|
2435
|
-
async getAccount() {
|
|
2436
|
-
this.log("Getting account via IPC");
|
|
2437
|
-
try {
|
|
2438
|
-
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
2439
|
-
} catch (error) {
|
|
2440
|
-
this.log("Get account failed:", error);
|
|
2441
|
-
return null;
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
/**
|
|
2445
|
-
* 触发登录流程
|
|
2446
|
-
* IDE 环境: 通过 IPC 通知 IDE 打开登录流程
|
|
2447
|
-
*/
|
|
2448
|
-
async login() {
|
|
2449
|
-
this.log("Triggering login via IPC");
|
|
2450
|
-
try {
|
|
2451
|
-
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGIN);
|
|
2452
|
-
} catch (error) {
|
|
2453
|
-
this.log("Login request failed:", error);
|
|
2454
|
-
throw error;
|
|
2455
|
-
}
|
|
2456
|
-
}
|
|
2457
|
-
/**
|
|
2458
|
-
* 登出账号
|
|
2459
|
-
* IDE 环境: 通过 IPC 通知 IDE 登出
|
|
2460
|
-
*/
|
|
2461
|
-
async logout() {
|
|
2462
|
-
this.log("Triggering logout via IPC");
|
|
2463
|
-
try {
|
|
2464
|
-
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGOUT);
|
|
2465
|
-
} catch (error) {
|
|
2466
|
-
this.log("Logout request failed:", error);
|
|
2467
|
-
throw error;
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
/**
|
|
2471
|
-
* 监听 channel 事件
|
|
2472
|
-
* 用于监听账户变化等事件
|
|
2473
|
-
*/
|
|
2474
|
-
on(event, callback) {
|
|
2475
|
-
this.log("Registering event listener:", event);
|
|
2476
|
-
this.channel.on(event, callback);
|
|
2477
|
-
return () => {
|
|
2478
|
-
this.channel.off(event, callback);
|
|
2479
|
-
};
|
|
2480
|
-
}
|
|
2481
|
-
/**
|
|
2482
|
-
* 调试日志
|
|
2483
|
-
*/
|
|
2484
|
-
log(...args) {
|
|
2485
|
-
if (this.debug) console.log("[IPCBackendProvider]", ...args);
|
|
2486
|
-
}
|
|
2487
|
-
};
|
|
2259
|
+
function resolveAgentUri(input) {
|
|
2260
|
+
if (!input.startsWith("agent://")) return input;
|
|
2261
|
+
const path = input.slice(8);
|
|
2262
|
+
if (!path.startsWith("/")) return input;
|
|
2263
|
+
const normalizedPath = normalizePath(path);
|
|
2264
|
+
if (normalizedPath.startsWith("/plans/") || normalizedPath === "/plans") {
|
|
2265
|
+
const finalPath = normalizePath("/root/.codebuddy" + normalizedPath);
|
|
2266
|
+
if (!finalPath.startsWith("/root/.codebuddy/plans")) throw new Error(`Invalid path: path traversal detected in ${input}`);
|
|
2267
|
+
return finalPath;
|
|
2268
|
+
}
|
|
2269
|
+
return normalizedPath;
|
|
2270
|
+
}
|
|
2488
2271
|
/**
|
|
2489
|
-
*
|
|
2272
|
+
* Create a FilesResource wrapper that resolves agent:/// URIs
|
|
2490
2273
|
*/
|
|
2491
|
-
function
|
|
2492
|
-
return
|
|
2274
|
+
function createAgentFilesystem(fs) {
|
|
2275
|
+
return {
|
|
2276
|
+
read: (path, opts) => fs.read(resolveAgentUri(path), opts),
|
|
2277
|
+
write: (pathOrFiles, dataOrOpts, opts) => {
|
|
2278
|
+
if (Array.isArray(pathOrFiles)) {
|
|
2279
|
+
const resolved = pathOrFiles.map((f) => ({
|
|
2280
|
+
...f,
|
|
2281
|
+
path: resolveAgentUri(f.path)
|
|
2282
|
+
}));
|
|
2283
|
+
return fs.write(resolved, dataOrOpts);
|
|
2284
|
+
}
|
|
2285
|
+
return fs.write(resolveAgentUri(pathOrFiles), dataOrOpts, opts);
|
|
2286
|
+
},
|
|
2287
|
+
list: (path, opts) => fs.list(resolveAgentUri(path), opts),
|
|
2288
|
+
exists: (path, opts) => fs.exists(resolveAgentUri(path), opts),
|
|
2289
|
+
makeDir: (path, opts) => fs.makeDir(resolveAgentUri(path), opts),
|
|
2290
|
+
remove: (path, opts) => fs.remove(resolveAgentUri(path), opts),
|
|
2291
|
+
rename: (oldPath, newPath, opts) => fs.rename(resolveAgentUri(oldPath), resolveAgentUri(newPath), opts),
|
|
2292
|
+
getInfo: (path, opts) => fs.getInfo(resolveAgentUri(path), opts),
|
|
2293
|
+
watchDir: (path, onEvent, opts) => fs.watchDir(resolveAgentUri(path), onEvent, opts)
|
|
2294
|
+
};
|
|
2493
2295
|
}
|
|
2494
|
-
|
|
2495
|
-
//#endregion
|
|
2496
|
-
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
|
|
2497
2296
|
/**
|
|
2498
2297
|
* CloudAgentProvider - Manages cloud-hosted agents via REST API
|
|
2499
2298
|
*
|
|
@@ -2567,6 +2366,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2567
2366
|
constructor(options) {
|
|
2568
2367
|
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
2569
2368
|
this.connectionCache = /* @__PURE__ */ new Map();
|
|
2369
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2570
2370
|
this.options = options;
|
|
2571
2371
|
this.logger = options.logger;
|
|
2572
2372
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
@@ -2577,6 +2377,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2577
2377
|
});
|
|
2578
2378
|
}
|
|
2579
2379
|
/**
|
|
2380
|
+
* Dispose the provider and clean up resources
|
|
2381
|
+
*/
|
|
2382
|
+
dispose() {
|
|
2383
|
+
this.filesystemCache.clear();
|
|
2384
|
+
this.connectionCache.clear();
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2580
2387
|
* Get the filesystem provider (returns self)
|
|
2581
2388
|
*/
|
|
2582
2389
|
get filesystem() {
|
|
@@ -2585,16 +2392,29 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2585
2392
|
/**
|
|
2586
2393
|
* Get filesystem resource for an agent
|
|
2587
2394
|
*
|
|
2588
|
-
* Creates or returns cached
|
|
2395
|
+
* Creates or returns cached filesystem instance for the agent's sandbox.
|
|
2396
|
+
* The filesystem supports both `agent:///` URIs and raw paths.
|
|
2589
2397
|
*
|
|
2590
2398
|
* @param agentId - Agent ID to get filesystem for
|
|
2591
|
-
* @returns FilesResource instance for the agent's sandbox
|
|
2399
|
+
* @returns FilesResource instance for the agent's sandbox (with URI support)
|
|
2400
|
+
*
|
|
2401
|
+
* @example
|
|
2402
|
+
* ```typescript
|
|
2403
|
+
* const fs = await provider.getFilesystem(agentId);
|
|
2404
|
+
*
|
|
2405
|
+
* // Use agent:/// URIs
|
|
2406
|
+
* const content = await fs.read('agent:///files/src/app.ts');
|
|
2407
|
+
* await fs.write('agent:///artifacts/output.txt', 'Hello');
|
|
2408
|
+
*
|
|
2409
|
+
* // Raw paths still work (backward compatible)
|
|
2410
|
+
* const content2 = await fs.read('/src/app.ts');
|
|
2411
|
+
* ```
|
|
2592
2412
|
*/
|
|
2593
2413
|
async getFilesystem(agentId) {
|
|
2594
2414
|
const cached = this.filesystemCache.get(agentId);
|
|
2595
2415
|
if (cached) return cached;
|
|
2596
2416
|
const info = await this.getSandboxInfo(agentId);
|
|
2597
|
-
const filesystem = await E2BFilesystem.connect(info);
|
|
2417
|
+
const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
|
|
2598
2418
|
this.filesystemCache.set(agentId, filesystem);
|
|
2599
2419
|
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
2600
2420
|
return filesystem;
|
|
@@ -2619,7 +2439,6 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2619
2439
|
return {
|
|
2620
2440
|
sandboxId: apiResponse.data.sandboxId,
|
|
2621
2441
|
apiUrl,
|
|
2622
|
-
apiKey: "backend-not-used",
|
|
2623
2442
|
accessToken: apiResponse.data.token,
|
|
2624
2443
|
headers: { ...currentEnterpriseId && { "X-Enterprise-Id": currentEnterpriseId } }
|
|
2625
2444
|
};
|
|
@@ -2722,31 +2541,55 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2722
2541
|
* and token expiration internally
|
|
2723
2542
|
*/
|
|
2724
2543
|
async connect(agentId) {
|
|
2725
|
-
const
|
|
2726
|
-
if (
|
|
2727
|
-
if (!
|
|
2728
|
-
const
|
|
2729
|
-
if (!
|
|
2730
|
-
const sessionData =
|
|
2544
|
+
const sessionResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
|
|
2545
|
+
if (sessionResponse.status === 404) throw new Error(`Agent not found: ${agentId}`);
|
|
2546
|
+
if (!sessionResponse.ok) throw new Error(`Failed to get agent for connection: ${sessionResponse.statusText}`);
|
|
2547
|
+
const sessionApiResponse = await sessionResponse.json();
|
|
2548
|
+
if (!sessionApiResponse.data) throw new Error("No data in API response");
|
|
2549
|
+
const sessionData = sessionApiResponse.data;
|
|
2731
2550
|
const endpoint = sessionData.link.replace(/^http:\/\//, "https://");
|
|
2551
|
+
const cwd = sessionApiResponse.data.cwd || "/workspace";
|
|
2552
|
+
const agentResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}`);
|
|
2553
|
+
let agentName;
|
|
2554
|
+
let agentCreatedAt;
|
|
2555
|
+
let agentStatus;
|
|
2556
|
+
if (agentResponse.ok) {
|
|
2557
|
+
const agentApiResponse = await agentResponse.json();
|
|
2558
|
+
if (agentApiResponse.data) {
|
|
2559
|
+
agentName = agentApiResponse.data.name;
|
|
2560
|
+
agentCreatedAt = agentApiResponse.data.createdAt ? new Date(agentApiResponse.data.createdAt) : void 0;
|
|
2561
|
+
agentStatus = agentApiResponse.data.sessionStatus || agentApiResponse.data.status;
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2732
2564
|
const existingConnection = this.connectionCache.get(endpoint);
|
|
2733
2565
|
if (existingConnection?.isInitialized) {
|
|
2734
2566
|
this.logger?.info(`Reusing existing connection for agent: ${agentId}`);
|
|
2735
2567
|
return existingConnection;
|
|
2736
2568
|
}
|
|
2569
|
+
const clientCapabilities = {
|
|
2570
|
+
...this.options.clientCapabilities,
|
|
2571
|
+
_meta: {
|
|
2572
|
+
...this.options.clientCapabilities?._meta,
|
|
2573
|
+
"codebuddy.ai": {
|
|
2574
|
+
...this.options.clientCapabilities?._meta?.["codebuddy.ai"],
|
|
2575
|
+
cwd
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2737
2579
|
const connection = new CloudAgentConnection(agentId, {
|
|
2738
2580
|
endpoint,
|
|
2739
2581
|
authToken: sessionData.token,
|
|
2740
2582
|
logger: this.logger,
|
|
2741
|
-
clientCapabilities
|
|
2742
|
-
});
|
|
2583
|
+
clientCapabilities
|
|
2584
|
+
}, cwd);
|
|
2743
2585
|
connection.setSessionConnectionInfo({
|
|
2744
2586
|
sessionId: sessionData.sessionId,
|
|
2745
2587
|
agentId: sessionData.id,
|
|
2746
2588
|
link: sessionData.link,
|
|
2747
2589
|
token: sessionData.token,
|
|
2748
2590
|
sandboxId: sessionData.sandboxId,
|
|
2749
|
-
expireAt: sessionData.expireAt
|
|
2591
|
+
expireAt: sessionData.expireAt,
|
|
2592
|
+
cwd
|
|
2750
2593
|
});
|
|
2751
2594
|
try {
|
|
2752
2595
|
await connection.connect();
|
|
@@ -2760,6 +2603,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2760
2603
|
this.logger?.debug(`Connection removed from cache: ${endpoint}`);
|
|
2761
2604
|
});
|
|
2762
2605
|
this.logger?.info(`Connected to agent: ${agentId}`);
|
|
2606
|
+
this.emitEvent("sessionCreated", {
|
|
2607
|
+
id: agentId,
|
|
2608
|
+
agentId,
|
|
2609
|
+
name: agentName,
|
|
2610
|
+
status: agentStatus,
|
|
2611
|
+
cwd,
|
|
2612
|
+
createdAt: agentCreatedAt
|
|
2613
|
+
});
|
|
2763
2614
|
return connection;
|
|
2764
2615
|
}
|
|
2765
2616
|
/**
|
|
@@ -2839,31 +2690,76 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2839
2690
|
}
|
|
2840
2691
|
}
|
|
2841
2692
|
/**
|
|
2842
|
-
* Get available models
|
|
2693
|
+
* Get available models from product configuration
|
|
2694
|
+
*
|
|
2695
|
+
* GET {endpoint}/v3/config?repos[]={repo}
|
|
2843
2696
|
*
|
|
2844
|
-
*
|
|
2697
|
+
* This method fetches the product configuration from /v3/config API
|
|
2698
|
+
* and extracts the models array from the response.
|
|
2845
2699
|
*
|
|
2846
|
-
* @param repo -
|
|
2847
|
-
* @returns Array of
|
|
2700
|
+
* @param repo - Optional repository URL for context-specific config
|
|
2701
|
+
* @returns Array of ModelInfo with full model details
|
|
2848
2702
|
*/
|
|
2849
2703
|
async getModels(repo) {
|
|
2850
2704
|
try {
|
|
2851
|
-
|
|
2852
|
-
if (
|
|
2705
|
+
let url = `${this.options.endpoint}/v3/config`;
|
|
2706
|
+
if (repo) url += `?repos[]=${encodeURIComponent(repo)}`;
|
|
2707
|
+
const headers = {
|
|
2708
|
+
"Accept": "application/json, text/plain, */*",
|
|
2709
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
2710
|
+
...this.options.headers
|
|
2711
|
+
};
|
|
2712
|
+
if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
2713
|
+
const account = accountService.getAccount();
|
|
2714
|
+
if (account?.uid) headers["X-User-Id"] = account.uid;
|
|
2715
|
+
if (account?.enterpriseId) {
|
|
2716
|
+
headers["X-Enterprise-Id"] = account.enterpriseId;
|
|
2717
|
+
headers["X-Tenant-Id"] = account.enterpriseId;
|
|
2718
|
+
}
|
|
2719
|
+
headers["X-Product"] = "SaaS";
|
|
2720
|
+
headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
|
|
2721
|
+
headers["X-Request-ID"] = this.generateRequestId();
|
|
2722
|
+
this.logger?.debug(`[CloudAgentProvider] GET ${url}`);
|
|
2723
|
+
const response = await this.fetchImpl(url, {
|
|
2724
|
+
method: "GET",
|
|
2725
|
+
headers,
|
|
2726
|
+
credentials: "include"
|
|
2727
|
+
});
|
|
2728
|
+
if (!response.ok) throw new Error(`Failed to get config: ${response.statusText}`);
|
|
2853
2729
|
const apiResponse = await response.json();
|
|
2854
|
-
if (!apiResponse.data)
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2730
|
+
if (!apiResponse.data) {
|
|
2731
|
+
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
2732
|
+
return [];
|
|
2733
|
+
}
|
|
2734
|
+
const models = apiResponse.data.models ?? [];
|
|
2735
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /v3/config`);
|
|
2736
|
+
return models.map((model) => ({
|
|
2737
|
+
id: model.id,
|
|
2738
|
+
name: model.name ?? model.id,
|
|
2739
|
+
description: model.description,
|
|
2740
|
+
credits: model.credits,
|
|
2741
|
+
configurable: model.configurable,
|
|
2742
|
+
configured: model.configured,
|
|
2743
|
+
isDefault: model.isDefault,
|
|
2744
|
+
supportsImages: model.supportsImages,
|
|
2745
|
+
supportsReasoning: model.supportsReasoning,
|
|
2746
|
+
onlyReasoning: model.onlyReasoning,
|
|
2747
|
+
disabledMultimodal: model.disabledMultimodal,
|
|
2748
|
+
disabled: model.disabled,
|
|
2749
|
+
disabledReason: model.disabledReason,
|
|
2750
|
+
disabledAction: model.disabledAction
|
|
2861
2751
|
}));
|
|
2862
2752
|
} catch (error) {
|
|
2863
|
-
this.logger?.error(`Failed to get models
|
|
2753
|
+
this.logger?.error(`[CloudAgentProvider] Failed to get models:`, error);
|
|
2864
2754
|
throw error;
|
|
2865
2755
|
}
|
|
2866
2756
|
}
|
|
2757
|
+
/**
|
|
2758
|
+
* Generate a unique request ID
|
|
2759
|
+
*/
|
|
2760
|
+
generateRequestId() {
|
|
2761
|
+
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/x/g, () => Math.floor(Math.random() * 16).toString(16));
|
|
2762
|
+
}
|
|
2867
2763
|
static {
|
|
2868
2764
|
this.IMAGE_MIME_TYPES = [
|
|
2869
2765
|
"image/png",
|
|
@@ -2955,9 +2851,47 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2955
2851
|
return {
|
|
2956
2852
|
success: result.success,
|
|
2957
2853
|
urls: result.urls,
|
|
2854
|
+
expireSeconds: result.expireSeconds,
|
|
2958
2855
|
error: result.error
|
|
2959
2856
|
};
|
|
2960
2857
|
}
|
|
2858
|
+
/**
|
|
2859
|
+
* Register event listener
|
|
2860
|
+
* @param event - Event name
|
|
2861
|
+
* @param handler - Event handler function
|
|
2862
|
+
*/
|
|
2863
|
+
on(event, handler) {
|
|
2864
|
+
if (!this.eventListeners.has(event)) this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
2865
|
+
this.eventListeners.get(event).add(handler);
|
|
2866
|
+
return () => {
|
|
2867
|
+
this.off(event, handler);
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
/**
|
|
2871
|
+
* Unregister event listener
|
|
2872
|
+
* @param event - Event name
|
|
2873
|
+
* @param handler - Event handler function
|
|
2874
|
+
*/
|
|
2875
|
+
off(event, handler) {
|
|
2876
|
+
const listeners = this.eventListeners.get(event);
|
|
2877
|
+
if (listeners) listeners.delete(handler);
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Emit event to all registered listeners
|
|
2881
|
+
* @param event - Event name
|
|
2882
|
+
* @param args - Event arguments
|
|
2883
|
+
*/
|
|
2884
|
+
emitEvent(event, ...args) {
|
|
2885
|
+
const listeners = this.eventListeners.get(event);
|
|
2886
|
+
if (listeners && listeners.size > 0) {
|
|
2887
|
+
this.logger?.debug(`Emitting event: ${event}`, args);
|
|
2888
|
+
for (const handler of listeners) try {
|
|
2889
|
+
handler(...args);
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
this.logger?.error(`Error in event handler for ${event}:`, error);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2961
2895
|
toAgentState(data) {
|
|
2962
2896
|
const status = data.sessionStatus || data.status;
|
|
2963
2897
|
return {
|
|
@@ -2977,8 +2911,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
2977
2911
|
...this.options.headers
|
|
2978
2912
|
};
|
|
2979
2913
|
if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
2980
|
-
const
|
|
2981
|
-
if (
|
|
2914
|
+
const account = accountService.getAccount();
|
|
2915
|
+
if (account?.uid) headers["X-User-Id"] = account.uid;
|
|
2916
|
+
if (account?.enterpriseId) {
|
|
2917
|
+
headers["X-Enterprise-Id"] = account.enterpriseId;
|
|
2918
|
+
headers["X-Tenant-Id"] = account.enterpriseId;
|
|
2919
|
+
}
|
|
2920
|
+
headers["X-Product"] = "SaaS";
|
|
2921
|
+
headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
|
|
2982
2922
|
const init = {
|
|
2983
2923
|
method,
|
|
2984
2924
|
headers
|
|
@@ -3076,8 +3016,8 @@ var ActiveSessionImpl = class {
|
|
|
3076
3016
|
id: this._agentId,
|
|
3077
3017
|
status: this.connection.state,
|
|
3078
3018
|
capabilities: this.connection.capabilities,
|
|
3079
|
-
type:
|
|
3080
|
-
cwd: ""
|
|
3019
|
+
type: this.connection.transport,
|
|
3020
|
+
cwd: this.connection.cwd || ""
|
|
3081
3021
|
};
|
|
3082
3022
|
}
|
|
3083
3023
|
/**
|
|
@@ -3864,6 +3804,514 @@ function isCloudAgentState(state) {
|
|
|
3864
3804
|
return state.type === "cloud";
|
|
3865
3805
|
}
|
|
3866
3806
|
|
|
3807
|
+
//#endregion
|
|
3808
|
+
//#region ../agent-provider/src/backend/types.ts
|
|
3809
|
+
/**
|
|
3810
|
+
* 套餐代码
|
|
3811
|
+
*/
|
|
3812
|
+
/**
|
|
3813
|
+
* TCACA_code_001_PqouKr6QWV CodeBuddy海外版免费包
|
|
3814
|
+
* TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
|
|
3815
|
+
* TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
|
|
3816
|
+
* TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
|
|
3817
|
+
* TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
|
|
3818
|
+
* TCACA_code_008_cfWoLwvjU4 赠送月包
|
|
3819
|
+
*/
|
|
3820
|
+
let CommodityCode = /* @__PURE__ */ function(CommodityCode) {
|
|
3821
|
+
CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
|
|
3822
|
+
CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
|
|
3823
|
+
CommodityCode["proMonPlus"] = "TCACA_code_005_maRGyrHhw1";
|
|
3824
|
+
CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
|
|
3825
|
+
CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
|
|
3826
|
+
CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
|
|
3827
|
+
CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
|
|
3828
|
+
CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
|
|
3829
|
+
return CommodityCode;
|
|
3830
|
+
}({});
|
|
3831
|
+
/** 账户状态 */
|
|
3832
|
+
let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
3833
|
+
/** 有效 */
|
|
3834
|
+
AccountStatus[AccountStatus["valid"] = 0] = "valid";
|
|
3835
|
+
/** 已退款 */
|
|
3836
|
+
AccountStatus[AccountStatus["refund"] = 1] = "refund";
|
|
3837
|
+
/** 已过期 */
|
|
3838
|
+
AccountStatus[AccountStatus["expired"] = 2] = "expired";
|
|
3839
|
+
/** 已用完 */
|
|
3840
|
+
AccountStatus[AccountStatus["usedUp"] = 3] = "usedUp";
|
|
3841
|
+
return AccountStatus;
|
|
3842
|
+
}({});
|
|
3843
|
+
|
|
3844
|
+
//#endregion
|
|
3845
|
+
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
3846
|
+
/**
|
|
3847
|
+
* Backend Provider 实现
|
|
3848
|
+
*
|
|
3849
|
+
* 封装与后端 API 的 HTTP 通信
|
|
3850
|
+
*/
|
|
3851
|
+
/** 获取当前域名的登录页面 URL */
|
|
3852
|
+
const getLoginUrl = () => `${window.location.origin}/login`;
|
|
3853
|
+
/** 获取当前域名的账号选择页面 URL */
|
|
3854
|
+
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
3855
|
+
/** localStorage 中存储选中账号 ID 的 key */
|
|
3856
|
+
const SELECTED_ACCOUNT_KEY = "CODEBUDDY_IDE_SELECTED_ACCOUNT_ID";
|
|
3857
|
+
/**
|
|
3858
|
+
* Backend Provider 实现类
|
|
3859
|
+
*
|
|
3860
|
+
* 职责:
|
|
3861
|
+
* - 与后端 API 通信(getAgents, getModels, getAccount 等)
|
|
3862
|
+
* - 触发登录/登出流程
|
|
3863
|
+
* - 获取 account 后自动同步到 accountService
|
|
3864
|
+
*/
|
|
3865
|
+
var BackendProvider = class {
|
|
3866
|
+
constructor(config) {
|
|
3867
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
3868
|
+
this.authToken = config.authToken;
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* 获取 Agent 列表
|
|
3872
|
+
* API 端点: GET /v2/cloudagent/agentmgmt/agents
|
|
3873
|
+
*/
|
|
3874
|
+
async getAgents(request = {}) {
|
|
3875
|
+
const { MockAgentProvider } = await Promise.resolve().then(() => require("./MockAgentProvider-4e4oOusg.cjs"));
|
|
3876
|
+
const sessions = new MockAgentProvider().getAllSessions();
|
|
3877
|
+
const mockTitles = {
|
|
3878
|
+
"1": "开发五子棋游戏",
|
|
3879
|
+
"2": "修复登录页面样式",
|
|
3880
|
+
"3": "API 接口优化"
|
|
3881
|
+
};
|
|
3882
|
+
const agents = sessions.map((session, index) => ({
|
|
3883
|
+
id: session.sessionId,
|
|
3884
|
+
name: mockTitles[session.sessionId] || `Agent ${session.sessionId}`,
|
|
3885
|
+
status: "RUNNING",
|
|
3886
|
+
visibility: "PRIVATE",
|
|
3887
|
+
createdAt: new Date(session.createdAt).toISOString(),
|
|
3888
|
+
summary: `Session created at ${new Date(session.createdAt).toLocaleString()}`,
|
|
3889
|
+
source: {
|
|
3890
|
+
provider: "github",
|
|
3891
|
+
ref: "refs/heads/main",
|
|
3892
|
+
repository: session.cwd
|
|
3893
|
+
},
|
|
3894
|
+
target: {
|
|
3895
|
+
autoCreatePr: false,
|
|
3896
|
+
branchName: "feature/mock",
|
|
3897
|
+
prUrl: void 0,
|
|
3898
|
+
url: void 0
|
|
3899
|
+
}
|
|
3900
|
+
}));
|
|
3901
|
+
return {
|
|
3902
|
+
agents,
|
|
3903
|
+
pagination: {
|
|
3904
|
+
hasNext: false,
|
|
3905
|
+
hasPrev: false,
|
|
3906
|
+
page: 1,
|
|
3907
|
+
size: agents.length,
|
|
3908
|
+
total: agents.length,
|
|
3909
|
+
totalPages: 1
|
|
3910
|
+
}
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
/**
|
|
3914
|
+
* 获取可用模型列表
|
|
3915
|
+
* API 端点: GET /v2/cloudagent/models (假设)
|
|
3916
|
+
*
|
|
3917
|
+
* 当前实现: 返回 Mock 数据
|
|
3918
|
+
*/
|
|
3919
|
+
async getModels(request) {
|
|
3920
|
+
const mockModels = [{
|
|
3921
|
+
id: "glm-4.7",
|
|
3922
|
+
name: "GLM-4.7",
|
|
3923
|
+
vendor: "f",
|
|
3924
|
+
maxOutputTokens: 48e3,
|
|
3925
|
+
maxInputTokens: 2e5,
|
|
3926
|
+
supportsToolCall: true,
|
|
3927
|
+
supportsImages: false,
|
|
3928
|
+
disabledMultimodal: true,
|
|
3929
|
+
maxAllowedSize: 2e5,
|
|
3930
|
+
supportsReasoning: true,
|
|
3931
|
+
onlyReasoning: true,
|
|
3932
|
+
temperature: 1,
|
|
3933
|
+
reasoning: {
|
|
3934
|
+
effort: "medium",
|
|
3935
|
+
summary: "auto"
|
|
3936
|
+
},
|
|
3937
|
+
descriptionEn: "GLM-4.7 model, Well-rounded model for everyday use",
|
|
3938
|
+
descriptionZh: "GLM-4.7 大模型,能力均衡,适合日常使用"
|
|
3939
|
+
}, {
|
|
3940
|
+
id: "glm-4.7-flash",
|
|
3941
|
+
name: "GLM-4.7 Flash",
|
|
3942
|
+
vendor: "f",
|
|
3943
|
+
maxOutputTokens: 4e4,
|
|
3944
|
+
maxInputTokens: 128e3,
|
|
3945
|
+
supportsToolCall: true,
|
|
3946
|
+
supportsImages: false,
|
|
3947
|
+
disabledMultimodal: false,
|
|
3948
|
+
maxAllowedSize: 128e3,
|
|
3949
|
+
supportsReasoning: false,
|
|
3950
|
+
onlyReasoning: false,
|
|
3951
|
+
temperature: .7,
|
|
3952
|
+
reasoning: {
|
|
3953
|
+
effort: "low",
|
|
3954
|
+
summary: "never"
|
|
3955
|
+
},
|
|
3956
|
+
descriptionEn: "GLM-4.7 Flash, Fast and efficient model",
|
|
3957
|
+
descriptionZh: "GLM-4.7 Flash,快速高效的模型"
|
|
3958
|
+
}];
|
|
3959
|
+
console.log("[BackendProvider] getModels called for repository:", request.repository);
|
|
3960
|
+
return { models: mockModels };
|
|
3961
|
+
}
|
|
3962
|
+
/**
|
|
3963
|
+
* 获取当前账号信息
|
|
3964
|
+
* API 端点: GET /console/accounts (返回账号列表)
|
|
3965
|
+
*
|
|
3966
|
+
* 逻辑:
|
|
3967
|
+
* 1. 从 localStorage 读取 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID
|
|
3968
|
+
* 2. 根据 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID 找到对应账号
|
|
3969
|
+
* - personal 类型: 用 uid 匹配
|
|
3970
|
+
* - 其他类型: 用 enterpriseId 匹配
|
|
3971
|
+
* 3. 如果没有选中的账号,跳转到账号选择页面
|
|
3972
|
+
* 4. 获取套餐信息并合并到账号中
|
|
3973
|
+
* 5. 同步到 accountService
|
|
3974
|
+
*/
|
|
3975
|
+
async getAccount() {
|
|
3976
|
+
const url = `${this.baseUrl}/console/accounts`;
|
|
3977
|
+
const headers = {
|
|
3978
|
+
"Content-Type": "application/json",
|
|
3979
|
+
"Accept": "application/json"
|
|
3980
|
+
};
|
|
3981
|
+
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
3982
|
+
try {
|
|
3983
|
+
const response = await fetch(url, {
|
|
3984
|
+
method: "GET",
|
|
3985
|
+
headers,
|
|
3986
|
+
credentials: "include"
|
|
3987
|
+
});
|
|
3988
|
+
if (!response.ok) {
|
|
3989
|
+
if (response.status === 401 || response.status === 403) {
|
|
3990
|
+
accountService.setAccount(null);
|
|
3991
|
+
return null;
|
|
3992
|
+
}
|
|
3993
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
3994
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
3995
|
+
}
|
|
3996
|
+
if (!(response.headers.get("Content-Type") || "").includes("application/json")) {
|
|
3997
|
+
accountService.setAccount(null);
|
|
3998
|
+
return null;
|
|
3999
|
+
}
|
|
4000
|
+
const { data = {} } = await response.json();
|
|
4001
|
+
const accounts = data?.accounts || [];
|
|
4002
|
+
if (!accounts || accounts.length === 0) {
|
|
4003
|
+
accountService.setAccount(null);
|
|
4004
|
+
return null;
|
|
4005
|
+
}
|
|
4006
|
+
const selectedAccountId = localStorage.getItem(SELECTED_ACCOUNT_KEY);
|
|
4007
|
+
let selectedAccount;
|
|
4008
|
+
if (selectedAccountId) {
|
|
4009
|
+
selectedAccount = accounts.find((account) => {
|
|
4010
|
+
if (account.type === "personal") return account.uid === selectedAccountId;
|
|
4011
|
+
return account.enterpriseId === selectedAccountId;
|
|
4012
|
+
});
|
|
4013
|
+
if (selectedAccount) try {
|
|
4014
|
+
const plan = await this.getCurrentPlan();
|
|
4015
|
+
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
4016
|
+
console.log("account", {
|
|
4017
|
+
...selectedAccount,
|
|
4018
|
+
...plan,
|
|
4019
|
+
editionType
|
|
4020
|
+
});
|
|
4021
|
+
const account = {
|
|
4022
|
+
...selectedAccount,
|
|
4023
|
+
...plan,
|
|
4024
|
+
editionType
|
|
4025
|
+
};
|
|
4026
|
+
accountService.setAccount(account);
|
|
4027
|
+
return account;
|
|
4028
|
+
} catch (error) {
|
|
4029
|
+
accountService.setAccount(selectedAccount);
|
|
4030
|
+
return { ...selectedAccount };
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
if (accounts.length === 1) {
|
|
4034
|
+
selectedAccount = accounts[0];
|
|
4035
|
+
const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
|
|
4036
|
+
if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
|
|
4037
|
+
const plan = await this.getCurrentPlan();
|
|
4038
|
+
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
4039
|
+
console.log("account (auto-selected)", {
|
|
4040
|
+
...selectedAccount,
|
|
4041
|
+
...plan,
|
|
4042
|
+
editionType
|
|
4043
|
+
});
|
|
4044
|
+
const account = {
|
|
4045
|
+
...selectedAccount,
|
|
4046
|
+
...plan,
|
|
4047
|
+
editionType
|
|
4048
|
+
};
|
|
4049
|
+
accountService.setAccount(account);
|
|
4050
|
+
return account;
|
|
4051
|
+
}
|
|
4052
|
+
const redirectUrl = encodeURIComponent(window.location.href);
|
|
4053
|
+
window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
4054
|
+
accountService.setAccount(null);
|
|
4055
|
+
return null;
|
|
4056
|
+
} catch (error) {
|
|
4057
|
+
console.error("[BackendProvider] getAccount failed:", error);
|
|
4058
|
+
accountService.setAccount(null);
|
|
4059
|
+
return null;
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
/**
|
|
4063
|
+
* 获取当前套餐信息
|
|
4064
|
+
* 从计量计费接口获取用户的套餐信息
|
|
4065
|
+
* API: POST /billing/meter/get-user-resource
|
|
4066
|
+
*/
|
|
4067
|
+
async getCurrentPlan() {
|
|
4068
|
+
const defaultPlan = {
|
|
4069
|
+
isPro: false,
|
|
4070
|
+
expireAt: 0,
|
|
4071
|
+
renewFlag: 0,
|
|
4072
|
+
PackageCode: void 0,
|
|
4073
|
+
name: ""
|
|
4074
|
+
};
|
|
4075
|
+
try {
|
|
4076
|
+
const url = `${this.baseUrl}/billing/meter/get-user-resource`;
|
|
4077
|
+
const headers = {
|
|
4078
|
+
"Content-Type": "application/json",
|
|
4079
|
+
"Accept": "application/json"
|
|
4080
|
+
};
|
|
4081
|
+
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
4082
|
+
const now = /* @__PURE__ */ new Date();
|
|
4083
|
+
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
4084
|
+
const formatDate = (d) => {
|
|
4085
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
4086
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
4087
|
+
};
|
|
4088
|
+
const body = {
|
|
4089
|
+
PageNumber: 1,
|
|
4090
|
+
PageSize: 100,
|
|
4091
|
+
ProductCode: "p_tcaca",
|
|
4092
|
+
Status: [AccountStatus.valid, AccountStatus.usedUp],
|
|
4093
|
+
PackageEndTimeRangeBegin: formatDate(now),
|
|
4094
|
+
PackageEndTimeRangeEnd: formatDate(futureDate)
|
|
4095
|
+
};
|
|
4096
|
+
const response = await fetch(url, {
|
|
4097
|
+
method: "POST",
|
|
4098
|
+
headers,
|
|
4099
|
+
credentials: "include",
|
|
4100
|
+
body: JSON.stringify(body)
|
|
4101
|
+
});
|
|
4102
|
+
if (!response.ok) {
|
|
4103
|
+
console.warn("[BackendProvider] getCurrentPlan failed:", response.status);
|
|
4104
|
+
return defaultPlan;
|
|
4105
|
+
}
|
|
4106
|
+
const resources = (await response.json())?.data?.Response?.Data?.Accounts || [];
|
|
4107
|
+
if (!resources || resources.length === 0) return defaultPlan;
|
|
4108
|
+
const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon || r.PackageCode === CommodityCode.proMonPlus);
|
|
4109
|
+
const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift || r.PackageCode === CommodityCode.freeMon);
|
|
4110
|
+
const activePlan = proPlan || trialPlan;
|
|
4111
|
+
if (activePlan) {
|
|
4112
|
+
const parseTime = (time) => {
|
|
4113
|
+
if (!time) return 0;
|
|
4114
|
+
return new Date(time).getTime();
|
|
4115
|
+
};
|
|
4116
|
+
return {
|
|
4117
|
+
isPro: !!proPlan,
|
|
4118
|
+
isTria: [AccountStatus.valid, AccountStatus.usedUp].includes(trialPlan?.Status),
|
|
4119
|
+
expireAt: parseTime(activePlan.DeductionEndTime || activePlan.ExpiredTime || activePlan.CycleEndTime),
|
|
4120
|
+
refreshAt: parseTime(activePlan.CycleEndTime),
|
|
4121
|
+
renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
|
|
4122
|
+
PackageCode: activePlan.PackageCode,
|
|
4123
|
+
name: activePlan.PackageName || "",
|
|
4124
|
+
usageTotal: activePlan.CycleCapacitySizePrecise,
|
|
4125
|
+
usageUsed: activePlan.CapacityUsedPrecise,
|
|
4126
|
+
usageLeft: activePlan.CycleCapacityRemainPrecise
|
|
4127
|
+
};
|
|
4128
|
+
}
|
|
4129
|
+
return defaultPlan;
|
|
4130
|
+
} catch (error) {
|
|
4131
|
+
console.error("[BackendProvider] getCurrentPlan error:", error);
|
|
4132
|
+
return defaultPlan;
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
/**
|
|
4136
|
+
* 根据账号类型和 Pro 状态计算版本展示类型
|
|
4137
|
+
* - personal + isPro = 'pro'
|
|
4138
|
+
* - personal + !isPro = 'free'
|
|
4139
|
+
* - ultimate = 'ultimate' (旗舰版/团队版)
|
|
4140
|
+
* - exclusive = 'exclusive' (专享版/企业版)
|
|
4141
|
+
*/
|
|
4142
|
+
getEditionDisplayType(type, isPro) {
|
|
4143
|
+
if (type === "personal") return isPro ? "pro" : "free";
|
|
4144
|
+
if (type === "ultimate") return "ultimate";
|
|
4145
|
+
if (type === "exclusive") return "exclusive";
|
|
4146
|
+
return "free";
|
|
4147
|
+
}
|
|
4148
|
+
/**
|
|
4149
|
+
* 触发登录流程
|
|
4150
|
+
* Web 环境: 跳转到登录页面
|
|
4151
|
+
*/
|
|
4152
|
+
async login() {
|
|
4153
|
+
const redirectUrl = encodeURIComponent(window.location.href);
|
|
4154
|
+
window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
4155
|
+
}
|
|
4156
|
+
/**
|
|
4157
|
+
* 登出账号
|
|
4158
|
+
* Web 环境: 调用登出接口并清除本地状态
|
|
4159
|
+
*/
|
|
4160
|
+
async logout() {
|
|
4161
|
+
const url = `${this.baseUrl}/console/logout`;
|
|
4162
|
+
try {
|
|
4163
|
+
await fetch(url, {
|
|
4164
|
+
method: "POST",
|
|
4165
|
+
credentials: "include"
|
|
4166
|
+
});
|
|
4167
|
+
} catch (error) {
|
|
4168
|
+
console.error("[BackendProvider] logout failed:", error);
|
|
4169
|
+
}
|
|
4170
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
4171
|
+
accountService.clearAccount();
|
|
4172
|
+
}
|
|
4173
|
+
};
|
|
4174
|
+
/**
|
|
4175
|
+
* 创建 BackendProvider 实例
|
|
4176
|
+
*/
|
|
4177
|
+
function createBackendProvider(config) {
|
|
4178
|
+
return new BackendProvider(config);
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
//#endregion
|
|
4182
|
+
//#region ../agent-provider/src/backend/ipc-backend-provider.ts
|
|
4183
|
+
/**
|
|
4184
|
+
* Backend 请求类型常量
|
|
4185
|
+
*/
|
|
4186
|
+
const BACKEND_REQUEST_TYPES = {
|
|
4187
|
+
GET_AGENTS: "backend:get-agents-request",
|
|
4188
|
+
GET_MODELS: "backend:get-models",
|
|
4189
|
+
LOGIN: "backend:login",
|
|
4190
|
+
LOGOUT: "backend:logout",
|
|
4191
|
+
GET_ACCOUNT: "backend:get-account"
|
|
4192
|
+
};
|
|
4193
|
+
/**
|
|
4194
|
+
* 生成唯一请求 ID
|
|
4195
|
+
*/
|
|
4196
|
+
function generateRequestId() {
|
|
4197
|
+
return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
4198
|
+
}
|
|
4199
|
+
/**
|
|
4200
|
+
* IPC Backend Provider 实现类
|
|
4201
|
+
*
|
|
4202
|
+
* 通过 IWidgetChannel 与后端通信获取 Agent 列表
|
|
4203
|
+
*/
|
|
4204
|
+
var IPCBackendProvider = class {
|
|
4205
|
+
constructor(config) {
|
|
4206
|
+
this.channel = config.channel;
|
|
4207
|
+
this.debug = config.debug ?? false;
|
|
4208
|
+
this.timeoutMs = config.timeoutMs ?? 3e4;
|
|
4209
|
+
this.log("Initialized with IWidgetChannel");
|
|
4210
|
+
}
|
|
4211
|
+
/**
|
|
4212
|
+
* 发送统一格式的后端请求
|
|
4213
|
+
* @param requestType 请求类型
|
|
4214
|
+
* @param params 请求参数
|
|
4215
|
+
* @returns 响应数据
|
|
4216
|
+
*/
|
|
4217
|
+
async sendBackendRequest(requestType, params) {
|
|
4218
|
+
const message = {
|
|
4219
|
+
type: "backend",
|
|
4220
|
+
requestId: generateRequestId(),
|
|
4221
|
+
params: {
|
|
4222
|
+
type: requestType,
|
|
4223
|
+
params
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
this.log("Sending backend request:", message);
|
|
4227
|
+
const response = await this.channel.callMethod("__backend__", message, this.timeoutMs);
|
|
4228
|
+
this.log("Received response:", response);
|
|
4229
|
+
if (response?.error) throw new Error(response.error);
|
|
4230
|
+
return response?.data !== void 0 ? response.data : response;
|
|
4231
|
+
}
|
|
4232
|
+
/**
|
|
4233
|
+
* 获取 Agent 列表
|
|
4234
|
+
* 通过 IWidgetChannel 发送请求到后端
|
|
4235
|
+
*/
|
|
4236
|
+
async getAgents(request = {}) {
|
|
4237
|
+
this.log("Getting agents with request:", request);
|
|
4238
|
+
try {
|
|
4239
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_AGENTS, request);
|
|
4240
|
+
} catch (error) {
|
|
4241
|
+
this.log("Get agents failed:", error);
|
|
4242
|
+
throw error;
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
/**
|
|
4246
|
+
* 获取可用模型列表
|
|
4247
|
+
* 通过 IWidgetChannel 发送请求到后端
|
|
4248
|
+
*/
|
|
4249
|
+
async getModels(request) {
|
|
4250
|
+
this.log("Getting models with request:", request);
|
|
4251
|
+
try {
|
|
4252
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_MODELS, request);
|
|
4253
|
+
} catch (error) {
|
|
4254
|
+
this.log("Get models failed:", error);
|
|
4255
|
+
throw error;
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
/**
|
|
4259
|
+
* 获取当前账号信息
|
|
4260
|
+
* IDE 环境: 通过 IPC 获取账号信息,并同步到 accountService
|
|
4261
|
+
*/
|
|
4262
|
+
async getAccount() {
|
|
4263
|
+
this.log("Getting account via IPC");
|
|
4264
|
+
try {
|
|
4265
|
+
const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
4266
|
+
accountService.setAccount(account);
|
|
4267
|
+
return account;
|
|
4268
|
+
} catch (error) {
|
|
4269
|
+
this.log("Get account failed:", error);
|
|
4270
|
+
accountService.setAccount(null);
|
|
4271
|
+
return null;
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* 触发登录流程
|
|
4276
|
+
* IDE 环境: 通过 IPC 通知 IDE 打开登录流程
|
|
4277
|
+
*/
|
|
4278
|
+
async login() {
|
|
4279
|
+
this.log("Triggering login via IPC");
|
|
4280
|
+
try {
|
|
4281
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGIN);
|
|
4282
|
+
} catch (error) {
|
|
4283
|
+
this.log("Login request failed:", error);
|
|
4284
|
+
throw error;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
/**
|
|
4288
|
+
* 登出账号
|
|
4289
|
+
* IDE 环境: 通过 IPC 通知 IDE 登出
|
|
4290
|
+
*/
|
|
4291
|
+
async logout() {
|
|
4292
|
+
this.log("Triggering logout via IPC");
|
|
4293
|
+
try {
|
|
4294
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGOUT);
|
|
4295
|
+
accountService.clearAccount();
|
|
4296
|
+
} catch (error) {
|
|
4297
|
+
this.log("Logout request failed:", error);
|
|
4298
|
+
throw error;
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* 调试日志
|
|
4303
|
+
*/
|
|
4304
|
+
log(...args) {
|
|
4305
|
+
if (this.debug) console.log("[IPCBackendProvider]", ...args);
|
|
4306
|
+
}
|
|
4307
|
+
};
|
|
4308
|
+
/**
|
|
4309
|
+
* 创建 IPCBackendProvider 实例
|
|
4310
|
+
*/
|
|
4311
|
+
function createIPCBackendProvider(config) {
|
|
4312
|
+
return new IPCBackendProvider(config);
|
|
4313
|
+
}
|
|
4314
|
+
|
|
3867
4315
|
//#endregion
|
|
3868
4316
|
exports.ActiveSessionImpl = ActiveSessionImpl;
|
|
3869
4317
|
exports.AgentClient = AgentClient;
|