dominus-cli 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -459,6 +459,166 @@ var init_ws_server = __esm({
459
459
  };
460
460
  }
461
461
  });
462
+ async function isPortInUse(port) {
463
+ return new Promise((resolve) => {
464
+ const ws = new WebSocket(`ws://localhost:${port}`);
465
+ const timer = setTimeout(() => {
466
+ ws.close();
467
+ resolve(false);
468
+ }, 1e3);
469
+ ws.on("open", () => {
470
+ clearTimeout(timer);
471
+ ws.close();
472
+ resolve(true);
473
+ });
474
+ ws.on("error", () => {
475
+ clearTimeout(timer);
476
+ resolve(false);
477
+ });
478
+ });
479
+ }
480
+ var DominusClient;
481
+ var init_ws_client = __esm({
482
+ "src/transport/ws-client.ts"() {
483
+ init_protocol();
484
+ DominusClient = class extends EventEmitter {
485
+ ws = null;
486
+ port;
487
+ studioConnected = false;
488
+ connectionInfo = null;
489
+ pendingRequests = /* @__PURE__ */ new Map();
490
+ constructor(port = 18088) {
491
+ super();
492
+ this.port = port;
493
+ }
494
+ connect() {
495
+ return new Promise((resolve, reject) => {
496
+ const url = `ws://localhost:${this.port}`;
497
+ const ws = new WebSocket(url);
498
+ ws.on("open", () => {
499
+ this.ws = ws;
500
+ ws.send(serializeMessage(createMessage("controller:connect", {})));
501
+ this.emit("server:started", { port: this.port, relay: true });
502
+ resolve();
503
+ });
504
+ ws.on("message", (raw) => {
505
+ try {
506
+ const msg = parseMessage(raw.toString());
507
+ this.handleMessage(msg);
508
+ } catch (err) {
509
+ this.emit("server:error", err);
510
+ }
511
+ });
512
+ ws.on("close", () => {
513
+ this.ws = null;
514
+ this.studioConnected = false;
515
+ this.emit("studio:disconnected");
516
+ for (const [id, pending] of this.pendingRequests) {
517
+ clearTimeout(pending.timer);
518
+ pending.reject(new Error("Relay connection lost"));
519
+ this.pendingRequests.delete(id);
520
+ }
521
+ });
522
+ ws.on("error", (err) => {
523
+ if (!this.ws) reject(err);
524
+ this.emit("server:error", err);
525
+ });
526
+ });
527
+ }
528
+ handleMessage(msg) {
529
+ if (msg.type === "controller:welcome") {
530
+ const p = msg.payload;
531
+ this.studioConnected = p.studioConnected;
532
+ if (this.studioConnected) {
533
+ this.emit("studio:connected", {});
534
+ }
535
+ return;
536
+ }
537
+ if (msg.type === "studio:connected") {
538
+ this.studioConnected = true;
539
+ const payload = msg.payload;
540
+ this.connectionInfo = {
541
+ connectedAt: Date.now(),
542
+ studioVersion: payload.studioVersion,
543
+ placeId: payload.placeId,
544
+ placeName: payload.placeName
545
+ };
546
+ this.emit("studio:connected", payload);
547
+ return;
548
+ }
549
+ if (msg.type === "studio:disconnected") {
550
+ this.studioConnected = false;
551
+ this.connectionInfo = null;
552
+ this.emit("studio:disconnected");
553
+ return;
554
+ }
555
+ if (msg.type === StudioMsg.RESPONSE) {
556
+ const pending = this.pendingRequests.get(msg.id);
557
+ if (pending) {
558
+ clearTimeout(pending.timer);
559
+ pending.resolve(msg);
560
+ this.pendingRequests.delete(msg.id);
561
+ }
562
+ return;
563
+ }
564
+ this.emit(msg.type, msg.payload);
565
+ this.emit("message", msg);
566
+ }
567
+ sendRequest(type, payload, timeoutMs = 1e4) {
568
+ return new Promise((resolve, reject) => {
569
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
570
+ reject(new Error("Not connected to Dominus server"));
571
+ return;
572
+ }
573
+ if (!this.studioConnected) {
574
+ reject(new Error("Studio not connected"));
575
+ return;
576
+ }
577
+ const msg = createMessage(type, payload);
578
+ const timer = setTimeout(() => {
579
+ this.pendingRequests.delete(msg.id);
580
+ reject(new Error(`Request timed out: ${type}`));
581
+ }, timeoutMs);
582
+ this.pendingRequests.set(msg.id, {
583
+ resolve,
584
+ reject,
585
+ timer
586
+ });
587
+ this.ws.send(serializeMessage(msg));
588
+ });
589
+ }
590
+ sendNotification(type, payload) {
591
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
592
+ const msg = createMessage(type, payload);
593
+ this.ws.send(serializeMessage(msg));
594
+ }
595
+ isConnected() {
596
+ return this.studioConnected;
597
+ }
598
+ getConnectionInfo() {
599
+ if (!this.connectionInfo) return null;
600
+ return { ...this.connectionInfo, ws: this.ws };
601
+ }
602
+ getPort() {
603
+ return this.port;
604
+ }
605
+ stop() {
606
+ return new Promise((resolve) => {
607
+ for (const [, pending] of this.pendingRequests) {
608
+ clearTimeout(pending.timer);
609
+ pending.reject(new Error("Client shutting down"));
610
+ }
611
+ this.pendingRequests.clear();
612
+ if (this.ws) {
613
+ this.ws.close();
614
+ this.ws = null;
615
+ }
616
+ resolve();
617
+ });
618
+ }
619
+ };
620
+ }
621
+ });
462
622
 
