@dexlyai/dexly 0.1.1 → 0.1.3

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.
Files changed (4) hide show
  1. package/README.md +4 -2
  2. package/dist/cli.js +611 -300
  3. package/dist/host.js +332 -111
  4. package/package.json +1 -1
package/dist/host.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
  "use strict";
4
3
  var __create = Object.create;
5
4
  var __defProp = Object.defineProperty;
@@ -118,33 +117,22 @@ var require_companion = __commonJS({
118
117
  });
119
118
 
120
119
  // src/host.ts
121
- var import_node_process4 = __toESM(require("node:process"));
120
+ var import_node_process5 = __toESM(require("node:process"));
122
121
 
123
122
  // src/host-runtime.ts
124
- var import_node_process3 = __toESM(require("node:process"));
123
+ var import_node_process4 = __toESM(require("node:process"));
125
124
 
126
125
  // src/codex-host.ts
127
- var import_node_process2 = __toESM(require("node:process"));
128
- var import_node_child_process2 = require("node:child_process");
126
+ var import_node_process3 = __toESM(require("node:process"));
129
127
  var import_node_readline = __toESM(require("node:readline"));
130
- var import_companion4 = __toESM(require_companion());
128
+ var import_companion5 = __toESM(require_companion());
131
129
 
132
130
  // src/constants.ts
133
- var import_node_os = __toESM(require("node:os"));
134
131
  var import_node_path = __toESM(require("node:path"));
135
132
  var import_companion = __toESM(require_companion());
136
- var DEXLY_COMPANION_VERSION = true ? "0.1.1" : packageJson.version;
133
+ var DEXLY_COMPANION_VERSION = true ? "0.1.3" : packageJson.version;
137
134
  var DEXLY_COMPANION_DESCRIPTION = "Dexly native bridge for Codex";
138
135
  var DEXLY_COMPANION_METADATA_FILE_NAME = "install-metadata.json";
