@kafca/agentdock 0.1.22 → 0.1.24

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 (89) hide show
  1. package/README.md +4 -0
  2. package/dist/renderer/assets/{Badge-B_rgzxYO.js → Badge-CXcUMqb-.js} +1 -1
  3. package/dist/renderer/assets/{Chat-CYtp6m7T.js → Chat-BRxdPzi-.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-DOBMwa9d.js → ChatMarkdown-sWGBIfIb.js} +1 -1
  5. package/dist/renderer/assets/{Config-DppMSPHF.js → Config-DqxuxIGg.js} +1 -1
  6. package/dist/renderer/assets/{CronList-BDtB1SuA.js → CronList-OgHxbti5.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-iFlRnd7Y.js → Dashboard-DmklXiH1.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-bKDt_T88.js → EmptyState-BvDmi8dq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-CKPhKPIg.js → KnowledgeDetail-Bj59Hdpo.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-CFeJre6i.js → KnowledgeHome-CigQ-nSo.js} +3 -3
  11. package/dist/renderer/assets/{Logs-CyxSVOFK.js → Logs-Bfc7gBpY.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CxPnk1Ey.js → Modal-CfmiFknz.js} +1 -1
  13. package/dist/renderer/assets/{Page-kU9vubzj.js → Page-BxtLEw_b.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-D9FY9NWZ.js → ProjectDetail-CcUphxGm.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-DIwr2TXg.js → ProjectList-CVkvDIfy.js} +1 -1
  16. package/dist/renderer/assets/{Select-BaWoz5Dn.js → Select-DgQMZ0MD.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-Cm9CW8Zx.js → SessionChat-CRmGKfBx.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-UPnWlXXO.js → SessionList-D0E8XbYw.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +23 -0
  20. package/dist/renderer/assets/Workspace-Q4g4rlRT.js +14 -0
  21. package/dist/renderer/assets/{arrow-left-BmZAVGV7.js → arrow-left-BySN_zhF.js} +1 -1
  22. package/dist/renderer/assets/{book-open-DZ3GXFgI.js → book-open-BGlrnrTc.js} +1 -1
  23. package/dist/renderer/assets/{check-CeVSvDCv.js → check-BiJogEa5.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-yryuMoQ-.js → chevron-down-D2fHRJv6.js} +1 -1
  25. package/dist/renderer/assets/{circle-eASsFISH.js → circle-CIcjixmI.js} +1 -1
  26. package/dist/renderer/assets/desktop-DAXY9Bxv.js +4 -0
  27. package/dist/renderer/assets/desktop-DyA_NYew.js +1 -0
  28. package/dist/renderer/assets/{funnel-pwAe2Ixp.js → funnel-BUzwb-id.js} +1 -1
  29. package/dist/renderer/assets/{heart-B2Sp-bCm.js → heart-DZEQ1z_V.js} +1 -1
  30. package/dist/renderer/assets/{index-BC3MWbS1.js → index-CB_KyLOr.js} +39 -39
  31. package/dist/renderer/assets/{index-Cse2Br64.js → index-DgsIo4vJ.js} +3 -3
  32. package/dist/renderer/assets/{pencil-BfEunUF3.js → pencil-DGL4agcl.js} +1 -1
  33. package/dist/renderer/assets/{play-Dcs9Lj_Z.js → play-DzasCLM4.js} +1 -1
  34. package/dist/renderer/assets/{plug-B_YschQe.js → plug-dubNMe_m.js} +1 -1
  35. package/dist/renderer/assets/{plus-Pqo-cXHQ.js → plus-DN4kML4k.js} +1 -1
  36. package/dist/renderer/assets/projects-migPtfRe.js +1 -0
  37. package/dist/renderer/assets/{save-BKM5yi1A.js → save-CNIc9tjl.js} +1 -1
  38. package/dist/renderer/assets/{search-BU42h10M.js → search-C0EGbF3I.js} +1 -1
  39. package/dist/renderer/assets/{server-DA5t3Rtr.js → server-BMxQ4zmc.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Cr9VhNSq.js → session-utils-Bf5O-iMi.js} +1 -1
  41. package/dist/renderer/assets/{sessions-bvV8ZMfX.js → sessions-BMSs6rEI.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-BeOJKur_.js → shield-check-i3Tf1TUz.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-CQ7HKO3q.js → trash-2-SKrcp18f.js} +1 -1
  44. package/dist/renderer/assets/{user-CtFjY9SV.js → user-cNUZddjZ.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-Ci5m0ysf.js → wifi-off-Dyb84ANR.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/channel-content.test.js +29 -0
  48. package/dist-electron/electron/lac-cli.test.js +49 -4
  49. package/dist-electron/electron/local-core-acp-content.test.js +39 -0
  50. package/dist-electron/electron/local-core-contracts.test.js +67 -0
  51. package/dist-electron/electron/local-core-refactor.test.js +628 -0
  52. package/dist-electron/electron/plugin-kernel.test.js +74 -14
  53. package/dist-electron/electron/workspace-task-store.test.js +124 -0
  54. package/dist-electron/packages/contracts/src/local-core.js +110 -0
  55. package/dist-electron/packages/core-sdk/src/index.js +40 -20
  56. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +25 -4
  57. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +3 -2
  58. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +44 -28
  59. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +5 -1
  60. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +15 -6
  61. package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +210 -0
  62. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +54 -0
  63. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +280 -496
  64. package/dist-electron/services/local-ai-core/src/channel/lark/registration.js +50 -0
  65. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +191 -0
  66. package/dist-electron/services/local-ai-core/src/channel/lark/types.js +2 -0
  67. package/dist-electron/services/local-ai-core/src/channel/shared/content.js +23 -0
  68. package/dist-electron/services/local-ai-core/src/channel/shared/plugin.js +31 -0
  69. package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +125 -0
  70. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +164 -352
  71. package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +169 -0
  72. package/dist-electron/services/local-ai-core/src/channel/weixin/types.js +2 -0
  73. package/dist-electron/services/local-ai-core/src/cli/lac.js +19 -16
  74. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +5 -0
  75. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-lark-plugin.js +18 -39
  76. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-weixin-plugin.js +18 -39
  77. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +55 -44
  78. package/dist-electron/services/local-ai-core/src/runtime/server.js +10 -8
  79. package/dist-electron/services/local-ai-core/src/scheduler/job-id.js +15 -0
  80. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +30 -5
  81. package/dist-electron/shared/desktop.js +37 -5
  82. package/dist-electron/src/pages/Threads/thread-chat-model.js +57 -0
  83. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +70 -0
  84. package/package.json +1 -1
  85. package/dist/renderer/assets/ThreadChat-YvVsACW3.js +0 -23
  86. package/dist/renderer/assets/Workspace-ST7V8u6T.js +0 -14
  87. package/dist/renderer/assets/desktop-Che1cbpE.js +0 -4
  88. package/dist/renderer/assets/desktop-DkrP_s-b.js +0 -1
  89. package/dist/renderer/assets/projects-BCj0k_Gy.js +0 -1
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UPLOAD_MEDIA_TYPE_FILE = exports.FILE_ITEM_TYPE = exports.VOICE_ITEM_TYPE = exports.IMAGE_ITEM_TYPE = exports.TEXT_ITEM_TYPE = exports.WEIXIN_ILINK_APP_CLIENT_VERSION = exports.WEIXIN_ILINK_APP_ID = exports.WEIXIN_CHANNEL_VERSION = exports.API_TIMEOUT_MS = void 0;
7
+ exports.createWechatUin = createWechatUin;
8
+ exports.createIlinkHeaders = createIlinkHeaders;
9
+ exports.weixinApiPost = weixinApiPost;
10
+ exports.weixinApiGet = weixinApiGet;
11
+ exports.getWeixinUpdates = getWeixinUpdates;
12
+ exports.sendWeixinTextMessageChunk = sendWeixinTextMessageChunk;
13
+ exports.sendWeixinFileMessage = sendWeixinFileMessage;
14
+ exports.getWeixinUploadUrl = getWeixinUploadUrl;
15
+ exports.uploadEncryptedBufferToWeixinCdn = uploadEncryptedBufferToWeixinCdn;
16
+ exports.isWeixinApiError = isWeixinApiError;
17
+ const node_crypto_1 = __importDefault(require("node:crypto"));
18
+ exports.API_TIMEOUT_MS = 15_000;
19
+ exports.WEIXIN_CHANNEL_VERSION = '2.1.7';
20
+ exports.WEIXIN_ILINK_APP_ID = 'bot';
21
+ exports.WEIXIN_ILINK_APP_CLIENT_VERSION = '131335';
22
+ exports.TEXT_ITEM_TYPE = 1;
23
+ exports.IMAGE_ITEM_TYPE = 2;
24
+ exports.VOICE_ITEM_TYPE = 3;
25
+ exports.FILE_ITEM_TYPE = 4;
26
+ exports.UPLOAD_MEDIA_TYPE_FILE = 3;
27
+ function createWechatUin() {
28
+ return Buffer.from(String(node_crypto_1.default.randomInt(0, 0xffffffff))).toString('base64');
29
+ }
30
+ function createIlinkHeaders() {
31
+ return {
32
+ 'iLink-App-Id': exports.WEIXIN_ILINK_APP_ID,
33
+ 'iLink-App-ClientVersion': exports.WEIXIN_ILINK_APP_CLIENT_VERSION,
34
+ };
35
+ }
36
+ async function weixinApiPost(binding, endpoint, bodyObj, timeoutMs, signal) {
37
+ const url = `${binding.baseUrl.replace(/\/$/, '')}/${endpoint}`;
38
+ const body = JSON.stringify(bodyObj);
39
+ const controller = new AbortController();
40
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
41
+ const onAbort = () => controller.abort();
42
+ signal?.addEventListener('abort', onAbort, { once: true });
43
+ try {
44
+ const headers = {
45
+ 'Content-Type': 'application/json',
46
+ 'Content-Length': String(Buffer.byteLength(body, 'utf-8')),
47
+ 'X-WECHAT-UIN': createWechatUin(),
48
+ ...createIlinkHeaders(),
49
+ };
50
+ if (binding.token) {
51
+ headers.AuthorizationType = 'ilink_bot_token';
52
+ headers.Authorization = `Bearer ${binding.token}`;
53
+ }
54
+ const res = await fetch(url, {
55
+ method: 'POST',
56
+ headers,
57
+ body,
58
+ signal: controller.signal,
59
+ });
60
+ if (!res.ok)
61
+ throw new Error(`HTTP ${res.status}`);
62
+ return (await res.json());
63
+ }
64
+ finally {
65
+ clearTimeout(timer);
66
+ signal?.removeEventListener('abort', onAbort);
67
+ }
68
+ }
69
+ async function weixinApiGet(binding, endpoint, timeoutMs = exports.API_TIMEOUT_MS) {
70
+ const url = `${binding.baseUrl.replace(/\/$/, '')}/${endpoint}`;
71
+ const controller = new AbortController();
72
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
73
+ try {
74
+ const headers = createIlinkHeaders();
75
+ if (binding.token) {
76
+ headers.AuthorizationType = 'ilink_bot_token';
77
+ headers.Authorization = `Bearer ${binding.token}`;
78
+ }
79
+ const res = await fetch(url, {
80
+ method: 'GET',
81
+ headers,
82
+ signal: controller.signal,
83
+ });
84
+ if (!res.ok)
85
+ throw new Error(`HTTP ${res.status}`);
86
+ return (await res.json());
87
+ }
88
+ finally {
89
+ clearTimeout(timer);
90
+ }
91
+ }
92
+ function getWeixinUpdates(binding, buf, signal) {
93
+ return weixinApiPost(binding, 'ilink/bot/getupdates', { get_updates_buf: buf, base_info: { channel_version: exports.WEIXIN_CHANNEL_VERSION } }, binding.longPollTimeoutMs, signal);
94
+ }
95
+ function sendWeixinTextMessageChunk(binding, toUserId, text, contextToken, options = {}) {
96
+ return weixinApiPost(binding, 'ilink/bot/sendmessage', {
97
+ msg: {
98
+ from_user_id: '',
99
+ to_user_id: toUserId,
100
+ client_id: options.clientId || `openclaw-weixin-${node_crypto_1.default.randomUUID()}`,
101
+ message_type: 2,
102
+ message_state: options.final === false ? 1 : 2,
103
+ item_list: [{ type: exports.TEXT_ITEM_TYPE, text_item: { text } }],
104
+ ...(contextToken ? { context_token: contextToken } : {}),
105
+ },
106
+ base_info: { channel_version: exports.WEIXIN_CHANNEL_VERSION },
107
+ }, exports.API_TIMEOUT_MS);
108
+ }
109
+ function sendWeixinFileMessage(binding, toUserId, fileName, uploaded, contextToken, options = {}) {
110
+ return weixinApiPost(binding, 'ilink/bot/sendmessage', {
111
+ msg: {
112
+ from_user_id: '',
113
+ to_user_id: toUserId,
114
+ client_id: options.clientId || `openclaw-weixin-${node_crypto_1.default.randomUUID()}`,
115
+ message_type: 2,
116
+ message_state: 2,
117
+ item_list: [{
118
+ type: exports.FILE_ITEM_TYPE,
119
+ file_item: {
120
+ media: {
121
+ encrypt_query_param: uploaded.encryptedQueryParam,
122
+ aes_key: Buffer.from(uploaded.aesKeyHex).toString('base64'),
123
+ encrypt_type: 1,
124
+ },
125
+ file_name: fileName,
126
+ len: String(uploaded.fileSize),
127
+ },
128
+ }],
129
+ ...(contextToken ? { context_token: contextToken } : {}),
130
+ },
131
+ base_info: { channel_version: exports.WEIXIN_CHANNEL_VERSION },
132
+ }, exports.API_TIMEOUT_MS);
133
+ }
134
+ function getWeixinUploadUrl(binding, fileKey, toUserId, plaintextLength, rawMd5, cipherSize, aesKeyHex) {
135
+ return weixinApiPost(binding, 'ilink/bot/getuploadurl', {
136
+ filekey: fileKey,
137
+ media_type: exports.UPLOAD_MEDIA_TYPE_FILE,
138
+ to_user_id: toUserId,
139
+ rawsize: plaintextLength,
140
+ rawfilemd5: rawMd5,
141
+ filesize: cipherSize,
142
+ no_need_thumb: true,
143
+ aeskey: aesKeyHex,
144
+ base_info: { channel_version: exports.WEIXIN_CHANNEL_VERSION },
145
+ }, exports.API_TIMEOUT_MS);
146
+ }
147
+ async function uploadEncryptedBufferToWeixinCdn(binding, plaintext, uploadParam, fileKey, aesKey) {
148
+ const cipher = node_crypto_1.default.createCipheriv('aes-128-ecb', aesKey, null);
149
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
150
+ const url = `${binding.cdnBaseUrl.replace(/\/$/, '')}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(fileKey)}`;
151
+ const res = await fetch(url, {
152
+ method: 'POST',
153
+ headers: {
154
+ 'Content-Type': 'application/octet-stream',
155
+ },
156
+ body: new Uint8Array(ciphertext),
157
+ });
158
+ if (!res.ok) {
159
+ throw new Error(`WeChat CDN upload failed: HTTP ${res.status}`);
160
+ }
161
+ const encryptedQueryParam = res.headers.get('x-encrypted-param') || '';
162
+ if (!encryptedQueryParam) {
163
+ throw new Error('WeChat CDN upload response missing x-encrypted-param');
164
+ }
165
+ return encryptedQueryParam;
166
+ }
167
+ function isWeixinApiError(resp) {
168
+ return (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
169
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runCli = runCli;
7
7
  const node_process_1 = __importDefault(require("node:process"));
8
+ const index_js_1 = require("../../../../packages/contracts/src/index.js");
9
+ const job_id_js_1 = require("../scheduler/job-id.js");
8
10
  const DEFAULT_BASE_URL = 'http://127.0.0.1:9831/api/local/v1';
9
11
  async function runCli(argv, env = node_process_1.default.env, io = node_process_1.default) {
10
12
  try {
@@ -51,7 +53,8 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
51
53
  async function handleChannelSendFile(flags, env, io, json) {
52
54
  const context = resolveContext(flags, env);
53
55
  const filePath = getRequiredFlag(flags, 'path');
54
- const platform = getFlag(flags, 'platform') || context.platform;
56
+ const rawPlatform = getFlag(flags, 'platform') || context.platform;
57
+ const platform = rawPlatform ? (0, index_js_1.normalizeChannelPlatform)(rawPlatform) : '';
55
58
  if (!platform) {
56
59
  throw new Error('channel send-file requires a platform. Set LOCAL_AI_PLATFORM or pass --platform.');
57
60
  }
@@ -113,8 +116,8 @@ async function handleAdd(flags, env, io, json) {
113
116
  description,
114
117
  enabled: true,
115
118
  });
116
- print(json, io.stdout, job, [
117
- `Created scheduler job ${job.id}`,
119
+ print(json, io.stdout, presentJob(job), [
120
+ `Created scheduler job ${(0, job_id_js_1.toPublicScheduledJobId)(job.id)}`,
118
121
  `Schedule: ${job.cronExpr || ''}`,
119
122
  `Execution mode: ${job.executionMode}`,
120
123
  `Description: ${job.description}`,
@@ -132,7 +135,7 @@ async function handleList(flags, env, io, json) {
132
135
  const jobs = threadId
133
136
  ? response.jobs.filter((job) => job.route.threadId === threadId)
134
137
  : response.jobs;
135
- print(json, io.stdout, { jobs }, jobs.length === 0 ? 'No scheduler jobs.' : jobs.map(formatJobLine).join('\n'));
138
+ print(json, io.stdout, { jobs: jobs.map(presentJob) }, jobs.length === 0 ? 'No scheduler jobs.' : jobs.map(formatJobLine).join('\n'));
136
139
  return 0;
137
140
  }
138
141
  async function handleInfo(jobId, flags, env, io, json) {
@@ -142,7 +145,7 @@ async function handleInfo(jobId, flags, env, io, json) {
142
145
  const context = resolveContext(flags, env);
143
146
  const job = await request(context.baseUrl, 'GET', `/scheduler/jobs/${encodeURIComponent(jobId)}`);
144
147
  const runs = await request(context.baseUrl, 'GET', `/scheduler/jobs/${encodeURIComponent(jobId)}/runs`);
145
- print(json, io.stdout, { job, runs: runs.runs }, formatJobDetails(job, runs.runs[0]));
148
+ print(json, io.stdout, { job: presentJob(job), runs: runs.runs }, formatJobDetails(job, runs.runs[0]));
146
149
  return 0;
147
150
  }
148
151
  async function handleEdit(jobId, flags, env, io, json) {
@@ -168,14 +171,14 @@ async function handleEdit(jobId, flags, env, io, json) {
168
171
  input.enabled = enabled;
169
172
  }
170
173
  if (executionMode) {
171
- input.executionMode = parseExecutionMode(executionMode);
174
+ input.executionMode = (0, index_js_1.normalizeScheduledJobExecutionMode)(executionMode);
172
175
  }
173
176
  if (Object.keys(input).length === 0) {
174
177
  throw new Error('scheduler edit requires at least one editable field.');
175
178
  }
176
179
  const context = resolveContext(flags, env);
177
180
  const job = await request(context.baseUrl, 'PATCH', `/scheduler/jobs/${encodeURIComponent(jobId)}`, input);
178
- print(json, io.stdout, job, `Updated scheduler job ${job.id}`);
181
+ print(json, io.stdout, presentJob(job), `Updated scheduler job ${(0, job_id_js_1.toPublicScheduledJobId)(job.id)}`);
179
182
  return 0;
180
183
  }
181
184
  async function handleDelete(jobId, flags, env, io, json) {
@@ -284,13 +287,7 @@ function normalizeMaybeBooleanFlag(value) {
284
287
  return value === 'true' ? '' : value;
285
288
  }
286
289
  function getExecutionMode(flags) {
287
- return parseExecutionMode(getFlag(flags, 'execution-mode') || 'same-thread');
288
- }
289
- function parseExecutionMode(value) {
290
- if (value === 'same-thread' || value === 'side-thread') {
291
- return value;
292
- }
293
- throw new Error('Flag --execution-mode must be same-thread or side-thread');
290
+ return (0, index_js_1.normalizeScheduledJobExecutionMode)(getFlag(flags, 'execution-mode'));
294
291
  }
295
292
  function print(asJson, output, payload, text) {
296
293
  output.write(asJson ? `${JSON.stringify(payload, null, 2)}\n` : `${text}\n`);
@@ -309,11 +306,11 @@ function printUsage(output) {
309
306
  }
310
307
  function formatJobLine(job) {
311
308
  const schedule = job.triggerType === 'cron' ? job.cronExpr || '' : job.runAt || '';
312
- return `${job.id} | ${job.enabled ? 'enabled' : 'disabled'} | ${job.executionMode} | ${schedule} | ${job.description}`;
309
+ return `${(0, job_id_js_1.toPublicScheduledJobId)(job.id)} | ${job.enabled ? 'enabled' : 'disabled'} | ${job.executionMode} | ${schedule} | ${job.description}`;
313
310
  }
314
311
  function formatJobDetails(job, latestRun) {
315
312
  return [
316
- `Job: ${job.id}`,
313
+ `Job: ${(0, job_id_js_1.toPublicScheduledJobId)(job.id)}`,
317
314
  `Workspace: ${job.workspaceId}`,
318
315
  `Platform: ${job.platform}`,
319
316
  `Thread: ${job.route.threadId || ''}`,
@@ -325,6 +322,12 @@ function formatJobDetails(job, latestRun) {
325
322
  latestRun ? `Latest run: ${latestRun.status} @ ${latestRun.triggeredAt}` : 'Latest run: none',
326
323
  ].join('\n');
327
324
  }
325
+ function presentJob(job) {
326
+ return {
327
+ ...job,
328
+ id: (0, job_id_js_1.toPublicScheduledJobId)(job.id),
329
+ };
330
+ }
328
331
  function formatError(error) {
329
332
  return error instanceof Error ? error.message : String(error);
330
333
  }
@@ -220,6 +220,10 @@ function bootstrapLocalCoreRuntime(options) {
220
220
  .map((plugin) => resolveAgentRuntime(plugin, kernel.context).runtime);
221
221
  const channelRuntime = resolveChannelRuntime(channelPlugin, kernel.context).channel;
222
222
  weixinChannelRuntime = resolveChannelRuntime(weixinChannelPlugin, kernel.context).channel;
223
+ const channelRuntimes = [
224
+ channelRuntime,
225
+ weixinChannelRuntime,
226
+ ];
223
227
  const knowledgeRuntime = kernel.plugins.isEnabled(knowledgePlugin.manifest.id)
224
228
  ? resolveKnowledgeRuntime(knowledgePlugin, kernel.context)
225
229
  : resolveKnowledgeRuntime((0, knowledge_noop_plugin_js_1.createBuiltinNoopKnowledgePlugin)(), kernel.context);
@@ -278,6 +282,7 @@ function bootstrapLocalCoreRuntime(options) {
278
282
  state,
279
283
  store,
280
284
  agentRuntimes,
285
+ channelRuntimes,
281
286
  channelRuntime,
282
287
  weixinChannelRuntime,
283
288
  knowledgeProvider,
@@ -2,10 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createBuiltinLarkChannelPlugin = createBuiltinLarkChannelPlugin;
4
4
  const local_core_lark_gateway_js_1 = require("../../channel/lark/local-core-lark-gateway.js");
5
+ const plugin_js_1 = require("../../channel/shared/plugin.js");
5
6
  function createBuiltinLarkChannelPlugin(options) {
6
- let runtime = null;
7
- let unsubscribeBridgeEvents = null;
8
- return {
7
+ return (0, plugin_js_1.createBuiltinChannelPlugin)({
9
8
  manifest: {
10
9
  id: 'builtin.channel-lark',
11
10
  kind: 'channel',
@@ -20,40 +19,20 @@ function createBuiltinLarkChannelPlugin(options) {
20
19
  ],
21
20
  },
22
21
  },
23
- capabilities: {
24
- channels: [
25
- {
26
- id: 'channel.lark',
27
- platform: 'lark',
28
- routeType: 'channel.chat',
29
- displayName: 'LocalCore Lark',
30
- },
31
- ],
32
- },
33
- createRuntime(ctx) {
34
- if (!runtime) {
35
- runtime = {
36
- channel: new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
37
- store: options.store,
38
- readConfig: options.readConfig,
39
- getWorkspaceRouter: options.getWorkspaceRouter,
40
- eventBus: ctx.bus,
41
- log: options.log,
42
- }),
43
- };
44
- unsubscribeBridgeEvents = ctx.bus.on('platform.bridge.updated', (event) => {
45
- void runtime?.channel.onBridgeEvent?.(event);
46
- });
47
- }
48
- return runtime;
49
- },
50
- async start() {
51
- await runtime?.channel.refreshBindings?.();
52
- },
53
- async stop() {
54
- unsubscribeBridgeEvents?.();
55
- unsubscribeBridgeEvents = null;
56
- runtime?.channel.close?.();
57
- },
58
- };
22
+ channels: [
23
+ {
24
+ id: 'channel.lark',
25
+ platform: 'lark',
26
+ routeType: 'channel.chat',
27
+ displayName: 'LocalCore Lark',
28
+ },
29
+ ],
30
+ createChannel: (ctx) => new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
31
+ store: options.store,
32
+ readConfig: options.readConfig,
33
+ getWorkspaceRouter: options.getWorkspaceRouter,
34
+ eventBus: ctx.bus,
35
+ log: options.log,
36
+ }),
37
+ });
59
38
  }
@@ -2,10 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createBuiltinWeixinChannelPlugin = createBuiltinWeixinChannelPlugin;
4
4
  const local_core_weixin_gateway_js_1 = require("../../channel/weixin/local-core-weixin-gateway.js");
5
+ const plugin_js_1 = require("../../channel/shared/plugin.js");
5
6
  function createBuiltinWeixinChannelPlugin(options) {
6
- let runtime = null;
7
- let unsubscribeBridgeEvents = null;
8
- return {
7
+ return (0, plugin_js_1.createBuiltinChannelPlugin)({
9
8
  manifest: {
10
9
  id: 'builtin.channel-weixin',
11
10
  kind: 'channel',
@@ -17,40 +16,20 @@ function createBuiltinWeixinChannelPlugin(options) {
17
16
  fields: [],
18
17
  },
19
18
  },
20
- capabilities: {
21
- channels: [
22
- {
23
- id: 'channel.weixin',
24
- platform: 'weixin',
25
- routeType: 'channel.chat',
26
- displayName: 'LocalCore WeChat',
27
- },
28
- ],
29
- },
30
- createRuntime(ctx) {
31
- if (!runtime) {
32
- runtime = {
33
- channel: new local_core_weixin_gateway_js_1.LocalCoreWeixinGateway({
34
- store: options.store,
35
- readConfig: options.readConfig,
36
- getWorkspaceRouter: options.getWorkspaceRouter,
37
- eventBus: ctx.bus,
38
- log: options.log,
39
- }),
40
- };
41
- unsubscribeBridgeEvents = ctx.bus.on('platform.bridge.updated', (event) => {
42
- void runtime?.channel.onBridgeEvent?.(event);
43
- });
44
- }
45
- return runtime;
46
- },
47
- async start() {
48
- await runtime?.channel.refreshBindings?.();
49
- },
50
- async stop() {
51
- unsubscribeBridgeEvents?.();
52
- unsubscribeBridgeEvents = null;
53
- runtime?.channel.close?.();
54
- },
55
- };
19
+ channels: [
20
+ {
21
+ id: 'channel.weixin',
22
+ platform: 'weixin',
23
+ routeType: 'channel.chat',
24
+ displayName: 'LocalCore WeChat',
25
+ },
26
+ ],
27
+ createChannel: (ctx) => new local_core_weixin_gateway_js_1.LocalCoreWeixinGateway({
28
+ store: options.store,
29
+ readConfig: options.readConfig,
30
+ getWorkspaceRouter: options.getWorkspaceRouter,
31
+ eventBus: ctx.bus,
32
+ log: options.log,
33
+ }),
34
+ });
56
35
  }
@@ -10,8 +10,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
10
10
  state;
11
11
  workspaceRouter;
12
12
  knowledgeProvider;
13
- channelRuntime;
14
- weixinChannelRuntime;
13
+ channelRuntimes;
15
14
  scheduler;
16
15
  kernel;
17
16
  runtime;
@@ -31,8 +30,8 @@ class LocalCoreController extends node_events_1.EventEmitter {
31
30
  this.kernel = this.runtime.kernel;
32
31
  this.knowledgeProvider = this.runtime.knowledgeProvider;
33
32
  this.workspaceRouter = this.runtime.workspaceRouter;
34
- this.channelRuntime = this.runtime.channelRuntime;
35
- this.weixinChannelRuntime = this.runtime.weixinChannelRuntime;
33
+ const runtimeChannels = this.runtime.channelRuntimes || [this.runtime.channelRuntime, this.runtime.weixinChannelRuntime];
34
+ this.channelRuntimes = new Map(runtimeChannels.filter(Boolean).map((runtime) => [runtime.platform, runtime]));
36
35
  this.scheduler = this.runtime.scheduler;
37
36
  this.runtimeDetection = new runtime_detection_service_js_1.RuntimeDetectionService({
38
37
  userDataPath,
@@ -99,8 +98,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
99
98
  return { status: 'running' };
100
99
  }
101
100
  async restartService() {
102
- await this.channelRuntime.refreshBindings?.();
103
- await this.weixinChannelRuntime.refreshBindings?.();
101
+ await this.refreshChannelBindings();
104
102
  await this.emitRuntime();
105
103
  return { status: 'running' };
106
104
  }
@@ -112,22 +110,19 @@ class LocalCoreController extends node_events_1.EventEmitter {
112
110
  }
113
111
  async saveRawConfigFile(raw) {
114
112
  const next = await this.state.saveRawConfigFile(raw);
115
- await this.channelRuntime.refreshBindings?.();
116
- await this.weixinChannelRuntime.refreshBindings?.();
113
+ await this.refreshChannelBindings();
117
114
  await this.emitRuntime();
118
115
  return next;
119
116
  }
120
117
  async saveStructuredConfigFile(config) {
121
118
  const next = await this.state.saveStructuredConfigFile(config);
122
- await this.channelRuntime.refreshBindings?.();
123
- await this.weixinChannelRuntime.refreshBindings?.();
119
+ await this.refreshChannelBindings();
124
120
  await this.emitRuntime();
125
121
  return next;
126
122
  }
127
123
  async saveSettings(input) {
128
124
  const settings = await this.state.saveSettings(input);
129
- await this.channelRuntime.refreshBindings?.();
130
- await this.weixinChannelRuntime.refreshBindings?.();
125
+ await this.refreshChannelBindings();
131
126
  await this.emitRuntime();
132
127
  return settings;
133
128
  }
@@ -311,25 +306,22 @@ class LocalCoreController extends node_events_1.EventEmitter {
311
306
  }
312
307
  async listChannelGatewayStatuses(platform) {
313
308
  if (!platform) {
314
- const [larkStatuses, weixinStatuses] = await Promise.all([
315
- this.channelRuntime.listStatuses(),
316
- this.weixinChannelRuntime.listStatuses(),
317
- ]);
318
- return [...larkStatuses, ...weixinStatuses];
309
+ const statuses = await Promise.all([...this.channelRuntimes.values()].map((runtime) => runtime.listStatuses()));
310
+ return statuses.flat();
319
311
  }
320
312
  return this.resolveChannelRuntime(platform).listStatuses();
321
313
  }
322
- async getChannelGatewayStatus(platform, workspaceId) {
323
- return this.resolveChannelRuntime(platform).getStatus(workspaceId);
314
+ async getChannelGatewayStatus(platform, workspaceId, instanceId) {
315
+ return this.resolveChannelRuntime(platform).getStatus(workspaceId, instanceId);
324
316
  }
325
- async testChannelConnection(platform, workspaceId) {
326
- return this.resolveChannelRuntime(platform).testConnection(workspaceId);
317
+ async testChannelConnection(platform, workspaceId, instanceId) {
318
+ return this.resolveChannelRuntime(platform).testConnection(workspaceId, instanceId);
327
319
  }
328
- async enableChannelGateway(platform, workspaceId) {
329
- return this.resolveChannelRuntime(platform).enable(workspaceId);
320
+ async enableChannelGateway(platform, workspaceId, instanceId) {
321
+ return this.resolveChannelRuntime(platform).enable(workspaceId, instanceId);
330
322
  }
331
- async disableChannelGateway(platform, workspaceId) {
332
- return this.resolveChannelRuntime(platform).disable(workspaceId);
323
+ async disableChannelGateway(platform, workspaceId, instanceId) {
324
+ return this.resolveChannelRuntime(platform).disable(workspaceId, instanceId);
333
325
  }
334
326
  async listChannelPendingPairings(platform, workspaceId) {
335
327
  return this.resolveChannelRuntime(platform).listPendingPairings(workspaceId);
@@ -384,28 +376,46 @@ class LocalCoreController extends node_events_1.EventEmitter {
384
376
  }
385
377
  return runtime.sendOutboundMessage(workspaceId, input);
386
378
  }
387
- async getWeixinQrCode(workspaceId) {
388
- const runtime = this.weixinChannelRuntime;
389
- return runtime.getQrCode(workspaceId);
379
+ async getWeixinQrCode(workspaceId, instanceId) {
380
+ return this.getChannelQrCode('weixin', workspaceId, instanceId);
390
381
  }
391
- async checkWeixinQrCodeStatus(workspaceId, ticket) {
392
- const runtime = this.weixinChannelRuntime;
393
- return runtime.checkQrCodeStatus(workspaceId, ticket);
382
+ async checkWeixinQrCodeStatus(workspaceId, ticket, instanceId) {
383
+ return this.checkChannelQrCodeStatus('weixin', workspaceId, ticket, instanceId);
384
+ }
385
+ async getLarkQrCode(workspaceId, instanceId) {
386
+ return this.getChannelQrCode('lark', workspaceId, instanceId);
387
+ }
388
+ async checkLarkQrCodeStatus(workspaceId, ticket, instanceId) {
389
+ return this.checkChannelQrCodeStatus('lark', workspaceId, ticket, instanceId);
390
+ }
391
+ async getChannelQrCode(platform, workspaceId, instanceId) {
392
+ const runtime = this.resolveChannelRuntime(platform);
393
+ if (!runtime.getQrCode) {
394
+ throw new Error(`Channel platform does not support QR setup: ${platform}`);
395
+ }
396
+ return runtime.getQrCode(workspaceId, instanceId);
397
+ }
398
+ async checkChannelQrCodeStatus(platform, workspaceId, ticket, instanceId) {
399
+ const runtime = this.resolveChannelRuntime(platform);
400
+ if (!runtime.checkQrCodeStatus) {
401
+ throw new Error(`Channel platform does not support QR setup: ${platform}`);
402
+ }
403
+ return runtime.checkQrCodeStatus(workspaceId, ticket, instanceId);
394
404
  }
395
405
  async listLarkGatewayStatuses() {
396
406
  return this.listChannelGatewayStatuses('lark');
397
407
  }
398
- async getLarkGatewayStatus(workspaceId) {
399
- return this.getChannelGatewayStatus('lark', workspaceId);
408
+ async getLarkGatewayStatus(workspaceId, instanceId) {
409
+ return this.getChannelGatewayStatus('lark', workspaceId, instanceId);
400
410
  }
401
- async testLarkConnection(workspaceId) {
402
- return this.testChannelConnection('lark', workspaceId);
411
+ async testLarkConnection(workspaceId, instanceId) {
412
+ return this.testChannelConnection('lark', workspaceId, instanceId);
403
413
  }
404
- async enableLarkGateway(workspaceId) {
405
- return this.enableChannelGateway('lark', workspaceId);
414
+ async enableLarkGateway(workspaceId, instanceId) {
415
+ return this.enableChannelGateway('lark', workspaceId, instanceId);
406
416
  }
407
- async disableLarkGateway(workspaceId) {
408
- return this.disableChannelGateway('lark', workspaceId);
417
+ async disableLarkGateway(workspaceId, instanceId) {
418
+ return this.disableChannelGateway('lark', workspaceId, instanceId);
409
419
  }
410
420
  async listLarkPendingPairings(workspaceId) {
411
421
  return this.listChannelPendingPairings('lark', workspaceId);
@@ -459,12 +469,13 @@ class LocalCoreController extends node_events_1.EventEmitter {
459
469
  this.handlingLog = false;
460
470
  }
461
471
  }
472
+ async refreshChannelBindings() {
473
+ await Promise.all([...this.channelRuntimes.values()].map((runtime) => runtime.refreshBindings?.()));
474
+ }
462
475
  resolveChannelRuntime(platform) {
463
- if (platform === this.channelRuntime.platform) {
464
- return this.channelRuntime;
465
- }
466
- if (platform === this.weixinChannelRuntime.platform) {
467
- return this.weixinChannelRuntime;
476
+ const runtime = this.channelRuntimes.get(platform);
477
+ if (runtime) {
478
+ return runtime;
468
479
  }
469
480
  throw new Error(`Unsupported channel platform: ${platform}`);
470
481
  }