twd-relay 0.1.0 → 0.2.1
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/dist/browser.cjs.js +1 -1
- package/dist/browser.d.ts +2 -0
- package/dist/browser.es.js +63 -53
- package/dist/cli.js +155 -28
- package/package.json +2 -2
package/dist/browser.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var P=Object.create;var C=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var I=(n,o,i,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of L(o))!z.call(n,l)&&l!==i&&C(n,l,{get:()=>o[l],enumerable:!(f=A(o,l))||f.enumerable});return n};var M=(n,o,i)=>(i=n!=null?P($(n)):{},I(o||!n||!n.__esModule?C(i,"default",{value:n,enumerable:!0}):i,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function j(){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/__twd/ws`}function p(n,o){if(!n.parent)return"";const i=o.get(n.parent);return i?i.name:""}function B(n){const o=n?.url??j(),i=n?.reconnect??!0,f=n?.reconnectInterval??2e3,l=n?.log??!1,T="[twd-relay]";function m(...t){l&&console.info(T,...t)}function S(...t){console.warn(T,...t)}let r=null,w=!1,g=null;function a(t){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(t))}function u(){window.dispatchEvent(new CustomEvent("twd:state-change"))}async function N(){const t=window.__TWD_STATE__;if(!t){S("TWD not initialized — make sure twd-js is loaded before running tests"),a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=Array.from(s.values()).filter(e=>e.type==="test").length;a({type:"run:start",testCount:y});let c=0,_=0,b=0;const h=performance.now(),O={onStart(e){e.status="running",u(),a({type:"test:start",id:e.id,name:e.name,suite:p(e,s)})},onPass(e){c++,e.status="pass",u(),a({type:"test:pass",id:e.id,name:e.name,suite:p(e,s),duration:performance.now()-h})},onFail(e,d){_++,e.status="fail",e.logs=[d.message],u(),a({type:"test:fail",id:e.id,name:e.name,suite:p(e,s),error:d.message,duration:performance.now()-h})},onSkip(e){b++,e.status="skip",u(),a({type:"test:skip",id:e.id,name:e.name,suite:p(e,s)})},onSuiteStart(e){e.status="running",u()},onSuiteEnd(e){e.status="idle",u()}};try{const{TestRunner:e}=await import("twd-js/runner");await new e(O).runAll()}catch(e){const d=e instanceof Error?e.message:String(e);S("Runner error:",d),a({type:"error",code:"RUNNER_ERROR",message:d})}const D=performance.now()-h;a({type:"run:complete",passed:c,failed:_,skipped:b,duration:D}),u()}function R(){const t=window.__TWD_STATE__;if(!t){a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=[];for(const[,c]of s)c.type==="test"&&y.push({id:c.id,name:c.name,suite:p(c,s),status:c.status??"idle"});a({type:"status:result",tests:y})}function W(t){let s;try{s=JSON.parse(t.data)}catch{return}s.type==="run"?(m("Received run command — running tests..."),N()):s.type==="status"&&R()}function k(){i&&!w&&(m(`Reconnecting in ${f}ms...`),g=setTimeout(()=>{E()},f))}function E(){r&&(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)||(w=!1,m("Connecting to",o),r=new WebSocket(o),r.addEventListener("open",()=>{a({type:"hello",role:"browser"}),m("Connected to relay — ready to receive run/status commands")}),r.addEventListener("message",W),r.addEventListener("close",t=>{if(r=null,t.reason==="Replaced by new browser"){S("Another browser instance connected — this instance will not reconnect");return}w||m("Disconnected",t.code?`(code ${t.code})`:"",t.reason||""),k()}),r.addEventListener("error",()=>{}))}function v(){w=!0,g&&(clearTimeout(g),g=null),r&&(r.close(1e3,"Client disconnecting"),r=null)}return{connect:E,disconnect:v,get connected(){return r!==null&&r.readyState===WebSocket.OPEN}}}exports.createBrowserClient=B;
|
package/dist/browser.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare interface BrowserClientOptions {
|
|
|
11
11
|
reconnect?: boolean;
|
|
12
12
|
/** Milliseconds between reconnect attempts. Default: 2000 */
|
|
13
13
|
reconnectInterval?: number;
|
|
14
|
+
/** Enable console logging. Default: false */
|
|
15
|
+
log?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export declare function createBrowserClient(options?: BrowserClientOptions): BrowserClient;
|
package/dist/browser.es.js
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
function
|
|
1
|
+
function O() {
|
|
2
2
|
return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/__twd/ws`;
|
|
3
3
|
}
|
|
4
|
-
function l(
|
|
5
|
-
if (!
|
|
6
|
-
const f = d.get(
|
|
4
|
+
function l(s, d) {
|
|
5
|
+
if (!s.parent) return "";
|
|
6
|
+
const f = d.get(s.parent);
|
|
7
7
|
return f ? f.name : "";
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
const d =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
function A(s) {
|
|
10
|
+
const d = s?.url ?? O(), f = s?.reconnect ?? !0, S = s?.reconnectInterval ?? 2e3, N = s?.log ?? !1, h = "[twd-relay]";
|
|
11
|
+
function u(...n) {
|
|
12
|
+
N && console.info(h, ...n);
|
|
13
|
+
}
|
|
14
|
+
function g(...n) {
|
|
15
|
+
console.warn(h, ...n);
|
|
16
|
+
}
|
|
17
|
+
let t = null, p = !1, m = null;
|
|
18
|
+
function r(n) {
|
|
19
|
+
t && t.readyState === WebSocket.OPEN && t.send(JSON.stringify(n));
|
|
14
20
|
}
|
|
15
21
|
function i() {
|
|
16
22
|
window.dispatchEvent(new CustomEvent("twd:state-change"));
|
|
17
23
|
}
|
|
18
|
-
async function
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
21
|
-
r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
|
|
24
|
+
async function C() {
|
|
25
|
+
const n = window.__TWD_STATE__;
|
|
26
|
+
if (!n) {
|
|
27
|
+
g("TWD not initialized — make sure twd-js is loaded before running tests"), r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
|
|
22
28
|
return;
|
|
23
29
|
}
|
|
24
|
-
const o =
|
|
30
|
+
const o = n.handlers, w = Array.from(o.values()).filter((e) => e.type === "test").length;
|
|
25
31
|
r({ type: "run:start", testCount: w });
|
|
26
|
-
let
|
|
32
|
+
let a = 0, T = 0, _ = 0;
|
|
27
33
|
const y = performance.now(), v = {
|
|
28
34
|
onStart(e) {
|
|
29
35
|
e.status = "running", i(), r({
|
|
@@ -34,7 +40,7 @@ function O(a) {
|
|
|
34
40
|
});
|
|
35
41
|
},
|
|
36
42
|
onPass(e) {
|
|
37
|
-
|
|
43
|
+
a++, e.status = "pass", i(), r({
|
|
38
44
|
type: "test:pass",
|
|
39
45
|
id: e.id,
|
|
40
46
|
name: e.name,
|
|
@@ -42,18 +48,18 @@ function O(a) {
|
|
|
42
48
|
duration: performance.now() - y
|
|
43
49
|
});
|
|
44
50
|
},
|
|
45
|
-
onFail(e,
|
|
46
|
-
|
|
51
|
+
onFail(e, c) {
|
|
52
|
+
T++, e.status = "fail", e.logs = [c.message], i(), r({
|
|
47
53
|
type: "test:fail",
|
|
48
54
|
id: e.id,
|
|
49
55
|
name: e.name,
|
|
50
56
|
suite: l(e, o),
|
|
51
|
-
error:
|
|
57
|
+
error: c.message,
|
|
52
58
|
duration: performance.now() - y
|
|
53
59
|
});
|
|
54
60
|
},
|
|
55
61
|
onSkip(e) {
|
|
56
|
-
|
|
62
|
+
_++, e.status = "skip", i(), r({
|
|
57
63
|
type: "test:skip",
|
|
58
64
|
id: e.id,
|
|
59
65
|
name: e.name,
|
|
@@ -71,61 +77,65 @@ function O(a) {
|
|
|
71
77
|
const { TestRunner: e } = await import("twd-js/runner");
|
|
72
78
|
await new e(v).runAll();
|
|
73
79
|
} catch (e) {
|
|
74
|
-
const
|
|
75
|
-
r({ type: "error", code: "RUNNER_ERROR", message:
|
|
80
|
+
const c = e instanceof Error ? e.message : String(e);
|
|
81
|
+
g("Runner error:", c), r({ type: "error", code: "RUNNER_ERROR", message: c });
|
|
76
82
|
}
|
|
77
|
-
const
|
|
78
|
-
r({ type: "run:complete", passed:
|
|
83
|
+
const D = performance.now() - y;
|
|
84
|
+
r({ type: "run:complete", passed: a, failed: T, skipped: _, duration: D }), i();
|
|
79
85
|
}
|
|
80
|
-
function
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
86
|
+
function R() {
|
|
87
|
+
const n = window.__TWD_STATE__;
|
|
88
|
+
if (!n) {
|
|
83
89
|
r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
|
|
84
90
|
return;
|
|
85
91
|
}
|
|
86
|
-
const o =
|
|
87
|
-
for (const [,
|
|
88
|
-
|
|
89
|
-
id:
|
|
90
|
-
name:
|
|
91
|
-
suite: l(
|
|
92
|
-
status:
|
|
92
|
+
const o = n.handlers, w = [];
|
|
93
|
+
for (const [, a] of o)
|
|
94
|
+
a.type === "test" && w.push({
|
|
95
|
+
id: a.id,
|
|
96
|
+
name: a.name,
|
|
97
|
+
suite: l(a, o),
|
|
98
|
+
status: a.status ?? "idle"
|
|
93
99
|
});
|
|
94
100
|
r({ type: "status:result", tests: w });
|
|
95
101
|
}
|
|
96
|
-
function
|
|
102
|
+
function W(n) {
|
|
97
103
|
let o;
|
|
98
104
|
try {
|
|
99
|
-
o = JSON.parse(
|
|
105
|
+
o = JSON.parse(n.data);
|
|
100
106
|
} catch {
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
103
|
-
o.type === "run" ? (
|
|
109
|
+
o.type === "run" ? (u("Received run command — running tests..."), C()) : o.type === "status" && R();
|
|
104
110
|
}
|
|
105
|
-
function
|
|
106
|
-
f && !p && (
|
|
107
|
-
|
|
108
|
-
},
|
|
111
|
+
function b() {
|
|
112
|
+
f && !p && (u(`Reconnecting in ${S}ms...`), m = setTimeout(() => {
|
|
113
|
+
E();
|
|
114
|
+
}, S));
|
|
109
115
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
r({ type: "hello", role: "browser" }),
|
|
113
|
-
}),
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
function E() {
|
|
117
|
+
t && (t.readyState === WebSocket.OPEN || t.readyState === WebSocket.CONNECTING) || (p = !1, u("Connecting to", d), t = new WebSocket(d), t.addEventListener("open", () => {
|
|
118
|
+
r({ type: "hello", role: "browser" }), u("Connected to relay — ready to receive run/status commands");
|
|
119
|
+
}), t.addEventListener("message", W), t.addEventListener("close", (n) => {
|
|
120
|
+
if (t = null, n.reason === "Replaced by new browser") {
|
|
121
|
+
g("Another browser instance connected — this instance will not reconnect");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
p || u("Disconnected", n.code ? `(code ${n.code})` : "", n.reason || ""), b();
|
|
125
|
+
}), t.addEventListener("error", () => {
|
|
116
126
|
}));
|
|
117
127
|
}
|
|
118
|
-
function
|
|
119
|
-
p = !0, m && (clearTimeout(m), m = null),
|
|
128
|
+
function k() {
|
|
129
|
+
p = !0, m && (clearTimeout(m), m = null), t && (t.close(1e3, "Client disconnecting"), t = null);
|
|
120
130
|
}
|
|
121
131
|
return {
|
|
122
|
-
connect:
|
|
123
|
-
disconnect:
|
|
132
|
+
connect: E,
|
|
133
|
+
disconnect: k,
|
|
124
134
|
get connected() {
|
|
125
|
-
return
|
|
135
|
+
return t !== null && t.readyState === WebSocket.OPEN;
|
|
126
136
|
}
|
|
127
137
|
};
|
|
128
138
|
}
|
|
129
139
|
export {
|
|
130
|
-
|
|
140
|
+
A as createBrowserClient
|
|
131
141
|
};
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createServer } from "http";
|
|
3
|
-
import { WebSocketServer, WebSocket } from "ws";
|
|
3
|
+
import WebSocket$1, { WebSocketServer, WebSocket } from "ws";
|
|
4
4
|
const DEFAULT_PATH = "/__twd/ws";
|
|
5
|
-
function createTwdRelay(
|
|
5
|
+
function createTwdRelay(server, options) {
|
|
6
6
|
const path = options?.path ?? DEFAULT_PATH;
|
|
7
7
|
const onError = options?.onError;
|
|
8
8
|
const wss = new WebSocketServer({ noServer: true });
|
|
@@ -140,13 +140,13 @@ function createTwdRelay(server2, options) {
|
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
|
-
|
|
143
|
+
server.on("upgrade", upgradeHandler);
|
|
144
144
|
wss.on("error", (err) => {
|
|
145
145
|
if (onError) onError(err);
|
|
146
146
|
});
|
|
147
147
|
return {
|
|
148
148
|
close() {
|
|
149
|
-
|
|
149
|
+
server.removeListener("upgrade", upgradeHandler);
|
|
150
150
|
for (const client of clients) {
|
|
151
151
|
client.close(1e3, "Relay shutting down");
|
|
152
152
|
}
|
|
@@ -166,34 +166,161 @@ function createTwdRelay(server2, options) {
|
|
|
166
166
|
}
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
|
+
function run(options) {
|
|
170
|
+
const { port, timeout } = options;
|
|
171
|
+
const url = `ws://localhost:${port}/__twd/ws`;
|
|
172
|
+
console.log(`Connecting to ${url}...`);
|
|
173
|
+
const ws = new WebSocket$1(url);
|
|
174
|
+
let runSent = false;
|
|
175
|
+
let runComplete = false;
|
|
176
|
+
let failed = false;
|
|
177
|
+
const timer = setTimeout(() => {
|
|
178
|
+
console.error(`
|
|
179
|
+
Timeout: no run:complete received within ${timeout / 1e3}s`);
|
|
180
|
+
ws.close();
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}, timeout);
|
|
183
|
+
ws.on("open", () => {
|
|
184
|
+
ws.send(JSON.stringify({ type: "hello", role: "client" }));
|
|
185
|
+
});
|
|
186
|
+
ws.on("message", (data) => {
|
|
187
|
+
const msg = JSON.parse(data.toString());
|
|
188
|
+
switch (msg.type) {
|
|
189
|
+
case "connected":
|
|
190
|
+
if (msg.browser && !runSent) {
|
|
191
|
+
runSent = true;
|
|
192
|
+
console.log("Browser connected, triggering test run...\n");
|
|
193
|
+
ws.send(JSON.stringify({ type: "run", scope: "all" }));
|
|
194
|
+
} else if (!msg.browser) {
|
|
195
|
+
console.log("Waiting for browser to connect...");
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case "run:start":
|
|
199
|
+
console.log(`Running ${msg.testCount} test(s)...
|
|
200
|
+
`);
|
|
201
|
+
break;
|
|
202
|
+
case "test:start":
|
|
203
|
+
console.log(` RUN: ${msg.suite} > ${msg.name}`);
|
|
204
|
+
break;
|
|
205
|
+
case "test:pass":
|
|
206
|
+
console.log(` PASS: ${msg.suite} > ${msg.name} (${msg.duration}ms)`);
|
|
207
|
+
break;
|
|
208
|
+
case "test:fail":
|
|
209
|
+
failed = true;
|
|
210
|
+
console.log(` FAIL: ${msg.suite} > ${msg.name} (${msg.duration}ms)`);
|
|
211
|
+
if (msg.error) {
|
|
212
|
+
console.log(` Error: ${msg.error}`);
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
case "test:skip":
|
|
216
|
+
console.log(` SKIP: ${msg.suite} > ${msg.name}`);
|
|
217
|
+
break;
|
|
218
|
+
case "run:complete": {
|
|
219
|
+
const duration = (msg.duration / 1e3).toFixed(1);
|
|
220
|
+
console.log(`
|
|
221
|
+
--- Run complete ---`);
|
|
222
|
+
console.log(`Passed: ${msg.passed} | Failed: ${msg.failed} | Skipped: ${msg.skipped}`);
|
|
223
|
+
console.log(`Duration: ${duration}s`);
|
|
224
|
+
runComplete = true;
|
|
225
|
+
clearTimeout(timer);
|
|
226
|
+
ws.close();
|
|
227
|
+
process.exit(failed || msg.failed > 0 ? 1 : 0);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "error":
|
|
231
|
+
console.error(`Error [${msg.code}]: ${msg.message}`);
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
ws.on("close", () => {
|
|
236
|
+
clearTimeout(timer);
|
|
237
|
+
if (!runComplete) {
|
|
238
|
+
console.error("Connection closed before run completed");
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
ws.on("error", (err) => {
|
|
243
|
+
clearTimeout(timer);
|
|
244
|
+
console.error(`Connection error: ${err.message}`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
169
248
|
const args = process.argv.slice(2);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
249
|
+
const subcommand = args.find((a) => !a.startsWith("--"));
|
|
250
|
+
function parseFlag(name) {
|
|
251
|
+
const idx = args.indexOf(name);
|
|
252
|
+
if (idx !== -1 && args[idx + 1]) return args[idx + 1];
|
|
253
|
+
return void 0;
|
|
254
|
+
}
|
|
255
|
+
function printHelp() {
|
|
256
|
+
console.log(`Usage: twd-relay [command] [options]
|
|
257
|
+
|
|
258
|
+
Commands:
|
|
259
|
+
(none), serve Start the standalone relay server (default)
|
|
260
|
+
run Connect to a relay and trigger a test run
|
|
261
|
+
|
|
262
|
+
Options for serve:
|
|
263
|
+
--port <port> Port to listen on (default: 9876)
|
|
264
|
+
|
|
265
|
+
Options for run:
|
|
266
|
+
--port <port> Relay port to connect to (default: 5173)
|
|
267
|
+
--timeout <ms> Timeout in ms (default: 180000)
|
|
268
|
+
|
|
269
|
+
Examples:
|
|
270
|
+
twd-relay # start relay on port 9876
|
|
271
|
+
twd-relay run # trigger run via Vite dev server on 5173
|
|
272
|
+
twd-relay run --port 9876 # trigger run on custom port
|
|
273
|
+
twd-relay run --timeout 30000 # custom timeout`);
|
|
274
|
+
}
|
|
275
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
276
|
+
printHelp();
|
|
277
|
+
process.exit(0);
|
|
278
|
+
}
|
|
279
|
+
if (subcommand === "run") {
|
|
280
|
+
const portStr = parseFlag("--port");
|
|
281
|
+
const timeoutStr = parseFlag("--timeout");
|
|
282
|
+
const port = portStr ? parseInt(portStr, 10) : 5173;
|
|
283
|
+
if (isNaN(port)) {
|
|
284
|
+
console.error("Invalid port number:", portStr);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
const timeout = timeoutStr ? parseInt(timeoutStr, 10) : 18e4;
|
|
288
|
+
if (isNaN(timeout)) {
|
|
289
|
+
console.error("Invalid timeout value:", timeoutStr);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
run({ port, timeout });
|
|
293
|
+
} else if (!subcommand || subcommand === "serve") {
|
|
294
|
+
let shutdown = function() {
|
|
295
|
+
console.log("\nShutting down...");
|
|
296
|
+
relay.close();
|
|
297
|
+
server.close(() => {
|
|
298
|
+
process.exit(0);
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
const portStr = parseFlag("--port");
|
|
302
|
+
let port = 9876;
|
|
303
|
+
if (portStr) {
|
|
304
|
+
port = parseInt(portStr, 10);
|
|
174
305
|
if (isNaN(port)) {
|
|
175
|
-
console.error("Invalid port number:",
|
|
306
|
+
console.error("Invalid port number:", portStr);
|
|
176
307
|
process.exit(1);
|
|
177
308
|
}
|
|
178
|
-
i++;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const server = createServer();
|
|
182
|
-
const relay = createTwdRelay(server, {
|
|
183
|
-
onError(err) {
|
|
184
|
-
console.error("[twd-relay] Error:", err.message);
|
|
185
309
|
}
|
|
186
|
-
|
|
187
|
-
server
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
process.exit(0);
|
|
310
|
+
const server = createServer();
|
|
311
|
+
const relay = createTwdRelay(server, {
|
|
312
|
+
onError(err) {
|
|
313
|
+
console.error("[twd-relay] Error:", err.message);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
server.listen(port, () => {
|
|
317
|
+
console.log(`TWD Relay running on ws://localhost:${port}/__twd/ws`);
|
|
318
|
+
console.log("Waiting for connections...");
|
|
196
319
|
});
|
|
320
|
+
process.on("SIGINT", shutdown);
|
|
321
|
+
process.on("SIGTERM", shutdown);
|
|
322
|
+
} else {
|
|
323
|
+
console.error(`Unknown command: ${subcommand}`);
|
|
324
|
+
printHelp();
|
|
325
|
+
process.exit(1);
|
|
197
326
|
}
|
|
198
|
-
process.on("SIGINT", shutdown);
|
|
199
|
-
process.on("SIGTERM", shutdown);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twd-relay",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "WebSocket relay for TWD — enables AI agents and external tools to run and observe in-browser tests",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BRIKEV",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"build": "vite build && vite build -c vite.cli.config.ts && node -e \"const fs=require('fs');const f='dist/cli.js';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'))\" && chmod +x dist/cli.js",
|
|
45
45
|
"dev": "node dist/cli.js",
|
|
46
46
|
"relay": "npm run build && node dist/cli.js",
|
|
47
|
-
"send-run": "node
|
|
47
|
+
"send-run": "node dist/cli.js run",
|
|
48
48
|
"test": "vitest",
|
|
49
49
|
"test:ci": "vitest --run --coverage"
|
|
50
50
|
},
|