@kafca/agentdock 0.1.24 → 0.1.25

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 (83) hide show
  1. package/README.md +2 -0
  2. package/dist/renderer/assets/{Badge-CXcUMqb-.js → Badge-Bn2h-Qkf.js} +1 -1
  3. package/dist/renderer/assets/{Chat-BRxdPzi-.js → Chat-Cgo9aneg.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-sWGBIfIb.js → ChatMarkdown-CYzHfSiR.js} +1 -1
  5. package/dist/renderer/assets/{Config-DqxuxIGg.js → Config-DljB2-eP.js} +1 -1
  6. package/dist/renderer/assets/{CronList-OgHxbti5.js → CronList-DeVbiY9a.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-DmklXiH1.js → Dashboard-fSqx5sm8.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BvDmi8dq.js → EmptyState-DgqKDwlq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Bj59Hdpo.js → KnowledgeDetail-u8s2SmPV.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CigQ-nSo.js → KnowledgeHome-BdQw8nbY.js} +1 -1
  11. package/dist/renderer/assets/{Logs-Bfc7gBpY.js → Logs-DSbndMpy.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CfmiFknz.js → Modal-0d0EmAuK.js} +1 -1
  13. package/dist/renderer/assets/{Page-BxtLEw_b.js → Page-CVbOH_Kh.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-CcUphxGm.js → ProjectDetail-C91tF7uA.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-CVkvDIfy.js → ProjectList-D78Pc-BQ.js} +1 -1
  16. package/dist/renderer/assets/{Select-DgQMZ0MD.js → Select-BbKzAIuO.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-CRmGKfBx.js → SessionChat-BERvL5e4.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-D0E8XbYw.js → SessionList-Bomz335M.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-CLr3amU2.js +23 -0
  20. package/dist/renderer/assets/{Workspace-Q4g4rlRT.js → Workspace-In91z4W2.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-BySN_zhF.js → arrow-left-FBnVKKBl.js} +1 -1
  22. package/dist/renderer/assets/{book-open-BGlrnrTc.js → book-open-4_CuP6Sa.js} +1 -1
  23. package/dist/renderer/assets/{check-BiJogEa5.js → check-vsoVjvac.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-D2fHRJv6.js → chevron-down-DFpmFTJW.js} +1 -1
  25. package/dist/renderer/assets/{circle-CIcjixmI.js → circle-vRULegER.js} +1 -1
  26. package/dist/renderer/assets/{desktop-DAXY9Bxv.js → desktop-BkMW-h5p.js} +2 -2
  27. package/dist/renderer/assets/{desktop-DyA_NYew.js → desktop-ul0Tpzyc.js} +1 -1
  28. package/dist/renderer/assets/{funnel-BUzwb-id.js → funnel-Gu0E_hJ_.js} +1 -1
  29. package/dist/renderer/assets/{heart-DZEQ1z_V.js → heart-DsdofOHf.js} +1 -1
  30. package/dist/renderer/assets/{index-CB_KyLOr.js → index-BD3aCyBa.js} +3 -3
  31. package/dist/renderer/assets/{index-DgsIo4vJ.js → index-pJUFobU7.js} +1 -1
  32. package/dist/renderer/assets/{pencil-DGL4agcl.js → pencil-DbKmV8nD.js} +1 -1
  33. package/dist/renderer/assets/{play-DzasCLM4.js → play-BDMsRxSm.js} +1 -1
  34. package/dist/renderer/assets/{plug-dubNMe_m.js → plug-t2dMl78-.js} +1 -1
  35. package/dist/renderer/assets/{plus-DN4kML4k.js → plus-BUb6zOAe.js} +1 -1
  36. package/dist/renderer/assets/projects-4F6D_Q8t.js +1 -0
  37. package/dist/renderer/assets/{save-CNIc9tjl.js → save-C-_5rQdv.js} +1 -1
  38. package/dist/renderer/assets/{search-C0EGbF3I.js → search-R3ekHFPM.js} +1 -1
  39. package/dist/renderer/assets/{server-BMxQ4zmc.js → server-BTuvHf5n.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Bf5O-iMi.js → session-utils-ClX6VPFG.js} +1 -1
  41. package/dist/renderer/assets/{sessions-BMSs6rEI.js → sessions-AFKIC-ub.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-i3Tf1TUz.js → shield-check-CqMW8CHe.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-SKrcp18f.js → trash-2-BNLcHyUw.js} +1 -1
  44. package/dist/renderer/assets/{user-cNUZddjZ.js → user-BCCN8FYK.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-Dyb84ANR.js → wifi-off-BsNsx3oX.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
  48. package/dist-electron/electron/architecture-docs.test.js +56 -0
  49. package/dist-electron/electron/channel-content.test.js +8 -0
  50. package/dist-electron/electron/lac-cli.test.js +248 -0
  51. package/dist-electron/electron/local-core-refactor.test.js +666 -3
  52. package/dist-electron/electron/plugin-kernel.test.js +108 -0
  53. package/dist-electron/electron/renderer-packaging.test.js +77 -0
  54. package/dist-electron/electron/workspace-task-store.test.js +57 -0
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
  56. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
  57. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
  58. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
  59. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +30 -18
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
  62. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +15 -3
  63. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +11 -0
  64. package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
  65. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +8 -1
  66. package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -1
  67. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
  68. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  69. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
  70. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
  71. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
  72. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
  73. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
  74. package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
  75. package/dist-electron/shared/desktop.js +7 -3
  76. package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
  77. package/dist-electron/src/pages/Threads/thread-chat-model.js +1 -0
  78. package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
  79. package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
  80. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +277 -0
  81. package/package.json +4 -2
  82. package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +0 -23
  83. package/dist/renderer/assets/projects-migPtfRe.js +0 -1
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalAiCoreServer = void 0;
4
4
  const node_http_1 = require("node:http");
5
+ const server_routes_js_1 = require("./server-routes.js");
5
6
  function json(res, statusCode, data, ok = true, error) {
6
7
  res.statusCode = statusCode;
7
8
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
@@ -109,43 +110,46 @@ class LocalAiCoreServer {
109
110
  const url = new URL(req.url || '/', `http://${this.host}:${this.port}`);
110
111
  const path = url.pathname;
111
112
  try {
112
- if (req.method === 'GET' && path === '/api/local/v1/health') {
113
- json(res, 200, { name: 'local-ai-core', version: '0.1.0' });
113
+ const route = (0, server_routes_js_1.parseLocalAiCoreRoute)(req.method, path);
114
+ if (route) {
115
+ await this.handleParsedRoute(route, req, res, url);
114
116
  return;
115
117
  }
116
- if (req.method === 'GET' && path === '/api/local/v1/runtime') {
118
+ json(res, 404, null, false, `Unknown route: ${path}`);
119
+ }
120
+ catch (error) {
121
+ json(res, 500, null, false, error instanceof Error ? error.message : String(error));
122
+ }
123
+ }
124
+ async handleParsedRoute(route, req, res, url) {
125
+ switch (route.name) {
126
+ case 'health':
127
+ json(res, 200, { name: 'local-ai-core', version: '0.1.0' });
128
+ return;
129
+ case 'runtime.status':
117
130
  json(res, 200, await this.bindings.getRuntimeStatus());
118
131
  return;
119
- }
120
- if (req.method === 'POST' && path === '/api/local/v1/runtime/service/start') {
132
+ case 'runtime.service.start':
121
133
  json(res, 200, await this.bindings.startService());
122
134
  return;
123
- }
124
- if (req.method === 'POST' && path === '/api/local/v1/runtime/service/stop') {
135
+ case 'runtime.service.stop':
125
136
  json(res, 200, await this.bindings.stopService());
126
137
  return;
127
- }
128
- if (req.method === 'POST' && path === '/api/local/v1/runtime/service/restart') {
138
+ case 'runtime.service.restart':
129
139
  json(res, 200, await this.bindings.restartService());
130
140
  return;
131
- }
132
- if (req.method === 'GET' && path === '/api/local/v1/runtime/logs') {
141
+ case 'runtime.logs': {
133
142
  const limit = Number(url.searchParams.get('limit') || '200');
134
143
  json(res, 200, this.bindings.getLogs(limit));
135
144
  return;
136
145
  }
137
- if (req.method === 'GET' && path === '/api/local/v1/runtime/agent-runtimes') {
138
- json(res, 200, await this.runtimeDetectionResponse());
139
- return;
140
- }
141
- if (req.method === 'GET' && path === '/api/local/v1/runtimes') {
146
+ case 'runtime.agent-runtimes':
147
+ case 'runtimes.list':
142
148
  json(res, 200, await this.runtimeDetectionResponse());
143
149
  return;
144
- }
145
- if (req.method === 'GET' && path.startsWith('/api/local/v1/runtimes/')) {
146
- const runtimeId = decodeURIComponent(path.slice('/api/local/v1/runtimes/'.length));
150
+ case 'runtimes.detail': {
147
151
  const runtimes = await this.bindings.listInstalledAgentRuntimes();
148
- const runtime = runtimes.find((entry) => entry.runtimeId === runtimeId || entry.agentType === runtimeId);
152
+ const runtime = runtimes.find((entry) => entry.runtimeId === route.runtimeId || entry.agentType === route.runtimeId);
149
153
  if (!runtime) {
150
154
  json(res, 404, null, false, 'Runtime not found');
151
155
  return;
@@ -153,205 +157,142 @@ class LocalAiCoreServer {
153
157
  json(res, 200, runtime);
154
158
  return;
155
159
  }
156
- if (req.method === 'POST' && path === '/api/local/v1/runtimes/refresh') {
160
+ case 'runtimes.refresh': {
157
161
  const runtimes = await this.bindings.refreshInstalledAgentRuntimes();
158
162
  json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning() });
159
163
  return;
160
164
  }
161
- if (req.method === 'POST' && path.startsWith('/api/local/v1/runtimes/') && path.endsWith('/refresh')) {
162
- const runtimeId = decodeURIComponent(path.slice('/api/local/v1/runtimes/'.length, -'/refresh'.length));
163
- const runtimes = await this.bindings.refreshInstalledAgentRuntimes(runtimeId);
164
- json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning(runtimeId) });
165
+ case 'runtimes.refresh-one': {
166
+ const runtimes = await this.bindings.refreshInstalledAgentRuntimes(route.runtimeId);
167
+ json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning(route.runtimeId) });
165
168
  return;
166
169
  }
167
- if (req.method === 'GET' && path === '/api/local/v1/runtime/config') {
170
+ case 'runtime.config.read':
168
171
  json(res, 200, await this.bindings.readConfigFile());
169
172
  return;
170
- }
171
- if (req.method === 'POST' && path === '/api/local/v1/runtime/config/raw') {
173
+ case 'runtime.config.save-raw': {
172
174
  const body = await readJsonBody(req);
173
175
  json(res, 200, await this.bindings.saveRawConfigFile(String(body.raw || '')));
174
176
  return;
175
177
  }
176
- if (req.method === 'POST' && path === '/api/local/v1/runtime/config/structured') {
178
+ case 'runtime.config.save-structured': {
177
179
  const body = await readJsonBody(req);
178
180
  json(res, 200, await this.bindings.saveStructuredConfigFile((body.config || {})));
179
181
  return;
180
182
  }
181
- if (req.method === 'POST' && path === '/api/local/v1/runtime/settings') {
183
+ case 'runtime.settings.save': {
182
184
  const body = await readJsonBody(req);
183
185
  json(res, 200, await this.bindings.saveSettings(body));
184
186
  return;
185
187
  }
186
- if (req.method === 'GET' && path.startsWith('/api/local/v1/platforms/')) {
187
- const suffix = path.slice('/api/local/v1/platforms/'.length);
188
- const segments = suffix.split('/').filter(Boolean).map((segment) => decodeURIComponent(segment));
189
- const [platform = '', workspaceOrCollection = '', action = ''] = segments;
190
- const instanceId = String(url.searchParams.get('instance_id') || url.searchParams.get('instanceId') || '').trim() || undefined;
191
- if (!platform) {
192
- json(res, 404, null, false, 'Platform not found');
193
- return;
194
- }
195
- if (segments.length === 1) {
196
- json(res, 200, { gateways: await this.bindings.listChannelGatewayStatuses(platform) });
197
- return;
198
- }
199
- if (workspaceOrCollection === 'pairings' && segments.length === 2) {
200
- const workspaceId = String(url.searchParams.get('workspace_id') || '');
201
- json(res, 200, { pairings: await this.bindings.listChannelPendingPairings(platform, workspaceId || undefined) });
202
- return;
203
- }
204
- if (workspaceOrCollection === 'users' && segments.length === 2) {
205
- const workspaceId = String(url.searchParams.get('workspace_id') || '');
206
- json(res, 200, { users: await this.bindings.listChannelAuthorizedUsers(platform, workspaceId || undefined) });
207
- return;
208
- }
209
- if (segments.length === 2) {
210
- json(res, 200, await this.bindings.getChannelGatewayStatus(platform, workspaceOrCollection, instanceId));
211
- return;
212
- }
213
- if (segments.length === 4 && action === 'qrcode' && segments[3] === 'status') {
214
- const ticket = String(url.searchParams.get('ticket') || '');
215
- if (!ticket) {
216
- json(res, 400, null, false, 'Missing ticket parameter');
217
- return;
218
- }
219
- json(res, 200, await this.bindings.checkChannelQrCodeStatus(platform, workspaceOrCollection, ticket, instanceId));
220
- return;
221
- }
222
- }
223
- if (req.method === 'POST' && path.startsWith('/api/local/v1/platforms/')) {
224
- const suffix = path.slice('/api/local/v1/platforms/'.length);
225
- const segments = suffix.split('/').filter(Boolean).map((segment) => decodeURIComponent(segment));
226
- const [platform = '', workspaceOrCollection = '', action = ''] = segments;
227
- const instanceId = String(url.searchParams.get('instance_id') || url.searchParams.get('instanceId') || '').trim() || undefined;
228
- if (!platform) {
229
- json(res, 404, null, false, 'Platform not found');
230
- return;
231
- }
232
- if (workspaceOrCollection === 'pairings' && action === 'approve') {
233
- const body = await readJsonBody(req);
234
- json(res, 200, await this.bindings.approveChannelPairing(platform, String(body.code || '')));
235
- return;
236
- }
237
- if (workspaceOrCollection === 'pairings' && action === 'reject') {
238
- const body = await readJsonBody(req);
239
- json(res, 200, await this.bindings.rejectChannelPairing(platform, String(body.code || '')));
240
- return;
241
- }
242
- if (action === 'test') {
243
- json(res, 200, await this.bindings.testChannelConnection(platform, workspaceOrCollection, instanceId));
244
- return;
245
- }
246
- if (action === 'enable') {
247
- json(res, 200, await this.bindings.enableChannelGateway(platform, workspaceOrCollection, instanceId));
248
- return;
249
- }
250
- if (action === 'disable') {
251
- json(res, 200, await this.bindings.disableChannelGateway(platform, workspaceOrCollection, instanceId));
252
- return;
253
- }
254
- if (action === 'files') {
255
- const body = await readJsonBody(req);
256
- json(res, 200, await this.bindings.sendChannelFile(platform, workspaceOrCollection, body));
257
- return;
258
- }
259
- if (action === 'messages') {
260
- const body = await readJsonBody(req);
261
- json(res, 200, await this.bindings.sendChannelMessage(platform, workspaceOrCollection, body));
262
- return;
263
- }
264
- if (segments.length === 3 && workspaceOrCollection !== 'pairings' && action === 'qrcode') {
265
- json(res, 200, await this.bindings.getChannelQrCode(platform, workspaceOrCollection, instanceId));
266
- return;
267
- }
268
- }
269
- if (req.method === 'GET' && path === '/api/local/v1/scheduler/jobs') {
188
+ case 'scheduler.jobs.list': {
270
189
  const workspaceId = String(url.searchParams.get('workspace_id') || '');
271
190
  json(res, 200, { jobs: await this.bindings.listScheduledJobs(workspaceId || undefined) });
272
191
  return;
273
192
  }
274
- if (req.method === 'POST' && path === '/api/local/v1/scheduler/jobs') {
193
+ case 'scheduler.jobs.create': {
275
194
  const body = await readJsonBody(req);
276
195
  json(res, 200, await this.bindings.createScheduledJob(body));
277
196
  return;
278
197
  }
279
- if (req.method === 'GET'
280
- && path.startsWith('/api/local/v1/scheduler/jobs/')
281
- && !path.endsWith('/runs')
282
- && !path.endsWith('/run')) {
283
- const jobId = decodeURIComponent(path.slice('/api/local/v1/scheduler/jobs/'.length));
284
- json(res, 200, await this.bindings.getScheduledJob(jobId));
198
+ case 'scheduler.job.get':
199
+ json(res, 200, await this.bindings.getScheduledJob(route.jobId));
200
+ return;
201
+ case 'scheduler.job.runs':
202
+ json(res, 200, { runs: await this.bindings.listScheduledJobRuns(route.jobId) });
203
+ return;
204
+ case 'scheduler.job.run':
205
+ json(res, 200, await this.bindings.runScheduledJob(route.jobId));
206
+ return;
207
+ case 'scheduler.job.update': {
208
+ const body = await readJsonBody(req);
209
+ json(res, 200, await this.bindings.updateScheduledJob(route.jobId, body));
285
210
  return;
286
211
  }
287
- if (req.method === 'GET' && path.startsWith('/api/local/v1/scheduler/jobs/') && path.endsWith('/runs')) {
288
- const jobId = decodeURIComponent(path.slice('/api/local/v1/scheduler/jobs/'.length, -'/runs'.length));
289
- json(res, 200, { runs: await this.bindings.listScheduledJobRuns(jobId) });
212
+ case 'scheduler.job.delete':
213
+ json(res, 200, await this.bindings.deleteScheduledJob(route.jobId));
214
+ return;
215
+ case 'threads.list': {
216
+ const workspaceId = String(url.searchParams.get('workspace_id') || '');
217
+ json(res, 200, { threads: workspaceId ? await this.bindings.listThreads(workspaceId) : [] });
290
218
  return;
291
219
  }
292
- if (req.method === 'POST' && path.startsWith('/api/local/v1/scheduler/jobs/') && path.endsWith('/run')) {
293
- const jobId = decodeURIComponent(path.slice('/api/local/v1/scheduler/jobs/'.length, -'/run'.length));
294
- json(res, 200, await this.bindings.runScheduledJob(jobId));
220
+ case 'threads.create': {
221
+ const body = await readJsonBody(req);
222
+ json(res, 200, await this.bindings.createThread(String(body.workspaceId || ''), String(body.title || '') || undefined));
295
223
  return;
296
224
  }
297
- if (req.method === 'PATCH' && path.startsWith('/api/local/v1/scheduler/jobs/')) {
298
- const jobId = decodeURIComponent(path.slice('/api/local/v1/scheduler/jobs/'.length));
225
+ case 'thread.get':
226
+ json(res, 200, await this.bindings.getThread(route.threadId));
227
+ return;
228
+ case 'thread.update-knowledge-bases': {
299
229
  const body = await readJsonBody(req);
300
- json(res, 200, await this.bindings.updateScheduledJob(jobId, body));
230
+ const knowledgeBaseIds = Array.isArray(body.knowledgeBaseIds)
231
+ ? body.knowledgeBaseIds.map((value) => String(value || ''))
232
+ : [];
233
+ json(res, 200, await this.bindings.updateThreadKnowledgeBases(route.threadId, knowledgeBaseIds));
301
234
  return;
302
235
  }
303
- if (req.method === 'DELETE' && path.startsWith('/api/local/v1/scheduler/jobs/')) {
304
- const jobId = decodeURIComponent(path.slice('/api/local/v1/scheduler/jobs/'.length));
305
- json(res, 200, await this.bindings.deleteScheduledJob(jobId));
236
+ case 'thread.rename': {
237
+ const body = await readJsonBody(req);
238
+ json(res, 200, await this.bindings.renameThread(route.threadId, String(body.title || '')));
306
239
  return;
307
240
  }
308
- if (req.method === 'GET' && path === '/api/local/v1/workspaces') {
309
- json(res, 200, { workspaces: await this.bindings.listWorkspaces() });
241
+ case 'thread.delete':
242
+ json(res, 200, await this.bindings.deleteThread(route.threadId));
243
+ return;
244
+ case 'thread.messages.send': {
245
+ const body = await readJsonBody(req);
246
+ json(res, 200, await this.bindings.sendThreadMessage(route.threadId, String(body.content || '')));
310
247
  return;
311
248
  }
312
- if (req.method === 'GET' && path === '/api/local/v1/workspace-registry') {
313
- json(res, 200, { workspaces: await this.bindings.listWorkspaceRegistry() });
249
+ case 'thread.actions.send': {
250
+ const body = await readJsonBody(req);
251
+ json(res, 200, await this.bindings.sendThreadAction(route.threadId, String(body.content || '')));
314
252
  return;
315
253
  }
316
- if (req.method === 'POST' && path === '/api/local/v1/workspace-registry') {
254
+ case 'run.interrupt':
255
+ json(res, 200, await this.bindings.interruptRun(route.runId));
256
+ return;
257
+ case 'workspaces.list':
258
+ json(res, 200, { workspaces: await this.bindings.listWorkspaces() });
259
+ return;
260
+ case 'workspace.streaming-probe':
261
+ json(res, 200, await this.bindings.probeWorkspaceStreaming(route.workspaceId));
262
+ return;
263
+ case 'workspace-registry.list':
264
+ json(res, 200, { workspaces: await this.bindings.listWorkspaceRegistry() });
265
+ return;
266
+ case 'workspace-registry.create': {
317
267
  const body = await readJsonBody(req);
318
268
  json(res, 200, await this.bindings.createWorkspaceRegistryEntry(body));
319
269
  return;
320
270
  }
321
- if (path.startsWith('/api/local/v1/workspace-registry/')) {
322
- const workspaceId = decodeURIComponent(path.slice('/api/local/v1/workspace-registry/'.length));
323
- if (req.method === 'GET') {
324
- json(res, 200, await this.bindings.getWorkspaceRegistryEntry(workspaceId));
325
- return;
326
- }
327
- if (req.method === 'PATCH') {
328
- const body = await readJsonBody(req);
329
- json(res, 200, await this.bindings.updateWorkspaceRegistryEntry(workspaceId, body));
330
- return;
331
- }
332
- if (req.method === 'DELETE') {
333
- json(res, 200, await this.bindings.deleteWorkspaceRegistryEntry(workspaceId));
334
- return;
335
- }
271
+ case 'workspace-registry.get':
272
+ json(res, 200, await this.bindings.getWorkspaceRegistryEntry(route.workspaceId));
273
+ return;
274
+ case 'workspace-registry.update': {
275
+ const body = await readJsonBody(req);
276
+ json(res, 200, await this.bindings.updateWorkspaceRegistryEntry(route.workspaceId, body));
277
+ return;
336
278
  }
337
- if (path.startsWith('/api/local/v1/workspace-security/')) {
338
- const workspaceId = decodeURIComponent(path.slice('/api/local/v1/workspace-security/'.length));
339
- if (req.method === 'GET') {
340
- json(res, 200, await this.bindings.getWorkspaceSecuritySettings(workspaceId));
341
- return;
342
- }
343
- if (req.method === 'PATCH') {
344
- const body = await readJsonBody(req);
345
- json(res, 200, await this.bindings.updateWorkspaceSecuritySettings(workspaceId, body));
346
- return;
347
- }
279
+ case 'workspace-registry.delete':
280
+ json(res, 200, await this.bindings.deleteWorkspaceRegistryEntry(route.workspaceId));
281
+ return;
282
+ case 'workspace-security.get':
283
+ json(res, 200, await this.bindings.getWorkspaceSecuritySettings(route.workspaceId));
284
+ return;
285
+ case 'workspace-security.update': {
286
+ const body = await readJsonBody(req);
287
+ json(res, 200, await this.bindings.updateWorkspaceSecuritySettings(route.workspaceId, body));
288
+ return;
348
289
  }
349
- if (req.method === 'POST' && path === '/api/local/v1/security/command-risk') {
290
+ case 'security.command-risk.classify': {
350
291
  const body = await readJsonBody(req);
351
292
  json(res, 200, await this.bindings.classifyCommand(String(body.command || ''), String(body.workspaceId || '') || undefined));
352
293
  return;
353
294
  }
354
- if (req.method === 'GET' && path === '/api/local/v1/approvals') {
295
+ case 'approvals.list': {
355
296
  const statusParam = url.searchParams.get('status') || '';
356
297
  const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
357
298
  json(res, 200, await this.bindings.listApprovalRequests({
@@ -362,26 +303,20 @@ class LocalAiCoreServer {
362
303
  }));
363
304
  return;
364
305
  }
365
- if (req.method === 'POST' && path === '/api/local/v1/approvals') {
306
+ case 'approvals.create': {
366
307
  const body = await readJsonBody(req);
367
308
  json(res, 200, await this.bindings.createApprovalRequest(body));
368
309
  return;
369
310
  }
370
- if (path.startsWith('/api/local/v1/approvals/')) {
371
- const suffix = path.slice('/api/local/v1/approvals/'.length);
372
- if (req.method === 'POST' && suffix.endsWith('/resolve')) {
373
- const approvalId = decodeURIComponent(suffix.slice(0, -'/resolve'.length));
374
- const body = await readJsonBody(req);
375
- json(res, 200, await this.bindings.resolveApprovalRequest(approvalId, body));
376
- return;
377
- }
378
- if (req.method === 'GET') {
379
- const approvalId = decodeURIComponent(suffix);
380
- json(res, 200, await this.bindings.getApprovalRequest(approvalId));
381
- return;
382
- }
311
+ case 'approval.get':
312
+ json(res, 200, await this.bindings.getApprovalRequest(route.approvalId));
313
+ return;
314
+ case 'approval.resolve': {
315
+ const body = await readJsonBody(req);
316
+ json(res, 200, await this.bindings.resolveApprovalRequest(route.approvalId, body));
317
+ return;
383
318
  }
384
- if (req.method === 'GET' && path === '/api/local/v1/audit-events') {
319
+ case 'audit-events.list': {
385
320
  const typeParam = url.searchParams.get('type') || '';
386
321
  const type = typeParam ? typeParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
387
322
  json(res, 200, await this.bindings.listAuditEvents({
@@ -393,7 +328,7 @@ class LocalAiCoreServer {
393
328
  }));
394
329
  return;
395
330
  }
396
- if (req.method === 'GET' && path === '/api/local/v1/tasks') {
331
+ case 'tasks.list': {
397
332
  const statusParam = url.searchParams.get('status') || '';
398
333
  const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
399
334
  json(res, 200, await this.bindings.listAgentTasks({
@@ -404,189 +339,159 @@ class LocalAiCoreServer {
404
339
  }));
405
340
  return;
406
341
  }
407
- if (req.method === 'POST' && path === '/api/local/v1/tasks') {
342
+ case 'tasks.create': {
408
343
  const body = await readJsonBody(req);
409
344
  json(res, 200, await this.bindings.createAgentTask(body));
410
345
  return;
411
346
  }
412
- if (path.startsWith('/api/local/v1/tasks/')) {
413
- const taskId = decodeURIComponent(path.slice('/api/local/v1/tasks/'.length));
414
- if (req.method === 'GET') {
415
- json(res, 200, await this.bindings.getAgentTask(taskId));
416
- return;
417
- }
418
- if (req.method === 'PATCH') {
419
- const body = await readJsonBody(req);
420
- json(res, 200, await this.bindings.updateAgentTask(taskId, body));
421
- return;
422
- }
423
- }
424
- if (req.method === 'GET' && path === '/api/local/v1/threads') {
425
- const workspaceId = String(url.searchParams.get('workspace_id') || '');
426
- json(res, 200, { threads: workspaceId ? await this.bindings.listThreads(workspaceId) : [] });
347
+ case 'task.get':
348
+ json(res, 200, await this.bindings.getAgentTask(route.taskId));
427
349
  return;
428
- }
429
- if (req.method === 'POST' && path === '/api/local/v1/threads') {
350
+ case 'task.update': {
430
351
  const body = await readJsonBody(req);
431
- json(res, 200, await this.bindings.createThread(String(body.workspaceId || ''), String(body.title || '') || undefined));
432
- return;
433
- }
434
- if (req.method === 'GET' && path.startsWith('/api/local/v1/threads/')) {
435
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length));
436
- json(res, 200, await this.bindings.getThread(threadId));
437
- return;
438
- }
439
- if (req.method === 'PATCH' && path.startsWith('/api/local/v1/threads/') && path.endsWith('/knowledge-bases')) {
440
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length, -'/knowledge-bases'.length));
441
- const body = await readJsonBody(req);
442
- const knowledgeBaseIds = Array.isArray(body.knowledgeBaseIds)
443
- ? body.knowledgeBaseIds.map((value) => String(value || ''))
444
- : [];
445
- json(res, 200, await this.bindings.updateThreadKnowledgeBases(threadId, knowledgeBaseIds));
446
- return;
447
- }
448
- if (req.method === 'PATCH' && path.startsWith('/api/local/v1/threads/')) {
449
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length));
450
- const body = await readJsonBody(req);
451
- json(res, 200, await this.bindings.renameThread(threadId, String(body.title || '')));
452
- return;
453
- }
454
- if (req.method === 'DELETE' && path.startsWith('/api/local/v1/threads/')) {
455
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length));
456
- json(res, 200, await this.bindings.deleteThread(threadId));
457
- return;
458
- }
459
- if (req.method === 'POST' && path.startsWith('/api/local/v1/threads/') && path.endsWith('/messages')) {
460
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length, -'/messages'.length));
461
- const body = await readJsonBody(req);
462
- json(res, 200, await this.bindings.sendThreadMessage(threadId, String(body.content || '')));
463
- return;
464
- }
465
- if (req.method === 'POST' && path.startsWith('/api/local/v1/threads/') && path.endsWith('/actions')) {
466
- const threadId = decodeURIComponent(path.slice('/api/local/v1/threads/'.length, -'/actions'.length));
467
- const body = await readJsonBody(req);
468
- json(res, 200, await this.bindings.sendThreadAction(threadId, String(body.content || '')));
469
- return;
470
- }
471
- if (req.method === 'POST' && path.startsWith('/api/local/v1/runs/') && path.endsWith('/interrupt')) {
472
- const runId = decodeURIComponent(path.slice('/api/local/v1/runs/'.length, -'/interrupt'.length));
473
- json(res, 200, await this.bindings.interruptRun(runId));
352
+ json(res, 200, await this.bindings.updateAgentTask(route.taskId, body));
474
353
  return;
475
354
  }
476
- if (req.method === 'GET' && path === '/api/local/v1/knowledge/sources') {
355
+ case 'knowledge.sources.list':
477
356
  json(res, 200, { sources: await this.bindings.listKnowledgeSources() });
478
357
  return;
479
- }
480
- if (req.method === 'GET' && path === '/api/local/v1/knowledge/config') {
358
+ case 'knowledge.config.read':
481
359
  json(res, 200, await this.bindings.getKnowledgeConfig());
482
360
  return;
483
- }
484
- if (req.method === 'POST' && path === '/api/local/v1/knowledge/config') {
361
+ case 'knowledge.config.update': {
485
362
  const body = await readJsonBody(req);
486
363
  json(res, 200, await this.bindings.updateKnowledgeConfig(body));
487
364
  return;
488
365
  }
489
- if (req.method === 'GET' && path === '/api/local/v1/knowledge/folders') {
366
+ case 'knowledge.folders.list':
490
367
  json(res, 200, { folders: await this.bindings.listKnowledgeFolders() });
491
368
  return;
492
- }
493
- if (req.method === 'POST' && path === '/api/local/v1/knowledge/folders') {
369
+ case 'knowledge.folders.create': {
494
370
  const body = await readJsonBody(req);
495
371
  json(res, 200, await this.bindings.createKnowledgeFolder(body));
496
372
  return;
497
373
  }
498
- if (req.method === 'PATCH' && path.startsWith('/api/local/v1/knowledge/folders/')) {
499
- const folderId = decodeURIComponent(path.slice('/api/local/v1/knowledge/folders/'.length));
374
+ case 'knowledge.folder.update': {
500
375
  const body = await readJsonBody(req);
501
- json(res, 200, await this.bindings.updateKnowledgeFolder(folderId, body));
376
+ json(res, 200, await this.bindings.updateKnowledgeFolder(route.folderId, body));
502
377
  return;
503
378
  }
504
- if (req.method === 'DELETE' && path.startsWith('/api/local/v1/knowledge/folders/')) {
505
- const folderId = decodeURIComponent(path.slice('/api/local/v1/knowledge/folders/'.length));
506
- json(res, 200, await this.bindings.deleteKnowledgeFolder(folderId));
379
+ case 'knowledge.folder.delete':
380
+ json(res, 200, await this.bindings.deleteKnowledgeFolder(route.folderId));
507
381
  return;
508
- }
509
- if (req.method === 'GET' && path === '/api/local/v1/knowledge/bases') {
382
+ case 'knowledge.bases.list':
510
383
  json(res, 200, { bases: await this.bindings.listKnowledgeBases() });
511
384
  return;
512
- }
513
- if (req.method === 'POST' && path === '/api/local/v1/knowledge/bases') {
385
+ case 'knowledge.bases.create': {
514
386
  const body = await readJsonBody(req);
515
387
  json(res, 200, await this.bindings.createKnowledgeBase(body));
516
388
  return;
517
389
  }
518
- if (req.method === 'GET' && path.startsWith('/api/local/v1/knowledge/bases/') && !path.endsWith('/files') && !path.endsWith('/search')) {
519
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length));
520
- json(res, 200, await this.bindings.getKnowledgeBase(knowledgeBaseId));
390
+ case 'knowledge.base.get':
391
+ json(res, 200, await this.bindings.getKnowledgeBase(route.knowledgeBaseId));
521
392
  return;
522
- }
523
- if (req.method === 'PATCH' && path.startsWith('/api/local/v1/knowledge/bases/')) {
524
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length));
393
+ case 'knowledge.base.update': {
525
394
  const body = await readJsonBody(req);
526
- json(res, 200, await this.bindings.updateKnowledgeBase(knowledgeBaseId, body));
395
+ json(res, 200, await this.bindings.updateKnowledgeBase(route.knowledgeBaseId, body));
527
396
  return;
528
397
  }
529
- if (req.method === 'DELETE' && path.startsWith('/api/local/v1/knowledge/bases/') && !path.includes('/files/')) {
530
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length));
531
- json(res, 200, await this.bindings.deleteKnowledgeBase(knowledgeBaseId));
398
+ case 'knowledge.base.delete':
399
+ json(res, 200, await this.bindings.deleteKnowledgeBase(route.knowledgeBaseId));
532
400
  return;
533
- }
534
- if (req.method === 'GET' && path.startsWith('/api/local/v1/knowledge/bases/') && path.endsWith('/files')) {
535
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length, -'/files'.length));
536
- json(res, 200, { files: await this.bindings.listKnowledgeBaseFiles(knowledgeBaseId) });
401
+ case 'knowledge.base.files.list':
402
+ json(res, 200, { files: await this.bindings.listKnowledgeBaseFiles(route.knowledgeBaseId) });
537
403
  return;
538
- }
539
- if (req.method === 'POST' && path.startsWith('/api/local/v1/knowledge/bases/') && path.endsWith('/files')) {
540
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length, -'/files'.length));
404
+ case 'knowledge.base.files.upload': {
541
405
  const contentType = String(req.headers['content-type'] || '').trim();
542
406
  if (!contentType) {
543
407
  throw new Error('Upload content type is required.');
544
408
  }
545
409
  const body = await readRawBody(req);
546
- json(res, 200, { results: await this.bindings.uploadKnowledgeBaseFiles(knowledgeBaseId, { contentType, body }) });
410
+ json(res, 200, { results: await this.bindings.uploadKnowledgeBaseFiles(route.knowledgeBaseId, { contentType, body }) });
547
411
  return;
548
412
  }
549
- if (req.method === 'DELETE' && path.includes('/api/local/v1/knowledge/bases/') && path.includes('/files/')) {
550
- const prefix = '/api/local/v1/knowledge/bases/';
551
- const fileMarker = '/files/';
552
- const knowledgeBaseId = decodeURIComponent(path.slice(prefix.length, path.indexOf(fileMarker)));
553
- const fileId = decodeURIComponent(path.slice(path.indexOf(fileMarker) + fileMarker.length));
554
- json(res, 200, await this.bindings.deleteKnowledgeBaseFile(knowledgeBaseId, fileId));
413
+ case 'knowledge.base.file.delete':
414
+ json(res, 200, await this.bindings.deleteKnowledgeBaseFile(route.knowledgeBaseId, route.fileId));
555
415
  return;
556
- }
557
- if (req.method === 'POST' && path.startsWith('/api/local/v1/knowledge/bases/') && path.endsWith('/search')) {
558
- const knowledgeBaseId = decodeURIComponent(path.slice('/api/local/v1/knowledge/bases/'.length, -'/search'.length));
416
+ case 'knowledge.base.search': {
559
417
  const body = await readJsonBody(req);
560
- json(res, 200, { results: await this.bindings.searchKnowledgeBase(knowledgeBaseId, body) });
418
+ json(res, 200, { results: await this.bindings.searchKnowledgeBase(route.knowledgeBaseId, body) });
561
419
  return;
562
420
  }
563
- if (req.method === 'GET' && path === '/api/local/v1/capabilities') {
421
+ case 'capabilities.read':
564
422
  json(res, 200, await this.bindings.getCapabilities());
565
423
  return;
566
- }
567
- if (req.method === 'GET' && path === '/api/local/v1/capabilities/snapshot') {
424
+ case 'capabilities.snapshot':
568
425
  json(res, 200, await this.bindings.getCapabilitySnapshot());
569
426
  return;
570
- }
571
- if (req.method === 'GET' && path === '/api/local/v1/plugins/diagnostics') {
427
+ case 'plugins.diagnostics':
572
428
  json(res, 200, await this.bindings.getPluginDiagnostics());
573
429
  return;
430
+ case 'events.stream':
431
+ this.attachSseClient(res);
432
+ return;
433
+ case 'platform.gateways.list':
434
+ json(res, 200, { gateways: await this.bindings.listChannelGatewayStatuses(route.platform) });
435
+ return;
436
+ case 'platform.pairings.list': {
437
+ const workspaceId = String(url.searchParams.get('workspace_id') || '');
438
+ json(res, 200, { pairings: await this.bindings.listChannelPendingPairings(route.platform, workspaceId || undefined) });
439
+ return;
574
440
  }
575
- if (req.method === 'POST' && path.startsWith('/api/local/v1/workspaces/') && path.endsWith('/streaming-probe')) {
576
- const workspaceId = decodeURIComponent(path.slice('/api/local/v1/workspaces/'.length, -'/streaming-probe'.length));
577
- json(res, 200, await this.bindings.probeWorkspaceStreaming(workspaceId));
441
+ case 'platform.users.list': {
442
+ const workspaceId = String(url.searchParams.get('workspace_id') || '');
443
+ json(res, 200, { users: await this.bindings.listChannelAuthorizedUsers(route.platform, workspaceId || undefined) });
578
444
  return;
579
445
  }
580
- if (req.method === 'GET' && path === '/api/local/v1/events') {
581
- this.attachSseClient(res);
446
+ case 'platform.gateway.get':
447
+ json(res, 200, await this.bindings.getChannelGatewayStatus(route.platform, route.workspaceId, this.channelInstanceId(url)));
448
+ return;
449
+ case 'platform.qrcode.status': {
450
+ const ticket = String(url.searchParams.get('ticket') || '');
451
+ if (!ticket) {
452
+ json(res, 400, null, false, 'Missing ticket parameter');
453
+ return;
454
+ }
455
+ json(res, 200, await this.bindings.checkChannelQrCodeStatus(route.platform, route.workspaceId, ticket, this.channelInstanceId(url)));
582
456
  return;
583
457
  }
584
- json(res, 404, null, false, `Unknown route: ${path}`);
585
- }
586
- catch (error) {
587
- json(res, 500, null, false, error instanceof Error ? error.message : String(error));
458
+ case 'platform.pairing.approve': {
459
+ const body = await readJsonBody(req);
460
+ json(res, 200, await this.bindings.approveChannelPairing(route.platform, String(body.code || '')));
461
+ return;
462
+ }
463
+ case 'platform.pairing.reject': {
464
+ const body = await readJsonBody(req);
465
+ json(res, 200, await this.bindings.rejectChannelPairing(route.platform, String(body.code || '')));
466
+ return;
467
+ }
468
+ case 'platform.gateway.test':
469
+ json(res, 200, await this.bindings.testChannelConnection(route.platform, route.workspaceId, this.channelInstanceId(url)));
470
+ return;
471
+ case 'platform.gateway.enable':
472
+ json(res, 200, await this.bindings.enableChannelGateway(route.platform, route.workspaceId, this.channelInstanceId(url)));
473
+ return;
474
+ case 'platform.gateway.disable':
475
+ json(res, 200, await this.bindings.disableChannelGateway(route.platform, route.workspaceId, this.channelInstanceId(url)));
476
+ return;
477
+ case 'platform.file.send': {
478
+ const body = await readJsonBody(req);
479
+ json(res, 200, await this.bindings.sendChannelFile(route.platform, route.workspaceId, body));
480
+ return;
481
+ }
482
+ case 'platform.message.send': {
483
+ const body = await readJsonBody(req);
484
+ json(res, 200, await this.bindings.sendChannelMessage(route.platform, route.workspaceId, body));
485
+ return;
486
+ }
487
+ case 'platform.qrcode.create':
488
+ json(res, 200, await this.bindings.getChannelQrCode(route.platform, route.workspaceId, this.channelInstanceId(url)));
489
+ return;
588
490
  }
589
491
  }
492
+ channelInstanceId(url) {
493
+ return String(url.searchParams.get('instance_id') || url.searchParams.get('instanceId') || '').trim() || undefined;
494
+ }
590
495
  attachSseClient(res) {
591
496
  res.writeHead(200, {
592
497
  'Content-Type': 'text/event-stream',