@qwen-code/arena-agent 0.0.9-dev.26 → 0.0.9-dev.28
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 +34 -33
- package/dist/audit.d.ts +14 -0
- package/dist/audit.js +31 -0
- package/dist/audit.js.map +1 -0
- package/dist/dashscope.d.ts +5 -0
- package/dist/dashscope.js +55 -0
- package/dist/dashscope.js.map +1 -0
- package/dist/fetch-page/aparat.d.ts +30 -0
- package/dist/fetch-page/aparat.js +209 -0
- package/dist/fetch-page/aparat.js.map +1 -0
- package/dist/fetch-page/json-path.d.ts +12 -0
- package/dist/fetch-page/json-path.js +111 -0
- package/dist/fetch-page/json-path.js.map +1 -0
- package/dist/fetch-page/sites.d.ts +26 -0
- package/dist/fetch-page/sites.js +39 -0
- package/dist/fetch-page/sites.js.map +1 -0
- package/dist/fetch-page/text.d.ts +5 -0
- package/dist/fetch-page/text.js +38 -0
- package/dist/fetch-page/text.js.map +1 -0
- package/dist/fetch-page/types.d.ts +17 -0
- package/dist/fetch-page/types.js +3 -0
- package/dist/fetch-page/types.js.map +1 -0
- package/dist/fetch-page/youtube.d.ts +35 -0
- package/dist/fetch-page/youtube.js +282 -0
- package/dist/fetch-page/youtube.js.map +1 -0
- package/dist/provider.js +72 -1
- package/dist/provider.js.map +1 -1
- package/dist/web-search.d.ts +5 -0
- package/dist/web-search.js +465 -0
- package/dist/web-search.js.map +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# Arena CodeAgent Harness
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
harness。运行时只依赖 Node.js,不再依赖 Python:
|
|
3
|
+
这是一个基于 `message.json` 与 `tools.json` 所定义消息和工具契约的 TypeScript
|
|
4
|
+
CodeAgent harness。运行时只依赖 Node.js,不再依赖 Python:
|
|
5
5
|
|
|
6
6
|
- `arena -p ...`:headless 单轮入口,支持文本、完整 JSON 和 NDJSON 流。
|
|
7
7
|
- `arena tui`:终端多轮对话,实时输出并自动保存、恢复历史。
|
|
8
8
|
- `arena serve`:HTTP daemon、Web UI 与双通道 SSE 实时接口。
|
|
9
9
|
- Agent loop:流式 assistant → tool call → tool result → assistant。
|
|
10
|
-
- 11
|
|
10
|
+
- 11 个工具按照 `tools.json` 中的 Draft-07 schema 提供参数校验。
|
|
11
|
+
|
|
12
|
+
本文提及的第三方产品、模型与服务名称仅用于说明兼容性和配置方式,不表示相关
|
|
13
|
+
权利人对本项目的认可或背书;相关商标归各自权利人所有。使用者应自行确认并遵守
|
|
14
|
+
所接入模型、数据源和在线服务的许可、服务条款及适用法律。
|
|
11
15
|
|
|
12
16
|
## 源码与 SDK 安装
|
|
13
17
|
|
|
@@ -99,9 +103,9 @@ arena serve --system-prompt '只使用中文回复。'
|
|
|
99
103
|
续跑已有 session 时,当前传入的值会替换历史中的 system message。未传参数时,
|
|
100
104
|
日期、workspace 和 mode 对应的现有 prompt 生成逻辑保持不变。
|
|
101
105
|
|
|
102
|
-
`arena serve` 是一个例外:未显式传入 `--system-prompt`
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
`arena serve` 是一个例外:未显式传入 `--system-prompt` 时,它会保留现有系统
|
|
107
|
+
提示词中的日期、workspace、工具和 mode 指令,但默认将身份声明设为 `Qwen`。
|
|
108
|
+
显式传入 `--system-prompt` 时仍会完整替换系统提示词。
|
|
105
109
|
|
|
106
110
|
Arena 模式下,如需从 system prompt 的身份模型列表中移除 `Qwen`,可增加
|
|
107
111
|
`--exclude-qwen-from-system-prompt`,或设置
|
|
@@ -113,7 +117,7 @@ arena tui --exclude-qwen-from-system-prompt
|
|
|
113
117
|
arena serve --exclude-qwen-from-system-prompt
|
|
114
118
|
```
|
|
115
119
|
|
|
116
|
-
三个 Code Arena
|
|
120
|
+
三个 Code Arena 模式的兼容性测试集、工具回归测试和模型行为评估入口见
|
|
117
121
|
[`tasks/code-harness/README.md`](tasks/code-harness/README.md);这些开发数据不会
|
|
118
122
|
进入 npm 或 SEA 发布包。
|
|
119
123
|
|
|
@@ -177,11 +181,11 @@ Anthropic 直连在原生协议上完整支持 thinking。旧版 Claude 和非 C
|
|
|
177
181
|
budget。Anthropic 要求启用 thinking 时不调整 temperature,此时不发送该
|
|
178
182
|
参数。带签名的 thinking/redacted_thinking block 会原样保存在对应 assistant
|
|
179
183
|
消息的 `reasoning_details` 字段——
|
|
180
|
-
Anthropic 要求 tool-use 续跑时回传签名 thinking block
|
|
184
|
+
Anthropic 要求 tool-use 续跑时回传签名 thinking block,恢复历史上下文时会按
|
|
181
185
|
原顺序还原;无签名的 thinking block(部分网关返回 `signature: ""`)
|
|
182
|
-
|
|
186
|
+
不会再次发送,因为协议不需要它们,而且某些网关收到会挂起。
|
|
183
187
|
`reasoning_content` 仍是纯文本推理,历史契约不变。向
|
|
184
|
-
OpenAI-compatible
|
|
188
|
+
OpenAI-compatible 服务发送同一份历史时,`reasoning_details` 与 `usage`
|
|
185
189
|
一样在 wire 边界剥离。usage 会映射为 OpenAI 风格字段(`prompt_tokens`
|
|
186
190
|
含 cache 创建/读取部分),同时保留 `input_tokens`、
|
|
187
191
|
`cache_read_input_tokens` 等原生字段。`OPENAI_BASE_URL` 对 anthropic
|
|
@@ -234,7 +238,7 @@ Jina Reader。
|
|
|
234
238
|
Harness 会在每次模型调用前估算消息、reasoning、工具 schema 和图片占用;达到
|
|
235
239
|
`ARENA_CONTEXT_WINDOW × ARENA_CONTEXT_COMPACTION_RATIO`(默认 80%)时,调用
|
|
236
240
|
当前模型把已完成的旧回合生成结构化续作摘要。只有发给模型的内存上下文会把
|
|
237
|
-
|
|
241
|
+
旧前缀替换为符合既有会话格式的 leading `<arena-system-message>`;持久化的
|
|
238
242
|
`history.json.messages` 保留原始消息,不会因压缩删除或替换前缀。当前用户回合及
|
|
239
243
|
其后的 tool-call / result 链在模型上下文中保持原样。摘要重点保留目标、硬约束、
|
|
240
244
|
发现、已完成/未完成事项、下一步和相关文件路径;摘要调用默认关闭 thinking,
|
|
@@ -242,14 +246,14 @@ Harness 会在每次模型调用前估算消息、reasoning、工具 schema 和
|
|
|
242
246
|
`ARENA_COMPACTION_SUMMARY_MAX_TOKENS` 控制(默认 8192)。
|
|
243
247
|
|
|
244
248
|
`ARENA_CONTEXT_WINDOW` 应设置为所用模型的真实上下文窗口;默认 `1000000` 是
|
|
245
|
-
|
|
249
|
+
当前默认模型的暂定值。token 数目前是本地保守估算,并非 provider tokenizer
|
|
246
250
|
的精确计数。如果摘要请求失败,Harness 会保留原历史并继续正常模型请求,避免
|
|
247
251
|
摘要服务的瞬时故障直接中断任务。
|
|
248
252
|
|
|
249
253
|
每次模型请求都会通过 OpenAI-compatible 的 `stream_options.include_usage`
|
|
250
254
|
请求 token 用量,并把服务端在最终 chunk 上报的 `usage` 对象原样记录到该轮
|
|
251
255
|
assistant 消息的 `usage` 字段(`history.json` 与事件流中均可见),供训练
|
|
252
|
-
|
|
256
|
+
和成本统计使用。再次发送历史上下文时,`usage` 会在 wire 边界剥离,不会发回
|
|
253
257
|
服务端。`model.complete` 事件同样携带该轮 `usage`;上下文压缩的摘要调用
|
|
254
258
|
不会在历史中留下消息,其用量通过 `compaction` 事件上报,只在事件流中可
|
|
255
259
|
见。不支持 `stream_options` 的服务可通过 `ARENA_EXTRA_BODY` 覆盖该参数。
|
|
@@ -377,17 +381,17 @@ arena -p 'redraw this photo with the reference face' \
|
|
|
377
381
|
每个附件会被保存到工作区 `uploads/`(模型内可通过
|
|
378
382
|
`/home/user/uploads/<name>` 访问;重名自动加 `-2`、`-3` 后缀,URL 无扩展名
|
|
379
383
|
时按图片字节特征补全扩展名)。本轮 user message 与 daemon 附件上传共用同
|
|
380
|
-
|
|
384
|
+
一构造逻辑,并使用统一的会话部件格式:图片部件
|
|
381
385
|
`{"image": ...}` 在前,随后是 prompt 文本 `{"text": ...}`,最后追加一条
|
|
382
386
|
`<arena-system-message>`,列出保存到 `/home/user/uploads/` 的文件名。单条
|
|
383
387
|
消息最多 10 个附件,单个附件上限 20 MB、总量上限 50 MB,与 daemon 一致。
|
|
384
388
|
|
|
385
389
|
多模态 user 消息(附件、图片 `read_file` 回灌、中断通知)在历史与事件流中
|
|
386
|
-
一律使用
|
|
387
|
-
|
|
390
|
+
一律使用 `{"image": ...}` / `{"text": ...}` 原生部件并保持字段格式一致;仅在
|
|
391
|
+
向模型发起 chat-completions 请求的边界处转换为
|
|
388
392
|
OpenAI 风格的 `image_url` 部件。`--attach` 传 URL 时 `image` 直接记录该
|
|
389
|
-
URL
|
|
390
|
-
|
|
393
|
+
URL(模型服务端会自行下载;恢复历史上下文时要求该 URL 仍可访问);本地文件
|
|
394
|
+
没有可引用的 URL,`image` 存自包含的 base64
|
|
391
395
|
data-URI。
|
|
392
396
|
|
|
393
397
|
`stream-json` 每行一个 JSON 对象。`start`、`message`、`result` 事件中的
|
|
@@ -401,7 +405,7 @@ data-URI。
|
|
|
401
405
|
|
|
402
406
|
`assistant.delta` 实时提供 content、reasoning 与 tool-call 参数碎片;
|
|
403
407
|
`model.start`、`model.complete`、`model.retry`、`tool.start`、`tool.complete`
|
|
404
|
-
标记阶段。最后的 `result`
|
|
408
|
+
标记阶段。最后的 `result` 带完整消息。
|
|
405
409
|
|
|
406
410
|
`ask_user` 只有在注入 handler 时才会提供给模型。TUI 和 WebUI 使用真人交互
|
|
407
411
|
handler;headless(`-p`)默认仍不开放该工具,但可配置远程 User Agent handler:
|
|
@@ -692,7 +696,7 @@ curl -N -X POST \
|
|
|
692
696
|
|
|
693
697
|
## 工具
|
|
694
698
|
|
|
695
|
-
|
|
699
|
+
消息与工具契约配置保存在:
|
|
696
700
|
|
|
697
701
|
- `spec/message.json`
|
|
698
702
|
- `spec/tools.json`
|
|
@@ -706,35 +710,32 @@ Draft-07 schema。repo mode 的动态提示词模板保存在 `src/prompts.ts`
|
|
|
706
710
|
`generate_speech`。
|
|
707
711
|
|
|
708
712
|
`web_search` 使用 Routify 网关提供的 Brave LLM Context,从工作区 `.env`
|
|
709
|
-
读取 `ROUTIFY_API_KEY
|
|
710
|
-
`
|
|
711
|
-
并使用 `brave.llm-context` 路由。实现按 depth 请求最多 3/5/8 条结果,并将
|
|
713
|
+
读取 `ROUTIFY_API_KEY`,默认通过内置的 ModelRouter 兼容接口调用
|
|
714
|
+
`brave.llm-context` 路由。实现按 depth 请求最多 3/5/8 条结果,并将
|
|
712
715
|
`grounding.generic` 的内容片段及 `sources` 的来源信息转换成 Arena 的既有工具
|
|
713
716
|
结果。请求固定考虑最多 20 个搜索候选,再通过 `maximum_number_of_urls` 将
|
|
714
717
|
最终来源数限制为 3/5/8。depth 还分别对应 1024/3072/8192 的总 token 预算和
|
|
715
718
|
512/552/2048 的单 URL token 预算;description 使用预算内的全部 grounding
|
|
716
719
|
snippets,仅在没有 grounding 内容时回退到来源摘要。三个 Code Arena 模式
|
|
717
|
-
|
|
720
|
+
使用独立的模式参数:来源数 3/5/10、总 token 预算 1280/4096/6144、单 URL
|
|
718
721
|
token 预算 512/1280/1536。
|
|
719
722
|
可用 `ROUTIFY_WEB_SEARCH_URL` 和 `ARENA_WEB_SEARCH_MODEL` 覆盖默认值。
|
|
720
723
|
|
|
721
724
|
`image_search` 使用 Routify 网关提供的 Brave Images Search,同样读取
|
|
722
|
-
`ROUTIFY_API_KEY`,通过 `X-Subscription-Token` 发送 GET
|
|
723
|
-
`
|
|
724
|
-
|
|
725
|
+
`ROUTIFY_API_KEY`,通过 `X-Subscription-Token` 发送 GET 请求。默认使用内置的
|
|
726
|
+
ModelRouter 兼容接口,可用 `ROUTIFY_IMAGE_SEARCH_URL` 覆盖。实现优先下载 Brave
|
|
727
|
+
返回的原图地址,原图不可用
|
|
725
728
|
时回退到 Brave 代理缩略图,并保持既有工具结果格式和工作区文件命名不变。
|
|
726
729
|
|
|
727
730
|
Arena 的 `fetch_page` 和 Code Arena 的 `web_fetch` 默认仍使用本地抓取与正文
|
|
728
|
-
解析。设置 `CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED=true` 后,两者改走 Routify
|
|
729
|
-
|
|
730
|
-
非 200 或响应不可用时回退到 `readpage-jina-inner`;两者都失败则返回普通工具
|
|
731
|
+
解析。设置 `CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED=true` 后,两者改走 Routify
|
|
732
|
+
页面读取接口,并在首选读取服务不可用时使用备用服务;两者都失败则返回普通工具
|
|
731
733
|
错误。`fetch_page` 仍按 8000 字符分块并缓存同一 URL 的后续 chunk。认证复用
|
|
732
734
|
`ROUTIFY_API_KEY`。
|
|
733
735
|
|
|
734
736
|
`generate_image`、`generate_speech` 使用 ModelRouter/Routify,并从工作区
|
|
735
|
-
`.env` 读取 `ROUTIFY_API_KEY
|
|
736
|
-
|
|
737
|
-
`ROUTIFY_VERTEX_BASE_URL` 覆盖 `/protocol/vertex/v1beta` 根地址。
|
|
737
|
+
`.env` 读取 `ROUTIFY_API_KEY`。两个工具均使用 Google 兼容协议调用;可用
|
|
738
|
+
`ROUTIFY_VERTEX_BASE_URL` 覆盖默认根地址。
|
|
738
739
|
|
|
739
740
|
`fetch_page` 使用 Defuddle 将 HTML 正文转换为 Markdown,并使用
|
|
740
741
|
`pdf-parse` 提取 PDF 的前 30 页。响应按 Markdown 边界稳定分块,同一轮内
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-secret description of an endpoint: the origin is recorded as-is, while
|
|
3
|
+
* the path is reduced to a hash so that same-origin routes stay distinguishable
|
|
4
|
+
* without writing userinfo, tokens, query strings or fragments to disk.
|
|
5
|
+
*/
|
|
6
|
+
export declare function describeUrlForAudit(value: string): {
|
|
7
|
+
origin: string;
|
|
8
|
+
fingerprint: string | null;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Hash of an already normalized, credential-free descriptor. Use it when two
|
|
12
|
+
* runs must be distinguishable without recording the value itself.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fingerprintForAudit(value: string): string;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Non-secret description of an endpoint: the origin is recorded as-is, while
|
|
4
|
+
* the path is reduced to a hash so that same-origin routes stay distinguishable
|
|
5
|
+
* without writing userinfo, tokens, query strings or fragments to disk.
|
|
6
|
+
*/
|
|
7
|
+
export function describeUrlForAudit(value) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(value);
|
|
10
|
+
if (parsed.origin === "null") {
|
|
11
|
+
return { origin: "invalid-url", fingerprint: null };
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
origin: parsed.origin,
|
|
15
|
+
fingerprint: createHash("sha256")
|
|
16
|
+
.update(parsed.origin + parsed.pathname)
|
|
17
|
+
.digest("hex"),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { origin: "invalid-url", fingerprint: null };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hash of an already normalized, credential-free descriptor. Use it when two
|
|
26
|
+
* runs must be distinguishable without recording the value itself.
|
|
27
|
+
*/
|
|
28
|
+
export function fingerprintForAudit(value) {
|
|
29
|
+
return createHash("sha256").update(value).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAI/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC7B,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACtD,CAAC;QACD,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,WAAW,EAAE,UAAU,CAAC,QAAQ,CAAC;iBAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;iBACvC,MAAM,CAAC,KAAK,CAAC;SACjB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function record(value: unknown): Record<string, unknown>;
|
|
2
|
+
export declare function stringValue(value: unknown): string;
|
|
3
|
+
export declare function dashScopeRoot(): string;
|
|
4
|
+
export declare function dashScopeFetch(protocol: "api" | "compatible", pathname: string, init: RequestInit, label: string): Promise<Response>;
|
|
5
|
+
export declare function dashScopeJson(protocol: "api" | "compatible", pathname: string, init: RequestInit, label: string): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function record(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: {};
|
|
5
|
+
}
|
|
6
|
+
export function stringValue(value) {
|
|
7
|
+
return typeof value === "string" ? value : "";
|
|
8
|
+
}
|
|
9
|
+
function dashScopeApiKey() {
|
|
10
|
+
const apiKey = process.env.DASHSCOPE_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error("DashScope tools require DASHSCOPE_API_KEY");
|
|
13
|
+
}
|
|
14
|
+
return apiKey;
|
|
15
|
+
}
|
|
16
|
+
export function dashScopeRoot() {
|
|
17
|
+
return (process.env.DASHSCOPE_API_BASE ||
|
|
18
|
+
process.env.DASHSCOPE_BASE_URL ||
|
|
19
|
+
"https://dashscope.aliyuncs.com")
|
|
20
|
+
.replace(/\/+$/, "")
|
|
21
|
+
.replace(/\/(?:api\/v1|compatible-mode\/v1)$/i, "");
|
|
22
|
+
}
|
|
23
|
+
function dashScopeUrl(protocol, pathname) {
|
|
24
|
+
const prefix = protocol === "api" ? "/api/v1" : "/compatible-mode/v1";
|
|
25
|
+
const normalized = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
26
|
+
return `${dashScopeRoot()}${prefix}${normalized}`;
|
|
27
|
+
}
|
|
28
|
+
export async function dashScopeFetch(protocol, pathname, init, label) {
|
|
29
|
+
const headers = new Headers(init.headers);
|
|
30
|
+
headers.set("authorization", `Bearer ${dashScopeApiKey()}`);
|
|
31
|
+
const response = await fetch(dashScopeUrl(protocol, pathname), {
|
|
32
|
+
...init,
|
|
33
|
+
headers,
|
|
34
|
+
});
|
|
35
|
+
if (response.ok)
|
|
36
|
+
return response;
|
|
37
|
+
const body = await response.text();
|
|
38
|
+
let detail = body;
|
|
39
|
+
try {
|
|
40
|
+
const payload = record(JSON.parse(body));
|
|
41
|
+
detail =
|
|
42
|
+
stringValue(payload.message) ||
|
|
43
|
+
stringValue(record(payload.error).message) ||
|
|
44
|
+
body;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Keep the response text as the diagnostic.
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`${label} failed (${response.status} ${response.statusText}): ${detail.slice(0, 2000)}`);
|
|
50
|
+
}
|
|
51
|
+
export async function dashScopeJson(protocol, pathname, init, label) {
|
|
52
|
+
const response = await dashScopeFetch(protocol, pathname, init, label);
|
|
53
|
+
return record(await response.json());
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=dashscope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashscope.js","sourceRoot":"","sources":["../src/dashscope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ParsedPage, SiteAdapterContext } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Aparat adapter.
|
|
4
|
+
*
|
|
5
|
+
* Only the profile page types observed in the source history are covered, and
|
|
6
|
+
* every value comes from Aparat's current public API rather than a replayed
|
|
7
|
+
* snapshot — live figures are expected to differ from the history. Per the
|
|
8
|
+
* golden spec this is an [H] local choice.
|
|
9
|
+
*/
|
|
10
|
+
export interface AparatTarget {
|
|
11
|
+
kind: "aparat";
|
|
12
|
+
username: string;
|
|
13
|
+
tab: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function matchAparat(url: URL, hostname: string): AparatTarget | undefined;
|
|
16
|
+
export declare function aparatShellReason(html: string): string | undefined;
|
|
17
|
+
interface AparatProfile {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
avatar: string;
|
|
21
|
+
followers: string;
|
|
22
|
+
videoCount: string;
|
|
23
|
+
totalVisit: string;
|
|
24
|
+
description: string;
|
|
25
|
+
startDate: string;
|
|
26
|
+
monthVisit: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function aparatPage(target: AparatTarget, profile: AparatProfile, finalUrl: string, videos?: Record<string, unknown>[], shorts?: Record<string, unknown>[]): ParsedPage | undefined;
|
|
29
|
+
export declare function loadAparat(target: AparatTarget, ctx: SiteAdapterContext): Promise<ParsedPage | undefined>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { jsonObject, recordArray, recordValue, stringField } from "./json-path.js";
|
|
2
|
+
import { normalizeMarkdown, requireReadableContent } from "./text.js";
|
|
3
|
+
export function matchAparat(url, hostname) {
|
|
4
|
+
if (hostname !== "aparat.com")
|
|
5
|
+
return undefined;
|
|
6
|
+
const match = url.pathname.match(/^\/([^/]+?)(?:\/(about|short-videos|videos))?\/?$/);
|
|
7
|
+
const username = match?.[1] || "";
|
|
8
|
+
if (!username || ["etc", "video", "v", "login"].includes(username)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return { kind: "aparat", username, tab: match?.[2] || "home" };
|
|
12
|
+
}
|
|
13
|
+
export function aparatShellReason(html) {
|
|
14
|
+
return /enable javascript/i.test(html) ||
|
|
15
|
+
/جاوا\s*اسکریپت[^<]{0,80}فعال/i.test(html)
|
|
16
|
+
? "requires JavaScript"
|
|
17
|
+
: undefined;
|
|
18
|
+
}
|
|
19
|
+
function aparatProfile(value) {
|
|
20
|
+
const legacyProfile = recordValue(value.profile);
|
|
21
|
+
const data = recordValue(value.data);
|
|
22
|
+
const attributes = recordValue(data?.attributes);
|
|
23
|
+
const fields = attributes || legacyProfile;
|
|
24
|
+
if (!fields)
|
|
25
|
+
return undefined;
|
|
26
|
+
return {
|
|
27
|
+
id: stringField(data || fields, "id") || stringField(fields, "userid"),
|
|
28
|
+
name: stringField(fields, "name"),
|
|
29
|
+
avatar: stringField(fields, "pic_m") || stringField(fields, "pic_b"),
|
|
30
|
+
followers: stringField(fields, "follower_cnt").trim(),
|
|
31
|
+
videoCount: stringField(fields, "video_cnt").trim(),
|
|
32
|
+
totalVisit: stringField(fields, "video_visit").trim() ||
|
|
33
|
+
stringField(fields, "total_visit").trim(),
|
|
34
|
+
description: stringField(fields, "description") || stringField(fields, "descr"),
|
|
35
|
+
startDate: stringField(fields, "start_date_jalali"),
|
|
36
|
+
monthVisit: stringField(fields, "month_visit").trim(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function mergeAparatAbout(profile, value) {
|
|
40
|
+
const meta = recordValue(value?.meta);
|
|
41
|
+
const data = recordValue(meta?.data);
|
|
42
|
+
if (!data)
|
|
43
|
+
return profile;
|
|
44
|
+
return {
|
|
45
|
+
...profile,
|
|
46
|
+
description: stringField(data, "description") || profile.description,
|
|
47
|
+
videoCount: stringField(data, "video_cnt").trim() || profile.videoCount,
|
|
48
|
+
totalVisit: stringField(data, "total_visit").trim() || profile.totalVisit,
|
|
49
|
+
startDate: stringField(data, "start_date_jalali") || profile.startDate,
|
|
50
|
+
monthVisit: stringField(data, "month_visit").trim() || profile.monthVisit,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function aparatVideos(value) {
|
|
54
|
+
if (!value)
|
|
55
|
+
return [];
|
|
56
|
+
const legacy = recordArray(value.videobyuser);
|
|
57
|
+
if (legacy.length)
|
|
58
|
+
return legacy;
|
|
59
|
+
return recordArray(value.included).flatMap((item) => {
|
|
60
|
+
if (item.type !== "Video")
|
|
61
|
+
return [];
|
|
62
|
+
const attributes = recordValue(item.attributes);
|
|
63
|
+
return attributes ? [attributes] : [];
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function aparatShorts(value) {
|
|
67
|
+
const data = recordValue(value?.data);
|
|
68
|
+
return recordArray(data?.posts);
|
|
69
|
+
}
|
|
70
|
+
export function aparatPage(target, profile, finalUrl, videos = [], shorts = []) {
|
|
71
|
+
const name = profile.name || target.username;
|
|
72
|
+
const root = `https://www.aparat.com/${encodeURIComponent(target.username)}`;
|
|
73
|
+
const lines = [
|
|
74
|
+
...(profile.avatar ? [``] : []),
|
|
75
|
+
"",
|
|
76
|
+
`## ${name}`,
|
|
77
|
+
"",
|
|
78
|
+
"دنبال کردن",
|
|
79
|
+
...(profile.followers ? ["", `${profile.followers} دنبالکننده`] : []),
|
|
80
|
+
...(profile.totalVisit ? ["", `${profile.totalVisit} بازدید کل`] : []),
|
|
81
|
+
"",
|
|
82
|
+
`- [خانه](${root})`,
|
|
83
|
+
`- [ویدیوها](${root}/videos)`,
|
|
84
|
+
`- [ویدیوهای کوتاه](${root}/short-videos)`,
|
|
85
|
+
`- [لیستهای پخش](${root}/playlists)`,
|
|
86
|
+
`- [درباره کانال](${root}/about)`,
|
|
87
|
+
];
|
|
88
|
+
if (target.tab === "about") {
|
|
89
|
+
lines.push("", "درباره کانال", "", profile.description);
|
|
90
|
+
if (profile.totalVisit)
|
|
91
|
+
lines.push("", `بازدید کل${profile.totalVisit}`);
|
|
92
|
+
if (profile.startDate)
|
|
93
|
+
lines.push("", `شروع فعالیت${profile.startDate}`);
|
|
94
|
+
if (profile.monthVisit)
|
|
95
|
+
lines.push("", `بازدید این ماه${profile.monthVisit}`);
|
|
96
|
+
if (profile.videoCount)
|
|
97
|
+
lines.push("", `تعداد ویدیو${profile.videoCount}`);
|
|
98
|
+
}
|
|
99
|
+
if (target.tab === "short-videos") {
|
|
100
|
+
lines.push("", "همه ویدیوهای کوتاه", "", "- تازهترین", "- پربازدید ترین");
|
|
101
|
+
for (const short of shorts) {
|
|
102
|
+
const title = stringField(short, "description") || "ویدیوی کوتاه";
|
|
103
|
+
const id = stringField(short, "id");
|
|
104
|
+
const cover = recordValue(short.cover);
|
|
105
|
+
const stats = recordValue(short.stats);
|
|
106
|
+
const poster = stringField(cover || {}, "cdn_link");
|
|
107
|
+
const views = stringField(stats || {}, "views");
|
|
108
|
+
if (!id)
|
|
109
|
+
continue;
|
|
110
|
+
const shortUrl = `https://aparat.com/shorts/${id}`;
|
|
111
|
+
if (poster) {
|
|
112
|
+
lines.push("", `[\\\\`, `\\\\`, `${views}](${shortUrl})`);
|
|
113
|
+
}
|
|
114
|
+
lines.push("", `[${title}](${shortUrl})`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (target.tab === "videos") {
|
|
118
|
+
lines.push("", "همه ویدیوها");
|
|
119
|
+
for (const video of videos) {
|
|
120
|
+
const videoTitle = stringField(video, "title");
|
|
121
|
+
const uid = stringField(video, "uid");
|
|
122
|
+
const poster = stringField(video, "big_poster") || stringField(video, "small_poster");
|
|
123
|
+
if (!videoTitle || !uid)
|
|
124
|
+
continue;
|
|
125
|
+
if (poster)
|
|
126
|
+
lines.push("", ``);
|
|
127
|
+
lines.push(`- [${videoTitle}](https://www.aparat.com/v/${uid})`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (target.tab === "home") {
|
|
131
|
+
if (!videos.length && profile.videoCount === "0") {
|
|
132
|
+
lines.push("", "", "", "### این کانال محتوایی ندارد!", "", "هنوز هیچ ویدیویی در این کانال منتشر نشده است.");
|
|
133
|
+
}
|
|
134
|
+
else if (videos.length) {
|
|
135
|
+
lines.push("", "آخرین ویدیوها");
|
|
136
|
+
for (const video of videos) {
|
|
137
|
+
const videoTitle = stringField(video, "title");
|
|
138
|
+
const uid = stringField(video, "uid");
|
|
139
|
+
const poster = stringField(video, "big_poster") || stringField(video, "small_poster");
|
|
140
|
+
if (!videoTitle || !uid)
|
|
141
|
+
continue;
|
|
142
|
+
if (poster)
|
|
143
|
+
lines.push("", ``);
|
|
144
|
+
lines.push(`[${videoTitle}](https://www.aparat.com/v/${uid})`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
finalUrl,
|
|
150
|
+
contentType: "text/html",
|
|
151
|
+
markdown: requireReadableContent(normalizeMarkdown(lines.join("\n"))),
|
|
152
|
+
title: `آپارات | ${name}`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
export async function loadAparat(target, ctx) {
|
|
156
|
+
try {
|
|
157
|
+
const username = encodeURIComponent(target.username);
|
|
158
|
+
let profilePayload;
|
|
159
|
+
try {
|
|
160
|
+
const profileResponse = await ctx.download(`https://www.aparat.com/api/fa/v1/user/user/information/username/${username}`);
|
|
161
|
+
profilePayload = jsonObject(profileResponse.bytes, profileResponse.contentType);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
ctx.throwIfAborted();
|
|
165
|
+
const profileResponse = await ctx.download(`https://www.aparat.com/etc/api/profile/username/${username}`);
|
|
166
|
+
profilePayload = jsonObject(profileResponse.bytes, profileResponse.contentType);
|
|
167
|
+
}
|
|
168
|
+
let profile = aparatProfile(profilePayload);
|
|
169
|
+
if (!profile)
|
|
170
|
+
return undefined;
|
|
171
|
+
let videos = [];
|
|
172
|
+
let shorts = [];
|
|
173
|
+
try {
|
|
174
|
+
if (target.tab === "about") {
|
|
175
|
+
const response = await ctx.download(`https://www.aparat.com/api/fa/v1/user/user/about/username/${username}`);
|
|
176
|
+
profile = mergeAparatAbout(profile, jsonObject(response.bytes, response.contentType));
|
|
177
|
+
}
|
|
178
|
+
else if (target.tab === "short-videos" && profile.id) {
|
|
179
|
+
const response = await ctx.download(`https://shorts.aparat.com/api/v1/web/profile/${encodeURIComponent(profile.id)}/posts`);
|
|
180
|
+
shorts = aparatShorts(jsonObject(response.bytes, response.contentType));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const endpoint = target.tab === "home"
|
|
184
|
+
? `user/user/profilehome_v2/username/${username}`
|
|
185
|
+
: `user/video/list/username/${username}`;
|
|
186
|
+
const response = await ctx.download(`https://www.aparat.com/api/fa/v1/${endpoint}`);
|
|
187
|
+
videos = aparatVideos(jsonObject(response.bytes, response.contentType));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
ctx.throwIfAborted();
|
|
192
|
+
if (target.tab === "videos") {
|
|
193
|
+
try {
|
|
194
|
+
const response = await ctx.download(`https://www.aparat.com/etc/api/videoByUser/username/${username}/perpage/10`);
|
|
195
|
+
videos = aparatVideos(jsonObject(response.bytes, response.contentType));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
ctx.throwIfAborted();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return aparatPage(target, profile, ctx.finalUrl, videos, shorts);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
ctx.throwIfAborted();
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=aparat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aparat.js","sourceRoot":"","sources":["../../src/fetch-page/aparat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation helpers for the undocumented JSON blobs embedded in dynamic site
|
|
3
|
+
* pages. Every accessor is total: a missing or wrongly typed field yields an
|
|
4
|
+
* empty value rather than throwing, because these shapes change without notice.
|
|
5
|
+
*/
|
|
6
|
+
export declare function jsonObject(bytes: Uint8Array, contentType: string): Record<string, unknown>;
|
|
7
|
+
export declare function stringField(value: Record<string, unknown>, key: string): string;
|
|
8
|
+
export declare function recordValue(value: unknown): Record<string, unknown> | undefined;
|
|
9
|
+
export declare function recordArray(value: unknown): Record<string, unknown>[];
|
|
10
|
+
export declare function findNestedValue(value: unknown, key: string): unknown;
|
|
11
|
+
export declare function findNestedRecord(value: unknown, predicate: (record: Record<string, unknown>) => boolean): Record<string, unknown> | undefined;
|
|
12
|
+
export declare function extractAssignedJson(html: string, marker: string): Record<string, unknown> | undefined;
|