calabasas 0.10.0 → 0.11.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,236 @@
1
+ import {
2
+ LogViewer
3
+ } from "./index-f8td7thj.js";
4
+ import {
5
+ getConfig,
6
+ getConvexUrl,
7
+ isAuthenticated,
8
+ resolveEnv
9
+ } from "./index-3mz2smj0.js";
10
+ import {
11
+ build_default,
12
+ formatLatency,
13
+ formatNumber
14
+ } from "./index-8gymgyxd.js";
15
+ import"./index-tre7d3f1.js";
16
+ import {
17
+ __toESM
18
+ } from "./index-sdksp5px.js";
19
+ import {
20
+ BotList
21
+ } from "./BotList-jqxxq32e.js";
22
+ import {
23
+ Box_default,
24
+ Text,
25
+ render_default,
26
+ use_app_default,
27
+ use_input_default
28
+ } from "./index-4rn9k8et.js";
29
+ import {
30
+ ConvexProvider,
31
+ cliApi,
32
+ createConvexClient,
33
+ require_jsx_dev_runtime,
34
+ useQuery
35
+ } from "./convex-1z1jsz1n.js";
36
+ import {
37
+ require_react
38
+ } from "./index-vmy4gfe1.js";
39
+
40
+ // src/components/Dashboard.tsx
41
+ var import_react2 = __toESM(require_react(), 1);
42
+
43
+ // src/components/Header.tsx
44
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
45
+ function Header({
46
+ botCount,
47
+ env
48
+ }) {
49
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
50
+ marginBottom: 1,
51
+ children: [
52
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
53
+ bold: true,
54
+ color: "magenta",
55
+ children: "calabasas"
56
+ }, undefined, false, undefined, this),
57
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
58
+ dimColor: true,
59
+ children: " v0.1.12"
60
+ }, undefined, false, undefined, this),
61
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
62
+ dimColor: true,
63
+ children: " · "
64
+ }, undefined, false, undefined, this),
65
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
66
+ children: [
67
+ botCount,
68
+ " bot",
69
+ botCount !== 1 ? "s" : ""
70
+ ]
71
+ }, undefined, true, undefined, this),
72
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
73
+ dimColor: true,
74
+ children: " · "
75
+ }, undefined, false, undefined, this),
76
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
77
+ color: env === "dev" ? "yellow" : "green",
78
+ children: env
79
+ }, undefined, false, undefined, this),
80
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
81
+ dimColor: true,
82
+ children: " · Press "
83
+ }, undefined, false, undefined, this),
84
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
85
+ bold: true,
86
+ children: "q"
87
+ }, undefined, false, undefined, this),
88
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
89
+ dimColor: true,
90
+ children: " to quit"
91
+ }, undefined, false, undefined, this)
92
+ ]
93
+ }, undefined, true, undefined, this);
94
+ }
95
+
96
+ // src/components/StatsPanel.tsx
97
+ var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
98
+ var ONE_HOUR = 60 * 60 * 1000;
99
+ function StatsPanel({
100
+ apiKey,
101
+ botId
102
+ }) {
103
+ const stats = useQuery(cliApi.botStats, {
104
+ apiKey,
105
+ botId,
106
+ since: Date.now() - ONE_HOUR
107
+ });
108
+ if (stats === undefined) {
109
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
110
+ children: [
111
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
112
+ color: "cyan",
113
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(build_default, {
114
+ type: "dots"
115
+ }, undefined, false, undefined, this)
116
+ }, undefined, false, undefined, this),
117
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
118
+ children: " Loading stats..."
119
+ }, undefined, false, undefined, this)
120
+ ]
121
+ }, undefined, true, undefined, this);
122
+ }
123
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
124
+ gap: 2,
125
+ marginBottom: 1,
126
+ children: [
127
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
128
+ dimColor: true,
129
+ children: "Events (1h):"
130
+ }, undefined, false, undefined, this),
131
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
132
+ bold: true,
133
+ children: formatNumber(stats.total)
134
+ }, undefined, false, undefined, this),
135
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
136
+ color: "green",
137
+ children: [
138
+ formatNumber(stats.success),
139
+ " ok"
140
+ ]
141
+ }, undefined, true, undefined, this),
142
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
143
+ color: "red",
144
+ children: [
145
+ formatNumber(stats.failed),
146
+ " failed"
147
+ ]
148
+ }, undefined, true, undefined, this),
149
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
150
+ dimColor: true,
151
+ children: [
152
+ "avg ",
153
+ formatLatency(stats.avgLatencyMs)
154
+ ]
155
+ }, undefined, true, undefined, this)
156
+ ]
157
+ }, undefined, true, undefined, this);
158
+ }
159
+
160
+ // src/components/Dashboard.tsx
161
+ var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
162
+ function Dashboard({
163
+ apiKey,
164
+ env
165
+ }) {
166
+ const { exit } = use_app_default();
167
+ const [selectedIndex, setSelectedIndex] = import_react2.useState(0);
168
+ const bots = useQuery(cliApi.listBots, { apiKey });
169
+ const botCount = bots?.length ?? 0;
170
+ const selectedBot = bots?.[selectedIndex];
171
+ use_input_default(import_react2.useCallback((input, key) => {
172
+ if (input === "q") {
173
+ exit();
174
+ return;
175
+ }
176
+ if ((input === "j" || key.downArrow) && botCount > 0) {
177
+ setSelectedIndex((i) => Math.min(i, botCount - 1) === botCount - 1 ? 0 : i + 1);
178
+ }
179
+ if ((input === "k" || key.upArrow) && botCount > 0) {
180
+ setSelectedIndex((i) => i === 0 ? botCount - 1 : i - 1);
181
+ }
182
+ }, [botCount, exit]));
183
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
184
+ flexDirection: "column",
185
+ children: [
186
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Header, {
187
+ botCount,
188
+ env
189
+ }, undefined, false, undefined, this),
190
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(BotList, {
191
+ apiKey,
192
+ selectedIndex
193
+ }, undefined, false, undefined, this),
194
+ selectedBot && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
195
+ flexDirection: "column",
196
+ marginTop: 1,
197
+ children: [
198
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(StatsPanel, {
199
+ apiKey,
200
+ botId: selectedBot._id
201
+ }, undefined, false, undefined, this),
202
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(LogViewer, {
203
+ apiKey,
204
+ botId: selectedBot._id
205
+ }, undefined, false, undefined, this)
206
+ ]
207
+ }, undefined, true, undefined, this)
208
+ ]
209
+ }, undefined, true, undefined, this);
210
+ }
211
+
212
+ // src/commands/dashboard.tsx
213
+ var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
214
+ async function dashboard(options) {
215
+ const env = resolveEnv(options);
216
+ if (!isAuthenticated(env)) {
217
+ console.log("Not logged in. Run `calabasas login` first.");
218
+ return;
219
+ }
220
+ const config = getConfig(env);
221
+ const apiKey = config.apiKey;
222
+ const convexUrl = getConvexUrl(env);
223
+ const client = createConvexClient(convexUrl);
224
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(ConvexProvider, {
225
+ client,
226
+ children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Dashboard, {
227
+ apiKey,
228
+ env
229
+ }, undefined, false, undefined, this)
230
+ }, undefined, false, undefined, this));
231
+ await waitUntilExit();
232
+ await client.close();
233
+ }
234
+ export {
235
+ dashboard
236
+ };
@@ -0,0 +1,236 @@
1
+ import {
2
+ LogViewer
3
+ } from "./index-f8td7thj.js";
4
+ import {
5
+ getConfig,
6
+ getConvexUrl,
7
+ isAuthenticated,
8
+ resolveEnv
9
+ } from "./index-qnd4rw0e.js";
10
+ import {
11
+ build_default,
12
+ formatLatency,
13
+ formatNumber
14
+ } from "./index-8gymgyxd.js";
15
+ import"./index-tre7d3f1.js";
16
+ import {
17
+ __toESM
18
+ } from "./index-sdksp5px.js";
19
+ import {
20
+ BotList
21
+ } from "./BotList-jqxxq32e.js";
22
+ import {
23
+ Box_default,
24
+ Text,
25
+ render_default,
26
+ use_app_default,
27
+ use_input_default
28
+ } from "./index-4rn9k8et.js";
29
+ import {
30
+ ConvexProvider,
31
+ cliApi,
32
+ createConvexClient,
33
+ require_jsx_dev_runtime,
34
+ useQuery
35
+ } from "./convex-1z1jsz1n.js";
36
+ import {
37
+ require_react
38
+ } from "./index-vmy4gfe1.js";
39
+
40
+ // src/components/Dashboard.tsx
41
+ var import_react2 = __toESM(require_react(), 1);
42
+
43
+ // src/components/Header.tsx
44
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
45
+ function Header({
46
+ botCount,
47
+ env
48
+ }) {
49
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
50
+ marginBottom: 1,
51
+ children: [
52
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
53
+ bold: true,
54
+ color: "magenta",
55
+ children: "calabasas"
56
+ }, undefined, false, undefined, this),
57
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
58
+ dimColor: true,
59
+ children: " v0.1.12"
60
+ }, undefined, false, undefined, this),
61
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
62
+ dimColor: true,
63
+ children: " · "
64
+ }, undefined, false, undefined, this),
65
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
66
+ children: [
67
+ botCount,
68
+ " bot",
69
+ botCount !== 1 ? "s" : ""
70
+ ]
71
+ }, undefined, true, undefined, this),
72
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
73
+ dimColor: true,
74
+ children: " · "
75
+ }, undefined, false, undefined, this),
76
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
77
+ color: env === "dev" ? "yellow" : "green",
78
+ children: env
79
+ }, undefined, false, undefined, this),
80
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
81
+ dimColor: true,
82
+ children: " · Press "
83
+ }, undefined, false, undefined, this),
84
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
85
+ bold: true,
86
+ children: "q"
87
+ }, undefined, false, undefined, this),
88
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
89
+ dimColor: true,
90
+ children: " to quit"
91
+ }, undefined, false, undefined, this)
92
+ ]
93
+ }, undefined, true, undefined, this);
94
+ }
95
+
96
+ // src/components/StatsPanel.tsx
97
+ var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
98
+ var ONE_HOUR = 60 * 60 * 1000;
99
+ function StatsPanel({
100
+ apiKey,
101
+ botId
102
+ }) {
103
+ const stats = useQuery(cliApi.botStats, {
104
+ apiKey,
105
+ botId,
106
+ since: Date.now() - ONE_HOUR
107
+ });
108
+ if (stats === undefined) {
109
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
110
+ children: [
111
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
112
+ color: "cyan",
113
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(build_default, {
114
+ type: "dots"
115
+ }, undefined, false, undefined, this)
116
+ }, undefined, false, undefined, this),
117
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
118
+ children: " Loading stats..."
119
+ }, undefined, false, undefined, this)
120
+ ]
121
+ }, undefined, true, undefined, this);
122
+ }
123
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
124
+ gap: 2,
125
+ marginBottom: 1,
126
+ children: [
127
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
128
+ dimColor: true,
129
+ children: "Events (1h):"
130
+ }, undefined, false, undefined, this),
131
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
132
+ bold: true,
133
+ children: formatNumber(stats.total)
134
+ }, undefined, false, undefined, this),
135
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
136
+ color: "green",
137
+ children: [
138
+ formatNumber(stats.success),
139
+ " ok"
140
+ ]
141
+ }, undefined, true, undefined, this),
142
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
143
+ color: "red",
144
+ children: [
145
+ formatNumber(stats.failed),
146
+ " failed"
147
+ ]
148
+ }, undefined, true, undefined, this),
149
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
150
+ dimColor: true,
151
+ children: [
152
+ "avg ",
153
+ formatLatency(stats.avgLatencyMs)
154
+ ]
155
+ }, undefined, true, undefined, this)
156
+ ]
157
+ }, undefined, true, undefined, this);
158
+ }
159
+
160
+ // src/components/Dashboard.tsx
161
+ var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
162
+ function Dashboard({
163
+ apiKey,
164
+ env
165
+ }) {
166
+ const { exit } = use_app_default();
167
+ const [selectedIndex, setSelectedIndex] = import_react2.useState(0);
168
+ const bots = useQuery(cliApi.listBots, { apiKey });
169
+ const botCount = bots?.length ?? 0;
170
+ const selectedBot = bots?.[selectedIndex];
171
+ use_input_default(import_react2.useCallback((input, key) => {
172
+ if (input === "q") {
173
+ exit();
174
+ return;
175
+ }
176
+ if ((input === "j" || key.downArrow) && botCount > 0) {
177
+ setSelectedIndex((i) => Math.min(i, botCount - 1) === botCount - 1 ? 0 : i + 1);
178
+ }
179
+ if ((input === "k" || key.upArrow) && botCount > 0) {
180
+ setSelectedIndex((i) => i === 0 ? botCount - 1 : i - 1);
181
+ }
182
+ }, [botCount, exit]));
183
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
184
+ flexDirection: "column",
185
+ children: [
186
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Header, {
187
+ botCount,
188
+ env
189
+ }, undefined, false, undefined, this),
190
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(BotList, {
191
+ apiKey,
192
+ selectedIndex
193
+ }, undefined, false, undefined, this),
194
+ selectedBot && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
195
+ flexDirection: "column",
196
+ marginTop: 1,
197
+ children: [
198
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(StatsPanel, {
199
+ apiKey,
200
+ botId: selectedBot._id
201
+ }, undefined, false, undefined, this),
202
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(LogViewer, {
203
+ apiKey,
204
+ botId: selectedBot._id
205
+ }, undefined, false, undefined, this)
206
+ ]
207
+ }, undefined, true, undefined, this)
208
+ ]
209
+ }, undefined, true, undefined, this);
210
+ }
211
+
212
+ // src/commands/dashboard.tsx
213
+ var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
214
+ async function dashboard(options) {
215
+ const env = resolveEnv(options);
216
+ if (!isAuthenticated(env)) {
217
+ console.log("Not logged in. Run `calabasas login` first.");
218
+ return;
219
+ }
220
+ const config = getConfig(env);
221
+ const apiKey = config.apiKey;
222
+ const convexUrl = getConvexUrl(env);
223
+ const client = createConvexClient(convexUrl);
224
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(ConvexProvider, {
225
+ client,
226
+ children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Dashboard, {
227
+ apiKey,
228
+ env
229
+ }, undefined, false, undefined, this)
230
+ }, undefined, false, undefined, this));
231
+ await waitUntilExit();
232
+ await client.close();
233
+ }
234
+ export {
235
+ dashboard
236
+ };
@@ -0,0 +1,185 @@
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) {
118
+ if (options.bot)
119
+ return options.bot;
120
+ const envLocal = parseEnvLocal();
121
+ const envBotId = envLocal["CALABASAS_BOT_ID"];
122
+ if (envBotId)
123
+ return envBotId;
124
+ const response = await fetch(`${apiUrl}/api/cli/bots`, {
125
+ method: "GET",
126
+ headers: {
127
+ "Content-Type": "application/json",
128
+ Authorization: `Bearer ${apiKey}`
129
+ }
130
+ });
131
+ if (!response.ok) {
132
+ console.error("Error fetching bots: " + await response.text());
133
+ process.exit(1);
134
+ }
135
+ const { bots } = await response.json();
136
+ if (!bots || bots.length === 0) {
137
+ console.error("No bots found. Add a bot first with `calabasas bot add`.");
138
+ process.exit(1);
139
+ }
140
+ if (bots.length === 1) {
141
+ return bots[0]._id;
142
+ }
143
+ const p = await import("@clack/prompts");
144
+ const selected = await p.select({
145
+ message: "Select a bot",
146
+ options: bots.map((bot) => ({
147
+ value: bot._id,
148
+ label: bot.name
149
+ }))
150
+ });
151
+ if (p.isCancel(selected)) {
152
+ p.cancel("Cancelled.");
153
+ process.exit(1);
154
+ }
155
+ return selected;
156
+ }
157
+ function appendToEnvLocal(entries) {
158
+ const envPath = path.join(process.cwd(), ".env.local");
159
+ let lines = [];
160
+ if (fs.existsSync(envPath)) {
161
+ lines = fs.readFileSync(envPath, "utf-8").split(`
162
+ `);
163
+ }
164
+ for (const [key, value] of Object.entries(entries)) {
165
+ const lineIndex = lines.findIndex((l) => {
166
+ const trimmed = l.trim();
167
+ return trimmed.startsWith(`${key}=`) || trimmed.startsWith(`${key} =`);
168
+ });
169
+ if (lineIndex !== -1) {
170
+ lines[lineIndex] = `${key}=${value}`;
171
+ } else {
172
+ if (!lines.some((l) => l.trim() === "# Calabasas")) {
173
+ if (lines.length > 0 && lines[lines.length - 1].trim() !== "") {
174
+ lines.push("");
175
+ }
176
+ lines.push("# Calabasas");
177
+ }
178
+ lines.push(`${key}=${value}`);
179
+ }
180
+ }
181
+ fs.writeFileSync(envPath, lines.join(`
182
+ `));
183
+ }
184
+
185
+ export { getConfig, saveConfig, clearConfig, isAuthenticated, getApiUrlForEnv, getWebUrlForEnv, getConvexUrl, resolveEnv, resolveBot, appendToEnvLocal };
@@ -0,0 +1,191 @@
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 appendToEnvLocal(entries) {
164
+ const envPath = path.join(process.cwd(), ".env.local");
165
+ let lines = [];
166
+ if (fs.existsSync(envPath)) {
167
+ lines = fs.readFileSync(envPath, "utf-8").split(`
168
+ `);
169
+ }
170
+ for (const [key, value] of Object.entries(entries)) {
171
+ const lineIndex = lines.findIndex((l) => {
172
+ const trimmed = l.trim();
173
+ return trimmed.startsWith(`${key}=`) || trimmed.startsWith(`${key} =`);
174
+ });
175
+ if (lineIndex !== -1) {
176
+ lines[lineIndex] = `${key}=${value}`;
177
+ } else {
178
+ if (!lines.some((l) => l.trim() === "# Calabasas")) {
179
+ if (lines.length > 0 && lines[lines.length - 1].trim() !== "") {
180
+ lines.push("");
181
+ }
182
+ lines.push("# Calabasas");
183
+ }
184
+ lines.push(`${key}=${value}`);
185
+ }
186
+ }
187
+ fs.writeFileSync(envPath, lines.join(`
188
+ `));
189
+ }
190
+
191
+ export { getConfig, saveConfig, clearConfig, isAuthenticated, getApiUrlForEnv, getWebUrlForEnv, getConvexUrl, resolveEnv, resolveBot, appendToEnvLocal };
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  resolveBot,
11
11
  resolveEnv,
