calabasas 0.11.0 → 0.14.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.
@@ -0,0 +1,153 @@
1
+ import {
2
+ build_default,
3
+ relativeTime
4
+ } from "./index-8gymgyxd.js";
5
+ import"./index-tre7d3f1.js";
6
+ import {
7
+ __toESM
8
+ } from "./index-sdksp5px.js";
9
+ import {
10
+ Box_default,
11
+ Text
12
+ } from "./index-4rn9k8et.js";
13
+ import {
14
+ cliApi,
15
+ require_jsx_dev_runtime,
16
+ useQuery
17
+ } from "./convex-1z1jsz1n.js";
18
+ import"./index-vmy4gfe1.js";
19
+
20
+ // src/components/StatusBadge.tsx
21
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
22
+ var STATUS_CONFIG = {
23
+ connected: { color: "green", dot: "●" },
24
+ connecting: { color: "yellow", dot: "●" },
25
+ error: { color: "red", dot: "●" },
26
+ disconnected: { color: "gray", dot: "●" }
27
+ };
28
+ function StatusBadge({ status, width = 0 }) {
29
+ const config = STATUS_CONFIG[status];
30
+ const label = `${config.dot} ${status}`;
31
+ const padded = width > 0 ? label.padEnd(width) : label;
32
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
33
+ children: [
34
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
35
+ color: config.color,
36
+ children: config.dot
37
+ }, undefined, false, undefined, this),
38
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
39
+ children: padded.slice(1)
40
+ }, undefined, false, undefined, this)
41
+ ]
42
+ }, undefined, true, undefined, this);
43
+ }
44
+
45
+ // src/components/BotList.tsx
46
+ var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
47
+ function BotList({
48
+ apiKey,
49
+ selectedIndex,
50
+ onSelect
51
+ }) {
52
+ const bots = useQuery(cliApi.listBots, { apiKey });
53
+ if (bots === undefined) {
54
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
55
+ children: [
56
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57
+ color: "cyan",
58
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(build_default, {
59
+ type: "dots"
60
+ }, undefined, false, undefined, this)
61
+ }, undefined, false, undefined, this),
62
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
63
+ children: " Loading bots..."
64
+ }, undefined, false, undefined, this)
65
+ ]
66
+ }, undefined, true, undefined, this);
67
+ }
68
+ if (bots.length === 0) {
69
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
70
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
71
+ dimColor: true,
72
+ children: "No bots found. Run `calabasas bot add` to create one."
73
+ }, undefined, false, undefined, this)
74
+ }, undefined, false, undefined, this);
75
+ }
76
+ const COL = { name: 30, status: 16, appId: 22, env: 8, last: 16 };
77
+ function truncate(str, max) {
78
+ if (str.length <= max)
79
+ return str.padEnd(max);
80
+ return str.slice(0, max - 1) + "…";
81
+ }
82
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
83
+ flexDirection: "column",
84
+ children: [
85
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
86
+ marginBottom: 1,
87
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
88
+ bold: true,
89
+ children: [
90
+ " ",
91
+ "Name".padEnd(COL.name),
92
+ "Status".padEnd(COL.status),
93
+ "Discord App ID".padEnd(COL.appId),
94
+ "Env".padEnd(COL.env),
95
+ "Last Connected"
96
+ ]
97
+ }, undefined, true, undefined, this)
98
+ }, undefined, false, undefined, this),
99
+ bots.map((bot, i) => {
100
+ const isSelected = selectedIndex === i;
101
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
102
+ flexDirection: "column",
103
+ children: [
104
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105
+ children: [
106
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
107
+ color: isSelected ? "cyan" : undefined,
108
+ children: isSelected ? "→ " : " "
109
+ }, undefined, false, undefined, this),
110
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
111
+ bold: isSelected,
112
+ children: truncate(bot.name, COL.name)
113
+ }, undefined, false, undefined, this),
114
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(StatusBadge, {
115
+ status: bot.status,
116
+ width: COL.status
117
+ }, undefined, false, undefined, this),
118
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
119
+ dimColor: true,
120
+ children: bot.discordAppId.padEnd(COL.appId)
121
+ }, undefined, false, undefined, this),
122
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
123
+ dimColor: true,
124
+ children: bot.environment.padEnd(COL.env)
125
+ }, undefined, false, undefined, this),
126
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
127
+ dimColor: true,
128
+ children: relativeTime(bot.lastConnectedAt)
129
+ }, undefined, false, undefined, this)
130
+ ]
131
+ }, undefined, true, undefined, this),
132
+ bot.errorMessage && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
133
+ marginLeft: 4,
134
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
135
+ color: "red",
136
+ dimColor: true,
137
+ children: [
138
+ "\\u2514 ",
139
+ bot.errorMessage
140
+ ]
141
+ }, undefined, true, undefined, this)
142
+ }, undefined, false, undefined, this)
143
+ ]
144
+ }, bot._id, true, undefined, this);
145
+ })
146
+ ]
147
+ }, undefined, true, undefined, this);
148
+ }
149
+ export {
150
+ BotList
151
+ };
152
+
153
+ ;
package/dist/config.d.ts CHANGED
@@ -9,9 +9,21 @@ export type SyncConfig = {
9
9
  presence?: boolean;
10
10
  };
