@mkterswingman/5mghost-yonder 0.0.40 → 0.0.41

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/setup.js CHANGED
@@ -443,8 +443,8 @@ export async function runSetup() {
443
443
  {
444
444
  "mcpServers": {
445
445
  "yt-mcp": {
446
- "command": "node",
447
- "args": [${JSON.stringify(PATHS.launcherJs)}, "serve"]
446
+ "command": ${JSON.stringify(launcherCommand.command)},
447
+ "args": ${JSON.stringify(launcherCommand.args)}
448
448
  }
449
449
  }
450
450
  }
@@ -456,14 +456,14 @@ export async function runSetup() {
456
456
  "servers": {
457
457
  "yt-mcp": {
458
458
  "transport": "stdio",
459
- "command": "node",
460
- "args": [${JSON.stringify(PATHS.launcherJs)}, "serve"]
459
+ "command": ${JSON.stringify(launcherCommand.command)},
460
+ "args": ${JSON.stringify(launcherCommand.args)}
461
461
  }
462
462
  }
463
463
  }
464
464
  `);
465
465
  console.log(" OpenClaw stdio CLI (recommended):");
466
- console.log(` mcporter config add yt-mcp --command node --arg ${JSON.stringify(PATHS.launcherJs)} --arg serve`);
466
+ console.log(` mcporter config add yt-mcp --command ${JSON.stringify(launcherCommand.command)} ${launcherCommand.args.map((arg) => `--arg ${JSON.stringify(arg)}`).join(" ")}`);
467
467
  console.log(` OpenClaw uses ${PATHS.sharedAuthJson} for PAT/JWT, so env.YT_MCP_TOKEN is optional after setup.`);
468
468
  console.log(` WorkBuddy MCP config: ${getWorkBuddyConfigPath()}`);
469
469
  console.log(` CodeBuddy MCP config: ${getCodeBuddyConfigPath()}`);
package/dist/telemetry.js CHANGED
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { PATHS } from "./utils/config.js";
5
5
  const PRODUCT = "5mghost-yonder";
6
- const PRODUCT_VERSION = "0.0.40";
6
+ const PRODUCT_VERSION = "0.0.41";
7
7
  const MAX_ERROR_CODE_CHARS = 64;
8
8
  export class ToolTelemetryClient {
9
9
  config;
@@ -1,7 +1,14 @@
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 };
1
+ export interface LauncherSourceOptions {
2
+ packageSpec: string;
3
+ npmCacheDir: string;
4
+ npxCommand?: string | null;
5
+ }
6
+ export interface LauncherCommand {
7
+ command: string;
8
+ args: string[];
9
+ }
5
10
  export declare function buildLauncherCommand(launcherPath?: string): LauncherCommand;
11
+ export declare function isRepairableNpxFailure(stderr: string): boolean;
12
+ export declare function resolveNpxCommand(): string | null;
6
13
  export declare function buildLauncherSource(options?: Partial<LauncherSourceOptions>): string;
7
14
  export declare function writeLauncherFile(options?: Partial<LauncherSourceOptions>): string;
@@ -1,19 +1,117 @@
1
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 };
2
+ import { execFileSync } from "node:child_process";
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { dirname } from "node:path";
4
5
  const DEFAULT_PACKAGE_SPEC = "@mkterswingman/5mghost-yonder@latest";
5
6
  export function buildLauncherCommand(launcherPath = PATHS.launcherJs) {
6
- return _buildLauncherCommand(launcherPath);
7
+ return {
8
+ command: process.execPath,
9
+ args: [launcherPath, "serve"],
10
+ };
11
+ }
12
+ export function isRepairableNpxFailure(stderr) {
13
+ const lower = stderr.toLowerCase();
14
+ const referencesNpxDir = lower.includes("_npx/") || lower.includes("_npx\\");
15
+ return referencesNpxDir && (lower.includes("enotempty") || lower.includes("rename"));
16
+ }
17
+ export function resolveNpxCommand() {
18
+ try {
19
+ const lookup = process.platform === "win32" ? "where" : "which";
20
+ const target = process.platform === "win32" ? "npx.cmd" : "npx";
21
+ const output = execFileSync(lookup, [target], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
22
+ return output.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? null;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
7
27
  }
8
28
  export function buildLauncherSource(options = {}) {
9
- return _buildLauncherSource({
10
- packageSpec: options.packageSpec ?? DEFAULT_PACKAGE_SPEC,
11
- npmCacheDir: options.npmCacheDir ?? PATHS.npmCacheDir,
12
- });
29
+ const packageSpec = options.packageSpec ?? DEFAULT_PACKAGE_SPEC;
30
+ const npmCacheDir = options.npmCacheDir ?? PATHS.npmCacheDir;
31
+ const npxCommand = "npxCommand" in options ? options.npxCommand : resolveNpxCommand();
32
+ return `#!/usr/bin/env node
33
+ import { existsSync, mkdirSync, renameSync } from "node:fs";
34
+ import { join } from "node:path";
35
+ import { spawnSync } from "node:child_process";
36
+
37
+ const packageSpec = ${JSON.stringify(packageSpec)};
38
+ const npmCacheDir = ${JSON.stringify(npmCacheDir)};
39
+ const configuredNpxCommand = ${JSON.stringify(npxCommand)};
40
+ const args = process.argv.slice(2);
41
+ const targetArgs = args.length > 0 ? args : ["serve"];
42
+
43
+ function isSkillInstallerMode(subArgs) {
44
+ return subArgs[0] === "install-skills";
45
+ }
46
+
47
+ function isRepairableNpxFailure(stderr) {
48
+ const lower = stderr.toLowerCase();
49
+ return (lower.includes("_npx/") || lower.includes("_npx\\\\"))
50
+ && (lower.includes("enotempty") || lower.includes("rename"));
51
+ }
52
+
53
+ function getNpxCommand() {
54
+ return configuredNpxCommand || (process.platform === "win32" ? "npx.cmd" : "npx");
55
+ }
56
+
57
+ function writeSpawnError(result, npxBin) {
58
+ if (!result.error) return;
59
+ const message = result.error instanceof Error ? result.error.message : String(result.error);
60
+ process.stderr.write(\`[launcher] failed to start npx command "\${npxBin}": \${message}\\n\`);
61
+ process.stderr.write("[launcher] rerun setup from a terminal where Node.js/npm are available: yt-mcp setup\\n");
62
+ }
63
+
64
+ function runNpx(subArgs, captureStdErrOnly = false) {
65
+ mkdirSync(npmCacheDir, { recursive: true });
66
+ const npxBin = getNpxCommand();
67
+ const result = spawnSync(npxBin, ["--yes", packageSpec, ...subArgs], {
68
+ env: { ...process.env, npm_config_cache: npmCacheDir },
69
+ // Why: probe runs before MCP starts; stdout must stay silent or it will corrupt stdio transport.
70
+ // The skill installer is an explicit one-shot CLI path, so it can inherit stdio safely.
71
+ stdio: captureStdErrOnly ? ["ignore", "ignore", "pipe"] : "inherit",
72
+ });
73
+ writeSpawnError(result, npxBin);
74
+ return result;
75
+ }
76
+
77
+ function rotateNpxDir() {
78
+ const npxDir = join(npmCacheDir, "_npx");
79
+ if (!existsSync(npxDir)) return false;
80
+ const backup = \`\${npxDir}.bad.\${new Date().toISOString().replace(/[^0-9]/g, "").slice(0, 14)}\`;
81
+ renameSync(npxDir, backup);
82
+ process.stderr.write(\`[launcher] repaired corrupted npx cache: \${backup}\\n\`);
83
+ return true;
84
+ }
85
+
86
+ function ensurePackageReady() {
87
+ const first = runNpx(["version"], true);
88
+ if ((first.status ?? 1) === 0) return;
89
+
90
+ const stderr = first.stderr ? String(first.stderr) : "";
91
+ if (!isRepairableNpxFailure(stderr) || !rotateNpxDir()) {
92
+ if (stderr) process.stderr.write(stderr);
93
+ process.exit(first.status ?? 1);
94
+ }
95
+
96
+ const second = runNpx(["version"], true);
97
+ const secondStderr = second.stderr ? String(second.stderr) : "";
98
+ if ((second.status ?? 1) !== 0) {
99
+ if (secondStderr) process.stderr.write(secondStderr);
100
+ process.exit(second.status ?? 1);
101
+ }
102
+ }
103
+
104
+ ensurePackageReady();
105
+ if (isSkillInstallerMode(targetArgs)) {
106
+ process.env.SHARED_CLIENT_INSTALL_SKILLS = "1";
107
+ }
108
+ const finalRun = runNpx(targetArgs, false);
109
+ process.exit(finalRun.status ?? (finalRun.error ? 1 : 0));
110
+ `;
13
111
  }
14
112
  export function writeLauncherFile(options = {}) {
15
- return _writeLauncherFile({
16
- packageSpec: options.packageSpec ?? DEFAULT_PACKAGE_SPEC,
17
- npmCacheDir: options.npmCacheDir ?? PATHS.npmCacheDir,
18
- }, PATHS.launcherJs);
113
+ mkdirSync(dirname(PATHS.launcherJs), { recursive: true });
114
+ const source = buildLauncherSource(options);
115
+ writeFileSync(PATHS.launcherJs, source, { encoding: "utf8", mode: 0o755 });
116
+ return PATHS.launcherJs;
19
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkterswingman/5mghost-yonder",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "Internal MCP client with local data tools and remote API proxy",
5
5
  "type": "module",
6
6
  "bin": {