12
12
  saveConfig
13
- } from "./index-85h6h388.js";
13
+ } from "./index-qnd4rw0e.js";
14
14
  import {
15
15
  __commonJS,
16
16
  __require,
@@ -2336,7 +2336,7 @@ async function push(options) {
2336
2336
  const config = getConfig(env);
2337
2337
  const apiKey = config.apiKey;
2338
2338
  p4.intro("calabasas push");
2339
- const botId = await resolveBot(options, apiKey, apiUrl);
2339
+ const botId = await resolveBot(options, apiKey, apiUrl, env);
2340
2340
  const calabasasConfig = await parseConfigFile(configPath);
2341
2341
  const eventConfigs = Object.keys(calabasasConfig.events ?? {}).map((eventType) => ({ eventType }));
2342
2342
  const s = p4.spinner();
@@ -5253,18 +5253,19 @@ async function botAdd(options) {
5253
5253
  p10.note(`${sharedSecret}
5254
5254
 
5255
5255
  Add this to your Convex environment as CALABASAS_SECRET`, "Shared Secret");
5256
+ const botIdKey = env === "dev" ? "CALABASAS_BOT_ID_DEV" : "CALABASAS_BOT_ID_PROD";
5256
5257
  if (nonInteractive) {
5257
- appendToEnvLocal({ CALABASAS_ENV: env, CALABASAS_BOT_ID: botId });
5258
+ appendToEnvLocal({ CALABASAS_ENV: env, [botIdKey]: botId });
5258
5259
  p10.note(`CALABASAS_ENV=${env}
5259
- CALABASAS_BOT_ID=${botId}`, "Saved to .env.local");
5260
+ ${botIdKey}=${botId}`, "Saved to .env.local");
5260
5261
  } else {
5261
5262
  const saveEnv = await p10.confirm({
5262
- message: "Save defaults to .env.local? (CALABASAS_ENV + CALABASAS_BOT_ID)"
5263
+ message: `Save defaults to .env.local? (CALABASAS_ENV + ${botIdKey})`
5263
5264
  });
5264
5265
  if (!p10.isCancel(saveEnv) && saveEnv) {
5265
- appendToEnvLocal({ CALABASAS_ENV: env, CALABASAS_BOT_ID: botId });
5266
+ appendToEnvLocal({ CALABASAS_ENV: env, [botIdKey]: botId });
5266
5267
  p10.note(`CALABASAS_ENV=${env}
5267
- CALABASAS_BOT_ID=${botId}`, "Saved to .env.local");
5268
+ ${botIdKey}=${botId}`, "Saved to .env.local");
5268
5269
  }
5269
5270
  }
5270
5271
  p10.note("1. Run `calabasas generate` to generate the event handler\n2. Create your handler in convex/discord.ts\n3. Add CALABASAS_SECRET to your Convex environment variables", "Next steps");
@@ -5426,11 +5427,11 @@ async function botEdit(botId, options) {
5426
5427
 
5427
5428
  // src/index.ts
5428
5429
  var dashboard = async (...args) => {
5429
- const mod = await import("./dashboard-ndj73vnp.js");
5430
+ const mod = await import("./dashboard-r2cptf7m.js");
5430
5431
  return mod.dashboard(args[0]);
5431
5432
  };
5432
5433
  var logs = async (...args) => {
5433
- const mod = await import("./logs-1c8x4217.js");
5434
+ const mod = await import("./logs-452qgkw7.js");
5434
5435
  return mod.logs(args[0], args[1]);
5435
5436
  };
5436
5437
  var program2 = new Command;
@@ -0,0 +1,70 @@
1
+ import {
2
+ LogViewer
3
+ } from "./index-f8td7thj.js";
4
+ import {
5
+ getApiUrlForEnv,
6
+ getConfig,
7
+ getConvexUrl,
8
+ isAuthenticated,
9
+ resolveBot,
10
+ resolveEnv
11
+ } from "./index-qnd4rw0e.js";
12
+ import"./index-8gymgyxd.js";
13
+ import"./index-tre7d3f1.js";
14
+ import {
15
+ __toESM
16
+ } from "./index-sdksp5px.js";
17
+ import {
18
+ render_default,
19
+ use_app_default,
20
+ use_input_default
21
+ } from "./index-4rn9k8et.js";
22
+ import {
23
+ ConvexProvider,
24
+ createConvexClient,
25
+ require_jsx_dev_runtime
26
+ } from "./convex-1z1jsz1n.js";
27
+ import"./index-vmy4gfe1.js";
28
+
29
+ // src/commands/logs.tsx
30
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
31
+ function LogsApp({ apiKey, botId, limit }) {
32
+ const { exit } = use_app_default();
33
+ use_input_default((input) => {
34
+ if (input === "q") {
35
+ exit();
36
+ }
37
+ });
38
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(LogViewer, {
39
+ apiKey,
40
+ botId,
41
+ limit
42
+ }, undefined, false, undefined, this);
43
+ }
44
+ async function logs(botId, options) {
45
+ const env = resolveEnv(options);
46
+ if (!isAuthenticated(env)) {
47
+ console.log("Not logged in. Run `calabasas login` first.");
48
+ return;
49
+ }
50
+ const config = getConfig(env);
51
+ const apiKey = config.apiKey;
52
+ const apiUrl = getApiUrlForEnv(env);
53
+ botId = await resolveBot({ bot: botId }, apiKey, apiUrl, env);
54
+ const limit = options.limit ? parseInt(options.limit, 10) : 50;
55
+ const convexUrl = getConvexUrl(env);
56
+ const client = createConvexClient(convexUrl);
57
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime.jsxDEV(ConvexProvider, {
58
+ client,
59
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(LogsApp, {
60
+ apiKey,
61
+ botId,
62
+ limit
63
+ }, undefined, false, undefined, this)
64
+ }, undefined, false, undefined, this));
65
+ await waitUntilExit();
66
+ await client.close();
67
+ }
68
+ export {
69
+ logs
70
+ };
@@ -0,0 +1,70 @@
1
+ import {
2
+ LogViewer
3
+ } from "./index-f8td7thj.js";
4
+ import {
5
+ getApiUrlForEnv,
6
+ getConfig,
7
+ getConvexUrl,
8
+ isAuthenticated,
9
+ resolveBot,
10
+ resolveEnv
11
+ } from "./index-3mz2smj0.js";
12
+ import"./index-8gymgyxd.js";
13
+ import"./index-tre7d3f1.js";
14
+ import {
15
+ __toESM
16
+ } from "./index-sdksp5px.js";
17
+ import {
18
+ render_default,
19
+ use_app_default,
20
+ use_input_default
21
+ } from "./index-4rn9k8et.js";
22
+ import {
23
+ ConvexProvider,
24
+ createConvexClient,
25
+ require_jsx_dev_runtime
26
+ } from "./convex-1z1jsz1n.js";
27
+ import"./index-vmy4gfe1.js";
28
+
29
+ // src/commands/logs.tsx
30
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
31
+ function LogsApp({ apiKey, botId, limit }) {
32
+ const { exit } = use_app_default();
33
+ use_input_default((input) => {
34
+ if (input === "q") {
35
+ exit();
36
+ }
37
+ });
38
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(LogViewer, {
39
+ apiKey,
40
+ botId,
41
+ limit
42
+ }, undefined, false, undefined, this);
43
+ }
44
+ async function logs(botId, options) {
45
+ const env = resolveEnv(options);
46
+ if (!isAuthenticated(env)) {
47
+ console.log("Not logged in. Run `calabasas login` first.");
48
+ return;
49
+ }
50
+ const config = getConfig(env);
51
+ const apiKey = config.apiKey;
52
+ const apiUrl = getApiUrlForEnv(env);
53
+ botId = await resolveBot({ bot: botId }, apiKey, apiUrl);
54
+ const limit = options.limit ? parseInt(options.limit, 10) : 50;
55
+ const convexUrl = getConvexUrl(env);
56
+ const client = createConvexClient(convexUrl);
57
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime.jsxDEV(ConvexProvider, {
58
+ client,
59
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(LogsApp, {
60
+ apiKey,
61
+ botId,
62
+ limit
63
+ }, undefined, false, undefined, this)
64
+ }, undefined, false, undefined, this));
65
+ await waitUntilExit();
66
+ await client.close();
67
+ }
68
+ export {
69
+ logs
70
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calabasas",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {