@gxp-dev/tools 2.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/.github/workflows/npm-publish.yml +48 -0
- package/CLAUDE.md +400 -0
- package/README.md +247 -0
- package/REFACTOR_PLAN.md +194 -0
- package/bin/gx-devtools.js +87 -0
- package/bin/lib/cli.js +251 -0
- package/bin/lib/commands/assets.js +337 -0
- package/bin/lib/commands/build.js +259 -0
- package/bin/lib/commands/datastore.js +433 -0
- package/bin/lib/commands/dev.js +328 -0
- package/bin/lib/commands/extensions.js +298 -0
- package/bin/lib/commands/index.js +35 -0
- package/bin/lib/commands/init.js +307 -0
- package/bin/lib/commands/publish.js +189 -0
- package/bin/lib/commands/socket.js +158 -0
- package/bin/lib/commands/ssl.js +47 -0
- package/bin/lib/constants.js +120 -0
- package/bin/lib/tui/App.tsx +600 -0
- package/bin/lib/tui/components/CommandInput.tsx +278 -0
- package/bin/lib/tui/components/GeminiPanel.tsx +161 -0
- package/bin/lib/tui/components/Header.tsx +27 -0
- package/bin/lib/tui/components/LogPanel.tsx +122 -0
- package/bin/lib/tui/components/TabBar.tsx +56 -0
- package/bin/lib/tui/components/WelcomeScreen.tsx +80 -0
- package/bin/lib/tui/index.tsx +63 -0
- package/bin/lib/tui/services/ExtensionService.ts +122 -0
- package/bin/lib/tui/services/GeminiService.ts +395 -0
- package/bin/lib/tui/services/ServiceManager.ts +336 -0
- package/bin/lib/tui/services/SocketService.ts +204 -0
- package/bin/lib/tui/services/ViteService.ts +107 -0
- package/bin/lib/tui/services/index.ts +13 -0
- package/bin/lib/utils/files.js +180 -0
- package/bin/lib/utils/index.js +17 -0
- package/bin/lib/utils/paths.js +138 -0
- package/bin/lib/utils/prompts.js +71 -0
- package/bin/lib/utils/ssl.js +233 -0
- package/browser-extensions/README.md +1 -0
- package/browser-extensions/chrome/background.js +857 -0
- package/browser-extensions/chrome/content.js +51 -0
- package/browser-extensions/chrome/devtools.html +9 -0
- package/browser-extensions/chrome/devtools.js +23 -0
- package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
- package/browser-extensions/chrome/inspector.js +1087 -0
- package/browser-extensions/chrome/manifest.json +70 -0
- package/browser-extensions/chrome/panel.html +638 -0
- package/browser-extensions/chrome/panel.js +862 -0
- package/browser-extensions/chrome/popup.html +399 -0
- package/browser-extensions/chrome/popup.js +515 -0
- package/browser-extensions/chrome/rules.json +1 -0
- package/browser-extensions/chrome/test-chrome.html +145 -0
- package/browser-extensions/chrome/test-mixed-content.html +190 -0
- package/browser-extensions/chrome/test-uri-pattern.html +199 -0
- package/browser-extensions/firefox/README.md +134 -0
- package/browser-extensions/firefox/background.js +804 -0
- package/browser-extensions/firefox/content.js +120 -0
- package/browser-extensions/firefox/debug-errors.html +229 -0
- package/browser-extensions/firefox/debug-https.html +113 -0
- package/browser-extensions/firefox/devtools.html +9 -0
- package/browser-extensions/firefox/devtools.js +24 -0
- package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
- package/browser-extensions/firefox/inspector.js +1087 -0
- package/browser-extensions/firefox/manifest.json +67 -0
- package/browser-extensions/firefox/panel.html +638 -0
- package/browser-extensions/firefox/panel.js +862 -0
- package/browser-extensions/firefox/popup.html +525 -0
- package/browser-extensions/firefox/popup.js +536 -0
- package/browser-extensions/firefox/test-gramercy.html +126 -0
- package/browser-extensions/firefox/test-imports.html +58 -0
- package/browser-extensions/firefox/test-masking.html +147 -0
- package/browser-extensions/firefox/test-uri-pattern.html +199 -0
- package/docs/DOCUSAURUS_IMPORT.md +378 -0
- package/docs/_category_.json +8 -0
- package/docs/app-manifest.md +272 -0
- package/docs/building-for-platform.md +315 -0
- package/docs/dev-tools.md +291 -0
- package/docs/getting-started.md +180 -0
- package/docs/gxp-store.md +305 -0
- package/docs/index.md +44 -0
- package/package.json +77 -0
- package/runtime/PortalContainer.vue +326 -0
- package/runtime/dev-tools/DevToolsModal.vue +217 -0
- package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
- package/runtime/dev-tools/MockDataEditor.vue +621 -0
- package/runtime/dev-tools/SocketSimulator.vue +562 -0
- package/runtime/dev-tools/StoreInspector.vue +644 -0
- package/runtime/dev-tools/index.js +6 -0
- package/runtime/gxpStringsPlugin.js +428 -0
- package/runtime/index.html +22 -0
- package/runtime/main.js +32 -0
- package/runtime/mock-api/auth-middleware.js +97 -0
- package/runtime/mock-api/image-generator.js +221 -0
- package/runtime/mock-api/index.js +197 -0
- package/runtime/mock-api/response-generator.js +394 -0
- package/runtime/mock-api/route-generator.js +323 -0
- package/runtime/mock-api/socket-triggers.js +371 -0
- package/runtime/mock-api/spec-loader.js +300 -0
- package/runtime/server.js +180 -0
- package/runtime/stores/gxpPortalConfigStore.js +554 -0
- package/runtime/stores/index.js +6 -0
- package/runtime/vite-inspector-plugin.js +749 -0
- package/runtime/vite-source-tracker-plugin.js +232 -0
- package/runtime/vite.config.js +402 -0
- package/scripts/launch-chrome.js +90 -0
- package/scripts/pack-chrome.js +91 -0
- package/socket-events/AiSessionMessageCreated.json +18 -0
- package/socket-events/SocialStreamPostCreated.json +24 -0
- package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
- package/template/README.md +332 -0
- package/template/app-manifest.json +32 -0
- package/template/dev-assets/images/avatar-placeholder.png +0 -0
- package/template/dev-assets/images/background-placeholder.jpg +0 -0
- package/template/dev-assets/images/banner-placeholder.jpg +0 -0
- package/template/dev-assets/images/icon-placeholder.png +0 -0
- package/template/dev-assets/images/logo-placeholder.png +0 -0
- package/template/dev-assets/images/product-placeholder.jpg +0 -0
- package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
- package/template/env.example +51 -0
- package/template/gitignore +53 -0
- package/template/index.html +22 -0
- package/template/main.js +28 -0
- package/template/src/DemoPage.vue +459 -0
- package/template/src/Plugin.vue +38 -0
- package/template/src/stores/index.js +9 -0
- package/template/src/stores/test-data.json +173 -0
- package/template/theme-layouts/AdditionalStyling.css +0 -0
- package/template/theme-layouts/PrivateLayout.vue +39 -0
- package/template/theme-layouts/PublicLayout.vue +39 -0
- package/template/theme-layouts/SystemLayout.vue +39 -0
- package/template/vite.config.js +333 -0
- package/tsconfig.tui.json +21 -0
- package/vite.config.js +164 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles file operations like safe copying and package.json management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const shell = require("shelljs");
|
|
10
|
+
const {
|
|
11
|
+
REQUIRED_DEPENDENCIES,
|
|
12
|
+
REQUIRED_DEV_DEPENDENCIES,
|
|
13
|
+
DEFAULT_SCRIPTS,
|
|
14
|
+
} = require("../constants");
|
|
15
|
+
const { loadGlobalConfig } = require("./paths");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Copies a file from source to destination, creating directories if needed
|
|
19
|
+
*/
|
|
20
|
+
function safeCopyFile(src, dest, description) {
|
|
21
|
+
if (!fs.existsSync(dest)) {
|
|
22
|
+
console.log(`Creating ${description}`);
|
|
23
|
+
const destDir = path.dirname(dest);
|
|
24
|
+
if (!fs.existsSync(destDir)) {
|
|
25
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
fs.copyFileSync(src, dest);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates package.json for new projects
|
|
33
|
+
*/
|
|
34
|
+
function createPackageJson(projectPath, projectName) {
|
|
35
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
36
|
+
const globalConfig = loadGlobalConfig();
|
|
37
|
+
|
|
38
|
+
const packageJson = {
|
|
39
|
+
name: projectName,
|
|
40
|
+
version: "1.0.0",
|
|
41
|
+
description: `GxP Plugin: ${projectName}`,
|
|
42
|
+
main: "main.js",
|
|
43
|
+
scripts: {
|
|
44
|
+
...DEFAULT_SCRIPTS,
|
|
45
|
+
placeholder:
|
|
46
|
+
"gxdev assets generate --size 400x300 --name custom-placeholder",
|
|
47
|
+
},
|
|
48
|
+
dependencies: REQUIRED_DEPENDENCIES,
|
|
49
|
+
devDependencies: REQUIRED_DEV_DEPENDENCIES,
|
|
50
|
+
author: globalConfig.author || "Your Name",
|
|
51
|
+
license: "ISC",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
55
|
+
console.log("โ Created package.json");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Installs npm dependencies
|
|
60
|
+
*/
|
|
61
|
+
function installDependencies(projectPath) {
|
|
62
|
+
console.log("\n๐ฆ Installing dependencies...");
|
|
63
|
+
const currentDir = process.cwd();
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
process.chdir(projectPath);
|
|
67
|
+
const result = shell.exec("npm install", { silent: false });
|
|
68
|
+
if (result.code !== 0) {
|
|
69
|
+
console.warn("โ npm install completed with warnings");
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
process.chdir(currentDir);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Updates existing project's package.json with missing dependencies and scripts
|
|
78
|
+
*/
|
|
79
|
+
function updateExistingProject(projectPath) {
|
|
80
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
81
|
+
|
|
82
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
88
|
+
let updated = false;
|
|
89
|
+
|
|
90
|
+
// Check and add missing dependencies
|
|
91
|
+
if (!packageJson.dependencies) {
|
|
92
|
+
packageJson.dependencies = {};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const [dep, version] of Object.entries(REQUIRED_DEPENDENCIES)) {
|
|
96
|
+
if (!packageJson.dependencies[dep]) {
|
|
97
|
+
packageJson.dependencies[dep] = version;
|
|
98
|
+
console.log(` + Adding dependency: ${dep}@${version}`);
|
|
99
|
+
updated = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check and add missing dev dependencies
|
|
104
|
+
if (!packageJson.devDependencies) {
|
|
105
|
+
packageJson.devDependencies = {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const [dep, version] of Object.entries(REQUIRED_DEV_DEPENDENCIES)) {
|
|
109
|
+
if (!packageJson.devDependencies[dep]) {
|
|
110
|
+
packageJson.devDependencies[dep] = version;
|
|
111
|
+
console.log(` + Adding devDependency: ${dep}@${version}`);
|
|
112
|
+
updated = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check and add missing scripts
|
|
117
|
+
if (!packageJson.scripts) {
|
|
118
|
+
packageJson.scripts = {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const [script, command] of Object.entries(DEFAULT_SCRIPTS)) {
|
|
122
|
+
if (!packageJson.scripts[script]) {
|
|
123
|
+
packageJson.scripts[script] = command;
|
|
124
|
+
console.log(` + Adding script: ${script}`);
|
|
125
|
+
updated = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (updated) {
|
|
130
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
131
|
+
console.log("โ Updated package.json");
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return false;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error updating package.json:", error.message);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Checks if ImageMagick is available globally
|
|
144
|
+
*/
|
|
145
|
+
function isImageMagickInstalled() {
|
|
146
|
+
return shell.which("magick") !== null || shell.which("convert") !== null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Ensures ImageMagick is available for placeholder generation
|
|
151
|
+
*/
|
|
152
|
+
function ensureImageMagickInstalled() {
|
|
153
|
+
if (isImageMagickInstalled()) {
|
|
154
|
+
console.log("โ ImageMagick is available");
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log("โ ๏ธ ImageMagick not found");
|
|
159
|
+
console.log("๐ฆ ImageMagick is required for generating placeholder images");
|
|
160
|
+
console.log("");
|
|
161
|
+
console.log("๐ macOS: brew install imagemagick");
|
|
162
|
+
console.log("๐ง Ubuntu/Debian: sudo apt-get install imagemagick");
|
|
163
|
+
console.log(
|
|
164
|
+
"๐ฆ Windows: Download from https://imagemagick.org/script/download.php#windows"
|
|
165
|
+
);
|
|
166
|
+
console.log("");
|
|
167
|
+
console.log("๐ก After installation, you can generate placeholders with:");
|
|
168
|
+
console.log(" gxdev assets generate --size 400x300 --name my-placeholder");
|
|
169
|
+
console.log(" gxdev assets generate --name icons --count 3 --size 64x64");
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
safeCopyFile,
|
|
175
|
+
createPackageJson,
|
|
176
|
+
installDependencies,
|
|
177
|
+
updateExistingProject,
|
|
178
|
+
isImageMagickInstalled,
|
|
179
|
+
ensureImageMagickInstalled,
|
|
180
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility Functions Index
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all utility modules for convenient importing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const paths = require("./paths");
|
|
8
|
+
const ssl = require("./ssl");
|
|
9
|
+
const files = require("./files");
|
|
10
|
+
const prompts = require("./prompts");
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
...paths,
|
|
14
|
+
...ssl,
|
|
15
|
+
...files,
|
|
16
|
+
...prompts,
|
|
17
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles finding project roots and resolving paths to toolkit resources.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const { isWin, PACKAGE_NAME } = require("../constants");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Determines the correct binary name based on platform and architecture
|
|
14
|
+
*/
|
|
15
|
+
function getBinaryName() {
|
|
16
|
+
if (isWin) return "gento-win";
|
|
17
|
+
if (process.arch === "x64") return "gento-darwin-amd64";
|
|
18
|
+
return "gento";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Finds the project root directory by looking for package.json
|
|
23
|
+
*/
|
|
24
|
+
function findProjectRoot() {
|
|
25
|
+
let currentDir = process.cwd();
|
|
26
|
+
|
|
27
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
28
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) {
|
|
29
|
+
return currentDir;
|
|
30
|
+
}
|
|
31
|
+
currentDir = path.dirname(currentDir);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return process.cwd(); // Fallback to current directory
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolves paths for gx-devtools resources based on installation context
|
|
39
|
+
*
|
|
40
|
+
* Directory structure:
|
|
41
|
+
* - /template/ - Files copied to new projects during init
|
|
42
|
+
* - /runtime/ - Files used from node_modules (not copied to projects)
|
|
43
|
+
* - /socket-events/ - Socket event templates
|
|
44
|
+
*/
|
|
45
|
+
function resolveGxPaths() {
|
|
46
|
+
const projectRoot = findProjectRoot();
|
|
47
|
+
|
|
48
|
+
// Try local installation first
|
|
49
|
+
const localNodeModules = path.join(projectRoot, "node_modules", PACKAGE_NAME);
|
|
50
|
+
if (fs.existsSync(localNodeModules)) {
|
|
51
|
+
return {
|
|
52
|
+
gentoPath: path.join(localNodeModules, "bin", getBinaryName()),
|
|
53
|
+
viteConfigPath: path.join(localNodeModules, "runtime", "vite.config.js"),
|
|
54
|
+
templateDir: path.join(localNodeModules, "template"),
|
|
55
|
+
runtimeDir: path.join(localNodeModules, "runtime"),
|
|
56
|
+
socketEventsDir: path.join(localNodeModules, "socket-events"),
|
|
57
|
+
packageRoot: localNodeModules,
|
|
58
|
+
// Legacy alias for backward compatibility
|
|
59
|
+
configDir: path.join(localNodeModules, "template"),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Try global installation (or running from the toolkit itself)
|
|
64
|
+
const globalNodeModules = path.join(__dirname, "..", "..", "..");
|
|
65
|
+
return {
|
|
66
|
+
gentoPath: path.join(globalNodeModules, "bin", getBinaryName()),
|
|
67
|
+
viteConfigPath: path.join(globalNodeModules, "runtime", "vite.config.js"),
|
|
68
|
+
templateDir: path.join(globalNodeModules, "template"),
|
|
69
|
+
runtimeDir: path.join(globalNodeModules, "runtime"),
|
|
70
|
+
socketEventsDir: path.join(globalNodeModules, "socket-events"),
|
|
71
|
+
packageRoot: globalNodeModules,
|
|
72
|
+
// Legacy alias for backward compatibility
|
|
73
|
+
configDir: path.join(globalNodeModules, "template"),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolves file path checking local project first, then package
|
|
79
|
+
*
|
|
80
|
+
* @param {string} fileName - The file to find
|
|
81
|
+
* @param {string} subDir - Subdirectory within the search locations
|
|
82
|
+
* @param {string} packageLocation - Which package directory to check: 'template', 'runtime', or 'socket-events'
|
|
83
|
+
*/
|
|
84
|
+
function resolveFilePath(fileName, subDir = "", packageLocation = "template") {
|
|
85
|
+
const projectRoot = findProjectRoot();
|
|
86
|
+
const paths = resolveGxPaths();
|
|
87
|
+
|
|
88
|
+
// Check local project first
|
|
89
|
+
const localPath = path.join(projectRoot, subDir, fileName);
|
|
90
|
+
if (fs.existsSync(localPath)) {
|
|
91
|
+
return { path: localPath, isLocal: true };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Determine which package directory to check
|
|
95
|
+
let packageDir;
|
|
96
|
+
switch (packageLocation) {
|
|
97
|
+
case "runtime":
|
|
98
|
+
packageDir = paths.runtimeDir;
|
|
99
|
+
break;
|
|
100
|
+
case "socket-events":
|
|
101
|
+
packageDir = paths.socketEventsDir;
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
packageDir = paths.templateDir;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Fall back to package version
|
|
108
|
+
const packagePath = path.join(packageDir, subDir, fileName);
|
|
109
|
+
if (fs.existsSync(packagePath)) {
|
|
110
|
+
return { path: packagePath, isLocal: false };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Return package path even if doesn't exist (for error handling)
|
|
114
|
+
return { path: packagePath, isLocal: false };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Loads global configuration if available
|
|
119
|
+
*/
|
|
120
|
+
function loadGlobalConfig() {
|
|
121
|
+
const globalConfigPath = path.join(os.homedir(), "gxdev-default-config.json");
|
|
122
|
+
if (fs.existsSync(globalConfigPath)) {
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(fs.readFileSync(globalConfigPath, "utf-8"));
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.warn("Warning: Could not parse global configuration");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
getBinaryName,
|
|
134
|
+
findProjectRoot,
|
|
135
|
+
resolveGxPaths,
|
|
136
|
+
resolveFilePath,
|
|
137
|
+
loadGlobalConfig,
|
|
138
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Prompts Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles interactive CLI prompts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const readline = require("readline");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Prompts user for input
|
|
11
|
+
* @param {string} question - The question to ask
|
|
12
|
+
* @returns {Promise<string>} The user's answer
|
|
13
|
+
*/
|
|
14
|
+
function promptUser(question) {
|
|
15
|
+
const rl = readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question(question, (answer) => {
|
|
22
|
+
rl.close();
|
|
23
|
+
resolve(answer);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Prompts user for yes/no confirmation
|
|
30
|
+
* @param {string} question - The question to ask
|
|
31
|
+
* @param {boolean} defaultYes - Whether default is yes (Y/n) or no (y/N)
|
|
32
|
+
* @returns {Promise<boolean>} True if user confirmed
|
|
33
|
+
*/
|
|
34
|
+
async function confirmPrompt(question, defaultYes = true) {
|
|
35
|
+
const suffix = defaultYes ? "(Y/n)" : "(y/N)";
|
|
36
|
+
const answer = await promptUser(`${question} ${suffix}: `);
|
|
37
|
+
|
|
38
|
+
if (answer === "") {
|
|
39
|
+
return defaultYes;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Prompts user to select from a list of options
|
|
47
|
+
* @param {string} question - The question to ask
|
|
48
|
+
* @param {string[]} options - Array of options
|
|
49
|
+
* @returns {Promise<number>} Index of selected option
|
|
50
|
+
*/
|
|
51
|
+
async function selectPrompt(question, options) {
|
|
52
|
+
console.log(question);
|
|
53
|
+
options.forEach((opt, i) => {
|
|
54
|
+
console.log(` ${i + 1}. ${opt}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const answer = await promptUser("Enter number: ");
|
|
58
|
+
const index = parseInt(answer, 10) - 1;
|
|
59
|
+
|
|
60
|
+
if (index >= 0 && index < options.length) {
|
|
61
|
+
return index;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return 0; // Default to first option
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
promptUser,
|
|
69
|
+
confirmPrompt,
|
|
70
|
+
selectPrompt,
|
|
71
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSL Certificate Management
|
|
3
|
+
*
|
|
4
|
+
* Handles SSL certificate generation and management using mkcert.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const shell = require("shelljs");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if mkcert is installed globally
|
|
13
|
+
*/
|
|
14
|
+
function isMkcertInstalled() {
|
|
15
|
+
return shell.which("mkcert") !== null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Installs mkcert globally if not already installed
|
|
20
|
+
*/
|
|
21
|
+
function ensureMkcertInstalled() {
|
|
22
|
+
if (isMkcertInstalled()) {
|
|
23
|
+
console.log("โ mkcert is already installed globally");
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log("Installing mkcert globally...");
|
|
28
|
+
const result = shell.exec("npm install -g mkcert", { silent: true });
|
|
29
|
+
|
|
30
|
+
if (result.code === 0) {
|
|
31
|
+
console.log("โ mkcert installed successfully");
|
|
32
|
+
return true;
|
|
33
|
+
} else {
|
|
34
|
+
console.warn("โ Could not install mkcert globally, will use local version");
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Finds existing SSL certificates in the certs directory, including those with suffixes
|
|
41
|
+
*/
|
|
42
|
+
function findExistingCertificates(certsDir) {
|
|
43
|
+
if (!fs.existsSync(certsDir)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const files = fs.readdirSync(certsDir);
|
|
48
|
+
|
|
49
|
+
// Look for localhost certificates (with or without suffixes)
|
|
50
|
+
const certFile = files.find(
|
|
51
|
+
(f) =>
|
|
52
|
+
f.startsWith("localhost") && f.endsWith(".pem") && !f.includes("-key")
|
|
53
|
+
);
|
|
54
|
+
const keyFile = files.find(
|
|
55
|
+
(f) => f.startsWith("localhost") && f.endsWith("-key.pem")
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (certFile && keyFile) {
|
|
59
|
+
const certPath = path.join(certsDir, certFile);
|
|
60
|
+
const keyPath = path.join(certsDir, keyFile);
|
|
61
|
+
|
|
62
|
+
// Verify files actually exist and have content
|
|
63
|
+
try {
|
|
64
|
+
const certStats = fs.statSync(certPath);
|
|
65
|
+
const keyStats = fs.statSync(keyPath);
|
|
66
|
+
|
|
67
|
+
if (certStats.size > 0 && keyStats.size > 0) {
|
|
68
|
+
return { certPath, keyPath };
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Files don't exist or can't be read
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Cleans up old SSL certificate files to prevent naming conflicts
|
|
80
|
+
*/
|
|
81
|
+
function cleanupOldCertificates(certsDir) {
|
|
82
|
+
if (!fs.existsSync(certsDir)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const files = fs.readdirSync(certsDir);
|
|
88
|
+
const certFiles = files.filter(
|
|
89
|
+
(f) =>
|
|
90
|
+
f.startsWith("localhost") &&
|
|
91
|
+
(f.endsWith(".pem") || f.endsWith("-key.pem"))
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (certFiles.length > 0) {
|
|
95
|
+
console.log("๐งน Cleaning up old certificate files...");
|
|
96
|
+
certFiles.forEach((file) => {
|
|
97
|
+
const filePath = path.join(certsDir, file);
|
|
98
|
+
try {
|
|
99
|
+
fs.unlinkSync(filePath);
|
|
100
|
+
console.log(` Removed: ${file}`);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.warn(` Could not remove ${file}: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.warn("โ Could not clean up old certificates:", error.message);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Generates SSL certificates for localhost using mkcert
|
|
113
|
+
*/
|
|
114
|
+
function generateSSLCertificates(projectPath) {
|
|
115
|
+
const certsDir = path.join(projectPath, ".certs");
|
|
116
|
+
|
|
117
|
+
// Create .certs directory
|
|
118
|
+
if (!fs.existsSync(certsDir)) {
|
|
119
|
+
fs.mkdirSync(certsDir, { recursive: true });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check for existing certificates (including those with suffixes like +2)
|
|
123
|
+
const existingCerts = findExistingCertificates(certsDir);
|
|
124
|
+
if (existingCerts) {
|
|
125
|
+
console.log("โ SSL certificates already exist");
|
|
126
|
+
return existingCerts;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log("Generating SSL certificates for localhost...");
|
|
130
|
+
|
|
131
|
+
// Clean up any leftover certificate files to avoid naming conflicts
|
|
132
|
+
cleanupOldCertificates(certsDir);
|
|
133
|
+
|
|
134
|
+
// Try global mkcert first
|
|
135
|
+
let mkcertCmd = "mkcert";
|
|
136
|
+
if (!isMkcertInstalled()) {
|
|
137
|
+
// Use local mkcert via npx
|
|
138
|
+
mkcertCmd = "npx mkcert";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Change to certs directory and generate certificates
|
|
142
|
+
const currentDir = process.cwd();
|
|
143
|
+
try {
|
|
144
|
+
process.chdir(certsDir);
|
|
145
|
+
|
|
146
|
+
// Install CA if needed (only for global mkcert)
|
|
147
|
+
if (isMkcertInstalled()) {
|
|
148
|
+
shell.exec(`${mkcertCmd} -install`, { silent: true });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Generate certificates for localhost
|
|
152
|
+
const result = shell.exec(`${mkcertCmd} localhost 127.0.0.1 ::1`, {
|
|
153
|
+
silent: true,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (result.code === 0) {
|
|
157
|
+
// Find the actual generated certificate files
|
|
158
|
+
const generatedCerts = findExistingCertificates(certsDir);
|
|
159
|
+
if (generatedCerts) {
|
|
160
|
+
console.log("โ SSL certificates generated successfully");
|
|
161
|
+
console.log(
|
|
162
|
+
`๐ Certificate: ${path.basename(generatedCerts.certPath)}`
|
|
163
|
+
);
|
|
164
|
+
console.log(`๐ Key: ${path.basename(generatedCerts.keyPath)}`);
|
|
165
|
+
return generatedCerts;
|
|
166
|
+
} else {
|
|
167
|
+
console.warn(
|
|
168
|
+
"โ Certificates generated but not found in expected location"
|
|
169
|
+
);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
console.warn(
|
|
174
|
+
"โ Failed to generate SSL certificates, falling back to HTTP"
|
|
175
|
+
);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.warn("โ Error generating SSL certificates:", error.message);
|
|
180
|
+
return null;
|
|
181
|
+
} finally {
|
|
182
|
+
process.chdir(currentDir);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Updates the .env file with the actual SSL certificate paths
|
|
188
|
+
*/
|
|
189
|
+
function updateEnvWithCertPaths(projectPath, certs) {
|
|
190
|
+
const envPath = path.join(projectPath, ".env");
|
|
191
|
+
|
|
192
|
+
if (!fs.existsSync(envPath)) {
|
|
193
|
+
console.warn("โ .env file not found, skipping certificate path update");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
let envContent = fs.readFileSync(envPath, "utf-8");
|
|
199
|
+
|
|
200
|
+
// Get just the filenames from the full paths
|
|
201
|
+
const certFileName = path.basename(certs.certPath);
|
|
202
|
+
const keyFileName = path.basename(certs.keyPath);
|
|
203
|
+
|
|
204
|
+
// Update CERT_PATH and KEY_PATH with actual filenames
|
|
205
|
+
envContent = envContent.replace(
|
|
206
|
+
/CERT_PATH=.*$/m,
|
|
207
|
+
`CERT_PATH=.certs/${certFileName}`
|
|
208
|
+
);
|
|
209
|
+
envContent = envContent.replace(
|
|
210
|
+
/KEY_PATH=.*$/m,
|
|
211
|
+
`KEY_PATH=.certs/${keyFileName}`
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
fs.writeFileSync(envPath, envContent);
|
|
215
|
+
console.log("โ Updated .env with SSL certificate paths");
|
|
216
|
+
console.log(` CERT_PATH=.certs/${certFileName}`);
|
|
217
|
+
console.log(` KEY_PATH=.certs/${keyFileName}`);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.warn(
|
|
220
|
+
"โ Could not update .env with certificate paths:",
|
|
221
|
+
error.message
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
isMkcertInstalled,
|
|
228
|
+
ensureMkcertInstalled,
|
|
229
|
+
findExistingCertificates,
|
|
230
|
+
cleanupOldCertificates,
|
|
231
|
+
generateSSLCertificates,
|
|
232
|
+
updateEnvWithCertPaths,
|
|
233
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|