claudeup 1.7.0 → 3.3.0
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/bin/claudeup.js +20 -2
- package/package.json +10 -19
- package/src/data/cli-tools.js +123 -0
- package/src/data/cli-tools.ts +140 -0
- package/{dist → src}/data/marketplaces.js +23 -24
- package/src/data/marketplaces.ts +95 -0
- package/src/data/mcp-servers.js +509 -0
- package/src/data/mcp-servers.ts +526 -0
- package/src/data/statuslines.js +159 -0
- package/src/data/statuslines.ts +188 -0
- package/src/index.js +4 -0
- package/src/index.ts +5 -0
- package/{dist → src}/main.js +46 -47
- package/src/main.tsx +145 -0
- package/src/opentui.d.ts +191 -0
- package/src/prerunner/index.js +87 -0
- package/src/prerunner/index.ts +124 -0
- package/{dist → src}/services/claude-runner.js +9 -10
- package/src/services/claude-runner.ts +31 -0
- package/{dist → src}/services/claude-settings.js +72 -33
- package/src/services/claude-settings.ts +934 -0
- package/src/services/local-marketplace.js +339 -0
- package/src/services/local-marketplace.ts +489 -0
- package/{dist → src}/services/mcp-registry.js +13 -14
- package/src/services/mcp-registry.ts +105 -0
- package/{dist → src}/services/plugin-manager.js +61 -13
- package/src/services/plugin-manager.ts +693 -0
- package/{dist → src}/services/plugin-mcp-config.js +19 -18
- package/src/services/plugin-mcp-config.ts +242 -0
- package/{dist → src}/services/update-cache.js +7 -8
- package/src/services/update-cache.ts +78 -0
- package/{dist → src}/services/version-check.js +15 -14
- package/src/services/version-check.ts +122 -0
- package/src/types/index.js +1 -0
- package/src/types/index.ts +141 -0
- package/src/ui/App.js +213 -0
- package/src/ui/App.tsx +359 -0
- package/src/ui/components/CategoryHeader.js +9 -0
- package/src/ui/components/CategoryHeader.tsx +41 -0
- package/{dist → src}/ui/components/ScrollableList.js +19 -6
- package/src/ui/components/ScrollableList.tsx +98 -0
- package/src/ui/components/SearchInput.js +19 -0
- package/src/ui/components/SearchInput.tsx +56 -0
- package/src/ui/components/StyledText.js +39 -0
- package/src/ui/components/StyledText.tsx +70 -0
- package/src/ui/components/TabBar.js +38 -0
- package/src/ui/components/TabBar.tsx +88 -0
- package/src/ui/components/layout/Panel.js +6 -0
- package/src/ui/components/layout/Panel.tsx +62 -0
- package/src/ui/components/layout/ProgressBar.js +14 -0
- package/src/ui/components/layout/ProgressBar.tsx +47 -0
- package/src/ui/components/layout/ScopeTabs.js +6 -0
- package/src/ui/components/layout/ScopeTabs.tsx +53 -0
- package/src/ui/components/layout/ScreenLayout.js +21 -0
- package/src/ui/components/layout/ScreenLayout.tsx +147 -0
- package/src/ui/components/layout/index.js +4 -0
- package/src/ui/components/layout/index.ts +4 -0
- package/src/ui/components/modals/ConfirmModal.js +14 -0
- package/src/ui/components/modals/ConfirmModal.tsx +59 -0
- package/src/ui/components/modals/InputModal.js +16 -0
- package/src/ui/components/modals/InputModal.tsx +68 -0
- package/src/ui/components/modals/LoadingModal.js +14 -0
- package/src/ui/components/modals/LoadingModal.tsx +40 -0
- package/src/ui/components/modals/MessageModal.js +16 -0
- package/src/ui/components/modals/MessageModal.tsx +64 -0
- package/src/ui/components/modals/ModalContainer.js +56 -0
- package/src/ui/components/modals/ModalContainer.tsx +104 -0
- package/src/ui/components/modals/SelectModal.js +26 -0
- package/src/ui/components/modals/SelectModal.tsx +82 -0
- package/src/ui/components/modals/index.js +6 -0
- package/src/ui/components/modals/index.ts +6 -0
- package/src/ui/hooks/index.js +3 -0
- package/src/ui/hooks/index.ts +3 -0
- package/{dist → src}/ui/hooks/useAsyncData.js +21 -22
- package/src/ui/hooks/useAsyncData.ts +127 -0
- package/src/ui/hooks/useKeyboard.js +13 -0
- package/src/ui/hooks/useKeyboard.ts +26 -0
- package/src/ui/hooks/useKeyboardHandler.js +39 -0
- package/src/ui/hooks/useKeyboardHandler.ts +63 -0
- package/{dist → src}/ui/screens/CliToolsScreen.js +60 -54
- package/src/ui/screens/CliToolsScreen.tsx +468 -0
- package/src/ui/screens/EnvVarsScreen.js +154 -0
- package/src/ui/screens/EnvVarsScreen.tsx +269 -0
- package/{dist → src}/ui/screens/McpRegistryScreen.js +56 -55
- package/src/ui/screens/McpRegistryScreen.tsx +331 -0
- package/{dist → src}/ui/screens/McpScreen.js +46 -47
- package/src/ui/screens/McpScreen.tsx +392 -0
- package/src/ui/screens/ModelSelectorScreen.js +292 -0
- package/src/ui/screens/ModelSelectorScreen.tsx +441 -0
- package/{dist → src}/ui/screens/PluginsScreen.js +305 -293
- package/src/ui/screens/PluginsScreen.tsx +1231 -0
- package/src/ui/screens/StatusLineScreen.js +200 -0
- package/src/ui/screens/StatusLineScreen.tsx +411 -0
- package/src/ui/screens/index.js +7 -0
- package/src/ui/screens/index.ts +7 -0
- package/src/ui/state/AnimationContext.js +34 -0
- package/src/ui/state/AnimationContext.tsx +76 -0
- package/{dist → src}/ui/state/AppContext.js +31 -32
- package/src/ui/state/AppContext.tsx +235 -0
- package/{dist → src}/ui/state/DimensionsContext.js +16 -17
- package/src/ui/state/DimensionsContext.tsx +144 -0
- package/{dist → src}/ui/state/reducer.js +89 -90
- package/src/ui/state/reducer.ts +467 -0
- package/src/ui/state/types.js +1 -0
- package/src/ui/state/types.ts +273 -0
- package/{dist → src}/utils/command-utils.js +3 -4
- package/src/utils/command-utils.ts +20 -0
- package/{dist → src}/utils/fuzzy-search.js +2 -3
- package/src/utils/fuzzy-search.ts +138 -0
- package/{dist → src}/utils/string-utils.js +6 -6
- package/src/utils/string-utils.ts +88 -0
- package/dist/data/cli-tools.d.ts +0 -13
- package/dist/data/cli-tools.d.ts.map +0 -1
- package/dist/data/cli-tools.js +0 -124
- package/dist/data/cli-tools.js.map +0 -1
- package/dist/data/marketplaces.d.ts +0 -6
- package/dist/data/marketplaces.d.ts.map +0 -1
- package/dist/data/marketplaces.js.map +0 -1
- package/dist/data/mcp-servers.d.ts +0 -8
- package/dist/data/mcp-servers.d.ts.map +0 -1
- package/dist/data/mcp-servers.js +0 -503
- package/dist/data/mcp-servers.js.map +0 -1
- package/dist/data/statuslines.d.ts +0 -10
- package/dist/data/statuslines.d.ts.map +0 -1
- package/dist/data/statuslines.js +0 -160
- package/dist/data/statuslines.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -90
- package/dist/index.js.map +0 -1
- package/dist/main.d.ts +0 -3
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/prerunner/index.d.ts +0 -7
- package/dist/prerunner/index.d.ts.map +0 -1
- package/dist/prerunner/index.js +0 -64
- package/dist/prerunner/index.js.map +0 -1
- package/dist/services/claude-runner.d.ts +0 -7
- package/dist/services/claude-runner.d.ts.map +0 -1
- package/dist/services/claude-runner.js.map +0 -1
- package/dist/services/claude-settings.d.ts +0 -73
- package/dist/services/claude-settings.d.ts.map +0 -1
- package/dist/services/claude-settings.js.map +0 -1
- package/dist/services/local-marketplace.d.ts +0 -111
- package/dist/services/local-marketplace.d.ts.map +0 -1
- package/dist/services/local-marketplace.js +0 -599
- package/dist/services/local-marketplace.js.map +0 -1
- package/dist/services/mcp-registry.d.ts +0 -10
- package/dist/services/mcp-registry.d.ts.map +0 -1
- package/dist/services/mcp-registry.js.map +0 -1
- package/dist/services/plugin-manager.d.ts +0 -65
- package/dist/services/plugin-manager.d.ts.map +0 -1
- package/dist/services/plugin-manager.js.map +0 -1
- package/dist/services/plugin-mcp-config.d.ts +0 -52
- package/dist/services/plugin-mcp-config.d.ts.map +0 -1
- package/dist/services/plugin-mcp-config.js.map +0 -1
- package/dist/services/update-cache.d.ts +0 -16
- package/dist/services/update-cache.d.ts.map +0 -1
- package/dist/services/update-cache.js.map +0 -1
- package/dist/services/version-check.d.ts +0 -20
- package/dist/services/version-check.d.ts.map +0 -1
- package/dist/services/version-check.js.map +0 -1
- package/dist/types/index.d.ts +0 -105
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/ui/InkApp.d.ts +0 -5
- package/dist/ui/InkApp.d.ts.map +0 -1
- package/dist/ui/InkApp.js +0 -188
- package/dist/ui/InkApp.js.map +0 -1
- package/dist/ui/components/CategoryHeader.d.ts +0 -16
- package/dist/ui/components/CategoryHeader.d.ts.map +0 -1
- package/dist/ui/components/CategoryHeader.js +0 -11
- package/dist/ui/components/CategoryHeader.js.map +0 -1
- package/dist/ui/components/ScrollableList.d.ts +0 -16
- package/dist/ui/components/ScrollableList.d.ts.map +0 -1
- package/dist/ui/components/ScrollableList.js.map +0 -1
- package/dist/ui/components/SearchInput.d.ts +0 -18
- package/dist/ui/components/SearchInput.d.ts.map +0 -1
- package/dist/ui/components/SearchInput.js +0 -30
- package/dist/ui/components/SearchInput.js.map +0 -1
- package/dist/ui/components/TabBar.d.ts +0 -8
- package/dist/ui/components/TabBar.d.ts.map +0 -1
- package/dist/ui/components/TabBar.js +0 -18
- package/dist/ui/components/TabBar.js.map +0 -1
- package/dist/ui/components/layout/Footer.d.ts +0 -14
- package/dist/ui/components/layout/Footer.d.ts.map +0 -1
- package/dist/ui/components/layout/Footer.js +0 -23
- package/dist/ui/components/layout/Footer.js.map +0 -1
- package/dist/ui/components/layout/Header.d.ts +0 -4
- package/dist/ui/components/layout/Header.d.ts.map +0 -1
- package/dist/ui/components/layout/Header.js +0 -25
- package/dist/ui/components/layout/Header.js.map +0 -1
- package/dist/ui/components/layout/Panel.d.ts +0 -22
- package/dist/ui/components/layout/Panel.d.ts.map +0 -1
- package/dist/ui/components/layout/Panel.js +0 -8
- package/dist/ui/components/layout/Panel.js.map +0 -1
- package/dist/ui/components/layout/ProgressBar.d.ts +0 -12
- package/dist/ui/components/layout/ProgressBar.d.ts.map +0 -1
- package/dist/ui/components/layout/ProgressBar.js +0 -16
- package/dist/ui/components/layout/ProgressBar.js.map +0 -1
- package/dist/ui/components/layout/ScopeTabs.d.ts +0 -12
- package/dist/ui/components/layout/ScopeTabs.d.ts.map +0 -1
- package/dist/ui/components/layout/ScopeTabs.js +0 -8
- package/dist/ui/components/layout/ScopeTabs.js.map +0 -1
- package/dist/ui/components/layout/ScreenLayout.d.ts +0 -30
- package/dist/ui/components/layout/ScreenLayout.d.ts.map +0 -1
- package/dist/ui/components/layout/ScreenLayout.js +0 -23
- package/dist/ui/components/layout/ScreenLayout.js.map +0 -1
- package/dist/ui/components/layout/index.d.ts +0 -7
- package/dist/ui/components/layout/index.d.ts.map +0 -1
- package/dist/ui/components/layout/index.js +0 -7
- package/dist/ui/components/layout/index.js.map +0 -1
- package/dist/ui/components/modals/ConfirmModal.d.ts +0 -14
- package/dist/ui/components/modals/ConfirmModal.d.ts.map +0 -1
- package/dist/ui/components/modals/ConfirmModal.js +0 -15
- package/dist/ui/components/modals/ConfirmModal.js.map +0 -1
- package/dist/ui/components/modals/InputModal.d.ts +0 -16
- package/dist/ui/components/modals/InputModal.d.ts.map +0 -1
- package/dist/ui/components/modals/InputModal.js +0 -23
- package/dist/ui/components/modals/InputModal.js.map +0 -1
- package/dist/ui/components/modals/LoadingModal.d.ts +0 -8
- package/dist/ui/components/modals/LoadingModal.d.ts.map +0 -1
- package/dist/ui/components/modals/LoadingModal.js +0 -8
- package/dist/ui/components/modals/LoadingModal.js.map +0 -1
- package/dist/ui/components/modals/MessageModal.d.ts +0 -14
- package/dist/ui/components/modals/MessageModal.d.ts.map +0 -1
- package/dist/ui/components/modals/MessageModal.js +0 -17
- package/dist/ui/components/modals/MessageModal.js.map +0 -1
- package/dist/ui/components/modals/ModalContainer.d.ts +0 -7
- package/dist/ui/components/modals/ModalContainer.d.ts.map +0 -1
- package/dist/ui/components/modals/ModalContainer.js +0 -38
- package/dist/ui/components/modals/ModalContainer.js.map +0 -1
- package/dist/ui/components/modals/SelectModal.d.ts +0 -17
- package/dist/ui/components/modals/SelectModal.d.ts.map +0 -1
- package/dist/ui/components/modals/SelectModal.js +0 -33
- package/dist/ui/components/modals/SelectModal.js.map +0 -1
- package/dist/ui/components/modals/index.d.ts +0 -7
- package/dist/ui/components/modals/index.d.ts.map +0 -1
- package/dist/ui/components/modals/index.js +0 -7
- package/dist/ui/components/modals/index.js.map +0 -1
- package/dist/ui/hooks/index.d.ts +0 -3
- package/dist/ui/hooks/index.d.ts.map +0 -1
- package/dist/ui/hooks/index.js +0 -3
- package/dist/ui/hooks/index.js.map +0 -1
- package/dist/ui/hooks/useAsyncData.d.ts +0 -40
- package/dist/ui/hooks/useAsyncData.d.ts.map +0 -1
- package/dist/ui/hooks/useAsyncData.js.map +0 -1
- package/dist/ui/hooks/useKeyboardNavigation.d.ts +0 -27
- package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +0 -1
- package/dist/ui/hooks/useKeyboardNavigation.js +0 -82
- package/dist/ui/hooks/useKeyboardNavigation.js.map +0 -1
- package/dist/ui/screens/CliToolsScreen.d.ts +0 -4
- package/dist/ui/screens/CliToolsScreen.d.ts.map +0 -1
- package/dist/ui/screens/CliToolsScreen.js.map +0 -1
- package/dist/ui/screens/EnvVarsScreen.d.ts +0 -4
- package/dist/ui/screens/EnvVarsScreen.d.ts.map +0 -1
- package/dist/ui/screens/EnvVarsScreen.js +0 -145
- package/dist/ui/screens/EnvVarsScreen.js.map +0 -1
- package/dist/ui/screens/McpRegistryScreen.d.ts +0 -4
- package/dist/ui/screens/McpRegistryScreen.d.ts.map +0 -1
- package/dist/ui/screens/McpRegistryScreen.js.map +0 -1
- package/dist/ui/screens/McpScreen.d.ts +0 -4
- package/dist/ui/screens/McpScreen.d.ts.map +0 -1
- package/dist/ui/screens/McpScreen.js.map +0 -1
- package/dist/ui/screens/ModelSelectorScreen.d.ts +0 -4
- package/dist/ui/screens/ModelSelectorScreen.d.ts.map +0 -1
- package/dist/ui/screens/ModelSelectorScreen.js +0 -143
- package/dist/ui/screens/ModelSelectorScreen.js.map +0 -1
- package/dist/ui/screens/PluginsScreen.d.ts +0 -4
- package/dist/ui/screens/PluginsScreen.d.ts.map +0 -1
- package/dist/ui/screens/PluginsScreen.js.map +0 -1
- package/dist/ui/screens/StatusLineScreen.d.ts +0 -4
- package/dist/ui/screens/StatusLineScreen.d.ts.map +0 -1
- package/dist/ui/screens/StatusLineScreen.js +0 -197
- package/dist/ui/screens/StatusLineScreen.js.map +0 -1
- package/dist/ui/screens/index.d.ts +0 -8
- package/dist/ui/screens/index.d.ts.map +0 -1
- package/dist/ui/screens/index.js +0 -8
- package/dist/ui/screens/index.js.map +0 -1
- package/dist/ui/state/AppContext.d.ts +0 -40
- package/dist/ui/state/AppContext.d.ts.map +0 -1
- package/dist/ui/state/AppContext.js.map +0 -1
- package/dist/ui/state/DimensionsContext.d.ts +0 -27
- package/dist/ui/state/DimensionsContext.d.ts.map +0 -1
- package/dist/ui/state/DimensionsContext.js.map +0 -1
- package/dist/ui/state/reducer.d.ts +0 -4
- package/dist/ui/state/reducer.d.ts.map +0 -1
- package/dist/ui/state/reducer.js.map +0 -1
- package/dist/ui/state/types.d.ts +0 -266
- package/dist/ui/state/types.d.ts.map +0 -1
- package/dist/ui/state/types.js +0 -2
- package/dist/ui/state/types.js.map +0 -1
- package/dist/utils/command-utils.d.ts +0 -8
- package/dist/utils/command-utils.d.ts.map +0 -1
- package/dist/utils/command-utils.js.map +0 -1
- package/dist/utils/fuzzy-search.d.ts +0 -33
- package/dist/utils/fuzzy-search.d.ts.map +0 -1
- package/dist/utils/fuzzy-search.js.map +0 -1
- package/dist/utils/string-utils.d.ts +0 -24
- package/dist/utils/string-utils.d.ts.map +0 -1
- package/dist/utils/string-utils.js.map +0 -1
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import os from
|
|
4
|
-
const CLAUDE_PLUGINS_DIR = path.join(os.homedir(),
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
const CLAUDE_PLUGINS_DIR = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
5
5
|
/**
|
|
6
6
|
* Extract ${VAR_NAME} patterns from a value
|
|
7
7
|
* @param value - The value to search for env var references
|
|
8
8
|
* @returns Array of env var names found
|
|
9
9
|
*/
|
|
10
10
|
function extractEnvVarReferences(value) {
|
|
11
|
-
if (typeof value !==
|
|
11
|
+
if (typeof value !== "string")
|
|
12
12
|
return [];
|
|
13
13
|
const matches = value.match(/\$\{([A-Z_][A-Z0-9_]*)\}/g);
|
|
14
14
|
if (!matches)
|
|
15
15
|
return [];
|
|
16
|
-
return matches
|
|
16
|
+
return matches
|
|
17
|
+
.map((match) => {
|
|
17
18
|
// Extract VAR_NAME from ${VAR_NAME}
|
|
18
19
|
const innerMatch = match.match(/\$\{([A-Z_][A-Z0-9_]*)\}/);
|
|
19
|
-
return innerMatch ? innerMatch[1] :
|
|
20
|
-
})
|
|
20
|
+
return innerMatch ? innerMatch[1] : "";
|
|
21
|
+
})
|
|
22
|
+
.filter(Boolean);
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
23
25
|
* Recursively extract all env var references from an object
|
|
24
26
|
*/
|
|
25
27
|
function extractAllEnvVarReferences(obj) {
|
|
26
28
|
const vars = [];
|
|
27
|
-
if (typeof obj ===
|
|
29
|
+
if (typeof obj === "string") {
|
|
28
30
|
vars.push(...extractEnvVarReferences(obj));
|
|
29
31
|
}
|
|
30
32
|
else if (Array.isArray(obj)) {
|
|
@@ -32,7 +34,7 @@ function extractAllEnvVarReferences(obj) {
|
|
|
32
34
|
vars.push(...extractAllEnvVarReferences(item));
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
else if (obj && typeof obj ===
|
|
37
|
+
else if (obj && typeof obj === "object") {
|
|
36
38
|
for (const value of Object.values(obj)) {
|
|
37
39
|
vars.push(...extractAllEnvVarReferences(value));
|
|
38
40
|
}
|
|
@@ -49,8 +51,8 @@ function getPluginMcpConfigPath(marketplaceName, pluginSource) {
|
|
|
49
51
|
if (!pluginSource)
|
|
50
52
|
return undefined;
|
|
51
53
|
const marketplacePath = path.join(CLAUDE_PLUGINS_DIR, marketplaceName);
|
|
52
|
-
const pluginPath = path.join(marketplacePath, pluginSource.replace(/^\.\//,
|
|
53
|
-
const mcpConfigPath = path.join(pluginPath,
|
|
54
|
+
const pluginPath = path.join(marketplacePath, pluginSource.replace(/^\.\//, ""));
|
|
55
|
+
const mcpConfigPath = path.join(pluginPath, "mcp-servers", "mcp-config.json");
|
|
54
56
|
return mcpConfigPath;
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
@@ -69,7 +71,7 @@ export async function readPluginMcpConfig(marketplaceName, pluginSource) {
|
|
|
69
71
|
// Filter out comment fields (keys starting with _)
|
|
70
72
|
const filtered = {};
|
|
71
73
|
for (const [key, value] of Object.entries(content)) {
|
|
72
|
-
if (!key.startsWith(
|
|
74
|
+
if (!key.startsWith("_") && value && typeof value === "object") {
|
|
73
75
|
filtered[key] = value;
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -88,9 +90,9 @@ export async function readPluginMcpConfig(marketplaceName, pluginSource) {
|
|
|
88
90
|
*/
|
|
89
91
|
function generateLabel(varName) {
|
|
90
92
|
return varName
|
|
91
|
-
.split(
|
|
92
|
-
.map(word => word.charAt(0) + word.slice(1).toLowerCase())
|
|
93
|
-
.join(
|
|
93
|
+
.split("_")
|
|
94
|
+
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
|
|
95
|
+
.join(" ");
|
|
94
96
|
}
|
|
95
97
|
/**
|
|
96
98
|
* Get all environment variable requirements for a plugin
|
|
@@ -148,7 +150,7 @@ export async function getPluginEnvRequirements(marketplaceName, pluginSource) {
|
|
|
148
150
|
*/
|
|
149
151
|
export async function getPluginSourcePath(marketplaceName, pluginName) {
|
|
150
152
|
const marketplacePath = path.join(CLAUDE_PLUGINS_DIR, marketplaceName);
|
|
151
|
-
const manifestPath = path.join(marketplacePath,
|
|
153
|
+
const manifestPath = path.join(marketplacePath, ".claude-plugin", "marketplace.json");
|
|
152
154
|
try {
|
|
153
155
|
if (await fs.pathExists(manifestPath)) {
|
|
154
156
|
const manifest = await fs.readJson(manifestPath);
|
|
@@ -173,4 +175,3 @@ export async function getPluginMcpServerNames(marketplaceName, pluginSource) {
|
|
|
173
175
|
const mcpConfig = await readPluginMcpConfig(marketplaceName, pluginSource);
|
|
174
176
|
return Object.keys(mcpConfig);
|
|
175
177
|
}
|
|
176
|
-
//# sourceMappingURL=plugin-mcp-config.js.map
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
|
|
5
|
+
const CLAUDE_PLUGINS_DIR = path.join(
|
|
6
|
+
os.homedir(),
|
|
7
|
+
".claude",
|
|
8
|
+
"plugins",
|
|
9
|
+
"marketplaces",
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents a required environment variable for a plugin's MCP server
|
|
14
|
+
*/
|
|
15
|
+
export interface PluginEnvRequirement {
|
|
16
|
+
/** The environment variable name (e.g., "FIGMA_ACCESS_TOKEN") */
|
|
17
|
+
name: string;
|
|
18
|
+
/** User-friendly label (defaults to name if not provided) */
|
|
19
|
+
label: string;
|
|
20
|
+
/** The MCP server name this env var is for */
|
|
21
|
+
serverName: string;
|
|
22
|
+
/** Whether this env var is required (all detected vars are considered required) */
|
|
23
|
+
required: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Extract ${VAR_NAME} patterns from a value
|
|
28
|
+
* @param value - The value to search for env var references
|
|
29
|
+
* @returns Array of env var names found
|
|
30
|
+
*/
|
|
31
|
+
function extractEnvVarReferences(value: unknown): string[] {
|
|
32
|
+
if (typeof value !== "string") return [];
|
|
33
|
+
|
|
34
|
+
const matches = value.match(/\$\{([A-Z_][A-Z0-9_]*)\}/g);
|
|
35
|
+
if (!matches) return [];
|
|
36
|
+
|
|
37
|
+
return matches
|
|
38
|
+
.map((match) => {
|
|
39
|
+
// Extract VAR_NAME from ${VAR_NAME}
|
|
40
|
+
const innerMatch = match.match(/\$\{([A-Z_][A-Z0-9_]*)\}/);
|
|
41
|
+
return innerMatch ? innerMatch[1] : "";
|
|
42
|
+
})
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Recursively extract all env var references from an object
|
|
48
|
+
*/
|
|
49
|
+
function extractAllEnvVarReferences(obj: unknown): string[] {
|
|
50
|
+
const vars: string[] = [];
|
|
51
|
+
|
|
52
|
+
if (typeof obj === "string") {
|
|
53
|
+
vars.push(...extractEnvVarReferences(obj));
|
|
54
|
+
} else if (Array.isArray(obj)) {
|
|
55
|
+
for (const item of obj) {
|
|
56
|
+
vars.push(...extractAllEnvVarReferences(item));
|
|
57
|
+
}
|
|
58
|
+
} else if (obj && typeof obj === "object") {
|
|
59
|
+
for (const value of Object.values(obj)) {
|
|
60
|
+
vars.push(...extractAllEnvVarReferences(value));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return vars;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface McpServerConfig {
|
|
68
|
+
command?: string;
|
|
69
|
+
args?: string[];
|
|
70
|
+
env?: Record<string, string>;
|
|
71
|
+
type?: "http";
|
|
72
|
+
url?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the path to a plugin's mcp-config.json in the marketplace cache
|
|
77
|
+
* @param marketplaceName - Name of the marketplace (e.g., "mag-claude-plugins")
|
|
78
|
+
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
79
|
+
* @returns Full path to mcp-config.json or undefined if not found
|
|
80
|
+
*/
|
|
81
|
+
function getPluginMcpConfigPath(
|
|
82
|
+
marketplaceName: string,
|
|
83
|
+
pluginSource?: string,
|
|
84
|
+
): string | undefined {
|
|
85
|
+
if (!pluginSource) return undefined;
|
|
86
|
+
|
|
87
|
+
const marketplacePath = path.join(CLAUDE_PLUGINS_DIR, marketplaceName);
|
|
88
|
+
const pluginPath = path.join(
|
|
89
|
+
marketplacePath,
|
|
90
|
+
pluginSource.replace(/^\.\//, ""),
|
|
91
|
+
);
|
|
92
|
+
const mcpConfigPath = path.join(pluginPath, "mcp-servers", "mcp-config.json");
|
|
93
|
+
|
|
94
|
+
return mcpConfigPath;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Read and parse a plugin's MCP configuration
|
|
99
|
+
* @param marketplaceName - Name of the marketplace
|
|
100
|
+
* @param pluginSource - Plugin source path from marketplace.json
|
|
101
|
+
* @returns Parsed MCP config or empty object if not found
|
|
102
|
+
*/
|
|
103
|
+
export async function readPluginMcpConfig(
|
|
104
|
+
marketplaceName: string,
|
|
105
|
+
pluginSource?: string,
|
|
106
|
+
): Promise<Record<string, McpServerConfig>> {
|
|
107
|
+
const configPath = getPluginMcpConfigPath(marketplaceName, pluginSource);
|
|
108
|
+
if (!configPath) return {};
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
if (await fs.pathExists(configPath)) {
|
|
112
|
+
const content = await fs.readJson(configPath);
|
|
113
|
+
// Filter out comment fields (keys starting with _)
|
|
114
|
+
const filtered: Record<string, McpServerConfig> = {};
|
|
115
|
+
for (const [key, value] of Object.entries(content)) {
|
|
116
|
+
if (!key.startsWith("_") && value && typeof value === "object") {
|
|
117
|
+
filtered[key] = value as McpServerConfig;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return filtered;
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(`Failed to read plugin MCP config: ${error}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate user-friendly label from env var name
|
|
131
|
+
* @param varName - Environment variable name (e.g., "FIGMA_ACCESS_TOKEN")
|
|
132
|
+
* @returns Human-readable label (e.g., "Figma Access Token")
|
|
133
|
+
*/
|
|
134
|
+
function generateLabel(varName: string): string {
|
|
135
|
+
return varName
|
|
136
|
+
.split("_")
|
|
137
|
+
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
|
|
138
|
+
.join(" ");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get all environment variable requirements for a plugin
|
|
143
|
+
* Extracts ${VAR_NAME} patterns from the plugin's mcp-config.json
|
|
144
|
+
*
|
|
145
|
+
* @param marketplaceName - Name of the marketplace (e.g., "mag-claude-plugins")
|
|
146
|
+
* @param pluginSource - Plugin source path from marketplace.json (e.g., "./plugins/frontend")
|
|
147
|
+
* @returns Array of required env vars with metadata
|
|
148
|
+
*/
|
|
149
|
+
export async function getPluginEnvRequirements(
|
|
150
|
+
marketplaceName: string,
|
|
151
|
+
pluginSource?: string,
|
|
152
|
+
): Promise<PluginEnvRequirement[]> {
|
|
153
|
+
const mcpConfig = await readPluginMcpConfig(marketplaceName, pluginSource);
|
|
154
|
+
const requirements: PluginEnvRequirement[] = [];
|
|
155
|
+
const seenVars = new Set<string>();
|
|
156
|
+
|
|
157
|
+
for (const [serverName, config] of Object.entries(mcpConfig)) {
|
|
158
|
+
// Extract from env object
|
|
159
|
+
if (config.env) {
|
|
160
|
+
for (const value of Object.values(config.env)) {
|
|
161
|
+
const vars = extractEnvVarReferences(value);
|
|
162
|
+
for (const varName of vars) {
|
|
163
|
+
if (!seenVars.has(varName)) {
|
|
164
|
+
seenVars.add(varName);
|
|
165
|
+
requirements.push({
|
|
166
|
+
name: varName,
|
|
167
|
+
label: generateLabel(varName),
|
|
168
|
+
serverName,
|
|
169
|
+
required: true,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Also check args array for env var references (e.g., sqlite with ${DATABASE_PATH})
|
|
177
|
+
if (config.args) {
|
|
178
|
+
const argsVars = extractAllEnvVarReferences(config.args);
|
|
179
|
+
for (const varName of argsVars) {
|
|
180
|
+
if (!seenVars.has(varName)) {
|
|
181
|
+
seenVars.add(varName);
|
|
182
|
+
requirements.push({
|
|
183
|
+
name: varName,
|
|
184
|
+
label: generateLabel(varName),
|
|
185
|
+
serverName,
|
|
186
|
+
required: true,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return requirements;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get plugin source path from local marketplace cache
|
|
198
|
+
* @param marketplaceName - Name of the marketplace
|
|
199
|
+
* @param pluginName - Name of the plugin
|
|
200
|
+
* @returns Plugin source path or undefined
|
|
201
|
+
*/
|
|
202
|
+
export async function getPluginSourcePath(
|
|
203
|
+
marketplaceName: string,
|
|
204
|
+
pluginName: string,
|
|
205
|
+
): Promise<string | undefined> {
|
|
206
|
+
const marketplacePath = path.join(CLAUDE_PLUGINS_DIR, marketplaceName);
|
|
207
|
+
const manifestPath = path.join(
|
|
208
|
+
marketplacePath,
|
|
209
|
+
".claude-plugin",
|
|
210
|
+
"marketplace.json",
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
if (await fs.pathExists(manifestPath)) {
|
|
215
|
+
const manifest = await fs.readJson(manifestPath);
|
|
216
|
+
if (manifest.plugins && Array.isArray(manifest.plugins)) {
|
|
217
|
+
const plugin = manifest.plugins.find(
|
|
218
|
+
(p: { name: string }) => p.name === pluginName,
|
|
219
|
+
);
|
|
220
|
+
return plugin?.source;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
// Return undefined if can't read
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get all MCP server names from a plugin's config
|
|
232
|
+
* @param marketplaceName - Name of the marketplace
|
|
233
|
+
* @param pluginSource - Plugin source path
|
|
234
|
+
* @returns Array of MCP server names
|
|
235
|
+
*/
|
|
236
|
+
export async function getPluginMcpServerNames(
|
|
237
|
+
marketplaceName: string,
|
|
238
|
+
pluginSource?: string,
|
|
239
|
+
): Promise<string[]> {
|
|
240
|
+
const mcpConfig = await readPluginMcpConfig(marketplaceName, pluginSource);
|
|
241
|
+
return Object.keys(mcpConfig);
|
|
242
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { promises as fs } from
|
|
2
|
-
import path from
|
|
3
|
-
import os from
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
4
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
5
5
|
export class UpdateCache {
|
|
6
6
|
cachePath;
|
|
7
7
|
constructor() {
|
|
8
8
|
// Cache at ~/.claude/claudeup-cache.json
|
|
9
|
-
this.cachePath = path.join(os.homedir(),
|
|
9
|
+
this.cachePath = path.join(os.homedir(), ".claude", "claudeup-cache.json");
|
|
10
10
|
}
|
|
11
11
|
async shouldCheckForUpdates() {
|
|
12
12
|
const data = await this.getLastCheck();
|
|
@@ -19,7 +19,7 @@ export class UpdateCache {
|
|
|
19
19
|
}
|
|
20
20
|
async getLastCheck() {
|
|
21
21
|
try {
|
|
22
|
-
const content = await fs.readFile(this.cachePath,
|
|
22
|
+
const content = await fs.readFile(this.cachePath, "utf-8");
|
|
23
23
|
const data = JSON.parse(content);
|
|
24
24
|
return data;
|
|
25
25
|
}
|
|
@@ -34,11 +34,11 @@ export class UpdateCache {
|
|
|
34
34
|
const cacheDir = path.dirname(this.cachePath);
|
|
35
35
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
36
36
|
// Write cache file
|
|
37
|
-
await fs.writeFile(this.cachePath, JSON.stringify(result, null, 2),
|
|
37
|
+
await fs.writeFile(this.cachePath, JSON.stringify(result, null, 2), "utf-8");
|
|
38
38
|
}
|
|
39
39
|
catch (error) {
|
|
40
40
|
// Non-fatal - just log and continue
|
|
41
|
-
console.warn(
|
|
41
|
+
console.warn("Warning: Failed to save update cache:", error instanceof Error ? error.message : "Unknown error");
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
async clear() {
|
|
@@ -50,4 +50,3 @@ export class UpdateCache {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
//# sourceMappingURL=update-cache.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
|
|
5
|
+
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
6
|
+
|
|
7
|
+
export interface UpdateCacheData {
|
|
8
|
+
lastUpdateCheck: string; // ISO timestamp
|
|
9
|
+
lastUpdateResult: {
|
|
10
|
+
updated: string[]; // ["frontend@mag-claude-plugins v3.13.0", ...]
|
|
11
|
+
failed: string[]; // Marketplaces that failed
|
|
12
|
+
autoUpdated?: Array<{
|
|
13
|
+
pluginId: string;
|
|
14
|
+
oldVersion: string;
|
|
15
|
+
newVersion: string;
|
|
16
|
+
}>; // Plugins that were auto-updated
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class UpdateCache {
|
|
21
|
+
private cachePath: string;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
// Cache at ~/.claude/claudeup-cache.json
|
|
25
|
+
this.cachePath = path.join(os.homedir(), ".claude", "claudeup-cache.json");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async shouldCheckForUpdates(): Promise<boolean> {
|
|
29
|
+
const data = await this.getLastCheck();
|
|
30
|
+
if (!data) return true; // No cache
|
|
31
|
+
|
|
32
|
+
const lastCheck = new Date(data.lastUpdateCheck);
|
|
33
|
+
const now = new Date();
|
|
34
|
+
const elapsed = now.getTime() - lastCheck.getTime();
|
|
35
|
+
|
|
36
|
+
return elapsed >= CACHE_TTL_MS;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getLastCheck(): Promise<UpdateCacheData | null> {
|
|
40
|
+
try {
|
|
41
|
+
const content = await fs.readFile(this.cachePath, "utf-8");
|
|
42
|
+
const data = JSON.parse(content) as UpdateCacheData;
|
|
43
|
+
return data;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// Cache doesn't exist or is corrupted
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async saveCheck(result: UpdateCacheData): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
// Ensure .claude directory exists
|
|
53
|
+
const cacheDir = path.dirname(this.cachePath);
|
|
54
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
55
|
+
|
|
56
|
+
// Write cache file
|
|
57
|
+
await fs.writeFile(
|
|
58
|
+
this.cachePath,
|
|
59
|
+
JSON.stringify(result, null, 2),
|
|
60
|
+
"utf-8",
|
|
61
|
+
);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
// Non-fatal - just log and continue
|
|
64
|
+
console.warn(
|
|
65
|
+
"Warning: Failed to save update cache:",
|
|
66
|
+
error instanceof Error ? error.message : "Unknown error",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async clear(): Promise<void> {
|
|
72
|
+
try {
|
|
73
|
+
await fs.unlink(this.cachePath);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// Ignore errors (file might not exist)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRequire } from
|
|
2
|
-
import semver from
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import semver from "semver";
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
// Cache the result for the session
|
|
5
5
|
let cachedResult = null;
|
|
@@ -8,11 +8,11 @@ let cachedResult = null;
|
|
|
8
8
|
*/
|
|
9
9
|
export function getCurrentVersion() {
|
|
10
10
|
try {
|
|
11
|
-
const pkg = require(
|
|
11
|
+
const pkg = require("../../package.json");
|
|
12
12
|
return pkg.version;
|
|
13
13
|
}
|
|
14
14
|
catch {
|
|
15
|
-
return
|
|
15
|
+
return "0.0.0";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -23,17 +23,17 @@ async function fetchLatestVersion() {
|
|
|
23
23
|
try {
|
|
24
24
|
const controller = new AbortController();
|
|
25
25
|
const timeout = setTimeout(() => controller.abort(), 3000); // 3s timeout
|
|
26
|
-
const response = await fetch(
|
|
26
|
+
const response = await fetch("https://registry.npmjs.org/claudeup/latest", {
|
|
27
27
|
signal: controller.signal,
|
|
28
28
|
headers: {
|
|
29
|
-
|
|
29
|
+
Accept: "application/json",
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
32
|
clearTimeout(timeout);
|
|
33
33
|
if (!response.ok) {
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
|
-
const data = await response.json();
|
|
36
|
+
const data = (await response.json());
|
|
37
37
|
return data.version || null;
|
|
38
38
|
}
|
|
39
39
|
catch {
|
|
@@ -50,11 +50,11 @@ function getUpdateType(current, latest) {
|
|
|
50
50
|
if (!currentParsed || !latestParsed)
|
|
51
51
|
return undefined;
|
|
52
52
|
if (latestParsed.major > currentParsed.major)
|
|
53
|
-
return
|
|
53
|
+
return "major";
|
|
54
54
|
if (latestParsed.minor > currentParsed.minor)
|
|
55
|
-
return
|
|
55
|
+
return "minor";
|
|
56
56
|
if (latestParsed.patch > currentParsed.patch)
|
|
57
|
-
return
|
|
57
|
+
return "patch";
|
|
58
58
|
return undefined;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -82,7 +82,9 @@ export async function checkForUpdates() {
|
|
|
82
82
|
currentVersion,
|
|
83
83
|
latestVersion,
|
|
84
84
|
updateAvailable,
|
|
85
|
-
updateType: updateAvailable
|
|
85
|
+
updateType: updateAvailable
|
|
86
|
+
? getUpdateType(currentVersion, latestVersion)
|
|
87
|
+
: undefined,
|
|
86
88
|
};
|
|
87
89
|
return cachedResult;
|
|
88
90
|
}
|
|
@@ -91,8 +93,7 @@ export async function checkForUpdates() {
|
|
|
91
93
|
*/
|
|
92
94
|
export function formatUpdateMessage(result) {
|
|
93
95
|
if (!result.updateAvailable)
|
|
94
|
-
return
|
|
95
|
-
const typeLabel = result.updateType ? ` (${result.updateType})` :
|
|
96
|
+
return "";
|
|
97
|
+
const typeLabel = result.updateType ? ` (${result.updateType})` : "";
|
|
96
98
|
return `Update available: v${result.currentVersion} → v${result.latestVersion}${typeLabel}. Run: npm i -g claudeup`;
|
|
97
99
|
}
|
|
98
|
-
//# sourceMappingURL=version-check.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import semver from "semver";
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// Cache the result for the session
|
|
7
|
+
let cachedResult: VersionCheckResult | null = null;
|
|
8
|
+
|
|
9
|
+
export interface VersionCheckResult {
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
latestVersion: string;
|
|
12
|
+
updateAvailable: boolean;
|
|
13
|
+
updateType?: "major" | "minor" | "patch";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the current version from package.json
|
|
18
|
+
*/
|
|
19
|
+
export function getCurrentVersion(): string {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = require("../../package.json");
|
|
22
|
+
return pkg.version;
|
|
23
|
+
} catch {
|
|
24
|
+
return "0.0.0";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fetch the latest version from npm registry
|
|
30
|
+
* Uses a short timeout to avoid blocking startup
|
|
31
|
+
*/
|
|
32
|
+
async function fetchLatestVersion(): Promise<string | null> {
|
|
33
|
+
try {
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const timeout = setTimeout(() => controller.abort(), 3000); // 3s timeout
|
|
36
|
+
|
|
37
|
+
const response = await fetch("https://registry.npmjs.org/claudeup/latest", {
|
|
38
|
+
signal: controller.signal,
|
|
39
|
+
headers: {
|
|
40
|
+
Accept: "application/json",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const data = (await response.json()) as { version?: string };
|
|
51
|
+
return data.version || null;
|
|
52
|
+
} catch {
|
|
53
|
+
// Network error, timeout, or parse error - silently fail
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Determine the type of update (major, minor, patch)
|
|
60
|
+
*/
|
|
61
|
+
function getUpdateType(
|
|
62
|
+
current: string,
|
|
63
|
+
latest: string,
|
|
64
|
+
): "major" | "minor" | "patch" | undefined {
|
|
65
|
+
const currentParsed = semver.parse(current);
|
|
66
|
+
const latestParsed = semver.parse(latest);
|
|
67
|
+
|
|
68
|
+
if (!currentParsed || !latestParsed) return undefined;
|
|
69
|
+
|
|
70
|
+
if (latestParsed.major > currentParsed.major) return "major";
|
|
71
|
+
if (latestParsed.minor > currentParsed.minor) return "minor";
|
|
72
|
+
if (latestParsed.patch > currentParsed.patch) return "patch";
|
|
73
|
+
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if a new version is available
|
|
79
|
+
* Returns cached result if already checked this session
|
|
80
|
+
*/
|
|
81
|
+
export async function checkForUpdates(): Promise<VersionCheckResult> {
|
|
82
|
+
// Return cached result if available
|
|
83
|
+
if (cachedResult) {
|
|
84
|
+
return cachedResult;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const currentVersion = getCurrentVersion();
|
|
88
|
+
const latestVersion = await fetchLatestVersion();
|
|
89
|
+
|
|
90
|
+
if (!latestVersion) {
|
|
91
|
+
// Couldn't fetch, assume no update
|
|
92
|
+
cachedResult = {
|
|
93
|
+
currentVersion,
|
|
94
|
+
latestVersion: currentVersion,
|
|
95
|
+
updateAvailable: false,
|
|
96
|
+
};
|
|
97
|
+
return cachedResult;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const updateAvailable = semver.gt(latestVersion, currentVersion);
|
|
101
|
+
|
|
102
|
+
cachedResult = {
|
|
103
|
+
currentVersion,
|
|
104
|
+
latestVersion,
|
|
105
|
+
updateAvailable,
|
|
106
|
+
updateType: updateAvailable
|
|
107
|
+
? getUpdateType(currentVersion, latestVersion)
|
|
108
|
+
: undefined,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return cachedResult;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Format update message for display
|
|
116
|
+
*/
|
|
117
|
+
export function formatUpdateMessage(result: VersionCheckResult): string {
|
|
118
|
+
if (!result.updateAvailable) return "";
|
|
119
|
+
|
|
120
|
+
const typeLabel = result.updateType ? ` (${result.updateType})` : "";
|
|
121
|
+
return `Update available: v${result.currentVersion} → v${result.latestVersion}${typeLabel}. Run: npm i -g claudeup`;
|
|
122
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|