@kafca/agentdock 0.1.53 → 0.1.55

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.
Files changed (65) hide show
  1. package/README.md +5 -0
  2. package/dist/renderer/assets/{Badge-pBiMqECO.js → Badge-Bg_nNvBL.js} +1 -1
  3. package/dist/renderer/assets/{Chat-pQVBr82B.js → Chat-BJveQQKu.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-SCxRo6-e.js → ChatMarkdown-Bqn2HJ6f.js} +1 -1
  5. package/dist/renderer/assets/{Config-DRtyF20X.js → Config-CMtZDRAE.js} +1 -1
  6. package/dist/renderer/assets/{CronList-BA5hVrYp.js → CronList-C-ILB_Ae.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-p3yI0Fck.js → Dashboard-CsKBSQIe.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-Dgh25c2x.js → EmptyState-C60g3RMj.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-CuC3Cjg1.js → KnowledgeDetail-CC5C7JYe.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CH4GqtEZ.js → KnowledgeHome-3rlvxgWf.js} +1 -1
  11. package/dist/renderer/assets/{Logs-BQrm2cq-.js → Logs-CktemGN0.js} +1 -1
  12. package/dist/renderer/assets/{Modal-DfZsUS3Q.js → Modal-BQUW9LSf.js} +1 -1
  13. package/dist/renderer/assets/{MonitorList-CDWGIf1Q.js → MonitorList-B0hxjuOZ.js} +1 -1
  14. package/dist/renderer/assets/{Page-CpmZ134Z.js → Page-DL_Nv2i5.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-B-Kwqodz.js → ProjectDetail-Djr9-MyT.js} +1 -1
  16. package/dist/renderer/assets/{ProjectList-DtNT6dv-.js → ProjectList-BKl4eW7q.js} +1 -1
  17. package/dist/renderer/assets/{Select--_s_a1uj.js → Select-CfRkos3z.js} +1 -1
  18. package/dist/renderer/assets/{SessionChat-BHfKpufe.js → SessionChat-Dxv8D5DW.js} +1 -1
  19. package/dist/renderer/assets/{SessionList-B0kQz-Ms.js → SessionList-BJ2GjXvR.js} +1 -1
  20. package/dist/renderer/assets/{ThreadChat-CRb6Z42k.js → ThreadChat-CAsIl9qf.js} +1 -1
  21. package/dist/renderer/assets/Workspace-CaUhiGBX.js +19 -0
  22. package/dist/renderer/assets/{arrow-left-DnhnG-Tj.js → arrow-left-D1DbXrLA.js} +1 -1
  23. package/dist/renderer/assets/{book-open-Dgp3xcUz.js → book-open-BxC1z1C1.js} +1 -1
  24. package/dist/renderer/assets/{bot-BollztzX.js → bot-Fw3IzhJA.js} +1 -1
  25. package/dist/renderer/assets/{check-BHj-iSsR.js → check-5zCTgp3A.js} +1 -1
  26. package/dist/renderer/assets/{chevron-down-CeXCREMg.js → chevron-down-N3PxTNxH.js} +1 -1
  27. package/dist/renderer/assets/{circle-B3eGQ-H3.js → circle-C_chUflo.js} +1 -1
  28. package/dist/renderer/assets/{desktop-B5cYIqJH.js → desktop-DkQTZNhe.js} +1 -1
  29. package/dist/renderer/assets/{funnel-BkgiQrZC.js → funnel-B8ZAv6I7.js} +1 -1
  30. package/dist/renderer/assets/{heart-C-H_BPOT.js → heart-M9A3E4Ed.js} +1 -1
  31. package/dist/renderer/assets/{index-C97cND_H.js → index-Bwv855ID.js} +1 -1
  32. package/dist/renderer/assets/{index-CYaol0bB.js → index-DJ8w4e0l.js} +2 -2
  33. package/dist/renderer/assets/{pencil-DBHjVmSd.js → pencil-X44UaxwR.js} +1 -1
  34. package/dist/renderer/assets/{play-Cu9TIeSO.js → play-ujo-De38.js} +1 -1
  35. package/dist/renderer/assets/{plus-PaUGBkMK.js → plus-BHgVdG_A.js} +1 -1
  36. package/dist/renderer/assets/projects-DB1FLZ3A.js +1 -0
  37. package/dist/renderer/assets/{save-dm5NifZE.js → save-Cz6giKrE.js} +1 -1
  38. package/dist/renderer/assets/{search-Cq8cwrp6.js → search-DKWvTqi5.js} +1 -1
  39. package/dist/renderer/assets/{server-DwiMsKFq.js → server-BsuHcYl4.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-CD2tqK35.js → session-utils-DnAWL3eo.js} +1 -1
  41. package/dist/renderer/assets/{sessions-quZ-oFVW.js → sessions-DptxO_BE.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-B9UsjycW.js → shield-check-V82O8AC1.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-CitZGPQS.js → trash-2-_Fgwk8mc.js} +1 -1
  44. package/dist/renderer/assets/{user-BgVotGiF.js → user-DQKTaUcR.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-CMgxdgfX.js → wifi-off-vjH-MD2w.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +1 -1
  48. package/dist-electron/services/local-ai-core/src/acp/store/runtime-config-store.js +39 -28
  49. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +4 -16
  50. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +72 -13
  51. package/dist-electron/services/local-ai-core/src/channel/shared/channel-keys.js +16 -0
  52. package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +3 -16
  53. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +12 -11
  54. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +1 -1
  55. package/dist-electron/services/local-ai-core/src/runtime/channel-service.js +0 -40
  56. package/dist-electron/services/local-ai-core/src/runtime/external-service.js +1 -14
  57. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -14
  58. package/dist-electron/services/local-ai-core/src/runtime/provider-config-migration.js +16 -0
  59. package/dist-electron/services/local-ai-core/src/scheduler/channel-execution-policy.js +12 -10
  60. package/dist-electron/tests/electron/sandbox-config.test.js +5 -0
  61. package/dist-electron/tests/integration/lark-gateway.test.js +27 -1
  62. package/dist-electron/tests/integration/local-core-refactor.test.js +51 -2
  63. package/package.json +1 -1
  64. package/dist/renderer/assets/Workspace-j-O-14yC.js +0 -19
  65. package/dist/renderer/assets/projects-BmW4zbEE.js +0 -1
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-CYaol0bB.js";/**
1
+ import{c as a}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as o}from"./index-CYaol0bB.js";/**
1
+ import{c as o}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-CYaol0bB.js";/**
1
+ import{c as e}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -0,0 +1 @@
1
+ import{D as t}from"./index-DJ8w4e0l.js";const e=()=>t.get("/projects"),r=o=>t.get(`/projects/${o}`);export{r as g,e as l};
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-CYaol0bB.js";/**
1
+ import{c as a}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-CYaol0bB.js";/**
1
+ import{c}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-CYaol0bB.js";/**
1
+ import{c as e}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as o}from"./index-CYaol0bB.js";/**
1
+ import{c as o}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{D as o}from"./index-CYaol0bB.js";const i=s=>o.get(`/projects/${s}/sessions`),c=(s,e,t)=>o.get(`/projects/${s}/sessions/${e}`,{history_limit:String(t)}),r=(s,e,t)=>o.patch(`/projects/${s}/sessions/${e}`,t),a=(s,e)=>o.delete(`/projects/${s}/sessions/${e}`),p=(s,e)=>o.post(`/projects/${s}/sessions/switch`,e),$=(s,e)=>o.post(`/projects/${s}/send`,e);export{$ as a,a as d,c as g,i as l,r,p as s};
1
+ import{D as o}from"./index-DJ8w4e0l.js";const i=s=>o.get(`/projects/${s}/sessions`),c=(s,e,t)=>o.get(`/projects/${s}/sessions/${e}`,{history_limit:String(t)}),r=(s,e,t)=>o.patch(`/projects/${s}/sessions/${e}`,t),a=(s,e)=>o.delete(`/projects/${s}/sessions/${e}`),p=(s,e)=>o.post(`/projects/${s}/sessions/switch`,e),$=(s,e)=>o.post(`/projects/${s}/send`,e);export{$ as a,a as d,c as g,i as l,r,p as s};
@@ -1,4 +1,4 @@
1
- import{c}from"./index-CYaol0bB.js";/**
1
+ import{c}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-CYaol0bB.js";/**
1
+ import{c as e}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-CYaol0bB.js";/**
1
+ import{c}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-CYaol0bB.js";/**
1
+ import{c as a}from"./index-DJ8w4e0l.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="./favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>AgentDock</title>
8
- <script type="module" crossorigin src="./assets/index-CYaol0bB.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-DJ8w4e0l.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="./assets/index-CjCuR6DI.css">
10
10
  </head>
11
11
  <body class="bg-[#f5f5f7] dark:bg-[#151516]">
@@ -47,7 +47,7 @@ class LocalCoreAcpStore {
47
47
  this.modelProviders = new model_provider_store_js_1.LocalModelProviderStore(this.db);
48
48
  this.external = new external_store_js_1.LocalExternalStore(this.db);
49
49
  (0, schema_js_1.ensureLocalCoreAcpSchema)(this.db);
50
- this.runtimeConfig = new runtime_config_store_js_1.LocalRuntimeConfigStore(this.db, dbPath, runtimeDir, resolveLegacyConfigPaths(runtimeDir));
50
+ this.runtimeConfig = new runtime_config_store_js_1.LocalRuntimeConfigStore(this.db, dbPath, resolveLegacyConfigPaths(runtimeDir));
51
51
  }
52
52
  close() {
53
53
  this.db.close();
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.LocalRuntimeConfigStore = void 0;
37
37
  const node_fs_1 = require("node:fs");
38
+ const node_path_1 = require("node:path");
38
39
  const TOML = __importStar(require("@iarna/toml"));
39
40
  const config_migration_js_1 = require("../../runtime/config-migration.js");
40
41
  const utils_js_1 = require("./utils.js");
@@ -42,21 +43,38 @@ const RUNTIME_CONFIG_ID = 'desktop';
42
43
  class LocalRuntimeConfigStore {
43
44
  db;
44
45
  databasePath;
45
- baseDir;
46
46
  legacyConfigPaths;
47
- constructor(db, databasePath, baseDir, legacyConfigPaths) {
47
+ baseDir;
48
+ selectStmt;
49
+ insertStmt;
50
+ updateStmt;
51
+ constructor(db, databasePath, legacyConfigPaths) {
48
52
  this.db = db;
49
53
  this.databasePath = databasePath;
50
- this.baseDir = baseDir;
51
54
  this.legacyConfigPaths = legacyConfigPaths;
55
+ this.baseDir = (0, node_path_1.dirname)(databasePath);
56
+ this.selectStmt = db.prepare(`
57
+ SELECT config_json, base_dir, migrated_from_path, updated_at
58
+ FROM runtime_config
59
+ WHERE id = ?
60
+ `);
61
+ this.insertStmt = db.prepare(`
62
+ INSERT INTO runtime_config (id, config_json, base_dir, migrated_from_path, created_at, updated_at)
63
+ VALUES (?, ?, ?, ?, ?, ?)
64
+ `);
65
+ this.updateStmt = db.prepare(`
66
+ UPDATE runtime_config
67
+ SET config_json = ?, base_dir = ?, migrated_from_path = ?, updated_at = ?
68
+ WHERE id = ?
69
+ `);
52
70
  }
53
71
  read() {
54
- const row = this.getRow();
72
+ const row = this.selectStmt.get(RUNTIME_CONFIG_ID);
55
73
  if (row) {
56
74
  const config = (0, utils_js_1.parseJson)(row.config_json, {});
57
75
  const migrated = (0, config_migration_js_1.migrateDesktopConnectConfig)(config);
58
76
  if (migrated.changed) {
59
- return this.save(migrated.config, {
77
+ return this.writeRow(migrated.config, {
60
78
  migratedFromPath: row.migrated_from_path || undefined,
61
79
  warnings: migrated.warnings,
62
80
  });
@@ -68,7 +86,7 @@ class LocalRuntimeConfigStore {
68
86
  if ('error' in legacy) {
69
87
  return this.errorState(legacy.path, legacy.error);
70
88
  }
71
- return this.save(legacy.config, {
89
+ return this.writeRow(legacy.config, {
72
90
  migratedFromPath: legacy.path,
73
91
  warnings: legacy.warnings,
74
92
  });
@@ -77,42 +95,35 @@ class LocalRuntimeConfigStore {
77
95
  }
78
96
  save(config, options = {}) {
79
97
  const migrated = (0, config_migration_js_1.migrateDesktopConnectConfig)(config);
80
- const row = this.getRow();
98
+ return this.writeRow(migrated.config, {
99
+ migratedFromPath: options.migratedFromPath,
100
+ warnings: [
101
+ ...(options.warnings || []),
102
+ ...migrated.warnings,
103
+ ],
104
+ });
105
+ }
106
+ writeRow(config, options = {}) {
107
+ const row = this.selectStmt.get(RUNTIME_CONFIG_ID);
81
108
  const now = new Date().toISOString();
82
109
  const migratedFromPath = options.migratedFromPath ?? row?.migrated_from_path ?? null;
110
+ const configJson = JSON.stringify(config);
83
111
  if (row) {
84
- this.db.prepare(`
85
- UPDATE runtime_config
86
- SET config_json = ?, base_dir = ?, migrated_from_path = ?, updated_at = ?
87
- WHERE id = ?
88
- `).run(JSON.stringify(migrated.config), this.baseDir, migratedFromPath, now, RUNTIME_CONFIG_ID);
112
+ this.updateStmt.run(configJson, this.baseDir, migratedFromPath, now, RUNTIME_CONFIG_ID);
89
113
  }
90
114
  else {
91
- this.db.prepare(`
92
- INSERT INTO runtime_config (id, config_json, base_dir, migrated_from_path, created_at, updated_at)
93
- VALUES (?, ?, ?, ?, ?, ?)
94
- `).run(RUNTIME_CONFIG_ID, JSON.stringify(migrated.config), this.baseDir, migratedFromPath, now, now);
115
+ this.insertStmt.run(RUNTIME_CONFIG_ID, configJson, this.baseDir, migratedFromPath, now, now);
95
116
  }
96
117
  return {
97
118
  storage: 'sqlite',
98
119
  databasePath: this.databasePath,
99
120
  baseDir: this.baseDir,
100
- config: migrated.config,
121
+ config,
101
122
  ...(migratedFromPath ? { migratedFromPath } : {}),
102
123
  updatedAt: now,
103
- warnings: [
104
- ...(options.warnings || []),
105
- ...migrated.warnings,
106
- ].filter(Boolean),
124
+ warnings: (options.warnings || []).filter(Boolean),
107
125
  };
108
126
  }
109
- getRow() {
110
- return this.db.prepare(`
111
- SELECT config_json, base_dir, migrated_from_path, updated_at
112
- FROM runtime_config
113
- WHERE id = ?
114
- `).get(RUNTIME_CONFIG_ID);
115
- }
116
127
  toState(row, config, warnings) {
117
128
  return {
118
129
  storage: 'sqlite',
@@ -1,21 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeChannelInstanceId = normalizeChannelInstanceId;
4
- exports.channelPlatformKey = channelPlatformKey;
5
- exports.runtimeKey = runtimeKey;
6
3
  exports.collectLarkWorkspaceBindings = collectLarkWorkspaceBindings;
7
4
  const desktop_js_1 = require("../../../../../shared/desktop.js");
8
- function normalizeChannelInstanceId(value, fallback) {
9
- const raw = String(value || '').trim();
10
- const normalized = raw.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/-+/g, '-').slice(0, 80);
11
- return normalized || fallback;
12
- }
13
- function channelPlatformKey(platform, instanceId) {
14
- return instanceId === 'default' ? platform : `${platform}:${instanceId}`;
15
- }
16
- function runtimeKey(workspaceId, instanceId) {
17
- return `${workspaceId}::${instanceId}`;
18
- }
5
+ const channel_keys_js_1 = require("../shared/channel-keys.js");
19
6
  function collectLarkWorkspaceBindings(config, options) {
20
7
  const projects = Array.isArray(config?.projects) ? config.projects : [];
21
8
  return projects.flatMap((project) => {
@@ -29,12 +16,12 @@ function collectLarkWorkspaceBindings(config, options) {
29
16
  }))
30
17
  .filter((platform) => platform.platformType === 'lark')
31
18
  .map((platform, index) => {
32
- const instanceId = normalizeChannelInstanceId(platform.options.instance_id || platform.options.id, index === 0 ? 'default' : `lark-${index + 1}`);
19
+ const instanceId = (0, channel_keys_js_1.normalizeChannelInstanceId)(platform.options.instance_id || platform.options.id, index === 0 ? 'default' : `lark-${index + 1}`);
33
20
  return {
34
21
  workspaceId: project.name,
35
22
  instanceId,
36
23
  displayName: String(platform.options.name || platform.options.display_name || `Lark ${index + 1}`).trim(),
37
- platformKey: channelPlatformKey('lark', instanceId),
24
+ platformKey: (0, channel_keys_js_1.channelPlatformKey)('lark', instanceId),
38
25
  appId: String(platform.options.app_id || '').trim(),
39
26
  appSecret: String(platform.options.app_secret || '').trim(),
40
27
  encryptKey: String(platform.options.encrypt_key || '').trim(),
@@ -47,6 +34,7 @@ function collectLarkWorkspaceBindings(config, options) {
47
34
  || options.defaultCardActionsEnabled,
48
35
  groupReplyAll: String(platform.options.group_reply_all || '').trim().toLowerCase() === 'true'
49
36
  || platform.options.group_reply_all === true,
37
+ downloadsDir: String(platform.options.downloads_dir || '').trim(),
50
38
  brand: String(platform.options.brand || platform.options.lark_brand || '').trim().toLowerCase() === 'lark' ? 'lark' : 'feishu',
51
39
  enabled: Boolean(String(platform.options.app_id || '').trim() && String(platform.options.app_secret || '').trim()),
52
40
  project,
@@ -36,7 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.LocalCoreLarkGateway = void 0;
37
37
  const node_crypto_1 = require("node:crypto");
38
38
  const node_fs_1 = require("node:fs");
39
+ const promises_1 = require("node:fs/promises");
39
40
  const node_path_1 = require("node:path");
41
+ const promises_2 = require("node:stream/promises");
40
42
  const local_core_errors_js_1 = require("../../kernel/local-core-errors.js");
41
43
  const desktop_js_1 = require("../../../../../shared/desktop.js");
42
44
  const content_js_1 = require("../shared/content.js");
@@ -44,6 +46,7 @@ const file_utils_js_1 = require("../shared/file-utils.js");
44
46
  const thread_routing_js_1 = require("../shared/thread-routing.js");
45
47
  const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
46
48
  const cards_js_1 = require("./cards.js");
49
+ const channel_keys_js_1 = require("../shared/channel-keys.js");
47
50
  const config_js_1 = require("./config.js");
48
51
  const registration_js_1 = require("./registration.js");
49
52
  const messages_js_1 = require("./rendering/messages.js");
@@ -53,6 +56,7 @@ const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
53
56
  const LARK_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
54
57
  const LARK_FINAL_PATCH_INTERVAL_MS = 900;
55
58
  const LARK_PROGRESS_PATCH_INTERVAL_MS = 3000;
59
+ const LARK_EMPTY_RENDER_LOG_WINDOW_MS = 5 * 60 * 1000;
56
60
  class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway {
57
61
  // Lark returns 200340 when card action events are not enabled in the app's
58
62
  // event subscription. Keep card actions opt-in so text approval always works.
@@ -60,6 +64,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
60
64
  // Keep permission state in a dedicated card to avoid mixing order in the main reply card.
61
65
  mirrorPermissionStateInMainCard = false;
62
66
  larkModulePromise = null;
67
+ emptyRenderLogWindows = new Map();
63
68
  platform = 'lark';
64
69
  constructor(options) {
65
70
  super(options);
@@ -129,7 +134,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
129
134
  }
130
135
  registerScheduledThreadBridge(input) {
131
136
  const instanceId = input.route.instanceId || getLarkInstanceId(input.platform) || 'default';
132
- const platformKey = (0, config_js_1.channelPlatformKey)('lark', instanceId);
137
+ const platformKey = (0, channel_keys_js_1.channelPlatformKey)('lark', instanceId);
133
138
  const route = {
134
139
  workspaceId: input.workspaceId,
135
140
  instanceId,
@@ -277,8 +282,8 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
277
282
  return;
278
283
  }
279
284
  const routeInstanceId = route.instanceId || 'default';
280
- const routePlatformKey = route.platformKey || (0, config_js_1.channelPlatformKey)('lark', routeInstanceId);
281
- const state = this.runtime.get((0, config_js_1.runtimeKey)(route.workspaceId, routeInstanceId)) || this.runtime.get(route.workspaceId);
285
+ const routePlatformKey = route.platformKey || (0, channel_keys_js_1.channelPlatformKey)('lark', routeInstanceId);
286
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(route.workspaceId, routeInstanceId)) || this.runtime.get(route.workspaceId);
282
287
  if (!state?.client || !state.connected) {
283
288
  this.options.log?.(`localcore-lark bridge event ignored because workspace is not connected: ${route.workspaceId}`);
284
289
  return;
@@ -353,7 +358,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
353
358
  const renderedMessages = (0, runtime_state_js_1.renderLarkBridgeEventMessages)(turn, event);
354
359
  for (const renderedMessage of renderedMessages) {
355
360
  if (!renderedMessage.text && renderedMessage.buttonRows.length === 0) {
356
- this.options.log?.(`localcore-lark bridge event produced empty render for sessionKey=${sessionKey} type=${event.type}`);
361
+ this.logEmptyRender(sessionKey, event.type);
357
362
  continue;
358
363
  }
359
364
  const existingMessageId = (0, runtime_state_js_1.getLarkRenderedMessageId)(turn, renderedMessage);
@@ -408,7 +413,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
408
413
  async handleInboundMessage(input) {
409
414
  const msg = input;
410
415
  const instanceId = msg.instanceId || 'default';
411
- const platformKey = msg.platformKey || (0, config_js_1.channelPlatformKey)('lark', instanceId);
416
+ const platformKey = msg.platformKey || (0, channel_keys_js_1.channelPlatformKey)('lark', instanceId);
412
417
  this.options.eventBus.emit({
413
418
  type: 'platform.message.received',
414
419
  payload: {
@@ -562,7 +567,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
562
567
  }
563
568
  }
564
569
  async startWorkspace(binding) {
565
- const key = (0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
570
+ const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
566
571
  await this.stopWorkspaceKey(key);
567
572
  const status = {
568
573
  workspaceId: binding.workspaceId,
@@ -576,6 +581,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
576
581
  autoApprove: binding.autoApprove,
577
582
  cardActionsEnabled: binding.cardActionsEnabled,
578
583
  groupReplyAll: binding.groupReplyAll,
584
+ downloadsDir: binding.downloadsDir,
579
585
  botOpenId: binding.botOpenId,
580
586
  };
581
587
  this.runtime.set(key, status);
@@ -652,7 +658,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
652
658
  async handleMessageEvent(workspaceId, instanceIdOrData, platformKeyOrData, maybeData) {
653
659
  const legacyCall = typeof instanceIdOrData === 'object';
654
660
  const instanceId = legacyCall ? 'default' : instanceIdOrData;
655
- const platformKey = legacyCall ? 'lark' : String(platformKeyOrData || (0, config_js_1.channelPlatformKey)('lark', instanceId));
661
+ const platformKey = legacyCall ? 'lark' : String(platformKeyOrData || (0, channel_keys_js_1.channelPlatformKey)('lark', instanceId));
656
662
  const data = (legacyCall ? instanceIdOrData : maybeData) || {};
657
663
  const runtimeState = this.resolveRuntimeState(workspaceId, instanceId).state;
658
664
  const normalized = (0, inbound_js_1.normalizeLarkInboundMessageEvent)(data, {
@@ -750,6 +756,10 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
750
756
  if (!messageId) {
751
757
  throw new Error('Lark file message is missing message_id');
752
758
  }
759
+ const downloadsDir = await this.resolveInboundDownloadsDir(workspaceId, instanceId, state.downloadsDir);
760
+ const storedFileName = `${this.safeInboundFilePart(messageId, 'message')}-${this.safeInboundFilePart(fileName || fileKey, 'file')}`;
761
+ const filePath = (0, node_path_1.join)(downloadsDir, storedFileName);
762
+ const temporaryPath = `${filePath}.${(0, node_crypto_1.randomUUID)()}.part`;
753
763
  const resource = await state.client.im.messageResource.get({
754
764
  path: {
755
765
  message_id: messageId,
@@ -759,18 +769,50 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
759
769
  type: 'file',
760
770
  },
761
771
  });
762
- const buffer = await this.readLarkResourceBuffer(resource);
763
- if (buffer.length === 0) {
764
- throw new Error('Lark file resource is empty');
772
+ const readable = resource?.getReadableStream?.();
773
+ if (!readable || typeof readable.pipe !== 'function') {
774
+ throw new Error('Lark file resource did not provide a readable stream');
775
+ }
776
+ await (0, promises_1.mkdir)(downloadsDir, { recursive: true });
777
+ try {
778
+ await (0, promises_2.pipeline)(readable, (0, node_fs_1.createWriteStream)(temporaryPath, { flags: 'wx' }));
779
+ const temporaryStat = await (0, promises_1.stat)(temporaryPath);
780
+ if (temporaryStat.size === 0) {
781
+ throw new Error('Lark file resource is empty');
782
+ }
783
+ await (0, promises_1.rename)(temporaryPath, filePath);
784
+ }
785
+ catch (error) {
786
+ await (0, promises_1.rm)(temporaryPath, { force: true }).catch(() => undefined);
787
+ throw error;
765
788
  }
789
+ const fileStat = await (0, promises_1.stat)(filePath);
766
790
  return {
767
791
  type: 'file',
768
- data: buffer.toString('base64'),
792
+ path: filePath,
769
793
  mimeType: this.extractHeaderMimeType(resource?.headers) || 'application/octet-stream',
770
794
  fileName: fileName || fileKey,
771
- size: buffer.length,
795
+ size: fileStat.size,
772
796
  };
773
797
  }
798
+ async resolveInboundDownloadsDir(workspaceId, instanceId = 'default', runtimeDownloadsDir) {
799
+ const binding = runtimeDownloadsDir === undefined
800
+ ? await this.getBinding(workspaceId, instanceId)
801
+ : undefined;
802
+ const configuredDir = String(runtimeDownloadsDir ?? binding?.downloadsDir ?? '').trim();
803
+ if (configuredDir && (0, node_path_1.isAbsolute)(configuredDir)) {
804
+ return configuredDir;
805
+ }
806
+ const workspace = await this.options.getWorkspaceRouter().getWorkspaceRegistryEntry(workspaceId);
807
+ return configuredDir
808
+ ? (0, node_path_1.resolve)(workspace.path, configuredDir)
809
+ : (0, node_path_1.join)(workspace.path, '.agentdock', 'channel-uploads', 'lark', instanceId);
810
+ }
811
+ safeInboundFilePart(value, fallback) {
812
+ return String(value || fallback)
813
+ .replace(/[^a-zA-Z0-9._-]/g, '_')
814
+ .slice(0, 120) || fallback;
815
+ }
774
816
  summarizeInboundContentParts(parts) {
775
817
  const text = parts
776
818
  .filter((part) => part.type === 'text')
@@ -913,7 +955,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
913
955
  this.options.store.expirePendingPairings();
914
956
  const resolved = this.resolveRuntimeState(state.workspaceId, state.instanceId);
915
957
  const binding = resolved.state;
916
- const platformKey = binding?.platformKey || (0, config_js_1.channelPlatformKey)('lark', resolved.instanceId);
958
+ const platformKey = binding?.platformKey || (0, channel_keys_js_1.channelPlatformKey)('lark', resolved.instanceId);
917
959
  const pairings = this.options.store.listPendingPairings(state.workspaceId)
918
960
  .filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
919
961
  const users = this.options.store.listAuthorizedUsers(state.workspaceId, platformKey);
@@ -947,6 +989,7 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
947
989
  autoApprove: binding.autoApprove,
948
990
  cardActionsEnabled: binding.cardActionsEnabled,
949
991
  groupReplyAll: binding.groupReplyAll,
992
+ downloadsDir: binding.downloadsDir,
950
993
  botOpenId: binding.botOpenId,
951
994
  };
952
995
  }
@@ -1247,6 +1290,22 @@ class LocalCoreLarkGateway extends base_channel_gateway_js_1.BaseChannelGateway
1247
1290
  this.options.log?.(`localcore-lark acknowledgement reaction failed for message=${messageId}: ${error instanceof Error ? error.message : String(error)}`);
1248
1291
  }
1249
1292
  }
1293
+ logEmptyRender(sessionKey, type) {
1294
+ const key = `${sessionKey}|${type}`;
1295
+ const now = Date.now();
1296
+ const lastAt = this.emptyRenderLogWindows.get(key);
1297
+ if (lastAt !== undefined && now - lastAt < LARK_EMPTY_RENDER_LOG_WINDOW_MS) {
1298
+ return;
1299
+ }
1300
+ this.emptyRenderLogWindows.set(key, now);
1301
+ if (this.emptyRenderLogWindows.size > 1000) {
1302
+ const oldestKey = this.emptyRenderLogWindows.keys().next().value;
1303
+ if (oldestKey) {
1304
+ this.emptyRenderLogWindows.delete(oldestKey);
1305
+ }
1306
+ }
1307
+ this.options.log?.(`localcore-lark bridge event produced empty render for sessionKey=${sessionKey} type=${type}`);
1308
+ }
1250
1309
  }
1251
1310
  exports.LocalCoreLarkGateway = LocalCoreLarkGateway;
1252
1311
  function getLarkInstanceId(platform) {
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeChannelInstanceId = normalizeChannelInstanceId;
4
+ exports.channelPlatformKey = channelPlatformKey;
5
+ exports.runtimeKey = runtimeKey;
6
+ function normalizeChannelInstanceId(value, fallback) {
7
+ const raw = String(value || '').trim();
8
+ const normalized = raw.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/-+/g, '-').slice(0, 80);
9
+ return normalized || fallback;
10
+ }
11
+ function channelPlatformKey(platform, instanceId) {
12
+ return instanceId === 'default' ? platform : `${platform}:${instanceId}`;
13
+ }
14
+ function runtimeKey(workspaceId, instanceId) {
15
+ return `${workspaceId}::${instanceId}`;
16
+ }
@@ -4,9 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.LONG_POLL_TIMEOUT_MS = exports.DEFAULT_CDN_BASE_URL = exports.DEFAULT_BASE_URL = void 0;
7
- exports.normalizeChannelInstanceId = normalizeChannelInstanceId;
8
- exports.channelPlatformKey = channelPlatformKey;
9
- exports.runtimeKey = runtimeKey;
10
7
  exports.getDefaultWeixinStateDir = getDefaultWeixinStateDir;
11
8
  exports.safeFilePart = safeFilePart;
12
9
  exports.getWeixinBufPath = getWeixinBufPath;
@@ -19,20 +16,10 @@ exports.collectWeixinWorkspaceBindings = collectWeixinWorkspaceBindings;
19
16
  const node_fs_1 = __importDefault(require("node:fs"));
20
17
  const node_path_1 = __importDefault(require("node:path"));
21
18
  const desktop_js_1 = require("../../../../../shared/desktop.js");
19
+ const channel_keys_js_1 = require("../shared/channel-keys.js");
22
20
  exports.DEFAULT_BASE_URL = 'https://ilinkai.weixin.qq.com';
23
21
  exports.DEFAULT_CDN_BASE_URL = 'https://novac2c.cdn.weixin.qq.com/c2c';
24
22
  exports.LONG_POLL_TIMEOUT_MS = 35_000;
25
- function normalizeChannelInstanceId(value, fallback) {
26
- const raw = String(value || '').trim();
27
- const normalized = raw.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/-+/g, '-').slice(0, 80);
28
- return normalized || fallback;
29
- }
30
- function channelPlatformKey(platform, instanceId) {
31
- return instanceId === 'default' ? platform : `${platform}:${instanceId}`;
32
- }
33
- function runtimeKey(workspaceId, instanceId) {
34
- return `${workspaceId}::${instanceId}`;
35
- }
36
23
  function getDefaultWeixinStateDir() {
37
24
  return node_path_1.default.join(process.cwd(), 'weixin-monitor');
38
25
  }
@@ -95,7 +82,7 @@ function collectWeixinWorkspaceBindings(config) {
95
82
  }))
96
83
  .filter((p) => p.platformType === 'weixin')
97
84
  .map((p, index) => {
98
- const instanceId = normalizeChannelInstanceId(p.options.instance_id || p.options.id, index === 0 ? 'default' : `weixin-${index + 1}`);
85
+ const instanceId = (0, channel_keys_js_1.normalizeChannelInstanceId)(p.options.instance_id || p.options.id, index === 0 ? 'default' : `weixin-${index + 1}`);
99
86
  const stateDir = String(p.options.state_dir || getDefaultWeixinStateDir()).trim();
100
87
  const credentials = loadWeixinCredentials(project.name, stateDir, instanceId);
101
88
  const configuredToken = String(p.options.token || '').trim();
@@ -105,7 +92,7 @@ function collectWeixinWorkspaceBindings(config) {
105
92
  workspaceId: project.name,
106
93
  instanceId,
107
94
  displayName: String(p.options.name || p.options.display_name || `WeChat ${index + 1}`).trim(),
108
- platformKey: channelPlatformKey('weixin', instanceId),
95
+ platformKey: (0, channel_keys_js_1.channelPlatformKey)('weixin', instanceId),
109
96
  token: configuredToken || credentials?.token || '',
110
97
  accountId,
111
98
  baseUrl: configuredBaseUrl || credentials?.baseUrl || exports.DEFAULT_BASE_URL,
@@ -15,6 +15,7 @@ const content_js_1 = require("../shared/content.js");
15
15
  const file_utils_js_1 = require("../shared/file-utils.js");
16
16
  const thread_routing_js_1 = require("../shared/thread-routing.js");
17
17
  const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
18
+ const channel_keys_js_1 = require("../shared/channel-keys.js");
18
19
  const config_js_1 = require("./config.js");
19
20
  const transport_js_1 = require("./transport.js");
20
21
  // ==================== Constants ====================
@@ -168,7 +169,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
168
169
  }
169
170
  buildStatusObject(state, resolved) {
170
171
  this.options.store.expirePendingPairings();
171
- const platformKey = state.platformKey || (0, config_js_1.channelPlatformKey)('weixin', resolved.instanceId);
172
+ const platformKey = state.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', resolved.instanceId);
172
173
  const pairings = this.options.store.listPendingPairings(state.workspaceId)
173
174
  .filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
174
175
  const users = this.options.store.listAuthorizedUsers(state.workspaceId, platformKey);
@@ -294,8 +295,8 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
294
295
  return;
295
296
  }
296
297
  const routeInstanceId = route.instanceId || 'default';
297
- const routePlatformKey = route.platformKey || (0, config_js_1.channelPlatformKey)('weixin', routeInstanceId);
298
- const state = this.runtime.get((0, config_js_1.runtimeKey)(route.workspaceId, routeInstanceId)) || this.runtime.get(route.workspaceId);
298
+ const routePlatformKey = route.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', routeInstanceId);
299
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(route.workspaceId, routeInstanceId)) || this.runtime.get(route.workspaceId);
299
300
  if (!state?.connected) {
300
301
  this.options.log?.(`localcore-weixin bridge event ignored because workspace is not connected: ${route.workspaceId}`);
301
302
  return;
@@ -372,7 +373,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
372
373
  }
373
374
  registerScheduledThreadBridge(input) {
374
375
  const instanceId = input.route.instanceId || getWeixinInstanceId(input.platform) || 'default';
375
- const platformKey = (0, config_js_1.channelPlatformKey)('weixin', instanceId);
376
+ const platformKey = (0, channel_keys_js_1.channelPlatformKey)('weixin', instanceId);
376
377
  const route = {
377
378
  workspaceId: input.workspaceId,
378
379
  instanceId,
@@ -470,7 +471,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
470
471
  async handleInboundMessage(input) {
471
472
  const msg = input;
472
473
  const instanceId = msg.instanceId || 'default';
473
- const platformKey = msg.platformKey || (0, config_js_1.channelPlatformKey)('weixin', instanceId);
474
+ const platformKey = msg.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', instanceId);
474
475
  if (this.isDuplicateInboundMessage(msg)) {
475
476
  this.options.log?.(`localcore-weixin skipped duplicate inbound message workspace=${msg.workspaceId} chat=${msg.chatId} id=${msg.contextToken || msg.messageId}`);
476
477
  return;
@@ -527,7 +528,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
527
528
  });
528
529
  this.notifyRuntimeStateChanged();
529
530
  }
530
- const state = this.runtime.get((0, config_js_1.runtimeKey)(msg.workspaceId, instanceId));
531
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(msg.workspaceId, instanceId));
531
532
  if (state?.connected) {
532
533
  await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${pairingCode}\``, msg.contextToken);
533
534
  }
@@ -599,7 +600,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
599
600
  // ==================== Private: Bindings ====================
600
601
  // ==================== Private: Workspace Lifecycle ====================
601
602
  async startWorkspace(binding) {
602
- const key = (0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
603
+ const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
603
604
  await this.stopWorkspaceKey(key);
604
605
  const status = {
605
606
  workspaceId: binding.workspaceId,
@@ -669,7 +670,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
669
670
  const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
670
671
  if (isApiError) {
671
672
  consecutiveFailures++;
672
- const state = this.runtime.get((0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
673
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
673
674
  const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
674
675
  if (resp.errcode === -14 || resp.ret === -14) {
675
676
  if (state) {
@@ -693,7 +694,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
693
694
  continue;
694
695
  }
695
696
  consecutiveFailures = 0;
696
- const state = this.runtime.get((0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
697
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
697
698
  if (state) {
698
699
  this.clearRuntimeError(state);
699
700
  state.status = 'running';
@@ -762,7 +763,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
762
763
  return;
763
764
  consecutiveFailures++;
764
765
  const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
765
- const state = this.runtime.get((0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
766
+ const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
766
767
  if (state) {
767
768
  state.consecutiveFailures = consecutiveFailures;
768
769
  state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
@@ -1115,7 +1116,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
1115
1116
  return 60_000;
1116
1117
  }
1117
1118
  logPollError(binding, errorKey, message) {
1118
- const key = (0, config_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
1119
+ const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
1119
1120
  const current = this.pollErrorLogWindows.get(key);
1120
1121
  const now = Date.now();
1121
1122
  if (!current || current.errorKey !== errorKey || now - current.at >= ERROR_LOG_WINDOW_MS) {