@kafca/agentdock 0.1.55 → 0.1.57
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 +14 -0
- package/dist/renderer/assets/{Badge-Bg_nNvBL.js → Badge-CDTbmw1h.js} +1 -1
- package/dist/renderer/assets/Card-DayUo35w.js +1 -0
- package/dist/renderer/assets/Config-ByGMQuzR.js +21 -0
- package/dist/renderer/assets/CronList-XqHdVlgu.js +1 -0
- package/dist/renderer/assets/Dashboard-CZj-oANw.js +41 -0
- package/dist/renderer/assets/{EmptyState-C60g3RMj.js → EmptyState-EFMkOjZO.js} +1 -1
- package/dist/renderer/assets/HighlightedMarkdown-DyvF3DHK.js +12 -0
- package/dist/renderer/assets/Input-DxoWVc9d.js +1 -0
- package/dist/renderer/assets/{KnowledgeDetail-CC5C7JYe.js → KnowledgeDetail-CbUb2tG7.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-3rlvxgWf.js → KnowledgeHome-BWV1gz4f.js} +3 -3
- package/dist/renderer/assets/Logs-DqnanQTc.js +6 -0
- package/dist/renderer/assets/{Modal-BQUW9LSf.js → Modal-yY7TzR0x.js} +3 -3
- package/dist/renderer/assets/MonitorList-CIjGQkFs.js +1 -0
- package/dist/renderer/assets/Page-vwHNUdoj.js +1 -0
- package/dist/renderer/assets/{Select-CfRkos3z.js → Select-CmbK36pN.js} +2 -2
- package/dist/renderer/assets/ThreadChat-4OceMgM4.js +86 -0
- package/dist/renderer/assets/Workspace-DplPJOjJ.js +19 -0
- package/dist/renderer/assets/{arrow-left-D1DbXrLA.js → arrow-left-C8HfBt2K.js} +1 -1
- package/dist/renderer/assets/{book-open-BxC1z1C1.js → book-open-BCdZWncQ.js} +1 -1
- package/dist/renderer/assets/channels-hgO3cAIx.js +6 -0
- package/dist/renderer/assets/chevron-down-BPk77tqY.js +11 -0
- package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
- package/dist/renderer/assets/index-BlA4xPdd.js +41 -0
- package/dist/renderer/assets/index-wYpjqVqC.js +167 -0
- package/dist/renderer/assets/knowledge-7pl06KuH.js +1 -0
- package/dist/renderer/assets/{pencil-X44UaxwR.js → pencil-CB1SWZ9A.js} +1 -1
- package/dist/renderer/assets/{play-ujo-De38.js → play-BvsoXUrM.js} +1 -1
- package/dist/renderer/assets/{plus-BHgVdG_A.js → plus-CokBDnaL.js} +1 -1
- package/dist/renderer/assets/{save-Cz6giKrE.js → save-D-w4kATO.js} +1 -1
- package/dist/renderer/assets/{search-DKWvTqi5.js → search-Cnsn6i4Q.js} +1 -1
- package/dist/renderer/assets/{shield-check-V82O8AC1.js → shield-check-Cjja2Gh6.js} +1 -1
- package/dist/renderer/assets/threads-BCm6j4eI.js +1 -0
- package/dist/renderer/assets/{trash-2-_Fgwk8mc.js → trash-2-CIxz9qYY.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/packages/contracts/src/automation.js +2 -0
- package/dist-electron/packages/contracts/src/channels.js +2 -0
- package/dist-electron/packages/contracts/src/knowledge.js +2 -0
- package/dist-electron/packages/contracts/src/local-core.js +15 -0
- package/dist-electron/packages/contracts/src/runtime.js +2 -0
- package/dist-electron/packages/contracts/src/scheduler.js +2 -0
- package/dist-electron/packages/contracts/src/threads.js +2 -0
- package/dist-electron/packages/core-sdk/src/automation.js +32 -0
- package/dist-electron/packages/core-sdk/src/channels.js +121 -0
- package/dist-electron/packages/core-sdk/src/client.js +455 -0
- package/dist-electron/packages/core-sdk/src/index.js +43 -607
- package/dist-electron/packages/core-sdk/src/knowledge.js +85 -0
- package/dist-electron/packages/core-sdk/src/request.js +22 -0
- package/dist-electron/packages/core-sdk/src/runtime.js +214 -0
- package/dist-electron/packages/core-sdk/src/scheduler.js +32 -0
- package/dist-electron/packages/core-sdk/src/threads.js +67 -0
- package/dist-electron/packages/plugin-sdk/src/agents.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/automation.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/channels.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/index.js +21 -0
- package/dist-electron/packages/plugin-sdk/src/knowledge.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/runtime.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/scheduler.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/threads.js +2 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +6 -9
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +5 -5
- package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +12 -12
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +11 -11
- package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +7 -7
- package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +4 -4
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +1 -1
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +4 -4
- package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +33 -18
- package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +52 -456
- package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +124 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/inbound-attachment-store.js +93 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +98 -483
- package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +156 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +100 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +6 -6
- package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
- package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +33 -5
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
- package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
- package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
- package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
- package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
- package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
- package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
- package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
- package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
- package/dist-electron/shared/desktop.js +1 -64
- package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
- package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
- package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
- package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
- package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
- package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
- package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
- package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
- package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
- package/dist-electron/tests/electron/core-client.test.js +284 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
- package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
- package/dist-electron/tests/integration/channel-inbound-attachment-store.test.js +117 -0
- package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
- package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +58 -4
- package/dist-electron/tests/integration/local-core-errors.test.js +41 -0
- package/dist-electron/tests/integration/weixin-gateway.test.js +36 -0
- package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
- package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
- package/package.json +9 -4
- package/dist/renderer/assets/Chat-BJveQQKu.js +0 -2
- package/dist/renderer/assets/ChatMarkdown-Bqn2HJ6f.js +0 -45
- package/dist/renderer/assets/Config-CMtZDRAE.js +0 -21
- package/dist/renderer/assets/CronList-C-ILB_Ae.js +0 -1
- package/dist/renderer/assets/Dashboard-CsKBSQIe.js +0 -26
- package/dist/renderer/assets/Logs-CktemGN0.js +0 -1
- package/dist/renderer/assets/MonitorList-B0hxjuOZ.js +0 -1
- package/dist/renderer/assets/Page-DL_Nv2i5.js +0 -1
- package/dist/renderer/assets/ProjectDetail-Djr9-MyT.js +0 -11
- package/dist/renderer/assets/ProjectList-BKl4eW7q.js +0 -1
- package/dist/renderer/assets/SessionChat-Dxv8D5DW.js +0 -1
- package/dist/renderer/assets/SessionList-BJ2GjXvR.js +0 -1
- package/dist/renderer/assets/ThreadChat-CAsIl9qf.js +0 -28
- package/dist/renderer/assets/Workspace-CaUhiGBX.js +0 -19
- package/dist/renderer/assets/bot-Fw3IzhJA.js +0 -6
- package/dist/renderer/assets/check-5zCTgp3A.js +0 -6
- package/dist/renderer/assets/chevron-down-N3PxTNxH.js +0 -6
- package/dist/renderer/assets/circle-C_chUflo.js +0 -6
- package/dist/renderer/assets/desktop-DkQTZNhe.js +0 -1
- package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
- package/dist/renderer/assets/funnel-B8ZAv6I7.js +0 -6
- package/dist/renderer/assets/heart-M9A3E4Ed.js +0 -6
- package/dist/renderer/assets/index-Bwv855ID.js +0 -41
- package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
- package/dist/renderer/assets/index-DJ8w4e0l.js +0 -182
- package/dist/renderer/assets/projects-DB1FLZ3A.js +0 -1
- package/dist/renderer/assets/server-BsuHcYl4.js +0 -11
- package/dist/renderer/assets/session-utils-DnAWL3eo.js +0 -6
- package/dist/renderer/assets/sessions-DptxO_BE.js +0 -1
- package/dist/renderer/assets/user-DQKTaUcR.js +0 -6
- package/dist/renderer/assets/wifi-off-vjH-MD2w.js +0 -11
- package/dist-electron/src/api/client.js +0 -73
- package/dist-electron/src/api/sessions.js +0 -21
- package/dist-electron/src/lib/session-utils.js +0 -51
- package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
- package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js
CHANGED
|
@@ -3,26 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LocalCoreWeixinGateway = void 0;
|
|
7
|
-
exports.createWeixinAttachmentContentPart = createWeixinAttachmentContentPart;
|
|
6
|
+
exports.LocalCoreWeixinGateway = exports.createWeixinAttachmentContentPart = void 0;
|
|
8
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
9
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
9
|
const node_crypto_2 = require("node:crypto");
|
|
12
|
-
const
|
|
10
|
+
const node_stream_1 = require("node:stream");
|
|
13
11
|
const local_core_errors_js_1 = require("../../kernel/local-core-errors.js");
|
|
14
12
|
const content_js_1 = require("../shared/content.js");
|
|
15
13
|
const file_utils_js_1 = require("../shared/file-utils.js");
|
|
14
|
+
const inbound_attachment_store_js_1 = require("../shared/inbound-attachment-store.js");
|
|
16
15
|
const thread_routing_js_1 = require("../shared/thread-routing.js");
|
|
17
16
|
const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
|
|
17
|
+
const inbound_authorization_js_1 = require("../shared/inbound-authorization.js");
|
|
18
18
|
const channel_keys_js_1 = require("../shared/channel-keys.js");
|
|
19
19
|
const config_js_1 = require("./config.js");
|
|
20
20
|
const transport_js_1 = require("./transport.js");
|
|
21
|
+
const runtime_state_js_1 = require("./runtime-state.js");
|
|
22
|
+
const text_utils_js_1 = require("./text-utils.js");
|
|
23
|
+
const inbound_poller_js_1 = require("./inbound-poller.js");
|
|
24
|
+
var inbound_media_js_1 = require("./inbound-media.js");
|
|
25
|
+
Object.defineProperty(exports, "createWeixinAttachmentContentPart", { enumerable: true, get: function () { return inbound_media_js_1.createWeixinAttachmentContentPart; } });
|
|
21
26
|
// ==================== Constants ====================
|
|
22
|
-
const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
|
|
23
|
-
const RETRY_DELAY_MS = 2_000;
|
|
24
|
-
const BACKOFF_DELAY_MS = 30_000;
|
|
25
|
-
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
26
27
|
const PROCESSED_MESSAGE_TTL_MS = 10 * 60 * 1000;
|
|
27
28
|
const WEIXIN_TEXT_MESSAGE_MAX_BYTES = 900;
|
|
28
29
|
const WEIXIN_CONTEXT_REPLY_MAX_BYTES = 3500;
|
|
@@ -31,124 +32,10 @@ const WEIXIN_RESERVED_TERMINAL_SENDS = 1;
|
|
|
31
32
|
const WEIXIN_PROGRESS_SEND_BUDGET = WEIXIN_CONTEXT_SEND_LIMIT - WEIXIN_RESERVED_TERMINAL_SENDS;
|
|
32
33
|
const RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
33
34
|
const WEIXIN_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
|
|
34
|
-
const ERROR_LOG_WINDOW_MS = 5 * 60 * 1000;
|
|
35
|
-
// ==================== Utilities ====================
|
|
36
|
-
function stripHtml(html) {
|
|
37
|
-
let result = html;
|
|
38
|
-
let prev;
|
|
39
|
-
do {
|
|
40
|
-
prev = result;
|
|
41
|
-
result = result.replace(/<[^>]*>/g, '');
|
|
42
|
-
} while (result !== prev);
|
|
43
|
-
return result.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, ' ');
|
|
44
|
-
}
|
|
45
|
-
function formatError(err) {
|
|
46
|
-
if (err instanceof Error) {
|
|
47
|
-
const cause = err.cause;
|
|
48
|
-
return cause !== undefined ? `${err.message}: ${String(cause)}` : err.message;
|
|
49
|
-
}
|
|
50
|
-
return String(err);
|
|
51
|
-
}
|
|
52
|
-
function sleep(ms, signal) {
|
|
53
|
-
return new Promise((resolve, reject) => {
|
|
54
|
-
const t = setTimeout(resolve, ms);
|
|
55
|
-
const onAbort = () => { clearTimeout(t); reject(new Error('aborted')); };
|
|
56
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
function utf8ByteLength(value) {
|
|
60
|
-
return Buffer.byteLength(value, 'utf-8');
|
|
61
|
-
}
|
|
62
|
-
function splitTextByUtf8Bytes(text, maxBytes) {
|
|
63
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
64
|
-
if (!normalized)
|
|
65
|
-
return [];
|
|
66
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
67
|
-
return [normalized];
|
|
68
|
-
const chunks = [];
|
|
69
|
-
let current = '';
|
|
70
|
-
const pushCurrent = () => {
|
|
71
|
-
const trimmed = current.trim();
|
|
72
|
-
if (trimmed)
|
|
73
|
-
chunks.push(trimmed);
|
|
74
|
-
current = '';
|
|
75
|
-
};
|
|
76
|
-
const appendPart = (part) => {
|
|
77
|
-
if (!part.trim())
|
|
78
|
-
return;
|
|
79
|
-
const separator = current ? '\n\n' : '';
|
|
80
|
-
if (current && utf8ByteLength(`${current}${separator}${part}`) <= maxBytes) {
|
|
81
|
-
current = `${current}${separator}${part}`;
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (current)
|
|
85
|
-
pushCurrent();
|
|
86
|
-
if (utf8ByteLength(part) <= maxBytes) {
|
|
87
|
-
current = part;
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
let segment = '';
|
|
91
|
-
for (const char of Array.from(part)) {
|
|
92
|
-
if (segment && utf8ByteLength(`${segment}${char}`) > maxBytes) {
|
|
93
|
-
chunks.push(segment);
|
|
94
|
-
segment = '';
|
|
95
|
-
}
|
|
96
|
-
segment += char;
|
|
97
|
-
}
|
|
98
|
-
current = segment;
|
|
99
|
-
};
|
|
100
|
-
for (const part of normalized.split(/\n{2,}/)) {
|
|
101
|
-
appendPart(part);
|
|
102
|
-
}
|
|
103
|
-
pushCurrent();
|
|
104
|
-
return chunks;
|
|
105
|
-
}
|
|
106
|
-
function createWeixinAttachmentContentPart(att) {
|
|
107
|
-
if (att.kind === 'image') {
|
|
108
|
-
if (!att.data)
|
|
109
|
-
return null;
|
|
110
|
-
return {
|
|
111
|
-
type: 'image',
|
|
112
|
-
data: att.data,
|
|
113
|
-
mimeType: att.mimeType,
|
|
114
|
-
fileName: att.name,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
type: 'file',
|
|
119
|
-
path: att.path,
|
|
120
|
-
fileName: att.name,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function truncateTextByUtf8Bytes(text, maxBytes) {
|
|
124
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
125
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
126
|
-
return normalized;
|
|
127
|
-
const suffix = '\n\n(内容过长,已截断以保证微信送达)';
|
|
128
|
-
const budget = Math.max(0, maxBytes - utf8ByteLength(suffix));
|
|
129
|
-
let result = '';
|
|
130
|
-
for (const char of Array.from(normalized)) {
|
|
131
|
-
if (utf8ByteLength(`${result}${char}`) > budget)
|
|
132
|
-
break;
|
|
133
|
-
result += char;
|
|
134
|
-
}
|
|
135
|
-
return `${result.trim()}${suffix}`;
|
|
136
|
-
}
|
|
137
|
-
function renderBridgeContentForWeixin(event) {
|
|
138
|
-
const toolCall = event.toolCall;
|
|
139
|
-
if (!toolCall) {
|
|
140
|
-
return String(event.content || '').trim();
|
|
141
|
-
}
|
|
142
|
-
const name = String(toolCall.name || '').trim() || 'Tool update';
|
|
143
|
-
const status = String(toolCall.status || '').trim();
|
|
144
|
-
if (name === 'Tool update' && status === 'completed')
|
|
145
|
-
return '';
|
|
146
|
-
return [name, status].filter(Boolean).join(' - ');
|
|
147
|
-
}
|
|
148
35
|
// ==================== Gateway Class ====================
|
|
149
36
|
class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGateway {
|
|
150
37
|
processedInboundMessages = new Map();
|
|
151
|
-
|
|
38
|
+
inboundAttachmentStore = new inbound_attachment_store_js_1.FileSystemInboundAttachmentStore();
|
|
152
39
|
platform = 'weixin';
|
|
153
40
|
constructor(options) {
|
|
154
41
|
super(options);
|
|
@@ -168,7 +55,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
168
55
|
return (0, config_js_1.collectWeixinWorkspaceBindings)(config);
|
|
169
56
|
}
|
|
170
57
|
buildStatusObject(state, resolved) {
|
|
171
|
-
this.options.store.expirePendingPairings();
|
|
172
58
|
const platformKey = state.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', resolved.instanceId);
|
|
173
59
|
const pairings = this.options.store.listPendingPairings(state.workspaceId)
|
|
174
60
|
.filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
|
|
@@ -325,19 +211,19 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
325
211
|
const bridgeThreadId = route.threadId || binding.thread_id;
|
|
326
212
|
if (this.mutedThreadBridgeCounts.has(bridgeThreadId))
|
|
327
213
|
return;
|
|
328
|
-
const turn = this.
|
|
214
|
+
const turn = (0, runtime_state_js_1.getOrCreateWeixinTurnState)(this.outboundTurns, sessionKey);
|
|
329
215
|
if (event.replyCtx) {
|
|
330
216
|
// WeChat doesn't support reply context; ignore
|
|
331
217
|
}
|
|
332
|
-
|
|
218
|
+
(0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, event);
|
|
333
219
|
if (event.type !== 'reply' && event.type !== 'buttons' && event.type !== 'status')
|
|
334
220
|
return;
|
|
335
|
-
const rendered =
|
|
221
|
+
const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
|
|
336
222
|
if (!rendered)
|
|
337
223
|
return;
|
|
338
224
|
if (rendered === turn.lastSentText)
|
|
339
225
|
return;
|
|
340
|
-
const terminalMessage =
|
|
226
|
+
const terminalMessage = (0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(event, rendered);
|
|
341
227
|
if (binding.last_platform_message_id && !terminalMessage && turn.sentCount >= WEIXIN_PROGRESS_SEND_BUDGET) {
|
|
342
228
|
turn.foldedProgressCount += 1;
|
|
343
229
|
this.options.log?.(`localcore-weixin folded progress for sessionKey=${sessionKey}: sent=${turn.sentCount} folded=${turn.foldedProgressCount}`);
|
|
@@ -360,7 +246,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
360
246
|
this.options.log?.(`localcore-weixin sent message for sessionKey=${sessionKey} type=${event.type} sent=${turn.sentCount}/${binding.last_platform_message_id ? WEIXIN_CONTEXT_SEND_LIMIT : 'unlimited'}`);
|
|
361
247
|
}
|
|
362
248
|
catch (error) {
|
|
363
|
-
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${
|
|
249
|
+
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, text_utils_js_1.formatWeixinError)(error)}`);
|
|
364
250
|
}
|
|
365
251
|
})
|
|
366
252
|
.finally(() => {
|
|
@@ -385,7 +271,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
385
271
|
const previousRoute = this.threadRouting.get(input.sessionKey);
|
|
386
272
|
this.threadRouting.set(input.sessionKey, route);
|
|
387
273
|
if (!this.outboundTurns.has(input.sessionKey)) {
|
|
388
|
-
this.
|
|
274
|
+
this.outboundTurns.set(input.sessionKey, (0, runtime_state_js_1.createWeixinTurnState)(input.sessionKey));
|
|
389
275
|
}
|
|
390
276
|
return () => {
|
|
391
277
|
if (previousRoute) {
|
|
@@ -488,52 +374,32 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
488
374
|
messageId: msg.messageId,
|
|
489
375
|
},
|
|
490
376
|
});
|
|
491
|
-
this.options.store.expirePendingPairings();
|
|
492
377
|
const binding = await this.getBinding(msg.workspaceId, instanceId);
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
509
|
-
this.notifyRuntimeStateChanged();
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
if (!authorized) {
|
|
513
|
-
const existingPending = this.options.store.listPendingPairings(msg.workspaceId).find((item) => item.platform === platformKey && item.platform_user_id === msg.platformUserId && item.chat_id === msg.chatId && item.status === 'pending');
|
|
514
|
-
let pairingCode = existingPending?.code || '';
|
|
515
|
-
if (!existingPending) {
|
|
516
|
-
const now = new Date();
|
|
517
|
-
pairingCode = String((0, node_crypto_2.randomInt)(100000, 1000000));
|
|
518
|
-
this.options.store.createPairingRequest({
|
|
519
|
-
code: pairingCode,
|
|
520
|
-
workspace_id: msg.workspaceId,
|
|
521
|
-
platform: platformKey,
|
|
522
|
-
platform_user_id: msg.platformUserId,
|
|
523
|
-
chat_id: msg.chatId,
|
|
524
|
-
display_name: msg.displayName,
|
|
525
|
-
requested_at: now.toISOString(),
|
|
526
|
-
expires_at: new Date(now.getTime() + PAIRING_EXPIRY_MS).toISOString(),
|
|
527
|
-
status: 'pending',
|
|
528
|
-
});
|
|
529
|
-
this.notifyRuntimeStateChanged();
|
|
530
|
-
}
|
|
378
|
+
const authorization = (0, inbound_authorization_js_1.resolveInboundChannelAuthorization)({
|
|
379
|
+
store: this.options.store,
|
|
380
|
+
identity: {
|
|
381
|
+
workspaceId: msg.workspaceId,
|
|
382
|
+
platformKey,
|
|
383
|
+
platformUserId: msg.platformUserId,
|
|
384
|
+
chatId: msg.chatId,
|
|
385
|
+
displayName: msg.displayName,
|
|
386
|
+
},
|
|
387
|
+
autoApprove: binding.allowFrom === '*',
|
|
388
|
+
authorizedUserIdPrefix: 'wx-user',
|
|
389
|
+
generatePairingCode: () => String((0, node_crypto_2.randomInt)(100000, 1000000)),
|
|
390
|
+
onStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
391
|
+
});
|
|
392
|
+
if (authorization.status === 'pending') {
|
|
531
393
|
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(msg.workspaceId, instanceId));
|
|
532
394
|
if (state?.connected) {
|
|
533
|
-
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${pairingCode}\``, msg.contextToken);
|
|
395
|
+
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${authorization.pairingCode}\``, msg.contextToken);
|
|
534
396
|
}
|
|
535
397
|
return;
|
|
536
398
|
}
|
|
399
|
+
if (authorization.autoApproved) {
|
|
400
|
+
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
401
|
+
}
|
|
402
|
+
const authorized = authorization.authorized;
|
|
537
403
|
const router = this.options.getWorkspaceRouter();
|
|
538
404
|
let { threadId } = await (0, thread_routing_js_1.resolveChannelThreadRoute)({
|
|
539
405
|
store: this.options.store,
|
|
@@ -584,10 +450,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
584
450
|
return;
|
|
585
451
|
}
|
|
586
452
|
this.options.store.updatePlatformThreadMessageId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.contextToken || msg.messageId, platformKey);
|
|
587
|
-
|
|
588
|
-
? msg.text
|
|
589
|
-
: (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(msg.text);
|
|
590
|
-
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(wrappedText, msg.contentParts));
|
|
453
|
+
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(msg.text, msg.contentParts));
|
|
591
454
|
return;
|
|
592
455
|
}
|
|
593
456
|
async resolveDefaultAgentType(workspaceId, threadId) {
|
|
@@ -634,7 +497,18 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
634
497
|
status.connectedAt = new Date().toISOString();
|
|
635
498
|
this.clearRuntimeError(status);
|
|
636
499
|
// Start long-poll loop in background
|
|
637
|
-
|
|
500
|
+
(0, inbound_poller_js_1.runWeixinInboundPoller)({
|
|
501
|
+
binding,
|
|
502
|
+
signal: abortController.signal,
|
|
503
|
+
getRuntimeState: () => this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId)),
|
|
504
|
+
getAuthorizedUser: (workspaceId, platformUserId, platformKey) => this.options.store.getAuthorizedUser(workspaceId, platformUserId, platformKey),
|
|
505
|
+
clearRuntimeError: (runtimeState) => this.clearRuntimeError(runtimeState),
|
|
506
|
+
setRuntimeError: (runtimeState, errorInfo) => this.setRuntimeError(runtimeState, errorInfo),
|
|
507
|
+
notifyRuntimeStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
508
|
+
downloadMediaItem: (item, messageId, index, uploadsDir, workspaceBinding) => this.downloadMediaItem(item, messageId, index, uploadsDir, workspaceBinding),
|
|
509
|
+
handleInboundMessage: (message) => this.handleInboundMessage(message),
|
|
510
|
+
log: this.options.log,
|
|
511
|
+
}).catch((err) => {
|
|
638
512
|
if (!abortController.signal.aborted) {
|
|
639
513
|
status.status = 'error';
|
|
640
514
|
status.connected = false;
|
|
@@ -661,127 +535,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
661
535
|
return 'expired';
|
|
662
536
|
return 'wait';
|
|
663
537
|
}
|
|
664
|
-
async runMonitorLoop(binding, signal) {
|
|
665
|
-
let buf = (0, config_js_1.loadWeixinBuf)(binding);
|
|
666
|
-
let consecutiveFailures = 0;
|
|
667
|
-
while (!signal.aborted) {
|
|
668
|
-
try {
|
|
669
|
-
const resp = await (0, transport_js_1.getWeixinUpdates)(binding, buf, signal);
|
|
670
|
-
const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
|
|
671
|
-
if (isApiError) {
|
|
672
|
-
consecutiveFailures++;
|
|
673
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
674
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
675
|
-
if (resp.errcode === -14 || resp.ret === -14) {
|
|
676
|
-
if (state) {
|
|
677
|
-
state.status = 'error';
|
|
678
|
-
state.connected = false;
|
|
679
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
680
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
681
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(new local_core_errors_js_1.LocalCoreError('channel_session_expired', 'WeChat login expired.')));
|
|
682
|
-
this.notifyRuntimeStateChanged();
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
this.logPollError(binding, `ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg || ''}`, `localcore-weixin getUpdates failed for ${binding.workspaceId}: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} (${consecutiveFailures})`);
|
|
686
|
-
if (state && resp.errcode !== -14 && resp.ret !== -14) {
|
|
687
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
688
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
689
|
-
}
|
|
690
|
-
await sleep(retryDelayMs, signal);
|
|
691
|
-
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
692
|
-
consecutiveFailures = MAX_CONSECUTIVE_FAILURES;
|
|
693
|
-
}
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
696
|
-
consecutiveFailures = 0;
|
|
697
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
698
|
-
if (state) {
|
|
699
|
-
this.clearRuntimeError(state);
|
|
700
|
-
state.status = 'running';
|
|
701
|
-
state.connected = true;
|
|
702
|
-
this.notifyRuntimeStateChanged();
|
|
703
|
-
}
|
|
704
|
-
if (resp.get_updates_buf) {
|
|
705
|
-
buf = resp.get_updates_buf;
|
|
706
|
-
(0, config_js_1.saveWeixinBuf)(binding, buf);
|
|
707
|
-
}
|
|
708
|
-
for (const msg of resp.msgs ?? []) {
|
|
709
|
-
const items = msg.item_list ?? [];
|
|
710
|
-
const textItem = items.find((i) => i.type === transport_js_1.TEXT_ITEM_TYPE);
|
|
711
|
-
const voiceTextItems = items.filter((i) => i.type === transport_js_1.VOICE_ITEM_TYPE && i.voice_item?.text);
|
|
712
|
-
const mediaItems = items.filter((i) => i.type === transport_js_1.IMAGE_ITEM_TYPE || i.type === transport_js_1.FILE_ITEM_TYPE);
|
|
713
|
-
if (!textItem && voiceTextItems.length === 0 && mediaItems.length === 0)
|
|
714
|
-
continue;
|
|
715
|
-
const conversationId = msg.from_user_id ?? '';
|
|
716
|
-
const text = [textItem?.text_item?.text?.trim(), ...voiceTextItems.map((i) => i.voice_item?.text?.trim())]
|
|
717
|
-
.filter((part) => Boolean(part))
|
|
718
|
-
.join('\n\n');
|
|
719
|
-
const msgId = msg.msg_id ?? String(Date.now());
|
|
720
|
-
// Handle attachments
|
|
721
|
-
let attachmentText = '';
|
|
722
|
-
const attachmentParts = [];
|
|
723
|
-
if (mediaItems.length > 0) {
|
|
724
|
-
const uploadsDir = node_path_1.default.join(binding.stateDir, 'weixin-uploads');
|
|
725
|
-
for (const [idx, item] of mediaItems.entries()) {
|
|
726
|
-
try {
|
|
727
|
-
const att = await this.downloadMediaItem(item, msgId, idx, uploadsDir, binding);
|
|
728
|
-
if (att) {
|
|
729
|
-
attachmentText += attachmentText ? '\n' : '';
|
|
730
|
-
attachmentText += att.kind === 'image' ? `[Image: ${att.path}]` : `[File "${att.name}": ${att.path}]`;
|
|
731
|
-
const part = createWeixinAttachmentContentPart(att);
|
|
732
|
-
if (part)
|
|
733
|
-
attachmentParts.push(part);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
catch (dlErr) {
|
|
737
|
-
this.options.log?.(`localcore-weixin attachment download failed (${conversationId}#${idx}): ${formatError(dlErr)}`);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
const fullText = [text, attachmentText].filter(Boolean).join('\n\n');
|
|
742
|
-
if (!fullText)
|
|
743
|
-
continue;
|
|
744
|
-
await this.handleInboundMessage({
|
|
745
|
-
workspaceId: binding.workspaceId,
|
|
746
|
-
instanceId: binding.instanceId,
|
|
747
|
-
platformKey: binding.platformKey,
|
|
748
|
-
platformUserId: conversationId,
|
|
749
|
-
chatId: conversationId,
|
|
750
|
-
displayName: conversationId.slice(-6),
|
|
751
|
-
text: fullText,
|
|
752
|
-
messageId: msgId,
|
|
753
|
-
contextToken: msg.context_token,
|
|
754
|
-
contentParts: [
|
|
755
|
-
...(text ? [{ type: 'text', text }] : []),
|
|
756
|
-
...attachmentParts,
|
|
757
|
-
],
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
catch (err) {
|
|
762
|
-
if (signal.aborted)
|
|
763
|
-
return;
|
|
764
|
-
consecutiveFailures++;
|
|
765
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
766
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
767
|
-
if (state) {
|
|
768
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
769
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
770
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(err));
|
|
771
|
-
this.notifyRuntimeStateChanged();
|
|
772
|
-
}
|
|
773
|
-
this.logPollError(binding, formatError(err), `localcore-weixin getUpdates error for ${binding.workspaceId} (${consecutiveFailures}): ${formatError(err)}`);
|
|
774
|
-
await sleep(retryDelayMs, signal);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
538
|
// ==================== Private: HTTP API ====================
|
|
779
539
|
async sendTextMessage(state, toUserId, text, contextToken, options = {}) {
|
|
780
540
|
const binding = await this.getBinding(state.workspaceId);
|
|
781
|
-
const stripped =
|
|
541
|
+
const stripped = (0, text_utils_js_1.stripWeixinHtml)(text);
|
|
782
542
|
const chunks = contextToken
|
|
783
|
-
? [truncateTextByUtf8Bytes(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
784
|
-
: splitTextByUtf8Bytes(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
543
|
+
? [(0, text_utils_js_1.truncateTextByUtf8Bytes)(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
544
|
+
: (0, text_utils_js_1.splitTextByUtf8Bytes)(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
785
545
|
for (const [index, chunk] of chunks.entries()) {
|
|
786
546
|
const finalChunk = options.final && index === chunks.length - 1;
|
|
787
547
|
const resp = await (0, transport_js_1.sendWeixinTextMessageChunk)(binding, toUserId, chunk, contextToken, {
|
|
@@ -789,7 +549,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
789
549
|
final: finalChunk,
|
|
790
550
|
});
|
|
791
551
|
if ((0, transport_js_1.isWeixinApiError)(resp)) {
|
|
792
|
-
throw new Error(`WeChat sendmessage failed: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} chunk=${index + 1}/${chunks.length} bytes=${utf8ByteLength(chunk)} context=${contextToken ? 'yes' : 'no'} message_state=${finalChunk ? 2 : 1}`);
|
|
552
|
+
throw new Error(`WeChat sendmessage failed: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} chunk=${index + 1}/${chunks.length} bytes=${(0, text_utils_js_1.utf8ByteLength)(chunk)} context=${contextToken ? 'yes' : 'no'} message_state=${finalChunk ? 2 : 1}`);
|
|
793
553
|
}
|
|
794
554
|
}
|
|
795
555
|
this.options.log?.(`localcore-weixin sent message to ${toUserId} for workspace ${state.workspaceId}${chunks.length > 1 ? ` chunks=${chunks.length}` : ''}`);
|
|
@@ -887,37 +647,58 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
887
647
|
aesKey = decoded.length === 16 ? decoded : decoded.length === 32 ? Buffer.from(decoded.toString('ascii'), 'hex') : undefined;
|
|
888
648
|
}
|
|
889
649
|
const cdnUrl = `${binding.cdnBaseUrl}/download?encrypted_query_param=${encodeURIComponent(encryptQueryParam)}`;
|
|
890
|
-
const resp = await fetch(cdnUrl, { signal: AbortSignal.timeout(30_000) });
|
|
891
|
-
if (!resp.ok)
|
|
892
|
-
throw new Error(`CDN HTTP ${resp.status}`);
|
|
893
|
-
const rawBuf = Buffer.from(await resp.arrayBuffer());
|
|
894
|
-
if (rawBuf.length === 0)
|
|
895
|
-
throw new Error('CDN returned empty data');
|
|
896
|
-
let resultBuf;
|
|
897
|
-
if (aesKey) {
|
|
898
|
-
const decipher = node_crypto_1.default.createDecipheriv('aes-128-ecb', aesKey, null);
|
|
899
|
-
decipher.setAutoPadding(true);
|
|
900
|
-
resultBuf = Buffer.concat([decipher.update(rawBuf), decipher.final()]);
|
|
901
|
-
}
|
|
902
|
-
else {
|
|
903
|
-
resultBuf = rawBuf;
|
|
904
|
-
}
|
|
905
|
-
const { ext, kind } = this.sniffExtAndKind(resultBuf);
|
|
906
650
|
const declaredName = String(itemData?.file_name ?? (item.type === transport_js_1.IMAGE_ITEM_TYPE ? 'image' : 'file'));
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
651
|
+
const stored = await this.inboundAttachmentStore.save({
|
|
652
|
+
directory: uploadsDir,
|
|
653
|
+
storedFileName: `${msgId}-${idx}-${declaredName}`,
|
|
654
|
+
displayFileName: declaredName,
|
|
655
|
+
maxBytes: WEIXIN_MAX_UPLOAD_FILE_SIZE,
|
|
656
|
+
includeBase64: ({ prefix }) => this.sniffExtAndKind(prefix).kind === 'image',
|
|
657
|
+
finalizeStoredFileName: ({ storedFileName, prefix }) => `${storedFileName}${this.sniffExtAndKind(prefix).ext}`,
|
|
658
|
+
source: {
|
|
659
|
+
open: async () => {
|
|
660
|
+
const resp = await fetch(cdnUrl, { signal: AbortSignal.timeout(30_000) });
|
|
661
|
+
if (!resp.ok)
|
|
662
|
+
throw new Error(`CDN HTTP ${resp.status}`);
|
|
663
|
+
if (!resp.body)
|
|
664
|
+
throw new Error('CDN returned no response body');
|
|
665
|
+
const rawStream = node_stream_1.Readable.fromWeb(resp.body);
|
|
666
|
+
if (!aesKey) {
|
|
667
|
+
return { stream: rawStream };
|
|
668
|
+
}
|
|
669
|
+
const decipher = node_crypto_1.default.createDecipheriv('aes-128-ecb', aesKey, null);
|
|
670
|
+
decipher.setAutoPadding(true);
|
|
671
|
+
return { stream: rawStream.pipe(decipher) };
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
});
|
|
675
|
+
const { ext, kind } = this.sniffExtAndKind(stored.prefix);
|
|
676
|
+
const uri = await this.resolveInboundAttachmentUri(binding, stored.path);
|
|
913
677
|
return {
|
|
914
|
-
path:
|
|
678
|
+
path: stored.path,
|
|
915
679
|
kind,
|
|
916
680
|
name: declaredName,
|
|
917
|
-
data:
|
|
681
|
+
data: stored.data,
|
|
918
682
|
mimeType: kind === 'image' ? this.mimeTypeForImageExt(ext) : undefined,
|
|
683
|
+
...(uri ? { uri } : {}),
|
|
919
684
|
};
|
|
920
685
|
}
|
|
686
|
+
async resolveInboundAttachmentUri(binding, filePath) {
|
|
687
|
+
const sandbox = binding.project?.agent?.options?.sandbox;
|
|
688
|
+
if (!sandbox?.enabled) {
|
|
689
|
+
return (0, inbound_attachment_store_js_1.resolveInboundAttachmentUri)({ filePath });
|
|
690
|
+
}
|
|
691
|
+
const workspace = await this.options.getWorkspaceRouter().getWorkspaceRegistryEntry(binding.workspaceId).catch(() => undefined);
|
|
692
|
+
if (!workspace) {
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
695
|
+
return (0, inbound_attachment_store_js_1.resolveInboundAttachmentUri)({
|
|
696
|
+
filePath,
|
|
697
|
+
workspacePath: workspace.path,
|
|
698
|
+
sandboxEnabled: true,
|
|
699
|
+
sandboxWorkspacePath: sandbox.workspace_mount_path,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
921
702
|
mimeTypeForImageExt(ext) {
|
|
922
703
|
if (ext === '.jpg')
|
|
923
704
|
return 'image/jpeg';
|
|
@@ -927,149 +708,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
927
708
|
return 'image/gif';
|
|
928
709
|
return 'application/octet-stream';
|
|
929
710
|
}
|
|
930
|
-
// ==================== Private: Turn State ====================
|
|
931
|
-
createTurnState(sessionKey) {
|
|
932
|
-
const turn = {
|
|
933
|
-
sessionKey,
|
|
934
|
-
sentCount: 0,
|
|
935
|
-
foldedProgressCount: 0,
|
|
936
|
-
awaitingPermission: false,
|
|
937
|
-
processing: false,
|
|
938
|
-
previewText: '',
|
|
939
|
-
finalText: '',
|
|
940
|
-
thinkingSteps: [],
|
|
941
|
-
pendingThoughtText: undefined,
|
|
942
|
-
statusLines: [],
|
|
943
|
-
buttonRows: [],
|
|
944
|
-
lastSentAt: 0,
|
|
945
|
-
lastSentText: '',
|
|
946
|
-
};
|
|
947
|
-
this.outboundTurns.set(sessionKey, turn);
|
|
948
|
-
return turn;
|
|
949
|
-
}
|
|
950
|
-
getOrCreateTurnState(sessionKey) {
|
|
951
|
-
return this.outboundTurns.get(sessionKey) || this.createTurnState(sessionKey);
|
|
952
|
-
}
|
|
953
|
-
consumeBridgeEvent(turn, event) {
|
|
954
|
-
const content = renderBridgeContentForWeixin(event);
|
|
955
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
956
|
-
if (event.type === 'typing_start') {
|
|
957
|
-
turn.processing = true;
|
|
958
|
-
turn.previewText = '';
|
|
959
|
-
turn.finalText = '';
|
|
960
|
-
turn.thinkingSteps = [];
|
|
961
|
-
turn.pendingThoughtText = undefined;
|
|
962
|
-
turn.statusLines = [];
|
|
963
|
-
turn.buttonRows = [];
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
if (event.type === 'typing_stop') {
|
|
967
|
-
turn.processing = false;
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
971
|
-
if (bridgeKind === 'thought') {
|
|
972
|
-
turn.pendingThoughtText = content;
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
turn.previewText = content;
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
if (bridgeKind !== 'thought') {
|
|
979
|
-
this.flushPendingThought(turn);
|
|
980
|
-
}
|
|
981
|
-
if (event.type === 'status') {
|
|
982
|
-
if (content) {
|
|
983
|
-
this.pushUnique(turn.statusLines, content);
|
|
984
|
-
turn.finalText = content;
|
|
985
|
-
turn.previewText = content;
|
|
986
|
-
}
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
if (event.type === 'buttons') {
|
|
990
|
-
turn.awaitingPermission = true;
|
|
991
|
-
turn.buttonRows = Array.isArray(event.buttonRows)
|
|
992
|
-
? event.buttonRows
|
|
993
|
-
.map((row) => Array.isArray(row)
|
|
994
|
-
? row.filter((b) => Boolean(b?.text && b?.data))
|
|
995
|
-
.map((b) => ({ text: b.text, data: b.data }))
|
|
996
|
-
: [])
|
|
997
|
-
.filter((row) => row.length > 0)
|
|
998
|
-
: [];
|
|
999
|
-
return;
|
|
1000
|
-
}
|
|
1001
|
-
if (!content)
|
|
1002
|
-
return;
|
|
1003
|
-
if (bridgeKind === 'thought' || bridgeKind === 'plan') {
|
|
1004
|
-
this.pushUnique(turn.thinkingSteps, content);
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
if (bridgeKind === 'tool' || bridgeKind === 'status') {
|
|
1008
|
-
this.pushUnique(turn.statusLines, content);
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
turn.finalText = content;
|
|
1012
|
-
turn.previewText = content;
|
|
1013
|
-
}
|
|
1014
|
-
renderTurnText(turn) {
|
|
1015
|
-
const sections = [];
|
|
1016
|
-
if (turn.thinkingSteps.length > 0) {
|
|
1017
|
-
sections.push(`**中间过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1018
|
-
}
|
|
1019
|
-
if (turn.finalText) {
|
|
1020
|
-
sections.push(turn.finalText);
|
|
1021
|
-
}
|
|
1022
|
-
else if (turn.previewText) {
|
|
1023
|
-
sections.push(turn.previewText);
|
|
1024
|
-
}
|
|
1025
|
-
else if (turn.statusLines.length > 0) {
|
|
1026
|
-
sections.push(`**处理中**\n${turn.statusLines.slice(-3).map((l) => `• ${l.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1027
|
-
}
|
|
1028
|
-
else if (turn.processing) {
|
|
1029
|
-
sections.push('**处理中**\n正在思考...');
|
|
1030
|
-
}
|
|
1031
|
-
if (turn.awaitingPermission) {
|
|
1032
|
-
sections.push('\n回复:`allow` / `allow all` / `deny`');
|
|
1033
|
-
}
|
|
1034
|
-
return sections.join('\n\n').trim();
|
|
1035
|
-
}
|
|
1036
|
-
flushPendingThought(turn) {
|
|
1037
|
-
const text = String(turn.pendingThoughtText || '').trim();
|
|
1038
|
-
if (!text)
|
|
1039
|
-
return;
|
|
1040
|
-
this.pushUnique(turn.thinkingSteps, text);
|
|
1041
|
-
turn.pendingThoughtText = undefined;
|
|
1042
|
-
}
|
|
1043
|
-
isTerminalBridgeMessage(event, rendered) {
|
|
1044
|
-
if (event.type === 'buttons')
|
|
1045
|
-
return true;
|
|
1046
|
-
if (event.type !== 'reply')
|
|
1047
|
-
return false;
|
|
1048
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
1049
|
-
if (bridgeKind === 'tool' || bridgeKind === 'thought' || bridgeKind === 'plan' || bridgeKind === 'status')
|
|
1050
|
-
return false;
|
|
1051
|
-
const normalized = rendered.trim();
|
|
1052
|
-
if (!normalized)
|
|
1053
|
-
return false;
|
|
1054
|
-
return true;
|
|
1055
|
-
}
|
|
1056
|
-
resolveBridgeEventKind(event) {
|
|
1057
|
-
if (event.bridgeKind) {
|
|
1058
|
-
return event.bridgeKind;
|
|
1059
|
-
}
|
|
1060
|
-
return event.type === 'status' ? 'status' : 'assistant';
|
|
1061
|
-
}
|
|
1062
|
-
// ==================== Private: Helpers ====================
|
|
1063
|
-
pushUnique(target, value) {
|
|
1064
|
-
const normalized = value.trim();
|
|
1065
|
-
if (!normalized)
|
|
1066
|
-
return;
|
|
1067
|
-
if (target[target.length - 1] === normalized)
|
|
1068
|
-
return;
|
|
1069
|
-
target.push(normalized);
|
|
1070
|
-
if (target.length > 8)
|
|
1071
|
-
target.splice(0, target.length - 8);
|
|
1072
|
-
}
|
|
1073
711
|
isDuplicateInboundMessage(input) {
|
|
1074
712
|
const messageKey = input.contextToken || input.messageId;
|
|
1075
713
|
if (!messageKey)
|
|
@@ -1104,29 +742,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
1104
742
|
return null;
|
|
1105
743
|
return { name: name.trim().toLowerCase(), args };
|
|
1106
744
|
}
|
|
1107
|
-
computeRetryDelay(failures) {
|
|
1108
|
-
if (failures <= 1)
|
|
1109
|
-
return RETRY_DELAY_MS;
|
|
1110
|
-
if (failures === 2)
|
|
1111
|
-
return 5_000;
|
|
1112
|
-
if (failures === 3)
|
|
1113
|
-
return 15_000;
|
|
1114
|
-
if (failures === 4)
|
|
1115
|
-
return 30_000;
|
|
1116
|
-
return 60_000;
|
|
1117
|
-
}
|
|
1118
|
-
logPollError(binding, errorKey, message) {
|
|
1119
|
-
const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
|
|
1120
|
-
const current = this.pollErrorLogWindows.get(key);
|
|
1121
|
-
const now = Date.now();
|
|
1122
|
-
if (!current || current.errorKey !== errorKey || now - current.at >= ERROR_LOG_WINDOW_MS) {
|
|
1123
|
-
this.pollErrorLogWindows.set(key, { at: now, count: 1, errorKey });
|
|
1124
|
-
this.options.log?.(message);
|
|
1125
|
-
return;
|
|
1126
|
-
}
|
|
1127
|
-
current.count += 1;
|
|
1128
|
-
this.pollErrorLogWindows.set(key, current);
|
|
1129
|
-
}
|
|
1130
745
|
}
|
|
1131
746
|
exports.LocalCoreWeixinGateway = LocalCoreWeixinGateway;
|
|
1132
747
|
function getWeixinInstanceId(platform) {
|