dominus-cli 2.1.0 → 2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.0 (2026-07-13)
4
+
5
+ ### MCP-native multi-agent coordinator
6
+
7
+ - Added `run_parallel_task` to the published MCP server. It uses the MCP client's
8
+ model through standard Sampling, so Dominus still requires no AI provider key.
9
+ - Added deterministic path-based task partitioning. UI and generic roots are split
10
+ by immediate child branches and distributed across one to five workers.
11
+ - Workers run concurrently and receive bounded hierarchy plus live typed property
12
+ evidence for only their owned scopes. Workers are proposal-only and never receive
13
+ Studio mutation tools.
14
+ - Added strict worker proposal and coordinator decision schemas. Existing instance
15
+ refs must exactly match supplied ID/path pairs, and every mutation must remain
16
+ inside the worker's owned branches.
17
+ - Added ancestor/descendant conflict detection and coordinator-only operation
18
+ ordering. The coordinator may select complete validated proposals but cannot
19
+ invent, rewrite, or partially accept worker operations.
20
+ - Final writes use one atomic `studio_apply` batch. Dominus then reads the root and
21
+ affected properties back, checks creates and moves, and performs at most one
22
+ bounded repair pass before reporting verification evidence.
23
+ - Parallel planning refuses truncated hierarchy evidence, and generic property
24
+ mutations can no longer bypass revision checks by writing script `Source`.
25
+ - Added summary-level MCP logging events for worker starts/completions, proposal
26
+ decisions, application, and verification without exposing full model traces.
27
+ - Clients without Sampling receive a clear capability response and keep the normal
28
+ single-agent inspect-plan-apply-verify workflow.
29
+
30
+ ### Studio connection reliability
31
+
32
+ - Made pending WebStream connection attempts cancellable so overlapping retries
33
+ cannot strand a waiting coroutine or deadlock future reconnects.
34
+ - Serialized reconnect attempts and reset failed open transports explicitly.
35
+ - Changed the Dominus 2 toolbar button to always force an immediate reconnect
36
+ instead of toggling automatic reconnection off.
37
+
38
+ ### Typed mutations and packaging
39
+
40
+ - Fixed valid Enum values such as `Slate`, `Wood`, `Glass`, and `Ball` being
41
+ rejected by resolving names against the live Enum type's items.
42
+ - Removed the stale binary `.rbxm` build artifact. The package and installer now
43
+ use the single canonical `.rbxmx` plugin build.
44
+ - Corrected documentation for the 17-tool MCP catalog, canonical plugin format,
45
+ and optional Studio restart behavior.
46
+
3
47
  ## 2.1.0 (2026-07-12)
4
48
 
5
49
  Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
@@ -8,7 +52,7 @@ Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
8
52
 
9
53
  - Removed the Ink/TUI command from the published binaries. Dominus now relies on
10
54
  the MCP host's model, context, consent UI, and agent runtime.
11
- - Replaced the monolithic legacy catalog with 17 focused tools for connection
55
+ - Replaced the monolithic legacy catalog with 16 focused tools for connection
12
56
  management, inspection, transactional changes, UI, scripts, tests, and docs.
13
57
  - Added strict input schemas, output schemas, `structuredContent`, text fallbacks,
14
58
  `isError`, and accurate read-only/destructive/idempotent/open-world annotations.
@@ -20,7 +64,8 @@ Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
20
64
  ### Secure Studio bridge
21
65
 
22
66
  - Bound the WebSocket bridge to `127.0.0.1` instead of every network interface.
23
- - Added a persistent 256-bit bridge token and six-digit first-use pairing flow.
67
+ - Added a persistent 256-bit bridge token that is provisioned directly into the
68
+ local plugin by `dominus-install-plugin`; users do not complete a pairing flow.
24
69
  - Authenticated relay/controller processes as well as Studio plugin connections.
25
70
  - Added one MiB payload limits, socket rate limits, handshake timeouts, bounded
26
71
  request timeouts, and strict message-envelope validation.
@@ -36,7 +81,7 @@ Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
36
81
  same place ID are now safe and independently selectable.
37
82
  - Relay clients carry their selected connection on each request, so separate MCP
38
83
  processes cannot overwrite each other's active target.
39
- - Added `dominus_status`, `dominus_pair_studio`, and `dominus_select_studio`.
84
+ - Added `dominus_status` and `dominus_select_studio`.
40
85
 
41
86
  ### Studio plugin harness
42
87
 
@@ -66,7 +111,7 @@ Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
66
111
  installer implementation.
67
112
  - The published runtime dependency set is MCP-only; old CLI dependencies are
68
113
  development-only and excluded from the MCP bundle.
69
- - Added transport tests for auth, pairing, relays, duplicate places, wrong-target
114
+ - Added transport tests for auth, credential rejection, relays, duplicate places, wrong-target
70
115
  responses, and disconnect isolation.
71
116
  - Added in-memory MCP conformance tests for the tool catalog, structured outputs,
72
117
  resources, prompts, routing, and error results.
