calabasas 0.4.0 → 0.5.1

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 (2) hide show
  1. package/dist/index.js +288 -85
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2236,7 +2236,8 @@ export default defineCalabasas({
2236
2236
  `;
2237
2237
  async function init() {
2238
2238
  const convexDir = path.resolve(process.cwd(), "convex");
2239
- const configPath = path.join(convexDir, "calabasas.config.ts");
2239
+ const calabasasDir = path.join(convexDir, "calabasas");
2240
+ const configPath = path.join(calabasasDir, "config.ts");
2240
2241
  p3.intro("calabasas init");
2241
2242
  if (!fs.existsSync(convexDir)) {
2242
2243
  p3.cancel(`convex/ directory not found.
@@ -2246,15 +2247,18 @@ Run \`npx convex dev\` to initialize a new Convex project.`);
2246
2247
  process.exit(1);
2247
2248
  }
2248
2249
  if (fs.existsSync(configPath)) {
2249
- p3.cancel(`convex/calabasas.config.ts already exists.
2250
+ p3.cancel(`convex/calabasas/config.ts already exists.
2250
2251
 
2251
2252
  Delete the existing file if you want to start fresh:
2252
- rm convex/calabasas.config.ts`);
2253
+ rm convex/calabasas/config.ts`);
2253
2254
  process.exit(1);
2254
2255
  }
2256
+ if (!fs.existsSync(calabasasDir)) {
2257
+ fs.mkdirSync(calabasasDir, { recursive: true });
2258
+ }
2255
2259
  fs.writeFileSync(configPath, CONFIG_TEMPLATE);
2256
- p3.note("1. Edit convex/calabasas.config.ts to configure sync and events\n2. Run `calabasas generate` to generate type-safe handlers\n3. Run `calabasas push` to sync config with Calabasas", "Next steps");
2257
- p3.outro("Created convex/calabasas.config.ts");
2260
+ p3.note("1. Edit convex/calabasas/config.ts to configure sync and events\n2. Run `calabasas generate` to generate type-safe handlers\n3. Run `calabasas push` to sync config with Calabasas", "Next steps");
2261
+ p3.outro("Created convex/calabasas/config.ts");
2258
2262
  }
2259
2263
 
2260
2264
  // src/commands/push.ts
