calabasas 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +162 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7834,6 +7834,164 @@ Run ${pc3.cyan("`calabasas init`")} first.`);
7834
7834
  await interactiveConfig(currentConfig, configPath);
7835
7835
  }
7836
7836
 
7837
+ // src/commands/webhook.ts
7838
+ import pc4 from "picocolors";
7839
+ function resolveAuthOrExit() {
7840
+ const resolved = resolvePlatformApiKey({});
7841
+ if (!resolved) {
7842
+ console.error(pc4.red("Error:") + ` no Calabasas credentials found.
7843
+ ` + " Run `calabasas login` (user key) or set CALABASAS_PLATFORM_API_KEY in .env.local.");
7844
+ process.exit(1);
7845
+ }
7846
+ return resolved.key;
7847
+ }
7848
+ function parseDuration(input) {
7849
+ const match = /^(\d+)(s|m|h|d)$/.exec(input.trim());
7850
+ if (!match)
7851
+ return null;
7852
+ const value = Number(match[1]);
7853
+ const unit = match[2];
7854
+ const multipliers = {
7855
+ s: 1000,
7856
+ m: 60000,
7857
+ h: 3600000,
7858
+ d: 86400000
7859
+ };
7860
+ return value * multipliers[unit];
7861
+ }
7862
+ function normalizeCreateInput(options, now = Date.now()) {
7863
+ if (!options.bot) {
7864
+ return { ok: false, error: "--bot <botId> is required" };
7865
+ }
7866
+ if (options.action !== "sendDM" && options.action !== "sendChannelMessage") {
7867
+ return {
7868
+ ok: false,
7869
+ error: `invalid --action: ${options.action ?? "(missing)"}. Must be one of: sendDM, sendChannelMessage`
7870
+ };
7871
+ }
7872
+ let params;
7873
+ if (options.action === "sendDM") {
7874
+ if (!options.userId) {
7875
+ return { ok: false, error: "--user-id <discordUserId> is required for sendDM" };
7876
+ }
7877
+ params = { userId: options.userId };
7878
+ } else {
7879
+ if (!options.channelId) {
7880
+ return {
7881
+ ok: false,
7882
+ error: "--channel-id <discordChannelId> is required for sendChannelMessage"
7883
+ };
7884
+ }
7885
+ params = { channelId: options.channelId };
7886
+ }
7887
+ let maxUses;
7888
+ if (options.maxUses !== undefined) {
7889
+ const parsed = Number(options.maxUses);
7890
+ if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
7891
+ return {
7892
+ ok: false,
7893
+ error: `invalid --max-uses: ${options.maxUses}. Must be a positive integer.`
7894
+ };
7895
+ }
7896
+ maxUses = parsed;
7897
+ }
7898
+ let expiresAt;
7899
+ if (options.expires !== undefined) {
7900
+ const ms = parseDuration(options.expires);
7901
+ if (ms === null) {
7902
+ return {
7903
+ ok: false,
7904
+ error: `invalid --expires: ${options.expires}. Format: <number><unit> where unit is s|m|h|d (e.g. 30m, 24h, 7d).`
7905
+ };
7906
+ }
7907
+ expiresAt = now + ms;
7908
+ }
7909
+ return {
7910
+ ok: true,
7911
+ payload: {
7912
+ botId: options.bot,
7913
+ action: options.action,
7914
+ params,
7915
+ label: options.label,
7916
+ maxUses,
7917
+ expiresAt,
7918
+ platformId: options.platformId
7919
+ }
7920
+ };
7921
+ }
7922
+ async function webhookCreate(options) {
7923
+ const apiKey = resolveAuthOrExit();
7924
+ const result = normalizeCreateInput(options);
7925
+ if (!result.ok) {
7926
+ console.error(pc4.red("Error:") + " " + result.error);
7927
+ process.exit(1);
7928
+ }
7929
+ const { payload } = result;
7930
+ const { ok, data, status } = await platformApiRequest("POST", "/api/cli/webhooks", apiKey, payload);
7931
+ if (!ok) {
7932
+ const errMsg = data?.error ?? `HTTP ${status}`;
7933
+ console.error(pc4.red("Error:") + " " + errMsg);
7934
+ process.exit(1);
7935
+ }
7936
+ const created = data;
7937
+ console.log(pc4.green("Webhook created."));
7938
+ console.log("");
7939
+ console.log(`URL: ${pc4.cyan(created.url)}`);
7940
+ console.log(`token: ${created.token}`);
7941
+ console.log(`webhookId: ${created.webhookId}`);
7942
+ console.log(`action: ${created.action}`);
7943
+ console.log(`params: ${JSON.stringify(created.params)}`);
7944
+ if (payload.label)
7945
+ console.log(`label: ${payload.label}`);
7946
+ if (payload.maxUses !== undefined)
7947
+ console.log(`maxUses: ${payload.maxUses}`);
7948
+ if (payload.expiresAt !== undefined) {
7949
+ console.log(`expiresAt: ${new Date(payload.expiresAt).toISOString()}`);
7950
+ }
7951
+ console.log("");
7952
+ console.log(pc4.dim(`Fire it with: curl -X POST -H 'Content-Type: application/json' -d '{"content":"hi"}' ` + created.url));
7953
+ }
7954
+ async function webhookList(options) {
7955
+ const apiKey = resolveAuthOrExit();
7956
+ const { ok, data, status } = await platformApiRequest("GET", "/api/cli/webhooks", apiKey);
7957
+ if (!ok) {
7958
+ const errMsg = data?.error ?? `HTTP ${status}`;
7959
+ console.error(pc4.red("Error:") + " " + errMsg);
7960
+ process.exit(1);
7961
+ }
7962
+ const webhooks = data.webhooks;
7963
+ if (options.json) {
7964
+ console.log(JSON.stringify(webhooks, null, 2));
7965
+ return;
7966
+ }
7967
+ if (webhooks.length === 0) {
7968
+ console.log("No webhooks found.");
7969
+ console.log(pc4.dim("Create one with: calabasas webhook create --bot <id> --action sendDM --user-id <id>"));
7970
+ return;
7971
+ }
7972
+ for (const w of webhooks) {
7973
+ const target = w.params.userId ? `user ${w.params.userId}` : `channel ${w.params.channelId}`;
7974
+ const uses = w.maxUses !== undefined ? `${w.usageCount}/${w.maxUses}` : `${w.usageCount}/—`;
7975
+ const statusColor = w.status === "active" ? pc4.green : w.status === "expired" ? pc4.yellow : pc4.red;
7976
+ console.log(`${pc4.cyan(w.token)} ${w.action} ${target} uses=${uses} ${statusColor(w.status)}` + (w.label ? ` label=${w.label}` : ""));
7977
+ console.log(` ${pc4.dim(w.url)}`);
7978
+ }
7979
+ }
7980
+ async function webhookRevoke(token, _options) {
7981
+ if (!token) {
7982
+ console.error(pc4.red("Error:") + " token argument is required.");
7983
+ process.exit(1);
7984
+ }
7985
+ const apiKey = resolveAuthOrExit();
7986
+ const { ok, data, status } = await platformApiRequest("DELETE", `/api/cli/webhooks?token=${encodeURIComponent(token)}`, apiKey);
7987
+ if (!ok) {
7988
+ const errMsg = data?.error ?? `HTTP ${status}`;
7989
+ console.error(pc4.red("Error:") + " " + errMsg);
7990
+ process.exit(1);
7991
+ }
7992
+ console.log(pc4.green("Webhook revoked."));
7993
+ }
7994
+
7837
7995
  // src/index.ts
7838
7996
  var dashboard = async () => {
7839
7997
  const mod = await import("./dashboard-a862sabz.js");
@@ -7870,4 +8028,8 @@ platformCmd.command("info").description("Show platform info (secret, URL, API ke
7870
8028
  platformCmd.command("set-url").description("Set Convex URL for your platform").action(platformSetUrl);
7871
8029
  platformCmd.command("rotate-key").description("Rotate your platform API key (for SDK access)").action(platformRotateKey);
7872
8030
  platformCmd.command("rotate-secret").description("Rotate the platform secret (invalidates CALABASAS_SECRET)").action(platformRotateSecret);
8031
+ var webhookCmd = program2.command("webhook").description("Manage one-off webhook URLs that fire pre-bound Discord actions");
8032
+ webhookCmd.command("create").description("Create a webhook URL bound to a single bot, action, and target").requiredOption("-b, --bot <botId>", "Bot ID that will perform the action").requiredOption("-a, --action <action>", "Action to fire: sendDM | sendChannelMessage").option("--user-id <discordUserId>", "Target user ID (required for sendDM)").option("--channel-id <discordChannelId>", "Target channel ID (required for sendChannelMessage)").option("--label <label>", "Human-readable label for listing").option("--max-uses <number>", "Max times this URL can be fired before it stops working").option("--expires <duration>", "Auto-expire after duration: e.g. 30m, 24h, 7d").option("--platform-id <id>", "Platform ID (only needed with user key + multiple platforms)").action(webhookCreate);
8033
+ webhookCmd.command("list").alias("ls").description("List all non-revoked webhooks for the current platform").option("--json", "Output raw JSON instead of a table").action(webhookList);
8034
+ webhookCmd.command("revoke <token>").description("Revoke a webhook by token (soft delete)").option("-y, --yes", "Skip confirmation prompt (currently a no-op)").action(webhookRevoke);
7873
8035
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calabasas",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {