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 +17 -0
- package/bin/coderail-watch.js +113 -0
- package/package.json +24 -0
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
|
+
}
|