@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.
Files changed (61) hide show
  1. package/README.md +69 -0
  2. package/dist/renderer/assets/{Badge-BDxYXRKv.js → Badge-DUa4J-09.js} +1 -1
  3. package/dist/renderer/assets/{Chat-B-ZXd4Ld.js → Chat-B1FWuTWV.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-Dn4XvCke.js → ChatMarkdown-DqOgFSgI.js} +1 -1
  5. package/dist/renderer/assets/{Config-DMOYyKOD.js → Config-CM5bqUrV.js} +1 -1
  6. package/dist/renderer/assets/{CronList-CMBhMs1J.js → CronList-CfXE7S0M.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-DApD4wOA.js → Dashboard-D5Dwbotu.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-CqcPky2i.js → EmptyState-Bs_6ISc0.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-DMOXK9Nz.js → KnowledgeDetail-0kdWnKyy.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-Dv4Ttb6N.js → KnowledgeHome-Bg8xbRe2.js} +1 -1
  11. package/dist/renderer/assets/Logs-BfgvW7qZ.js +1 -0
  12. package/dist/renderer/assets/{Modal-Cj_0qtXr.js → Modal-BkuKCDDx.js} +1 -1
  13. package/dist/renderer/assets/{Page-BlKjqLN4.js → Page-ddaAS_jP.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-aMl1VGTt.js → ProjectDetail-C9x-Ib5p.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-R2YvsII0.js → ProjectList-XIIikxrj.js} +1 -1
  16. package/dist/renderer/assets/{Select-CrqwGrrm.js → Select-BBL_5aa5.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-CGtpyGKR.js → SessionChat-BFsTM0c7.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-D8gRUc8M.js → SessionList-DWh6n10K.js} +1 -1
  19. package/dist/renderer/assets/{ThreadChat-BCGm6bQP.js → ThreadChat-CzPiDA92.js} +1 -1
  20. package/dist/renderer/assets/{Workspace-DnePK6tn.js → Workspace-BW8v3Nia.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-SVmCyBR4.js → arrow-left-CcoHvwbx.js} +1 -1
  22. package/dist/renderer/assets/{book-open-CZi1avEJ.js → book-open-RweyWxPq.js} +1 -1
  23. package/dist/renderer/assets/{check-C76I9BFh.js → check-B5YOrBNT.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-C7YSxzHw.js → chevron-down-HjaPrxdd.js} +1 -1
  25. package/dist/renderer/assets/{circle-_s8tmsAJ.js → circle-C-aaO7LR.js} +1 -1
  26. package/dist/renderer/assets/{desktop-66ot_8sB.js → desktop-DUWkNnbA.js} +1 -1
  27. package/dist/renderer/assets/{funnel-cDV0Q_Mr.js → funnel-IWZmi-_V.js} +1 -1
  28. package/dist/renderer/assets/{heart-qMUQm0tf.js → heart-DM5_F2nX.js} +1 -1
  29. package/dist/renderer/assets/{index-BY88QEsS.js → index-Bin2xyBS.js} +2 -2
  30. package/dist/renderer/assets/{index-DFiJR0vv.css → index-CaQZSRbg.css} +1 -1
  31. package/dist/renderer/assets/{index-Cr6erCLm.js → index-DvIUdsAm.js} +1 -1
  32. package/dist/renderer/assets/{pencil-C3MA3apq.js → pencil-DcxwCYPi.js} +1 -1
  33. package/dist/renderer/assets/{play-VZKHv5dN.js → play-DUdnnxLC.js} +1 -1
  34. package/dist/renderer/assets/{plug-Cm7l0fVk.js → plug-B-rDbk24.js} +1 -1
  35. package/dist/renderer/assets/{plus-DjGK7f-K.js → plus-DslMGvrO.js} +1 -1
  36. package/dist/renderer/assets/projects-JFuOB4DM.js +1 -0
  37. package/dist/renderer/assets/{save-Doi-FwDR.js → save-DlkR3jLs.js} +1 -1
  38. package/dist/renderer/assets/{search-DHqpbK_c.js → search-ULTp3p_0.js} +1 -1
  39. package/dist/renderer/assets/{server-Bmtxxlph.js → server-DBdiKfv9.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Z-jLtVLY.js → session-utils-BPyVVONo.js} +1 -1
  41. package/dist/renderer/assets/{sessions-C5TSSo_R.js → sessions-B0NecMYO.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-DhZ05nhI.js → shield-check-BNSk8nza.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-DiyFflVR.js → trash-2-CGydl763.js} +1 -1
  44. package/dist/renderer/assets/{user-CgsIPd7T.js → user-D1xkQo35.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-CqWbWFzp.js → wifi-off-Iy1XPeZ3.js} +1 -1
  46. package/dist/renderer/index.html +2 -2
  47. package/dist-electron/services/local-ai-core/src/kernel/rotating-logger.js +232 -0
  48. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +3 -0
  49. package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +40 -84
  50. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +3 -0
  51. package/dist-electron/services/local-ai-core/src/runtime/server.js +14 -0
  52. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +1 -1
  53. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -2
  54. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +1 -1
  55. package/dist-electron/src/api/client.js +1 -1
  56. package/dist-electron/tests/electron/plugin-kernel.test.js +161 -44
  57. package/dist-electron/tests/electron/workspace-task-store.test.js +70 -0
  58. package/dist-electron/tests/integration/local-core-refactor.test.js +3 -2
  59. package/package.json +1 -1
  60. package/dist/renderer/assets/Logs-CHrUm25Y.js +0 -1
  61. package/dist/renderer/assets/projects-Dcki78KB.js +0 -1
@@ -1 +1 @@
1
- import{z as o}from"./index-BY88QEsS.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};
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};
@@ -1,4 +1,4 @@
1
- import{c}from"./index-BY88QEsS.js";/**
1
+ import{c}from"./index-Bin2xyBS.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-BY88QEsS.js";/**
1
+ import{c as e}from"./index-Bin2xyBS.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-BY88QEsS.js";/**
1
+ import{c as e}from"./index-Bin2xyBS.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-BY88QEsS.js";/**
1
+ import{c as a}from"./index-Bin2xyBS.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -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-BY88QEsS.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-DFiJR0vv.css">
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.logPath = (0, node_path_1.join)(this.runtimeDir, LOG_FILE_NAME);
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
- 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
+ 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.appendLogFile(message);
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
- workspaceRouter: options.getWorkspaceRouter(),
15
+ getWorkspaceRouter: options.getWorkspaceRouter,
16
16
  });
17
17
  }
18
18
  supports(job) {
@@ -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 sendResult = await this.options.workspaceRouter.sendThreadMessage(target.threadId, prompt, {
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 this.options.workspaceRouter.getThread(target.threadId);
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
- workspaceRouter: options.getWorkspaceRouter(),
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
  }