@@ -81,95 +126,95 @@ Dominus has been rewritten as an MCP-first Roblox Studio engineering server.
81
126
  `instanceId` and/or `pathSegments`.
82
127
  - Script updates require `expectedRevision` from `studio_read_script`.
83
128
  - Existing UI is not replaced unless `replaceExisting: true` is supplied.
84
-
85
- ## 0.5.5 (2026-04-23)
86
-
87
- ### New MCP Tools (30 total, up from 24)
88
-
89
- - **`move_instance`** — Reparent an instance preserving all descendants. Sets ChangeHistoryService waypoint.
90
- - **`bulk_set_properties`** — Set properties on multiple instances in one call. Explicit paths or className filter mode.
91
- - **`find_replace_scripts`** — Find and replace across all scripts with optional Lua pattern matching and dry run preview.
92
- - **`undo`** — Undo last action (ChangeHistoryService). Like Ctrl+Z.
93
- - **`redo`** — Redo last undone action. Like Ctrl+Y.
94
-
95
- ### Improvements
96
-
97
- - **`get_properties` now supports compact mode** — Same filtering as `get_descendants_properties`. Strips read-only, nil, deprecated, and default-valued properties by default. The previous version returned all ~50 properties per instance even though most were noise.
98
- - **Fixed code injection in `clone_instance`, `group_instances`, `build_multiple`** — Instance names and paths with special characters (quotes, backslashes) no longer break generated Luau code.
99
- - **`build_multiple` now supports `rgb()` colors and `canCollide`** — Previously only handled `#hex` and BrickColor names; `rgb(255,0,0)` would silently fail.
100
- - **Better tool descriptions** — More specific descriptions help AI models pick the right tool. `get_properties` disambiguates vs `serialize_ui` and `get_descendants_properties`.
101
- - **Shared `luaStr()` and `resolvePath()` helpers** — Centralized safe string generation for all code-gen tools.
102
-
103
- ## 0.5.4 (2026-04-23)
104
-
105
- ### Fixes
106
-
107
- - **Rebuilt plugin** — 0.5.3 shipped a stale `Dominus.rbxm` missing the compact property filtering and protocol changes. This release includes the correctly built plugin.
108
-
109
- ## 0.5.3 (2026-04-23)
110
-
111
- ### Improvements
112
-
113
- - **Compact `get_descendants_properties`** — Now strips read-only, nil, deprecated, and default-valued properties by default, reducing response size by ~80%. Pass `compact: false` for the full dump.
114
- - **`serialize_ui` exposed as MCP tool** — Was previously only in the internal tool registry. Now available to all MCP clients (Copilot, Cursor, Claude, etc.) for converting existing Roblox UI into clean JSON trees.
115
- - **UI → Roact/React conversion guidance** — System prompt and internal memory now teach the agent the full workflow: `serialize_ui` → JSON tree → Roact/React component with correct type mappings (UDim2, Color3, Vector2, enums, FontFace).
116
- - **Smarter tool selection** — Agent now prefers `serialize_ui` over `get_descendants_properties` for UI conversion tasks, avoiding context bloat.
117
-
118
- ## 0.2.1 (2026-02-24)
119
-
120
- ### New
121
-
122
- - **Auto-update checker** — On startup, Dominus checks npm for newer versions
123
- and prints an upgrade notice. Cached for 4 hours, non-blocking, never throws.
124
-
125
- ### Bug Fixes
126
-
127
- - **MCP server crashes on EADDRINUSE** — `dominus mcp` would crash if port 18088
128
- was already held by a running `dominus start` (or another MCP instance). The MCP
129
- server now checks for an existing listener and connects as a relay client instead
130
- of trying to bind the same port.
131
-
132
- ### New MCP Tools (22 total, up from 14)
133
-
134
- - **`get_class_info`** — Look up the full schema of any Roblox class via
135
- ReflectionService (properties, methods, events with types).
136
- - **`create_ui`** — Create an entire UI tree in one call with a declarative JSON
137
- spec. Auto-coerces UDim2, Color3, Font, and enums. ~10x faster than `run_code`.
138
- - **`create_part`** — One-call Part creation with position, size, color, material,
139
- shape, transparency, anchored, and collision settings.
140
- - **`clone_instance`** — Clone an instance with optional rename, reparent, and
141
- position offset.
142
- - **`group_instances`** — Group multiple instances into a Model or Folder.
143
- - **`build_multiple`** — Batch-create many parts/instances in a single round-trip.
144
- - **`execute_run_test`** — Enter Run mode via `StudioTestService:ExecuteRunModeAsync()`
145
- with pass-through args and result capture.
146
- - **`execute_play_test`** — Enter Play mode via `StudioTestService:ExecutePlayModeAsync()`
147
- for full client-server simulation tests.
148
-
149
- ### New CLI Features
150
-
151
- - **`dominus mcp-install`** — Auto-install MCP config into Cursor, VS Code,
152
- Claude Desktop, Windsurf, and Claude Code with one command.
153
- - **`dominus mcp-setup`** — Print manual MCP configuration instructions.
154
- - **Relay client architecture** — `dominus start` can now connect as a relay
155
- client to an existing MCP-owned WebSocket server, allowing the TUI and MCP
156
- to coexist on the same port.
157
-
158
- ### Plugin Improvements
159
-
160
- - Enhanced `Properties.lua` with broader property type support.
161
- - Improved `Reflection.lua` for richer class metadata.
162
- - Updated `TestRunner.lua` with Run/Play mode test support.
163
- - New `UIBuilder.lua` module for server-side declarative UI construction.
164
-
165
- ### Other Changes
166
-
167
- - Multi-provider AI config improvements (OpenRouter, xAI, Gemini, custom).
168
- - TUI setup wizard for first-run API key and provider configuration.
169
- - Improved system prompt with builder-tool awareness.
170
- - WebSocket server now supports controller relay connections.
171
-
172
- ## 0.1.0 (2026-02-23)
173
-
174
- - Initial release — 14 MCP tools, TUI agent, persistent memory, Roblox Studio
175
- WebSocket bridge.
129
+
130
+ ## 0.5.5 (2026-04-23)
131
+
132
+ ### New MCP Tools (30 total, up from 24)
133
+
134
+ - **`move_instance`** — Reparent an instance preserving all descendants. Sets ChangeHistoryService waypoint.
135
+ - **`bulk_set_properties`** — Set properties on multiple instances in one call. Explicit paths or className filter mode.
136
+ - **`find_replace_scripts`** — Find and replace across all scripts with optional Lua pattern matching and dry run preview.
137
+ - **`undo`** — Undo last action (ChangeHistoryService). Like Ctrl+Z.
138
+ - **`redo`** — Redo last undone action. Like Ctrl+Y.
139
+
140
+ ### Improvements
141
+
142
+ - **`get_properties` now supports compact mode** — Same filtering as `get_descendants_properties`. Strips read-only, nil, deprecated, and default-valued properties by default. The previous version returned all ~50 properties per instance even though most were noise.
143
+ - **Fixed code injection in `clone_instance`, `group_instances`, `build_multiple`** — Instance names and paths with special characters (quotes, backslashes) no longer break generated Luau code.
144
+ - **`build_multiple` now supports `rgb()` colors and `canCollide`** — Previously only handled `#hex` and BrickColor names; `rgb(255,0,0)` would silently fail.
145
+ - **Better tool descriptions** — More specific descriptions help AI models pick the right tool. `get_properties` disambiguates vs `serialize_ui` and `get_descendants_properties`.
146
+ - **Shared `luaStr()` and `resolvePath()` helpers** — Centralized safe string generation for all code-gen tools.
147
+
148
+ ## 0.5.4 (2026-04-23)
149
+
150
+ ### Fixes
151
+
152
+ - **Rebuilt plugin** — 0.5.3 shipped a stale `Dominus.rbxm` missing the compact property filtering and protocol changes. This release includes the correctly built plugin.
153
+
154
+ ## 0.5.3 (2026-04-23)
155
+
156
+ ### Improvements
157
+
158
+ - **Compact `get_descendants_properties`** — Now strips read-only, nil, deprecated, and default-valued properties by default, reducing response size by ~80%. Pass `compact: false` for the full dump.
159
+ - **`serialize_ui` exposed as MCP tool** — Was previously only in the internal tool registry. Now available to all MCP clients (Copilot, Cursor, Claude, etc.) for converting existing Roblox UI into clean JSON trees.
160
+ - **UI → Roact/React conversion guidance** — System prompt and internal memory now teach the agent the full workflow: `serialize_ui` → JSON tree → Roact/React component with correct type mappings (UDim2, Color3, Vector2, enums, FontFace).
161
+ - **Smarter tool selection** — Agent now prefers `serialize_ui` over `get_descendants_properties` for UI conversion tasks, avoiding context bloat.
162
+
163
+ ## 0.2.1 (2026-02-24)
164
+
165
+ ### New
166
+
167
+ - **Auto-update checker** — On startup, Dominus checks npm for newer versions
168
+ and prints an upgrade notice. Cached for 4 hours, non-blocking, never throws.
169
+
170
+ ### Bug Fixes
171
+
172
+ - **MCP server crashes on EADDRINUSE** — `dominus mcp` would crash if port 18088
173
+ was already held by a running `dominus start` (or another MCP instance). The MCP
174
+ server now checks for an existing listener and connects as a relay client instead
175
+ of trying to bind the same port.
176
+
177
+ ### New MCP Tools (22 total, up from 14)
178
+
179
+ - **`get_class_info`** — Look up the full schema of any Roblox class via
180
+ ReflectionService (properties, methods, events with types).
181
+ - **`create_ui`** — Create an entire UI tree in one call with a declarative JSON
182
+ spec. Auto-coerces UDim2, Color3, Font, and enums. ~10x faster than `run_code`.
183
+ - **`create_part`** — One-call Part creation with position, size, color, material,
184
+ shape, transparency, anchored, and collision settings.
185
+ - **`clone_instance`** — Clone an instance with optional rename, reparent, and
186
+ position offset.
187
+ - **`group_instances`** — Group multiple instances into a Model or Folder.
188
+ - **`build_multiple`** — Batch-create many parts/instances in a single round-trip.
189
+ - **`execute_run_test`** — Enter Run mode via `StudioTestService:ExecuteRunModeAsync()`
190
+ with pass-through args and result capture.
191
+ - **`execute_play_test`** — Enter Play mode via `StudioTestService:ExecutePlayModeAsync()`
192
+ for full client-server simulation tests.
193
+
194
+ ### New CLI Features
195
+
196
+ - **`dominus mcp-install`** — Auto-install MCP config into Cursor, VS Code,
197
+ Claude Desktop, Windsurf, and Claude Code with one command.
198
+ - **`dominus mcp-setup`** — Print manual MCP configuration instructions.
199
+ - **Relay client architecture** — `dominus start` can now connect as a relay
200
+ client to an existing MCP-owned WebSocket server, allowing the TUI and MCP
201
+ to coexist on the same port.
202
+
203
+ ### Plugin Improvements
204
+
205
+ - Enhanced `Properties.lua` with broader property type support.
206
+ - Improved `Reflection.lua` for richer class metadata.
207
+ - Updated `TestRunner.lua` with Run/Play mode test support.
208
+ - New `UIBuilder.lua` module for server-side declarative UI construction.
209
+
210
+ ### Other Changes
211
+
212
+ - Multi-provider AI config improvements (OpenRouter, xAI, Gemini, custom).
213
+ - TUI setup wizard for first-run API key and provider configuration.
214
+ - Improved system prompt with builder-tool awareness.
215
+ - WebSocket server now supports controller relay connections.
216
+
217
+ ## 0.1.0 (2026-02-23)
218
+
219
+ - Initial release — 14 MCP tools, TUI agent, persistent memory, Roblox Studio
220
+ WebSocket bridge.
package/README.md CHANGED
@@ -9,11 +9,13 @@ Luau execution.
9
9
 
