@akanjs/devkit 2.2.1 → 2.2.2-rc.0
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/aiEditor.ts +28 -15
- package/cloud/cloudApi.ts +19 -11
- package/cloud/constants.ts +4 -3
- package/cloud/globalConfig.ts +11 -6
- package/package.json +2 -2
package/aiEditor.ts
CHANGED
|
@@ -19,11 +19,16 @@ import type { FileContent } from "./types";
|
|
|
19
19
|
|
|
20
20
|
const MAX_ASK_TRY = 300;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const deepSeekLlmModels = ["deepseek-chat", "deepseek-reasoner"] as const;
|
|
23
|
+
|
|
24
|
+
const openAiLlmModels = ["gpt-5.5", "gpt-4.1", "gpt-4.1-mini", "gpt-4o", "gpt-4o-mini"] as const;
|
|
25
|
+
|
|
26
|
+
export const supportedLlmModels = [...deepSeekLlmModels, ...openAiLlmModels] as const;
|
|
26
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);
|
|
27
32
|
|
|
28
33
|
interface EditOptions {
|
|
29
34
|
onReasoning?: (reasoning: string) => void;
|
|
@@ -102,19 +107,31 @@ export class AiSession {
|
|
|
102
107
|
await session.setLlmConfig({ model, apiKey });
|
|
103
108
|
return session;
|
|
104
109
|
}
|
|
105
|
-
static #setChatModel(
|
|
110
|
+
static #setChatModel(model: SupportedLlmModel, apiKey: string, { temperature = 0 }: { temperature?: number } = {}) {
|
|
111
|
+
AiSession.#chat = AiSession.#createChatModel(model, apiKey, {
|
|
112
|
+
temperature,
|
|
113
|
+
streaming: true,
|
|
114
|
+
});
|
|
115
|
+
return AiSession;
|
|
116
|
+
}
|
|
117
|
+
static #createChatModel(
|
|
106
118
|
model: SupportedLlmModel,
|
|
107
119
|
apiKey: string,
|
|
108
|
-
{ temperature = 0 }: { temperature?: number } = {},
|
|
120
|
+
{ temperature = 0, streaming = false }: { temperature?: number; streaming?: boolean } = {},
|
|
109
121
|
) {
|
|
110
|
-
|
|
122
|
+
if (isOpenAiLlmModel(model))
|
|
123
|
+
return new ChatOpenAI({
|
|
124
|
+
modelName: model,
|
|
125
|
+
temperature,
|
|
126
|
+
streaming,
|
|
127
|
+
openAIApiKey: apiKey,
|
|
128
|
+
});
|
|
129
|
+
return new ChatDeepSeek({
|
|
111
130
|
modelName: model,
|
|
112
131
|
temperature,
|
|
113
|
-
streaming
|
|
132
|
+
streaming,
|
|
114
133
|
apiKey,
|
|
115
|
-
// configuration: { baseURL: "https://api.deepseek.com/v1", apiKey },
|
|
116
134
|
});
|
|
117
|
-
return AiSession;
|
|
118
135
|
}
|
|
119
136
|
static async getLlmConfig() {
|
|
120
137
|
return await GlobalConfig.getLlmConfig();
|
|
@@ -137,11 +154,7 @@ export class AiSession {
|
|
|
137
154
|
const spinner = new Spinner("Validating LLM API key...", {
|
|
138
155
|
prefix: `🤖akan-editor`,
|
|
139
156
|
}).start();
|
|
140
|
-
const chat =
|
|
141
|
-
modelName,
|
|
142
|
-
temperature: 0,
|
|
143
|
-
configuration: { baseURL: "https://api.deepseek.com/v1", apiKey },
|
|
144
|
-
});
|
|
157
|
+
const chat = AiSession.#createChatModel(modelName, apiKey);
|
|
145
158
|
try {
|
|
146
159
|
await chat.invoke("Hi, and just say 'ok'");
|
|
147
160
|
spinner.succeed("LLM API key is valid");
|
package/cloud/cloudApi.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Workspace } from "../commandDecorators";
|
|
2
|
-
import {
|
|
2
|
+
import type { AccessToken, AccessTokenDto, HostConfig } from "./constants";
|
|
3
3
|
import { GlobalConfig } from "./globalConfig";
|
|
4
4
|
|
|
5
5
|
class HttpClient {
|
|
@@ -17,7 +17,7 @@ class HttpClient {
|
|
|
17
17
|
...headers,
|
|
18
18
|
},
|
|
19
19
|
});
|
|
20
|
-
return response.json();
|
|
20
|
+
return await response.json();
|
|
21
21
|
}
|
|
22
22
|
async getFile(url: string, localPath: string, headers?: Record<string, string>): Promise<void> {
|
|
23
23
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
@@ -35,7 +35,7 @@ class HttpClient {
|
|
|
35
35
|
? { ...this.headers, ...headers }
|
|
36
36
|
: { "Content-Type": "application/json", ...this.headers, ...headers },
|
|
37
37
|
});
|
|
38
|
-
return response.json();
|
|
38
|
+
return await response.json();
|
|
39
39
|
}
|
|
40
40
|
setHeaders(headers: Record<string, string>) {
|
|
41
41
|
Object.assign(this.headers, headers);
|
|
@@ -47,6 +47,8 @@ export class CloudApi {
|
|
|
47
47
|
readonly #api: HttpClient;
|
|
48
48
|
#accessToken: AccessToken | null = null;
|
|
49
49
|
#workspace: Workspace;
|
|
50
|
+
host: string;
|
|
51
|
+
url: string;
|
|
50
52
|
|
|
51
53
|
static async fromHost(workspace: Workspace, host?: string) {
|
|
52
54
|
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
@@ -54,9 +56,10 @@ export class CloudApi {
|
|
|
54
56
|
}
|
|
55
57
|
constructor(workspace: Workspace, hostConfig: HostConfig) {
|
|
56
58
|
this.#workspace = workspace;
|
|
57
|
-
const host = akanCloudHost;
|
|
58
|
-
this.#api = new HttpClient(`${host}/api`);
|
|
59
59
|
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
60
|
+
this.host = hostConfig.host;
|
|
61
|
+
this.url = `${this.host}/api`;
|
|
62
|
+
this.#api = new HttpClient(this.url);
|
|
60
63
|
if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
|
|
61
64
|
this.#api.setHeaders({
|
|
62
65
|
Authorization: `Bearer ${this.#accessToken.jwt}`,
|
|
@@ -64,6 +67,7 @@ export class CloudApi {
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
async uploadEnv(devProjectId: string, file: File): Promise<boolean> {
|
|
70
|
+
await this.#ensureAccessTokenLive();
|
|
67
71
|
const formData = new FormData();
|
|
68
72
|
formData.append("devProjectId", devProjectId);
|
|
69
73
|
formData.append("file", file);
|
|
@@ -71,16 +75,13 @@ export class CloudApi {
|
|
|
71
75
|
return data;
|
|
72
76
|
}
|
|
73
77
|
async downloadEnv(devProjectId: string): Promise<unknown> {
|
|
78
|
+
await this.#ensureAccessTokenLive();
|
|
74
79
|
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
75
80
|
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
76
81
|
return localPath;
|
|
77
82
|
}
|
|
78
83
|
async getRemoteAuthToken(remoteId: string): Promise<AccessToken | null> {
|
|
79
84
|
try {
|
|
80
|
-
if (this.#accessToken) {
|
|
81
|
-
if (GlobalConfig.needRefreshToken(this.#accessToken)) return await this.#refreshAuthToken();
|
|
82
|
-
else return await this.#refreshAuthToken();
|
|
83
|
-
}
|
|
84
85
|
const accessToken = await this.#api.get<AccessTokenDto>(`/getRemoteAuthToken/${remoteId}`);
|
|
85
86
|
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
86
87
|
this.#api.setHeaders({
|
|
@@ -91,13 +92,20 @@ export class CloudApi {
|
|
|
91
92
|
return null;
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
|
-
async #
|
|
95
|
+
async #ensureAccessTokenLive({
|
|
96
|
+
allowUnauthorized = false,
|
|
97
|
+
}: {
|
|
98
|
+
allowUnauthorized?: boolean;
|
|
99
|
+
} = {}): Promise<AccessToken> {
|
|
100
|
+
if (!this.#accessToken) throw new Error("No access token");
|
|
101
|
+
const needRefresh = GlobalConfig.needRefreshToken(this.#accessToken);
|
|
102
|
+
if (!needRefresh) return this.#accessToken;
|
|
95
103
|
const refreshToken = this.#accessToken?.refreshToken;
|
|
96
104
|
if (!refreshToken) throw new Error("No refresh token");
|
|
97
105
|
return await this.refreshAuthToken(refreshToken);
|
|
98
106
|
}
|
|
99
107
|
async refreshAuthToken(refreshToken: string): Promise<AccessToken> {
|
|
100
|
-
const response = await this.#api.post<AccessTokenDto>(`/
|
|
108
|
+
const response = await this.#api.post<AccessTokenDto>(`/refreshAuthToken`, { refreshToken });
|
|
101
109
|
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
102
110
|
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
103
111
|
return this.#accessToken;
|
package/cloud/constants.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import type { Dayjs } from "dayjs";
|
|
2
|
+
import { GlobalConfig } from "..";
|
|
2
3
|
import type { SupportedLlmModel } from "../aiEditor";
|
|
3
4
|
|
|
4
5
|
export const basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
5
6
|
export const configPath = `${basePath}/config.json`;
|
|
6
|
-
export const akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? "http://localhost" : "https://cloud.akanjs.com";
|
|
7
|
-
export const akanCloudUrl = `${akanCloudHost}${process.env.USE_AKANJS_PKGS === "true" ? ":8282" : ""}/api`;
|
|
8
7
|
|
|
9
8
|
export interface HostConfig {
|
|
9
|
+
host: string;
|
|
10
10
|
auth?: {
|
|
11
11
|
accessToken?: AccessToken;
|
|
12
12
|
self?: { id: string; nickname: string };
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
export interface HostConfigDto {
|
|
16
|
+
host: string;
|
|
16
17
|
auth?: {
|
|
17
18
|
accessToken?: AccessTokenDto;
|
|
18
19
|
self?: { id: string; nickname: string };
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
|
-
export const
|
|
22
|
+
export const getDefaultHostConfig = (host = GlobalConfig.akanCloudHost): HostConfig => ({ host });
|
|
22
23
|
export interface RemoteEnvServerConfig {
|
|
23
24
|
host: string;
|
|
24
25
|
username?: string;
|
package/cloud/globalConfig.ts
CHANGED
|
@@ -5,17 +5,20 @@ import {
|
|
|
5
5
|
type AccessToken,
|
|
6
6
|
type AccessTokenDto,
|
|
7
7
|
type AkanGlobalConfig,
|
|
8
|
-
akanCloudHost,
|
|
9
8
|
basePath,
|
|
10
9
|
configPath,
|
|
11
10
|
defaultAkanGlobalConfig,
|
|
12
|
-
|
|
11
|
+
getDefaultHostConfig,
|
|
13
12
|
type HostConfig,
|
|
14
13
|
type HostConfigDto,
|
|
15
14
|
type RemoteEnvServerConfig,
|
|
16
15
|
} from "./constants";
|
|
17
16
|
|
|
18
17
|
export class GlobalConfig {
|
|
18
|
+
static akanCloudHost =
|
|
19
|
+
process.env.USE_AKANJS_PKGS === "true"
|
|
20
|
+
? `http://localhost:${process.env.CLOUD_HOST_PORT ?? 8283}`
|
|
21
|
+
: "https://cloud.akanjs.com";
|
|
19
22
|
static async #getAkanGlobalConfig(): Promise<AkanGlobalConfig> {
|
|
20
23
|
const exists = await FileSys.fileExists(configPath);
|
|
21
24
|
const akanConfig = exists ? await FileSys.readJson<Partial<AkanGlobalConfig>>(configPath) : {};
|
|
@@ -30,13 +33,13 @@ export class GlobalConfig {
|
|
|
30
33
|
await mkdir(basePath, { recursive: true });
|
|
31
34
|
await Bun.write(configPath, JSON.stringify(akanConfig, null, 2));
|
|
32
35
|
}
|
|
33
|
-
static async getHostConfig(host = akanCloudHost): Promise<HostConfig> {
|
|
36
|
+
static async getHostConfig(host = GlobalConfig.akanCloudHost): Promise<HostConfig> {
|
|
34
37
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
35
|
-
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ??
|
|
38
|
+
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
|
|
36
39
|
}
|
|
37
|
-
static async setHostConfig(
|
|
40
|
+
static async setHostConfig(config: HostConfig = getDefaultHostConfig()) {
|
|
38
41
|
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
39
|
-
akanConfig.cloudHost[host] = GlobalConfig.toHostConfigDto(config);
|
|
42
|
+
akanConfig.cloudHost[config.host] = GlobalConfig.toHostConfigDto(config);
|
|
40
43
|
await GlobalConfig.#setAkanGlobalConfig(akanConfig);
|
|
41
44
|
}
|
|
42
45
|
static async getLlmConfig(): Promise<AkanGlobalConfig["llm"]> {
|
|
@@ -88,6 +91,7 @@ export class GlobalConfig {
|
|
|
88
91
|
}
|
|
89
92
|
static toHostConfigDto(hostConfig: HostConfig): HostConfigDto {
|
|
90
93
|
return {
|
|
94
|
+
host: hostConfig.host,
|
|
91
95
|
auth: {
|
|
92
96
|
accessToken: hostConfig.auth?.accessToken
|
|
93
97
|
? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken)
|
|
@@ -98,6 +102,7 @@ export class GlobalConfig {
|
|
|
98
102
|
}
|
|
99
103
|
static toHostConfig(hostConfigDto: HostConfigDto): HostConfig {
|
|
100
104
|
return {
|
|
105
|
+
host: hostConfigDto.host,
|
|
101
106
|
auth: {
|
|
102
107
|
accessToken: hostConfigDto.auth?.accessToken
|
|
103
108
|
? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2-rc.0",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@langchain/openai": "^1.4.6",
|
|
33
33
|
"@tailwindcss/node": "^4.3.0",
|
|
34
34
|
"@trapezedev/project": "^7.1.4",
|
|
35
|
-
"akanjs": "2.2.
|
|
35
|
+
"akanjs": "2.2.2-rc.0",
|
|
36
36
|
"chalk": "^5.6.2",
|
|
37
37
|
"commander": "^14.0.3",
|
|
38
38
|
"daisyui": "^5.5.20",
|