@botonic/nx-plugin 2.29.0 → 2.31.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/executors.json +0 -5
  3. package/package.json +3 -2
  4. package/src/executors/delete-bot/executor.js +0 -2
  5. package/src/executors/deploy-to-hubtype/executor.js +24 -160
  6. package/src/executors/e2e-webchat/botonic-package-publish.spec.ts +7 -11
  7. package/src/executors/integrate-provider/executor.js +0 -2
  8. package/src/executors/login-to-hubtype/executor.js +0 -2
  9. package/src/executors/logout-from-hubtype/executor.js +2 -2
  10. package/src/executors/serve-bot/executor.js +142 -24
  11. package/src/executors/serve-bot/schema.json +13 -5
  12. package/src/generators/bot-app/files/src/client/webchat/index.tsx.template +2 -0
  13. package/src/generators/bot-app/files/src/client/webchat/webchat.tsx.template +111 -0
  14. package/src/generators/bot-app/files/src/server/bot/plugins/flow-builder/index.ts.template +13 -4
  15. package/src/generators/bot-app/files/src/server/bot/plugins/index.ts.template +0 -3
  16. package/src/generators/bot-app/files/src/server/lambda/handler.js.template +1 -1
  17. package/src/generators/bot-app/files/src/server/lambda/package.json +3 -3
  18. package/src/generators/bot-app/files/vite/node.config.ts.template +2 -4
  19. package/src/generators/bot-app/files/vite/webchat.config.ts.template +20 -1
  20. package/src/generators/bot-app/generator.js +6 -5
  21. package/src/generators/bot-app/lilara-version.json +1 -1
  22. package/src/lib/api-service.d.ts +19 -20
  23. package/src/lib/api-service.js +150 -82
  24. package/src/lib/bot-config.d.ts +10 -7
  25. package/src/lib/bot-config.js +5 -1
  26. package/src/lib/constants.d.ts +2 -3
  27. package/src/lib/constants.js +6 -9
  28. package/src/lib/credentials-handler.d.ts +9 -18
  29. package/src/lib/credentials-handler.js +42 -24
  30. package/src/lib/interfaces.d.ts +10 -13
  31. package/src/lib/util/executor-helpers.d.ts +58 -18
  32. package/src/lib/util/executor-helpers.js +501 -102
  33. package/src/plugin.js +6 -15
  34. package/src/executors/deploy-local-runtime/executor.d.ts +0 -5
  35. package/src/executors/deploy-local-runtime/executor.js +0 -144
  36. package/src/executors/deploy-local-runtime/schema.d.js +0 -16
  37. package/src/executors/deploy-local-runtime/schema.json +0 -34
  38. package/src/generators/bot-app/files/src/server/bot/tracking.ts.template +0 -35
  39. package/src/generators/preset/files/package.json +0 -26
@@ -39,7 +39,6 @@ var import_path = require("path");
39
39
  var import_qs = require("qs");
40
40
  var import_constants = require("./constants");
41
41
  var import_credentials_handler = require("./credentials-handler");
42
- var import_file_system = require("./util/file-system");
43
42
  const FormData = require("form-data");
44
43
  const BOTONIC_CLIENT_ID = process.env["VITE_HUBTYPE_CLIENT_ID"] || "";
45
44
  const BOTONIC_URL = process.env["VITE_HUBTYPE_API_URL"] || "";
@@ -51,7 +50,6 @@ const resolveEnvironment = (environmentVariables) => {
51
50
  };
