@kafca/agentdock 0.1.34 → 0.1.36
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 +69 -0
- package/dist/renderer/assets/{Badge-BDxYXRKv.js → Badge-DUa4J-09.js} +1 -1
- package/dist/renderer/assets/{Chat-B-ZXd4Ld.js → Chat-B1FWuTWV.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-Dn4XvCke.js → ChatMarkdown-DqOgFSgI.js} +1 -1
- package/dist/renderer/assets/{Config-DMOYyKOD.js → Config-CM5bqUrV.js} +1 -1
- package/dist/renderer/assets/{CronList-CMBhMs1J.js → CronList-CfXE7S0M.js} +1 -1
- package/dist/renderer/assets/{Dashboard-DApD4wOA.js → Dashboard-D5Dwbotu.js} +2 -2
- package/dist/renderer/assets/{EmptyState-CqcPky2i.js → EmptyState-Bs_6ISc0.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-DMOXK9Nz.js → KnowledgeDetail-0kdWnKyy.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-Dv4Ttb6N.js → KnowledgeHome-Bg8xbRe2.js} +1 -1
- package/dist/renderer/assets/Logs-BfgvW7qZ.js +1 -0
- package/dist/renderer/assets/{Modal-Cj_0qtXr.js → Modal-BkuKCDDx.js} +1 -1
- package/dist/renderer/assets/{Page-BlKjqLN4.js → Page-ddaAS_jP.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-aMl1VGTt.js → ProjectDetail-C9x-Ib5p.js} +1 -1
- package/dist/renderer/assets/{ProjectList-R2YvsII0.js → ProjectList-XIIikxrj.js} +1 -1
- package/dist/renderer/assets/{Select-CrqwGrrm.js → Select-BBL_5aa5.js} +1 -1
- package/dist/renderer/assets/{SessionChat-CGtpyGKR.js → SessionChat-BFsTM0c7.js} +1 -1
- package/dist/renderer/assets/{SessionList-D8gRUc8M.js → SessionList-DWh6n10K.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-BCGm6bQP.js → ThreadChat-CzPiDA92.js} +1 -1
- package/dist/renderer/assets/{Workspace-DnePK6tn.js → Workspace-BW8v3Nia.js} +1 -1
- package/dist/renderer/assets/{arrow-left-SVmCyBR4.js → arrow-left-CcoHvwbx.js} +1 -1
- package/dist/renderer/assets/{book-open-CZi1avEJ.js → book-open-RweyWxPq.js} +1 -1
- package/dist/renderer/assets/{check-C76I9BFh.js → check-B5YOrBNT.js} +1 -1
- package/dist/renderer/assets/{chevron-down-C7YSxzHw.js → chevron-down-HjaPrxdd.js} +1 -1
- package/dist/renderer/assets/{circle-_s8tmsAJ.js → circle-C-aaO7LR.js} +1 -1
- package/dist/renderer/assets/{desktop-66ot_8sB.js → desktop-DUWkNnbA.js} +1 -1
- package/dist/renderer/assets/{funnel-cDV0Q_Mr.js → funnel-IWZmi-_V.js} +1 -1
- package/dist/renderer/assets/{heart-qMUQm0tf.js → heart-DM5_F2nX.js} +1 -1
- package/dist/renderer/assets/{index-BY88QEsS.js → index-Bin2xyBS.js} +2 -2
- package/dist/renderer/assets/{index-DFiJR0vv.css → index-CaQZSRbg.css} +1 -1
- package/dist/renderer/assets/{index-Cr6erCLm.js → index-DvIUdsAm.js} +1 -1
- package/dist/renderer/assets/{pencil-C3MA3apq.js → pencil-DcxwCYPi.js} +1 -1
- package/dist/renderer/assets/{play-VZKHv5dN.js → play-DUdnnxLC.js} +1 -1
- package/dist/renderer/assets/{plug-Cm7l0fVk.js → plug-B-rDbk24.js} +1 -1
- package/dist/renderer/assets/{plus-DjGK7f-K.js → plus-DslMGvrO.js} +1 -1
- package/dist/renderer/assets/projects-JFuOB4DM.js +1 -0
- package/dist/renderer/assets/{save-Doi-FwDR.js → save-DlkR3jLs.js} +1 -1
- package/dist/renderer/assets/{search-DHqpbK_c.js → search-ULTp3p_0.js} +1 -1
- package/dist/renderer/assets/{server-Bmtxxlph.js → server-DBdiKfv9.js} +1 -1
- package/dist/renderer/assets/{session-utils-Z-jLtVLY.js → session-utils-BPyVVONo.js} +1 -1
- package/dist/renderer/assets/{sessions-C5TSSo_R.js → sessions-B0NecMYO.js} +1 -1
- package/dist/renderer/assets/{shield-check-DhZ05nhI.js → shield-check-BNSk8nza.js} +1 -1
- package/dist/renderer/assets/{trash-2-DiyFflVR.js → trash-2-CGydl763.js} +1 -1
- package/dist/renderer/assets/{user-CgsIPd7T.js → user-D1xkQo35.js} +1 -1
- package/dist/renderer/assets/{wifi-off-CqWbWFzp.js → wifi-off-Iy1XPeZ3.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/services/local-ai-core/src/kernel/rotating-logger.js +232 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +3 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +40 -84
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +3 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +14 -0
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +1 -1
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -2
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +1 -1
- package/dist-electron/src/api/client.js +1 -1
- package/dist-electron/tests/electron/plugin-kernel.test.js +161 -44
- package/dist-electron/tests/electron/workspace-task-store.test.js +70 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +3 -2
- package/package.json +1 -1
- package/dist/renderer/assets/Logs-CHrUm25Y.js +0 -1
- package/dist/renderer/assets/projects-Dcki78KB.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as o}from"./index-
|
|
1
|
+
import{z as o}from"./index-Bin2xyBS.js";const i=s=>o.get(`/projects/${s}/sessions`),c=(s,e,t)=>o.get(`/projects/${s}/sessions/${e}`,{history_limit:String(t)}),r=(s,e,t)=>o.patch(`/projects/${s}/sessions/${e}`,t),a=(s,e)=>o.delete(`/projects/${s}/sessions/${e}`),p=(s,e)=>o.post(`/projects/${s}/sessions/switch`,e),$=(s,e)=>o.post(`/projects/${s}/send`,e);export{$ as a,a as d,c as g,i as l,r,p as s};
|
package/dist/renderer/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>AgentDock</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-Bin2xyBS.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CaQZSRbg.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body class="bg-[#f5f5f7] dark:bg-[#151516]">
|
|
12
12
|
<div id="root"></div>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentDockRotatingLogger = void 0;
|
|
4
|
+
exports.resolveAgentDockLogDir = resolveAgentDockLogDir;
|
|
5
|
+
exports.inferLogLevel = inferLogLevel;
|
|
6
|
+
exports.normalizeLogLimit = normalizeLogLimit;
|
|
7
|
+
exports.formatLocalLogTimestamp = formatLocalLogTimestamp;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
12
|
+
const DEFAULT_LOG_MAX_FILES = 5;
|
|
13
|
+
const LOG_TAIL_CHUNK_BYTES = 64 * 1024;
|
|
14
|
+
const LOG_MAX_RETURN_LINES = 5000;
|
|
15
|
+
const LEVEL_FILE_NAMES = {
|
|
16
|
+
sys: 'sys.log',
|
|
17
|
+
debug: 'debug.log',
|
|
18
|
+
info: 'info.log',
|
|
19
|
+
warn: 'warn.log',
|
|
20
|
+
error: 'error.log',
|
|
21
|
+
};
|
|
22
|
+
class AgentDockRotatingLogger {
|
|
23
|
+
logDir;
|
|
24
|
+
sysLogPath;
|
|
25
|
+
maxBytes;
|
|
26
|
+
maxFiles;
|
|
27
|
+
scope;
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.logDir = options.logDir || resolveAgentDockLogDir();
|
|
30
|
+
this.sysLogPath = (0, node_path_1.join)(this.logDir, LEVEL_FILE_NAMES.sys);
|
|
31
|
+
this.maxBytes = normalizePositiveInteger(options.maxBytes, resolveLogMaxBytes());
|
|
32
|
+
this.maxFiles = normalizePositiveInteger(options.maxFiles, resolveLogMaxFiles());
|
|
33
|
+
this.scope = options.scope || 'local-ai-core';
|
|
34
|
+
(0, node_fs_1.mkdirSync)(this.logDir, { recursive: true });
|
|
35
|
+
this.ensureLogFiles();
|
|
36
|
+
}
|
|
37
|
+
write(level, message, meta) {
|
|
38
|
+
if (!message) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const entry = {
|
|
42
|
+
ts: formatLocalLogTimestamp(new Date()),
|
|
43
|
+
level,
|
|
44
|
+
scope: this.scope,
|
|
45
|
+
message: sanitizeLogMessage(message),
|
|
46
|
+
...(meta && Object.keys(meta).length > 0 ? { meta } : {}),
|
|
47
|
+
};
|
|
48
|
+
const line = `${JSON.stringify(entry)}\n`;
|
|
49
|
+
this.append('sys', line);
|
|
50
|
+
this.append(level, line);
|
|
51
|
+
}
|
|
52
|
+
tailSysLog(limit = 200) {
|
|
53
|
+
return this.tail('sys', limit);
|
|
54
|
+
}
|
|
55
|
+
tail(level, limit = 200) {
|
|
56
|
+
return tailRotatedLogFiles(this.pathFor(level), limit, this.maxFiles);
|
|
57
|
+
}
|
|
58
|
+
pathFor(level) {
|
|
59
|
+
return (0, node_path_1.join)(this.logDir, LEVEL_FILE_NAMES[level]);
|
|
60
|
+
}
|
|
61
|
+
ensureLogFiles() {
|
|
62
|
+
for (const level of Object.keys(LEVEL_FILE_NAMES)) {
|
|
63
|
+
try {
|
|
64
|
+
(0, node_fs_1.closeSync)((0, node_fs_1.openSync)(this.pathFor(level), 'a'));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// File logging must never break Local AI Core runtime behavior.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
append(level, line) {
|
|
72
|
+
try {
|
|
73
|
+
const path = this.pathFor(level);
|
|
74
|
+
this.rotateIfNeeded(path, Buffer.byteLength(line, 'utf8'));
|
|
75
|
+
(0, node_fs_1.appendFileSync)(path, line, 'utf8');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// File logging must never break Local AI Core runtime behavior.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
rotateIfNeeded(path, incomingBytes) {
|
|
82
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let size = 0;
|
|
86
|
+
try {
|
|
87
|
+
size = (0, node_fs_1.statSync)(path).size;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (size + incomingBytes <= this.maxBytes) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
for (let index = this.maxFiles; index >= 1; index -= 1) {
|
|
96
|
+
const current = `${path}.${index}`;
|
|
97
|
+
const next = `${path}.${index + 1}`;
|
|
98
|
+
if (!(0, node_fs_1.existsSync)(current)) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
if (index >= this.maxFiles) {
|
|
103
|
+
(0, node_fs_1.unlinkSync)(current);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
(0, node_fs_1.renameSync)(current, next);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Rotation is best-effort; the append path still attempts to continue.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
(0, node_fs_1.renameSync)(path, `${path}.1`);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Rotation is best-effort; the append path still attempts to continue.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.AgentDockRotatingLogger = AgentDockRotatingLogger;
|
|
122
|
+
function resolveAgentDockLogDir() {
|
|
123
|
+
const override = process.env.AGENTDOCK_LOG_DIR?.trim();
|
|
124
|
+
if (override) {
|
|
125
|
+
return override;
|
|
126
|
+
}
|
|
127
|
+
return (0, node_path_1.join)((0, node_os_1.homedir)(), '.agentdock', 'logs');
|
|
128
|
+
}
|
|
129
|
+
function inferLogLevel(message) {
|
|
130
|
+
const normalized = message.toLowerCase();
|
|
131
|
+
if (/\b(debug|trace)\b/.test(normalized)) {
|
|
132
|
+
return 'debug';
|
|
133
|
+
}
|
|
134
|
+
if (/\b(warn|warning)\b/.test(normalized)) {
|
|
135
|
+
return 'warn';
|
|
136
|
+
}
|
|
137
|
+
if (/\b(error|failed|failure|fatal|exception)\b/.test(normalized)) {
|
|
138
|
+
return 'error';
|
|
139
|
+
}
|
|
140
|
+
return 'info';
|
|
141
|
+
}
|
|
142
|
+
function normalizeLogLimit(limit) {
|
|
143
|
+
if (!Number.isFinite(limit)) {
|
|
144
|
+
return 200;
|
|
145
|
+
}
|
|
146
|
+
return Math.min(Math.max(Math.floor(limit), 1), LOG_MAX_RETURN_LINES);
|
|
147
|
+
}
|
|
148
|
+
function resolveLogMaxBytes() {
|
|
149
|
+
return normalizePositiveInteger(Number(process.env.AGENTDOCK_LOG_MAX_BYTES), DEFAULT_LOG_MAX_BYTES);
|
|
150
|
+
}
|
|
151
|
+
function resolveLogMaxFiles() {
|
|
152
|
+
return normalizePositiveInteger(Number(process.env.AGENTDOCK_LOG_MAX_FILES), DEFAULT_LOG_MAX_FILES);
|
|
153
|
+
}
|
|
154
|
+
function normalizePositiveInteger(value, fallback) {
|
|
155
|
+
if (!Number.isFinite(value) || !value || value <= 0) {
|
|
156
|
+
return fallback;
|
|
157
|
+
}
|
|
158
|
+
return Math.floor(value);
|
|
159
|
+
}
|
|
160
|
+
function sanitizeLogMessage(message) {
|
|
161
|
+
return String(message).replace(/\r?\n/g, '\\n');
|
|
162
|
+
}
|
|
163
|
+
function formatLocalLogTimestamp(date) {
|
|
164
|
+
return [
|
|
165
|
+
`${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`,
|
|
166
|
+
' ',
|
|
167
|
+
`${pad2(date.getHours())}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())}.${pad3(date.getMilliseconds())}`,
|
|
168
|
+
].join('');
|
|
169
|
+
}
|
|
170
|
+
function pad2(value) {
|
|
171
|
+
return String(value).padStart(2, '0');
|
|
172
|
+
}
|
|
173
|
+
function pad3(value) {
|
|
174
|
+
return String(value).padStart(3, '0');
|
|
175
|
+
}
|
|
176
|
+
function tailRotatedLogFiles(path, limit, maxFiles) {
|
|
177
|
+
const normalizedLimit = normalizeLogLimit(limit);
|
|
178
|
+
const files = Array.from({ length: maxFiles }, (_, index) => `${path}.${maxFiles - index}`)
|
|
179
|
+
.concat(path);
|
|
180
|
+
const lines = [];
|
|
181
|
+
for (const file of files) {
|
|
182
|
+
lines.push(...tailLogFile(file, normalizedLimit));
|
|
183
|
+
}
|
|
184
|
+
return lines.slice(-normalizedLimit);
|
|
185
|
+
}
|
|
186
|
+
function tailLogFile(path, limit) {
|
|
187
|
+
if (limit <= 0 || !(0, node_fs_1.existsSync)(path)) {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
let fd = -1;
|
|
191
|
+
try {
|
|
192
|
+
const size = (0, node_fs_1.statSync)(path).size;
|
|
193
|
+
if (size <= 0) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
fd = (0, node_fs_1.openSync)(path, 'r');
|
|
197
|
+
const chunks = [];
|
|
198
|
+
let position = size;
|
|
199
|
+
let newlineCount = 0;
|
|
200
|
+
while (position > 0 && newlineCount <= limit) {
|
|
201
|
+
const bytesToRead = Math.min(LOG_TAIL_CHUNK_BYTES, position);
|
|
202
|
+
position -= bytesToRead;
|
|
203
|
+
const chunk = Buffer.allocUnsafe(bytesToRead);
|
|
204
|
+
const bytesRead = (0, node_fs_1.readSync)(fd, chunk, 0, bytesToRead, position);
|
|
205
|
+
const slice = bytesRead === bytesToRead ? chunk : chunk.subarray(0, bytesRead);
|
|
206
|
+
chunks.unshift(slice);
|
|
207
|
+
for (let index = 0; index < slice.length; index += 1) {
|
|
208
|
+
if (slice[index] === 10) {
|
|
209
|
+
newlineCount += 1;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return Buffer.concat(chunks)
|
|
214
|
+
.toString('utf8')
|
|
215
|
+
.split(/\r?\n/)
|
|
216
|
+
.filter((line) => line.length > 0)
|
|
217
|
+
.slice(-limit);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
if (fd >= 0) {
|
|
224
|
+
try {
|
|
225
|
+
(0, node_fs_1.closeSync)(fd);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Log reads are best-effort; failed closes cannot be surfaced to callers.
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -105,6 +105,9 @@ class LocalCoreController extends node_events_1.EventEmitter {
|
|
|
105
105
|
getLogs(limit = 200) {
|
|
106
106
|
return this.state.getLogs(limit);
|
|
107
107
|
}
|
|
108
|
+
getLogEntries(level = 'sys', limit = 200) {
|
|
109
|
+
return this.state.getLogEntries(level, limit);
|
|
110
|
+
}
|
|
108
111
|
async readConfigFile() {
|
|
109
112
|
return this.state.readConfigFile();
|
|
110
113
|
}
|
|
@@ -37,13 +37,10 @@ exports.createLocalCoreRuntimeState = createLocalCoreRuntimeState;
|
|
|
37
37
|
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
|
+
const rotating_logger_js_1 = require("../kernel/rotating-logger.js");
|
|
40
41
|
const DEFAULT_CONFIG = `# Managed by Local AI Core
|
|
41
42
|
# Add [[projects]] entries from the workspace page before starting a conversation.
|
|
42
43
|
`;
|
|
43
|
-
const LOG_FILE_NAME = 'local-core.log';
|
|
44
|
-
const LOG_FILE_MAX_BYTES = 2 * 1024 * 1024;
|
|
45
|
-
const LOG_TAIL_CHUNK_BYTES = 64 * 1024;
|
|
46
|
-
const LOG_MAX_RETURN_LINES = 5000;
|
|
47
44
|
class FileBackedLocalCoreRuntimeState {
|
|
48
45
|
userDataPath;
|
|
49
46
|
onLog;
|
|
@@ -53,12 +50,14 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
53
50
|
logPath;
|
|
54
51
|
settings;
|
|
55
52
|
logs = [];
|
|
53
|
+
logger;
|
|
56
54
|
constructor(userDataPath, onLog) {
|
|
57
55
|
this.userDataPath = userDataPath;
|
|
58
56
|
this.onLog = onLog;
|
|
59
57
|
this.runtimeDir = (0, node_path_1.join)(userDataPath, 'runtime');
|
|
60
58
|
this.settingsPath = (0, node_path_1.join)(this.runtimeDir, 'local-core-settings.json');
|
|
61
|
-
this.
|
|
59
|
+
this.logger = new rotating_logger_js_1.AgentDockRotatingLogger({ scope: 'local-ai-core' });
|
|
60
|
+
this.logPath = this.logger.sysLogPath;
|
|
62
61
|
(0, node_fs_1.mkdirSync)(this.runtimeDir, { recursive: true });
|
|
63
62
|
this.cliBinDir = this.ensureCliWrapper();
|
|
64
63
|
this.settings = this.loadSettings();
|
|
@@ -114,43 +113,25 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
114
113
|
return this.settings.knowledge;
|
|
115
114
|
}
|
|
116
115
|
getLogs(limit = 200) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
return this.logger.tailSysLog(limit);
|
|
117
|
+
}
|
|
118
|
+
getLogEntries(level = 'sys', limit = 200) {
|
|
119
|
+
const normalizedLevel = normalizeLogFile(level);
|
|
120
|
+
return this.logger.tail(normalizedLevel, limit)
|
|
121
|
+
.map(parseLogEntry)
|
|
122
|
+
.filter((entry) => Boolean(entry));
|
|
124
123
|
}
|
|
125
124
|
pushLog(message) {
|
|
126
125
|
if (!message) {
|
|
127
126
|
return;
|
|
128
127
|
}
|
|
129
128
|
this.logs.push(message);
|
|
130
|
-
this.
|
|
129
|
+
this.logger.write((0, rotating_logger_js_1.inferLogLevel)(message), message);
|
|
131
130
|
this.onLog?.(message);
|
|
132
131
|
if (this.logs.length > 400) {
|
|
133
132
|
this.logs.splice(0, this.logs.length - 400);
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
|
-
appendLogFile(message) {
|
|
137
|
-
try {
|
|
138
|
-
if ((0, node_fs_1.existsSync)(this.logPath) && (0, node_fs_1.statSync)(this.logPath).size > LOG_FILE_MAX_BYTES) {
|
|
139
|
-
const rotatedPath = `${this.logPath}.1`;
|
|
140
|
-
try {
|
|
141
|
-
(0, node_fs_1.renameSync)(this.logPath, rotatedPath);
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
// Keep runtime logging best-effort; stdout/UI logs still work.
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const line = `${new Date().toISOString()} ${message.replace(/\r?\n/g, '\\n')}\n`;
|
|
148
|
-
(0, node_fs_1.appendFileSync)(this.logPath, line, 'utf-8');
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// File logging must never break Local AI Core runtime behavior.
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
135
|
async readConfigFile() {
|
|
155
136
|
const path = this.settings.configPath;
|
|
156
137
|
if (!(0, node_fs_1.existsSync)(path)) {
|
|
@@ -262,6 +243,34 @@ class FileBackedLocalCoreRuntimeState {
|
|
|
262
243
|
return binDir;
|
|
263
244
|
}
|
|
264
245
|
}
|
|
246
|
+
function normalizeLogFile(level) {
|
|
247
|
+
if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') {
|
|
248
|
+
return level;
|
|
249
|
+
}
|
|
250
|
+
return 'sys';
|
|
251
|
+
}
|
|
252
|
+
function parseLogEntry(line) {
|
|
253
|
+
try {
|
|
254
|
+
const parsed = JSON.parse(line);
|
|
255
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const level = normalizeLogFile(String(parsed.level || 'info'));
|
|
259
|
+
if (level === 'sys') {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
ts: String(parsed.ts || ''),
|
|
264
|
+
level,
|
|
265
|
+
scope: String(parsed.scope || ''),
|
|
266
|
+
message: String(parsed.message || ''),
|
|
267
|
+
...(parsed.meta && typeof parsed.meta === 'object' ? { meta: parsed.meta } : {}),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
265
274
|
function normalizePluginSettings(input) {
|
|
266
275
|
if (!input || typeof input !== 'object') {
|
|
267
276
|
return {};
|
|
@@ -281,59 +290,6 @@ function normalizePluginSettings(input) {
|
|
|
281
290
|
}
|
|
282
291
|
return output;
|
|
283
292
|
}
|
|
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
|
-
}
|
|
337
293
|
function createLocalCoreRuntimeState(options) {
|
|
338
294
|
return new FileBackedLocalCoreRuntimeState(options.userDataPath, options.onLog);
|
|
339
295
|
}
|
|
@@ -23,6 +23,9 @@ function parseLocalAiCoreRoute(method, path) {
|
|
|
23
23
|
if (normalizedMethod === 'GET' && relativePath === '/runtime/logs') {
|
|
24
24
|
return { name: 'runtime.logs' };
|
|
25
25
|
}
|
|
26
|
+
if (normalizedMethod === 'GET' && relativePath === '/logs') {
|
|
27
|
+
return { name: 'logs.list' };
|
|
28
|
+
}
|
|
26
29
|
if (normalizedMethod === 'GET' && relativePath === '/runtime/agent-runtimes') {
|
|
27
30
|
return { name: 'runtime.agent-runtimes' };
|
|
28
31
|
}
|
|
@@ -143,6 +143,20 @@ class LocalAiCoreServer {
|
|
|
143
143
|
json(res, 200, this.bindings.getLogs(limit));
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
|
+
case 'logs.list': {
|
|
147
|
+
const level = url.searchParams.get('level') || 'sys';
|
|
148
|
+
const limit = Number(url.searchParams.get('limit') || '200');
|
|
149
|
+
json(res, 200, {
|
|
150
|
+
entries: this.bindings.getLogEntries(level, limit).map((entry) => ({
|
|
151
|
+
time: entry.ts,
|
|
152
|
+
level: entry.level,
|
|
153
|
+
scope: entry.scope,
|
|
154
|
+
message: entry.message,
|
|
155
|
+
meta: entry.meta,
|
|
156
|
+
})),
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
146
160
|
case 'runtime.agent-runtimes':
|
|
147
161
|
case 'runtimes.list':
|
|
148
162
|
json(res, 200, await this.runtimeDetectionResponse());
|
|
@@ -12,7 +12,7 @@ class LarkScheduleAdapter {
|
|
|
12
12
|
this.options = options;
|
|
13
13
|
this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
|
|
14
14
|
store: options.store,
|
|
15
|
-
|
|
15
|
+
getWorkspaceRouter: options.getWorkspaceRouter,
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
supports(job) {
|
package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js
CHANGED
|
@@ -13,12 +13,13 @@ class ScheduledConversationExecutor {
|
|
|
13
13
|
const target = await policy.resolveTarget(job);
|
|
14
14
|
await policy.beforeExecute?.(target, job);
|
|
15
15
|
try {
|
|
16
|
-
const
|
|
16
|
+
const workspaceRouter = this.options.getWorkspaceRouter();
|
|
17
|
+
const sendResult = await workspaceRouter.sendThreadMessage(target.threadId, prompt, {
|
|
17
18
|
permissionMode: SCHEDULED_RUN_PERMISSION_MODE,
|
|
18
19
|
runtimeEnv: this.buildScheduledRuntimeEnv(target),
|
|
19
20
|
});
|
|
20
21
|
await this.waitForRun(sendResult.runId, timeoutMs);
|
|
21
|
-
const thread = await
|
|
22
|
+
const thread = await workspaceRouter.getThread(target.threadId);
|
|
22
23
|
const replyText = [...thread.messages]
|
|
23
24
|
.reverse()
|
|
24
25
|
.find((message) => message.role === 'assistant' && message.kind === 'final')
|
|
@@ -12,7 +12,7 @@ class WeixinScheduleAdapter {
|
|
|
12
12
|
this.options = options;
|
|
13
13
|
this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
|
|
14
14
|
store: options.store,
|
|
15
|
-
|
|
15
|
+
getWorkspaceRouter: options.getWorkspaceRouter,
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
supports(job) {
|
|
@@ -14,7 +14,7 @@ class ApiClient {
|
|
|
14
14
|
this.baseUrl = DEFAULT_API_BASE;
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
this.baseUrl = trimmed.endsWith('/api/v1')
|
|
17
|
+
this.baseUrl = trimmed.endsWith('/api/v1') || trimmed.endsWith('/api/local/v1')
|
|
18
18
|
? trimmed
|
|
19
19
|
: `${trimmed.replace(/\/+$/, '')}/api/v1`;
|
|
20
20
|
}
|