@akanjs/devkit 0.0.137 → 0.0.139
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/command.js +3 -0
- package/cjs/src/executors.js +43 -10
- package/cjs/src/index.js +3 -1
- package/cjs/src/spinner.js +93 -0
- package/esm/src/aiEditor.js +13 -6
- package/esm/src/commandDecorators/command.js +4 -1
- package/esm/src/executors.js +41 -9
- package/esm/src/index.js +1 -0
- package/esm/src/spinner.js +60 -0
- package/package.json +1 -1
- package/src/executors.d.ts +23 -0
- package/src/index.d.ts +1 -0
- package/src/spinner.d.ts +20 -0
package/cjs/src/aiEditor.js
CHANGED
|
@@ -33,11 +33,11 @@ __export(aiEditor_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(aiEditor_exports);
|
|
34
34
|
var import_common = require("@akanjs/common");
|
|
35
35
|
var import_prompts = require("@inquirer/prompts");
|
|
36
|
-
var import_ora = __toESM(require("ora"));
|
|
37
36
|
var import_messages = require("@langchain/core/messages");
|
|
38
37
|
var import_openai = require("@langchain/openai");
|
|
39
38
|
var import_chalk = __toESM(require("chalk"));
|
|
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())
|
|
119
126
|
loader.succeed(`${AiSession.#chat.model} responded`);
|
|
120
127
|
const content = chunk.content;
|
|
121
128
|
if (typeof content === "string") {
|
|
@@ -184,6 +184,7 @@ const runCommands = async (...commands) => {
|
|
|
184
184
|
`${sysType} in this workspace ${sysType}s/<${sysType}Name>`
|
|
185
185
|
);
|
|
186
186
|
}
|
|
187
|
+
programCommand = programCommand.option(`-v, --verbose [boolean]`, `verbose output`);
|
|
187
188
|
programCommand.action(async (...args) => {
|
|
188
189
|
const cmdArgs = args.slice(0, args.length - 2);
|
|
189
190
|
const opt = args[args.length - 2];
|
|
@@ -196,6 +197,8 @@ const runCommands = async (...commands) => {
|
|
|
196
197
|
commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
|
|
197
198
|
if (commandArgs[argMeta.idx] instanceof import_executors.AppExecutor)
|
|
198
199
|
process.env.NEXT_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
|
|
200
|
+
if (opt.verbose)
|
|
201
|
+
import_executors.Executor.setVerbose(true);
|
|
199
202
|
}
|
|
200
203
|
const cmd = new command();
|
|
201
204
|
try {
|
package/cjs/src/executors.js
CHANGED
|
@@ -32,7 +32,8 @@ __export(executors_exports, {
|
|
|
32
32
|
LibExecutor: () => LibExecutor,
|
|
33
33
|
PkgExecutor: () => PkgExecutor,
|
|
34
34
|
SysExecutor: () => SysExecutor,
|
|
35
|
-
WorkspaceExecutor: () => WorkspaceExecutor
|
|
35
|
+
WorkspaceExecutor: () => WorkspaceExecutor,
|
|
36
|
+
execEmoji: () => execEmoji
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(executors_exports);
|
|
38
39
|
var import_common = require("@akanjs/common");
|
|
@@ -44,10 +45,25 @@ var import_fs = __toESM(require("fs"));
|
|
|
44
45
|
var import_promises = __toESM(require("fs/promises"));
|
|
45
46
|
var import_path = __toESM(require("path"));
|
|
46
47
|
var import_dependencyScanner = require("./dependencyScanner");
|
|
48
|
+
var import_spinner = require("./spinner");
|
|
49
|
+
const execEmoji = {
|
|
50
|
+
workspace: "\u{1F3E0}",
|
|
51
|
+
app: "\u{1F680}",
|
|
52
|
+
lib: "\u{1F527}",
|
|
53
|
+
pkg: "\u{1F4E6}",
|
|
54
|
+
dist: "\u{1F4BF}",
|
|
55
|
+
default: "\u2708\uFE0F"
|
|
56
|
+
// for sys executor
|
|
57
|
+
};
|
|
47
58
|
class Executor {
|
|
59
|
+
static verbose = false;
|
|
60
|
+
static setVerbose(verbose) {
|
|
61
|
+
Executor.verbose = verbose;
|
|
62
|
+
}
|
|
48
63
|
name;
|
|
49
64
|
logger;
|
|
50
65
|
cwdPath;
|
|
66
|
+
emoji = execEmoji.default;
|
|
51
67
|
constructor(name, cwdPath) {
|
|
52
68
|
this.name = name;
|
|
53
69
|
this.logger = new import_common.Logger(name);
|
|
@@ -73,7 +89,11 @@ class Executor {
|
|
|
73
89
|
});
|
|
74
90
|
}
|
|
75
91
|
spawn(command, args = [], options = {}) {
|
|
76
|
-
const proc = (0, import_child_process.spawn)(command, args, {
|
|
92
|
+
const proc = (0, import_child_process.spawn)(command, args, {
|
|
93
|
+
cwd: this.cwdPath,
|
|
94
|
+
stdio: Executor.verbose ? "inherit" : "ignore",
|
|
95
|
+
...options
|
|
96
|
+
});
|
|
77
97
|
proc.stdout?.on("data", (data) => {
|
|
78
98
|
import_common.Logger.raw(import_chalk.default.dim(data.toString()));
|
|
79
99
|
});
|
|
@@ -92,7 +112,7 @@ class Executor {
|
|
|
92
112
|
fork(modulePath, args = [], options = {}) {
|
|
93
113
|
const proc = (0, import_child_process.fork)(modulePath, args, {
|
|
94
114
|
cwd: this.cwdPath,
|
|
95
|
-
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
115
|
+
stdio: Executor.verbose ? ["ignore", "inherit", "inherit", "ipc"] : ["ignore", "ignore", "ignore", "ipc"],
|
|
96
116
|
...options
|
|
97
117
|
});
|
|
98
118
|
proc.stdout?.on("data", (data) => {
|
|
@@ -174,6 +194,9 @@ class Executor {
|
|
|
174
194
|
this.logger.verbose(msg);
|
|
175
195
|
return this;
|
|
176
196
|
}
|
|
197
|
+
spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
|
|
198
|
+
return new import_spinner.Spinner(msg, { prefix, indent, enableSpin }).start();
|
|
199
|
+
}
|
|
177
200
|
getTsConfig(pathname = "tsconfig.json") {
|
|
178
201
|
const tsconfig = this.readJson(pathname);
|
|
179
202
|
if (tsconfig.extends) {
|
|
@@ -260,8 +283,9 @@ class Executor {
|
|
|
260
283
|
class WorkspaceExecutor extends Executor {
|
|
261
284
|
workspaceRoot;
|
|
262
285
|
repoName;
|
|
286
|
+
emoji = execEmoji.workspace;
|
|
263
287
|
constructor({ workspaceRoot, repoName }) {
|
|
264
|
-
super(
|
|
288
|
+
super("workspace", workspaceRoot);
|
|
265
289
|
this.workspaceRoot = workspaceRoot;
|
|
266
290
|
this.repoName = repoName;
|
|
267
291
|
}
|
|
@@ -416,11 +440,13 @@ class SysExecutor extends Executor {
|
|
|
416
440
|
workspace;
|
|
417
441
|
name;
|
|
418
442
|
type;
|
|
443
|
+
emoji;
|
|
419
444
|
constructor({ workspace = WorkspaceExecutor.fromRoot(), name, type }) {
|
|
420
|
-
super(
|
|
445
|
+
super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
|
|
421
446
|
this.workspace = workspace;
|
|
422
447
|
this.name = name;
|
|
423
448
|
this.type = type;
|
|
449
|
+
this.emoji = execEmoji[type];
|
|
424
450
|
}
|
|
425
451
|
async getConfig(command) {
|
|
426
452
|
return this.type === "app" ? await (0, import_config.getAppConfig)(this.cwdPath, { ...this.workspace.getBaseDevEnv(), type: "app", name: this.name, command }) : await (0, import_config.getLibConfig)(this.cwdPath, { ...this.workspace.getBaseDevEnv(), type: "lib", name: this.name, command });
|
|
@@ -641,15 +667,19 @@ class SysExecutor extends Executor {
|
|
|
641
667
|
}
|
|
642
668
|
class AppExecutor extends SysExecutor {
|
|
643
669
|
dist;
|
|
670
|
+
emoji = execEmoji.app;
|
|
644
671
|
constructor({ workspace, name }) {
|
|
645
672
|
super({ workspace, name, type: "app" });
|
|
646
|
-
this.dist = new Executor(
|
|
673
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
|
|
647
674
|
}
|
|
648
675
|
static from(executor, name) {
|
|
649
676
|
if (executor instanceof WorkspaceExecutor)
|
|
650
677
|
return new AppExecutor({ workspace: executor, name });
|
|
651
678
|
return new AppExecutor({ workspace: executor.workspace, name });
|
|
652
679
|
}
|
|
680
|
+
getEnv() {
|
|
681
|
+
return this.workspace.getBaseDevEnv().env;
|
|
682
|
+
}
|
|
653
683
|
async getConfig(command) {
|
|
654
684
|
return await (0, import_config.getAppConfig)(this.cwdPath, {
|
|
655
685
|
...this.workspace.getBaseDevEnv(),
|
|
@@ -677,9 +707,10 @@ class LibExecutor extends SysExecutor {
|
|
|
677
707
|
workspaceRoot;
|
|
678
708
|
repoName;
|
|
679
709
|
dist;
|
|
710
|
+
emoji = execEmoji.lib;
|
|
680
711
|
constructor({ workspace, name }) {
|
|
681
712
|
super({ workspace, name, type: "lib" });
|
|
682
|
-
this.dist = new Executor(
|
|
713
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
|
|
683
714
|
}
|
|
684
715
|
static from(executor, name) {
|
|
685
716
|
if (executor instanceof WorkspaceExecutor)
|
|
@@ -699,11 +730,12 @@ class PkgExecutor extends Executor {
|
|
|
699
730
|
workspace;
|
|
700
731
|
name;
|
|
701
732
|
dist;
|
|
733
|
+
emoji = execEmoji.pkg;
|
|
702
734
|
constructor({ workspace = WorkspaceExecutor.fromRoot(), name }) {
|
|
703
|
-
super(
|
|
735
|
+
super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
|
|
704
736
|
this.workspace = workspace;
|
|
705
737
|
this.name = name;
|
|
706
|
-
this.dist = new Executor(
|
|
738
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
|
|
707
739
|
}
|
|
708
740
|
static from(executor, name) {
|
|
709
741
|
if (executor instanceof WorkspaceExecutor)
|
|
@@ -740,5 +772,6 @@ class PkgExecutor extends Executor {
|
|
|
740
772
|
LibExecutor,
|
|
741
773
|
PkgExecutor,
|
|
742
774
|
SysExecutor,
|
|
743
|
-
WorkspaceExecutor
|
|
775
|
+
WorkspaceExecutor,
|
|
776
|
+
execEmoji
|
|
744
777
|
});
|
package/cjs/src/index.js
CHANGED
|
@@ -31,6 +31,7 @@ __reExport(src_exports, require("./extractDeps"), module.exports);
|
|
|
31
31
|
__reExport(src_exports, require("./commandDecorators"), module.exports);
|
|
32
32
|
__reExport(src_exports, require("./aiEditor"), module.exports);
|
|
33
33
|
__reExport(src_exports, require("./builder"), module.exports);
|
|
34
|
+
__reExport(src_exports, require("./spinner"), module.exports);
|
|
34
35
|
// Annotate the CommonJS export names for ESM import in node:
|
|
35
36
|
0 && (module.exports = {
|
|
36
37
|
...require("./createTunnel"),
|
|
@@ -49,5 +50,6 @@ __reExport(src_exports, require("./builder"), module.exports);
|
|
|
49
50
|
...require("./extractDeps"),
|
|
50
51
|
...require("./commandDecorators"),
|
|
51
52
|
...require("./aiEditor"),
|
|
52
|
-
...require("./builder")
|
|
53
|
+
...require("./builder"),
|
|
54
|
+
...require("./spinner")
|
|
53
55
|
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var spinner_exports = {};
|
|
29
|
+
__export(spinner_exports, {
|
|
30
|
+
Spinner: () => Spinner
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(spinner_exports);
|
|
33
|
+
var import_ora = __toESM(require("ora"));
|
|
34
|
+
class Spinner {
|
|
35
|
+
static padding = 12;
|
|
36
|
+
spinner;
|
|
37
|
+
stopWatch;
|
|
38
|
+
startAt;
|
|
39
|
+
prefix;
|
|
40
|
+
message;
|
|
41
|
+
enableSpin;
|
|
42
|
+
constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
|
|
43
|
+
Spinner.padding = Math.max(Spinner.padding, prefix.length);
|
|
44
|
+
this.prefix = prefix;
|
|
45
|
+
this.message = message;
|
|
46
|
+
this.spinner = (0, import_ora.default)(message);
|
|
47
|
+
this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
|
|
48
|
+
this.spinner.indent = indent;
|
|
49
|
+
this.enableSpin = enableSpin;
|
|
50
|
+
}
|
|
51
|
+
start() {
|
|
52
|
+
this.startAt = /* @__PURE__ */ new Date();
|
|
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();
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
succeed(message) {
|
|
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;
|
|
78
|
+
}
|
|
79
|
+
#getElapsedTimeStr() {
|
|
80
|
+
const ms = (/* @__PURE__ */ new Date()).getTime() - this.startAt.getTime();
|
|
81
|
+
if (ms < 1e3)
|
|
82
|
+
return `${ms}ms`;
|
|
83
|
+
const s = Math.floor(ms / 1e3);
|
|
84
|
+
if (s < 60)
|
|
85
|
+
return `${s}s`;
|
|
86
|
+
const m = Math.floor(s / 60);
|
|
87
|
+
return `${m}m ${s % 60}s`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
91
|
+
0 && (module.exports = {
|
|
92
|
+
Spinner
|
|
93
|
+
});
|
package/esm/src/aiEditor.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Logger } from "@akanjs/common";
|
|
2
2
|
import { input, select } from "@inquirer/prompts";
|
|
3
|
-
import ora from "ora";
|
|
4
3
|
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
5
4
|
import { ChatOpenAI } from "@langchain/openai";
|
|
6
5
|
import chalk from "chalk";
|
|
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())
|
|
86
93
|
loader.succeed(`${AiSession.#chat.model} responded`);
|
|
87
94
|
const content = chunk.content;
|
|
88
95
|
if (typeof content === "string") {
|
|
@@ -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();
|
|
@@ -152,6 +152,7 @@ const runCommands = async (...commands) => {
|
|
|
152
152
|
`${sysType} in this workspace ${sysType}s/<${sysType}Name>`
|
|
153
153
|
);
|
|
154
154
|
}
|
|
155
|
+
programCommand = programCommand.option(`-v, --verbose [boolean]`, `verbose output`);
|
|
155
156
|
programCommand.action(async (...args) => {
|
|
156
157
|
const cmdArgs = args.slice(0, args.length - 2);
|
|
157
158
|
const opt = args[args.length - 2];
|
|
@@ -164,6 +165,8 @@ const runCommands = async (...commands) => {
|
|
|
164
165
|
commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
|
|
165
166
|
if (commandArgs[argMeta.idx] instanceof AppExecutor)
|
|
166
167
|
process.env.NEXT_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
|
|
168
|
+
if (opt.verbose)
|
|
169
|
+
Executor.setVerbose(true);
|
|
167
170
|
}
|
|
168
171
|
const cmd = new command();
|
|
169
172
|
try {
|
package/esm/src/executors.js
CHANGED
|
@@ -11,10 +11,25 @@ import fs from "fs";
|
|
|
11
11
|
import fsPromise from "fs/promises";
|
|
12
12
|
import path from "path";
|
|
13
13
|
import { TypeScriptDependencyScanner } from "./dependencyScanner";
|
|
14
|
+
import { Spinner } from "./spinner";
|
|
15
|
+
const execEmoji = {
|
|
16
|
+
workspace: "\u{1F3E0}",
|
|
17
|
+
app: "\u{1F680}",
|
|
18
|
+
lib: "\u{1F527}",
|
|
19
|
+
pkg: "\u{1F4E6}",
|
|
20
|
+
dist: "\u{1F4BF}",
|
|
21
|
+
default: "\u2708\uFE0F"
|
|
22
|
+
// for sys executor
|
|
23
|
+
};
|
|
14
24
|
class Executor {
|
|
25
|
+
static verbose = false;
|
|
26
|
+
static setVerbose(verbose) {
|
|
27
|
+
Executor.verbose = verbose;
|
|
28
|
+
}
|
|
15
29
|
name;
|
|
16
30
|
logger;
|
|
17
31
|
cwdPath;
|
|
32
|
+
emoji = execEmoji.default;
|
|
18
33
|
constructor(name, cwdPath) {
|
|
19
34
|
this.name = name;
|
|
20
35
|
this.logger = new Logger(name);
|
|
@@ -40,7 +55,11 @@ class Executor {
|
|
|
40
55
|
});
|
|
41
56
|
}
|
|
42
57
|
spawn(command, args = [], options = {}) {
|
|
43
|
-
const proc = spawn(command, args, {
|
|
58
|
+
const proc = spawn(command, args, {
|
|
59
|
+
cwd: this.cwdPath,
|
|
60
|
+
stdio: Executor.verbose ? "inherit" : "ignore",
|
|
61
|
+
...options
|
|
62
|
+
});
|
|
44
63
|
proc.stdout?.on("data", (data) => {
|
|
45
64
|
Logger.raw(chalk.dim(data.toString()));
|
|
46
65
|
});
|
|
@@ -59,7 +78,7 @@ class Executor {
|
|
|
59
78
|
fork(modulePath, args = [], options = {}) {
|
|
60
79
|
const proc = fork(modulePath, args, {
|
|
61
80
|
cwd: this.cwdPath,
|
|
62
|
-
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
81
|
+
stdio: Executor.verbose ? ["ignore", "inherit", "inherit", "ipc"] : ["ignore", "ignore", "ignore", "ipc"],
|
|
63
82
|
...options
|
|
64
83
|
});
|
|
65
84
|
proc.stdout?.on("data", (data) => {
|
|
@@ -141,6 +160,9 @@ class Executor {
|
|
|
141
160
|
this.logger.verbose(msg);
|
|
142
161
|
return this;
|
|
143
162
|
}
|
|
163
|
+
spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
|
|
164
|
+
return new Spinner(msg, { prefix, indent, enableSpin }).start();
|
|
165
|
+
}
|
|
144
166
|
getTsConfig(pathname = "tsconfig.json") {
|
|
145
167
|
const tsconfig = this.readJson(pathname);
|
|
146
168
|
if (tsconfig.extends) {
|
|
@@ -227,8 +249,9 @@ class Executor {
|
|
|
227
249
|
class WorkspaceExecutor extends Executor {
|
|
228
250
|
workspaceRoot;
|
|
229
251
|
repoName;
|
|
252
|
+
emoji = execEmoji.workspace;
|
|
230
253
|
constructor({ workspaceRoot, repoName }) {
|
|
231
|
-
super(
|
|
254
|
+
super("workspace", workspaceRoot);
|
|
232
255
|
this.workspaceRoot = workspaceRoot;
|
|
233
256
|
this.repoName = repoName;
|
|
234
257
|
}
|
|
@@ -383,11 +406,13 @@ class SysExecutor extends Executor {
|
|
|
383
406
|
workspace;
|
|
384
407
|
name;
|
|
385
408
|
type;
|
|
409
|
+
emoji;
|
|
386
410
|
constructor({ workspace = WorkspaceExecutor.fromRoot(), name, type }) {
|
|
387
|
-
super(
|
|
411
|
+
super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
|
|
388
412
|
this.workspace = workspace;
|
|
389
413
|
this.name = name;
|
|
390
414
|
this.type = type;
|
|
415
|
+
this.emoji = execEmoji[type];
|
|
391
416
|
}
|
|
392
417
|
async getConfig(command) {
|
|
393
418
|
return this.type === "app" ? await getAppConfig(this.cwdPath, { ...this.workspace.getBaseDevEnv(), type: "app", name: this.name, command }) : await getLibConfig(this.cwdPath, { ...this.workspace.getBaseDevEnv(), type: "lib", name: this.name, command });
|
|
@@ -608,15 +633,19 @@ class SysExecutor extends Executor {
|
|
|
608
633
|
}
|
|
609
634
|
class AppExecutor extends SysExecutor {
|
|
610
635
|
dist;
|
|
636
|
+
emoji = execEmoji.app;
|
|
611
637
|
constructor({ workspace, name }) {
|
|
612
638
|
super({ workspace, name, type: "app" });
|
|
613
|
-
this.dist = new Executor(
|
|
639
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
|
|
614
640
|
}
|
|
615
641
|
static from(executor, name) {
|
|
616
642
|
if (executor instanceof WorkspaceExecutor)
|
|
617
643
|
return new AppExecutor({ workspace: executor, name });
|
|
618
644
|
return new AppExecutor({ workspace: executor.workspace, name });
|
|
619
645
|
}
|
|
646
|
+
getEnv() {
|
|
647
|
+
return this.workspace.getBaseDevEnv().env;
|
|
648
|
+
}
|
|
620
649
|
async getConfig(command) {
|
|
621
650
|
return await getAppConfig(this.cwdPath, {
|
|
622
651
|
...this.workspace.getBaseDevEnv(),
|
|
@@ -644,9 +673,10 @@ class LibExecutor extends SysExecutor {
|
|
|
644
673
|
workspaceRoot;
|
|
645
674
|
repoName;
|
|
646
675
|
dist;
|
|
676
|
+
emoji = execEmoji.lib;
|
|
647
677
|
constructor({ workspace, name }) {
|
|
648
678
|
super({ workspace, name, type: "lib" });
|
|
649
|
-
this.dist = new Executor(
|
|
679
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
|
|
650
680
|
}
|
|
651
681
|
static from(executor, name) {
|
|
652
682
|
if (executor instanceof WorkspaceExecutor)
|
|
@@ -666,11 +696,12 @@ class PkgExecutor extends Executor {
|
|
|
666
696
|
workspace;
|
|
667
697
|
name;
|
|
668
698
|
dist;
|
|
699
|
+
emoji = execEmoji.pkg;
|
|
669
700
|
constructor({ workspace = WorkspaceExecutor.fromRoot(), name }) {
|
|
670
|
-
super(
|
|
701
|
+
super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
|
|
671
702
|
this.workspace = workspace;
|
|
672
703
|
this.name = name;
|
|
673
|
-
this.dist = new Executor(
|
|
704
|
+
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
|
|
674
705
|
}
|
|
675
706
|
static from(executor, name) {
|
|
676
707
|
if (executor instanceof WorkspaceExecutor)
|
|
@@ -706,5 +737,6 @@ export {
|
|
|
706
737
|
LibExecutor,
|
|
707
738
|
PkgExecutor,
|
|
708
739
|
SysExecutor,
|
|
709
|
-
WorkspaceExecutor
|
|
740
|
+
WorkspaceExecutor,
|
|
741
|
+
execEmoji
|
|
710
742
|
};
|
package/esm/src/index.js
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
class Spinner {
|
|
3
|
+
static padding = 12;
|
|
4
|
+
spinner;
|
|
5
|
+
stopWatch;
|
|
6
|
+
startAt;
|
|
7
|
+
prefix;
|
|
8
|
+
message;
|
|
9
|
+
enableSpin;
|
|
10
|
+
constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
|
|
11
|
+
Spinner.padding = Math.max(Spinner.padding, prefix.length);
|
|
12
|
+
this.prefix = prefix;
|
|
13
|
+
this.message = message;
|
|
14
|
+
this.spinner = ora(message);
|
|
15
|
+
this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
|
|
16
|
+
this.spinner.indent = indent;
|
|
17
|
+
this.enableSpin = enableSpin;
|
|
18
|
+
}
|
|
19
|
+
start() {
|
|
20
|
+
this.startAt = /* @__PURE__ */ new Date();
|
|
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();
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
succeed(message) {
|
|
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;
|
|
46
|
+
}
|
|
47
|
+
#getElapsedTimeStr() {
|
|
48
|
+
const ms = (/* @__PURE__ */ new Date()).getTime() - this.startAt.getTime();
|
|
49
|
+
if (ms < 1e3)
|
|
50
|
+
return `${ms}ms`;
|
|
51
|
+
const s = Math.floor(ms / 1e3);
|
|
52
|
+
if (s < 60)
|
|
53
|
+
return `${s}s`;
|
|
54
|
+
const m = Math.floor(s / 60);
|
|
55
|
+
return `${m}m ${s % 60}s`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
Spinner
|
|
60
|
+
};
|
package/package.json
CHANGED
package/src/executors.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { Logger } from "@akanjs/common";
|
|
2
2
|
import { type AppConfigResult, AppScanResult, type LibConfigResult, LibScanResult, PkgScanResult, WorkspaceScanResult } from "@akanjs/config";
|
|
3
3
|
import { type ExecOptions, type ForkOptions, type SpawnOptions } from "child_process";
|
|
4
|
+
import { Spinner } from "./spinner";
|
|
4
5
|
import type { PackageJson, TsConfigJson } from "./types";
|
|
6
|
+
export declare const execEmoji: {
|
|
7
|
+
workspace: string;
|
|
8
|
+
app: string;
|
|
9
|
+
lib: string;
|
|
10
|
+
pkg: string;
|
|
11
|
+
dist: string;
|
|
12
|
+
default: string;
|
|
13
|
+
};
|
|
5
14
|
export declare class Executor {
|
|
6
15
|
#private;
|
|
16
|
+
static verbose: boolean;
|
|
17
|
+
static setVerbose(verbose: boolean): void;
|
|
7
18
|
name: string;
|
|
8
19
|
logger: Logger;
|
|
9
20
|
cwdPath: string;
|
|
21
|
+
emoji: string;
|
|
10
22
|
constructor(name: string, cwdPath: string);
|
|
11
23
|
exec(command: string, options?: ExecOptions): Promise<unknown>;
|
|
12
24
|
spawn(command: string, args?: string[], options?: SpawnOptions): Promise<{
|
|
@@ -29,6 +41,11 @@ export declare class Executor {
|
|
|
29
41
|
cp(srcPath: string, destPath: string): Promise<void>;
|
|
30
42
|
log(msg: string): this;
|
|
31
43
|
verbose(msg: string): this;
|
|
44
|
+
spinning(msg: string, { prefix, indent, enableSpin }?: {
|
|
45
|
+
prefix?: string | undefined;
|
|
46
|
+
indent?: number | undefined;
|
|
47
|
+
enableSpin?: boolean | undefined;
|
|
48
|
+
}): Spinner;
|
|
32
49
|
getTsConfig(pathname?: string): TsConfigJson;
|
|
33
50
|
applyTemplate({ basePath, template, scanResult, dict, overwrite, }: {
|
|
34
51
|
basePath: string;
|
|
@@ -48,6 +65,7 @@ export declare class WorkspaceExecutor extends Executor {
|
|
|
48
65
|
#private;
|
|
49
66
|
workspaceRoot: string;
|
|
50
67
|
repoName: string;
|
|
68
|
+
emoji: string;
|
|
51
69
|
constructor({ workspaceRoot, repoName }: ExecutorOptions);
|
|
52
70
|
static fromRoot(): WorkspaceExecutor;
|
|
53
71
|
getBaseDevEnv(): {
|
|
@@ -86,6 +104,7 @@ export declare class SysExecutor extends Executor {
|
|
|
86
104
|
workspace: WorkspaceExecutor;
|
|
87
105
|
name: string;
|
|
88
106
|
type: "app" | "lib";
|
|
107
|
+
emoji: string;
|
|
89
108
|
constructor({ workspace, name, type }: SysExecutorOptions);
|
|
90
109
|
getConfig(command?: string): Promise<LibConfigResult>;
|
|
91
110
|
getModules(): Promise<string[]>;
|
|
@@ -133,8 +152,10 @@ interface AppExecutorOptions {
|
|
|
133
152
|
}
|
|
134
153
|
export declare class AppExecutor extends SysExecutor {
|
|
135
154
|
dist: Executor;
|
|
155
|
+
emoji: string;
|
|
136
156
|
constructor({ workspace, name }: AppExecutorOptions);
|
|
137
157
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string): AppExecutor;
|
|
158
|
+
getEnv(): "testing" | "local" | "debug" | "develop" | "main";
|
|
138
159
|
getConfig(command?: string): Promise<AppConfigResult>;
|
|
139
160
|
syncAssets(libDeps: string[]): Promise<void>;
|
|
140
161
|
}
|
|
@@ -146,6 +167,7 @@ export declare class LibExecutor extends SysExecutor {
|
|
|
146
167
|
workspaceRoot: string;
|
|
147
168
|
repoName: string;
|
|
148
169
|
dist: Executor;
|
|
170
|
+
emoji: string;
|
|
149
171
|
constructor({ workspace, name }: LibExecutorOptions);
|
|
150
172
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string): LibExecutor;
|
|
151
173
|
getConfig(command?: string): Promise<LibConfigResult>;
|
|
@@ -158,6 +180,7 @@ export declare class PkgExecutor extends Executor {
|
|
|
158
180
|
workspace: WorkspaceExecutor;
|
|
159
181
|
name: string;
|
|
160
182
|
dist: Executor;
|
|
183
|
+
emoji: string;
|
|
161
184
|
constructor({ workspace, name }: PkgExecutorOptions);
|
|
162
185
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string): PkgExecutor;
|
|
163
186
|
scan({ packageJson, tsconfig, }?: {
|
package/src/index.d.ts
CHANGED
package/src/spinner.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
export declare class Spinner {
|
|
3
|
+
#private;
|
|
4
|
+
static padding: number;
|
|
5
|
+
spinner: ora.Ora;
|
|
6
|
+
stopWatch: NodeJS.Timeout | null;
|
|
7
|
+
startAt: Date;
|
|
8
|
+
prefix: string;
|
|
9
|
+
message: string;
|
|
10
|
+
enableSpin: boolean;
|
|
11
|
+
constructor(message: string, { prefix, indent, enableSpin }?: {
|
|
12
|
+
prefix?: string | undefined;
|
|
13
|
+
indent?: number | undefined;
|
|
14
|
+
enableSpin?: boolean | undefined;
|
|
15
|
+
});
|
|
16
|
+
start(): this;
|
|
17
|
+
succeed(message: string): void;
|
|
18
|
+
fail(message: string): void;
|
|
19
|
+
isSpinning(): boolean;
|
|
20
|
+
}
|