@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.
@@ -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] ?? defaultHostConfig);
91
+ return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
93
92
  }
94
- static async setHostConfig(host = akanCloudHost, config = {}) {
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#refreshAuthToken() {
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(`/refreshRemoteAuthToken`, { refreshToken });
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 supportedLlmModels = [
337
- "deepseek-chat",
338
- "deepseek-reasoner"
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 = new ChatDeepSeek({
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: true,
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 = new ChatOpenAI({
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] ?? defaultHostConfig);
89
+ return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
91
90
  }
92
- static async setHostConfig(host = akanCloudHost, config = {}) {
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#refreshAuthToken() {
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(`/refreshRemoteAuthToken`, { refreshToken });
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 supportedLlmModels = [
335
- "deepseek-chat",
336
- "deepseek-reasoner"
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 = new ChatDeepSeek({
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: true,
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 = new ChatOpenAI({
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 = `${akanCloudUrl}/signin?remoteId=${remoteId}`;
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(akanCloudHost, {
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(akanCloudHost, {});
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: true });
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.1",
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.1",
38
+ "akanjs": "2.2.2",
39
39
  "chalk": "^5.6.2",
40
40
  "commander": "^14.0.3",
41
41
  "daisyui": "^5.5.20",