@kafca/agentdock 0.1.56 → 0.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/renderer/assets/{Badge-DjcoXx2J.js → Badge-BfY1VaHS.js} +1 -1
- package/dist/renderer/assets/Card-DWA_Tww_.js +1 -0
- package/dist/renderer/assets/Config-nHj3g1U5.js +21 -0
- package/dist/renderer/assets/CronList-BwkA_I7v.js +1 -0
- package/dist/renderer/assets/Dashboard-D0_A7k02.js +41 -0
- package/dist/renderer/assets/{EmptyState-DIZLiccW.js → EmptyState-zatDjLj_.js} +1 -1
- package/dist/renderer/assets/HighlightedMarkdown-CsM5n70d.js +12 -0
- package/dist/renderer/assets/Input-R1XdFK1K.js +1 -0
- package/dist/renderer/assets/{KnowledgeDetail-CJ9IYOsa.js → KnowledgeDetail-Co5sxeGd.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-BLGPwnBO.js → KnowledgeHome-DwziPw5m.js} +3 -3
- package/dist/renderer/assets/Logs-NwZUtR_G.js +6 -0
- package/dist/renderer/assets/{Modal-CQiUpfl4.js → Modal-CrcIwIFL.js} +3 -3
- package/dist/renderer/assets/MonitorList-CS1uLS5A.js +1 -0
- package/dist/renderer/assets/Page-Dp0obeSw.js +1 -0
- package/dist/renderer/assets/{Select-D6z65IT3.js → Select-6mVQaf84.js} +2 -2
- package/dist/renderer/assets/ThreadChat-BFzbn3L6.js +86 -0
- package/dist/renderer/assets/Workspace-DCmo8BMm.js +19 -0
- package/dist/renderer/assets/{arrow-left-CV5cL3f4.js → arrow-left-9rjg_Hp4.js} +1 -1
- package/dist/renderer/assets/{book-open-DThvB7pj.js → book-open-Bu8GCHnG.js} +1 -1
- package/dist/renderer/assets/channels-BxVbG-6j.js +6 -0
- package/dist/renderer/assets/chevron-down-BE8uQmWM.js +11 -0
- package/dist/renderer/assets/index-B7TeF6Es.js +41 -0
- package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
- package/dist/renderer/assets/index-BkI9POxj.js +167 -0
- package/dist/renderer/assets/knowledge-Kl19SqGS.js +1 -0
- package/dist/renderer/assets/{pencil-Dv5WxhdC.js → pencil-CPB7SK1m.js} +1 -1
- package/dist/renderer/assets/{play-DnCnft83.js → play-DGAO4OCu.js} +1 -1
- package/dist/renderer/assets/{plus-DsxIagzS.js → plus-Bt26z-4q.js} +1 -1
- package/dist/renderer/assets/{save-CST01lIc.js → save-Une7GMxT.js} +1 -1
- package/dist/renderer/assets/{search-B_bqy6Wd.js → search-DE0agJjJ.js} +1 -1
- package/dist/renderer/assets/{shield-check-D6lG5Aoc.js → shield-check-De7xaXZh.js} +1 -1
- package/dist/renderer/assets/threads-BnsNL5ei.js +1 -0
- package/dist/renderer/assets/{trash-2-Cefngb6Q.js → trash-2-6iGF59Ac.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/packages/contracts/src/automation.js +2 -0
- package/dist-electron/packages/contracts/src/channels.js +2 -0
- package/dist-electron/packages/contracts/src/knowledge.js +2 -0
- package/dist-electron/packages/contracts/src/local-core.js +15 -0
- package/dist-electron/packages/contracts/src/runtime.js +2 -0
- package/dist-electron/packages/contracts/src/scheduler.js +2 -0
- package/dist-electron/packages/contracts/src/threads.js +2 -0
- package/dist-electron/packages/core-sdk/src/automation.js +32 -0
- package/dist-electron/packages/core-sdk/src/channels.js +121 -0
- package/dist-electron/packages/core-sdk/src/client.js +455 -0
- package/dist-electron/packages/core-sdk/src/index.js +43 -607
- package/dist-electron/packages/core-sdk/src/knowledge.js +85 -0
- package/dist-electron/packages/core-sdk/src/request.js +22 -0
- package/dist-electron/packages/core-sdk/src/runtime.js +214 -0
- package/dist-electron/packages/core-sdk/src/scheduler.js +32 -0
- package/dist-electron/packages/core-sdk/src/threads.js +67 -0
- package/dist-electron/packages/plugin-sdk/src/agents.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/automation.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/channels.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/index.js +21 -0
- package/dist-electron/packages/plugin-sdk/src/knowledge.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/runtime.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/scheduler.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/threads.js +2 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +5 -8
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +5 -5
- package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +12 -12
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +11 -11
- package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +7 -7
- package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +4 -4
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +1 -1
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +4 -4
- package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +6 -47
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +2 -5
- package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +33 -18
- package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +58 -539
- package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +112 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +5 -20
- package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +37 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/bridge-event-helpers.js +23 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/channel-keys.js +6 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +55 -512
- package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +149 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +92 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +9 -11
- package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
- package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +8 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
- package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
- package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
- package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
- package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
- package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
- package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
- package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
- package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +3 -2
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
- package/dist-electron/services/local-ai-core/src/scheduler/base-channel-schedule-adapter.js +77 -0
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +13 -79
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +2 -10
- package/dist-electron/services/local-ai-core/src/scheduler/run-polling.js +19 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -37
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +20 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +15 -28
- package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +12 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +13 -79
- package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
- package/dist-electron/shared/desktop.js +1 -64
- package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
- package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
- package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
- package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
- package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
- package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
- package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
- package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
- package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
- package/dist-electron/tests/electron/core-client.test.js +284 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
- package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
- package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
- package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +4 -4
- package/dist-electron/tests/integration/local-core-refactor.test.js +15 -8
- package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
- package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
- package/package.json +9 -4
- package/dist/renderer/assets/Chat-C5Bkm9-M.js +0 -2
- package/dist/renderer/assets/ChatMarkdown-uc36EzyD.js +0 -45
- package/dist/renderer/assets/Config-DHtQuuLy.js +0 -21
- package/dist/renderer/assets/CronList-BYUnhzSC.js +0 -1
- package/dist/renderer/assets/Dashboard-BPnGMDg3.js +0 -26
- package/dist/renderer/assets/Logs-Dyd2Uwlt.js +0 -1
- package/dist/renderer/assets/MonitorList-Byg3E3Gt.js +0 -1
- package/dist/renderer/assets/Page-7DxXvTMm.js +0 -1
- package/dist/renderer/assets/ProjectDetail-Dfr5s0Ja.js +0 -11
- package/dist/renderer/assets/ProjectList-CDDQjAQ_.js +0 -1
- package/dist/renderer/assets/SessionChat-s7CAaCPL.js +0 -1
- package/dist/renderer/assets/SessionList-D-OycKKS.js +0 -1
- package/dist/renderer/assets/ThreadChat-CLdFynB5.js +0 -28
- package/dist/renderer/assets/Workspace-xfT5z4j_.js +0 -19
- package/dist/renderer/assets/bot-CWQ5-ovl.js +0 -6
- package/dist/renderer/assets/check-DKM7zfWG.js +0 -6
- package/dist/renderer/assets/chevron-down-Dfw2KU87.js +0 -6
- package/dist/renderer/assets/circle-CoP4RA4s.js +0 -6
- package/dist/renderer/assets/desktop-CsDYrOOU.js +0 -1
- package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
- package/dist/renderer/assets/funnel-DpVLlG3g.js +0 -6
- package/dist/renderer/assets/heart-BMD36ejP.js +0 -6
- package/dist/renderer/assets/index-COxSYBjE.js +0 -182
- package/dist/renderer/assets/index-CTGTI-cm.js +0 -41
- package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
- package/dist/renderer/assets/projects-DgjHcNgt.js +0 -1
- package/dist/renderer/assets/server-ecH4GNwa.js +0 -11
- package/dist/renderer/assets/session-utils-Dd_WKMYW.js +0 -6
- package/dist/renderer/assets/sessions-BQ33Mv4k.js +0 -1
- package/dist/renderer/assets/user-D_8RSH-T.js +0 -6
- package/dist/renderer/assets/wifi-off-DB9D-pOu.js +0 -11
- package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +0 -13
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +0 -13
- package/dist-electron/src/api/client.js +0 -73
- package/dist-electron/src/api/sessions.js +0 -21
- package/dist-electron/src/lib/session-utils.js +0 -51
- package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
- package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.coreClient = exports.LOCAL_AI_CORE_ORIGIN = exports.LOCAL_AI_CORE_BASE = exports.LOCAL_CORE_EVENT_NAMES = void 0;
|
|
4
|
+
exports.normalizeLocalAiCoreBase = normalizeLocalAiCoreBase;
|
|
5
|
+
exports.createCoreClient = createCoreClient;
|
|
6
|
+
const DEFAULT_LOCAL_AI_CORE_ORIGIN = 'http://127.0.0.1:9831';
|
|
7
|
+
const DEFAULT_LOCAL_AI_CORE_BASE = `${DEFAULT_LOCAL_AI_CORE_ORIGIN}/api/local/v1`;
|
|
8
|
+
exports.LOCAL_CORE_EVENT_NAMES = [
|
|
9
|
+
'runtime.updated',
|
|
10
|
+
'runtime.detect.started',
|
|
11
|
+
'runtime.detect.completed',
|
|
12
|
+
'runtime.detect.failed',
|
|
13
|
+
'runtime.status.changed',
|
|
14
|
+
'thread.updated',
|
|
15
|
+
'thread.session.activated',
|
|
16
|
+
'message.created',
|
|
17
|
+
'message.updated',
|
|
18
|
+
'run.updated',
|
|
19
|
+
'scheduler.job.updated',
|
|
20
|
+
'scheduler.run.updated',
|
|
21
|
+
'automation.monitor.updated',
|
|
22
|
+
'automation.monitor.run.updated',
|
|
23
|
+
'presence.updated',
|
|
24
|
+
'stream.updated',
|
|
25
|
+
];
|
|
26
|
+
function normalizeLocalAiCoreBase(baseUrl) {
|
|
27
|
+
const trimmed = baseUrl.trim();
|
|
28
|
+
return (trimmed || DEFAULT_LOCAL_AI_CORE_BASE).replace(/\/+$/, '');
|
|
29
|
+
}
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function hasString(value, key) {
|
|
34
|
+
return typeof value[key] === 'string';
|
|
35
|
+
}
|
|
36
|
+
function hasBoolean(value, key) {
|
|
37
|
+
return typeof value[key] === 'boolean';
|
|
38
|
+
}
|
|
39
|
+
function hasRecord(value, key) {
|
|
40
|
+
return isRecord(value[key]);
|
|
41
|
+
}
|
|
42
|
+
function isChannelRoute(value) {
|
|
43
|
+
return isRecord(value) && hasString(value, 'type') && hasString(value, 'channelId');
|
|
44
|
+
}
|
|
45
|
+
function isAutomationMonitorCondition(value) {
|
|
46
|
+
if (!isRecord(value)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const conditionValue = value.value;
|
|
50
|
+
return hasString(value, 'metric') &&
|
|
51
|
+
hasString(value, 'operator') &&
|
|
52
|
+
(typeof conditionValue === 'number' || typeof conditionValue === 'string' || typeof conditionValue === 'boolean');
|
|
53
|
+
}
|
|
54
|
+
function isThreadSummary(value) {
|
|
55
|
+
return isRecord(value) &&
|
|
56
|
+
hasString(value, 'id') &&
|
|
57
|
+
hasString(value, 'workspaceId') &&
|
|
58
|
+
hasString(value, 'title') &&
|
|
59
|
+
hasBoolean(value, 'live') &&
|
|
60
|
+
hasString(value, 'updatedAt') &&
|
|
61
|
+
hasString(value, 'createdAt') &&
|
|
62
|
+
typeof value.historyCount === 'number' &&
|
|
63
|
+
hasString(value, 'excerpt');
|
|
64
|
+
}
|
|
65
|
+
function isThreadMessage(value) {
|
|
66
|
+
return isRecord(value) &&
|
|
67
|
+
hasString(value, 'id') &&
|
|
68
|
+
hasString(value, 'role') &&
|
|
69
|
+
hasString(value, 'content') &&
|
|
70
|
+
hasString(value, 'timestamp');
|
|
71
|
+
}
|
|
72
|
+
function isThreadMessagePatch(value) {
|
|
73
|
+
if (!isRecord(value)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return (value.id === undefined || typeof value.id === 'string') &&
|
|
77
|
+
(value.role === undefined || ['user', 'assistant', 'system'].includes(value.role)) &&
|
|
78
|
+
(value.content === undefined || typeof value.content === 'string') &&
|
|
79
|
+
(value.timestamp === undefined || typeof value.timestamp === 'string');
|
|
80
|
+
}
|
|
81
|
+
function isRunSummary(value) {
|
|
82
|
+
return isRecord(value) &&
|
|
83
|
+
hasString(value, 'id') &&
|
|
84
|
+
hasString(value, 'threadId') &&
|
|
85
|
+
hasString(value, 'status') &&
|
|
86
|
+
hasString(value, 'startedAt') &&
|
|
87
|
+
hasString(value, 'updatedAt');
|
|
88
|
+
}
|
|
89
|
+
function isScheduledJob(value) {
|
|
90
|
+
return isRecord(value) &&
|
|
91
|
+
hasString(value, 'id') &&
|
|
92
|
+
hasString(value, 'workspaceId') &&
|
|
93
|
+
hasString(value, 'platform') &&
|
|
94
|
+
isChannelRoute(value.route) &&
|
|
95
|
+
hasString(value, 'executionMode') &&
|
|
96
|
+
hasString(value, 'triggerType') &&
|
|
97
|
+
hasString(value, 'promptTemplate') &&
|
|
98
|
+
hasString(value, 'description') &&
|
|
99
|
+
hasBoolean(value, 'enabled') &&
|
|
100
|
+
hasString(value, 'concurrencyPolicy') &&
|
|
101
|
+
hasString(value, 'createdAt') &&
|
|
102
|
+
hasString(value, 'updatedAt');
|
|
103
|
+
}
|
|
104
|
+
function isScheduledJobRun(value) {
|
|
105
|
+
return isRecord(value) &&
|
|
106
|
+
hasString(value, 'id') &&
|
|
107
|
+
hasString(value, 'jobId') &&
|
|
108
|
+
hasString(value, 'status') &&
|
|
109
|
+
hasString(value, 'triggeredAt');
|
|
110
|
+
}
|
|
111
|
+
function isAutomationMonitor(value) {
|
|
112
|
+
return isRecord(value) &&
|
|
113
|
+
hasString(value, 'id') &&
|
|
114
|
+
hasString(value, 'workspaceId') &&
|
|
115
|
+
hasString(value, 'title') &&
|
|
116
|
+
hasString(value, 'sourceType') &&
|
|
117
|
+
hasRecord(value, 'sourceConfig') &&
|
|
118
|
+
isAutomationMonitorCondition(value.condition) &&
|
|
119
|
+
hasString(value, 'promptTemplate') &&
|
|
120
|
+
hasString(value, 'platform') &&
|
|
121
|
+
isChannelRoute(value.route) &&
|
|
122
|
+
hasString(value, 'executionMode') &&
|
|
123
|
+
hasBoolean(value, 'enabled') &&
|
|
124
|
+
typeof value.cooldownMs === 'number' &&
|
|
125
|
+
hasString(value, 'concurrencyPolicy') &&
|
|
126
|
+
hasString(value, 'createdAt') &&
|
|
127
|
+
hasString(value, 'updatedAt');
|
|
128
|
+
}
|
|
129
|
+
function isAutomationMonitorRun(value) {
|
|
130
|
+
return isRecord(value) &&
|
|
131
|
+
hasString(value, 'id') &&
|
|
132
|
+
hasString(value, 'monitorId') &&
|
|
133
|
+
hasString(value, 'status') &&
|
|
134
|
+
hasString(value, 'triggeredAt');
|
|
135
|
+
}
|
|
136
|
+
function isInstalledAgentRuntime(value) {
|
|
137
|
+
return isRecord(value) &&
|
|
138
|
+
hasString(value, 'agentType') &&
|
|
139
|
+
hasString(value, 'runtimeId') &&
|
|
140
|
+
hasString(value, 'displayName') &&
|
|
141
|
+
hasString(value, 'status') &&
|
|
142
|
+
hasBoolean(value, 'installed') &&
|
|
143
|
+
hasString(value, 'detectedAt') &&
|
|
144
|
+
hasString(value, 'summary') &&
|
|
145
|
+
Array.isArray(value.issues) &&
|
|
146
|
+
Array.isArray(value.recommendedActions) &&
|
|
147
|
+
hasString(value, 'source');
|
|
148
|
+
}
|
|
149
|
+
function isDesktopRuntimeStatus(value) {
|
|
150
|
+
return isRecord(value) &&
|
|
151
|
+
value.mode === 'desktop' &&
|
|
152
|
+
hasString(value, 'phase') &&
|
|
153
|
+
hasBoolean(value, 'pendingRestart') &&
|
|
154
|
+
hasRecord(value, 'service') &&
|
|
155
|
+
hasRecord(value, 'roles') &&
|
|
156
|
+
hasRecord(value, 'settings') &&
|
|
157
|
+
hasRecord(value, 'runtimeConfig') &&
|
|
158
|
+
Array.isArray(value.logs) &&
|
|
159
|
+
value.logs.every((entry) => typeof entry === 'string');
|
|
160
|
+
}
|
|
161
|
+
function isDesktopBridgeEvent(value) {
|
|
162
|
+
return isRecord(value) && hasString(value, 'type');
|
|
163
|
+
}
|
|
164
|
+
// isLocalCoreEvent guards the *structure* of events arriving on the shared SSE
|
|
165
|
+
// stream (rejecting malformed or missing-field payloads) but intentionally does
|
|
166
|
+
// NOT hardcode enum value lists (run status, bridge type, policy, role, ...).
|
|
167
|
+
// Hardcoded whitelists would silently drop events the moment the contract adds a
|
|
168
|
+
// new status/type, with no telemetry. Unknown enum strings are accepted; consumers
|
|
169
|
+
// already fall through to safe defaults for values they do not recognize.
|
|
170
|
+
function isLocalCoreEvent(value) {
|
|
171
|
+
if (!isRecord(value) || !hasString(value, 'type')) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
switch (value.type) {
|
|
175
|
+
case 'runtime.updated':
|
|
176
|
+
return isDesktopRuntimeStatus(value.runtime);
|
|
177
|
+
case 'runtime.detect.started':
|
|
178
|
+
return hasString(value, 'detectedAt');
|
|
179
|
+
case 'runtime.detect.completed':
|
|
180
|
+
return hasString(value, 'detectedAt') &&
|
|
181
|
+
Array.isArray(value.runtimes) &&
|
|
182
|
+
value.runtimes.every(isInstalledAgentRuntime);
|
|
183
|
+
case 'runtime.detect.failed':
|
|
184
|
+
return hasString(value, 'detectedAt') && hasString(value, 'error');
|
|
185
|
+
case 'runtime.status.changed':
|
|
186
|
+
return isInstalledAgentRuntime(value.runtime);
|
|
187
|
+
case 'thread.updated':
|
|
188
|
+
return isThreadSummary(value.thread);
|
|
189
|
+
case 'thread.session.activated':
|
|
190
|
+
return hasString(value, 'workspaceId') &&
|
|
191
|
+
hasString(value, 'threadId') &&
|
|
192
|
+
hasString(value, 'reason');
|
|
193
|
+
case 'message.created':
|
|
194
|
+
return hasString(value, 'threadId') && isThreadMessage(value.message) &&
|
|
195
|
+
(value.stream === undefined || isDesktopBridgeEvent(value.stream));
|
|
196
|
+
case 'message.updated':
|
|
197
|
+
return hasString(value, 'threadId') && isThreadMessagePatch(value.message) &&
|
|
198
|
+
(value.stream === undefined || isDesktopBridgeEvent(value.stream));
|
|
199
|
+
case 'run.updated':
|
|
200
|
+
return isRunSummary(value.run) && (value.stream === undefined || isDesktopBridgeEvent(value.stream));
|
|
201
|
+
case 'scheduler.job.updated':
|
|
202
|
+
return isScheduledJob(value.job);
|
|
203
|
+
case 'scheduler.run.updated':
|
|
204
|
+
return isScheduledJobRun(value.run);
|
|
205
|
+
case 'automation.monitor.updated':
|
|
206
|
+
return isAutomationMonitor(value.monitor);
|
|
207
|
+
case 'automation.monitor.run.updated':
|
|
208
|
+
return isAutomationMonitorRun(value.run);
|
|
209
|
+
case 'presence.updated':
|
|
210
|
+
return hasBoolean(value, 'live') && (value.stream === undefined || isDesktopBridgeEvent(value.stream));
|
|
211
|
+
case 'stream.updated':
|
|
212
|
+
return isDesktopBridgeEvent(value.stream);
|
|
213
|
+
case 'external.run.snapshot':
|
|
214
|
+
return isRecord(value.snapshot) && hasString(value.snapshot, 'runId');
|
|
215
|
+
case 'external.run.stream':
|
|
216
|
+
return hasString(value, 'runId') && isDesktopBridgeEvent(value.stream);
|
|
217
|
+
default:
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function bridgeEventFromLocalCoreEvent(event) {
|
|
222
|
+
if (event.type === 'stream.updated') {
|
|
223
|
+
return event.stream;
|
|
224
|
+
}
|
|
225
|
+
if ((event.type === 'message.created' || event.type === 'message.updated' || event.type === 'run.updated') &&
|
|
226
|
+
event.stream) {
|
|
227
|
+
return event.stream;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
// Shared SSE connections, keyed by normalized base URL. The first createCoreClient
|
|
232
|
+
// to subscribe for a given baseUrl donates the transport (eventSourceFactory /
|
|
233
|
+
// scheduleReconnect / cancelReconnect); subsequent clients reuse that connection
|
|
234
|
+
// and their own transport options are intentionally ignored — sharing one socket
|
|
235
|
+
// per origin is the point. This is safe because the connection is removed by
|
|
236
|
+
// `onIdle` once its last subscriber unsubscribes, so a discarded client's deps
|
|
237
|
+
// never outlive active subscribers.
|
|
238
|
+
const eventConnections = new Map();
|
|
239
|
+
function createCoreEventConnection(baseUrl, deps, onIdle) {
|
|
240
|
+
const listeners = new Set();
|
|
241
|
+
const connectionStateListeners = new Set();
|
|
242
|
+
let eventSource = null;
|
|
243
|
+
let reconnectHandle = null;
|
|
244
|
+
let currentState = 'disconnected';
|
|
245
|
+
const notifyConnectionState = (state) => {
|
|
246
|
+
currentState = state;
|
|
247
|
+
connectionStateListeners.forEach((listener) => {
|
|
248
|
+
try {
|
|
249
|
+
listener(state);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// Connection observers are isolated for the same reason as event subscribers.
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
const cancelPendingReconnect = () => {
|
|
257
|
+
if (reconnectHandle !== null) {
|
|
258
|
+
deps.cancelReconnect(reconnectHandle);
|
|
259
|
+
reconnectHandle = null;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const closeEventSource = () => {
|
|
263
|
+
eventSource?.close();
|
|
264
|
+
eventSource = null;
|
|
265
|
+
};
|
|
266
|
+
const shutdown = () => {
|
|
267
|
+
cancelPendingReconnect();
|
|
268
|
+
closeEventSource();
|
|
269
|
+
onIdle();
|
|
270
|
+
};
|
|
271
|
+
const ensureEventSource = () => {
|
|
272
|
+
if (eventSource || (listeners.size === 0 && connectionStateListeners.size === 0)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const source = deps.eventSourceFactory(`${baseUrl}/events`);
|
|
276
|
+
eventSource = source;
|
|
277
|
+
source.onopen = () => {
|
|
278
|
+
if (eventSource === source) {
|
|
279
|
+
notifyConnectionState('connected');
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
const forward = (message) => {
|
|
283
|
+
try {
|
|
284
|
+
const payload = JSON.parse(message.data);
|
|
285
|
+
if (isLocalCoreEvent(payload)) {
|
|
286
|
+
listeners.forEach((listener) => {
|
|
287
|
+
try {
|
|
288
|
+
listener(payload);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Subscribers are isolated so one renderer callback cannot block the rest.
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// A malformed local event must not tear down the shared stream.
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
exports.LOCAL_CORE_EVENT_NAMES.forEach((eventName) => source.addEventListener(eventName, forward));
|
|
301
|
+
source.onerror = () => {
|
|
302
|
+
if (eventSource !== source) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
closeEventSource();
|
|
306
|
+
notifyConnectionState('disconnected');
|
|
307
|
+
if ((listeners.size > 0 || connectionStateListeners.size > 0) && reconnectHandle === null) {
|
|
308
|
+
reconnectHandle = deps.scheduleReconnect(() => {
|
|
309
|
+
reconnectHandle = null;
|
|
310
|
+
ensureEventSource();
|
|
311
|
+
}, 1000);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
return {
|
|
316
|
+
subscribe(listener) {
|
|
317
|
+
const registration = (event) => listener(event);
|
|
318
|
+
listeners.add(registration);
|
|
319
|
+
ensureEventSource();
|
|
320
|
+
return () => {
|
|
321
|
+
if (!listeners.delete(registration)) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (listeners.size === 0) {
|
|
325
|
+
if (connectionStateListeners.size === 0) {
|
|
326
|
+
shutdown();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
},
|
|
331
|
+
subscribeConnectionState(listener) {
|
|
332
|
+
connectionStateListeners.add(listener);
|
|
333
|
+
ensureEventSource();
|
|
334
|
+
// If the shared source is already open, onopen has already fired in the past
|
|
335
|
+
// and will not repeat — replay the current state so a late subscriber learns
|
|
336
|
+
// it is connected (otherwise it would wait until the next disconnect/reconnect).
|
|
337
|
+
if (currentState === 'connected') {
|
|
338
|
+
try {
|
|
339
|
+
listener('connected');
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// Late connection observers are isolated for the same reason as live ones.
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return () => {
|
|
346
|
+
if (!connectionStateListeners.delete(listener)) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (listeners.size === 0 && connectionStateListeners.size === 0) {
|
|
350
|
+
shutdown();
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function getCoreEventConnection(baseUrl, deps) {
|
|
357
|
+
const existing = eventConnections.get(baseUrl);
|
|
358
|
+
if (existing) {
|
|
359
|
+
return existing;
|
|
360
|
+
}
|
|
361
|
+
let connection;
|
|
362
|
+
connection = createCoreEventConnection(baseUrl, deps, () => {
|
|
363
|
+
if (eventConnections.get(baseUrl) === connection) {
|
|
364
|
+
eventConnections.delete(baseUrl);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
eventConnections.set(baseUrl, connection);
|
|
368
|
+
return connection;
|
|
369
|
+
}
|
|
370
|
+
function createCoreClient(options) {
|
|
371
|
+
const baseUrl = normalizeLocalAiCoreBase(options.baseUrl);
|
|
372
|
+
const fetchImpl = options.fetchImpl || globalThis.fetch.bind(globalThis);
|
|
373
|
+
const eventSourceFactory = options.eventSourceFactory || ((url) => new EventSource(url));
|
|
374
|
+
const scheduleReconnect = options.scheduleReconnect || ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
375
|
+
const cancelReconnect = options.cancelReconnect || ((handle) => clearTimeout(handle));
|
|
376
|
+
const unsubscribeHandlers = new Set();
|
|
377
|
+
const subscribe = (listener) => {
|
|
378
|
+
const eventConnection = getCoreEventConnection(baseUrl, { eventSourceFactory, scheduleReconnect, cancelReconnect });
|
|
379
|
+
const unsubscribe = eventConnection.subscribe(listener);
|
|
380
|
+
unsubscribeHandlers.add(unsubscribe);
|
|
381
|
+
return () => {
|
|
382
|
+
unsubscribeHandlers.delete(unsubscribe);
|
|
383
|
+
unsubscribe();
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
const subscribeConnectionState = (listener) => {
|
|
387
|
+
const eventConnection = getCoreEventConnection(baseUrl, { eventSourceFactory, scheduleReconnect, cancelReconnect });
|
|
388
|
+
const unsubscribe = eventConnection.subscribeConnectionState(listener);
|
|
389
|
+
unsubscribeHandlers.add(unsubscribe);
|
|
390
|
+
return () => {
|
|
391
|
+
unsubscribeHandlers.delete(unsubscribe);
|
|
392
|
+
unsubscribe();
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
const request = async (method, path, body) => {
|
|
396
|
+
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
397
|
+
method,
|
|
398
|
+
headers: { 'Content-Type': 'application/json' },
|
|
399
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
400
|
+
});
|
|
401
|
+
const json = await response.json();
|
|
402
|
+
if (!response.ok || !json.ok) {
|
|
403
|
+
throw new Error(json.error || `Local AI Core request failed: ${response.status}`);
|
|
404
|
+
}
|
|
405
|
+
return json.data;
|
|
406
|
+
};
|
|
407
|
+
return {
|
|
408
|
+
baseUrl,
|
|
409
|
+
request,
|
|
410
|
+
async detect(timeoutMs = 350) {
|
|
411
|
+
const controller = new AbortController();
|
|
412
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
413
|
+
try {
|
|
414
|
+
const response = await fetchImpl(`${baseUrl}/health`, { signal: controller.signal });
|
|
415
|
+
const json = await response.json();
|
|
416
|
+
return response.ok && json.ok && json.data?.name === 'local-ai-core';
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
clearTimeout(timer);
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
events: {
|
|
426
|
+
subscribe,
|
|
427
|
+
subscribeConnectionState,
|
|
428
|
+
subscribeRuntime(listener) {
|
|
429
|
+
return subscribe((event) => {
|
|
430
|
+
if (event.type === 'runtime.updated') {
|
|
431
|
+
listener(event.runtime);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
subscribeBridge(listener) {
|
|
436
|
+
return subscribe((event) => {
|
|
437
|
+
const bridgeEvent = bridgeEventFromLocalCoreEvent(event);
|
|
438
|
+
if (bridgeEvent) {
|
|
439
|
+
listener(bridgeEvent);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
close() {
|
|
444
|
+
const handlers = Array.from(unsubscribeHandlers);
|
|
445
|
+
unsubscribeHandlers.clear();
|
|
446
|
+
handlers.forEach((unsubscribe) => unsubscribe());
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
exports.LOCAL_AI_CORE_BASE = normalizeLocalAiCoreBase(typeof __LOCAL_AI_CORE_BASE__ !== 'undefined' ? __LOCAL_AI_CORE_BASE__ : '');
|
|
452
|
+
exports.LOCAL_AI_CORE_ORIGIN = exports.LOCAL_AI_CORE_BASE.endsWith('/api/local/v1')
|
|
453
|
+
? exports.LOCAL_AI_CORE_BASE.slice(0, -'/api/local/v1'.length) || DEFAULT_LOCAL_AI_CORE_ORIGIN
|
|
454
|
+
: DEFAULT_LOCAL_AI_CORE_ORIGIN;
|
|
455
|
+
exports.coreClient = createCoreClient({ baseUrl: exports.LOCAL_AI_CORE_BASE });
|