codexport 0.1.6 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +61 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { homedir, platform } from "node:os";
11
11
  import path from "node:path";
12
12
  import { spawn } from "node:child_process";
13
13
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
14
- const VERSION = "0.1.6";
14
+ const VERSION = "0.1.7";
15
15
  const DEFAULT_PORT = 17342;
16
16
  const DEFAULT_TIMEOUT_MS = 5_000;
17
17
  const CODEXPORT_DIR = ".codexport";
@@ -415,19 +415,21 @@ function rewritePortableTableKeys(table, sourceRoot, sourceHome) {
415
415
  function rewritePortableMcpServer(_name, server, sourceRoot, sourceHome) {
416
416
  if (typeof server.url === "string")
417
417
  return;
418
- if (typeof server.command === "string" && Array.isArray(server.args)) {
419
- const nodePackage = nodePackageFromServer(server.command, server.args);
420
- if (nodePackage) {
421
- server.command = "npx";
422
- server.args = ["-y", nodePackage.packageName, ...nodePackage.remainingArgs.map((arg) => rewritePortablePath(arg, sourceRoot, sourceHome))];
423
- return;
424
- }
418
+ const command = typeof server.command === "string" ? server.command : undefined;
419
+ const args = Array.isArray(server.args) ? server.args : [];
420
+ const launcher = command ? portableMcpLauncher(_name, command, args, sourceHome) : undefined;
421
+ if (launcher) {
422
+ server.command = launcher.command;
423
+ server.args = launcher.args.map((arg) => rewritePortablePath(arg, sourceRoot, sourceHome));
424
+ }
425
+ else if (command && isAbsoluteAnyPlatform(command)) {
426
+ server.enabled = false;
425
427
  }
426
- if (typeof server.command === "string") {
427
- server.command = rewritePortableCommand(server.command, sourceRoot);
428
+ else if (command) {
429
+ server.command = rewritePortableCommand(command, sourceRoot);
428
430
  }
429
- if (Array.isArray(server.args)) {
430
- server.args = server.args.map((arg) => typeof arg === "string" ? rewritePortablePath(arg, sourceRoot, sourceHome) : arg);
431
+ if (!launcher && args.length) {
432
+ server.args = args.map((arg) => typeof arg === "string" ? rewritePortablePath(arg, sourceRoot, sourceHome) : arg);
431
433
  }
432
434
  if (server.env && typeof server.env === "object" && !Array.isArray(server.env)) {
433
435
  for (const [key, value] of Object.entries(server.env)) {
@@ -437,6 +439,34 @@ function rewritePortableMcpServer(_name, server, sourceRoot, sourceHome) {
437
439
  }
438
440
  }
439
441
  }
442
+ function portableMcpLauncher(name, command, args, sourceHome) {
443
+ const commandName = basenameAnyPlatform(command);
444
+ if (commandName === "npx" || commandName === "bunx" || commandName === "uvx") {
445
+ return allStrings(args) ? { command: commandName, args: args } : undefined;
446
+ }
447
+ const nodePackage = nodePackageFromServer(command, args) ?? workspacePackageFromServer(command, args, sourceHome);
448
+ if (nodePackage) {
449
+ return { command: "npx", args: ["-y", nodePackage.packageName, ...nodePackage.remainingArgs] };
450
+ }
451
+ const npmPackage = npmPackageForPortableMcp(name, commandName);
452
+ if (npmPackage) {
453
+ const remainingArgs = allStrings(args) ? args : [];
454
+ return { command: "npx", args: ["-y", npmPackage, ...remainingArgs] };
455
+ }
456
+ return undefined;
457
+ }
458
+ function npmPackageForPortableMcp(name, commandName) {
459
+ const knownPackages = {
460
+ "dora": "dora",
461
+ "kagi-mcp": "kagi-mcp",
462
+ "opensrc-mcp": "opensrc-mcp",
463
+ "opensrc-mcp-stdio": "opensrc-mcp",
464
+ "perplexity-webui": "perplexity-webui-mcp",
465
+ "perplexity-webui-mcp": "perplexity-webui-mcp",
466
+ "reddit-mcp-buddy": "reddit-mcp-buddy"
467
+ };
468
+ return knownPackages[name] ?? knownPackages[commandName];
469
+ }
440
470
  function rewritePortableCommand(command, sourceRoot) {
441
471
  const sourceRelative = rewriteSourceRootPath(command, sourceRoot);
442
472
  if (sourceRelative !== command)
@@ -457,13 +487,31 @@ function nodePackageFromServer(command, args) {
457
487
  const [entrypoint, ...remainingArgs] = args;
458
488
  if (typeof entrypoint !== "string" || !isAbsoluteAnyPlatform(entrypoint))
459
489
  return undefined;
460
- if (!remainingArgs.every((arg) => typeof arg === "string"))
490
+ if (!allStrings(remainingArgs))
461
491
  return undefined;
462
492
  const packageName = packageNameFromNodeModulesPath(entrypoint);
463
493
  if (!packageName)
464
494
  return undefined;
465
495
  return { packageName, remainingArgs: remainingArgs };
466
496
  }
497
+ function workspacePackageFromServer(command, args, sourceHome) {
498
+ if (basenameAnyPlatform(command) !== "node")
499
+ return undefined;
500
+ const [entrypoint, ...remainingArgs] = args;
501
+ if (!sourceHome || typeof entrypoint !== "string" || !isAbsoluteAnyPlatform(entrypoint) || !allStrings(remainingArgs))
502
+ return undefined;
503
+ const normalizedEntry = normalizePathForCompare(entrypoint);
504
+ const workspacePrefix = `${normalizePathForCompare(sourceHome)}/workspace/`;
505
+ if (!normalizedEntry.startsWith(workspacePrefix))
506
+ return undefined;
507
+ const packageName = normalizedEntry.slice(workspacePrefix.length).split("/")[0];
508
+ if (!packageName)
509
+ return undefined;
510
+ return { packageName, remainingArgs: remainingArgs };
511
+ }
512
+ function allStrings(values) {
513
+ return values.every((value) => typeof value === "string");
514
+ }
467
515
  function packageNameFromNodeModulesPath(value) {
468
516
  const parts = normalizePathForCompare(value).split("/");
469
517
  const nodeModulesIndex = parts.lastIndexOf("node_modules");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexport",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "sync a canonical Codex setup from one master machine to follower machines",
5
5
  "author": "Microck <contact@micr.dev>",
6
6
  "license": "MIT",