@mkterswingman/5mghost-yonder 0.0.33 → 0.0.35

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 CHANGED
@@ -18,8 +18,8 @@ The bootstrap installer:
18
18
  - installs required runtimes (`playwright`, `yt-dlp`, `ffmpeg`)
19
19
  - runs `yt-mcp setup`
20
20
  - runs `yt-mcp smoke` to verify MCP startup and authenticated remote access
21
- - first tries a headless YouTube cookie import from your local Chrome/Edge session
22
- - only asks to open a visible browser for YouTube login if the headless import fails
21
+ - opens a dedicated Playwright browser profile for YouTube login
22
+ - keeps cookie refresh inside the same dedicated profile (does not import system Chrome/Edge profiles)
23
23
  - in unattended installs, defaults to OAuth auth mode and defaults to headed cookie setup after the prompt timeout
24
24
  - in installer mode, OAuth waits up to `180s` and prints PAT fallback commands before waiting
25
25
  - if auth is still incomplete after `setup`, the installer stops before smoke tests and YouTube cookie setup instead of pretending the install fully passed
@@ -57,7 +57,7 @@ Media download runtime expectations:
57
57
  - `smoke` — run the installer smoke checks (`search_videos`, plus `validate_cookies` with `--subtitles`)
58
58
  - `runtime` — install, update, or check required runtimes
59
59
  - `check` — inspect shared auth, runtime status, and YouTube cookies
60
- - `setup-cookies` — first try importing an existing YouTube login from the most recently active local Chrome/Edge profile on macOS or Windows with a headless real-browser CDP probe, then fall back to manual browser login
60
+ - `setup-cookies` — open a dedicated Playwright browser profile for manual YouTube login and save cookies locally
61
61
  - `uninstall` — remove MCP registrations and local `~/.yt-mcp` config
62
62
  - `update` — update the main package and required runtimes
63
63
  - `version` — print the installed version
package/dist/cli/index.js CHANGED
@@ -92,7 +92,15 @@ async function main() {
92
92
  }
93
93
  case "setup-cookies": {
94
94
  const { runSetupCookies, parseSetupCookiesArgs } = await import("./setupCookies.js");
95
- await runSetupCookies({}, parseSetupCookiesArgs(process.argv.slice(3)));
95
+ try {
96
+ parseSetupCookiesArgs(process.argv.slice(3));
97
+ }
98
+ catch (err) {
99
+ const msg = err instanceof Error ? err.message : String(err);
100
+ console.error(msg);
101
+ process.exit(1);
102
+ }
103
+ await runSetupCookies({});
96
104
  break;
97
105
  }
