@akanjs/devkit 0.0.138 → 0.0.140

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.
@@ -36,8 +36,8 @@ var import_prompts = require("@inquirer/prompts");
36
36
  var import_messages = require("@langchain/core/messages");
37
37
  var import_openai = require("@langchain/openai");
38
38
  var import_chalk = __toESM(require("chalk"));
39
- var import_ora = __toESM(require("ora"));
40
39
  var import_auth = require("./auth");
40
+ var import_spinner = require("./spinner");
41
41
  const MAX_ASK_TRY = 300;
42
42
  const supportedLlmModels = ["deepseek-chat", "deepseek-reasoner"];
43
43
  class AiSession {
@@ -45,8 +45,11 @@ class AiSession {
45
45
  static async init({ temperature = 0.7, useExisting = true } = {}) {
46
46
  if (useExisting) {
47
47
  const llmConfig2 = this.getLlmConfig();
48
- if (llmConfig2)
49
- return this.#setChatModel(llmConfig2.model, llmConfig2.apiKey);
48
+ if (llmConfig2) {
49
+ this.#setChatModel(llmConfig2.model, llmConfig2.apiKey);
50
+ import_common.Logger.rawLog(import_chalk.default.dim(`\u{1F916}akan editor uses existing LLM config (${llmConfig2.model})`));
51
+ return this;
52
+ }
50
53
  }
51
54
  const llmConfig = await this.#requestLlmConfig();
52
55
  const { model, apiKey } = llmConfig;
@@ -78,6 +81,7 @@ class AiSession {
78
81
  return { model, apiKey };
79
82
  }
80
83
  static async #validateApiKey(modelName, apiKey) {
84
+ const spinner = new import_spinner.Spinner("Validating LLM API key...", { prefix: `\u{1F916}akan-editor` }).start();
81
85
  const chat = new import_openai.ChatOpenAI({
82
86
  modelName,
83
87
  temperature: 0,
@@ -85,9 +89,10 @@ class AiSession {
85
89
  });
86
90
  try {
87
91
  await chat.invoke("Hi, and just say 'ok'");
92
+ spinner.succeed("LLM API key is valid");
88
93
  return true;
89
94
  } catch (error) {
90
- import_common.Logger.rawLog(
95
+ spinner.fail(
91
96
  import_chalk.default.red(
92
97
  `LLM API key is invalid. Please check your API key and try again. You can set it again by running "akan set-llm" or reset by running "akan reset-llm"`
93
98
  )
@@ -108,14 +113,16 @@ class AiSession {
108
113
  await AiSession.init();
109
114
  if (!AiSession.#chat)
110
115
  throw new Error("Failed to initialize the AI session");
111
- const loader = (0, import_ora.default)(`${AiSession.#chat.model} is thinking...`).start();
116
+ const loader = new import_spinner.Spinner(`${AiSession.#chat.model} is thinking...`, {
117
+ prefix: `\u{1F916}akan-editor`
118
+ }).start();
112
119
  try {
113
120
  const humanMessage = new import_messages.HumanMessage(question);
114
121
  this.messageHistory.push(humanMessage);
115
122
  const stream = await AiSession.#chat.stream(this.messageHistory);
116
123
  let fullResponse = "", tokenIdx = 0;
117
124
  for await (const chunk of stream) {
118
- if (loader.isSpinning)
125
+ if (loader.isSpinning() && chunk.content.length)
119
126
  loader.succeed(`${AiSession.#chat.model} responded`);
120
127
  const content = chunk.content;
121
128
  if (typeof content === "string") {
@@ -18,6 +18,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var argMeta_exports = {};
19
19
  __export(argMeta_exports, {
20
20
  App: () => App,
21
+ Argument: () => Argument,
21
22
  Exec: () => Exec,
22
23
  Lib: () => Lib,
23
24
  Option: () => Option,
@@ -30,7 +31,7 @@ __export(argMeta_exports, {
30
31
  });
31
32
  module.exports = __toCommonJS(argMeta_exports);
32
33
  var import_reflect_metadata = require("reflect-metadata");
33
- const argTypes = ["Option"];
34
+ const argTypes = ["Argument", "Option"];
34
35
  const internalArgTypes = ["Workspace", "App", "Lib", "Sys", "Pkg", "Exec"];
35
36
  const getArgMetas = (command, key) => {
36
37
  const allArgMetas = getArgMetasOnPrototype(command.prototype, key);
@@ -51,6 +52,7 @@ const getArg = (type) => function(name, argsOption = {}) {
51
52
  setArgMetasOnPrototype(prototype, key, argMetas);
52
53
  };
53
54
  };
55
+ const Argument = getArg("Argument");
54
56
  const Option = getArg("Option");
55
57
  const createArgMetaDecorator = (type) => {
56
58
  return function(option = {}) {
@@ -70,6 +72,7 @@ const Workspace = createArgMetaDecorator("Workspace");
70
72
  // Annotate the CommonJS export names for ESM import in node:
71
73
  0 && (module.exports = {
72
74
  App,
75
+ Argument,
73
76
  Exec,
74
77
  Lib,
75
78
  Option,
@@ -55,6 +55,16 @@ const handleOption = (programCommand, argMeta) => {
55
55
  );
56
56
  return programCommand;
57
57
  };
58
+ const handleArgument = (programCommand, argMeta) => {
59
+ const kebabName = camelToKebabCase(argMeta.name);
60
+ if ((argMeta.argsOption.type ?? "string") !== "string")
61
+ throw new Error(`Argument type must be string: ${argMeta.name}`);
62
+ programCommand.argument(
63
+ `[${kebabName}]`,
64
+ `${argMeta.argsOption.desc}${argMeta.argsOption.example ? ` (example: ${argMeta.argsOption.example})` : ""}`
65
+ );
66
+ return programCommand;
67
+ };
58
68
  const convertOptionValue = (value, type) => {
59
69
  if (type === "string")
60
70
  return value;
@@ -91,7 +101,21 @@ const getOptionValue = async (argMeta, opt) => {
91
101
  return convertOptionValue(await (0, import_prompts.input)({ message }), type ?? "string");
92
102
  }
93
103
  };
94
- const getArgumentValue = async (argMeta, value, workspace) => {
104
+ const getArgumentValue = async (argMeta, value) => {
105
+ const {
106
+ name,
107
+ argsOption: { default: defaultValue, type, desc, nullable, example, ask }
108
+ } = argMeta;
109
+ if (value !== void 0)
110
+ return value;
111
+ else if (defaultValue !== void 0)
112
+ return defaultValue;
113
+ else if (nullable)
114
+ return null;
115
+ const message = ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
116
+ return await (0, import_prompts.input)({ message });
117
+ };
118
+ const getInternalArgumentValue = async (argMeta, value, workspace) => {
95
119
  if (argMeta.type === "Workspace")
96
120
  return workspace;
97
121
  const sysType = argMeta.type.toLowerCase();
@@ -156,6 +180,7 @@ const getArgumentValue = async (argMeta, value, workspace) => {
156
180
  const runCommands = async (...commands) => {
157
181
  process.on("unhandledRejection", (error) => {
158
182
  console.error(import_chalk.default.red("[fatal]"), error);
183
+ process.exit(1);
159
184
  });
160
185
  const hasPackageJson = import_fs.default.existsSync(`${__dirname}/../package.json`);
161
186
  const version = hasPackageJson ? JSON.parse(import_fs.default.readFileSync(`${__dirname}/../package.json`, "utf8")).version : "0.0.1";
@@ -176,6 +201,8 @@ const runCommands = async (...commands) => {
176
201
  for (const argMeta of allArgMetas) {
177
202
  if (argMeta.type === "Option")
178
203
  programCommand = handleOption(programCommand, argMeta);
204
+ else if (argMeta.type === "Argument")
205
+ programCommand = handleArgument(programCommand, argMeta);
179
206
  else if (argMeta.type === "Workspace")
180
207
  continue;
181
208
  const sysType = argMeta.type.toLowerCase();
@@ -184,7 +211,9 @@ const runCommands = async (...commands) => {
184
211
  `${sysType} in this workspace ${sysType}s/<${sysType}Name>`
185
212
  );
186
213
  }
214
+ programCommand = programCommand.option(`-v, --verbose [boolean]`, `verbose output`);
187
215
  programCommand.action(async (...args) => {
216
+ import_common.Logger.rawLog();
188
217
  const cmdArgs = args.slice(0, args.length - 2);
189
218
  const opt = args[args.length - 2];
190
219
  const commandArgs = [];
@@ -192,14 +221,23 @@ const runCommands = async (...commands) => {
192
221
  for (const argMeta of allArgMetas) {
193
222
  if (argMeta.type === "Option")
194
223
  commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt);
224
+ else if (argMeta.type === "Argument")
225
+ commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx]);
195
226
  else
196
- commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
227
+ commandArgs[argMeta.idx] = await getInternalArgumentValue(
228
+ argMeta,
229
+ cmdArgs[argMeta.idx],
230
+ workspace
231
+ );
197
232
  if (commandArgs[argMeta.idx] instanceof import_executors.AppExecutor)
198
233
  process.env.NEXT_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
234
+ if (opt.verbose)
235
+ import_executors.Executor.setVerbose(true);
199
236
  }
200
237
  const cmd = new command();
201
238
  try {
202
239
  await cmd[targetMeta.key](...commandArgs);
240
+ import_common.Logger.rawLog();
203
241
  } catch (e) {
204
242
  const errMsg = e instanceof Error ? e.message : typeof e === "string" ? e : JSON.stringify(e);
205
243
  import_common.Logger.error(`Command Error: ${import_chalk.default.red(errMsg)}`);
@@ -56,6 +56,10 @@ const execEmoji = {
56
56
  // for sys executor
57
57
  };
58
58
  class Executor {
59
+ static verbose = false;
60
+ static setVerbose(verbose) {
61
+ Executor.verbose = verbose;
62
+ }
59
63
  name;
60
64
  logger;
61
65
  cwdPath;
@@ -67,13 +71,20 @@ class Executor {
67
71
  if (!import_fs.default.existsSync(cwdPath))
68
72
  import_fs.default.mkdirSync(cwdPath, { recursive: true });
69
73
  }
74
+ #stdout(data) {
75
+ if (Executor.verbose)
76
+ import_common.Logger.raw(import_chalk.default.dim(data.toString()));
77
+ }
78
+ #stderr(data) {
79
+ import_common.Logger.raw(import_chalk.default.red(data.toString()));
80
+ }
70
81
  exec(command, options = {}) {
71
82
  const proc = (0, import_child_process.exec)(command, { cwd: this.cwdPath, ...options });
72
83
  proc.stdout?.on("data", (data) => {
73
- import_common.Logger.raw(import_chalk.default.dim(data.toString()));
84
+ this.#stdout(data);
74
85
  });
75
86
  proc.stderr?.on("data", (data) => {
76
- import_common.Logger.raw(import_chalk.default.dim(data.toString()));
87
+ this.#stdout(data);
77
88
  });
78
89
  return new Promise((resolve, reject) => {
79
90
  proc.on("exit", (code, signal) => {
@@ -85,12 +96,16 @@ class Executor {
85
96
  });
86
97
  }
87
98
  spawn(command, args = [], options = {}) {
88
- const proc = (0, import_child_process.spawn)(command, args, { cwd: this.cwdPath, stdio: "inherit", ...options });
99
+ const proc = (0, import_child_process.spawn)(command, args, {
100
+ cwd: this.cwdPath,
101
+ stdio: "inherit",
102
+ ...options
103
+ });
89
104
  proc.stdout?.on("data", (data) => {
90
- import_common.Logger.raw(import_chalk.default.dim(data.toString()));
105
+ this.#stdout(data);
91
106
  });
92
107
  proc.stderr?.on("data", (data) => {
93
- import_common.Logger.raw(import_chalk.default.dim(data.toString()));
108
+ this.#stderr(data);
94
109
  });
95
110
  return new Promise((resolve, reject) => {
96
111
  proc.on("exit", (code, signal) => {
@@ -108,10 +123,10 @@ class Executor {
108
123
  ...options
109
124
  });
110
125
  proc.stdout?.on("data", (data) => {
111
- import_common.Logger.raw(data.toString());
126
+ this.#stdout(data);
112
127
  });
113
128
  proc.stderr?.on("data", (data) => {
114
- import_common.Logger.raw(data.toString());
129
+ this.#stderr(data);
115
130
  });
116
131
  return new Promise((resolve, reject) => {
117
132
  proc.on("exit", (code, signal) => {
@@ -136,6 +151,20 @@ class Executor {
136
151
  const readPath = this.#getPath(filePath);
137
152
  return import_fs.default.existsSync(readPath);
138
153
  }
154
+ remove(filePath) {
155
+ const readPath = this.#getPath(filePath);
156
+ if (import_fs.default.existsSync(readPath))
157
+ import_fs.default.unlinkSync(readPath);
158
+ this.logger.verbose(`Remove file ${readPath}`);
159
+ return this;
160
+ }
161
+ removeDir(dirPath) {
162
+ const readPath = this.#getPath(dirPath);
163
+ if (import_fs.default.existsSync(readPath))
164
+ import_fs.default.rmSync(readPath, { recursive: true });
165
+ this.logger.verbose(`Remove directory ${readPath}`);
166
+ return this;
167
+ }
139
168
  writeFile(filePath, content, { overwrite = true } = {}) {
140
169
  const writePath = this.#getPath(filePath);
141
170
  const dir = import_path.default.dirname(writePath);
@@ -186,8 +215,8 @@ class Executor {
186
215
  this.logger.verbose(msg);
187
216
  return this;
188
217
  }
189
- spinning(msg, { prefix = `${this.emoji}${this.name} -`, indent = 0 } = {}) {
190
- return new import_spinner.Spinner(msg, { prefix, indent }).start();
218
+ spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
219
+ return new import_spinner.Spinner(msg, { prefix, indent, enableSpin }).start();
191
220
  }
192
221
  getTsConfig(pathname = "tsconfig.json") {
193
222
  const tsconfig = this.readJson(pathname);
@@ -356,23 +385,41 @@ class WorkspaceExecutor extends Executor {
356
385
  }
357
386
  setTsPaths(type, name) {
358
387
  const rootTsConfig = this.readJson("tsconfig.json");
359
- if (type === "lib")
388
+ if (type === "lib" || type === "pkg")
360
389
  rootTsConfig.compilerOptions.paths[`@${name}`] = [`${type}s/${name}/index.ts`];
361
390
  rootTsConfig.compilerOptions.paths[`@${name}/*`] = [`${type}s/${name}/*`];
391
+ if (rootTsConfig.references) {
392
+ if (!rootTsConfig.references.some((ref) => ref.path === `./${type}s/${name}/tsconfig.json`))
393
+ rootTsConfig.references.push({ path: `./${type}s/${name}/tsconfig.json` });
394
+ }
395
+ this.writeJson("tsconfig.json", rootTsConfig);
396
+ return this;
397
+ }
398
+ unsetTsPaths(type, name) {
399
+ const rootTsConfig = this.readJson("tsconfig.json");
400
+ const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths).filter((key) => !key.startsWith(`@${name}`));
401
+ rootTsConfig.compilerOptions.paths = Object.fromEntries(
402
+ filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths[key]])
403
+ );
404
+ if (rootTsConfig.references) {
405
+ rootTsConfig.references = rootTsConfig.references.filter(
406
+ (ref) => !ref.path.startsWith(`./${type}s/${name}`)
407
+ );
408
+ }
362
409
  this.writeJson("tsconfig.json", rootTsConfig);
363
410
  return this;
364
411
  }
365
412
  async getDirInModule(basePath, name) {
366
- const AVOID_DIRS = ["__lib", "__scalar", `_${name}`];
413
+ const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
367
414
  const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
368
415
  const dirs = await import_promises.default.readdir(dirname);
369
416
  await Promise.all(
370
417
  dirs.map(async (dir) => {
371
- if (AVOID_DIRS.includes(dir))
418
+ if (dir.includes("_") || AVOID_DIRS.includes(dir))
372
419
  return;
373
420
  const dirPath = import_path.default.join(dirname, dir);
374
421
  if (import_fs.default.lstatSync(dirPath).isDirectory()) {
375
- results.push(`${name}/${prefix}${dir}`);
422
+ results.push(`${prefix}${dir}`);
376
423
  if (maxDepth > 0)
377
424
  await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
378
425
  }
@@ -419,6 +466,22 @@ class WorkspaceExecutor extends Executor {
419
466
  ];
420
467
  return scalarConstantExampleFiles;
421
468
  }
469
+ async getConstantFiles() {
470
+ const [appNames, libNames] = await this.getSyss();
471
+ const moduleConstantExampleFiles = [
472
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getConstantFiles()))).flat(),
473
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getConstantFiles()))).flat()
474
+ ];
475
+ return moduleConstantExampleFiles;
476
+ }
477
+ async getDictionaryFiles() {
478
+ const [appNames, libNames] = await this.getSyss();
479
+ const moduleDictionaryExampleFiles = [
480
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getDictionaryFiles()))).flat(),
481
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getDictionaryFiles()))).flat()
482
+ ];
483
+ return moduleDictionaryExampleFiles;
484
+ }
422
485
  async getViewFiles() {
423
486
  const [appNames, libNames] = await this.getSyss();
424
487
  const viewExampleFiles = [
@@ -648,9 +711,15 @@ class SysExecutor extends Executor {
648
711
  }
649
712
  async getScalarDictionaryFiles() {
650
713
  const scalarModules = await this.getScalarModules();
651
- return scalarModules.map(
652
- (scalarModule) => this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.dictionary.ts`)
653
- );
714
+ return scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`));
715
+ }
716
+ async getConstantFiles() {
717
+ const modules = await this.getModules();
718
+ return modules.map((module2) => this.getLocalFile(`lib/${module2}/${module2}.constant.ts`));
719
+ }
720
+ async getDictionaryFiles() {
721
+ const modules = await this.getModules();
722
+ return modules.map((module2) => this.getLocalFile(`lib/${module2}/${module2}.dictionary.ts`));
654
723
  }
655
724
  setTsPaths() {
656
725
  this.workspace.setTsPaths(this.type, this.name);
@@ -669,6 +738,9 @@ class AppExecutor extends SysExecutor {
669
738
  return new AppExecutor({ workspace: executor, name });
670
739
  return new AppExecutor({ workspace: executor.workspace, name });
671
740
  }
741
+ getEnv() {
742
+ return this.workspace.getBaseDevEnv().env;
743
+ }
672
744
  async getConfig(command) {
673
745
  return await (0, import_config.getAppConfig)(this.cwdPath, {
674
746
  ...this.workspace.getBaseDevEnv(),
@@ -32,27 +32,49 @@ __export(spinner_exports, {
32
32
  module.exports = __toCommonJS(spinner_exports);
33
33
  var import_ora = __toESM(require("ora"));
34
34
  class Spinner {
35
+ static padding = 12;
35
36
  spinner;
36
37
  stopWatch;
37
38
  startAt;
39
+ prefix;
38
40
  message;
39
- constructor(message, { prefix = "", indent = 0 } = {}) {
41
+ enableSpin;
42
+ constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
43
+ Spinner.padding = Math.max(Spinner.padding, prefix.length);
44
+ this.prefix = prefix;
40
45
  this.message = message;
41
46
  this.spinner = (0, import_ora.default)(message);
42
- this.spinner.prefixText = prefix;
47
+ this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
43
48
  this.spinner.indent = indent;
49
+ this.enableSpin = enableSpin;
44
50
  }
45
51
  start() {
46
52
  this.startAt = /* @__PURE__ */ new Date();
47
- const spinner = this.spinner.start();
48
- this.stopWatch = setInterval(() => {
49
- spinner.text = `${this.message} (${this.#getElapsedTimeStr()})`;
50
- }, 1e3);
53
+ if (this.enableSpin) {
54
+ this.spinner.start();
55
+ this.stopWatch = setInterval(() => {
56
+ this.spinner.prefixText = this.prefix.padStart(Spinner.padding, " ");
57
+ this.spinner.text = `${this.message} (${this.#getElapsedTimeStr()})`;
58
+ }, 1e3);
59
+ } else
60
+ this.spinner.info();
51
61
  return this;
52
62
  }
53
63
  succeed(message) {
54
- clearInterval(this.stopWatch);
55
64
  this.spinner.succeed(`${message} (${this.#getElapsedTimeStr()})`);
65
+ this.#reset();
66
+ }
67
+ fail(message) {
68
+ this.spinner.fail(`${message} (${this.#getElapsedTimeStr()})`);
69
+ this.#reset();
70
+ }
71
+ isSpinning() {
72
+ return this.spinner.isSpinning;
73
+ }
74
+ #reset() {
75
+ if (this.stopWatch)
76
+ clearInterval(this.stopWatch);
77
+ this.stopWatch = null;
56
78
  }
57
79
  #getElapsedTimeStr() {
58
80
  const ms = (/* @__PURE__ */ new Date()).getTime() - this.startAt.getTime();
@@ -3,8 +3,8 @@ import { input, select } from "@inquirer/prompts";
3
3
  import { AIMessage, HumanMessage } from "@langchain/core/messages";
4
4
  import { ChatOpenAI } from "@langchain/openai";
5
5
  import chalk from "chalk";
6
- import ora from "ora";
7
6
  import { getAkanGlobalConfig, setAkanGlobalConfig } from "./auth";
7
+ import { Spinner } from "./spinner";
8
8
  const MAX_ASK_TRY = 300;
9
9
  const supportedLlmModels = ["deepseek-chat", "deepseek-reasoner"];
10
10
  class AiSession {
@@ -12,8 +12,11 @@ class AiSession {
12
12
  static async init({ temperature = 0.7, useExisting = true } = {}) {
13
13
  if (useExisting) {
14
14
  const llmConfig2 = this.getLlmConfig();
15
- if (llmConfig2)
16
- return this.#setChatModel(llmConfig2.model, llmConfig2.apiKey);
15
+ if (llmConfig2) {
16
+ this.#setChatModel(llmConfig2.model, llmConfig2.apiKey);
17
+ Logger.rawLog(chalk.dim(`\u{1F916}akan editor uses existing LLM config (${llmConfig2.model})`));
18
+ return this;
19
+ }
17
20
  }
18
21
  const llmConfig = await this.#requestLlmConfig();
19
22
  const { model, apiKey } = llmConfig;
@@ -45,6 +48,7 @@ class AiSession {
45
48
  return { model, apiKey };
46
49
  }
47
50
  static async #validateApiKey(modelName, apiKey) {
51
+ const spinner = new Spinner("Validating LLM API key...", { prefix: `\u{1F916}akan-editor` }).start();
48
52
  const chat = new ChatOpenAI({
49
53
  modelName,
50
54
  temperature: 0,
@@ -52,9 +56,10 @@ class AiSession {
52
56
  });
53
57
  try {
54
58
  await chat.invoke("Hi, and just say 'ok'");
59
+ spinner.succeed("LLM API key is valid");
55
60
  return true;
56
61
  } catch (error) {
57
- Logger.rawLog(
62
+ spinner.fail(
58
63
  chalk.red(
59
64
  `LLM API key is invalid. Please check your API key and try again. You can set it again by running "akan set-llm" or reset by running "akan reset-llm"`
60
65
  )
@@ -75,14 +80,16 @@ class AiSession {
75
80
  await AiSession.init();
76
81
  if (!AiSession.#chat)
77
82
  throw new Error("Failed to initialize the AI session");
78
- const loader = ora(`${AiSession.#chat.model} is thinking...`).start();
83
+ const loader = new Spinner(`${AiSession.#chat.model} is thinking...`, {
84
+ prefix: `\u{1F916}akan-editor`
85
+ }).start();
79
86
  try {
80
87
  const humanMessage = new HumanMessage(question);
81
88
  this.messageHistory.push(humanMessage);
82
89
  const stream = await AiSession.#chat.stream(this.messageHistory);
83
90
  let fullResponse = "", tokenIdx = 0;
84
91
  for await (const chunk of stream) {
85
- if (loader.isSpinning)
92
+ if (loader.isSpinning() && chunk.content.length)
86
93
  loader.succeed(`${AiSession.#chat.model} responded`);
87
94
  const content = chunk.content;
88
95
  if (typeof content === "string") {
@@ -1,5 +1,5 @@
1
1
  import "reflect-metadata";
2
- const argTypes = ["Option"];
2
+ const argTypes = ["Argument", "Option"];
3
3
  const internalArgTypes = ["Workspace", "App", "Lib", "Sys", "Pkg", "Exec"];
4
4
  const getArgMetas = (command, key) => {
5
5
  const allArgMetas = getArgMetasOnPrototype(command.prototype, key);
@@ -20,6 +20,7 @@ const getArg = (type) => function(name, argsOption = {}) {
20
20
  setArgMetasOnPrototype(prototype, key, argMetas);
21
21
  };
22
22
  };
23
+ const Argument = getArg("Argument");
23
24
  const Option = getArg("Option");
24
25
  const createArgMetaDecorator = (type) => {
25
26
  return function(option = {}) {
@@ -38,6 +39,7 @@ const Pkg = createArgMetaDecorator("Pkg");
38
39
  const Workspace = createArgMetaDecorator("Workspace");
39
40
  export {
40
41
  App,
42
+ Argument,
41
43
  Exec,
42
44
  Lib,
43
45
  Option,
@@ -3,7 +3,7 @@ import { confirm, input, select } from "@inquirer/prompts";
3
3
  import chalk from "chalk";
4
4
  import { program } from "commander";
5
5
  import fs from "fs";
6
- import { AppExecutor, LibExecutor, PkgExecutor, WorkspaceExecutor } from "../executors";
6
+ import { AppExecutor, Executor, LibExecutor, PkgExecutor, WorkspaceExecutor } from "../executors";
7
7
  import { getArgMetas } from "./argMeta";
8
8
  import { getTargetMetas } from "./targetMeta";
9
9
  const camelToKebabCase = (str) => str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -23,6 +23,16 @@ const handleOption = (programCommand, argMeta) => {
23
23
  );
24
24
  return programCommand;
25
25
  };
26
+ const handleArgument = (programCommand, argMeta) => {
27
+ const kebabName = camelToKebabCase(argMeta.name);
28
+ if ((argMeta.argsOption.type ?? "string") !== "string")
29
+ throw new Error(`Argument type must be string: ${argMeta.name}`);
30
+ programCommand.argument(
31
+ `[${kebabName}]`,
32
+ `${argMeta.argsOption.desc}${argMeta.argsOption.example ? ` (example: ${argMeta.argsOption.example})` : ""}`
33
+ );
34
+ return programCommand;
35
+ };
26
36
  const convertOptionValue = (value, type) => {
27
37
  if (type === "string")
28
38
  return value;
@@ -59,7 +69,21 @@ const getOptionValue = async (argMeta, opt) => {
59
69
  return convertOptionValue(await input({ message }), type ?? "string");
60
70
  }
61
71
  };
62
- const getArgumentValue = async (argMeta, value, workspace) => {
72
+ const getArgumentValue = async (argMeta, value) => {
73
+ const {
74
+ name,
75
+ argsOption: { default: defaultValue, type, desc, nullable, example, ask }
76
+ } = argMeta;
77
+ if (value !== void 0)
78
+ return value;
79
+ else if (defaultValue !== void 0)
80
+ return defaultValue;
81
+ else if (nullable)
82
+ return null;
83
+ const message = ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
84
+ return await input({ message });
85
+ };
86
+ const getInternalArgumentValue = async (argMeta, value, workspace) => {
63
87
  if (argMeta.type === "Workspace")
64
88
  return workspace;
65
89
  const sysType = argMeta.type.toLowerCase();
@@ -124,6 +148,7 @@ const getArgumentValue = async (argMeta, value, workspace) => {
124
148
  const runCommands = async (...commands) => {
125
149
  process.on("unhandledRejection", (error) => {
126
150
  console.error(chalk.red("[fatal]"), error);
151
+ process.exit(1);
127
152
  });
128
153
  const hasPackageJson = fs.existsSync(`${__dirname}/../package.json`);
129
154
  const version = hasPackageJson ? JSON.parse(fs.readFileSync(`${__dirname}/../package.json`, "utf8")).version : "0.0.1";
@@ -144,6 +169,8 @@ const runCommands = async (...commands) => {
144
169
  for (const argMeta of allArgMetas) {
145
170
  if (argMeta.type === "Option")
146
171
  programCommand = handleOption(programCommand, argMeta);
172
+ else if (argMeta.type === "Argument")
173
+ programCommand = handleArgument(programCommand, argMeta);
147
174
  else if (argMeta.type === "Workspace")
148
175
  continue;
149
176
  const sysType = argMeta.type.toLowerCase();
@@ -152,7 +179,9 @@ const runCommands = async (...commands) => {
152
179
  `${sysType} in this workspace ${sysType}s/<${sysType}Name>`
153
180
  );
154
181
  }
182
+ programCommand = programCommand.option(`-v, --verbose [boolean]`, `verbose output`);
155
183
  programCommand.action(async (...args) => {
184
+ Logger.rawLog();
156
185
  const cmdArgs = args.slice(0, args.length - 2);
157
186
  const opt = args[args.length - 2];
158
187
  const commandArgs = [];
@@ -160,14 +189,23 @@ const runCommands = async (...commands) => {
160
189
  for (const argMeta of allArgMetas) {
161
190
  if (argMeta.type === "Option")
162
191
  commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt);
192
+ else if (argMeta.type === "Argument")
193
+ commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx]);
163
194
  else
164
- commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
195
+ commandArgs[argMeta.idx] = await getInternalArgumentValue(
196
+ argMeta,
197
+ cmdArgs[argMeta.idx],
198
+ workspace
199
+ );
165
200
  if (commandArgs[argMeta.idx] instanceof AppExecutor)
166
201
  process.env.NEXT_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
202
+ if (opt.verbose)
203
+ Executor.setVerbose(true);
167
204
  }
168
205
  const cmd = new command();
169
206
  try {
170
207
  await cmd[targetMeta.key](...commandArgs);
208
+ Logger.rawLog();
171
209
  } catch (e) {
172
210
  const errMsg = e instanceof Error ? e.message : typeof e === "string" ? e : JSON.stringify(e);
173
211
  Logger.error(`Command Error: ${chalk.red(errMsg)}`);
@@ -22,6 +22,10 @@ const execEmoji = {
22
22
  // for sys executor
23
23
  };
24
24
  class Executor {
25
+ static verbose = false;
26
+ static setVerbose(verbose) {
27
+ Executor.verbose = verbose;
28
+ }
25
29
  name;
26
30
  logger;
27
31
  cwdPath;
@@ -33,13 +37,20 @@ class Executor {
33
37
  if (!fs.existsSync(cwdPath))
34
38
  fs.mkdirSync(cwdPath, { recursive: true });
35
39
  }
40
+ #stdout(data) {
41
+ if (Executor.verbose)
42
+ Logger.raw(chalk.dim(data.toString()));
43
+ }
44
+ #stderr(data) {
45
+ Logger.raw(chalk.red(data.toString()));
46
+ }
36
47
  exec(command, options = {}) {
37
48
  const proc = exec(command, { cwd: this.cwdPath, ...options });
38
49
  proc.stdout?.on("data", (data) => {
39
- Logger.raw(chalk.dim(data.toString()));
50
+ this.#stdout(data);
40
51
  });
41
52
  proc.stderr?.on("data", (data) => {
42
- Logger.raw(chalk.dim(data.toString()));
53
+ this.#stdout(data);
43
54
  });
44
55
  return new Promise((resolve, reject) => {
45
56
  proc.on("exit", (code, signal) => {
@@ -51,12 +62,16 @@ class Executor {
51
62
  });
52
63
  }
53
64
  spawn(command, args = [], options = {}) {
54
- const proc = spawn(command, args, { cwd: this.cwdPath, stdio: "inherit", ...options });
65
+ const proc = spawn(command, args, {
66
+ cwd: this.cwdPath,
67
+ stdio: "inherit",
68
+ ...options
69
+ });
55
70
  proc.stdout?.on("data", (data) => {
56
- Logger.raw(chalk.dim(data.toString()));
71
+ this.#stdout(data);
57
72
  });
58
73
  proc.stderr?.on("data", (data) => {
59
- Logger.raw(chalk.dim(data.toString()));
74
+ this.#stderr(data);
60
75
  });
61
76
  return new Promise((resolve, reject) => {
62
77
  proc.on("exit", (code, signal) => {
@@ -74,10 +89,10 @@ class Executor {
74
89
  ...options
75
90
  });
76
91
  proc.stdout?.on("data", (data) => {
77
- Logger.raw(data.toString());
92
+ this.#stdout(data);
78
93
  });
79
94
  proc.stderr?.on("data", (data) => {
80
- Logger.raw(data.toString());
95
+ this.#stderr(data);
81
96
  });
82
97
  return new Promise((resolve, reject) => {
83
98
  proc.on("exit", (code, signal) => {
@@ -102,6 +117,20 @@ class Executor {
102
117
  const readPath = this.#getPath(filePath);
103
118
  return fs.existsSync(readPath);
104
119
  }
120
+ remove(filePath) {
121
+ const readPath = this.#getPath(filePath);
122
+ if (fs.existsSync(readPath))
123
+ fs.unlinkSync(readPath);
124
+ this.logger.verbose(`Remove file ${readPath}`);
125
+ return this;
126
+ }
127
+ removeDir(dirPath) {
128
+ const readPath = this.#getPath(dirPath);
129
+ if (fs.existsSync(readPath))
130
+ fs.rmSync(readPath, { recursive: true });
131
+ this.logger.verbose(`Remove directory ${readPath}`);
132
+ return this;
133
+ }
105
134
  writeFile(filePath, content, { overwrite = true } = {}) {
106
135
  const writePath = this.#getPath(filePath);
107
136
  const dir = path.dirname(writePath);
@@ -152,8 +181,8 @@ class Executor {
152
181
  this.logger.verbose(msg);
153
182
  return this;
154
183
  }
155
- spinning(msg, { prefix = `${this.emoji}${this.name} -`, indent = 0 } = {}) {
156
- return new Spinner(msg, { prefix, indent }).start();
184
+ spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
185
+ return new Spinner(msg, { prefix, indent, enableSpin }).start();
157
186
  }
158
187
  getTsConfig(pathname = "tsconfig.json") {
159
188
  const tsconfig = this.readJson(pathname);
@@ -322,23 +351,41 @@ class WorkspaceExecutor extends Executor {
322
351
  }
323
352
  setTsPaths(type, name) {
324
353
  const rootTsConfig = this.readJson("tsconfig.json");
325
- if (type === "lib")
354
+ if (type === "lib" || type === "pkg")
326
355
  rootTsConfig.compilerOptions.paths[`@${name}`] = [`${type}s/${name}/index.ts`];
327
356
  rootTsConfig.compilerOptions.paths[`@${name}/*`] = [`${type}s/${name}/*`];
357
+ if (rootTsConfig.references) {
358
+ if (!rootTsConfig.references.some((ref) => ref.path === `./${type}s/${name}/tsconfig.json`))
359
+ rootTsConfig.references.push({ path: `./${type}s/${name}/tsconfig.json` });
360
+ }
361
+ this.writeJson("tsconfig.json", rootTsConfig);
362
+ return this;
363
+ }
364
+ unsetTsPaths(type, name) {
365
+ const rootTsConfig = this.readJson("tsconfig.json");
366
+ const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths).filter((key) => !key.startsWith(`@${name}`));
367
+ rootTsConfig.compilerOptions.paths = Object.fromEntries(
368
+ filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths[key]])
369
+ );
370
+ if (rootTsConfig.references) {
371
+ rootTsConfig.references = rootTsConfig.references.filter(
372
+ (ref) => !ref.path.startsWith(`./${type}s/${name}`)
373
+ );
374
+ }
328
375
  this.writeJson("tsconfig.json", rootTsConfig);
329
376
  return this;
330
377
  }
331
378
  async getDirInModule(basePath, name) {
332
- const AVOID_DIRS = ["__lib", "__scalar", `_${name}`];
379
+ const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
333
380
  const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
334
381
  const dirs = await fsPromise.readdir(dirname);
335
382
  await Promise.all(
336
383
  dirs.map(async (dir) => {
337
- if (AVOID_DIRS.includes(dir))
384
+ if (dir.includes("_") || AVOID_DIRS.includes(dir))
338
385
  return;
339
386
  const dirPath = path.join(dirname, dir);
340
387
  if (fs.lstatSync(dirPath).isDirectory()) {
341
- results.push(`${name}/${prefix}${dir}`);
388
+ results.push(`${prefix}${dir}`);
342
389
  if (maxDepth > 0)
343
390
  await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
344
391
  }
@@ -385,6 +432,22 @@ class WorkspaceExecutor extends Executor {
385
432
  ];
386
433
  return scalarConstantExampleFiles;
387
434
  }
435
+ async getConstantFiles() {
436
+ const [appNames, libNames] = await this.getSyss();
437
+ const moduleConstantExampleFiles = [
438
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getConstantFiles()))).flat(),
439
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getConstantFiles()))).flat()
440
+ ];
441
+ return moduleConstantExampleFiles;
442
+ }
443
+ async getDictionaryFiles() {
444
+ const [appNames, libNames] = await this.getSyss();
445
+ const moduleDictionaryExampleFiles = [
446
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getDictionaryFiles()))).flat(),
447
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getDictionaryFiles()))).flat()
448
+ ];
449
+ return moduleDictionaryExampleFiles;
450
+ }
388
451
  async getViewFiles() {
389
452
  const [appNames, libNames] = await this.getSyss();
390
453
  const viewExampleFiles = [
@@ -614,9 +677,15 @@ class SysExecutor extends Executor {
614
677
  }
615
678
  async getScalarDictionaryFiles() {
616
679
  const scalarModules = await this.getScalarModules();
617
- return scalarModules.map(
618
- (scalarModule) => this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.dictionary.ts`)
619
- );
680
+ return scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`));
681
+ }
682
+ async getConstantFiles() {
683
+ const modules = await this.getModules();
684
+ return modules.map((module) => this.getLocalFile(`lib/${module}/${module}.constant.ts`));
685
+ }
686
+ async getDictionaryFiles() {
687
+ const modules = await this.getModules();
688
+ return modules.map((module) => this.getLocalFile(`lib/${module}/${module}.dictionary.ts`));
620
689
  }
621
690
  setTsPaths() {
622
691
  this.workspace.setTsPaths(this.type, this.name);
@@ -635,6 +704,9 @@ class AppExecutor extends SysExecutor {
635
704
  return new AppExecutor({ workspace: executor, name });
636
705
  return new AppExecutor({ workspace: executor.workspace, name });
637
706
  }
707
+ getEnv() {
708
+ return this.workspace.getBaseDevEnv().env;
709
+ }
638
710
  async getConfig(command) {
639
711
  return await getAppConfig(this.cwdPath, {
640
712
  ...this.workspace.getBaseDevEnv(),
@@ -1,26 +1,48 @@
1
1
  import ora from "ora";
2
2
  class Spinner {
3
+ static padding = 12;
3
4
  spinner;
4
5
  stopWatch;
5
6
  startAt;
7
+ prefix;
6
8
  message;
7
- constructor(message, { prefix = "", indent = 0 } = {}) {
9
+ enableSpin;
10
+ constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
11
+ Spinner.padding = Math.max(Spinner.padding, prefix.length);
12
+ this.prefix = prefix;
8
13
  this.message = message;
9
14
  this.spinner = ora(message);
10
- this.spinner.prefixText = prefix;
15
+ this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
11
16
  this.spinner.indent = indent;
17
+ this.enableSpin = enableSpin;
12
18
  }
13
19
  start() {
14
20
  this.startAt = /* @__PURE__ */ new Date();
15
- const spinner = this.spinner.start();
16
- this.stopWatch = setInterval(() => {
17
- spinner.text = `${this.message} (${this.#getElapsedTimeStr()})`;
18
- }, 1e3);
21
+ if (this.enableSpin) {
22
+ this.spinner.start();
23
+ this.stopWatch = setInterval(() => {
24
+ this.spinner.prefixText = this.prefix.padStart(Spinner.padding, " ");
25
+ this.spinner.text = `${this.message} (${this.#getElapsedTimeStr()})`;
26
+ }, 1e3);
27
+ } else
28
+ this.spinner.info();
19
29
  return this;
20
30
  }
21
31
  succeed(message) {
22
- clearInterval(this.stopWatch);
23
32
  this.spinner.succeed(`${message} (${this.#getElapsedTimeStr()})`);
33
+ this.#reset();
34
+ }
35
+ fail(message) {
36
+ this.spinner.fail(`${message} (${this.#getElapsedTimeStr()})`);
37
+ this.#reset();
38
+ }
39
+ isSpinning() {
40
+ return this.spinner.isSpinning;
41
+ }
42
+ #reset() {
43
+ if (this.stopWatch)
44
+ clearInterval(this.stopWatch);
45
+ this.stopWatch = null;
24
46
  }
25
47
  #getElapsedTimeStr() {
26
48
  const ms = (/* @__PURE__ */ new Date()).getTime() - this.startAt.getTime();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/devkit",
3
- "version": "0.0.138",
3
+ "version": "0.0.140",
4
4
  "sourceType": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,7 +1,7 @@
1
1
  import "reflect-metadata";
2
2
  import type { AppExecutor, Executor, LibExecutor, PkgExecutor, SysExecutor, WorkspaceExecutor } from "../executors";
3
3
  import type { Type } from "./types";
4
- export declare const argTypes: readonly ["Option"];
4
+ export declare const argTypes: readonly ["Argument", "Option"];
5
5
  export type ArgType = (typeof argTypes)[number];
6
6
  export declare const internalArgTypes: readonly ["Workspace", "App", "Lib", "Sys", "Pkg", "Exec"];
7
7
  export type InternalArgType = (typeof internalArgTypes)[number];
@@ -31,6 +31,7 @@ export interface InternalArgMeta {
31
31
  };
32
32
  }
33
33
  export declare const getArgMetas: (command: Type, key: string) => [(ArgMeta | InternalArgMeta)[], ArgMeta[], InternalArgMeta[]];
34
+ export declare const Argument: (name: string, argsOption?: ArgsOption) => (prototype: object, key: string, idx: number) => void;
34
35
  export declare const Option: (name: string, argsOption?: ArgsOption) => (prototype: object, key: string, idx: number) => void;
35
36
  export declare const App: (option?: {
36
37
  nullable?: boolean;
@@ -13,6 +13,8 @@ export declare const execEmoji: {
13
13
  };
14
14
  export declare class Executor {
15
15
  #private;
16
+ static verbose: boolean;
17
+ static setVerbose(verbose: boolean): void;
16
18
  name: string;
17
19
  logger: Logger;
18
20
  cwdPath: string;
@@ -26,6 +28,8 @@ export declare class Executor {
26
28
  fork(modulePath: string, args?: string[], options?: ForkOptions): Promise<unknown>;
27
29
  mkdir(dirPath: string): this;
28
30
  exists(filePath: string): boolean;
31
+ remove(filePath: string): this;
32
+ removeDir(dirPath: string): this;
29
33
  writeFile(filePath: string, content: string | object, { overwrite }?: {
30
34
  overwrite?: boolean;
31
35
  }): this;
@@ -39,9 +43,10 @@ export declare class Executor {
39
43
  cp(srcPath: string, destPath: string): Promise<void>;
40
44
  log(msg: string): this;
41
45
  verbose(msg: string): this;
42
- spinning(msg: string, { prefix, indent }?: {
46
+ spinning(msg: string, { prefix, indent, enableSpin }?: {
43
47
  prefix?: string | undefined;
44
48
  indent?: number | undefined;
49
+ enableSpin?: boolean | undefined;
45
50
  }): Spinner;
46
51
  getTsConfig(pathname?: string): TsConfigJson;
47
52
  applyTemplate({ basePath, template, scanResult, dict, overwrite, }: {
@@ -68,7 +73,7 @@ export declare class WorkspaceExecutor extends Executor {
68
73
  getBaseDevEnv(): {
69
74
  repoName: string;
70
75
  serveDomain: string;
71
- env: "testing" | "local" | "debug" | "develop" | "main";
76
+ env: "debug" | "testing" | "local" | "develop" | "main";
72
77
  name?: string | undefined;
73
78
  };
74
79
  scan(): Promise<WorkspaceScanResult>;
@@ -77,7 +82,8 @@ export declare class WorkspaceExecutor extends Executor {
77
82
  getSyss(): Promise<[string[], string[]]>;
78
83
  getPkgs(): Promise<string[]>;
79
84
  getExecs(): Promise<[string[], string[], string[]]>;
80
- setTsPaths(type: "app" | "lib", name: string): this;
85
+ setTsPaths(type: "app" | "lib" | "pkg", name: string): this;
86
+ unsetTsPaths(type: "app" | "lib" | "pkg", name: string): this;
81
87
  getDirInModule(basePath: string, name: string): Promise<string[]>;
82
88
  commit(message: string, { init, add }?: {
83
89
  init?: boolean;
@@ -87,6 +93,14 @@ export declare class WorkspaceExecutor extends Executor {
87
93
  filepath: string;
88
94
  content: string;
89
95
  }[]>;
96
+ getConstantFiles(): Promise<{
97
+ filepath: string;
98
+ content: string;
99
+ }[]>;
100
+ getDictionaryFiles(): Promise<{
101
+ filepath: string;
102
+ content: string;
103
+ }[]>;
90
104
  getViewFiles(): Promise<{
91
105
  filepath: string;
92
106
  content: string;
@@ -141,6 +155,14 @@ export declare class SysExecutor extends Executor {
141
155
  filepath: string;
142
156
  content: string;
143
157
  }[]>;
158
+ getConstantFiles(): Promise<{
159
+ filepath: string;
160
+ content: string;
161
+ }[]>;
162
+ getDictionaryFiles(): Promise<{
163
+ filepath: string;
164
+ content: string;
165
+ }[]>;
144
166
  setTsPaths(): this;
145
167
  }
146
168
  interface AppExecutorOptions {
@@ -152,6 +174,7 @@ export declare class AppExecutor extends SysExecutor {
152
174
  emoji: string;
153
175
  constructor({ workspace, name }: AppExecutorOptions);
154
176
  static from(executor: SysExecutor | WorkspaceExecutor, name: string): AppExecutor;
177
+ getEnv(): "debug" | "testing" | "local" | "develop" | "main";
155
178
  getConfig(command?: string): Promise<AppConfigResult>;
156
179
  syncAssets(libDeps: string[]): Promise<void>;
157
180
  }
package/src/spinner.d.ts CHANGED
@@ -1,14 +1,20 @@
1
1
  import ora from "ora";
2
2
  export declare class Spinner {
3
3
  #private;
4
+ static padding: number;
4
5
  spinner: ora.Ora;
5
- stopWatch: NodeJS.Timeout;
6
+ stopWatch: NodeJS.Timeout | null;
6
7
  startAt: Date;
8
+ prefix: string;
7
9
  message: string;
8
- constructor(message: string, { prefix, indent }?: {
10
+ enableSpin: boolean;
11
+ constructor(message: string, { prefix, indent, enableSpin }?: {
9
12
  prefix?: string | undefined;
10
13
  indent?: number | undefined;
14
+ enableSpin?: boolean | undefined;
11
15
  });
12
16
  start(): this;
13
17
  succeed(message: string): void;
18
+ fail(message: string): void;
19
+ isSpinning(): boolean;
14
20
  }