@mkterswingman/5mghost-yonder 0.0.20 → 0.0.21

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,7 +18,10 @@ 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
- - asks whether to configure YouTube cookies now; if you confirm, it runs `yt-mcp setup-cookies` and a subtitle smoke check immediately
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
23
+ - in unattended installs, defaults to OAuth auth mode and defaults to headed cookie setup after the prompt timeout
24
+ - runs a subtitle smoke check immediately after cookies are available
22
25
 
23
26
  If you are working inside the repo instead of using the hosted installer:
24
27
 
@@ -13,6 +13,16 @@ export declare function canOpenBrowserForEnv(input: {
13
13
  env: NodeJS.ProcessEnv;
14
14
  stdinIsTTY: boolean;
15
15
  }): boolean;
16
+ type PromptQuestionFn = (question: string, signal: AbortSignal) => Promise<string>;
17
+ export interface PromptWithDefaultOptions {
18
+ defaultValue: string;
19
+ defaultLabel?: string;
20
+ timeoutMs?: number;
21
+ stdinIsTTY?: boolean;
22
+ log?: (message: string) => void;
23
+ questionFn?: PromptQuestionFn;
24
+ }
25
+ export declare function promptWithDefault(question: string, options: PromptWithDefaultOptions): Promise<string>;
16
26
  export declare function getNoBrowserSessionNotice(): string;
17
27
  export declare function getNoBrowserPatHint(authUrl: string): string[];
18
28
  export declare function getCookieSetupDeferredHint(): string[];
package/dist/cli/setup.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execFileSync, execSync } from "node:child_process";
2
- import { createInterface } from "node:readline";
2
+ import { createInterface } from "node:readline/promises";
3
3
  import { loadConfig, saveConfig, PATHS, ensureConfigDir } from "../utils/config.js";
4
4
  import { TokenManager } from "../auth/tokenManager.js";
5
5
  import { runOAuthFlow } from "../auth/oauthFlow.js";
@@ -93,17 +93,53 @@ function openUrl(url) {
93
93
  // Can't open — user will see the URL in console
94
94
  }
95
95
  }