@@ -2321,7 +2325,7 @@ async function push(options) {
2321
2325
  const configPath = path3.resolve(process.cwd(), options.config);
2322
2326
  if (!fs3.existsSync(configPath)) {
2323
2327
  console.error(`Error: Config file not found: ${configPath}`);
2324
- console.error("\nRun `calabasas init` to create a calabasas.config.ts file.");
2328
+ console.error("\nRun `calabasas init` to create a config file.");
2325
2329
  process.exit(1);
2326
2330
  }
2327
2331
  if (!isAuthenticated(env)) {
@@ -2840,7 +2844,7 @@ export const syncGuildAdmins = mutation({
2840
2844
  if (!expectedSecret || secret !== expectedSecret) {
2841
2845
  throw new Error("Invalid secret");
2842
2846
  }
2843
- await ctx.runMutation(internal.calabasas.sync._syncGuildAdmins, { data });
2847
+ await ctx.runMutation(internal.calabasas._generated.sync._syncGuildAdmins, { data });
2844
2848
  return null;
2845
2849
  },
2846
2850
  });`;
@@ -2864,7 +2868,7 @@ export const sync${capitalType} = mutation({
2864
2868
  if (!expectedSecret || secret !== expectedSecret) {
2865
2869
  throw new Error("Invalid secret");
2866
2870
  }
2867
- await ctx.runMutation(internal.calabasas.sync._sync${capitalType}, { data, operation });
2871
+ await ctx.runMutation(internal.calabasas._generated.sync._sync${capitalType}, { data, operation });
2868
2872
  return null;
2869
2873
  },
2870
2874
  });`;
@@ -2920,7 +2924,7 @@ function generateSyncFile(sync) {
2920
2924
  * updated, run \`calabasas generate\` to regenerate it from the config.
2921
2925
  * Editing this file directly will cause your changes to be overwritten.
2922
2926
  *
2923
- * No sync handlers configured - enable sync options in calabasas.config.ts
2927
+ * No sync handlers configured - enable sync options in convex/calabasas/config.ts
2924
2928
  */
2925
2929
 
2926
2930
  export {};
@@ -2936,8 +2940,8 @@ export {};
2936
2940
  * Editing this file directly will cause your changes to be overwritten.
2937
2941
  */
2938
2942
 
2939
- import { mutation, internalMutation } from "../_generated/server";
2940
- import { internal } from "../_generated/api";
2943
+ import { mutation, internalMutation } from "../../_generated/server";
2944
+ import { internal } from "../../_generated/api";
2941
2945
  import { v } from "convex/values";
2942
2946
 
2943
2947
  // ============================================================================
@@ -3273,9 +3277,9 @@ function generateHandlersCode(eventNames) {
3273
3277
  * Editing this file directly will cause your changes to be overwritten.
3274
3278
  */
3275
3279
 
3276
- import { mutation } from "./_generated/server";
3280
+ import { mutation } from "../../_generated/server";
3277
3281
  import { v } from "convex/values";
3278
- import type { MutationCtx } from "./_generated/server";
3282
+ import type { MutationCtx } from "../../_generated/server";
3279
3283
 
3280
3284
  // Event type validators
3281
3285
  ${eventNames.map((name) => `const ${name}Validator = ${generateValidatorForEvent(name)};`).join(`
@@ -3496,17 +3500,27 @@ export const discord = {
3496
3500
  }
3497
3501
  async function generate(options) {
3498
3502
  const convexDir = path4.resolve(process.cwd(), "convex");
3499
- const configPath = path4.join(convexDir, "calabasas.config.ts");
3503
+ const calabasasDir = path4.join(convexDir, "calabasas");
3504
+ const configPath = path4.join(calabasasDir, "config.ts");
3505
+ const legacyConfigPath = path4.join(convexDir, "calabasas.config.ts");
3500
3506
  p5.intro("calabasas generate");
3501
3507
  let config = { sync: {}, events: {} };
3502
3508
  if (fs4.existsSync(configPath)) {
3503
3509
  config = await parseConfigFile(configPath);
3510
+ } else if (fs4.existsSync(legacyConfigPath)) {
3511
+ p5.log.warn("Found config at legacy path convex/calabasas.config.ts");
3512
+ p5.log.info("Run `calabasas migrate dedicated-folder` to move files to the new layout.");
3513
+ config = await parseConfigFile(legacyConfigPath);
3504
3514
  } else {
3505
- p5.log.warn("No calabasas.config.ts found, generating with default settings...");
3515
+ p5.log.warn("No config found, generating with default settings...");
3506
3516
  p5.log.info("Run `calabasas init` to create a config file.");
3507
3517
  }
3508
3518
  const s = p5.spinner();
3509
3519
  s.start("Generating files...");
3520
+ const generatedDir = path4.join(calabasasDir, "_generated");
3521
+ if (!fs4.existsSync(generatedDir)) {
3522
+ fs4.mkdirSync(generatedDir, { recursive: true });
3523
+ }
3510
3524
  const eventHandlersPath = path4.resolve(process.cwd(), options.output);
3511
3525
  const eventHandlersCode = generateEventHandlersFile(config.events ?? {});
3512
3526
  fs4.writeFileSync(eventHandlersPath, eventHandlersCode);
@@ -3514,23 +3528,19 @@ async function generate(options) {
3514
3528
  const syncConfig = config.sync ?? {};
3515
3529
  const hasSyncEnabled = syncConfig.guilds || syncConfig.channels || syncConfig.roles || syncConfig.members;
3516
3530
  if (hasSyncEnabled) {
3517
- const calabasasDir = path4.join(convexDir, "calabasas");
3518
- if (!fs4.existsSync(calabasasDir)) {
3519
- fs4.mkdirSync(calabasasDir, { recursive: true });
3520
- }
3521
- const schemaPath = path4.join(calabasasDir, "schema.ts");
3531
+ const schemaPath = path4.join(generatedDir, "schema.ts");
3522
3532
  const schemaCode = generateSchemaFile(syncConfig);
3523
3533
  fs4.writeFileSync(schemaPath, schemaCode);
3524
- generated.push("convex/calabasas/schema.ts");
3525
- const syncPath = path4.join(calabasasDir, "sync.ts");
3534
+ generated.push("convex/calabasas/_generated/schema.ts");
3535
+ const syncPath = path4.join(generatedDir, "sync.ts");
3526
3536
  const syncCode = generateSyncFile(syncConfig);
3527
3537
  fs4.writeFileSync(syncPath, syncCode);
3528
- generated.push("convex/calabasas/sync.ts");
3538
+ generated.push("convex/calabasas/_generated/sync.ts");
3529
3539
  }
3530
- const actionsPath = path4.join(convexDir, "discord.actions.generated.ts");
3540
+ const actionsPath = path4.join(generatedDir, "discord.actions.ts");
3531
3541
  const actionsCode = generateActionsFile();
3532
3542
  fs4.writeFileSync(actionsPath, actionsCode);
3533
- generated.push("convex/discord.actions.generated.ts");
3543
+ generated.push("convex/calabasas/_generated/discord.actions.ts");
3534
3544
  s.stop(`Generated ${generated.length} file${generated.length > 1 ? "s" : ""}`);
3535
3545
  p5.note(generated.join(`
3536
3546
  `), "Generated files");
@@ -3547,14 +3557,14 @@ async function generate(options) {
3547
3557
  if (syncConfig.members)
3548
3558
  syncExports.push("syncMember");
3549
3559
  p5.note(`1. Add tables to your convex/schema.ts:
3550
- import { calabasasTables } from "./calabasas/schema";
3560
+ import { calabasasTables } from "./calabasas/_generated/schema";
3551
3561
 
3552
3562
  export default defineSchema({
3553
3563
  ...calabasasTables,
3554
3564
  });
3555
3565
 
3556
3566
  2. Re-export sync mutations in convex/discord.ts:
3557
- export { ${syncExports.join(", ")} } from "./calabasas/sync";
3567
+ export { ${syncExports.join(", ")} } from "./calabasas/_generated/sync";
3558
3568
 
3559
3569
  3. Add CALABASAS_SECRET to your Convex environment variables
3560
3570
  4. Create your event handler in convex/discord.ts
@@ -3563,7 +3573,7 @@ async function generate(options) {
3563
3573
  p5.note(`1. Add CALABASAS_SECRET to your Convex environment variables
3564
3574
  2. Create your handler in convex/discord.ts:
3565
3575
 
3566
- import { handleDiscordEvent } from "./discord.generated";
3576
+ import { handleDiscordEvent } from "./calabasas/_generated/discord";
3567
3577
 
3568
3578
  export const receive = handleDiscordEvent({
3569
3579
  messageCreate: async (ctx, event) => {
@@ -5702,9 +5712,10 @@ async function add(componentNames) {
5702
5712
  const cwd = process.cwd();
5703
5713
  const convexDir = path6.resolve(cwd, "convex");
5704
5714
  const calabasasDir = path6.join(convexDir, "calabasas");
5705
- const configPath = path6.join(convexDir, "calabasas.config.ts");
5706
- if (!fs6.existsSync(calabasasDir) || !fs6.existsSync(path6.join(calabasasDir, "schema.ts"))) {
5707
- console.log("Error: convex/calabasas/schema.ts not found.");
5715
+ const configPath = path6.join(calabasasDir, "config.ts");
5716
+ const generatedDir = path6.join(calabasasDir, "_generated");
5717
+ if (!fs6.existsSync(generatedDir) || !fs6.existsSync(path6.join(generatedDir, "schema.ts"))) {
5718
+ console.log("Error: convex/calabasas/_generated/schema.ts not found.");
5708
5719
  console.log("");
5709
5720
  console.log("Run `calabasas init` and `calabasas generate` first to enable sync.");
5710
5721
  return;
@@ -5755,7 +5766,7 @@ async function add(componentNames) {
5755
5766
  const missingSyncTypes = comp.requiredSyncTypes.filter((t) => !enabledSync.has(t));
5756
5767
  if (missingSyncTypes.length > 0) {
5757
5768
  p7.log.warn(`Skipped ${comp.name} — requires sync types not enabled: ${missingSyncTypes.join(", ")}`);
5758
- p7.log.info(`Enable them in calabasas.config.ts and re-run \`calabasas generate\`.`);
5769
+ p7.log.info(`Enable them in convex/calabasas/config.ts and re-run \`calabasas generate\`.`);
5759
5770
  continue;
5760
5771
  }
5761
5772
  if (!fs6.existsSync(componentsDir)) {
@@ -5858,8 +5869,199 @@ ${props}
5858
5869
  }
5859
5870
  }
5860
5871
 
5861
- // src/commands/bot.ts
5872
+ // src/commands/migrate.ts
5873
+ import * as p9 from "@clack/prompts";
5874
+
5875
+ // src/migrations/dedicated-folder.ts
5876
+ import * as fs7 from "fs";
5877
+ import * as path7 from "path";
5862
5878
  import * as p8 from "@clack/prompts";
5879
+ var FILE_MOVES = [
5880
+ ["convex/calabasas.config.ts", "convex/calabasas/config.ts"],
5881
+ ["convex/discord.generated.ts", "convex/calabasas/_generated/discord.ts"],
5882
+ [
5883
+ "convex/discord.actions.generated.ts",
5884
+ "convex/calabasas/_generated/discord.actions.ts"
5885
+ ],
5886
+ ["convex/calabasas/schema.ts", "convex/calabasas/_generated/schema.ts"],
5887
+ ["convex/calabasas/sync.ts", "convex/calabasas/_generated/sync.ts"]
5888
+ ];
5889
+ var IMPORT_REWRITES = [
5890
+ [
5891
+ /from\s+["']\.\/discord\.generated["']/g,
5892
+ `from "./calabasas/_generated/discord"`
5893
+ ],
5894
+ [
5895
+ /from\s+["']\.\/discord\.actions\.generated["']/g,
5896
+ `from "./calabasas/_generated/discord.actions"`
5897
+ ],
5898
+ [
5899
+ /from\s+["']\.\/calabasas\/schema["']/g,
5900
+ `from "./calabasas/_generated/schema"`
5901
+ ],
5902
+ [
5903
+ /from\s+["']\.\/calabasas\/sync["']/g,
5904
+ `from "./calabasas/_generated/sync"`
5905
+ ]
5906
+ ];
5907
+ var GENERATED_IMPORT_REWRITES = {
5908
+ "convex/calabasas/_generated/discord.ts": [
5909
+ [
5910
+ /from\s+["']\.\/(_generated\/server)["']/g,
5911
+ `from "../../_generated/server"`
5912
+ ],
5913
+ [
5914
+ /from\s+["']\.\/(_generated\/api)["']/g,
5915
+ `from "../../_generated/api"`
5916
+ ]
5917
+ ],
5918
+ "convex/calabasas/_generated/sync.ts": [
5919
+ [
5920
+ /from\s+["']\.\.\/(_generated\/server)["']/g,
5921
+ `from "../../_generated/server"`
5922
+ ],
5923
+ [
5924
+ /from\s+["']\.\.\/(_generated\/api)["']/g,
5925
+ `from "../../_generated/api"`
5926
+ ],
5927
+ [
5928
+ /internal\.calabasas\.sync\./g,
5929
+ `internal.calabasas._generated.sync.`
5930
+ ]
5931
+ ]
5932
+ };
5933
+ function isUserFile(filePath) {
5934
+ const rel = path7.relative(path7.resolve(process.cwd(), "convex"), filePath);
5935
+ if (rel.startsWith("_generated"))
5936
+ return false;
5937
+ if (rel.includes("_generated"))
5938
+ return false;
5939
+ return /\.tsx?$/.test(filePath);
5940
+ }
5941
+ var dedicatedFolder = {
5942
+ name: "dedicated-folder",
5943
+ description: "Move all Calabasas files under convex/calabasas/ with _generated/ subdirectory",
5944
+ async run() {
5945
+ const cwd = process.cwd();
5946
+ const convexDir = path7.resolve(cwd, "convex");
5947
+ if (!fs7.existsSync(convexDir)) {
5948
+ p8.log.error("convex/ directory not found.");
5949
+ return;
5950
+ }
5951
+ const generatedDir = path7.join(convexDir, "calabasas", "_generated");
5952
+ if (!fs7.existsSync(generatedDir)) {
5953
+ fs7.mkdirSync(generatedDir, { recursive: true });
5954
+ }
5955
+ let movedCount = 0;
5956
+ for (const [oldRel, newRel] of FILE_MOVES) {
5957
+ const oldPath = path7.join(cwd, oldRel);
5958
+ const newPath = path7.join(cwd, newRel);
5959
+ if (!fs7.existsSync(oldPath))
5960
+ continue;
5961
+ if (fs7.existsSync(newPath)) {
5962
+ p8.log.warn(`Skipped ${oldRel} → ${newRel} (target already exists)`);
5963
+ continue;
5964
+ }
5965
+ fs7.renameSync(oldPath, newPath);
5966
+ p8.log.success(`Moved ${oldRel} → ${newRel}`);
5967
+ movedCount++;
5968
+ }
5969
+ let fixedGeneratedCount = 0;
5970
+ for (const [relPath, rewrites] of Object.entries(GENERATED_IMPORT_REWRITES)) {
5971
+ const absPath = path7.join(cwd, relPath);
5972
+ if (!fs7.existsSync(absPath))
5973
+ continue;
5974
+ let content = fs7.readFileSync(absPath, "utf-8");
5975
+ let changed = false;
5976
+ for (const [pattern, replacement] of rewrites) {
5977
+ const updated = content.replace(pattern, replacement);
5978
+ if (updated !== content) {
5979
+ content = updated;
5980
+ changed = true;
5981
+ }
5982
+ }
5983
+ if (changed) {
5984
+ fs7.writeFileSync(absPath, content);
5985
+ fixedGeneratedCount++;
5986
+ p8.log.success(`Fixed imports in ${relPath}`);
5987
+ }
5988
+ }
5989
+ let rewrittenCount = 0;
5990
+ const userFiles = collectTsFiles(convexDir).filter(isUserFile);
5991
+ for (const filePath of userFiles) {
5992
+ let content = fs7.readFileSync(filePath, "utf-8");
5993
+ let changed = false;
5994
+ for (const [pattern, replacement] of IMPORT_REWRITES) {
5995
+ const updated = content.replace(pattern, replacement);
5996
+ if (updated !== content) {
5997
+ content = updated;
5998
+ changed = true;
5999
+ }
6000
+ }
6001
+ if (changed) {
6002
+ fs7.writeFileSync(filePath, content);
6003
+ const rel = path7.relative(cwd, filePath);
6004
+ p8.log.success(`Rewrote imports in ${rel}`);
6005
+ rewrittenCount++;
6006
+ }
6007
+ }
6008
+ if (movedCount === 0 && rewrittenCount === 0 && fixedGeneratedCount === 0) {
6009
+ p8.log.info("Already up to date.");
6010
+ return;
6011
+ }
6012
+ p8.log.info(`
6013
+ Moved ${movedCount} file${movedCount !== 1 ? "s" : ""}, ` + `fixed imports in ${fixedGeneratedCount + rewrittenCount} file${fixedGeneratedCount + rewrittenCount !== 1 ? "s" : ""}.`);
6014
+ }
6015
+ };
6016
+ function collectTsFiles(dir) {
6017
+ const results = [];
6018
+ const entries = fs7.readdirSync(dir, { withFileTypes: true });
6019
+ for (const entry of entries) {
6020
+ const fullPath = path7.join(dir, entry.name);
6021
+ if (entry.isDirectory()) {
6022
+ results.push(...collectTsFiles(fullPath));
6023
+ } else if (/\.tsx?$/.test(entry.name)) {
6024
+ results.push(fullPath);
6025
+ }
6026
+ }
6027
+ return results;
6028
+ }
6029
+
6030
+ // src/migrations/index.ts
6031
+ var migrations = [dedicatedFolder];
6032
+
6033
+ // src/commands/migrate.ts
6034
+ async function migrate(name) {
6035
+ if (!name) {
6036
+ p9.intro("calabasas migrate");
6037
+ if (migrations.length === 0) {
6038
+ p9.log.info("No migrations available.");
6039
+ p9.outro("Done");
6040
+ return;
6041
+ }
6042
+ p9.log.info(`Available migrations:
6043
+ `);
6044
+ for (const m of migrations) {
6045
+ p9.log.message(` ${m.name} — ${m.description}`);
6046
+ }
6047
+ p9.log.info(`
6048
+ Run a migration with: calabasas migrate <name>`);
6049
+ p9.outro("Done");
6050
+ return;
6051
+ }
6052
+ const migration = migrations.find((m) => m.name === name);
6053
+ if (!migration) {
6054
+ p9.log.error(`Unknown migration: ${name}`);
6055
+ p9.log.info(`Available migrations: ${migrations.map((m) => m.name).join(", ")}`);
6056
+ return;
6057
+ }
6058
+ p9.intro(`calabasas migrate ${name}`);
6059
+ await migration.run();
6060
+ p9.outro("Migration complete!");
6061
+ }
6062
+
6063
+ // src/commands/bot.ts
6064
+ import * as p10 from "@clack/prompts";
5863
6065
  import pc from "picocolors";
5864
6066
  var INTENTS = [
5865
6067
  { name: "Guilds", value: 1 << 0, description: "Guild create/update/delete, roles, channels" },
@@ -5882,9 +6084,9 @@ var INTENTS = [
5882
6084
  { name: "Auto Moderation Config", value: 1 << 20, description: "Auto mod rule changes" },
5883
6085
  { name: "Auto Moderation Execution", value: 1 << 21, description: "Auto mod action execution" }
5884
6086
  ];
5885
- async function apiRequest(method, path7, body, env) {
6087
+ async function apiRequest(method, path8, body, env) {
5886
6088
  const config = getConfig(env);
5887
- const url = `${getApiUrlForEnv(env)}${path7}`;
6089
+ const url = `${getApiUrlForEnv(env)}${path8}`;
5888
6090
  const response = await fetch(url, {
5889
6091
  method,
5890
6092
  headers: {
@@ -5910,43 +6112,43 @@ async function botAdd(options) {
5910
6112
  console.log("Not logged in. Run `calabasas login` first.");
5911
6113
  return;
5912
6114
  }
5913
- p8.intro("calabasas bot add");
5914
- const name = await p8.text({
6115
+ p10.intro("calabasas bot add");
6116
+ const name = await p10.text({
5915
6117
  message: "Bot name",
5916
6118
  placeholder: "My Discord Bot",
5917
6119
  validate: (v) => v.trim() ? undefined : "Bot name is required"
5918
6120
  });
5919
- if (p8.isCancel(name)) {
5920
- p8.cancel("Cancelled.");
6121
+ if (p10.isCancel(name)) {
6122
+ p10.cancel("Cancelled.");
5921
6123
  return;
5922
6124
  }
5923
- const discordAppId = await p8.text({
6125
+ const discordAppId = await p10.text({
5924
6126
  message: "Discord Application ID",
5925
6127
  placeholder: "123456789012345678",
5926
6128
  validate: (v) => v.trim() ? undefined : "Discord Application ID is required"
5927
6129
  });
5928
- if (p8.isCancel(discordAppId)) {
5929
- p8.cancel("Cancelled.");
6130
+ if (p10.isCancel(discordAppId)) {
6131
+ p10.cancel("Cancelled.");
5930
6132
  return;
5931
6133
  }
5932
- const token = await p8.password({
6134
+ const token = await p10.password({
5933
6135
  message: "Bot token",
5934
6136
  validate: (v) => v.trim() ? undefined : "Bot token is required"
5935
6137
  });
5936
- if (p8.isCancel(token)) {
5937
- p8.cancel("Cancelled.");
6138
+ if (p10.isCancel(token)) {
6139
+ p10.cancel("Cancelled.");
5938
6140
  return;
5939
6141
  }
5940
- const convexUrl = await p8.text({
6142
+ const convexUrl = await p10.text({
5941
6143
  message: "Your Convex URL",
5942
6144
  placeholder: "https://your-project.convex.cloud",
5943
6145
  validate: (v) => v.trim() ? undefined : "Convex URL is required"
5944
6146
  });
5945
- if (p8.isCancel(convexUrl)) {
5946
- p8.cancel("Cancelled.");
6147
+ if (p10.isCancel(convexUrl)) {
6148
+ p10.cancel("Cancelled.");
5947
6149
  return;
5948
6150
  }
5949
- const selectedIntents = await p8.multiselect({
6151
+ const selectedIntents = await p10.multiselect({
5950
6152
  message: "Select Gateway Intents",
5951
6153
  options: INTENTS.map((intent, i) => ({
5952
6154
  value: i,
@@ -5956,12 +6158,12 @@ async function botAdd(options) {
5956
6158
  initialValues: [0, 9],
5957
6159
  required: true
5958
6160
  });
5959
- if (p8.isCancel(selectedIntents)) {
5960
- p8.cancel("Cancelled.");
6161
+ if (p10.isCancel(selectedIntents)) {
6162
+ p10.cancel("Cancelled.");
5961
6163
  return;
5962
6164
  }
5963
6165
  const intents = selectedIntents.reduce((sum, i) => sum + INTENTS[i].value, 0);
5964
- const s = p8.spinner();
6166
+ const s = p10.spinner();
5965
6167
  s.start("Creating bot...");
5966
6168
  const { ok, data, status } = await apiRequest("POST", "/api/cli/bots", {
5967
6169
  name: name.trim(),
@@ -5972,16 +6174,16 @@ async function botAdd(options) {
5972
6174
  }, env);
5973
6175
  if (!ok) {
5974
6176
  s.stop("Failed to create bot");
5975
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6177
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
5976
6178
  return;
5977
6179
  }
5978
6180
  s.stop("Bot created!");
5979
6181
  const sharedSecret = data.sharedSecret;
5980
- p8.note(`${sharedSecret}
6182
+ p10.note(`${sharedSecret}
5981
6183
 
5982
6184
  Add this to your Convex environment as CALABASAS_SECRET`, "Shared Secret");
5983
- p8.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");
5984
- p8.outro("Bot created successfully!");
6185
+ 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
+ p10.outro("Bot created successfully!");
5985
6187
  }
5986
6188
  async function botList(options) {
5987
6189
  if (options.dev && options.prod) {
@@ -6052,24 +6254,24 @@ async function botRemove(botId, options) {
6052
6254
  console.log("Not logged in. Run `calabasas login` first.");
6053
6255
  return;
6054
6256
  }
6055
- p8.intro("calabasas bot remove");
6056
- const confirmed = await p8.confirm({
6257
+ p10.intro("calabasas bot remove");
6258
+ const confirmed = await p10.confirm({
6057
6259
  message: `Are you sure you want to remove bot ${botId}?`
6058
6260
  });
6059
- if (p8.isCancel(confirmed) || !confirmed) {
6060
- p8.cancel("Cancelled.");
6261
+ if (p10.isCancel(confirmed) || !confirmed) {
6262
+ p10.cancel("Cancelled.");
6061
6263
  return;
6062
6264
  }
6063
- const s = p8.spinner();
6265
+ const s = p10.spinner();
6064
6266
  s.start("Removing bot...");
6065
6267
  const { ok, data, status } = await apiRequest("DELETE", `/api/cli/bots?id=${botId}`, undefined, env);
6066
6268
  if (!ok) {
6067
6269
  s.stop("Failed to remove bot");
6068
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6270
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
6069
6271
  return;
6070
6272
  }
6071
6273
  s.stop("Done");
6072
- p8.outro("Bot removed successfully!");
6274
+ p10.outro("Bot removed successfully!");
6073
6275
  }
6074
6276
  async function botEdit(botId, options) {
6075
6277
  if (options.dev && options.prod) {
@@ -6088,8 +6290,8 @@ async function botEdit(botId, options) {
6088
6290
  const updates = { botId };
6089
6291
  const hasFlags = options.name || options.url || options.token || options.intents;
6090
6292
  if (!hasFlags) {
6091
- p8.intro("calabasas bot edit");
6092
- const field = await p8.select({
6293
+ p10.intro("calabasas bot edit");
6294
+ const field = await p10.select({
6093
6295
  message: "Which field do you want to edit?",
6094
6296
  options: [
6095
6297
  { value: "name", label: "Bot name" },
@@ -6098,35 +6300,35 @@ async function botEdit(botId, options) {
6098
6300
  { value: "intents", label: "Intents value" }
6099
6301
  ]
6100
6302
  });
6101
- if (p8.isCancel(field)) {
6102
- p8.cancel("Cancelled.");
6303
+ if (p10.isCancel(field)) {
6304
+ p10.cancel("Cancelled.");
6103
6305
  return;
6104
6306
  }
6105
6307
  if (field === "name") {
6106
- const value = await p8.text({ message: "New bot name" });
6107
- if (p8.isCancel(value)) {
6108
- p8.cancel("Cancelled.");
6308
+ const value = await p10.text({ message: "New bot name" });
6309
+ if (p10.isCancel(value)) {
6310
+ p10.cancel("Cancelled.");
6109
6311
  return;
6110
6312
  }
6111
6313
  updates.name = value;
6112
6314
  } else if (field === "url") {
6113
- const value = await p8.text({ message: "New Convex URL" });
6114
- if (p8.isCancel(value)) {
6115
- p8.cancel("Cancelled.");
6315
+ const value = await p10.text({ message: "New Convex URL" });
6316
+ if (p10.isCancel(value)) {
6317
+ p10.cancel("Cancelled.");
6116
6318
  return;
6117
6319
  }
6118
6320
  updates.convexUrl = value;
6119
6321
  } else if (field === "token") {
6120
- const value = await p8.password({ message: "New bot token" });
6121
- if (p8.isCancel(value)) {
6122
- p8.cancel("Cancelled.");
6322
+ const value = await p10.password({ message: "New bot token" });
6323
+ if (p10.isCancel(value)) {
6324
+ p10.cancel("Cancelled.");
6123
6325
  return;
6124
6326
  }
6125
6327
  updates.token = value;
6126
6328
  } else if (field === "intents") {
6127
- const value = await p8.text({ message: "New intents value (number)" });
6128
- if (p8.isCancel(value)) {
6129
- p8.cancel("Cancelled.");
6329
+ const value = await p10.text({ message: "New intents value (number)" });
6330
+ if (p10.isCancel(value)) {
6331
+ p10.cancel("Cancelled.");
6130
6332
  return;
6131
6333
  }
6132
6334
  updates.intents = parseInt(value, 10);
@@ -6146,17 +6348,17 @@ async function botEdit(botId, options) {
6146
6348
  console.log("No updates specified.");
6147
6349
  return;
6148
6350
  }
6149
- const s = p8.spinner();
6351
+ const s = p10.spinner();
6150
6352
  s.start("Updating bot...");
6151
6353
  const { ok, data, status } = await apiRequest("PATCH", "/api/cli/bots", updates, env);
6152
6354
  if (!ok) {
6153
6355
  s.stop("Failed to update bot");
6154
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6356
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
6155
6357
  return;
6156
6358
  }
6157
6359
  s.stop("Done");
6158
- p8.note("The Gateway will automatically reconnect with the new settings.", "Note");
6159
- p8.outro("Bot updated successfully!");
6360
+ p10.note("The Gateway will automatically reconnect with the new settings.", "Note");
6361
+ p10.outro("Bot updated successfully!");
6160
6362
  }
6161
6363
 
6162
6364
  // src/index.ts
@@ -6173,10 +6375,11 @@ program2.name("calabasas").description("CLI for Calabasas - Discord Gateway as a
6173
6375
  program2.command("login").description("Authenticate with Calabasas using your API key").option("--dev", "Use development environment").option("--prod", "Use production environment").action(login);
6174
6376
  program2.command("logout").description("Clear stored credentials").option("--dev", "Use development environment").option("--prod", "Use production environment").action(logout);
6175
6377
  program2.command("init").description("Initialize Calabasas config in your Convex project").action(init);
6176
- program2.command("push").description("Push your calabasas.config.ts to Calabasas").option("-c, --config <path>", "Path to config file", "convex/calabasas.config.ts").option("-b, --bot <botId>", "Bot ID to configure (prompts if not specified)").option("--dev", "Push to development environment").option("--prod", "Push to production environment").action(push);
6177
- program2.command("generate").description("Generate discord.generated.ts with type-safe handlers").option("-o, --output <path>", "Output path", "convex/discord.generated.ts").action(generate);
6378
+ program2.command("push").description("Push your Calabasas config to the server").option("-c, --config <path>", "Path to config file", "convex/calabasas/config.ts").option("-b, --bot <botId>", "Bot ID to configure (prompts if not specified)").option("--dev", "Push to development environment").option("--prod", "Push to production environment").action(push);
6379
+ program2.command("generate").description("Generate type-safe Discord handlers and helpers").option("-o, --output <path>", "Output path", "convex/calabasas/_generated/discord.ts").action(generate);
6178
6380
  program2.command("skill").description("Generate Calabasas documentation for AI assistants").option("--dev", "Use development environment").option("--prod", "Use production environment").action(skill);
6179
6381
  program2.command("add [components...]").description("Add Discord UI components to your project").action(add);
6382
+ program2.command("migrate [name]").description("Run a codemod migration (list available if no name given)").action(migrate);
6180
6383
  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);
6181
6384
  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);
6182
6385
  var bot = program2.command("bot").description("Manage Discord bots");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calabasas",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {