@smithery/cli 0.0.7 → 0.0.10
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 +0 -7
- package/dist/index.js +96 -45
- package/package.json +12 -12
- package/LICENSE +0 -21
- package/dist/auto-update.js +0 -33
- package/dist/commands/get.js +0 -25
- package/dist/commands/install.js +0 -76
- package/dist/commands/installed.js +0 -31
- package/dist/commands/list.js +0 -45
- package/dist/commands/uninstall.js +0 -47
- package/dist/extractors/modelcontextprotocol-extractor.js +0 -209
- package/dist/helpers/index.js +0 -94
- package/dist/install.js +0 -79
- package/dist/types/analytics.js +0 -1
- package/dist/types/index.js +0 -1
- package/dist/types/package.js +0 -1
- package/dist/types.js +0 -1
- package/dist/utils/analytics.js +0 -44
- package/dist/utils/config-manager.js +0 -122
- package/dist/utils/config.js +0 -74
- package/dist/utils/display.js +0 -39
- package/dist/utils/package-actions.js +0 -52
- package/dist/utils/package-management.js +0 -217
- package/dist/utils/package-resolver.js +0 -133
- package/dist/utils/runtime-utils.js +0 -37
- package/dist/utils/ui.js +0 -65
package/dist/install.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { dirname } from "node:path";
|
|
5
|
-
import { installPackage as installPkg } from "./utils/package-management.js";
|
|
6
|
-
import inquirer from "inquirer";
|
|
7
|
-
import chalk from "chalk";
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = dirname(__filename);
|
|
10
|
-
const packageListPath = join(__dirname, "../packages/package-list.json");
|
|
11
|
-
async function promptForRuntime() {
|
|
12
|
-
const { runtime } = await inquirer.prompt([
|
|
13
|
-
{
|
|
14
|
-
type: "list",
|
|
15
|
-
name: "runtime",
|
|
16
|
-
message: "What runtime does this package use?",
|
|
17
|
-
choices: [
|
|
18
|
-
{ name: "Node.js", value: "node" },
|
|
19
|
-
{ name: "Python", value: "python" },
|
|
20
|
-
],
|
|
21
|
-
},
|
|
22
|
-
]);
|
|
23
|
-
return runtime;
|
|
24
|
-
}
|
|
25
|
-
function createUnknownPackage(packageName, runtime) {
|
|
26
|
-
return {
|
|
27
|
-
id: packageName,
|
|
28
|
-
name: packageName,
|
|
29
|
-
description: "Unverified package",
|
|
30
|
-
// runtime,
|
|
31
|
-
vendor: "",
|
|
32
|
-
sourceUrl: "",
|
|
33
|
-
homepage: "",
|
|
34
|
-
license: "",
|
|
35
|
-
isInstalled: false,
|
|
36
|
-
isVerified: false,
|
|
37
|
-
connections: [
|
|
38
|
-
{
|
|
39
|
-
stdio: {
|
|
40
|
-
command: runtime === "node" ? "npx" : "python",
|
|
41
|
-
args: runtime === "node" ? ["-y", packageName] : ["-m", packageName],
|
|
42
|
-
env: {},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export async function installPackage(pkg) {
|
|
49
|
-
return installPkg(pkg);
|
|
50
|
-
}
|
|
51
|
-
export async function install(packageName) {
|
|
52
|
-
const packageList = JSON.parse(readFileSync(packageListPath, "utf-8"));
|
|
53
|
-
const pkg = packageList.find((p) => p.name === packageName);
|
|
54
|
-
if (!pkg) {
|
|
55
|
-
console.warn(chalk.yellow(`Package ${packageName} not found in the curated list.`));
|
|
56
|
-
const { proceedWithInstall } = await inquirer.prompt([
|
|
57
|
-
{
|
|
58
|
-
type: "confirm",
|
|
59
|
-
name: "proceedWithInstall",
|
|
60
|
-
message: `Would you like to try installing ${packageName} anyway? This package hasn't been verified.`,
|
|
61
|
-
default: false,
|
|
62
|
-
},
|
|
63
|
-
]);
|
|
64
|
-
if (proceedWithInstall) {
|
|
65
|
-
console.log(chalk.cyan(`Proceeding with installation of ${packageName}...`));
|
|
66
|
-
// Prompt for runtime for unverified packages
|
|
67
|
-
const runtime = await promptForRuntime();
|
|
68
|
-
// Create a basic package object for unverified packages
|
|
69
|
-
const unknownPkg = createUnknownPackage(packageName, runtime);
|
|
70
|
-
await installPkg(unknownPkg);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
console.log("Installation cancelled.");
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
await installPkg(pkg);
|
|
79
|
-
}
|
package/dist/types/analytics.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./package.js";
|
package/dist/types/package.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/analytics.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { ConfigManager } from "./config-manager.js";
|
|
3
|
-
import { REGISTRY_ENDPOINT } from "./package-resolver.js";
|
|
4
|
-
export class Analytics {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.sessionId = randomUUID();
|
|
7
|
-
const prefs = ConfigManager.readPreferences();
|
|
8
|
-
if (!prefs.anonUserId) {
|
|
9
|
-
ConfigManager.writePreferences({ ...prefs, anonUserId: randomUUID() });
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
async trackInstallation(packageId, clientType) {
|
|
13
|
-
const prefs = ConfigManager.readPreferences();
|
|
14
|
-
try {
|
|
15
|
-
const event = {
|
|
16
|
-
anonUserId: prefs.anonUserId,
|
|
17
|
-
eventType: "package_installation",
|
|
18
|
-
packageId: packageId,
|
|
19
|
-
timestamp: new Date().toISOString(),
|
|
20
|
-
platform: process.platform,
|
|
21
|
-
nodeVersion: process.version,
|
|
22
|
-
sessionId: this.sessionId,
|
|
23
|
-
clientType,
|
|
24
|
-
};
|
|
25
|
-
await this.sendEvent(event);
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
console.error("Failed to track installation:", error);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
async sendEvent(event) {
|
|
32
|
-
const response = await fetch(`${REGISTRY_ENDPOINT}/install`, {
|
|
33
|
-
method: "POST",
|
|
34
|
-
headers: {
|
|
35
|
-
"Content-Type": "application/json",
|
|
36
|
-
},
|
|
37
|
-
body: JSON.stringify(event),
|
|
38
|
-
});
|
|
39
|
-
if (!response.ok) {
|
|
40
|
-
throw new Error(`Analytics request failed: ${response.statusText}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
export const analytics = new Analytics();
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
5
|
-
export class ConfigManager {
|
|
6
|
-
static getConfigPath(client) {
|
|
7
|
-
const baseDir = path.dirname(ConfigManager.configPath);
|
|
8
|
-
// Default to Claude if no client specified or if explicitly Claude
|
|
9
|
-
if (!client || client.toLowerCase() === "claude") {
|
|
10
|
-
return ConfigManager.configPath;
|
|
11
|
-
}
|
|
12
|
-
// Support for future AI clients
|
|
13
|
-
// Currently only Claude is officially supported
|
|
14
|
-
// 'jan' and others are placeholders for future integrations
|
|
15
|
-
return path.join(baseDir, "..", client, `${client.toLowerCase()}_config.json`);
|
|
16
|
-
}
|
|
17
|
-
static readConfig(client) {
|
|
18
|
-
try {
|
|
19
|
-
const configPath = ConfigManager.getConfigPath(client);
|
|
20
|
-
if (!fs.existsSync(configPath)) {
|
|
21
|
-
return { mcpServers: {} };
|
|
22
|
-
}
|
|
23
|
-
const rawConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
24
|
-
return {
|
|
25
|
-
mcpServers: rawConfig.mcpServers || {},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
console.error("Error reading config:", error);
|
|
30
|
-
return { mcpServers: {} };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
static writeConfig(config, client) {
|
|
34
|
-
try {
|
|
35
|
-
const configPath = ConfigManager.getConfigPath(client);
|
|
36
|
-
const configDir = path.dirname(configPath);
|
|
37
|
-
if (!fs.existsSync(configDir)) {
|
|
38
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
41
|
-
throw new Error("Invalid config structure");
|
|
42
|
-
}
|
|
43
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
console.error("Error writing config:", error);
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
static readPreferences() {
|
|
51
|
-
try {
|
|
52
|
-
if (!fs.existsSync(ConfigManager.preferencesPath)) {
|
|
53
|
-
return {};
|
|
54
|
-
}
|
|
55
|
-
return JSON.parse(fs.readFileSync(ConfigManager.preferencesPath, "utf8"));
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
return {};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
static writePreferences(prefs) {
|
|
62
|
-
try {
|
|
63
|
-
const prefsDir = path.dirname(ConfigManager.preferencesPath);
|
|
64
|
-
if (!fs.existsSync(prefsDir)) {
|
|
65
|
-
fs.mkdirSync(prefsDir, { recursive: true });
|
|
66
|
-
}
|
|
67
|
-
fs.writeFileSync(ConfigManager.preferencesPath, JSON.stringify(prefs, null, 2));
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
console.error("Error writing preferences:", error);
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
static isPackageInstalled(id, client) {
|
|
75
|
-
const config = ConfigManager.readConfig(client);
|
|
76
|
-
return id in config.mcpServers;
|
|
77
|
-
}
|
|
78
|
-
static async installPackage(id, serverConfig, client) {
|
|
79
|
-
try {
|
|
80
|
-
const config = ConfigManager.readConfig(client);
|
|
81
|
-
config.mcpServers[id] = serverConfig;
|
|
82
|
-
ConfigManager.writeConfig(config, client);
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
console.error("Error installing server:", error);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
static async uninstallPackage(id, client) {
|
|
90
|
-
const config = ConfigManager.readConfig(client);
|
|
91
|
-
if (!config.mcpServers[id]) {
|
|
92
|
-
console.log(`Server ${id} is not installed.`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
delete config.mcpServers[id];
|
|
96
|
-
ConfigManager.writeConfig(config, client);
|
|
97
|
-
}
|
|
98
|
-
static getServerConfig(id, client) {
|
|
99
|
-
const config = ConfigManager.readConfig(client);
|
|
100
|
-
return config.mcpServers[id] || null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
(() => {
|
|
104
|
-
if (process.platform === "win32") {
|
|
105
|
-
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
106
|
-
ConfigManager.configPath = path.join(appData, "Claude", "claude_desktop_config.json");
|
|
107
|
-
ConfigManager.preferencesPath = path.join(appData, "smithery-cli", "preferences.json");
|
|
108
|
-
}
|
|
109
|
-
else if (process.platform === "darwin") {
|
|
110
|
-
// macOS
|
|
111
|
-
const homeDir = os.homedir();
|
|
112
|
-
ConfigManager.configPath = path.join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
113
|
-
ConfigManager.preferencesPath = path.join(homeDir, ".smithery-cli", "preferences.json");
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
// Linux
|
|
117
|
-
const homeDir = os.homedir();
|
|
118
|
-
const configDir = process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config");
|
|
119
|
-
ConfigManager.configPath = path.join(configDir, "Claude", "claude_desktop_config.json");
|
|
120
|
-
ConfigManager.preferencesPath = path.join(homeDir, ".smithery-cli", "preferences.json");
|
|
121
|
-
}
|
|
122
|
-
})();
|
package/dist/utils/config.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { dirname } from "node:path";
|
|
6
|
-
import { exec } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = dirname(__filename);
|
|
10
|
-
const execAsync = promisify(exec);
|
|
11
|
-
export const VALID_CLIENTS = ["claude"];
|
|
12
|
-
function getPackageRuntime(packageName) {
|
|
13
|
-
const packageListPath = path.join(dirname(__dirname), "../packages/package-list.json");
|
|
14
|
-
if (!fs.existsSync(packageListPath)) {
|
|
15
|
-
return "node"; // Default to node if package list doesn't exist
|
|
16
|
-
}
|
|
17
|
-
const packageList = JSON.parse(fs.readFileSync(packageListPath, "utf8"));
|
|
18
|
-
const pkg = packageList.find((p) => p.name === packageName);
|
|
19
|
-
return pkg?.runtime || "node";
|
|
20
|
-
}
|
|
21
|
-
export function getConfigPath() {
|
|
22
|
-
if (process.platform === "win32") {
|
|
23
|
-
return path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
24
|
-
}
|
|
25
|
-
const configDir = path.join(os.homedir(), "Library", "Application Support", "Claude");
|
|
26
|
-
return path.join(configDir, "claude_desktop_config.json");
|
|
27
|
-
}
|
|
28
|
-
export function readConfig() {
|
|
29
|
-
const configPath = getConfigPath();
|
|
30
|
-
if (!fs.existsSync(configPath)) {
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
33
|
-
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
34
|
-
}
|
|
35
|
-
export function writeConfig(config) {
|
|
36
|
-
const configPath = getConfigPath();
|
|
37
|
-
const configDir = path.dirname(configPath);
|
|
38
|
-
if (!fs.existsSync(configDir)) {
|
|
39
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
40
|
-
}
|
|
41
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
42
|
-
}
|
|
43
|
-
export async function installMCPServer(packageName, envVars, runtime) {
|
|
44
|
-
const config = readConfig();
|
|
45
|
-
const serverName = packageName.replace(/\//g, "-");
|
|
46
|
-
const effectiveRuntime = runtime || getPackageRuntime(packageName);
|
|
47
|
-
if (!config.mcpServers) {
|
|
48
|
-
config.mcpServers = {};
|
|
49
|
-
}
|
|
50
|
-
let command = "npx";
|
|
51
|
-
if (effectiveRuntime === "python") {
|
|
52
|
-
try {
|
|
53
|
-
const { stdout } = await execAsync("which uvx");
|
|
54
|
-
command = stdout.trim();
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
command = "uvx"; // Fallback to just 'uvx' if which fails
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const serverConfig = {
|
|
61
|
-
runtime: effectiveRuntime,
|
|
62
|
-
env: envVars,
|
|
63
|
-
command,
|
|
64
|
-
args: effectiveRuntime === "python" ? [packageName] : ["-y", packageName],
|
|
65
|
-
};
|
|
66
|
-
config.mcpServers[serverName] = serverConfig;
|
|
67
|
-
writeConfig(config);
|
|
68
|
-
}
|
|
69
|
-
export function envVarsToArgs(envVars) {
|
|
70
|
-
return Object.entries(envVars).flatMap(([key, value]) => {
|
|
71
|
-
const argName = key.toLowerCase().replace(/_/g, "-");
|
|
72
|
-
return [`--${argName}`, value];
|
|
73
|
-
});
|
|
74
|
-
}
|
package/dist/utils/display.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
|
-
export async function displayPackageDetailsWithActions(pkg) {
|
|
4
|
-
console.log(`\n${chalk.bold.cyan("Package Details:")}`);
|
|
5
|
-
console.log(chalk.bold("ID: ") + pkg.id);
|
|
6
|
-
console.log(chalk.bold("Name: ") + pkg.name);
|
|
7
|
-
console.log(chalk.bold("Description: ") + pkg.description);
|
|
8
|
-
console.log(chalk.bold("Vendor: ") + pkg.vendor);
|
|
9
|
-
console.log(chalk.bold("License: ") + pkg.license);
|
|
10
|
-
// console.log(chalk.bold('Runtime: ') + (pkg.runtime || 'node'));
|
|
11
|
-
console.log(chalk.bold("Source: ") + (pkg.sourceUrl || "Not available"));
|
|
12
|
-
console.log(chalk.bold("Homepage: ") + (pkg.homepage || "Not available"));
|
|
13
|
-
console.log(chalk.bold("Status: ") +
|
|
14
|
-
(pkg.isInstalled ? chalk.green("Installed") : "Not installed") +
|
|
15
|
-
(pkg.isVerified ? "" : chalk.yellow(" (Unverified package)")));
|
|
16
|
-
const choices = [
|
|
17
|
-
{
|
|
18
|
-
name: pkg.isInstalled
|
|
19
|
-
? "🔄 Reinstall this package"
|
|
20
|
-
: "📦 Install this package",
|
|
21
|
-
value: "install",
|
|
22
|
-
},
|
|
23
|
-
...(pkg.isInstalled
|
|
24
|
-
? [{ name: "🗑️ Uninstall this package", value: "uninstall" }]
|
|
25
|
-
: []),
|
|
26
|
-
...(pkg.sourceUrl ? [{ name: "🔗 Open source URL", value: "open" }] : []),
|
|
27
|
-
{ name: "⬅️ Back to list", value: "back" },
|
|
28
|
-
{ name: "❌ Exit", value: "exit" },
|
|
29
|
-
];
|
|
30
|
-
const { action } = await inquirer.prompt([
|
|
31
|
-
{
|
|
32
|
-
type: "list",
|
|
33
|
-
name: "action",
|
|
34
|
-
message: "What would you like to do?",
|
|
35
|
-
choices,
|
|
36
|
-
},
|
|
37
|
-
]);
|
|
38
|
-
return action;
|
|
39
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { displayPackageDetailsWithActions } from "./display.js";
|
|
3
|
-
import { installPackage, uninstallPackage } from "./package-management.js";
|
|
4
|
-
import { confirmUninstall } from "./ui.js";
|
|
5
|
-
export async function handlePackageAction(pkg, action, handlers, showActionsAfter = true) {
|
|
6
|
-
switch (action) {
|
|
7
|
-
case "install":
|
|
8
|
-
console.log(chalk.cyan(`\nPreparing to install ${pkg.name}...`));
|
|
9
|
-
await installPackage(pkg);
|
|
10
|
-
pkg.isInstalled = true;
|
|
11
|
-
if (handlers.onInstall) {
|
|
12
|
-
await handlers.onInstall(pkg);
|
|
13
|
-
}
|
|
14
|
-
break;
|
|
15
|
-
case "uninstall":
|
|
16
|
-
if (await confirmUninstall(pkg.name)) {
|
|
17
|
-
await uninstallPackage(pkg.name);
|
|
18
|
-
console.log(chalk.green(`Successfully uninstalled ${pkg.name}`));
|
|
19
|
-
pkg.isInstalled = false;
|
|
20
|
-
if (handlers.onUninstall) {
|
|
21
|
-
await handlers.onUninstall(pkg);
|
|
22
|
-
return; // Don't show actions after uninstall if handler provided
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
console.log("Uninstallation cancelled.");
|
|
27
|
-
}
|
|
28
|
-
break;
|
|
29
|
-
case "open":
|
|
30
|
-
if (pkg.sourceUrl) {
|
|
31
|
-
const open = (await import("open")).default;
|
|
32
|
-
await open(pkg.sourceUrl);
|
|
33
|
-
console.log(chalk.green(`\nOpened ${pkg.sourceUrl} in your browser`));
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
console.log(chalk.yellow("\nNo source URL available for this package"));
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
case "back":
|
|
40
|
-
if (handlers.onBack) {
|
|
41
|
-
await handlers.onBack();
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
case "exit":
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
// Show actions again after completing an action (except for exit/back)
|
|
48
|
-
if (showActionsAfter) {
|
|
49
|
-
const nextAction = await displayPackageDetailsWithActions(pkg);
|
|
50
|
-
await handlePackageAction(pkg, nextAction, handlers);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { exec } from "node:child_process";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
|
-
import { promisify } from "node:util";
|
|
4
|
-
import { ConfigManager } from "./config-manager.js";
|
|
5
|
-
import { analytics } from "./analytics.js";
|
|
6
|
-
const execAsync = promisify(exec);
|
|
7
|
-
async function checkAnalyticsConsent() {
|
|
8
|
-
const prefs = ConfigManager.readPreferences();
|
|
9
|
-
if (typeof prefs.allowAnalytics === "boolean") {
|
|
10
|
-
return prefs.allowAnalytics;
|
|
11
|
-
}
|
|
12
|
-
const { allowAnalytics } = await inquirer.prompt([
|
|
13
|
-
{
|
|
14
|
-
type: "confirm",
|
|
15
|
-
name: "allowAnalytics",
|
|
16
|
-
message: "Would you like to help improve @smithery/cli by sharing anonymous installation analytics?",
|
|
17
|
-
default: true,
|
|
18
|
-
},
|
|
19
|
-
]);
|
|
20
|
-
ConfigManager.writePreferences({ ...prefs, allowAnalytics });
|
|
21
|
-
return allowAnalytics;
|
|
22
|
-
}
|
|
23
|
-
async function trackInstallation(packageName, client) {
|
|
24
|
-
return analytics.trackInstallation(packageName, client);
|
|
25
|
-
}
|
|
26
|
-
async function promptForPlaceholder(key, message) {
|
|
27
|
-
const { value } = await inquirer.prompt([
|
|
28
|
-
{
|
|
29
|
-
type: "input",
|
|
30
|
-
name: "value",
|
|
31
|
-
message: message || `Please enter value for ${key}:`,
|
|
32
|
-
validate: (input) => (input ? true : `${key} is required`),
|
|
33
|
-
},
|
|
34
|
-
]);
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
async function promptForEnvVars(pkg) {
|
|
38
|
-
const connection = pkg.connections?.[0];
|
|
39
|
-
if (!connection?.stdio) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const envVars = {};
|
|
43
|
-
const placeholderPattern = /\${(\w+)}/;
|
|
44
|
-
// Handle env variables
|
|
45
|
-
if (connection.stdio.env) {
|
|
46
|
-
for (const [key, value] of Object.entries(connection.stdio.env)) {
|
|
47
|
-
const match = String(value).match(placeholderPattern);
|
|
48
|
-
if (!match)
|
|
49
|
-
continue;
|
|
50
|
-
const varName = match[1];
|
|
51
|
-
const existingEnvVar = process.env[key];
|
|
52
|
-
if (existingEnvVar) {
|
|
53
|
-
const { reuseExisting } = await inquirer.prompt([
|
|
54
|
-
{
|
|
55
|
-
type: "confirm",
|
|
56
|
-
name: "reuseExisting",
|
|
57
|
-
message: `Found ${key} in your environment variables. Would you like to use it?`,
|
|
58
|
-
default: true,
|
|
59
|
-
},
|
|
60
|
-
]);
|
|
61
|
-
if (reuseExisting) {
|
|
62
|
-
envVars[key] = existingEnvVar;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
envVars[key] = await promptForPlaceholder(key);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Handle args placeholders
|
|
70
|
-
if (connection.stdio.args) {
|
|
71
|
-
for (let i = 0; i < connection.stdio.args.length; i++) {
|
|
72
|
-
const arg = connection.stdio.args[i];
|
|
73
|
-
const match = String(arg).match(placeholderPattern);
|
|
74
|
-
if (!match)
|
|
75
|
-
continue;
|
|
76
|
-
const varName = match[1];
|
|
77
|
-
const value = await promptForPlaceholder(varName);
|
|
78
|
-
connection.stdio.args[i] = arg.replace(placeholderPattern, value);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return Object.keys(envVars).length > 0 ? envVars : undefined;
|
|
82
|
-
}
|
|
83
|
-
async function isClientRunning(client) {
|
|
84
|
-
if (!client)
|
|
85
|
-
return false;
|
|
86
|
-
try {
|
|
87
|
-
const platform = process.platform;
|
|
88
|
-
// Map of supported clients to their process names
|
|
89
|
-
// Currently only Claude is officially supported
|
|
90
|
-
// Other entries are placeholders for future integrations
|
|
91
|
-
const clientProcess = {
|
|
92
|
-
claude: "Claude",
|
|
93
|
-
// jan: "Jan", // Placeholder for future client integration
|
|
94
|
-
// Add more clients here as they become supported
|
|
95
|
-
}[client] || client;
|
|
96
|
-
if (platform === "win32") {
|
|
97
|
-
const { stdout } = await execAsync(`tasklist /FI "IMAGENAME eq ${clientProcess}.exe" /NH`);
|
|
98
|
-
return stdout.includes(`${clientProcess}.exe`);
|
|
99
|
-
}
|
|
100
|
-
else if (platform === "darwin") {
|
|
101
|
-
const { stdout } = await execAsync(`pgrep -x "${clientProcess}"`);
|
|
102
|
-
return !!stdout.trim();
|
|
103
|
-
}
|
|
104
|
-
else if (platform === "linux") {
|
|
105
|
-
const { stdout } = await execAsync(`pgrep -f "${clientProcess.toLowerCase()}"`);
|
|
106
|
-
return !!stdout.trim();
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
// If the command fails, assume client is not running
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async function restartClient(client) {
|
|
116
|
-
const clientProcess = {
|
|
117
|
-
claude: "Claude",
|
|
118
|
-
jan: "Jan",
|
|
119
|
-
// Add more clients here
|
|
120
|
-
}[client] || client;
|
|
121
|
-
try {
|
|
122
|
-
const platform = process.platform;
|
|
123
|
-
if (platform === "win32") {
|
|
124
|
-
await execAsync(`taskkill /F /IM "${clientProcess}.exe" && start "" "${clientProcess}.exe"`);
|
|
125
|
-
}
|
|
126
|
-
else if (platform === "darwin") {
|
|
127
|
-
await execAsync(`killall "${clientProcess}" && open -a "${clientProcess}"`);
|
|
128
|
-
}
|
|
129
|
-
else if (platform === "linux") {
|
|
130
|
-
await execAsync(`pkill -f "${clientProcess.toLowerCase()}" && ${clientProcess.toLowerCase()}`);
|
|
131
|
-
}
|
|
132
|
-
// Wait a moment for the app to close before reopening
|
|
133
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
134
|
-
// Reopen the app
|
|
135
|
-
if (platform === "win32") {
|
|
136
|
-
await execAsync(`start "" "${clientProcess}.exe"`);
|
|
137
|
-
}
|
|
138
|
-
else if (platform === "darwin") {
|
|
139
|
-
await execAsync(`open -a "${clientProcess}"`);
|
|
140
|
-
}
|
|
141
|
-
else if (platform === "linux") {
|
|
142
|
-
await execAsync(clientProcess.toLowerCase());
|
|
143
|
-
}
|
|
144
|
-
console.log(`${clientProcess} has been restarted.`);
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
console.error(`Failed to restart ${clientProcess}:`, error);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
async function promptForRestart(client) {
|
|
151
|
-
if (!client)
|
|
152
|
-
return false;
|
|
153
|
-
// Check if client is running first
|
|
154
|
-
const isRunning = await isClientRunning(client);
|
|
155
|
-
if (!isRunning) {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
const { shouldRestart } = await inquirer.prompt([
|
|
159
|
-
{
|
|
160
|
-
type: "confirm",
|
|
161
|
-
name: "shouldRestart",
|
|
162
|
-
message: `Would you like to restart the ${client} app to apply changes?`,
|
|
163
|
-
default: true,
|
|
164
|
-
},
|
|
165
|
-
]);
|
|
166
|
-
if (shouldRestart) {
|
|
167
|
-
console.log(`Restarting ${client} app...`);
|
|
168
|
-
await restartClient(client);
|
|
169
|
-
}
|
|
170
|
-
return shouldRestart;
|
|
171
|
-
}
|
|
172
|
-
export async function installPackage(pkg) {
|
|
173
|
-
try {
|
|
174
|
-
// Temporarily bypass UV check for Python packages
|
|
175
|
-
/* if (pkg.runtime === 'python') {
|
|
176
|
-
const hasUV = await checkUVInstalled();
|
|
177
|
-
if (!hasUV) {
|
|
178
|
-
const installed = await promptForUVInstall(inquirer);
|
|
179
|
-
if (!installed) {
|
|
180
|
-
console.log('Proceeding with installation, but uvx commands may fail...');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} */
|
|
184
|
-
const envVars = await promptForEnvVars(pkg);
|
|
185
|
-
// Use the connection stdio config directly
|
|
186
|
-
const connection = pkg.connections?.[0];
|
|
187
|
-
const serverConfig = {
|
|
188
|
-
command: connection.stdio.command,
|
|
189
|
-
args: connection.stdio.args,
|
|
190
|
-
env: {
|
|
191
|
-
...(envVars || {}),
|
|
192
|
-
},
|
|
193
|
-
};
|
|
194
|
-
// Install package with client-specific configuration
|
|
195
|
-
await ConfigManager.installPackage(pkg.id, serverConfig, pkg.client);
|
|
196
|
-
console.log(`Updated ${pkg.client || "default"} configuration`);
|
|
197
|
-
if (await checkAnalyticsConsent()) {
|
|
198
|
-
await trackInstallation(pkg.id, pkg.client);
|
|
199
|
-
}
|
|
200
|
-
await promptForRestart(pkg.client);
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
console.error("Failed to install package:", error);
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
export async function uninstallPackage(packageName, client) {
|
|
208
|
-
try {
|
|
209
|
-
await ConfigManager.uninstallPackage(packageName);
|
|
210
|
-
console.log(`\nUninstalled ${packageName}`);
|
|
211
|
-
await promptForRestart(client);
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
console.error("Failed to uninstall package:", error);
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
}
|