botim-cli 0.0.7
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 +375 -0
- package/bin/cli +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +459 -0
- package/dist/cli.mjs +106 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +13 -0
- package/package.json +83 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createReactApp, createVueApp, handleLogin, handleLogout, handleQRCode } from "./commands/index.js";
|
|
4
|
+
import { showMenuOrRequireAuth } from "./commands/auth/index.js";
|
|
5
|
+
import { displayExamples, displayQuickStart } from "./utils/help.js";
|
|
6
|
+
import { logger } from "./utils/logger.js";
|
|
7
|
+
import { isLoggedIn } from "./utils/config.js";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const packageJsonPath = path.join(__dirname, "..", "package.json");
|
|
15
|
+
let version = "1.0.0";
|
|
16
|
+
try {
|
|
17
|
+
const packageJson = fs.readJSONSync(packageJsonPath);
|
|
18
|
+
version = packageJson.version;
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
const program = new Command();
|
|
22
|
+
program.name("botim-cli").description("CLI tool to generate boilerplate code for React and Vue applications").version(version, "-v, --version", "Output the current version");
|
|
23
|
+
program.configureHelp({
|
|
24
|
+
sortSubcommands: true,
|
|
25
|
+
sortOptions: true
|
|
26
|
+
});
|
|
27
|
+
program.command("create-react-app [project-name]").description("Create a new React application with Vite and TypeScript").alias("react").action(async (projectName) => {
|
|
28
|
+
console.log(chalk.cyan.bold("\n\u{1F680} Botim CLI - React App Generator\n"));
|
|
29
|
+
await createReactApp(projectName);
|
|
30
|
+
});
|
|
31
|
+
program.command("create-vue-app [project-name]").description("Create a new Vue application with Vite and TypeScript").alias("vue").action(async (projectName) => {
|
|
32
|
+
console.log(chalk.cyan.bold("\n\u{1F680} Botim CLI - Vue App Generator\n"));
|
|
33
|
+
await createVueApp(projectName);
|
|
34
|
+
});
|
|
35
|
+
program.command("create [project-name]").description("Create a new project in interactive mode - choose framework and features").alias("init").action(async (projectName) => {
|
|
36
|
+
console.log(chalk.cyan.bold("\n\u{1F680} Botim CLI - Project Generator\n"));
|
|
37
|
+
const { promptProjectDetails } = await import("./utils/prompts.js");
|
|
38
|
+
const config = await promptProjectDetails(projectName);
|
|
39
|
+
if (config.framework === "react") {
|
|
40
|
+
await createReactApp(config.name);
|
|
41
|
+
} else if (config.framework === "vue") {
|
|
42
|
+
await createVueApp(config.name);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
program.command("examples").description("Show usage examples and common patterns").action(() => {
|
|
46
|
+
displayExamples();
|
|
47
|
+
});
|
|
48
|
+
program.command("quick-start").description("Show quick start guide").alias("qs").action(() => {
|
|
49
|
+
displayQuickStart();
|
|
50
|
+
});
|
|
51
|
+
program.command("login [login-url]").description("Login via browser and save authentication token").action(async (loginUrl) => {
|
|
52
|
+
console.log(chalk.cyan.bold("\n\u{1F510} Botim CLI - Login\n"));
|
|
53
|
+
await handleLogin(loginUrl);
|
|
54
|
+
});
|
|
55
|
+
program.command("logout").description("Logout and clear saved authentication token").action(async () => {
|
|
56
|
+
console.log(chalk.cyan.bold("\n\u{1F6AA} Botim CLI - Logout\n"));
|
|
57
|
+
await handleLogout();
|
|
58
|
+
});
|
|
59
|
+
program.command("auth").description("Access authenticated commands (requires login)").alias("authenticated").action(async () => {
|
|
60
|
+
await showMenuOrRequireAuth();
|
|
61
|
+
});
|
|
62
|
+
program.command("create-mp-app").description("Create a new Mini-Program app non-interactively (requires authentication)").alias("cmp").requiredOption("--projectName <name>", "Project folder name").option("--framework <framework>", "Template framework: react or vue", "react").option("--title <title>", "App title (default: derived from projectName)").option("--domainPrefix <prefix>", "Domain prefix (default: auto-selected)").option("--reviewNote <note>", "Review note for submission (default: auto-generated)").option("--creatorType <type>", "Creator type: hybrid or h5bridge", "hybrid").option("--website <url>", "Website URL (required if creatorType=h5bridge)").option("--shareText <text>", "Share card description (default: same as title)").option("--screenOrientation <orientation>", "Screen mode: fullscreen, immersive, or standard", "fullscreen").option("--apiLevel <version>", "API level version (default: latest available)").option("--audience <audience>", "Audience: 1=Private or 2=Public", "2").addHelpText("after", `
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
Basic usage with minimal options:
|
|
66
|
+
$ botim-cli create-mp-app --projectName my-app
|
|
67
|
+
|
|
68
|
+
Create a React app with custom title:
|
|
69
|
+
$ botim-cli create-mp-app --projectName my-shop --framework react --title "My Shop"
|
|
70
|
+
|
|
71
|
+
Create a Vue app with all options:
|
|
72
|
+
$ botim-cli create-mp-app \\
|
|
73
|
+
--projectName my-vue-app \\
|
|
74
|
+
--framework vue \\
|
|
75
|
+
--title "My Vue App" \\
|
|
76
|
+
--domainPrefix me.botim.mp.test \\
|
|
77
|
+
--shareText "Explore my amazing app" \\
|
|
78
|
+
--screenOrientation immersive \\
|
|
79
|
+
--audience 2
|
|
80
|
+
|
|
81
|
+
Create an H5BRIDGE app (wrapping a website):
|
|
82
|
+
$ botim-cli create-mp-app \\
|
|
83
|
+
--projectName my-website-app \\
|
|
84
|
+
--creatorType h5bridge \\
|
|
85
|
+
--website https://example.com \\
|
|
86
|
+
--title "My Website" \\
|
|
87
|
+
--shareText "Visit my website"
|
|
88
|
+
|
|
89
|
+
Create a private hybrid app:
|
|
90
|
+
$ botim-cli create-mp-app \\
|
|
91
|
+
--projectName internal-tool \\
|
|
92
|
+
--title "Internal Tool" \\
|
|
93
|
+
--audience 1 \\
|
|
94
|
+
--reviewNote "Internal testing app"
|
|
95
|
+
|
|
96
|
+
Creator Types:
|
|
97
|
+
hybrid - Fastest, local resources with full platform capabilities (default)
|
|
98
|
+
h5bridge - Standard, wraps a responsive HTML5 website (requires --website)
|
|
99
|
+
|
|
100
|
+
Screen Orientations:
|
|
101
|
+
fullscreen - Full screen with status bar visible (default)
|
|
102
|
+
immersive - Full screen with status bar hidden
|
|
103
|
+
standard - Standard page with title bar
|
|
104
|
+
|
|
105
|
+
Audience Options:
|
|
106
|
+
1 - Private (only accessible to specific users)
|
|
107
|
+
2 - Public (accessible to all users) (default)
|
|
108
|
+
`).action(async (options) => {
|
|
109
|
+
const loggedIn = await isLoggedIn();
|
|
110
|
+
if (!loggedIn) {
|
|
111
|
+
console.log(chalk.red("\n\u274C Authentication required. Please login first using: botim-cli login\n"));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
115
|
+
const command = authenticatedCommandRegistry.get("cmp");
|
|
116
|
+
if (command) {
|
|
117
|
+
const processedOptions = {
|
|
118
|
+
...options,
|
|
119
|
+
audience: parseInt(options.audience, 10)
|
|
120
|
+
};
|
|
121
|
+
await command.execute(processedOptions);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
program.command("init-mp-app").description("Initialize MP app in current directory (requires authentication)").alias("init-mp").action(async () => {
|
|
125
|
+
const loggedIn = await isLoggedIn();
|
|
126
|
+
if (!loggedIn) {
|
|
127
|
+
console.log(chalk.red("\n\u274C Authentication required. Please login first using: botim-cli login\n"));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
131
|
+
const command = authenticatedCommandRegistry.get("init-mp-app");
|
|
132
|
+
if (command) {
|
|
133
|
+
await command.execute();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
program.command("deploy-mp-app").description("Deploy MP app to server (requires authentication)").alias("deploy").option("-w, --website <url>", "Website/Endpoint URL (required for H5BRIDGE apps)").option("-n, --note <note>", 'Review note (optional, defaults to "Deployed via CLI")').option("-t, --testflight", "Release to TestFlight (Beta) - this is the default for direct mode").option("-y, --yes", "Skip confirmation prompts (Note: Production releases require interactive mode)").action(async (options) => {
|
|
137
|
+
const loggedIn = await isLoggedIn();
|
|
138
|
+
if (!loggedIn) {
|
|
139
|
+
console.log(chalk.red("\n\u274C Authentication required. Please login first using: botim-cli login\n"));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
143
|
+
const command = authenticatedCommandRegistry.get("deploy-mp-app");
|
|
144
|
+
if (command) {
|
|
145
|
+
await command.execute(options);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
program.command("debug-mp-app").description("Debug MP app (requires authentication and botim_config.json)").alias("debug").action(async () => {
|
|
149
|
+
const loggedIn = await isLoggedIn();
|
|
150
|
+
if (!loggedIn) {
|
|
151
|
+
console.log(chalk.red("\n\u274C Authentication required. Please login first using: botim-cli login\n"));
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
155
|
+
const command = authenticatedCommandRegistry.get("debug-mp-app");
|
|
156
|
+
if (command) {
|
|
157
|
+
await command.execute();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
program.command("list-mp-permissions [app-id]").description("List permissions for a mini-program app (requires authentication)").alias("perms").action(async (appId) => {
|
|
161
|
+
const loggedIn = await isLoggedIn();
|
|
162
|
+
if (!loggedIn) {
|
|
163
|
+
console.log(chalk.red("\n\u274C Authentication required. Please login first using: botim-cli login\n"));
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
167
|
+
const command = authenticatedCommandRegistry.get("list-mp-permissions");
|
|
168
|
+
if (command) {
|
|
169
|
+
await command.execute({ appId });
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const configCommand = program.command("config").description("Manage CLI configuration");
|
|
173
|
+
configCommand.command("set <key> <value>").description("Set a configuration value").action(async (key) => {
|
|
174
|
+
console.log(chalk.cyan.bold("\n\u2699\uFE0F Botim CLI - Configuration\n"));
|
|
175
|
+
if (key.startsWith("templateRepositoryUrl") || key.startsWith("templateRepositoryBranch")) {
|
|
176
|
+
console.log(chalk.yellow("\u26A0\uFE0F Template repository URLs use built-in defaults (part of the project).\n"));
|
|
177
|
+
console.log(chalk.gray("To override defaults, create a .env file in your project root or ~/.botim-cli/.env with:\n"));
|
|
178
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_REACT_URL=<url>\n"));
|
|
179
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_VUE_URL=<url>\n"));
|
|
180
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_BRANCH=<branch> (optional, default: main)\n"));
|
|
181
|
+
console.log(chalk.gray("\nSee .env.example for more details.\n"));
|
|
182
|
+
process.exit(1);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(chalk.red(`\u274C Unknown configuration key: ${key}
|
|
185
|
+
`));
|
|
186
|
+
console.log(chalk.yellow("Note: Template repository URLs are configured via .env file, not CLI commands.\n"));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
configCommand.command("get <key>").description("Get a configuration value").action(async (key) => {
|
|
191
|
+
const {
|
|
192
|
+
getTemplateRepositoryUrlForFramework,
|
|
193
|
+
getTemplateRepositoryBranchForFramework
|
|
194
|
+
} = await import("./utils/template-fetcher.js");
|
|
195
|
+
const { getAvailableFrameworks } = await import("./utils/template-repositories.js");
|
|
196
|
+
console.log(chalk.cyan.bold("\n\u2699\uFE0F Botim CLI - Configuration\n"));
|
|
197
|
+
if (key.startsWith("templateRepositoryUrl:")) {
|
|
198
|
+
const framework = key.split(":")[1];
|
|
199
|
+
const availableFrameworks = getAvailableFrameworks();
|
|
200
|
+
if (!availableFrameworks.includes(framework)) {
|
|
201
|
+
console.log(chalk.red(`\u274C Unknown framework: ${framework}
|
|
202
|
+
`));
|
|
203
|
+
console.log(chalk.yellow(`Available frameworks: ${availableFrameworks.join(", ")}
|
|
204
|
+
`));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const url = await getTemplateRepositoryUrlForFramework(framework);
|
|
208
|
+
if (url) {
|
|
209
|
+
console.log(chalk.green(`Template repository URL for ${framework}: ${url}
|
|
210
|
+
`));
|
|
211
|
+
} else {
|
|
212
|
+
console.log(chalk.yellow(`Template repository URL for ${framework} not set
|
|
213
|
+
`));
|
|
214
|
+
}
|
|
215
|
+
} else if (key === "templateRepositoryUrl") {
|
|
216
|
+
const availableFrameworks = getAvailableFrameworks();
|
|
217
|
+
console.log(chalk.gray("Template repository URLs:\n"));
|
|
218
|
+
for (const framework of availableFrameworks) {
|
|
219
|
+
const url = await getTemplateRepositoryUrlForFramework(framework);
|
|
220
|
+
if (url) {
|
|
221
|
+
console.log(chalk.white(` ${framework}: ${url}`));
|
|
222
|
+
} else {
|
|
223
|
+
console.log(chalk.yellow(` ${framework}: (not set)`));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
console.log("");
|
|
227
|
+
} else if (key.startsWith("templateRepositoryBranch:")) {
|
|
228
|
+
const framework = key.split(":")[1];
|
|
229
|
+
const availableFrameworks = getAvailableFrameworks();
|
|
230
|
+
if (!availableFrameworks.includes(framework)) {
|
|
231
|
+
console.log(chalk.red(`\u274C Unknown framework: ${framework}
|
|
232
|
+
`));
|
|
233
|
+
console.log(chalk.yellow(`Available frameworks: ${availableFrameworks.join(", ")}
|
|
234
|
+
`));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
const branch = await getTemplateRepositoryBranchForFramework(framework);
|
|
238
|
+
console.log(chalk.green(`Template repository branch for ${framework}: ${branch}
|
|
239
|
+
`));
|
|
240
|
+
} else if (key === "templateRepositoryBranch") {
|
|
241
|
+
const availableFrameworks = getAvailableFrameworks();
|
|
242
|
+
console.log(chalk.gray("Template repository branches:\n"));
|
|
243
|
+
for (const framework of availableFrameworks) {
|
|
244
|
+
const branch = await getTemplateRepositoryBranchForFramework(framework);
|
|
245
|
+
console.log(chalk.white(` ${framework}: ${branch}`));
|
|
246
|
+
}
|
|
247
|
+
console.log("");
|
|
248
|
+
} else {
|
|
249
|
+
console.log(chalk.red(`\u274C Unknown configuration key: ${key}
|
|
250
|
+
`));
|
|
251
|
+
console.log(chalk.yellow("Available keys:\n"));
|
|
252
|
+
console.log(chalk.yellow(" - templateRepositoryUrl (shows all frameworks from .env)\n"));
|
|
253
|
+
console.log(chalk.yellow(" - templateRepositoryUrl:<framework> (e.g., templateRepositoryUrl:react)\n"));
|
|
254
|
+
console.log(chalk.yellow(" - templateRepositoryBranch (shows all frameworks from .env)\n"));
|
|
255
|
+
console.log(chalk.yellow(" - templateRepositoryBranch:<framework> (e.g., templateRepositoryBranch:vue)\n"));
|
|
256
|
+
console.log(chalk.gray("\nNote: Configure these values in your .env file, not via CLI commands.\n"));
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
configCommand.command("list").description("List all configuration values").action(async () => {
|
|
261
|
+
const {
|
|
262
|
+
getTemplateRepositoryUrlForFramework,
|
|
263
|
+
getTemplateRepositoryBranchForFramework
|
|
264
|
+
} = await import("./utils/template-fetcher.js");
|
|
265
|
+
const { getAvailableFrameworks } = await import("./utils/template-repositories.js");
|
|
266
|
+
console.log(chalk.cyan.bold("\n\u2699\uFE0F Botim CLI - Configuration\n"));
|
|
267
|
+
const availableFrameworks = getAvailableFrameworks();
|
|
268
|
+
console.log(chalk.gray("Template Repository Configuration:\n"));
|
|
269
|
+
let hasAnyUrl = false;
|
|
270
|
+
for (const framework of availableFrameworks) {
|
|
271
|
+
const url = await getTemplateRepositoryUrlForFramework(framework);
|
|
272
|
+
const branch = await getTemplateRepositoryBranchForFramework(framework);
|
|
273
|
+
if (url) {
|
|
274
|
+
hasAnyUrl = true;
|
|
275
|
+
console.log(chalk.white(` ${framework.toUpperCase()}:`));
|
|
276
|
+
console.log(chalk.white(` URL: ${url}`));
|
|
277
|
+
console.log(chalk.white(` Branch: ${branch}
|
|
278
|
+
`));
|
|
279
|
+
} else {
|
|
280
|
+
console.log(chalk.yellow(` ${framework.toUpperCase()}: (not configured)
|
|
281
|
+
`));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!hasAnyUrl) {
|
|
285
|
+
console.log(chalk.yellow("\u26A0\uFE0F No template repository URLs are configured.\n"));
|
|
286
|
+
console.log(chalk.gray("Built-in defaults are included in the project, but you can override them.\n"));
|
|
287
|
+
console.log(chalk.gray("To override defaults, create a .env file in your project root or ~/.botim-cli/.env with:\n"));
|
|
288
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_REACT_URL=git@gitlab.com:group/react-templates.git\n"));
|
|
289
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_VUE_URL=git@gitlab.com:group/vue-templates.git\n"));
|
|
290
|
+
console.log(chalk.gray(" BOTIM_TEMPLATE_BRANCH=main (optional, default: main)\n"));
|
|
291
|
+
console.log(chalk.gray("\nOr set environment variables directly:\n"));
|
|
292
|
+
console.log(chalk.gray(" export BOTIM_TEMPLATE_REACT_URL=...\n"));
|
|
293
|
+
console.log(chalk.gray(" export BOTIM_TEMPLATE_VUE_URL=...\n"));
|
|
294
|
+
console.log(chalk.gray("\nSee .env.example for a template.\n"));
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
program.command("clear-template-cache").description("Clear cached templates to force fresh download on next use").alias("clear-cache").action(async () => {
|
|
298
|
+
const { clearTemplateCache } = await import("./utils/template-fetcher.js");
|
|
299
|
+
console.log(chalk.cyan.bold("\n\u{1F5D1}\uFE0F Botim CLI - Clear Template Cache\n"));
|
|
300
|
+
try {
|
|
301
|
+
await clearTemplateCache();
|
|
302
|
+
console.log(chalk.green("\u2713 Template cache cleared successfully\n"));
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.log(chalk.red(`\u274C Failed to clear template cache: ${error.message}
|
|
305
|
+
`));
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
program.command("logs").description("Show log file location or view recent logs").option("-t, --tail <lines>", "Show last N lines of log file", "50").action(async (options) => {
|
|
310
|
+
const logPath = logger.getLogFilePath();
|
|
311
|
+
const tailLines = parseInt(options.tail) || 50;
|
|
312
|
+
console.log(chalk.cyan.bold("\n\u{1F4CB} Botim CLI - Logs\n"));
|
|
313
|
+
console.log(chalk.gray(`Log file: ${logPath}
|
|
314
|
+
`));
|
|
315
|
+
if (await fs.pathExists(logPath)) {
|
|
316
|
+
const logContent = await fs.readFile(logPath, "utf8");
|
|
317
|
+
const lines = logContent.split("\n").filter((line) => line.trim());
|
|
318
|
+
const recentLines = lines.slice(-tailLines);
|
|
319
|
+
console.log(chalk.yellow(`Showing last ${recentLines.length} lines:
|
|
320
|
+
`));
|
|
321
|
+
console.log(chalk.gray("\u2500".repeat(60)));
|
|
322
|
+
recentLines.forEach((line) => console.log(line));
|
|
323
|
+
console.log(chalk.gray("\u2500".repeat(60)));
|
|
324
|
+
console.log(chalk.gray(`
|
|
325
|
+
To view full log: cat ${logPath}
|
|
326
|
+
`));
|
|
327
|
+
} else {
|
|
328
|
+
console.log(chalk.yellow("No log file found yet. Logs will be created when you run commands.\n"));
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
program.on("command:*", function() {
|
|
332
|
+
console.error(chalk.red("\n\u274C Invalid command: %s\n"), program.args.join(" "));
|
|
333
|
+
console.log(chalk.gray("Run --help to see available commands\n"));
|
|
334
|
+
process.exit(1);
|
|
335
|
+
});
|
|
336
|
+
async function showMainMenu() {
|
|
337
|
+
try {
|
|
338
|
+
console.log(chalk.cyan.bold("\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
339
|
+
console.log(chalk.cyan.bold("\u2551 \u{1F680} Botim CLI Generator \u2551"));
|
|
340
|
+
console.log(chalk.cyan.bold("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n"));
|
|
341
|
+
const inquirer = (await import("inquirer")).default;
|
|
342
|
+
const loggedIn = await isLoggedIn();
|
|
343
|
+
let authenticatedChoices = [];
|
|
344
|
+
if (loggedIn) {
|
|
345
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
346
|
+
const commands = authenticatedCommandRegistry.getAll();
|
|
347
|
+
const currentDir = process.cwd();
|
|
348
|
+
const configFilePath = path.join(currentDir, "botim_config.json");
|
|
349
|
+
const hasConfigFile = await fs.pathExists(configFilePath);
|
|
350
|
+
const filteredCommands = commands.filter((cmd) => {
|
|
351
|
+
if (cmd.hideFromMenu) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
if (cmd.name === "deploy-mp-app" || cmd.name === "debug-mp-app") {
|
|
355
|
+
return hasConfigFile;
|
|
356
|
+
}
|
|
357
|
+
if (cmd.name === "create-mp-app" || cmd.name === "init-mp-app") {
|
|
358
|
+
return !hasConfigFile;
|
|
359
|
+
}
|
|
360
|
+
return true;
|
|
361
|
+
});
|
|
362
|
+
authenticatedChoices = filteredCommands.map((cmd) => ({
|
|
363
|
+
name: cmd.description,
|
|
364
|
+
value: `auth:${cmd.name}`
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
const unauthenticatedChoices = [
|
|
368
|
+
{ name: "\u{1F4F1} Create a Mini-Program (Local)", value: "mini-program" },
|
|
369
|
+
{ name: "\u{1F510} Login", value: "login" },
|
|
370
|
+
// { name: '📱 Generate QR Code', value: 'qrcode' },
|
|
371
|
+
{ name: "\u{1F4DA} View examples", value: "examples" },
|
|
372
|
+
{ name: "\u2753 Show help", value: "help" },
|
|
373
|
+
{ name: "\u{1F6AA} Exit", value: "exit" }
|
|
374
|
+
];
|
|
375
|
+
authenticatedChoices = [
|
|
376
|
+
...authenticatedChoices,
|
|
377
|
+
{ name: "\u{1F4F1} Create a Mini-Program (Local)", value: "mini-program" },
|
|
378
|
+
// { name: '📱 Generate QR Code', value: 'qrcode' },
|
|
379
|
+
{ name: "\u{1F6AA} Logout", value: "logout" },
|
|
380
|
+
{ name: "\u{1F4DA} View examples", value: "examples" },
|
|
381
|
+
{ name: "\u2753 Show help", value: "help" },
|
|
382
|
+
{ name: "\u{1F6AA} Exit", value: "exit" }
|
|
383
|
+
];
|
|
384
|
+
const { action } = await inquirer.prompt([
|
|
385
|
+
{
|
|
386
|
+
type: "list",
|
|
387
|
+
name: "action",
|
|
388
|
+
message: loggedIn ? chalk.green("\u2713 Authenticated") + " - What would you like to do?" : "What would you like to do?",
|
|
389
|
+
choices: loggedIn ? authenticatedChoices : unauthenticatedChoices
|
|
390
|
+
}
|
|
391
|
+
]);
|
|
392
|
+
if (action.startsWith("auth:")) {
|
|
393
|
+
const commandName = action.replace("auth:", "");
|
|
394
|
+
const { authenticatedCommandRegistry } = await import("./commands/auth/index.js");
|
|
395
|
+
const command = authenticatedCommandRegistry.get(commandName);
|
|
396
|
+
if (command) {
|
|
397
|
+
try {
|
|
398
|
+
await command.execute();
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error(chalk.red("\n\u274C Error executing command:"), error);
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} else if (action === "mini-program") {
|
|
405
|
+
const { framework } = await inquirer.prompt([
|
|
406
|
+
{
|
|
407
|
+
type: "list",
|
|
408
|
+
name: "framework",
|
|
409
|
+
message: "Select a framework template:",
|
|
410
|
+
choices: [
|
|
411
|
+
{ name: "\u269B\uFE0F React - Modern React 18 with Vite", value: "react" },
|
|
412
|
+
{ name: "\u{1F596} Vue - Vue 3 with Composition API", value: "vue" },
|
|
413
|
+
new inquirer.Separator(),
|
|
414
|
+
{ name: "\u2190 Back to main menu", value: "back" }
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
]);
|
|
418
|
+
if (framework === "back") {
|
|
419
|
+
await showMainMenu();
|
|
420
|
+
} else if (framework === "react") {
|
|
421
|
+
console.log(chalk.cyan.bold("\n\u{1F680} Botim CLI - React Mini-Program\n"));
|
|
422
|
+
await createReactApp();
|
|
423
|
+
} else if (framework === "vue") {
|
|
424
|
+
console.log(chalk.cyan.bold("\n\u{1F680} Botim CLI - Vue Mini-Program\n"));
|
|
425
|
+
await createVueApp();
|
|
426
|
+
}
|
|
427
|
+
} else if (action === "login") {
|
|
428
|
+
await handleLogin();
|
|
429
|
+
const stillLoggedIn = await isLoggedIn();
|
|
430
|
+
if (stillLoggedIn) {
|
|
431
|
+
console.log(chalk.green("\n\u2713 Login successful! You can now access authenticated commands.\n"));
|
|
432
|
+
await showMainMenu();
|
|
433
|
+
}
|
|
434
|
+
} else if (action === "qrcode") {
|
|
435
|
+
await handleQRCode();
|
|
436
|
+
} else if (action === "logout") {
|
|
437
|
+
await handleLogout();
|
|
438
|
+
await showMainMenu();
|
|
439
|
+
} else if (action === "examples") {
|
|
440
|
+
displayExamples();
|
|
441
|
+
await showMainMenu();
|
|
442
|
+
} else if (action === "help") {
|
|
443
|
+
program.outputHelp();
|
|
444
|
+
console.log("");
|
|
445
|
+
await showMainMenu();
|
|
446
|
+
} else if (action === "exit") {
|
|
447
|
+
console.log(chalk.gray("\nGoodbye! \u{1F44B}\n"));
|
|
448
|
+
process.exit(0);
|
|
449
|
+
}
|
|
450
|
+
} catch (error) {
|
|
451
|
+
console.error(chalk.red("\n\u274C Error:"), error);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (!process.argv.slice(2).length) {
|
|
456
|
+
showMainMenu();
|
|
457
|
+
} else {
|
|
458
|
+
program.parse(process.argv);
|
|
459
|
+
}
|