calabasas 0.5.1 → 0.6.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-85h6h388.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://savory-llama-364.convex.site";
17
+ var DEV_API_URL = "https://notable-monitor-41.convex.site";
18
+ var PROD_CONVEX_URL = "https://savory-llama-364.convex.cloud";
19
+ var DEV_CONVEX_URL = "https://notable-monitor-41.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 };
package/dist/index.js CHANGED
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ appendToEnvLocal,
3
4
  clearConfig,
4
5
  getApiUrlForEnv,
5
6
  getConfig,
6
7
  getConvexUrl,
7
8
  getWebUrlForEnv,
8
9
  isAuthenticated,
10
+ resolveBot,
11
+ resolveEnv,
9
12
  saveConfig
10
- } from "./index-enq39f86.js";
13
+ } from "./index-85h6h388.js";
11
14
  import {
12
15
  __commonJS,
13
16
  __require,
@@ -2069,15 +2072,7 @@ import * as http from "http";
2069
2072
  import * as p from "@clack/prompts";
2070
2073
  var CALLBACK_PORT = 9876;
2071
2074
  async function login(options) {
2072
- if (options.dev && options.prod) {
2073
- console.error("Error: Cannot use both --dev and --prod flags.");
2074
- process.exit(1);
2075
- }
2076
- if (!options.dev && !options.prod) {
2077
- console.error("Error: You must specify either --dev or --prod.");
2078
- process.exit(1);
2079
- }
2080
- const env = options.dev ? "dev" : "prod";
2075
+ const env = resolveEnv(options);
2081
2076
  const existing = getConfig(env);
2082
2077
  p.intro("calabasas login");
2083
2078
  if (existing.apiKey) {
@@ -2169,15 +2164,7 @@ ${authUrl}`, "Authentication URL");
2169
2164
  // src/commands/logout.ts
2170
2165
  import * as p2 from "@clack/prompts";
2171
2166
  async function logout(options) {
2172
- if (options.dev && options.prod) {
2173
- console.error("Error: Cannot use both --dev and --prod flags.");
2174
- process.exit(1);
2175
- }
2176
- if (!options.dev && !options.prod) {
2177
- console.error("Error: You must specify either --dev or --prod.");
2178
- process.exit(1);
2179
- }
2180
- const env = options.dev ? "dev" : "prod";
2167
+ const env = resolveEnv(options);
2181
2168
  p2.intro("calabasas logout");
2182
2169
  if (!isAuthenticated(env)) {
2183
2170
  p2.outro("Not logged in. Nothing to clear.");
@@ -2278,49 +2265,8 @@ async function parseConfigFile(configPath) {
2278
2265
  }
2279
2266
 
2280
2267
  // src/commands/push.ts
2281
- async function selectBot(apiKey, apiUrl) {
2282
- const response = await fetch(`${apiUrl}/api/cli/bots`, {
2283
- method: "GET",
2284
- headers: {
2285
- "Content-Type": "application/json",
2286
- Authorization: `Bearer ${apiKey}`
2287
- }
2288
- });
2289
- if (!response.ok) {
2290
- p4.cancel("Error fetching bots: " + await response.text());
2291
- return null;
2292
- }
2293
- const { bots } = await response.json();
2294
- if (!bots || bots.length === 0) {
2295
- p4.cancel("No bots found. Add a bot first with `calabasas bot add`");
2296
- return null;
2297
- }
2298
- if (bots.length === 1) {
2299
- return bots[0]._id;
2300
- }
2301
- const selected = await p4.select({
2302
- message: "Select a bot to push config to",
2303
- options: bots.map((bot) => ({
2304
- value: bot._id,
2305
- label: bot.name
2306
- }))
2307
- });
2308
- if (p4.isCancel(selected)) {
2309
- p4.cancel("Cancelled.");
2310
- return null;
2311
- }
2312
- return selected;
2313
- }
2314
2268
  async function push(options) {
2315
- if (options.dev && options.prod) {
2316
- console.error("Error: Cannot use both --dev and --prod flags.");
2317
- process.exit(1);
2318
- }
2319
- if (!options.dev && !options.prod) {
2320
- console.error("Error: You must specify either --dev or --prod.");
2321
- process.exit(1);
2322
- }
2323
- const env = options.dev ? "dev" : "prod";
2269
+ const env = resolveEnv(options);
2324
2270
  const apiUrl = getApiUrlForEnv(env);
2325
2271
  const configPath = path3.resolve(process.cwd(), options.config);
2326
2272
  if (!fs3.existsSync(configPath)) {
@@ -2335,13 +2281,7 @@ async function push(options) {
2335
2281
  const config = getConfig(env);
2336
2282
  const apiKey = config.apiKey;
2337
2283
  p4.intro("calabasas push");
2338
- let botId = options.bot;
2339
- if (!botId) {
2340
- botId = await selectBot(apiKey, apiUrl) ?? undefined;
2341
- if (!botId) {
2342
- process.exit(1);
2343
- }
2344
- }
2284
+ const botId = await resolveBot(options, apiKey, apiUrl);
2345
2285
  const calabasasConfig = await parseConfigFile(configPath);
2346
2286
  const eventConfigs = Object.keys(calabasasConfig.events ?? {}).map((eventType) => ({ eventType }));
2347
2287
  const s = p4.spinner();
@@ -3620,15 +3560,7 @@ function hasCalabsasSection(content) {
3620
3560
  return content.includes(SECTION_HEADER) || content.includes("## Calabasas Guidelines");
3621
3561
  }
3622
3562
  async function skill(options) {
3623
- if (options.dev && options.prod) {
3624
- console.error("Error: Cannot use both --dev and --prod flags.");
3625
- process.exit(1);
3626
- }
3627
- if (!options.dev && !options.prod) {
3628
- console.error("Error: You must specify either --dev or --prod.");
3629
- process.exit(1);
3630
- }
3631
- const env = options.dev ? "dev" : "prod";
3563
+ const env = resolveEnv(options);
3632
3564
  p6.intro("calabasas skill");
3633
3565
  const s = p6.spinner();
3634
3566
  s.start("Fetching latest Calabasas guidelines...");
@@ -6099,70 +6031,90 @@ async function apiRequest(method, path8, body, env) {
6099
6031
  return { ok: response.ok, data, status: response.status };
6100
6032
  }
6101
6033
  async function botAdd(options) {
6102
- if (options.dev && options.prod) {
6103
- console.error("Error: Cannot use both --dev and --prod flags.");
6104
- process.exit(1);
6105
- }
6106
- if (!options.dev && !options.prod) {
6107
- console.error("Error: You must specify either --dev or --prod.");
6108
- process.exit(1);
6109
- }
6110
- const env = options.dev ? "dev" : "prod";
6034
+ const env = resolveEnv(options);
6111
6035
  if (!isAuthenticated(env)) {
6112
6036
  console.log("Not logged in. Run `calabasas login` first.");
6113
6037
  return;
6114
6038
  }
6039
+ const nonInteractive = !!(options.name && options.appId && options.token && options.url);
6115
6040
  p10.intro("calabasas bot add");
6116
- const name = await p10.text({
6117
- message: "Bot name",
6118
- placeholder: "My Discord Bot",
6119
- validate: (v) => v.trim() ? undefined : "Bot name is required"
6120
- });
6121
- if (p10.isCancel(name)) {
6122
- p10.cancel("Cancelled.");
6123
- return;
6041
+ let name;
6042
+ if (options.name) {
6043
+ name = options.name;
6044
+ } else {
6045
+ name = await p10.text({
6046
+ message: "Bot name",
6047
+ placeholder: "My Discord Bot",
6048
+ validate: (v) => v.trim() ? undefined : "Bot name is required"
6049
+ });
6050
+ if (p10.isCancel(name)) {
6051
+ p10.cancel("Cancelled.");
6052
+ return;
6053
+ }
6124
6054
  }
6125
- const discordAppId = await p10.text({
6126
- message: "Discord Application ID",
6127
- placeholder: "123456789012345678",
6128
- validate: (v) => v.trim() ? undefined : "Discord Application ID is required"
6129
- });
6130
- if (p10.isCancel(discordAppId)) {
6131
- p10.cancel("Cancelled.");
6132
- return;
6055
+ let discordAppId;
6056
+ if (options.appId) {
6057
+ discordAppId = options.appId;
6058
+ } else {
6059
+ discordAppId = await p10.text({
6060
+ message: "Discord Application ID",
6061
+ placeholder: "123456789012345678",
6062
+ validate: (v) => v.trim() ? undefined : "Discord Application ID is required"
6063
+ });
6064
+ if (p10.isCancel(discordAppId)) {
6065
+ p10.cancel("Cancelled.");
6066
+ return;
6067
+ }
6133
6068
  }
6134
- const token = await p10.password({
6135
- message: "Bot token",
6136
- validate: (v) => v.trim() ? undefined : "Bot token is required"
6137
- });
6138
- if (p10.isCancel(token)) {
6139
- p10.cancel("Cancelled.");
6140
- return;
6069
+ let token;
6070
+ if (options.token) {
6071
+ token = options.token;
6072
+ } else {
6073
+ token = await p10.password({
6074
+ message: "Bot token",
6075
+ validate: (v) => v.trim() ? undefined : "Bot token is required"
6076
+ });
6077
+ if (p10.isCancel(token)) {
6078
+ p10.cancel("Cancelled.");
6079
+ return;
6080
+ }
6141
6081
  }
6142
- const convexUrl = await p10.text({
6143
- message: "Your Convex URL",
6144
- placeholder: "https://your-project.convex.cloud",
6145
- validate: (v) => v.trim() ? undefined : "Convex URL is required"
6146
- });
6147
- if (p10.isCancel(convexUrl)) {
6148
- p10.cancel("Cancelled.");
6149
- return;
6082
+ let convexUrl;
6083
+ if (options.url) {
6084
+ convexUrl = options.url;
6085
+ } else {
6086
+ convexUrl = await p10.text({
6087
+ message: "Your Convex URL",
6088
+ placeholder: "https://your-project.convex.cloud",
6089
+ validate: (v) => v.trim() ? undefined : "Convex URL is required"
6090
+ });
6091
+ if (p10.isCancel(convexUrl)) {
6092
+ p10.cancel("Cancelled.");
6093
+ return;
6094
+ }
6150
6095
  }
6151
- const selectedIntents = await p10.multiselect({
6152
- message: "Select Gateway Intents",
6153
- options: INTENTS.map((intent, i) => ({
6154
- value: i,
6155
- label: intent.privileged ? pc.yellow(intent.name + " (Privileged)") : intent.name,
6156
- hint: intent.description
6157
- })),
6158
- initialValues: [0, 9],
6159
- required: true
6160
- });
6161
- if (p10.isCancel(selectedIntents)) {
6162
- p10.cancel("Cancelled.");
6163
- return;
6096
+ let intents;
6097
+ if (options.intents) {
6098
+ intents = parseInt(options.intents, 10);
6099
+ } else if (nonInteractive) {
6100
+ intents = 513;
6101
+ } else {
6102
+ const selectedIntents = await p10.multiselect({
6103
+ message: "Select Gateway Intents",
6104
+ options: INTENTS.map((intent, i) => ({
6105
+ value: i,
6106
+ label: intent.privileged ? pc.yellow(intent.name + " (Privileged)") : intent.name,
6107
+ hint: intent.description
6108
+ })),
6109
+ initialValues: [0, 9],
6110
+ required: true
6111
+ });
6112
+ if (p10.isCancel(selectedIntents)) {
6113
+ p10.cancel("Cancelled.");
6114
+ return;
6115
+ }
6116
+ intents = selectedIntents.reduce((sum, i) => sum + INTENTS[i].value, 0);
6164
6117
  }
6165
- const intents = selectedIntents.reduce((sum, i) => sum + INTENTS[i].value, 0);
6166
6118
  const s = p10.spinner();
6167
6119
  s.start("Creating bot...");
6168
6120
  const { ok, data, status } = await apiRequest("POST", "/api/cli/bots", {
@@ -6178,23 +6130,29 @@ async function botAdd(options) {
6178
6130
  return;
6179
6131
  }
6180
6132
  s.stop("Bot created!");
6181
- const sharedSecret = data.sharedSecret;
6133
+ const { sharedSecret, botId } = data;
6182
6134
  p10.note(`${sharedSecret}
6183
6135
 
6184
6136
  Add this to your Convex environment as CALABASAS_SECRET`, "Shared Secret");
6137
+ if (nonInteractive) {
6138
+ appendToEnvLocal({ CALABASAS_ENV: env, CALABASAS_BOT_ID: botId });
6139
+ p10.note(`CALABASAS_ENV=${env}
6140
+ CALABASAS_BOT_ID=${botId}`, "Saved to .env.local");
6141
+ } else {
6142
+ const saveEnv = await p10.confirm({
6143
+ message: "Save defaults to .env.local? (CALABASAS_ENV + CALABASAS_BOT_ID)"
6144
+ });
6145
+ if (!p10.isCancel(saveEnv) && saveEnv) {
6146
+ appendToEnvLocal({ CALABASAS_ENV: env, CALABASAS_BOT_ID: botId });
6147
+ p10.note(`CALABASAS_ENV=${env}
6148
+ CALABASAS_BOT_ID=${botId}`, "Saved to .env.local");
6149
+ }
6150
+ }
6185
6151
  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");
6186
6152
  p10.outro("Bot created successfully!");
6187
6153
  }
6188
6154
  async function botList(options) {
6189
- if (options.dev && options.prod) {
6190
- console.error("Error: Cannot use both --dev and --prod flags.");
6191
- process.exit(1);
6192
- }
6193
- if (!options.dev && !options.prod) {
6194
- console.error("Error: You must specify either --dev or --prod.");
6195
- process.exit(1);
6196
- }
6197
- const env = options.dev ? "dev" : "prod";
6155
+ const env = resolveEnv(options);
6198
6156
  if (!isAuthenticated(env)) {
6199
6157
  console.log("Not logged in. Run `calabasas login` first.");
6200
6158
  return;
@@ -6241,26 +6199,20 @@ Your Discord bots:
6241
6199
  }
6242
6200
  }
6243
6201
  async function botRemove(botId, options) {
6244
- if (options.dev && options.prod) {
6245
- console.error("Error: Cannot use both --dev and --prod flags.");
6246
- process.exit(1);
6247
- }
6248
- if (!options.dev && !options.prod) {
6249
- console.error("Error: You must specify either --dev or --prod.");
6250
- process.exit(1);
6251
- }
6252
- const env = options.dev ? "dev" : "prod";
6202
+ const env = resolveEnv(options);
6253
6203
  if (!isAuthenticated(env)) {
6254
6204
  console.log("Not logged in. Run `calabasas login` first.");
6255
6205
  return;
6256
6206
  }
6257
6207
  p10.intro("calabasas bot remove");
6258
- const confirmed = await p10.confirm({
6259
- message: `Are you sure you want to remove bot ${botId}?`
6260
- });
6261
- if (p10.isCancel(confirmed) || !confirmed) {
6262
- p10.cancel("Cancelled.");
6263
- return;
6208
+ if (!options.yes) {
6209
+ const confirmed = await p10.confirm({
6210
+ message: `Are you sure you want to remove bot ${botId}?`
6211
+ });
6212
+ if (p10.isCancel(confirmed) || !confirmed) {
6213
+ p10.cancel("Cancelled.");
6214
+ return;
6215
+ }
6264
6216
  }
6265
6217
  const s = p10.spinner();
6266
6218
  s.start("Removing bot...");
@@ -6274,15 +6226,7 @@ async function botRemove(botId, options) {
6274
6226
  p10.outro("Bot removed successfully!");
6275
6227
  }
6276
6228
  async function botEdit(botId, options) {
6277
- if (options.dev && options.prod) {
6278
- console.error("Error: Cannot use both --dev and --prod flags.");
6279
- process.exit(1);
6280
- }
6281
- if (!options.dev && !options.prod) {
6282
- console.error("Error: You must specify either --dev or --prod.");
6283
- process.exit(1);
6284
- }
6285
- const env = options.dev ? "dev" : "prod";
6229
+ const env = resolveEnv(options);
6286
6230
  if (!isAuthenticated(env)) {
6287
6231
  console.log("Not logged in. Run `calabasas login` first.");
6288
6232
  return;
@@ -6363,11 +6307,11 @@ async function botEdit(botId, options) {
6363
6307
 
6364
6308
  // src/index.ts
6365
6309
  var dashboard = async (...args) => {
6366
- const mod = await import("./dashboard-sp8am093.js");
6310
+ const mod = await import("./dashboard-ndj73vnp.js");
6367
6311
  return mod.dashboard(args[0]);
6368
6312
  };
6369
6313
  var logs = async (...args) => {
6370
- const mod = await import("./logs-9y7zq71r.js");
6314
+ const mod = await import("./logs-1c8x4217.js");
6371
6315
  return mod.logs(args[0], args[1]);
6372
6316
  };
6373
6317
  var program2 = new Command;
@@ -6383,8 +6327,8 @@ program2.command("migrate [name]").description("Run a codemod migration (list av
6383
6327
  program2.command("status").alias("dashboard").description("Real-time dashboard showing bot status, events, and stats").option("--dev", "Use development environment").option("--prod", "Use production environment").action(dashboard);
6384
6328
  program2.command("logs [botId]").description("Live event log viewer for a bot").option("-n, --limit <number>", "Number of log entries to show", "50").option("--dev", "Use development environment").option("--prod", "Use production environment").action(logs);
6385
6329
  var bot = program2.command("bot").description("Manage Discord bots");
6386
- bot.command("add").description("Add a new Discord bot").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botAdd);
6330
+ bot.command("add").description("Add a new Discord bot").option("-n, --name <name>", "Bot name").option("-a, --app-id <id>", "Discord Application ID").option("-t, --token <token>", "Bot token").option("-u, --url <url>", "Convex URL").option("-i, --intents <intents>", "Intents value (number)").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botAdd);
6387
6331
  bot.command("list").alias("ls").description("List your Discord bots (real-time by default)").option("--once", "One-shot output instead of real-time view").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botList);
6388
- bot.command("remove <botId>").alias("rm").description("Remove a Discord bot").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botRemove);
6332
+ bot.command("remove <botId>").alias("rm").description("Remove a Discord bot").option("-y, --yes", "Skip confirmation prompt").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botRemove);
6389
6333
  bot.command("edit <botId>").description("Edit a Discord bot").option("-n, --name <name>", "New bot name").option("-u, --url <url>", "New Convex URL").option("-t, --token <token>", "New bot token").option("-i, --intents <intents>", "New intents value").option("--dev", "Use development environment").option("--prod", "Use production environment").action(botEdit);
6390
6334
  program2.parse();
@@ -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-85h6h388.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.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {