coderail-watch 0.1.3 → 0.1.4

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 CHANGED
@@ -8,6 +8,12 @@ Usage:
8
8
  npx coderail-watch --session-id <session_id> --project-key <project_public_id> --base-url http://localhost:8000
9
9
  ```
10
10
 
11
+ Watch a log file instead of stdin:
12
+
13
+ ```bash
14
+ npx coderail-watch --session-id <session_id> --project-key <project_public_id> --base-url http://localhost:8000 --log-path /path/to/logfile
15
+ ```
16
+
11
17
  Publish (npm):
12
18
 
13
19
  ```bash
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
+ const fs = require("fs");
4
5
  const readline = require("readline");
5
6
 
6
7
  const DEFAULT_BASE_URL = "http://localhost:8000";
@@ -68,6 +69,7 @@ const projectKey = args["project-key"];
68
69
  const baseUrl = args["base-url"] || DEFAULT_BASE_URL;
69
70
  const token = args["token"];
70
71
  const retryWaitMs = Number(args["retry-wait"] || DEFAULT_RETRY_WAIT_MS);
72
+ const logPath = args["log-path"];
71
73
 
72
74
  if (!sessionId || !projectKey) {
73
75
  log("Missing required args: --session-id and --project-key");
@@ -179,7 +181,7 @@ const enqueueOrSend = (payload) => {
179
181
  queue.push(payload);
180
182
  };
181
183
 
182
- rl.on("line", (line) => {
184
+ const handleLine = (line) => {
183
185
  const payload = JSON.stringify({
184
186
  type: "log",
185
187
  content: line,
@@ -194,10 +196,105 @@ rl.on("line", (line) => {
194
196
  });
195
197
  enqueueOrSend(alertPayload);
196
198
  }
197
- });
199
+ };
200
+
201
+ const startFileTailer = (targetPath) => {
202
+ let fileOffset = 0;
203
+ let remainder = "";
204
+
205
+ const initializeOffset = () => {
206
+ try {
207
+ const stats = fs.statSync(targetPath);
208
+ fileOffset = stats.size;
209
+ return true;
210
+ } catch (error) {
211
+ log(`log-path not found: ${targetPath} (retrying...)`);
212
+ return false;
213
+ }
214
+ };
215
+
216
+ const flushChunk = (chunk) => {
217
+ const text = `${remainder}${chunk}`;
218
+ const lines = text.split(/\r?\n/);
219
+ remainder = lines.pop() ?? "";
220
+ lines.forEach((line) => {
221
+ if (line.length > 0) {
222
+ handleLine(line);
223
+ }
224
+ });
225
+ };
226
+
227
+ const readNewData = () => {
228
+ fs.stat(targetPath, (error, stats) => {
229
+ if (error) {
230
+ log(`log-path read error: ${error.message || String(error)}`);
231
+ return;
232
+ }
233
+ if (stats.size < fileOffset) {
234
+ fileOffset = 0;
235
+ }
236
+ if (stats.size === fileOffset) {
237
+ return;
238
+ }
239
+ const stream = fs.createReadStream(targetPath, {
240
+ start: fileOffset,
241
+ end: stats.size - 1,
242
+ encoding: "utf8",
243
+ });
244
+ stream.on("data", flushChunk);
245
+ stream.on("end", () => {
246
+ fileOffset = stats.size;
247
+ });
248
+ stream.on("error", (streamError) => {
249
+ log(
250
+ `log-path stream error: ${
251
+ streamError.message || String(streamError)
252
+ }`,
253
+ );
254
+ });
255
+ });
256
+ };
257
+
258
+ const startWatcher = () => {
259
+ try {
260
+ fs.watch(targetPath, { persistent: true }, (eventType) => {
261
+ if (eventType === "change") {
262
+ readNewData();
263
+ }
264
+ });
265
+ } catch (error) {
266
+ log(`log-path watch error: ${error.message || String(error)}`);
267
+ }
268
+ };
198
269
 
199
- rl.on("close", () => {
200
- if (socket) {
201
- socket.close();
270
+ if (!initializeOffset()) {
271
+ const retryTimer = setInterval(() => {
272
+ if (initializeOffset()) {
273
+ clearInterval(retryTimer);
274
+ startWatcher();
275
+ }
276
+ }, retryWaitMs);
277
+ return;
202
278
  }
203
- });
279
+
280
+ startWatcher();
281
+ };
282
+
283
+ if (logPath) {
284
+ startFileTailer(logPath);
285
+ } else {
286
+ const rl = readline.createInterface({
287
+ input: process.stdin,
288
+ crlfDelay: Infinity,
289
+ });
290
+
291
+ rl.on("line", (line) => {
292
+ handleLine(line);
293
+ });
294
+
295
+ rl.on("close", () => {
296
+ if (socket) {
297
+ socket.close();
298
+ }
299
+ });
300
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coderail-watch",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Stream terminal output to CodeRail backend over WebSocket.",
5
5
  "bin": {
6
6
  "coderail-watch": "bin/coderail-watch.js"