@mkterswingman/5mghost-yonder 0.0.7 → 0.0.9

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.
@@ -6,6 +6,7 @@ export interface UnifiedUpdateDeps {
6
6
  installLatestPackage(): Promise<void>;
7
7
  updateRuntime(): Promise<import("../runtime/installers.js").RuntimeSummary>;
8
8
  }
9
+ export declare function compareVersions(currentVersion: string, latestVersion: string): number;
9
10
  export declare function runUnifiedUpdate(deps: UnifiedUpdateDeps): Promise<{
10
11
  package: {
11
12
  currentVersion: string;
package/dist/cli/index.js CHANGED
@@ -35,11 +35,25 @@ Environment variables:
35
35
  YT_MCP_TOKEN Personal Access Token (skips OAuth)
36
36
  `;
37
37
  }
38
+ export function compareVersions(currentVersion, latestVersion) {
39
+ const normalize = (value) => value.split(".").map((part) => Number.parseInt(part, 10)).map((part) => (Number.isFinite(part) ? part : 0));
40
+ const current = normalize(currentVersion);
41
+ const latest = normalize(latestVersion);
42
+ const max = Math.max(current.length, latest.length);
43
+ for (let index = 0; index < max; index += 1) {
44
+ const a = current[index] ?? 0;
45
+ const b = latest[index] ?? 0;
46
+ if (a === b)
47
+ continue;
48
+ return a < b ? -1 : 1;
49
+ }
50
+ return 0;
51
+ }
38
52
  export async function runUnifiedUpdate(deps) {
39
53
  const currentVersion = deps.getCurrentVersion();
40
54
  const latestVersion = await deps.getLatestVersion();
41
55
  let updated = false;
42
- if (latestVersion !== currentVersion) {
56
+ if (compareVersions(currentVersion, latestVersion) < 0) {
43
57
  await deps.installLatestPackage();
44
58
  updated = true;
45
59
  }
@@ -1,3 +1,13 @@
1
+ interface CliCommand {
2
+ file: string;
3
+ args: string[];
4
+ }
5
+ interface SetupCliCandidate {
6
+ bin: string;
7
+ label: string;
8
+ command: CliCommand;
9
+ }
10
+ export declare function buildSetupCliCandidates(launcherCommand: CliCommand): SetupCliCandidate[];
1
11
  export declare function canOpenBrowserForEnv(input: {
2
12
  platform: NodeJS.Platform;
3
13
  env: NodeJS.ProcessEnv;
@@ -7,3 +17,4 @@ export declare function getNoBrowserSessionNotice(): string;
7
17
  export declare function getNoBrowserPatHint(authUrl: string): string[];
8
18
  export declare function getCookieSetupDeferredHint(): string[];
9
19
  export declare function runSetup(): Promise<void>;
20
+ export {};
package/dist/cli/setup.js CHANGED
@@ -7,6 +7,7 @@ import { hasSIDCookies } from "../utils/cookies.js";
7
7
  import { buildLauncherCommand, writeLauncherFile } from "../utils/launcher.js";
8
8
  import { checkAll } from "../runtime/installers.js";
9
9
  import { getOpenClawConfigPath, isOpenClawInstallLikelyInstalled, writeOpenClawConfig, } from "../utils/openClaw.js";
10
+ import { getCodexInternalConfigPath, writeCodexInternalConfig, } from "../utils/codexInternal.js";
10
11
  import { MCP_REGISTER_TIMEOUT_MS, classifyRegistrationFailure, } from "../utils/mcpRegistration.js";
11
12
  function detectCli(name) {
12
13
  try {
@@ -40,6 +41,16 @@ function tryRegisterMcp(cmd, label) {
40
41
  return false;
41
42
  }
42
43
  }
44
+ export function buildSetupCliCandidates(launcherCommand) {
45
+ return [
46
+ { bin: "claude-internal", label: "Claude Code (internal)", command: { file: "claude-internal", args: ["mcp", "add", "-s", "user", "yt-mcp", "--", launcherCommand.file, ...launcherCommand.args] } },
47
+ { bin: "claude", label: "Claude Code", command: { file: "claude", args: ["mcp", "add", "-s", "user", "yt-mcp", "--", launcherCommand.file, ...launcherCommand.args] } },
48
+ { bin: "codex", label: "Codex CLI / Codex App", command: { file: "codex", args: ["mcp", "add", "yt-mcp", "--", launcherCommand.file, ...launcherCommand.args] } },
49
+ { bin: "gemini-internal", label: "Gemini CLI (internal)", command: { file: "gemini-internal", args: ["mcp", "add", "-s", "user", "yt-mcp", launcherCommand.file, ...launcherCommand.args] } },
50
+ { bin: "gemini", label: "Gemini CLI", command: { file: "gemini", args: ["mcp", "add", "-s", "user", "yt-mcp", launcherCommand.file, ...launcherCommand.args] } },
51
+ { bin: "opencode", label: "OpenCode", command: { file: "opencode", args: ["mcp", "add", "yt-mcp", "--", launcherCommand.file, ...launcherCommand.args] } },
52
+ ];
53
+ }
43
54
  export function canOpenBrowserForEnv(input) {
44
55
  if (input.env.YT_MCP_NO_BROWSER === "1") {
45
56
  return false;
@@ -243,36 +254,12 @@ export async function runSetup() {
243
254
  console.log("Step 5/5: Registering MCP in AI clients...");
244
255
  const launcherCommand = buildLauncherCommand();
245
256
  let registered = false;
246
- const cliCandidates = [
247
- // Claude Code: {bin} mcp add yt-mcp -- npx ... serve
248
- { bin: "claude-internal", label: "Claude Code (internal)",
249
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "-s", "user", "yt-mcp", "--", launcher.command, ...launcher.args] }) },
250
- { bin: "claude", label: "Claude Code",
251
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "-s", "user", "yt-mcp", "--", launcher.command, ...launcher.args] }) },
252
- // Codex (public): {bin} mcp add yt-mcp -- npx ... serve
253
- { bin: "codex", label: "Codex CLI / Codex App",
254
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "yt-mcp", "--", launcher.command, ...launcher.args] }) },
255
- // Codex-internal doesn't support mcp add — needs manual config
256
- { bin: "codex-internal", label: "Codex CLI (internal)",
257
- cmd: () => null },
258
- // Gemini: {bin} mcp add -s user yt-mcp node <launcher> serve (no --)
259
- { bin: "gemini-internal", label: "Gemini CLI (internal)",
260
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "-s", "user", "yt-mcp", launcher.command, ...launcher.args] }) },
261
- { bin: "gemini", label: "Gemini CLI",
262
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "-s", "user", "yt-mcp", launcher.command, ...launcher.args] }) },
263
- // Others: assume Claude-style syntax
264
- { bin: "opencode", label: "OpenCode",
265
- cmd: (b, launcher) => ({ file: b, args: ["mcp", "add", "yt-mcp", "--", launcher.command, ...launcher.args] }) },
266
- ];
267
- for (const { bin, label, cmd } of cliCandidates) {
257
+ for (const { bin, label, command } of buildSetupCliCandidates({
258
+ file: launcherCommand.command,
259
+ args: launcherCommand.args,
260
+ })) {
268
261
  if (!detectCli(bin))
269
262
  continue;
270
- const command = cmd(bin, launcherCommand);
271
- if (!command) {
272
- // CLI detected but doesn't support auto-registration
273
- console.log(` ⚠️ ${label} detected but requires manual MCP config.`);
274
- continue;
275
- }
276
263
  if (tryRegisterMcp(command, label)) {
277
264
  registered = true;
278
265
  }
@@ -288,6 +275,17 @@ export async function runSetup() {
288
275
  console.log(` ⚠️ OpenClaw auto-register failed: ${err instanceof Error ? err.message : String(err)}`);
289
276
  }
290
277
  }
278
+ if (detectCli("codex-internal")) {
279
+ try {
280
+ const status = writeCodexInternalConfig("yt-mcp", launcherCommand);
281
+ const suffix = status === "created" ? "created" : "updated";
282
+ console.log(` ✅ MCP registered in Codex CLI (internal) (${suffix} ${getCodexInternalConfigPath()})`);
283
+ registered = true;
284
+ }
285
+ catch (err) {
286
+ console.log(` ⚠️ Codex CLI (internal) auto-register failed: ${err instanceof Error ? err.message : String(err)}`);
287
+ }
288
+ }
291
289
  if (!registered) {
292
290
  console.log(" ℹ️ No supported CLI found. Add manually to your AI client:");
293
291
  }
@@ -1 +1,16 @@
1
+ interface CliCommand {
2
+ file: string;
3
+ args: string[];
4
+ }
5
+ interface UninstallCliCandidate {
6
+ bin: string;
7
+ label: string;
8
+ command: CliCommand;
9
+ }
10
+ export declare function buildUninstallCliCandidates(): UninstallCliCandidate[];
11
+ export declare function buildSelfUninstallInvocation(): {
12
+ command: string;
13
+ args: string[];
14
+ };
1
15
  export declare function runUninstall(): Promise<void>;
16
+ export {};
@@ -1,7 +1,8 @@
1
- import { execFileSync } from "node:child_process";
1
+ import { execFileSync, spawn } from "node:child_process";
2
2
  import { existsSync, rmSync } from "node:fs";
3
3
  import { PATHS } from "../utils/config.js";
4
4
  import { getOpenClawConfigPath, removeOpenClawConfig } from "../utils/openClaw.js";
5
+ import { getCodexInternalConfigPath, removeCodexInternalConfig, } from "../utils/codexInternal.js";
5
6
  function detectCli(name) {
6
7
  try {
7
8
  execFileSync(name, ["--version"], { stdio: "pipe" });
@@ -30,10 +31,8 @@ function tryRemoveMcp(command, label) {
30
31
  return "failed";
31
32
  }
32
33
  }
33
- export async function runUninstall() {
34
- console.log("\n🧹 yt-mcp uninstall\n");
35
- console.log("Removing MCP client registrations...");
36
- const cliCandidates = [
34
+ export function buildUninstallCliCandidates() {
35
+ return [
37
36
  { bin: "claude-internal", label: "Claude Code (internal)", command: { file: "claude-internal", args: ["mcp", "remove", "-s", "user", "yt-mcp"] } },
38
37
  { bin: "claude", label: "Claude Code", command: { file: "claude", args: ["mcp", "remove", "-s", "user", "yt-mcp"] } },
39
38
  { bin: "codex", label: "Codex CLI / Codex App", command: { file: "codex", args: ["mcp", "remove", "yt-mcp"] } },
@@ -41,8 +40,31 @@ export async function runUninstall() {
41
40
  { bin: "gemini", label: "Gemini CLI", command: { file: "gemini", args: ["mcp", "remove", "-s", "user", "yt-mcp"] } },
42
41
  { bin: "opencode", label: "OpenCode", command: { file: "opencode", args: ["mcp", "remove", "yt-mcp"] } },
43
42
  ];
44
- for (const candidate of cliCandidates) {
45
- if (!detectCli(candidate.bin) || !candidate.command)
43
+ }
44
+ export function buildSelfUninstallInvocation() {
45
+ const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
46
+ const helper = process.platform === "win32"
47
+ ? `setTimeout(() => { require("node:child_process").spawnSync(${JSON.stringify(npmBin)}, ["uninstall","-g","@mkterswingman/5mghost-yonder"], { stdio: "ignore", shell: false }); }, 1500);`
48
+ : `setTimeout(() => { require("node:child_process").spawnSync(${JSON.stringify(npmBin)}, ["uninstall","-g","@mkterswingman/5mghost-yonder"], { stdio: "ignore" }); }, 1500);`;
49
+ return {
50
+ command: process.execPath,
51
+ args: ["-e", helper],
52
+ };
53
+ }
54
+ function scheduleSelfUninstall() {
55
+ const invocation = buildSelfUninstallInvocation();
56
+ const child = spawn(invocation.command, invocation.args, {
57
+ detached: true,
58
+ stdio: "ignore",
59
+ windowsHide: true,
60
+ });
61
+ child.unref();
62
+ }
63
+ export async function runUninstall() {
64
+ console.log("\n🧹 yt-mcp uninstall\n");
65
+ console.log("Removing MCP client registrations...");
66
+ for (const candidate of buildUninstallCliCandidates()) {
67
+ if (!detectCli(candidate.bin))
46
68
  continue;
47
69
  tryRemoveMcp(candidate.command, candidate.label);
48
70
  }
@@ -53,6 +75,15 @@ export async function runUninstall() {
53
75
  else {
54
76
  console.log(" ℹ️ OpenClaw did not have yt-mcp registered");
55
77
  }
78
+ if (detectCli("codex-internal")) {
79
+ const codexInternalStatus = removeCodexInternalConfig("yt-mcp");
80
+ if (codexInternalStatus === "removed") {
81
+ console.log(` ✅ Removed MCP registration from Codex CLI (internal) (${getCodexInternalConfigPath()})`);
82
+ }
83
+ else {
84
+ console.log(" ℹ️ Codex CLI (internal) did not have yt-mcp registered");
85
+ }
86
+ }
56
87
  if (existsSync(PATHS.configDir)) {
57
88
  // Why: ~/.yt-mcp contains launcher, token cache, cookies, and npm cache owned by this package.
58
89
  rmSync(PATHS.configDir, { recursive: true, force: true });
@@ -62,6 +93,7 @@ export async function runUninstall() {
62
93
  console.log(` ℹ️ Local yt-mcp config already absent: ${PATHS.configDir}`);
63
94
  }
64
95
  console.log(` ℹ️ Preserved downloaded media: ${PATHS.subtitlesDir}`);
65
- console.log(" ℹ️ If you installed globally, remove the package with: npm uninstall -g @mkterswingman/5mghost-yonder");
96
+ scheduleSelfUninstall();
97
+ console.log(" ✅ Scheduled global npm package removal");
66
98
  console.log("");
67
99
  }
@@ -0,0 +1,9 @@
1
+ import type { LauncherCommand } from "./launcher.js";
2
+ export declare function getCodexInternalConfigPath(homeDir?: string): string;
3
+ export declare function upsertCodexInternalConfigText(currentText: string | null, serverName: string, launcherCommand: LauncherCommand): string;
4
+ export declare function removeCodexInternalConfigEntryText(currentText: string | null, serverName: string): {
5
+ changed: boolean;
6
+ nextText: string | null;
7
+ };
8
+ export declare function writeCodexInternalConfig(serverName: string, launcherCommand: LauncherCommand, configPath?: string): "created" | "updated";
9
+ export declare function removeCodexInternalConfig(serverName: string, configPath?: string): "removed" | "missing";
@@ -0,0 +1,60 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ export function getCodexInternalConfigPath(homeDir = homedir()) {
5
+ return join(homeDir, ".codex-internal", "config.toml");
6
+ }
7
+ function formatCodexInternalSection(serverName, launcherCommand) {
8
+ const escapedName = serverName.replace(/"/g, '\\"');
9
+ const args = launcherCommand.args.map((arg) => JSON.stringify(arg)).join(", ");
10
+ return `[mcp_servers.${JSON.stringify(escapedName)}]
11
+ command = ${JSON.stringify(launcherCommand.command)}
12
+ args = [${args}]
13
+ `;
14
+ }
15
+ export function upsertCodexInternalConfigText(currentText, serverName, launcherCommand) {
16
+ const section = formatCodexInternalSection(serverName, launcherCommand).trimEnd();
17
+ const sectionPattern = new RegExp(String.raw `(?:^|\n)\[mcp_servers\.${JSON.stringify(serverName).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n(?:.+\n?)*?(?=\n\[|$)`, "m");
18
+ if (!currentText || currentText.trim() === "") {
19
+ return `${section}\n`;
20
+ }
21
+ if (sectionPattern.test(currentText)) {
22
+ return `${currentText.replace(sectionPattern, `\n${section}\n`).trim()}\n`;
23
+ }
24
+ return `${currentText.trimEnd()}\n\n${section}\n`;
25
+ }
26
+ export function removeCodexInternalConfigEntryText(currentText, serverName) {
27
+ if (!currentText) {
28
+ return { changed: false, nextText: null };
29
+ }
30
+ const sectionPattern = new RegExp(String.raw `(?:^|\n)\[mcp_servers\.${JSON.stringify(serverName).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n(?:.+\n?)*?(?=\n\[|$)`, "m");
31
+ if (!sectionPattern.test(currentText)) {
32
+ return { changed: false, nextText: currentText };
33
+ }
34
+ const nextText = currentText.replace(sectionPattern, "\n").replace(/\n{3,}/g, "\n\n").trim();
35
+ return {
36
+ changed: true,
37
+ nextText: nextText ? `${nextText}\n` : null,
38
+ };
39
+ }
40
+ export function writeCodexInternalConfig(serverName, launcherCommand, configPath = getCodexInternalConfigPath()) {
41
+ const existingText = existsSync(configPath) ? readFileSync(configPath, "utf8") : null;
42
+ const created = existingText === null;
43
+ const nextText = upsertCodexInternalConfigText(existingText, serverName, launcherCommand);
44
+ mkdirSync(dirname(configPath), { recursive: true });
45
+ writeFileSync(configPath, nextText, "utf8");
46
+ return created ? "created" : "updated";
47
+ }
48
+ export function removeCodexInternalConfig(serverName, configPath = getCodexInternalConfigPath()) {
49
+ const existingText = existsSync(configPath) ? readFileSync(configPath, "utf8") : null;
50
+ const result = removeCodexInternalConfigEntryText(existingText, serverName);
51
+ if (!result.changed) {
52
+ return "missing";
53
+ }
54
+ if (!result.nextText) {
55
+ rmSync(configPath, { force: true });
56
+ return "removed";
57
+ }
58
+ writeFileSync(configPath, result.nextText, "utf8");
59
+ return "removed";
60
+ }
@@ -28,7 +28,7 @@ const targetArgs = args.length > 0 ? args : ["serve"];
28
28
 
29
29
  function isRepairableNpxFailure(stderr) {
30
30
  const lower = stderr.toLowerCase();
31
- return (lower.includes("_npx/") || lower.includes("_npx\\"))
31
+ return (lower.includes("_npx/") || lower.includes("_npx\\\\"))
32
32
  && (lower.includes("enotempty") || lower.includes("rename"));
33
33
  }
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkterswingman/5mghost-yonder",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Internal MCP client with local data tools and remote API proxy",
5
5
  "type": "module",
6
6
  "bin": {