@kafca/agentdock 0.1.44 → 0.1.46
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 +76 -11
- package/bin/agentdock.mjs +5 -5
- package/dist/renderer/assets/{Badge-Qk9qHovH.js → Badge-BIesaDYG.js} +1 -1
- package/dist/renderer/assets/Chat-CpataoDh.js +2 -0
- package/dist/renderer/assets/{ChatMarkdown-BKdW8OON.js → ChatMarkdown-_Mdmltgv.js} +1 -1
- package/dist/renderer/assets/Config-Pe5-zKZk.js +21 -0
- package/dist/renderer/assets/CronList-BnaOSPT8.js +1 -0
- package/dist/renderer/assets/Dashboard-D05l8GKE.js +26 -0
- package/dist/renderer/assets/{EmptyState-kD9yAwtw.js → EmptyState-BDBavDll.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-BAMCLrjz.js → KnowledgeDetail-DvR798l_.js} +2 -2
- package/dist/renderer/assets/KnowledgeHome-r2ZuvU8-.js +11 -0
- package/dist/renderer/assets/Logs-BaljtmAV.js +1 -0
- package/dist/renderer/assets/{Modal-DwY4lecS.js → Modal-CXhD_OWR.js} +1 -1
- package/dist/renderer/assets/MonitorList-D7tyipbx.js +1 -0
- package/dist/renderer/assets/{Page-CnB9ePsb.js → Page-KUuFYgjg.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-Ddq94-yu.js +11 -0
- package/dist/renderer/assets/ProjectList-CbdHd5Ev.js +1 -0
- package/dist/renderer/assets/{Select-zfoksLGB.js → Select-Djc8BXMC.js} +1 -1
- package/dist/renderer/assets/SessionChat-DOy0iKQR.js +1 -0
- package/dist/renderer/assets/SessionList-DHgl1s1D.js +1 -0
- package/dist/renderer/assets/ThreadChat-I7dYibDc.js +23 -0
- package/dist/renderer/assets/Workspace-DTocqY-F.js +19 -0
- package/dist/renderer/assets/{arrow-left-CNEAuElg.js → arrow-left-CXyya3hP.js} +1 -1
- package/dist/renderer/assets/{book-open-BNJMhNI-.js → book-open-9n4Kxc7E.js} +1 -1
- package/dist/renderer/assets/bot-C9HBk5XH.js +6 -0
- package/dist/renderer/assets/{check-BnclClRU.js → check-CutSg9a5.js} +1 -1
- package/dist/renderer/assets/{chevron-down-Bh53e0O-.js → chevron-down-DrydR4i3.js} +1 -1
- package/dist/renderer/assets/{circle-ClZTWCWO.js → circle-BEjtahw6.js} +1 -1
- package/dist/renderer/assets/desktop-DATgk_ka.js +1 -0
- package/dist/renderer/assets/{desktop-C6q6wBae.js → desktop-JQkhNOF4.js} +5 -5
- package/dist/renderer/assets/en-CmAr_0e6.js +1 -0
- package/dist/renderer/assets/es-Cs9x52ca.js +1 -0
- package/dist/renderer/assets/{funnel-_5hsz0xI.js → funnel-BhqZpS4f.js} +1 -1
- package/dist/renderer/assets/{heart-u4Q1aOqj.js → heart-Dp8Vs-7_.js} +1 -1
- package/dist/renderer/assets/{index-BNFx4uYv.js → index-BKpv0P0s.js} +2 -2
- package/dist/renderer/assets/index-CO4fGqmv.css +11 -0
- package/dist/renderer/assets/{index-B36eowj-.js → index-cI3pWBnN.js} +36 -36
- package/dist/renderer/assets/ja-DtvtBRTW.js +1 -0
- package/dist/renderer/assets/{pencil-DLh9QNmT.js → pencil-ywcJzeOo.js} +1 -1
- package/dist/renderer/assets/{play-BDTk9Scm.js → play-AtN492Cx.js} +1 -1
- package/dist/renderer/assets/{plus-BSc8POsz.js → plus-BEj14BBV.js} +1 -1
- package/dist/renderer/assets/projects-Clar8lgb.js +1 -0
- package/dist/renderer/assets/save-D6ov4CNv.js +11 -0
- package/dist/renderer/assets/{search-CniPk7Ey.js → search-C3bvw7AR.js} +1 -1
- package/dist/renderer/assets/{server-GsXyQAZm.js → server-C1Uyedyx.js} +1 -1
- package/dist/renderer/assets/{session-utils-BDxzgTBC.js → session-utils-DpRwJ6Nh.js} +1 -1
- package/dist/renderer/assets/{sessions-CLcKA1Tt.js → sessions-BWmAppwK.js} +1 -1
- package/dist/renderer/assets/{shield-check-4i0E3hmf.js → shield-check-CVx1mEs2.js} +1 -1
- package/dist/renderer/assets/{trash-2-DhtOCsO5.js → trash-2-o-heXOgs.js} +1 -1
- package/dist/renderer/assets/user-dPOzUqIc.js +6 -0
- package/dist/renderer/assets/{wifi-off-BHPzPY_B.js → wifi-off-Fz5c3_x_.js} +1 -1
- package/dist/renderer/assets/zh-CcJ9vBZj.js +1 -0
- package/dist/renderer/assets/zh-TW-elI72AC1.js +1 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/main.js +26 -1
- package/dist-electron/packages/core-sdk/src/index.js +28 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +111 -62
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +80 -7
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +78 -5
- package/dist-electron/services/local-ai-core/src/acp/store/external-store.js +98 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +33 -0
- package/dist-electron/services/local-ai-core/src/acp/store/model-provider-store.js +88 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +43 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +2 -119
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +148 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound.js +55 -1
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +65 -10
- package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +4 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +139 -29
- package/dist-electron/services/local-ai-core/src/execution/agent-execution-backend.js +56 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +18 -57
- package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +255 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/catalog.js +78 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +3 -1
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +64 -9
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -2
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +152 -0
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +175 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +330 -3
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/provider-config-migration.js +32 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +76 -8
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +62 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +708 -4
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +13 -3
- package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +113 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +273 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-manager.js +215 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-stdio-proxy.js +188 -0
- package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +3 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +32 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-slash-command-dispatcher.js +71 -0
- package/dist-electron/shared/desktop.js +85 -1
- package/dist-electron/tests/contracts/architecture-docs.test.js +83 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +225 -18
- package/dist-electron/tests/electron/runtime-detection-service.test.js +57 -5
- package/dist-electron/tests/electron/sandbox-config.test.js +541 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +257 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +139 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +183 -20
- package/dist-electron/tests/integration/local-core-routes.test.js +34 -0
- package/package.json +2 -3
- package/dist/renderer/assets/Chat-BK-fm0Xr.js +0 -2
- package/dist/renderer/assets/Config-B93CQGvY.js +0 -16
- package/dist/renderer/assets/CronList-Z3TvSRKP.js +0 -1
- package/dist/renderer/assets/Dashboard-Ch3QkWwf.js +0 -26
- package/dist/renderer/assets/KnowledgeHome-ChvjOfrc.js +0 -11
- package/dist/renderer/assets/Logs-Cv0nth3j.js +0 -1
- package/dist/renderer/assets/MonitorList-JEvMoWUm.js +0 -1
- package/dist/renderer/assets/ProjectDetail-DJa3sMmU.js +0 -11
- package/dist/renderer/assets/ProjectList-D6CXumR_.js +0 -1
- package/dist/renderer/assets/SessionChat-DUTh4WoM.js +0 -1
- package/dist/renderer/assets/SessionList-D9o-lUst.js +0 -1
- package/dist/renderer/assets/ThreadChat-BYXlT-Kc.js +0 -23
- package/dist/renderer/assets/Workspace-BrhSwRQ-.js +0 -14
- package/dist/renderer/assets/desktop-C5vBT0yM.js +0 -1
- package/dist/renderer/assets/en-D2SkCrhG.js +0 -1
- package/dist/renderer/assets/es-C8UBPhxM.js +0 -1
- package/dist/renderer/assets/index-CaQZSRbg.css +0 -11
- package/dist/renderer/assets/ja-ChF4vbBL.js +0 -1
- package/dist/renderer/assets/plug-BG0JHW6B.js +0 -6
- package/dist/renderer/assets/projects-DwfZwxDW.js +0 -1
- package/dist/renderer/assets/save-D5AO_G6a.js +0 -6
- package/dist/renderer/assets/user-BtC_pVXV.js +0 -11
- package/dist/renderer/assets/zh-DIcdHycM.js +0 -1
- package/dist/renderer/assets/zh-TW-BtRwreY_.js +0 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runDeploymentDiagnostics = runDeploymentDiagnostics;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const index_js_1 = require("../../../../packages/contracts/src/index.js");
|
|
6
|
+
const sandbox_config_js_1 = require("../sandbox/sandbox-config.js");
|
|
7
|
+
async function runDeploymentDiagnostics(input) {
|
|
8
|
+
const env = input.env || process.env;
|
|
9
|
+
const checkedAt = new Date().toISOString();
|
|
10
|
+
const config = input.config || {};
|
|
11
|
+
const profile = (0, index_js_1.getDesktopDeploymentProfile)(String(config.deployment_profile || env.AGENTDOCK_DEPLOYMENT_PROFILE || '').trim());
|
|
12
|
+
const provider = Array.isArray(config.sandbox_providers)
|
|
13
|
+
? config.sandbox_providers.find((item) => item.id === profile.defaultSandboxProviderId) || config.sandbox_providers[0]
|
|
14
|
+
: undefined;
|
|
15
|
+
const sandboxProvider = provider || (0, index_js_1.defaultSandboxProviderForProfile)(profile.id);
|
|
16
|
+
const opensandboxUrl = normalizeUrl(sandboxProvider.server_url || env.AGENTDOCK_OPENSANDBOX_SERVER_URL || (0, sandbox_config_js_1.defaultOpenSandboxServerUrl)(env));
|
|
17
|
+
const checks = [
|
|
18
|
+
{
|
|
19
|
+
id: 'deployment.profile',
|
|
20
|
+
label: 'Deployment profile',
|
|
21
|
+
status: 'pass',
|
|
22
|
+
summary: `${profile.label} uses OpenSandbox at ${opensandboxUrl || 'not configured'}.`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'core.bind-host',
|
|
26
|
+
label: 'Core bind host',
|
|
27
|
+
status: profile.id === 'docker-compose' && env.AI_WORKSTATION_HOST !== '0.0.0.0' ? 'warn' : 'pass',
|
|
28
|
+
summary: `AI_WORKSTATION_HOST=${env.AI_WORKSTATION_HOST || '127.0.0.1'}.`,
|
|
29
|
+
},
|
|
30
|
+
dockerSocketCheck(profile.id),
|
|
31
|
+
workspacePathCheck(config, profile.id),
|
|
32
|
+
allowlistCheck(config, env),
|
|
33
|
+
sandboxImageCheck(config),
|
|
34
|
+
await opensandboxHealthCheck(opensandboxUrl, sandboxProvider.api_key_env, env),
|
|
35
|
+
];
|
|
36
|
+
return {
|
|
37
|
+
status: summarizeStatus(checks),
|
|
38
|
+
checkedAt,
|
|
39
|
+
checks,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function dockerSocketCheck(profileId) {
|
|
43
|
+
if (profileId === 'docker-compose') {
|
|
44
|
+
return {
|
|
45
|
+
id: 'docker.socket',
|
|
46
|
+
label: 'Docker socket',
|
|
47
|
+
status: 'pass',
|
|
48
|
+
summary: 'Docker Compose delegates Docker access to OpenSandbox.',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const socketPath = '/var/run/docker.sock';
|
|
52
|
+
const ok = (0, node_fs_1.existsSync)(socketPath);
|
|
53
|
+
return {
|
|
54
|
+
id: 'docker.socket',
|
|
55
|
+
label: 'Docker socket',
|
|
56
|
+
status: ok ? 'pass' : 'warn',
|
|
57
|
+
summary: ok ? `${socketPath} is available.` : `${socketPath} is not visible to Core.`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function workspacePathCheck(config, profileId) {
|
|
61
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
62
|
+
const missing = projects
|
|
63
|
+
.map((project) => ({
|
|
64
|
+
name: project.name,
|
|
65
|
+
workDir: String(project.agent?.options?.work_dir || '').trim(),
|
|
66
|
+
sandboxEnabled: Boolean(project.agent?.options?.sandbox?.enabled),
|
|
67
|
+
}))
|
|
68
|
+
.filter((project) => !(profileId === 'docker-compose' && project.sandboxEnabled))
|
|
69
|
+
.filter((project) => project.workDir && !(0, node_fs_1.existsSync)(project.workDir));
|
|
70
|
+
if (missing.length > 0) {
|
|
71
|
+
return {
|
|
72
|
+
id: 'workspace.paths',
|
|
73
|
+
label: 'Workspace paths',
|
|
74
|
+
status: 'warn',
|
|
75
|
+
summary: `${missing.length} configured workspace path(s) are not visible to Core.`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (profileId === 'docker-compose' && projects.some((project) => project.agent?.options?.sandbox?.enabled)) {
|
|
79
|
+
return {
|
|
80
|
+
id: 'workspace.paths',
|
|
81
|
+
label: 'Workspace paths',
|
|
82
|
+
status: 'pass',
|
|
83
|
+
summary: 'Sandbox workspace host paths are delegated to OpenSandbox.',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
id: 'workspace.paths',
|
|
88
|
+
label: 'Workspace paths',
|
|
89
|
+
status: 'pass',
|
|
90
|
+
summary: projects.length > 0 ? `${projects.length} workspace path(s) checked.` : 'No projects configured.',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function allowlistCheck(config, env) {
|
|
94
|
+
const allowlist = String(env.OPEN_SANDBOX_HOST_MOUNT_ALLOWLIST || '')
|
|
95
|
+
.split(',')
|
|
96
|
+
.map((item) => item.trim().replace(/\/+$/, ''))
|
|
97
|
+
.filter(Boolean);
|
|
98
|
+
if (allowlist.length === 0) {
|
|
99
|
+
return {
|
|
100
|
+
id: 'opensandbox.allowlist',
|
|
101
|
+
label: 'OpenSandbox mount allowlist',
|
|
102
|
+
status: 'warn',
|
|
103
|
+
summary: 'OPEN_SANDBOX_HOST_MOUNT_ALLOWLIST is not set.',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
107
|
+
const denied = projects.filter((project) => {
|
|
108
|
+
const workDir = String(project.agent?.options?.work_dir || '').trim().replace(/\/+$/, '');
|
|
109
|
+
return workDir && !allowlist.some((root) => workDir === root || workDir.startsWith(`${root}/`));
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
id: 'opensandbox.allowlist',
|
|
113
|
+
label: 'OpenSandbox mount allowlist',
|
|
114
|
+
status: denied.length > 0 ? 'warn' : 'pass',
|
|
115
|
+
summary: denied.length > 0
|
|
116
|
+
? `${denied.length} project workspace path(s) are outside the allowlist.`
|
|
117
|
+
: `Allowlist covers configured workspace paths: ${allowlist.join(', ')}.`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function sandboxImageCheck(config) {
|
|
121
|
+
const images = Array.isArray(config.sandbox_runtime_images) ? config.sandbox_runtime_images : [];
|
|
122
|
+
const missing = images.filter((image) => !String(image.image || '').trim());
|
|
123
|
+
return {
|
|
124
|
+
id: 'sandbox.images',
|
|
125
|
+
label: 'Sandbox images',
|
|
126
|
+
status: missing.length > 0 ? 'fail' : 'pass',
|
|
127
|
+
summary: images.length > 0 ? `${images.length} sandbox image registration(s) configured.` : 'No sandbox image registrations configured.',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async function opensandboxHealthCheck(serverUrl, apiKeyEnv, env) {
|
|
131
|
+
if (!serverUrl) {
|
|
132
|
+
return {
|
|
133
|
+
id: 'opensandbox.health',
|
|
134
|
+
label: 'OpenSandbox health',
|
|
135
|
+
status: 'fail',
|
|
136
|
+
summary: 'OpenSandbox server URL is empty.',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const controller = new AbortController();
|
|
141
|
+
const timer = setTimeout(() => controller.abort(), 3000);
|
|
142
|
+
const apiKey = apiKeyEnv ? env[apiKeyEnv] : undefined;
|
|
143
|
+
const response = await fetch(`${serverUrl}/health`, {
|
|
144
|
+
signal: controller.signal,
|
|
145
|
+
headers: apiKey ? { authorization: `Bearer ${apiKey}` } : undefined,
|
|
146
|
+
});
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
return {
|
|
149
|
+
id: 'opensandbox.health',
|
|
150
|
+
label: 'OpenSandbox health',
|
|
151
|
+
status: response.ok ? 'pass' : 'fail',
|
|
152
|
+
summary: `GET ${serverUrl}/health returned HTTP ${response.status}.`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
id: 'opensandbox.health',
|
|
158
|
+
label: 'OpenSandbox health',
|
|
159
|
+
status: 'fail',
|
|
160
|
+
summary: error instanceof Error ? error.message : String(error),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function normalizeUrl(value) {
|
|
165
|
+
return String(value || '').trim().replace(/\/+$/, '');
|
|
166
|
+
}
|
|
167
|
+
function summarizeStatus(checks) {
|
|
168
|
+
if (checks.some((check) => check.status === 'fail')) {
|
|
169
|
+
return 'fail';
|
|
170
|
+
}
|
|
171
|
+
if (checks.some((check) => check.status === 'warn')) {
|
|
172
|
+
return 'warn';
|
|
173
|
+
}
|
|
174
|
+
return 'pass';
|
|
175
|
+
}
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LocalCoreController = void 0;
|
|
4
4
|
const node_events_1 = require("node:events");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
5
8
|
const desktop_js_1 = require("../../../../shared/desktop.js");
|
|
6
9
|
const bootstrap_js_1 = require("../kernel/bootstrap.js");
|
|
10
|
+
const local_core_errors_js_1 = require("../kernel/local-core-errors.js");
|
|
7
11
|
const runtime_detection_service_js_1 = require("./runtime-detection-service.js");
|
|
12
|
+
const provider_config_migration_js_1 = require("./provider-config-migration.js");
|
|
13
|
+
const deployment_diagnostics_js_1 = require("./deployment-diagnostics.js");
|
|
8
14
|
class LocalCoreController extends node_events_1.EventEmitter {
|
|
9
15
|
userDataPath;
|
|
10
16
|
state;
|
|
@@ -13,9 +19,11 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
13
19
|
channelRuntimes;
|
|
14
20
|
scheduledJobs;
|
|
15
21
|
automationMonitors;
|
|
22
|
+
store;
|
|
16
23
|
kernel;
|
|
17
24
|
runtime;
|
|
18
25
|
runtimeDetection;
|
|
26
|
+
errorReporter;
|
|
19
27
|
busUnsubscribers = [];
|
|
20
28
|
pendingLogs = [];
|
|
21
29
|
handlingLog = false;
|
|
@@ -28,6 +36,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
28
36
|
log: (message) => this.handleLog(message),
|
|
29
37
|
});
|
|
30
38
|
this.state = this.runtime.state;
|
|
39
|
+
this.store = this.runtime.store;
|
|
31
40
|
this.kernel = this.runtime.kernel;
|
|
32
41
|
this.knowledgeProvider = this.runtime.knowledgeProvider;
|
|
33
42
|
this.workspaceRouter = this.runtime.workspaceRouter;
|
|
@@ -43,6 +52,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
43
52
|
runMonitorNow: async () => { throw new Error('Automation monitor service is not available.'); },
|
|
44
53
|
listRuns: () => [],
|
|
45
54
|
};
|
|
55
|
+
this.errorReporter = new local_core_errors_js_1.LocalCoreErrorReporter((message) => this.handleLog(message));
|
|
46
56
|
this.runtimeDetection = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
47
57
|
userDataPath,
|
|
48
58
|
readConfig: async () => (await this.readConfigFile()).parsed,
|
|
@@ -64,12 +74,20 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
64
74
|
this.emit('automation-monitor-run', run);
|
|
65
75
|
}), this.kernel.context.bus.on('runtime.state.changed', () => {
|
|
66
76
|
void this.emitRuntime();
|
|
77
|
+
}), this.kernel.context.bus.on('localcore.error', (event) => {
|
|
78
|
+
const errorInfo = this.errorReporter.report(String(event.scope || 'local-ai-core'), event.errorInfo || event.error || 'Unknown error', event.context || {});
|
|
79
|
+
const runtimeId = String(event.context?.runtimeId || event.context?.agentType || '').trim();
|
|
80
|
+
if (runtimeId) {
|
|
81
|
+
this.runtimeDetection.recordLaunchError(runtimeId, errorInfo);
|
|
82
|
+
}
|
|
67
83
|
}));
|
|
68
84
|
}
|
|
69
85
|
async init() {
|
|
70
86
|
await this.runtime.start();
|
|
71
87
|
await this.emitRuntime();
|
|
72
|
-
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
void this.runtimeDetection.refreshOnStartup();
|
|
90
|
+
}, 5000);
|
|
73
91
|
}
|
|
74
92
|
async close() {
|
|
75
93
|
for (const unsubscribe of this.busUnsubscribers) {
|
|
@@ -125,7 +143,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
125
143
|
return this.state.getLogEntries(level, limit);
|
|
126
144
|
}
|
|
127
145
|
async readConfigFile() {
|
|
128
|
-
return this.
|
|
146
|
+
return this.readAndMigrateConfigFile();
|
|
129
147
|
}
|
|
130
148
|
async saveRawConfigFile(raw) {
|
|
131
149
|
const next = await this.state.saveRawConfigFile(raw);
|
|
@@ -134,17 +152,228 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
134
152
|
return next;
|
|
135
153
|
}
|
|
136
154
|
async saveStructuredConfigFile(config) {
|
|
137
|
-
const
|
|
155
|
+
const migrated = (0, provider_config_migration_js_1.migrateLegacyProjectProvidersToStore)(config, this.store);
|
|
156
|
+
const next = await this.state.saveStructuredConfigFile(migrated.config);
|
|
138
157
|
await this.refreshChannelBindings();
|
|
139
158
|
await this.emitRuntime();
|
|
140
159
|
return next;
|
|
141
160
|
}
|
|
161
|
+
async listModelProviders() {
|
|
162
|
+
return { providers: this.store.listModelProviders() };
|
|
163
|
+
}
|
|
164
|
+
async createModelProvider(input) {
|
|
165
|
+
return this.store.upsertModelProvider(input);
|
|
166
|
+
}
|
|
167
|
+
async updateModelProvider(providerId, input) {
|
|
168
|
+
const existing = this.store.getModelProvider(providerId);
|
|
169
|
+
if (!existing) {
|
|
170
|
+
throw new Error(`Provider not found: ${providerId}`);
|
|
171
|
+
}
|
|
172
|
+
return this.store.upsertModelProvider({ ...input, id: providerId });
|
|
173
|
+
}
|
|
174
|
+
async deleteModelProvider(providerId) {
|
|
175
|
+
const config = await this.readAndMigrateConfigFile();
|
|
176
|
+
const referencingProjects = (config.parsed?.projects || [])
|
|
177
|
+
.filter((project) => project.agent?.options?.provider_id === providerId)
|
|
178
|
+
.map((project) => project.name);
|
|
179
|
+
if (referencingProjects.length > 0) {
|
|
180
|
+
throw new Error(`Provider "${providerId}" is still used by projects: ${referencingProjects.join(', ')}`);
|
|
181
|
+
}
|
|
182
|
+
return this.store.deleteModelProvider(providerId);
|
|
183
|
+
}
|
|
142
184
|
async saveSettings(input) {
|
|
143
185
|
const settings = await this.state.saveSettings(input);
|
|
144
186
|
await this.refreshChannelBindings();
|
|
145
187
|
await this.emitRuntime();
|
|
146
188
|
return settings;
|
|
147
189
|
}
|
|
190
|
+
async ensureExternalProject(input) {
|
|
191
|
+
const userId = normalizeExternalId(input.user_id, 'user_id');
|
|
192
|
+
const externalProjectId = normalizeExternalId(input.external_project_id, 'external_project_id');
|
|
193
|
+
const agentType = normalizeExternalSegment(input.agent_type || 'pi', 'pi');
|
|
194
|
+
const existing = this.store.getExternalProject(userId, externalProjectId);
|
|
195
|
+
const provider = this.resolveExternalProvider(input.provider_id || existing?.providerId);
|
|
196
|
+
const workspaceId = externalWorkspaceId(userId, externalProjectId);
|
|
197
|
+
const workspacePath = this.externalProjectBasePath(userId, externalProjectId);
|
|
198
|
+
const displayName = String(input.display_name || externalProjectId).trim() || externalProjectId;
|
|
199
|
+
(0, node_fs_1.mkdirSync)(workspacePath, { recursive: true, mode: 0o700 });
|
|
200
|
+
const now = new Date().toISOString();
|
|
201
|
+
const project = this.store.upsertExternalProject({
|
|
202
|
+
userId,
|
|
203
|
+
externalProjectId,
|
|
204
|
+
workspaceId,
|
|
205
|
+
workspacePath,
|
|
206
|
+
displayName,
|
|
207
|
+
agentType,
|
|
208
|
+
providerId: provider.id,
|
|
209
|
+
metadata: input.metadata || existing?.metadata || {},
|
|
210
|
+
createdAt: existing?.createdAt || now,
|
|
211
|
+
updatedAt: now,
|
|
212
|
+
});
|
|
213
|
+
await this.ensureExternalWorkspaceConfig(project, input.model);
|
|
214
|
+
this.store.upsertWorkspaceRegistryEntry({
|
|
215
|
+
workspaceId: project.workspaceId,
|
|
216
|
+
displayName: project.displayName,
|
|
217
|
+
path: project.workspacePath,
|
|
218
|
+
deviceId: 'external',
|
|
219
|
+
defaultRuntimeId: project.agentType,
|
|
220
|
+
health: { status: 'healthy', summary: 'External workspace is available.', issues: [], checkedAt: now },
|
|
221
|
+
metadata: {
|
|
222
|
+
external: true,
|
|
223
|
+
userId: project.userId,
|
|
224
|
+
externalProjectId: project.externalProjectId,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
return project;
|
|
228
|
+
}
|
|
229
|
+
async createExternalRun(input) {
|
|
230
|
+
if (!String(input.prompt || '').trim()) {
|
|
231
|
+
throw new Error('prompt is required.');
|
|
232
|
+
}
|
|
233
|
+
const project = await this.ensureExternalProject(input);
|
|
234
|
+
const thread = await this.ensureExternalThread(project, input);
|
|
235
|
+
const sent = await this.workspaceRouter.sendThreadMessage(thread.threadId, input.prompt, {
|
|
236
|
+
permissionMode: input.permission_mode,
|
|
237
|
+
runtimeEnv: input.runtime_env,
|
|
238
|
+
});
|
|
239
|
+
const task = this.store.getAgentTaskByRunId(sent.runId);
|
|
240
|
+
return {
|
|
241
|
+
project,
|
|
242
|
+
thread,
|
|
243
|
+
workspace_id: project.workspaceId,
|
|
244
|
+
thread_id: thread.threadId,
|
|
245
|
+
run_id: sent.runId,
|
|
246
|
+
task_id: task?.taskId,
|
|
247
|
+
events_url: `/api/local/v1/external/runs/${encodeURIComponent(sent.runId)}/events`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
async getExternalRunSnapshot(runId) {
|
|
251
|
+
const task = this.store.getAgentTaskByRunId(runId);
|
|
252
|
+
const run = this.store.getRun(runId);
|
|
253
|
+
const threadId = task?.threadId || run?.thread_id || '';
|
|
254
|
+
const thread = threadId
|
|
255
|
+
? await this.workspaceRouter.getThread(threadId).catch(() => undefined)
|
|
256
|
+
: undefined;
|
|
257
|
+
return {
|
|
258
|
+
runId,
|
|
259
|
+
task,
|
|
260
|
+
thread,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async readAndMigrateConfigFile() {
|
|
264
|
+
const current = await this.state.readConfigFile();
|
|
265
|
+
if (!current.parsed) {
|
|
266
|
+
return current;
|
|
267
|
+
}
|
|
268
|
+
const migrated = (0, provider_config_migration_js_1.migrateLegacyProjectProvidersToStore)(current.parsed, this.store);
|
|
269
|
+
if (!migrated.changed) {
|
|
270
|
+
return current;
|
|
271
|
+
}
|
|
272
|
+
const saved = await this.state.saveStructuredConfigFile(migrated.config);
|
|
273
|
+
return {
|
|
274
|
+
...saved,
|
|
275
|
+
warnings: [
|
|
276
|
+
...(current.warnings || []),
|
|
277
|
+
...(saved.warnings || []),
|
|
278
|
+
...migrated.warnings,
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
resolveExternalProvider(providerId) {
|
|
283
|
+
const requested = String(providerId || '').trim();
|
|
284
|
+
if (requested) {
|
|
285
|
+
const provider = this.store.getModelProvider(requested);
|
|
286
|
+
if (!provider) {
|
|
287
|
+
throw new Error(`Provider not found: ${requested}`);
|
|
288
|
+
}
|
|
289
|
+
return provider;
|
|
290
|
+
}
|
|
291
|
+
const provider = this.store.listModelProviders()[0];
|
|
292
|
+
if (!provider) {
|
|
293
|
+
throw new Error('No model provider is configured. Create a provider before starting an external run.');
|
|
294
|
+
}
|
|
295
|
+
return provider;
|
|
296
|
+
}
|
|
297
|
+
async ensureExternalWorkspaceConfig(project, model) {
|
|
298
|
+
const current = await this.readAndMigrateConfigFile();
|
|
299
|
+
const config = {
|
|
300
|
+
...(current.parsed || {}),
|
|
301
|
+
projects: Array.isArray(current.parsed?.projects) ? [...current.parsed.projects] : [],
|
|
302
|
+
};
|
|
303
|
+
const existingIndex = config.projects.findIndex((item) => item?.name === project.workspaceId);
|
|
304
|
+
const existing = existingIndex >= 0 ? config.projects[existingIndex] : undefined;
|
|
305
|
+
const defaultRuntimeImageId = Array.isArray(config.sandbox_runtime_images)
|
|
306
|
+
? config.sandbox_runtime_images.find((image) => image?.agent_type === project.agentType)?.id
|
|
307
|
+
: undefined;
|
|
308
|
+
const options = {
|
|
309
|
+
...(existing?.agent?.options || {}),
|
|
310
|
+
work_dir: project.workspacePath,
|
|
311
|
+
user_id: project.userId,
|
|
312
|
+
provider_id: project.providerId,
|
|
313
|
+
...(model ? { model } : {}),
|
|
314
|
+
sandbox: {
|
|
315
|
+
...(existing?.agent?.options?.sandbox || {}),
|
|
316
|
+
enabled: true,
|
|
317
|
+
...(defaultRuntimeImageId ? { runtime_image_id: defaultRuntimeImageId } : {}),
|
|
318
|
+
state_scope: 'project',
|
|
319
|
+
sandbox_lifecycle: 'per_thread',
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
const nextProject = {
|
|
323
|
+
name: project.workspaceId,
|
|
324
|
+
agent: {
|
|
325
|
+
...(existing?.agent || {}),
|
|
326
|
+
type: project.agentType,
|
|
327
|
+
options,
|
|
328
|
+
},
|
|
329
|
+
platforms: Array.isArray(existing?.platforms) ? existing.platforms : [],
|
|
330
|
+
admin_from: existing?.admin_from,
|
|
331
|
+
disabled_commands: existing?.disabled_commands,
|
|
332
|
+
};
|
|
333
|
+
if (existingIndex >= 0) {
|
|
334
|
+
config.projects[existingIndex] = nextProject;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
config.projects.push(nextProject);
|
|
338
|
+
}
|
|
339
|
+
await this.saveStructuredConfigFile(config);
|
|
340
|
+
}
|
|
341
|
+
async ensureExternalThread(project, input) {
|
|
342
|
+
const externalThreadId = normalizeExternalId(input.external_thread_id || `thread-${(0, node_crypto_1.randomUUID)()}`, 'external_thread_id');
|
|
343
|
+
const existing = this.store.getExternalThread(project.userId, project.externalProjectId, externalThreadId);
|
|
344
|
+
if (existing) {
|
|
345
|
+
const thread = await this.workspaceRouter.getThread(existing.threadId).catch(() => undefined);
|
|
346
|
+
if (thread) {
|
|
347
|
+
return existing;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const workspacePath = this.externalThreadWorkspacePath(project.userId, project.externalProjectId, externalThreadId);
|
|
351
|
+
(0, node_fs_1.mkdirSync)(workspacePath, { recursive: true, mode: 0o700 });
|
|
352
|
+
const title = String(input.title || externalThreadId).trim() || externalThreadId;
|
|
353
|
+
const thread = await this.workspaceRouter.createThread(project.workspaceId, title);
|
|
354
|
+
const now = new Date().toISOString();
|
|
355
|
+
return this.store.upsertExternalThread({
|
|
356
|
+
userId: project.userId,
|
|
357
|
+
externalProjectId: project.externalProjectId,
|
|
358
|
+
externalThreadId,
|
|
359
|
+
workspaceId: project.workspaceId,
|
|
360
|
+
threadId: thread.id,
|
|
361
|
+
workspacePath,
|
|
362
|
+
metadata: input.metadata || {},
|
|
363
|
+
createdAt: now,
|
|
364
|
+
updatedAt: now,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
externalProjectBasePath(userId, externalProjectId) {
|
|
368
|
+
return (0, node_path_1.join)(this.externalWorkspaceRoot(), 'users', normalizeExternalSegment(userId, 'user'), 'projects', normalizeExternalSegment(externalProjectId, 'project'));
|
|
369
|
+
}
|
|
370
|
+
externalThreadWorkspacePath(userId, externalProjectId, externalThreadId) {
|
|
371
|
+
return (0, node_path_1.join)(this.externalProjectBasePath(userId, externalProjectId), 'threads', normalizeExternalSegment(externalThreadId, 'thread'), 'workspace');
|
|
372
|
+
}
|
|
373
|
+
externalWorkspaceRoot() {
|
|
374
|
+
return String(process.env.AGENTDOCK_EXTERNAL_WORKSPACE_ROOT || '').trim()
|
|
375
|
+
|| (0, node_path_1.join)(this.userDataPath, 'external-workspaces');
|
|
376
|
+
}
|
|
148
377
|
async listWorkspaces() {
|
|
149
378
|
return this.workspaceRouter.listWorkspaces();
|
|
150
379
|
}
|
|
@@ -342,6 +571,77 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
342
571
|
async getPluginDiagnostics() {
|
|
343
572
|
return this.kernel.diagnostics.snapshot();
|
|
344
573
|
}
|
|
574
|
+
async listDiagnosticErrors() {
|
|
575
|
+
return this.errorReporter.list();
|
|
576
|
+
}
|
|
577
|
+
async runDiagnosticsDoctor() {
|
|
578
|
+
const checkedAt = new Date().toISOString();
|
|
579
|
+
const configFile = await this.readConfigFile();
|
|
580
|
+
const runtimeChecks = await this.refreshInstalledAgentRuntimes().catch((error) => {
|
|
581
|
+
const errorInfo = (0, local_core_errors_js_1.toLocalCoreErrorInfo)(error, 'internal_error');
|
|
582
|
+
return [{
|
|
583
|
+
agentType: 'runtime',
|
|
584
|
+
runtimeId: 'runtime',
|
|
585
|
+
displayName: 'Runtime Detection',
|
|
586
|
+
status: 'error',
|
|
587
|
+
installed: false,
|
|
588
|
+
detectedAt: checkedAt,
|
|
589
|
+
summary: errorInfo.userMessage,
|
|
590
|
+
issues: [{ code: errorInfo.code, severity: errorInfo.severity, message: errorInfo.message, help: errorInfo.suggestedAction }],
|
|
591
|
+
recommendedActions: errorInfo.suggestedAction ? [{ label: 'Fix runtime detection', description: errorInfo.suggestedAction }] : [],
|
|
592
|
+
source: 'path',
|
|
593
|
+
error: errorInfo.message,
|
|
594
|
+
readiness: 'failed',
|
|
595
|
+
lastLaunchError: errorInfo,
|
|
596
|
+
}];
|
|
597
|
+
});
|
|
598
|
+
const channelStatuses = await this.listChannelGatewayStatuses().catch(() => []);
|
|
599
|
+
const checks = [
|
|
600
|
+
configFile.error
|
|
601
|
+
? {
|
|
602
|
+
id: 'config',
|
|
603
|
+
label: 'Configuration',
|
|
604
|
+
status: 'fail',
|
|
605
|
+
summary: configFile.error,
|
|
606
|
+
errorInfo: new local_core_errors_js_1.LocalCoreError('config_invalid', configFile.error).info,
|
|
607
|
+
}
|
|
608
|
+
: {
|
|
609
|
+
id: 'config',
|
|
610
|
+
label: 'Configuration',
|
|
611
|
+
status: configFile.exists ? 'pass' : 'warn',
|
|
612
|
+
summary: configFile.exists ? 'Configuration file is readable.' : 'Configuration file has not been created yet.',
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
id: 'runtime-detection',
|
|
616
|
+
label: 'Runtime Detection',
|
|
617
|
+
status: runtimeChecks.some((runtime) => runtime.status === 'error') ? 'fail' : 'pass',
|
|
618
|
+
summary: `${runtimeChecks.length} runtime(s) checked.`,
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: 'channels',
|
|
622
|
+
label: 'Channel Gateways',
|
|
623
|
+
status: channelStatuses.some((status) => status.status === 'error') ? 'warn' : 'pass',
|
|
624
|
+
summary: `${channelStatuses.length} channel gateway status record(s) checked.`,
|
|
625
|
+
errorInfo: channelStatuses.find((status) => status.lastErrorInfo)?.lastErrorInfo,
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
id: 'logs',
|
|
629
|
+
label: 'Logs',
|
|
630
|
+
status: 'pass',
|
|
631
|
+
summary: 'Local AI Core log reader is available.',
|
|
632
|
+
},
|
|
633
|
+
];
|
|
634
|
+
const status = checks.some((check) => check.status === 'fail')
|
|
635
|
+
? 'fail'
|
|
636
|
+
: checks.some((check) => check.status === 'warn')
|
|
637
|
+
? 'warn'
|
|
638
|
+
: 'pass';
|
|
639
|
+
return { status, checkedAt, checks };
|
|
640
|
+
}
|
|
641
|
+
async runDeploymentDiagnostics() {
|
|
642
|
+
const configFile = await this.readConfigFile();
|
|
643
|
+
return (0, deployment_diagnostics_js_1.runDeploymentDiagnostics)({ config: configFile.parsed });
|
|
644
|
+
}
|
|
345
645
|
async probeWorkspaceStreaming(workspaceId) {
|
|
346
646
|
return this.workspaceRouter.probeWorkspaceStreaming(workspaceId);
|
|
347
647
|
}
|
|
@@ -529,3 +829,30 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
529
829
|
}
|
|
530
830
|
}
|
|
531
831
|
exports.LocalCoreController = LocalCoreController;
|
|
832
|
+
function normalizeExternalId(value, label) {
|
|
833
|
+
const normalized = String(value || '').trim();
|
|
834
|
+
if (!normalized) {
|
|
835
|
+
throw new Error(`${label} is required.`);
|
|
836
|
+
}
|
|
837
|
+
return normalized;
|
|
838
|
+
}
|
|
839
|
+
function normalizeExternalSegment(value, fallback) {
|
|
840
|
+
return String(value || '')
|
|
841
|
+
.trim()
|
|
842
|
+
.toLowerCase()
|
|
843
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
844
|
+
.replace(/^-+|-+$/g, '')
|
|
845
|
+
|| fallback;
|
|
846
|
+
}
|
|
847
|
+
function externalWorkspaceId(userId, externalProjectId) {
|
|
848
|
+
const base = [
|
|
849
|
+
'external',
|
|
850
|
+
normalizeExternalSegment(userId, 'user'),
|
|
851
|
+
normalizeExternalSegment(externalProjectId, 'project'),
|
|
852
|
+
].join('-');
|
|
853
|
+
const digest = (0, node_crypto_1.createHash)('sha256')
|
|
854
|
+
.update(`${userId}\0${externalProjectId}`)
|
|
855
|
+
.digest('hex')
|
|
856
|
+
.slice(0, 8);
|
|
857
|
+
return `${base}-${digest}`;
|
|
858
|
+
}
|
|
@@ -38,6 +38,7 @@ const node_fs_1 = require("node:fs");
|
|
|
38
38
|
const node_path_1 = require("node:path");
|
|
39
39
|
const TOML = __importStar(require("@iarna/toml"));
|
|
40
40
|
const rotating_logger_js_1 = require("../kernel/rotating-logger.js");
|
|
41
|
+
const config_migration_js_1 = require("./config-migration.js");
|
|
41
42
|
const DEFAULT_CONFIG = `# Managed by Local AI Core
|
|
42
43
|
# Add [[projects]] entries from the workspace page before starting a conversation.
|
|
43
44
|
`;
|
|
@@ -140,7 +141,14 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
140
141
|
const raw = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
141
142
|
try {
|
|
142
143
|
const parsed = TOML.parse(raw);
|
|
143
|
-
|
|
144
|
+
const migrated = (0, config_migration_js_1.migrateDesktopConnectConfig)(parsed);
|
|
145
|
+
return {
|
|
146
|
+
path,
|
|
147
|
+
exists: true,
|
|
148
|
+
raw,
|
|
149
|
+
parsed: migrated.config,
|
|
150
|
+
warnings: migrated.warnings.length > 0 ? migrated.warnings : undefined,
|
|
151
|
+
};
|
|
144
152
|
}
|
|
145
153
|
catch (error) {
|
|
146
154
|
return {
|
|
@@ -158,8 +166,9 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
158
166
|
return this.readConfigFile();
|
|
159
167
|
}
|
|
160
168
|
async saveStructuredConfigFile(config) {
|
|
169
|
+
const migrated = (0, config_migration_js_1.migrateDesktopConnectConfig)(config);
|
|
161
170
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.settings.configPath), { recursive: true });
|
|
162
|
-
(0, node_fs_1.writeFileSync)(this.settings.configPath, TOML.stringify(config), 'utf8');
|
|
171
|
+
(0, node_fs_1.writeFileSync)(this.settings.configPath, TOML.stringify(migrated.config), 'utf8');
|
|
163
172
|
return this.readConfigFile();
|
|
164
173
|
}
|
|
165
174
|
loadSettings() {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migrateLegacyProjectProvidersToStore = migrateLegacyProjectProvidersToStore;
|
|
4
|
+
const config_migration_js_1 = require("./config-migration.js");
|
|
5
|
+
function migrateLegacyProjectProvidersToStore(input, store) {
|
|
6
|
+
const config = cloneConfig(input);
|
|
7
|
+
const warnings = [];
|
|
8
|
+
let changed = false;
|
|
9
|
+
for (const project of Array.isArray(config.projects) ? config.projects : []) {
|
|
10
|
+
const legacyProviders = Array.isArray(project.agent?.providers) ? project.agent.providers : [];
|
|
11
|
+
if (legacyProviders.length === 0) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
project.agent ||= { type: 'pi', options: {}, providers: [] };
|
|
15
|
+
project.agent.options ||= {};
|
|
16
|
+
const migratedProviderIds = [];
|
|
17
|
+
for (const provider of legacyProviders) {
|
|
18
|
+
const migrated = store.upsertModelProvider((0, config_migration_js_1.normalizeDesktopProviderForStorage)(provider));
|
|
19
|
+
migratedProviderIds.push(migrated.id);
|
|
20
|
+
}
|
|
21
|
+
if (!project.agent.options.provider_id && migratedProviderIds[0]) {
|
|
22
|
+
project.agent.options.provider_id = migratedProviderIds[0];
|
|
23
|
+
}
|
|
24
|
+
delete project.agent.providers;
|
|
25
|
+
warnings.push(`Project "${project.name}" migrated embedded providers to shared providers.`);
|
|
26
|
+
changed = true;
|
|
27
|
+
}
|
|
28
|
+
return { config, changed, warnings };
|
|
29
|
+
}
|
|
30
|
+
function cloneConfig(input) {
|
|
31
|
+
return JSON.parse(JSON.stringify(input || {}));
|
|
32
|
+
}
|