@akanjs/cli 2.1.1-rc.0 → 2.1.1-rc.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 +344 -132
- package/index.js +578 -167
- package/package.json +3 -2
|
@@ -17,10 +17,7 @@ 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/
|
|
21
|
-
import { mkdir } from "fs/promises";
|
|
22
|
-
|
|
23
|
-
// pkgs/@akanjs/devkit/constants.ts
|
|
20
|
+
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
24
21
|
var basePath = `${Bun.env.HOME ?? Bun.env.USERPROFILE}/.akan`;
|
|
25
22
|
var configPath = `${basePath}/config.json`;
|
|
26
23
|
var akanCloudHost = process.env.AKAN_PUBLIC_OPERATION_MODE === "local" ? "http://localhost" : "https://cloud.akanjs.com";
|
|
@@ -28,9 +25,14 @@ var akanCloudUrl = `${akanCloudHost}${process.env.AKAN_PUBLIC_OPERATION_MODE ===
|
|
|
28
25
|
var defaultHostConfig = {};
|
|
29
26
|
var defaultAkanGlobalConfig = {
|
|
30
27
|
cloudHost: {},
|
|
28
|
+
remoteEnvServers: {},
|
|
31
29
|
llm: null
|
|
32
30
|
};
|
|
33
31
|
|
|
32
|
+
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
33
|
+
import { mkdir } from "fs/promises";
|
|
34
|
+
import dayjs from "dayjs";
|
|
35
|
+
|
|
34
36
|
// pkgs/@akanjs/devkit/fileSys.ts
|
|
35
37
|
import { stat } from "fs/promises";
|
|
36
38
|
import { Logger } from "akanjs/common";
|
|
@@ -69,35 +71,198 @@ class FileSys {
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
// pkgs/@akanjs/devkit/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const akanConfig = await getAkanGlobalConfig();
|
|
84
|
-
return akanConfig.cloudHost[host] ?? defaultHostConfig;
|
|
85
|
-
};
|
|
86
|
-
var setHostConfig = async (host = akanCloudHost, config = {}) => {
|
|
87
|
-
const akanConfig = await getAkanGlobalConfig();
|
|
88
|
-
akanConfig.cloudHost[host] = config;
|
|
89
|
-
await setAkanGlobalConfig(akanConfig);
|
|
90
|
-
};
|
|
91
|
-
var getSelf = async (token) => {
|
|
92
|
-
try {
|
|
93
|
-
const res = await fetch(`${akanCloudUrl}/user/getSelf`, { headers: { Authorization: `Bearer ${token}` } });
|
|
94
|
-
const user = await res.json();
|
|
95
|
-
return user;
|
|
96
|
-
} catch (e) {
|
|
97
|
-
return null;
|
|
74
|
+
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
75
|
+
class GlobalConfig {
|
|
76
|
+
static async#getAkanGlobalConfig() {
|
|
77
|
+
const exists = await FileSys.fileExists(configPath);
|
|
78
|
+
const akanConfig = exists ? await FileSys.readJson(configPath) : {};
|
|
79
|
+
return {
|
|
80
|
+
...defaultAkanGlobalConfig,
|
|
81
|
+
...akanConfig,
|
|
82
|
+
cloudHost: akanConfig.cloudHost ?? defaultAkanGlobalConfig.cloudHost,
|
|
83
|
+
remoteEnvServers: akanConfig.remoteEnvServers ?? defaultAkanGlobalConfig.remoteEnvServers
|
|
84
|
+
};
|
|
98
85
|
}
|
|
99
|
-
|
|
86
|
+
static async#setAkanGlobalConfig(akanConfig) {
|
|
87
|
+
await mkdir(basePath, { recursive: true });
|
|
88
|
+
await Bun.write(configPath, JSON.stringify(akanConfig, null, 2));
|
|
89
|
+
}
|
|
90
|
+
static async getHostConfig(host = akanCloudHost) {
|
|
91
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
92
|
+
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? defaultHostConfig);
|
|
93
|
+
}
|
|
94
|
+
static async setHostConfig(host = akanCloudHost, config = {}) {
|
|
95
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
96
|
+
akanConfig.cloudHost[host] = GlobalConfig.toHostConfigDto(config);
|
|
97
|
+
await GlobalConfig.#setAkanGlobalConfig(akanConfig);
|
|
98
|
+
}
|
|
99
|
+
static async getLlmConfig() {
|
|
100
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
101
|
+
return akanConfig.llm ?? null;
|
|
102
|
+
}
|
|
103
|
+
static async setLlmConfig(llmConfig) {
|
|
104
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
105
|
+
await GlobalConfig.#setAkanGlobalConfig({ ...akanConfig, llm: llmConfig });
|
|
106
|
+
}
|
|
107
|
+
static async getRemoteEnvServers() {
|
|
108
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
109
|
+
return akanConfig.remoteEnvServers;
|
|
110
|
+
}
|
|
111
|
+
static async setRemoteEnvServer(name, config) {
|
|
112
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
113
|
+
await GlobalConfig.#setAkanGlobalConfig({
|
|
114
|
+
...akanConfig,
|
|
115
|
+
remoteEnvServers: {
|
|
116
|
+
...akanConfig.remoteEnvServers,
|
|
117
|
+
[name]: config
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
static async removeRemoteEnvServer(name) {
|
|
122
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
123
|
+
const { [name]: _, ...remoteEnvServers } = akanConfig.remoteEnvServers;
|
|
124
|
+
await GlobalConfig.#setAkanGlobalConfig({
|
|
125
|
+
...akanConfig,
|
|
126
|
+
remoteEnvServers
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
static needRefreshToken(accessToken) {
|
|
130
|
+
return !!accessToken?.expiresAt?.isBefore(dayjs().add(1, "hour"));
|
|
131
|
+
}
|
|
132
|
+
static toAccessToken(accessToken) {
|
|
133
|
+
return {
|
|
134
|
+
jwt: accessToken.jwt,
|
|
135
|
+
refreshToken: accessToken.refreshToken ?? null,
|
|
136
|
+
expiresAt: accessToken.expiresAt ? dayjs(accessToken.expiresAt) : null
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
static toAccessTokenDto(accessToken) {
|
|
140
|
+
return {
|
|
141
|
+
jwt: accessToken.jwt,
|
|
142
|
+
refreshToken: accessToken.refreshToken ?? null,
|
|
143
|
+
expiresAt: accessToken.expiresAt?.toString() ?? null
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
static toHostConfigDto(hostConfig) {
|
|
147
|
+
return {
|
|
148
|
+
auth: {
|
|
149
|
+
accessToken: hostConfig.auth?.accessToken ? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken) : undefined,
|
|
150
|
+
self: hostConfig.auth?.self
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
static toHostConfig(hostConfigDto) {
|
|
155
|
+
return {
|
|
156
|
+
auth: {
|
|
157
|
+
accessToken: hostConfigDto.auth?.accessToken ? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken) : undefined,
|
|
158
|
+
self: hostConfigDto.auth?.self
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
100
163
|
|
|
164
|
+
// pkgs/@akanjs/devkit/cloud/cloudApi.ts
|
|
165
|
+
class HttpClient {
|
|
166
|
+
baseUrl;
|
|
167
|
+
headers = {};
|
|
168
|
+
constructor(baseUrl, headers = {}) {
|
|
169
|
+
this.baseUrl = baseUrl;
|
|
170
|
+
this.headers = headers;
|
|
171
|
+
}
|
|
172
|
+
async get(url, { headers } = {}) {
|
|
173
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
174
|
+
headers: {
|
|
175
|
+
"Content-Type": "application/json",
|
|
176
|
+
...this.headers,
|
|
177
|
+
...headers
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return response.json();
|
|
181
|
+
}
|
|
182
|
+
async getFile(url, localPath, headers) {
|
|
183
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
184
|
+
headers: { ...this.headers, ...headers }
|
|
185
|
+
});
|
|
186
|
+
if (!response.ok)
|
|
187
|
+
throw new Error(`Failed to download file: ${response.status} ${response.statusText}`);
|
|
188
|
+
await Bun.write(localPath, response);
|
|
189
|
+
}
|
|
190
|
+
async post(url, data, { headers } = {}) {
|
|
191
|
+
const isFormData = data instanceof FormData;
|
|
192
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
193
|
+
method: "POST",
|
|
194
|
+
body: isFormData ? data : JSON.stringify(data),
|
|
195
|
+
headers: isFormData ? { ...this.headers, ...headers } : { "Content-Type": "application/json", ...this.headers, ...headers }
|
|
196
|
+
});
|
|
197
|
+
return response.json();
|
|
198
|
+
}
|
|
199
|
+
setHeaders(headers) {
|
|
200
|
+
Object.assign(this.headers, headers);
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
class CloudApi {
|
|
206
|
+
#api;
|
|
207
|
+
#accessToken = null;
|
|
208
|
+
static async fromHost(host) {
|
|
209
|
+
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
210
|
+
return new CloudApi(hostConfig);
|
|
211
|
+
}
|
|
212
|
+
constructor(hostConfig) {
|
|
213
|
+
const host = akanCloudHost;
|
|
214
|
+
this.#api = new HttpClient(`${host}/api`);
|
|
215
|
+
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
216
|
+
if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
|
|
217
|
+
this.#api.setHeaders({
|
|
218
|
+
Authorization: `Bearer ${this.#accessToken.jwt}`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async uploadEnv(devProjectId, file) {
|
|
222
|
+
const formData = new FormData;
|
|
223
|
+
formData.append("devProjectId", devProjectId);
|
|
224
|
+
formData.append("file", file);
|
|
225
|
+
const data = await this.#api.post(`/uploadEnv/${devProjectId}`, formData);
|
|
226
|
+
return data;
|
|
227
|
+
}
|
|
228
|
+
async downloadEnv(devProjectId, localPath) {
|
|
229
|
+
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
230
|
+
}
|
|
231
|
+
async getRemoteAuthToken(remoteId) {
|
|
232
|
+
try {
|
|
233
|
+
if (this.#accessToken) {
|
|
234
|
+
if (GlobalConfig.needRefreshToken(this.#accessToken))
|
|
235
|
+
return await this.refreshAuthToken();
|
|
236
|
+
else
|
|
237
|
+
return await this.refreshAuthToken();
|
|
238
|
+
}
|
|
239
|
+
const accessToken = await this.#api.get(`/getRemoteAuthToken/${remoteId}`);
|
|
240
|
+
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
241
|
+
this.#api.setHeaders({
|
|
242
|
+
Authorization: `Bearer ${this.#accessToken.jwt}`
|
|
243
|
+
});
|
|
244
|
+
return this.#accessToken;
|
|
245
|
+
} catch (_) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async refreshAuthToken() {
|
|
250
|
+
const response = await this.#api.post(`/refreshRemoteAuthToken`, {
|
|
251
|
+
refreshToken: this.#accessToken?.refreshToken
|
|
252
|
+
});
|
|
253
|
+
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
254
|
+
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
255
|
+
return this.#accessToken;
|
|
256
|
+
}
|
|
257
|
+
async getRemoteSelf() {
|
|
258
|
+
try {
|
|
259
|
+
const data = await this.#api.get(`/getRemoteSelf`);
|
|
260
|
+
return data;
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
101
266
|
// pkgs/@akanjs/devkit/spinner.ts
|
|
102
267
|
import ora from "ora";
|
|
103
268
|
|
|
@@ -160,12 +325,18 @@ class Spinner {
|
|
|
160
325
|
|
|
161
326
|
// pkgs/@akanjs/devkit/aiEditor.ts
|
|
162
327
|
var MAX_ASK_TRY = 300;
|
|
163
|
-
var supportedLlmModels = [
|
|
328
|
+
var supportedLlmModels = [
|
|
329
|
+
"deepseek-chat",
|
|
330
|
+
"deepseek-reasoner"
|
|
331
|
+
];
|
|
164
332
|
|
|
165
333
|
class AiSession {
|
|
166
334
|
static #cacheDir = "node_modules/.cache/akan/aiSession";
|
|
167
335
|
static #chat = null;
|
|
168
|
-
static async init({
|
|
336
|
+
static async init({
|
|
337
|
+
temperature = 0,
|
|
338
|
+
useExisting = true
|
|
339
|
+
} = {}) {
|
|
169
340
|
if (useExisting) {
|
|
170
341
|
const llmConfig2 = await AiSession.getLlmConfig();
|
|
171
342
|
if (llmConfig2) {
|
|
@@ -192,22 +363,24 @@ class AiSession {
|
|
|
192
363
|
return AiSession;
|
|
193
364
|
}
|
|
194
365
|
static async getLlmConfig() {
|
|
195
|
-
|
|
196
|
-
return akanConfig.llm ?? null;
|
|
366
|
+
return await GlobalConfig.getLlmConfig();
|
|
197
367
|
}
|
|
198
368
|
static async setLlmConfig(llmConfig) {
|
|
199
|
-
|
|
200
|
-
akanConfig.llm = llmConfig;
|
|
201
|
-
await setAkanGlobalConfig(akanConfig);
|
|
369
|
+
await GlobalConfig.setLlmConfig(llmConfig);
|
|
202
370
|
return AiSession;
|
|
203
371
|
}
|
|
204
372
|
static async#requestLlmConfig() {
|
|
205
|
-
const model = await select({
|
|
373
|
+
const model = await select({
|
|
374
|
+
message: "Select a LLM model",
|
|
375
|
+
choices: supportedLlmModels
|
|
376
|
+
});
|
|
206
377
|
const apiKey = await input({ message: "Enter your API key" });
|
|
207
378
|
return { model, apiKey };
|
|
208
379
|
}
|
|
209
380
|
static async#validateApiKey(modelName, apiKey) {
|
|
210
|
-
const spinner = new Spinner("Validating LLM API key...", {
|
|
381
|
+
const spinner = new Spinner("Validating LLM API key...", {
|
|
382
|
+
prefix: `\uD83E\uDD16akan-editor`
|
|
383
|
+
}).start();
|
|
211
384
|
const chat = new ChatOpenAI({
|
|
212
385
|
modelName,
|
|
213
386
|
temperature: 0,
|
|
@@ -230,7 +403,11 @@ class AiSession {
|
|
|
230
403
|
sessionKey;
|
|
231
404
|
isCacheLoaded = false;
|
|
232
405
|
workspace;
|
|
233
|
-
constructor(type, {
|
|
406
|
+
constructor(type, {
|
|
407
|
+
workspace,
|
|
408
|
+
cacheKey,
|
|
409
|
+
isContinued
|
|
410
|
+
}) {
|
|
234
411
|
this.workspace = workspace;
|
|
235
412
|
this.sessionKey = `${type}${cacheKey ? `-${cacheKey}` : ""}`;
|
|
236
413
|
if (isContinued)
|
|
@@ -305,7 +482,13 @@ class AiSession {
|
|
|
305
482
|
throw new Error("Failed to stream response");
|
|
306
483
|
}
|
|
307
484
|
}
|
|
308
|
-
async edit(question, {
|
|
485
|
+
async edit(question, {
|
|
486
|
+
onChunk,
|
|
487
|
+
onReasoning,
|
|
488
|
+
maxTry = MAX_ASK_TRY,
|
|
489
|
+
validate,
|
|
490
|
+
approve
|
|
491
|
+
} = {}) {
|
|
309
492
|
for (let tryCount = 0;tryCount < maxTry; tryCount++) {
|
|
310
493
|
let response = await this.ask(question, { onChunk, onReasoning });
|
|
311
494
|
if (validate?.length && tryCount === 0) {
|
|
@@ -358,7 +541,9 @@ ${validate.map((v) => `- ${v}`).join(`
|
|
|
358
541
|
async#tryFixTypescripts(writes, executor, options = {}) {
|
|
359
542
|
const MAX_EDIT_TRY = 5;
|
|
360
543
|
for (let tryCount = 0;tryCount < MAX_EDIT_TRY; tryCount++) {
|
|
361
|
-
const loader = new Spinner(`Type checking and linting...`, {
|
|
544
|
+
const loader = new Spinner(`Type checking and linting...`, {
|
|
545
|
+
prefix: `\uD83E\uDD16akan-editor`
|
|
546
|
+
}).start();
|
|
362
547
|
const fileChecks = await Promise.all(writes.map(async ({ filePath }) => {
|
|
363
548
|
const typeCheckResult = executor.typeCheck(filePath);
|
|
364
549
|
const lintResult = await executor.lint(filePath);
|
|
@@ -2166,11 +2351,26 @@ class Executor {
|
|
|
2166
2351
|
});
|
|
2167
2352
|
return new Promise((resolve, reject) => {
|
|
2168
2353
|
proc.on("error", (error) => {
|
|
2169
|
-
reject(new CommandExecutionError({
|
|
2354
|
+
reject(new CommandExecutionError({
|
|
2355
|
+
command,
|
|
2356
|
+
cwd,
|
|
2357
|
+
code: null,
|
|
2358
|
+
signal: null,
|
|
2359
|
+
stdout,
|
|
2360
|
+
stderr,
|
|
2361
|
+
cause: error
|
|
2362
|
+
}));
|
|
2170
2363
|
});
|
|
2171
2364
|
proc.on("exit", (code, signal) => {
|
|
2172
2365
|
if (!!code || signal)
|
|
2173
|
-
reject(new CommandExecutionError({
|
|
2366
|
+
reject(new CommandExecutionError({
|
|
2367
|
+
command,
|
|
2368
|
+
cwd,
|
|
2369
|
+
code,
|
|
2370
|
+
signal,
|
|
2371
|
+
stdout,
|
|
2372
|
+
stderr
|
|
2373
|
+
}));
|
|
2174
2374
|
else
|
|
2175
2375
|
resolve({ code, signal });
|
|
2176
2376
|
});
|
|
@@ -2196,11 +2396,28 @@ class Executor {
|
|
|
2196
2396
|
});
|
|
2197
2397
|
return new Promise((resolve, reject) => {
|
|
2198
2398
|
proc.on("error", (error) => {
|
|
2199
|
-
reject(new CommandExecutionError({
|
|
2399
|
+
reject(new CommandExecutionError({
|
|
2400
|
+
command,
|
|
2401
|
+
args,
|
|
2402
|
+
cwd,
|
|
2403
|
+
code: null,
|
|
2404
|
+
signal: null,
|
|
2405
|
+
stdout,
|
|
2406
|
+
stderr,
|
|
2407
|
+
cause: error
|
|
2408
|
+
}));
|
|
2200
2409
|
});
|
|
2201
2410
|
proc.on("close", (code, signal) => {
|
|
2202
2411
|
if (code !== 0 || signal)
|
|
2203
|
-
reject(new CommandExecutionError({
|
|
2412
|
+
reject(new CommandExecutionError({
|
|
2413
|
+
command,
|
|
2414
|
+
args,
|
|
2415
|
+
cwd,
|
|
2416
|
+
code,
|
|
2417
|
+
signal,
|
|
2418
|
+
stdout,
|
|
2419
|
+
stderr
|
|
2420
|
+
}));
|
|
2204
2421
|
else
|
|
2205
2422
|
resolve(stdout);
|
|
2206
2423
|
});
|
|
@@ -2244,7 +2461,15 @@ class Executor {
|
|
|
2244
2461
|
});
|
|
2245
2462
|
proc.on("exit", (code, signal) => {
|
|
2246
2463
|
if (!!code || signal)
|
|
2247
|
-
reject(new CommandExecutionError({
|
|
2464
|
+
reject(new CommandExecutionError({
|
|
2465
|
+
command: modulePath,
|
|
2466
|
+
args,
|
|
2467
|
+
cwd,
|
|
2468
|
+
code,
|
|
2469
|
+
signal,
|
|
2470
|
+
stdout,
|
|
2471
|
+
stderr
|
|
2472
|
+
}));
|
|
2248
2473
|
else
|
|
2249
2474
|
resolve({ code, signal });
|
|
2250
2475
|
});
|
|
@@ -2391,7 +2616,10 @@ class Executor {
|
|
|
2391
2616
|
const result = {
|
|
2392
2617
|
...extendsTsconfig,
|
|
2393
2618
|
...tsconfig,
|
|
2394
|
-
compilerOptions: {
|
|
2619
|
+
compilerOptions: {
|
|
2620
|
+
...extendsTsconfig.compilerOptions,
|
|
2621
|
+
...tsconfig.compilerOptions
|
|
2622
|
+
}
|
|
2395
2623
|
};
|
|
2396
2624
|
this.#tsconfig = result;
|
|
2397
2625
|
return result;
|
|
@@ -2446,7 +2674,9 @@ class Executor {
|
|
|
2446
2674
|
const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), targetPath.slice(0, -9));
|
|
2447
2675
|
const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
|
|
2448
2676
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2449
|
-
return this.writeFile(convertedTargetPath, convertedContent, {
|
|
2677
|
+
return this.writeFile(convertedTargetPath, convertedContent, {
|
|
2678
|
+
overwrite
|
|
2679
|
+
});
|
|
2450
2680
|
} else if (staticTemplateFileExtensions.has(path7.extname(targetPath).toLowerCase())) {
|
|
2451
2681
|
const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
|
|
2452
2682
|
const writePath = this.getPath(convertedTargetPath);
|
|
@@ -2472,14 +2702,24 @@ class Executor {
|
|
|
2472
2702
|
const prefixTemplatePath = templatePath;
|
|
2473
2703
|
if ((await stat2(prefixTemplatePath)).isFile()) {
|
|
2474
2704
|
const filename = path7.basename(prefixTemplatePath);
|
|
2475
|
-
const fileContent = await this.#applyTemplateFile({
|
|
2705
|
+
const fileContent = await this.#applyTemplateFile({
|
|
2706
|
+
templatePath: prefixTemplatePath,
|
|
2707
|
+
targetPath: path7.join(basePath2, filename),
|
|
2708
|
+
scanInfo,
|
|
2709
|
+
overwrite
|
|
2710
|
+
}, dict, options);
|
|
2476
2711
|
return fileContent ? [fileContent] : [];
|
|
2477
2712
|
} else {
|
|
2478
2713
|
const subdirs = await readDirEntries(templatePath);
|
|
2479
2714
|
const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
|
|
2480
2715
|
const subpath = path7.join(templatePath, subdir);
|
|
2481
2716
|
if ((await stat2(subpath)).isFile()) {
|
|
2482
|
-
const fileContent = await this.#applyTemplateFile({
|
|
2717
|
+
const fileContent = await this.#applyTemplateFile({
|
|
2718
|
+
templatePath: subpath,
|
|
2719
|
+
targetPath: path7.join(basePath2, subdir),
|
|
2720
|
+
scanInfo,
|
|
2721
|
+
overwrite
|
|
2722
|
+
}, dict, options);
|
|
2483
2723
|
return fileContent ? [fileContent] : [];
|
|
2484
2724
|
} else
|
|
2485
2725
|
return await this._applyTemplate({
|
|
@@ -2530,7 +2770,10 @@ class Executor {
|
|
|
2530
2770
|
async lint(filePath, { fix = false, dryRun = false } = {}) {
|
|
2531
2771
|
const path8 = this.getPath(filePath);
|
|
2532
2772
|
const linter = this.getLinter();
|
|
2533
|
-
const { results, errors, warnings } = await linter.lint(path8, {
|
|
2773
|
+
const { results, errors, warnings } = await linter.lint(path8, {
|
|
2774
|
+
fix,
|
|
2775
|
+
dryRun
|
|
2776
|
+
});
|
|
2534
2777
|
const message = linter.formatLintResults(results);
|
|
2535
2778
|
return { results, message, errors, warnings };
|
|
2536
2779
|
}
|
|
@@ -2556,6 +2799,7 @@ class WorkspaceExecutor extends Executor {
|
|
|
2556
2799
|
const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
|
|
2557
2800
|
const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
|
|
2558
2801
|
const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
|
|
2802
|
+
const workspaceId = sourceEnv.AKAN_WORKSPACE_ID;
|
|
2559
2803
|
const repoName = sourceEnv.AKAN_PUBLIC_REPO_NAME;
|
|
2560
2804
|
if (!repoName)
|
|
2561
2805
|
throw new Error("AKAN_PUBLIC_REPO_NAME is not set");
|
|
@@ -2566,7 +2810,15 @@ class WorkspaceExecutor extends Executor {
|
|
|
2566
2810
|
const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
|
|
2567
2811
|
if (!env)
|
|
2568
2812
|
throw new Error("AKAN_PUBLIC_ENV is not set");
|
|
2569
|
-
return { ...appName ? { appName } : {}, workspaceRoot, repoName, serveDomain, env, portOffset };
|
|
2813
|
+
return { ...appName ? { appName } : {}, workspaceRoot, repoName, serveDomain, env, portOffset, workspaceId };
|
|
2814
|
+
}
|
|
2815
|
+
getWorkspaceId({
|
|
2816
|
+
allowEmpty
|
|
2817
|
+
} = {}) {
|
|
2818
|
+
const { workspaceId } = WorkspaceExecutor.getBaseDevEnv();
|
|
2819
|
+
if (!workspaceId && !allowEmpty)
|
|
2820
|
+
throw new Error("Workspace ID is not found");
|
|
2821
|
+
return workspaceId;
|
|
2570
2822
|
}
|
|
2571
2823
|
async scan() {
|
|
2572
2824
|
return await WorkspaceInfo.fromExecutor(this);
|
|
@@ -2770,7 +3022,11 @@ class SysExecutor extends Executor {
|
|
|
2770
3022
|
} = {}) {
|
|
2771
3023
|
if (this.#scanInfo && !refresh)
|
|
2772
3024
|
return this.#scanInfo;
|
|
2773
|
-
const scanInfo = this.type === "app" ? await AppInfo.fromExecutor(this, {
|
|
3025
|
+
const scanInfo = this.type === "app" ? await AppInfo.fromExecutor(this, {
|
|
3026
|
+
refresh
|
|
3027
|
+
}) : await LibInfo.fromExecutor(this, {
|
|
3028
|
+
refresh
|
|
3029
|
+
});
|
|
2774
3030
|
if (write) {
|
|
2775
3031
|
await Promise.all(this.#getScanTemplateTasks(scanInfo));
|
|
2776
3032
|
await this.writeJson(`akan.${this.type}.json`, scanInfo.getScanResult());
|
|
@@ -2879,7 +3135,11 @@ class SysExecutor extends Executor {
|
|
|
2879
3135
|
...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
|
|
2880
3136
|
};
|
|
2881
3137
|
const scanInfo = await this.scan();
|
|
2882
|
-
const fileContents = await this._applyTemplate({
|
|
3138
|
+
const fileContents = await this._applyTemplate({
|
|
3139
|
+
...options,
|
|
3140
|
+
scanInfo,
|
|
3141
|
+
dict
|
|
3142
|
+
});
|
|
2883
3143
|
await this.scan();
|
|
2884
3144
|
return fileContents;
|
|
2885
3145
|
}
|
|
@@ -2985,7 +3245,11 @@ class AppExecutor extends SysExecutor {
|
|
|
2985
3245
|
this.#pageKeys = [];
|
|
2986
3246
|
return this.#pageKeys;
|
|
2987
3247
|
}
|
|
2988
|
-
for await (const rel of glob.scan({
|
|
3248
|
+
for await (const rel of glob.scan({
|
|
3249
|
+
cwd: pageDir,
|
|
3250
|
+
absolute: false,
|
|
3251
|
+
onlyFiles: true
|
|
3252
|
+
})) {
|
|
2989
3253
|
const segments = rel.split(path7.sep);
|
|
2990
3254
|
if (segments.some((s) => s === "node_modules"))
|
|
2991
3255
|
continue;
|
|
@@ -2995,7 +3259,10 @@ class AppExecutor extends SysExecutor {
|
|
|
2995
3259
|
if (!isRouteSourceFile(posix))
|
|
2996
3260
|
continue;
|
|
2997
3261
|
const key = `./${posix}`;
|
|
2998
|
-
validateSubRoutePageKey(key, akanConfig2.basePaths, {
|
|
3262
|
+
validateSubRoutePageKey(key, akanConfig2.basePaths, {
|
|
3263
|
+
appName: this.name,
|
|
3264
|
+
filePath: absPath
|
|
3265
|
+
});
|
|
2999
3266
|
const parsed = parseRouteModuleKey(key);
|
|
3000
3267
|
if (parsed.isInternalRootLayout) {
|
|
3001
3268
|
throw new Error(`[route-convention] __root_layout is reserved for Akan.js generated root layout: ${absPath}`);
|
|
@@ -3035,7 +3302,11 @@ class AppExecutor extends SysExecutor {
|
|
|
3035
3302
|
]);
|
|
3036
3303
|
}
|
|
3037
3304
|
async scanSync({ refresh = false, write = true } = {}) {
|
|
3038
|
-
const scanInfo = await this.scan({
|
|
3305
|
+
const scanInfo = await this.scan({
|
|
3306
|
+
refresh,
|
|
3307
|
+
write,
|
|
3308
|
+
writeLib: write
|
|
3309
|
+
});
|
|
3039
3310
|
if (write)
|
|
3040
3311
|
await this.syncAssets(scanInfo.getScanResult().libDeps);
|
|
3041
3312
|
return scanInfo;
|
|
@@ -3099,7 +3370,10 @@ class PkgExecutor extends Executor {
|
|
|
3099
3370
|
return scanInfo;
|
|
3100
3371
|
}
|
|
3101
3372
|
async#getDependencyVersion(rootPackageJson, dep) {
|
|
3102
|
-
const rootDeps = {
|
|
3373
|
+
const rootDeps = {
|
|
3374
|
+
...rootPackageJson.dependencies,
|
|
3375
|
+
...rootPackageJson.devDependencies
|
|
3376
|
+
};
|
|
3103
3377
|
const rootVersion = rootDeps[dep];
|
|
3104
3378
|
if (rootVersion)
|
|
3105
3379
|
return rootVersion;
|
|
@@ -3155,7 +3429,14 @@ class PkgExecutor extends Executor {
|
|
|
3155
3429
|
const distPkgJson = {
|
|
3156
3430
|
...pkgJson,
|
|
3157
3431
|
type: "module",
|
|
3158
|
-
exports: {
|
|
3432
|
+
exports: {
|
|
3433
|
+
...pkgJson.exports,
|
|
3434
|
+
".": {
|
|
3435
|
+
import: "./index.ts",
|
|
3436
|
+
types: "./index.ts",
|
|
3437
|
+
default: "./index.ts"
|
|
3438
|
+
}
|
|
3439
|
+
},
|
|
3159
3440
|
engines: { bun: ">=1.3.13" },
|
|
3160
3441
|
...dependencyMaps
|
|
3161
3442
|
};
|
|
@@ -7449,7 +7730,7 @@ class ApplicationBuildRunner {
|
|
|
7449
7730
|
import { cp, mkdir as mkdir8, rm as rm3 } from "fs/promises";
|
|
7450
7731
|
|
|
7451
7732
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
7452
|
-
import { HttpClient, Logger as Logger10 } from "akanjs/common";
|
|
7733
|
+
import { HttpClient as HttpClient2, Logger as Logger10 } from "akanjs/common";
|
|
7453
7734
|
var spinning = (message) => {
|
|
7454
7735
|
const spinner = new Spinner(message, { prefix: message, enableSpin: true }).start();
|
|
7455
7736
|
return spinner;
|
|
@@ -7464,7 +7745,7 @@ var uploadRelease = async (appName, {
|
|
|
7464
7745
|
}) => {
|
|
7465
7746
|
const logger = new Logger10("uploadRelease");
|
|
7466
7747
|
const basePath2 = local ? "http://localhost:8282/backend" : "https://cloud.akanjs.com/backend";
|
|
7467
|
-
const httpClient = new
|
|
7748
|
+
const httpClient = new HttpClient2(basePath2);
|
|
7468
7749
|
const buildPath = `${workspaceRoot}/releases/builds/${appName}-release.tar.gz`;
|
|
7469
7750
|
const appBuildPath = `${workspaceRoot}/releases/builds/${appName}-appBuild.zip`;
|
|
7470
7751
|
const sourcePath = `${workspaceRoot}/releases/sources/${appName}-source.tar.gz`;
|
|
@@ -9451,75 +9732,6 @@ import { Box as Box2, Newline, Text as Text2, useInput as useInput2 } from "ink"
|
|
|
9451
9732
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
9452
9733
|
import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
9453
9734
|
"use client";
|
|
9454
|
-
// pkgs/@akanjs/devkit/cloud/cloudApi.ts
|
|
9455
|
-
class HttpClient2 {
|
|
9456
|
-
baseUrl;
|
|
9457
|
-
constructor(baseUrl) {
|
|
9458
|
-
this.baseUrl = baseUrl;
|
|
9459
|
-
}
|
|
9460
|
-
async get(url, { headers } = {}) {
|
|
9461
|
-
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
9462
|
-
headers: { "Content-Type": "application/json", ...headers }
|
|
9463
|
-
});
|
|
9464
|
-
return response.json();
|
|
9465
|
-
}
|
|
9466
|
-
async post(url, data, { headers } = {}) {
|
|
9467
|
-
const isFormData = data instanceof FormData;
|
|
9468
|
-
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
9469
|
-
method: "POST",
|
|
9470
|
-
body: isFormData ? data : JSON.stringify(data),
|
|
9471
|
-
headers: isFormData ? headers : { "Content-Type": "application/json", ...headers }
|
|
9472
|
-
});
|
|
9473
|
-
return response.json();
|
|
9474
|
-
}
|
|
9475
|
-
}
|
|
9476
|
-
|
|
9477
|
-
class CloudApi {
|
|
9478
|
-
api;
|
|
9479
|
-
#accessToken = null;
|
|
9480
|
-
constructor(host, { accessToken } = {}) {
|
|
9481
|
-
this.api = new HttpClient2(`${host}/api`);
|
|
9482
|
-
this.#accessToken = accessToken ?? null;
|
|
9483
|
-
}
|
|
9484
|
-
async uploadEnv(devProjectId, fileStream) {
|
|
9485
|
-
const formData = new FormData;
|
|
9486
|
-
formData.append("devProjectId", devProjectId);
|
|
9487
|
-
formData.append("fileStream", await new Response(fileStream).blob());
|
|
9488
|
-
const response = await this.api.post(`/uploadEnv/${devProjectId}`, formData);
|
|
9489
|
-
return response.success;
|
|
9490
|
-
}
|
|
9491
|
-
async downloadEnv(devProjectId) {
|
|
9492
|
-
const response = await this.api.get(`/downloadEnv/${devProjectId}`);
|
|
9493
|
-
return response.success;
|
|
9494
|
-
}
|
|
9495
|
-
async getRemoteAuthToken(remoteId) {
|
|
9496
|
-
if (this.#needRefreshToken())
|
|
9497
|
-
return await this.refreshAuthToken();
|
|
9498
|
-
else if (this.#accessToken)
|
|
9499
|
-
return this.#accessToken;
|
|
9500
|
-
const accessToken = await this.api.get(`/getRemoteAuthToken/${remoteId}`);
|
|
9501
|
-
this.#accessToken = {
|
|
9502
|
-
jwt: accessToken.jwt,
|
|
9503
|
-
refreshToken: accessToken.refreshToken,
|
|
9504
|
-
expiresAt: new Date(accessToken.expiresAt)
|
|
9505
|
-
};
|
|
9506
|
-
return accessToken;
|
|
9507
|
-
}
|
|
9508
|
-
async refreshAuthToken() {
|
|
9509
|
-
const response = await this.api.post(`/refreshRemoteAuthToken`, {
|
|
9510
|
-
refreshToken: this.#accessToken?.refreshToken
|
|
9511
|
-
});
|
|
9512
|
-
this.#accessToken = {
|
|
9513
|
-
jwt: response.jwt,
|
|
9514
|
-
refreshToken: response.refreshToken,
|
|
9515
|
-
expiresAt: new Date(response.expiresAt)
|
|
9516
|
-
};
|
|
9517
|
-
return response;
|
|
9518
|
-
}
|
|
9519
|
-
#needRefreshToken() {
|
|
9520
|
-
return !!(this.#accessToken?.expiresAt && this.#accessToken.expiresAt.getTime() < Date.now() - 1000 * 60 * 60);
|
|
9521
|
-
}
|
|
9522
|
-
}
|
|
9523
9735
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
9524
9736
|
import { Logger as Logger12 } from "akanjs/common";
|
|
9525
9737
|
|