10
10
  - MCP-only product: the old Ink chat CLI is no longer published.
11
11
  - 17 focused tools with input/output schemas, structured results, and risk hints.
12
+ - MCP-native parallel coordination uses the host's model through Sampling, with
13
+ proposal-only workers and coordinator-owned Studio writes.
12
14
  - Stable instance refs survive dots in names, renames, reparenting, and duplicate
13
15
  sibling names for the life of the Studio session.
14
16
  - Script writes require a revision from `studio_read_script`.
15
17
  - Mutation batches and UI replacement are undoable and roll back on failure.
16
- - The loopback Studio bridge requires a persistent token and first-use pairing.
18
+ - The installer provisions a private bridge token into the local Studio plugin.
17
19
  - Multiple Studio windows are selected by unique connection ID, not place ID.
18
20
  - Roblox Creator Docs API lookup is built in.
19
21
 
@@ -39,7 +41,8 @@ Install or update the Studio plugin:
39
41
  dominus-install-plugin
40
42
  ```
41
43
 
42
- Restart Roblox Studio after installing the plugin.
44
+ Roblox Studio normally reloads the local plugin automatically. Restart Studio
45
+ only if the Dominus 2 toolbar button does not appear or reconnect.
43
46
 
44
47
  ## MCP configuration
45
48
 
@@ -62,7 +65,7 @@ For a version-pinned configuration that does not require a global install:
62
65
  "mcpServers": {
63
66
  "dominus": {
64
67
  "command": "npx",
65
- "args": ["-y", "-p", "dominus-cli@2.1.0", "dominus-mcp"]
68
+ "args": ["-y", "-p", "dominus-cli@2.2.0", "dominus-mcp"]
66
69
  }
67
70
  }
68
71
  }
@@ -72,12 +75,9 @@ Restart the MCP client after changing its configuration.
72
75
 
73
76
  ## First connection
74
77
 
75
- 1. Start the MCP client so `dominus-mcp` is running.
76
- 2. Open Roblox Studio. The Dominus 2 plugin connects to `127.0.0.1:18088`.
77
- 3. On first use, Studio Output prints a connection ID and six-digit pairing code.
78
- 4. Ask the MCP agent to call `dominus_status`, then `dominus_pair_studio` with
79
- that connection ID and code.
80
- 5. Pairing is saved. Future Studio sessions authenticate automatically.
78
+ 1. Run `dominus-install-plugin`; restart Roblox Studio only if it does not reload.
79
+ 2. Start the MCP client so `dominus-mcp` is running.
80
+ 3. Open Roblox Studio. The plugin connects and authenticates automatically.
81
81
 
82
82
  If several Studio windows are open, call `dominus_select_studio` with the exact
83
83
  connection ID before editing. Two windows on the same place ID remain distinct.
@@ -96,14 +96,24 @@ For UI fidelity work, use `studio_snapshot_ui` after `studio_build_ui` and compa
96
96
  fonts, UDim2 values, colors, strokes, and hierarchy rather than relying on a
97
97
  successful write response alone.
98
98
 
99
+ For broad tasks with independent branches, use `run_parallel_task`. Dominus
100
+ partitions immediate child scopes, supplies each worker with hierarchy and typed
101
+ property evidence, runs up to five Sampling workers concurrently, validates their
102
+ proposals, and applies only the coordinator-approved atomic plan. Workers never
103
+ receive direct write access. Clients without Sampling continue to use the normal
104
+ single-agent workflow.
105
+
99
106
  ## Tool catalog
100
107
 
101
108
  Connection tools:
102
109
 
103
110
  - `dominus_status`
104
- - `dominus_pair_studio`
105
111
  - `dominus_select_studio`
106
112
 
113
+ Parallel coordination:
114
+
115
+ - `run_parallel_task`
116
+
107
117
  Studio inspection:
108
118
 
109
119
  - `studio_get_tree`
@@ -131,9 +141,10 @@ Roblox documentation:
131
141
 
132
142
  The MCP transport is stdio. The separate Studio WebSocket bridge binds only to
133
143
  `127.0.0.1`, limits messages to one MiB, rate-limits sockets, times out incomplete
134
- handshakes, and authenticates both Studio plugins and relay controllers. First-use
135
- pairing requires a code visible in Studio. The plugin only accepts an explicit
136
- command allowlist and contains no `loadstring` executor.
144
+ handshakes, and authenticates both Studio plugins and relay controllers. The local
145
+ installer injects a user-specific token into the plugin, so no manual pairing flow
146
+ is needed. The plugin only accepts an explicit command allowlist and contains no
147
+ `loadstring` executor.
137
148
 
138
149
  The bridge token protects against accidental or opportunistic access by unrelated
139
150
  local processes. Like other user-level credentials, it cannot protect against a
@@ -1,30 +1,77 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'fs';
3
- import os from 'os';
4
- import path from 'path';
2
+ import fs3 from 'fs';
3
+ import os2 from 'os';
4
+ import path3 from 'path';
5
5
  import { fileURLToPath } from 'url';
6
+ import * as crypto from 'crypto';
6
7
 
8
+ var DOMINUS_DIR = path3.join(os2.homedir(), ".dominus");
9
+ path3.join(DOMINUS_DIR, "config.json");
10
+ path3.join(DOMINUS_DIR, "dominus.db");
11
+ path3.join(DOMINUS_DIR, "rules.json");
12
+ function ensureDir() {
13
+ if (!fs3.existsSync(DOMINUS_DIR)) {
14
+ fs3.mkdirSync(DOMINUS_DIR, { recursive: true });
15
+ }
16
+ }
17
+ function getDominusDir() {
18
+ ensureDir();
19
+ return DOMINUS_DIR;
20
+ }
21
+
22
+ // src/transport/auth.ts
23
+ var TOKEN_FILE = "bridge-token";
24
+ function loadOrCreateBridgeToken() {
25
+ const tokenPath = path3.join(getDominusDir(), TOKEN_FILE);
26
+ try {
27
+ const existing = fs3.readFileSync(tokenPath, "utf8").trim();
28
+ if (existing.length >= 32) return existing;
29
+ } catch {
30
+ }
31
+ const token = crypto.randomBytes(32).toString("base64url");
32
+ fs3.writeFileSync(tokenPath, `${token}
33
+ `, { encoding: "utf8", mode: 384 });
34
+ try {
35
+ fs3.chmodSync(tokenPath, 384);
36
+ } catch {
37
+ }
38
+ return token;
39
+ }
40
+
41
+ // src/install-plugin.ts
42
+ var TOKEN_PLACEHOLDER = "__DOMINUS_BRIDGE_TOKEN__";
7
43
  function getPluginDirectory() {
8
44
  if (process.platform === "win32") {
9
45
  const localAppData = process.env.LOCALAPPDATA;
10
46
  if (!localAppData) throw new Error("LOCALAPPDATA is not set");
11
- return path.join(localAppData, "Roblox", "Plugins");
47
+ return path3.join(localAppData, "Roblox", "Plugins");
12
48
  }
13
49
  if (process.platform === "darwin") {
14
- return path.join(os.homedir(), "Documents", "Roblox", "Plugins");
50
+ return path3.join(os2.homedir(), "Documents", "Roblox", "Plugins");
15
51
  }
16
- return path.join(os.homedir(), ".local", "share", "Roblox", "Plugins");
52
+ return path3.join(os2.homedir(), ".local", "share", "Roblox", "Plugins");
17
53
  }
18
- var distDirectory = path.dirname(fileURLToPath(import.meta.url));
19
- var source = path.resolve(distDirectory, "..", "plugin", "Dominus.rbxm");
54
+ var distDirectory = path3.dirname(fileURLToPath(import.meta.url));
55
+ var source = path3.resolve(distDirectory, "..", "plugin", "Dominus.rbxmx");
20
56
  var destinationDirectory = getPluginDirectory();
21
- var destination = path.join(destinationDirectory, "Dominus2.rbxm");
22
- if (!fs.existsSync(source)) {
57
+ var destination = path3.join(destinationDirectory, "Dominus2.rbxmx");
58
+ if (!fs3.existsSync(source)) {
23
59
  throw new Error(`Packaged plugin not found at ${source}. Run pnpm build:plugin first.`);
24
60
  }
25
- fs.mkdirSync(destinationDirectory, { recursive: true });
26
- fs.copyFileSync(source, destination);
61
+ var template = fs3.readFileSync(source, "utf8");
62
+ if (!template.includes(TOKEN_PLACEHOLDER)) {
63
+ throw new Error("Packaged plugin template is missing its bridge token placeholder");
64
+ }
65
+ var personalizedPlugin = template.replaceAll(TOKEN_PLACEHOLDER, loadOrCreateBridgeToken());
66
+ fs3.mkdirSync(destinationDirectory, { recursive: true });
67
+ fs3.writeFileSync(destination, personalizedPlugin, "utf8");
68
+ for (const obsoleteName of ["Dominus2.rbxm", "Dominus.rbxm", "Dominus.rbxmx"]) {
69
+ const obsoletePath = path3.join(destinationDirectory, obsoleteName);
70
+ if (obsoletePath !== destination) fs3.rmSync(obsoletePath, { force: true });
71
+ }
27
72
  console.log(`Installed Dominus 2 Studio plugin to ${destination}`);
28
- console.log("Restart Roblox Studio, start dominus-mcp, then approve the pairing code shown in Studio Output.");
73
+ console.log(
74
+ "Roblox Studio normally reloads Dominus 2 automatically. Restart Studio only if the toolbar does not reconnect."
75
+ );
29
76
  //# sourceMappingURL=install-plugin.js.map
30
77
  //# sourceMappingURL=install-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/install-plugin.ts"],"names":[],"mappings":";;;;;;AAKA,SAAS,kBAAA,GAA6B;AACpC,EAAA,IAAI,OAAA,CAAQ,aAAa,OAAA,EAAS;AAChC,IAAA,MAAM,YAAA,GAAe,QAAQ,GAAA,CAAI,YAAA;AACjC,IAAA,IAAI,CAAC,YAAA,EAAc,MAAM,IAAI,MAAM,yBAAyB,CAAA;AAC5D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,YAAA,EAAc,QAAA,EAAU,SAAS,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,OAAA,CAAQ,aAAa,QAAA,EAAU;AACjC,IAAA,OAAO,KAAK,IAAA,CAAK,EAAA,CAAG,SAAQ,EAAG,WAAA,EAAa,UAAU,SAAS,CAAA;AAAA,EACjE;AACA,EAAA,OAAO,IAAA,CAAK,KAAK,EAAA,CAAG,OAAA,IAAW,QAAA,EAAU,OAAA,EAAS,UAAU,SAAS,CAAA;AACvE;AAEA,IAAM,gBAAgB,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACjE,IAAM,SAAS,IAAA,CAAK,OAAA,CAAQ,aAAA,EAAe,IAAA,EAAM,UAAU,cAAc,CAAA;AACzE,IAAM,uBAAuB,kBAAA,EAAmB;AAChD,IAAM,WAAA,GAAc,IAAA,CAAK,IAAA,CAAK,oBAAA,EAAsB,eAAe,CAAA;AAEnE,IAAI,CAAC,EAAA,CAAG,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,MAAM,CAAA,8BAAA,CAAgC,CAAA;AACxF;AAEA,EAAA,CAAG,SAAA,CAAU,oBAAA,EAAsB,EAAE,SAAA,EAAW,MAAM,CAAA;AACtD,EAAA,CAAG,YAAA,CAAa,QAAQ,WAAW,CAAA;AACnC,OAAA,CAAQ,GAAA,CAAI,CAAA,qCAAA,EAAwC,WAAW,CAAA,CAAE,CAAA;AACjE,OAAA,CAAQ,IAAI,iGAAiG,CAAA","file":"install-plugin.js","sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction getPluginDirectory(): string {\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) throw new Error('LOCALAPPDATA is not set');\n return path.join(localAppData, 'Roblox', 'Plugins');\n }\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Documents', 'Roblox', 'Plugins');\n }\n return path.join(os.homedir(), '.local', 'share', 'Roblox', 'Plugins');\n}\n\nconst distDirectory = path.dirname(fileURLToPath(import.meta.url));\nconst source = path.resolve(distDirectory, '..', 'plugin', 'Dominus.rbxm');\nconst destinationDirectory = getPluginDirectory();\nconst destination = path.join(destinationDirectory, 'Dominus2.rbxm');\n\nif (!fs.existsSync(source)) {\n throw new Error(`Packaged plugin not found at ${source}. Run pnpm build:plugin first.`);\n}\n\nfs.mkdirSync(destinationDirectory, { recursive: true });\nfs.copyFileSync(source, destination);\nconsole.log(`Installed Dominus 2 Studio plugin to ${destination}`);\nconsole.log('Restart Roblox Studio, start dominus-mcp, then approve the pairing code shown in Studio Output.');\n"]}
1
+ {"version":3,"sources":["../src/config/config.ts","../src/transport/auth.ts","../src/install-plugin.ts"],"names":["path","os","fs"],"mappings":"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","file":"install-plugin.js","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { ConfigAccess } from '../tools/types.js';\n\nconst DOMINUS_DIR = path.join(os.homedir(), '.dominus');\nconst CONFIG_PATH = path.join(DOMINUS_DIR, 'config.json');\nconst DB_PATH = path.join(DOMINUS_DIR, 'dominus.db');\nconst RULES_PATH = path.join(DOMINUS_DIR, 'rules.json');\n\nexport type AIProviderType =\n | 'openai'\n | 'anthropic'\n | 'gemini'\n | 'xai'\n | 'openrouter'\n | 'custom';\n\nexport interface ProviderConfig {\n name: string;\n baseUrl: string;\n envVar: string;\n defaultModel: string;\n}\n\nexport const PROVIDERS: Record<AIProviderType, ProviderConfig> = {\n openai: {\n name: 'OpenAI',\n baseUrl: 'https://api.openai.com/v1',\n envVar: 'OPENAI_API_KEY',\n defaultModel: 'gpt-5.3-codex',\n },\n anthropic: {\n name: 'Anthropic',\n baseUrl: 'https://api.anthropic.com/v1/',\n envVar: 'ANTHROPIC_API_KEY',\n defaultModel: 'claude-sonnet-4-6',\n },\n gemini: {\n name: 'Google Gemini',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',\n envVar: 'GEMINI_API_KEY',\n defaultModel: 'gemini-3.1-pro',\n },\n xai: {\n name: 'xAI (Grok)',\n baseUrl: 'https://api.x.ai/v1',\n envVar: 'XAI_API_KEY',\n defaultModel: 'grok-4.2',\n },\n openrouter: {\n name: 'OpenRouter',\n baseUrl: 'https://openrouter.ai/api/v1',\n envVar: 'OPENROUTER_API_KEY',\n defaultModel: 'anthropic/claude-sonnet-4-6',\n },\n custom: {\n name: 'Custom',\n baseUrl: '',\n envVar: '',\n defaultModel: '',\n },\n};\n\nexport interface DominusConfig {\n provider: AIProviderType;\n apiKey?: string;\n apiBaseUrl: string;\n model: string;\n port: number;\n maxToolIterations: number;\n maxVerifyRetries: number;\n tokenBudget: number;\n autoIndex: boolean;\n autoLearn: boolean;\n userName: string;\n openCloudApiKey?: string;\n}\n\nconst DEFAULTS: DominusConfig = {\n provider: 'openai',\n apiBaseUrl: '',\n model: '',\n port: 18088,\n maxToolIterations: 25,\n maxVerifyRetries: 3,\n tokenBudget: 128000,\n autoIndex: true,\n autoLearn: true,\n userName: 'Developer',\n};\n\n/**\n * Auto-detect provider from environment variables.\n * Returns the first provider whose env var is set, or null.\n */\nexport function detectProviderFromEnv(): { provider: AIProviderType; apiKey: string } | null {\n const priority: AIProviderType[] = ['openai', 'anthropic', 'gemini', 'xai', 'openrouter'];\n for (const p of priority) {\n const key = process.env[PROVIDERS[p].envVar];\n if (key) return { provider: p, apiKey: key };\n }\n return null;\n}\n\n/**\n * Resolve the effective base URL and model for a provider config.\n */\nexport function resolveProviderSettings(config: DominusConfig): {\n baseUrl: string;\n model: string;\n} {\n const providerInfo = PROVIDERS[config.provider];\n return {\n baseUrl: config.apiBaseUrl || providerInfo?.baseUrl || PROVIDERS.openrouter.baseUrl,\n model: config.model || providerInfo?.defaultModel || 'gpt-4o',\n };\n}\n\nexport interface DominusRules {\n do: string[];\n dont: string[];\n conventions: string[];\n context: string;\n}\n\nconst DEFAULT_RULES: DominusRules = {\n do: [],\n dont: [],\n conventions: [],\n context: '',\n};\n\nfunction ensureDir(): void {\n if (!fs.existsSync(DOMINUS_DIR)) {\n fs.mkdirSync(DOMINUS_DIR, { recursive: true });\n }\n}\n\nfunction readJson<T>(filePath: string, defaults: T): T {\n try {\n if (fs.existsSync(filePath)) {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return { ...defaults, ...JSON.parse(raw) };\n }\n } catch {\n // Corrupted file; reset to defaults\n }\n return { ...defaults };\n}\n\nfunction writeJson(filePath: string, data: unknown): void {\n ensureDir();\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\nexport function loadConfig(): DominusConfig {\n ensureDir();\n return readJson(CONFIG_PATH, DEFAULTS);\n}\n\nexport function saveConfig(config: DominusConfig): void {\n writeJson(CONFIG_PATH, config);\n}\n\nexport function getConfigValue<K extends keyof DominusConfig>(key: K): DominusConfig[K] {\n const config = loadConfig();\n return config[key];\n}\n\nexport function setConfigValue<K extends keyof DominusConfig>(\n key: K,\n value: DominusConfig[K],\n): void {\n const config = loadConfig();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (config as any)[key] = value;\n saveConfig(config);\n}\n\nexport function loadRules(): DominusRules {\n ensureDir();\n return readJson(RULES_PATH, DEFAULT_RULES);\n}\n\nexport function saveRules(rules: DominusRules): void {\n writeJson(RULES_PATH, rules);\n}\n\nexport function getDominusDir(): string {\n ensureDir();\n return DOMINUS_DIR;\n}\n\nexport function getDbPath(): string {\n ensureDir();\n return DB_PATH;\n}\n\nexport function createConfigAccess(): ConfigAccess {\n return {\n get<T>(key: string): T | undefined {\n const config = loadConfig() as unknown as Record<string, unknown>;\n return config[key] as T | undefined;\n },\n set(key: string, value: unknown): void {\n const config = loadConfig() as unknown as Record<string, unknown>;\n config[key] = value;\n saveConfig(config as unknown as DominusConfig);\n },\n getAll(): Record<string, unknown> {\n return loadConfig() as unknown as Record<string, unknown>;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport * as crypto from 'node:crypto';\nimport { getDominusDir } from '../config/config.js';\n\nconst TOKEN_FILE = 'bridge-token';\n\nexport function loadOrCreateBridgeToken(): string {\n const tokenPath = path.join(getDominusDir(), TOKEN_FILE);\n try {\n const existing = fs.readFileSync(tokenPath, 'utf8').trim();\n if (existing.length >= 32) return existing;\n } catch {\n // First run or an unreadable token is repaired below.\n }\n\n const token = crypto.randomBytes(32).toString('base64url');\n fs.writeFileSync(tokenPath, `${token}\\n`, { encoding: 'utf8', mode: 0o600 });\n try {\n fs.chmodSync(tokenPath, 0o600);\n } catch {\n // Windows ACLs do not map directly to POSIX modes.\n }\n return token;\n}\n\nexport function tokensEqual(actual: unknown, expected: string): boolean {\n if (typeof actual !== 'string') return false;\n const actualBuffer = Buffer.from(actual);\n const expectedBuffer = Buffer.from(expected);\n return actualBuffer.length === expectedBuffer.length\n && crypto.timingSafeEqual(actualBuffer, expectedBuffer);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { loadOrCreateBridgeToken } from './transport/auth.js';\n\nconst TOKEN_PLACEHOLDER = '__DOMINUS_BRIDGE_TOKEN__';\n\nfunction getPluginDirectory(): string {\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) throw new Error('LOCALAPPDATA is not set');\n return path.join(localAppData, 'Roblox', 'Plugins');\n }\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Documents', 'Roblox', 'Plugins');\n }\n return path.join(os.homedir(), '.local', 'share', 'Roblox', 'Plugins');\n}\n\nconst distDirectory = path.dirname(fileURLToPath(import.meta.url));\nconst source = path.resolve(distDirectory, '..', 'plugin', 'Dominus.rbxmx');\nconst destinationDirectory = getPluginDirectory();\nconst destination = path.join(destinationDirectory, 'Dominus2.rbxmx');\n\nif (!fs.existsSync(source)) {\n throw new Error(`Packaged plugin not found at ${source}. Run pnpm build:plugin first.`);\n}\n\nconst template = fs.readFileSync(source, 'utf8');\nif (!template.includes(TOKEN_PLACEHOLDER)) {\n throw new Error('Packaged plugin template is missing its bridge token placeholder');\n}\nconst personalizedPlugin = template.replaceAll(TOKEN_PLACEHOLDER, loadOrCreateBridgeToken());\n\nfs.mkdirSync(destinationDirectory, { recursive: true });\nfs.writeFileSync(destination, personalizedPlugin, 'utf8');\nfor (const obsoleteName of ['Dominus2.rbxm', 'Dominus.rbxm', 'Dominus.rbxmx']) {\n const obsoletePath = path.join(destinationDirectory, obsoleteName);\n if (obsoletePath !== destination) fs.rmSync(obsoletePath, { force: true });\n}\nconsole.log(`Installed Dominus 2 Studio plugin to ${destination}`);\nconsole.log(\n 'Roblox Studio normally reloads Dominus 2 automatically. Restart Studio only if the toolbar does not reconnect.',\n);\n"]}