@akanjs/cli 2.2.1 → 2.2.2
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/incrementalBuilder.proc.js +58 -52
- package/index.js +76 -72
- package/package.json +2 -2
|
@@ -17,18 +17,6 @@ import { ChatOpenAI } from "@langchain/openai";
|
|
|
17
17
|
import { Logger as Logger2 } from "akanjs/common";
|
|
18
18
|
import chalk from "chalk";
|
|
19
19
|
|
|
20
|
-
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
21
|
-
var basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
22
|
-
var configPath = `${basePath}/config.json`;
|
|
23
|
-
var akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? "http://localhost" : "https://cloud.akanjs.com";
|
|
24
|
-
var akanCloudUrl = `${akanCloudHost}${process.env.USE_AKANJS_PKGS === "true" ? ":8282" : ""}/api`;
|
|
25
|
-
var defaultHostConfig = {};
|
|
26
|
-
var defaultAkanGlobalConfig = {
|
|
27
|
-
cloudHost: {},
|
|
28
|
-
remoteEnvServers: {},
|
|
29
|
-
llm: null
|
|
30
|
-
};
|
|
31
|
-
|
|
32
20
|
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
33
21
|
import { mkdir } from "fs/promises";
|
|
34
22
|
import dayjs from "dayjs";
|
|
@@ -71,8 +59,19 @@ class FileSys {
|
|
|
71
59
|
}
|
|
72
60
|
}
|
|
73
61
|
|
|
62
|
+
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
63
|
+
var basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
64
|
+
var configPath = `${basePath}/config.json`;
|
|
65
|
+
var getDefaultHostConfig = (host = GlobalConfig.akanCloudHost) => ({ host });
|
|
66
|
+
var defaultAkanGlobalConfig = {
|
|
67
|
+
cloudHost: {},
|
|
68
|
+
remoteEnvServers: {},
|
|
69
|
+
llm: null
|
|
70
|
+
};
|
|
71
|
+
|
|
74
72
|
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
75
73
|
class GlobalConfig {
|
|
74
|
+
static akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? `http://localhost:${process.env.CLOUD_HOST_PORT ?? 8283}` : "https://cloud.akanjs.com";
|
|
76
75
|
static async#getAkanGlobalConfig() {
|
|
77
76
|
const exists = await FileSys.fileExists(configPath);
|
|
78
77
|
const akanConfig = exists ? await FileSys.readJson(configPath) : {};
|
|
@@ -87,13 +86,13 @@ class GlobalConfig {
|
|
|
87
86
|
await mkdir(basePath, { recursive: true });
|
|
88
87
|
await Bun.write(configPath, JSON.stringify(akanConfig, null, 2));
|
|
89
88
|
}
|
|
90
|
-
static async getHostConfig(host = akanCloudHost) {
|
|
89
|
+
static async getHostConfig(host = GlobalConfig.akanCloudHost) {
|
|
91
90
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
92
|
-
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ??
|
|
91
|
+
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
|
|
93
92
|
}
|
|
94
|
-
static async setHostConfig(
|
|
93
|
+
static async setHostConfig(config = getDefaultHostConfig()) {
|
|
95
94
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
96
|
-
akanConfig.cloudHost[host] = GlobalConfig.toHostConfigDto(config);
|
|
95
|
+
akanConfig.cloudHost[config.host] = GlobalConfig.toHostConfigDto(config);
|
|
97
96
|
await GlobalConfig.#setAkanGlobalConfig(akanConfig);
|
|
98
97
|
}
|
|
99
98
|
static async getLlmConfig() {
|
|
@@ -145,6 +144,7 @@ class GlobalConfig {
|
|
|
145
144
|
}
|
|
146
145
|
static toHostConfigDto(hostConfig) {
|
|
147
146
|
return {
|
|
147
|
+
host: hostConfig.host,
|
|
148
148
|
auth: {
|
|
149
149
|
accessToken: hostConfig.auth?.accessToken ? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken) : undefined,
|
|
150
150
|
self: hostConfig.auth?.self
|
|
@@ -153,6 +153,7 @@ class GlobalConfig {
|
|
|
153
153
|
}
|
|
154
154
|
static toHostConfig(hostConfigDto) {
|
|
155
155
|
return {
|
|
156
|
+
host: hostConfigDto.host,
|
|
156
157
|
auth: {
|
|
157
158
|
accessToken: hostConfigDto.auth?.accessToken ? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken) : undefined,
|
|
158
159
|
self: hostConfigDto.auth?.self
|
|
@@ -177,7 +178,7 @@ class HttpClient {
|
|
|
177
178
|
...headers
|
|
178
179
|
}
|
|
179
180
|
});
|
|
180
|
-
return response.json();
|
|
181
|
+
return await response.json();
|
|
181
182
|
}
|
|
182
183
|
async getFile(url, localPath, headers) {
|
|
183
184
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
@@ -194,7 +195,7 @@ class HttpClient {
|
|
|
194
195
|
body: isFormData ? data : JSON.stringify(data),
|
|
195
196
|
headers: isFormData ? { ...this.headers, ...headers } : { "Content-Type": "application/json", ...this.headers, ...headers }
|
|
196
197
|
});
|
|
197
|
-
return response.json();
|
|
198
|
+
return await response.json();
|
|
198
199
|
}
|
|
199
200
|
setHeaders(headers) {
|
|
200
201
|
Object.assign(this.headers, headers);
|
|
@@ -206,21 +207,25 @@ class CloudApi {
|
|
|
206
207
|
#api;
|
|
207
208
|
#accessToken = null;
|
|
208
209
|
#workspace;
|
|
210
|
+
host;
|
|
211
|
+
url;
|
|
209
212
|
static async fromHost(workspace, host) {
|
|
210
213
|
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
211
214
|
return new CloudApi(workspace, hostConfig);
|
|
212
215
|
}
|
|
213
216
|
constructor(workspace, hostConfig) {
|
|
214
217
|
this.#workspace = workspace;
|
|
215
|
-
const host = akanCloudHost;
|
|
216
|
-
this.#api = new HttpClient(`${host}/api`);
|
|
217
218
|
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
219
|
+
this.host = hostConfig.host;
|
|
220
|
+
this.url = `${this.host}/api`;
|
|
221
|
+
this.#api = new HttpClient(this.url);
|
|
218
222
|
if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
|
|
219
223
|
this.#api.setHeaders({
|
|
220
224
|
Authorization: `Bearer ${this.#accessToken.jwt}`
|
|
221
225
|
});
|
|
222
226
|
}
|
|
223
227
|
async uploadEnv(devProjectId, file) {
|
|
228
|
+
await this.#ensureAccessTokenLive();
|
|
224
229
|
const formData = new FormData;
|
|
225
230
|
formData.append("devProjectId", devProjectId);
|
|
226
231
|
formData.append("file", file);
|
|
@@ -228,18 +233,13 @@ class CloudApi {
|
|
|
228
233
|
return data;
|
|
229
234
|
}
|
|
230
235
|
async downloadEnv(devProjectId) {
|
|
236
|
+
await this.#ensureAccessTokenLive();
|
|
231
237
|
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
232
238
|
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
233
239
|
return localPath;
|
|
234
240
|
}
|
|
235
241
|
async getRemoteAuthToken(remoteId) {
|
|
236
242
|
try {
|
|
237
|
-
if (this.#accessToken) {
|
|
238
|
-
if (GlobalConfig.needRefreshToken(this.#accessToken))
|
|
239
|
-
return await this.#refreshAuthToken();
|
|
240
|
-
else
|
|
241
|
-
return await this.#refreshAuthToken();
|
|
242
|
-
}
|
|
243
243
|
const accessToken = await this.#api.get(`/getRemoteAuthToken/${remoteId}`);
|
|
244
244
|
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
245
245
|
this.#api.setHeaders({
|
|
@@ -250,14 +250,21 @@ class CloudApi {
|
|
|
250
250
|
return null;
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
|
-
async#
|
|
253
|
+
async#ensureAccessTokenLive({
|
|
254
|
+
allowUnauthorized = false
|
|
255
|
+
} = {}) {
|
|
256
|
+
if (!this.#accessToken)
|
|
257
|
+
throw new Error("No access token");
|
|
258
|
+
const needRefresh = GlobalConfig.needRefreshToken(this.#accessToken);
|
|
259
|
+
if (!needRefresh)
|
|
260
|
+
return this.#accessToken;
|
|
254
261
|
const refreshToken = this.#accessToken?.refreshToken;
|
|
255
262
|
if (!refreshToken)
|
|
256
263
|
throw new Error("No refresh token");
|
|
257
264
|
return await this.refreshAuthToken(refreshToken);
|
|
258
265
|
}
|
|
259
266
|
async refreshAuthToken(refreshToken) {
|
|
260
|
-
const response = await this.#api.post(`/
|
|
267
|
+
const response = await this.#api.post(`/refreshAuthToken`, { refreshToken });
|
|
261
268
|
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
262
269
|
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
263
270
|
return this.#accessToken;
|
|
@@ -333,10 +340,10 @@ class Spinner {
|
|
|
333
340
|
|
|
334
341
|
// pkgs/@akanjs/devkit/aiEditor.ts
|
|
335
342
|
var MAX_ASK_TRY = 300;
|
|
336
|
-
var
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
343
|
+
var deepSeekLlmModels = ["deepseek-chat", "deepseek-reasoner"];
|
|
344
|
+
var openAiLlmModels = ["gpt-5.5"];
|
|
345
|
+
var supportedLlmModels = [...deepSeekLlmModels, ...openAiLlmModels];
|
|
346
|
+
var isOpenAiLlmModel = (model) => openAiLlmModels.includes(model);
|
|
340
347
|
var parseTypescriptFileBlocks = (text) => {
|
|
341
348
|
const fileBlocks = [];
|
|
342
349
|
const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
|
|
@@ -366,10 +373,7 @@ var preserveTypescriptResponseContent = (previousContent, nextContent) => {
|
|
|
366
373
|
class AiSession {
|
|
367
374
|
static #cacheDir = "node_modules/.cache/akan/aiSession";
|
|
368
375
|
static #chat = null;
|
|
369
|
-
static async init({
|
|
370
|
-
temperature = 0,
|
|
371
|
-
useExisting = true
|
|
372
|
-
} = {}) {
|
|
376
|
+
static async init({ temperature = 0, useExisting = true } = {}) {
|
|
373
377
|
if (useExisting) {
|
|
374
378
|
const llmConfig2 = await AiSession.getLlmConfig();
|
|
375
379
|
if (llmConfig2) {
|
|
@@ -387,13 +391,26 @@ class AiSession {
|
|
|
387
391
|
return session;
|
|
388
392
|
}
|
|
389
393
|
static #setChatModel(model, apiKey, { temperature = 0 } = {}) {
|
|
390
|
-
AiSession.#chat =
|
|
394
|
+
AiSession.#chat = AiSession.#createChatModel(model, apiKey, {
|
|
395
|
+
temperature,
|
|
396
|
+
streaming: true
|
|
397
|
+
});
|
|
398
|
+
return AiSession;
|
|
399
|
+
}
|
|
400
|
+
static #createChatModel(model, apiKey, { temperature = 0, streaming = false } = {}) {
|
|
401
|
+
if (isOpenAiLlmModel(model))
|
|
402
|
+
return new ChatOpenAI({
|
|
403
|
+
modelName: model,
|
|
404
|
+
temperature,
|
|
405
|
+
streaming,
|
|
406
|
+
openAIApiKey: apiKey
|
|
407
|
+
});
|
|
408
|
+
return new ChatDeepSeek({
|
|
391
409
|
modelName: model,
|
|
392
410
|
temperature,
|
|
393
|
-
streaming
|
|
411
|
+
streaming,
|
|
394
412
|
apiKey
|
|
395
413
|
});
|
|
396
|
-
return AiSession;
|
|
397
414
|
}
|
|
398
415
|
static async getLlmConfig() {
|
|
399
416
|
return await GlobalConfig.getLlmConfig();
|
|
@@ -414,11 +431,7 @@ class AiSession {
|
|
|
414
431
|
const spinner = new Spinner("Validating LLM API key...", {
|
|
415
432
|
prefix: `\uD83E\uDD16akan-editor`
|
|
416
433
|
}).start();
|
|
417
|
-
const chat =
|
|
418
|
-
modelName,
|
|
419
|
-
temperature: 0,
|
|
420
|
-
configuration: { baseURL: "https://api.deepseek.com/v1", apiKey }
|
|
421
|
-
});
|
|
434
|
+
const chat = AiSession.#createChatModel(modelName, apiKey);
|
|
422
435
|
try {
|
|
423
436
|
await chat.invoke("Hi, and just say 'ok'");
|
|
424
437
|
spinner.succeed("LLM API key is valid");
|
|
@@ -514,14 +527,7 @@ class AiSession {
|
|
|
514
527
|
throw new Error("Failed to stream response");
|
|
515
528
|
}
|
|
516
529
|
}
|
|
517
|
-
async edit(question, {
|
|
518
|
-
onChunk,
|
|
519
|
-
onReasoning,
|
|
520
|
-
maxTry = MAX_ASK_TRY,
|
|
521
|
-
validate,
|
|
522
|
-
approve,
|
|
523
|
-
fallbackToPreviousTypescript
|
|
524
|
-
} = {}) {
|
|
530
|
+
async edit(question, { onChunk, onReasoning, maxTry = MAX_ASK_TRY, validate, approve, fallbackToPreviousTypescript } = {}) {
|
|
525
531
|
for (let tryCount = 0;tryCount < maxTry; tryCount++) {
|
|
526
532
|
let response = await this.ask(question, { onChunk, onReasoning });
|
|
527
533
|
if (validate?.length && tryCount === 0) {
|
package/index.js
CHANGED
|
@@ -15,18 +15,6 @@ import { ChatOpenAI } from "@langchain/openai";
|
|
|
15
15
|
import { Logger as Logger2 } from "akanjs/common";
|
|
16
16
|
import chalk from "chalk";
|
|
17
17
|
|
|
18
|
-
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
19
|
-
var basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
20
|
-
var configPath = `${basePath}/config.json`;
|
|
21
|
-
var akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? "http://localhost" : "https://cloud.akanjs.com";
|
|
22
|
-
var akanCloudUrl = `${akanCloudHost}${process.env.USE_AKANJS_PKGS === "true" ? ":8282" : ""}/api`;
|
|
23
|
-
var defaultHostConfig = {};
|
|
24
|
-
var defaultAkanGlobalConfig = {
|
|
25
|
-
cloudHost: {},
|
|
26
|
-
remoteEnvServers: {},
|
|
27
|
-
llm: null
|
|
28
|
-
};
|
|
29
|
-
|
|
30
18
|
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
31
19
|
import { mkdir } from "fs/promises";
|
|
32
20
|
import dayjs from "dayjs";
|
|
@@ -69,8 +57,19 @@ class FileSys {
|
|
|
69
57
|
}
|
|
70
58
|
}
|
|
71
59
|
|
|
60
|
+
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
61
|
+
var basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
62
|
+
var configPath = `${basePath}/config.json`;
|
|
63
|
+
var getDefaultHostConfig = (host = GlobalConfig.akanCloudHost) => ({ host });
|
|
64
|
+
var defaultAkanGlobalConfig = {
|
|
65
|
+
cloudHost: {},
|
|
66
|
+
remoteEnvServers: {},
|
|
67
|
+
llm: null
|
|
68
|
+
};
|
|
69
|
+
|
|
72
70
|
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
73
71
|
class GlobalConfig {
|
|
72
|
+
static akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? `http://localhost:${process.env.CLOUD_HOST_PORT ?? 8283}` : "https://cloud.akanjs.com";
|
|
74
73
|
static async#getAkanGlobalConfig() {
|
|
75
74
|
const exists = await FileSys.fileExists(configPath);
|
|
76
75
|
const akanConfig = exists ? await FileSys.readJson(configPath) : {};
|
|
@@ -85,13 +84,13 @@ class GlobalConfig {
|
|
|
85
84
|
await mkdir(basePath, { recursive: true });
|
|
86
85
|
await Bun.write(configPath, JSON.stringify(akanConfig, null, 2));
|
|
87
86
|
}
|
|
88
|
-
static async getHostConfig(host = akanCloudHost) {
|
|
87
|
+
static async getHostConfig(host = GlobalConfig.akanCloudHost) {
|
|
89
88
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
90
|
-
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ??
|
|
89
|
+
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
|
|
91
90
|
}
|
|
92
|
-
static async setHostConfig(
|
|
91
|
+
static async setHostConfig(config = getDefaultHostConfig()) {
|
|
93
92
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
94
|
-
akanConfig.cloudHost[host] = GlobalConfig.toHostConfigDto(config);
|
|
93
|
+
akanConfig.cloudHost[config.host] = GlobalConfig.toHostConfigDto(config);
|
|
95
94
|
await GlobalConfig.#setAkanGlobalConfig(akanConfig);
|
|
96
95
|
}
|
|
97
96
|
static async getLlmConfig() {
|
|
@@ -143,6 +142,7 @@ class GlobalConfig {
|
|
|
143
142
|
}
|
|
144
143
|
static toHostConfigDto(hostConfig) {
|
|
145
144
|
return {
|
|
145
|
+
host: hostConfig.host,
|
|
146
146
|
auth: {
|
|
147
147
|
accessToken: hostConfig.auth?.accessToken ? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken) : undefined,
|
|
148
148
|
self: hostConfig.auth?.self
|
|
@@ -151,6 +151,7 @@ class GlobalConfig {
|
|
|
151
151
|
}
|
|
152
152
|
static toHostConfig(hostConfigDto) {
|
|
153
153
|
return {
|
|
154
|
+
host: hostConfigDto.host,
|
|
154
155
|
auth: {
|
|
155
156
|
accessToken: hostConfigDto.auth?.accessToken ? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken) : undefined,
|
|
156
157
|
self: hostConfigDto.auth?.self
|
|
@@ -175,7 +176,7 @@ class HttpClient {
|
|
|
175
176
|
...headers
|
|
176
177
|
}
|
|
177
178
|
});
|
|
178
|
-
return response.json();
|
|
179
|
+
return await response.json();
|
|
179
180
|
}
|
|
180
181
|
async getFile(url, localPath, headers) {
|
|
181
182
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
@@ -192,7 +193,7 @@ class HttpClient {
|
|
|
192
193
|
body: isFormData ? data : JSON.stringify(data),
|
|
193
194
|
headers: isFormData ? { ...this.headers, ...headers } : { "Content-Type": "application/json", ...this.headers, ...headers }
|
|
194
195
|
});
|
|
195
|
-
return response.json();
|
|
196
|
+
return await response.json();
|
|
196
197
|
}
|
|
197
198
|
setHeaders(headers) {
|
|
198
199
|
Object.assign(this.headers, headers);
|
|
@@ -204,21 +205,25 @@ class CloudApi {
|
|
|
204
205
|
#api;
|
|
205
206
|
#accessToken = null;
|
|
206
207
|
#workspace;
|
|
208
|
+
host;
|
|
209
|
+
url;
|
|
207
210
|
static async fromHost(workspace, host) {
|
|
208
211
|
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
209
212
|
return new CloudApi(workspace, hostConfig);
|
|
210
213
|
}
|
|
211
214
|
constructor(workspace, hostConfig) {
|
|
212
215
|
this.#workspace = workspace;
|
|
213
|
-
const host = akanCloudHost;
|
|
214
|
-
this.#api = new HttpClient(`${host}/api`);
|
|
215
216
|
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
217
|
+
this.host = hostConfig.host;
|
|
218
|
+
this.url = `${this.host}/api`;
|
|
219
|
+
this.#api = new HttpClient(this.url);
|
|
216
220
|
if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
|
|
217
221
|
this.#api.setHeaders({
|
|
218
222
|
Authorization: `Bearer ${this.#accessToken.jwt}`
|
|
219
223
|
});
|
|
220
224
|
}
|
|
221
225
|
async uploadEnv(devProjectId, file) {
|
|
226
|
+
await this.#ensureAccessTokenLive();
|
|
222
227
|
const formData = new FormData;
|
|
223
228
|
formData.append("devProjectId", devProjectId);
|
|
224
229
|
formData.append("file", file);
|
|
@@ -226,18 +231,13 @@ class CloudApi {
|
|
|
226
231
|
return data;
|
|
227
232
|
}
|
|
228
233
|
async downloadEnv(devProjectId) {
|
|
234
|
+
await this.#ensureAccessTokenLive();
|
|
229
235
|
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
230
236
|
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
231
237
|
return localPath;
|
|
232
238
|
}
|
|
233
239
|
async getRemoteAuthToken(remoteId) {
|
|
234
240
|
try {
|
|
235
|
-
if (this.#accessToken) {
|
|
236
|
-
if (GlobalConfig.needRefreshToken(this.#accessToken))
|
|
237
|
-
return await this.#refreshAuthToken();
|
|
238
|
-
else
|
|
239
|
-
return await this.#refreshAuthToken();
|
|
240
|
-
}
|
|
241
241
|
const accessToken = await this.#api.get(`/getRemoteAuthToken/${remoteId}`);
|
|
242
242
|
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
243
243
|
this.#api.setHeaders({
|
|
@@ -248,14 +248,21 @@ class CloudApi {
|
|
|
248
248
|
return null;
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
async#
|
|
251
|
+
async#ensureAccessTokenLive({
|
|
252
|
+
allowUnauthorized = false
|
|
253
|
+
} = {}) {
|
|
254
|
+
if (!this.#accessToken)
|
|
255
|
+
throw new Error("No access token");
|
|
256
|
+
const needRefresh = GlobalConfig.needRefreshToken(this.#accessToken);
|
|
257
|
+
if (!needRefresh)
|
|
258
|
+
return this.#accessToken;
|
|
252
259
|
const refreshToken = this.#accessToken?.refreshToken;
|
|
253
260
|
if (!refreshToken)
|
|
254
261
|
throw new Error("No refresh token");
|
|
255
262
|
return await this.refreshAuthToken(refreshToken);
|
|
256
263
|
}
|
|
257
264
|
async refreshAuthToken(refreshToken) {
|
|
258
|
-
const response = await this.#api.post(`/
|
|
265
|
+
const response = await this.#api.post(`/refreshAuthToken`, { refreshToken });
|
|
259
266
|
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
260
267
|
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
261
268
|
return this.#accessToken;
|
|
@@ -331,10 +338,10 @@ class Spinner {
|
|
|
331
338
|
|
|
332
339
|
// pkgs/@akanjs/devkit/aiEditor.ts
|
|
333
340
|
var MAX_ASK_TRY = 300;
|
|
334
|
-
var
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
var deepSeekLlmModels = ["deepseek-chat", "deepseek-reasoner"];
|
|
342
|
+
var openAiLlmModels = ["gpt-5.5"];
|
|
343
|
+
var supportedLlmModels = [...deepSeekLlmModels, ...openAiLlmModels];
|
|
344
|
+
var isOpenAiLlmModel = (model) => openAiLlmModels.includes(model);
|
|
338
345
|
var parseTypescriptFileBlocks = (text) => {
|
|
339
346
|
const fileBlocks = [];
|
|
340
347
|
const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
|
|
@@ -364,10 +371,7 @@ var preserveTypescriptResponseContent = (previousContent, nextContent) => {
|
|
|
364
371
|
class AiSession {
|
|
365
372
|
static #cacheDir = "node_modules/.cache/akan/aiSession";
|
|
366
373
|
static #chat = null;
|
|
367
|
-
static async init({
|
|
368
|
-
temperature = 0,
|
|
369
|
-
useExisting = true
|
|
370
|
-
} = {}) {
|
|
374
|
+
static async init({ temperature = 0, useExisting = true } = {}) {
|
|
371
375
|
if (useExisting) {
|
|
372
376
|
const llmConfig2 = await AiSession.getLlmConfig();
|
|
373
377
|
if (llmConfig2) {
|
|
@@ -385,13 +389,26 @@ class AiSession {
|
|
|
385
389
|
return session;
|
|
386
390
|
}
|
|
387
391
|
static #setChatModel(model, apiKey, { temperature = 0 } = {}) {
|
|
388
|
-
AiSession.#chat =
|
|
392
|
+
AiSession.#chat = AiSession.#createChatModel(model, apiKey, {
|
|
393
|
+
temperature,
|
|
394
|
+
streaming: true
|
|
395
|
+
});
|
|
396
|
+
return AiSession;
|
|
397
|
+
}
|
|
398
|
+
static #createChatModel(model, apiKey, { temperature = 0, streaming = false } = {}) {
|
|
399
|
+
if (isOpenAiLlmModel(model))
|
|
400
|
+
return new ChatOpenAI({
|
|
401
|
+
modelName: model,
|
|
402
|
+
temperature,
|
|
403
|
+
streaming,
|
|
404
|
+
openAIApiKey: apiKey
|
|
405
|
+
});
|
|
406
|
+
return new ChatDeepSeek({
|
|
389
407
|
modelName: model,
|
|
390
408
|
temperature,
|
|
391
|
-
streaming
|
|
409
|
+
streaming,
|
|
392
410
|
apiKey
|
|
393
411
|
});
|
|
394
|
-
return AiSession;
|
|
395
412
|
}
|
|
396
413
|
static async getLlmConfig() {
|
|
397
414
|
return await GlobalConfig.getLlmConfig();
|
|
@@ -412,11 +429,7 @@ class AiSession {
|
|
|
412
429
|
const spinner = new Spinner("Validating LLM API key...", {
|
|
413
430
|
prefix: `\uD83E\uDD16akan-editor`
|
|
414
431
|
}).start();
|
|
415
|
-
const chat =
|
|
416
|
-
modelName,
|
|
417
|
-
temperature: 0,
|
|
418
|
-
configuration: { baseURL: "https://api.deepseek.com/v1", apiKey }
|
|
419
|
-
});
|
|
432
|
+
const chat = AiSession.#createChatModel(modelName, apiKey);
|
|
420
433
|
try {
|
|
421
434
|
await chat.invoke("Hi, and just say 'ok'");
|
|
422
435
|
spinner.succeed("LLM API key is valid");
|
|
@@ -512,14 +525,7 @@ class AiSession {
|
|
|
512
525
|
throw new Error("Failed to stream response");
|
|
513
526
|
}
|
|
514
527
|
}
|
|
515
|
-
async edit(question, {
|
|
516
|
-
onChunk,
|
|
517
|
-
onReasoning,
|
|
518
|
-
maxTry = MAX_ASK_TRY,
|
|
519
|
-
validate,
|
|
520
|
-
approve,
|
|
521
|
-
fallbackToPreviousTypescript
|
|
522
|
-
} = {}) {
|
|
528
|
+
async edit(question, { onChunk, onReasoning, maxTry = MAX_ASK_TRY, validate, approve, fallbackToPreviousTypescript } = {}) {
|
|
523
529
|
for (let tryCount = 0;tryCount < maxTry; tryCount++) {
|
|
524
530
|
let response = await this.ask(question, { onChunk, onReasoning });
|
|
525
531
|
if (validate?.length && tryCount === 0) {
|
|
@@ -11273,8 +11279,8 @@ class CloudRunner extends runner("cloud") {
|
|
|
11273
11279
|
#getSshArgs(config, command3) {
|
|
11274
11280
|
return [...config.port ? ["-p", config.port.toString()] : [], this.#getSshTarget(config), command3];
|
|
11275
11281
|
}
|
|
11276
|
-
async login(workspace) {
|
|
11277
|
-
const config = await GlobalConfig.getHostConfig();
|
|
11282
|
+
async login(host, workspace) {
|
|
11283
|
+
const config = await GlobalConfig.getHostConfig(host);
|
|
11278
11284
|
const cloudApi2 = new CloudApi(workspace, config);
|
|
11279
11285
|
const self = config.auth ? await cloudApi2.getRemoteSelf() : null;
|
|
11280
11286
|
if (self) {
|
|
@@ -11284,7 +11290,7 @@ class CloudRunner extends runner("cloud") {
|
|
|
11284
11290
|
return true;
|
|
11285
11291
|
}
|
|
11286
11292
|
const remoteId = crypto.randomUUID();
|
|
11287
|
-
const signinUrl = `${
|
|
11293
|
+
const signinUrl = `${cloudApi2.host}/remoteAuth?remoteId=${encodeURIComponent(remoteId)}`;
|
|
11288
11294
|
Logger14.rawLog(chalk7.bold(`
|
|
11289
11295
|
${chalk7.green("\u27A4")} Authentication Required`));
|
|
11290
11296
|
Logger14.rawLog(chalk7.dim("Please visit or click the following URL:"));
|
|
@@ -11310,12 +11316,10 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11310
11316
|
const accessToken = await cloudApi2.getRemoteAuthToken(remoteId);
|
|
11311
11317
|
const self2 = await cloudApi2.getRemoteSelf();
|
|
11312
11318
|
if (accessToken && self2) {
|
|
11313
|
-
await GlobalConfig.setHostConfig(
|
|
11314
|
-
auth: { accessToken, self: self2 }
|
|
11315
|
-
});
|
|
11319
|
+
await GlobalConfig.setHostConfig({ host: config.host, auth: { accessToken, self: self2 } });
|
|
11316
11320
|
Logger14.rawLog(chalk7.green(`\r\u2713 Authentication successful!`));
|
|
11317
11321
|
Logger14.rawLog(chalk7.green.bold(`
|
|
11318
|
-
\u2728 Welcome aboard, ${self2.nickname}!`));
|
|
11322
|
+
\u2728 Welcome aboard, ${self2.nickname ?? "anonymous"}!`));
|
|
11319
11323
|
Logger14.rawLog(chalk7.dim(`You're now ready to use Akan CLI!
|
|
11320
11324
|
`));
|
|
11321
11325
|
return true;
|
|
@@ -11324,12 +11328,12 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11324
11328
|
}
|
|
11325
11329
|
throw new Error(chalk7.red("\u2716 Authentication timed out after 10 minutes. Please try again."));
|
|
11326
11330
|
}
|
|
11327
|
-
async logout() {
|
|
11328
|
-
const config = await GlobalConfig.getHostConfig();
|
|
11331
|
+
async logout(host) {
|
|
11332
|
+
const config = await GlobalConfig.getHostConfig(host);
|
|
11329
11333
|
if (config.auth?.self) {
|
|
11330
|
-
await GlobalConfig.setHostConfig(
|
|
11334
|
+
await GlobalConfig.setHostConfig(getDefaultHostConfig(config.host));
|
|
11331
11335
|
Logger14.rawLog(chalk7.magenta.bold(`
|
|
11332
|
-
\uD83D\uDC4B Goodbye, ${config.auth.self.nickname}!`));
|
|
11336
|
+
\uD83D\uDC4B Goodbye, ${config.auth.self.nickname ?? "anonymous"}!`));
|
|
11333
11337
|
Logger14.rawLog(chalk7.dim(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
11334
11338
|
`));
|
|
11335
11339
|
Logger14.rawLog(chalk7.cyan("You have been successfully logged out."));
|
|
@@ -11343,7 +11347,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11343
11347
|
}
|
|
11344
11348
|
}
|
|
11345
11349
|
async setLlm() {
|
|
11346
|
-
await AiSession.init({ useExisting:
|
|
11350
|
+
await AiSession.init({ useExisting: false });
|
|
11347
11351
|
}
|
|
11348
11352
|
resetLlm() {
|
|
11349
11353
|
AiSession.setLlmConfig(null);
|
|
@@ -11520,11 +11524,11 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11520
11524
|
|
|
11521
11525
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
11522
11526
|
class CloudScript extends script("cloud", [CloudRunner, ApplicationScript, PackageScript]) {
|
|
11523
|
-
async login(workspace) {
|
|
11524
|
-
await this.cloudRunner.login(workspace);
|
|
11527
|
+
async login(host, workspace) {
|
|
11528
|
+
await this.cloudRunner.login(host, workspace);
|
|
11525
11529
|
}
|
|
11526
|
-
async logout(workspace) {
|
|
11527
|
-
await this.cloudRunner.logout();
|
|
11530
|
+
async logout(host, workspace) {
|
|
11531
|
+
await this.cloudRunner.logout(host);
|
|
11528
11532
|
}
|
|
11529
11533
|
async setLlm(workspace) {
|
|
11530
11534
|
await this.cloudRunner.setLlm();
|
|
@@ -11580,11 +11584,11 @@ var localRegistryUrl = () => process.env.AKAN_NPM_REGISTRY ?? "http://127.0.0.1:
|
|
|
11580
11584
|
var resolveRegistryUrl = (registry) => registry === "local" ? localRegistryUrl() : undefined;
|
|
11581
11585
|
|
|
11582
11586
|
class CloudCommand extends command("cloud", [CloudScript], ({ public: target }) => ({
|
|
11583
|
-
login: target({ desc: "Login to Akan Cloud services" }).with(Workspace).exec(async function(workspace) {
|
|
11584
|
-
await this.cloudScript.login(workspace);
|
|
11587
|
+
login: target({ desc: "Login to Akan Cloud services" }).option("host", String, { desc: "host of the cloud", default: GlobalConfig.akanCloudHost }).with(Workspace).exec(async function(host, workspace) {
|
|
11588
|
+
await this.cloudScript.login(host, workspace);
|
|
11585
11589
|
}),
|
|
11586
|
-
logout: target({ desc: "Logout from Akan Cloud services" }).with(Workspace).exec(async function(workspace) {
|
|
11587
|
-
await this.cloudScript.logout(workspace);
|
|
11590
|
+
logout: target({ desc: "Logout from Akan Cloud services" }).option("host", String, { desc: "host of the cloud", default: GlobalConfig.akanCloudHost }).with(Workspace).exec(async function(host, workspace) {
|
|
11591
|
+
await this.cloudScript.logout(host, workspace);
|
|
11588
11592
|
}),
|
|
11589
11593
|
setLlm: target({ desc: "Configure LLM (Large Language Model) API key" }).with(Workspace).exec(async function(workspace) {
|
|
11590
11594
|
await this.cloudScript.setLlm(workspace);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@langchain/openai": "^1.4.6",
|
|
36
36
|
"@tailwindcss/node": "^4.3.0",
|
|
37
37
|
"@trapezedev/project": "^7.1.4",
|
|
38
|
-
"akanjs": "2.2.
|
|
38
|
+
"akanjs": "2.2.2",
|
|
39
39
|
"chalk": "^5.6.2",
|
|
40
40
|
"commander": "^14.0.3",
|
|
41
41
|
"daisyui": "^5.5.20",
|