@gadmin2n/cli 0.0.104 → 0.0.106

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.
@@ -37,105 +37,107 @@ class NewAction extends abstract_action_1.AbstractAction {
37
37
  const shouldSkipInstall = options.some((option) => option.name === 'skip-install' && option.value === true);
38
38
  const shouldSkipGit = options.some((option) => option.name === 'skip-git' && option.value === true);
39
39
  const projectDirectory = getProjectDirectory(getApplicationNameInput(inputs), directoryOption);
40
+ // 落盘用户提供的环境配置(dry-run 模式跳过,因为目录可能未实际生成)
41
+ if (!isDryRunEnabled) {
42
+ yield applyEnvConfig(projectDirectory, options).catch((err) => {
43
+ console.error(chalk.yellow(`[warn] Failed to apply env config: ${err && err.message ? err.message : err}`));
44
+ });
45
+ }
40
46
  if (!shouldSkipInstall) {
41
- if (options.find(option => option.name === 'server-framework' && option.value === 'nest')) {
42
- console.info('Begin to install server dependence ...');
43
- yield installPackages(options, isDryRunEnabled, (0, path_1.join)(projectDirectory, 'server'));
44
- console.info();
45
- }
46
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
47
- console.info('Begin to install web dependence ...');
48
- yield installPackages(options, isDryRunEnabled, (0, path_1.join)(projectDirectory, 'web'));
49
- console.info();
50
- }
47
+ console.info('Begin to install server dependence ...');
48
+ yield installPackages(options, isDryRunEnabled, (0, path_1.join)(projectDirectory, 'server'));
49
+ console.info();
50
+ console.info('Begin to install web dependence ...');
51
+ yield installPackages(options, isDryRunEnabled, (0, path_1.join)(projectDirectory, 'web'));
52
+ console.info();
51
53
  }
52
54
  if (!isDryRunEnabled) {
53
55
  if (!shouldSkipGit) {
54
56
  yield initializeGitRepository(projectDirectory);
55
57
  yield createGitIgnoreFile(projectDirectory);
56
58
  }
57
- const inputPackageManager = (options.find((option) => option.name === 'package-manager').value || 'yarn').toLowerCase();
59
+ const inputPackageManager = (options.find((option) => option.name === 'package-manager')
60
+ .value || 'yarn').toLowerCase();
61
+ // 收集仍未填写、需要在引导中提示用户处理的环境变量
62
+ const viteAgentId = getOptionByName(options, 'vite-agent-id') || '';
63
+ const taihuToken = getOptionByName(options, 'taihu-app-token') || '';
64
+ const databaseUrl = getOptionByName(options, 'database-url') || '';
65
+ const missingEnv = [];
66
+ if (!viteAgentId) {
67
+ missingEnv.push({
68
+ var: 'VITE_AGENT_ID',
69
+ file: 'web/.env.local',
70
+ desc: 'knot Agent ID',
71
+ });
72
+ }
73
+ if (!taihuToken) {
74
+ missingEnv.push({
75
+ var: 'TAIHU_APP_TOKEN',
76
+ file: 'server/.env',
77
+ desc: '太湖应用 ID — 替换 TAIHU_APP_TOKEN= 后的 token',
78
+ });
79
+ }
80
+ if (!databaseUrl) {
81
+ missingEnv.push({
82
+ var: 'DATABASE_URL',
83
+ file: 'server/.env.local',
84
+ desc: 'PostgreSQL 连接串',
85
+ });
86
+ }
58
87
  console.info();
59
88
  console.info(ui_1.MESSAGES.PACKAGE_MANAGER_INSTALLATION_SUCCEED(projectDirectory));
60
89
  console.info(ui_1.MESSAGES.GET_STARTED_INFORMATION);
61
90
  console.info();
62
91
  console.info(chalk.gray(ui_1.MESSAGES.CHANGE_DIR_COMMAND(projectDirectory)));
92
+ let step = 1;
93
+ // Step: Install dependencies (only if user skipped install)
63
94
  if (shouldSkipInstall) {
64
- console.info('#Install dependence: ');
65
- if (options.find(option => option.name === 'server-framework' && option.value === 'nest')) {
66
- if (inputPackageManager === 'yarn') {
67
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
68
- console.info(chalk.gray(`cd server && ${inputPackageManager}; cd ../web && ${inputPackageManager}; cd ..`));
69
- }
70
- else {
71
- console.info(chalk.gray(`cd server && ${inputPackageManager}; cd ..`));
72
- }
73
- }
74
- else {
75
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
76
- console.info(chalk.gray(`cd server && ${inputPackageManager} install; cd ../web && ${inputPackageManager} install; cd ..`));
77
- }
78
- else {
79
- console.info(chalk.gray(`cd server && ${inputPackageManager} install; cd ..`));
80
- }
81
- }
95
+ console.info(chalk.bold(`# ${step}. Install dependencies:`));
96
+ if (inputPackageManager === 'yarn') {
97
+ console.info(chalk.gray(` cd server && ${inputPackageManager}; cd ../web && ${inputPackageManager}; cd ..`));
82
98
  }
83
- else { // go iris
84
- if (inputPackageManager === 'yarn') {
85
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
86
- console.info(chalk.gray(`cd server && go get && cd bin/prisma-parse && ${inputPackageManager}; cd ../../web && ${inputPackageManager}; cd ..`));
87
- }
88
- else {
89
- console.info(chalk.gray(`cd server && go get && cd bin/prisma-parse && ${inputPackageManager}; cd ../..`));
90
- }
91
- }
92
- else {
93
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
94
- console.info(chalk.gray(`cd server && go get && cd bin/prisma-parse && ${inputPackageManager} install; cd ../../web && ${inputPackageManager} install; cd ..`));
95
- }
96
- else {
97
- console.info(chalk.gray(`cd server && go get && cd bin/prisma-parse && ${inputPackageManager} install; cd ../..`));
98
- }
99
- }
99
+ else {
100
+ console.info(chalk.gray(` cd server && ${inputPackageManager} install; cd ../web && ${inputPackageManager} install; cd ..`));
100
101
  }
