@mkterswingman/5mghost-yonder 0.0.1 → 0.0.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.
- package/README.md +44 -3
- package/dist/auth/sharedAuth.d.ts +10 -0
- package/dist/auth/sharedAuth.js +24 -0
- package/dist/auth/tokenManager.d.ts +10 -1
- package/dist/auth/tokenManager.js +14 -22
- package/dist/cli/check.d.ts +4 -0
- package/dist/cli/check.js +90 -0
- package/dist/cli/index.d.ts +15 -1
- package/dist/cli/index.js +76 -27
- package/dist/cli/runtime.d.ts +9 -0
- package/dist/cli/runtime.js +35 -0
- package/dist/cli/serve.js +3 -1
- package/dist/cli/setup.js +60 -61
- package/dist/cli/setupCookies.js +2 -2
- package/dist/cli/smoke.d.ts +27 -0
- package/dist/cli/smoke.js +108 -0
- package/dist/cli/uninstall.d.ts +1 -0
- package/dist/cli/uninstall.js +67 -0
- package/dist/download/downloader.d.ts +64 -0
- package/dist/download/downloader.js +264 -0
- package/dist/download/jobManager.d.ts +21 -0
- package/dist/download/jobManager.js +198 -0
- package/dist/download/types.d.ts +43 -0
- package/dist/download/types.js +1 -0
- package/dist/runtime/ffmpegRuntime.d.ts +13 -0
- package/dist/runtime/ffmpegRuntime.js +51 -0
- package/dist/runtime/installers.d.ts +12 -0
- package/dist/runtime/installers.js +45 -0
- package/dist/runtime/manifest.d.ts +18 -0
- package/dist/runtime/manifest.js +43 -0
- package/dist/runtime/playwrightRuntime.d.ts +13 -0
- package/dist/runtime/playwrightRuntime.js +37 -0
- package/dist/runtime/systemDeps.d.ts +3 -0
- package/dist/runtime/systemDeps.js +30 -0
- package/dist/runtime/ytdlpRuntime.d.ts +14 -0
- package/dist/runtime/ytdlpRuntime.js +58 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +4 -1
- package/dist/tools/downloads.d.ts +11 -0
- package/dist/tools/downloads.js +220 -0
- package/dist/tools/subtitles.d.ts +25 -0
- package/dist/tools/subtitles.js +135 -47
- package/dist/utils/config.d.ts +28 -0
- package/dist/utils/config.js +40 -11
- package/dist/utils/ffmpeg.d.ts +5 -0
- package/dist/utils/ffmpeg.js +16 -0
- package/dist/utils/ffmpegPath.d.ts +8 -0
- package/dist/utils/ffmpegPath.js +21 -0
- package/dist/utils/formatters.d.ts +4 -0
- package/dist/utils/formatters.js +42 -0
- package/dist/utils/mediaPaths.d.ts +7 -0
- package/dist/utils/mediaPaths.js +10 -0
- package/dist/utils/openClaw.d.ts +17 -0
- package/dist/utils/openClaw.js +79 -0
- package/dist/utils/videoInput.js +3 -0
- package/dist/utils/videoMetadata.d.ts +11 -0
- package/dist/utils/videoMetadata.js +1 -0
- package/dist/utils/ytdlp.d.ts +17 -1
- package/dist/utils/ytdlp.js +89 -2
- package/dist/utils/ytdlpPath.d.ts +9 -2
- package/dist/utils/ytdlpPath.js +19 -20
- package/dist/utils/ytdlpProgress.d.ts +13 -0
- package/dist/utils/ytdlpProgress.js +77 -0
- package/package.json +5 -3
- package/scripts/download-ytdlp.mjs +1 -1
- package/scripts/install.ps1 +9 -0
- package/scripts/install.sh +15 -0
package/README.md
CHANGED
|
@@ -5,15 +5,56 @@ Internal MCP server package for 5mghost workflows.
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
curl -fsSL https://mkterswingman.com/install/yt-mcp.sh | bash
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```powershell
|
|
12
|
+
powershell -ExecutionPolicy Bypass -Command "irm https://mkterswingman.com/install/yt-mcp.ps1 | iex"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The bootstrap installer:
|
|
18
|
+
|
|
19
|
+
- installs the npm package
|
|
20
|
+
- installs required runtimes (`playwright`, `yt-dlp`, `ffmpeg`)
|
|
21
|
+
- runs `yt-mcp setup`
|
|
22
|
+
- runs `yt-mcp smoke` to verify MCP startup and authenticated remote access
|
|
23
|
+
- asks whether to configure YouTube cookies now; if you confirm, it runs `yt-mcp setup-cookies` and a subtitle smoke check immediately
|
|
24
|
+
|
|
25
|
+
If you are working inside the repo instead of using the hosted installer:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bash scripts/install.sh
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`yt-mcp` stores first-party shared auth at `~/.mkterswingman/auth.json`, so logging in here also covers other first-party local MCPs on the same machine. YouTube cookies remain local to `~/.yt-mcp/`.
|
|
36
|
+
|
|
37
|
+
Client registration notes:
|
|
38
|
+
|
|
39
|
+
- `setup` auto-registers Claude/Codex/Gemini/OpenCode when their CLI supports `mcp add`
|
|
40
|
+
- OpenClaw is registered by writing `mcporter.json` directly, which avoids Windows path escaping issues
|
|
41
|
+
- pasted PAT tokens are stored in `~/.mkterswingman/auth.json`; client `env.YT_MCP_TOKEN` is optional after setup
|
|
42
|
+
|
|
43
|
+
Media download runtime expectations:
|
|
44
|
+
|
|
45
|
+
- `start_download_job` and `poll_download_job` are job-based local tools
|
|
46
|
+
- `ffmpeg` is required for video download modes
|
|
47
|
+
- each job handles up to 5 YouTube videos
|
|
48
|
+
- downloads are written to `~/Downloads/yt-mcp/YYYY-MM-DD_<video_id>`
|
|
12
49
|
|
|
13
50
|
## Commands
|
|
14
51
|
|
|
15
52
|
- `setup` — first-time setup
|
|
16
53
|
- `serve` — start the stdio MCP server
|
|
54
|
+
- `smoke` — run the installer smoke checks (`search_videos`, plus `validate_cookies` with `--subtitles`)
|
|
55
|
+
- `runtime` — install, update, or check required runtimes
|
|
56
|
+
- `check` — inspect shared auth, runtime status, and YouTube cookies
|
|
17
57
|
- `setup-cookies` — refresh YouTube cookies
|
|
18
|
-
- `
|
|
58
|
+
- `uninstall` — remove MCP registrations and local `~/.yt-mcp` config
|
|
59
|
+
- `update` — update the main package and required runtimes
|
|
19
60
|
- `version` — print the installed version
|
|
@@ -0,0 +1,10 @@
|
|
|
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;
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
mkdirSync(dirname(authPath), { recursive: true });
|
|
15
|
+
const tempPath = `${authPath}.tmp`;
|
|
16
|
+
writeFileSync(tempPath, JSON.stringify(data, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
17
|
+
renameSync(tempPath, authPath);
|
|
18
|
+
try {
|
|
19
|
+
chmodSync(authPath, 0o600);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Why: chmod is best-effort on Windows and should not block auth persistence.
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
interface TokenManagerOptions {
|
|
2
|
+
authPath?: string;
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
fetchImpl?: typeof fetch;
|
|
5
|
+
}
|
|
1
6
|
export declare class TokenManager {
|
|
2
7
|
private authUrl;
|
|
3
|
-
|
|
8
|
+
private authPath;
|
|
9
|
+
private env;
|
|
10
|
+
private fetchImpl;
|
|
11
|
+
constructor(authUrl: string, options?: TokenManagerOptions);
|
|
4
12
|
getValidToken(): Promise<string | null>;
|
|
5
13
|
saveTokens(accessToken: string, refreshToken: string, expiresIn: number, clientId?: string): Promise<void>;
|
|
6
14
|
savePAT(pat: string): Promise<void>;
|
|
7
15
|
private readAuth;
|
|
8
16
|
private refreshTokens;
|
|
9
17
|
}
|
|
18
|
+
export {};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { PATHS } from "../utils/config.js";
|
|
2
|
+
import { readSharedAuth, writeSharedAuth } from "./sharedAuth.js";
|
|
3
3
|
export class TokenManager {
|
|
4
4
|
authUrl;
|
|
5
|
-
|
|
5
|
+
authPath;
|
|
6
|
+
env;
|
|
7
|
+
fetchImpl;
|
|
8
|
+
constructor(authUrl, options = {}) {
|
|
6
9
|
this.authUrl = authUrl;
|
|
10
|
+
this.authPath = options.authPath ?? PATHS.sharedAuthJson;
|
|
11
|
+
this.env = options.env ?? process.env;
|
|
12
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
7
13
|
}
|
|
8
14
|
async getValidToken() {
|
|
9
15
|
// Check env var PAT first
|
|
10
|
-
const envPat =
|
|
16
|
+
const envPat = this.env.YT_MCP_TOKEN;
|
|
11
17
|
if (envPat)
|
|
12
18
|
return envPat;
|
|
13
19
|
const auth = this.readAuth();
|
|
@@ -38,7 +44,6 @@ export class TokenManager {
|
|
|
38
44
|
return null;
|
|
39
45
|
}
|
|
40
46
|
async saveTokens(accessToken, refreshToken, expiresIn, clientId) {
|
|
41
|
-
ensureConfigDir();
|
|
42
47
|
const data = {
|
|
43
48
|
type: "jwt",
|
|
44
49
|
access_token: accessToken,
|
|
@@ -46,30 +51,17 @@ export class TokenManager {
|
|
|
46
51
|
expires_at: Date.now() + expiresIn * 1000,
|
|
47
52
|
client_id: clientId,
|
|
48
53
|
};
|
|
49
|
-
|
|
50
|
-
mode: 0o600,
|
|
51
|
-
});
|
|
54
|
+
writeSharedAuth(this.authPath, data);
|
|
52
55
|
}
|
|
53
56
|
async savePAT(pat) {
|
|
54
|
-
ensureConfigDir();
|
|
55
57
|
const data = {
|
|
56
58
|
type: "pat",
|
|
57
59
|
pat,
|
|
58
60
|
};
|
|
59
|
-
|
|
60
|
-
mode: 0o600,
|
|
61
|
-
});
|
|
61
|
+
writeSharedAuth(this.authPath, data);
|
|
62
62
|
}
|
|
63
63
|
readAuth() {
|
|
64
|
-
|
|
65
|
-
return null;
|
|
66
|
-
try {
|
|
67
|
-
const raw = readFileSync(PATHS.authJson, "utf8");
|
|
68
|
-
return JSON.parse(raw);
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
64
|
+
return readSharedAuth(this.authPath);
|
|
73
65
|
}
|
|
74
66
|
async refreshTokens(refreshToken, clientId) {
|
|
75
67
|
const url = `${this.authUrl}/oauth/token`;
|
|
@@ -80,7 +72,7 @@ export class TokenManager {
|
|
|
80
72
|
if (clientId) {
|
|
81
73
|
body.client_id = clientId;
|
|
82
74
|
}
|
|
83
|
-
const res = await
|
|
75
|
+
const res = await this.fetchImpl(url, {
|
|
84
76
|
method: "POST",
|
|
85
77
|
headers: { "Content-Type": "application/json" },
|
|
86
78
|
body: JSON.stringify(body),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { PATHS, loadConfig } from "../utils/config.js";
|
|
3
|
+
import { TokenManager } from "../auth/tokenManager.js";
|
|
4
|
+
import { hasSIDCookies, areCookiesExpired } from "../utils/cookies.js";
|
|
5
|
+
import { getYtDlpVersion } from "../utils/ytdlpPath.js";
|
|
6
|
+
import { runRuntimeCommand } from "./runtime.js";
|
|
7
|
+
/**
|
|
8
|
+
* `yt-mcp check` — verify token, cookies, yt-dlp, and remote API connectivity.
|
|
9
|
+
*/
|
|
10
|
+
export async function runCheck() {
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
const tm = new TokenManager(config.auth_url);
|
|
13
|
+
console.log("yt-mcp check\n");
|
|
14
|
+
// 1. Token
|
|
15
|
+
const token = await tm.getValidToken();
|
|
16
|
+
if (token) {
|
|
17
|
+
const masked = token.slice(0, 8) + "...";
|
|
18
|
+
const source = process.env.YT_MCP_TOKEN ? "env" : PATHS.sharedAuthJson;
|
|
19
|
+
console.log(`🔑 Shared auth: ✅ present (${masked}) [${source}]`);
|
|
20
|
+
// Server verification
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`${config.auth_url}/api/me`, {
|
|
23
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
24
|
+
signal: AbortSignal.timeout(10_000),
|
|
25
|
+
});
|
|
26
|
+
if (res.ok) {
|
|
27
|
+
console.log("🔐 Server verification: ✅ active");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(`🔐 Server verification: ❌ ${res.status} ${res.statusText}`);
|
|
31
|
+
console.log(" Run `yt-mcp setup` to re-authenticate.");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
36
|
+
console.log(`🔐 Server verification: ⚠️ unreachable (${msg})`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log("🔑 Shared auth: ❌ not configured");
|
|
41
|
+
console.log(" Run `yt-mcp setup` or set YT_MCP_TOKEN env var");
|
|
42
|
+
}
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log(await runRuntimeCommand("check"));
|
|
45
|
+
// 2. Cookies
|
|
46
|
+
if (existsSync(PATHS.cookiesTxt)) {
|
|
47
|
+
const hasSID = hasSIDCookies(PATHS.cookiesTxt);
|
|
48
|
+
const expired = areCookiesExpired(PATHS.cookiesTxt);
|
|
49
|
+
if (hasSID && !expired) {
|
|
50
|
+
console.log("🍪 Cookies: ✅ valid (SID present, not expired)");
|
|
51
|
+
}
|
|
52
|
+
else if (hasSID && expired) {
|
|
53
|
+
console.log("🍪 Cookies: ❌ expired");
|
|
54
|
+
console.log(" Run `yt-mcp setup-cookies` to refresh");
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log("🍪 Cookies: ❌ no YouTube session cookies found");
|
|
58
|
+
console.log(" Run `yt-mcp setup-cookies`");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log("🍪 Cookies: ❌ not found");
|
|
63
|
+
console.log(" Run `yt-mcp setup-cookies`");
|
|
64
|
+
}
|
|
65
|
+
// 3. yt-dlp
|
|
66
|
+
const ytdlp = getYtDlpVersion();
|
|
67
|
+
if (ytdlp) {
|
|
68
|
+
console.log(`🎬 yt-dlp: ✅ ${ytdlp.version} (${ytdlp.source})`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log("🎬 yt-dlp: ❌ not found");
|
|
72
|
+
console.log(" Install: brew install yt-dlp (or pip install yt-dlp)");
|
|
73
|
+
}
|
|
74
|
+
// 4. Remote API
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch(`${config.api_url}/healthz`, {
|
|
77
|
+
signal: AbortSignal.timeout(10_000),
|
|
78
|
+
});
|
|
79
|
+
if (res.ok) {
|
|
80
|
+
console.log(`🌐 Remote API: ✅ reachable (${config.api_url})`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(`🌐 Remote API: ⚠️ ${res.status} ${res.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
88
|
+
console.log(`🌐 Remote API: ❌ unreachable (${msg})`);
|
|
89
|
+
}
|
|
90
|
+
}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export declare function buildHelpText(version: string): string;
|
|
3
|
+
export interface UnifiedUpdateDeps {
|
|
4
|
+
getCurrentVersion(): string;
|
|
5
|
+
getLatestVersion(): Promise<string>;
|
|
6
|
+
installLatestPackage(): Promise<void>;
|
|
7
|
+
updateRuntime(): Promise<import("../runtime/installers.js").RuntimeSummary>;
|
|
8
|
+
}
|
|
9
|
+
export declare function runUnifiedUpdate(deps: UnifiedUpdateDeps): Promise<{
|
|
10
|
+
package: {
|
|
11
|
+
currentVersion: string;
|
|
12
|
+
latestVersion: string;
|
|
13
|
+
updated: boolean;
|
|
14
|
+
};
|
|
15
|
+
runtime: import("../runtime/installers.js").RuntimeSummary;
|
|
16
|
+
}>;
|
package/dist/cli/index.js
CHANGED
|
@@ -14,6 +14,41 @@ function getVersion() {
|
|
|
14
14
|
return "unknown";
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
export function buildHelpText(version) {
|
|
18
|
+
return `
|
|
19
|
+
yt-mcp v${version}
|
|
20
|
+
|
|
21
|
+
Usage: yt-mcp <command>
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
setup Run first-time setup (OAuth + cookies + MCP registration)
|
|
25
|
+
serve Start the MCP server (stdio transport)
|
|
26
|
+
smoke Run installer smoke checks
|
|
27
|
+
setup-cookies Refresh YouTube cookies using browser login
|
|
28
|
+
runtime Manage required runtimes
|
|
29
|
+
check Check auth, runtime, cookies, and connectivity
|
|
30
|
+
uninstall Remove MCP registrations and local yt-mcp config
|
|
31
|
+
update Update the main package and required runtimes
|
|
32
|
+
version Show current version
|
|
33
|
+
|
|
34
|
+
Environment variables:
|
|
35
|
+
YT_MCP_TOKEN Personal Access Token (skips OAuth)
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
export async function runUnifiedUpdate(deps) {
|
|
39
|
+
const currentVersion = deps.getCurrentVersion();
|
|
40
|
+
const latestVersion = await deps.getLatestVersion();
|
|
41
|
+
let updated = false;
|
|
42
|
+
if (latestVersion !== currentVersion) {
|
|
43
|
+
await deps.installLatestPackage();
|
|
44
|
+
updated = true;
|
|
45
|
+
}
|
|
46
|
+
const runtime = await deps.updateRuntime();
|
|
47
|
+
return {
|
|
48
|
+
package: { currentVersion, latestVersion, updated },
|
|
49
|
+
runtime,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
17
52
|
const command = process.argv[2];
|
|
18
53
|
async function main() {
|
|
19
54
|
switch (command) {
|
|
@@ -27,31 +62,59 @@ async function main() {
|
|
|
27
62
|
await runServe();
|
|
28
63
|
break;
|
|
29
64
|
}
|
|
65
|
+
case "smoke": {
|
|
66
|
+
const { runSmoke } = await import("./smoke.js");
|
|
67
|
+
await runSmoke(process.argv.slice(3));
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
30
70
|
case "setup-cookies": {
|
|
31
71
|
const { runSetupCookies } = await import("./setupCookies.js");
|
|
32
72
|
await runSetupCookies();
|
|
33
73
|
break;
|
|
34
74
|
}
|
|
75
|
+
case "uninstall": {
|
|
76
|
+
const { runUninstall } = await import("./uninstall.js");
|
|
77
|
+
await runUninstall();
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "runtime": {
|
|
81
|
+
const { runRuntimeCli } = await import("./runtime.js");
|
|
82
|
+
await runRuntimeCli(process.argv[3]);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "check": {
|
|
86
|
+
const { runCheck } = await import("./check.js");
|
|
87
|
+
await runCheck();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
35
90
|
case "update": {
|
|
36
91
|
const current = getVersion();
|
|
37
92
|
console.log(`Current version: ${current}`);
|
|
38
93
|
console.log("Checking for updates...\n");
|
|
39
94
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
const { updateAll } = await import("../runtime/installers.js");
|
|
96
|
+
const summary = await runUnifiedUpdate({
|
|
97
|
+
getCurrentVersion: getVersion,
|
|
98
|
+
getLatestVersion: async () => execSync("npm view @mkterswingman/5mghost-yonder version", {
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
101
|
+
}).trim(),
|
|
102
|
+
installLatestPackage: async () => {
|
|
103
|
+
execSync("npm install -g @mkterswingman/5mghost-yonder@latest", {
|
|
104
|
+
stdio: "inherit",
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
updateRuntime: updateAll,
|
|
108
|
+
});
|
|
109
|
+
if (summary.package.updated) {
|
|
110
|
+
console.log(`\n✅ Updated package to ${summary.package.latestVersion}`);
|
|
46
111
|
}
|
|
47
112
|
else {
|
|
48
|
-
console.log(
|
|
49
|
-
console.log("Updating...\n");
|
|
50
|
-
execSync("npm install -g @mkterswingman/5mghost-yonder@latest", {
|
|
51
|
-
stdio: "inherit",
|
|
52
|
-
});
|
|
53
|
-
console.log(`\n✅ Updated to ${latest}`);
|
|
113
|
+
console.log(`✅ Already on the latest version (${summary.package.currentVersion})`);
|
|
54
114
|
}
|
|
115
|
+
console.log("");
|
|
116
|
+
const { formatRuntimeSummary } = await import("./runtime.js");
|
|
117
|
+
console.log(formatRuntimeSummary(summary.runtime));
|
|
55
118
|
}
|
|
56
119
|
catch (err) {
|
|
57
120
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -68,21 +131,7 @@ async function main() {
|
|
|
68
131
|
break;
|
|
69
132
|
}
|
|
70
133
|
default:
|
|
71
|
-
console.log(
|
|
72
|
-
yt-mcp v${getVersion()}
|
|
73
|
-
|
|
74
|
-
Usage: yt-mcp <command>
|
|
75
|
-
|
|
76
|
-
Commands:
|
|
77
|
-
setup Run first-time setup (OAuth + cookies + MCP registration)
|
|
78
|
-
serve Start the MCP server (stdio transport)
|
|
79
|
-
setup-cookies Refresh YouTube cookies using browser login
|
|
80
|
-
update Update to the latest version
|
|
81
|
-
version Show current version
|
|
82
|
-
|
|
83
|
-
Environment variables:
|
|
84
|
-
YT_MCP_TOKEN Personal Access Token (skips OAuth)
|
|
85
|
-
`);
|
|
134
|
+
console.log(buildHelpText(getVersion()));
|
|
86
135
|
process.exit(command ? 1 : 0);
|
|
87
136
|
}
|
|
88
137
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RuntimeSummary } from "../runtime/installers.js";
|
|
2
|
+
export interface RuntimeCommandDeps {
|
|
3
|
+
installAll(): Promise<RuntimeSummary>;
|
|
4
|
+
updateAll(): Promise<RuntimeSummary>;
|
|
5
|
+
checkAll(): Promise<RuntimeSummary>;
|
|
6
|
+
}
|
|
7
|
+
export declare function formatRuntimeSummary(summary: RuntimeSummary): string;
|
|
8
|
+
export declare function runRuntimeCommand(action: "install" | "update" | "check", deps?: RuntimeCommandDeps): Promise<string>;
|
|
9
|
+
export declare function runRuntimeCli(action?: string): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { checkAll, installAll, updateAll } from "../runtime/installers.js";
|
|
2
|
+
export function formatRuntimeSummary(summary) {
|
|
3
|
+
const lines = [`Runtime ${summary.action} completed`];
|
|
4
|
+
for (const component of summary.components) {
|
|
5
|
+
const base = `${component.name}: ${component.status} (${component.source}`;
|
|
6
|
+
if (component.version) {
|
|
7
|
+
lines.push(`${base}, ${component.version})`);
|
|
8
|
+
}
|
|
9
|
+
else if (component.message) {
|
|
10
|
+
lines.push(`${base}) — ${component.message}`);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
lines.push(`${base})`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return lines.join("\n");
|
|
17
|
+
}
|
|
18
|
+
export async function runRuntimeCommand(action, deps = {
|
|
19
|
+
installAll,
|
|
20
|
+
updateAll,
|
|
21
|
+
checkAll,
|
|
22
|
+
}) {
|
|
23
|
+
const summary = action === "install"
|
|
24
|
+
? await deps.installAll()
|
|
25
|
+
: action === "update"
|
|
26
|
+
? await deps.updateAll()
|
|
27
|
+
: await deps.checkAll();
|
|
28
|
+
return formatRuntimeSummary(summary);
|
|
29
|
+
}
|
|
30
|
+
export async function runRuntimeCli(action = "check") {
|
|
31
|
+
const normalized = action === "install" || action === "update" || action === "check"
|
|
32
|
+
? action
|
|
33
|
+
: "check";
|
|
34
|
+
console.log(await runRuntimeCommand(normalized));
|
|
35
|
+
}
|
package/dist/cli/serve.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
2
|
import { loadConfig } from "../utils/config.js";
|
|
3
3
|
import { TokenManager } from "../auth/tokenManager.js";
|
|
4
|
+
import { DownloadJobManager } from "../download/jobManager.js";
|
|
4
5
|
import { createServer } from "../server.js";
|
|
5
6
|
export async function runServe() {
|
|
6
7
|
const config = loadConfig();
|
|
7
8
|
const tokenManager = new TokenManager(config.auth_url);
|
|
9
|
+
const downloadJobManager = new DownloadJobManager();
|
|
8
10
|
// PAT mode via env var (don't persist — just keep in memory for this session)
|
|
9
11
|
const pat = process.env.YT_MCP_TOKEN;
|
|
10
12
|
if (pat) {
|
|
11
13
|
await tokenManager.savePAT(pat);
|
|
12
14
|
}
|
|
13
|
-
const server = await createServer(config, tokenManager);
|
|
15
|
+
const server = await createServer(config, tokenManager, downloadJobManager);
|
|
14
16
|
const transport = new StdioServerTransport();
|
|
15
17
|
await server.connect(transport);
|
|
16
18
|
process.on("SIGINT", async () => {
|