coderail-watch 0.1.0

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 ADDED
@@ -0,0 +1,17 @@
1
+ # coderail-watch
2
+
3
+ Stream terminal output to CodeRail backend over WebSocket.
4
+
5
+ Usage:
6
+
7
+ ```bash
8
+ npx coderail-watch --session-id <session_id> --project-key <project_public_id> --base-url http://localhost:8000
9
+ ```
10
+
11
+ Publish (npm):
12
+
13
+ ```bash
14
+ cd tools/coderail-watch
15
+ npm login
16
+ npm run publish:public
17
+ ```
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const readline = require("readline");
5
+ const WebSocket = require("ws");
6
+
7
+ const DEFAULT_BASE_URL = "http://localhost:8000";
8
+ const DEFAULT_RETRY_WAIT_MS = 2000;
9
+
10
+ const parseArgs = (argv) => {
11
+ const args = {};
12
+ for (let index = 0; index < argv.length; index += 1) {
13
+ const token = argv[index];
14
+ if (!token.startsWith("--")) continue;
15
+ const key = token.slice(2);
16
+ const next = argv[index + 1];
17
+ if (next && !next.startsWith("--")) {
18
+ args[key] = next;
19
+ index += 1;
20
+ } else {
21
+ args[key] = "true";
22
+ }
23
+ }
24
+ return args;
25
+ };
26
+
27
+ const log = (message) => {
28
+ process.stderr.write(`[coderail-watch] ${message}\n`);
29
+ };
30
+
31
+ const buildWsUrl = (baseUrl, sessionId, projectKey, token) => {
32
+ let normalized = baseUrl.trim();
33
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
34
+ normalized = `http://${normalized}`;
35
+ }
36
+ const url = new URL(normalized);
37
+ const wsProtocol = url.protocol === "https:" ? "wss:" : "ws:";
38
+ url.protocol = wsProtocol;
39
+ url.pathname = `${url.pathname.replace(/\\/$/, "")}/api/v1/terminal_watch/ws`;
40
+ url.searchParams.set("session_id", sessionId);
41
+ url.searchParams.set("project_key", projectKey);
42
+ url.searchParams.set("role", "producer");
43
+ if (token) {
44
+ url.searchParams.set("token", token);
45
+ }
46
+ return url.toString();
47
+ };
48
+
49
+ const args = parseArgs(process.argv.slice(2));
50
+ const sessionId = args["session-id"];
51
+ const projectKey = args["project-key"];
52
+ const baseUrl = args["base-url"] || DEFAULT_BASE_URL;
53
+ const token = args["token"];
54
+ const retryWaitMs = Number(args["retry-wait"] || DEFAULT_RETRY_WAIT_MS);
55
+
56
+ if (!sessionId || !projectKey) {
57
+ log("Missing required args: --session-id and --project-key");
58
+ process.exit(1);
59
+ }
60
+
61
+ const wsUrl = buildWsUrl(baseUrl, sessionId, projectKey, token);
62
+ let socket = null;
63
+ let isOpen = false;
64
+ const queue = [];
65
+
66
+ const connect = () => {
67
+ socket = new WebSocket(wsUrl);
68
+
69
+ socket.on("open", () => {
70
+ isOpen = true;
71
+ log(`connected: ${wsUrl}`);
72
+ while (queue.length) {
73
+ const payload = queue.shift();
74
+ socket.send(payload);
75
+ }
76
+ });
77
+
78
+ socket.on("close", () => {
79
+ isOpen = false;
80
+ log("disconnected; retrying...");
81
+ setTimeout(connect, retryWaitMs);
82
+ });
83
+
84
+ socket.on("error", (error) => {
85
+ log(`error: ${error.message}`);
86
+ });
87
+ };
88
+
89
+ connect();
90
+
91
+ const rl = readline.createInterface({
92
+ input: process.stdin,
93
+ crlfDelay: Infinity,
94
+ });
95
+
96
+ rl.on("line", (line) => {
97
+ const payload = JSON.stringify({
98
+ type: "log",
99
+ content: line,
100
+ timestamp: new Date().toISOString(),
101
+ });
102
+ if (isOpen && socket) {
103
+ socket.send(payload);
104
+ } else {
105
+ queue.push(payload);
106
+ }
107
+ });
108
+
109
+ rl.on("close", () => {
110
+ if (socket) {
111
+ socket.close();
112
+ }
113
+ });
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "coderail-watch",
3
+ "version": "0.1.0",
4
+ "description": "Stream terminal output to CodeRail backend over WebSocket.",
5
+ "bin": {
6
+ "coderail-watch": "bin/coderail-watch.js"
7
+ },
8
+ "files": [
9
+ "bin"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "scripts": {
15
+ "publish:public": "npm publish --access public"
16
+ },
17
+ "dependencies": {
18
+ "ws": "^8.18.0"
19
+ },
20
+ "license": "MIT",
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ }
24
+ }