@akanjs/devkit 3.0.0-beta.2 → 3.0.0-beta.21

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.
Files changed (289) hide show
  1. package/README.ko.md +2 -2
  2. package/README.md +3 -2
  3. package/abstractDoc.test.ts +6 -15
  4. package/abstractDoc.ts +6 -8
  5. package/agentsIndex.test.ts +4 -7
  6. package/agentsIndex.ts +6 -22
  7. package/akanApp/BackendImportGraph.test.ts +8 -22
  8. package/akanApp/BackendImportGraph.ts +1 -20
  9. package/akanApp/akanApp.host.test.ts +1 -3
  10. package/akanApp/akanApp.host.ts +82 -254
  11. package/akanApp/devHostPolicy.test.ts +10 -34
  12. package/akanApp/devHostPolicy.ts +12 -98
  13. package/akanConfig/akanConfig.test.ts +142 -106
  14. package/akanConfig/akanConfig.ts +147 -191
  15. package/akanConfig/types.ts +4 -3
  16. package/akanContext.ts +110 -160
  17. package/akanMcpContract.ts +28 -73
  18. package/appSelectionMemory.ts +1 -4
  19. package/applicationBuildReporter.ts +37 -5
  20. package/applicationBuildRunner.test.ts +5 -3
  21. package/applicationBuildRunner.ts +54 -61
  22. package/applicationTestPreload.ts +3 -12
  23. package/artifact/implicitRootLayout.test.ts +32 -30
  24. package/artifact/implicitRootLayout.ts +58 -106
  25. package/artifact/routeSeedIndex.test.ts +7 -6
  26. package/artifact/routeSeedIndex.ts +11 -44
  27. package/biome.base.json +32 -1
  28. package/biomeBase.ts +2 -5
  29. package/cloud/cloudApi.test.ts +202 -0
  30. package/cloud/cloudApi.ts +77 -39
  31. package/cloud/configLock.test.ts +64 -0
  32. package/cloud/configLock.ts +96 -0
  33. package/cloud/constants.ts +26 -5
  34. package/cloud/globalConfig.ts +70 -32
  35. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  36. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  37. package/codeAgent/agent/CodeAgent.ts +551 -0
  38. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  39. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  40. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  41. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  42. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  43. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  44. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  45. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  46. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  47. package/codeAgent/agent/CodeMailbox.ts +148 -0
  48. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  49. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  50. package/codeAgent/agent/SubagentPool.ts +214 -0
  51. package/codeAgent/agent/akanCodeModel.ts +47 -0
  52. package/codeAgent/agent/akanCodePaths.ts +31 -0
  53. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  54. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  55. package/codeAgent/agent/codeAgent.test.ts +178 -0
  56. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  57. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  58. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  59. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  60. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  61. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  62. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  63. package/codeAgent/agent/consoleToStderr.ts +8 -0
  64. package/codeAgent/agent/jsonLines.ts +29 -0
  65. package/codeAgent/agent/stringArg.ts +5 -0
  66. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  67. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  68. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  69. package/codeAgent/feedback/PreviewView.ts +95 -0
  70. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  71. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  72. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  73. package/codeAgent/index.ts +34 -0
  74. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  75. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  76. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  77. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  78. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  79. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  80. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  81. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  82. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  83. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  84. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  85. package/codeAgent/tools/AkanCli.ts +50 -0
  86. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  87. package/codeAgent/tools/AkanEditScope.ts +48 -0
  88. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  89. package/codeAgent/tools/AkanToolPack.ts +92 -0
  90. package/codeAgent/tools/AkanVerifier.ts +48 -0
  91. package/codeAgent/tools/AskToolPack.ts +99 -0
  92. package/codeAgent/tools/MailToolPack.ts +76 -0
  93. package/codeAgent/tools/McpClient.ts +187 -0
  94. package/codeAgent/tools/McpOAuth.ts +233 -0
  95. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  96. package/codeAgent/tools/McpServerConfig.ts +129 -0
  97. package/codeAgent/tools/McpSignIn.ts +158 -0
  98. package/codeAgent/tools/McpTokenStore.ts +81 -0
  99. package/codeAgent/tools/McpToolPack.ts +199 -0
  100. package/codeAgent/tools/SessionToolPack.ts +91 -0
  101. package/codeAgent/tools/WebToolPack.ts +147 -0
  102. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  103. package/codeAgent/tools/askToolPack.test.ts +116 -0
  104. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  105. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  106. package/codegenLock.test.ts +4 -13
  107. package/codegenLock.ts +3 -17
  108. package/commandDecorators/argMeta.ts +1 -5
  109. package/commandDecorators/camelToKebabCase.ts +1 -0
  110. package/commandDecorators/command.ts +46 -102
  111. package/commandDecorators/commandBuilder.ts +30 -61
  112. package/commandDecorators/commandDecorators.test.ts +2 -10
  113. package/commandDecorators/dependencyBuilder.ts +10 -1
  114. package/commandDecorators/helpFormatter.ts +68 -145
  115. package/commandDecorators/index.ts +0 -2
  116. package/commandDecorators/targetMeta.ts +9 -7
  117. package/createTunnel.ts +7 -14
  118. package/dependencyScanner.test.ts +4 -16
  119. package/dependencyScanner.ts +31 -80
  120. package/devkitUtils.test.ts +48 -20
  121. package/executors.linkFallback.test.ts +103 -0
  122. package/executors.test.ts +132 -106
  123. package/executors.ts +195 -496
  124. package/fileEditor.ts +9 -37
  125. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  126. package/fileSys.ts +6 -8
  127. package/formSetterScanner.ts +3 -16
  128. package/frontendBuild/allRoutesBuilder.ts +7 -21
  129. package/frontendBuild/autoImportSync.test.ts +27 -38
  130. package/frontendBuild/autoImportSync.ts +31 -65
  131. package/frontendBuild/buildRouteClient.test.ts +62 -37
  132. package/frontendBuild/bundleDefine.ts +12 -0
  133. package/frontendBuild/clientBuildTypes.ts +5 -36
  134. package/frontendBuild/clientEntriesBundler.ts +9 -40
  135. package/frontendBuild/clientEntryDiscovery.ts +6 -41
  136. package/frontendBuild/csrArtifactBuilder.ts +6 -25
  137. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  138. package/frontendBuild/cssCandidateCache.ts +6 -41
  139. package/frontendBuild/cssCompiler.ts +29 -78
  140. package/frontendBuild/cssImportResolver.ts +6 -19
  141. package/frontendBuild/devChangePlanner.ts +7 -10
  142. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  143. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  144. package/frontendBuild/fontOptimizer.test.ts +6 -14
  145. package/frontendBuild/fontOptimizer.ts +15 -72
  146. package/frontendBuild/fontPruner.test.ts +5 -10
  147. package/frontendBuild/fontPruner.ts +5 -20
  148. package/frontendBuild/frontendBuild.test.ts +46 -69
  149. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  150. package/frontendBuild/hmrWatcher.test.ts +8 -32
  151. package/frontendBuild/hmrWatcher.ts +14 -71
  152. package/frontendBuild/pagesBundleBuilder.ts +4 -29
  153. package/frontendBuild/precompressArtifacts.ts +5 -18
  154. package/frontendBuild/routeClientBuilder.ts +23 -46
  155. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  156. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  157. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  158. package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
  159. package/frontendBuild/styleContract.ts +1 -8
  160. package/frontendBuild/themeValidator.ts +4 -26
  161. package/frontendBuild/watchRootResolver.test.ts +23 -42
  162. package/frontendBuild/watchRootResolver.ts +5 -27
  163. package/getRelatedCnsts.ts +12 -92
  164. package/incrementalBuilder/buildBatch.proc.ts +29 -58
  165. package/incrementalBuilder/buildBatchProtocol.ts +8 -36
  166. package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
  167. package/incrementalBuilder/buildBatchRunner.ts +10 -31
  168. package/incrementalBuilder/builderChannel.test.ts +24 -49
  169. package/incrementalBuilder/builderChannel.ts +6 -37
  170. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  171. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  172. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  173. package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
  174. package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
  175. package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
  176. package/incrementalBuilder/incrementalBuilder.proc.ts +49 -161
  177. package/index.ts +5 -19
  178. package/integration/devResourceProbe.ts +50 -56
  179. package/integration/devStability.integration.test.ts +47 -194
  180. package/integration/devStabilityHarness.test.ts +13 -28
  181. package/integration/devStabilityHarness.ts +65 -330
  182. package/integration/ssrMemoryProbe.ts +41 -167
  183. package/libSource.test.ts +4 -15
  184. package/libSource.ts +19 -31
  185. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  186. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  187. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  188. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  189. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  190. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  191. package/lint/gritRules.test.ts +8 -24
  192. package/lint/no-deep-internal-import.grit +2 -2
  193. package/lint/no-document-cookie.grit +29 -0
  194. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  195. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  196. package/lint/no-web-storage.grit +30 -0
  197. package/linter.test.ts +1 -2
  198. package/linter.ts +26 -116
  199. package/mobile/index.ts +6 -0
  200. package/mobile/mobileTarget.ts +16 -13
  201. package/mobile/nativeApi.test.ts +30 -0
  202. package/mobile/nativeApi.ts +40 -0
  203. package/mobile/nativeApp.test.ts +85 -0
  204. package/mobile/nativeApp.ts +210 -0
  205. package/mobile/nativeConfig.test.ts +199 -0
  206. package/mobile/nativeConfig.ts +184 -0
  207. package/mobile/nativeWebDir.test.ts +80 -0
  208. package/mobile/nativeWebDir.ts +62 -0
  209. package/mobile/placeholderAppId.test.ts +13 -0
  210. package/mobile/placeholderAppId.ts +16 -0
  211. package/mobile/usageDescriptions.test.ts +27 -0
  212. package/mobile/usageDescriptions.ts +24 -0
  213. package/package.json +10 -10
  214. package/packageExports.test.ts +3 -18
  215. package/packageExportsMap.ts +3 -19
  216. package/piDependencyPins.test.ts +46 -0
  217. package/platformTest/BunTestReport.test.ts +60 -0
  218. package/platformTest/BunTestReport.ts +65 -0
  219. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  220. package/platformTest/IdleWatchedProcess.ts +73 -0
  221. package/platformTest/LinuxDockerTarget.ts +105 -0
  222. package/platformTest/PlatformTestRun.test.ts +64 -0
  223. package/platformTest/PlatformTestRun.ts +141 -0
  224. package/platformTest/PlatformTestTarget.ts +142 -0
  225. package/platformTest/TestSnapshot.test.ts +72 -0
  226. package/platformTest/TestSnapshot.ts +103 -0
  227. package/platformTest/WindowsSshTarget.ts +151 -0
  228. package/prompter.ts +3 -13
  229. package/qualityScanner.test.ts +9 -26
  230. package/qualityScanner.ts +200 -286
  231. package/recipeScanner.test.ts +7 -12
  232. package/recipeScanner.ts +9 -38
  233. package/repoIdentity.ts +2 -9
  234. package/routeSourceValidator.test.ts +0 -1
  235. package/routeSourceValidator.ts +7 -42
  236. package/scanInfo.ts +91 -225
  237. package/selectModel.ts +1 -2
  238. package/semver.ts +3 -5
  239. package/slicePlanner.test.ts +6 -24
  240. package/slicePlanner.ts +11 -32
  241. package/spinner.test.ts +3 -8
  242. package/spinner.ts +4 -16
  243. package/sshTunnel.ts +1 -6
  244. package/ssrScanner.test.ts +1 -6
  245. package/ssrScanner.ts +103 -113
  246. package/stripAnsi.ts +3 -0
  247. package/subspace.test.ts +30 -25
  248. package/subspace.ts +81 -115
  249. package/subspaceConfig.ts +11 -9
  250. package/testHelpers.ts +194 -0
  251. package/transforms/asyncDefaultExportDetector.ts +3 -11
  252. package/transforms/barrelAnalyzer.ts +43 -135
  253. package/transforms/barrelImportsPlugin.ts +38 -175
  254. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  255. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  256. package/transforms/moduleSyntax.ts +32 -0
  257. package/transforms/rscUseClientTransform.ts +35 -20
  258. package/transforms/transforms.test.ts +153 -44
  259. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  260. package/transforms/useClientBundlePlugin.ts +2 -22
  261. package/typeChecker.ts +7 -63
  262. package/types.ts +1 -0
  263. package/uploadRelease.ts +10 -25
  264. package/workflow/artifacts.ts +18 -0
  265. package/workflow/executor.test.ts +1 -12
  266. package/workflow/executor.ts +101 -110
  267. package/workflow/index.ts +0 -1
  268. package/workflow/moduleIndex.test.ts +4 -15
  269. package/workflow/moduleIndex.ts +9 -49
  270. package/workflow/plan.ts +18 -25
  271. package/workflow/render.ts +80 -96
  272. package/workflow/rolloutGate.test.ts +1 -1
  273. package/workflow/source.ts +52 -69
  274. package/workflow/sourceAst.ts +50 -0
  275. package/workspaceLayout.test.ts +13 -3
  276. package/workspaceLayout.ts +20 -23
  277. package/abstractCompactor.ts +0 -112
  278. package/aiEditor.test.ts +0 -60
  279. package/aiEditor.ts +0 -387
  280. package/applicationReleasePackager.ts +0 -209
  281. package/capacitorApp.test.ts +0 -372
  282. package/capacitorApp.ts +0 -1648
  283. package/commandDecorators/commandMeta.ts +0 -7
  284. package/commandDecorators/types.ts +0 -10
  285. package/getDirname.ts +0 -4
  286. package/guideline.ts +0 -19
  287. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  288. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  289. package/workflow/primitive.ts +0 -59
