@downcity/agent 1.1.157 → 1.1.160
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/bin/executor/messages/AssistantFileResource.d.ts.map +1 -1
- package/bin/executor/messages/AssistantFileResource.js +106 -15
- package/bin/executor/messages/AssistantFileResource.js.map +1 -1
- package/package.json +3 -3
- package/scripts/image-plugin-job.test.mjs +73 -0
- package/src/executor/messages/AssistantFileResource.ts +121 -16
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantFileResource.d.ts","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAcrC;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"AssistantFileResource.d.ts","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAcrC;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AA8RD;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,mCAAmC,GAC1C,OAAO,CAAC,UAAU,EAAE,CAAC,CAYvB"}
|
|
@@ -98,21 +98,81 @@ async function write_resource_file(params) {
|
|
|
98
98
|
return file_path;
|
|
99
99
|
}
|
|
100
100
|
async function read_remote_resource(raw_url) {
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
103
|
-
throw new Error(`Failed to download assistant file resource: ${
|
|
101
|
+
const result = await fetch_with_retry(raw_url);
|
|
102
|
+
if (!result.ok) {
|
|
103
|
+
throw new Error(`Failed to download assistant file resource: ${result.status} :: url=${raw_url}`);
|
|
104
104
|
}
|
|
105
|
-
const array_buffer = await
|
|
105
|
+
const array_buffer = await result.arrayBuffer();
|
|
106
106
|
const bytes = Buffer.from(array_buffer);
|
|
107
107
|
if (bytes.length === 0) {
|
|
108
|
-
throw new Error(
|
|
108
|
+
throw new Error(`Downloaded assistant file resource is empty :: url=${raw_url}`);
|
|
109
109
|
}
|
|
110
110
|
return {
|
|
111
|
-
mediaType:
|
|
111
|
+
mediaType: result.headers.get("content-type")?.split(";")[0]?.trim(),
|
|
112
112
|
filename: filename_from_url(raw_url),
|
|
113
113
|
bytes,
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* 带重试的 fetch 远程资源。
|
|
118
|
+
*
|
|
119
|
+
* 关键点(中文)
|
|
120
|
+
* - 对 transient 网络错误(fetch failed / UND_ERR_* / ECONNRESET 等)做 2 次指数退避重试。
|
|
121
|
+
* - 最终失败时把 error.cause 链展开到 message,方便定位是代理问题、DNS 问题还是上游问题。
|
|
122
|
+
*/
|
|
123
|
+
async function fetch_with_retry(raw_url) {
|
|
124
|
+
const delays_ms = [250, 1_000];
|
|
125
|
+
const attempts = delays_ms.length + 1;
|
|
126
|
+
let last_error;
|
|
127
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
128
|
+
try {
|
|
129
|
+
return await fetch(raw_url);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
last_error = error;
|
|
133
|
+
if (!is_transient_fetch_error(error) || attempt === attempts - 1) {
|
|
134
|
+
throw enrich_fetch_error(error, raw_url);
|
|
135
|
+
}
|
|
136
|
+
const delay_ms = delays_ms[attempt] ?? 1_000;
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, delay_ms));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
throw enrich_fetch_error(last_error, raw_url);
|
|
141
|
+
}
|
|
142
|
+
function is_transient_fetch_error(error) {
|
|
143
|
+
if (!(error instanceof Error))
|
|
144
|
+
return false;
|
|
145
|
+
const text = `${error.message} ${describe_error_cause(error)}`;
|
|
146
|
+
return /fetch failed|UND_ERR|ECONN(RESET|REFUSED|ABORTED)|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up/i.test(text);
|
|
147
|
+
}
|
|
148
|
+
function enrich_fetch_error(error, url) {
|
|
149
|
+
if (!(error instanceof Error)) {
|
|
150
|
+
return new Error(`Download failed: ${String(error)} :: url=${url}`);
|
|
151
|
+
}
|
|
152
|
+
const cause_text = describe_error_cause(error);
|
|
153
|
+
const enriched = new Error(`${error.message}${cause_text ? ` :: cause=${cause_text}` : ""} :: url=${url}`, error.cause ? { cause: error.cause } : undefined);
|
|
154
|
+
enriched.stack = error.stack;
|
|
155
|
+
return enriched;
|
|
156
|
+
}
|
|
157
|
+
function describe_error_cause(error) {
|
|
158
|
+
const parts = [];
|
|
159
|
+
let current = error.cause;
|
|
160
|
+
let depth = 0;
|
|
161
|
+
while (current && depth < 3) {
|
|
162
|
+
if (current instanceof Error) {
|
|
163
|
+
const code = current.code;
|
|
164
|
+
const code_text = typeof code === "string" && code ? `[${code}] ` : "";
|
|
165
|
+
parts.push(`${code_text}${current.message || current.name}`.trim());
|
|
166
|
+
current = current.cause;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
parts.push(String(current));
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
depth += 1;
|
|
173
|
+
}
|
|
174
|
+
return parts.filter(Boolean).join(" -> ");
|
|
175
|
+
}
|
|
116
176
|
async function read_local_resource(projectRoot, raw_url) {
|
|
117
177
|
const raw = String(raw_url || "").trim();
|
|
118
178
|
const file_path = raw.startsWith("file://")
|
|
@@ -137,17 +197,48 @@ async function materialize_file_part(params) {
|
|
|
137
197
|
if (raw_url.startsWith("resources://"))
|
|
138
198
|
return params.part;
|
|
139
199
|
const parsed_data_url = parse_data_url(raw_url);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
200
|
+
if (parsed_data_url) {
|
|
201
|
+
const media_type = String(params.part.mediaType || parsed_data_url.media_type || "").trim() ||
|
|
202
|
+
"application/octet-stream";
|
|
203
|
+
const file_path = await write_resource_file({
|
|
204
|
+
projectRoot: params.projectRoot,
|
|
205
|
+
mediaType: media_type,
|
|
206
|
+
filename: typeof params.part.filename === "string" ? params.part.filename : undefined,
|
|
146
207
|
bytes: parsed_data_url.bytes,
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
...params.part,
|
|
211
|
+
mediaType: media_type,
|
|
212
|
+
url: to_resources_url(params.projectRoot, file_path),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (raw_url.startsWith("http://") || raw_url.startsWith("https://")) {
|
|
216
|
+
try {
|
|
217
|
+
const source = await read_remote_resource(raw_url);
|
|
218
|
+
const media_type = String(params.part.mediaType || source.mediaType || "").trim() ||
|
|
219
|
+
"application/octet-stream";
|
|
220
|
+
const file_path = await write_resource_file({
|
|
221
|
+
projectRoot: params.projectRoot,
|
|
222
|
+
mediaType: media_type,
|
|
223
|
+
filename: typeof params.part.filename === "string"
|
|
224
|
+
? params.part.filename
|
|
225
|
+
: source.filename,
|
|
226
|
+
bytes: source.bytes,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
...params.part,
|
|
230
|
+
mediaType: media_type,
|
|
231
|
+
url: to_resources_url(params.projectRoot, file_path),
|
|
232
|
+
};
|
|
147
233
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
234
|
+
catch (error) {
|
|
235
|
+
// 关键点(中文):远程下载失败时保留原始 URL,不让整张图导致 action 失败。
|
|
236
|
+
// 这样图片至少以链接形式存在,用户可手动点击或后续再试。
|
|
237
|
+
console.warn(`[AssistantFileResource] remote download failed, keeping original url :: ${describe_error_cause(error instanceof Error ? error : new Error(String(error)))} :: url=${raw_url}`);
|
|
238
|
+
return params.part;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const source = await read_local_resource(params.projectRoot, raw_url);
|
|
151
242
|
const media_type = String(params.part.mediaType || source.mediaType || "").trim() ||
|
|
152
243
|
"application/octet-stream";
|
|
153
244
|
const file_path = await write_resource_file({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantFileResource.js","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,UAAU,CAAC;AAE1B,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAgChE,SAAS,oBAAoB,CAAC,WAA+B;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,WAAW,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,UAAU,GACd,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,0BAA0B,CAAC;IAChF,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;IAE/E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,SAAS;YACrB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC7B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAClD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO;YACL,UAAU;YACV,KAAK;SACN,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,SAAiB;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,IAAI,KAAK,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IACnE,IAAI,KAAK,KAAK,YAAY;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,KAAK,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,KAAK,KAAK,iBAAiB;QAAE,OAAO,MAAM,CAAC;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,uBAAuB,CAAC,QAA4B;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,SAAS,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,WAAmB,EAAE,QAAgB;IAC7D,MAAM,QAAQ,GAAG,IAAI;SAClB,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;SAC/B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,eAAe,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,MAKlC;IACC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5E,MAAM,GAAG,GACP,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxC,yBAAyB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC;IAClC,MAAM,aAAa,GAAG,2BAA2B,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAEtD,MAAM,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;QACnD,IAAI,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACrC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,OAAe;IAKjD,MAAM,
|
|
1
|
+
{"version":3,"file":"AssistantFileResource.js","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@downcity/agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.160",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Downcity Agent 运行时 — 单 Agent 执行壳与本机 RPC 能力",
|
|
6
6
|
"main": "./bin/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@ai-sdk/openai-compatible": "^2.0.48",
|
|
20
|
-
"@downcity/shell": "^0.1.
|
|
21
|
-
"@downcity/type": "0.1.
|
|
20
|
+
"@downcity/shell": "^0.1.36",
|
|
21
|
+
"@downcity/type": "0.1.70",
|
|
22
22
|
"@larksuiteoapi/node-sdk": "^1.66.0",
|
|
23
23
|
"ai": "^6.0.193",
|
|
24
24
|
"commander": "^15.0.0",
|
|
@@ -354,3 +354,76 @@ test("ImagePlugin image_create rejects legacy messages and data URLs", async ()
|
|
|
354
354
|
assert.equal(data_url_result.success, false);
|
|
355
355
|
assert.match(data_url_result.error, /does not accept data URLs/);
|
|
356
356
|
});
|
|
357
|
+
|
|
358
|
+
test("ImagePlugin image_result polls until terminal when until_done=true", async () => {
|
|
359
|
+
const calls = [];
|
|
360
|
+
let next_status = "running";
|
|
361
|
+
const message = create_image_message();
|
|
362
|
+
const plugin = new ImagePlugin({
|
|
363
|
+
image_create: () => ({ job_id: "img_wait", status: "queued", poll_after_ms: 1 }),
|
|
364
|
+
image_result: (input) => {
|
|
365
|
+
calls.push(input.job_id);
|
|
366
|
+
if (calls.length >= 3) {
|
|
367
|
+
next_status = "succeeded";
|
|
368
|
+
}
|
|
369
|
+
if (next_status === "succeeded") {
|
|
370
|
+
return {
|
|
371
|
+
job_id: input.job_id,
|
|
372
|
+
status: "succeeded",
|
|
373
|
+
result: message,
|
|
374
|
+
poll_after_ms: 1,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
job_id: input.job_id,
|
|
379
|
+
status: "running",
|
|
380
|
+
message: "still going",
|
|
381
|
+
poll_after_ms: 1,
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const result = await plugin.actions.image_result.execute({
|
|
387
|
+
context: create_context(),
|
|
388
|
+
payload: {
|
|
389
|
+
job_id: "img_wait",
|
|
390
|
+
until_done: true,
|
|
391
|
+
max_wait_ms: 500,
|
|
392
|
+
poll_interval_ms: 5,
|
|
393
|
+
},
|
|
394
|
+
pluginName: "image",
|
|
395
|
+
actionName: "image_result",
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
assert.equal(result.success, true);
|
|
399
|
+
assert.equal(result.data.role, "assistant");
|
|
400
|
+
assert.ok(calls.length >= 3);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("ImagePlugin image_result returns last status when max_wait_ms elapses", async () => {
|
|
404
|
+
const plugin = new ImagePlugin({
|
|
405
|
+
image_create: () => ({ job_id: "img_timeout", status: "queued", poll_after_ms: 1 }),
|
|
406
|
+
image_result: (input) => ({
|
|
407
|
+
job_id: input.job_id,
|
|
408
|
+
status: "running",
|
|
409
|
+
message: "always pending",
|
|
410
|
+
poll_after_ms: 1,
|
|
411
|
+
}),
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const result = await plugin.actions.image_result.execute({
|
|
415
|
+
context: create_context(),
|
|
416
|
+
payload: {
|
|
417
|
+
job_id: "img_timeout",
|
|
418
|
+
until_done: true,
|
|
419
|
+
max_wait_ms: 30,
|
|
420
|
+
poll_interval_ms: 5,
|
|
421
|
+
},
|
|
422
|
+
pluginName: "image",
|
|
423
|
+
actionName: "image_result",
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
assert.equal(result.success, true);
|
|
427
|
+
assert.equal(result.data.status, "running");
|
|
428
|
+
assert.equal(result.data.message, "always pending");
|
|
429
|
+
});
|
|
@@ -140,22 +140,92 @@ async function read_remote_resource(raw_url: string): Promise<{
|
|
|
140
140
|
filename?: string;
|
|
141
141
|
bytes: Buffer;
|
|
142
142
|
}> {
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
145
|
-
throw new Error(
|
|
143
|
+
const result = await fetch_with_retry(raw_url);
|
|
144
|
+
if (!result.ok) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Failed to download assistant file resource: ${result.status} :: url=${raw_url}`,
|
|
147
|
+
);
|
|
146
148
|
}
|
|
147
|
-
const array_buffer = await
|
|
149
|
+
const array_buffer = await result.arrayBuffer();
|
|
148
150
|
const bytes = Buffer.from(array_buffer);
|
|
149
151
|
if (bytes.length === 0) {
|
|
150
|
-
throw new Error(
|
|
152
|
+
throw new Error(`Downloaded assistant file resource is empty :: url=${raw_url}`);
|
|
151
153
|
}
|
|
152
154
|
return {
|
|
153
|
-
mediaType:
|
|
155
|
+
mediaType: result.headers.get("content-type")?.split(";")[0]?.trim(),
|
|
154
156
|
filename: filename_from_url(raw_url),
|
|
155
157
|
bytes,
|
|
156
158
|
};
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
/**
|
|
162
|
+
* 带重试的 fetch 远程资源。
|
|
163
|
+
*
|
|
164
|
+
* 关键点(中文)
|
|
165
|
+
* - 对 transient 网络错误(fetch failed / UND_ERR_* / ECONNRESET 等)做 2 次指数退避重试。
|
|
166
|
+
* - 最终失败时把 error.cause 链展开到 message,方便定位是代理问题、DNS 问题还是上游问题。
|
|
167
|
+
*/
|
|
168
|
+
async function fetch_with_retry(
|
|
169
|
+
raw_url: string,
|
|
170
|
+
): Promise<Response> {
|
|
171
|
+
const delays_ms = [250, 1_000];
|
|
172
|
+
const attempts = delays_ms.length + 1;
|
|
173
|
+
let last_error: unknown;
|
|
174
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
175
|
+
try {
|
|
176
|
+
return await fetch(raw_url);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
last_error = error;
|
|
179
|
+
if (!is_transient_fetch_error(error) || attempt === attempts - 1) {
|
|
180
|
+
throw enrich_fetch_error(error, raw_url);
|
|
181
|
+
}
|
|
182
|
+
const delay_ms = delays_ms[attempt] ?? 1_000;
|
|
183
|
+
await new Promise((resolve) => setTimeout(resolve, delay_ms));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
throw enrich_fetch_error(last_error, raw_url);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function is_transient_fetch_error(error: unknown): boolean {
|
|
190
|
+
if (!(error instanceof Error)) return false;
|
|
191
|
+
const text = `${error.message} ${describe_error_cause(error)}`;
|
|
192
|
+
return /fetch failed|UND_ERR|ECONN(RESET|REFUSED|ABORTED)|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up/i.test(
|
|
193
|
+
text,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function enrich_fetch_error(error: unknown, url: string): Error {
|
|
198
|
+
if (!(error instanceof Error)) {
|
|
199
|
+
return new Error(`Download failed: ${String(error)} :: url=${url}`);
|
|
200
|
+
}
|
|
201
|
+
const cause_text = describe_error_cause(error);
|
|
202
|
+
const enriched = new Error(
|
|
203
|
+
`${error.message}${cause_text ? ` :: cause=${cause_text}` : ""} :: url=${url}`,
|
|
204
|
+
error.cause ? { cause: error.cause } : undefined,
|
|
205
|
+
);
|
|
206
|
+
enriched.stack = error.stack;
|
|
207
|
+
return enriched;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function describe_error_cause(error: Error): string {
|
|
211
|
+
const parts: string[] = [];
|
|
212
|
+
let current: unknown = (error as { cause?: unknown }).cause;
|
|
213
|
+
let depth = 0;
|
|
214
|
+
while (current && depth < 3) {
|
|
215
|
+
if (current instanceof Error) {
|
|
216
|
+
const code = (current as { code?: unknown }).code;
|
|
217
|
+
const code_text = typeof code === "string" && code ? `[${code}] ` : "";
|
|
218
|
+
parts.push(`${code_text}${current.message || current.name}`.trim());
|
|
219
|
+
current = (current as { cause?: unknown }).cause;
|
|
220
|
+
} else {
|
|
221
|
+
parts.push(String(current));
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
depth += 1;
|
|
225
|
+
}
|
|
226
|
+
return parts.filter(Boolean).join(" -> ");
|
|
227
|
+
}
|
|
228
|
+
|
|
159
229
|
async function read_local_resource(
|
|
160
230
|
projectRoot: string,
|
|
161
231
|
raw_url: string,
|
|
@@ -191,19 +261,55 @@ async function materialize_file_part(params: {
|
|
|
191
261
|
if (raw_url.startsWith("resources://")) return params.part;
|
|
192
262
|
|
|
193
263
|
const parsed_data_url = parse_data_url(raw_url);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
264
|
+
if (parsed_data_url) {
|
|
265
|
+
const media_type =
|
|
266
|
+
String(params.part.mediaType || parsed_data_url.media_type || "").trim() ||
|
|
267
|
+
"application/octet-stream";
|
|
268
|
+
const file_path = await write_resource_file({
|
|
269
|
+
projectRoot: params.projectRoot,
|
|
270
|
+
mediaType: media_type,
|
|
271
|
+
filename:
|
|
272
|
+
typeof params.part.filename === "string" ? params.part.filename : undefined,
|
|
273
|
+
bytes: parsed_data_url.bytes,
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
...params.part,
|
|
277
|
+
mediaType: media_type,
|
|
278
|
+
url: to_resources_url(params.projectRoot, file_path),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (raw_url.startsWith("http://") || raw_url.startsWith("https://")) {
|
|
283
|
+
try {
|
|
284
|
+
const source = await read_remote_resource(raw_url);
|
|
285
|
+
const media_type =
|
|
286
|
+
String(params.part.mediaType || source.mediaType || "").trim() ||
|
|
287
|
+
"application/octet-stream";
|
|
288
|
+
const file_path = await write_resource_file({
|
|
289
|
+
projectRoot: params.projectRoot,
|
|
290
|
+
mediaType: media_type,
|
|
197
291
|
filename:
|
|
198
292
|
typeof params.part.filename === "string"
|
|
199
293
|
? params.part.filename
|
|
200
|
-
:
|
|
201
|
-
bytes:
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
294
|
+
: source.filename,
|
|
295
|
+
bytes: source.bytes,
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
...params.part,
|
|
299
|
+
mediaType: media_type,
|
|
300
|
+
url: to_resources_url(params.projectRoot, file_path),
|
|
301
|
+
};
|
|
302
|
+
} catch (error) {
|
|
303
|
+
// 关键点(中文):远程下载失败时保留原始 URL,不让整张图导致 action 失败。
|
|
304
|
+
// 这样图片至少以链接形式存在,用户可手动点击或后续再试。
|
|
305
|
+
console.warn(
|
|
306
|
+
`[AssistantFileResource] remote download failed, keeping original url :: ${describe_error_cause(error instanceof Error ? error : new Error(String(error)))} :: url=${raw_url}`,
|
|
307
|
+
);
|
|
308
|
+
return params.part;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
206
311
|
|
|
312
|
+
const source = await read_local_resource(params.projectRoot, raw_url);
|
|
207
313
|
const media_type =
|
|
208
314
|
String(params.part.mediaType || source.mediaType || "").trim() ||
|
|
209
315
|
"application/octet-stream";
|
|
@@ -216,7 +322,6 @@ async function materialize_file_part(params: {
|
|
|
216
322
|
: source.filename,
|
|
217
323
|
bytes: source.bytes,
|
|
218
324
|
});
|
|
219
|
-
|
|
220
325
|
return {
|
|
221
326
|
...params.part,
|
|
222
327
|
mediaType: media_type,
|