dominus-cli 2.2.1 → 2.4.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 +58 -0
- package/README.md +32 -3
- package/dist/bridge-daemon.js.map +1 -1
- package/dist/install-plugin.js.map +1 -1
- package/dist/mcp.js +658 -20
- package/dist/mcp.js.map +1 -1
- package/package.json +5 -5
- package/plugin/Dominus.rbxmx +1064 -12
- package/plugin/src/Building.lua +427 -0
- package/plugin/src/CommandRouter.lua +41 -0
- package/plugin/src/Metadata.lua +411 -0
- package/plugin/src/Mutation.lua +45 -0
- package/plugin/src/ValueCodec.lua +116 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/config.ts","../src/transport/protocol.ts","../src/transport/auth.ts","../src/transport/ws-client.ts","../src/transport/daemon-launcher.ts","../src/install-plugin.ts"],"names":["path","os","fs","fileURLToPath"],"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 { nanoid } from 'nanoid';\nimport { z } from 'zod';\n\nexport const DOMINUS_PROTOCOL_VERSION = 2;\nexport const MAX_MESSAGE_BYTES = 1024 * 1024;\n\nexport interface WsMessage<T = unknown> {\n id: string;\n type: string;\n payload: T;\n ts: number;\n}\n\nexport const StudioMsg = {\n HELLO: 'studio:hello',\n CONNECTED: 'studio:connected',\n DISCONNECTED: 'studio:disconnected',\n EXPLORER: 'studio:explorer',\n OUTPUT: 'studio:output',\n ERROR: 'studio:error',\n SELECTION: 'studio:selection',\n SCRIPT_CONTENT: 'studio:script:content',\n SCRIPT_CHANGED: 'studio:script:changed',\n RESPONSE: 'studio:response',\n REFLECTION: 'studio:reflection',\n TEST_RESULTS: 'studio:test:results',\n AUTHENTICATED: 'server:authenticated',\n AUTH_REJECTED: 'server:auth_rejected',\n} as const;\n\nexport const ControllerMsg = {\n HELLO: 'controller:hello',\n WELCOME: 'controller:welcome',\n RELAY: 'controller:relay',\n TARGET_STATE: 'controller:target_state',\n SET_ACTIVE_CONNECTION: 'controller:set_active_connection',\n} as const;\n\nexport const CliMsg = {\n GET_EXPLORER: 'cli:get:explorer',\n GET_SCRIPT: 'cli:get:script',\n SET_SCRIPT: 'cli:set:script',\n RUN_CODE: 'cli:run',\n GET_PROPERTIES: 'cli:get:properties',\n GET_DESCENDANTS_PROPERTIES: 'cli:get:descendants:properties',\n SET_PROPERTIES: 'cli:set:properties',\n INSERT_INSTANCE: 'cli:insert',\n DELETE_INSTANCE: 'cli:delete',\n GET_SELECTION: 'cli:get:selection',\n SELECT: 'cli:select',\n SEARCH_SCRIPTS: 'cli:search:scripts',\n GET_OUTPUT: 'cli:get:output',\n RUN_TESTS: 'cli:run:tests',\n EXECUTE_RUN_TEST: 'cli:test:run',\n EXECUTE_PLAY_TEST: 'cli:test:play',\n END_TEST: 'cli:test:end',\n BUILD_UI: 'cli:build:ui',\n GET_REFLECTION: 'cli:get:reflection',\n UPLOAD_ASSET: 'cli:upload:asset',\n SERIALIZE_UI: 'cli:serialize:ui',\n MOVE_INSTANCE: 'cli:move:instance',\n UNDO: 'cli:undo',\n REDO: 'cli:redo',\n BULK_SET_PROPERTIES: 'cli:bulk:set:properties',\n FIND_REPLACE_SCRIPTS: 'cli:find:replace:scripts',\n V2_GET_TREE: 'studio:v2:get_tree',\n V2_INSPECT: 'studio:v2:inspect',\n V2_GET_SELECTION: 'studio:v2:get_selection',\n V2_READ_SCRIPT: 'studio:v2:read_script',\n V2_UPDATE_SCRIPT: 'studio:v2:update_script',\n V2_APPLY: 'studio:v2:apply',\n V2_DELETE: 'studio:v2:delete',\n V2_BUILD_UI: 'studio:v2:build_ui',\n V2_SNAPSHOT_UI: 'studio:v2:snapshot_ui',\n V2_RUN_TEST: 'studio:v2:run_test',\n V2_GET_OUTPUT: 'studio:v2:get_output',\n V2_GET_REFLECTION: 'studio:v2:get_reflection',\n} as const;\n\nexport type StudioMsgType = (typeof StudioMsg)[keyof typeof StudioMsg];\nexport type CliMsgType = (typeof CliMsg)[keyof typeof CliMsg];\n\nexport interface ExplorerNode {\n name: string;\n className: string;\n path: string;\n ref?: InstanceRef;\n children?: ExplorerNode[];\n}\n\nexport interface InstanceRef {\n instanceId?: string;\n pathSegments?: string[];\n}\n\nexport interface StudioConnectedPayload {\n studioVersion: string;\n protocolVersion?: number;\n clientId?: string;\n token?: string;\n connectionId?: string;\n placeId?: number;\n placeName?: string;\n engineVersion?: string;\n}\n\nexport interface ScriptContentPayload {\n path: string;\n className: 'Script' | 'LocalScript' | 'ModuleScript';\n source: string;\n lineCount: number;\n}\n\nexport interface ScriptChangedPayload {\n path: string;\n source: string;\n}\n\nexport interface OutputPayload {\n message: string;\n level: 'info' | 'warn' | 'error';\n timestamp: number;\n source?: string;\n}\n\nexport interface SelectionPayload {\n paths: string[];\n classNames: string[];\n}\n\nexport interface PropertyEntry {\n name: string;\n value: string;\n type: string;\n category?: string;\n}\n\nexport interface TestResultPayload {\n name: string;\n passed: boolean;\n duration: number;\n error?: string;\n}\n\nexport interface StudioTestPayload {\n args?: unknown;\n}\n\nexport interface StudioTestResultPayload {\n success: boolean;\n result?: unknown;\n error?: string;\n duration?: number;\n}\n\nexport interface ReflectionPayload {\n className: string;\n properties: { name: string; type: string; category: string }[];\n methods: { name: string; parameters: string[] }[];\n events: { name: string; parameters: string[] }[];\n}\n\nexport interface RunCodePayload {\n code: string;\n}\n\nexport interface RunCodeResponsePayload {\n output: string;\n error?: string;\n duration: number;\n}\n\nexport interface SearchScriptsPayload {\n query: string;\n maxResults?: number;\n}\n\nexport interface SearchResultPayload {\n results: {\n path: string;\n className: string;\n matches: { line: number; text: string }[];\n }[];\n}\n\nexport interface InsertInstancePayload {\n className: string;\n parentPath: string;\n name: string;\n properties?: Record<string, unknown>;\n}\n\nexport interface SetPropertiesPayload {\n path: string;\n properties: Record<string, unknown>;\n}\n\nexport function createMessage<T>(type: string, payload: T): WsMessage<T> {\n return { id: nanoid(), type, payload, ts: Date.now() };\n}\n\nexport function createRequest<T>(type: string, payload: T): WsMessage<T> {\n return createMessage(type, payload);\n}\n\nexport function parseMessage(raw: string): WsMessage {\n if (Buffer.byteLength(raw, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`WebSocket message exceeds ${MAX_MESSAGE_BYTES} bytes`);\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n return wsMessageSchema.parse(parsed) as WsMessage;\n } catch {\n throw new Error(`Failed to parse WebSocket message: ${raw.slice(0, 100)}`);\n }\n}\n\nexport function serializeMessage(msg: WsMessage): string {\n const serialized = JSON.stringify(msg);\n if (Buffer.byteLength(serialized, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`WebSocket message exceeds ${MAX_MESSAGE_BYTES} bytes`);\n }\n return serialized;\n}\n\nconst wsMessageSchema = z\n .object({\n id: z.string().min(1).max(128),\n type: z.string().min(1).max(128),\n payload: z.unknown(),\n ts: z.number().finite(),\n })\n .strict();\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 (\n actualBuffer.length === expectedBuffer.length &&\n crypto.timingSafeEqual(actualBuffer, expectedBuffer)\n );\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport {\n ControllerMsg,\n DOMINUS_PROTOCOL_VERSION,\n StudioMsg,\n createMessage,\n parseMessage,\n serializeMessage,\n type WsMessage,\n} from './protocol.js';\nimport { loadOrCreateBridgeToken } from './auth.js';\nimport type { PlaceInfo, StudioBridge, StudioConnection } from './ws-server.js';\n\ninterface ClientOptions {\n token?: string;\n connectTimeoutMs?: number;\n}\n\ninterface ControlResult {\n success?: boolean;\n error?: string;\n [key: string]: unknown;\n}\n\nexport class DominusClient extends EventEmitter implements StudioBridge {\n private ws: WebSocket | null = null;\n private readonly port: number;\n private readonly token: string;\n private readonly connectTimeoutMs: number;\n private readonly connections = new Map<string, Omit<StudioConnection, 'ws'>>();\n private activeConnectionId: string | null = null;\n private readonly pendingRequests = new Map<\n string,\n {\n resolve: (msg: WsMessage) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n constructor(port = 18088, options: ClientOptions = {}) {\n super();\n this.port = port;\n this.token = options.token ?? loadOrCreateBridgeToken();\n this.connectTimeoutMs = options.connectTimeoutMs ?? 8_000;\n }\n\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(`ws://127.0.0.1:${this.port}`, {\n perMessageDeflate: false,\n maxPayload: 1024 * 1024,\n });\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n ws.close(1008, 'Dominus controller handshake timed out');\n reject(new Error('Timed out authenticating with the Dominus bridge'));\n }\n }, this.connectTimeoutMs);\n\n ws.on('open', () => {\n this.ws = ws;\n ws.send(\n serializeMessage(\n createMessage(ControllerMsg.HELLO, {\n protocolVersion: DOMINUS_PROTOCOL_VERSION,\n token: this.token,\n }),\n ),\n );\n });\n\n ws.on('message', (raw) => {\n try {\n const msg = parseMessage(raw.toString());\n if (msg.type === ControllerMsg.WELCOME && !settled) {\n settled = true;\n clearTimeout(timer);\n this.applyWelcome(msg.payload);\n this.emit('server:started', { port: this.port, relay: true });\n resolve();\n return;\n }\n if (msg.type === StudioMsg.AUTH_REJECTED && !settled) {\n settled = true;\n clearTimeout(timer);\n const payload = msg.payload as { error?: string };\n reject(new Error(payload.error ?? 'Dominus controller authentication failed'));\n ws.close(1008, 'Controller authentication failed');\n return;\n }\n this.handleMessage(msg);\n } catch (err) {\n this.emit('server:error', err);\n }\n });\n\n ws.on('close', () => {\n clearTimeout(timer);\n this.ws = null;\n this.connections.clear();\n this.activeConnectionId = null;\n if (!settled) {\n settled = true;\n reject(new Error('Dominus bridge closed before authentication completed'));\n }\n this.rejectPending(new Error('Dominus relay connection lost'));\n this.emit('studio:disconnected');\n });\n\n ws.on('error', (err) => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n reject(err);\n }\n this.emit('server:error', err);\n });\n });\n }\n\n private applyWelcome(payload: unknown): void {\n const welcome = payload as {\n connections?: PlaceInfo[];\n activeConnectionId?: string | null;\n };\n this.replaceConnections(welcome.connections ?? []);\n this.activeConnectionId = welcome.activeConnectionId ?? null;\n if (!this.resolveTargetInfo() && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n }\n\n private handleMessage(msg: WsMessage): void {\n if (msg.type === ControllerMsg.TARGET_STATE) {\n const payload = msg.payload as {\n connections?: PlaceInfo[];\n defaultActiveConnectionId?: string | null;\n };\n this.replaceConnections(payload.connections ?? []);\n if (this.activeConnectionId && !this.connections.has(this.activeConnectionId)) {\n this.activeConnectionId = null;\n }\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n this.emit('controller:target_state', payload);\n return;\n }\n\n if (msg.type === StudioMsg.CONNECTED) {\n const connection = msg.payload as PlaceInfo;\n this.connections.set(connection.connectionId, toConnectionInfo(connection));\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = connection.connectionId;\n }\n this.emit('studio:connected', connection);\n return;\n }\n\n if (msg.type === StudioMsg.DISCONNECTED) {\n const payload = msg.payload as { connectionId?: string };\n if (payload.connectionId) this.connections.delete(payload.connectionId);\n if (this.activeConnectionId === payload.connectionId) this.activeConnectionId = null;\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n this.emit('studio:disconnected', payload);\n return;\n }\n\n if (msg.type === StudioMsg.RESPONSE) {\n const pending = this.pendingRequests.get(msg.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(msg.id);\n pending.resolve(msg);\n }\n return;\n }\n\n this.emit(msg.type, msg.payload);\n this.emit('message', msg);\n }\n\n private replaceConnections(connections: PlaceInfo[]): void {\n this.connections.clear();\n for (const connection of connections) {\n this.connections.set(connection.connectionId, toConnectionInfo(connection));\n }\n }\n\n private sendMessage(message: WsMessage): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to the Dominus bridge');\n }\n this.ws.send(serializeMessage(message));\n }\n\n private waitForResponse<T = unknown>(\n message: WsMessage,\n timeoutMs: number,\n ): Promise<WsMessage<T>> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(message.id);\n reject(new Error(`Dominus request timed out: ${message.type}`));\n }, timeoutMs);\n this.pendingRequests.set(message.id, {\n resolve: resolve as (msg: WsMessage) => void,\n reject,\n timer,\n });\n try {\n this.sendMessage(message);\n } catch (err) {\n clearTimeout(timer);\n this.pendingRequests.delete(message.id);\n reject(err);\n }\n });\n }\n\n private async sendControlRequest<T extends ControlResult>(\n type: string,\n payload: unknown,\n timeoutMs = 10_000,\n ): Promise<T> {\n const response = await this.waitForResponse<T>(createMessage(type, payload), timeoutMs);\n const result = response.payload;\n if (result.error || result.success === false) throw new Error(result.error ?? `${type} failed`);\n return result;\n }\n\n sendRequest<T = unknown>(\n type: string,\n payload: unknown,\n timeoutMs = 15_000,\n ): Promise<WsMessage<T>> {\n const target = this.resolveTargetInfo();\n if (!target || !this.activeConnectionId) {\n return Promise.reject(\n new Error(\n this.connections.size > 1\n ? 'Multiple Studio sessions are connected. Call dominus_select_studio first.'\n : 'No authenticated Roblox Studio connection',\n ),\n );\n }\n const request = createMessage(type, payload);\n const relay = createMessage(ControllerMsg.RELAY, {\n connectionId: this.activeConnectionId,\n request,\n });\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(request.id);\n reject(new Error(`Studio request timed out: ${type}`));\n }, timeoutMs);\n this.pendingRequests.set(request.id, {\n resolve: resolve as (msg: WsMessage) => void,\n reject,\n timer,\n });\n try {\n this.sendMessage(relay);\n } catch (err) {\n clearTimeout(timer);\n this.pendingRequests.delete(request.id);\n reject(err);\n }\n });\n }\n\n sendNotification(type: string, payload: unknown): void {\n if (!this.activeConnectionId) return;\n const request = createMessage(type, payload);\n this.sendMessage(\n createMessage(ControllerMsg.RELAY, {\n connectionId: this.activeConnectionId,\n request,\n }),\n );\n }\n\n isConnected(): boolean {\n return this.resolveTargetInfo() !== null;\n }\n\n getConnectionInfo(): StudioConnection | null {\n const target = this.resolveTargetInfo();\n if (!target || !this.ws) return null;\n return { ...target, ws: this.ws };\n }\n\n listConnections(): PlaceInfo[] {\n return [...this.connections.values()].map((connection) => ({\n placeId: connection.placeId ?? 0,\n connectionId: connection.connectionId,\n clientId: connection.clientId,\n placeName: connection.placeName,\n studioVersion: connection.studioVersion,\n connectedAt: connection.connectedAt,\n active: connection.connectionId === this.activeConnectionId,\n }));\n }\n\n getActiveConnectionId(): string | null {\n return this.activeConnectionId;\n }\n\n async setActiveConnectionId(connectionId: string | null): Promise<void> {\n if (connectionId && !this.connections.has(connectionId)) {\n throw new Error(`Studio connection ${connectionId} is not connected`);\n }\n await this.sendControlRequest(ControllerMsg.SET_ACTIVE_CONNECTION, { connectionId });\n this.activeConnectionId = connectionId;\n }\n\n getActivePlaceId(): number {\n return this.resolveTargetInfo()?.placeId ?? 0;\n }\n\n setActivePlaceId(placeId: number): void {\n if (placeId === 0) {\n void this.setActiveConnectionId(null);\n return;\n }\n const matches = [...this.connections.values()].filter(\n (connection) => connection.placeId === placeId,\n );\n if (matches.length !== 1) {\n throw new Error(\n matches.length === 0\n ? `Place ${placeId} is not connected`\n : `Place ${placeId} has multiple Studio sessions; select a connectionId instead`,\n );\n }\n void this.setActiveConnectionId(matches[0].connectionId);\n }\n\n private resolveTargetInfo(): Omit<StudioConnection, 'ws'> | null {\n if (this.activeConnectionId) return this.connections.get(this.activeConnectionId) ?? null;\n if (this.connections.size === 1) return this.connections.values().next().value ?? null;\n return null;\n }\n\n getPort(): number {\n return this.port;\n }\n\n stop(): Promise<void> {\n this.rejectPending(new Error('Dominus client is shutting down'));\n return new Promise((resolve) => {\n if (!this.ws) {\n resolve();\n return;\n }\n const ws = this.ws;\n this.ws = null;\n ws.once('close', resolve);\n ws.close(1000, 'Dominus client shutdown');\n });\n }\n\n private rejectPending(error: Error): void {\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pendingRequests.clear();\n }\n}\n\nfunction toConnectionInfo(connection: PlaceInfo): Omit<StudioConnection, 'ws'> {\n return {\n connectedAt: connection.connectedAt,\n connectionId: connection.connectionId,\n clientId: connection.clientId,\n studioVersion: connection.studioVersion,\n placeId: connection.placeId,\n placeName: connection.placeName,\n };\n}\n\nexport async function isPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const ws = new WebSocket(`ws://127.0.0.1:${port}`);\n const timer = setTimeout(() => {\n ws.close();\n resolve(false);\n }, 750);\n ws.on('open', () => {\n clearTimeout(timer);\n ws.close();\n resolve(true);\n });\n ws.on('error', () => {\n clearTimeout(timer);\n resolve(false);\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { DominusClient, isPortInUse } from './ws-client.js';\n\nexport interface BridgeLaunchOptions {\n attempts?: number;\n connectTimeoutMs?: number;\n daemonPath?: string;\n launch?: (port: number) => void;\n retryDelayMs?: number;\n token?: string;\n}\n\nfunction resolveDaemonPath(): string {\n const besideEntry = fileURLToPath(new URL('./bridge-daemon.js', import.meta.url));\n if (fs.existsSync(besideEntry)) return besideEntry;\n\n const projectBuild = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n '../../dist/bridge-daemon.js',\n );\n if (fs.existsSync(projectBuild)) return projectBuild;\n\n throw new Error('Dominus bridge daemon is missing. Reinstall or rebuild dominus-cli.');\n}\n\nexport function launchBridgeDaemon(port: number, daemonPath = resolveDaemonPath()): void {\n const child = spawn(process.execPath, [daemonPath], {\n detached: true,\n env: { ...process.env, DOMINUS_BRIDGE_PORT: String(port) },\n stdio: 'ignore',\n windowsHide: true,\n });\n child.unref();\n}\n\nasync function connectController(\n port: number,\n timeoutMs: number,\n token?: string,\n): Promise<DominusClient> {\n const client = new DominusClient(port, { connectTimeoutMs: timeoutMs, token });\n try {\n await client.connect();\n return client;\n } catch (error) {\n await client.stop().catch(() => undefined);\n throw error;\n }\n}\n\nexport async function ensureBridgeDaemon(\n port: number,\n options: BridgeLaunchOptions = {},\n): Promise<DominusClient> {\n const attempts = options.attempts ?? 30;\n const connectTimeoutMs = options.connectTimeoutMs ?? 750;\n const retryDelayMs = options.retryDelayMs ?? 100;\n\n try {\n return await connectController(port, connectTimeoutMs, options.token);\n } catch (initialError) {\n if (await isPortInUse(port)) {\n throw new Error(\n `Port ${port} is occupied by a process that is not this Dominus bridge: ${formatError(initialError)}`,\n );\n }\n }\n\n const launch =\n options.launch ?? ((targetPort) => launchBridgeDaemon(targetPort, options.daemonPath));\n launch(port);\n\n let lastError: unknown;\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n try {\n return await connectController(port, connectTimeoutMs, options.token);\n } catch (error) {\n lastError = error;\n }\n }\n\n throw new Error(\n `Dominus could not start its local Studio bridge on port ${port}: ${formatError(lastError)}`,\n );\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error ?? 'unknown error');\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { loadConfig } from './config/config.js';\nimport { ensureBridgeDaemon } from './transport/daemon-launcher.js';\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 'Dominus 2 authenticates automatically. Restart Studio only if the toolbar does not reconnect.',\n);\n\nconst bridge = await ensureBridgeDaemon(loadConfig().port);\nawait bridge.stop();\nconsole.log('Dominus Studio bridge is running in the background.');\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/config/config.ts","../src/transport/protocol.ts","../src/transport/auth.ts","../src/transport/ws-client.ts","../src/transport/daemon-launcher.ts","../src/install-plugin.ts"],"names":["path","os","fs","fileURLToPath"],"mappings":";;;;;;;;;;;;AAKA,IAAM,cAAcA,KAAA,CAAK,IAAA,CAAKC,GAAA,CAAG,OAAA,IAAW,UAAU,CAAA;AACtD,IAAM,WAAA,GAAcD,KAAA,CAAK,IAAA,CAAK,WAAA,EAAa,aAAa,CAAA;AACxCA,KAAA,CAAK,IAAA,CAAK,WAAA,EAAa,YAAY;AAChCA,KAAA,CAAK,IAAA,CAAK,WAAA,EAAa,YAAY;AAuEtD,IAAM,QAAA,GAA0B;AAAA,EAC9B,QAAA,EAAU,QAAA;AAAA,EACV,UAAA,EAAY,EAAA;AAAA,EACZ,KAAA,EAAO,EAAA;AAAA,EACP,IAAA,EAAM,KAAA;AAAA,EACN,iBAAA,EAAmB,EAAA;AAAA,EACnB,gBAAA,EAAkB,CAAA;AAAA,EAClB,WAAA,EAAa,KAAA;AAAA,EACb,SAAA,EAAW,IAAA;AAAA,EACX,SAAA,EAAW,IAAA;AAAA,EACX,QAAA,EAAU;AACZ,CAAA;AA2CA,SAAS,SAAA,GAAkB;AACzB,EAAA,IAAI,CAACE,GAAA,CAAG,UAAA,CAAW,WAAW,CAAA,EAAG;AAC/B,IAAAA,GAAA,CAAG,SAAA,CAAU,WAAA,EAAa,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,EAC/C;AACF;AAEA,SAAS,QAAA,CAAY,UAAkB,QAAA,EAAgB;AACrD,EAAA,IAAI;AACF,IAAA,IAAIA,GAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC3B,MAAA,MAAM,GAAA,GAAMA,GAAA,CAAG,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AAC7C,MAAA,OAAO,EAAE,GAAG,QAAA,EAAU,GAAG,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAE;AAAA,IAC3C;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAE,GAAG,QAAA,EAAS;AACvB;AAOO,SAAS,UAAA,GAA4B;AAC1C,EAAA,SAAA,EAAU;AACV,EAAA,OAAO,QAAA,CAAS,aAAa,QAAQ,CAAA;AACvC;AA8BO,SAAS,aAAA,GAAwB;AACtC,EAAA,SAAA,EAAU;AACV,EAAA,OAAO,WAAA;AACT;AC7LO,IAAM,wBAAA,GAA2B,CAAA;AACjC,IAAM,oBAAoB,IAAA,GAAO,IAAA;AASjC,IAAM,SAAA,GAAY;AAAA,EACvB,KAAA,EAAO,cAAA;AAAA,EACP,SAAA,EAAW,kBAAA;AAAA,EACX,YAAA,EAAc,qBAAA;AAAA,EACd,QAAA,EAAU,iBAAA;AAAA,EACV,MAAA,EAAQ,eAAA;AAAA,EACR,KAAA,EAAO,cAAA;AAAA,EACP,SAAA,EAAW,kBAAA;AAAA,EACX,cAAA,EAAgB,uBAAA;AAAA,EAChB,cAAA,EAAgB,uBAAA;AAAA,EAChB,QAAA,EAAU,iBAAA;AAAA,EACV,UAAA,EAAY,mBAAA;AAAA,EACZ,YAAA,EAAc,qBAAA;AAAA,EACd,aAAA,EAAe,sBAAA;AAAA,EACf,aAAA,EAAe;AACjB,CAAA;AAEO,IAAM,aAAA,GAAgB;AAAA,EAC3B,KAAA,EAAO,kBAAA;AAAA,EACP,OAAA,EAAS,oBAAA;AAAA,EACT,KAAA,EAAO,kBAAA;AAAA,EACP,YAAA,EAAc,yBAAA;AAAA,EACd,qBAAA,EAAuB;AACzB,CAAA;AA8KO,SAAS,aAAA,CAAiB,MAAc,OAAA,EAA0B;AACvE,EAAA,OAAO,EAAE,IAAI,MAAA,EAAO,EAAG,MAAM,OAAA,EAAS,EAAA,EAAI,IAAA,CAAK,GAAA,EAAI,EAAE;AACvD;AAMO,SAAS,aAAa,GAAA,EAAwB;AACnD,EAAA,IAAI,MAAA,CAAO,UAAA,CAAW,GAAA,EAAK,MAAM,IAAI,iBAAA,EAAmB;AACtD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,iBAAiB,CAAA,MAAA,CAAQ,CAAA;AAAA,EACxE;AACA,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,OAAO,eAAA,CAAgB,MAAM,MAAM,CAAA;AAAA,EACrC,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,MAAM,CAAA,mCAAA,EAAsC,GAAA,CAAI,MAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,EAC3E;AACF;AAEO,SAAS,iBAAiB,GAAA,EAAwB;AACvD,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA;AACrC,EAAA,IAAI,MAAA,CAAO,UAAA,CAAW,UAAA,EAAY,MAAM,IAAI,iBAAA,EAAmB;AAC7D,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,iBAAiB,CAAA,MAAA,CAAQ,CAAA;AAAA,EACxE;AACA,EAAA,OAAO,UAAA;AACT;AAEA,IAAM,eAAA,GAAkB,EACrB,MAAA,CAAO;AAAA,EACN,EAAA,EAAI,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,IAAI,GAAG,CAAA;AAAA,EAC7B,IAAA,EAAM,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,IAAI,GAAG,CAAA;AAAA,EAC/B,OAAA,EAAS,EAAE,OAAA,EAAQ;AAAA,EACnB,EAAA,EAAI,CAAA,CAAE,MAAA,EAAO,CAAE,MAAA;AACjB,CAAC,EACA,MAAA,EAAO;AChPV,IAAM,UAAA,GAAa,cAAA;AAEZ,SAAS,uBAAA,GAAkC;AAChD,EAAA,MAAM,SAAA,GAAYF,KAAAA,CAAK,IAAA,CAAK,aAAA,IAAiB,UAAU,CAAA;AACvD,EAAA,IAAI;AACF,IAAA,MAAM,WAAWE,GAAAA,CAAG,YAAA,CAAa,SAAA,EAAW,MAAM,EAAE,IAAA,EAAK;AACzD,IAAA,IAAI,QAAA,CAAS,MAAA,IAAU,EAAA,EAAI,OAAO,QAAA;AAAA,EACpC,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,MAAM,KAAA,GAAe,MAAA,CAAA,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,WAAW,CAAA;AACzD,EAAAA,GAAAA,CAAG,aAAA,CAAc,SAAA,EAAW,CAAA,EAAG,KAAK;AAAA,CAAA,EAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,IAAA,EAAM,KAAO,CAAA;AAC3E,EAAA,IAAI;AACF,IAAAA,GAAAA,CAAG,SAAA,CAAU,SAAA,EAAW,GAAK,CAAA;AAAA,EAC/B,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,aAAA,GAAN,cAA4B,YAAA,CAAqC;AAAA,EAC9D,EAAA,GAAuB,IAAA;AAAA,EACd,IAAA;AAAA,EACA,KAAA;AAAA,EACA,gBAAA;AAAA,EACA,WAAA,uBAAkB,GAAA,EAA0C;AAAA,EACrE,kBAAA,GAAoC,IAAA;AAAA,EAC3B,eAAA,uBAAsB,GAAA,EAOrC;AAAA,EAEF,WAAA,CAAY,IAAA,GAAO,KAAA,EAAO,OAAA,GAAyB,EAAC,EAAG;AACrD,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,uBAAA,EAAwB;AACtD,IAAA,IAAA,CAAK,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,GAAA;AAAA,EACtD;AAAA,EAEA,OAAA,GAAyB;AACvB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,KAAK,IAAI,SAAA,CAAU,CAAA,eAAA,EAAkB,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI;AAAA,QACtD,iBAAA,EAAmB,KAAA;AAAA,QACnB,YAAY,IAAA,GAAO;AAAA,OACpB,CAAA;AACD,MAAA,IAAI,OAAA,GAAU,KAAA;AACd,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAA,GAAU,IAAA;AACV,UAAA,EAAA,CAAG,KAAA,CAAM,MAAM,wCAAwC,CAAA;AACvD,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,kDAAkD,CAAC,CAAA;AAAA,QACtE;AAAA,MACF,CAAA,EAAG,KAAK,gBAAgB,CAAA;AAExB,MAAA,EAAA,CAAG,EAAA,CAAG,QAAQ,MAAM;AAClB,QAAA,IAAA,CAAK,EAAA,GAAK,EAAA;AACV,QAAA,EAAA,CAAG,IAAA;AAAA,UACD,gBAAA;AAAA,YACE,aAAA,CAAc,cAAc,KAAA,EAAO;AAAA,cACjC,eAAA,EAAiB,wBAAA;AAAA,cACjB,OAAO,IAAA,CAAK;AAAA,aACb;AAAA;AACH,SACF;AAAA,MACF,CAAC,CAAA;AAED,MAAA,EAAA,CAAG,EAAA,CAAG,SAAA,EAAW,CAAC,GAAA,KAAQ;AACxB,QAAA,IAAI;AACF,UAAA,MAAM,GAAA,GAAM,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AACvC,UAAA,IAAI,GAAA,CAAI,IAAA,KAAS,aAAA,CAAc,OAAA,IAAW,CAAC,OAAA,EAAS;AAClD,YAAA,OAAA,GAAU,IAAA;AACV,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,IAAA,CAAK,YAAA,CAAa,IAAI,OAAO,CAAA;AAC7B,YAAA,IAAA,CAAK,IAAA,CAAK,kBAAkB,EAAE,IAAA,EAAM,KAAK,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAC5D,YAAA,OAAA,EAAQ;AACR,YAAA;AAAA,UACF;AACA,UAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,CAAU,aAAA,IAAiB,CAAC,OAAA,EAAS;AACpD,YAAA,OAAA,GAAU,IAAA;AACV,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AACpB,YAAA,MAAA,CAAO,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,IAAS,0CAA0C,CAAC,CAAA;AAC7E,YAAA,EAAA,CAAG,KAAA,CAAM,MAAM,kCAAkC,CAAA;AACjD,YAAA;AAAA,UACF;AACA,UAAA,IAAA,CAAK,cAAc,GAAG,CAAA;AAAA,QACxB,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,IAAA,CAAK,gBAAgB,GAAG,CAAA;AAAA,QAC/B;AAAA,MACF,CAAC,CAAA;AAED,MAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACnB,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,IAAA,CAAK,EAAA,GAAK,IAAA;AACV,QAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AACvB,QAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAC1B,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAA,GAAU,IAAA;AACV,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,uDAAuD,CAAC,CAAA;AAAA,QAC3E;AACA,QAAA,IAAA,CAAK,aAAA,CAAc,IAAI,KAAA,CAAM,+BAA+B,CAAC,CAAA;AAC7D,QAAA,IAAA,CAAK,KAAK,qBAAqB,CAAA;AAAA,MACjC,CAAC,CAAA;AAED,MAAA,EAAA,CAAG,EAAA,CAAG,OAAA,EAAS,CAAC,GAAA,KAAQ;AACtB,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAA,GAAU,IAAA;AACV,UAAA,YAAA,CAAa,KAAK,CAAA;AAClB,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACZ;AACA,QAAA,IAAA,CAAK,IAAA,CAAK,gBAAgB,GAAG,CAAA;AAAA,MAC/B,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AAAA,EAEQ,aAAa,OAAA,EAAwB;AAC3C,IAAA,MAAM,OAAA,GAAU,OAAA;AAIhB,IAAA,IAAA,CAAK,kBAAA,CAAmB,OAAA,CAAQ,WAAA,IAAe,EAAE,CAAA;AACjD,IAAA,IAAA,CAAK,kBAAA,GAAqB,QAAQ,kBAAA,IAAsB,IAAA;AACxD,IAAA,IAAI,CAAC,IAAA,CAAK,iBAAA,MAAuB,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAC5D,MAAA,IAAA,CAAK,qBAAqB,IAAA,CAAK,WAAA,CAAY,MAAK,CAAE,IAAA,GAAO,KAAA,IAAS,IAAA;AAAA,IACpE;AAAA,EACF;AAAA,EAEQ,cAAc,GAAA,EAAsB;AAC1C,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,aAAA,CAAc,YAAA,EAAc;AAC3C,MAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AAIpB,MAAA,IAAA,CAAK,kBAAA,CAAmB,OAAA,CAAQ,WAAA,IAAe,EAAE,CAAA;AACjD,MAAA,IAAI,IAAA,CAAK,sBAAsB,CAAC,IAAA,CAAK,YAAY,GAAA,CAAI,IAAA,CAAK,kBAAkB,CAAA,EAAG;AAC7E,QAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAAA,MAC5B;AACA,MAAA,IAAI,CAAC,IAAA,CAAK,kBAAA,IAAsB,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAC3D,QAAA,IAAA,CAAK,qBAAqB,IAAA,CAAK,WAAA,CAAY,MAAK,CAAE,IAAA,GAAO,KAAA,IAAS,IAAA;AAAA,MACpE;AACA,MAAA,IAAA,CAAK,IAAA,CAAK,2BAA2B,OAAO,CAAA;AAC5C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,CAAU,SAAA,EAAW;AACpC,MAAA,MAAM,aAAa,GAAA,CAAI,OAAA;AACvB,MAAA,IAAA,CAAK,YAAY,GAAA,CAAI,UAAA,CAAW,YAAA,EAAc,gBAAA,CAAiB,UAAU,CAAC,CAAA;AAC1E,MAAA,IAAI,CAAC,IAAA,CAAK,kBAAA,IAAsB,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAC3D,QAAA,IAAA,CAAK,qBAAqB,UAAA,CAAW,YAAA;AAAA,MACvC;AACA,MAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,UAAU,CAAA;AACxC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,CAAU,YAAA,EAAc;AACvC,MAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AACpB,MAAA,IAAI,QAAQ,YAAA,EAAc,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,QAAQ,YAAY,CAAA;AACtE,MAAA,IAAI,IAAA,CAAK,kBAAA,KAAuB,OAAA,CAAQ,YAAA,OAAmB,kBAAA,GAAqB,IAAA;AAChF,MAAA,IAAI,CAAC,IAAA,CAAK,kBAAA,IAAsB,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAC3D,QAAA,IAAA,CAAK,qBAAqB,IAAA,CAAK,WAAA,CAAY,MAAK,CAAE,IAAA,GAAO,KAAA,IAAS,IAAA;AAAA,MACpE;AACA,MAAA,IAAA,CAAK,IAAA,CAAK,uBAAuB,OAAO,CAAA;AACxC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,CAAU,QAAA,EAAU;AACnC,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,IAAI,EAAE,CAAA;AAC/C,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,YAAA,CAAa,QAAQ,KAAK,CAAA;AAC1B,QAAA,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA;AAClC,QAAA,OAAA,CAAQ,QAAQ,GAAG,CAAA;AAAA,MACrB;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA;AAC/B,IAAA,IAAA,CAAK,IAAA,CAAK,WAAW,GAAG,CAAA;AAAA,EAC1B;AAAA,EAEQ,mBAAmB,WAAA,EAAgC;AACzD,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AACvB,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,IAAA,CAAK,YAAY,GAAA,CAAI,UAAA,CAAW,YAAA,EAAc,gBAAA,CAAiB,UAAU,CAAC,CAAA;AAAA,IAC5E;AAAA,EACF;AAAA,EAEQ,YAAY,OAAA,EAA0B;AAC5C,IAAA,IAAI,CAAC,IAAA,CAAK,EAAA,IAAM,KAAK,EAAA,CAAG,UAAA,KAAe,UAAU,IAAA,EAAM;AACrD,MAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,IACvD;AACA,IAAA,IAAA,CAAK,EAAA,CAAG,IAAA,CAAK,gBAAA,CAAiB,OAAO,CAAC,CAAA;AAAA,EACxC;AAAA,EAEQ,eAAA,CACN,SACA,SAAA,EACuB;AACvB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,QAAA,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACtC,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,OAAA,CAAQ,IAAI,EAAE,CAAC,CAAA;AAAA,MAChE,GAAG,SAAS,CAAA;AACZ,MAAA,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,OAAA,CAAQ,EAAA,EAAI;AAAA,QACnC,OAAA;AAAA,QACA,MAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,MAC1B,SAAS,GAAA,EAAK;AACZ,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACtC,QAAA,MAAA,CAAO,GAAG,CAAA;AAAA,MACZ;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,kBAAA,CACZ,IAAA,EACA,OAAA,EACA,YAAY,GAAA,EACA;AACZ,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,eAAA,CAAmB,cAAc,IAAA,EAAM,OAAO,GAAG,SAAS,CAAA;AACtF,IAAA,MAAM,SAAS,QAAA,CAAS,OAAA;AACxB,IAAA,IAAI,MAAA,CAAO,KAAA,IAAS,MAAA,CAAO,OAAA,KAAY,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,MAAA,CAAO,KAAA,IAAS,CAAA,EAAG,IAAI,CAAA,OAAA,CAAS,CAAA;AAC9F,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,WAAA,CACE,IAAA,EACA,OAAA,EACA,SAAA,GAAY,IAAA,EACW;AACvB,IAAA,MAAM,MAAA,GAAS,KAAK,iBAAA,EAAkB;AACtC,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,IAAA,CAAK,kBAAA,EAAoB;AACvC,MAAA,OAAO,OAAA,CAAQ,MAAA;AAAA,QACb,IAAI,KAAA;AAAA,UACF,IAAA,CAAK,WAAA,CAAY,IAAA,GAAO,CAAA,GACpB,2EAAA,GACA;AAAA;AACN,OACF;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GAAU,aAAA,CAAc,IAAA,EAAM,OAAO,CAAA;AAC3C,IAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,aAAA,CAAc,KAAA,EAAO;AAAA,MAC/C,cAAc,IAAA,CAAK,kBAAA;AAAA,MACnB;AAAA,KACD,CAAA;AACD,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,QAAA,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACtC,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAI,EAAE,CAAC,CAAA;AAAA,MACvD,GAAG,SAAS,CAAA;AACZ,MAAA,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,OAAA,CAAQ,EAAA,EAAI;AAAA,QACnC,OAAA;AAAA,QACA,MAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AAAA,MACxB,SAAS,GAAA,EAAK;AACZ,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACtC,QAAA,MAAA,CAAO,GAAG,CAAA;AAAA,MACZ;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,gBAAA,CAAiB,MAAc,OAAA,EAAwB;AACrD,IAAA,IAAI,CAAC,KAAK,kBAAA,EAAoB;AAC9B,IAAA,MAAM,OAAA,GAAU,aAAA,CAAc,IAAA,EAAM,OAAO,CAAA;AAC3C,IAAA,IAAA,CAAK,WAAA;AAAA,MACH,aAAA,CAAc,cAAc,KAAA,EAAO;AAAA,QACjC,cAAc,IAAA,CAAK,kBAAA;AAAA,QACnB;AAAA,OACD;AAAA,KACH;AAAA,EACF;AAAA,EAEA,WAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,mBAAkB,KAAM,IAAA;AAAA,EACtC;AAAA,EAEA,iBAAA,GAA6C;AAC3C,IAAA,MAAM,MAAA,GAAS,KAAK,iBAAA,EAAkB;AACtC,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,IAAA,CAAK,IAAI,OAAO,IAAA;AAChC,IAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,EAAA,EAAI,KAAK,EAAA,EAAG;AAAA,EAClC;AAAA,EAEA,eAAA,GAA+B;AAC7B,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,WAAA,CAAY,QAAQ,CAAA,CAAE,GAAA,CAAI,CAAC,UAAA,MAAgB;AAAA,MACzD,OAAA,EAAS,WAAW,OAAA,IAAW,CAAA;AAAA,MAC/B,cAAc,UAAA,CAAW,YAAA;AAAA,MACzB,UAAU,UAAA,CAAW,QAAA;AAAA,MACrB,WAAW,UAAA,CAAW,SAAA;AAAA,MACtB,eAAe,UAAA,CAAW,aAAA;AAAA,MAC1B,aAAa,UAAA,CAAW,WAAA;AAAA,MACxB,MAAA,EAAQ,UAAA,CAAW,YAAA,KAAiB,IAAA,CAAK;AAAA,KAC3C,CAAE,CAAA;AAAA,EACJ;AAAA,EAEA,qBAAA,GAAuC;AACrC,IAAA,OAAO,IAAA,CAAK,kBAAA;AAAA,EACd;AAAA,EAEA,MAAM,sBAAsB,YAAA,EAA4C;AACtE,IAAA,IAAI,gBAAgB,CAAC,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,YAAY,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,YAAY,CAAA,iBAAA,CAAmB,CAAA;AAAA,IACtE;AACA,IAAA,MAAM,KAAK,kBAAA,CAAmB,aAAA,CAAc,qBAAA,EAAuB,EAAE,cAAc,CAAA;AACnF,IAAA,IAAA,CAAK,kBAAA,GAAqB,YAAA;AAAA,EAC5B;AAAA,EAEA,gBAAA,GAA2B;AACzB,IAAA,OAAO,IAAA,CAAK,iBAAA,EAAkB,EAAG,OAAA,IAAW,CAAA;AAAA,EAC9C;AAAA,EAEA,iBAAiB,OAAA,EAAuB;AACtC,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,KAAK,IAAA,CAAK,sBAAsB,IAAI,CAAA;AACpC,MAAA;AAAA,IACF;AACA,IAAA,MAAM,UAAU,CAAC,GAAG,KAAK,WAAA,CAAY,MAAA,EAAQ,CAAA,CAAE,MAAA;AAAA,MAC7C,CAAC,UAAA,KAAe,UAAA,CAAW,OAAA,KAAY;AAAA,KACzC;AACA,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,QAAQ,MAAA,KAAW,CAAA,GACf,SAAS,OAAO,CAAA,iBAAA,CAAA,GAChB,SAAS,OAAO,CAAA,4DAAA;AAAA,OACtB;AAAA,IACF;AACA,IAAA,KAAK,IAAA,CAAK,qBAAA,CAAsB,OAAA,CAAQ,CAAC,EAAE,YAAY,CAAA;AAAA,EACzD;AAAA,EAEQ,iBAAA,GAAyD;AAC/D,IAAA,IAAI,IAAA,CAAK,oBAAoB,OAAO,IAAA,CAAK,YAAY,GAAA,CAAI,IAAA,CAAK,kBAAkB,CAAA,IAAK,IAAA;AACrF,IAAA,IAAI,IAAA,CAAK,WAAA,CAAY,IAAA,KAAS,CAAA,EAAG,OAAO,IAAA,CAAK,WAAA,CAAY,MAAA,EAAO,CAAE,IAAA,EAAK,CAAE,KAAA,IAAS,IAAA;AAClF,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,OAAA,GAAkB;AAChB,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AAAA,EAEA,IAAA,GAAsB;AACpB,IAAA,IAAA,CAAK,aAAA,CAAc,IAAI,KAAA,CAAM,iCAAiC,CAAC,CAAA;AAC/D,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,MAAA,IAAI,CAAC,KAAK,EAAA,EAAI;AACZ,QAAA,OAAA,EAAQ;AACR,QAAA;AAAA,MACF;AACA,MAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,MAAA,IAAA,CAAK,EAAA,GAAK,IAAA;AACV,MAAA,EAAA,CAAG,IAAA,CAAK,SAAS,OAAO,CAAA;AACxB,MAAA,EAAA,CAAG,KAAA,CAAM,KAAM,yBAAyB,CAAA;AAAA,IAC1C,CAAC,CAAA;AAAA,EACH;AAAA,EAEQ,cAAc,KAAA,EAAoB;AACxC,IAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,eAAA,CAAgB,MAAA,EAAO,EAAG;AACnD,MAAA,YAAA,CAAa,QAAQ,KAAK,CAAA;AAC1B,MAAA,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,IACtB;AACA,IAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,EAC7B;AACF,CAAA;AAEA,SAAS,iBAAiB,UAAA,EAAqD;AAC7E,EAAA,OAAO;AAAA,IACL,aAAa,UAAA,CAAW,WAAA;AAAA,IACxB,cAAc,UAAA,CAAW,YAAA;AAAA,IACzB,UAAU,UAAA,CAAW,QAAA;AAAA,IACrB,eAAe,UAAA,CAAW,aAAA;AAAA,IAC1B,SAAS,UAAA,CAAW,OAAA;AAAA,IACpB,WAAW,UAAA,CAAW;AAAA,GACxB;AACF;AAEA,eAAsB,YAAY,IAAA,EAAgC;AAChE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,MAAM,EAAA,GAAK,IAAI,SAAA,CAAU,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,CAAA;AACjD,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,MAAA,EAAA,CAAG,KAAA,EAAM;AACT,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf,GAAG,GAAG,CAAA;AACN,IAAA,EAAA,CAAG,EAAA,CAAG,QAAQ,MAAM;AAClB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,EAAA,CAAG,KAAA,EAAM;AACT,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAC,CAAA;AACD,IAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACnB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;;;ACtYA,SAAS,iBAAA,GAA4B;AACnC,EAAA,MAAM,cAAc,aAAA,CAAc,IAAI,IAAI,oBAAA,EAAsB,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAChF,EAAA,IAAIA,GAAAA,CAAG,UAAA,CAAW,WAAW,CAAA,EAAG,OAAO,WAAA;AAEvC,EAAA,MAAM,eAAeF,KAAAA,CAAK,OAAA;AAAA,IACxBA,KAAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAAA,IAC3C;AAAA,GACF;AACA,EAAA,IAAIE,GAAAA,CAAG,UAAA,CAAW,YAAY,CAAA,EAAG,OAAO,YAAA;AAExC,EAAA,MAAM,IAAI,MAAM,qEAAqE,CAAA;AACvF;AAEO,SAAS,kBAAA,CAAmB,IAAA,EAAc,UAAA,GAAa,iBAAA,EAAkB,EAAS;AACvF,EAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,QAAA,EAAU,CAAC,UAAU,CAAA,EAAG;AAAA,IAClD,QAAA,EAAU,IAAA;AAAA,IACV,GAAA,EAAK,EAAE,GAAG,OAAA,CAAQ,KAAK,mBAAA,EAAqB,MAAA,CAAO,IAAI,CAAA,EAAE;AAAA,IACzD,KAAA,EAAO,QAAA;AAAA,IACP,WAAA,EAAa;AAAA,GACd,CAAA;AACD,EAAA,KAAA,CAAM,KAAA,EAAM;AACd;AAEA,eAAe,iBAAA,CACb,IAAA,EACA,SAAA,EACA,KAAA,EACwB;AACxB,EAAA,MAAM,MAAA,GAAS,IAAI,aAAA,CAAc,IAAA,EAAM,EAAE,gBAAA,EAAkB,SAAA,EAAW,OAAO,CAAA;AAC7E,EAAA,IAAI;AACF,IAAA,MAAM,OAAO,OAAA,EAAQ;AACrB,IAAA,OAAO,MAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,CAAO,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACzC,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAEA,eAAsB,kBAAA,CACpB,IAAA,EACA,OAAA,GAA+B,EAAC,EACR;AACxB,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,EAAA;AACrC,EAAA,MAAM,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,GAAA;AACrD,EAAA,MAAM,YAAA,GAAe,QAAQ,YAAA,IAAgB,GAAA;AAE7C,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,iBAAA,CAAkB,IAAA,EAAM,gBAAA,EAAkB,QAAQ,KAAK,CAAA;AAAA,EACtE,SAAS,YAAA,EAAc;AACrB,IAAA,IAAI,MAAM,WAAA,CAAY,IAAI,CAAA,EAAG;AAC3B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,KAAA,EAAQ,IAAI,CAAA,2DAAA,EAA8D,WAAA,CAAY,YAAY,CAAC,CAAA;AAAA,OACrG;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GACJ,QAAQ,MAAA,KAAW,CAAC,eAAe,kBAAA,CAAmB,UAAA,EAAY,QAAQ,UAAU,CAAA,CAAA;AACtF,EAAA,MAAA,CAAO,IAAI,CAAA;AAEX,EAAA,IAAI,SAAA;AACJ,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,QAAA,EAAU,WAAW,CAAA,EAAG;AACtD,IAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,YAAY,CAAC,CAAA;AAChE,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,iBAAA,CAAkB,IAAA,EAAM,gBAAA,EAAkB,QAAQ,KAAK,CAAA;AAAA,IACtE,SAAS,KAAA,EAAO;AACd,MAAA,SAAA,GAAY,KAAA;AAAA,IACd;AAAA,EACF;AAEA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,CAAA,wDAAA,EAA2D,IAAI,CAAA,EAAA,EAAK,WAAA,CAAY,SAAS,CAAC,CAAA;AAAA,GAC5F;AACF;AAEA,SAAS,YAAY,KAAA,EAAwB;AAC3C,EAAA,OAAO,iBAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,SAAS,eAAe,CAAA;AACjF;;;ACpFA,IAAM,iBAAA,GAAoB,0BAAA;AAE1B,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,OAAOF,KAAAA,CAAK,IAAA,CAAK,YAAA,EAAc,QAAA,EAAU,SAAS,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,OAAA,CAAQ,aAAa,QAAA,EAAU;AACjC,IAAA,OAAOA,MAAK,IAAA,CAAKC,GAAAA,CAAG,SAAQ,EAAG,WAAA,EAAa,UAAU,SAAS,CAAA;AAAA,EACjE;AACA,EAAA,OAAOD,KAAAA,CAAK,KAAKC,GAAAA,CAAG,OAAA,IAAW,QAAA,EAAU,OAAA,EAAS,UAAU,SAAS,CAAA;AACvE;AAEA,IAAM,gBAAgBD,KAAAA,CAAK,OAAA,CAAQG,aAAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACjE,IAAM,SAASH,KAAAA,CAAK,OAAA,CAAQ,aAAA,EAAe,IAAA,EAAM,UAAU,eAAe,CAAA;AAC1E,IAAM,uBAAuB,kBAAA,EAAmB;AAChD,IAAM,WAAA,GAAcA,KAAAA,CAAK,IAAA,CAAK,oBAAA,EAAsB,gBAAgB,CAAA;AAEpE,IAAI,CAACE,GAAAA,CAAG,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,MAAM,CAAA,8BAAA,CAAgC,CAAA;AACxF;AAEA,IAAM,QAAA,GAAWA,GAAAA,CAAG,YAAA,CAAa,MAAA,EAAQ,MAAM,CAAA;AAC/C,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,iBAAiB,CAAA,EAAG;AACzC,EAAA,MAAM,IAAI,MAAM,kEAAkE,CAAA;AACpF;AACA,IAAM,kBAAA,GAAqB,QAAA,CAAS,UAAA,CAAW,iBAAA,EAAmB,yBAAyB,CAAA;AAE3FA,GAAAA,CAAG,SAAA,CAAU,oBAAA,EAAsB,EAAE,SAAA,EAAW,MAAM,CAAA;AACtDA,GAAAA,CAAG,aAAA,CAAc,WAAA,EAAa,kBAAA,EAAoB,MAAM,CAAA;AACxD,KAAA,MAAW,YAAA,IAAgB,CAAC,eAAA,EAAiB,cAAA,EAAgB,eAAe,CAAA,EAAG;AAC7E,EAAA,MAAM,YAAA,GAAeF,KAAAA,CAAK,IAAA,CAAK,oBAAA,EAAsB,YAAY,CAAA;AACjE,EAAA,IAAI,YAAA,KAAiB,aAAaE,GAAAA,CAAG,OAAO,YAAA,EAAc,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA;AAC3E;AACA,OAAA,CAAQ,GAAA,CAAI,CAAA,qCAAA,EAAwC,WAAW,CAAA,CAAE,CAAA;AACjE,OAAA,CAAQ,GAAA;AAAA,EACN;AACF,CAAA;AAEA,IAAM,MAAA,GAAS,MAAM,kBAAA,CAAmB,UAAA,GAAa,IAAI,CAAA;AACzD,MAAM,OAAO,IAAA,EAAK;AAClB,OAAA,CAAQ,IAAI,qDAAqD,CAAA","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 { nanoid } from 'nanoid';\nimport { z } from 'zod';\n\nexport const DOMINUS_PROTOCOL_VERSION = 2;\nexport const MAX_MESSAGE_BYTES = 1024 * 1024;\n\nexport interface WsMessage<T = unknown> {\n id: string;\n type: string;\n payload: T;\n ts: number;\n}\n\nexport const StudioMsg = {\n HELLO: 'studio:hello',\n CONNECTED: 'studio:connected',\n DISCONNECTED: 'studio:disconnected',\n EXPLORER: 'studio:explorer',\n OUTPUT: 'studio:output',\n ERROR: 'studio:error',\n SELECTION: 'studio:selection',\n SCRIPT_CONTENT: 'studio:script:content',\n SCRIPT_CHANGED: 'studio:script:changed',\n RESPONSE: 'studio:response',\n REFLECTION: 'studio:reflection',\n TEST_RESULTS: 'studio:test:results',\n AUTHENTICATED: 'server:authenticated',\n AUTH_REJECTED: 'server:auth_rejected',\n} as const;\n\nexport const ControllerMsg = {\n HELLO: 'controller:hello',\n WELCOME: 'controller:welcome',\n RELAY: 'controller:relay',\n TARGET_STATE: 'controller:target_state',\n SET_ACTIVE_CONNECTION: 'controller:set_active_connection',\n} as const;\n\nexport const CliMsg = {\n GET_EXPLORER: 'cli:get:explorer',\n GET_SCRIPT: 'cli:get:script',\n SET_SCRIPT: 'cli:set:script',\n RUN_CODE: 'cli:run',\n GET_PROPERTIES: 'cli:get:properties',\n GET_DESCENDANTS_PROPERTIES: 'cli:get:descendants:properties',\n SET_PROPERTIES: 'cli:set:properties',\n INSERT_INSTANCE: 'cli:insert',\n DELETE_INSTANCE: 'cli:delete',\n GET_SELECTION: 'cli:get:selection',\n SELECT: 'cli:select',\n SEARCH_SCRIPTS: 'cli:search:scripts',\n GET_OUTPUT: 'cli:get:output',\n RUN_TESTS: 'cli:run:tests',\n EXECUTE_RUN_TEST: 'cli:test:run',\n EXECUTE_PLAY_TEST: 'cli:test:play',\n END_TEST: 'cli:test:end',\n BUILD_UI: 'cli:build:ui',\n GET_REFLECTION: 'cli:get:reflection',\n UPLOAD_ASSET: 'cli:upload:asset',\n SERIALIZE_UI: 'cli:serialize:ui',\n MOVE_INSTANCE: 'cli:move:instance',\n UNDO: 'cli:undo',\n REDO: 'cli:redo',\n BULK_SET_PROPERTIES: 'cli:bulk:set:properties',\n FIND_REPLACE_SCRIPTS: 'cli:find:replace:scripts',\n V2_GET_TREE: 'studio:v2:get_tree',\n V2_INSPECT: 'studio:v2:inspect',\n V2_GET_SELECTION: 'studio:v2:get_selection',\n V2_READ_SCRIPT: 'studio:v2:read_script',\n V2_UPDATE_SCRIPT: 'studio:v2:update_script',\n V2_APPLY: 'studio:v2:apply',\n V2_CREATE_PARTS: 'studio:v2:create_parts',\n V2_CLONE_INSTANCES: 'studio:v2:clone_instances',\n V2_GROUP_INSTANCES: 'studio:v2:group_instances',\n V2_UNGROUP_INSTANCES: 'studio:v2:ungroup_instances',\n V2_TRANSFORM_INSTANCES: 'studio:v2:transform_instances',\n V2_CREATE_WELDS: 'studio:v2:create_welds',\n V2_QUERY_PARTS: 'studio:v2:query_parts',\n V2_SET_SELECTION: 'studio:v2:set_selection',\n V2_GET_METADATA: 'studio:v2:get_metadata',\n V2_SET_ATTRIBUTES: 'studio:v2:set_attributes',\n V2_UPDATE_TAGS: 'studio:v2:update_tags',\n V2_LIST_CALLABLE_METHODS: 'studio:v2:list_callable_methods',\n V2_INVOKE_METHODS: 'studio:v2:invoke_methods',\n V2_DELETE: 'studio:v2:delete',\n V2_BUILD_UI: 'studio:v2:build_ui',\n V2_SNAPSHOT_UI: 'studio:v2:snapshot_ui',\n V2_RUN_TEST: 'studio:v2:run_test',\n V2_GET_OUTPUT: 'studio:v2:get_output',\n V2_GET_REFLECTION: 'studio:v2:get_reflection',\n} as const;\n\nexport type StudioMsgType = (typeof StudioMsg)[keyof typeof StudioMsg];\nexport type CliMsgType = (typeof CliMsg)[keyof typeof CliMsg];\n\nexport interface ExplorerNode {\n name: string;\n className: string;\n path: string;\n ref?: InstanceRef;\n children?: ExplorerNode[];\n}\n\nexport interface InstanceRef {\n instanceId?: string;\n pathSegments?: string[];\n}\n\nexport interface StudioConnectedPayload {\n studioVersion: string;\n protocolVersion?: number;\n clientId?: string;\n token?: string;\n connectionId?: string;\n placeId?: number;\n placeName?: string;\n engineVersion?: string;\n}\n\nexport interface ScriptContentPayload {\n path: string;\n className: 'Script' | 'LocalScript' | 'ModuleScript';\n source: string;\n lineCount: number;\n}\n\nexport interface ScriptChangedPayload {\n path: string;\n source: string;\n}\n\nexport interface OutputPayload {\n message: string;\n level: 'info' | 'warn' | 'error';\n timestamp: number;\n source?: string;\n}\n\nexport interface SelectionPayload {\n paths: string[];\n classNames: string[];\n}\n\nexport interface PropertyEntry {\n name: string;\n value: string;\n type: string;\n category?: string;\n}\n\nexport interface TestResultPayload {\n name: string;\n passed: boolean;\n duration: number;\n error?: string;\n}\n\nexport interface StudioTestPayload {\n args?: unknown;\n}\n\nexport interface StudioTestResultPayload {\n success: boolean;\n result?: unknown;\n error?: string;\n duration?: number;\n}\n\nexport interface ReflectionPayload {\n className: string;\n properties: { name: string; type: string; category: string }[];\n methods: { name: string; parameters: string[] }[];\n events: { name: string; parameters: string[] }[];\n}\n\nexport interface RunCodePayload {\n code: string;\n}\n\nexport interface RunCodeResponsePayload {\n output: string;\n error?: string;\n duration: number;\n}\n\nexport interface SearchScriptsPayload {\n query: string;\n maxResults?: number;\n}\n\nexport interface SearchResultPayload {\n results: {\n path: string;\n className: string;\n matches: { line: number; text: string }[];\n }[];\n}\n\nexport interface InsertInstancePayload {\n className: string;\n parentPath: string;\n name: string;\n properties?: Record<string, unknown>;\n}\n\nexport interface SetPropertiesPayload {\n path: string;\n properties: Record<string, unknown>;\n}\n\nexport function createMessage<T>(type: string, payload: T): WsMessage<T> {\n return { id: nanoid(), type, payload, ts: Date.now() };\n}\n\nexport function createRequest<T>(type: string, payload: T): WsMessage<T> {\n return createMessage(type, payload);\n}\n\nexport function parseMessage(raw: string): WsMessage {\n if (Buffer.byteLength(raw, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`WebSocket message exceeds ${MAX_MESSAGE_BYTES} bytes`);\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n return wsMessageSchema.parse(parsed) as WsMessage;\n } catch {\n throw new Error(`Failed to parse WebSocket message: ${raw.slice(0, 100)}`);\n }\n}\n\nexport function serializeMessage(msg: WsMessage): string {\n const serialized = JSON.stringify(msg);\n if (Buffer.byteLength(serialized, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`WebSocket message exceeds ${MAX_MESSAGE_BYTES} bytes`);\n }\n return serialized;\n}\n\nconst wsMessageSchema = z\n .object({\n id: z.string().min(1).max(128),\n type: z.string().min(1).max(128),\n payload: z.unknown(),\n ts: z.number().finite(),\n })\n .strict();\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 (\n actualBuffer.length === expectedBuffer.length &&\n crypto.timingSafeEqual(actualBuffer, expectedBuffer)\n );\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport {\n ControllerMsg,\n DOMINUS_PROTOCOL_VERSION,\n StudioMsg,\n createMessage,\n parseMessage,\n serializeMessage,\n type WsMessage,\n} from './protocol.js';\nimport { loadOrCreateBridgeToken } from './auth.js';\nimport type { PlaceInfo, StudioBridge, StudioConnection } from './ws-server.js';\n\ninterface ClientOptions {\n token?: string;\n connectTimeoutMs?: number;\n}\n\ninterface ControlResult {\n success?: boolean;\n error?: string;\n [key: string]: unknown;\n}\n\nexport class DominusClient extends EventEmitter implements StudioBridge {\n private ws: WebSocket | null = null;\n private readonly port: number;\n private readonly token: string;\n private readonly connectTimeoutMs: number;\n private readonly connections = new Map<string, Omit<StudioConnection, 'ws'>>();\n private activeConnectionId: string | null = null;\n private readonly pendingRequests = new Map<\n string,\n {\n resolve: (msg: WsMessage) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n constructor(port = 18088, options: ClientOptions = {}) {\n super();\n this.port = port;\n this.token = options.token ?? loadOrCreateBridgeToken();\n this.connectTimeoutMs = options.connectTimeoutMs ?? 8_000;\n }\n\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(`ws://127.0.0.1:${this.port}`, {\n perMessageDeflate: false,\n maxPayload: 1024 * 1024,\n });\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n ws.close(1008, 'Dominus controller handshake timed out');\n reject(new Error('Timed out authenticating with the Dominus bridge'));\n }\n }, this.connectTimeoutMs);\n\n ws.on('open', () => {\n this.ws = ws;\n ws.send(\n serializeMessage(\n createMessage(ControllerMsg.HELLO, {\n protocolVersion: DOMINUS_PROTOCOL_VERSION,\n token: this.token,\n }),\n ),\n );\n });\n\n ws.on('message', (raw) => {\n try {\n const msg = parseMessage(raw.toString());\n if (msg.type === ControllerMsg.WELCOME && !settled) {\n settled = true;\n clearTimeout(timer);\n this.applyWelcome(msg.payload);\n this.emit('server:started', { port: this.port, relay: true });\n resolve();\n return;\n }\n if (msg.type === StudioMsg.AUTH_REJECTED && !settled) {\n settled = true;\n clearTimeout(timer);\n const payload = msg.payload as { error?: string };\n reject(new Error(payload.error ?? 'Dominus controller authentication failed'));\n ws.close(1008, 'Controller authentication failed');\n return;\n }\n this.handleMessage(msg);\n } catch (err) {\n this.emit('server:error', err);\n }\n });\n\n ws.on('close', () => {\n clearTimeout(timer);\n this.ws = null;\n this.connections.clear();\n this.activeConnectionId = null;\n if (!settled) {\n settled = true;\n reject(new Error('Dominus bridge closed before authentication completed'));\n }\n this.rejectPending(new Error('Dominus relay connection lost'));\n this.emit('studio:disconnected');\n });\n\n ws.on('error', (err) => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n reject(err);\n }\n this.emit('server:error', err);\n });\n });\n }\n\n private applyWelcome(payload: unknown): void {\n const welcome = payload as {\n connections?: PlaceInfo[];\n activeConnectionId?: string | null;\n };\n this.replaceConnections(welcome.connections ?? []);\n this.activeConnectionId = welcome.activeConnectionId ?? null;\n if (!this.resolveTargetInfo() && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n }\n\n private handleMessage(msg: WsMessage): void {\n if (msg.type === ControllerMsg.TARGET_STATE) {\n const payload = msg.payload as {\n connections?: PlaceInfo[];\n defaultActiveConnectionId?: string | null;\n };\n this.replaceConnections(payload.connections ?? []);\n if (this.activeConnectionId && !this.connections.has(this.activeConnectionId)) {\n this.activeConnectionId = null;\n }\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n this.emit('controller:target_state', payload);\n return;\n }\n\n if (msg.type === StudioMsg.CONNECTED) {\n const connection = msg.payload as PlaceInfo;\n this.connections.set(connection.connectionId, toConnectionInfo(connection));\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = connection.connectionId;\n }\n this.emit('studio:connected', connection);\n return;\n }\n\n if (msg.type === StudioMsg.DISCONNECTED) {\n const payload = msg.payload as { connectionId?: string };\n if (payload.connectionId) this.connections.delete(payload.connectionId);\n if (this.activeConnectionId === payload.connectionId) this.activeConnectionId = null;\n if (!this.activeConnectionId && this.connections.size === 1) {\n this.activeConnectionId = this.connections.keys().next().value ?? null;\n }\n this.emit('studio:disconnected', payload);\n return;\n }\n\n if (msg.type === StudioMsg.RESPONSE) {\n const pending = this.pendingRequests.get(msg.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(msg.id);\n pending.resolve(msg);\n }\n return;\n }\n\n this.emit(msg.type, msg.payload);\n this.emit('message', msg);\n }\n\n private replaceConnections(connections: PlaceInfo[]): void {\n this.connections.clear();\n for (const connection of connections) {\n this.connections.set(connection.connectionId, toConnectionInfo(connection));\n }\n }\n\n private sendMessage(message: WsMessage): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to the Dominus bridge');\n }\n this.ws.send(serializeMessage(message));\n }\n\n private waitForResponse<T = unknown>(\n message: WsMessage,\n timeoutMs: number,\n ): Promise<WsMessage<T>> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(message.id);\n reject(new Error(`Dominus request timed out: ${message.type}`));\n }, timeoutMs);\n this.pendingRequests.set(message.id, {\n resolve: resolve as (msg: WsMessage) => void,\n reject,\n timer,\n });\n try {\n this.sendMessage(message);\n } catch (err) {\n clearTimeout(timer);\n this.pendingRequests.delete(message.id);\n reject(err);\n }\n });\n }\n\n private async sendControlRequest<T extends ControlResult>(\n type: string,\n payload: unknown,\n timeoutMs = 10_000,\n ): Promise<T> {\n const response = await this.waitForResponse<T>(createMessage(type, payload), timeoutMs);\n const result = response.payload;\n if (result.error || result.success === false) throw new Error(result.error ?? `${type} failed`);\n return result;\n }\n\n sendRequest<T = unknown>(\n type: string,\n payload: unknown,\n timeoutMs = 15_000,\n ): Promise<WsMessage<T>> {\n const target = this.resolveTargetInfo();\n if (!target || !this.activeConnectionId) {\n return Promise.reject(\n new Error(\n this.connections.size > 1\n ? 'Multiple Studio sessions are connected. Call dominus_select_studio first.'\n : 'No authenticated Roblox Studio connection',\n ),\n );\n }\n const request = createMessage(type, payload);\n const relay = createMessage(ControllerMsg.RELAY, {\n connectionId: this.activeConnectionId,\n request,\n });\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(request.id);\n reject(new Error(`Studio request timed out: ${type}`));\n }, timeoutMs);\n this.pendingRequests.set(request.id, {\n resolve: resolve as (msg: WsMessage) => void,\n reject,\n timer,\n });\n try {\n this.sendMessage(relay);\n } catch (err) {\n clearTimeout(timer);\n this.pendingRequests.delete(request.id);\n reject(err);\n }\n });\n }\n\n sendNotification(type: string, payload: unknown): void {\n if (!this.activeConnectionId) return;\n const request = createMessage(type, payload);\n this.sendMessage(\n createMessage(ControllerMsg.RELAY, {\n connectionId: this.activeConnectionId,\n request,\n }),\n );\n }\n\n isConnected(): boolean {\n return this.resolveTargetInfo() !== null;\n }\n\n getConnectionInfo(): StudioConnection | null {\n const target = this.resolveTargetInfo();\n if (!target || !this.ws) return null;\n return { ...target, ws: this.ws };\n }\n\n listConnections(): PlaceInfo[] {\n return [...this.connections.values()].map((connection) => ({\n placeId: connection.placeId ?? 0,\n connectionId: connection.connectionId,\n clientId: connection.clientId,\n placeName: connection.placeName,\n studioVersion: connection.studioVersion,\n connectedAt: connection.connectedAt,\n active: connection.connectionId === this.activeConnectionId,\n }));\n }\n\n getActiveConnectionId(): string | null {\n return this.activeConnectionId;\n }\n\n async setActiveConnectionId(connectionId: string | null): Promise<void> {\n if (connectionId && !this.connections.has(connectionId)) {\n throw new Error(`Studio connection ${connectionId} is not connected`);\n }\n await this.sendControlRequest(ControllerMsg.SET_ACTIVE_CONNECTION, { connectionId });\n this.activeConnectionId = connectionId;\n }\n\n getActivePlaceId(): number {\n return this.resolveTargetInfo()?.placeId ?? 0;\n }\n\n setActivePlaceId(placeId: number): void {\n if (placeId === 0) {\n void this.setActiveConnectionId(null);\n return;\n }\n const matches = [...this.connections.values()].filter(\n (connection) => connection.placeId === placeId,\n );\n if (matches.length !== 1) {\n throw new Error(\n matches.length === 0\n ? `Place ${placeId} is not connected`\n : `Place ${placeId} has multiple Studio sessions; select a connectionId instead`,\n );\n }\n void this.setActiveConnectionId(matches[0].connectionId);\n }\n\n private resolveTargetInfo(): Omit<StudioConnection, 'ws'> | null {\n if (this.activeConnectionId) return this.connections.get(this.activeConnectionId) ?? null;\n if (this.connections.size === 1) return this.connections.values().next().value ?? null;\n return null;\n }\n\n getPort(): number {\n return this.port;\n }\n\n stop(): Promise<void> {\n this.rejectPending(new Error('Dominus client is shutting down'));\n return new Promise((resolve) => {\n if (!this.ws) {\n resolve();\n return;\n }\n const ws = this.ws;\n this.ws = null;\n ws.once('close', resolve);\n ws.close(1000, 'Dominus client shutdown');\n });\n }\n\n private rejectPending(error: Error): void {\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pendingRequests.clear();\n }\n}\n\nfunction toConnectionInfo(connection: PlaceInfo): Omit<StudioConnection, 'ws'> {\n return {\n connectedAt: connection.connectedAt,\n connectionId: connection.connectionId,\n clientId: connection.clientId,\n studioVersion: connection.studioVersion,\n placeId: connection.placeId,\n placeName: connection.placeName,\n };\n}\n\nexport async function isPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const ws = new WebSocket(`ws://127.0.0.1:${port}`);\n const timer = setTimeout(() => {\n ws.close();\n resolve(false);\n }, 750);\n ws.on('open', () => {\n clearTimeout(timer);\n ws.close();\n resolve(true);\n });\n ws.on('error', () => {\n clearTimeout(timer);\n resolve(false);\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { DominusClient, isPortInUse } from './ws-client.js';\n\nexport interface BridgeLaunchOptions {\n attempts?: number;\n connectTimeoutMs?: number;\n daemonPath?: string;\n launch?: (port: number) => void;\n retryDelayMs?: number;\n token?: string;\n}\n\nfunction resolveDaemonPath(): string {\n const besideEntry = fileURLToPath(new URL('./bridge-daemon.js', import.meta.url));\n if (fs.existsSync(besideEntry)) return besideEntry;\n\n const projectBuild = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n '../../dist/bridge-daemon.js',\n );\n if (fs.existsSync(projectBuild)) return projectBuild;\n\n throw new Error('Dominus bridge daemon is missing. Reinstall or rebuild dominus-cli.');\n}\n\nexport function launchBridgeDaemon(port: number, daemonPath = resolveDaemonPath()): void {\n const child = spawn(process.execPath, [daemonPath], {\n detached: true,\n env: { ...process.env, DOMINUS_BRIDGE_PORT: String(port) },\n stdio: 'ignore',\n windowsHide: true,\n });\n child.unref();\n}\n\nasync function connectController(\n port: number,\n timeoutMs: number,\n token?: string,\n): Promise<DominusClient> {\n const client = new DominusClient(port, { connectTimeoutMs: timeoutMs, token });\n try {\n await client.connect();\n return client;\n } catch (error) {\n await client.stop().catch(() => undefined);\n throw error;\n }\n}\n\nexport async function ensureBridgeDaemon(\n port: number,\n options: BridgeLaunchOptions = {},\n): Promise<DominusClient> {\n const attempts = options.attempts ?? 30;\n const connectTimeoutMs = options.connectTimeoutMs ?? 750;\n const retryDelayMs = options.retryDelayMs ?? 100;\n\n try {\n return await connectController(port, connectTimeoutMs, options.token);\n } catch (initialError) {\n if (await isPortInUse(port)) {\n throw new Error(\n `Port ${port} is occupied by a process that is not this Dominus bridge: ${formatError(initialError)}`,\n );\n }\n }\n\n const launch =\n options.launch ?? ((targetPort) => launchBridgeDaemon(targetPort, options.daemonPath));\n launch(port);\n\n let lastError: unknown;\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n try {\n return await connectController(port, connectTimeoutMs, options.token);\n } catch (error) {\n lastError = error;\n }\n }\n\n throw new Error(\n `Dominus could not start its local Studio bridge on port ${port}: ${formatError(lastError)}`,\n );\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error ?? 'unknown error');\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { loadConfig } from './config/config.js';\nimport { ensureBridgeDaemon } from './transport/daemon-launcher.js';\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 'Dominus 2 authenticates automatically. Restart Studio only if the toolbar does not reconnect.',\n);\n\nconst bridge = await ensureBridgeDaemon(loadConfig().port);\nawait bridge.stop();\nconsole.log('Dominus Studio bridge is running in the background.');\n"]}
|