98
106
  case "uninstall": {
@@ -51,10 +51,6 @@ export interface SetupCookiesDeps {
51
51
  runManualCookieSetup: typeof runManualCookieSetup;
52
52
  log: (message: string) => void;
53
53
  }
54
- export interface SetupCookiesOptions {
55
- importOnly?: boolean;
56
- headed?: boolean;
57
- }
58
54
  export declare class BrowserProfileLockedError extends Error {
59
55
  constructor(message?: string);
60
56
  }
@@ -83,6 +79,6 @@ export declare function runManualCookieSetup(chromium: SetupCookiesChromium, dep
83
79
  /**
84
80
  * Interactive cookie setup — opens a visible browser for user to log in.
85
81
  */
86
- export declare function parseSetupCookiesArgs(argv: string[]): SetupCookiesOptions;
87
- export declare function runSetupCookies(overrides?: Partial<SetupCookiesDeps>, options?: SetupCookiesOptions): Promise<void>;
82
+ export declare function parseSetupCookiesArgs(argv: string[]): void;
83
+ export declare function runSetupCookies(overrides?: Partial<SetupCookiesDeps>): Promise<void>;
88
84
  export {};
@@ -413,12 +413,11 @@ export async function runManualCookieSetup(chromium, deps) {
413
413
  * Interactive cookie setup — opens a visible browser for user to log in.
414
414
  */
415
415
  export function parseSetupCookiesArgs(argv) {
416
- return {
417
- importOnly: argv.includes("--import-only"),
418
- headed: argv.includes("--headed"),
419
- };
416
+ for (const arg of argv) {
417
+ throw new Error(`Unknown option for setup-cookies: ${arg}. setup-cookies accepts no options.`);
418
+ }
420
419
  }
421
- export async function runSetupCookies(overrides = {}, options = {}) {
420
+ export async function runSetupCookies(overrides = {}) {
422
421
  const deps = buildSetupCookiesDeps(overrides);
423
422
  deps.log("\n🍪 YouTube Cookie Setup\n");
424
423
  deps.ensureConfigDir();
@@ -429,23 +428,5 @@ export async function runSetupCookies(overrides = {}, options = {}) {
429
428
  catch {
430
429
  throw new Error("Playwright runtime is not installed.\nRun: yt-mcp runtime install");
431
430
  }
432
- if (!options.headed) {
433
- let imported = false;
434
- try {
435
- imported = await deps.tryImportBrowserCookies(chromium, deps);
436
- }
437
- catch (err) {
438
- if (!(err instanceof BrowserProfileLockedError) || options.importOnly) {
439
- throw err;
440
- }
441
- deps.log(`${err.message}\n`);
442
- }
443
- if (imported) {
444
- return;
445
- }
446
- }
447
- if (options.importOnly && !options.headed) {
448
- throw new Error("No reusable YouTube session found in local Chrome/Edge profiles");
449
- }
450
431
  await deps.runManualCookieSetup(chromium, deps);
451
432
  }
@@ -34,11 +34,24 @@ export async function downloadOneItem(input) {
34
34
  await deps.rm(input.item.output_dir, { recursive: true, force: true });
35
35
  await deps.mkdir(input.item.output_dir, { recursive: true });
36
36
  await deps.mkdir(subtitlesDir, { recursive: true });
37
- const metadata = await deps.fetchMetadata({
37
+ const metadataInput = {
38
38
  sourceUrl: input.item.source_url,
39
39
  videoId: input.item.video_id,
40
40
  title: input.item.title,
41
- });
41
+ };
42
+ let metadata;
43
+ try {
44
+ metadata = await deps.fetchMetadata(metadataInput);
45
+ }
46
+ catch (error) {
47
+ const failureKind = classifyRefreshableFailure(error);
48
+ if (failureKind != null && failureKind !== "RATE_LIMITED" && await deps.refreshCookies()) {
49
+ metadata = await deps.fetchMetadata(metadataInput);
50
+ }
51
+ else {
52
+ throw error;
53
+ }
54
+ }
42
55
  const result = {
43
56
  video_file: null,
44
57
  metadata_file: metadataFile,
@@ -63,7 +76,7 @@ export async function downloadOneItem(input) {
63
76
  });
64
77
  }
65
78
  catch (error) {
66
- const failureKind = classifyVideoDownloadError(error);
79
+ const failureKind = classifyRefreshableFailure(error);
67
80
  if (failureKind != null && failureKind !== "RATE_LIMITED" && await deps.refreshCookies()) {
68
81
  result.video_file = await deps.downloadVideo({
69
82
  sourceUrl: input.item.source_url,
@@ -311,7 +324,7 @@ function sortSubtitleFiles(files) {
311
324
  .sort(([left], [right]) => left.localeCompare(right))
312
325
  .map(([format, paths]) => [format, [...paths].sort()]));
313
326
  }
314
- function classifyVideoDownloadError(error) {
327
+ function classifyRefreshableFailure(error) {
315
328
  const message = error instanceof Error ? error.message : String(error);
316
329
  return classifyYtDlpFailure(message);
317
330
  }
@@ -1,19 +1,15 @@
1
1
  /**
2
- * Cookie auto-refresh with three-tier fallback.
2
+ * Cookie auto-refresh with two-tier fallback.
3
3
  *
4
4
  * Tier 1: Headless Playwright refresh using existing browser-profile.
5
5
  * If Google session is still valid, cookies are extracted automatically.
6
6
  *
7
- * Tier 2: Re-import from system Chrome/Edge (headless, about:blank).
8
- * Copies the real browser profile to a temp dir and reads cookies via CDP
9
- * without making any network requests (avoids Google token rotation).
10
- *
11
- * Tier 3: Open a headed browser for manual user login.
7
+ * Tier 2: Open a headed browser for manual user login.
12
8
  * Blocks the MCP tool call for up to ~2 minutes while the user logs in.
13
- * Creates browser-profile so Tier 1 works next time.
9
+ * Uses the dedicated yt-mcp profile so refresh never touches system profiles.
14
10
  */
15
11
  /**
16
- * Attempt to refresh YouTube cookies through a three-tier fallback chain.
12
+ * Attempt to refresh YouTube cookies through the dedicated two-tier fallback chain.
17
13
  *
18
14
  * @returns true if cookies were refreshed, false if all tiers failed.
19
15
  */
@@ -1,16 +1,12 @@
1
1
  /**
2
- * Cookie auto-refresh with three-tier fallback.
2
+ * Cookie auto-refresh with two-tier fallback.
3
3
  *
4
4
  * Tier 1: Headless Playwright refresh using existing browser-profile.
5
5
  * If Google session is still valid, cookies are extracted automatically.
6
6
  *
7
- * Tier 2: Re-import from system Chrome/Edge (headless, about:blank).
8
- * Copies the real browser profile to a temp dir and reads cookies via CDP
9
- * without making any network requests (avoids Google token rotation).
10
- *
11
- * Tier 3: Open a headed browser for manual user login.
7
+ * Tier 2: Open a headed browser for manual user login.
12
8
  * Blocks the MCP tool call for up to ~2 minutes while the user logs in.
13
- * Creates browser-profile so Tier 1 works next time.
9
+ * Uses the dedicated yt-mcp profile so refresh never touches system profiles.
14
10
  */
15
11
  import { existsSync } from "node:fs";
16
12
  import { PATHS, ensureConfigDir } from "./config.js";
@@ -18,7 +14,7 @@ import { detectBrowserChannel, hasYouTubeSession, saveCookiesToDisk, } from "../
18
14
  import { hasSIDCookies } from "./cookies.js";
19
15
  const HEADLESS_TIMEOUT_MS = 30_000;
20
16
  /**
21
- * Attempt to refresh YouTube cookies through a three-tier fallback chain.
17
+ * Attempt to refresh YouTube cookies through the dedicated two-tier fallback chain.
22
18
  *
23
19
  * @returns true if cookies were refreshed, false if all tiers failed.
24
20
  */
@@ -30,13 +26,8 @@ export async function tryHeadlessRefresh() {
30
26
  if (tier1)
31
27
  return true;
32
28
  }
33
- // Tier 2: Re-import from system browser (headless, no network requests)
34
- const tier2 = await tryReimportFromSystemBrowser();
35
- if (tier2)
36
- return true;
37
- // Tier 3: Open headed browser for manual login
38
- const tier3 = await tryHeadedManualLogin();
39
- return tier3;
29
+ // Tier 2: Open headed browser for manual login
30
+ return tryHeadedManualLogin();
40
31
  }
41
32
  /**
42
33
  * Tier 1: Use existing browser-profile with Playwright to refresh cookies.
@@ -84,49 +75,15 @@ async function tryPlaywrightRefresh() {
84
75
  }
85
76
  }
86
77
  /**
87
- * Tier 2: Re-import cookies from the user's system Chrome/Edge.
88
- * Uses headless Chrome with about:blank to avoid triggering Google token rotation.
89
- */
90
- async function tryReimportFromSystemBrowser() {
91
- try {
92
- const pw = await import("playwright");
93
- const { tryImportBrowserCookies, saveCookiesAndClose, readImportedBrowserCookies, } = await import("../cli/setupCookies.js");
94
- const { findImportableBrowserProfileCandidates, prepareImportedBrowserWorkspace, cleanupImportedBrowserWorkspace, } = await import("./browserProfileImport.js");
95
- const deps = {
96
- ensureConfigDir,
97
- loadChromium: async () => pw.chromium,
98
- detectBrowserChannel,
99
- hasYouTubeSession,
100
- saveCookiesAndClose,
101
- // Why: waitForLogin is unused in import path but required by SetupCookiesDeps type
102
- waitForLogin: async () => null,
103
- findImportableBrowserProfileCandidates,
104
- prepareImportedBrowserWorkspace,
105
- cleanupImportedBrowserWorkspace,
106
- readImportedBrowserCookies,
107
- tryImportBrowserCookies,
108
- // Why: runManualCookieSetup is unused in import path but required by type
109
- runManualCookieSetup: async () => { },
110
- // Why: silent logging during auto-refresh — user doesn't see MCP server stdout
111
- log: () => { },
112
- };
113
- return await tryImportBrowserCookies(pw.chromium, deps);
114
- }
115
- catch {
116
- return false;
117
- }
118
- }
119
- /**
120
- * Tier 3: Open a headed browser for the user to manually log in.
78
+ * Tier 2: Open a headed browser for the user to manually log in.
121
79
  * Blocks the MCP tool call for up to ~2 minutes.
122
- * Creates browser-profile so Tier 1 works for future refreshes.
80
+ * Uses the dedicated yt-mcp browser-profile for future Tier 1 refreshes.
123
81
  */
124
82
  async function tryHeadedManualLogin() {
125
83
  try {
126
- // Why: runSetupCookies with { headed: true } skips import and goes straight
127
- // to manual Playwright login, which creates browser-profile for future Tier 1 use.
84
+ // Why: setup-cookies now only supports the dedicated manual Playwright login flow.
128
85
  const { runSetupCookies } = await import("../cli/setupCookies.js");
129
- await runSetupCookies({}, { headed: true });
86
+ await runSetupCookies({});
130
87
  return hasSIDCookies(PATHS.cookiesTxt);
131
88
  }
132
89
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkterswingman/5mghost-yonder",
3
- "version": "0.0.33",
3
+ "version": "0.0.35",
4
4
  "description": "Internal MCP client with local data tools and remote API proxy",
5
5
  "type": "module",
6
6
  "bin": {