@mkterswingman/5mghost-yonder 0.0.37 → 0.0.38
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/check.js +6 -2
- package/dist/cli/index.js +0 -0
- package/dist/cli/installSkills.js +3 -12
- package/dist/cli/serve.js +7 -3
- package/dist/cli/setup.js +24 -32
- package/dist/cli/uninstall.js +5 -18
- package/dist/server.d.ts +1 -1
- package/dist/tools/downloads.d.ts +1 -1
- package/dist/tools/remote.d.ts +1 -1
- package/dist/tools/subtitles.d.ts +1 -1
- package/dist/utils/launcher.d.ts +6 -11
- package/dist/utils/launcher.js +11 -82
- package/package.json +2 -1
- package/dist/auth/oauthFlow.d.ts +0 -9
- package/dist/auth/oauthFlow.js +0 -151
- package/dist/auth/sharedAuth.d.ts +0 -10
- package/dist/auth/sharedAuth.js +0 -31
- package/dist/auth/tokenManager.d.ts +0 -18
- package/dist/auth/tokenManager.js +0 -92
- package/dist/utils/codexInternal.d.ts +0 -9
- package/dist/utils/codexInternal.js +0 -60
- package/dist/utils/mcpRegistration.d.ts +0 -7
- package/dist/utils/mcpRegistration.js +0 -23
- package/dist/utils/openClaw.d.ts +0 -18
- package/dist/utils/openClaw.js +0 -82
- package/dist/utils/skills.d.ts +0 -16
- package/dist/utils/skills.js +0 -56
package/dist/cli/check.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { PATHS, loadConfig } from "../utils/config.js";
|
|
3
|
-
import { TokenManager } from "
|
|
3
|
+
import { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
import { hasSIDCookies, areCookiesExpired } from "../utils/cookies.js";
|
|
5
5
|
import { getYtDlpVersion } from "../utils/ytdlpPath.js";
|
|
6
6
|
import { runRuntimeCommand } from "./runtime.js";
|
|
@@ -9,7 +9,11 @@ import { runRuntimeCommand } from "./runtime.js";
|
|
|
9
9
|
*/
|
|
10
10
|
export async function runCheck() {
|
|
11
11
|
const config = loadConfig();
|
|
12
|
-
const tm = new TokenManager(
|
|
12
|
+
const tm = new TokenManager({
|
|
13
|
+
authUrl: config.auth_url,
|
|
14
|
+
authJsonPath: PATHS.sharedAuthJson,
|
|
15
|
+
envTokenName: "YT_MCP_TOKEN",
|
|
16
|
+
});
|
|
13
17
|
console.log("yt-mcp check\n");
|
|
14
18
|
// 1. Token
|
|
15
19
|
const token = await tm.getValidToken();
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
function detectCli(name) {
|
|
6
|
-
try {
|
|
7
|
-
execFileSync(name, ["--version"], { stdio: "pipe" });
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
3
|
+
import { buildSkillInstallPlan, installSkillTarget, } from "@mkterswingman/5mghost-shared-client/registration";
|
|
4
|
+
import { detectCli } from "@mkterswingman/5mghost-shared-client";
|
|
14
5
|
function resolvePackageRoot() {
|
|
15
6
|
return join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
16
7
|
}
|
|
@@ -24,7 +15,7 @@ export async function runInstallSkills() {
|
|
|
24
15
|
"gemini-internal",
|
|
25
16
|
"gemini",
|
|
26
17
|
].filter(detectCli);
|
|
27
|
-
const plan = buildSkillInstallPlan(packageRoot, {
|
|
18
|
+
const plan = buildSkillInstallPlan(packageRoot, "use-yt-mcp", {
|
|
28
19
|
availableCliNames,
|
|
29
20
|
includeOpenClaw: process.env.YT_MCP_INSTALL_OPENCLAW_SKILL === "1",
|
|
30
21
|
});
|
package/dist/cli/serve.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
-
import { loadConfig } from "../utils/config.js";
|
|
3
|
-
import { TokenManager } from "
|
|
2
|
+
import { loadConfig, PATHS } from "../utils/config.js";
|
|
3
|
+
import { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
import { DownloadJobManager } from "../download/jobManager.js";
|
|
5
5
|
import { createServer } from "../server.js";
|
|
6
6
|
export async function runServe() {
|
|
7
7
|
const config = loadConfig();
|
|
8
|
-
const tokenManager = new TokenManager(
|
|
8
|
+
const tokenManager = new TokenManager({
|
|
9
|
+
authUrl: config.auth_url,
|
|
10
|
+
authJsonPath: PATHS.sharedAuthJson,
|
|
11
|
+
envTokenName: "YT_MCP_TOKEN",
|
|
12
|
+
});
|
|
9
13
|
const downloadJobManager = new DownloadJobManager();
|
|
10
14
|
// PAT mode via env var (don't persist — just keep in memory for this session)
|
|
11
15
|
const pat = process.env.YT_MCP_TOKEN;
|
package/dist/cli/setup.js
CHANGED
|
@@ -2,25 +2,15 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { loadConfig, saveConfig, PATHS, ensureConfigDir } from "../utils/config.js";
|
|
5
|
-
import { TokenManager } from "
|
|
6
|
-
import { runOAuthFlow } from "
|
|
5
|
+
import { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
6
|
+
import { runOAuthFlow } from "@mkterswingman/5mghost-shared-client/auth";
|
|
7
7
|
import { hasSIDCookies } from "../utils/cookies.js";
|
|
8
8
|
import { buildLauncherCommand, writeLauncherFile } from "../utils/launcher.js";
|
|
9
9
|
import { buildBrowserOpenCommand } from "../utils/browserLaunch.js";
|
|
10
10
|
import { checkAll } from "../runtime/installers.js";
|
|
11
11
|
import { runInstallSkills } from "./installSkills.js";
|
|
12
|
-
import {
|
|
13
|
-
import { getCodexInternalConfigPath, writeCodexInternalConfig, } from "
|
|
14
|
-
import { MCP_REGISTER_TIMEOUT_MS, classifyRegistrationFailure, } from "../utils/mcpRegistration.js";
|
|
15
|
-
function detectCli(name) {
|
|
16
|
-
try {
|
|
17
|
-
execFileSync(name, ["--version"], { stdio: "pipe" });
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
12
|
+
import { registerCliCandidates, registerOpenClaw, } from "@mkterswingman/5mghost-shared-client/registration";
|
|
13
|
+
import { getOpenClawConfigPath, getCodexInternalConfigPath, writeCodexInternalConfig, detectCli, isOpenClawInstallLikelyInstalled, MCP_REGISTER_TIMEOUT_MS, classifyRegistrationFailure, } from "@mkterswingman/5mghost-shared-client";
|
|
24
14
|
function tryRegisterMcp(cmd, label) {
|
|
25
15
|
try {
|
|
26
16
|
execFileSync(cmd.file, cmd.args, { stdio: "pipe", timeout: MCP_REGISTER_TIMEOUT_MS });
|
|
@@ -215,7 +205,11 @@ export async function runSetup() {
|
|
|
215
205
|
console.log(` ✅ Shared auth: ${PATHS.sharedAuthJson}`);
|
|
216
206
|
// ── Step 3: Authentication ──
|
|
217
207
|
console.log("Step 3/5: Authentication...");
|
|
218
|
-
const tokenManager = new TokenManager(
|
|
208
|
+
const tokenManager = new TokenManager({
|
|
209
|
+
authUrl: config.auth_url,
|
|
210
|
+
authJsonPath: PATHS.sharedAuthJson,
|
|
211
|
+
envTokenName: "YT_MCP_TOKEN",
|
|
212
|
+
});
|
|
219
213
|
const pat = process.env.YT_MCP_TOKEN;
|
|
220
214
|
if (pat) {
|
|
221
215
|
// Explicit PAT provided via env var
|
|
@@ -270,7 +264,11 @@ export async function runSetup() {
|
|
|
270
264
|
}
|
|
271
265
|
console.log(` OAuth 等待上限:${Math.round(oauthTimeoutMs / 1000)}s`);
|
|
272
266
|
try {
|
|
273
|
-
const tokens = await runOAuthFlow(
|
|
267
|
+
const tokens = await runOAuthFlow({
|
|
268
|
+
clientName: "yt-mcp-cli",
|
|
269
|
+
authUrl: config.auth_url,
|
|
270
|
+
timeoutMs: oauthTimeoutMs,
|
|
271
|
+
});
|
|
274
272
|
await tokenManager.saveTokens(tokens.accessToken, tokens.refreshToken, tokens.expiresIn, tokens.clientId);
|
|
275
273
|
console.log(" ✅ OAuth login successful");
|
|
276
274
|
console.log(" ℹ️ Other first-party local MCPs on this machine can reuse this login.");
|
|
@@ -363,18 +361,10 @@ export async function runSetup() {
|
|
|
363
361
|
// ── Step 5: MCP Registration ──
|
|
364
362
|
console.log("Step 5/5: Registering MCP in AI clients...");
|
|
365
363
|
const launcherCommand = buildLauncherCommand();
|
|
366
|
-
let registered =
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
args: launcherCommand.args,
|
|
371
|
-
})) {
|
|
372
|
-
if (!detectCli(bin))
|
|
373
|
-
continue;
|
|
374
|
-
if (tryRegisterMcp(command, label)) {
|
|
375
|
-
registered = true;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
364
|
+
let registered = registerCliCandidates({
|
|
365
|
+
serverName: "yt-mcp",
|
|
366
|
+
launcherCommand,
|
|
367
|
+
});
|
|
378
368
|
if (isOpenClawInstallLikelyInstalled(detectCli)) {
|
|
379
369
|
try {
|
|
380
370
|
let openClawRegistered = false;
|
|
@@ -385,10 +375,11 @@ export async function runSetup() {
|
|
|
385
375
|
}), "OpenClaw (mcporter config)");
|
|
386
376
|
}
|
|
387
377
|
if (!openClawRegistered) {
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
378
|
+
const result = registerOpenClaw("yt-mcp", launcherCommand);
|
|
379
|
+
if (result) {
|
|
380
|
+
console.log(` ✅ MCP registered in OpenClaw (${result.method === "cli" ? "mcporter config" : result.status} ${getOpenClawConfigPath()})`);
|
|
381
|
+
openClawRegistered = true;
|
|
382
|
+
}
|
|
392
383
|
}
|
|
393
384
|
if (openClawRegistered) {
|
|
394
385
|
registered = true;
|
|
@@ -409,6 +400,7 @@ export async function runSetup() {
|
|
|
409
400
|
console.log(` ⚠️ Codex CLI (internal) auto-register failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
410
401
|
}
|
|
411
402
|
}
|
|
403
|
+
let skillsInstalled = false;
|
|
412
404
|
try {
|
|
413
405
|
process.env.YT_MCP_INSTALL_OPENCLAW_SKILL = isOpenClawInstallLikelyInstalled(detectCli) ? "1" : "0";
|
|
414
406
|
await runInstallSkills();
|
package/dist/cli/uninstall.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
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
|
-
import { getOpenClawConfigPath,
|
|
5
|
-
import {
|
|
6
|
-
function detectCli(name) {
|
|
7
|
-
try {
|
|
8
|
-
execFileSync(name, ["--version"], { stdio: "pipe" });
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
4
|
+
import { getOpenClawConfigPath, getCodexInternalConfigPath, removeCodexInternalConfig, unregisterOpenClaw, detectCli, } from "@mkterswingman/5mghost-shared-client";
|
|
5
|
+
import { unregisterCliCandidates } from "@mkterswingman/5mghost-shared-client/registration";
|
|
15
6
|
function tryRemoveMcp(command, label) {
|
|
16
7
|
try {
|
|
17
8
|
execFileSync(command.file, command.args, { stdio: "pipe" });
|
|
@@ -64,13 +55,9 @@ function scheduleSelfUninstall() {
|
|
|
64
55
|
export async function runUninstall() {
|
|
65
56
|
console.log("\n🧹 yt-mcp uninstall\n");
|
|
66
57
|
console.log("Removing MCP client registrations...");
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
tryRemoveMcp(candidate.command, candidate.label);
|
|
71
|
-
}
|
|
72
|
-
const openClawStatus = removeOpenClawConfig("yt-mcp");
|
|
73
|
-
if (openClawStatus === "removed") {
|
|
58
|
+
unregisterCliCandidates({ serverName: "yt-mcp" });
|
|
59
|
+
const openClawResult = unregisterOpenClaw("yt-mcp");
|
|
60
|
+
if (openClawResult === "removed") {
|
|
74
61
|
console.log(` ✅ Removed MCP registration from OpenClaw (${getOpenClawConfigPath()})`);
|
|
75
62
|
}
|
|
76
63
|
else {
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { YtMcpConfig } from "./utils/config.js";
|
|
3
|
-
import type { TokenManager } from "
|
|
3
|
+
import type { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
import { DownloadJobManager } from "./download/jobManager.js";
|
|
5
5
|
import type { DownloadToolDeps } from "./tools/downloads.js";
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { YtMcpConfig } from "../utils/config.js";
|
|
3
|
-
import type { TokenManager } from "
|
|
3
|
+
import type { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
import { downloadOneItem } from "../download/downloader.js";
|
|
5
5
|
import type { DownloadJobManager } from "../download/jobManager.js";
|
|
6
6
|
export interface DownloadToolDeps {
|
package/dist/tools/remote.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { YtMcpConfig } from "../utils/config.js";
|
|
3
|
-
import type { TokenManager } from "
|
|
3
|
+
import type { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
export declare function registerRemoteTools(server: McpServer, config: YtMcpConfig, tokenManager: TokenManager): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { YtMcpConfig } from "../utils/config.js";
|
|
3
|
-
import type { TokenManager } from "
|
|
3
|
+
import type { TokenManager } from "@mkterswingman/5mghost-shared-client/auth";
|
|
4
4
|
export declare function toReadableSubtitleJobError(error: unknown): string;
|
|
5
5
|
/**
|
|
6
6
|
* Convert VTT subtitle content to clean, human-readable CSV.
|
package/dist/utils/launcher.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
export interface LauncherCommand {
|
|
6
|
-
command: string;
|
|
7
|
-
args: string[];
|
|
8
|
-
}
|
|
1
|
+
import { isRepairableNpxFailure } from "@mkterswingman/5mghost-shared-client/launcher";
|
|
2
|
+
import type { LauncherCommand, LauncherSourceOptions } from "@mkterswingman/5mghost-shared-client/launcher";
|
|
3
|
+
export type { LauncherCommand, LauncherSourceOptions };
|
|
4
|
+
export { isRepairableNpxFailure };
|
|
9
5
|
export declare function buildLauncherCommand(launcherPath?: string): LauncherCommand;
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function writeLauncherFile(options?: LauncherSourceOptions): string;
|
|
6
|
+
export declare function buildLauncherSource(options?: Partial<LauncherSourceOptions>): string;
|
|
7
|
+
export declare function writeLauncherFile(options?: Partial<LauncherSourceOptions>): string;
|
package/dist/utils/launcher.js
CHANGED
|
@@ -1,90 +1,19 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
1
|
import { PATHS } from "./config.js";
|
|
2
|
+
import { buildLauncherCommand as _buildLauncherCommand, buildLauncherSource as _buildLauncherSource, writeLauncherFile as _writeLauncherFile, isRepairableNpxFailure, } from "@mkterswingman/5mghost-shared-client/launcher";
|
|
3
|
+
export { isRepairableNpxFailure };
|
|
4
4
|
const DEFAULT_PACKAGE_SPEC = "@mkterswingman/5mghost-yonder@latest";
|
|
5
5
|
export function buildLauncherCommand(launcherPath = PATHS.launcherJs) {
|
|
6
|
-
return
|
|
7
|
-
command: "node",
|
|
8
|
-
args: [launcherPath, "serve"],
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export function isRepairableNpxFailure(stderr) {
|
|
12
|
-
const lower = stderr.toLowerCase();
|
|
13
|
-
const referencesNpxDir = lower.includes("_npx/") || lower.includes("_npx\\");
|
|
14
|
-
return referencesNpxDir && (lower.includes("enotempty") || lower.includes("rename"));
|
|
6
|
+
return _buildLauncherCommand(launcherPath);
|
|
15
7
|
}
|
|
16
8
|
export function buildLauncherSource(options = {}) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import { join } from "node:path";
|
|
22
|
-
import { spawnSync } from "node:child_process";
|
|
23
|
-
|
|
24
|
-
const packageSpec = ${JSON.stringify(packageSpec)};
|
|
25
|
-
const npmCacheDir = ${JSON.stringify(npmCacheDir)};
|
|
26
|
-
const args = process.argv.slice(2);
|
|
27
|
-
const targetArgs = args.length > 0 ? args : ["serve"];
|
|
28
|
-
|
|
29
|
-
function isSkillInstallerMode(subArgs) {
|
|
30
|
-
return subArgs[0] === "install-skills";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function isRepairableNpxFailure(stderr) {
|
|
34
|
-
const lower = stderr.toLowerCase();
|
|
35
|
-
return (lower.includes("_npx/") || lower.includes("_npx\\\\"))
|
|
36
|
-
&& (lower.includes("enotempty") || lower.includes("rename"));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function runNpx(subArgs, captureStdErrOnly = false) {
|
|
40
|
-
mkdirSync(npmCacheDir, { recursive: true });
|
|
41
|
-
const npxBin = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
42
|
-
return spawnSync(npxBin, ["--yes", packageSpec, ...subArgs], {
|
|
43
|
-
env: { ...process.env, npm_config_cache: npmCacheDir },
|
|
44
|
-
// Why: probe runs before MCP starts; stdout must stay silent or it will corrupt stdio transport.
|
|
45
|
-
// The skill installer is an explicit one-shot CLI path, so it can inherit stdio safely.
|
|
46
|
-
stdio: captureStdErrOnly ? ["ignore", "ignore", "pipe"] : "inherit",
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function rotateNpxDir() {
|
|
51
|
-
const npxDir = join(npmCacheDir, "_npx");
|
|
52
|
-
if (!existsSync(npxDir)) return false;
|
|
53
|
-
const backup = \`\${npxDir}.bad.\${new Date().toISOString().replace(/[^0-9]/g, "").slice(0, 14)}\`;
|
|
54
|
-
renameSync(npxDir, backup);
|
|
55
|
-
process.stderr.write(\`[yt-mcp launcher] repaired corrupted npx cache: \${backup}\\n\`);
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function ensurePackageReady() {
|
|
60
|
-
const first = runNpx(["version"], true);
|
|
61
|
-
if ((first.status ?? 1) === 0) return;
|
|
62
|
-
|
|
63
|
-
const stderr = first.stderr ? String(first.stderr) : "";
|
|
64
|
-
if (!isRepairableNpxFailure(stderr) || !rotateNpxDir()) {
|
|
65
|
-
if (stderr) process.stderr.write(stderr);
|
|
66
|
-
process.exit(first.status ?? 1);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const second = runNpx(["version"], true);
|
|
70
|
-
const secondStderr = second.stderr ? String(second.stderr) : "";
|
|
71
|
-
if ((second.status ?? 1) !== 0) {
|
|
72
|
-
if (secondStderr) process.stderr.write(secondStderr);
|
|
73
|
-
process.exit(second.status ?? 1);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
ensurePackageReady();
|
|
78
|
-
if (isSkillInstallerMode(targetArgs)) {
|
|
79
|
-
process.env.YT_MCP_INSTALL_SKILLS = "1";
|
|
80
|
-
}
|
|
81
|
-
const finalRun = runNpx(targetArgs, false);
|
|
82
|
-
process.exit(finalRun.status ?? 0);
|
|
83
|
-
`;
|
|
9
|
+
return _buildLauncherSource({
|
|
10
|
+
packageSpec: options.packageSpec ?? DEFAULT_PACKAGE_SPEC,
|
|
11
|
+
npmCacheDir: options.npmCacheDir ?? PATHS.npmCacheDir,
|
|
12
|
+
});
|
|
84
13
|
}
|
|
85
14
|
export function writeLauncherFile(options = {}) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
15
|
+
return _writeLauncherFile({
|
|
16
|
+
packageSpec: options.packageSpec ?? DEFAULT_PACKAGE_SPEC,
|
|
17
|
+
npmCacheDir: options.npmCacheDir ?? PATHS.npmCacheDir,
|
|
18
|
+
}, PATHS.launcherJs);
|
|
90
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mkterswingman/5mghost-yonder",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.38",
|
|
4
4
|
"description": "Internal MCP client with local data tools and remote API proxy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"start": "node dist/cli/index.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@mkterswingman/5mghost-shared-client": "^0.0.2",
|
|
20
21
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
21
22
|
"playwright": "^1.58.0",
|
|
22
23
|
"zod": "^4.3.6"
|
package/dist/auth/oauthFlow.d.ts
DELETED
package/dist/auth/oauthFlow.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
|
-
import { randomBytes, createHash } from "node:crypto";
|
|
3
|
-
import { URL } from "node:url";
|
|
4
|
-
import { buildBrowserOpenCommand } from "../utils/browserLaunch.js";
|
|
5
|
-
function base64url(buf) {
|
|
6
|
-
return buf
|
|
7
|
-
.toString("base64")
|
|
8
|
-
.replace(/\+/g, "-")
|
|
9
|
-
.replace(/\//g, "_")
|
|
10
|
-
.replace(/=+$/, "");
|
|
11
|
-
}
|
|
12
|
-
export async function runOAuthFlow(authUrl, options = {}) {
|
|
13
|
-
// 1. Generate PKCE + state
|
|
14
|
-
const codeVerifier = base64url(randomBytes(32));
|
|
15
|
-
const codeChallenge = base64url(createHash("sha256").update(codeVerifier).digest());
|
|
16
|
-
const state = base64url(randomBytes(32));
|
|
17
|
-
// 2. Start temp HTTP server to get the actual port for redirect_uri
|
|
18
|
-
const { server: httpServer, port } = await startCallbackServer();
|
|
19
|
-
const redirectUri = `http://127.0.0.1:${port}`;
|
|
20
|
-
// 3. DCR register client with actual redirect_uri (including port)
|
|
21
|
-
let clientId;
|
|
22
|
-
let clientSecret;
|
|
23
|
-
try {
|
|
24
|
-
const dcrRes = await fetch(`${authUrl}/oauth/register`, {
|
|
25
|
-
method: "POST",
|
|
26
|
-
headers: { "Content-Type": "application/json" },
|
|
27
|
-
body: JSON.stringify({
|
|
28
|
-
client_name: "yt-mcp-cli",
|
|
29
|
-
redirect_uris: [redirectUri],
|
|
30
|
-
}),
|
|
31
|
-
});
|
|
32
|
-
if (!dcrRes.ok) {
|
|
33
|
-
const text = await dcrRes.text().catch(() => "");
|
|
34
|
-
httpServer.close();
|
|
35
|
-
throw new Error(`DCR registration failed: ${dcrRes.status} ${text}`);
|
|
36
|
-
}
|
|
37
|
-
const dcrBody = (await dcrRes.json());
|
|
38
|
-
clientId = dcrBody.client_id;
|
|
39
|
-
clientSecret = dcrBody.client_secret;
|
|
40
|
-
}
|
|
41
|
-
catch (err) {
|
|
42
|
-
httpServer.close();
|
|
43
|
-
throw err;
|
|
44
|
-
}
|
|
45
|
-
// 4. Wait for OAuth callback
|
|
46
|
-
return new Promise((resolve, reject) => {
|
|
47
|
-
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1000;
|
|
48
|
-
const timeout = setTimeout(() => {
|
|
49
|
-
httpServer.close();
|
|
50
|
-
reject(new Error(`OAuth flow timed out after ${Math.round(timeoutMs / 1000)}s`));
|
|
51
|
-
}, timeoutMs);
|
|
52
|
-
function cleanup() {
|
|
53
|
-
clearTimeout(timeout);
|
|
54
|
-
httpServer.close();
|
|
55
|
-
}
|
|
56
|
-
httpServer.on("request", async (req, res) => {
|
|
57
|
-
try {
|
|
58
|
-
const url = new URL(req.url ?? "/", `http://127.0.0.1`);
|
|
59
|
-
const code = url.searchParams.get("code");
|
|
60
|
-
const error = url.searchParams.get("error");
|
|
61
|
-
const returnedState = url.searchParams.get("state");
|
|
62
|
-
if (error) {
|
|
63
|
-
const safeError = error.replace(/[<>&"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
64
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
65
|
-
res.end(`<h1>Authorization failed</h1><p>${safeError}</p>`);
|
|
66
|
-
cleanup();
|
|
67
|
-
reject(new Error(`OAuth error: ${error}`));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (!code) {
|
|
71
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
72
|
-
res.end("<h1>Waiting for authorization...</h1>");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// Verify state to prevent CSRF
|
|
76
|
-
if (returnedState !== state) {
|
|
77
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
78
|
-
res.end("<h1>Authorization failed</h1><p>State mismatch — possible CSRF attack.</p>");
|
|
79
|
-
cleanup();
|
|
80
|
-
reject(new Error("OAuth state mismatch"));
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
// Exchange code for tokens
|
|
84
|
-
const tokenBody = {
|
|
85
|
-
grant_type: "authorization_code",
|
|
86
|
-
code,
|
|
87
|
-
redirect_uri: redirectUri,
|
|
88
|
-
client_id: clientId,
|
|
89
|
-
code_verifier: codeVerifier,
|
|
90
|
-
};
|
|
91
|
-
if (clientSecret) {
|
|
92
|
-
tokenBody.client_secret = clientSecret;
|
|
93
|
-
}
|
|
94
|
-
const tokenRes = await fetch(`${authUrl}/oauth/token`, {
|
|
95
|
-
method: "POST",
|
|
96
|
-
headers: { "Content-Type": "application/json" },
|
|
97
|
-
body: JSON.stringify(tokenBody),
|
|
98
|
-
});
|
|
99
|
-
if (!tokenRes.ok) {
|
|
100
|
-
const text = await tokenRes.text().catch(() => "");
|
|
101
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
102
|
-
res.end(`<h1>Token exchange failed</h1><p>${text}</p>`);
|
|
103
|
-
cleanup();
|
|
104
|
-
reject(new Error(`Token exchange failed: ${tokenRes.status} ${text}`));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const tokens = (await tokenRes.json());
|
|
108
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
109
|
-
res.end("<h1>Authorization successful!</h1><p>You can close this window.</p>");
|
|
110
|
-
cleanup();
|
|
111
|
-
resolve({
|
|
112
|
-
accessToken: tokens.access_token,
|
|
113
|
-
refreshToken: tokens.refresh_token,
|
|
114
|
-
expiresIn: tokens.expires_in,
|
|
115
|
-
clientId,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
catch (err) {
|
|
119
|
-
res.writeHead(500);
|
|
120
|
-
res.end("Internal error");
|
|
121
|
-
cleanup();
|
|
122
|
-
reject(err);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
// Open browser
|
|
126
|
-
const authorizeUrl = `${authUrl}/oauth/authorize?response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${encodeURIComponent(codeChallenge)}&code_challenge_method=S256&state=${encodeURIComponent(state)}`;
|
|
127
|
-
console.log("\n\x1b[1mOpen this URL in your browser to authorize:\x1b[0m");
|
|
128
|
-
console.log(`\n ${authorizeUrl}\n`);
|
|
129
|
-
import("node:child_process").then(({ execFile }) => {
|
|
130
|
-
const command = buildBrowserOpenCommand(authorizeUrl);
|
|
131
|
-
execFile(command.file, command.args);
|
|
132
|
-
}).catch(() => {
|
|
133
|
-
// ignore — user can open manually
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
/** Start an HTTP server on a random port and return the server + port. */
|
|
138
|
-
async function startCallbackServer() {
|
|
139
|
-
const server = createServer();
|
|
140
|
-
return new Promise((resolve, reject) => {
|
|
141
|
-
server.listen(0, "127.0.0.1", () => {
|
|
142
|
-
const addr = server.address();
|
|
143
|
-
if (!addr || typeof addr === "string") {
|
|
144
|
-
server.close();
|
|
145
|
-
reject(new Error("Failed to start callback server"));
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
resolve({ server, port: addr.port });
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface SharedAuthData {
|
|
2
|
-
type: "jwt" | "pat";
|
|
3
|
-
access_token?: string;
|
|
4
|
-
refresh_token?: string;
|
|
5
|
-
expires_at?: number;
|
|
6
|
-
client_id?: string;
|
|
7
|
-
pat?: string;
|
|
8
|
-
}
|
|
9
|
-
export declare function readSharedAuth(authPath: string): SharedAuthData | null;
|
|
10
|
-
export declare function writeSharedAuth(authPath: string, data: SharedAuthData): void;
|
package/dist/auth/sharedAuth.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
export function readSharedAuth(authPath) {
|
|
4
|
-
if (!existsSync(authPath))
|
|
5
|
-
return null;
|
|
6
|
-
try {
|
|
7
|
-
return JSON.parse(readFileSync(authPath, "utf8"));
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export function writeSharedAuth(authPath, data) {
|
|
14
|
-
const authDir = dirname(authPath);
|
|
15
|
-
mkdirSync(authDir, { recursive: true });
|
|
16
|
-
try {
|
|
17
|
-
chmodSync(authDir, 0o700);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
// Why: best-effort hardening; Windows may ignore POSIX-style directory modes.
|
|
21
|
-
}
|
|
22
|
-
const tempPath = `${authPath}.tmp`;
|
|
23
|
-
writeFileSync(tempPath, JSON.stringify(data, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
24
|
-
renameSync(tempPath, authPath);
|
|
25
|
-
try {
|
|
26
|
-
chmodSync(authPath, 0o600);
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
// Why: chmod is best-effort on Windows and should not block auth persistence.
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
interface TokenManagerOptions {
|
|
2
|
-
authPath?: string;
|
|
3
|
-
env?: NodeJS.ProcessEnv;
|
|
4
|
-
fetchImpl?: typeof fetch;
|
|
5
|
-
}
|
|
6
|
-
export declare class TokenManager {
|
|
7
|
-
private authUrl;
|
|
8
|
-
private authPath;
|
|
9
|
-
private env;
|
|
10
|
-
private fetchImpl;
|
|
11
|
-
constructor(authUrl: string, options?: TokenManagerOptions);
|
|
12
|
-
getValidToken(): Promise<string | null>;
|
|
13
|
-
saveTokens(accessToken: string, refreshToken: string, expiresIn: number, clientId?: string): Promise<void>;
|
|
14
|
-
savePAT(pat: string): Promise<void>;
|
|
15
|
-
private readAuth;
|
|
16
|
-
private refreshTokens;
|
|
17
|
-
}
|
|
18
|
-
export {};
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { PATHS } from "../utils/config.js";
|
|
2
|
-
import { readSharedAuth, writeSharedAuth } from "./sharedAuth.js";
|
|
3
|
-
export class TokenManager {
|
|
4
|
-
authUrl;
|
|
5
|
-
authPath;
|
|
6
|
-
env;
|
|
7
|
-
fetchImpl;
|
|
8
|
-
constructor(authUrl, options = {}) {
|
|
9
|
-
this.authUrl = authUrl;
|
|
10
|
-
this.authPath = options.authPath ?? PATHS.sharedAuthJson;
|
|
11
|
-
this.env = options.env ?? process.env;
|
|
12
|
-
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
13
|
-
}
|
|
14
|
-
async getValidToken() {
|
|
15
|
-
// Check env var PAT first
|
|
16
|
-
const envPat = this.env.YT_MCP_TOKEN;
|
|
17
|
-
if (envPat)
|
|
18
|
-
return envPat;
|
|
19
|
-
const auth = this.readAuth();
|
|
20
|
-
if (!auth)
|
|
21
|
-
return null;
|
|
22
|
-
if (auth.type === "pat" && auth.pat) {
|
|
23
|
-
return auth.pat;
|
|
24
|
-
}
|
|
25
|
-
if (auth.type === "jwt") {
|
|
26
|
-
if (auth.access_token && auth.expires_at && Date.now() < auth.expires_at) {
|
|
27
|
-
return auth.access_token;
|
|
28
|
-
}
|
|
29
|
-
// Try refresh
|
|
30
|
-
if (auth.refresh_token) {
|
|
31
|
-
try {
|
|
32
|
-
const refreshed = await this.refreshTokens(auth.refresh_token, auth.client_id);
|
|
33
|
-
if (refreshed) {
|
|
34
|
-
await this.saveTokens(refreshed.access_token, refreshed.refresh_token, refreshed.expires_in, auth.client_id);
|
|
35
|
-
return refreshed.access_token;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// refresh failed → AUTH_EXPIRED
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
async saveTokens(accessToken, refreshToken, expiresIn, clientId) {
|
|
47
|
-
const data = {
|
|
48
|
-
type: "jwt",
|
|
49
|
-
access_token: accessToken,
|
|
50
|
-
refresh_token: refreshToken,
|
|
51
|
-
expires_at: Date.now() + expiresIn * 1000,
|
|
52
|
-
client_id: clientId,
|
|
53
|
-
};
|
|
54
|
-
writeSharedAuth(this.authPath, data);
|
|
55
|
-
}
|
|
56
|
-
async savePAT(pat) {
|
|
57
|
-
const data = {
|
|
58
|
-
type: "pat",
|
|
59
|
-
pat,
|
|
60
|
-
};
|
|
61
|
-
writeSharedAuth(this.authPath, data);
|
|
62
|
-
}
|
|
63
|
-
readAuth() {
|
|
64
|
-
return readSharedAuth(this.authPath);
|
|
65
|
-
}
|
|
66
|
-
async refreshTokens(refreshToken, clientId) {
|
|
67
|
-
const url = `${this.authUrl}/oauth/token`;
|
|
68
|
-
const body = {
|
|
69
|
-
grant_type: "refresh_token",
|
|
70
|
-
refresh_token: refreshToken,
|
|
71
|
-
};
|
|
72
|
-
if (clientId) {
|
|
73
|
-
body.client_id = clientId;
|
|
74
|
-
}
|
|
75
|
-
const res = await this.fetchImpl(url, {
|
|
76
|
-
method: "POST",
|
|
77
|
-
headers: { "Content-Type": "application/json" },
|
|
78
|
-
body: JSON.stringify(body),
|
|
79
|
-
});
|
|
80
|
-
if (!res.ok)
|
|
81
|
-
return null;
|
|
82
|
-
try {
|
|
83
|
-
const body = (await res.json());
|
|
84
|
-
if (!body.access_token)
|
|
85
|
-
return null;
|
|
86
|
-
return body;
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
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";
|
|
@@ -1,60 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare const MCP_REGISTER_TIMEOUT_MS = 5000;
|
|
2
|
-
export type RegistrationFailureKind = "already_exists" | "authentication" | "timeout" | "other";
|
|
3
|
-
export interface RegistrationFailure {
|
|
4
|
-
kind: RegistrationFailureKind;
|
|
5
|
-
output: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function classifyRegistrationFailure(err: unknown): RegistrationFailure;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const MCP_REGISTER_TIMEOUT_MS = 5000;
|
|
2
|
-
function readOutput(err) {
|
|
3
|
-
if (!(err instanceof Error))
|
|
4
|
-
return "";
|
|
5
|
-
const stderr = "stderr" in err ? String(err.stderr ?? "") : "";
|
|
6
|
-
const stdout = "stdout" in err ? String(err.stdout ?? "") : "";
|
|
7
|
-
return `${stderr}${stdout}`;
|
|
8
|
-
}
|
|
9
|
-
export function classifyRegistrationFailure(err) {
|
|
10
|
-
const output = readOutput(err);
|
|
11
|
-
const lower = output.toLowerCase();
|
|
12
|
-
if (lower.includes("already exists")) {
|
|
13
|
-
return { kind: "already_exists", output };
|
|
14
|
-
}
|
|
15
|
-
if (lower.includes("waiting for authentication")) {
|
|
16
|
-
return { kind: "authentication", output };
|
|
17
|
-
}
|
|
18
|
-
if (err instanceof Error &&
|
|
19
|
-
("code" in err && err.code === "ETIMEDOUT")) {
|
|
20
|
-
return { kind: "timeout", output };
|
|
21
|
-
}
|
|
22
|
-
return { kind: "other", output };
|
|
23
|
-
}
|
package/dist/utils/openClaw.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { LauncherCommand } from "./launcher.js";
|
|
2
|
-
export interface OpenClawServerConfig {
|
|
3
|
-
transport: "stdio";
|
|
4
|
-
command: string;
|
|
5
|
-
args: string[];
|
|
6
|
-
env?: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
export declare function getOpenClawConfigPath(homeDir?: string): string;
|
|
9
|
-
export declare function getOpenClawSkillsDir(homeDir?: string): string;
|
|
10
|
-
export declare function buildOpenClawServerConfig(launcherCommand: LauncherCommand): OpenClawServerConfig;
|
|
11
|
-
export declare function upsertOpenClawConfigText(currentText: string | null, serverName: string, launcherCommand: LauncherCommand): string;
|
|
12
|
-
export declare function removeOpenClawConfigEntryText(currentText: string | null, serverName: string): {
|
|
13
|
-
changed: boolean;
|
|
14
|
-
nextText: string | null;
|
|
15
|
-
};
|
|
16
|
-
export declare function isOpenClawInstallLikelyInstalled(detectBinary: (name: string) => boolean, configPath?: string): boolean;
|
|
17
|
-
export declare function writeOpenClawConfig(serverName: string, launcherCommand: LauncherCommand, configPath?: string): "created" | "updated";
|
|
18
|
-
export declare function removeOpenClawConfig(serverName: string, configPath?: string): "removed" | "missing";
|
package/dist/utils/openClaw.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
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 getOpenClawConfigPath(homeDir = homedir()) {
|
|
5
|
-
return join(homeDir, ".openclaw", "workspace", "config", "mcporter.json");
|
|
6
|
-
}
|
|
7
|
-
export function getOpenClawSkillsDir(homeDir = homedir()) {
|
|
8
|
-
return join(homeDir, ".openclaw", "skills");
|
|
9
|
-
}
|
|
10
|
-
export function buildOpenClawServerConfig(launcherCommand) {
|
|
11
|
-
return {
|
|
12
|
-
transport: "stdio",
|
|
13
|
-
command: launcherCommand.command,
|
|
14
|
-
args: [...launcherCommand.args],
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function parseOpenClawConfig(raw) {
|
|
18
|
-
const parsed = JSON.parse(raw);
|
|
19
|
-
if (parsed && typeof parsed === "object") {
|
|
20
|
-
return parsed;
|
|
21
|
-
}
|
|
22
|
-
throw new Error("OpenClaw config must be a JSON object");
|
|
23
|
-
}
|
|
24
|
-
export function upsertOpenClawConfigText(currentText, serverName, launcherCommand) {
|
|
25
|
-
const config = currentText ? parseOpenClawConfig(currentText) : {};
|
|
26
|
-
const servers = config.servers && typeof config.servers === "object" ? { ...config.servers } : {};
|
|
27
|
-
servers[serverName] = buildOpenClawServerConfig(launcherCommand);
|
|
28
|
-
const nextConfig = {
|
|
29
|
-
...config,
|
|
30
|
-
servers,
|
|
31
|
-
};
|
|
32
|
-
return JSON.stringify(nextConfig, null, 2);
|
|
33
|
-
}
|
|
34
|
-
export function removeOpenClawConfigEntryText(currentText, serverName) {
|
|
35
|
-
if (!currentText) {
|
|
36
|
-
return { changed: false, nextText: null };
|
|
37
|
-
}
|
|
38
|
-
const config = parseOpenClawConfig(currentText);
|
|
39
|
-
if (!config.servers || typeof config.servers !== "object" || !(serverName in config.servers)) {
|
|
40
|
-
return { changed: false, nextText: currentText };
|
|
41
|
-
}
|
|
42
|
-
const servers = { ...config.servers };
|
|
43
|
-
delete servers[serverName];
|
|
44
|
-
const nextConfig = { ...config };
|
|
45
|
-
if (Object.keys(servers).length === 0) {
|
|
46
|
-
delete nextConfig.servers;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
nextConfig.servers = servers;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
changed: true,
|
|
53
|
-
nextText: JSON.stringify(nextConfig, null, 2),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
export function isOpenClawInstallLikelyInstalled(detectBinary, configPath = getOpenClawConfigPath()) {
|
|
57
|
-
if (detectBinary("mcporter") || detectBinary("openclaw")) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return existsSync(configPath) || existsSync(dirname(configPath));
|
|
61
|
-
}
|
|
62
|
-
export function writeOpenClawConfig(serverName, launcherCommand, configPath = getOpenClawConfigPath()) {
|
|
63
|
-
const existingText = existsSync(configPath) ? readFileSync(configPath, "utf8") : null;
|
|
64
|
-
const created = existingText === null;
|
|
65
|
-
const nextText = upsertOpenClawConfigText(existingText, serverName, launcherCommand);
|
|
66
|
-
mkdirSync(dirname(configPath), { recursive: true });
|
|
67
|
-
writeFileSync(configPath, `${nextText}\n`, "utf8");
|
|
68
|
-
return created ? "created" : "updated";
|
|
69
|
-
}
|
|
70
|
-
export function removeOpenClawConfig(serverName, configPath = getOpenClawConfigPath()) {
|
|
71
|
-
const existingText = existsSync(configPath) ? readFileSync(configPath, "utf8") : null;
|
|
72
|
-
const result = removeOpenClawConfigEntryText(existingText, serverName);
|
|
73
|
-
if (!result.changed) {
|
|
74
|
-
return "missing";
|
|
75
|
-
}
|
|
76
|
-
if (!result.nextText) {
|
|
77
|
-
rmSync(configPath, { force: true });
|
|
78
|
-
return "removed";
|
|
79
|
-
}
|
|
80
|
-
writeFileSync(configPath, `${result.nextText}\n`, "utf8");
|
|
81
|
-
return "removed";
|
|
82
|
-
}
|
package/dist/utils/skills.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { LauncherCommand } from "./launcher.js";
|
|
2
|
-
export interface SkillInstallTarget {
|
|
3
|
-
client: string;
|
|
4
|
-
label: string;
|
|
5
|
-
sourceDir: string;
|
|
6
|
-
targetDir: string;
|
|
7
|
-
}
|
|
8
|
-
export interface SkillInstallPlanOptions {
|
|
9
|
-
homeDir?: string;
|
|
10
|
-
availableCliNames?: string[];
|
|
11
|
-
includeOpenClaw?: boolean;
|
|
12
|
-
}
|
|
13
|
-
export declare function getSkillPackageSourcePath(packageRoot: string): string;
|
|
14
|
-
export declare function buildSkillLauncherCommand(launcherPath: string): LauncherCommand;
|
|
15
|
-
export declare function buildSkillInstallPlan(packageRoot: string, options?: SkillInstallPlanOptions): SkillInstallTarget[];
|
|
16
|
-
export declare function installSkillTarget(target: SkillInstallTarget): void;
|
package/dist/utils/skills.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { getOpenClawSkillsDir } from "./openClaw.js";
|
|
5
|
-
const SKILL_NAME = "use-yt-mcp";
|
|
6
|
-
export function getSkillPackageSourcePath(packageRoot) {
|
|
7
|
-
return join(packageRoot, "skills", SKILL_NAME);
|
|
8
|
-
}
|
|
9
|
-
export function buildSkillLauncherCommand(launcherPath) {
|
|
10
|
-
return {
|
|
11
|
-
command: "node",
|
|
12
|
-
args: [launcherPath, "install-skills"],
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
export function buildSkillInstallPlan(packageRoot, options = {}) {
|
|
16
|
-
const homeDir = options.homeDir ?? homedir();
|
|
17
|
-
const availableCliNames = new Set(options.availableCliNames ?? []);
|
|
18
|
-
const sourceDir = getSkillPackageSourcePath(packageRoot);
|
|
19
|
-
const plan = [];
|
|
20
|
-
const cliTargets = [
|
|
21
|
-
{ client: "claude-internal", label: "Claude Code (internal)", dir: join(homeDir, ".claude-internal", "skills") },
|
|
22
|
-
{ client: "claude", label: "Claude Code", dir: join(homeDir, ".claude", "skills") },
|
|
23
|
-
{ client: "codex-internal", label: "Codex CLI (internal)", dir: join(homeDir, ".codex-internal", "skills") },
|
|
24
|
-
{ client: "codex", label: "Codex CLI / Codex App", dir: join(homeDir, ".codex", "skills") },
|
|
25
|
-
{ client: "gemini-internal", label: "Gemini CLI (internal)", dir: join(homeDir, ".gemini-internal", "skills") },
|
|
26
|
-
{ client: "gemini", label: "Gemini CLI", dir: join(homeDir, ".gemini", "skills") },
|
|
27
|
-
];
|
|
28
|
-
for (const target of cliTargets) {
|
|
29
|
-
if (!availableCliNames.has(target.client)) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
plan.push({
|
|
33
|
-
client: target.client,
|
|
34
|
-
label: target.label,
|
|
35
|
-
sourceDir,
|
|
36
|
-
targetDir: join(target.dir, SKILL_NAME),
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
if (options.includeOpenClaw) {
|
|
40
|
-
plan.push({
|
|
41
|
-
client: "openclaw",
|
|
42
|
-
label: "OpenClaw",
|
|
43
|
-
sourceDir,
|
|
44
|
-
targetDir: join(getOpenClawSkillsDir(homeDir), SKILL_NAME),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return plan;
|
|
48
|
-
}
|
|
49
|
-
export function installSkillTarget(target) {
|
|
50
|
-
if (!existsSync(target.sourceDir)) {
|
|
51
|
-
throw new Error(`Bundled skill not found: ${target.sourceDir}`);
|
|
52
|
-
}
|
|
53
|
-
mkdirSync(dirname(target.targetDir), { recursive: true });
|
|
54
|
-
rmSync(target.targetDir, { recursive: true, force: true });
|
|
55
|
-
cpSync(target.sourceDir, target.targetDir, { recursive: true });
|
|
56
|
-
}
|