101
102
  console.info();
103
+ step++;
102
104
  }
103
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
104
- console.info("#Change dev's coding agent(knot Agent) id:");
105
- console.info(chalk.gray("vi web/.env.local"));
105
+ // Step: Configure environment variables (only items the user skipped)
106
+ if (missingEnv.length > 0) {
107
+ console.info(chalk.bold(`# ${step}. Configure environment variables:`));
108
+ for (const m of missingEnv) {
109
+ console.info(` - ${chalk.cyan(m.var)} (${m.desc})`);
110
+ console.info(chalk.gray(` vi ${m.file}`));
111
+ }
106
112
  console.info();
113
+ step++;
107
114
  }
108
- console.info("#Change dev's env database config:");
109
- console.info(chalk.gray("vi server/.env.local"));
115
+ // Step: Generate code from prisma schema
116
+ console.info(chalk.bold(`# ${step}. Generate code from prisma schema:`));
117
+ console.info(chalk.gray(' gadmin2 g prisma:dev'));
110
118
  console.info();
111
- console.info("#Generate code by config/schema.prisma:");
112
- console.info(chalk.gray("gadmin2 g prisma:dev"));
113
- console.info();
114
- if (options.find(option => option.name === 'server-framework' && option.value === 'nest')) {
115
- console.info('#Start server devServer:');
116
- console.info(chalk.gray('cd server'));
117
- if (inputPackageManager === 'yarn') {
118
- console.info(chalk.gray(`${inputPackageManager} seed (only run once)`));
119
- console.info(chalk.gray(`${inputPackageManager} start:dev`));
120
- }
121
- else {
122
- console.info(chalk.gray(`${inputPackageManager} run seed (only run once)`));
123
- console.info(chalk.gray(`${inputPackageManager} run start:dev`));
124
- }
119
+ step++;
120
+ // Step: Start server (nest)
121
+ console.info(chalk.bold(`# ${step}. Start server (terminal 1):`));
122
+ console.info(chalk.gray(' cd server'));
123
+ if (inputPackageManager === 'yarn') {
124
+ console.info(chalk.gray(` ${inputPackageManager} seed (only run once)`));
125
+ console.info(chalk.gray(` ${inputPackageManager} start:dev`));
125
126
  }
126
127
  else {
127
- console.info(chalk.gray(`swag init -g main.go && go run main.go`));
128
+ console.info(chalk.gray(` ${inputPackageManager} run seed (only run once)`));
129
+ console.info(chalk.gray(` ${inputPackageManager} run start:dev`));
128
130
  }
129
- if (options.find(option => option.name === 'with-web' && option.value === true)) {
130
- console.info();
131
- console.info('#Start web devServer (In anthor terminal): ');
132
- console.info(chalk.gray('cd web'));
133
- if (inputPackageManager === 'yarn') {
134
- console.info(chalk.gray(`${inputPackageManager} dev`));
135
- }
136
- else {
137
- console.info(chalk.gray(`${inputPackageManager} run dev`));
138
- }
131
+ console.info();
132
+ step++;
133
+ // Step: Start web (always, since web is now mandatory)
134
+ console.info(chalk.bold(`# ${step}. Start web (terminal 2):`));
135
+ console.info(chalk.gray(' cd web'));
136
+ if (inputPackageManager === 'yarn') {
137
+ console.info(chalk.gray(` ${inputPackageManager} dev`));
138
+ }
139
+ else {
140
+ console.info(chalk.gray(` ${inputPackageManager} run dev`));
139
141
  }
140
142
  printCollective();
141
143
  }
@@ -161,24 +163,23 @@ const askForMissingInformation = (inputs, options) => __awaiter(void 0, void 0,
161
163
  const answers = yield prompt(questions);
162
164
  replaceInputMissingInformation(inputs, answers);
163
165
  }
164
- // 默认使用 gadmin2-game-angle-demo 模板、nest + react(antd) 技术栈
165
- replaceInputMissingInformation(options, { 'template': commands_1.Template.GameAngle });
166
- replaceInputMissingInformation(options, { 'server-framework': 'nest' });
167
- if (getOptionByName(options, 'with-web') === undefined) {
168
- const answers = yield askForWithWeb();
169
- answers['with-web'] = answers['with-web'] === 'yes';
170
- replaceInputMissingInformation(options, answers);
171
- }
166
+ // 默认使用 gadmin2-game-angle-demo 模板;server-framework new.command.ts 固定为 'nest'
167
+ replaceInputMissingInformation(options, { template: commands_1.Template.GameAngle });
168
+ // 收集环境配置(支持空字符串跳过)
169
+ yield promptIfMissing(options, 'vite-agent-id', 'VITE_AGENT_ID (knot Agent ID, leave blank to skip):');
170
+ yield promptIfMissing(options, 'taihu-app-token', 'TAIHU_APP_TOKEN (太湖应用 ID, leave blank to skip):');
171
+ yield promptIfMissing(options, 'database-url', 'DATABASE_URL (postgresql://user:pwd@host:port/db?schema=public, leave blank to skip):');
172
172
  });
