@zqagent/shared 0.0.0-stage → 0.3.8
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/LICENSE +190 -0
- package/README.md +21 -2
- package/dist/approval-view.cjs +99 -0
- package/dist/approval-view.cjs.map +1 -0
- package/dist/approval-view.d.cts +39 -0
- package/dist/approval-view.d.ts +39 -0
- package/dist/approval-view.js +69 -0
- package/dist/approval-view.js.map +1 -0
- package/dist/fleet.cjs +263 -0
- package/dist/fleet.cjs.map +1 -0
- package/dist/fleet.d.cts +284 -0
- package/dist/fleet.d.ts +284 -0
- package/dist/fleet.js +214 -0
- package/dist/fleet.js.map +1 -0
- package/dist/hub-api.cjs +901 -0
- package/dist/hub-api.cjs.map +1 -0
- package/dist/hub-api.d.cts +1677 -0
- package/dist/hub-api.d.ts +1677 -0
- package/dist/hub-api.js +776 -0
- package/dist/hub-api.js.map +1 -0
- package/dist/index-DokiNlGJ.d.cts +182 -0
- package/dist/index-DokiNlGJ.d.ts +182 -0
- package/dist/index.cjs +861 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +779 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-presets.cjs +92 -0
- package/dist/llm-presets.cjs.map +1 -0
- package/dist/llm-presets.d.cts +28 -0
- package/dist/llm-presets.d.ts +28 -0
- package/dist/llm-presets.js +66 -0
- package/dist/llm-presets.js.map +1 -0
- package/dist/media.cjs +111 -0
- package/dist/media.cjs.map +1 -0
- package/dist/media.d.cts +59 -0
- package/dist/media.d.ts +59 -0
- package/dist/media.js +79 -0
- package/dist/media.js.map +1 -0
- package/dist/messages-CiXjdR1Z.d.cts +257 -0
- package/dist/messages-CiXjdR1Z.d.ts +257 -0
- package/dist/model-capabilities.cjs +105 -0
- package/dist/model-capabilities.cjs.map +1 -0
- package/dist/model-capabilities.d.cts +46 -0
- package/dist/model-capabilities.d.ts +46 -0
- package/dist/model-capabilities.js +74 -0
- package/dist/model-capabilities.js.map +1 -0
- package/dist/notify/index.cjs +375 -0
- package/dist/notify/index.cjs.map +1 -0
- package/dist/notify/index.d.cts +2 -0
- package/dist/notify/index.d.ts +2 -0
- package/dist/notify/index.js +330 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/secrets.cjs +121 -0
- package/dist/secrets.cjs.map +1 -0
- package/dist/secrets.d.cts +25 -0
- package/dist/secrets.d.ts +25 -0
- package/dist/secrets.js +82 -0
- package/dist/secrets.js.map +1 -0
- package/dist/wire/index.cjs +1265 -0
- package/dist/wire/index.cjs.map +1 -0
- package/dist/wire/index.d.cts +199 -0
- package/dist/wire/index.d.ts +199 -0
- package/dist/wire/index.js +1181 -0
- package/dist/wire/index.js.map +1 -0
- package/dist/wire/node.cjs +224 -0
- package/dist/wire/node.cjs.map +1 -0
- package/dist/wire/node.d.cts +14 -0
- package/dist/wire/node.d.ts +14 -0
- package/dist/wire/node.js +195 -0
- package/dist/wire/node.js.map +1 -0
- package/package.json +99 -3
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/model-capabilities.ts
|
|
21
|
+
var model_capabilities_exports = {};
|
|
22
|
+
__export(model_capabilities_exports, {
|
|
23
|
+
CAPABILITY_LABELS_ZH: () => CAPABILITY_LABELS_ZH,
|
|
24
|
+
MODEL_CAPABILITIES: () => MODEL_CAPABILITIES,
|
|
25
|
+
capabilitiesOf: () => capabilitiesOf,
|
|
26
|
+
capabilityOfMime: () => capabilityOfMime,
|
|
27
|
+
normalizeCapabilities: () => normalizeCapabilities,
|
|
28
|
+
suggestedCapabilities: () => suggestedCapabilities,
|
|
29
|
+
supportsCapability: () => supportsCapability
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(model_capabilities_exports);
|
|
32
|
+
var MODEL_CAPABILITIES = ["text", "image", "audio", "video"];
|
|
33
|
+
var CAPABILITY_LABELS_ZH = {
|
|
34
|
+
text: "\u6587\u672C",
|
|
35
|
+
image: "\u56FE\u7247",
|
|
36
|
+
audio: "\u97F3\u9891",
|
|
37
|
+
video: "\u89C6\u9891"
|
|
38
|
+
};
|
|
39
|
+
function capabilityOfMime(mimeType) {
|
|
40
|
+
const m = mimeType.trim().toLowerCase();
|
|
41
|
+
if (m.startsWith("image/")) return "image";
|
|
42
|
+
if (m.startsWith("audio/")) return "audio";
|
|
43
|
+
if (m.startsWith("video/")) return "video";
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function normalizeCapabilities(v) {
|
|
47
|
+
if (!Array.isArray(v)) return void 0;
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const x of v) {
|
|
50
|
+
const s = String(x ?? "").trim().toLowerCase();
|
|
51
|
+
if (!MODEL_CAPABILITIES.includes(s)) continue;
|
|
52
|
+
const cap = s;
|
|
53
|
+
if (!out.includes(cap)) out.push(cap);
|
|
54
|
+
}
|
|
55
|
+
return out.length ? out : void 0;
|
|
56
|
+
}
|
|
57
|
+
function capabilitiesOf(meta, model) {
|
|
58
|
+
if (!model || !meta?.length) return ["text"];
|
|
59
|
+
const exact = meta.find((m) => m.id === model && m.capabilities?.length);
|
|
60
|
+
if (exact?.capabilities?.length) return exact.capabilities;
|
|
61
|
+
const prefix = meta.find((m) => m.capabilities?.length && model.startsWith(m.id));
|
|
62
|
+
return prefix?.capabilities?.length ? prefix.capabilities : ["text"];
|
|
63
|
+
}
|
|
64
|
+
function supportsCapability(meta, model, capability) {
|
|
65
|
+
return capabilitiesOf(meta, model).includes(capability);
|
|
66
|
+
}
|
|
67
|
+
var KNOWN_VISION_MODELS = [
|
|
68
|
+
"deepseek-flash",
|
|
69
|
+
"deepseek-v4-flash",
|
|
70
|
+
"deepseek-v4-flash-vision",
|
|
71
|
+
"gpt-4o",
|
|
72
|
+
"gpt-4.1",
|
|
73
|
+
"gpt-5",
|
|
74
|
+
"o3",
|
|
75
|
+
"o4-mini",
|
|
76
|
+
"claude-3",
|
|
77
|
+
"claude-4",
|
|
78
|
+
"claude-sonnet",
|
|
79
|
+
"claude-opus",
|
|
80
|
+
"gemini-1.5",
|
|
81
|
+
"gemini-2",
|
|
82
|
+
"qwen-vl",
|
|
83
|
+
"qwen2-vl",
|
|
84
|
+
"qwen2.5-vl",
|
|
85
|
+
"qwen3-vl",
|
|
86
|
+
"glm-4v",
|
|
87
|
+
"glm-4.5v",
|
|
88
|
+
"moonshot-v1-vision",
|
|
89
|
+
"kimi-vl"
|
|
90
|
+
];
|
|
91
|
+
function suggestedCapabilities(modelId) {
|
|
92
|
+
const id = modelId.trim().toLowerCase();
|
|
93
|
+
return KNOWN_VISION_MODELS.some((p) => id.startsWith(p)) ? ["text", "image"] : ["text"];
|
|
94
|
+
}
|
|
95
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
96
|
+
0 && (module.exports = {
|
|
97
|
+
CAPABILITY_LABELS_ZH,
|
|
98
|
+
MODEL_CAPABILITIES,
|
|
99
|
+
capabilitiesOf,
|
|
100
|
+
capabilityOfMime,
|
|
101
|
+
normalizeCapabilities,
|
|
102
|
+
suggestedCapabilities,
|
|
103
|
+
supportsCapability
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=model-capabilities.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/model-capabilities.ts"],"sourcesContent":["/**\n * 模型输入能力(文本 / 图片 / 音频 / 视频)\n *\n * 供应商的 `/models` 接口只给模型 id(少数给上下文长度),**不告诉客户端这个模型\n * 能不能看图、听音频**。因此能力由用户在「节点配置 → LLM」里为每个模型勾选,\n * 随 `modelMeta` 一起落盘:\n *\n * - 后端在提交消息前据此校验附件类型(不支持的附件直接 400,而不是让供应商报错);\n * - 前端据此决定输入区可上传的文件类型(未勾选则不提供入口)。\n *\n * 纯数据 + 纯函数,供 core(服务端校验)与 console-ui(界面)共用。\n */\n\nexport const MODEL_CAPABILITIES = [\"text\", \"image\", \"audio\", \"video\"] as const;\n\n/** 模型可接收的输入类型 */\nexport type ModelCapability = (typeof MODEL_CAPABILITIES)[number];\n\n/** 非文本能力(对应可上传的附件种类) */\nexport type AttachmentCapability = Exclude<ModelCapability, \"text\">;\n\n/** 能力的中文名(后端错误提示用;界面文案在前端 i18n 里) */\nexport const CAPABILITY_LABELS_ZH: Record<ModelCapability, string> = {\n text: \"文本\",\n image: \"图片\",\n audio: \"音频\",\n video: \"视频\",\n};\n\n/** 附件 MIME → 能力;非图片/音频/视频返回 null */\nexport function capabilityOfMime(mimeType: string): AttachmentCapability | null {\n const m = mimeType.trim().toLowerCase();\n if (m.startsWith(\"image/\")) return \"image\";\n if (m.startsWith(\"audio/\")) return \"audio\";\n if (m.startsWith(\"video/\")) return \"video\";\n return null;\n}\n\n/**\n * 规范化能力列表:丢弃未知值、去重、保持声明顺序。\n * 空数组/非法输入返回 undefined(表示「未配置」,由调用方按默认值处理)。\n */\nexport function normalizeCapabilities(v: unknown): ModelCapability[] | undefined {\n if (!Array.isArray(v)) return undefined;\n const out: ModelCapability[] = [];\n for (const x of v) {\n const s = String(x ?? \"\").trim().toLowerCase();\n if (!(MODEL_CAPABILITIES as readonly string[]).includes(s)) continue;\n const cap = s as ModelCapability;\n if (!out.includes(cap)) out.push(cap);\n }\n return out.length ? out : undefined;\n}\n\n/**\n * 取某个模型的输入能力。\n *\n * 匹配规则与上下文长度一致:精确 id 优先,其次前缀匹配(兼容 `-2024-08-06`\n * 这类日期后缀)。**未配置时只按文本处理**——多模态要显式勾选,避免把图片\n * 发给不支持视觉的模型后收到 400。\n */\nexport function capabilitiesOf(\n meta: { id: string; capabilities?: ModelCapability[] }[] | undefined,\n model: string,\n): ModelCapability[] {\n if (!model || !meta?.length) return [\"text\"];\n const exact = meta.find((m) => m.id === model && m.capabilities?.length);\n if (exact?.capabilities?.length) return exact.capabilities;\n const prefix = meta.find((m) => m.capabilities?.length && model.startsWith(m.id));\n return prefix?.capabilities?.length ? prefix.capabilities : [\"text\"];\n}\n\n/** 该模型是否启用了某类附件输入 */\nexport function supportsCapability(\n meta: { id: string; capabilities?: ModelCapability[] }[] | undefined,\n model: string,\n capability: ModelCapability,\n): boolean {\n return capabilitiesOf(meta, model).includes(capability);\n}\n\n/**\n * 已知支持视觉输入的模型(前缀匹配)。\n *\n * 供应商不返回能力信息,但让用户面对一个空勾选框、还要自己去查文档也不友好。\n * 「刷新模型」时用这张表给新模型一个合理的初始值(用户仍可在设置里改):\n * 只列确定支持图片的公开模型;宁缺勿滥——错勾会把图发给纯文本模型并收到 400。\n */\nconst KNOWN_VISION_MODELS = [\n \"deepseek-flash\",\n \"deepseek-v4-flash\",\n \"deepseek-v4-flash-vision\",\n \"gpt-4o\",\n \"gpt-4.1\",\n \"gpt-5\",\n \"o3\",\n \"o4-mini\",\n \"claude-3\",\n \"claude-4\",\n \"claude-sonnet\",\n \"claude-opus\",\n \"gemini-1.5\",\n \"gemini-2\",\n \"qwen-vl\",\n \"qwen2-vl\",\n \"qwen2.5-vl\",\n \"qwen3-vl\",\n \"glm-4v\",\n \"glm-4.5v\",\n \"moonshot-v1-vision\",\n \"kimi-vl\",\n];\n\n/** 新模型的建议能力(未配置时的初始勾选):已知视觉模型 → 文本 + 图片,其余仅文本 */\nexport function suggestedCapabilities(modelId: string): ModelCapability[] {\n const id = modelId.trim().toLowerCase();\n return KNOWN_VISION_MODELS.some((p) => id.startsWith(p)) ? [\"text\", \"image\"] : [\"text\"];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaO,IAAM,qBAAqB,CAAC,QAAQ,SAAS,SAAS,OAAO;AAS7D,IAAM,uBAAwD;AAAA,EACnE,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAGO,SAAS,iBAAiB,UAA+C;AAC9E,QAAM,IAAI,SAAS,KAAK,EAAE,YAAY;AACtC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,SAAO;AACT;AAMO,SAAS,sBAAsB,GAA2C;AAC/E,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,QAAM,MAAyB,CAAC;AAChC,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY;AAC7C,QAAI,CAAE,mBAAyC,SAAS,CAAC,EAAG;AAC5D,UAAM,MAAM;AACZ,QAAI,CAAC,IAAI,SAAS,GAAG,EAAG,KAAI,KAAK,GAAG;AAAA,EACtC;AACA,SAAO,IAAI,SAAS,MAAM;AAC5B;AASO,SAAS,eACd,MACA,OACmB;AACnB,MAAI,CAAC,SAAS,CAAC,MAAM,OAAQ,QAAO,CAAC,MAAM;AAC3C,QAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,EAAE,cAAc,MAAM;AACvE,MAAI,OAAO,cAAc,OAAQ,QAAO,MAAM;AAC9C,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,cAAc,UAAU,MAAM,WAAW,EAAE,EAAE,CAAC;AAChF,SAAO,QAAQ,cAAc,SAAS,OAAO,eAAe,CAAC,MAAM;AACrE;AAGO,SAAS,mBACd,MACA,OACA,YACS;AACT,SAAO,eAAe,MAAM,KAAK,EAAE,SAAS,UAAU;AACxD;AASA,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,sBAAsB,SAAoC;AACxE,QAAM,KAAK,QAAQ,KAAK,EAAE,YAAY;AACtC,SAAO,oBAAoB,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,MAAM;AACxF;","names":[]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型输入能力(文本 / 图片 / 音频 / 视频)
|
|
3
|
+
*
|
|
4
|
+
* 供应商的 `/models` 接口只给模型 id(少数给上下文长度),**不告诉客户端这个模型
|
|
5
|
+
* 能不能看图、听音频**。因此能力由用户在「节点配置 → LLM」里为每个模型勾选,
|
|
6
|
+
* 随 `modelMeta` 一起落盘:
|
|
7
|
+
*
|
|
8
|
+
* - 后端在提交消息前据此校验附件类型(不支持的附件直接 400,而不是让供应商报错);
|
|
9
|
+
* - 前端据此决定输入区可上传的文件类型(未勾选则不提供入口)。
|
|
10
|
+
*
|
|
11
|
+
* 纯数据 + 纯函数,供 core(服务端校验)与 console-ui(界面)共用。
|
|
12
|
+
*/
|
|
13
|
+
declare const MODEL_CAPABILITIES: readonly ["text", "image", "audio", "video"];
|
|
14
|
+
/** 模型可接收的输入类型 */
|
|
15
|
+
type ModelCapability = (typeof MODEL_CAPABILITIES)[number];
|
|
16
|
+
/** 非文本能力(对应可上传的附件种类) */
|
|
17
|
+
type AttachmentCapability = Exclude<ModelCapability, "text">;
|
|
18
|
+
/** 能力的中文名(后端错误提示用;界面文案在前端 i18n 里) */
|
|
19
|
+
declare const CAPABILITY_LABELS_ZH: Record<ModelCapability, string>;
|
|
20
|
+
/** 附件 MIME → 能力;非图片/音频/视频返回 null */
|
|
21
|
+
declare function capabilityOfMime(mimeType: string): AttachmentCapability | null;
|
|
22
|
+
/**
|
|
23
|
+
* 规范化能力列表:丢弃未知值、去重、保持声明顺序。
|
|
24
|
+
* 空数组/非法输入返回 undefined(表示「未配置」,由调用方按默认值处理)。
|
|
25
|
+
*/
|
|
26
|
+
declare function normalizeCapabilities(v: unknown): ModelCapability[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* 取某个模型的输入能力。
|
|
29
|
+
*
|
|
30
|
+
* 匹配规则与上下文长度一致:精确 id 优先,其次前缀匹配(兼容 `-2024-08-06`
|
|
31
|
+
* 这类日期后缀)。**未配置时只按文本处理**——多模态要显式勾选,避免把图片
|
|
32
|
+
* 发给不支持视觉的模型后收到 400。
|
|
33
|
+
*/
|
|
34
|
+
declare function capabilitiesOf(meta: {
|
|
35
|
+
id: string;
|
|
36
|
+
capabilities?: ModelCapability[];
|
|
37
|
+
}[] | undefined, model: string): ModelCapability[];
|
|
38
|
+
/** 该模型是否启用了某类附件输入 */
|
|
39
|
+
declare function supportsCapability(meta: {
|
|
40
|
+
id: string;
|
|
41
|
+
capabilities?: ModelCapability[];
|
|
42
|
+
}[] | undefined, model: string, capability: ModelCapability): boolean;
|
|
43
|
+
/** 新模型的建议能力(未配置时的初始勾选):已知视觉模型 → 文本 + 图片,其余仅文本 */
|
|
44
|
+
declare function suggestedCapabilities(modelId: string): ModelCapability[];
|
|
45
|
+
|
|
46
|
+
export { type AttachmentCapability, CAPABILITY_LABELS_ZH, MODEL_CAPABILITIES, type ModelCapability, capabilitiesOf, capabilityOfMime, normalizeCapabilities, suggestedCapabilities, supportsCapability };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型输入能力(文本 / 图片 / 音频 / 视频)
|
|
3
|
+
*
|
|
4
|
+
* 供应商的 `/models` 接口只给模型 id(少数给上下文长度),**不告诉客户端这个模型
|
|
5
|
+
* 能不能看图、听音频**。因此能力由用户在「节点配置 → LLM」里为每个模型勾选,
|
|
6
|
+
* 随 `modelMeta` 一起落盘:
|
|
7
|
+
*
|
|
8
|
+
* - 后端在提交消息前据此校验附件类型(不支持的附件直接 400,而不是让供应商报错);
|
|
9
|
+
* - 前端据此决定输入区可上传的文件类型(未勾选则不提供入口)。
|
|
10
|
+
*
|
|
11
|
+
* 纯数据 + 纯函数,供 core(服务端校验)与 console-ui(界面)共用。
|
|
12
|
+
*/
|
|
13
|
+
declare const MODEL_CAPABILITIES: readonly ["text", "image", "audio", "video"];
|
|
14
|
+
/** 模型可接收的输入类型 */
|
|
15
|
+
type ModelCapability = (typeof MODEL_CAPABILITIES)[number];
|
|
16
|
+
/** 非文本能力(对应可上传的附件种类) */
|
|
17
|
+
type AttachmentCapability = Exclude<ModelCapability, "text">;
|
|
18
|
+
/** 能力的中文名(后端错误提示用;界面文案在前端 i18n 里) */
|
|
19
|
+
declare const CAPABILITY_LABELS_ZH: Record<ModelCapability, string>;
|
|
20
|
+
/** 附件 MIME → 能力;非图片/音频/视频返回 null */
|
|
21
|
+
declare function capabilityOfMime(mimeType: string): AttachmentCapability | null;
|
|
22
|
+
/**
|
|
23
|
+
* 规范化能力列表:丢弃未知值、去重、保持声明顺序。
|
|
24
|
+
* 空数组/非法输入返回 undefined(表示「未配置」,由调用方按默认值处理)。
|
|
25
|
+
*/
|
|
26
|
+
declare function normalizeCapabilities(v: unknown): ModelCapability[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* 取某个模型的输入能力。
|
|
29
|
+
*
|
|
30
|
+
* 匹配规则与上下文长度一致:精确 id 优先,其次前缀匹配(兼容 `-2024-08-06`
|
|
31
|
+
* 这类日期后缀)。**未配置时只按文本处理**——多模态要显式勾选,避免把图片
|
|
32
|
+
* 发给不支持视觉的模型后收到 400。
|
|
33
|
+
*/
|
|
34
|
+
declare function capabilitiesOf(meta: {
|
|
35
|
+
id: string;
|
|
36
|
+
capabilities?: ModelCapability[];
|
|
37
|
+
}[] | undefined, model: string): ModelCapability[];
|
|
38
|
+
/** 该模型是否启用了某类附件输入 */
|
|
39
|
+
declare function supportsCapability(meta: {
|
|
40
|
+
id: string;
|
|
41
|
+
capabilities?: ModelCapability[];
|
|
42
|
+
}[] | undefined, model: string, capability: ModelCapability): boolean;
|
|
43
|
+
/** 新模型的建议能力(未配置时的初始勾选):已知视觉模型 → 文本 + 图片,其余仅文本 */
|
|
44
|
+
declare function suggestedCapabilities(modelId: string): ModelCapability[];
|
|
45
|
+
|
|
46
|
+
export { type AttachmentCapability, CAPABILITY_LABELS_ZH, MODEL_CAPABILITIES, type ModelCapability, capabilitiesOf, capabilityOfMime, normalizeCapabilities, suggestedCapabilities, supportsCapability };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/model-capabilities.ts
|
|
2
|
+
var MODEL_CAPABILITIES = ["text", "image", "audio", "video"];
|
|
3
|
+
var CAPABILITY_LABELS_ZH = {
|
|
4
|
+
text: "\u6587\u672C",
|
|
5
|
+
image: "\u56FE\u7247",
|
|
6
|
+
audio: "\u97F3\u9891",
|
|
7
|
+
video: "\u89C6\u9891"
|
|
8
|
+
};
|
|
9
|
+
function capabilityOfMime(mimeType) {
|
|
10
|
+
const m = mimeType.trim().toLowerCase();
|
|
11
|
+
if (m.startsWith("image/")) return "image";
|
|
12
|
+
if (m.startsWith("audio/")) return "audio";
|
|
13
|
+
if (m.startsWith("video/")) return "video";
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function normalizeCapabilities(v) {
|
|
17
|
+
if (!Array.isArray(v)) return void 0;
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const x of v) {
|
|
20
|
+
const s = String(x ?? "").trim().toLowerCase();
|
|
21
|
+
if (!MODEL_CAPABILITIES.includes(s)) continue;
|
|
22
|
+
const cap = s;
|
|
23
|
+
if (!out.includes(cap)) out.push(cap);
|
|
24
|
+
}
|
|
25
|
+
return out.length ? out : void 0;
|
|
26
|
+
}
|
|
27
|
+
function capabilitiesOf(meta, model) {
|
|
28
|
+
if (!model || !meta?.length) return ["text"];
|
|
29
|
+
const exact = meta.find((m) => m.id === model && m.capabilities?.length);
|
|
30
|
+
if (exact?.capabilities?.length) return exact.capabilities;
|
|
31
|
+
const prefix = meta.find((m) => m.capabilities?.length && model.startsWith(m.id));
|
|
32
|
+
return prefix?.capabilities?.length ? prefix.capabilities : ["text"];
|
|
33
|
+
}
|
|
34
|
+
function supportsCapability(meta, model, capability) {
|
|
35
|
+
return capabilitiesOf(meta, model).includes(capability);
|
|
36
|
+
}
|
|
37
|
+
var KNOWN_VISION_MODELS = [
|
|
38
|
+
"deepseek-flash",
|
|
39
|
+
"deepseek-v4-flash",
|
|
40
|
+
"deepseek-v4-flash-vision",
|
|
41
|
+
"gpt-4o",
|
|
42
|
+
"gpt-4.1",
|
|
43
|
+
"gpt-5",
|
|
44
|
+
"o3",
|
|
45
|
+
"o4-mini",
|
|
46
|
+
"claude-3",
|
|
47
|
+
"claude-4",
|
|
48
|
+
"claude-sonnet",
|
|
49
|
+
"claude-opus",
|
|
50
|
+
"gemini-1.5",
|
|
51
|
+
"gemini-2",
|
|
52
|
+
"qwen-vl",
|
|
53
|
+
"qwen2-vl",
|
|
54
|
+
"qwen2.5-vl",
|
|
55
|
+
"qwen3-vl",
|
|
56
|
+
"glm-4v",
|
|
57
|
+
"glm-4.5v",
|
|
58
|
+
"moonshot-v1-vision",
|
|
59
|
+
"kimi-vl"
|
|
60
|
+
];
|
|
61
|
+
function suggestedCapabilities(modelId) {
|
|
62
|
+
const id = modelId.trim().toLowerCase();
|
|
63
|
+
return KNOWN_VISION_MODELS.some((p) => id.startsWith(p)) ? ["text", "image"] : ["text"];
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
CAPABILITY_LABELS_ZH,
|
|
67
|
+
MODEL_CAPABILITIES,
|
|
68
|
+
capabilitiesOf,
|
|
69
|
+
capabilityOfMime,
|
|
70
|
+
normalizeCapabilities,
|
|
71
|
+
suggestedCapabilities,
|
|
72
|
+
supportsCapability
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=model-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/model-capabilities.ts"],"sourcesContent":["/**\n * 模型输入能力(文本 / 图片 / 音频 / 视频)\n *\n * 供应商的 `/models` 接口只给模型 id(少数给上下文长度),**不告诉客户端这个模型\n * 能不能看图、听音频**。因此能力由用户在「节点配置 → LLM」里为每个模型勾选,\n * 随 `modelMeta` 一起落盘:\n *\n * - 后端在提交消息前据此校验附件类型(不支持的附件直接 400,而不是让供应商报错);\n * - 前端据此决定输入区可上传的文件类型(未勾选则不提供入口)。\n *\n * 纯数据 + 纯函数,供 core(服务端校验)与 console-ui(界面)共用。\n */\n\nexport const MODEL_CAPABILITIES = [\"text\", \"image\", \"audio\", \"video\"] as const;\n\n/** 模型可接收的输入类型 */\nexport type ModelCapability = (typeof MODEL_CAPABILITIES)[number];\n\n/** 非文本能力(对应可上传的附件种类) */\nexport type AttachmentCapability = Exclude<ModelCapability, \"text\">;\n\n/** 能力的中文名(后端错误提示用;界面文案在前端 i18n 里) */\nexport const CAPABILITY_LABELS_ZH: Record<ModelCapability, string> = {\n text: \"文本\",\n image: \"图片\",\n audio: \"音频\",\n video: \"视频\",\n};\n\n/** 附件 MIME → 能力;非图片/音频/视频返回 null */\nexport function capabilityOfMime(mimeType: string): AttachmentCapability | null {\n const m = mimeType.trim().toLowerCase();\n if (m.startsWith(\"image/\")) return \"image\";\n if (m.startsWith(\"audio/\")) return \"audio\";\n if (m.startsWith(\"video/\")) return \"video\";\n return null;\n}\n\n/**\n * 规范化能力列表:丢弃未知值、去重、保持声明顺序。\n * 空数组/非法输入返回 undefined(表示「未配置」,由调用方按默认值处理)。\n */\nexport function normalizeCapabilities(v: unknown): ModelCapability[] | undefined {\n if (!Array.isArray(v)) return undefined;\n const out: ModelCapability[] = [];\n for (const x of v) {\n const s = String(x ?? \"\").trim().toLowerCase();\n if (!(MODEL_CAPABILITIES as readonly string[]).includes(s)) continue;\n const cap = s as ModelCapability;\n if (!out.includes(cap)) out.push(cap);\n }\n return out.length ? out : undefined;\n}\n\n/**\n * 取某个模型的输入能力。\n *\n * 匹配规则与上下文长度一致:精确 id 优先,其次前缀匹配(兼容 `-2024-08-06`\n * 这类日期后缀)。**未配置时只按文本处理**——多模态要显式勾选,避免把图片\n * 发给不支持视觉的模型后收到 400。\n */\nexport function capabilitiesOf(\n meta: { id: string; capabilities?: ModelCapability[] }[] | undefined,\n model: string,\n): ModelCapability[] {\n if (!model || !meta?.length) return [\"text\"];\n const exact = meta.find((m) => m.id === model && m.capabilities?.length);\n if (exact?.capabilities?.length) return exact.capabilities;\n const prefix = meta.find((m) => m.capabilities?.length && model.startsWith(m.id));\n return prefix?.capabilities?.length ? prefix.capabilities : [\"text\"];\n}\n\n/** 该模型是否启用了某类附件输入 */\nexport function supportsCapability(\n meta: { id: string; capabilities?: ModelCapability[] }[] | undefined,\n model: string,\n capability: ModelCapability,\n): boolean {\n return capabilitiesOf(meta, model).includes(capability);\n}\n\n/**\n * 已知支持视觉输入的模型(前缀匹配)。\n *\n * 供应商不返回能力信息,但让用户面对一个空勾选框、还要自己去查文档也不友好。\n * 「刷新模型」时用这张表给新模型一个合理的初始值(用户仍可在设置里改):\n * 只列确定支持图片的公开模型;宁缺勿滥——错勾会把图发给纯文本模型并收到 400。\n */\nconst KNOWN_VISION_MODELS = [\n \"deepseek-flash\",\n \"deepseek-v4-flash\",\n \"deepseek-v4-flash-vision\",\n \"gpt-4o\",\n \"gpt-4.1\",\n \"gpt-5\",\n \"o3\",\n \"o4-mini\",\n \"claude-3\",\n \"claude-4\",\n \"claude-sonnet\",\n \"claude-opus\",\n \"gemini-1.5\",\n \"gemini-2\",\n \"qwen-vl\",\n \"qwen2-vl\",\n \"qwen2.5-vl\",\n \"qwen3-vl\",\n \"glm-4v\",\n \"glm-4.5v\",\n \"moonshot-v1-vision\",\n \"kimi-vl\",\n];\n\n/** 新模型的建议能力(未配置时的初始勾选):已知视觉模型 → 文本 + 图片,其余仅文本 */\nexport function suggestedCapabilities(modelId: string): ModelCapability[] {\n const id = modelId.trim().toLowerCase();\n return KNOWN_VISION_MODELS.some((p) => id.startsWith(p)) ? [\"text\", \"image\"] : [\"text\"];\n}\n"],"mappings":";AAaO,IAAM,qBAAqB,CAAC,QAAQ,SAAS,SAAS,OAAO;AAS7D,IAAM,uBAAwD;AAAA,EACnE,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAGO,SAAS,iBAAiB,UAA+C;AAC9E,QAAM,IAAI,SAAS,KAAK,EAAE,YAAY;AACtC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,MAAI,EAAE,WAAW,QAAQ,EAAG,QAAO;AACnC,SAAO;AACT;AAMO,SAAS,sBAAsB,GAA2C;AAC/E,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,QAAM,MAAyB,CAAC;AAChC,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY;AAC7C,QAAI,CAAE,mBAAyC,SAAS,CAAC,EAAG;AAC5D,UAAM,MAAM;AACZ,QAAI,CAAC,IAAI,SAAS,GAAG,EAAG,KAAI,KAAK,GAAG;AAAA,EACtC;AACA,SAAO,IAAI,SAAS,MAAM;AAC5B;AASO,SAAS,eACd,MACA,OACmB;AACnB,MAAI,CAAC,SAAS,CAAC,MAAM,OAAQ,QAAO,CAAC,MAAM;AAC3C,QAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,EAAE,cAAc,MAAM;AACvE,MAAI,OAAO,cAAc,OAAQ,QAAO,MAAM;AAC9C,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,cAAc,UAAU,MAAM,WAAW,EAAE,EAAE,CAAC;AAChF,SAAO,QAAQ,cAAc,SAAS,OAAO,eAAe,CAAC,MAAM;AACrE;AAGO,SAAS,mBACd,MACA,OACA,YACS;AACT,SAAO,eAAe,MAAM,KAAK,EAAE,SAAS,UAAU;AACxD;AASA,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,sBAAsB,SAAoC;AACxE,QAAM,KAAK,QAAQ,KAAK,EAAE,YAAY;AACtC,SAAO,oBAAoB,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,MAAM;AACxF;","names":[]}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/notify/index.ts
|
|
31
|
+
var notify_exports = {};
|
|
32
|
+
__export(notify_exports, {
|
|
33
|
+
BarkNotifier: () => BarkNotifier,
|
|
34
|
+
EmailNotifier: () => EmailNotifier,
|
|
35
|
+
NotificationHub: () => NotificationHub,
|
|
36
|
+
ServerChanNotifier: () => ServerChanNotifier,
|
|
37
|
+
WebhookNotifier: () => WebhookNotifier,
|
|
38
|
+
channelWants: () => channelWants,
|
|
39
|
+
createNotificationHub: () => createNotificationHub,
|
|
40
|
+
notifyBody: () => notifyBody,
|
|
41
|
+
notifyTitle: () => notifyTitle
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(notify_exports);
|
|
44
|
+
|
|
45
|
+
// src/approval-view.ts
|
|
46
|
+
var APPROVAL_VALUE_MAX = 120;
|
|
47
|
+
var APPROVAL_COLUMNS = ["\u9879\u76EE", "\u8BE6\u60C5"];
|
|
48
|
+
var TOOL_LABEL = "\u{1F527} \u5DE5\u5177";
|
|
49
|
+
var REASON_LABEL = "\u{1F4AC} \u539F\u56E0";
|
|
50
|
+
function formatValue(v, maxLen) {
|
|
51
|
+
let text;
|
|
52
|
+
if (v == null) {
|
|
53
|
+
text = "";
|
|
54
|
+
} else if (typeof v === "string") {
|
|
55
|
+
text = v;
|
|
56
|
+
} else {
|
|
57
|
+
try {
|
|
58
|
+
text = JSON.stringify(v);
|
|
59
|
+
} catch {
|
|
60
|
+
text = String(v);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
text = text.replace(/\r?\n/g, "\u21B5");
|
|
64
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "\u2026" : text;
|
|
65
|
+
}
|
|
66
|
+
function approvalInputFromData(data) {
|
|
67
|
+
const args = data["arguments"];
|
|
68
|
+
return {
|
|
69
|
+
toolName: String(data["toolName"] ?? ""),
|
|
70
|
+
reason: String(data["reason"] ?? ""),
|
|
71
|
+
arguments: args && typeof args === "object" ? args : void 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function approvalFields(input, maxLen = APPROVAL_VALUE_MAX) {
|
|
75
|
+
const fields = [
|
|
76
|
+
{ label: TOOL_LABEL, value: formatValue(input.toolName, maxLen) },
|
|
77
|
+
{ label: REASON_LABEL, value: formatValue(input.reason, maxLen) }
|
|
78
|
+
];
|
|
79
|
+
for (const [key, value] of Object.entries(input.arguments ?? {})) {
|
|
80
|
+
fields.push({ label: formatValue(key, maxLen), value: formatValue(value, maxLen) });
|
|
81
|
+
}
|
|
82
|
+
return fields;
|
|
83
|
+
}
|
|
84
|
+
function escapeCell(text) {
|
|
85
|
+
return text.replace(/\|/g, "\\|").replace(/\r?\n/g, "\u21B5");
|
|
86
|
+
}
|
|
87
|
+
function approvalToMarkdownTable(input, opts = {}) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
if (opts.title) lines.push(opts.title, "");
|
|
90
|
+
lines.push(`| ${APPROVAL_COLUMNS[0]} | ${APPROVAL_COLUMNS[1]} |`, "| --- | --- |");
|
|
91
|
+
for (const f of approvalFields(input, opts.maxLen)) {
|
|
92
|
+
lines.push(`| ${escapeCell(f.label)} | ${escapeCell(f.value)} |`);
|
|
93
|
+
}
|
|
94
|
+
return lines.join("\n");
|
|
95
|
+
}
|
|
96
|
+
function escapeHtml(text) {
|
|
97
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
98
|
+
}
|
|
99
|
+
function approvalToHtmlTable(input, maxLen = APPROVAL_VALUE_MAX) {
|
|
100
|
+
const rows = approvalFields(input, maxLen).map(
|
|
101
|
+
(f) => `<tr><th style="text-align:left;padding:4px 12px 4px 0;color:#6b7280;font-weight:500;white-space:nowrap;vertical-align:top;">${escapeHtml(f.label)}</th><td style="padding:4px 0;font-family:ui-monospace,Menlo,Consolas,monospace;">${escapeHtml(f.value)}</td></tr>`
|
|
102
|
+
).join("");
|
|
103
|
+
return `<table style="border-collapse:collapse;font-size:13px;">${rows}</table>`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/notify/Notifier.ts
|
|
107
|
+
function channelWants(channel, kind) {
|
|
108
|
+
const ev = channel.events;
|
|
109
|
+
switch (kind) {
|
|
110
|
+
case "approval_request":
|
|
111
|
+
return ev.approval;
|
|
112
|
+
case "question_asked":
|
|
113
|
+
return ev.question;
|
|
114
|
+
case "turn_completed":
|
|
115
|
+
case "turn_stopped":
|
|
116
|
+
return ev.completed;
|
|
117
|
+
case "error":
|
|
118
|
+
return ev.error;
|
|
119
|
+
case "session_status":
|
|
120
|
+
return false;
|
|
121
|
+
default:
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function notifyTitle(kind) {
|
|
126
|
+
switch (kind) {
|
|
127
|
+
case "approval_request":
|
|
128
|
+
return "\u9700\u8981\u5BA1\u6279";
|
|
129
|
+
case "question_asked":
|
|
130
|
+
return "\u9700\u8981\u56DE\u7B54";
|
|
131
|
+
case "turn_completed":
|
|
132
|
+
return "\u4EFB\u52A1\u5B8C\u6210";
|
|
133
|
+
case "turn_stopped":
|
|
134
|
+
return "\u8FBE\u5230\u8FED\u4EE3\u4E0A\u9650";
|
|
135
|
+
case "error":
|
|
136
|
+
return "\u4EFB\u52A1\u51FA\u9519";
|
|
137
|
+
default:
|
|
138
|
+
return "Agent \u901A\u77E5";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function notifyBody(ev, maxLen = 300) {
|
|
142
|
+
const d = ev.data;
|
|
143
|
+
switch (ev.kind) {
|
|
144
|
+
case "approval_request":
|
|
145
|
+
return approvalToMarkdownTable(approvalInputFromData(d), {
|
|
146
|
+
title: "\u{1F512} \u9700\u8981\u5BA1\u6279"
|
|
147
|
+
});
|
|
148
|
+
case "question_asked":
|
|
149
|
+
return String(d["question"] ?? "");
|
|
150
|
+
case "turn_completed":
|
|
151
|
+
return `\u72B6\u6001: ${String(d["status"] ?? "completed")}`;
|
|
152
|
+
case "turn_stopped":
|
|
153
|
+
return "\u5DF2\u8FBE\u6700\u5927\u8FED\u4EE3\u6B21\u6570\uFF0C\u53EF\u7EE7\u7EED\u5B8C\u6210\u5269\u4E0B\u7684\u4EFB\u52A1";
|
|
154
|
+
case "error":
|
|
155
|
+
return String(d["error"] ?? "\u672A\u77E5\u9519\u8BEF");
|
|
156
|
+
default:
|
|
157
|
+
return "";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/notify/NotificationHub.ts
|
|
162
|
+
var NotificationHub = class {
|
|
163
|
+
notifiers = /* @__PURE__ */ new Map();
|
|
164
|
+
/** 注册一个通道实现(幂等,后注册覆盖) */
|
|
165
|
+
register(notifier) {
|
|
166
|
+
this.notifiers.set(notifier.type, notifier);
|
|
167
|
+
}
|
|
168
|
+
/** 已注册的通道类型 */
|
|
169
|
+
registeredTypes() {
|
|
170
|
+
return [...this.notifiers.keys()];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 分发通知到给定的启用通道。
|
|
174
|
+
* 单个通道失败仅记录,不影响其他通道与核心逻辑。
|
|
175
|
+
*/
|
|
176
|
+
async dispatch(channels, ev) {
|
|
177
|
+
for (const ch of channels) {
|
|
178
|
+
if (!ch.enabled) continue;
|
|
179
|
+
if (!channelWants(ch, ev.kind)) continue;
|
|
180
|
+
const notifier = this.notifiers.get(ch.type);
|
|
181
|
+
if (!notifier) continue;
|
|
182
|
+
try {
|
|
183
|
+
await notifier.send(ch, ev);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.log(
|
|
186
|
+
`[notify] ${ch.type} \u53D1\u9001\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/notify/notifiers/BarkNotifier.ts
|
|
194
|
+
var TIMEOUT_MS = 1e4;
|
|
195
|
+
var BarkNotifier = class {
|
|
196
|
+
type = "bark";
|
|
197
|
+
async send(config, ev) {
|
|
198
|
+
const key = config.config["key"];
|
|
199
|
+
if (!key) return;
|
|
200
|
+
const server = (config.config["server"] ?? "https://api.day.app").replace(/\/+$/, "");
|
|
201
|
+
const url = `${server}/${encodeURIComponent(key)}/${encodeURIComponent(notifyTitle(ev.kind))}/${encodeURIComponent(notifyBody(ev))}`;
|
|
202
|
+
const res = await fetch(url, {
|
|
203
|
+
method: "GET",
|
|
204
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
205
|
+
});
|
|
206
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/notify/notifiers/ServerChanNotifier.ts
|
|
211
|
+
var TIMEOUT_MS2 = 1e4;
|
|
212
|
+
var ServerChanNotifier = class {
|
|
213
|
+
type = "serverchan";
|
|
214
|
+
async send(config, ev) {
|
|
215
|
+
const sendkey = config.config["sendkey"];
|
|
216
|
+
if (!sendkey) return;
|
|
217
|
+
const url = `https://sctapi.ftqq.com/${encodeURIComponent(sendkey)}.send`;
|
|
218
|
+
const body = new URLSearchParams({
|
|
219
|
+
title: notifyTitle(ev.kind),
|
|
220
|
+
desp: notifyBody(ev)
|
|
221
|
+
});
|
|
222
|
+
const res = await fetch(url, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
225
|
+
body,
|
|
226
|
+
signal: AbortSignal.timeout(TIMEOUT_MS2)
|
|
227
|
+
});
|
|
228
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/notify/notifiers/WebhookNotifier.ts
|
|
233
|
+
var TIMEOUT_MS3 = 1e4;
|
|
234
|
+
var WebhookNotifier = class {
|
|
235
|
+
type = "webhook";
|
|
236
|
+
async send(config, ev) {
|
|
237
|
+
const url = config.config["url"];
|
|
238
|
+
if (!url) return;
|
|
239
|
+
const res = await fetch(url, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: { "Content-Type": "application/json" },
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
title: notifyTitle(ev.kind),
|
|
244
|
+
body: notifyBody(ev),
|
|
245
|
+
kind: ev.kind,
|
|
246
|
+
sessionId: ev.sessionId,
|
|
247
|
+
ts: ev.ts,
|
|
248
|
+
data: ev.data
|
|
249
|
+
}),
|
|
250
|
+
signal: AbortSignal.timeout(TIMEOUT_MS3)
|
|
251
|
+
});
|
|
252
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/notify/notifiers/EmailNotifier.ts
|
|
257
|
+
var import_nodemailer = __toESM(require("nodemailer"), 1);
|
|
258
|
+
function defaultTransportFactory(opts) {
|
|
259
|
+
return import_nodemailer.default.createTransport(opts);
|
|
260
|
+
}
|
|
261
|
+
function buildText(ev) {
|
|
262
|
+
const d = ev.data;
|
|
263
|
+
switch (ev.kind) {
|
|
264
|
+
case "turn_completed":
|
|
265
|
+
case "turn_stopped": {
|
|
266
|
+
const lines = [];
|
|
267
|
+
lines.push(`\u4F1A\u8BDD: ${ev.sessionId}`);
|
|
268
|
+
lines.push(`\u72B6\u6001: ${String(d["status"] ?? ev.kind)}`);
|
|
269
|
+
if (d["iterations"] !== void 0)
|
|
270
|
+
lines.push(`\u8FED\u4EE3\u6B21\u6570: ${String(d["iterations"])}`);
|
|
271
|
+
const files = d["produced_files"];
|
|
272
|
+
if (Array.isArray(files) && files.length > 0)
|
|
273
|
+
lines.push(`\u4EA7\u51FA\u6587\u4EF6:
|
|
274
|
+
${files.join("\n ")}`);
|
|
275
|
+
const summary = typeof d["summary"] === "string" ? d["summary"] : "";
|
|
276
|
+
if (summary.trim()) lines.push(`
|
|
277
|
+
\u6458\u8981:
|
|
278
|
+
${summary}`);
|
|
279
|
+
return lines.join("\n");
|
|
280
|
+
}
|
|
281
|
+
case "approval_request":
|
|
282
|
+
return `${approvalToMarkdownTable(approvalInputFromData(d), {
|
|
283
|
+
title: "\u{1F512} \u9700\u8981\u5BA1\u6279"
|
|
284
|
+
})}
|
|
285
|
+
\u4F1A\u8BDD: ${ev.sessionId}`;
|
|
286
|
+
case "question_asked":
|
|
287
|
+
return `\u95EE\u9898: ${String(d["question"] ?? "")}
|
|
288
|
+
\u4F1A\u8BDD: ${ev.sessionId}`;
|
|
289
|
+
case "error":
|
|
290
|
+
return `\u9519\u8BEF: ${String(d["error"] ?? "\u672A\u77E5\u9519\u8BEF")}
|
|
291
|
+
\u4F1A\u8BDD: ${ev.sessionId}`;
|
|
292
|
+
default:
|
|
293
|
+
return `\u4F1A\u8BDD: ${ev.sessionId}
|
|
294
|
+
\u65F6\u95F4: ${ev.ts}`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function escapeHtml2(text) {
|
|
298
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
299
|
+
}
|
|
300
|
+
function buildHtml(ev) {
|
|
301
|
+
if (ev.kind === "approval_request") {
|
|
302
|
+
const table = approvalToHtmlTable(approvalInputFromData(ev.data));
|
|
303
|
+
return table + `<p style="color:#6b7280;font-size:12px;margin:10px 0 0;">\u4F1A\u8BDD: ${escapeHtml2(ev.sessionId)}</p>`;
|
|
304
|
+
}
|
|
305
|
+
return `<pre style="white-space:pre-wrap;font-family:inherit;">${escapeHtml2(buildText(ev))}</pre>`;
|
|
306
|
+
}
|
|
307
|
+
var EmailNotifier = class {
|
|
308
|
+
constructor(transportFactory = defaultTransportFactory) {
|
|
309
|
+
this.transportFactory = transportFactory;
|
|
310
|
+
}
|
|
311
|
+
transportFactory;
|
|
312
|
+
type = "email";
|
|
313
|
+
async send(config, ev) {
|
|
314
|
+
if (ev.kind === "turn_completed") {
|
|
315
|
+
const minSec = Number(config.config["minDurationSeconds"] ?? 60);
|
|
316
|
+
const durationMs = ev.data["durationMs"];
|
|
317
|
+
if (typeof durationMs === "number" && durationMs < minSec * 1e3) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const host = config.config["host"];
|
|
322
|
+
const to = config.config["to"];
|
|
323
|
+
if (!host || !to) return;
|
|
324
|
+
const secure = config.config["secure"] === "true";
|
|
325
|
+
const port = Number(config.config["port"]) || (secure ? 465 : 587);
|
|
326
|
+
const user = config.config["user"] ?? "";
|
|
327
|
+
const pass = config.config["pass"] ?? "";
|
|
328
|
+
const from = config.config["from"] ?? user;
|
|
329
|
+
if (!from) return;
|
|
330
|
+
const prefix = config.config["subjectPrefix"] ?? "[zqagent]";
|
|
331
|
+
const transport = this.transportFactory({
|
|
332
|
+
host,
|
|
333
|
+
port,
|
|
334
|
+
secure,
|
|
335
|
+
auth: user ? { user, pass } : void 0
|
|
336
|
+
});
|
|
337
|
+
try {
|
|
338
|
+
await transport.sendMail({
|
|
339
|
+
from,
|
|
340
|
+
to,
|
|
341
|
+
subject: `${prefix}${notifyTitle(ev.kind)}`,
|
|
342
|
+
text: buildText(ev),
|
|
343
|
+
html: buildHtml(ev)
|
|
344
|
+
});
|
|
345
|
+
} finally {
|
|
346
|
+
try {
|
|
347
|
+
transport.close();
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/notify/index.ts
|
|
355
|
+
function createNotificationHub() {
|
|
356
|
+
const hub = new NotificationHub();
|
|
357
|
+
hub.register(new BarkNotifier());
|
|
358
|
+
hub.register(new ServerChanNotifier());
|
|
359
|
+
hub.register(new WebhookNotifier());
|
|
360
|
+
hub.register(new EmailNotifier());
|
|
361
|
+
return hub;
|
|
362
|
+
}
|
|
363
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
364
|
+
0 && (module.exports = {
|
|
365
|
+
BarkNotifier,
|
|
366
|
+
EmailNotifier,
|
|
367
|
+
NotificationHub,
|
|
368
|
+
ServerChanNotifier,
|
|
369
|
+
WebhookNotifier,
|
|
370
|
+
channelWants,
|
|
371
|
+
createNotificationHub,
|
|
372
|
+
notifyBody,
|
|
373
|
+
notifyTitle
|
|
374
|
+
});
|
|
375
|
+
//# sourceMappingURL=index.cjs.map
|