@vite-plugin-opencode-assistant/shared 1.0.58 → 1.0.60

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.
@@ -4,50 +4,20 @@ export interface FileLogEntry {
4
4
  timestamp: string;
5
5
  source: string;
6
6
  }
7
- export interface FileLogBufferOptions {
7
+ export interface LogFileOptions {
8
8
  name: string;
9
9
  filePath: string;
10
- maxBufferSize?: number;
11
- watchExisting?: boolean;
12
10
  }
13
- declare class FileLogBuffer {
14
- private buffer;
15
- private maxSize;
16
- private name;
17
- private filePath;
18
- private lastPosition;
19
- private watcher;
20
- private enabled;
21
- constructor(options: FileLogBufferOptions);
22
- start(projectRoot?: string): void;
23
- stop(): void;
24
- private resolvePath;
25
- private readNewLogs;
26
- private processLogContent;
27
- private detectLogLevel;
28
- addEntry(entry: FileLogEntry): void;
29
- getLogs(options?: {
30
- level?: FileLogEntry["level"] | FileLogEntry["level"][];
31
- limit?: number;
32
- since?: string;
33
- }): FileLogEntry[];
34
- clear(): void;
35
- size(): number;
36
- setEnabled(enabled: boolean): void;
37
- isEnabled(): boolean;
38
- getName(): string;
39
- getFilePath(): string;
40
- }
41
- export declare class ServiceLogWatcher {
42
- private buffers;
43
- private projectRoot;
44
- setProjectRoot(root: string): void;
45
- addLogFile(options: FileLogBufferOptions): void;
46
- removeLogFile(name: string): void;
47
- getBuffer(name: string): FileLogBuffer | undefined;
48
- getAllBuffers(): Map<string, FileLogBuffer>;
49
- stopAll(): void;
50
- getLogFileNames(): string[];
51
- }
52
- export declare function getServiceLogWatcher(): ServiceLogWatcher;
53
- export {};
11
+ export declare function readLogFile(options: LogFileOptions & {
12
+ projectRoot?: string;
13
+ level?: ("info" | "warn" | "error") | ("info" | "warn" | "error")[];
14
+ limit?: number;
15
+ since?: string;
16
+ }): Promise<FileLogEntry[]>;
17
+ export declare function readLogFileTail(options: LogFileOptions & {
18
+ projectRoot?: string;
19
+ lines?: number;
20
+ limit?: number;
21
+ level?: ("info" | "warn" | "error") | ("info" | "warn" | "error")[];
22
+ since?: string;
23
+ }): Promise<FileLogEntry[]>;
@@ -1,193 +1,177 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
4
21
  import fs from "fs";
5
22
  import path from "path";
6
23
  import { createLogger } from "./logger.mjs";
7
- const log = createLogger("FileLogWatcher");
8
- class FileLogBuffer {
9
- constructor(options) {
10
- __publicField(this, "buffer", []);
11
- __publicField(this, "maxSize");
12
- __publicField(this, "name");
13
- __publicField(this, "filePath");
14
- __publicField(this, "lastPosition", 0);
15
- __publicField(this, "watcher", null);
16
- __publicField(this, "enabled", false);
17
- var _a;
18
- this.name = options.name;
19
- this.filePath = options.filePath;
20
- this.maxSize = (_a = options.maxBufferSize) != null ? _a : 200;
21
- this.enabled = true;
24
+ const log = createLogger("FileLogReader");
25
+ function detectLogLevel(line) {
26
+ const lowerLine = line.toLowerCase();
27
+ if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
28
+ return "error";
29
+ }
30
+ if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
31
+ return "warn";
22
32
  }
23
- start(projectRoot) {
24
- const resolvedPath = this.resolvePath(projectRoot);
33
+ return "info";
34
+ }
35
+ function parseLogTimestamp(line) {
36
+ const timestampPatterns = [
37
+ /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?)/,
38
+ /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/,
39
+ /(\[([^\]]+)\])/
40
+ ];
41
+ for (const pattern of timestampPatterns) {
42
+ const match = line.match(pattern);
43
+ if (match) {
44
+ const timestampStr = match[1];
45
+ const date = new Date(timestampStr);
46
+ if (!isNaN(date.getTime())) {
47
+ return date.toISOString();
48
+ }
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+ function readLogFile(options) {
54
+ return __async(this, null, function* () {
55
+ const { name, filePath, projectRoot, level, limit, since } = options;
56
+ const resolvedPath = resolvePath(filePath, projectRoot);
25
57
  if (!fs.existsSync(resolvedPath)) {
26
58
  log.debug(`Log file does not exist: ${resolvedPath}`);
27
- return;
59
+ return [];
28
60
  }
29
- const stat = fs.statSync(resolvedPath);
30
- this.lastPosition = stat.size;
31
- this.watcher = fs.watch(resolvedPath, (eventType) => {
32
- if (eventType === "change") {
33
- this.readNewLogs(resolvedPath);
61
+ try {
62
+ const content = yield fs.promises.readFile(resolvedPath, "utf-8");
63
+ const lines = content.split("\n").filter((line) => line.trim());
64
+ const entries = [];
65
+ const sinceDate = since ? new Date(since) : null;
66
+ for (const line of lines) {
67
+ const entry = {
68
+ level: detectLogLevel(line),
69
+ message: line,
70
+ timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
71
+ source: `file:${name}`
72
+ };
73
+ if (sinceDate && new Date(entry.timestamp) < sinceDate) {
74
+ continue;
75
+ }
76
+ if (level) {
77
+ const levels = Array.isArray(level) ? level : [level];
78
+ if (!levels.includes(entry.level)) {
79
+ continue;
80
+ }
81
+ }
82
+ entries.push(entry);
34
83
  }
35
- });
36
- this.watcher.on("error", (err) => {
37
- log.error(`Error watching file ${resolvedPath}`, { error: err });
38
- });
39
- log.info(`Started watching log file: ${resolvedPath}`);
40
- }
41
- stop() {
42
- if (this.watcher) {
43
- this.watcher.close();
44
- this.watcher = null;
45
- log.debug(`Stopped watching log file: ${this.filePath}`);
46
- }
47
- }
48
- resolvePath(projectRoot) {
49
- if (path.isAbsolute(this.filePath)) {
50
- return this.filePath;
84
+ if (limit && limit > 0) {
85
+ return entries.slice(-limit);
86
+ }
87
+ return entries;
88
+ } catch (err) {
89
+ log.error(`Error reading log file ${resolvedPath}`, { error: err });
90
+ return [];
51
91
  }
52
- if (projectRoot) {
53
- return path.resolve(projectRoot, this.filePath);
92
+ });
93
+ }
94
+ function readLogFileTail(options) {
95
+ return __async(this, null, function* () {
96
+ const { name, filePath, projectRoot, lines = 200, limit, level, since } = options;
97
+ const resolvedPath = resolvePath(filePath, projectRoot);
98
+ if (!fs.existsSync(resolvedPath)) {
99
+ log.debug(`Log file does not exist: ${resolvedPath}`);
100
+ return [];
54
101
  }
55
- return path.resolve(process.cwd(), this.filePath);
56
- }
57
- readNewLogs(filePath) {
58
102
  try {
59
- const stat = fs.statSync(filePath);
60
- if (stat.size <= this.lastPosition) {
61
- return;
103
+ const stat = fs.statSync(resolvedPath);
104
+ const fd = fs.openSync(resolvedPath, "r");
105
+ const chunkSize = 16 * 1024;
106
+ let position = stat.size;
107
+ let buffer = Buffer.alloc(0);
108
+ const lineCount = 0;
109
+ while (position > 0 && lineCount <= lines) {
110
+ const readSize = Math.min(chunkSize, position);
111
+ position -= readSize;
112
+ const chunk = Buffer.alloc(readSize);
113
+ fs.readSync(fd, chunk, 0, readSize, position);
114
+ buffer = Buffer.concat([chunk, buffer]);
115
+ const newLineCount = buffer.filter((byte) => byte === 10).length;
116
+ if (newLineCount >= lines) {
117
+ const linesArray = buffer.toString("utf-8").split("\n");
118
+ const excessLines = newLineCount - lines;
119
+ let charsToRemove = 0;
120
+ let count = 0;
121
+ for (let i = 0; i < linesArray.length; i++) {
122
+ count += linesArray[i].length + 1;
123
+ if (count > excessLines) {
124
+ charsToRemove = linesArray.slice(0, i + 1).join("\n").length + 1;
125
+ break;
126
+ }
127
+ }
128
+ buffer = buffer.slice(charsToRemove);
129
+ break;
130
+ }
62
131
  }
63
- const fd = fs.openSync(filePath, "r");
64
- const buffer = Buffer.alloc(stat.size - this.lastPosition);
65
- fs.readSync(fd, buffer, 0, buffer.length, this.lastPosition);
66
132
  fs.closeSync(fd);
67
- this.lastPosition = stat.size;
68
133
  const content = buffer.toString("utf-8").trim();
69
- if (content) {
70
- this.processLogContent(content);
134
+ const logLines = content.split("\n").filter((line) => line.trim());
135
+ const entries = [];
136
+ const sinceDate = since ? new Date(since) : null;
137
+ for (const line of logLines) {
138
+ const entry = {
139
+ level: detectLogLevel(line),
140
+ message: line,
141
+ timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
142
+ source: `file:${name}`
143
+ };
144
+ if (sinceDate && new Date(entry.timestamp) < sinceDate) {
145
+ continue;
146
+ }
147
+ if (level) {
148
+ const levels = Array.isArray(level) ? level : [level];
149
+ if (!levels.includes(entry.level)) {
150
+ continue;
151
+ }
152
+ }
153
+ entries.push(entry);
71
154
  }
155
+ if (limit && limit > 0) {
156
+ return entries.slice(-limit);
157
+ }
158
+ return entries;
72
159
  } catch (err) {
73
- log.error(`Error reading log file ${filePath}`, { error: err });
74
- }
75
- }
76
- processLogContent(content) {
77
- const lines = content.split("\n");
78
- for (const line of lines) {
79
- if (!line.trim()) continue;
80
- this.addEntry({
81
- level: this.detectLogLevel(line),
82
- message: line,
83
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
84
- source: `file:${this.name}`
85
- });
86
- }
87
- }
88
- detectLogLevel(line) {
89
- const lowerLine = line.toLowerCase();
90
- if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
91
- return "error";
92
- }
93
- if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
94
- return "warn";
160
+ log.error(`Error reading log file ${resolvedPath}`, { error: err });
161
+ return [];
95
162
  }
96
- return "info";
97
- }
98
- addEntry(entry) {
99
- if (!this.enabled) return;
100
- if (this.buffer.length >= this.maxSize) {
101
- this.buffer.shift();
102
- }
103
- this.buffer.push(entry);
104
- }
105
- getLogs(options = {}) {
106
- let logs = [...this.buffer];
107
- if (options.level) {
108
- const levels = Array.isArray(options.level) ? options.level : [options.level];
109
- logs = logs.filter((log2) => levels.includes(log2.level));
110
- }
111
- if (options.since) {
112
- const sinceDate = new Date(options.since);
113
- logs = logs.filter((log2) => new Date(log2.timestamp) >= sinceDate);
114
- }
115
- if (options.limit && options.limit > 0) {
116
- logs = logs.slice(-options.limit);
117
- }
118
- return logs;
119
- }
120
- clear() {
121
- this.buffer = [];
122
- }
123
- size() {
124
- return this.buffer.length;
125
- }
126
- setEnabled(enabled) {
127
- this.enabled = enabled;
128
- }
129
- isEnabled() {
130
- return this.enabled;
131
- }
132
- getName() {
133
- return this.name;
134
- }
135
- getFilePath() {
136
- return this.filePath;
137
- }
163
+ });
138
164
  }
139
- class ServiceLogWatcher {
140
- constructor() {
141
- __publicField(this, "buffers", /* @__PURE__ */ new Map());
142
- __publicField(this, "projectRoot", null);
143
- }
144
- setProjectRoot(root) {
145
- this.projectRoot = root;
146
- }
147
- addLogFile(options) {
148
- var _a;
149
- if (this.buffers.has(options.name)) {
150
- log.warn(`Log file "${options.name}" already exists, skipping`);
151
- return;
152
- }
153
- const buffer = new FileLogBuffer(options);
154
- buffer.start((_a = this.projectRoot) != null ? _a : void 0);
155
- this.buffers.set(options.name, buffer);
156
- log.info(`Added log file watcher: ${options.name} -> ${options.filePath}`);
157
- }
158
- removeLogFile(name) {
159
- const buffer = this.buffers.get(name);
160
- if (buffer) {
161
- buffer.stop();
162
- this.buffers.delete(name);
163
- log.info(`Removed log file watcher: ${name}`);
164
- }
165
+ function resolvePath(filePath, projectRoot) {
166
+ if (path.isAbsolute(filePath)) {
167
+ return filePath;
165
168
  }
166
- getBuffer(name) {
167
- return this.buffers.get(name);
168
- }
169
- getAllBuffers() {
170
- return this.buffers;
171
- }
172
- stopAll() {
173
- for (const [name, buffer] of this.buffers) {
174
- buffer.stop();
175
- log.debug(`Stopped log file watcher: ${name}`);
176
- }
177
- this.buffers.clear();
178
- }
179
- getLogFileNames() {
180
- return Array.from(this.buffers.keys());
181
- }
182
- }
183
- let globalWatcher = null;
184
- function getServiceLogWatcher() {
185
- if (!globalWatcher) {
186
- globalWatcher = new ServiceLogWatcher();
169
+ if (projectRoot) {
170
+ return path.resolve(projectRoot, filePath);
187
171
  }
188
- return globalWatcher;
172
+ return path.resolve(process.cwd(), filePath);
189
173
  }
190
174
  export {
191
- ServiceLogWatcher,
192
- getServiceLogWatcher
175
+ readLogFile,
176
+ readLogFileTail
193
177
  };
@@ -4,7 +4,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
7
  var __export = (target, all) => {
9
8
  for (var name in all)
10
9
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -26,201 +25,187 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
25
  mod
27
26
  ));
28
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
28
+ var __async = (__this, __arguments, generator) => {
29
+ return new Promise((resolve, reject) => {
30
+ var fulfilled = (value) => {
31
+ try {
32
+ step(generator.next(value));
33
+ } catch (e) {
34
+ reject(e);
35
+ }
36
+ };
37
+ var rejected = (value) => {
38
+ try {
39
+ step(generator.throw(value));
40
+ } catch (e) {
41
+ reject(e);
42
+ }
43
+ };
44
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
45
+ step((generator = generator.apply(__this, __arguments)).next());
46
+ });
47
+ };
30
48
  var file_log_watcher_exports = {};
31
49
  __export(file_log_watcher_exports, {
32
- ServiceLogWatcher: () => ServiceLogWatcher,
33
- getServiceLogWatcher: () => getServiceLogWatcher
50
+ readLogFile: () => readLogFile,
51
+ readLogFileTail: () => readLogFileTail
34
52
  });
35
53
  module.exports = __toCommonJS(file_log_watcher_exports);
36
54
  var import_fs = __toESM(require("fs"));
37
55
  var import_path = __toESM(require("path"));
38
56
  var import_logger = require("./logger.cjs");
39
- const log = (0, import_logger.createLogger)("FileLogWatcher");
40
- class FileLogBuffer {
41
- constructor(options) {
42
- __publicField(this, "buffer", []);
43
- __publicField(this, "maxSize");
44
- __publicField(this, "name");
45
- __publicField(this, "filePath");
46
- __publicField(this, "lastPosition", 0);
47
- __publicField(this, "watcher", null);
48
- __publicField(this, "enabled", false);
49
- var _a;
50
- this.name = options.name;
51
- this.filePath = options.filePath;
52
- this.maxSize = (_a = options.maxBufferSize) != null ? _a : 200;
53
- this.enabled = true;
57
+ const log = (0, import_logger.createLogger)("FileLogReader");
58
+ function detectLogLevel(line) {
59
+ const lowerLine = line.toLowerCase();
60
+ if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
61
+ return "error";
62
+ }
63
+ if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
64
+ return "warn";
54
65
  }
55
- start(projectRoot) {
56
- const resolvedPath = this.resolvePath(projectRoot);
66
+ return "info";
67
+ }
68
+ function parseLogTimestamp(line) {
69
+ const timestampPatterns = [
70
+ /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?)/,
71
+ /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/,
72
+ /(\[([^\]]+)\])/
73
+ ];
74
+ for (const pattern of timestampPatterns) {
75
+ const match = line.match(pattern);
76
+ if (match) {
77
+ const timestampStr = match[1];
78
+ const date = new Date(timestampStr);
79
+ if (!isNaN(date.getTime())) {
80
+ return date.toISOString();
81
+ }
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function readLogFile(options) {
87
+ return __async(this, null, function* () {
88
+ const { name, filePath, projectRoot, level, limit, since } = options;
89
+ const resolvedPath = resolvePath(filePath, projectRoot);
57
90
  if (!import_fs.default.existsSync(resolvedPath)) {
58
91
  log.debug(`Log file does not exist: ${resolvedPath}`);
59
- return;
92
+ return [];
60
93
  }
61
- const stat = import_fs.default.statSync(resolvedPath);
62
- this.lastPosition = stat.size;
63
- this.watcher = import_fs.default.watch(resolvedPath, (eventType) => {
64
- if (eventType === "change") {
65
- this.readNewLogs(resolvedPath);
94
+ try {
95
+ const content = yield import_fs.default.promises.readFile(resolvedPath, "utf-8");
96
+ const lines = content.split("\n").filter((line) => line.trim());
97
+ const entries = [];
98
+ const sinceDate = since ? new Date(since) : null;
99
+ for (const line of lines) {
100
+ const entry = {
101
+ level: detectLogLevel(line),
102
+ message: line,
103
+ timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
104
+ source: `file:${name}`
105
+ };
106
+ if (sinceDate && new Date(entry.timestamp) < sinceDate) {
107
+ continue;
108
+ }
109
+ if (level) {
110
+ const levels = Array.isArray(level) ? level : [level];
111
+ if (!levels.includes(entry.level)) {
112
+ continue;
113
+ }
114
+ }
115
+ entries.push(entry);
66
116
  }
67
- });
68
- this.watcher.on("error", (err) => {
69
- log.error(`Error watching file ${resolvedPath}`, { error: err });
70
- });
71
- log.info(`Started watching log file: ${resolvedPath}`);
72
- }
73
- stop() {
74
- if (this.watcher) {
75
- this.watcher.close();
76
- this.watcher = null;
77
- log.debug(`Stopped watching log file: ${this.filePath}`);
78
- }
79
- }
80
- resolvePath(projectRoot) {
81
- if (import_path.default.isAbsolute(this.filePath)) {
82
- return this.filePath;
117
+ if (limit && limit > 0) {
118
+ return entries.slice(-limit);
119
+ }
120
+ return entries;
121
+ } catch (err) {
122
+ log.error(`Error reading log file ${resolvedPath}`, { error: err });
123
+ return [];
83
124
  }
84
- if (projectRoot) {
85
- return import_path.default.resolve(projectRoot, this.filePath);
125
+ });
126
+ }
127
+ function readLogFileTail(options) {
128
+ return __async(this, null, function* () {
129
+ const { name, filePath, projectRoot, lines = 200, limit, level, since } = options;
130
+ const resolvedPath = resolvePath(filePath, projectRoot);
131
+ if (!import_fs.default.existsSync(resolvedPath)) {
132
+ log.debug(`Log file does not exist: ${resolvedPath}`);
133
+ return [];
86
134
  }
87
- return import_path.default.resolve(process.cwd(), this.filePath);
88
- }
89
- readNewLogs(filePath) {
90
135
  try {
91
- const stat = import_fs.default.statSync(filePath);
92
- if (stat.size <= this.lastPosition) {
93
- return;
136
+ const stat = import_fs.default.statSync(resolvedPath);
137
+ const fd = import_fs.default.openSync(resolvedPath, "r");
138
+ const chunkSize = 16 * 1024;
139
+ let position = stat.size;
140
+ let buffer = Buffer.alloc(0);
141
+ const lineCount = 0;
142
+ while (position > 0 && lineCount <= lines) {
143
+ const readSize = Math.min(chunkSize, position);
144
+ position -= readSize;
145
+ const chunk = Buffer.alloc(readSize);
146
+ import_fs.default.readSync(fd, chunk, 0, readSize, position);
147
+ buffer = Buffer.concat([chunk, buffer]);
148
+ const newLineCount = buffer.filter((byte) => byte === 10).length;
149
+ if (newLineCount >= lines) {
150
+ const linesArray = buffer.toString("utf-8").split("\n");
151
+ const excessLines = newLineCount - lines;
152
+ let charsToRemove = 0;
153
+ let count = 0;
154
+ for (let i = 0; i < linesArray.length; i++) {
155
+ count += linesArray[i].length + 1;
156
+ if (count > excessLines) {
157
+ charsToRemove = linesArray.slice(0, i + 1).join("\n").length + 1;
158
+ break;
159
+ }
160
+ }
161
+ buffer = buffer.slice(charsToRemove);
162
+ break;
163
+ }
94
164
  }
95
- const fd = import_fs.default.openSync(filePath, "r");
96
- const buffer = Buffer.alloc(stat.size - this.lastPosition);
97
- import_fs.default.readSync(fd, buffer, 0, buffer.length, this.lastPosition);
98
165
  import_fs.default.closeSync(fd);
99
- this.lastPosition = stat.size;
100
166
  const content = buffer.toString("utf-8").trim();
101
- if (content) {
102
- this.processLogContent(content);
167
+ const logLines = content.split("\n").filter((line) => line.trim());
168
+ const entries = [];
169
+ const sinceDate = since ? new Date(since) : null;
170
+ for (const line of logLines) {
171
+ const entry = {
172
+ level: detectLogLevel(line),
173
+ message: line,
174
+ timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
175
+ source: `file:${name}`
176
+ };
177
+ if (sinceDate && new Date(entry.timestamp) < sinceDate) {
178
+ continue;
179
+ }
180
+ if (level) {
181
+ const levels = Array.isArray(level) ? level : [level];
182
+ if (!levels.includes(entry.level)) {
183
+ continue;
184
+ }
185
+ }
186
+ entries.push(entry);
103
187
  }
188
+ if (limit && limit > 0) {
189
+ return entries.slice(-limit);
190
+ }
191
+ return entries;
104
192
  } catch (err) {
105
- log.error(`Error reading log file ${filePath}`, { error: err });
106
- }
107
- }
108
- processLogContent(content) {
109
- const lines = content.split("\n");
110
- for (const line of lines) {
111
- if (!line.trim()) continue;
112
- this.addEntry({
113
- level: this.detectLogLevel(line),
114
- message: line,
115
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
116
- source: `file:${this.name}`
117
- });
118
- }
119
- }
120
- detectLogLevel(line) {
121
- const lowerLine = line.toLowerCase();
122
- if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
123
- return "error";
124
- }
125
- if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
126
- return "warn";
193
+ log.error(`Error reading log file ${resolvedPath}`, { error: err });
194
+ return [];
127
195
  }
128
- return "info";
129
- }
130
- addEntry(entry) {
131
- if (!this.enabled) return;
132
- if (this.buffer.length >= this.maxSize) {
133
- this.buffer.shift();
134
- }
135
- this.buffer.push(entry);
136
- }
137
- getLogs(options = {}) {
138
- let logs = [...this.buffer];
139
- if (options.level) {
140
- const levels = Array.isArray(options.level) ? options.level : [options.level];
141
- logs = logs.filter((log2) => levels.includes(log2.level));
142
- }
143
- if (options.since) {
144
- const sinceDate = new Date(options.since);
145
- logs = logs.filter((log2) => new Date(log2.timestamp) >= sinceDate);
146
- }
147
- if (options.limit && options.limit > 0) {
148
- logs = logs.slice(-options.limit);
149
- }
150
- return logs;
151
- }
152
- clear() {
153
- this.buffer = [];
154
- }
155
- size() {
156
- return this.buffer.length;
157
- }
158
- setEnabled(enabled) {
159
- this.enabled = enabled;
160
- }
161
- isEnabled() {
162
- return this.enabled;
163
- }
164
- getName() {
165
- return this.name;
166
- }
167
- getFilePath() {
168
- return this.filePath;
169
- }
196
+ });
170
197
  }
171
- class ServiceLogWatcher {
172
- constructor() {
173
- __publicField(this, "buffers", /* @__PURE__ */ new Map());
174
- __publicField(this, "projectRoot", null);
175
- }
176
- setProjectRoot(root) {
177
- this.projectRoot = root;
178
- }
179
- addLogFile(options) {
180
- var _a;
181
- if (this.buffers.has(options.name)) {
182
- log.warn(`Log file "${options.name}" already exists, skipping`);
183
- return;
184
- }
185
- const buffer = new FileLogBuffer(options);
186
- buffer.start((_a = this.projectRoot) != null ? _a : void 0);
187
- this.buffers.set(options.name, buffer);
188
- log.info(`Added log file watcher: ${options.name} -> ${options.filePath}`);
189
- }
190
- removeLogFile(name) {
191
- const buffer = this.buffers.get(name);
192
- if (buffer) {
193
- buffer.stop();
194
- this.buffers.delete(name);
195
- log.info(`Removed log file watcher: ${name}`);
196
- }
198
+ function resolvePath(filePath, projectRoot) {
199
+ if (import_path.default.isAbsolute(filePath)) {
200
+ return filePath;
197
201
  }
198
- getBuffer(name) {
199
- return this.buffers.get(name);
200
- }
201
- getAllBuffers() {
202
- return this.buffers;
203
- }
204
- stopAll() {
205
- for (const [name, buffer] of this.buffers) {
206
- buffer.stop();
207
- log.debug(`Stopped log file watcher: ${name}`);
208
- }
209
- this.buffers.clear();
210
- }
211
- getLogFileNames() {
212
- return Array.from(this.buffers.keys());
213
- }
214
- }
215
- let globalWatcher = null;
216
- function getServiceLogWatcher() {
217
- if (!globalWatcher) {
218
- globalWatcher = new ServiceLogWatcher();
202
+ if (projectRoot) {
203
+ return import_path.default.resolve(projectRoot, filePath);
219
204
  }
220
- return globalWatcher;
205
+ return import_path.default.resolve(process.cwd(), filePath);
221
206
  }
222
207
  // Annotate the CommonJS export names for ESM import in node:
223
208
  0 && (module.exports = {
224
- ServiceLogWatcher,
225
- getServiceLogWatcher
209
+ readLogFile,
210
+ readLogFileTail
226
211
  });
@@ -4,50 +4,20 @@ export interface FileLogEntry {
4
4
  timestamp: string;
5
5
  source: string;
6
6
  }
7
- export interface FileLogBufferOptions {
7
+ export interface LogFileOptions {
8
8
  name: string;
9
9
  filePath: string;
10
- maxBufferSize?: number;
11
- watchExisting?: boolean;
12
10
  }
13
- declare class FileLogBuffer {
14
- private buffer;
15
- private maxSize;
16
- private name;
17
- private filePath;
18
- private lastPosition;
19
- private watcher;
20
- private enabled;
21
- constructor(options: FileLogBufferOptions);
22
- start(projectRoot?: string): void;
23
- stop(): void;
24
- private resolvePath;
25
- private readNewLogs;
26
- private processLogContent;
27
- private detectLogLevel;
28
- addEntry(entry: FileLogEntry): void;
29
- getLogs(options?: {
30
- level?: FileLogEntry["level"] | FileLogEntry["level"][];
31
- limit?: number;
32
- since?: string;
33
- }): FileLogEntry[];
34
- clear(): void;
35
- size(): number;
36
- setEnabled(enabled: boolean): void;
37
- isEnabled(): boolean;
38
- getName(): string;
39
- getFilePath(): string;
40
- }
41
- export declare class ServiceLogWatcher {
42
- private buffers;
43
- private projectRoot;
44
- setProjectRoot(root: string): void;
45
- addLogFile(options: FileLogBufferOptions): void;
46
- removeLogFile(name: string): void;
47
- getBuffer(name: string): FileLogBuffer | undefined;
48
- getAllBuffers(): Map<string, FileLogBuffer>;
49
- stopAll(): void;
50
- getLogFileNames(): string[];
51
- }
52
- export declare function getServiceLogWatcher(): ServiceLogWatcher;
53
- export {};
11
+ export declare function readLogFile(options: LogFileOptions & {
12
+ projectRoot?: string;
13
+ level?: ("info" | "warn" | "error") | ("info" | "warn" | "error")[];
14
+ limit?: number;
15
+ since?: string;
16
+ }): Promise<FileLogEntry[]>;
17
+ export declare function readLogFileTail(options: LogFileOptions & {
18
+ projectRoot?: string;
19
+ lines?: number;
20
+ limit?: number;
21
+ level?: ("info" | "warn" | "error") | ("info" | "warn" | "error")[];
22
+ since?: string;
23
+ }): Promise<FileLogEntry[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vite-plugin-opencode-assistant/shared",
3
- "version": "1.0.58",
3
+ "version": "1.0.60",
4
4
  "type": "module",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.mjs",