@kafca/agentdock 0.1.56 → 0.1.58
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 +7 -0
- package/dist/renderer/assets/{Badge-DjcoXx2J.js → Badge-BfY1VaHS.js} +1 -1
- package/dist/renderer/assets/Card-DWA_Tww_.js +1 -0
- package/dist/renderer/assets/Config-nHj3g1U5.js +21 -0
- package/dist/renderer/assets/CronList-BwkA_I7v.js +1 -0
- package/dist/renderer/assets/Dashboard-D0_A7k02.js +41 -0
- package/dist/renderer/assets/{EmptyState-DIZLiccW.js → EmptyState-zatDjLj_.js} +1 -1
- package/dist/renderer/assets/HighlightedMarkdown-CsM5n70d.js +12 -0
- package/dist/renderer/assets/Input-R1XdFK1K.js +1 -0
- package/dist/renderer/assets/{KnowledgeDetail-CJ9IYOsa.js → KnowledgeDetail-Co5sxeGd.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-BLGPwnBO.js → KnowledgeHome-DwziPw5m.js} +3 -3
- package/dist/renderer/assets/Logs-NwZUtR_G.js +6 -0
- package/dist/renderer/assets/{Modal-CQiUpfl4.js → Modal-CrcIwIFL.js} +3 -3
- package/dist/renderer/assets/MonitorList-CS1uLS5A.js +1 -0
- package/dist/renderer/assets/Page-Dp0obeSw.js +1 -0
- package/dist/renderer/assets/{Select-D6z65IT3.js → Select-6mVQaf84.js} +2 -2
- package/dist/renderer/assets/ThreadChat-BFzbn3L6.js +86 -0
- package/dist/renderer/assets/Workspace-DCmo8BMm.js +19 -0
- package/dist/renderer/assets/{arrow-left-CV5cL3f4.js → arrow-left-9rjg_Hp4.js} +1 -1
- package/dist/renderer/assets/{book-open-DThvB7pj.js → book-open-Bu8GCHnG.js} +1 -1
- package/dist/renderer/assets/channels-BxVbG-6j.js +6 -0
- package/dist/renderer/assets/chevron-down-BE8uQmWM.js +11 -0
- package/dist/renderer/assets/index-B7TeF6Es.js +41 -0
- package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
- package/dist/renderer/assets/index-BkI9POxj.js +167 -0
- package/dist/renderer/assets/knowledge-Kl19SqGS.js +1 -0
- package/dist/renderer/assets/{pencil-Dv5WxhdC.js → pencil-CPB7SK1m.js} +1 -1
- package/dist/renderer/assets/{play-DnCnft83.js → play-DGAO4OCu.js} +1 -1
- package/dist/renderer/assets/{plus-DsxIagzS.js → plus-Bt26z-4q.js} +1 -1
- package/dist/renderer/assets/{save-CST01lIc.js → save-Une7GMxT.js} +1 -1
- package/dist/renderer/assets/{search-B_bqy6Wd.js → search-DE0agJjJ.js} +1 -1
- package/dist/renderer/assets/{shield-check-D6lG5Aoc.js → shield-check-De7xaXZh.js} +1 -1
- package/dist/renderer/assets/threads-BnsNL5ei.js +1 -0
- package/dist/renderer/assets/{trash-2-Cefngb6Q.js → trash-2-6iGF59Ac.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 +5 -8
- 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/automation/automation-conversation-executor.js +6 -47
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +2 -5
- 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 +58 -539
- package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +112 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +5 -20
- package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +37 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/bridge-event-helpers.js +23 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/channel-keys.js +6 -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 +55 -512
- package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +149 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +92 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +9 -11
- 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 +8 -0
- 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/opensandbox-client.js +3 -2
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
- package/dist-electron/services/local-ai-core/src/scheduler/base-channel-schedule-adapter.js +77 -0
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +13 -79
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +2 -10
- package/dist-electron/services/local-ai-core/src/scheduler/run-polling.js +19 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -37
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +20 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +15 -28
- package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +12 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +13 -79
- 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-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 +4 -4
- package/dist-electron/tests/integration/local-core-refactor.test.js +15 -8
- 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-C5Bkm9-M.js +0 -2
- package/dist/renderer/assets/ChatMarkdown-uc36EzyD.js +0 -45
- package/dist/renderer/assets/Config-DHtQuuLy.js +0 -21
- package/dist/renderer/assets/CronList-BYUnhzSC.js +0 -1
- package/dist/renderer/assets/Dashboard-BPnGMDg3.js +0 -26
- package/dist/renderer/assets/Logs-Dyd2Uwlt.js +0 -1
- package/dist/renderer/assets/MonitorList-Byg3E3Gt.js +0 -1
- package/dist/renderer/assets/Page-7DxXvTMm.js +0 -1
- package/dist/renderer/assets/ProjectDetail-Dfr5s0Ja.js +0 -11
- package/dist/renderer/assets/ProjectList-CDDQjAQ_.js +0 -1
- package/dist/renderer/assets/SessionChat-s7CAaCPL.js +0 -1
- package/dist/renderer/assets/SessionList-D-OycKKS.js +0 -1
- package/dist/renderer/assets/ThreadChat-CLdFynB5.js +0 -28
- package/dist/renderer/assets/Workspace-xfT5z4j_.js +0 -19
- package/dist/renderer/assets/bot-CWQ5-ovl.js +0 -6
- package/dist/renderer/assets/check-DKM7zfWG.js +0 -6
- package/dist/renderer/assets/chevron-down-Dfw2KU87.js +0 -6
- package/dist/renderer/assets/circle-CoP4RA4s.js +0 -6
- package/dist/renderer/assets/desktop-CsDYrOOU.js +0 -1
- package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
- package/dist/renderer/assets/funnel-DpVLlG3g.js +0 -6
- package/dist/renderer/assets/heart-BMD36ejP.js +0 -6
- package/dist/renderer/assets/index-COxSYBjE.js +0 -182
- package/dist/renderer/assets/index-CTGTI-cm.js +0 -41
- package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
- package/dist/renderer/assets/projects-DgjHcNgt.js +0 -1
- package/dist/renderer/assets/server-ecH4GNwa.js +0 -11
- package/dist/renderer/assets/session-utils-Dd_WKMYW.js +0 -6
- package/dist/renderer/assets/sessions-BQ33Mv4k.js +0 -1
- package/dist/renderer/assets/user-D_8RSH-T.js +0 -6
- package/dist/renderer/assets/wifi-off-DB9D-pOu.js +0 -11
- package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +0 -13
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +0 -13
- 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,28 +3,28 @@ 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
10
|
const node_stream_1 = require("node:stream");
|
|
13
|
-
const desktop_js_1 = require("../../../../../shared/desktop.js");
|
|
14
11
|
const local_core_errors_js_1 = require("../../kernel/local-core-errors.js");
|
|
15
12
|
const content_js_1 = require("../shared/content.js");
|
|
16
13
|
const file_utils_js_1 = require("../shared/file-utils.js");
|
|
17
14
|
const inbound_attachment_store_js_1 = require("../shared/inbound-attachment-store.js");
|
|
18
15
|
const thread_routing_js_1 = require("../shared/thread-routing.js");
|
|
19
16
|
const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
|
|
17
|
+
const inbound_authorization_js_1 = require("../shared/inbound-authorization.js");
|
|
20
18
|
const channel_keys_js_1 = require("../shared/channel-keys.js");
|
|
19
|
+
const local_core_slash_commands_js_1 = require("../../acp/local-core-slash-commands.js");
|
|
21
20
|
const config_js_1 = require("./config.js");
|
|
22
21
|
const transport_js_1 = require("./transport.js");
|
|
22
|
+
const runtime_state_js_1 = require("./runtime-state.js");
|
|
23
|
+
const text_utils_js_1 = require("./text-utils.js");
|
|
24
|
+
const inbound_poller_js_1 = require("./inbound-poller.js");
|
|
25
|
+
var inbound_media_js_1 = require("./inbound-media.js");
|
|
26
|
+
Object.defineProperty(exports, "createWeixinAttachmentContentPart", { enumerable: true, get: function () { return inbound_media_js_1.createWeixinAttachmentContentPart; } });
|
|
23
27
|
// ==================== Constants ====================
|
|
24
|
-
const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
|
|
25
|
-
const RETRY_DELAY_MS = 2_000;
|
|
26
|
-
const BACKOFF_DELAY_MS = 30_000;
|
|
27
|
-
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
28
28
|
const PROCESSED_MESSAGE_TTL_MS = 10 * 60 * 1000;
|
|
29
29
|
const WEIXIN_TEXT_MESSAGE_MAX_BYTES = 900;
|
|
30
30
|
const WEIXIN_CONTEXT_REPLY_MAX_BYTES = 3500;
|
|
@@ -33,125 +33,9 @@ const WEIXIN_RESERVED_TERMINAL_SENDS = 1;
|
|
|
33
33
|
const WEIXIN_PROGRESS_SEND_BUDGET = WEIXIN_CONTEXT_SEND_LIMIT - WEIXIN_RESERVED_TERMINAL_SENDS;
|
|
34
34
|
const RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
35
35
|
const WEIXIN_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
|
|
36
|
-
const ERROR_LOG_WINDOW_MS = 5 * 60 * 1000;
|
|
37
|
-
// ==================== Utilities ====================
|
|
38
|
-
function stripHtml(html) {
|
|
39
|
-
let result = html;
|
|
40
|
-
let prev;
|
|
41
|
-
do {
|
|
42
|
-
prev = result;
|
|
43
|
-
result = result.replace(/<[^>]*>/g, '');
|
|
44
|
-
} while (result !== prev);
|
|
45
|
-
return result.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, ' ');
|
|
46
|
-
}
|
|
47
|
-
function formatError(err) {
|
|
48
|
-
if (err instanceof Error) {
|
|
49
|
-
const cause = err.cause;
|
|
50
|
-
return cause !== undefined ? `${err.message}: ${String(cause)}` : err.message;
|
|
51
|
-
}
|
|
52
|
-
return String(err);
|
|
53
|
-
}
|
|
54
|
-
function sleep(ms, signal) {
|
|
55
|
-
return new Promise((resolve, reject) => {
|
|
56
|
-
const t = setTimeout(resolve, ms);
|
|
57
|
-
const onAbort = () => { clearTimeout(t); reject(new Error('aborted')); };
|
|
58
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
function utf8ByteLength(value) {
|
|
62
|
-
return Buffer.byteLength(value, 'utf-8');
|
|
63
|
-
}
|
|
64
|
-
function splitTextByUtf8Bytes(text, maxBytes) {
|
|
65
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
66
|
-
if (!normalized)
|
|
67
|
-
return [];
|
|
68
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
69
|
-
return [normalized];
|
|
70
|
-
const chunks = [];
|
|
71
|
-
let current = '';
|
|
72
|
-
const pushCurrent = () => {
|
|
73
|
-
const trimmed = current.trim();
|
|
74
|
-
if (trimmed)
|
|
75
|
-
chunks.push(trimmed);
|
|
76
|
-
current = '';
|
|
77
|
-
};
|
|
78
|
-
const appendPart = (part) => {
|
|
79
|
-
if (!part.trim())
|
|
80
|
-
return;
|
|
81
|
-
const separator = current ? '\n\n' : '';
|
|
82
|
-
if (current && utf8ByteLength(`${current}${separator}${part}`) <= maxBytes) {
|
|
83
|
-
current = `${current}${separator}${part}`;
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (current)
|
|
87
|
-
pushCurrent();
|
|
88
|
-
if (utf8ByteLength(part) <= maxBytes) {
|
|
89
|
-
current = part;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
let segment = '';
|
|
93
|
-
for (const char of Array.from(part)) {
|
|
94
|
-
if (segment && utf8ByteLength(`${segment}${char}`) > maxBytes) {
|
|
95
|
-
chunks.push(segment);
|
|
96
|
-
segment = '';
|
|
97
|
-
}
|
|
98
|
-
segment += char;
|
|
99
|
-
}
|
|
100
|
-
current = segment;
|
|
101
|
-
};
|
|
102
|
-
for (const part of normalized.split(/\n{2,}/)) {
|
|
103
|
-
appendPart(part);
|
|
104
|
-
}
|
|
105
|
-
pushCurrent();
|
|
106
|
-
return chunks;
|
|
107
|
-
}
|
|
108
|
-
function createWeixinAttachmentContentPart(att) {
|
|
109
|
-
if (att.kind === 'image') {
|
|
110
|
-
if (!att.data)
|
|
111
|
-
return null;
|
|
112
|
-
return {
|
|
113
|
-
type: 'image',
|
|
114
|
-
data: att.data,
|
|
115
|
-
...(att.uri ? { uri: att.uri } : {}),
|
|
116
|
-
mimeType: att.mimeType,
|
|
117
|
-
fileName: att.name,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
type: 'file',
|
|
122
|
-
path: att.path,
|
|
123
|
-
fileName: att.name,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function truncateTextByUtf8Bytes(text, maxBytes) {
|
|
127
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
128
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
129
|
-
return normalized;
|
|
130
|
-
const suffix = '\n\n(内容过长,已截断以保证微信送达)';
|
|
131
|
-
const budget = Math.max(0, maxBytes - utf8ByteLength(suffix));
|
|
132
|
-
let result = '';
|
|
133
|
-
for (const char of Array.from(normalized)) {
|
|
134
|
-
if (utf8ByteLength(`${result}${char}`) > budget)
|
|
135
|
-
break;
|
|
136
|
-
result += char;
|
|
137
|
-
}
|
|
138
|
-
return `${result.trim()}${suffix}`;
|
|
139
|
-
}
|
|
140
|
-
function renderBridgeContentForWeixin(event) {
|
|
141
|
-
const toolCall = event.toolCall;
|
|
142
|
-
if (!toolCall) {
|
|
143
|
-
return String(event.content || '').trim();
|
|
144
|
-
}
|
|
145
|
-
const name = String(toolCall.name || '').trim() || 'Tool update';
|
|
146
|
-
const status = String(toolCall.status || '').trim();
|
|
147
|
-
if (name === 'Tool update' && status === 'completed')
|
|
148
|
-
return '';
|
|
149
|
-
return [name, status].filter(Boolean).join(' - ');
|
|
150
|
-
}
|
|
151
36
|
// ==================== Gateway Class ====================
|
|
152
37
|
class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGateway {
|
|
153
38
|
processedInboundMessages = new Map();
|
|
154
|
-
pollErrorLogWindows = new Map();
|
|
155
39
|
inboundAttachmentStore = new inbound_attachment_store_js_1.FileSystemInboundAttachmentStore();
|
|
156
40
|
platform = 'weixin';
|
|
157
41
|
constructor(options) {
|
|
@@ -172,7 +56,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
172
56
|
return (0, config_js_1.collectWeixinWorkspaceBindings)(config);
|
|
173
57
|
}
|
|
174
58
|
buildStatusObject(state, resolved) {
|
|
175
|
-
this.options.store.expirePendingPairings();
|
|
176
59
|
const platformKey = state.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', resolved.instanceId);
|
|
177
60
|
const pairings = this.options.store.listPendingPairings(state.workspaceId)
|
|
178
61
|
.filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
|
|
@@ -319,29 +202,26 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
319
202
|
&& event.type !== 'status') {
|
|
320
203
|
return;
|
|
321
204
|
}
|
|
322
|
-
const
|
|
323
|
-
const current = previous
|
|
324
|
-
.catch(() => undefined)
|
|
325
|
-
.then(async () => {
|
|
205
|
+
const current = this.scheduleOutboundChain(sessionKey, async () => {
|
|
326
206
|
const binding = this.options.store.getPlatformThreadBinding(route.workspaceId, route.chatId, route.platformUserId, routePlatformKey);
|
|
327
207
|
if (!binding)
|
|
328
208
|
return;
|
|
329
209
|
const bridgeThreadId = route.threadId || binding.thread_id;
|
|
330
210
|
if (this.mutedThreadBridgeCounts.has(bridgeThreadId))
|
|
331
211
|
return;
|
|
332
|
-
const turn = this.
|
|
212
|
+
const turn = (0, runtime_state_js_1.getOrCreateWeixinTurnState)(this.outboundTurns, sessionKey);
|
|
333
213
|
if (event.replyCtx) {
|
|
334
214
|
// WeChat doesn't support reply context; ignore
|
|
335
215
|
}
|
|
336
|
-
|
|
216
|
+
(0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, event);
|
|
337
217
|
if (event.type !== 'reply' && event.type !== 'buttons' && event.type !== 'status')
|
|
338
218
|
return;
|
|
339
|
-
const rendered =
|
|
219
|
+
const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
|
|
340
220
|
if (!rendered)
|
|
341
221
|
return;
|
|
342
222
|
if (rendered === turn.lastSentText)
|
|
343
223
|
return;
|
|
344
|
-
const terminalMessage =
|
|
224
|
+
const terminalMessage = (0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(event, rendered);
|
|
345
225
|
if (binding.last_platform_message_id && !terminalMessage && turn.sentCount >= WEIXIN_PROGRESS_SEND_BUDGET) {
|
|
346
226
|
turn.foldedProgressCount += 1;
|
|
347
227
|
this.options.log?.(`localcore-weixin folded progress for sessionKey=${sessionKey}: sent=${turn.sentCount} folded=${turn.foldedProgressCount}`);
|
|
@@ -364,19 +244,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
364
244
|
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'}`);
|
|
365
245
|
}
|
|
366
246
|
catch (error) {
|
|
367
|
-
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${
|
|
368
|
-
}
|
|
369
|
-
})
|
|
370
|
-
.finally(() => {
|
|
371
|
-
if (this.outboundEventChains.get(sessionKey) === current) {
|
|
372
|
-
this.outboundEventChains.delete(sessionKey);
|
|
247
|
+
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, local_core_errors_js_1.formatSafeError)(error)}`);
|
|
373
248
|
}
|
|
374
249
|
});
|
|
375
|
-
this.outboundEventChains.set(sessionKey, current);
|
|
376
250
|
await current;
|
|
377
251
|
}
|
|
378
252
|
registerScheduledThreadBridge(input) {
|
|
379
|
-
const instanceId = input.route.instanceId ||
|
|
253
|
+
const instanceId = input.route.instanceId || (0, channel_keys_js_1.extractChannelInstanceId)(input.platform, 'weixin') || 'default';
|
|
380
254
|
const platformKey = (0, channel_keys_js_1.channelPlatformKey)('weixin', instanceId);
|
|
381
255
|
const route = {
|
|
382
256
|
workspaceId: input.workspaceId,
|
|
@@ -389,7 +263,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
389
263
|
const previousRoute = this.threadRouting.get(input.sessionKey);
|
|
390
264
|
this.threadRouting.set(input.sessionKey, route);
|
|
391
265
|
if (!this.outboundTurns.has(input.sessionKey)) {
|
|
392
|
-
this.
|
|
266
|
+
this.outboundTurns.set(input.sessionKey, (0, runtime_state_js_1.createWeixinTurnState)(input.sessionKey));
|
|
393
267
|
}
|
|
394
268
|
return () => {
|
|
395
269
|
if (previousRoute) {
|
|
@@ -492,52 +366,32 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
492
366
|
messageId: msg.messageId,
|
|
493
367
|
},
|
|
494
368
|
});
|
|
495
|
-
this.options.store.expirePendingPairings();
|
|
496
369
|
const binding = await this.getBinding(msg.workspaceId, instanceId);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
513
|
-
this.notifyRuntimeStateChanged();
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
if (!authorized) {
|
|
517
|
-
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');
|
|
518
|
-
let pairingCode = existingPending?.code || '';
|
|
519
|
-
if (!existingPending) {
|
|
520
|
-
const now = new Date();
|
|
521
|
-
pairingCode = String((0, node_crypto_2.randomInt)(100000, 1000000));
|
|
522
|
-
this.options.store.createPairingRequest({
|
|
523
|
-
code: pairingCode,
|
|
524
|
-
workspace_id: msg.workspaceId,
|
|
525
|
-
platform: platformKey,
|
|
526
|
-
platform_user_id: msg.platformUserId,
|
|
527
|
-
chat_id: msg.chatId,
|
|
528
|
-
display_name: msg.displayName,
|
|
529
|
-
requested_at: now.toISOString(),
|
|
530
|
-
expires_at: new Date(now.getTime() + PAIRING_EXPIRY_MS).toISOString(),
|
|
531
|
-
status: 'pending',
|
|
532
|
-
});
|
|
533
|
-
this.notifyRuntimeStateChanged();
|
|
534
|
-
}
|
|
370
|
+
const authorization = (0, inbound_authorization_js_1.resolveInboundChannelAuthorization)({
|
|
371
|
+
store: this.options.store,
|
|
372
|
+
identity: {
|
|
373
|
+
workspaceId: msg.workspaceId,
|
|
374
|
+
platformKey,
|
|
375
|
+
platformUserId: msg.platformUserId,
|
|
376
|
+
chatId: msg.chatId,
|
|
377
|
+
displayName: msg.displayName,
|
|
378
|
+
},
|
|
379
|
+
autoApprove: binding.allowFrom === '*',
|
|
380
|
+
authorizedUserIdPrefix: 'wx-user',
|
|
381
|
+
generatePairingCode: () => String((0, node_crypto_2.randomInt)(100000, 1000000)),
|
|
382
|
+
onStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
383
|
+
});
|
|
384
|
+
if (authorization.status === 'pending') {
|
|
535
385
|
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(msg.workspaceId, instanceId));
|
|
536
386
|
if (state?.connected) {
|
|
537
|
-
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${pairingCode}\``, msg.contextToken);
|
|
387
|
+
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${authorization.pairingCode}\``, msg.contextToken);
|
|
538
388
|
}
|
|
539
389
|
return;
|
|
540
390
|
}
|
|
391
|
+
if (authorization.autoApproved) {
|
|
392
|
+
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
393
|
+
}
|
|
394
|
+
const authorized = authorization.authorized;
|
|
541
395
|
const router = this.options.getWorkspaceRouter();
|
|
542
396
|
let { threadId } = await (0, thread_routing_js_1.resolveChannelThreadRoute)({
|
|
543
397
|
store: this.options.store,
|
|
@@ -551,7 +405,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
551
405
|
authorized,
|
|
552
406
|
});
|
|
553
407
|
const normalizedText = String(msg.text || '').trim().toLowerCase();
|
|
554
|
-
const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny') ? this.findAwaitingPermissionThreadId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.platformKey) : '';
|
|
408
|
+
const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny') ? this.findAwaitingPermissionThreadId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.platformKey || 'weixin') : '';
|
|
555
409
|
if (permissionThreadId && permissionThreadId !== threadId) {
|
|
556
410
|
threadId = permissionThreadId;
|
|
557
411
|
}
|
|
@@ -565,7 +419,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
565
419
|
threadId,
|
|
566
420
|
});
|
|
567
421
|
// Handle slash commands
|
|
568
|
-
const slashCommand =
|
|
422
|
+
const slashCommand = (0, local_core_slash_commands_js_1.parseSlashCommand)(msg.text);
|
|
569
423
|
const sessionCommand = await this.executeSessionCommand({
|
|
570
424
|
workspaceId: msg.workspaceId,
|
|
571
425
|
currentThreadId: threadId,
|
|
@@ -588,19 +442,9 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
588
442
|
return;
|
|
589
443
|
}
|
|
590
444
|
this.options.store.updatePlatformThreadMessageId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.contextToken || msg.messageId, platformKey);
|
|
591
|
-
|
|
592
|
-
? msg.text
|
|
593
|
-
: (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(msg.text);
|
|
594
|
-
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(wrappedText, msg.contentParts));
|
|
445
|
+
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(msg.text, msg.contentParts));
|
|
595
446
|
return;
|
|
596
447
|
}
|
|
597
|
-
async resolveDefaultAgentType(workspaceId, threadId) {
|
|
598
|
-
const router = this.options.getWorkspaceRouter();
|
|
599
|
-
if (typeof router.getWorkspaceDefaultAgentType === 'function') {
|
|
600
|
-
return router.getWorkspaceDefaultAgentType(workspaceId);
|
|
601
|
-
}
|
|
602
|
-
return this.options.store.getThreadRow(threadId)?.agent_type || 'codex';
|
|
603
|
-
}
|
|
604
448
|
// ==================== Private: Bindings ====================
|
|
605
449
|
// ==================== Private: Workspace Lifecycle ====================
|
|
606
450
|
async startWorkspace(binding) {
|
|
@@ -638,7 +482,18 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
638
482
|
status.connectedAt = new Date().toISOString();
|
|
639
483
|
this.clearRuntimeError(status);
|
|
640
484
|
// Start long-poll loop in background
|
|
641
|
-
|
|
485
|
+
(0, inbound_poller_js_1.runWeixinInboundPoller)({
|
|
486
|
+
binding,
|
|
487
|
+
signal: abortController.signal,
|
|
488
|
+
getRuntimeState: () => this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId)),
|
|
489
|
+
getAuthorizedUser: (workspaceId, platformUserId, platformKey) => this.options.store.getAuthorizedUser(workspaceId, platformUserId, platformKey),
|
|
490
|
+
clearRuntimeError: (runtimeState) => this.clearRuntimeError(runtimeState),
|
|
491
|
+
setRuntimeError: (runtimeState, errorInfo) => this.setRuntimeError(runtimeState, errorInfo),
|
|
492
|
+
notifyRuntimeStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
493
|
+
downloadMediaItem: (item, messageId, index, uploadsDir, workspaceBinding) => this.downloadMediaItem(item, messageId, index, uploadsDir, workspaceBinding),
|
|
494
|
+
handleInboundMessage: (message) => this.handleInboundMessage(message),
|
|
495
|
+
log: this.options.log,
|
|
496
|
+
}).catch((err) => {
|
|
642
497
|
if (!abortController.signal.aborted) {
|
|
643
498
|
status.status = 'error';
|
|
644
499
|
status.connected = false;
|
|
@@ -665,136 +520,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
665
520
|
return 'expired';
|
|
666
521
|
return 'wait';
|
|
667
522
|
}
|
|
668
|
-
async runMonitorLoop(binding, signal) {
|
|
669
|
-
let buf = (0, config_js_1.loadWeixinBuf)(binding);
|
|
670
|
-
let consecutiveFailures = 0;
|
|
671
|
-
while (!signal.aborted) {
|
|
672
|
-
try {
|
|
673
|
-
const resp = await (0, transport_js_1.getWeixinUpdates)(binding, buf, signal);
|
|
674
|
-
const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
|
|
675
|
-
if (isApiError) {
|
|
676
|
-
consecutiveFailures++;
|
|
677
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
678
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
679
|
-
if (resp.errcode === -14 || resp.ret === -14) {
|
|
680
|
-
if (state) {
|
|
681
|
-
state.status = 'error';
|
|
682
|
-
state.connected = false;
|
|
683
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
684
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
685
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(new local_core_errors_js_1.LocalCoreError('channel_session_expired', 'WeChat login expired.')));
|
|
686
|
-
this.notifyRuntimeStateChanged();
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
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})`);
|
|
690
|
-
if (state && resp.errcode !== -14 && resp.ret !== -14) {
|
|
691
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
692
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
693
|
-
}
|
|
694
|
-
await sleep(retryDelayMs, signal);
|
|
695
|
-
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
696
|
-
consecutiveFailures = MAX_CONSECUTIVE_FAILURES;
|
|
697
|
-
}
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
consecutiveFailures = 0;
|
|
701
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
702
|
-
if (state) {
|
|
703
|
-
this.clearRuntimeError(state);
|
|
704
|
-
state.status = 'running';
|
|
705
|
-
state.connected = true;
|
|
706
|
-
this.notifyRuntimeStateChanged();
|
|
707
|
-
}
|
|
708
|
-
if (resp.get_updates_buf) {
|
|
709
|
-
buf = resp.get_updates_buf;
|
|
710
|
-
(0, config_js_1.saveWeixinBuf)(binding, buf);
|
|
711
|
-
}
|
|
712
|
-
for (const msg of resp.msgs ?? []) {
|
|
713
|
-
const items = msg.item_list ?? [];
|
|
714
|
-
const textItem = items.find((i) => i.type === transport_js_1.TEXT_ITEM_TYPE);
|
|
715
|
-
const voiceTextItems = items.filter((i) => i.type === transport_js_1.VOICE_ITEM_TYPE && i.voice_item?.text);
|
|
716
|
-
const mediaItems = items.filter((i) => i.type === transport_js_1.IMAGE_ITEM_TYPE || i.type === transport_js_1.FILE_ITEM_TYPE);
|
|
717
|
-
if (!textItem && voiceTextItems.length === 0 && mediaItems.length === 0)
|
|
718
|
-
continue;
|
|
719
|
-
const conversationId = msg.from_user_id ?? '';
|
|
720
|
-
const text = [textItem?.text_item?.text?.trim(), ...voiceTextItems.map((i) => i.voice_item?.text?.trim())]
|
|
721
|
-
.filter((part) => Boolean(part))
|
|
722
|
-
.join('\n\n');
|
|
723
|
-
const msgId = msg.msg_id ?? String(Date.now());
|
|
724
|
-
// Handle attachments
|
|
725
|
-
let attachmentText = '';
|
|
726
|
-
const attachmentParts = [];
|
|
727
|
-
if (mediaItems.length > 0) {
|
|
728
|
-
const uploadsDir = node_path_1.default.join(binding.stateDir, 'weixin-uploads');
|
|
729
|
-
const mayMaterializeAttachments = binding.allowFrom === '*'
|
|
730
|
-
|| Boolean(this.options.store.getAuthorizedUser(binding.workspaceId, conversationId, binding.platformKey));
|
|
731
|
-
for (const [idx, item] of mediaItems.entries()) {
|
|
732
|
-
const itemData = item.image_item ?? item.file_item ?? null;
|
|
733
|
-
const declaredName = String(itemData?.file_name ?? (item.type === transport_js_1.IMAGE_ITEM_TYPE ? 'image' : 'file'));
|
|
734
|
-
if (!mayMaterializeAttachments) {
|
|
735
|
-
attachmentText += attachmentText ? '\n' : '';
|
|
736
|
-
attachmentText += item.type === transport_js_1.IMAGE_ITEM_TYPE ? '[Image]' : `[File: ${declaredName}]`;
|
|
737
|
-
continue;
|
|
738
|
-
}
|
|
739
|
-
try {
|
|
740
|
-
const att = await this.downloadMediaItem(item, msgId, idx, uploadsDir, binding);
|
|
741
|
-
if (att) {
|
|
742
|
-
attachmentText += attachmentText ? '\n' : '';
|
|
743
|
-
attachmentText += att.kind === 'image' ? `[Image: ${att.path}]` : `[File "${att.name}": ${att.path}]`;
|
|
744
|
-
const part = createWeixinAttachmentContentPart(att);
|
|
745
|
-
if (part)
|
|
746
|
-
attachmentParts.push(part);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
catch (dlErr) {
|
|
750
|
-
this.options.log?.(`localcore-weixin attachment download failed (${conversationId}#${idx}): ${formatError(dlErr)}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
const fullText = [text, attachmentText].filter(Boolean).join('\n\n');
|
|
755
|
-
if (!fullText)
|
|
756
|
-
continue;
|
|
757
|
-
await this.handleInboundMessage({
|
|
758
|
-
workspaceId: binding.workspaceId,
|
|
759
|
-
instanceId: binding.instanceId,
|
|
760
|
-
platformKey: binding.platformKey,
|
|
761
|
-
platformUserId: conversationId,
|
|
762
|
-
chatId: conversationId,
|
|
763
|
-
displayName: conversationId.slice(-6),
|
|
764
|
-
text: fullText,
|
|
765
|
-
messageId: msgId,
|
|
766
|
-
contextToken: msg.context_token,
|
|
767
|
-
contentParts: [
|
|
768
|
-
...(text ? [{ type: 'text', text }] : []),
|
|
769
|
-
...attachmentParts,
|
|
770
|
-
],
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
catch (err) {
|
|
775
|
-
if (signal.aborted)
|
|
776
|
-
return;
|
|
777
|
-
consecutiveFailures++;
|
|
778
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
779
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
780
|
-
if (state) {
|
|
781
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
782
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
783
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(err));
|
|
784
|
-
this.notifyRuntimeStateChanged();
|
|
785
|
-
}
|
|
786
|
-
this.logPollError(binding, formatError(err), `localcore-weixin getUpdates error for ${binding.workspaceId} (${consecutiveFailures}): ${formatError(err)}`);
|
|
787
|
-
await sleep(retryDelayMs, signal);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
523
|
// ==================== Private: HTTP API ====================
|
|
792
524
|
async sendTextMessage(state, toUserId, text, contextToken, options = {}) {
|
|
793
525
|
const binding = await this.getBinding(state.workspaceId);
|
|
794
|
-
const stripped =
|
|
526
|
+
const stripped = (0, text_utils_js_1.stripWeixinHtml)(text);
|
|
795
527
|
const chunks = contextToken
|
|
796
|
-
? [truncateTextByUtf8Bytes(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
797
|
-
: splitTextByUtf8Bytes(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
528
|
+
? [(0, text_utils_js_1.truncateTextByUtf8Bytes)(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
529
|
+
: (0, text_utils_js_1.splitTextByUtf8Bytes)(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
798
530
|
for (const [index, chunk] of chunks.entries()) {
|
|
799
531
|
const finalChunk = options.final && index === chunks.length - 1;
|
|
800
532
|
const resp = await (0, transport_js_1.sendWeixinTextMessageChunk)(binding, toUserId, chunk, contextToken, {
|
|
@@ -802,7 +534,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
802
534
|
final: finalChunk,
|
|
803
535
|
});
|
|
804
536
|
if ((0, transport_js_1.isWeixinApiError)(resp)) {
|
|
805
|
-
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}`);
|
|
537
|
+
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}`);
|
|
806
538
|
}
|
|
807
539
|
}
|
|
808
540
|
this.options.log?.(`localcore-weixin sent message to ${toUserId} for workspace ${state.workspaceId}${chunks.length > 1 ? ` chunks=${chunks.length}` : ''}`);
|
|
@@ -961,149 +693,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
961
693
|
return 'image/gif';
|
|
962
694
|
return 'application/octet-stream';
|
|
963
695
|
}
|
|
964
|
-
// ==================== Private: Turn State ====================
|
|
965
|
-
createTurnState(sessionKey) {
|
|
966
|
-
const turn = {
|
|
967
|
-
sessionKey,
|
|
968
|
-
sentCount: 0,
|
|
969
|
-
foldedProgressCount: 0,
|
|
970
|
-
awaitingPermission: false,
|
|
971
|
-
processing: false,
|
|
972
|
-
previewText: '',
|
|
973
|
-
finalText: '',
|
|
974
|
-
thinkingSteps: [],
|
|
975
|
-
pendingThoughtText: undefined,
|
|
976
|
-
statusLines: [],
|
|
977
|
-
buttonRows: [],
|
|
978
|
-
lastSentAt: 0,
|
|
979
|
-
lastSentText: '',
|
|
980
|
-
};
|
|
981
|
-
this.outboundTurns.set(sessionKey, turn);
|
|
982
|
-
return turn;
|
|
983
|
-
}
|
|
984
|
-
getOrCreateTurnState(sessionKey) {
|
|
985
|
-
return this.outboundTurns.get(sessionKey) || this.createTurnState(sessionKey);
|
|
986
|
-
}
|
|
987
|
-
consumeBridgeEvent(turn, event) {
|
|
988
|
-
const content = renderBridgeContentForWeixin(event);
|
|
989
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
990
|
-
if (event.type === 'typing_start') {
|
|
991
|
-
turn.processing = true;
|
|
992
|
-
turn.previewText = '';
|
|
993
|
-
turn.finalText = '';
|
|
994
|
-
turn.thinkingSteps = [];
|
|
995
|
-
turn.pendingThoughtText = undefined;
|
|
996
|
-
turn.statusLines = [];
|
|
997
|
-
turn.buttonRows = [];
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
if (event.type === 'typing_stop') {
|
|
1001
|
-
turn.processing = false;
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
1005
|
-
if (bridgeKind === 'thought') {
|
|
1006
|
-
turn.pendingThoughtText = content;
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
|
-
turn.previewText = content;
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
if (bridgeKind !== 'thought') {
|
|
1013
|
-
this.flushPendingThought(turn);
|
|
1014
|
-
}
|
|
1015
|
-
if (event.type === 'status') {
|
|
1016
|
-
if (content) {
|
|
1017
|
-
this.pushUnique(turn.statusLines, content);
|
|
1018
|
-
turn.finalText = content;
|
|
1019
|
-
turn.previewText = content;
|
|
1020
|
-
}
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
if (event.type === 'buttons') {
|
|
1024
|
-
turn.awaitingPermission = true;
|
|
1025
|
-
turn.buttonRows = Array.isArray(event.buttonRows)
|
|
1026
|
-
? event.buttonRows
|
|
1027
|
-
.map((row) => Array.isArray(row)
|
|
1028
|
-
? row.filter((b) => Boolean(b?.text && b?.data))
|
|
1029
|
-
.map((b) => ({ text: b.text, data: b.data }))
|
|
1030
|
-
: [])
|
|
1031
|
-
.filter((row) => row.length > 0)
|
|
1032
|
-
: [];
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
if (!content)
|
|
1036
|
-
return;
|
|
1037
|
-
if (bridgeKind === 'thought' || bridgeKind === 'plan') {
|
|
1038
|
-
this.pushUnique(turn.thinkingSteps, content);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (bridgeKind === 'tool' || bridgeKind === 'status') {
|
|
1042
|
-
this.pushUnique(turn.statusLines, content);
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
turn.finalText = content;
|
|
1046
|
-
turn.previewText = content;
|
|
1047
|
-
}
|
|
1048
|
-
renderTurnText(turn) {
|
|
1049
|
-
const sections = [];
|
|
1050
|
-
if (turn.thinkingSteps.length > 0) {
|
|
1051
|
-
sections.push(`**中间过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1052
|
-
}
|
|
1053
|
-
if (turn.finalText) {
|
|
1054
|
-
sections.push(turn.finalText);
|
|
1055
|
-
}
|
|
1056
|
-
else if (turn.previewText) {
|
|
1057
|
-
sections.push(turn.previewText);
|
|
1058
|
-
}
|
|
1059
|
-
else if (turn.statusLines.length > 0) {
|
|
1060
|
-
sections.push(`**处理中**\n${turn.statusLines.slice(-3).map((l) => `• ${l.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1061
|
-
}
|
|
1062
|
-
else if (turn.processing) {
|
|
1063
|
-
sections.push('**处理中**\n正在思考...');
|
|
1064
|
-
}
|
|
1065
|
-
if (turn.awaitingPermission) {
|
|
1066
|
-
sections.push('\n回复:`allow` / `allow all` / `deny`');
|
|
1067
|
-
}
|
|
1068
|
-
return sections.join('\n\n').trim();
|
|
1069
|
-
}
|
|
1070
|
-
flushPendingThought(turn) {
|
|
1071
|
-
const text = String(turn.pendingThoughtText || '').trim();
|
|
1072
|
-
if (!text)
|
|
1073
|
-
return;
|
|
1074
|
-
this.pushUnique(turn.thinkingSteps, text);
|
|
1075
|
-
turn.pendingThoughtText = undefined;
|
|
1076
|
-
}
|
|
1077
|
-
isTerminalBridgeMessage(event, rendered) {
|
|
1078
|
-
if (event.type === 'buttons')
|
|
1079
|
-
return true;
|
|
1080
|
-
if (event.type !== 'reply')
|
|
1081
|
-
return false;
|
|
1082
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
1083
|
-
if (bridgeKind === 'tool' || bridgeKind === 'thought' || bridgeKind === 'plan' || bridgeKind === 'status')
|
|
1084
|
-
return false;
|
|
1085
|
-
const normalized = rendered.trim();
|
|
1086
|
-
if (!normalized)
|
|
1087
|
-
return false;
|
|
1088
|
-
return true;
|
|
1089
|
-
}
|
|
1090
|
-
resolveBridgeEventKind(event) {
|
|
1091
|
-
if (event.bridgeKind) {
|
|
1092
|
-
return event.bridgeKind;
|
|
1093
|
-
}
|
|
1094
|
-
return event.type === 'status' ? 'status' : 'assistant';
|
|
1095
|
-
}
|
|
1096
|
-
// ==================== Private: Helpers ====================
|
|
1097
|
-
pushUnique(target, value) {
|
|
1098
|
-
const normalized = value.trim();
|
|
1099
|
-
if (!normalized)
|
|
1100
|
-
return;
|
|
1101
|
-
if (target[target.length - 1] === normalized)
|
|
1102
|
-
return;
|
|
1103
|
-
target.push(normalized);
|
|
1104
|
-
if (target.length > 8)
|
|
1105
|
-
target.splice(0, target.length - 8);
|
|
1106
|
-
}
|
|
1107
696
|
isDuplicateInboundMessage(input) {
|
|
1108
697
|
const messageKey = input.contextToken || input.messageId;
|
|
1109
698
|
if (!messageKey)
|
|
@@ -1119,51 +708,5 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
1119
708
|
this.processedInboundMessages.set(key, now + PROCESSED_MESSAGE_TTL_MS);
|
|
1120
709
|
return false;
|
|
1121
710
|
}
|
|
1122
|
-
findAwaitingPermissionThreadId(workspaceId, chatId, platformUserId, platformKey = 'weixin') {
|
|
1123
|
-
for (const [sessionKey, route] of this.threadRouting.entries()) {
|
|
1124
|
-
if (route.workspaceId !== workspaceId || route.platformKey !== platformKey || route.chatId !== chatId || route.platformUserId !== platformUserId)
|
|
1125
|
-
continue;
|
|
1126
|
-
const turn = this.outboundTurns.get(sessionKey);
|
|
1127
|
-
if (turn?.awaitingPermission && route.threadId)
|
|
1128
|
-
return route.threadId;
|
|
1129
|
-
}
|
|
1130
|
-
return '';
|
|
1131
|
-
}
|
|
1132
|
-
parseSlashCommand(text) {
|
|
1133
|
-
const normalized = String(text || '').trim();
|
|
1134
|
-
if (!normalized.startsWith('/'))
|
|
1135
|
-
return null;
|
|
1136
|
-
const [name = '', ...args] = normalized.slice(1).split(/\s+/);
|
|
1137
|
-
if (!name)
|
|
1138
|
-
return null;
|
|
1139
|
-
return { name: name.trim().toLowerCase(), args };
|
|
1140
|
-
}
|
|
1141
|
-
computeRetryDelay(failures) {
|
|
1142
|
-
if (failures <= 1)
|
|
1143
|
-
return RETRY_DELAY_MS;
|
|
1144
|
-
if (failures === 2)
|
|
1145
|
-
return 5_000;
|
|
1146
|
-
if (failures === 3)
|
|
1147
|
-
return 15_000;
|
|
1148
|
-
if (failures === 4)
|
|
1149
|
-
return 30_000;
|
|
1150
|
-
return 60_000;
|
|
1151
|
-
}
|
|
1152
|
-
logPollError(binding, errorKey, message) {
|
|
1153
|
-
const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
|
|
1154
|
-
const current = this.pollErrorLogWindows.get(key);
|
|
1155
|
-
const now = Date.now();
|
|
1156
|
-
if (!current || current.errorKey !== errorKey || now - current.at >= ERROR_LOG_WINDOW_MS) {
|
|
1157
|
-
this.pollErrorLogWindows.set(key, { at: now, count: 1, errorKey });
|
|
1158
|
-
this.options.log?.(message);
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
current.count += 1;
|
|
1162
|
-
this.pollErrorLogWindows.set(key, current);
|
|
1163
|
-
}
|
|
1164
711
|
}
|
|
1165
712
|
exports.LocalCoreWeixinGateway = LocalCoreWeixinGateway;
|
|
1166
|
-
function getWeixinInstanceId(platform) {
|
|
1167
|
-
const normalized = String(platform || '').trim();
|
|
1168
|
-
return normalized.startsWith('weixin:') ? normalized.slice('weixin:'.length).trim() : '';
|
|
1169
|
-
}
|