173
- const askForWithWeb = () => __awaiter(void 0, void 0, void 0, function* () {
174
- const questions = [
175
- (0, questions_1.generateSelect)('with-web')(ui_1.MESSAGES.WITH_WEB_QUESTION)([
176
- commands_1.WithWeb.Yes,
177
- commands_1.WithWeb.No,
178
- ]),
179
- ];
173
+ /**
174
+ * 仅当 CLI 未提供该 option 时弹出 inquirer 提问。
175
+ * 用户直接回车则填入空字符串,后续逻辑会把空串视为"跳过"。
176
+ */
177
+ const promptIfMissing = (options, name, message) => __awaiter(void 0, void 0, void 0, function* () {
178
+ if (getOptionByName(options, name) !== undefined)
179
+ return;
180
180
  const prompt = inquirer.createPromptModule();
181
- return yield prompt(questions);
181
+ const answers = yield prompt([(0, questions_1.generateInput)(name, message)('')]);
182
+ replaceInputMissingInformation(options, answers);
182
183
  });
183
184
  const replaceInputMissingInformation = (inputs, answers) => {
184
185
  return inputs.map((input) => (input.value =
@@ -194,12 +195,57 @@ const generateApplicationFiles = (args, options) => __awaiter(void 0, void 0, vo
194
195
  const mapSchematicOptions = (options) => {
195
196
  return options.reduce((schematicOptions, option) => {
196
197
  if (option.name !== 'skip-install' &&
197
- option.value !== 'package-manager') {
198
+ option.value !== 'package-manager' &&
199
+ option.name !== 'vite-agent-id' &&
200
+ option.name !== 'taihu-app-token' &&
201
+ option.name !== 'database-url') {
198
202
  schematicOptions.push(new schematics_1.SchematicOption(option.name, option.value));
199
203
  }
200
204
  return schematicOptions;
201
205
  }, []);
202
206
  };
207
+ /**
208
+ * 在项目生成完毕后,把用户提供的环境配置写入对应文件。
209
+ * - VITE_AGENT_ID → web/.env.local (始终创建,含注释模板)
210
+ * - DATABASE_URL → server/.env.local (始终创建,含注释模板)
211
+ * - TAIHU_APP_TOKEN→ server/.env (仅当用户提供时才正则替换)
212
+ */
213
+ const applyEnvConfig = (projectDirectory, options) => __awaiter(void 0, void 0, void 0, function* () {
214
+ const viteAgentId = getOptionByName(options, 'vite-agent-id') || '';
215
+ const taihuToken = getOptionByName(options, 'taihu-app-token') || '';
216
+ const databaseUrl = getOptionByName(options, 'database-url') || '';
217
+ const writeFile = (0, util_1.promisify)(fs.writeFile);
218
+ const readFile = (0, util_1.promisify)(fs.readFile);
219
+ // (1) web/.env.local
220
+ const webEnvLocal = (0, path_1.join)(process.cwd(), projectDirectory, 'web', '.env.local');
221
+ const webContent = [
222
+ '# Local overrides for web/.env (do not commit)',
223
+ '# knot Agent ID — see https://knot.woa.com',
224
+ viteAgentId ? `VITE_AGENT_ID=${viteAgentId}` : '# VITE_AGENT_ID=',
225
+ ].join('\n') + '\n';
226
+ if (fs.existsSync((0, path_1.join)(process.cwd(), projectDirectory, 'web'))) {
227
+ yield writeFile(webEnvLocal, webContent);
228
+ }
229
+ // (2) server/.env.local
230
+ const serverEnvLocal = (0, path_1.join)(process.cwd(), projectDirectory, 'server', '.env.local');
231
+ const serverContent = [
232
+ '# Local overrides for server/.env (do not commit)',
233
+ '# PostgreSQL: postgresql://user:password@ip:port/db_name?schema=public',
234
+ databaseUrl ? `DATABASE_URL=${databaseUrl}` : '# DATABASE_URL=',
235
+ ].join('\n') + '\n';
236
+ if (fs.existsSync((0, path_1.join)(process.cwd(), projectDirectory, 'server'))) {
237
+ yield writeFile(serverEnvLocal, serverContent);
238
+ }
239
+ // (3) server/.env — sed 替换 TAIHU_APP_TOKEN=token 那行的 token
240
+ if (taihuToken) {
241
+ const serverEnv = (0, path_1.join)(process.cwd(), projectDirectory, 'server', '.env');
242
+ if (fs.existsSync(serverEnv)) {
243
+ const original = yield readFile(serverEnv, 'utf8');
244
+ const replaced = original.replace(/^TAIHU_APP_TOKEN=.*$/m, `TAIHU_APP_TOKEN=${taihuToken}`);
245
+ yield writeFile(serverEnv, replaced);
246
+ }
247
+ }
248
+ });
203
249
  const installPackages = (options, dryRunMode, installDirectory) => __awaiter(void 0, void 0, void 0, function* () {
204
250
  const inputPackageManager = options.find((option) => option.name === 'package-manager').value;
205
251
  let packageManager;
@@ -277,7 +323,7 @@ const printCollective = () => {
277
323
  };
278
324
  const print = (color = null) => (str = '') => {
279
325
  const terminalCols = (0, exports.retrieveCols)();
280
- const strLength = str.replace(/\u001b\[[0-9]{2}m/g, '').length;
326
+ const strLength = str.replace(/\[[0-9]{2}m/g, '').length;
281
327
  const leftPaddingLength = Math.floor((terminalCols - strLength) / 2);
282
328
  const leftPadding = ' '.repeat(Math.max(leftPaddingLength, 0));
283
329
  if (color) {
@@ -638,6 +638,18 @@ class UpdateAction extends abstract_action_1.AbstractAction {
638
638
  console.error(chalk.red(`Unknown subcommand "${subcommand}". Expected: (none) | diff`));
639
639
  process.exit(1);
640
640
  }
641
+ // Guard: refuse to run anywhere except a gadmin2 instance project root.
642
+ // The canonical marker is server/gadmin-cli.json (created by `gadmin2 n`).
643
+ // We check this up-front so commands like `gadmin2 update --no-template`
644
+ // can't silently sync .claude into an unrelated directory.
645
+ const instanceMarker = path.join(process.cwd(), 'server', 'gadmin-cli.json');
646
+ if (!fs.existsSync(instanceMarker)) {
647
+ console.error(chalk.red('\nNot at a gadmin2 instance project root.\n' +
648
+ ` Expected to find: server/gadmin-cli.json\n` +
649
+ ` Current directory: ${process.cwd()}\n\n` +
650
+ 'Run this command from the project root created by `gadmin2 n <name>`.\n'));
651
+ process.exit(1);
652
+ }
641
653
  // Pre-flight version check (B): silent network call to npm registry,
642
654
  // hard 1.5s timeout, never blocks. Prints a yellow banner if outdated.
643
655
  const versionCheckEnabled = ((_b = options.find((o) => o.name === 'version-check')) === null || _b === void 0 ? void 0 : _b.value) !==
@@ -12,14 +12,9 @@ export declare enum TransferProtocol {
12
12
  GraphQL = "graphQL"
13
13
  }
14
14
  export declare enum ServerFramework {
15
- Nest = "nest",
16
- Iris = "iris"
15
+ Nest = "nest"
17
16
  }
18
17
  export declare enum UiFramework {
19
18
  Tea = "tea",
20
19
  Antd = "antd"
21
20
  }
22
- export declare enum WithWeb {
23
- Yes = "yes",
24
- No = "no"
25
- }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WithWeb = exports.UiFramework = exports.ServerFramework = exports.TransferProtocol = exports.Template = void 0;
3
+ exports.UiFramework = exports.ServerFramework = exports.TransferProtocol = exports.Template = void 0;
4
4
  var Template;
5
5
  (function (Template) {
6
6
  Template["GameAngle"] = "gadmin2-game-angle-demo";
@@ -14,15 +14,9 @@ var TransferProtocol;
14
14
  var ServerFramework;
15
15
  (function (ServerFramework) {
16
16
  ServerFramework["Nest"] = "nest";
17
- ServerFramework["Iris"] = "iris";
18
17
  })(ServerFramework = exports.ServerFramework || (exports.ServerFramework = {}));
19
18
  var UiFramework;
20
19
  (function (UiFramework) {
21
20
  UiFramework["Tea"] = "tea";
22
21
  UiFramework["Antd"] = "antd";
23
22
  })(UiFramework = exports.UiFramework || (exports.UiFramework = {}));
24
- var WithWeb;
25
- (function (WithWeb) {
26
- WithWeb["Yes"] = "yes";
27
- WithWeb["No"] = "no";
28
- })(WithWeb = exports.WithWeb || (exports.WithWeb = {}));
@@ -21,11 +21,13 @@ class NewCommand extends abstract_command_1.AbstractCommand {
21
21
  .option('--directory [directory]', 'Specify the destination directory')
22
22
  .option('-d, --dry-run', 'Report actions that would be performed without writing out results.', false)
23
23
  .option('-g, --skip-git', 'Skip git repository initialization.', false)
24
- .option('-w, --with-web', 'Need generate frontend web pages.')
25
24
  .option('-s, --skip-install', 'Skip package installation.', false)
26
25
  .option('-p, --package-manager [package-manager]', 'Specify package manager.')
27
26
  .option('-c, --collection [collectionName]', 'Schematics collection to use', schematics_1.Collection.GADMIN_GENERATOR)
28
27
  .option('--strict', 'Enables strict mode in TypeScript.', false)
28
+ .option('--vite-agent-id [id]', 'Set VITE_AGENT_ID (knot Agent ID), written to web/.env.local')
29
+ .option('--taihu-app-token [token]', 'Set TAIHU_APP_TOKEN, replaced into server/.env')
30
+ .option('--database-url [url]', 'Set DATABASE_URL (PostgreSQL connection string), written to server/.env.local')
29
31
  .action((name, command) => __awaiter(this, void 0, void 0, function* () {
30
32
  const options = [];
31
33
  options.push({ name: 'directory', value: command.directory });
@@ -40,9 +42,12 @@ class NewCommand extends abstract_command_1.AbstractCommand {
40
42
  });
41
43
  options.push({ name: 'collection', value: command.collection });
42
44
  options.push({ name: 'transfer-protocol', value: 'restful' });
43
- options.push({ name: 'server-framework', value: undefined });
44
- options.push({ name: 'with-web', value: command.withWeb });
45
+ options.push({ name: 'server-framework', value: 'nest' });
46
+ options.push({ name: 'with-web', value: true });
45
47
  options.push({ name: 'ui-framework', value: 'antd' });
48
+ options.push({ name: 'vite-agent-id', value: command.viteAgentId });
49
+ options.push({ name: 'taihu-app-token', value: command.taihuAppToken });
50
+ options.push({ name: 'database-url', value: command.databaseUrl });
46
51
  const inputs = [];
47
52
  inputs.push({ name: 'name', value: name });
48
53
  yield this.action.handle(inputs, options);
@@ -19,8 +19,6 @@ export declare const MESSAGES: {
19
19
  LIBRARY_INSTALLATION_FAILED_NO_LIBRARY: string;
20
20
  LIBRARY_INSTALLATION_STARTS: string;
21
21
  TEMPLATE_QUESTION: string;
22
- WITH_WEB_QUESTION: string;
23
22
  TRANSFER_PROTOCOL_QUESTION: string;
24
- SERVER_FRAMEWORK_QUESTION: string;
25
23
  UI_FRAMEWORK_QUESTION: string;
26
24
  };
@@ -27,8 +27,6 @@ exports.MESSAGES = {
27
27
  LIBRARY_INSTALLATION_FAILED_NO_LIBRARY: 'No library found.',
28
28
  LIBRARY_INSTALLATION_STARTS: 'Starting library setup...',
29
29
  TEMPLATE_QUESTION: 'Which template would you like to use?',
30
- WITH_WEB_QUESTION: 'Need generate frontend web pages?',
31
30
  TRANSFER_PROTOCOL_QUESTION: 'Transfer protocol to use (Restful or GraphQL)?',
32
- SERVER_FRAMEWORK_QUESTION: 'Server framework to use (Nest or Iris)?',
33
31
  UI_FRAMEWORK_QUESTION: 'UI framework to use (Antd or Tea)?',
34
32
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gadmin2n/cli",
3
- "version": "0.0.104",
3
+ "version": "0.0.106",
4
4
  "description": "Gadmin - modern, fast, powerful node.js web framework (@cli)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -47,7 +47,7 @@
47
47
  "@angular-devkit/core": "13.3.2",
48
48
  "@angular-devkit/schematics": "13.3.2",
49
49
  "@angular-devkit/schematics-cli": "13.3.2",
50
- "@gadmin2n/schematics": "^0.0.86",
50
+ "@gadmin2n/schematics": "^0.0.87",
51
51
  "abc": "^0.6.1",
52
52
  "chalk": "3.0.0",
53
53
  "chokidar": "3.5.3",