463
623
  // src/memory/store.ts
464
624
  var store_exports = {};
@@ -1994,11 +2154,19 @@ __export(server_exports, {
1994
2154
  async function startMcpServer() {
1995
2155
  const config = loadConfig();
1996
2156
  await initMemoryStore();
1997
- wsServer = new DominusServer(config.port);
1998
- await wsServer.start();
2157
+ const portTaken = await isPortInUse(config.port);
2158
+ if (portTaken) {
2159
+ const client = new DominusClient(config.port);
2160
+ await client.connect();
2161
+ bridge = client;
2162
+ } else {
2163
+ const server = new DominusServer(config.port);
2164
+ await server.start();
2165
+ bridge = server;
2166
+ }
1999
2167
  const mcp = new McpServer({
2000
2168
  name: "dominus",
2001
- version: "0.1.0"
2169
+ version: "0.2.0"
2002
2170
  });
2003
2171
  mcp.tool(
2004
2172
  "read_script",
@@ -2006,7 +2174,7 @@ async function startMcpServer() {
2006
2174
  { path: z.string().describe('Full instance path, e.g. "ServerScriptService.GameManager"') },
2007
2175
  async ({ path: path4 }) => {
2008
2176
  assertConnected();
2009
- const res = await wsServer.sendRequest(CliMsg.GET_SCRIPT, { path: path4 });
2177
+ const res = await bridge.sendRequest(CliMsg.GET_SCRIPT, { path: path4 });
2010
2178
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2011
2179
  }
2012
2180
  );
@@ -2019,7 +2187,7 @@ async function startMcpServer() {
2019
2187
  },
2020
2188
  async ({ path: path4, source }) => {
2021
2189
  assertConnected();
2022
- const res = await wsServer.sendRequest(CliMsg.SET_SCRIPT, { path: path4, source });
2190
+ const res = await bridge.sendRequest(CliMsg.SET_SCRIPT, { path: path4, source });
2023
2191
  return { content: [{ type: "text", text: JSON.stringify(res.payload) }] };
2024
2192
  }
2025
2193
  );
@@ -2029,7 +2197,7 @@ async function startMcpServer() {
2029
2197
  { code: z.string().describe("Luau code to execute") },
2030
2198
  async ({ code }) => {
2031
2199
  assertConnected();
2032
- const res = await wsServer.sendRequest(CliMsg.RUN_CODE, { code });
2200
+ const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code });
2033
2201
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2034
2202
  }
2035
2203
  );
@@ -2042,7 +2210,7 @@ async function startMcpServer() {
2042
2210
  },
2043
2211
  async ({ rootPath, maxDepth }) => {
2044
2212
  assertConnected();
2045
- const res = await wsServer.sendRequest(CliMsg.GET_EXPLORER, {
2213
+ const res = await bridge.sendRequest(CliMsg.GET_EXPLORER, {
2046
2214
  rootPath: rootPath ?? null,
2047
2215
  maxDepth
2048
2216
  });
@@ -2055,7 +2223,7 @@ async function startMcpServer() {
2055
2223
  { path: z.string().describe("Full instance path") },
2056
2224
  async ({ path: path4 }) => {
2057
2225
  assertConnected();
2058
- const res = await wsServer.sendRequest(CliMsg.GET_PROPERTIES, { path: path4 });
2226
+ const res = await bridge.sendRequest(CliMsg.GET_PROPERTIES, { path: path4 });
2059
2227
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2060
2228
  }
2061
2229
  );
@@ -2068,7 +2236,7 @@ async function startMcpServer() {
2068
2236
  },
2069
2237
  async ({ path: path4, properties }) => {
2070
2238
  assertConnected();
2071
- const res = await wsServer.sendRequest(CliMsg.SET_PROPERTIES, { path: path4, properties });
2239
+ const res = await bridge.sendRequest(CliMsg.SET_PROPERTIES, { path: path4, properties });
2072
2240
  return { content: [{ type: "text", text: JSON.stringify(res.payload) }] };
2073
2241
  }
2074
2242
  );
@@ -2083,7 +2251,7 @@ async function startMcpServer() {
2083
2251
  },
2084
2252
  async ({ className, parentPath, name, properties }) => {
2085
2253
  assertConnected();
2086
- const res = await wsServer.sendRequest(CliMsg.INSERT_INSTANCE, {
2254
+ const res = await bridge.sendRequest(CliMsg.INSERT_INSTANCE, {
2087
2255
  className,
2088
2256
  parentPath,
2089
2257
  name,
@@ -2098,7 +2266,7 @@ async function startMcpServer() {
2098
2266
  { path: z.string().describe("Full instance path to delete") },
2099
2267
  async ({ path: path4 }) => {
2100
2268
  assertConnected();
2101
- const res = await wsServer.sendRequest(CliMsg.DELETE_INSTANCE, { path: path4 });
2269
+ const res = await bridge.sendRequest(CliMsg.DELETE_INSTANCE, { path: path4 });
2102
2270
  return { content: [{ type: "text", text: JSON.stringify(res.payload) }] };
2103
2271
  }
2104
2272
  );
@@ -2108,7 +2276,7 @@ async function startMcpServer() {
2108
2276
  {},
2109
2277
  async () => {
2110
2278
  assertConnected();
2111
- const res = await wsServer.sendRequest(CliMsg.GET_SELECTION, {});
2279
+ const res = await bridge.sendRequest(CliMsg.GET_SELECTION, {});
2112
2280
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2113
2281
  }
2114
2282
  );
@@ -2121,7 +2289,7 @@ async function startMcpServer() {
2121
2289
  },
2122
2290
  async ({ query, maxResults }) => {
2123
2291
  assertConnected();
2124
- const res = await wsServer.sendRequest(CliMsg.SEARCH_SCRIPTS, { query, maxResults });
2292
+ const res = await bridge.sendRequest(CliMsg.SEARCH_SCRIPTS, { query, maxResults });
2125
2293
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2126
2294
  }
2127
2295
  );
@@ -2134,7 +2302,7 @@ async function startMcpServer() {
2134
2302
  },
2135
2303
  async ({ limit, level }) => {
2136
2304
  assertConnected();
2137
- const res = await wsServer.sendRequest(CliMsg.GET_OUTPUT, {
2305
+ const res = await bridge.sendRequest(CliMsg.GET_OUTPUT, {
2138
2306
  limit,
2139
2307
  level: level ?? null
2140
2308
  });
@@ -2147,7 +2315,7 @@ async function startMcpServer() {
2147
2315
  { className: z.string().describe('Roblox class name, e.g. "Frame", "TextLabel", "Part", "MeshPart"') },
2148
2316
  async ({ className }) => {
2149
2317
  assertConnected();
2150
- const res = await wsServer.sendRequest(CliMsg.GET_REFLECTION, { className });
2318
+ const res = await bridge.sendRequest(CliMsg.GET_REFLECTION, { className });
2151
2319
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2152
2320
  }
2153
2321
  );
@@ -2162,7 +2330,7 @@ async function startMcpServer() {
2162
2330
  },
2163
2331
  async ({ parent, tree }) => {
2164
2332
  assertConnected();
2165
- const res = await wsServer.sendRequest(
2333
+ const res = await bridge.sendRequest(
2166
2334
  CliMsg.BUILD_UI,
2167
2335
  { parent: parent ?? "StarterGui", tree }
2168
2336
  );
@@ -2175,7 +2343,7 @@ async function startMcpServer() {
2175
2343
  { testName: z.string().optional().describe("Specific test name, or omit to run all") },
2176
2344
  async ({ testName }) => {
2177
2345
  assertConnected();
2178
- const res = await wsServer.sendRequest(CliMsg.RUN_TESTS, {
2346
+ const res = await bridge.sendRequest(CliMsg.RUN_TESTS, {
2179
2347
  testName: testName ?? null
2180
2348
  });
2181
2349
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
@@ -2187,7 +2355,7 @@ async function startMcpServer() {
2187
2355
  { args: z.unknown().optional().describe("Arguments passed to the test session. Server scripts get this via StudioTestService:GetTestArgs()") },
2188
2356
  async ({ args }) => {
2189
2357
  assertConnected();
2190
- const res = await wsServer.sendRequest(CliMsg.EXECUTE_RUN_TEST, { args: args ?? null }, 12e4);
2358
+ const res = await bridge.sendRequest(CliMsg.EXECUTE_RUN_TEST, { args: args ?? null }, 12e4);
2191
2359
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2192
2360
  }
2193
2361
  );
@@ -2197,7 +2365,7 @@ async function startMcpServer() {
2197
2365
  { args: z.unknown().optional().describe("Arguments passed to the test session. Server scripts get this via StudioTestService:GetTestArgs()") },
2198
2366
  async ({ args }) => {
2199
2367
  assertConnected();
2200
- const res = await wsServer.sendRequest(CliMsg.EXECUTE_PLAY_TEST, { args: args ?? null }, 12e4);
2368
+ const res = await bridge.sendRequest(CliMsg.EXECUTE_PLAY_TEST, { args: args ?? null }, 12e4);
2201
2369
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2202
2370
  }
2203
2371
  );
@@ -2236,7 +2404,7 @@ async function startMcpServer() {
2236
2404
  if (params.canCollide !== void 0) properties.CanCollide = params.canCollide;
2237
2405
  if (params.shape && params.shape !== "Block") properties.Shape = params.shape;
2238
2406
  const className = params.shape === "Wedge" ? "WedgePart" : params.shape === "CornerWedge" ? "CornerWedgePart" : "Part";
2239
- const res = await wsServer.sendRequest(CliMsg.INSERT_INSTANCE, {
2407
+ const res = await bridge.sendRequest(CliMsg.INSERT_INSTANCE, {
2240
2408
  className,
2241
2409
  parentPath: params.parent ?? "Workspace",
2242
2410
  name: params.name,
@@ -2275,7 +2443,7 @@ async function startMcpServer() {
2275
2443
  lines.push(`end`);
2276
2444
  }
2277
2445
  lines.push(`print("Cloned: " .. clone:GetFullName())`);
2278
- const res = await wsServer.sendRequest(CliMsg.RUN_CODE, { code: lines.join("\n") });
2446
+ const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code: lines.join("\n") });
2279
2447
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2280
2448
  }
2281
2449
  );
@@ -2308,7 +2476,7 @@ async function startMcpServer() {
2308
2476
  ...resolves.map((r) => `${r}.Parent = group`),
2309
2477
  `print("Grouped ${params.paths.length} instances into: " .. group:GetFullName())`
2310
2478
  ].join("\n");
2311
- const res = await wsServer.sendRequest(CliMsg.RUN_CODE, { code });
2479
+ const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code });
2312
2480
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2313
2481
  }
2314
2482
  );
@@ -2367,7 +2535,7 @@ async function startMcpServer() {
2367
2535
  lines.push(`${v}.Parent = ${groupName ? "_group" : resolveP(p.parent ?? "Workspace")}`);
2368
2536
  }
2369
2537
  lines.push(`print("Built ${parts.length} parts${groupName ? ` in ${groupName}` : ""}")`);
2370
- const res = await wsServer.sendRequest(CliMsg.RUN_CODE, { code: lines.join("\n") });
2538
+ const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code: lines.join("\n") });
2371
2539
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
2372
2540
  }
2373
2541
  );
@@ -2403,16 +2571,16 @@ async function startMcpServer() {
2403
2571
  "studio://status",
2404
2572
  "studio://status",
2405
2573
  async () => {
2406
- const info = wsServer.getConnectionInfo();
2574
+ const info = bridge.getConnectionInfo();
2407
2575
  return {
2408
2576
  contents: [{
2409
2577
  uri: "studio://status",
2410
2578
  text: JSON.stringify({
2411
- connected: wsServer.isConnected(),
2579
+ connected: bridge.isConnected(),
2412
2580
  placeName: info?.placeName,
2413
2581
  placeId: info?.placeId,
2414
2582
  studioVersion: info?.studioVersion,
2415
- port: wsServer.getPort()
2583
+ port: bridge.getPort()
2416
2584
  }, null, 2),
2417
2585
  mimeType: "application/json"
2418
2586
  }]
@@ -2423,16 +2591,17 @@ async function startMcpServer() {
2423
2591
  await mcp.connect(transport);
2424
2592
  }
2425
2593
  function assertConnected() {
2426
- if (!wsServer.isConnected()) {
2594
+ if (!bridge.isConnected()) {
2427
2595
  throw new Error(
2428
2596
  "Roblox Studio is not connected. Open Studio and click the Dominus plugin Connect button."
2429
2597
  );
2430
2598
  }
2431
2599
  }
2432
- var wsServer;
2600
+ var bridge;
2433
2601
  var init_server = __esm({
2434
2602
  "src/mcp/server.ts"() {
2435
2603
  init_ws_server();
2604
+ init_ws_client();
2436
2605
  init_protocol();
2437
2606
  init_store();
2438
2607
  init_config();
@@ -3475,163 +3644,7 @@ var App = ({ server, agent: initialAgent, version }) => {
3475
3644
 
3476
3645
  // src/index.tsx
3477
3646
  init_ws_server();
3478
-
3479
- // src/transport/ws-client.ts
3480
- init_protocol();
3481
- var DominusClient = class extends EventEmitter {
3482
- ws = null;
3483
- port;
3484
- studioConnected = false;
3485
- connectionInfo = null;
3486
- pendingRequests = /* @__PURE__ */ new Map();
3487
- constructor(port = 18088) {
3488
- super();
3489
- this.port = port;
3490
- }
3491
- connect() {
3492
- return new Promise((resolve, reject) => {
3493
- const url = `ws://localhost:${this.port}`;
3494
- const ws = new WebSocket(url);
3495
- ws.on("open", () => {
3496
- this.ws = ws;
3497
- ws.send(serializeMessage(createMessage("controller:connect", {})));
3498
- this.emit("server:started", { port: this.port, relay: true });
3499
- resolve();
3500
- });
3501
- ws.on("message", (raw) => {
3502
- try {
3503
- const msg = parseMessage(raw.toString());
3504
- this.handleMessage(msg);
3505
- } catch (err) {
3506
- this.emit("server:error", err);
3507
- }
3508
- });
3509
- ws.on("close", () => {
3510
- this.ws = null;
3511
- this.studioConnected = false;
3512
- this.emit("studio:disconnected");
3513
- for (const [id, pending] of this.pendingRequests) {
3514
- clearTimeout(pending.timer);
3515
- pending.reject(new Error("Relay connection lost"));
3516
- this.pendingRequests.delete(id);
3517
- }
3518
- });
3519
- ws.on("error", (err) => {
3520
- if (!this.ws) reject(err);
3521
- this.emit("server:error", err);
3522
- });
3523
- });
3524
- }
3525
- handleMessage(msg) {
3526
- if (msg.type === "controller:welcome") {
3527
- const p = msg.payload;
3528
- this.studioConnected = p.studioConnected;
3529
- if (this.studioConnected) {
3530
- this.emit("studio:connected", {});
3531
- }
3532
- return;
3533
- }
3534
- if (msg.type === "studio:connected") {
3535
- this.studioConnected = true;
3536
- const payload = msg.payload;
3537
- this.connectionInfo = {
3538
- connectedAt: Date.now(),
3539
- studioVersion: payload.studioVersion,
3540
- placeId: payload.placeId,
3541
- placeName: payload.placeName
3542
- };
3543
- this.emit("studio:connected", payload);
3544
- return;
3545
- }
3546
- if (msg.type === "studio:disconnected") {
3547
- this.studioConnected = false;
3548
- this.connectionInfo = null;
3549
- this.emit("studio:disconnected");
3550
- return;
3551
- }
3552
- if (msg.type === StudioMsg.RESPONSE) {
3553
- const pending = this.pendingRequests.get(msg.id);
3554
- if (pending) {
3555
- clearTimeout(pending.timer);
3556
- pending.resolve(msg);
3557
- this.pendingRequests.delete(msg.id);
3558
- }
3559
- return;
3560
- }
3561
- this.emit(msg.type, msg.payload);
3562
- this.emit("message", msg);
3563
- }
3564
- sendRequest(type, payload, timeoutMs = 1e4) {
3565
- return new Promise((resolve, reject) => {
3566
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
3567
- reject(new Error("Not connected to Dominus server"));
3568
- return;
3569
- }
3570
- if (!this.studioConnected) {
3571
- reject(new Error("Studio not connected"));
3572
- return;
3573
- }
3574
- const msg = createMessage(type, payload);
3575
- const timer = setTimeout(() => {
3576
- this.pendingRequests.delete(msg.id);
3577
- reject(new Error(`Request timed out: ${type}`));
3578
- }, timeoutMs);
3579
- this.pendingRequests.set(msg.id, {
3580
- resolve,
3581
- reject,
3582
- timer
3583
- });
3584
- this.ws.send(serializeMessage(msg));
3585
- });
3586
- }
3587
- sendNotification(type, payload) {
3588
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
3589
- const msg = createMessage(type, payload);
3590
- this.ws.send(serializeMessage(msg));
3591
- }
3592
- isConnected() {
3593
- return this.studioConnected;
3594
- }
3595
- getConnectionInfo() {
3596
- if (!this.connectionInfo) return null;
3597
- return { ...this.connectionInfo, ws: this.ws };
3598
- }
3599
- getPort() {
3600
- return this.port;
3601
- }
3602
- stop() {
3603
- return new Promise((resolve) => {
3604
- for (const [, pending] of this.pendingRequests) {
3605
- clearTimeout(pending.timer);
3606
- pending.reject(new Error("Client shutting down"));
3607
- }
3608
- this.pendingRequests.clear();
3609
- if (this.ws) {
3610
- this.ws.close();
3611
- this.ws = null;
3612
- }
3613
- resolve();
3614
- });
3615
- }
3616
- };
3617
- async function isPortInUse(port) {
3618
- return new Promise((resolve) => {
3619
- const ws = new WebSocket(`ws://localhost:${port}`);
3620
- const timer = setTimeout(() => {
3621
- ws.close();
3622
- resolve(false);
3623
- }, 1e3);
3624
- ws.on("open", () => {
3625
- clearTimeout(timer);
3626
- ws.close();
3627
- resolve(true);
3628
- });
3629
- ws.on("error", () => {
3630
- clearTimeout(timer);
3631
- resolve(false);
3632
- });
3633
- });
3634
- }
3647
+ init_ws_client();
3635
3648
 
3636
3649
  // src/ai/provider.ts
3637
3650
  init_config();
@@ -4293,7 +4306,7 @@ function createDefaultRegistry() {
4293
4306
  // src/index.tsx
4294
4307
  init_store();
4295
4308
  init_config();
4296
- var VERSION = "0.1.0";
4309
+ var VERSION = "0.2.0";
4297
4310
  var program = new Command();
4298
4311
  program.name("dominus").description("AI-powered autonomous agent for Roblox Studio development").version(VERSION);
4299
4312
  program.command("start", { isDefault: true }).description("Launch the Dominus TUI agent").option("-p, --port <port>", "WebSocket server port", "18088").action(async (opts) => {