@mkterswingman/5mghost-yonder 0.0.19 → 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 +4 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/setup.d.ts +10 -0
- package/dist/cli/setup.js +72 -16
- package/package.json +1 -1
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
|
-
-
|
|
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
|
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface UnifiedUpdateDeps {
|
|
|
5
5
|
getLatestVersion(): Promise<string>;
|
|
6
6
|
installLatestPackage(): Promise<void>;
|
|
7
7
|
updateRuntime(): Promise<import("../runtime/installers.js").RuntimeSummary>;
|
|
8
|
+
postUpdate?: () => Promise<void>;
|
|
8
9
|
}
|
|
9
10
|
export declare function compareVersions(currentVersion: string, latestVersion: string): number;
|
|
10
11
|
export declare function runUnifiedUpdate(deps: UnifiedUpdateDeps): Promise<{
|
package/dist/cli/index.js
CHANGED
|
@@ -59,6 +59,9 @@ export async function runUnifiedUpdate(deps) {
|
|
|
59
59
|
updated = true;
|
|
60
60
|
}
|
|
61
61
|
const runtime = await deps.updateRuntime();
|
|
62
|
+
if (deps.postUpdate) {
|
|
63
|
+
await deps.postUpdate();
|
|
64
|
+
}
|
|
62
65
|
return {
|
|
63
66
|
package: { currentVersion, latestVersion, updated },
|
|
64
67
|
runtime,
|
|
@@ -113,6 +116,7 @@ async function main() {
|
|
|
113
116
|
console.log("Checking for updates...\n");
|
|
114
117
|
try {
|
|
115
118
|
const { updateAll } = await import("../runtime/installers.js");
|
|
119
|
+
const { runInstallSkills } = await import("./installSkills.js");
|
|
116
120
|
const summary = await runUnifiedUpdate({
|
|
117
121
|
getCurrentVersion: getVersion,
|
|
118
122
|
getLatestVersion: async () => execSync("npm view @mkterswingman/5mghost-yonder version", {
|
|
@@ -125,6 +129,7 @@ async function main() {
|
|
|
125
129
|
});
|
|
126
130
|
},
|
|
127
131
|
updateRuntime: updateAll,
|
|
132
|
+
postUpdate: runInstallSkills,
|
|
128
133
|
});
|
|
129
134
|
if (summary.package.updated) {
|
|
130
135
|
console.log(`\n✅ Updated package to ${summary.package.latestVersion}`);
|
package/dist/cli/setup.d.ts
CHANGED
|
@@ -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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
173
|
-
const usePAT =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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");
|