96
- /**
97
- * Prompt user for input in terminal.
98
- */
99
- function prompt(question) {
100
- const rl = createInterface({ input: process.stdin, output: process.stdout });
101
- return new Promise((resolve) => {
102
- rl.question(question, (answer) => {
96
+ function createPromptQuestionFn() {
97
+ return async (question, signal) => {
98
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
99
+ try {
100
+ return (await rl.question(question, { signal })).trim();
101
+ }
102
+ finally {
103
103
  rl.close();
104
- resolve(answer.trim());
105
- });
106
- });
104
+ }
105
+ };
106
+ }
107
+ export async function promptWithDefault(question, options) {
108
+ const stdinIsTTY = options.stdinIsTTY ?? Boolean(process.stdin.isTTY);
109
+ const defaultLabel = options.defaultLabel ?? options.defaultValue;
110
+ const log = options.log ?? console.log;
111
+ if (!stdinIsTTY) {
112
+ log(` ℹ️ No interactive terminal detected; defaulting to ${defaultLabel}.`);
113
+ return options.defaultValue;
114
+ }
115
+ const questionFn = options.questionFn ?? createPromptQuestionFn();
116
+ const timeoutMs = options.timeoutMs ?? 15_000;
117
+ const controller = new AbortController();
118
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
119
+ try {
120
+ const answer = await questionFn(question, controller.signal);
121
+ return answer || options.defaultValue;
122
+ }
123
+ catch (err) {
124
+ if (err instanceof Error && err.name === "AbortError") {
125
+ log(` ℹ️ No input received within ${Math.round(timeoutMs / 1000)}s; defaulting to ${defaultLabel}.`);
126
+ return options.defaultValue;
127
+ }
128
+ throw err;
129
+ }
130
+ finally {
131
+ clearTimeout(timer);
132
+ }
133
+ }
134
+ function getRequestedAuthModeFromEnv(env) {
135
+ const value = env.YT_MCP_AUTH_MODE?.trim().toLowerCase();
136
+ if (value === "oauth" || value === "1") {
137
+ return "oauth";
138
+ }
139
+ if (value === "pat" || value === "2") {
140
+ return "pat";
141
+ }
142
+ return null;
107
143
  }
108
144
  export function getNoBrowserSessionNotice() {
109
145
  return " ℹ️ 当前安装会话无法自动拉起浏览器 — 使用 PAT 模式\n";
@@ -169,8 +205,13 @@ export async function runSetup() {
169
205
  console.log(" [1] OAuth 自动登录(推荐 — 自动弹出浏览器完成登录)");
170
206
  console.log(" [2] PAT Token 登录(手动 — 在浏览器中生成 token 后粘贴)");
171
207
  console.log("");
172
- const choice = await prompt(" 请输入 1 或 2 (默认 1): ");
173
- const usePAT = choice === "2";
208
+ const forcedAuthMode = getRequestedAuthModeFromEnv(process.env);
209
+ const usePAT = forcedAuthMode
210
+ ? forcedAuthMode === "pat"
211
+ : (await promptWithDefault(" 请输入 1 或 2 (默认 1): ", {
212
+ defaultValue: "1",
213
+ defaultLabel: "1",
214
+ })) === "2";
174
215
  if (usePAT) {
175
216
  // User chose PAT
176
217
  const patUrl = `${config.auth_url}/pat/login`;
@@ -179,7 +220,12 @@ export async function runSetup() {
179
220
  openUrl(patUrl);
180
221
  console.log("");
181
222
  console.log(" 请在浏览器中登录并生成 PAT token。");
182
- const patInput = await prompt(" 粘贴你的 PAT token (pat_xxx): ");
223
+ const patInput = process.stdin.isTTY
224
+ ? await promptWithDefault(" 粘贴你的 PAT token (pat_xxx): ", {
225
+ defaultValue: "",
226
+ defaultLabel: "skip",
227
+ })
228
+ : "";
183
229
  if (patInput) {
184
230
  await tokenManager.savePAT(patInput);
185
231
  console.log(" ✅ PAT saved to shared auth");
@@ -210,7 +256,12 @@ export async function runSetup() {
210
256
  console.log(` 🔗 Opening PAT login page: ${patUrl}`);
211
257
  openUrl(patUrl);
212
258
  console.log("");
213
- const patInput = await prompt(" 粘贴你的 PAT token (pat_xxx), 或直接回车跳过: ");
259
+ const patInput = process.stdin.isTTY
260
+ ? await promptWithDefault(" 粘贴你的 PAT token (pat_xxx), 或直接回车跳过: ", {
261
+ defaultValue: "",
262
+ defaultLabel: "skip",
263
+ })
264
+ : "";
214
265
  if (patInput) {
215
266
  await tokenManager.savePAT(patInput);
216
267
  console.log(" ✅ PAT saved to shared auth");
@@ -227,7 +278,12 @@ export async function runSetup() {
227
278
  for (const line of getNoBrowserPatHint(config.auth_url)) {
228
279
  console.log(line);
229
280
  }
230
- const patInput = await prompt(" 粘贴你的 PAT token (pat_xxx), 或直接回车跳过: ");
281
+ const patInput = process.stdin.isTTY
282
+ ? await promptWithDefault(" 粘贴你的 PAT token (pat_xxx), 或直接回车跳过: ", {
283
+ defaultValue: "",
284
+ defaultLabel: "skip",
285
+ })
286
+ : "";
231
287
  if (patInput) {
232
288
  await tokenManager.savePAT(patInput);
233
289
  console.log(" ✅ PAT saved to shared auth");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkterswingman/5mghost-yonder",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "Internal MCP client with local data tools and remote API proxy",
5
5
  "type": "module",
6
6
  "bin": {