@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.
- package/cjs/src/aiEditor.js +13 -6
- package/cjs/src/commandDecorators/argMeta.js +4 -1
- package/cjs/src/commandDecorators/command.js +40 -2
- package/cjs/src/executors.js +88 -16
- package/cjs/src/spinner.js +29 -7
- package/esm/src/aiEditor.js +13 -6
- package/esm/src/commandDecorators/argMeta.js +3 -1
- package/esm/src/commandDecorators/command.js +41 -3
- package/esm/src/executors.js +88 -16
- package/esm/src/spinner.js +29 -7
- package/package.json +1 -1
- package/src/commandDecorators/argMeta.d.ts +2 -1
- package/src/executors.d.ts +26 -3
- package/src/spinner.d.ts +8 -2
package/cjs/src/aiEditor.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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)}`);
|
package/cjs/src/executors.js
CHANGED
|
@@ -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
|
-
|
|
84
|
+
this.#stdout(data);
|
|
74
85
|
});
|
|
75
86
|
proc.stderr?.on("data", (data) => {
|
|
76
|
-
|
|
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, {
|
|
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
|
-
|
|
105
|
+
this.#stdout(data);
|
|
91
106
|
});
|
|
92
107
|
proc.stderr?.on("data", (data) => {
|
|
93
|
-
|
|
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
|
-
|
|
126
|
+
this.#stdout(data);
|
|
112
127
|
});
|
|
113
128
|
proc.stderr?.on("data", (data) => {
|
|
114
|
-
|
|
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}
|
|
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(`${
|
|
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
|
-
|
|
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(),
|
package/cjs/src/spinner.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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();
|
package/esm/src/aiEditor.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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)}`);
|
package/esm/src/executors.js
CHANGED
|
@@ -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
|
-
|
|
50
|
+
this.#stdout(data);
|
|
40
51
|
});
|
|
41
52
|
proc.stderr?.on("data", (data) => {
|
|
42
|
-
|
|
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, {
|
|
65
|
+
const proc = spawn(command, args, {
|
|
66
|
+
cwd: this.cwdPath,
|
|
67
|
+
stdio: "inherit",
|
|
68
|
+
...options
|
|
69
|
+
});
|
|
55
70
|
proc.stdout?.on("data", (data) => {
|
|
56
|
-
|
|
71
|
+
this.#stdout(data);
|
|
57
72
|
});
|
|
58
73
|
proc.stderr?.on("data", (data) => {
|
|
59
|
-
|
|
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
|
-
|
|
92
|
+
this.#stdout(data);
|
|
78
93
|
});
|
|
79
94
|
proc.stderr?.on("data", (data) => {
|
|
80
|
-
|
|
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}
|
|
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(`${
|
|
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
|
-
|
|
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(),
|
package/esm/src/spinner.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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,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;
|
package/src/executors.d.ts
CHANGED
|
@@ -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: "
|
|
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
|
-
|
|
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
|
}
|