@kafca/agentdock 0.1.30 → 0.1.33
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/LICENSE.md +73 -0
- package/README.md +15 -0
- package/dist/renderer/assets/{Badge-DlVJI1Ys.js → Badge-VeSLCPPV.js} +1 -1
- package/dist/renderer/assets/{Chat-oa8OjaHQ.js → Chat-DWTQeTgn.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-C-N8KS5C.js → ChatMarkdown-C9gSQIVb.js} +1 -1
- package/dist/renderer/assets/{Config-D9sL_d4D.js → Config-CTA-pZNR.js} +1 -1
- package/dist/renderer/assets/{CronList-Z5JU3DHk.js → CronList-BngBCnFY.js} +1 -1
- package/dist/renderer/assets/{Dashboard-D3BwbG43.js → Dashboard-D4pGvDpc.js} +2 -2
- package/dist/renderer/assets/{EmptyState-QOj765Ul.js → EmptyState-DMf8rzDs.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-J7n01mPu.js → KnowledgeDetail-C1Ym_cHk.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-BFnH0NVk.js → KnowledgeHome-DPRiR72G.js} +1 -1
- package/dist/renderer/assets/{Logs-DfSUifXc.js → Logs-CRHecmzO.js} +1 -1
- package/dist/renderer/assets/{Modal-QjEGhyru.js → Modal-BDhhPv2l.js} +1 -1
- package/dist/renderer/assets/{Page-qdHFSw7x.js → Page-DdDZN7AE.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-DS_rfvhh.js → ProjectDetail-DCsr3LkW.js} +1 -1
- package/dist/renderer/assets/{ProjectList-COtyZd7H.js → ProjectList-Cnm-iLvg.js} +1 -1
- package/dist/renderer/assets/{Select-BQ8mncVu.js → Select-Dw4FCXhJ.js} +1 -1
- package/dist/renderer/assets/{SessionChat-DjVnjuQy.js → SessionChat-O-6abK8x.js} +1 -1
- package/dist/renderer/assets/{SessionList-88dgGZJ8.js → SessionList-5yptvVL9.js} +1 -1
- package/dist/renderer/assets/ThreadChat-CIM_o2HO.js +23 -0
- package/dist/renderer/assets/{Workspace-D_hzSJjX.js → Workspace-D9UeUCNr.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BMpknulW.js → arrow-left-CKfxNOXE.js} +1 -1
- package/dist/renderer/assets/{book-open-CiaOeQbU.js → book-open-CQddlENV.js} +1 -1
- package/dist/renderer/assets/{check-CgwYH4GF.js → check-CZYvOmIS.js} +1 -1
- package/dist/renderer/assets/{chevron-down-BzD2S1OO.js → chevron-down-CVIWFoXF.js} +1 -1
- package/dist/renderer/assets/{circle-BDHD7g7l.js → circle-DSfdQM7k.js} +1 -1
- package/dist/renderer/assets/{desktop-BiR5LPL5.js → desktop-B9KUgeOV.js} +1 -1
- package/dist/renderer/assets/{desktop-BkMW-h5p.js → desktop-O8_6ZCVZ.js} +2 -2
- package/dist/renderer/assets/{funnel-Bruf5FYA.js → funnel-pB5NegZB.js} +1 -1
- package/dist/renderer/assets/{heart-D7Mz04B0.js → heart-BT5laZ_b.js} +1 -1
- package/dist/renderer/assets/{index-CL1e8m3U.js → index-BYyDk28K.js} +2 -2
- package/dist/renderer/assets/{index-gZIRr4_o.js → index-zTUUYax3.js} +1 -1
- package/dist/renderer/assets/{pencil-C6gzDMse.js → pencil-C0thxLi6.js} +1 -1
- package/dist/renderer/assets/{play-DqTXX-Pr.js → play-CmZ0kWtt.js} +1 -1
- package/dist/renderer/assets/{plug-ykmbeaO3.js → plug-CeDQatBe.js} +1 -1
- package/dist/renderer/assets/{plus-DHE60vMX.js → plus-Di__xFOZ.js} +1 -1
- package/dist/renderer/assets/projects-B-pbhhtH.js +1 -0
- package/dist/renderer/assets/{save-CGIvrdQI.js → save-BDatCjfO.js} +1 -1
- package/dist/renderer/assets/{search-D7IFAwyE.js → search-DnTOjP0C.js} +1 -1
- package/dist/renderer/assets/{server-aOsirp7I.js → server-Ey_iHY6m.js} +1 -1
- package/dist/renderer/assets/{session-utils-Dxfp4ohQ.js → session-utils-DWaM7Rkr.js} +1 -1
- package/dist/renderer/assets/{sessions-Db1XPbG2.js → sessions-CYYjLD5I.js} +1 -1
- package/dist/renderer/assets/{shield-check-ChXeQK-d.js → shield-check-Br8BQzcn.js} +1 -1
- package/dist/renderer/assets/{trash-2-CfN-c1zw.js → trash-2-7YD616fw.js} +1 -1
- package/dist/renderer/assets/{user-C7OHaBp4.js → user-BR_5jmr9.js} +1 -1
- package/dist/renderer/assets/{wifi-off-CpaJigye.js → wifi-off-By9QdREz.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +28 -4
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +2 -4
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +102 -15
- package/dist-electron/services/local-ai-core/src/agents/claudecode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +36 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +24 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/behavior.js +29 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/definition.js +22 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/index.js +98 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +14 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +27 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/definition.js +18 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +119 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +23 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +139 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/acp-behavior.js +11 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +60 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/definition.js +12 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +122 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/progress-replay.js +19 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/replay-prefix.js +68 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +0 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +0 -1
- package/dist-electron/services/local-ai-core/src/cli/lac.js +2 -1
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +17 -11
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -130
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +22 -397
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +9 -45
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +9 -4
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +62 -1
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +2 -12
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +16 -7
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +10 -2
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +24 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +15 -6
- package/dist-electron/shared/desktop.js +3 -1
- package/dist-electron/src/pages/Threads/thread-chat-model.js +27 -18
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +57 -18
- package/dist-electron/{electron → tests/contracts}/local-core-contracts.test.js +1 -1
- package/dist-electron/{electron → tests/electron}/agent-runtime-detector.test.js +31 -1
- package/dist-electron/{electron → tests/electron}/lac-cli.test.js +6 -3
- package/dist-electron/{electron → tests/electron}/plugin-kernel.test.js +93 -13
- package/dist-electron/{electron → tests/electron}/renderer-packaging.test.js +11 -6
- package/dist-electron/{electron → tests/electron}/runtime-detection-service.test.js +1 -1
- package/dist-electron/{electron → tests/electron}/security-approval-store.test.js +2 -2
- package/dist-electron/{electron → tests/electron}/workspace-task-store.test.js +95 -7
- package/dist-electron/{electron → tests/integration}/channel-content.test.js +2 -2
- package/dist-electron/{electron → tests/integration}/local-core-acp-content.test.js +1 -1
- package/dist-electron/{electron → tests/integration}/local-core-refactor.test.js +437 -15
- package/package.json +3 -2
- package/dist/renderer/assets/ThreadChat-BCdbTxBo.js +0 -23
- package/dist/renderer/assets/projects-Bx5p10th.js +0 -1
- /package/dist-electron/{electron → tests/contracts}/architecture-docs.test.js +0 -0
- /package/dist-electron/{electron → tests/electron}/knowledge-skill-script.test.js +0 -0
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.normalizePlatformTypes = normalizePlatformTypes;
|
|
4
4
|
exports.isLocalCoreNativeAcpProject = isLocalCoreNativeAcpProject;
|
|
5
5
|
exports.toLocalCoreProjectConfig = toLocalCoreProjectConfig;
|
|
6
|
-
const node_module_1 = require("node:module");
|
|
7
|
-
const node_fs_1 = require("node:fs");
|
|
8
6
|
const node_path_1 = require("node:path");
|
|
9
7
|
const desktop_js_1 = require("../../../../shared/desktop.js");
|
|
8
|
+
const index_js_1 = require("../agents/index.js");
|
|
9
|
+
const launch_utils_js_1 = require("../agents/shared/launch-utils.js");
|
|
10
10
|
function normalizePlatformTypes(project) {
|
|
11
11
|
return Array.isArray(project?.platforms)
|
|
12
12
|
? project.platforms.map((platform) => (0, desktop_js_1.normalizeDesktopPlatformType)(platform?.type)).filter(Boolean)
|
|
@@ -18,348 +18,16 @@ function isLocalCoreNativeAcpProject(project) {
|
|
|
18
18
|
|| agentType === 'acp'
|
|
19
19
|
|| agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE;
|
|
20
20
|
}
|
|
21
|
-
function resolveAgentModel(project, providers) {
|
|
21
|
+
function resolveAgentModel(project, providers, definition) {
|
|
22
22
|
const agentType = String(project.agent?.type || '').trim().toLowerCase();
|
|
23
23
|
const rawModel = String(project.agent?.options?.model || '').trim();
|
|
24
24
|
const normalizedModel = (0, desktop_js_1.normalizeDesktopAgentModel)(agentType, rawModel);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
const configuredProviderModel = getFirstProviderModelRef(providers);
|
|
32
|
-
if (configuredProviderModel &&
|
|
33
|
-
(!rawModel || normalizedModel === desktop_js_1.DEFAULT_DESKTOP_OPENCODE_MODEL)) {
|
|
34
|
-
return configuredProviderModel;
|
|
35
|
-
}
|
|
36
|
-
return normalizedModel;
|
|
37
|
-
}
|
|
38
|
-
function getFirstProviderModelId(providers) {
|
|
39
|
-
for (const provider of providers) {
|
|
40
|
-
const modelId = getProviderDefaultModelId(provider);
|
|
41
|
-
if (modelId) {
|
|
42
|
-
return modelId;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
function getFirstProviderModelRef(providers) {
|
|
48
|
-
for (const provider of providers) {
|
|
49
|
-
const providerId = normalizeOpencodeProviderId(provider.name);
|
|
50
|
-
const modelId = getProviderDefaultModelId(provider);
|
|
51
|
-
if (providerId && modelId) {
|
|
52
|
-
return `${providerId}/${modelId}`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return '';
|
|
56
|
-
}
|
|
57
|
-
function getProviderDefaultModelId(provider) {
|
|
58
|
-
const directModel = String(provider.model || '').trim();
|
|
59
|
-
if (directModel) {
|
|
60
|
-
return directModel;
|
|
61
|
-
}
|
|
62
|
-
const firstModel = Array.isArray(provider.models)
|
|
63
|
-
? provider.models.find((entry) => String(entry?.model || '').trim())
|
|
64
|
-
: null;
|
|
65
|
-
return String(firstModel?.model || '').trim();
|
|
66
|
-
}
|
|
67
|
-
function buildOpencodeInlineConfig(model, providers) {
|
|
68
|
-
const config = {
|
|
69
|
-
$schema: 'https://opencode.ai/config.json',
|
|
70
|
-
};
|
|
71
|
-
const env = {};
|
|
72
|
-
if (model) {
|
|
73
|
-
config.model = model;
|
|
74
|
-
}
|
|
75
|
-
const providerConfig = {};
|
|
76
|
-
for (const provider of providers) {
|
|
77
|
-
const providerId = normalizeOpencodeProviderId(provider.name);
|
|
78
|
-
if (!providerId) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const entry = {
|
|
82
|
-
name: String(provider.name || providerId),
|
|
83
|
-
};
|
|
84
|
-
if (shouldUseOpenAiCompatibleProvider(providerId)) {
|
|
85
|
-
entry.npm = '@ai-sdk/openai-compatible';
|
|
86
|
-
}
|
|
87
|
-
const options = {};
|
|
88
|
-
const baseUrl = String(provider.base_url || '').trim();
|
|
89
|
-
if (baseUrl) {
|
|
90
|
-
options.baseURL = baseUrl;
|
|
91
|
-
}
|
|
92
|
-
const apiKey = String(provider.api_key || '').trim();
|
|
93
|
-
if (apiKey) {
|
|
94
|
-
const envName = opencodeProviderApiKeyEnvName(providerId);
|
|
95
|
-
env[envName] = apiKey;
|
|
96
|
-
options.apiKey = `{env:${envName}}`;
|
|
97
|
-
}
|
|
98
|
-
if (Object.keys(options).length > 0) {
|
|
99
|
-
entry.options = options;
|
|
100
|
-
}
|
|
101
|
-
const models = buildOpencodeProviderModels(provider);
|
|
102
|
-
if (Object.keys(models).length > 0) {
|
|
103
|
-
entry.models = models;
|
|
104
|
-
}
|
|
105
|
-
providerConfig[providerId] = entry;
|
|
106
|
-
}
|
|
107
|
-
if (Object.keys(providerConfig).length > 0) {
|
|
108
|
-
config.provider = providerConfig;
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
config,
|
|
112
|
-
env,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
function buildOpencodeProviderModels(provider) {
|
|
116
|
-
const models = {};
|
|
117
|
-
const addModel = (model, alias) => {
|
|
118
|
-
const modelId = String(model || '').trim();
|
|
119
|
-
if (!modelId || models[modelId]) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
models[modelId] = {
|
|
123
|
-
name: String(alias || modelId).trim() || modelId,
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
addModel(provider.model);
|
|
127
|
-
for (const model of Array.isArray(provider.models) ? provider.models : []) {
|
|
128
|
-
addModel(model?.model, model?.alias);
|
|
129
|
-
}
|
|
130
|
-
return models;
|
|
131
|
-
}
|
|
132
|
-
function collectProviderEnv(providers) {
|
|
133
|
-
const env = {};
|
|
134
|
-
for (const provider of providers) {
|
|
135
|
-
if (!provider.env || typeof provider.env !== 'object') {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
for (const [key, value] of Object.entries(provider.env)) {
|
|
139
|
-
const envKey = key.trim();
|
|
140
|
-
if (envKey) {
|
|
141
|
-
env[envKey] = String(value ?? '');
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return env;
|
|
146
|
-
}
|
|
147
|
-
function collectPiProviderEnv(providers) {
|
|
148
|
-
const env = {};
|
|
149
|
-
for (const provider of providers) {
|
|
150
|
-
const apiKey = String(provider.api_key || '').trim();
|
|
151
|
-
const envName = piProviderApiKeyEnvName(provider.name);
|
|
152
|
-
if (apiKey && envName) {
|
|
153
|
-
env[envName] = apiKey;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return env;
|
|
157
|
-
}
|
|
158
|
-
function resolvePiProviderModel(providers, model) {
|
|
159
|
-
const [explicitProvider, explicitModel] = splitProviderModelRef(model);
|
|
160
|
-
if (explicitProvider && explicitModel) {
|
|
161
|
-
return {
|
|
162
|
-
providerId: normalizePiProviderId(explicitProvider),
|
|
163
|
-
modelId: explicitModel,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
for (const provider of providers) {
|
|
167
|
-
const providerId = normalizePiProviderId(provider.name);
|
|
168
|
-
const modelId = getProviderDefaultModelId(provider);
|
|
169
|
-
if (providerId && modelId && (!model || model === modelId)) {
|
|
170
|
-
return { providerId, modelId };
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
const fallbackProvider = providers.find((provider) => normalizePiProviderId(provider.name));
|
|
174
|
-
return {
|
|
175
|
-
providerId: normalizePiProviderId(fallbackProvider?.name),
|
|
176
|
-
modelId: model,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function splitProviderModelRef(value) {
|
|
180
|
-
const trimmed = value.trim();
|
|
181
|
-
const slashIndex = trimmed.indexOf('/');
|
|
182
|
-
if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
|
|
183
|
-
return ['', trimmed];
|
|
184
|
-
}
|
|
185
|
-
return [trimmed.slice(0, slashIndex), trimmed.slice(slashIndex + 1)];
|
|
186
|
-
}
|
|
187
|
-
function normalizePiProviderId(providerName) {
|
|
188
|
-
const providerId = normalizeProviderId(providerName);
|
|
189
|
-
const aliases = {
|
|
190
|
-
google: 'google',
|
|
191
|
-
gemini: 'google',
|
|
192
|
-
kimi: 'kimi-coding',
|
|
193
|
-
moonshot: 'moonshotai',
|
|
194
|
-
zhipuai: 'zai',
|
|
195
|
-
};
|
|
196
|
-
return aliases[providerId] || providerId;
|
|
197
|
-
}
|
|
198
|
-
function piProviderAgentDir(configState, projectName) {
|
|
199
|
-
const configDir = (0, node_path_1.dirname)(configState.path);
|
|
200
|
-
const safeProjectName = String(projectName || 'workspace')
|
|
201
|
-
.trim()
|
|
202
|
-
.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
203
|
-
.replace(/^-+|-+$/g, '') || 'workspace';
|
|
204
|
-
return (0, node_path_1.resolve)(configDir, '.pi-agent', safeProjectName);
|
|
205
|
-
}
|
|
206
|
-
function writeJsonFile(path, value, mode = 0o600) {
|
|
207
|
-
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode });
|
|
208
|
-
(0, node_fs_1.chmodSync)(path, mode);
|
|
209
|
-
}
|
|
210
|
-
function writePiProviderRuntimeConfig(configState, project, providers, model) {
|
|
211
|
-
if (providers.length === 0) {
|
|
212
|
-
return {};
|
|
213
|
-
}
|
|
214
|
-
const { providerId, modelId } = resolvePiProviderModel(providers, model);
|
|
215
|
-
const agentDir = piProviderAgentDir(configState, project.name);
|
|
216
|
-
(0, node_fs_1.mkdirSync)(agentDir, { recursive: true, mode: 0o700 });
|
|
217
|
-
(0, node_fs_1.chmodSync)(agentDir, 0o700);
|
|
218
|
-
const auth = {};
|
|
219
|
-
const modelProviders = {};
|
|
220
|
-
for (const provider of providers) {
|
|
221
|
-
const id = normalizePiProviderId(provider.name);
|
|
222
|
-
if (!id) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const apiKey = String(provider.api_key || '').trim();
|
|
226
|
-
const baseUrl = String(provider.base_url || '').trim();
|
|
227
|
-
if (apiKey) {
|
|
228
|
-
auth[id] = { type: 'api_key', key: apiKey };
|
|
229
|
-
}
|
|
230
|
-
if (baseUrl || apiKey) {
|
|
231
|
-
modelProviders[id] = {
|
|
232
|
-
name: String(provider.name || id).trim() || id,
|
|
233
|
-
...(baseUrl ? { baseUrl } : {}),
|
|
234
|
-
...(apiKey ? { apiKey } : {}),
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
writeJsonFile((0, node_path_1.resolve)(agentDir, 'auth.json'), auth);
|
|
239
|
-
writeJsonFile((0, node_path_1.resolve)(agentDir, 'models.json'), { providers: modelProviders });
|
|
240
|
-
writeJsonFile((0, node_path_1.resolve)(agentDir, 'settings.json'), {
|
|
241
|
-
...(providerId ? { defaultProvider: providerId } : {}),
|
|
242
|
-
...(modelId ? { defaultModel: modelId } : {}),
|
|
243
|
-
quietStartup: true,
|
|
244
|
-
}, 0o644);
|
|
245
|
-
return {
|
|
246
|
-
PI_CODING_AGENT_DIR: agentDir,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
function normalizeOpencodeProviderId(value) {
|
|
250
|
-
return String(value || '')
|
|
251
|
-
.trim()
|
|
252
|
-
.toLowerCase()
|
|
253
|
-
.replace(/[^a-z0-9._-]+/g, '-')
|
|
254
|
-
.replace(/^-+|-+$/g, '');
|
|
255
|
-
}
|
|
256
|
-
function normalizeProviderId(value) {
|
|
257
|
-
return String(value || '')
|
|
258
|
-
.trim()
|
|
259
|
-
.toLowerCase()
|
|
260
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
261
|
-
.replace(/^-+|-+$/g, '');
|
|
262
|
-
}
|
|
263
|
-
function piProviderApiKeyEnvName(providerName) {
|
|
264
|
-
const providerId = normalizeProviderId(providerName);
|
|
265
|
-
const envNames = {
|
|
266
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
267
|
-
'azure-openai': 'AZURE_OPENAI_API_KEY',
|
|
268
|
-
'azure-openai-responses': 'AZURE_OPENAI_API_KEY',
|
|
269
|
-
cerebras: 'CEREBRAS_API_KEY',
|
|
270
|
-
cloudflare: 'CLOUDFLARE_API_KEY',
|
|
271
|
-
'cloudflare-ai-gateway': 'CLOUDFLARE_API_KEY',
|
|
272
|
-
'cloudflare-workers-ai': 'CLOUDFLARE_API_KEY',
|
|
273
|
-
deepseek: 'DEEPSEEK_API_KEY',
|
|
274
|
-
fireworks: 'FIREWORKS_API_KEY',
|
|
275
|
-
gemini: 'GEMINI_API_KEY',
|
|
276
|
-
google: 'GEMINI_API_KEY',
|
|
277
|
-
groq: 'GROQ_API_KEY',
|
|
278
|
-
huggingface: 'HF_TOKEN',
|
|
279
|
-
kimi: 'KIMI_API_KEY',
|
|
280
|
-
'kimi-coding': 'KIMI_API_KEY',
|
|
281
|
-
minimax: 'MINIMAX_API_KEY',
|
|
282
|
-
'minimax-cn': 'MINIMAX_CN_API_KEY',
|
|
283
|
-
mistral: 'MISTRAL_API_KEY',
|
|
284
|
-
moonshot: 'KIMI_API_KEY',
|
|
285
|
-
openai: 'OPENAI_API_KEY',
|
|
286
|
-
opencode: 'OPENCODE_API_KEY',
|
|
287
|
-
'opencode-go': 'OPENCODE_API_KEY',
|
|
288
|
-
openrouter: 'OPENROUTER_API_KEY',
|
|
289
|
-
siliconflow: 'SILICONFLOW_API_KEY',
|
|
290
|
-
vercel: 'AI_GATEWAY_API_KEY',
|
|
291
|
-
'vercel-ai-gateway': 'AI_GATEWAY_API_KEY',
|
|
292
|
-
xai: 'XAI_API_KEY',
|
|
293
|
-
zai: 'ZAI_API_KEY',
|
|
294
|
-
zhipuai: 'ZAI_API_KEY',
|
|
295
|
-
};
|
|
296
|
-
return envNames[providerId] || '';
|
|
297
|
-
}
|
|
298
|
-
function opencodeProviderApiKeyEnvName(providerId) {
|
|
299
|
-
const suffix = providerId.toUpperCase().replace(/[^A-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
300
|
-
return `AI_WORKSTATION_OPENCODE_${suffix || 'PROVIDER'}_API_KEY`;
|
|
301
|
-
}
|
|
302
|
-
function shouldUseOpenAiCompatibleProvider(providerId) {
|
|
303
|
-
const builtInNonCompatibleProviders = new Set([
|
|
304
|
-
'anthropic',
|
|
305
|
-
'google',
|
|
306
|
-
'gemini',
|
|
307
|
-
]);
|
|
308
|
-
return !builtInNonCompatibleProviders.has(providerId);
|
|
309
|
-
}
|
|
310
|
-
function resolveBundledAcpCommand(packageName, binName) {
|
|
311
|
-
const require = (0, node_module_1.createRequire)(__filename);
|
|
312
|
-
const packageJsonPath = resolveBundledPackageJsonPath(packageName);
|
|
313
|
-
const packageJson = require(packageJsonPath);
|
|
314
|
-
const binField = packageJson.bin;
|
|
315
|
-
const relativeBinPath = typeof binField === 'string'
|
|
316
|
-
? binField
|
|
317
|
-
: binField?.[binName];
|
|
318
|
-
if (!relativeBinPath) {
|
|
319
|
-
throw new Error(`Bundled package "${packageName}" does not declare the ${binName} bin.`);
|
|
320
|
-
}
|
|
321
|
-
return {
|
|
322
|
-
command: process.execPath,
|
|
323
|
-
args: [(0, node_path_1.resolve)((0, node_path_1.dirname)(packageJsonPath), relativeBinPath)],
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
function resolveBundledPackageJsonPath(packageName) {
|
|
327
|
-
const require = (0, node_module_1.createRequire)(__filename);
|
|
328
|
-
try {
|
|
329
|
-
return require.resolve(`${packageName}/package.json`);
|
|
330
|
-
}
|
|
331
|
-
catch (error) {
|
|
332
|
-
if (error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
|
|
333
|
-
throw error;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
for (const basePath of require.resolve.paths(packageName) || []) {
|
|
337
|
-
const packageJsonPath = (0, node_path_1.resolve)(basePath, ...packageName.split('/'), 'package.json');
|
|
338
|
-
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
339
|
-
return packageJsonPath;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
let current = (0, node_path_1.dirname)(require.resolve(packageName));
|
|
343
|
-
while (current && current !== (0, node_path_1.dirname)(current)) {
|
|
344
|
-
const packageJsonPath = (0, node_path_1.resolve)(current, 'package.json');
|
|
345
|
-
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
346
|
-
return packageJsonPath;
|
|
347
|
-
}
|
|
348
|
-
current = (0, node_path_1.dirname)(current);
|
|
349
|
-
}
|
|
350
|
-
throw new Error(`Bundled package "${packageName}" package.json could not be resolved.`);
|
|
351
|
-
}
|
|
352
|
-
function resolveBundledCodexCommand() {
|
|
353
|
-
return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, 'codex-acp');
|
|
354
|
-
}
|
|
355
|
-
function resolveBundledClaudeCodeCommand() {
|
|
356
|
-
return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, 'claude-agent-acp');
|
|
357
|
-
}
|
|
358
|
-
function resolveBundledPiAcpCommand() {
|
|
359
|
-
return resolveBundledAcpCommand(desktop_js_1.DESKTOP_PI_ACP_PACKAGE, 'pi-acp');
|
|
360
|
-
}
|
|
361
|
-
function resolveBundledPiCommand() {
|
|
362
|
-
return resolveBundledAcpCommand(desktop_js_1.DESKTOP_PI_CODING_AGENT_PACKAGE, 'pi').args[0];
|
|
25
|
+
return definition?.resolveModel?.({
|
|
26
|
+
project,
|
|
27
|
+
providers,
|
|
28
|
+
rawModel,
|
|
29
|
+
normalizedModel,
|
|
30
|
+
}) || normalizedModel;
|
|
363
31
|
}
|
|
364
32
|
function toLocalCoreProjectConfig(configState, project) {
|
|
365
33
|
const rawWorkDir = String(project.agent?.options?.work_dir || '.').trim() || '.';
|
|
@@ -377,63 +45,22 @@ function toLocalCoreProjectConfig(configState, project) {
|
|
|
377
45
|
: {};
|
|
378
46
|
const agentType = String(project.agent?.type || '').trim().toLowerCase();
|
|
379
47
|
const providers = Array.isArray(project.agent?.providers) ? project.agent.providers : [];
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeInlineConfig?.config || { $schema: 'https://opencode.ai/config.json' }),
|
|
392
|
-
}
|
|
393
|
-
: {};
|
|
394
|
-
const inferredClaudeCodeEnv = agentType === 'claudecode' && model
|
|
395
|
-
? {
|
|
396
|
-
ANTHROPIC_MODEL: model,
|
|
397
|
-
}
|
|
398
|
-
: {};
|
|
399
|
-
const bundledPiAcp = agentType === 'pi'
|
|
400
|
-
? resolveBundledPiAcpCommand()
|
|
401
|
-
: null;
|
|
402
|
-
const bundledPiCommand = agentType === 'pi'
|
|
403
|
-
? resolveBundledPiCommand()
|
|
404
|
-
: '';
|
|
405
|
-
const piProviderRuntimeEnv = agentType === 'pi'
|
|
406
|
-
? writePiProviderRuntimeConfig(configState, project, providers, model)
|
|
407
|
-
: {};
|
|
408
|
-
const inferredPiEnv = agentType === 'pi'
|
|
409
|
-
? {
|
|
410
|
-
...piProviderEnv,
|
|
411
|
-
...piProviderRuntimeEnv,
|
|
412
|
-
PI_ACP_PI_COMMAND: bundledPiCommand,
|
|
413
|
-
}
|
|
414
|
-
: {};
|
|
415
|
-
const bundledCodex = agentType === 'codex'
|
|
416
|
-
? resolveBundledCodexCommand()
|
|
417
|
-
: null;
|
|
418
|
-
const bundledClaudeCode = agentType === 'claudecode'
|
|
419
|
-
? resolveBundledClaudeCodeCommand()
|
|
420
|
-
: null;
|
|
421
|
-
const inferredCommand = agentType === 'opencode'
|
|
422
|
-
? 'opencode'
|
|
423
|
-
: bundledPiAcp?.command || bundledCodex?.command || bundledClaudeCode?.command || '';
|
|
48
|
+
const definition = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType);
|
|
49
|
+
const model = resolveAgentModel(project, providers, definition);
|
|
50
|
+
const providerEnv = (0, launch_utils_js_1.collectProviderEnv)(providers);
|
|
51
|
+
const launchDefaults = definition?.buildLaunchConfig?.({
|
|
52
|
+
configState,
|
|
53
|
+
project,
|
|
54
|
+
agentType,
|
|
55
|
+
providers,
|
|
56
|
+
model,
|
|
57
|
+
}) || {};
|
|
58
|
+
const inferredCommand = launchDefaults.command || '';
|
|
424
59
|
const command = String(project.agent?.options?.command || inferredCommand).trim();
|
|
425
60
|
if (!command) {
|
|
426
61
|
throw new Error(`Workspace "${project.name}" requires [projects.agent.options].command for Local AI Core ACP execution.`);
|
|
427
62
|
}
|
|
428
|
-
const defaultArgs =
|
|
429
|
-
? ['acp']
|
|
430
|
-
: agentType === 'pi'
|
|
431
|
-
? [...(bundledPiAcp?.args || [])]
|
|
432
|
-
: agentType === 'codex'
|
|
433
|
-
? [...(bundledCodex?.args || [])]
|
|
434
|
-
: agentType === 'claudecode'
|
|
435
|
-
? [...(bundledClaudeCode?.args || [])]
|
|
436
|
-
: [];
|
|
63
|
+
const defaultArgs = launchDefaults.args || [];
|
|
437
64
|
return {
|
|
438
65
|
workspaceId: project.name,
|
|
439
66
|
agentType,
|
|
@@ -441,9 +68,7 @@ function toLocalCoreProjectConfig(configState, project) {
|
|
|
441
68
|
command,
|
|
442
69
|
args: args.length > 0 ? args : defaultArgs,
|
|
443
70
|
env: {
|
|
444
|
-
...
|
|
445
|
-
...inferredClaudeCodeEnv,
|
|
446
|
-
...inferredPiEnv,
|
|
71
|
+
...(launchDefaults.env || {}),
|
|
447
72
|
...providerEnv,
|
|
448
73
|
...env,
|
|
449
74
|
},
|
|
@@ -6,44 +6,16 @@ const node_path_1 = require("node:path");
|
|
|
6
6
|
const node_module_1 = require("node:module");
|
|
7
7
|
const node_child_process_1 = require("node:child_process");
|
|
8
8
|
const desktop_js_1 = require("../../../../shared/desktop.js");
|
|
9
|
-
const
|
|
10
|
-
pi: 'Pi',
|
|
11
|
-
opencode: 'OpenCode',
|
|
12
|
-
codex: 'Codex',
|
|
13
|
-
claudecode: 'Claude Code',
|
|
14
|
-
cursor: 'Cursor',
|
|
15
|
-
gemini: 'Gemini',
|
|
16
|
-
qoder: 'Qoder',
|
|
17
|
-
iflow: 'iFlow',
|
|
18
|
-
[desktop_js_1.LOCALCORE_ACP_AGENT_TYPE]: 'LocalCore ACP',
|
|
19
|
-
};
|
|
20
|
-
const COMMAND_CANDIDATES = {
|
|
21
|
-
pi: ['pi'],
|
|
22
|
-
opencode: ['opencode'],
|
|
23
|
-
codex: ['codex-acp', 'codex'],
|
|
24
|
-
claudecode: ['claude-agent-acp', 'claude'],
|
|
25
|
-
cursor: ['cursor-agent', 'cursor'],
|
|
26
|
-
gemini: ['gemini'],
|
|
27
|
-
qoder: ['qoder'],
|
|
28
|
-
iflow: ['iflow'],
|
|
29
|
-
};
|
|
30
|
-
const VERSION_ARGUMENTS = {
|
|
31
|
-
pi: ['--version'],
|
|
32
|
-
opencode: ['--version'],
|
|
33
|
-
codex: ['--version'],
|
|
34
|
-
claudecode: ['--version'],
|
|
35
|
-
cursor: ['--version'],
|
|
36
|
-
gemini: ['--version'],
|
|
37
|
-
qoder: ['--version'],
|
|
38
|
-
iflow: ['--version'],
|
|
39
|
-
};
|
|
9
|
+
const index_js_1 = require("../agents/index.js");
|
|
40
10
|
function detectInstalledAgentRuntimes(options = {}) {
|
|
41
11
|
const env = options.env || process.env;
|
|
42
12
|
const configuredCommands = collectConfiguredAgentCommands(options.config);
|
|
43
13
|
const detectedAt = (options.now || new Date()).toISOString();
|
|
44
14
|
const versionTimeoutMs = options.versionTimeoutMs ?? 2500;
|
|
45
15
|
return desktop_js_1.DESKTOP_AGENT_TYPE_OPTIONS.map((agentType) => {
|
|
46
|
-
|
|
16
|
+
const definition = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType);
|
|
17
|
+
const detection = definition?.detection;
|
|
18
|
+
if (detection?.builtin || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
|
|
47
19
|
return {
|
|
48
20
|
agentType,
|
|
49
21
|
runtimeId: agentType,
|
|
@@ -64,21 +36,13 @@ function detectInstalledAgentRuntimes(options = {}) {
|
|
|
64
36
|
? installedRuntime(agentType, resolved, 'config', detectedAt, env, versionTimeoutMs)
|
|
65
37
|
: missingRuntime(agentType, detectedAt, `Configured command not found: ${configured}`);
|
|
66
38
|
}
|
|
67
|
-
|
|
68
|
-
const bundled =
|
|
69
|
-
? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_PI_CODING_AGENT_PACKAGE, ['dist/cli.js'])
|
|
70
|
-
: agentType === 'codex'
|
|
71
|
-
? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, ['bin/codex-acp.js'])
|
|
72
|
-
: resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, [
|
|
73
|
-
'dist/cli.js',
|
|
74
|
-
'bin/claude-agent-acp.js',
|
|
75
|
-
'cli.js',
|
|
76
|
-
]);
|
|
39
|
+
for (const bundledRuntime of detection?.bundledRuntimes || []) {
|
|
40
|
+
const bundled = resolveBundledAcpPackage(options.requireFrom, bundledRuntime.packageName, bundledRuntime.candidates);
|
|
77
41
|
if (bundled) {
|
|
78
42
|
return installedRuntime(agentType, bundled, 'bundled', detectedAt, env, versionTimeoutMs);
|
|
79
43
|
}
|
|
80
44
|
}
|
|
81
|
-
for (const command of
|
|
45
|
+
for (const command of detection?.commandCandidates || [agentType]) {
|
|
82
46
|
const resolved = resolveCommand(command, env);
|
|
83
47
|
if (resolved) {
|
|
84
48
|
return installedRuntime(agentType, resolved, 'path', detectedAt, env, versionTimeoutMs);
|
|
@@ -143,7 +107,7 @@ function missingRuntime(agentType, detectedAt, error) {
|
|
|
143
107
|
};
|
|
144
108
|
}
|
|
145
109
|
function displayName(agentType) {
|
|
146
|
-
return
|
|
110
|
+
return (0, index_js_1.resolveAgentRuntimeDefinition)(agentType)?.displayName || agentType;
|
|
147
111
|
}
|
|
148
112
|
function collectConfiguredAgentCommands(config) {
|
|
149
113
|
const commands = new Map();
|
|
@@ -194,7 +158,7 @@ function isExecutableFile(path) {
|
|
|
194
158
|
}
|
|
195
159
|
}
|
|
196
160
|
function detectRuntimeVersion(agentType, command, env, timeoutMs) {
|
|
197
|
-
const args =
|
|
161
|
+
const args = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType)?.detection?.versionArgs;
|
|
198
162
|
if (!args || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
|
|
199
163
|
return {};
|
|
200
164
|
}
|
|
@@ -4,6 +4,7 @@ exports.LocalCoreController = void 0;
|
|
|
4
4
|
const node_events_1 = require("node:events");
|
|
5
5
|
const desktop_js_1 = require("../../../../shared/desktop.js");
|
|
6
6
|
const bootstrap_js_1 = require("../kernel/bootstrap.js");
|
|
7
|
+
const scheduled_job_route_js_1 = require("../scheduler/scheduled-job-route.js");
|
|
7
8
|
const runtime_detection_service_js_1 = require("./runtime-detection-service.js");
|
|
8
9
|
class LocalCoreController extends node_events_1.EventEmitter {
|
|
9
10
|
userDataPath;
|
|
@@ -195,7 +196,10 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
195
196
|
}
|
|
196
197
|
resolveScheduledJobCreateInput(input) {
|
|
197
198
|
if (input.platform && input.route) {
|
|
198
|
-
return
|
|
199
|
+
return {
|
|
200
|
+
...input,
|
|
201
|
+
route: (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route),
|
|
202
|
+
};
|
|
199
203
|
}
|
|
200
204
|
const threadId = String(input.threadId || input.route?.threadId || '').trim();
|
|
201
205
|
if (threadId) {
|
|
@@ -208,7 +212,6 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
208
212
|
type: 'channel.chat',
|
|
209
213
|
channelId: binding.chat_id,
|
|
210
214
|
participantId: binding.platform_user_id,
|
|
211
|
-
threadId,
|
|
212
215
|
},
|
|
213
216
|
};
|
|
214
217
|
}
|
|
@@ -219,12 +222,14 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
219
222
|
route: {
|
|
220
223
|
type: 'local.thread',
|
|
221
224
|
channelId: input.workspaceId,
|
|
222
|
-
...(threadId ? { threadId } : {}),
|
|
223
225
|
},
|
|
224
226
|
};
|
|
225
227
|
}
|
|
226
228
|
async updateScheduledJob(jobId, input) {
|
|
227
|
-
return this.scheduler.updateJob(jobId,
|
|
229
|
+
return this.scheduler.updateJob(jobId, {
|
|
230
|
+
...input,
|
|
231
|
+
...(input.route ? { route: (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route) } : {}),
|
|
232
|
+
});
|
|
228
233
|
}
|
|
229
234
|
async deleteScheduledJob(jobId) {
|
|
230
235
|
return this.scheduler.deleteJob(jobId);
|
|
@@ -42,6 +42,8 @@ const DEFAULT_CONFIG = `# Managed by Local AI Core
|
|
|
42
42
|
`;
|
|
43
43
|
const LOG_FILE_NAME = 'local-core.log';
|
|
44
44
|
const LOG_FILE_MAX_BYTES = 2 * 1024 * 1024;
|
|
45
|
+
const LOG_TAIL_CHUNK_BYTES = 64 * 1024;
|
|
46
|
+
const LOG_MAX_RETURN_LINES = 5000;
|
|
45
47
|
class FileBackedLocalCoreRuntimeState {
|
|
46
48
|
userDataPath;
|
|
47
49
|
onLog;
|
|
@@ -112,7 +114,13 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
112
114
|
return this.settings.knowledge;
|
|
113
115
|
}
|
|
114
116
|
getLogs(limit = 200) {
|
|
115
|
-
|
|
117
|
+
const normalizedLimit = normalizeLogLimit(limit);
|
|
118
|
+
const current = tailLogFile(this.logPath, normalizedLimit);
|
|
119
|
+
if (current.length >= normalizedLimit) {
|
|
120
|
+
return current.slice(-normalizedLimit);
|
|
121
|
+
}
|
|
122
|
+
const rotated = tailLogFile(`${this.logPath}.1`, normalizedLimit - current.length);
|
|
123
|
+
return [...rotated, ...current].slice(-normalizedLimit);
|
|
116
124
|
}
|
|
117
125
|
pushLog(message) {
|
|
118
126
|
if (!message) {
|
|
@@ -273,6 +281,59 @@ function normalizePluginSettings(input) {
|
|
|
273
281
|
}
|
|
274
282
|
return output;
|
|
275
283
|
}
|
|
284
|
+
function normalizeLogLimit(limit) {
|
|
285
|
+
if (!Number.isFinite(limit)) {
|
|
286
|
+
return 200;
|
|
287
|
+
}
|
|
288
|
+
return Math.min(Math.max(Math.floor(limit), 1), LOG_MAX_RETURN_LINES);
|
|
289
|
+
}
|
|
290
|
+
function tailLogFile(path, limit) {
|
|
291
|
+
if (limit <= 0 || !(0, node_fs_1.existsSync)(path)) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
let fd = -1;
|
|
295
|
+
try {
|
|
296
|
+
const size = (0, node_fs_1.statSync)(path).size;
|
|
297
|
+
if (size <= 0) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
fd = (0, node_fs_1.openSync)(path, 'r');
|
|
301
|
+
const chunks = [];
|
|
302
|
+
let position = size;
|
|
303
|
+
let newlineCount = 0;
|
|
304
|
+
while (position > 0 && newlineCount <= limit) {
|
|
305
|
+
const bytesToRead = Math.min(LOG_TAIL_CHUNK_BYTES, position);
|
|
306
|
+
position -= bytesToRead;
|
|
307
|
+
const chunk = Buffer.allocUnsafe(bytesToRead);
|
|
308
|
+
const bytesRead = (0, node_fs_1.readSync)(fd, chunk, 0, bytesToRead, position);
|
|
309
|
+
const slice = bytesRead === bytesToRead ? chunk : chunk.subarray(0, bytesRead);
|
|
310
|
+
chunks.unshift(slice);
|
|
311
|
+
for (let index = 0; index < slice.length; index += 1) {
|
|
312
|
+
if (slice[index] === 10) {
|
|
313
|
+
newlineCount += 1;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return Buffer.concat(chunks)
|
|
318
|
+
.toString('utf8')
|
|
319
|
+
.split(/\r?\n/)
|
|
320
|
+
.filter((line) => line.length > 0)
|
|
321
|
+
.slice(-limit);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
finally {
|
|
327
|
+
if (fd >= 0) {
|
|
328
|
+
try {
|
|
329
|
+
(0, node_fs_1.closeSync)(fd);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
// Log reads are best-effort; failed closes cannot be surfaced to callers.
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
276
337
|
function createLocalCoreRuntimeState(options) {
|
|
277
338
|
return new FileBackedLocalCoreRuntimeState(options.userDataPath, options.onLog);
|
|
278
339
|
}
|