@mkterswingman/5mghost-yonder 0.0.7 → 0.0.8
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/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +15 -1
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.js +26 -28
- package/dist/cli/uninstall.d.ts +15 -0
- package/dist/cli/uninstall.js +40 -8
- package/dist/utils/codexInternal.d.ts +9 -0
- package/dist/utils/codexInternal.js +60 -0
- package/package.json +1 -1
package/dist/cli/index.d.ts
CHANGED
|
@@ -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
|
|
56
|
+
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
43
57
|
await deps.installLatestPackage();
|
|
44
58
|
updated = true;
|
|
45
59
|
}
|
package/dist/cli/setup.d.ts
CHANGED
|
@@ -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
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
}
|
package/dist/cli/uninstall.d.ts
CHANGED
|
@@ -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 {};
|
package/dist/cli/uninstall.js
CHANGED
|
@@ -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
|
|
34
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
+
}
|