52
51
  class BotonicAPIService {
53
52
  constructor(config = {
54
- isLocalRuntimeDeployment: false,
55
53
  projectRoot: void 0,
56
54
  environmentVariables: {},
57
55
  targetEnvironment: "local"
@@ -60,7 +58,6 @@ class BotonicAPIService {
60
58
  this.baseUrl = BOTONIC_URL;
61
59
  this.loginUrl = BOTONIC_URL + "/o/token/";
62
60
  this.bot = null;
63
- this.localRuntimeBot = null;
64
61
  this.headers = new import_axios.AxiosHeaders();
65
62
  const { hubtypeClientId, hubtypeApiUrl } = resolveEnvironment(
66
63
  config.environmentVariables || {}
@@ -71,25 +68,12 @@ class BotonicAPIService {
71
68
  this.globalCredentialsHandler = config.workspaceRoot != null ? new import_credentials_handler.GlobalCredentialsHandler(
72
69
  (0, import_path.join)(config.workspaceRoot, import_constants.BOTONIC_HOME_DIRNAME)
73
70
  ) : new import_credentials_handler.GlobalCredentialsHandler();
74
- this.botCredentialsHandler = new import_credentials_handler.BotCredentialsHandler(config.projectRoot);
75
- this.isLocalRuntimeDeployment = config.isLocalRuntimeDeployment;
71
+ this.botsRegistryHandler = new import_credentials_handler.BotsRegistryHandler();
76
72
  this.projectRoot = config.projectRoot;
77
73
  this.targetEnvironment = config.targetEnvironment;
78
74
  this.loadGlobalCredentials();
79
- this.loadBotCredentials();
75
+ this.loadBotFromRegistry();
80
76
  this.setHeaders(this.oauth?.access_token);
81
- if (this.isLocalRuntimeDeployment && this.projectRoot) {
82
- try {
83
- const appBotPath = (0, import_path.join)(this.projectRoot, import_constants.BOT_CREDENTIALS_FILENAME);
84
- if ((0, import_fs.existsSync)(appBotPath)) {
85
- const appCreds = (0, import_file_system.readJSON)(appBotPath);
86
- if (appCreds?.bot) {
87
- this.localRuntimeBot = appCreds.bot;
88
- }
89
- }
90
- } catch {
91
- }
92
- }
93
77
  this.apiClient = import_axios.default.create({
94
78
  baseURL: this.baseUrl
95
79
  // Remove headers from here - we'll pass them explicitly in each request
@@ -128,22 +112,13 @@ class BotonicAPIService {
128
112
  }
129
113
  saveAllCredentials() {
130
114
  this.saveGlobalCredentials();
131
- if (!this.isLocalRuntimeDeployment) {
132
- this.saveBotCredentials();
133
- }
115
+ this.saveToBotsRegistry();
134
116
  }
135
117
  botInfo() {
136
- if (this.isLocalRuntimeDeployment) {
137
- if (!this.localRuntimeBot) {
138
- throw new Error("Not local runtime bot info available");
139
- }
140
- return this.localRuntimeBot;
141
- } else {
142
- if (!this.bot) {
143
- throw new Error("Not bot info available");
144
- }
145
- return this.bot;
118
+ if (!this.bot) {
119
+ throw new Error("No bot selected");
146
120
  }
121
+ return this.bot;
147
122
  }
148
123
  getOauth() {
149
124
  if (!this.oauth) {
@@ -154,10 +129,6 @@ class BotonicAPIService {
154
129
  isAuthenticated() {
155
130
  return !!this.oauth?.access_token;
156
131
  }
157
- /** True when in local runtime mode and a bot was loaded (e.g. from app .botonic.json). */
158
- hasLocalRuntimeBot() {
159
- return Boolean(this.isLocalRuntimeDeployment && this.localRuntimeBot);
160
- }
161
132
  async ensureAuthenticated() {
162
133
  if (!this.isAuthenticated()) {
163
134
  throw new Error("Not authenticated. Please login first.");
@@ -173,9 +144,16 @@ class BotonicAPIService {
173
144
  }
174
145
  setCurrentBot(bot) {
175
146
  this.bot = bot;
147
+ this.saveToBotsRegistry();
176
148
  }
177
- setLocalRuntimeBot(bot) {
178
- this.localRuntimeBot = bot;
149
+ async selectBot(bot) {
150
+ this.bot = bot;
151
+ try {
152
+ const providers = await this.fetchProviders(bot.id);
153
+ this.bot = { ...bot, providers };
154
+ } catch {
155
+ }
156
+ this.saveToBotsRegistry();
179
157
  }
180
158
  loadGlobalCredentials() {
181
159
  const store = this.normalizeCredentialsStore(
@@ -202,10 +180,53 @@ class BotonicAPIService {
202
180
  }
203
181
  return store;
204
182
  }
205
- loadBotCredentials() {
206
- const credentials = this.botCredentialsHandler.load();
207
- if (credentials?.bot) {
208
- this.bot = credentials.bot;
183
+ loadBotFromRegistry() {
184
+ try {
185
+ const entry = this.botsRegistryHandler.findLatestForEnv(
186
+ this.targetEnvironment ?? "local"
187
+ );
188
+ if (!entry) return;
189
+ this.bot = entry;
190
+ } catch {
191
+ }
192
+ }
193
+ saveToBotsRegistry() {
194
+ try {
195
+ if (!this.bot) return;
196
+ this.botsRegistryHandler.upsert(
197
+ this.targetEnvironment ?? "local",
198
+ this.bot
199
+ );
200
+ } catch (e) {
201
+ console.warn("bots.json could not be saved:", e);
202
+ }
203
+ }
204
+ saveProviders(providers) {
205
+ try {
206
+ if (!this.bot) return;
207
+ this.bot = { ...this.bot, providers };
208
+ this.botsRegistryHandler.upsert(
209
+ this.targetEnvironment ?? "local",
210
+ this.bot
211
+ );
212
+ } catch (e) {
213
+ console.warn("bots.json providers could not be saved:", e);
214
+ }
215
+ }
216
+ async fetchProviders(botId) {
217
+ const resp = await this.getProviders(botId);
218
+ return (resp.data.results ?? []).map((p) => ({
219
+ id: p.id,
220
+ provider: p.provider ?? "",
221
+ is_test: p.is_test ?? false,
222
+ is_active: p.is_active ?? false
223
+ }));
224
+ }
225
+ saveBotEntry(bot) {
226
+ try {
227
+ this.botsRegistryHandler.upsert(this.targetEnvironment ?? "local", bot);
228
+ } catch (e) {
229
+ console.warn("bots.json could not be saved:", e);
209
230
  }
210
231
  }
211
232
  setHeaders(accessToken) {
@@ -227,11 +248,6 @@ class BotonicAPIService {
227
248
  };
228
249
  this.globalCredentialsHandler.dump(store);
229
250
  }
230
- saveBotCredentials() {
231
- this.botCredentialsHandler.dump({
232
- bot: this.bot
233
- });
234
- }
235
251
  async refreshToken() {
236
252
  if (!this.oauth?.refresh_token) {
237
253
  throw new Error("No refresh token available");
@@ -257,6 +273,7 @@ class BotonicAPIService {
257
273
  throw new Error("Invalid token refresh response: missing access_token");
258
274
  }
259
275
  this.oauth = oauthResponse.data;
276
+ this._tokenExpiresAt = Date.now() + oauthResponse.data.expires_in * 1e3;
260
277
  const accessToken = this.getOauth().access_token;
261
278
  this.setHeaders(accessToken);
262
279
  this.saveGlobalCredentials();
@@ -279,6 +296,7 @@ class BotonicAPIService {
279
296
  }
280
297
  });
281
298
  this.oauth = loginResponse.data;
299
+ this._tokenExpiresAt = Date.now() + loginResponse.data.expires_in * 1e3;
282
300
  const accessToken = this.getOauth().access_token;
283
301
  this.setHeaders(accessToken);
284
302
  try {
@@ -296,11 +314,11 @@ class BotonicAPIService {
296
314
  console.log("Already logged out...\n");
297
315
  }
298
316
  this.oauth = void 0;
317
+ this._tokenExpiresAt = void 0;
299
318
  this.me = void 0;
300
319
  this.loggedUserName = void 0;
301
320
  this.loggedEnvironmentUrl = void 0;
302
321
  this.bot = null;
303
- this.localRuntimeBot = null;
304
322
  this.headers.delete("Authorization");
305
323
  }
306
324
  /**
@@ -334,17 +352,13 @@ class BotonicAPIService {
334
352
  const signupData = { email, password, org_name: orgName, campaign };
335
353
  return this.apiPost({ path: "sign-up/", body: signupData });
336
354
  }
337
- async createBot(botName) {
355
+ async createBot(botName, isTest = false) {
338
356
  const resp = await this.apiPost({
339
357
  apiVersion: "v2",
340
358
  path: "bots/",
341
- body: { name: botName }
359
+ body: { name: botName, is_test: isTest }
342
360
  });
343
- if (this.isLocalRuntimeDeployment) {
344
- this.setLocalRuntimeBot(resp.data);
345
- } else {
346
- this.setCurrentBot(resp.data);
347
- }
361
+ this.setCurrentBot(resp.data);
348
362
  return resp;
349
363
  }
350
364
  async getBots() {
@@ -396,31 +410,6 @@ class BotonicAPIService {
396
410
  params: { deploy_id: deployId }
397
411
  });
398
412
  }
399
- async deployLocalRuntime({
400
- botConfigJson,
401
- lambdaFunctionName,
402
- lambdaEndpoint
403
- }) {
404
- try {
405
- await this.getMe();
406
- } catch (e) {
407
- console.log(`Error authenticating: ${String(e)}`);
408
- }
409
- const form = new FormData();
410
- form.append("bot_config", JSON.stringify(botConfigJson));
411
- form.append("lambda_function_name", lambdaFunctionName);
412
- form.append("lambda_endpoint", lambdaEndpoint);
413
- const headers = await this.getHeaders(form);
414
- return await this.apiPost({
415
- apiVersion: "v2",
416
- path: `bots/${this.botInfo().id}/deploy_local_runtime/`,
417
- body: form,
418
- headers: {
419
- ...this.headers,
420
- ...headers
421
- }
422
- });
423
- }
424
413
  async build({
425
414
  projectRoot,
426
415
  projectName
@@ -474,7 +463,7 @@ class BotonicAPIService {
474
463
  async prepareLambda(projectRoot) {
475
464
  try {
476
465
  (0, import_child_process.execSync)(
477
- `mkdir -p ${projectRoot}/dist/lambda && cp -r ${projectRoot}/src/server/lambda/* ${projectRoot}/dist/lambda/ && cd ${projectRoot}/dist/lambda && npm install`
466
+ `mkdir -p ${projectRoot}/dist/lambda && rsync -a --exclude=node_modules/ ${projectRoot}/src/server/lambda/ ${projectRoot}/dist/lambda/`
478
467
  );
479
468
  return true;
480
469
  } catch (error) {
@@ -532,6 +521,7 @@ class BotonicAPIService {
532
521
  async apiDelete({
533
522
  apiVersion = "v1",
534
523
  path,
524
+ body,
535
525
  headers,
536
526
  params
537
527
  }) {
@@ -547,9 +537,84 @@ class BotonicAPIService {
547
537
  }
548
538
  return this.apiClient.delete(`${this.baseUrl}/${apiVersion}/${path}`, {
549
539
  headers: headers || this.headers,
550
- params
540
+ params,
541
+ ...body !== void 0 && { data: body }
551
542
  });
552
543
  }
544
+ async apiPut({
545
+ apiVersion = "v1",
546
+ path,
547
+ body,
548
+ headers,
549
+ params
550
+ }) {
551
+ if (this.oauth?.access_token && this.isTokenExpired()) {
552
+ try {
553
+ await this.refreshToken();
554
+ } catch (error) {
555
+ console.warn(
556
+ "Proactive token refresh failed, proceeding with request:",
557
+ error
558
+ );
559
+ }
560
+ }
561
+ return this.apiClient.put(
562
+ `${this.baseUrl}/${apiVersion}/${path}`,
563
+ body,
564
+ {
565
+ headers: headers || this.headers,
566
+ params
567
+ }
568
+ );
569
+ }
570
+ async registerDevSessionWhatsapp(botId, chatProviderId, devSessionUrl, lambdaFunctionName, botConfig) {
571
+ return this.apiPost({
572
+ apiVersion: "v2",
573
+ path: `bots/${botId}/dev_session/`,
574
+ body: {
575
+ channel: "whatsapp",
576
+ chat_provider_id: chatProviderId,
577
+ lambda_endpoint: devSessionUrl,
578
+ lambda_function_name: lambdaFunctionName,
579
+ ...botConfig && { bot_config: botConfig }
580
+ }
581
+ });
582
+ }
583
+ async registerDevSessionWebchat(botId, chatProviderId, devSessionUrl, lambdaFunctionName, botConfig) {
584
+ return this.apiPost({
585
+ apiVersion: "v2",
586
+ path: `bots/${botId}/dev_session/`,
587
+ body: {
588
+ channel: "webchat",
589
+ chat_provider_id: chatProviderId,
590
+ lambda_endpoint: devSessionUrl,
591
+ lambda_function_name: lambdaFunctionName,
592
+ ...botConfig && { bot_config: botConfig }
593
+ }
594
+ });
595
+ }
596
+ async unregisterDevSessionWhatsapp(botId, chatProviderId) {
597
+ return this.apiDelete({
598
+ apiVersion: "v2",
599
+ path: `bots/${botId}/dev_session/`,
600
+ body: { channel: "whatsapp", chat_provider_id: chatProviderId }
601
+ });
602
+ }
603
+ async unregisterDevSessionWebchat(botId, chatProviderId) {
604
+ return this.apiDelete({
605
+ apiVersion: "v2",
606
+ path: `bots/${botId}/dev_session/`,
607
+ body: { channel: "webchat", chat_provider_id: chatProviderId }
608
+ });
609
+ }
610
+ async refreshTokenIfNeeded() {
611
+ if (this.oauth?.access_token) {
612
+ try {
613
+ await this.refreshToken();
614
+ } catch {
615
+ }
616
+ }
617
+ }
553
618
  async getMe() {
554
619
  return this.apiClient.get("/v3/users/me/");
555
620
  }
@@ -568,7 +633,10 @@ class BotonicAPIService {
568
633
  if (!this.oauth?.access_token) {
569
634
  return true;
570
635
  }
571
- return false;
636
+ if (!this._tokenExpiresAt) {
637
+ return false;
638
+ }
639
+ return Date.now() >= this._tokenExpiresAt - 6e4;
572
640
  }
573
641
  async getHeaders(form) {
574
642
  return new Promise((resolve, reject) => {
@@ -1,3 +1,11 @@
1
+ export interface ToolConfigJSON {
2
+ name: string;
3
+ schema?: Record<string, unknown>;
4
+ description: string;
5
+ }
6
+ export interface WebviewConfigJSON {
7
+ name: string;
8
+ }
1
9
  export interface BotConfigJSON {
2
10
  build_info: {
3
11
  node_version: string;
@@ -7,14 +15,9 @@ export interface BotConfigJSON {
7
15
  packages: Record<string, {
8
16
  version: string;
9
17
  }>;
10
- tools: Array<{
11
- name: string;
12
- description: string;
13
- }>;
18
+ tools: ToolConfigJSON[];
14
19
  payloads: string[];
15
- webviews: Array<{
16
- name: string;
17
- }>;
20
+ webviews: WebviewConfigJSON[];
18
21
  }
19
22
  export declare class BotConfig {
20
23
  static get(projectRoot: string): Promise<BotConfigJSON>;
@@ -35,6 +35,7 @@ var childProcess = __toESM(require("child_process"));
35
35
  var import_fs = require("fs");
36
36
  var import_path = require("path");
37
37
  var util = __toESM(require("util"));
38
+ var import_zod = require("zod");
38
39
  const CONSTANTS_PATH = "src/shared/constants";
39
40
  const TOOLS_PATH = "src/server/bot/tools";
40
41
  class BotConfig {
@@ -108,7 +109,10 @@ class BotConfig {
108
109
  return toolsModule.customTools.map(
109
110
  (tool) => ({
110
111
  name: tool.name,
111
- description: tool.description
112
+ description: tool.description,
113
+ schema: import_zod.z.toJSONSchema(tool.schema, {
114
+ target: "draft-07"
115
+ })
112
116
  })
113
117
  );
114
118
  }
@@ -1,9 +1,8 @@
1
1
  export declare const BOTONIC_NPM_NAMESPACE = "@botonic";
2
2
  export declare const BOTONIC_HOME_DIRNAME = ".botonic";
3
- export declare const GLOBAL_CREDS_FILENAME = "credentials.json";
3
+ export declare const ENV_CREDENTIALS_FILENAME = "env-credentials.json";
4
+ export declare const BOTS_REGISTRY_FILENAME = "bots.json";
4
5
  export declare const BOTONIC_PROJECT_PATH: string;
5
- export declare const BOT_CREDENTIALS_FILENAME = ".botonic.json";
6
- export declare const LOCAL_RUNTIME_BOT_CREDENTIALS_FILENAME = ".local-runtime-botonic.json";
7
6
  export declare const ANALYTICS_WRITE_KEY = "YD0jpJHNGW12uhLNbgB4wbdTRQ4Cy1Zu";
8
7
  export declare const CLOUD_PROVIDERS: Readonly<{
9
8
  AWS: "aws";
@@ -23,10 +23,9 @@ __export(constants_exports, {
23
23
  BOTONIC_HOME_DIRNAME: () => BOTONIC_HOME_DIRNAME,
24
24
  BOTONIC_NPM_NAMESPACE: () => BOTONIC_NPM_NAMESPACE,
25
25
  BOTONIC_PROJECT_PATH: () => BOTONIC_PROJECT_PATH,
26
- BOT_CREDENTIALS_FILENAME: () => BOT_CREDENTIALS_FILENAME,
26
+ BOTS_REGISTRY_FILENAME: () => BOTS_REGISTRY_FILENAME,
27
27
  CLOUD_PROVIDERS: () => CLOUD_PROVIDERS,
28
- GLOBAL_CREDS_FILENAME: () => GLOBAL_CREDS_FILENAME,
29
- LOCAL_RUNTIME_BOT_CREDENTIALS_FILENAME: () => LOCAL_RUNTIME_BOT_CREDENTIALS_FILENAME,
28
+ ENV_CREDENTIALS_FILENAME: () => ENV_CREDENTIALS_FILENAME,
30
29
  PATH_TO_AWS_CONFIG: () => PATH_TO_AWS_CONFIG
31
30
  });
32
31
  module.exports = __toCommonJS(constants_exports);
@@ -34,10 +33,9 @@ var import_path = require("path");
34
33
  var import_process = require("process");
35
34
  const BOTONIC_NPM_NAMESPACE = "@botonic";
36
35
  const BOTONIC_HOME_DIRNAME = ".botonic";
37
- const GLOBAL_CREDS_FILENAME = "credentials.json";
36
+ const ENV_CREDENTIALS_FILENAME = "env-credentials.json";
37
+ const BOTS_REGISTRY_FILENAME = "bots.json";
38
38
  const BOTONIC_PROJECT_PATH = process.cwd();
39
- const BOT_CREDENTIALS_FILENAME = ".botonic.json";
40
- const LOCAL_RUNTIME_BOT_CREDENTIALS_FILENAME = ".local-runtime-botonic.json";
41
39
  const ANALYTICS_WRITE_KEY = "YD0jpJHNGW12uhLNbgB4wbdTRQ4Cy1Zu";
42
40
  const CLOUD_PROVIDERS = Object.freeze({
43
41
  AWS: "aws",
@@ -52,9 +50,8 @@ const PATH_TO_AWS_CONFIG = (0, import_path.join)((0, import_process.cwd)(), AWS_
52
50
  BOTONIC_HOME_DIRNAME,
53
51
  BOTONIC_NPM_NAMESPACE,
54
52
  BOTONIC_PROJECT_PATH,
55
- BOT_CREDENTIALS_FILENAME,
53
+ BOTS_REGISTRY_FILENAME,
56
54
  CLOUD_PROVIDERS,
57
- GLOBAL_CREDS_FILENAME,
58
- LOCAL_RUNTIME_BOT_CREDENTIALS_FILENAME,
55
+ ENV_CREDENTIALS_FILENAME,
59
56
  PATH_TO_AWS_CONFIG
60
57
  });
@@ -1,4 +1,4 @@
1
- import type { BotCredentials, GlobalCredentialsStore, JSONObject } from './interfaces';
1
+ import type { BotRegistryEntry, BotsRegistry, GlobalCredentialsStore, JSONObject } from './interfaces';
2
2
  export declare class CredentialsHandler {
3
3
  homeDir: string;
4
4
  pathToCredentials: string;
@@ -9,12 +9,12 @@ export declare class CredentialsHandler {
9
9
  initialize(): void;
10
10
  createDirIfNotExists(): void;
11
11
  loadJSON(): JSONObject | undefined;
12
- dumpJSON(obj: JSONObject): void;
12
+ dumpJSON(obj: JSONObject | JSONObject[]): void;
13
13
  }
14
14
  export declare class GlobalCredentialsHandler extends CredentialsHandler {
15
15
  /**
16
16
  * @param baseDir - Optional directory for credentials (e.g. monorepo_root/.botonic).
17
- * When provided, credentials are stored at baseDir/credentials.json (source of truth in monorepo).
17
+ * When provided, credentials are stored at baseDir/env-credentials.json.
18
18
  * When omitted, uses ~/.botonic (user home).
19
19
  */
20
20
  constructor(baseDir?: string);
@@ -22,19 +22,10 @@ export declare class GlobalCredentialsHandler extends CredentialsHandler {
22
22
  load(): GlobalCredentialsStore | undefined;
23
23
  dump(obj: GlobalCredentialsStore): void;
24
24
  }
25
- declare class BaseBotCredentialsHandler<T> extends CredentialsHandler {
26
- constructor(args: {
27
- homeDir: string;
28
- filename: string;
29
- });
30
- load(): T | undefined;
31
- dump(obj: T): void;
32
- }
33
- export declare class BotCredentialsHandler extends BaseBotCredentialsHandler<BotCredentials> {
34
- /**
35
- * @param projectRoot - Directory for app/project .botonic.json (e.g. apps/my-bot).
36
- * When omitted, uses process.cwd() (may write to repo root when Nx cwd is workspace root).
37
- */
38
- constructor(projectRoot?: string);
25
+ export declare class BotsRegistryHandler extends CredentialsHandler {
26
+ constructor();
27
+ load(): BotsRegistry;
28
+ dump(registry: BotsRegistry): void;
29
+ upsert(env: string, entry: BotRegistryEntry): void;
30
+ findLatestForEnv(env: string): BotRegistryEntry | undefined;
39
31
  }
40
- export {};
@@ -18,7 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var credentials_handler_exports = {};
20
20
  __export(credentials_handler_exports, {
21
- BotCredentialsHandler: () => BotCredentialsHandler,
21
+ BotsRegistryHandler: () => BotsRegistryHandler,
22
22
  CredentialsHandler: () => CredentialsHandler,
23
23
  GlobalCredentialsHandler: () => GlobalCredentialsHandler
24
24
  });
@@ -58,13 +58,13 @@ class CredentialsHandler {
58
58
  class GlobalCredentialsHandler extends CredentialsHandler {
59
59
  /**
60
60
  * @param baseDir - Optional directory for credentials (e.g. monorepo_root/.botonic).
61
- * When provided, credentials are stored at baseDir/credentials.json (source of truth in monorepo).
61
+ * When provided, credentials are stored at baseDir/env-credentials.json.
62
62
  * When omitted, uses ~/.botonic (user home).
63
63
  */
64
64
  constructor(baseDir) {
65
65
  super({
66
66
  homeDir: baseDir ? (0, import_path.resolve)(baseDir) : (0, import_path.join)((0, import_file_system.getHomeDirectory)(), import_constants.BOTONIC_HOME_DIRNAME),
67
- filename: import_constants.GLOBAL_CREDS_FILENAME
67
+ filename: import_constants.ENV_CREDENTIALS_FILENAME
68
68
  });
69
69
  }
70
70
  initialize() {
@@ -79,37 +79,55 @@ class GlobalCredentialsHandler extends CredentialsHandler {
79
79
  return this.dumpJSON(obj);
80
80
  }
81
81
  }
82
- class BaseBotCredentialsHandler extends CredentialsHandler {
83
- constructor(args) {
82
+ class BotsRegistryHandler extends CredentialsHandler {
83
+ constructor() {
84
84
  super({
85
- homeDir: args.homeDir,
86
- filename: args.filename
85
+ homeDir: (0, import_path.join)((0, import_file_system.getHomeDirectory)(), import_constants.BOTONIC_HOME_DIRNAME),
86
+ filename: import_constants.BOTS_REGISTRY_FILENAME
87
87
  });
88
88
  }
89
89
  load() {
90
- const json = this.loadJSON();
91
- if (!json) return void 0;
92
- return json;
90
+ try {
91
+ if (!(0, import_file_system.pathExists)(this.pathToCredentials)) return {};
92
+ const result = (0, import_file_system.readJSON)(this.pathToCredentials);
93
+ return result && !Array.isArray(result) ? result : {};
94
+ } catch {
95
+ console.warn("bots.json could not be loaded");
96
+ return {};
97
+ }
93
98
  }
94
- dump(obj) {
95
- return this.dumpJSON(obj);
99
+ dump(registry) {
100
+ try {
101
+ (0, import_file_system.writeJSON)(this.pathToCredentials, registry);
102
+ } catch {
103
+ console.warn("bots.json could not be written");
104
+ }
96
105
  }
97
- }
98
- class BotCredentialsHandler extends BaseBotCredentialsHandler {
99
- /**
100
- * @param projectRoot - Directory for app/project .botonic.json (e.g. apps/my-bot).
101
- * When omitted, uses process.cwd() (may write to repo root when Nx cwd is workspace root).
102
- */
103
- constructor(projectRoot) {
104
- super({
105
- homeDir: projectRoot ? (0, import_path.resolve)(projectRoot) : import_constants.BOTONIC_PROJECT_PATH,
106
- filename: import_constants.BOT_CREDENTIALS_FILENAME
107
- });
106
+ upsert(env, entry) {
107
+ try {
108
+ const registry = this.load();
109
+ const list = registry[env] ?? [];
110
+ const idx = list.findIndex((e) => e.id === entry.id);
111
+ if (idx !== -1) {
112
+ list[idx] = entry;
113
+ } else {
114
+ list.push(entry);
115
+ }
116
+ registry[env] = list;
117
+ this.dump(registry);
118
+ } catch {
119
+ console.warn("bots.json could not be updated");
120
+ }
121
+ }
122
+ findLatestForEnv(env) {
123
+ const registry = this.load();
124
+ const list = registry[env] ?? [];
125
+ return list.length > 0 ? list[list.length - 1] : void 0;
108
126
  }
109
127
  }
110
128
  // Annotate the CommonJS export names for ESM import in node:
111
129
  0 && (module.exports = {
112
- BotCredentialsHandler,
130
+ BotsRegistryHandler,
113
131
  CredentialsHandler,
114
132
  GlobalCredentialsHandler
115
133
  });
@@ -24,23 +24,20 @@ export interface EnvironmentCredentials {
24
24
  }
25
25
  /** Store: one entry per environment so multiple envs can be logged in. */
26
26
  export type GlobalCredentialsStore = Record<string, EnvironmentCredentials>;
27
+ export interface ProviderAccountEntry {
28
+ id: string;
29
+ provider: string;
30
+ is_test: boolean;
31
+ is_active: boolean;
32
+ }
27
33
  export interface BotDetail {
28
34
  id: string;
29
35
  name: string;
30
- organization: string;
31
- last_update: any;
32
- created_at: string;
33
- provider_accounts: any[];
34
- is_debug: boolean;
35
- is_published: boolean;
36
- active_users: number;
37
- }
38
- export interface BotCredentials {
39
- bot: BotDetail | null;
40
- }
41
- export interface LocalRuntimeBotCredentials {
42
- localRuntimeBot: BotDetail | null;
36
+ providers?: ProviderAccountEntry[];
37
+ [key: string]: unknown;
43
38
  }
39
+ export type BotRegistryEntry = BotDetail;
40
+ export type BotsRegistry = Record<string, BotRegistryEntry[]>;
44
41
  export type JSONPrimitive = string | number | boolean | null;
45
42
  export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
46
43
  export type JSONObject = {