imcp 0.0.4 → 0.0.5
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 +3 -4
- package/dist/cli/commands/install.js +2 -0
- package/dist/cli/commands/list.js +1 -0
- package/dist/cli/index.js +1 -2
- package/dist/core/ConfigurationLoader.d.ts +32 -0
- package/dist/core/ConfigurationLoader.js +213 -0
- package/dist/core/ConfigurationProvider.d.ts +2 -3
- package/dist/core/ConfigurationProvider.js +13 -182
- package/dist/core/InstallationService.d.ts +8 -0
- package/dist/core/InstallationService.js +124 -96
- package/dist/core/RequirementService.d.ts +1 -1
- package/dist/core/RequirementService.js +5 -9
- package/dist/core/constants.js +14 -1
- package/dist/core/installers/BaseInstaller.d.ts +5 -4
- package/dist/core/installers/BaseInstaller.js +17 -28
- package/dist/core/installers/ClientInstaller.js +134 -43
- package/dist/core/installers/CommandInstaller.d.ts +1 -0
- package/dist/core/installers/CommandInstaller.js +3 -0
- package/dist/core/installers/GeneralInstaller.d.ts +1 -0
- package/dist/core/installers/GeneralInstaller.js +3 -0
- package/dist/core/installers/InstallerFactory.d.ts +9 -7
- package/dist/core/installers/InstallerFactory.js +10 -8
- package/dist/core/installers/NpmInstaller.d.ts +1 -0
- package/dist/core/installers/NpmInstaller.js +3 -0
- package/dist/core/installers/PipInstaller.d.ts +6 -3
- package/dist/core/installers/PipInstaller.js +21 -8
- package/dist/core/installers/RequirementInstaller.d.ts +4 -3
- package/dist/core/installers/clients/ClientInstaller.d.ts +23 -0
- package/dist/core/installers/clients/ClientInstaller.js +573 -0
- package/dist/core/installers/clients/ExtensionInstaller.d.ts +26 -0
- package/dist/core/installers/clients/ExtensionInstaller.js +149 -0
- package/dist/core/installers/index.d.ts +8 -6
- package/dist/core/installers/index.js +8 -6
- package/dist/core/installers/requirements/BaseInstaller.d.ts +59 -0
- package/dist/core/installers/requirements/BaseInstaller.js +168 -0
- package/dist/core/installers/requirements/CommandInstaller.d.ts +37 -0
- package/dist/core/installers/requirements/CommandInstaller.js +173 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +33 -0
- package/dist/core/installers/requirements/GeneralInstaller.js +86 -0
- package/dist/core/installers/requirements/InstallerFactory.d.ts +54 -0
- package/dist/core/installers/requirements/InstallerFactory.js +97 -0
- package/dist/core/installers/requirements/NpmInstaller.d.ts +26 -0
- package/dist/core/installers/requirements/NpmInstaller.js +128 -0
- package/dist/core/installers/requirements/PipInstaller.d.ts +28 -0
- package/dist/core/installers/requirements/PipInstaller.js +128 -0
- package/{src/core/installers/RequirementInstaller.ts → dist/core/installers/requirements/RequirementInstaller.d.ts} +33 -38
- package/dist/core/installers/requirements/RequirementInstaller.js +3 -0
- package/dist/core/types.d.ts +4 -1
- package/dist/services/ServerService.js +1 -1
- package/dist/utils/githubUtils.d.ts +11 -0
- package/dist/utils/githubUtils.js +88 -0
- package/dist/utils/osUtils.d.ts +17 -0
- package/dist/utils/osUtils.js +184 -0
- package/dist/web/public/css/modal.css +97 -3
- package/dist/web/public/index.html +21 -2
- package/dist/web/public/js/modal.js +177 -28
- package/dist/web/public/js/serverCategoryDetails.js +12 -10
- package/dist/web/public/js/serverCategoryList.js +20 -5
- package/dist/web/public/modal.html +27 -13
- package/package.json +1 -1
- package/src/cli/commands/install.ts +4 -2
- package/src/cli/commands/list.ts +1 -0
- package/src/cli/index.ts +1 -1
- package/src/core/ConfigurationLoader.ts +251 -0
- package/src/core/ConfigurationProvider.ts +13 -195
- package/src/core/InstallationService.ts +140 -106
- package/src/core/RequirementService.ts +5 -10
- package/src/core/constants.ts +15 -1
- package/src/core/installers/{ClientInstaller.ts → clients/ClientInstaller.ts} +157 -51
- package/src/core/installers/clients/ExtensionInstaller.ts +162 -0
- package/src/core/installers/index.ts +9 -7
- package/src/core/installers/{BaseInstaller.ts → requirements/BaseInstaller.ts} +10 -118
- package/src/core/installers/{CommandInstaller.ts → requirements/CommandInstaller.ts} +7 -3
- package/src/core/installers/{GeneralInstaller.ts → requirements/GeneralInstaller.ts} +6 -2
- package/src/core/installers/{InstallerFactory.ts → requirements/InstallerFactory.ts} +11 -9
- package/src/core/installers/{NpmInstaller.ts → requirements/NpmInstaller.ts} +7 -4
- package/src/core/installers/{PipInstaller.ts → requirements/PipInstaller.ts} +26 -10
- package/src/core/installers/requirements/RequirementInstaller.ts +41 -0
- package/src/core/types.ts +4 -1
- package/src/services/ServerService.ts +1 -1
- package/src/utils/githubUtils.ts +103 -0
- package/src/utils/osUtils.ts +206 -15
- package/src/web/public/css/modal.css +97 -3
- package/src/web/public/index.html +21 -2
- package/src/web/public/js/modal.js +177 -28
- package/src/web/public/js/serverCategoryDetails.js +12 -10
- package/src/web/public/js/serverCategoryList.js +20 -5
- package/src/web/public/modal.html +27 -13
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RegistryConfig, RequirementConfig } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Helper to handle GitHub release downloads
|
|
4
|
+
* @param requirement The requirement configuration
|
|
5
|
+
* @param registry The GitHub release registry configuration
|
|
6
|
+
* @returns The path to the downloaded file
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleGitHubRelease(requirement: RequirementConfig, registry: RegistryConfig['githubRelease']): Promise<{
|
|
9
|
+
resolvedVersion: string;
|
|
10
|
+
resolvedPath: string;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import util from 'util';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { extractZipFile } from './clientUtils.js';
|
|
6
|
+
import { Logger } from './logger.js';
|
|
7
|
+
import { SETTINGS_DIR } from '../core/constants.js';
|
|
8
|
+
const execAsync = util.promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Helper to handle GitHub release downloads
|
|
11
|
+
* @param requirement The requirement configuration
|
|
12
|
+
* @param registry The GitHub release registry configuration
|
|
13
|
+
* @returns The path to the downloaded file
|
|
14
|
+
*/
|
|
15
|
+
export async function handleGitHubRelease(requirement, registry) {
|
|
16
|
+
if (!registry) {
|
|
17
|
+
throw new Error('GitHub release registry configuration is required');
|
|
18
|
+
}
|
|
19
|
+
const downloadsDir = path.join(SETTINGS_DIR, 'downloads');
|
|
20
|
+
await fs.mkdir(downloadsDir, { recursive: true });
|
|
21
|
+
const { repository, assetsName, assetName } = registry;
|
|
22
|
+
if (!repository) {
|
|
23
|
+
throw new Error('GitHub repository is required for GitHub release downloads');
|
|
24
|
+
}
|
|
25
|
+
let version = requirement.version;
|
|
26
|
+
let resolvedAssetName = assetName || '';
|
|
27
|
+
let resolvedAssetsName = assetsName || '';
|
|
28
|
+
const { stdout } = await execAsync(`gh release view --repo ${repository} --json tagName --jq .tagName`);
|
|
29
|
+
const latestTag = stdout.trim();
|
|
30
|
+
let latestVersion = latestTag;
|
|
31
|
+
const tagWithVPrefix = latestVersion.startsWith('v');
|
|
32
|
+
if (tagWithVPrefix)
|
|
33
|
+
latestVersion = latestVersion.substring(1); // Remove 'v' prefix if present
|
|
34
|
+
version = version.includes("latest") ? latestVersion : version;
|
|
35
|
+
if (assetsName) {
|
|
36
|
+
resolvedAssetsName = assetsName.replace('${latest}', version).replace('${version}', version);
|
|
37
|
+
}
|
|
38
|
+
if (assetName) {
|
|
39
|
+
resolvedAssetName = assetName.replace('${latest}', version).replace('${version}', version);
|
|
40
|
+
}
|
|
41
|
+
Logger.debug(`Downloading ${requirement.name} from GitHub release ${repository} version ${version}`);
|
|
42
|
+
Logger.debug(`ResolvedAssetsName} ${resolvedAssetName}; ResolvedAsetName} ${resolvedAssetName}`);
|
|
43
|
+
const pattern = resolvedAssetsName ? resolvedAssetsName : resolvedAssetName;
|
|
44
|
+
Logger.debug(`Resolved pattern: ${pattern}`);
|
|
45
|
+
if (!pattern) {
|
|
46
|
+
throw new Error('Either assetsName or assetName must be specified for GitHub release downloads');
|
|
47
|
+
}
|
|
48
|
+
// Download the release asset
|
|
49
|
+
const downloadPath = path.join(downloadsDir, path.basename(pattern));
|
|
50
|
+
if (!await fileExists(downloadPath)) {
|
|
51
|
+
await execAsync(`gh release download ${tagWithVPrefix ? `v${version}` : version} --repo ${repository} --pattern "${pattern}" -O "${downloadPath}"`);
|
|
52
|
+
}
|
|
53
|
+
// Handle zip file extraction if the downloaded file is a zip
|
|
54
|
+
if (downloadPath.endsWith('.zip')) {
|
|
55
|
+
const extractDir = path.join(downloadsDir, path.basename(pattern, '.zip'));
|
|
56
|
+
await fs.mkdir(extractDir, { recursive: true });
|
|
57
|
+
// Extract the zip file
|
|
58
|
+
await extractZipFile(downloadPath, { dir: extractDir });
|
|
59
|
+
let assetPath = '';
|
|
60
|
+
// If resolvedAssetName is specified, look for it in the extracted directory
|
|
61
|
+
if (resolvedAssetName) {
|
|
62
|
+
assetPath = path.join(extractDir, resolvedAssetName);
|
|
63
|
+
try {
|
|
64
|
+
await fs.access(assetPath);
|
|
65
|
+
return { resolvedVersion: version, resolvedPath: assetPath };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Asset ${resolvedAssetName} not found in extracted directory ${extractDir}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
assetPath = path.join(extractDir, path.basename(pattern, '.zip') + '.tgz');
|
|
73
|
+
}
|
|
74
|
+
// If no specific asset is required, return the extraction directory
|
|
75
|
+
return { resolvedVersion: version, resolvedPath: extractDir };
|
|
76
|
+
}
|
|
77
|
+
return { resolvedVersion: version, resolvedPath: downloadPath };
|
|
78
|
+
}
|
|
79
|
+
async function fileExists(filePath) {
|
|
80
|
+
try {
|
|
81
|
+
await fs.access(filePath);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=githubUtils.js.map
|
package/dist/utils/osUtils.d.ts
CHANGED
|
@@ -14,3 +14,20 @@ export declare function isToolInstalled(tool: 'gh' | 'git', retries?: number): P
|
|
|
14
14
|
* @returns A promise that resolves when the browser is launched
|
|
15
15
|
*/
|
|
16
16
|
export declare function openBrowser(url: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Gets the directory containing Python packages (site-packages) based on Python executable path.
|
|
19
|
+
* Handles different Python installations (system, venv, conda) across different OS platforms.
|
|
20
|
+
* @param pythonExecutablePath The full path to the Python executable
|
|
21
|
+
* @returns The site-packages directory path
|
|
22
|
+
*/
|
|
23
|
+
export declare function getPythonPackagePath(pythonExecutablePath: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Finds the directory of the system's default Python installation.
|
|
26
|
+
* @returns The directory path or null if not found.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getSystemPythonPackageDirectory(): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the path of the system's default browser.
|
|
31
|
+
* @returns The path to the default browser executable.
|
|
32
|
+
*/
|
|
33
|
+
export declare function GetBrowserPath(): Promise<string>;
|
package/dist/utils/osUtils.js
CHANGED
|
@@ -3,6 +3,8 @@ import os from 'os';
|
|
|
3
3
|
import { exec } from 'child_process';
|
|
4
4
|
import util from 'util';
|
|
5
5
|
import { Logger } from './logger.js';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
6
8
|
const execAsync = util.promisify(exec);
|
|
7
9
|
export function getOSType() {
|
|
8
10
|
const platform = os.platform();
|
|
@@ -232,4 +234,186 @@ export async function openBrowser(url) {
|
|
|
232
234
|
// Don't throw the error - just log it and continue
|
|
233
235
|
}
|
|
234
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Gets the directory containing Python packages (site-packages) based on Python executable path.
|
|
239
|
+
* Handles different Python installations (system, venv, conda) across different OS platforms.
|
|
240
|
+
* @param pythonExecutablePath The full path to the Python executable
|
|
241
|
+
* @returns The site-packages directory path
|
|
242
|
+
*/
|
|
243
|
+
export function getPythonPackagePath(pythonExecutablePath) {
|
|
244
|
+
Logger.debug({
|
|
245
|
+
action: 'get_python_package_path',
|
|
246
|
+
pythonExecutablePath
|
|
247
|
+
});
|
|
248
|
+
try {
|
|
249
|
+
const dir = path.dirname(pythonExecutablePath);
|
|
250
|
+
const isWindows = process.platform === 'win32';
|
|
251
|
+
// Handle common Python installation patterns
|
|
252
|
+
if (isWindows) {
|
|
253
|
+
// Windows: Check for Scripts directory (venv) or python.exe location
|
|
254
|
+
if (dir.endsWith('Scripts')) {
|
|
255
|
+
// Virtual environment structure on Windows: <venv>/Scripts/python.exe
|
|
256
|
+
const venvRoot = path.dirname(dir);
|
|
257
|
+
return path.join(venvRoot, 'Lib', 'site-packages');
|
|
258
|
+
}
|
|
259
|
+
else if (dir.toLowerCase().includes('python')) {
|
|
260
|
+
// System Python or Conda on Windows
|
|
261
|
+
return path.join(dir, 'Lib', 'site-packages');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// Unix systems (MacOS/Linux)
|
|
266
|
+
if (dir.endsWith('bin')) {
|
|
267
|
+
// Virtual environment structure on Unix: <venv>/bin/python
|
|
268
|
+
const venvRoot = path.dirname(dir);
|
|
269
|
+
// Try to find python version-specific site-packages
|
|
270
|
+
const libDir = path.join(venvRoot, 'lib');
|
|
271
|
+
if (fs.existsSync(libDir)) {
|
|
272
|
+
const pythonDirs = fs.readdirSync(libDir).filter(d => d.startsWith('python'));
|
|
273
|
+
if (pythonDirs.length > 0) {
|
|
274
|
+
// Use the first python directory found
|
|
275
|
+
return path.join(libDir, pythonDirs[0], 'site-packages');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Fallback to a generic lib/python3/site-packages if version-specific not found
|
|
279
|
+
return path.join(venvRoot, 'lib', 'python3', 'site-packages');
|
|
280
|
+
}
|
|
281
|
+
else if (dir.toLowerCase().includes('python')) {
|
|
282
|
+
// System Python or Conda on Unix
|
|
283
|
+
const libDir = path.join(dir, 'lib');
|
|
284
|
+
if (fs.existsSync(libDir)) {
|
|
285
|
+
const pythonDirs = fs.readdirSync(libDir).filter(d => d.startsWith('python'));
|
|
286
|
+
if (pythonDirs.length > 0) {
|
|
287
|
+
// Use the first python directory found
|
|
288
|
+
return path.join(libDir, pythonDirs[0], 'site-packages');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Fallback to a generic lib/python3/site-packages
|
|
292
|
+
return path.join(dir, 'lib', 'python3', 'site-packages');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// Default fallback: return the original directory
|
|
296
|
+
Logger.debug('No standard Python directory structure found, using original directory');
|
|
297
|
+
return dir;
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
Logger.error('Error getting Python package path', {
|
|
301
|
+
pythonExecutablePath,
|
|
302
|
+
error
|
|
303
|
+
});
|
|
304
|
+
return path.dirname(pythonExecutablePath);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Finds the directory of the system's default Python installation.
|
|
309
|
+
* @returns The directory path or null if not found.
|
|
310
|
+
*/
|
|
311
|
+
export async function getSystemPythonPackageDirectory() {
|
|
312
|
+
const command = process.platform === 'win32' ? 'where python' : 'which python';
|
|
313
|
+
Logger.debug({
|
|
314
|
+
action: 'get_system_python_package_directory',
|
|
315
|
+
command
|
|
316
|
+
});
|
|
317
|
+
try {
|
|
318
|
+
const { stdout } = await execAsync(command);
|
|
319
|
+
// Use the first path found, trim whitespace
|
|
320
|
+
const pythonPath = stdout.split('\n')[0].trim();
|
|
321
|
+
if (pythonPath) {
|
|
322
|
+
const packagePath = getPythonPackagePath(pythonPath);
|
|
323
|
+
Logger.debug({
|
|
324
|
+
action: 'get_system_python_package_directory_success',
|
|
325
|
+
pythonPath,
|
|
326
|
+
packagePath
|
|
327
|
+
});
|
|
328
|
+
return packagePath;
|
|
329
|
+
}
|
|
330
|
+
Logger.debug('No Python executable found');
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
Logger.debug(`Could not find system python using "${command}": ${error}`);
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Gets the path of the system's default browser.
|
|
340
|
+
* @returns The path to the default browser executable.
|
|
341
|
+
*/
|
|
342
|
+
export async function GetBrowserPath() {
|
|
343
|
+
const osType = getOSType();
|
|
344
|
+
Logger.debug({
|
|
345
|
+
action: 'get_system_browser_path',
|
|
346
|
+
osType
|
|
347
|
+
});
|
|
348
|
+
try {
|
|
349
|
+
switch (osType) {
|
|
350
|
+
case OSType.Windows: {
|
|
351
|
+
// Check for Edge first
|
|
352
|
+
const edgePaths = [
|
|
353
|
+
`${process.env['PROGRAMFILES(X86)']}\\Microsoft\\Edge\\Application\\msedge.exe`,
|
|
354
|
+
`${process.env['PROGRAMFILES']}\\Microsoft\\Edge\\Application\\msedge.exe`
|
|
355
|
+
];
|
|
356
|
+
for (const path of edgePaths) {
|
|
357
|
+
if (fs.existsSync(path)) {
|
|
358
|
+
return path;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Then check for Chrome
|
|
362
|
+
const chromePaths = [
|
|
363
|
+
`${process.env['PROGRAMFILES(X86)']}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
364
|
+
`${process.env['PROGRAMFILES']}\\Google\\Chrome\\Application\\chrome.exe`
|
|
365
|
+
];
|
|
366
|
+
for (const path of chromePaths) {
|
|
367
|
+
if (fs.existsSync(path)) {
|
|
368
|
+
return path;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case OSType.MacOS: {
|
|
374
|
+
// Check for Edge first
|
|
375
|
+
const edgePath = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge';
|
|
376
|
+
if (fs.existsSync(edgePath)) {
|
|
377
|
+
return edgePath;
|
|
378
|
+
}
|
|
379
|
+
// Then check for Chrome
|
|
380
|
+
const chromePath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
381
|
+
if (fs.existsSync(chromePath)) {
|
|
382
|
+
return chromePath;
|
|
383
|
+
}
|
|
384
|
+
// Finally check for Safari
|
|
385
|
+
const safariPath = '/Applications/Safari.app/Contents/MacOS/Safari';
|
|
386
|
+
if (fs.existsSync(safariPath)) {
|
|
387
|
+
return safariPath;
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case OSType.Linux: {
|
|
392
|
+
// Try Edge first
|
|
393
|
+
try {
|
|
394
|
+
const { stdout: edgePath } = await execAsync('which microsoft-edge');
|
|
395
|
+
if (edgePath.trim()) {
|
|
396
|
+
return edgePath.trim();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch { }
|
|
400
|
+
// Then try Chrome or Chromium
|
|
401
|
+
try {
|
|
402
|
+
const { stdout: chromePath } = await execAsync('which google-chrome chromium');
|
|
403
|
+
if (chromePath.trim()) {
|
|
404
|
+
return chromePath.trim();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
catch { }
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// If no browser found, throw error
|
|
412
|
+
throw new Error('No supported browser found on the system');
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
Logger.error('Failed to get browser path', error);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
235
419
|
//# sourceMappingURL=osUtils.js.map
|
|
@@ -60,7 +60,7 @@ body {
|
|
|
60
60
|
border: none;
|
|
61
61
|
border-radius: 1rem;
|
|
62
62
|
width: 90%;
|
|
63
|
-
max-width:
|
|
63
|
+
max-width: 900px;
|
|
64
64
|
position: relative;
|
|
65
65
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
66
66
|
transform: translateY(0);
|
|
@@ -117,6 +117,9 @@ body {
|
|
|
117
117
|
/* Sections layout */
|
|
118
118
|
.modal-sections {
|
|
119
119
|
margin-top: 1.5rem;
|
|
120
|
+
display: grid;
|
|
121
|
+
grid-template-columns: 1fr 1fr;
|
|
122
|
+
gap: 1rem;
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
.section-container {
|
|
@@ -135,10 +138,16 @@ body {
|
|
|
135
138
|
/* Client grid */
|
|
136
139
|
.client-grid {
|
|
137
140
|
display: grid;
|
|
138
|
-
grid-template-columns: 1fr;
|
|
141
|
+
grid-template-columns: repeat(2, 1fr);
|
|
139
142
|
gap: 0.75rem;
|
|
140
143
|
}
|
|
141
144
|
|
|
145
|
+
/* Make sections take full width */
|
|
146
|
+
.section-container:first-child,
|
|
147
|
+
.section-container:nth-child(2) {
|
|
148
|
+
grid-column: 1 / -1;
|
|
149
|
+
}
|
|
150
|
+
|
|
142
151
|
/* Client item styling */
|
|
143
152
|
.client-item {
|
|
144
153
|
display: flex;
|
|
@@ -221,10 +230,11 @@ body {
|
|
|
221
230
|
/* Environment variables section */
|
|
222
231
|
#modalEnvInputs input {
|
|
223
232
|
width: 100%;
|
|
224
|
-
padding: 0.75rem
|
|
233
|
+
padding: 0.5rem 0.75rem;
|
|
225
234
|
border: 1px solid #e5e7eb;
|
|
226
235
|
border-radius: 0.5rem;
|
|
227
236
|
transition: all 0.2s ease;
|
|
237
|
+
height: 36px;
|
|
228
238
|
}
|
|
229
239
|
|
|
230
240
|
#modalEnvInputs input:focus {
|
|
@@ -233,6 +243,90 @@ body {
|
|
|
233
243
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
234
244
|
}
|
|
235
245
|
|
|
246
|
+
/* Arguments section styling */
|
|
247
|
+
.args-container {
|
|
248
|
+
display: flex;
|
|
249
|
+
flex-direction: column;
|
|
250
|
+
gap: 0.5rem;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.arg-input {
|
|
254
|
+
height: 36px !important;
|
|
255
|
+
padding: 0.5rem 0.75rem !important;
|
|
256
|
+
border: 1px solid #e5e7eb;
|
|
257
|
+
border-radius: 0.5rem;
|
|
258
|
+
transition: all 0.2s ease;
|
|
259
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
260
|
+
font-size: 0.875rem;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.arg-input:focus {
|
|
264
|
+
outline: none;
|
|
265
|
+
border-color: #2563eb;
|
|
266
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.add-arg-button {
|
|
270
|
+
align-self: flex-start;
|
|
271
|
+
display: inline-flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
gap: 0.25rem;
|
|
274
|
+
height: 32px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.add-arg-button i {
|
|
278
|
+
font-size: 1.25rem;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.remove-arg-button {
|
|
282
|
+
padding: 0.25rem;
|
|
283
|
+
border-radius: 0.375rem;
|
|
284
|
+
transition: all 0.2s ease;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.remove-arg-button:hover {
|
|
288
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.remove-arg-button i {
|
|
292
|
+
font-size: 1.25rem;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* Arguments textarea styling */
|
|
296
|
+
#install_args {
|
|
297
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
298
|
+
font-size: 0.875rem;
|
|
299
|
+
line-height: 1.5;
|
|
300
|
+
resize: vertical;
|
|
301
|
+
transition: all 0.2s ease;
|
|
302
|
+
margin-bottom: 0.5rem;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#install_args:focus {
|
|
306
|
+
outline: none;
|
|
307
|
+
border-color: #2563eb;
|
|
308
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Python environment input styling */
|
|
312
|
+
#python_env {
|
|
313
|
+
height: 36px !important;
|
|
314
|
+
width: 100%;
|
|
315
|
+
padding: 0.75rem 1rem;
|
|
316
|
+
border: 1px solid #e5e7eb;
|
|
317
|
+
border-radius: 0.5rem;
|
|
318
|
+
transition: all 0.2s ease;
|
|
319
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
320
|
+
font-size: 0.875rem;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
#python_env:focus {
|
|
324
|
+
outline: none;
|
|
325
|
+
border-color: #2563eb;
|
|
326
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
236
330
|
/* Form buttons */
|
|
237
331
|
.submit-button {
|
|
238
332
|
background-color: #2563eb;
|
|
@@ -97,6 +97,11 @@
|
|
|
97
97
|
<!-- Environment variable inputs will be injected here -->
|
|
98
98
|
</div>
|
|
99
99
|
|
|
100
|
+
<!-- Arguments Section -->
|
|
101
|
+
<div id="modalArguments" class="space-y-4 mb-6">
|
|
102
|
+
<!-- Arguments will be injected here -->
|
|
103
|
+
</div>
|
|
104
|
+
|
|
100
105
|
<div class="flex justify-end space-x-4 mt-6">
|
|
101
106
|
<button type="button" onclick="closeModal()"
|
|
102
107
|
class="px-4 py-2 text-gray-600 hover:text-gray-800 font-medium rounded-md hover:bg-gray-100 transition-colors">
|
|
@@ -151,10 +156,24 @@
|
|
|
151
156
|
window.uninstallTool = uninstallTool;
|
|
152
157
|
|
|
153
158
|
// Initialize
|
|
154
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
159
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
155
160
|
setupSearch();
|
|
156
161
|
setupModalOutsideClick();
|
|
157
|
-
|
|
162
|
+
|
|
163
|
+
// Check URL parameters for category
|
|
164
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
165
|
+
const categoryParam = urlParams.get('category');
|
|
166
|
+
|
|
167
|
+
// If we have a category parameter or last selected category
|
|
168
|
+
const lastSelected = categoryParam || localStorage.getItem('lastSelectedCategory');
|
|
169
|
+
|
|
170
|
+
// First fetch categories
|
|
171
|
+
await fetchServerCategories();
|
|
172
|
+
|
|
173
|
+
// Then show the selected category if it exists
|
|
174
|
+
if (lastSelected) {
|
|
175
|
+
showServerDetails(lastSelected);
|
|
176
|
+
}
|
|
158
177
|
});
|
|
159
178
|
</script>
|
|
160
179
|
<script src="js/modal.js" type="module"></script>
|