@kweaver-ai/kweaver-sdk 0.5.2 → 0.6.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/README.md +19 -1
- package/README.zh.md +19 -1
- package/dist/api/agent-chat.d.ts +7 -1
- package/dist/api/agent-chat.js +146 -40
- package/dist/api/agent-list.js +13 -13
- package/dist/api/business-domains.js +9 -5
- package/dist/api/context-loader.js +4 -1
- package/dist/api/conversations.js +4 -9
- package/dist/api/dataflow2.d.ts +95 -0
- package/dist/api/dataflow2.js +80 -0
- package/dist/api/headers.d.ts +2 -0
- package/dist/api/headers.js +7 -2
- package/dist/api/skills.js +2 -10
- package/dist/api/vega.d.ts +0 -16
- package/dist/api/vega.js +0 -33
- package/dist/auth/oauth.d.ts +1 -1
- package/dist/auth/oauth.js +64 -7
- package/dist/cli.js +21 -1
- package/dist/client.d.ts +9 -0
- package/dist/client.js +48 -8
- package/dist/commands/auth.js +80 -32
- package/dist/commands/bkn-schema.js +22 -0
- package/dist/commands/call.js +8 -5
- package/dist/commands/dataflow.d.ts +1 -0
- package/dist/commands/dataflow.js +251 -0
- package/dist/commands/explore-bkn.d.ts +79 -0
- package/dist/commands/explore-bkn.js +273 -0
- package/dist/commands/explore-chat.d.ts +3 -0
- package/dist/commands/explore-chat.js +193 -0
- package/dist/commands/explore-vega.d.ts +3 -0
- package/dist/commands/explore-vega.js +71 -0
- package/dist/commands/explore.d.ts +9 -0
- package/dist/commands/explore.js +258 -0
- package/dist/commands/vega.js +2 -104
- package/dist/config/no-auth.d.ts +3 -0
- package/dist/config/no-auth.js +5 -0
- package/dist/config/store.d.ts +8 -0
- package/dist/config/store.js +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/kweaver.d.ts +5 -0
- package/dist/kweaver.js +32 -2
- package/dist/resources/bkn.js +2 -3
- package/dist/resources/knowledge-networks.js +3 -8
- package/dist/resources/vega.d.ts +0 -6
- package/dist/resources/vega.js +1 -10
- package/dist/templates/explorer/app.js +136 -0
- package/dist/templates/explorer/bkn.js +747 -0
- package/dist/templates/explorer/chat.js +980 -0
- package/dist/templates/explorer/dashboard.js +82 -0
- package/dist/templates/explorer/index.html +35 -0
- package/dist/templates/explorer/style.css +2440 -0
- package/dist/templates/explorer/vega.js +291 -0
- package/dist/utils/http.d.ts +3 -0
- package/dist/utils/http.js +37 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -142,7 +142,7 @@ const skillMd = await client.skills.fetchContent("skill-id");
|
|
|
142
142
|
## CLI Reference
|
|
143
143
|
|
|
144
144
|
```
|
|
145
|
-
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
145
|
+
kweaver auth login <url> [--alias name] [--no-auth] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
146
146
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (headless login)
|
|
147
147
|
kweaver auth export [url|alias] [--json] (export command to run on a headless host)
|
|
148
148
|
kweaver auth status/list/use/delete/logout
|
|
@@ -150,6 +150,7 @@ kweaver config show / list-bd / set-bd <value> # platform business domain —
|
|
|
150
150
|
kweaver token
|
|
151
151
|
kweaver ds list/get/delete/tables/connect
|
|
152
152
|
kweaver ds import-csv <ds_id> --files <glob> [--table-prefix <p>] [--batch-size 500] [--recreate]
|
|
153
|
+
kweaver dataflow list/run/runs/logs
|
|
153
154
|
kweaver dataview list/find/get/query/delete
|
|
154
155
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
155
156
|
kweaver bkn create-from-ds <ds_id> --name <name> [--tables t1,t2] [--build]
|
|
@@ -169,6 +170,22 @@ kweaver context-loader kn-search/query-object-instance/...
|
|
|
169
170
|
kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
170
171
|
```
|
|
171
172
|
|
|
173
|
+
### Dataflow CLI examples
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
kweaver dataflow list
|
|
177
|
+
kweaver dataflow run <dagId> --file ./demo.pdf
|
|
178
|
+
kweaver dataflow run <dagId> --url https://example.com/demo.pdf --name demo.pdf
|
|
179
|
+
kweaver dataflow runs <dagId>
|
|
180
|
+
kweaver dataflow runs <dagId> --since 2026-04-01
|
|
181
|
+
kweaver dataflow logs <dagId> <instanceId>
|
|
182
|
+
kweaver dataflow logs <dagId> <instanceId> --detail
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`kweaver dataflow runs --since` filters one local natural day. If the value cannot be parsed by `new Date(...)`, the CLI falls back to the most recent 20 runs. `kweaver dataflow logs` defaults to summary output; add `--detail` to print indented `input` and `output` payloads.
|
|
186
|
+
|
|
187
|
+
**No-auth platforms:** If OAuth is not enabled, use `kweaver auth <url> --no-auth` (or run a normal `auth login`; a **404** on `POST /oauth2/clients` switches to no-auth automatically). Credentials are still saved under `~/.kweaver/` and work with `auth use` / `auth list`. Optional: `KWEAVER_NO_AUTH=1` with `KWEAVER_BASE_URL` when no token env is set. SDK: `new KWeaverClient({ baseUrl, auth: false })` or `kweaver.configure({ baseUrl, auth: false })`.
|
|
188
|
+
|
|
172
189
|
## Environment Variables
|
|
173
190
|
|
|
174
191
|
| Variable | Description |
|
|
@@ -176,6 +193,7 @@ kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
|
176
193
|
| `KWEAVER_BASE_URL` | KWeaver instance URL |
|
|
177
194
|
| `KWEAVER_BUSINESS_DOMAIN` | Business domain identifier |
|
|
178
195
|
| `KWEAVER_TOKEN` | Access token |
|
|
196
|
+
| `KWEAVER_NO_AUTH` | Set to `1`/`true`/`yes` to use no-auth sentinel when `KWEAVER_TOKEN` is unset (with `KWEAVER_BASE_URL` or active platform) |
|
|
179
197
|
| `KWEAVER_TLS_INSECURE` | Set to `1` or `true` to skip TLS certificate verification for all HTTPS in the process (dev only; prefer `kweaver auth … --insecure` which saves per platform) |
|
|
180
198
|
| `NODE_TLS_REJECT_UNAUTHORIZED` | Node.js built-in TLS switch: set to `0` to skip certificate verification for HTTPS in this process. The `kweaver` CLI sets this when `KWEAVER_TLS_INSECURE` is set or the saved token has insecure TLS (same scope as above; dev only). |
|
|
181
199
|
|
package/README.zh.md
CHANGED
|
@@ -131,13 +131,14 @@ const skillMd = await client.skills.fetchContent("skill-id");
|
|
|
131
131
|
## 命令速查
|
|
132
132
|
|
|
133
133
|
```
|
|
134
|
-
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
134
|
+
kweaver auth login <url> [--alias name] [--no-auth] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
135
135
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (无浏览器登录)
|
|
136
136
|
kweaver auth export [url|alias] [--json] (导出在无浏览器机器上运行的命令)
|
|
137
137
|
kweaver auth status/list/use/delete/logout
|
|
138
138
|
kweaver config show / list-bd / set-bd <value> # 平台业务域,登录后优先
|
|
139
139
|
kweaver token
|
|
140
140
|
kweaver ds list/get/delete/tables/connect
|
|
141
|
+
kweaver dataflow list/run/runs/logs
|
|
141
142
|
kweaver dataview list/find/get/query/delete
|
|
142
143
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
143
144
|
kweaver bkn object-type list/get/create/update/delete/query/properties
|
|
@@ -153,6 +154,22 @@ kweaver context-loader kn-search/query-object-instance/...
|
|
|
153
154
|
kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
154
155
|
```
|
|
155
156
|
|
|
157
|
+
### Dataflow CLI 示例
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
kweaver dataflow list
|
|
161
|
+
kweaver dataflow run <dagId> --file ./demo.pdf
|
|
162
|
+
kweaver dataflow run <dagId> --url https://example.com/demo.pdf --name demo.pdf
|
|
163
|
+
kweaver dataflow runs <dagId>
|
|
164
|
+
kweaver dataflow runs <dagId> --since 2026-04-01
|
|
165
|
+
kweaver dataflow logs <dagId> <instanceId>
|
|
166
|
+
kweaver dataflow logs <dagId> <instanceId> --detail
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`kweaver dataflow runs --since` 会按本地自然日过滤;如果参数无法被 `new Date(...)` 解析,CLI 会回退到最近 20 条运行记录。`kweaver dataflow logs` 默认输出摘要;加上 `--detail` 会打印带缩进的 `input` 和 `output` 载荷。
|
|
170
|
+
|
|
171
|
+
**无 OAuth 的平台:** 使用 `kweaver auth <url> --no-auth`,或照常 `auth login`;若 `POST /oauth2/clients` 返回 **404**,CLI 会提示并自动保存为 no-auth。凭据仍在 `~/.kweaver/`,可用 `auth use` / `auth list` 切换。可选环境变量 `KWEAVER_NO_AUTH=1`(未设置 `KWEAVER_TOKEN` 时)配合 `KWEAVER_BASE_URL`。SDK:`new KWeaverClient({ baseUrl, auth: false })` 或 `kweaver.configure({ baseUrl, auth: false })`。
|
|
172
|
+
|
|
156
173
|
## 环境变量
|
|
157
174
|
|
|
158
175
|
| 变量 | 说明 |
|
|
@@ -160,6 +177,7 @@ kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
|
160
177
|
| `KWEAVER_BASE_URL` | KWeaver 实例地址 |
|
|
161
178
|
| `KWEAVER_BUSINESS_DOMAIN` | 业务域标识 |
|
|
162
179
|
| `KWEAVER_TOKEN` | 访问令牌 |
|
|
180
|
+
| `KWEAVER_NO_AUTH` | 设为 `1`/`true`/`yes` 且未设置 `KWEAVER_TOKEN` 时使用 no-auth 占位(需 `KWEAVER_BASE_URL` 或已选平台) |
|
|
163
181
|
| `KWEAVER_TLS_INSECURE` | 设为 `1` 或 `true` 时跳过 TLS 证书校验(仅开发;更推荐 `kweaver auth … --insecure` 以按平台持久化) |
|
|
164
182
|
| `NODE_TLS_REJECT_UNAUTHORIZED` | Node.js 内置 TLS 开关:设为 `0` 时在本进程内跳过 HTTPS 证书校验。`kweaver` 在 `KWEAVER_TLS_INSECURE` 生效或已保存 token 为不安全 TLS 时会设置此项(范围同上;仅开发)。 |
|
|
165
183
|
|
package/dist/api/agent-chat.d.ts
CHANGED
|
@@ -58,9 +58,15 @@ export declare function processIncrementalUpdate(data: {
|
|
|
58
58
|
}, result: Record<string, unknown>): void;
|
|
59
59
|
export declare function sendChatRequest(options: SendChatRequestOptions): Promise<ChatResult>;
|
|
60
60
|
export interface SendChatRequestStreamCallbacks {
|
|
61
|
-
onTextDelta: (fullText: string) => void;
|
|
61
|
+
onTextDelta: (fullText: string, currentSegmentText: string) => void;
|
|
62
62
|
/** Optional: called when message.content.middle_answer.progress updates (tool/skill steps). */
|
|
63
63
|
onProgress?: (progress: ProgressItem[]) => void;
|
|
64
|
+
/** Optional: called when a text segment is completed and a new phase starts. */
|
|
65
|
+
onSegmentComplete?: (segmentText: string, segmentIndex: number) => void;
|
|
66
|
+
/** Optional: called when answer_type_other changes (step metadata like status, tool info). */
|
|
67
|
+
onStepMeta?: (meta: Record<string, unknown>) => void;
|
|
68
|
+
/** Optional: called as soon as conversationId is discovered in the stream. */
|
|
69
|
+
onConversationId?: (conversationId: string) => void;
|
|
64
70
|
}
|
|
65
71
|
/**
|
|
66
72
|
* Stream-only entry point for TUI: same as sendChatRequest with stream=true,
|
package/dist/api/agent-chat.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isNoAuth } from "../config/no-auth.js";
|
|
2
|
+
import { fetchTextOrThrow, fetchWithRetry, HttpError } from "../utils/http.js";
|
|
2
3
|
import { normalizeDisplayText } from "../utils/display-text.js";
|
|
3
4
|
const CHAT_PATH = "/api/agent-factory/v1/app";
|
|
4
5
|
const AGENT_INFO_PATH = "/api/agent-factory/v3/agent-market/agent";
|
|
@@ -13,16 +14,19 @@ export function buildAgentInfoUrl(baseUrl, agentId, version) {
|
|
|
13
14
|
export async function fetchAgentInfo(options) {
|
|
14
15
|
const { baseUrl, accessToken, agentId, version, businessDomain = "bd_public" } = options;
|
|
15
16
|
const url = buildAgentInfoUrl(baseUrl, agentId, version);
|
|
17
|
+
const agentHeaders = {
|
|
18
|
+
accept: "application/json, text/plain, */*",
|
|
19
|
+
"x-business-domain": businessDomain,
|
|
20
|
+
"x-language": "zh-CN",
|
|
21
|
+
"x-requested-with": "XMLHttpRequest",
|
|
22
|
+
};
|
|
23
|
+
if (!isNoAuth(accessToken)) {
|
|
24
|
+
agentHeaders.Authorization = `Bearer ${accessToken}`;
|
|
25
|
+
agentHeaders.token = accessToken;
|
|
26
|
+
}
|
|
16
27
|
const { body } = await fetchTextOrThrow(url, {
|
|
17
28
|
method: "GET",
|
|
18
|
-
headers:
|
|
19
|
-
accept: "application/json, text/plain, */*",
|
|
20
|
-
Authorization: `Bearer ${accessToken}`,
|
|
21
|
-
token: accessToken,
|
|
22
|
-
"x-business-domain": businessDomain,
|
|
23
|
-
"x-language": "zh-CN",
|
|
24
|
-
"x-requested-with": "XMLHttpRequest",
|
|
25
|
-
},
|
|
29
|
+
headers: agentHeaders,
|
|
26
30
|
});
|
|
27
31
|
const data = JSON.parse(body);
|
|
28
32
|
if (!data.id || !data.key) {
|
|
@@ -67,6 +71,19 @@ function stringFromAnswerObject(obj) {
|
|
|
67
71
|
const parts = [d, c ? `code: ${c}` : "", s ? `solution: ${s}` : ""].filter(Boolean);
|
|
68
72
|
return parts.join("\n");
|
|
69
73
|
}
|
|
74
|
+
/** Format answer_type_other which may be an array of strings or an object. */
|
|
75
|
+
function stringFromAnswerTypeOther(other) {
|
|
76
|
+
if (Array.isArray(other)) {
|
|
77
|
+
const strings = other.filter((s) => typeof s === "string" && s);
|
|
78
|
+
if (strings.length > 0)
|
|
79
|
+
return JSON.stringify(strings);
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
if (other && typeof other === "object") {
|
|
83
|
+
return stringFromAnswerObject(other);
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
70
87
|
export function extractText(data) {
|
|
71
88
|
if (!data || typeof data !== "object")
|
|
72
89
|
return "";
|
|
@@ -74,44 +91,44 @@ export function extractText(data) {
|
|
|
74
91
|
const fa = obj.final_answer;
|
|
75
92
|
if (fa?.answer && typeof fa.answer === "object") {
|
|
76
93
|
const ans = fa.answer;
|
|
77
|
-
if (typeof ans.text === "string")
|
|
94
|
+
if (typeof ans.text === "string" && ans.text)
|
|
78
95
|
return ans.text;
|
|
79
96
|
}
|
|
80
|
-
if (typeof fa?.text === "string") {
|
|
97
|
+
if (typeof fa?.text === "string" && fa.text) {
|
|
81
98
|
return fa.text;
|
|
82
99
|
}
|
|
100
|
+
// Check answer_type_other at final_answer level (for content_type "other")
|
|
101
|
+
if (fa?.answer_type_other) {
|
|
102
|
+
const desc = stringFromAnswerTypeOther(fa.answer_type_other);
|
|
103
|
+
if (desc)
|
|
104
|
+
return desc;
|
|
105
|
+
}
|
|
83
106
|
const msg = obj.message;
|
|
84
|
-
if (typeof msg?.text === "string") {
|
|
107
|
+
if (typeof msg?.text === "string" && msg.text) {
|
|
85
108
|
return msg.text;
|
|
86
109
|
}
|
|
87
110
|
if (msg?.content && typeof msg.content === "object") {
|
|
88
111
|
const content = msg.content;
|
|
89
|
-
if (typeof content.text === "string")
|
|
112
|
+
if (typeof content.text === "string" && content.text)
|
|
90
113
|
return content.text;
|
|
91
114
|
const contentFinalAnswer = content.final_answer;
|
|
92
115
|
if (contentFinalAnswer?.answer && typeof contentFinalAnswer.answer === "object") {
|
|
93
116
|
const answer = contentFinalAnswer.answer;
|
|
94
|
-
if (typeof answer.text === "string")
|
|
117
|
+
if (typeof answer.text === "string" && answer.text)
|
|
95
118
|
return answer.text;
|
|
96
119
|
}
|
|
97
|
-
if (typeof contentFinalAnswer?.text === "string") {
|
|
120
|
+
if (typeof contentFinalAnswer?.text === "string" && contentFinalAnswer.text) {
|
|
98
121
|
return contentFinalAnswer.text;
|
|
99
122
|
}
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
const desc =
|
|
123
|
+
// Check answer_type_other at content.final_answer level
|
|
124
|
+
if (contentFinalAnswer?.answer_type_other) {
|
|
125
|
+
const desc = stringFromAnswerTypeOther(contentFinalAnswer.answer_type_other);
|
|
103
126
|
if (desc)
|
|
104
127
|
return desc;
|
|
105
128
|
}
|
|
106
129
|
}
|
|
107
|
-
const topOther = fa?.answer_type_other;
|
|
108
|
-
if (topOther && typeof topOther === "object") {
|
|
109
|
-
const desc = stringFromAnswerObject(topOther);
|
|
110
|
-
if (desc)
|
|
111
|
-
return desc;
|
|
112
|
-
}
|
|
113
130
|
const answer = obj.answer;
|
|
114
|
-
if (typeof answer?.text === "string") {
|
|
131
|
+
if (typeof answer?.text === "string" && answer.text) {
|
|
115
132
|
return answer.text;
|
|
116
133
|
}
|
|
117
134
|
return "";
|
|
@@ -194,7 +211,7 @@ function getProgressFromResult(result) {
|
|
|
194
211
|
};
|
|
195
212
|
});
|
|
196
213
|
}
|
|
197
|
-
function applySseDataLine(line, state, verbose, onTextDelta, onProgress) {
|
|
214
|
+
function applySseDataLine(line, state, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId) {
|
|
198
215
|
if (!line.startsWith("data: ")) {
|
|
199
216
|
return;
|
|
200
217
|
}
|
|
@@ -208,21 +225,45 @@ function applySseDataLine(line, state, verbose, onTextDelta, onProgress) {
|
|
|
208
225
|
if (data.key?.length === 1 && data.key[0] === "conversation_id" && data.action === "upsert") {
|
|
209
226
|
state.conversationId =
|
|
210
227
|
typeof data.content === "string" ? data.content : String(data.content ?? "");
|
|
228
|
+
if (state.conversationId && onConversationId) {
|
|
229
|
+
onConversationId(state.conversationId);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Detect answer_type_other changes (step metadata: status, end_time, etc.)
|
|
233
|
+
if (data.key && data.key.join(".").includes("answer_type_other") && data.action === "upsert") {
|
|
234
|
+
const ato = getByPath(state.result, ["message", "content", "final_answer", "answer_type_other"]);
|
|
235
|
+
console.error(`[STEP_META] ${JSON.stringify(ato).slice(0, 500)}`);
|
|
236
|
+
if (ato && typeof ato === "object" && onStepMeta) {
|
|
237
|
+
onStepMeta(ato);
|
|
238
|
+
}
|
|
211
239
|
}
|
|
212
240
|
const progress = getProgressFromResult(state.result);
|
|
213
241
|
if (progress.length > 0 && onProgress) {
|
|
214
242
|
onProgress(progress);
|
|
215
243
|
}
|
|
216
|
-
const
|
|
217
|
-
|
|
244
|
+
const rawText = normalizeDisplayText(extractText(state.result));
|
|
245
|
+
// Detect when the upstream clears text between steps: previous had content, now empty or
|
|
246
|
+
// significantly shorter (new segment starting). Save the completed segment.
|
|
247
|
+
if (state.prevRawText && (!rawText || rawText.length < state.prevRawText.length * 0.5)) {
|
|
248
|
+
state.completedSegments.push(state.prevRawText);
|
|
249
|
+
if (onSegmentComplete) {
|
|
250
|
+
onSegmentComplete(state.prevRawText, state.completedSegments.length - 1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
state.prevRawText = rawText;
|
|
254
|
+
// Build full text: completed segments + current segment
|
|
255
|
+
const fullText = state.completedSegments.length > 0
|
|
256
|
+
? state.completedSegments.join("\n\n") + (rawText ? "\n\n" + rawText : "")
|
|
257
|
+
: rawText;
|
|
258
|
+
if (fullText && fullText !== state.lastText) {
|
|
218
259
|
if (onTextDelta) {
|
|
219
|
-
onTextDelta(
|
|
260
|
+
onTextDelta(fullText, rawText);
|
|
220
261
|
}
|
|
221
262
|
else {
|
|
222
|
-
const delta =
|
|
263
|
+
const delta = fullText.slice(state.lastText.length);
|
|
223
264
|
process.stdout.write(delta);
|
|
224
265
|
}
|
|
225
|
-
state.lastText =
|
|
266
|
+
state.lastText = fullText;
|
|
226
267
|
}
|
|
227
268
|
}
|
|
228
269
|
catch {
|
|
@@ -247,11 +288,13 @@ export async function sendChatRequest(options) {
|
|
|
247
288
|
const headers = {
|
|
248
289
|
"Content-Type": "application/json",
|
|
249
290
|
accept: stream ? "text/event-stream" : "application/json",
|
|
250
|
-
Authorization: `Bearer ${accessToken}`,
|
|
251
291
|
"Accept-Language": "zh-CN",
|
|
252
292
|
"x-Language": "zh-CN",
|
|
253
293
|
"x-business-domain": businessDomain,
|
|
254
294
|
};
|
|
295
|
+
if (!isNoAuth(accessToken)) {
|
|
296
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
297
|
+
}
|
|
255
298
|
if (verbose) {
|
|
256
299
|
console.error(`POST ${url}`);
|
|
257
300
|
const safeHeaders = Object.fromEntries(Object.entries(headers).map(([k, v]) => k.toLowerCase() === "authorization" ? [k, "Bearer ***"] : [k, v]));
|
|
@@ -260,7 +303,7 @@ export async function sendChatRequest(options) {
|
|
|
260
303
|
}
|
|
261
304
|
let response;
|
|
262
305
|
try {
|
|
263
|
-
response = await
|
|
306
|
+
response = await fetchWithRetry(url, {
|
|
264
307
|
method: "POST",
|
|
265
308
|
headers,
|
|
266
309
|
body: JSON.stringify(body),
|
|
@@ -306,11 +349,13 @@ export async function sendChatRequestStream(options, callbacks) {
|
|
|
306
349
|
const headers = {
|
|
307
350
|
"Content-Type": "application/json",
|
|
308
351
|
accept: "text/event-stream",
|
|
309
|
-
Authorization: `Bearer ${accessToken}`,
|
|
310
352
|
"Accept-Language": "zh-CN",
|
|
311
353
|
"x-Language": "zh-CN",
|
|
312
354
|
"x-business-domain": businessDomain,
|
|
313
355
|
};
|
|
356
|
+
if (!isNoAuth(accessToken)) {
|
|
357
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
358
|
+
}
|
|
314
359
|
if (verbose) {
|
|
315
360
|
console.error(`POST ${url}`);
|
|
316
361
|
const safeHeaders = Object.fromEntries(Object.entries(headers).map(([k, v]) => k.toLowerCase() === "authorization" ? [k, "Bearer ***"] : [k, v]));
|
|
@@ -319,7 +364,7 @@ export async function sendChatRequestStream(options, callbacks) {
|
|
|
319
364
|
}
|
|
320
365
|
let response;
|
|
321
366
|
try {
|
|
322
|
-
response = await
|
|
367
|
+
response = await fetchWithRetry(url, {
|
|
323
368
|
method: "POST",
|
|
324
369
|
headers,
|
|
325
370
|
body: JSON.stringify(body),
|
|
@@ -335,29 +380,75 @@ export async function sendChatRequestStream(options, callbacks) {
|
|
|
335
380
|
throw new HttpError(response.status, response.statusText, text);
|
|
336
381
|
}
|
|
337
382
|
if (contentType.includes("text/event-stream")) {
|
|
338
|
-
return handleStreamResponse(response, verbose, callbacks.onTextDelta, callbacks.onProgress);
|
|
383
|
+
return handleStreamResponse(response, verbose, callbacks.onTextDelta, callbacks.onProgress, callbacks.onSegmentComplete, callbacks.onStepMeta, callbacks.onConversationId);
|
|
339
384
|
}
|
|
340
385
|
const text = await response.text();
|
|
341
386
|
const json = parseJsonResponse(text);
|
|
342
387
|
const resultText = normalizeDisplayText(extractText(json));
|
|
343
388
|
const convId = json.conversation_id;
|
|
344
|
-
callbacks.onTextDelta(resultText);
|
|
389
|
+
callbacks.onTextDelta(resultText, resultText);
|
|
345
390
|
return { text: resultText, conversationId: convId, progress: getProgressFromResult(json) };
|
|
346
391
|
}
|
|
347
|
-
async function handleStreamResponse(response, verbose, onTextDelta, onProgress) {
|
|
392
|
+
async function handleStreamResponse(response, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId) {
|
|
348
393
|
const reader = response.body?.getReader();
|
|
349
394
|
if (!reader) {
|
|
350
395
|
throw new Error("No response body for stream");
|
|
351
396
|
}
|
|
352
397
|
const decoder = new TextDecoder();
|
|
353
398
|
let buffer = "";
|
|
399
|
+
let pendingEventType = "";
|
|
354
400
|
const state = {
|
|
355
401
|
result: {},
|
|
356
402
|
conversationId: undefined,
|
|
357
403
|
lastText: "",
|
|
404
|
+
completedSegments: [],
|
|
405
|
+
prevRawText: "",
|
|
358
406
|
};
|
|
359
407
|
const applyLine = (line) => {
|
|
360
|
-
|
|
408
|
+
// Track SSE event type (e.g., "event:error")
|
|
409
|
+
if (line.startsWith("event:")) {
|
|
410
|
+
pendingEventType = line.slice(6).trim();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// If we have an error event type, handle the data line as an error
|
|
414
|
+
if (pendingEventType === "error" && line.startsWith("data: ")) {
|
|
415
|
+
pendingEventType = "";
|
|
416
|
+
const errStr = line.slice(6).trim();
|
|
417
|
+
// Emit as error text rather than processing as incremental update
|
|
418
|
+
if (onTextDelta) {
|
|
419
|
+
let errMsg = errStr;
|
|
420
|
+
let errDetail = "";
|
|
421
|
+
try {
|
|
422
|
+
const errObj = JSON.parse(errStr);
|
|
423
|
+
errMsg = errObj.description || errObj.details || errStr;
|
|
424
|
+
if (errObj.solution && errObj.solution !== "无")
|
|
425
|
+
errMsg += "\n💡 " + errObj.solution;
|
|
426
|
+
// Collect all remaining fields as detail context
|
|
427
|
+
const detailFields = {};
|
|
428
|
+
for (const [k, v] of Object.entries(errObj)) {
|
|
429
|
+
if (k !== "description" && k !== "solution" && v != null && v !== "") {
|
|
430
|
+
detailFields[k] = v;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (Object.keys(detailFields).length > 0) {
|
|
434
|
+
errDetail = "\n\n<details><summary>详细错误信息</summary>\n\n```json\n" + JSON.stringify(detailFields, null, 2) + "\n```\n</details>";
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
if (verbose)
|
|
439
|
+
console.error("Failed to parse SSE error JSON:", errStr);
|
|
440
|
+
}
|
|
441
|
+
const errText = "⚠️ " + errMsg + errDetail;
|
|
442
|
+
const fullText = state.completedSegments.length > 0
|
|
443
|
+
? state.completedSegments.join("\n\n") + "\n\n" + errText
|
|
444
|
+
: errText;
|
|
445
|
+
onTextDelta(fullText, errText);
|
|
446
|
+
state.lastText = fullText;
|
|
447
|
+
}
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
pendingEventType = "";
|
|
451
|
+
applySseDataLine(line, state, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId);
|
|
361
452
|
};
|
|
362
453
|
while (true) {
|
|
363
454
|
const { done, value } = await reader.read();
|
|
@@ -376,6 +467,21 @@ async function handleStreamResponse(response, verbose, onTextDelta, onProgress)
|
|
|
376
467
|
if (!onTextDelta && state.lastText && !state.lastText.endsWith("\n")) {
|
|
377
468
|
process.stdout.write("\n");
|
|
378
469
|
}
|
|
379
|
-
|
|
470
|
+
// Fallback: try to extract conversationId from accumulated result if not found in stream
|
|
471
|
+
if (!state.conversationId) {
|
|
472
|
+
const r = state.result;
|
|
473
|
+
const candidate = r.conversation_id ??
|
|
474
|
+
r.message?.conversation_id ??
|
|
475
|
+
r.conversationId;
|
|
476
|
+
if (typeof candidate === "string" && candidate) {
|
|
477
|
+
state.conversationId = candidate;
|
|
478
|
+
if (onConversationId)
|
|
479
|
+
onConversationId(candidate);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const rawFinal = normalizeDisplayText(extractText(state.result));
|
|
483
|
+
const finalText = state.completedSegments.length > 0
|
|
484
|
+
? state.completedSegments.join("\n\n") + (rawFinal ? "\n\n" + rawFinal : "")
|
|
485
|
+
: rawFinal || state.lastText;
|
|
380
486
|
return { text: finalText, conversationId: state.conversationId };
|
|
381
487
|
}
|
package/dist/api/agent-list.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpError } from "../utils/http.js";
|
|
1
|
+
import { HttpError, fetchWithRetry } from "../utils/http.js";
|
|
2
2
|
import { buildHeaders } from "./headers.js";
|
|
3
3
|
export async function listAgents(options) {
|
|
4
4
|
const { baseUrl, accessToken, businessDomain = "bd_public", name = "", offset = 0, limit = 50, category_id = "", custom_space_id = "", is_to_square = 1, } = options;
|
|
@@ -12,7 +12,7 @@ export async function listAgents(options) {
|
|
|
12
12
|
custom_space_id,
|
|
13
13
|
is_to_square,
|
|
14
14
|
});
|
|
15
|
-
const response = await
|
|
15
|
+
const response = await fetchWithRetry(url, {
|
|
16
16
|
method: "POST",
|
|
17
17
|
headers: buildHeaders(accessToken, businessDomain),
|
|
18
18
|
body,
|
|
@@ -27,7 +27,7 @@ export async function getAgent(options) {
|
|
|
27
27
|
const { baseUrl, accessToken, agentId, businessDomain = "bd_public", } = options;
|
|
28
28
|
const base = baseUrl.replace(/\/+$/, "");
|
|
29
29
|
const url = `${base}/api/agent-factory/v3/agent/${encodeURIComponent(agentId)}`;
|
|
30
|
-
const response = await
|
|
30
|
+
const response = await fetchWithRetry(url, {
|
|
31
31
|
method: "GET",
|
|
32
32
|
headers: buildHeaders(accessToken, businessDomain),
|
|
33
33
|
});
|
|
@@ -41,7 +41,7 @@ export async function getAgentByKey(options) {
|
|
|
41
41
|
const { baseUrl, accessToken, key, businessDomain = "bd_public", } = options;
|
|
42
42
|
const base = baseUrl.replace(/\/+$/, "");
|
|
43
43
|
const url = `${base}/api/agent-factory/v3/agent/by-key/${encodeURIComponent(key)}`;
|
|
44
|
-
const response = await
|
|
44
|
+
const response = await fetchWithRetry(url, {
|
|
45
45
|
method: "GET",
|
|
46
46
|
headers: buildHeaders(accessToken, businessDomain),
|
|
47
47
|
});
|
|
@@ -55,7 +55,7 @@ export async function createAgent(options) {
|
|
|
55
55
|
const { baseUrl, accessToken, body, businessDomain = "bd_public", } = options;
|
|
56
56
|
const base = baseUrl.replace(/\/+$/, "");
|
|
57
57
|
const url = `${base}/api/agent-factory/v3/agent`;
|
|
58
|
-
const response = await
|
|
58
|
+
const response = await fetchWithRetry(url, {
|
|
59
59
|
method: "POST",
|
|
60
60
|
headers: {
|
|
61
61
|
...buildHeaders(accessToken, businessDomain),
|
|
@@ -73,7 +73,7 @@ export async function updateAgent(options) {
|
|
|
73
73
|
const { baseUrl, accessToken, agentId, body, businessDomain = "bd_public", } = options;
|
|
74
74
|
const base = baseUrl.replace(/\/+$/, "");
|
|
75
75
|
const url = `${base}/api/agent-factory/v3/agent/${encodeURIComponent(agentId)}`;
|
|
76
|
-
const response = await
|
|
76
|
+
const response = await fetchWithRetry(url, {
|
|
77
77
|
method: "PUT",
|
|
78
78
|
headers: {
|
|
79
79
|
...buildHeaders(accessToken, businessDomain),
|
|
@@ -91,7 +91,7 @@ export async function deleteAgent(options) {
|
|
|
91
91
|
const { baseUrl, accessToken, agentId, businessDomain = "bd_public", } = options;
|
|
92
92
|
const base = baseUrl.replace(/\/+$/, "");
|
|
93
93
|
const url = `${base}/api/agent-factory/v3/agent/${encodeURIComponent(agentId)}`;
|
|
94
|
-
const response = await
|
|
94
|
+
const response = await fetchWithRetry(url, {
|
|
95
95
|
method: "DELETE",
|
|
96
96
|
headers: buildHeaders(accessToken, businessDomain),
|
|
97
97
|
});
|
|
@@ -113,7 +113,7 @@ export async function publishAgent(options) {
|
|
|
113
113
|
publish_to_bes: ["skill_agent"],
|
|
114
114
|
pms_control: null,
|
|
115
115
|
});
|
|
116
|
-
const response = await
|
|
116
|
+
const response = await fetchWithRetry(url, {
|
|
117
117
|
method: "POST",
|
|
118
118
|
headers: {
|
|
119
119
|
...buildHeaders(accessToken, businessDomain),
|
|
@@ -131,7 +131,7 @@ export async function unpublishAgent(options) {
|
|
|
131
131
|
const { baseUrl, accessToken, agentId, businessDomain = "bd_public", } = options;
|
|
132
132
|
const base = baseUrl.replace(/\/+$/, "");
|
|
133
133
|
const url = `${base}/api/agent-factory/v3/agent/${encodeURIComponent(agentId)}/unpublish`;
|
|
134
|
-
const response = await
|
|
134
|
+
const response = await fetchWithRetry(url, {
|
|
135
135
|
method: "PUT",
|
|
136
136
|
headers: buildHeaders(accessToken, businessDomain),
|
|
137
137
|
});
|
|
@@ -154,7 +154,7 @@ export async function listPersonalAgents(options) {
|
|
|
154
154
|
params.append("publish_to_be", publish_to_be);
|
|
155
155
|
params.append("size", String(size));
|
|
156
156
|
const url = `${base}/api/agent-factory/v3/personal-space/agent-list?${params.toString()}`;
|
|
157
|
-
const response = await
|
|
157
|
+
const response = await fetchWithRetry(url, {
|
|
158
158
|
method: "GET",
|
|
159
159
|
headers: buildHeaders(accessToken, businessDomain),
|
|
160
160
|
});
|
|
@@ -176,7 +176,7 @@ export async function listPublishedAgentTemplates(options) {
|
|
|
176
176
|
params.append("pagination_marker_str", pagination_marker_str);
|
|
177
177
|
params.append("size", String(size));
|
|
178
178
|
const url = `${base}/api/agent-factory/v3/published/agent-tpl?${params.toString()}`;
|
|
179
|
-
const response = await
|
|
179
|
+
const response = await fetchWithRetry(url, {
|
|
180
180
|
method: "GET",
|
|
181
181
|
headers: buildHeaders(accessToken, businessDomain),
|
|
182
182
|
});
|
|
@@ -190,7 +190,7 @@ export async function getPublishedAgentTemplate(options) {
|
|
|
190
190
|
const { baseUrl, accessToken, templateId, businessDomain = "bd_public", } = options;
|
|
191
191
|
const base = baseUrl.replace(/\/+$/, "");
|
|
192
192
|
const url = `${base}/api/agent-factory/v3/published/agent-tpl/${encodeURIComponent(templateId)}`;
|
|
193
|
-
const response = await
|
|
193
|
+
const response = await fetchWithRetry(url, {
|
|
194
194
|
method: "GET",
|
|
195
195
|
headers: buildHeaders(accessToken, businessDomain),
|
|
196
196
|
});
|
|
@@ -204,7 +204,7 @@ export async function listAgentCategories(options) {
|
|
|
204
204
|
const { baseUrl, accessToken, businessDomain = "bd_public", } = options;
|
|
205
205
|
const base = baseUrl.replace(/\/+$/, "");
|
|
206
206
|
const url = `${base}/api/agent-factory/v3/category`;
|
|
207
|
-
const response = await
|
|
207
|
+
const response = await fetchWithRetry(url, {
|
|
208
208
|
method: "GET",
|
|
209
209
|
headers: buildHeaders(accessToken, businessDomain),
|
|
210
210
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNoAuth } from "../config/no-auth.js";
|
|
1
2
|
import { HttpError } from "../utils/http.js";
|
|
2
3
|
async function withTlsInsecure(tlsInsecure, fn) {
|
|
3
4
|
if (!tlsInsecure) {
|
|
@@ -26,13 +27,16 @@ export async function listBusinessDomains(options) {
|
|
|
26
27
|
const base = baseUrl.replace(/\/+$/, "");
|
|
27
28
|
const url = `${base}/api/business-system/v1/business-domain`;
|
|
28
29
|
return withTlsInsecure(tlsInsecure, async () => {
|
|
30
|
+
const headers = {
|
|
31
|
+
accept: "application/json, text/plain, */*",
|
|
32
|
+
};
|
|
33
|
+
if (!isNoAuth(accessToken)) {
|
|
34
|
+
headers.authorization = `Bearer ${accessToken}`;
|
|
35
|
+
headers.token = accessToken;
|
|
36
|
+
}
|
|
29
37
|
const response = await fetch(url, {
|
|
30
38
|
method: "GET",
|
|
31
|
-
headers
|
|
32
|
-
accept: "application/json, text/plain, */*",
|
|
33
|
-
authorization: `Bearer ${accessToken}`,
|
|
34
|
-
token: accessToken,
|
|
35
|
-
},
|
|
39
|
+
headers,
|
|
36
40
|
});
|
|
37
41
|
const body = await response.text();
|
|
38
42
|
if (!response.ok) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNoAuth } from "../config/no-auth.js";
|
|
1
2
|
import { fetchTextOrThrow } from "../utils/http.js";
|
|
2
3
|
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
3
4
|
const SESSION_TTL_MS = 300_000; // 5 minutes
|
|
@@ -9,10 +10,12 @@ function buildHeaders(options, sessionId) {
|
|
|
9
10
|
const headers = {
|
|
10
11
|
"Content-Type": "application/json",
|
|
11
12
|
Accept: "application/json, text/event-stream",
|
|
12
|
-
Authorization: `Bearer ${options.accessToken}`,
|
|
13
13
|
"X-Kn-ID": options.knId,
|
|
14
14
|
"MCP-Protocol-Version": MCP_PROTOCOL_VERSION,
|
|
15
15
|
};
|
|
16
|
+
if (!isNoAuth(options.accessToken)) {
|
|
17
|
+
headers.Authorization = `Bearer ${options.accessToken}`;
|
|
18
|
+
}
|
|
16
19
|
if (sessionId) {
|
|
17
20
|
headers["MCP-Session-Id"] = sessionId;
|
|
18
21
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildHeaders } from "./headers.js";
|
|
1
2
|
function buildConversationsUrl(baseUrl, agentKey) {
|
|
2
3
|
const base = baseUrl.replace(/\/+$/, "");
|
|
3
4
|
return `${base}/api/agent-factory/v1/app/${agentKey}/conversation`;
|
|
@@ -19,9 +20,7 @@ export async function listConversations(opts) {
|
|
|
19
20
|
method: "GET",
|
|
20
21
|
headers: {
|
|
21
22
|
accept: "application/json",
|
|
22
|
-
|
|
23
|
-
token: accessToken,
|
|
24
|
-
"x-business-domain": businessDomain,
|
|
23
|
+
...buildHeaders(accessToken, businessDomain),
|
|
25
24
|
},
|
|
26
25
|
});
|
|
27
26
|
const body = await response.text();
|
|
@@ -42,9 +41,7 @@ export async function getTracesByConversation(opts) {
|
|
|
42
41
|
headers: {
|
|
43
42
|
"Content-Type": "application/json",
|
|
44
43
|
accept: "application/json",
|
|
45
|
-
|
|
46
|
-
token: accessToken,
|
|
47
|
-
"x-business-domain": businessDomain,
|
|
44
|
+
...buildHeaders(accessToken, businessDomain),
|
|
48
45
|
},
|
|
49
46
|
body: JSON.stringify({
|
|
50
47
|
agent_id: agentId,
|
|
@@ -71,9 +68,7 @@ export async function listMessages(opts) {
|
|
|
71
68
|
method: "GET",
|
|
72
69
|
headers: {
|
|
73
70
|
accept: "application/json",
|
|
74
|
-
|
|
75
|
-
token: accessToken,
|
|
76
|
-
"x-business-domain": businessDomain,
|
|
71
|
+
...buildHeaders(accessToken, businessDomain),
|
|
77
72
|
},
|
|
78
73
|
});
|
|
79
74
|
const body = await response.text();
|