139
- function defaultInstallRoot(homeDir = import_node_os.default.homedir()) {
140
- return import_node_path.default.join(homeDir, "Library", "Application Support", "Dexly", "companion");
141
- }
142
- function defaultChromeHostManifestDir(homeDir = import_node_os.default.homedir()) {
143
- return import_node_path.default.join(homeDir, "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
144
- }
145
- function defaultChromeAllowedOrigins() {
146
- return [...import_companion.DEXLY_FIXED_ALLOWED_ORIGINS];
147
- }
148
136
  function currentInstallPath(installRoot) {
149
137
  return import_node_path.default.join(installRoot, "current");
150
138
  }
@@ -154,11 +142,8 @@ function installMetadataPath(installRoot) {
154
142
  function installedHostScriptPath(installRoot) {
155
143
  return import_node_path.default.join(currentInstallPath(installRoot), "dist", "host.js");
156
144
  }
157
- function installedHostLauncherPath(installRoot) {
158
- return import_node_path.default.join(currentInstallPath(installRoot), "bin", "dexly-companion-host");
159
- }
160
- function installedHostManifestPath(manifestDir) {
161
- return import_node_path.default.join(manifestDir, `${import_companion.DEXLY_COMPANION_HOST_NAME}.json`);
145
+ function installedHostLauncherPath(installRoot, platform = process.platform) {
146
+ return import_node_path.default.join(currentInstallPath(installRoot), "bin", resolveLauncherFileName(platform));
162
147
  }
163
148
  function buildChromeHostManifest(hostPath, allowedOrigins) {
164
149
  return {
@@ -169,9 +154,12 @@ function buildChromeHostManifest(hostPath, allowedOrigins) {
169
154
  allowed_origins: allowedOrigins
170
155
  };
171
156
  }
157
+ function resolveLauncherFileName(platform) {
158
+ return platform === "win32" ? "dexly-companion-host.cmd" : "dexly-companion-host";
159
+ }
172
160
 
173
161
  // src/management.ts
174
- var import_companion3 = __toESM(require_companion());
162
+ var import_companion4 = __toESM(require_companion());
175
163
 
176
164
  // src/install-metadata.ts
177
165
  var import_promises = require("node:fs/promises");
@@ -234,14 +222,171 @@ async function saveInstallMetadata(installRoot, metadata) {
234
222
 
235
223
  // src/install.ts
236
224
  var import_promises2 = require("node:fs/promises");
237
- var import_node_path3 = __toESM(require("node:path"));
225
+ var import_node_path4 = __toESM(require("node:path"));
238
226
 
239
- // src/tooling.ts
227
+ // src/platform.ts
228
+ var import_node_os = __toESM(require("node:os"));
240
229
  var import_node_path2 = __toESM(require("node:path"));
241
230
  var import_node_process = __toESM(require("node:process"));
231
+ var import_companion3 = __toESM(require_companion());
232
+ function assertSupportedPlatform(platform = import_node_process.default.platform) {
233
+ switch (platform) {
234
+ case "darwin":
235
+ case "linux":
236
+ case "win32":
237
+ return platform;
238
+ default:
239
+ throw new Error(
240
+ `Dexly Companion currently supports macOS, Windows, and Linux only. Unsupported platform: ${platform}.`
241
+ );
242
+ }
243
+ }
244
+ function defaultChromeAllowedOrigins() {
245
+ return [...import_companion3.DEXLY_FIXED_ALLOWED_ORIGINS];
246
+ }
247
+ function defaultInstallRoot(options = {}) {
248
+ const platform = assertSupportedPlatform(options.platform);
249
+ const homeDir = options.homeDir ?? import_node_os.default.homedir();
250
+ const env = options.env ?? import_node_process.default.env;
251
+ switch (platform) {
252
+ case "darwin":
253
+ return import_node_path2.default.join(homeDir, "Library", "Application Support", "Dexly", "companion");
254
+ case "linux": {
255
+ const dataHome = env.XDG_DATA_HOME?.trim() || import_node_path2.default.join(homeDir, ".local", "share");
256
+ return import_node_path2.default.join(dataHome, "Dexly", "companion");
257
+ }
258
+ case "win32": {
259
+ const localAppData = env.LOCALAPPDATA?.trim() || import_node_path2.default.join(homeDir, "AppData", "Local");
260
+ return import_node_path2.default.join(localAppData, "Dexly", "companion");
261
+ }
262
+ }
263
+ }
264
+ function resolveChromeHostRegistration(options = {}) {
265
+ const platform = assertSupportedPlatform(options.platform);
266
+ const homeDir = options.homeDir ?? import_node_os.default.homedir();
267
+ const env = options.env ?? import_node_process.default.env;
268
+ const installRoot = options.installRoot ?? defaultInstallRoot({
269
+ platform,
270
+ homeDir,
271
+ env
272
+ });
273
+ const manifestPath = options.manifestDir ? import_node_path2.default.join(options.manifestDir, `${import_companion3.DEXLY_COMPANION_HOST_NAME}.json`) : defaultChromeManifestPath({
274
+ platform,
275
+ homeDir,
276
+ env,
277
+ installRoot
278
+ });
279
+ if (platform === "win32") {
280
+ return {
281
+ kind: "windows-registry",
282
+ manifestPath,
283
+ allowedOrigins: defaultChromeAllowedOrigins(),
284
+ registryKey: options.windowsRegistryKey ?? `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${import_companion3.DEXLY_COMPANION_HOST_NAME}`
285
+ };
286
+ }
287
+ return {
288
+ kind: "manifest-file",
289
+ manifestPath,
290
+ allowedOrigins: defaultChromeAllowedOrigins(),
291
+ registryKey: null
292
+ };
293
+ }
294
+ function resolvePlatformLayout(options = {}) {
295
+ const platform = assertSupportedPlatform(options.platform);
296
+ const installRoot = options.installRoot ?? defaultInstallRoot(options);
297
+ return {
298
+ platform,
299
+ installRoot,
300
+ activationStrategy: platform === "win32" ? "copy" : "symlink",
301
+ launcherKind: platform === "win32" ? "windows-cmd" : "posix",
302
+ launcherFileName: platform === "win32" ? "dexly-companion-host.cmd" : "dexly-companion-host",
303
+ hostRegistration: resolveChromeHostRegistration({
304
+ ...options,
305
+ platform,
306
+ installRoot
307
+ })
308
+ };
309
+ }
310
+ function defaultLauncherPathEntries(platform = import_node_process.default.platform, env = import_node_process.default.env) {
311
+ switch (assertSupportedPlatform(platform)) {
312
+ case "darwin":
313
+ return ["/usr/local/bin", "/opt/homebrew/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"];
314
+ case "linux":
315
+ return ["/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"];
316
+ case "win32": {
317
+ const systemRoot = env.SystemRoot?.trim() || "C:\\Windows";
318
+ return [import_node_path2.default.join(systemRoot, "System32"), systemRoot];
319
+ }
320
+ }
321
+ }
322
+ function renderHostLauncher(options) {
323
+ const platform = assertSupportedPlatform(options.platform);
324
+ const pathEntries = [
325
+ import_node_path2.default.dirname(options.nodePath),
326
+ options.codexPath ? import_node_path2.default.dirname(options.codexPath) : null,
327
+ ...defaultLauncherPathEntries(platform, options.env ?? import_node_process.default.env)
328
+ ].filter((entry, index, entries) => typeof entry === "string" && entries.indexOf(entry) === index);
329
+ if (platform === "win32") {
330
+ const lines2 = [
331
+ "@echo off",
332
+ "setlocal",
333
+ `set "PATH=${toBatchLiteral(pathEntries.join(import_node_path2.default.delimiter))};%PATH%"`,
334
+ options.codexPath ? `set "DEXLY_COMPANION_CODEX_PATH=${toBatchLiteral(options.codexPath)}"` : null,
335
+ `set "DEXLY_COMPANION_NODE_PATH=${toBatchLiteral(options.nodePath)}"`,
336
+ `set "DEXLY_COMPANION_HOST_SCRIPT=${toBatchLiteral(options.hostScriptPath)}"`,
337
+ '"%DEXLY_COMPANION_NODE_PATH%" "%DEXLY_COMPANION_HOST_SCRIPT%" %*'
338
+ ].filter((line) => typeof line === "string");
339
+ return `${lines2.join("\r\n")}\r
340
+ `;
341
+ }
342
+ const lines = [
343
+ "#!/bin/sh",
344
+ "set -eu",
345
+ `export PATH=${toShellLiteral(pathEntries.join(import_node_path2.default.delimiter))}`,
346
+ options.codexPath ? `export DEXLY_COMPANION_CODEX_PATH=${toShellLiteral(options.codexPath)}` : null,
347
+ `exec ${toShellLiteral(options.nodePath)} ${toShellLiteral(options.hostScriptPath)} "$@"`
348
+ ].filter((line) => typeof line === "string");
349
+ return `${lines.join("\n")}
350
+ `;
351
+ }
352
+ function defaultChromeManifestPath(options) {
353
+ switch (options.platform) {
354
+ case "darwin":
355
+ return import_node_path2.default.join(
356
+ options.homeDir,
357
+ "Library",
358
+ "Application Support",
359
+ "Google",
360
+ "Chrome",
361
+ "NativeMessagingHosts",
362
+ `${import_companion3.DEXLY_COMPANION_HOST_NAME}.json`
363
+ );
364
+ case "linux": {
365
+ const configHome = options.env.XDG_CONFIG_HOME?.trim() || import_node_path2.default.join(options.homeDir, ".config");
366
+ return import_node_path2.default.join(
367
+ configHome,
368
+ "google-chrome",
369
+ "NativeMessagingHosts",
370
+ `${import_companion3.DEXLY_COMPANION_HOST_NAME}.json`
371
+ );
372
+ }
373
+ case "win32":
374
+ return import_node_path2.default.join(options.installRoot, "native-host", `${import_companion3.DEXLY_COMPANION_HOST_NAME}.json`);
375
+ }
376
+ }
377
+ function toShellLiteral(value) {
378
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
379
+ }
380
+ function toBatchLiteral(value) {
381
+ return value.replace(/%/g, "%%").replace(/"/g, '""');
382
+ }
383
+
384
+ // src/tooling.ts
385
+ var import_node_path3 = __toESM(require("node:path"));
386
+ var import_node_process2 = __toESM(require("node:process"));
242
387
  var import_node_child_process = require("node:child_process");
243
388
  function detectToolPaths() {
244
- const nodePath = import_node_process.default.execPath;
389
+ const nodePath = import_node_process2.default.execPath;
245
390
  const npmPath = resolveExecutablePath("npm") ?? "npm";
246
391
  const codexPath = resolveExecutablePath("codex");
247
392
  return {
@@ -250,40 +395,38 @@ function detectToolPaths() {
250
395
  codexPath
251
396
  };
252
397
  }
253
- function resolveExecutablePath(command, env = import_node_process.default.env) {
254
- const result = (0, import_node_child_process.spawnSync)("which", [command], {
398
+ function resolveExecutablePath(command, env = import_node_process2.default.env, platform = import_node_process2.default.platform) {
399
+ const lookupCommand = platform === "win32" ? "where" : "which";
400
+ const result = (0, import_node_child_process.spawnSync)(lookupCommand, [command], {
255
401
  encoding: "utf8",
256
402
  env
257
403
  });
258
- if (result.status !== 0) {
404
+ if (result.error || result.status !== 0) {
259
405
  return null;
260
406
  }
261
- const executablePath = result.stdout.trim();
262
- return executablePath.length > 0 ? executablePath : null;
407
+ const executablePath = result.stdout.split(/\r?\n/).map((value) => value.trim()).find((value) => value.length > 0);
408
+ return executablePath ?? null;
263
409
  }
264
- function buildToolEnv(toolPaths, extraPaths = []) {
410
+ function buildToolEnv(toolPaths, extraPaths = [], platform = import_node_process2.default.platform) {
411
+ const supportedPlatform = assertSupportedPlatform(platform);
412
+ const pathSeparator = supportedPlatform === "win32" ? ";" : ":";
265
413
  const pathEntries = [
266
- import_node_path2.default.dirname(toolPaths.nodePath),
267
- import_node_path2.default.dirname(toolPaths.npmPath),
268
- toolPaths.codexPath ? import_node_path2.default.dirname(toolPaths.codexPath) : null,
414
+ import_node_path3.default.dirname(toolPaths.nodePath),
415
+ import_node_path3.default.dirname(toolPaths.npmPath),
416
+ toolPaths.codexPath ? import_node_path3.default.dirname(toolPaths.codexPath) : null,
269
417
  ...extraPaths,
270
- "/usr/local/bin",
271
- "/opt/homebrew/bin",
272
- "/usr/bin",
273
- "/bin",
274
- "/usr/sbin",
275
- "/sbin",
276
- import_node_process.default.env.PATH ?? null
418
+ ...defaultLauncherPathEntries(supportedPlatform, import_node_process2.default.env),
419
+ import_node_process2.default.env.PATH ?? null
277
420
  ].filter((entry, index, entries) => typeof entry === "string" && entries.indexOf(entry) === index);
278
421
  return {
279
- ...import_node_process.default.env,
280
- PATH: pathEntries.join(":"),
422
+ ...import_node_process2.default.env,
423
+ PATH: pathEntries.join(pathSeparator),
281
424
  ...toolPaths.codexPath ? { DEXLY_COMPANION_CODEX_PATH: toolPaths.codexPath } : {}
282
425
  };
283
426
  }
284
427
  async function runCommand(command, args, options = {}) {
285
428
  return await new Promise((resolve, reject) => {
286
- const child = (0, import_node_child_process.spawn)(command, args, {
429
+ const child = spawnManaged(command, args, {
287
430
  cwd: options.cwd,
288
431
  env: options.env,
289
432
  stdio: "pipe"
@@ -311,8 +454,8 @@ async function runCommand(command, args, options = {}) {
311
454
  });
312
455
  });
313
456
  }
314
- function resolveCodexVersion(codexCommand = import_node_process.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex", env = import_node_process.default.env) {
315
- const result = (0, import_node_child_process.spawnSync)(codexCommand, ["--version"], {
457
+ function resolveCodexVersion(codexCommand = import_node_process2.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex", env = import_node_process2.default.env) {
458
+ const result = spawnManagedSync(codexCommand, ["--version"], {
316
459
  encoding: "utf8",
317
460
  env
318
461
  });
@@ -321,8 +464,8 @@ function resolveCodexVersion(codexCommand = import_node_process.default.env.DEXL
321
464
  }
322
465
  return result.stdout.trim() || null;
323
466
  }
324
- function resolveNpmGlobalBinDir(npmPath, env = import_node_process.default.env) {
325
- const result = (0, import_node_child_process.spawnSync)(npmPath, ["config", "get", "prefix"], {
467
+ function resolveNpmGlobalBinDir(npmPath, env = import_node_process2.default.env, platform = import_node_process2.default.platform) {
468
+ const result = spawnManagedSync(npmPath, ["config", "get", "prefix"], {
326
469
  encoding: "utf8",
327
470
  env
328
471
  });
@@ -330,76 +473,151 @@ function resolveNpmGlobalBinDir(npmPath, env = import_node_process.default.env)
330
473
  return null;
331
474
  }
332
475
  const prefix = result.stdout.trim();
333
- return prefix.length > 0 ? import_node_path2.default.join(prefix, "bin") : null;
476
+ if (prefix.length === 0) {
477
+ return null;
478
+ }
479
+ return npmGlobalBinDirFromPrefix(prefix, platform);
480
+ }
481
+ function spawnManaged(command, args, options = {}) {
482
+ const invocation = buildSpawnInvocation(command, args, options.env);
483
+ return (0, import_node_child_process.spawn)(invocation.command, invocation.args, {
484
+ ...options,
485
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments
486
+ });
487
+ }
488
+ function spawnManagedSync(command, args, options = {}) {
489
+ const invocation = buildSpawnInvocation(command, args, options.env);
490
+ return (0, import_node_child_process.spawnSync)(invocation.command, invocation.args, {
491
+ ...options,
492
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments
493
+ });
494
+ }
495
+ function buildSpawnInvocation(command, args, env = import_node_process2.default.env, platform = import_node_process2.default.platform) {
496
+ if (platform !== "win32" || !isWindowsCommandScript(command, platform)) {
497
+ return {
498
+ command,
499
+ args
500
+ };
501
+ }
502
+ return {
503
+ command: resolveWindowsCommandShell(env),
504
+ args: ["/d", "/s", "/c", quoteWindowsCommand([command, ...args])],
505
+ windowsVerbatimArguments: false
506
+ };
507
+ }
508
+ function isWindowsCommandScript(command, platform = import_node_process2.default.platform) {
509
+ if (platform !== "win32") {
510
+ return false;
511
+ }
512
+ const normalized = command.trim().toLowerCase();
513
+ return normalized.endsWith(".cmd") || normalized.endsWith(".bat");
514
+ }
515
+ function resolveWindowsCommandShell(env = import_node_process2.default.env) {
516
+ const comSpec = env.ComSpec?.trim();
517
+ if (comSpec) {
518
+ return comSpec;
519
+ }
520
+ const systemRoot = env.SystemRoot?.trim() || "C:\\Windows";
521
+ return import_node_path3.default.join(systemRoot, "System32", "cmd.exe");
522
+ }
523
+ function quoteWindowsCommand(parts) {
524
+ return parts.map(quoteWindowsArgument).join(" ");
525
+ }
526
+ function quoteWindowsArgument(value) {
527
+ if (value.length === 0) {
528
+ return '""';
529
+ }
530
+ if (!/[ \t"&()^<>|]/.test(value)) {
531
+ return value;
532
+ }
533
+ return `"${value.replace(/"/g, '""')}"`;
534
+ }
535
+ function npmGlobalBinDirFromPrefix(prefix, platform = import_node_process2.default.platform) {
536
+ return platform === "win32" ? prefix : import_node_path3.default.join(prefix, "bin");
334
537
  }
335
538
 
336
539
  // src/install.ts
337
- async function rewriteCurrentLauncher(installRoot, toolPaths) {
338
- const launcherPath = installedHostLauncherPath(installRoot);
339
- await (0, import_promises2.mkdir)(import_node_path3.default.dirname(launcherPath), { recursive: true });
540
+ async function rewriteCurrentLauncher(installRoot, toolPaths, options = {}) {
541
+ const layout = resolvePlatformLayout({
542
+ installRoot,
543
+ platform: options.platform
544
+ });
545
+ const launcherPath = installedHostLauncherPath(installRoot, layout.platform);
546
+ await (0, import_promises2.mkdir)(import_node_path4.default.dirname(launcherPath), { recursive: true });
340
547
  await (0, import_promises2.writeFile)(
341
548
  launcherPath,
342
549
  renderHostLauncher({
550
+ platform: layout.platform,
343
551
  nodePath: toolPaths.nodePath,
344
552
  hostScriptPath: installedHostScriptPath(installRoot),
345
553
  codexPath: toolPaths.codexPath
346
554
  }),
347
555
  "utf8"
348
556
  );
349
- await (0, import_promises2.chmod)(launcherPath, 493);
557
+ await chmodIfSupported(layout.platform, launcherPath);
350
558
  return launcherPath;
351
559
  }
352
- async function ensureChromeHostManifest(installRoot, manifestDir) {
353
- const manifestPath = installedHostManifestPath(manifestDir);
354
- await (0, import_promises2.mkdir)(manifestDir, { recursive: true });
560
+ async function ensureChromeHostRegistration(installRoot, options = {}) {
561
+ const layout = resolvePlatformLayout({
562
+ installRoot,
563
+ platform: options.platform,
564
+ manifestDir: options.manifestDir,
565
+ windowsRegistryKey: options.windowsRegistryKey
566
+ });
567
+ const manifestPath = layout.hostRegistration.manifestPath;
568
+ await (0, import_promises2.mkdir)(import_node_path4.default.dirname(manifestPath), { recursive: true });
355
569
  await (0, import_promises2.writeFile)(
356
570
  manifestPath,
357
- `${JSON.stringify(buildChromeHostManifest(installedHostLauncherPath(installRoot), defaultChromeAllowedOrigins()), null, 2)}
571
+ `${JSON.stringify(
572
+ buildChromeHostManifest(installedHostLauncherPath(installRoot, layout.platform), layout.hostRegistration.allowedOrigins),
573
+ null,
574
+ 2
575
+ )}
358
576
  `,
359
577
  "utf8"
360
578
  );
579
+ if (layout.hostRegistration.kind === "windows-registry" && layout.hostRegistration.registryKey) {
580
+ await (options.registryWriter ?? writeWindowsRegistryManifestPath)(
581
+ layout.hostRegistration.registryKey,
582
+ manifestPath
583
+ );
584
+ }
361
585
  return manifestPath;
362
586
  }
363
- function renderHostLauncher(options) {
364
- const pathEntries = [
365
- import_node_path3.default.dirname(options.nodePath),
366
- options.codexPath ? import_node_path3.default.dirname(options.codexPath) : null,
367
- "/usr/local/bin",
368
- "/opt/homebrew/bin",
369
- "/usr/bin",
370
- "/bin",
371
- "/usr/sbin",
372
- "/sbin"
373
- ].filter((entry, index, entries) => typeof entry === "string" && entries.indexOf(entry) === index);
374
- const lines = [
375
- "#!/bin/sh",
376
- "set -eu",
377
- `export PATH=${toShellLiteral(pathEntries.join(":"))}`,
378
- options.codexPath ? `export DEXLY_COMPANION_CODEX_PATH=${toShellLiteral(options.codexPath)}` : null,
379
- `exec ${toShellLiteral(options.nodePath)} ${toShellLiteral(options.hostScriptPath)} "$@"`
380
- ].filter((line) => typeof line === "string");
381
- return `${lines.join("\n")}
382
- `;
587
+ async function chmodIfSupported(platform, targetPath) {
588
+ if (platform === "win32") {
589
+ return;
590
+ }
591
+ await (0, import_promises2.chmod)(targetPath, 493);
383
592
  }
384
- function toShellLiteral(value) {
385
- return `'${value.replace(/'/g, `'"'"'`)}'`;
593
+ async function writeWindowsRegistryManifestPath(registryKey, manifestPath) {
594
+ await runCommand("reg.exe", [
595
+ "add",
596
+ registryKey,
597
+ "/ve",
598
+ "/t",
599
+ "REG_SZ",
600
+ "/d",
601
+ manifestPath,
602
+ "/f"
603
+ ]);
386
604
  }
387
605
 
388
606
  // src/management.ts
389
607
  async function upgradeInstalledCompanion(options) {
390
- const installRoot = options.installRoot ?? defaultInstallRoot();
608
+ const installRoot = options.installRoot ?? defaultInstallRoot({ platform: options.platform });
391
609
  const metadata = await loadInstallMetadata(installRoot);
392
610
  if (!metadata) {
393
611
  throw new Error("Dexly Companion install metadata is missing. Reinstall Dexly Companion first.");
394
612
  }
395
613
  const specifier = options.version?.trim() ? options.version.trim() : options.distTag.trim();
396
- const targetSpecifier = `${import_companion3.DEXLY_COMPANION_PACKAGE_NAME}@${specifier}`;
397
- const env = buildToolEnv(metadata.tools);
614
+ const targetSpecifier = `${import_companion4.DEXLY_COMPANION_PACKAGE_NAME}@${specifier}`;
615
+ const env = buildToolEnv(metadata.tools, [], options.platform);
398
616
  await runCommand(metadata.tools.npmPath, [
399
617
  "exec",
400
618
  "--yes",
401
619
  `--package=${targetSpecifier}`,
402
- import_companion3.DEXLY_COMPANION_EXECUTABLE_NAME,
620
+ import_companion4.DEXLY_COMPANION_EXECUTABLE_NAME,
403
621
  "install",
404
622
  "--channel",
405
623
  options.distTag
@@ -415,15 +633,14 @@ async function upgradeInstalledCompanion(options) {
415
633
  };
416
634
  }
417
635
  async function installCodexWithCompanion(options) {
418
- const installRoot = options?.installRoot ?? defaultInstallRoot();
419
- const manifestDir = options?.manifestDir ?? defaultChromeHostManifestDir();
636
+ const installRoot = options?.installRoot ?? defaultInstallRoot({ platform: options?.platform });
420
637
  const existingMetadata = await loadInstallMetadata(installRoot);
421
638
  const toolPaths = existingMetadata?.tools ?? detectToolPaths();
422
- const env = buildToolEnv(toolPaths);
639
+ const env = buildToolEnv(toolPaths, [], options?.platform);
423
640
  await runCommand(toolPaths.npmPath, ["install", "-g", "@openai/codex"], { env });
424
- const globalBinDir = resolveNpmGlobalBinDir(toolPaths.npmPath, env);
425
- const nextEnv = buildToolEnv(toolPaths, globalBinDir ? [globalBinDir] : []);
426
- const codexPath = resolveExecutablePath("codex", nextEnv);
641
+ const globalBinDir = resolveNpmGlobalBinDir(toolPaths.npmPath, env, options?.platform);
642
+ const nextEnv = buildToolEnv(toolPaths, globalBinDir ? [globalBinDir] : [], options?.platform);
643
+ const codexPath = resolveExecutablePath("codex", nextEnv, options?.platform);
427
644
  const codexVersion = resolveCodexVersion(codexPath ?? void 0, nextEnv);
428
645
  if (!codexPath || !codexVersion) {
429
646
  throw new Error("Codex install completed, but Dexly Companion could not resolve the installed codex binary.");
@@ -432,8 +649,12 @@ async function installCodexWithCompanion(options) {
432
649
  ...toolPaths,
433
650
  codexPath
434
651
  };
435
- await rewriteCurrentLauncher(installRoot, nextToolPaths);
436
- await ensureChromeHostManifest(installRoot, manifestDir);
652
+ await rewriteCurrentLauncher(installRoot, nextToolPaths, { platform: options?.platform });
653
+ await ensureChromeHostRegistration(installRoot, {
654
+ platform: options?.platform,
655
+ manifestDir: options?.manifestDir,
656
+ windowsRegistryKey: options?.windowsRegistryKey
657
+ });
437
658
  if (existingMetadata) {
438
659
  await saveInstallMetadata(installRoot, {
439
660
  ...existingMetadata,
@@ -444,7 +665,7 @@ async function installCodexWithCompanion(options) {
444
665
  return {
445
666
  codexPath,
446
667
  codexVersion,
447
- installCommand: import_companion3.DEXLY_CODEX_INSTALL_COMMAND
668
+ installCommand: import_companion4.DEXLY_CODEX_INSTALL_COMMAND
448
669
  };
449
670
  }
450
671
 
@@ -462,9 +683,9 @@ var DexlyNativeHost = class {
462
683
  stderrLines = [];
463
684
  constructor(options) {
464
685
  this.send = options.send;
465
- this.spawnProcess = options.spawnProcess ?? import_node_child_process2.spawn;
466
- this.spawnProcessSync = options.spawnProcessSync ?? import_node_child_process2.spawnSync;
467
- this.codexCommand = options.codexCommand ?? import_node_process2.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex";
686
+ this.spawnProcess = options.spawnProcess ?? ((command, args, spawnOptions) => spawnManaged(command, [...args], spawnOptions));
687
+ this.spawnProcessSync = options.spawnProcessSync ?? ((command, args, spawnOptions) => spawnManagedSync(command, [...args], spawnOptions));
688
+ this.codexCommand = options.codexCommand ?? import_node_process3.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex";
468
689
  this.requestProcessExit = options.requestProcessExit ?? null;
469
690
  }
470
691
  async handleMessage(message) {
@@ -724,7 +945,7 @@ var DexlyNativeHost = class {
724
945
  null,
725
946
  null,
726
947
  "codex_not_connected",
727
- `${import_companion4.DEXLY_COMPANION_DISPLAY_NAME} is not connected to Codex app-server.`
948
+ `${import_companion5.DEXLY_COMPANION_DISPLAY_NAME} is not connected to Codex app-server.`
728
949
  ));
729
950
  return;
730
951
  }
@@ -814,19 +1035,19 @@ function writeNativeMessage(output, message) {
814
1035
  // src/host-runtime.ts
815
1036
  async function runNativeHost() {
816
1037
  const host = new DexlyNativeHost({
817
- send: (message) => writeNativeMessage(import_node_process3.default.stdout, message),
1038
+ send: (message) => writeNativeMessage(import_node_process4.default.stdout, message),
818
1039
  requestProcessExit: () => {
819
- setTimeout(() => import_node_process3.default.exit(0), 25);
1040
+ setTimeout(() => import_node_process4.default.exit(0), 25);
820
1041
  }
821
1042
  });
822
- import_node_process3.default.stdout.on("error", (error) => {
1043
+ import_node_process4.default.stdout.on("error", (error) => {
823
1044
  if ("code" in error && error.code === "EPIPE") {
824
- void host.close().finally(() => import_node_process3.default.exit(0));
1045
+ void host.close().finally(() => import_node_process4.default.exit(0));
825
1046
  }
826
1047
  });
827
- const detachReader = createNativeMessageReader(import_node_process3.default.stdin, (message) => {
1048
+ const detachReader = createNativeMessageReader(import_node_process4.default.stdin, (message) => {
828
1049
  void host.handleMessage(message).catch((error) => {
829
- writeNativeMessage(import_node_process3.default.stdout, {
1050
+ writeNativeMessage(import_node_process4.default.stdout, {
830
1051
  kind: "host/error",
831
1052
  action: null,
832
1053
  requestId: null,
@@ -839,21 +1060,21 @@ async function runNativeHost() {
839
1060
  detachReader();
840
1061
  await host.close();
841
1062
  };
842
- import_node_process3.default.stdin.on("end", () => {
843
- void shutdown().finally(() => import_node_process3.default.exit(0));
1063
+ import_node_process4.default.stdin.on("end", () => {
1064
+ void shutdown().finally(() => import_node_process4.default.exit(0));
844
1065
  });
845
- import_node_process3.default.on("SIGINT", () => {
846
- void shutdown().finally(() => import_node_process3.default.exit(0));
1066
+ import_node_process4.default.on("SIGINT", () => {
1067
+ void shutdown().finally(() => import_node_process4.default.exit(0));
847
1068
  });
848
- import_node_process3.default.on("SIGTERM", () => {
849
- void shutdown().finally(() => import_node_process3.default.exit(0));
1069
+ import_node_process4.default.on("SIGTERM", () => {
1070
+ void shutdown().finally(() => import_node_process4.default.exit(0));
850
1071
  });
851
1072
  }
852
1073
 
853
1074
  // src/host.ts
854
1075
  void runNativeHost().catch((error) => {
855
1076
  const message = error instanceof Error ? error.message : "Dexly Companion host failed.";
856
- import_node_process4.default.stderr.write(`${message}
1077
+ import_node_process5.default.stderr.write(`${message}
857
1078
  `);
858
- import_node_process4.default.exit(1);
1079
+ import_node_process5.default.exit(1);
859
1080
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexlyai/dexly",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "commonjs",
5
5
  "bin": {
6
6
  "dexly": "dist/cli.js"