@akanjs/devkit 0.0.138 → 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.
@@ -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())
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 {
@@ -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;
@@ -85,7 +89,11 @@ class Executor {
85
89
  });
86
90
  }
87
91
  spawn(command, args = [], options = {}) {
88
- const proc = (0, import_child_process.spawn)(command, args, { cwd: this.cwdPath, stdio: "inherit", ...options });
92
+ const proc = (0, import_child_process.spawn)(command, args, {
93
+ cwd: this.cwdPath,
94
+ stdio: Executor.verbose ? "inherit" : "ignore",
95
+ ...options
96
+ });
89
97
  proc.stdout?.on("data", (data) => {
90
98
  import_common.Logger.raw(import_chalk.default.dim(data.toString()));
91
99
  });
@@ -104,7 +112,7 @@ class Executor {
104
112
  fork(modulePath, args = [], options = {}) {
105
113
  const proc = (0, import_child_process.fork)(modulePath, args, {
106
114
  cwd: this.cwdPath,
107
- stdio: ["ignore", "inherit", "inherit", "ipc"],
115
+ stdio: Executor.verbose ? ["ignore", "inherit", "inherit", "ipc"] : ["ignore", "ignore", "ignore", "ipc"],
108
116
  ...options
109
117
  });
110
118
  proc.stdout?.on("data", (data) => {
@@ -186,8 +194,8 @@ class Executor {
186
194
  this.logger.verbose(msg);
187
195
  return this;
188
196
  }
189
- spinning(msg, { prefix = `${this.emoji}${this.name} -`, indent = 0 } = {}) {
190
- return new import_spinner.Spinner(msg, { prefix, indent }).start();
197
+ spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
198
+ return new import_spinner.Spinner(msg, { prefix, indent, enableSpin }).start();
191
199
  }
192
200
  getTsConfig(pathname = "tsconfig.json") {
193
201
  const tsconfig = this.readJson(pathname);
@@ -669,6 +677,9 @@ class AppExecutor extends SysExecutor {
669
677
  return new AppExecutor({ workspace: executor, name });
670
678
  return new AppExecutor({ workspace: executor.workspace, name });
671
679
  }
680
+ getEnv() {
681
+ return this.workspace.getBaseDevEnv().env;
682
+ }
672
683
  async getConfig(command) {
673
684
  return await (0, import_config.getAppConfig)(this.cwdPath, {
674
685
  ...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())
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 {
@@ -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;
@@ -51,7 +55,11 @@ class Executor {
51
55
  });
52
56
  }
53
57
  spawn(command, args = [], options = {}) {
54
- const proc = spawn(command, args, { cwd: this.cwdPath, stdio: "inherit", ...options });
58
+ const proc = spawn(command, args, {
59
+ cwd: this.cwdPath,
60
+ stdio: Executor.verbose ? "inherit" : "ignore",
61
+ ...options
62
+ });
55
63
  proc.stdout?.on("data", (data) => {
56
64
  Logger.raw(chalk.dim(data.toString()));
57
65
  });
@@ -70,7 +78,7 @@ class Executor {
70
78
  fork(modulePath, args = [], options = {}) {
71
79
  const proc = fork(modulePath, args, {
72
80
  cwd: this.cwdPath,
73
- stdio: ["ignore", "inherit", "inherit", "ipc"],
81
+ stdio: Executor.verbose ? ["ignore", "inherit", "inherit", "ipc"] : ["ignore", "ignore", "ignore", "ipc"],
74
82
  ...options
75
83
  });
76
84
  proc.stdout?.on("data", (data) => {
@@ -152,8 +160,8 @@ class Executor {
152
160
  this.logger.verbose(msg);
153
161
  return this;
154
162
  }
155
- spinning(msg, { prefix = `${this.emoji}${this.name} -`, indent = 0 } = {}) {
156
- return new Spinner(msg, { prefix, indent }).start();
163
+ spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
164
+ return new Spinner(msg, { prefix, indent, enableSpin }).start();
157
165
  }
158
166
  getTsConfig(pathname = "tsconfig.json") {
159
167
  const tsconfig = this.readJson(pathname);
@@ -635,6 +643,9 @@ class AppExecutor extends SysExecutor {
635
643
  return new AppExecutor({ workspace: executor, name });
636
644
  return new AppExecutor({ workspace: executor.workspace, name });
637
645
  }
646
+ getEnv() {
647
+ return this.workspace.getBaseDevEnv().env;
648
+ }
638
649
  async getConfig(command) {
639
650
  return await getAppConfig(this.cwdPath, {
640
651
  ...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.139",
4
4
  "sourceType": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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;
@@ -39,9 +41,10 @@ export declare class Executor {
39
41
  cp(srcPath: string, destPath: string): Promise<void>;
40
42
  log(msg: string): this;
41
43
  verbose(msg: string): this;
42
- spinning(msg: string, { prefix, indent }?: {
44
+ spinning(msg: string, { prefix, indent, enableSpin }?: {
43
45
  prefix?: string | undefined;
44
46
  indent?: number | undefined;
47
+ enableSpin?: boolean | undefined;
45
48
  }): Spinner;
46
49
  getTsConfig(pathname?: string): TsConfigJson;
47
50
  applyTemplate({ basePath, template, scanResult, dict, overwrite, }: {
@@ -152,6 +155,7 @@ export declare class AppExecutor extends SysExecutor {
152
155
  emoji: string;
153
156
  constructor({ workspace, name }: AppExecutorOptions);
154
157
  static from(executor: SysExecutor | WorkspaceExecutor, name: string): AppExecutor;
158
+ getEnv(): "testing" | "local" | "debug" | "develop" | "main";
155
159
  getConfig(command?: string): Promise<AppConfigResult>;
156
160
  syncAssets(libDeps: string[]): Promise<void>;
157
161
  }
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
  }