@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.
@@ -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
- 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 {
@@ -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, { 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
+ });
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(`${repoName} Executor`, workspaceRoot);
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(`${name} Sys Executor`, `${workspace.workspaceRoot}/${type}s/${name}`);
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(`${name} Dist App Executor`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
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(`${name} Dist Lib Executor`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
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(`${name} Pkg Executor`, `${workspace.workspaceRoot}/pkgs/${name}`);
735
+ super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
704
736
  this.workspace = workspace;
705
737
  this.name = name;
706
- this.dist = new Executor(`${name} Dist Pkg Executor`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
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
+ });
@@ -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
- 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 {
@@ -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, { 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
+ });
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(`${repoName} Executor`, workspaceRoot);
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(`${name} Sys Executor`, `${workspace.workspaceRoot}/${type}s/${name}`);
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(`${name} Dist App Executor`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
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(`${name} Dist Lib Executor`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
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(`${name} Pkg Executor`, `${workspace.workspaceRoot}/pkgs/${name}`);
701
+ super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
671
702
  this.workspace = workspace;
672
703
  this.name = name;
673
- this.dist = new Executor(`${name} Dist Pkg Executor`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
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
@@ -15,3 +15,4 @@ export * from "./extractDeps";
15
15
  export * from "./commandDecorators";
16
16
  export * from "./aiEditor";
17
17
  export * from "./builder";
18
+ export * from "./spinner";
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/devkit",
3
- "version": "0.0.137",
3
+ "version": "0.0.139",
4
4
  "sourceType": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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
@@ -15,3 +15,4 @@ export * from "./extractDeps";
15
15
  export * from "./commandDecorators";
16
16
  export * from "./aiEditor";
17
17
  export * from "./builder";
18
+ export * from "./spinner";
@@ -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
+ }