package/aiEditor.ts DELETED
@@ -1,387 +0,0 @@
1
- import { input, select } from "@inquirer/prompts";
2
- import {
3
- AIMessage,
4
- type BaseMessage,
5
- HumanMessage,
6
- mapChatMessagesToStoredMessages,
7
- mapStoredMessagesToChatMessages,
8
- type StoredMessage,
9
- } from "@langchain/core/messages";
10
- import { ChatDeepSeek } from "@langchain/deepseek";
11
- import { ChatOpenAI } from "@langchain/openai";
12
- import { Logger } from "akanjs/common";
13
- import chalk from "chalk";
14
-
15
- import { GlobalConfig } from "./cloud";
16
- import type { Executor, WorkspaceExecutor } from "./executors";
17
- import { Spinner } from "./spinner";
18
- import type { FileContent } from "./types";
19
-
20
- const MAX_ASK_TRY = 300;
21
-
22
- const deepSeekLlmModels = ["deepseek-v4-flash", "deepseek-v4-pro"] as const;
23
-
24
- const openAiLlmModels = ["gpt-5.5"] as const;
25
-
26
- export const supportedLlmModels = [...deepSeekLlmModels, ...openAiLlmModels] as const;
27
- export type SupportedLlmModel = (typeof supportedLlmModels)[number];
28
- type OpenAiLlmModel = (typeof openAiLlmModels)[number];
29
-
30
- const isOpenAiLlmModel = (model: SupportedLlmModel): model is OpenAiLlmModel =>
31
- openAiLlmModels.includes(model as OpenAiLlmModel);
32
-
33
- interface EditOptions {
34
- onReasoning?: (reasoning: string) => void;
35
- onChunk?: (chunk: string) => void;
36
- maxTry?: number;
37
- validate?: string[];
38
- approve?: boolean;
39
- fallbackToPreviousTypescript?: boolean;
40
- }
41
-
42
- export const parseTypescriptFileBlocks = (text: string): FileContent[] => {
43
- const fileBlocks: FileContent[] = [];
44
- const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
45
- const filePathRegex = /^\s*\/\/\s*File:\s*(.+?)\s*$/im;
46
-
47
- for (const codeBlock of text.matchAll(codeBlockRegex)) {
48
- const content = codeBlock[1]?.trim();
49
- if (!content) continue;
50
-
51
- const filePath = filePathRegex.exec(content)?.[1]?.trim();
52
- if (!filePath) continue;
53
-
54
- fileBlocks.push({
55
- filePath,
56
- content: content.replace(filePathRegex, "").trim(),
57
- });
58
- }
59
-
60
- return fileBlocks;
61
- };
62
-
63
- export const preserveTypescriptResponseContent = (previousContent: string, nextContent: string) => {
64
- const previousWrites = parseTypescriptFileBlocks(previousContent);
65
- const nextWrites = parseTypescriptFileBlocks(nextContent);
66
- if (previousWrites.length > 0 && nextWrites.length === 0) return previousContent;
67
- return nextContent;
68
- };
69
-
70
- export class AiSession {
71
- static #cacheDir = "node_modules/.cache/akan/aiSession";
72
- static #chat: ChatDeepSeek | ChatOpenAI | null = null;
73
- static async init({ temperature = 0, useExisting = true }: { temperature?: number; useExisting?: boolean } = {}) {
74
- if (useExisting) {
75
- const llmConfig = await AiSession.getLlmConfig();
76
- if (llmConfig) {
77
- AiSession.#setChatModel(llmConfig.model, llmConfig.apiKey);
78
- Logger.rawLog(chalk.dim(`🤖akan editor uses existing LLM config (${llmConfig.model})`));
79
- return AiSession;
80
- }
81
- } else Logger.rawLog(chalk.yellow("🤖akan-editor is not initialized. LLM configuration should be set first."));
82
-
83
- const llmConfig = await AiSession.#requestLlmConfig();
84
- const { model, apiKey } = llmConfig;
85
-
86
- await AiSession.#validateApiKey(model, apiKey);
87
- const session = AiSession.#setChatModel(model, apiKey, { temperature });
88
- await session.setLlmConfig({ model, apiKey });
89
- return session;
90
- }
91
- static #setChatModel(model: SupportedLlmModel, apiKey: string, { temperature = 0 }: { temperature?: number } = {}) {
92
- AiSession.#chat = AiSession.#createChatModel(model, apiKey, {
93
- temperature,
94
- streaming: true,
95
- });
96
- return AiSession;
97
- }
98
- static #createChatModel(
99
- model: SupportedLlmModel,
100
- apiKey: string,
101
- { temperature = 0, streaming = false }: { temperature?: number; streaming?: boolean } = {},
102
- ) {
103
- if (isOpenAiLlmModel(model))
104
- return new ChatOpenAI({
105
- modelName: model,
106
- temperature,
107
- streaming,
108
- openAIApiKey: apiKey,
109
- });
110
- return new ChatDeepSeek({
111
- modelName: model,
112
- temperature,
113
- streaming,
114
- apiKey,
115
- });
116
- }
117
- static async getLlmConfig() {
118
- return await GlobalConfig.getLlmConfig();
119
- }
120
- static async setLlmConfig(llmConfig: { model: SupportedLlmModel; apiKey: string } | null) {
121
- await GlobalConfig.setLlmConfig(llmConfig);
122
- return AiSession;
123
- }
124
- static async #requestLlmConfig() {
125
- const model = await select<SupportedLlmModel>({
126
- message: "Select a LLM model",
127
- choices: supportedLlmModels,
128
- });
129
- const apiKey = await input({ message: "Enter your API key" });
130
- return { model, apiKey };
131
- }
132
- static async #validateApiKey(modelName: SupportedLlmModel, apiKey: string) {
133
- const spinner = new Spinner("Validating LLM API key...", {
134
- prefix: `🤖akan-editor`,
135
- }).start();
136
- const chat = AiSession.#createChatModel(modelName, apiKey);
137
- try {
138
- await chat.invoke("Hi, and just say 'ok'");
139
- spinner.succeed("LLM API key is valid");
140
- return true;
141
- } catch (error) {
142
- spinner.fail(
143
- chalk.red(
144
- `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"`,
145
- ),
146
- );
147
- throw error;
148
- }
149
- }
150
- static async clearCache(workspaceRoot: string) {
151
- const cacheDir = `${workspaceRoot}/${AiSession.#cacheDir}`;
152
- await Bun.$`rm -rf ${cacheDir}`;
153
- }
154
-
155
- messageHistory: BaseMessage[] = [];
156
- readonly sessionKey: string;
157
- isCacheLoaded: boolean = false;
158
- workspace: WorkspaceExecutor;
159
- constructor(
160
- type: string,
161
- {
162
- workspace,
163
- cacheKey,
164
- isContinued,
165
- }: {
166
- workspace: WorkspaceExecutor;
167
- cacheKey?: string;
168
- isContinued?: boolean;
169
- },
170
- ) {
171
- this.workspace = workspace;
172
- this.sessionKey = `${type}${cacheKey ? `-${cacheKey}` : ""}`;
173
- if (isContinued) this.#cacheLoadPromise = this.#loadCache();
174
- }
175
- #cacheLoadPromise: Promise<void> | null = null;
176
- async #loadCache() {
177
- const cacheFile = `${AiSession.#cacheDir}/${this.sessionKey}.json`;
178
- const isCacheExists = await this.workspace.exists(cacheFile);
179
- if (isCacheExists)
180
- this.messageHistory = mapStoredMessagesToChatMessages(
181
- (await this.workspace.readJson(cacheFile)) as StoredMessage[],
182
- );
183
- else this.messageHistory = [];
184
- this.isCacheLoaded = isCacheExists;
185
- }
186
- async #saveCache() {
187
- const cacheFilePath = `${AiSession.#cacheDir}/${this.sessionKey}.json`;
188
- await this.workspace.writeJson(cacheFilePath, mapChatMessagesToStoredMessages(this.messageHistory));
189
- }
190
- async ask(
191
- question: string,
192
- {
193
- onReasoning = (reasoning: string) => {
194
- Logger.raw(chalk.dim(reasoning));
195
- },
196
- onChunk = (chunk: string) => {
197
- Logger.raw(chunk);
198
- },
199
- }: EditOptions = {},
200
- ): Promise<{ content: string; messageHistory: BaseMessage[] }> {
201
- if (!AiSession.#chat) await AiSession.init();
202
- if (this.#cacheLoadPromise) await this.#cacheLoadPromise;
203
-
204
- if (!AiSession.#chat) throw new Error("Failed to initialize the AI session");
205
- const loader = new Spinner(`${AiSession.#chat.model} is thinking...`, {
206
- prefix: `🤖akan-editor`,
207
- }).start();
208
- try {
209
- const humanMessage = new HumanMessage(question);
210
- this.messageHistory.push(humanMessage);
211
- const stream = await AiSession.#chat.stream(this.messageHistory);
212
- let reasoningResponse = "",
213
- fullResponse = "";
214
- for await (const chunk of stream) {
215
- if (loader.isSpinning()) loader.succeed(`${AiSession.#chat.model} responded`);
216
-
217
- if (!fullResponse.length) {
218
- const reasoningContent = (chunk.additional_kwargs as { reasoning_content?: string }).reasoning_content ?? "";
219
- if (reasoningContent.length) {
220
- reasoningResponse += reasoningContent;
221
- onReasoning(reasoningContent);
222
- continue;
223
- } else if (chunk.content.length) {
224
- reasoningResponse += "\n";
225
- onReasoning(reasoningResponse);
226
- }
227
- }
228
-
229
- const content = chunk.content;
230
- if (typeof content === "string") {
231
- fullResponse += content;
232
- onChunk(content); // Send individual chunks to callback
233
- }
234
- }
235
- fullResponse += "\n";
236
- onChunk("\n");
237
- this.messageHistory.push(new AIMessage(fullResponse));
238
- return { content: fullResponse, messageHistory: this.messageHistory };
239
- } catch {
240
- loader.fail(`${AiSession.#chat.model} failed to respond`);
241
- throw new Error("Failed to stream response");
242
- }
243
- }
244
- async edit(
245
- question: string,
246
- { onChunk, onReasoning, maxTry = MAX_ASK_TRY, validate, approve, fallbackToPreviousTypescript }: EditOptions = {},
247
- ) {
248
- for (let tryCount = 0; tryCount < maxTry; tryCount++) {
249
- let response = await this.ask(question, { onChunk, onReasoning });
250
- if (validate?.length && tryCount === 0) {
251
- const validateQuestion = `Double check if the response meets the requirements and conditions, and follow the instructions. If not, rewrite it.
252
- ${validate.map((v) => `- ${v}`).join("\n")}`;
253
- const validateResponse = await this.ask(validateQuestion, {
254
- onChunk,
255
- onReasoning,
256
- });
257
- response = {
258
- ...validateResponse,
259
- content: fallbackToPreviousTypescript
260
- ? preserveTypescriptResponseContent(response.content, validateResponse.content)
261
- : validateResponse.content,
262
- };
263
- }
264
- const isConfirmed = approve
265
- ? true
266
- : await select({
267
- message: "Do you want to edit the response?",
268
- choices: [
269
- { name: "✅ Yes, confirm and apply this result", value: true },
270
- { name: "🔄 No, I want to edit it more", value: false },
271
- ],
272
- });
273
- if (isConfirmed) {
274
- await this.#saveCache();
275
- return response.content;
276
- }
277
- question = await input({ message: "What do you want to change?" });
278
- tryCount++;
279
- }
280
- throw new Error("Failed to edit");
281
- }
282
- async editTypescript(question: string, options: EditOptions = {}) {
283
- const content = await this.edit(question, options);
284
- return this.#getTypescriptCode(content);
285
- }
286
- #getTypescriptCode(content: string) {
287
- //! will be deprecated
288
- const code = /```(typescript|tsx)([\s\S]*?)```/.exec(content);
289
- // 2번째로 해야 반환되는데 모르겟음 아무튼 일단 이렇게 함.
290
-
291
- return code?.[2] ?? content;
292
- // return code ? code[1] : content;
293
- }
294
- addToolMessgaes(messages: { type: string; content: string }[]) {
295
- // const toolMessages = messages.map(
296
- // (message) => new ToolMessage({ content: message.content, tool_call_id: message.type })
297
- // );
298
- const toolMessages = messages.map((message) => new HumanMessage(message.content));
299
- this.messageHistory.push(...toolMessages);
300
- return this;
301
- }
302
- async writeTypescripts(question: string, executor: Executor, options: EditOptions = {}) {
303
- const content = await this.edit(question, {
304
- ...options,
305
- fallbackToPreviousTypescript: true,
306
- });
307
- const writes = this.#getTypescriptCodes(content);
308
- if (!writes.length)
309
- throw new Error(
310
- "No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.",
311
- );
312
- for (const write of writes) await executor.writeFile(write.filePath, write.content);
313
- return await this.#tryFixTypescripts(writes, executor, options);
314
- }
315
- async #editTypescripts(question: string, options: EditOptions = {}, fallbackWrites?: FileContent[]) {
316
- const content = await this.edit(question, {
317
- ...options,
318
- fallbackToPreviousTypescript: true,
319
- });
320
- const writes = this.#getTypescriptCodes(content);
321
- if (!writes.length && fallbackWrites?.length) return fallbackWrites;
322
- if (!writes.length)
323
- throw new Error(
324
- "No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.",
325
- );
326
- return writes;
327
- }
328
- async #tryFixTypescripts(writes: FileContent[], executor: Executor, options: EditOptions = {}) {
329
- const MAX_EDIT_TRY = 5;
330
- for (let tryCount = 0; tryCount < MAX_EDIT_TRY; tryCount++) {
331
- const loader = new Spinner(`Type checking and linting...`, {
332
- prefix: `🤖akan-editor`,
333
- }).start();
334
- const fileChecks = await Promise.all(
335
- writes.map(async ({ filePath }) => {
336
- const lintResult = await executor.lint(filePath, { fix: true });
337
- const typeCheckResult = await executor.typeCheckAsync(filePath);
338
- const hasTypeErrors = typeCheckResult.fileErrors.length > 0;
339
- const hasLintErrors = lintResult.errors.length > 0;
340
- const needFix = hasTypeErrors || hasLintErrors;
341
- return { filePath, typeCheckResult, lintResult, needFix };
342
- }),
343
- );
344
- const hasAnyFix = fileChecks.some((fileCheck) => fileCheck.needFix);
345
- if (hasAnyFix) {
346
- loader.fail("Type checking and linting has some errors, try to fix them");
347
- fileChecks.forEach((fileCheck) => {
348
- Logger.rawLog(
349
- `TypeCheck Result \n${fileCheck.typeCheckResult.message}\nLint Result \n${fileCheck.lintResult.message}`,
350
- );
351
- this.addToolMessgaes([
352
- { type: "typescript", content: fileCheck.typeCheckResult.message },
353
- { type: "eslint", content: fileCheck.lintResult.message },
354
- ]);
355
- });
356
- writes = await this.#editTypescripts(
357
- "Fix the typescript and eslint errors",
358
- {
359
- ...options,
360
- validate: undefined,
361
- approve: true,
362
- },
363
- writes,
364
- );
365
- for (const write of writes) await executor.writeFile(write.filePath, write.content);
366
- } else {
367
- loader.succeed("Type checking and linting has no errors");
368
- return writes;
369
- }
370
- }
371
- throw new Error("Failed to create scalar");
372
- }
373
- #getTypescriptCodes(text: string): FileContent[] {
374
- return parseTypescriptFileBlocks(text);
375
- }
376
- async editMarkdown(request: string, options: EditOptions = {}) {
377
- const content = await this.edit(request, options);
378
- return this.#getMarkdownContent(content);
379
- }
380
- #getMarkdownContent(text: string) {
381
- const searchText = "```markdown";
382
- const firstIndex = text.indexOf("```markdown");
383
- const lastIndex = text.lastIndexOf("```");
384
- if (firstIndex === -1) return text;
385
- else return text.slice(firstIndex + searchText.length, lastIndex).trim();
386
- }
387
- }
@@ -1,209 +0,0 @@
1
- import { cp, mkdir, rm } from "node:fs/promises";
2
- import path from "node:path";
3
- import type { App } from "./commandDecorators";
4
- import { FileSys } from "./fileSys";
5
- import { uploadRelease } from "./uploadRelease";
6
-
7
- export interface ReleaseSourceOptions {
8
- rebuild?: boolean;
9
- buildNum?: number;
10
- environment?: string;
11
- local?: boolean;
12
- }
13
-
14
- interface ApplicationReleasePackagerOptions {
15
- build: () => Promise<void>;
16
- }
17
-
18
- export class ApplicationReleasePackager {
19
- #app: App;
20
- #build: () => Promise<void>;
21
-
22
- constructor(app: App, options: ApplicationReleasePackagerOptions) {
23
- this.#app = app;
24
- this.#build = options.build;
25
- }
26
-
27
- async releaseSource({
28
- rebuild,
29
- buildNum = 0,
30
- environment = "debug",
31
- local = true,
32
- }: ReleaseSourceOptions = {}): Promise<void> {
33
- const { platformVersion } = await this.#prepareReleaseBuild({ rebuild, buildNum });
34
- await this.#writeSourceArchive({ readme: this.#confidentialReadme() });
35
- await uploadRelease(this.#app.name, {
36
- local,
37
- buildNum,
38
- environment,
39
- platformVersion,
40
- workspaceRoot: this.#app.workspace.cwdPath,
41
- });
42
- }
43
-
44
- async compressProjectFiles({ rebuild, buildNum = 0 }: ReleaseSourceOptions = {}): Promise<void> {
45
- await this.#prepareReleaseBuild({ rebuild, buildNum });
46
- await this.#writeSourceArchive({ readme: this.#publicReadme() });
47
- }
48
-
49
- async #prepareReleaseBuild({ rebuild, buildNum }: { rebuild?: boolean; buildNum: number }) {
50
- const akanConfig = await this.#app.getConfig();
51
- const platformVersion = akanConfig.mobile.version;
52
- const buildRoot = `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}`;
53
- if (await FileSys.dirExists(buildRoot)) await rm(buildRoot, { recursive: true, force: true });
54
- await mkdir(buildRoot, { recursive: true });
55
- if (rebuild || !(await FileSys.dirExists(`${this.#app.dist.cwdPath}/backend`))) await this.#build();
56
-
57
- const buildVersion = `${platformVersion}-${buildNum}`;
58
- const buildPath = `${buildRoot}/${buildVersion}`;
59
- await mkdir(buildPath, { recursive: true });
60
- await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
61
-
62
- await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
63
- await rm(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
64
-
65
- const releaseRoot = this.#app.workspace.workspaceRoot;
66
- // Pass a path relative to cwd so tar never sees a Windows drive letter (e.g. "C:\...")
67
- // which GNU tar would interpret as a remote "host:file" spec.
68
- const releaseArchivePath = path
69
- .relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`)
70
- .split(path.sep)
71
- .join("/");
72
- await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
73
- await this.#writeCsrZipIfPresent();
74
- return { platformVersion };
75
- }
76
-
77
- async #writeCsrZipIfPresent(): Promise<void> {
78
- if (!(await FileSys.dirExists(`${this.#app.dist.cwdPath}/csr`))) return;
79
- await cp(`${this.#app.dist.cwdPath}/csr`, "./csr", { recursive: true });
80
- await this.#app.workspace.spawn("zip", [
81
- "-r",
82
- `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-appBuild.zip`,
83
- "./csr",
84
- ]);
85
- await rm("./csr", { recursive: true, force: true });
86
- }
87
-
88
- async #writeSourceArchive({ readme }: { readme: string }): Promise<void> {
89
- const sourceRoot = `${this.#app.workspace.workspaceRoot}/releases/sources/${this.#app.name}`;
90
- await this.#resetSourceRoot(sourceRoot);
91
- await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
92
-
93
- const libDeps = ["social", "shared", "platform", "util"];
94
- await Promise.all(
95
- libDeps.map((lib) =>
96
- cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true }),
97
- ),
98
- );
99
- await Promise.all(
100
- [".next", "ios", "android", "public/libs"].map(async (path) => {
101
- const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path}`;
102
- if (await FileSys.dirExists(targetPath)) await rm(targetPath, { recursive: true, force: true });
103
- }),
104
- );
105
-
106
- const syncPaths = [".husky", ".gitignore", "package.json"];
107
- await Promise.all(
108
- syncPaths.map((path) =>
109
- cp(`${this.#app.workspace.cwdPath}/${path}`, `${sourceRoot}/${path}`, { recursive: true }),
110
- ),
111
- );
112
- await this.#writeSourceTsconfig(sourceRoot, libDeps);
113
- await Bun.write(`${sourceRoot}/README.md`, readme);
114
- const sourceCwd = this.#app.workspace.cwdPath;
115
- // Pass a path relative to cwd so tar never sees a Windows drive letter (e.g. "C:\...")
116
- // which GNU tar would interpret as a remote "host:file" spec.
117
- const sourceArchivePath = path
118
- .relative(sourceCwd, `${sourceCwd}/releases/sources/${this.#app.name}-source.tar.gz`)
119
- .split(path.sep)
120
- .join("/");
121
- await this.#app.workspace.spawn("tar", ["-zcf", sourceArchivePath, "-C", sourceRoot, "./"], { cwd: sourceCwd });
122
- }
123
-
124
- async #resetSourceRoot(sourceRoot: string): Promise<void> {
125
- if (await FileSys.dirExists(sourceRoot)) {
126
- const maxRetry = 3;
127
- for (let i = 0; i < maxRetry; i++) {
128
- try {
129
- await rm(sourceRoot, { recursive: true, force: true });
130
- } catch {
131
- //
132
- }
133
- }
134
- }
135
- await mkdir(sourceRoot, { recursive: true });
136
- }
137
-
138
- async #writeSourceTsconfig(sourceRoot: string, libDeps: string[]): Promise<void> {
139
- const tsconfig = (await this.#app.workspace.readJson("tsconfig.json")) as {
140
- compilerOptions: { paths: Record<string, string[]> };
141
- };
142
- const pathEntries: [string, string[]][] = [[`@${this.#app.name}/*`, [`apps/${this.#app.name}/*`]]];
143
- for (const lib of libDeps) {
144
- pathEntries.push([`@${lib}`, [`libs/${lib}/index.ts`]], [`@${lib}/*`, [`libs/${lib}/*`]]);
145
- }
146
- tsconfig.compilerOptions.paths = Object.fromEntries(pathEntries) as Record<string, string[]>;
147
- await Bun.write(`${sourceRoot}/tsconfig.json`, JSON.stringify(tsconfig, null, 2));
148
- }
149
-
150
- #confidentialReadme(): string {
151
- return `# ${this.#app.name}
152
- 본 프로젝트의 소스코드 및 관련자료는 모두 비밀정보로 관리됩니다.
153
-
154
- ## Get Started
155
- Run the code below.
156
- \`\`\`
157
- bun install -g bun
158
- bun i
159
-
160
- cat <<EOF >> .env
161
- # ENV For Server => debug | debug.local | develop | develop.local | main | main.local
162
- SERVER_ENV=debug.local
163
- # Run Mode For Server => federation | batch | all
164
- SERVER_MODE=federation
165
- # ENV For Client => debug | debug.local | develop | develop.local | main | main.local
166
- AKAN_PUBLIC_CLIENT_ENV=debug.local
167
- ANALYZE=false
168
- EOF
169
-
170
- akn start-backend ${this.#app.name}
171
- # or akn start-frontend ${this.#app.name}, etc
172
- \`\`\`
173
-
174
- ## Build
175
- Run the code below.
176
- \`\`\`
177
- akn build-backend ${this.#app.name}
178
- # or akn build-frontend ${this.#app.name}, etc
179
- \`\`\`
180
- `;
181
- }
182
-
183
- #publicReadme(): string {
184
- return `# ${this.#app.name}
185
- ## Get Started
186
- Run the code below.
187
- \`\`\`
188
- cat <<EOF >> .env
189
- # ENV For Server => debug | debug.local | develop | develop.local | main | main.local
190
- SERVER_ENV=debug.local
191
- # Run Mode For Server => federation | batch | all
192
- SERVER_MODE=federation
193
- # ENV For Client => debug | debug.local | develop | develop.local | main | main.local
194
- AKAN_PUBLIC_CLIENT_ENV=debug.local
195
- ANALYZE=false
196
- EOF
197
-
198
- akan start ${this.#app.name}
199
- \`\`\`
200
-
201
- ## Build
202
- Run the code below.
203
- \`\`\`
204
- akan build ${this.#app.name}
205
- # or akn build-frontend ${this.#app.name}, etc
206
- \`\`\`
207
- `;
208
- }
209
- }