11
11
 
12
- export type EventConfig = {
13
- [eventType: string]: true;
14
- };
12
+ export type DiscordEventName =
13
+ | "messageCreate"
14
+ | "messageUpdate"
15
+ | "messageDelete"
16
+ | "guildMemberAdd"
17
+ | "guildMemberRemove"
18
+ | "guildMemberUpdate"
19
+ | "interactionCreate"
20
+ | "voiceStateUpdate"
21
+ | "presenceUpdate"
22
+ | "typingStart"
23
+ | "messageReactionAdd"
24
+ | "messageReactionRemove";
25
+
26
+ export type EventConfig = { [K in DiscordEventName]?: true };
15
27
 
16
28
  export type CommandOptionType = "string" | "integer" | "number" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment";
17
29
 
@@ -0,0 +1,235 @@
1
+ import {
2
+ LogViewer
3
+ } from "./index-f8td7thj.js";
4
+ import {
5
+ getConvexUrl,
6
+ resolveEnv,
7
+ resolvePlatformApiKey
8
+ } from "./index-a8vtmtf9.js";
9
+ import {
10
+ build_default,
11
+ formatLatency,
12
+ formatNumber
13
+ } from "./index-8gymgyxd.js";
14
+ import"./index-tre7d3f1.js";
15
+ import {
16
+ __toESM
17
+ } from "./index-sdksp5px.js";
18
+ import {
19
+ BotList
20
+ } from "./BotList-pbt2yxmj.js";
21
+ import {
22
+ Box_default,
23
+ Text,
24
+ render_default,
25
+ use_app_default,
26
+ use_input_default
27
+ } from "./index-4rn9k8et.js";
28
+ import {
29
+ ConvexProvider,
30
+ cliApi,
31
+ createConvexClient,
32
+ require_jsx_dev_runtime,
33
+ useQuery
34
+ } from "./convex-1z1jsz1n.js";
35
+ import {
36
+ require_react
37
+ } from "./index-vmy4gfe1.js";
38
+
39
+ // src/components/Dashboard.tsx
40
+ var import_react2 = __toESM(require_react(), 1);
41
+
42
+ // src/components/Header.tsx
43
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
44
+ function Header({
45
+ botCount,
46
+ env
47
+ }) {
48
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
49
+ marginBottom: 1,
50
+ children: [
51
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
52
+ bold: true,
53
+ color: "magenta",
54
+ children: "calabasas"
55
+ }, undefined, false, undefined, this),
56
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
57
+ dimColor: true,
58
+ children: " v0.1.12"
59
+ }, undefined, false, undefined, this),
60
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
61
+ dimColor: true,
62
+ children: " · "
63
+ }, undefined, false, undefined, this),
64
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
65
+ children: [
66
+ botCount,
67
+ " bot",
68
+ botCount !== 1 ? "s" : ""
69
+ ]
70
+ }, undefined, true, undefined, this),
71
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
72
+ dimColor: true,
73
+ children: " · "
74
+ }, undefined, false, undefined, this),
75
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
76
+ color: env === "dev" ? "yellow" : "green",
77
+ children: env
78
+ }, undefined, false, undefined, this),
79
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
80
+ dimColor: true,
81
+ children: " · Press "
82
+ }, undefined, false, undefined, this),
83
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
84
+ bold: true,
85
+ children: "q"
86
+ }, undefined, false, undefined, this),
87
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
88
+ dimColor: true,
89
+ children: " to quit"
90
+ }, undefined, false, undefined, this)
91
+ ]
92
+ }, undefined, true, undefined, this);
93
+ }
94
+
95
+ // src/components/StatsPanel.tsx
96
+ var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
97
+ var ONE_HOUR = 60 * 60 * 1000;
98
+ function StatsPanel({
99
+ apiKey,
100
+ botId
101
+ }) {
102
+ const stats = useQuery(cliApi.botStats, {
103
+ apiKey,
104
+ botId,
105
+ since: Date.now() - ONE_HOUR
106
+ });
107
+ if (stats === undefined) {
108
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
109
+ children: [
110
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
111
+ color: "cyan",
112
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(build_default, {
113
+ type: "dots"
114
+ }, undefined, false, undefined, this)
115
+ }, undefined, false, undefined, this),
116
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
117
+ children: " Loading stats..."
118
+ }, undefined, false, undefined, this)
119
+ ]
120
+ }, undefined, true, undefined, this);
121
+ }
122
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
123
+ gap: 2,
124
+ marginBottom: 1,
125
+ children: [
126
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
127
+ dimColor: true,
128
+ children: "Events (1h):"
129
+ }, undefined, false, undefined, this),
130
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
131
+ bold: true,
132
+ children: formatNumber(stats.total)
133
+ }, undefined, false, undefined, this),
134
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
135
+ color: "green",
136
+ children: [
137
+ formatNumber(stats.success),
138
+ " ok"
139
+ ]
140
+ }, undefined, true, undefined, this),
141
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
142
+ color: "red",
143
+ children: [
144
+ formatNumber(stats.failed),
145
+ " failed"
146
+ ]
147
+ }, undefined, true, undefined, this),
148
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
149
+ dimColor: true,
150
+ children: [
151
+ "avg ",
152
+ formatLatency(stats.avgLatencyMs)
153
+ ]
154
+ }, undefined, true, undefined, this)
155
+ ]
156
+ }, undefined, true, undefined, this);
157
+ }
158
+
159
+ // src/components/Dashboard.tsx
160
+ var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
161
+ function Dashboard({
162
+ apiKey,
163
+ env
164
+ }) {
165
+ const { exit } = use_app_default();
166
+ const [selectedIndex, setSelectedIndex] = import_react2.useState(0);
167
+ const bots = useQuery(cliApi.listBots, { apiKey });
168
+ const botCount = bots?.length ?? 0;
169
+ const selectedBot = bots?.[selectedIndex];
170
+ use_input_default(import_react2.useCallback((input, key) => {
171
+ if (input === "q") {
172
+ exit();
173
+ return;
174
+ }
175
+ if ((input === "j" || key.downArrow) && botCount > 0) {
176
+ setSelectedIndex((i) => Math.min(i, botCount - 1) === botCount - 1 ? 0 : i + 1);
177
+ }
178
+ if ((input === "k" || key.upArrow) && botCount > 0) {
179
+ setSelectedIndex((i) => i === 0 ? botCount - 1 : i - 1);
180
+ }
181
+ }, [botCount, exit]));
182
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
183
+ flexDirection: "column",
184
+ children: [
185
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Header, {
186
+ botCount,
187
+ env
188
+ }, undefined, false, undefined, this),
189
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(BotList, {
190
+ apiKey,
191
+ selectedIndex
192
+ }, undefined, false, undefined, this),
193
+ selectedBot && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
194
+ flexDirection: "column",
195
+ marginTop: 1,
196
+ children: [
197
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(StatsPanel, {
198
+ apiKey,
199
+ botId: selectedBot._id
200
+ }, undefined, false, undefined, this),
201
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(LogViewer, {
202
+ apiKey,
203
+ botId: selectedBot._id
204
+ }, undefined, false, undefined, this)
205
+ ]
206
+ }, undefined, true, undefined, this)
207
+ ]
208
+ }, undefined, true, undefined, this);
209
+ }
210
+
211
+ // src/commands/dashboard.tsx
212
+ var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
213
+ async function dashboard(options) {
214
+ const env = resolveEnv(options);
215
+ const resolved = resolvePlatformApiKey({}, env);
216
+ if (!resolved) {
217
+ console.log("Not logged in. Run `calabasas login` first.");
218
+ return;
219
+ }
220
+ const apiKey = resolved.key;
221
+ const convexUrl = getConvexUrl(env);
222
+ const client = createConvexClient(convexUrl);
223
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(ConvexProvider, {
224
+ client,
225
+ children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Dashboard, {
226
+ apiKey,
227
+ env
228
+ }, undefined, false, undefined, this)
229
+ }, undefined, false, undefined, this));
230
+ await waitUntilExit();
231
+ await client.close();
232
+ }
233
+ export {
234
+ dashboard
235
+ };
@@ -0,0 +1,220 @@
1
+ import {
2
+ __require,
3
+ __toESM
4
+ } from "./index-sdksp5px.js";
5
+
6
+ // src/lib/config.ts
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ var CONFIG_DIR = path.join(os.homedir(), ".calabasas");
11
+ function getConfigFile(env) {
12
+ if (env === "dev")
13
+ return path.join(CONFIG_DIR, "config.dev.json");
14
+ return path.join(CONFIG_DIR, "config.json");
15
+ }
16
+ var PROD_API_URL = "https://sensible-starfish-338.convex.site";
17
+ var DEV_API_URL = "https://diligent-swordfish-752.convex.site";
18
+ var PROD_CONVEX_URL = "https://sensible-starfish-338.convex.cloud";
19
+ var DEV_CONVEX_URL = "https://diligent-swordfish-752.convex.cloud";
20
+ var PROD_WEB_URL = "https://calabasas-web.vercel.app";
21
+ var DEV_WEB_URL = "http://localhost:3000";
22
+ function getConfig(env) {
23
+ try {
24
+ const configFile = getConfigFile(env);
25
+ if (!fs.existsSync(configFile)) {
26
+ return {};
27
+ }
28
+ const content = fs.readFileSync(configFile, "utf-8");
29
+ return JSON.parse(content);
30
+ } catch {
31
+ return {};
32
+ }
33
+ }
34
+ function saveConfig(config, env) {
35
+ const existing = getConfig(env);
36
+ const merged = { ...existing, ...config };
37
+ if (!fs.existsSync(CONFIG_DIR)) {
38
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
39
+ }
40
+ fs.writeFileSync(getConfigFile(env), JSON.stringify(merged, null, 2));
41
+ }
42
+ function clearConfig(env) {
43
+ const configFile = getConfigFile(env);
44
+ if (fs.existsSync(configFile)) {
45
+ fs.unlinkSync(configFile);
46
+ }
47
+ }
48
+ function isAuthenticated(env) {
49
+ const config = getConfig(env);
50
+ return !!config.apiKey;
51
+ }
52
+ function getApiUrl(env) {
53
+ const config = getConfig(env);
54
+ return config.apiUrl || PROD_API_URL;
55
+ }
56
+ function getApiUrlForEnv(env) {
57
+ if (env === "dev")
58
+ return DEV_API_URL;
59
+ if (env === "prod")
60
+ return PROD_API_URL;
61
+ return getApiUrl();
62
+ }
63
+ function getWebUrlForEnv(env) {
64
+ if (env === "dev")
65
+ return DEV_WEB_URL;
66
+ return PROD_WEB_URL;
67
+ }
68
+ function getConvexUrl(env) {
69
+ if (env === "dev")
70
+ return DEV_CONVEX_URL;
71
+ return PROD_CONVEX_URL;
72
+ }
73
+ function parseEnvLocal() {
74
+ const envPath = path.join(process.cwd(), ".env.local");
75
+ if (!fs.existsSync(envPath))
76
+ return {};
77
+ const content = fs.readFileSync(envPath, "utf-8");
78
+ const result = {};
79
+ for (const line of content.split(`
80
+ `)) {
81
+ const trimmed = line.trim();
82
+ if (!trimmed || trimmed.startsWith("#"))
83
+ continue;
84
+ const eqIndex = trimmed.indexOf("=");
85
+ if (eqIndex === -1)
86
+ continue;
87
+ const key = trimmed.slice(0, eqIndex).trim();
88
+ let value = trimmed.slice(eqIndex + 1).trim();
89
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
90
+ value = value.slice(1, -1);
91
+ }
92
+ result[key] = value;
93
+ }
94
+ return result;
95
+ }
96
+ function resolveEnv(options) {
97
+ if (options.dev && options.prod) {
98
+ console.error("Error: Cannot use both --dev and --prod flags.");
99
+ process.exit(1);
100
+ }
101
+ if (options.dev)
102
+ return "dev";
103
+ if (options.prod)
104
+ return "prod";
105
+ const envLocal = parseEnvLocal();
106
+ const calabasasEnv = envLocal["CALABASAS_ENV"];
107
+ if (calabasasEnv === "dev" || calabasasEnv === "prod") {
108
+ return calabasasEnv;
109
+ }
110
+ if (calabasasEnv) {
111
+ console.error(`Error: CALABASAS_ENV in .env.local must be "dev" or "prod", got "${calabasasEnv}".`);
112
+ process.exit(1);
113
+ }
114
+ console.error("Error: Specify --dev or --prod, or set CALABASAS_ENV in .env.local");
115
+ process.exit(1);
116
+ }
117
+ async function resolveBot(options, apiKey, apiUrl, env) {
118
+ if (options.bot)
119
+ return options.bot;
120
+ const envLocal = parseEnvLocal();
121
+ const scopedKey = env === "dev" ? "CALABASAS_BOT_ID_DEV" : "CALABASAS_BOT_ID_PROD";
122
+ const scopedBotId = envLocal[scopedKey];
123
+ if (scopedBotId)
124
+ return scopedBotId;
125
+ const legacyBotId = envLocal["CALABASAS_BOT_ID"];
126
+ if (legacyBotId) {
127
+ console.warn("Warning: CALABASAS_BOT_ID is deprecated. Use CALABASAS_BOT_ID_DEV and CALABASAS_BOT_ID_PROD instead.");
128
+ return legacyBotId;
129
+ }
130
+ const response = await fetch(`${apiUrl}/api/cli/bots`, {
131
+ method: "GET",
132
+ headers: {
133
+ "Content-Type": "application/json",
134
+ Authorization: `Bearer ${apiKey}`
135
+ }
136
+ });
137
+ if (!response.ok) {
138
+ console.error("Error fetching bots: " + await response.text());
139
+ process.exit(1);
140
+ }
141
+ const { bots } = await response.json();
142
+ if (!bots || bots.length === 0) {
143
+ console.error("No bots found. Add a bot first with `calabasas bot add`.");
144
+ process.exit(1);
145
+ }
146
+ if (bots.length === 1) {
147
+ return bots[0]._id;
148
+ }
149
+ const p = await import("@clack/prompts");
150
+ const selected = await p.select({
151
+ message: "Select a bot",
152
+ options: bots.map((bot) => ({
153
+ value: bot._id,
154
+ label: bot.name
155
+ }))
156
+ });
157
+ if (p.isCancel(selected)) {
158
+ p.cancel("Cancelled.");
159
+ process.exit(1);
160
+ }
161
+ return selected;
162
+ }
163
+ function resolvePlatformApiKey(options, env) {
164
+ if (options.platformKey) {
165
+ return { key: options.platformKey, source: "flag", isPlatformKey: true };
166
+ }
167
+ const envLocal = parseEnvLocal();
168
+ const platformKey = envLocal["CALABASAS_PLATFORM_API_KEY"];
169
+ if (platformKey) {
170
+ return { key: platformKey, source: "env", isPlatformKey: true };
171
+ }
172
+ const config = getConfig(env);
173
+ if (config.apiKey) {
174
+ return { key: config.apiKey, source: "user", isPlatformKey: false };
175
+ }
176
+ return null;
177
+ }
178
+ async function platformApiRequest(method, path2, platformApiKey, body, env, extraHeaders) {
179
+ const url = `${getApiUrlForEnv(env)}${path2}`;
180
+ const response = await fetch(url, {
181
+ method,
182
+ headers: {
183
+ "Content-Type": "application/json",
184
+ Authorization: `Bearer ${platformApiKey}`,
185
+ ...extraHeaders
186
+ },
187
+ body: body ? JSON.stringify(body) : undefined
188
+ });
189
+ const data = await response.json();
190
+ return { ok: response.ok, data, status: response.status };
191
+ }
192
+ function appendToEnvLocal(entries) {
193
+ const envPath = path.join(process.cwd(), ".env.local");
194
+ let lines = [];
195
+ if (fs.existsSync(envPath)) {
196
+ lines = fs.readFileSync(envPath, "utf-8").split(`
197
+ `);
198
+ }
199
+ for (const [key, value] of Object.entries(entries)) {
200
+ const lineIndex = lines.findIndex((l) => {
201
+ const trimmed = l.trim();
202
+ return trimmed.startsWith(`${key}=`) || trimmed.startsWith(`${key} =`);
203
+ });
204
+ if (lineIndex !== -1) {
205
+ lines[lineIndex] = `${key}=${value}`;
206
+ } else {
207
+ if (!lines.some((l) => l.trim() === "# Calabasas")) {
208
+ if (lines.length > 0 && lines[lines.length - 1].trim() !== "") {
209
+ lines.push("");
210
+ }
211
+ lines.push("# Calabasas");
212
+ }
213
+ lines.push(`${key}=${value}`);
214
+ }
215
+ }
216
+ fs.writeFileSync(envPath, lines.join(`
217
+ `));
218
+ }
219
+
220
+ export { getConfig, saveConfig, clearConfig, isAuthenticated, getApiUrlForEnv, getWebUrlForEnv, getConvexUrl, resolveEnv, resolveBot, resolvePlatformApiKey, platformApiRequest, appendToEnvLocal };