fix: add hosted minimax onboarding (#495, thanks @tobiasbischoff)

This commit is contained in:
Peter Steinberger
2026-01-09 13:38:51 +01:00
parent 897685a2de
commit dc6f22c2c5
6 changed files with 527 additions and 149 deletions

View File

@@ -56,6 +56,7 @@
- Onboarding: QuickStart jumps straight into provider selection with Telegram preselected when unset.
- Onboarding: QuickStart auto-installs the Gateway daemon with Node (no runtime picker).
- Onboarding: clarify WhatsApp owner number prompt and label pairing phone number.
- Onboarding: add hosted MiniMax M2.1 API key flow + config. (#495) — thanks @tobiasbischoff
- Daemon runtime: remove Bun from selection options.
- CLI: restore hidden `gateway-daemon` alias for legacy launchd configs.
- Onboarding/Configure: add OpenAI API key flow that stores in shared `~/.clawdbot/.env` for launchd; simplify Anthropic token prompt order.

View File

@@ -1,5 +1,5 @@
---
summary: "CLI reference for clawdbot commands, subcommands, and options"
summary: "Clawdbot CLI reference for `clawdbot` commands, subcommands, and options"
read_when:
- Adding or modifying CLI commands or options
- Documenting new command surfaces
@@ -7,13 +7,13 @@ read_when:
# CLI reference
This page mirrors `src/cli/*` and is the source of truth for CLI behavior.
If you change the CLI code, update this doc.
This page describes the current CLI behavior. If commands change, update this doc.
## Global flags
- `--dev`: isolate state under `~/.clawdbot-dev` and shift default ports.
- `--profile <name>`: isolate state under `~/.clawdbot-<name>`.
- `--no-color`: disable ANSI colors.
- `-V`, `--version`, `-v`: print version and exit.
## Output styling
@@ -21,11 +21,12 @@ If you change the CLI code, update this doc.
- ANSI colors and progress indicators only render in TTY sessions.
- OSC-8 hyperlinks render as clickable links in supported terminals; otherwise we fall back to plain URLs.
- `--json` (and `--plain` where supported) disables styling for clean output.
- `--no-color` disables ANSI styling; `NO_COLOR=1` is also respected.
- Long-running commands show a progress indicator (OSC 9;4 when supported).
## Color palette
Clawdbot uses a lobster palette for CLI output. Source of truth: `src/terminal/theme.ts`.
Clawdbot uses a lobster palette for CLI output.
- `accent` (#FF5A2D): headings, provider labels, primary highlights.
- `accentBright` (#FF7A3D): command names, emphasis.
@@ -36,6 +37,8 @@ Clawdbot uses a lobster palette for CLI output. Source of truth: `src/terminal/t
- `error` (#E23D2D): errors, failures.
- `muted` (#8B7F77): de-emphasis, metadata.
Palette source of truth: `src/terminal/palette.ts` (aka “lobster seam”).
## Command tree
```
@@ -55,8 +58,7 @@ clawdbot [--dev] [--profile <name>] <command>
list
info
check
send
poll
message
agent
agents
list
@@ -69,6 +71,7 @@ clawdbot [--dev] [--profile <name>] <command>
call
health
status
discover
models
list
status
@@ -206,7 +209,8 @@ Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage).
Subcommands:
- `providers list`: show configured chat providers and auth profiles (Claude Code + Codex CLI OAuth sync included).
- `providers status`: check gateway reachability and provider health (`--probe` to verify credentials; use `status --deep` for local-only probes).
- `providers status`: check gateway reachability and provider health (`--probe` to verify credentials and run small provider audits; use `status --deep` for local-only probes).
- Tip: `providers status` prints warnings with suggested fixes when it can detect common misconfigurations (then points you to `clawdbot doctor`).
- `providers add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode.
- `providers remove`: disable by default; pass `--delete` to remove config entries without prompts.
- `providers login`: interactive provider login (WhatsApp Web only).
@@ -231,7 +235,9 @@ Common options:
- `--json`: output JSON (includes usage unless `--no-usage` is set).
OAuth sync sources:
- `~/.claude/.credentials.json``anthropic:claude-cli`
- Claude Code`anthropic:claude-cli`
- macOS: Keychain item "Claude Code-credentials" (choose "Always Allow" to avoid launchd prompts)
- Linux/Windows: `~/.claude/.credentials.json`
- `~/.codex/auth.json``openai-codex:codex-cli`
More detail: [/concepts/oauth](/concepts/oauth)
@@ -282,37 +288,25 @@ Options:
## Messaging + agent
### `send`
Send a message through a provider.
### `message`
Unified outbound messaging + provider actions.
Required:
- `--to <dest>`
- `--message <text>`
See: [/cli/message](/cli/message)
Options:
- `--media <path-or-url>`
- `--gif-playback`
- `--provider <whatsapp|telegram|discord|slack|signal|imessage>`
- `--account <id>` (WhatsApp)
- `--dry-run`
- `--json`
- `--verbose`
Subcommands:
- `message send|poll|react|reactions|read|edit|delete|pin|unpin|pins|permissions|search|timeout|kick|ban`
- `message thread <create|list|reply>`
- `message emoji <list|upload>`
- `message sticker <send|upload>`
- `message role <info|add|remove>`
- `message channel <info|list>`
- `message member info`
- `message voice status`
- `message event <list|create>`
### `poll`
Create a poll (WhatsApp or Discord).
Required:
- `--to <id>`
- `--question <text>`
- `--option <choice>` (repeat 2-12 times)
Options:
- `--max-selections <n>`
- `--duration-hours <n>` (Discord)
- `--provider <whatsapp|discord>`
- `--dry-run`
- `--json`
- `--verbose`
Examples:
- `clawdbot message send --to +15555550123 --message "Hi"`
- `clawdbot message poll --provider discord --to channel:123 --poll-question "Snack?" --poll-option Pizza --poll-option Sushi`
### `agent`
Run one agent turn via the Gateway (or `--local` embedded).
@@ -416,6 +410,8 @@ Options:
- `--tailscale <off|serve|funnel>`
- `--tailscale-reset-on-exit`
- `--allow-unconfigured`
- `--dev`
- `--reset`
- `--force` (kill existing listener on port)
- `--verbose`
- `--ws-log <auto|full|compact>`
@@ -443,10 +439,17 @@ Notes:
### `logs`
Tail Gateway file logs via RPC.
Notes:
- TTY sessions render a colorized, structured view; non-TTY falls back to plain text.
- `--json` emits line-delimited JSON (one log event per line).
Examples:
```bash
clawdbot logs --follow
clawdbot logs --limit 200
clawdbot logs --plain
clawdbot logs --json
clawdbot logs --no-color
```
### `gateway <subcommand>`
@@ -480,6 +483,9 @@ Options:
Options:
- `--json`
- `--plain`
- `--check` (exit 1=expired/missing, 2=expiring)
Always includes the auth overview and OAuth expiry status for profiles in the auth store.
### `models set <model>`
Set `agent.model.primary`.

View File

@@ -8,9 +8,8 @@ import {
import { configureCommand } from "../commands/configure.js";
import { doctorCommand } from "../commands/doctor.js";
import { healthCommand } from "../commands/health.js";
import { messageCommand } from "../commands/message.js";
import { onboardCommand } from "../commands/onboard.js";
import { pollCommand } from "../commands/poll.js";
import { sendCommand } from "../commands/send.js";
import { sessionsCommand } from "../commands/sessions.js";
import { setupCommand } from "../commands/setup.js";
import { statusCommand } from "../commands/status.js";
@@ -26,7 +25,11 @@ import { autoMigrateLegacyState } from "../infra/state-migrations.js";
import { defaultRuntime } from "../runtime.js";
import { isRich, theme } from "../terminal/theme.js";
import { VERSION } from "../version.js";
import { emitCliBanner, formatCliBannerLine } from "./banner.js";
import {
emitCliBanner,
formatCliBannerArt,
formatCliBannerLine,
} from "./banner.js";
import { registerBrowserCli } from "./browser-cli.js";
import { hasExplicitOptions } from "./command-options.js";
import { registerCronCli } from "./cron-cli.js";
@@ -70,6 +73,8 @@ export function buildProgram() {
"Use a named profile (isolates CLAWDBOT_STATE_DIR/CLAWDBOT_CONFIG_PATH under ~/.clawdbot-<name>)",
);
program.option("--no-color", "Disable ANSI colors", false);
program.configureHelp({
optionTerm: (option) => theme.option(option.flags),
subcommandTerm: (cmd) => theme.command(cmd.name()),
@@ -97,8 +102,10 @@ export function buildProgram() {
}
program.addHelpText("beforeAll", () => {
const line = formatCliBannerLine(PROGRAM_VERSION, { richTty: isRich() });
return `\n${line}\n`;
const rich = isRich();
const art = formatCliBannerArt({ richTty: rich });
const line = formatCliBannerLine(PROGRAM_VERSION, { richTty: rich });
return `\n${art}\n${line}\n`;
});
program.hook("preAction", async (_thisCommand, actionCommand) => {
@@ -147,7 +154,7 @@ export function buildProgram() {
"Link personal WhatsApp Web and show QR + connection logs.",
],
[
'clawdbot send --to +15555550123 --message "Hi" --json',
'clawdbot message send --to +15555550123 --message "Hi" --json',
"Send via your web session and print JSON result.",
],
["clawdbot gateway --port 18789", "Run the WebSocket Gateway locally."],
@@ -165,7 +172,7 @@ export function buildProgram() {
"Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.",
],
[
'clawdbot send --provider telegram --to @mychat --message "Hi"',
'clawdbot message send --provider telegram --to @mychat --message "Hi"',
"Send via your Telegram bot.",
],
] as const;
@@ -339,6 +346,12 @@ export function buildProgram() {
false,
)
.option("--yes", "Accept defaults without prompting", false)
.option("--repair", "Apply recommended repairs without prompting", false)
.option(
"--force",
"Apply aggressive repairs (overwrites custom service config)",
false,
)
.option(
"--non-interactive",
"Run without prompts (safe migrations only)",
@@ -350,6 +363,8 @@ export function buildProgram() {
await doctorCommand(defaultRuntime, {
workspaceSuggestions: opts.workspaceSuggestions,
yes: Boolean(opts.yes),
repair: Boolean(opts.repair),
force: Boolean(opts.force),
nonInteractive: Boolean(opts.nonInteractive),
deep: Boolean(opts.deep),
});
@@ -402,107 +417,472 @@ export function buildProgram() {
}
});
program
.command("send")
.description(
"Send a message (WhatsApp Web, Telegram bot, Discord, Slack, Signal, iMessage)",
const message = program
.command("message")
.description("Send messages and provider actions")
.addHelpText(
"after",
`
Examples:
clawdbot message send --to +15555550123 --message "Hi"
clawdbot message send --to +15555550123 --message "Hi" --media photo.jpg
clawdbot message poll --provider discord --to channel:123 --poll-question "Snack?" --poll-option Pizza --poll-option Sushi
clawdbot message react --provider discord --to 123 --message-id 456 --emoji "✅"`,
)
.requiredOption(
"-t, --to <number>",
"Recipient: E.164 for WhatsApp/Signal, Telegram chat id/@username, Discord channel/user, or iMessage handle/chat_id",
.action(() => {
message.help({ error: true });
});
const withMessageBase = (command: Command) =>
command
.option(
"--provider <provider>",
"Provider: whatsapp|telegram|discord|slack|signal|imessage",
)
.option("--account <id>", "Provider account id")
.option("--json", "Output result as JSON", false)
.option("--dry-run", "Print payload and skip sending", false)
.option("--verbose", "Verbose logging", false);
const withMessageTarget = (command: Command) =>
command.option(
"-t, --to <dest>",
"Recipient/channel: E.164 for WhatsApp/Signal, Telegram chat id/@username, Discord/Slack channel/user, or iMessage handle/chat_id",
);
const withRequiredMessageTarget = (command: Command) =>
command.requiredOption(
"-t, --to <dest>",
"Recipient/channel: E.164 for WhatsApp/Signal, Telegram chat id/@username, Discord/Slack channel/user, or iMessage handle/chat_id",
);
const runMessageAction = async (
action: string,
opts: Record<string, unknown>,
) => {
setVerbose(Boolean(opts.verbose));
const deps = createDefaultDeps();
try {
await messageCommand(
{
...opts,
action,
account: opts.account as string | undefined,
},
deps,
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
};
withMessageBase(
withRequiredMessageTarget(
message
.command("send")
.description("Send a message")
.requiredOption("-m, --message <text>", "Message body"),
)
.requiredOption("-m, --message <text>", "Message body")
.option(
"--media <path-or-url>",
"Attach media (image/audio/video/document). Accepts local paths or URLs.",
)
.option("--reply-to <id>", "Reply-to message id")
.option("--thread-id <id>", "Thread id (Telegram forum thread)")
.option(
"--gif-playback",
"Treat video media as GIF playback (WhatsApp only).",
false,
),
).action(async (opts) => {
await runMessageAction("send", opts);
});
withMessageBase(
withRequiredMessageTarget(
message.command("poll").description("Send a poll"),
),
)
.requiredOption("--poll-question <text>", "Poll question")
.option(
"--poll-option <choice>",
"Poll option (repeat 2-12 times)",
collectOption,
[] as string[],
)
.option("--poll-multi", "Allow multiple selections", false)
.option("--poll-duration-hours <n>", "Poll duration (Discord)")
.option("-m, --message <text>", "Optional message body")
.action(async (opts) => {
await runMessageAction("poll", opts);
});
withMessageBase(
withMessageTarget(
message.command("react").description("Add or remove a reaction"),
),
)
.requiredOption("--message-id <id>", "Message id")
.option("--emoji <emoji>", "Emoji for reactions")
.option("--remove", "Remove reaction", false)
.option("--participant <id>", "WhatsApp reaction participant")
.option("--from-me", "WhatsApp reaction fromMe", false)
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("react", opts);
});
withMessageBase(
withMessageTarget(
message.command("reactions").description("List reactions on a message"),
),
)
.requiredOption("--message-id <id>", "Message id")
.option("--limit <n>", "Result limit")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("reactions", opts);
});
withMessageBase(
withMessageTarget(
message.command("read").description("Read recent messages"),
),
)
.option("--limit <n>", "Result limit")
.option("--before <id>", "Read/search before id")
.option("--after <id>", "Read/search after id")
.option("--around <id>", "Read around id (Discord)")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("read", opts);
});
withMessageBase(
withMessageTarget(
message
.command("edit")
.description("Edit a message")
.requiredOption("-m, --message <text>", "Message body"),
),
)
.requiredOption("--message-id <id>", "Message id")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("edit", opts);
});
withMessageBase(
withMessageTarget(
message.command("delete").description("Delete a message"),
),
)
.requiredOption("--message-id <id>", "Message id")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("delete", opts);
});
withMessageBase(
withMessageTarget(message.command("pin").description("Pin a message")),
)
.requiredOption("--message-id <id>", "Message id")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("pin", opts);
});
withMessageBase(
withMessageTarget(message.command("unpin").description("Unpin a message")),
)
.option("--message-id <id>", "Message id")
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("unpin", opts);
});
withMessageBase(
withMessageTarget(
message.command("pins").description("List pinned messages"),
),
)
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("list-pins", opts);
});
withMessageBase(
withMessageTarget(
message.command("permissions").description("Fetch channel permissions"),
),
)
.option("--channel-id <id>", "Channel id (defaults to --to)")
.action(async (opts) => {
await runMessageAction("permissions", opts);
});
withMessageBase(
message.command("search").description("Search Discord messages"),
)
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--query <text>", "Search query")
.option("--channel-id <id>", "Channel id")
.option(
"--channel-ids <id>",
"Channel id (repeat)",
collectOption,
[] as string[],
)
.option("--author-id <id>", "Author id")
.option(
"--author-ids <id>",
"Author id (repeat)",
collectOption,
[] as string[],
)
.option("--limit <n>", "Result limit")
.action(async (opts) => {
await runMessageAction("search", opts);
});
const thread = message.command("thread").description("Thread actions");
withMessageBase(
withMessageTarget(
thread
.command("create")
.description("Create a thread")
.requiredOption("--thread-name <name>", "Thread name"),
),
)
.option("--channel-id <id>", "Channel id (defaults to --to)")
.option("--message-id <id>", "Message id (optional)")
.option("--auto-archive-min <n>", "Thread auto-archive minutes")
.action(async (opts) => {
await runMessageAction("thread-create", opts);
});
withMessageBase(
thread
.command("list")
.description("List threads")
.requiredOption("--guild-id <id>", "Guild id"),
)
.option("--channel-id <id>", "Channel id")
.option("--include-archived", "Include archived threads", false)
.option("--before <id>", "Read/search before id")
.option("--limit <n>", "Result limit")
.action(async (opts) => {
await runMessageAction("thread-list", opts);
});
withMessageBase(
withRequiredMessageTarget(
thread
.command("reply")
.description("Reply in a thread")
.requiredOption("-m, --message <text>", "Message body"),
),
)
.option(
"--media <path-or-url>",
"Attach media (image/audio/video/document). Accepts local paths or URLs.",
)
.option(
"--gif-playback",
"Treat video media as GIF playback (WhatsApp only).",
false,
)
.option(
"--provider <provider>",
"Delivery provider: whatsapp|telegram|discord|slack|signal|imessage (default: whatsapp)",
)
.option("--account <id>", "WhatsApp account id (accountId)")
.option("--dry-run", "Print payload and skip sending", false)
.option("--json", "Output result as JSON", false)
.option("--verbose", "Verbose logging", false)
.addHelpText(
"after",
`
Examples:
clawdbot send --to +15555550123 --message "Hi"
clawdbot send --to +15555550123 --message "Hi" --media photo.jpg
clawdbot send --to +15555550123 --message "Hi" --dry-run # print payload only
clawdbot send --to +15555550123 --message "Hi" --json # machine-readable result`,
)
.option("--reply-to <id>", "Reply-to message id")
.action(async (opts) => {
setVerbose(Boolean(opts.verbose));
const deps = createDefaultDeps();
try {
await sendCommand(
{
...opts,
account: opts.account as string | undefined,
},
deps,
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
await runMessageAction("thread-reply", opts);
});
program
.command("poll")
.description("Create a poll via WhatsApp or Discord")
.requiredOption(
"-t, --to <id>",
"Recipient: WhatsApp JID/number or Discord channel/user",
)
.requiredOption("-q, --question <text>", "Poll question")
.requiredOption(
"-o, --option <choice>",
"Poll option (use multiple times, 2-12 required)",
(value: string, previous: string[]) => previous.concat([value]),
const emoji = message.command("emoji").description("Emoji actions");
withMessageBase(emoji.command("list").description("List emojis"))
.option("--guild-id <id>", "Guild id (Discord)")
.action(async (opts) => {
await runMessageAction("emoji-list", opts);
});
withMessageBase(
emoji
.command("upload")
.description("Upload an emoji")
.requiredOption("--guild-id <id>", "Guild id"),
)
.requiredOption("--emoji-name <name>", "Emoji name")
.requiredOption("--media <path-or-url>", "Emoji media (path or URL)")
.option(
"--role-ids <id>",
"Role id (repeat)",
collectOption,
[] as string[],
)
.option(
"-s, --max-selections <n>",
"How many options can be selected (default: 1)",
)
.option(
"--duration-hours <n>",
"Poll duration in hours (Discord only, default: 24)",
)
.option(
"--provider <provider>",
"Delivery provider: whatsapp|discord (default: whatsapp)",
)
.option("--dry-run", "Print payload and skip sending", false)
.option("--json", "Output result as JSON", false)
.option("--verbose", "Verbose logging", false)
.addHelpText(
"after",
`
Examples:
clawdbot poll --to +15555550123 -q "Lunch today?" -o "Yes" -o "No" -o "Maybe"
clawdbot poll --to 123456789@g.us -q "Meeting time?" -o "10am" -o "2pm" -o "4pm" -s 2
clawdbot poll --to channel:123456789 -q "Snack?" -o "Pizza" -o "Sushi" --provider discord
clawdbot poll --to channel:123456789 -q "Plan?" -o "A" -o "B" --provider discord --duration-hours 48`,
)
.action(async (opts) => {
setVerbose(Boolean(opts.verbose));
const deps = createDefaultDeps();
try {
await pollCommand(opts, deps, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
await runMessageAction("emoji-upload", opts);
});
const sticker = message.command("sticker").description("Sticker actions");
withMessageBase(
withRequiredMessageTarget(
sticker.command("send").description("Send stickers"),
),
)
.requiredOption("--sticker-id <id>", "Sticker id (repeat)", collectOption)
.option("-m, --message <text>", "Optional message body")
.action(async (opts) => {
await runMessageAction("sticker", opts);
});
withMessageBase(
sticker
.command("upload")
.description("Upload a sticker")
.requiredOption("--guild-id <id>", "Guild id"),
)
.requiredOption("--sticker-name <name>", "Sticker name")
.requiredOption("--sticker-desc <text>", "Sticker description")
.requiredOption("--sticker-tags <tags>", "Sticker tags")
.requiredOption("--media <path-or-url>", "Sticker media (path or URL)")
.action(async (opts) => {
await runMessageAction("sticker-upload", opts);
});
const role = message.command("role").description("Role actions");
withMessageBase(
role
.command("info")
.description("List roles")
.requiredOption("--guild-id <id>", "Guild id"),
).action(async (opts) => {
await runMessageAction("role-info", opts);
});
withMessageBase(
role
.command("add")
.description("Add role to a member")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id")
.requiredOption("--role-id <id>", "Role id"),
).action(async (opts) => {
await runMessageAction("role-add", opts);
});
withMessageBase(
role
.command("remove")
.description("Remove role from a member")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id")
.requiredOption("--role-id <id>", "Role id"),
).action(async (opts) => {
await runMessageAction("role-remove", opts);
});
const channel = message.command("channel").description("Channel actions");
withMessageBase(
channel
.command("info")
.description("Fetch channel info")
.requiredOption("--channel-id <id>", "Channel id"),
).action(async (opts) => {
await runMessageAction("channel-info", opts);
});
withMessageBase(
channel
.command("list")
.description("List channels")
.requiredOption("--guild-id <id>", "Guild id"),
).action(async (opts) => {
await runMessageAction("channel-list", opts);
});
const member = message.command("member").description("Member actions");
withMessageBase(
member
.command("info")
.description("Fetch member info")
.requiredOption("--user-id <id>", "User id"),
)
.option("--guild-id <id>", "Guild id (Discord)")
.action(async (opts) => {
await runMessageAction("member-info", opts);
});
const voice = message.command("voice").description("Voice actions");
withMessageBase(
voice
.command("status")
.description("Fetch voice status")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id"),
).action(async (opts) => {
await runMessageAction("voice-status", opts);
});
const event = message.command("event").description("Event actions");
withMessageBase(
event
.command("list")
.description("List scheduled events")
.requiredOption("--guild-id <id>", "Guild id"),
).action(async (opts) => {
await runMessageAction("event-list", opts);
});
withMessageBase(
event
.command("create")
.description("Create a scheduled event")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--event-name <name>", "Event name")
.requiredOption("--start-time <iso>", "Event start time"),
)
.option("--end-time <iso>", "Event end time")
.option("--desc <text>", "Event description")
.option("--channel-id <id>", "Channel id")
.option("--location <text>", "Event location")
.option("--event-type <stage|external|voice>", "Event type")
.action(async (opts) => {
await runMessageAction("event-create", opts);
});
withMessageBase(
message
.command("timeout")
.description("Timeout a member")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id"),
)
.option("--duration-min <n>", "Timeout duration minutes")
.option("--until <iso>", "Timeout until")
.option("--reason <text>", "Moderation reason")
.action(async (opts) => {
await runMessageAction("timeout", opts);
});
withMessageBase(
message
.command("kick")
.description("Kick a member")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id"),
)
.option("--reason <text>", "Moderation reason")
.action(async (opts) => {
await runMessageAction("kick", opts);
});
withMessageBase(
message
.command("ban")
.description("Ban a member")
.requiredOption("--guild-id <id>", "Guild id")
.requiredOption("--user-id <id>", "User id"),
)
.option("--reason <text>", "Moderation reason")
.option("--delete-days <n>", "Ban delete message days")
.action(async (opts) => {
await runMessageAction("ban", opts);
});
program

View File

@@ -2,6 +2,7 @@ import type { OAuthCredentials, OAuthProvider } from "@mariozechner/pi-ai";
import { resolveDefaultAgentDir } from "../agents/agent-scope.js";
import { upsertAuthProfile } from "../agents/auth-profiles.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { ModelDefinitionConfig } from "../config/types.js";
const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.1";
@@ -173,7 +174,7 @@ export function applyMinimaxHostedProviderConfig(
};
const providers = { ...cfg.models?.providers };
const hostedModel = {
const hostedModel: ModelDefinitionConfig = {
id: MINIMAX_HOSTED_MODEL_ID,
name: "MiniMax M2.1",
reasoning: false,

View File

@@ -1,5 +1,5 @@
import type { ReactionType, ReactionTypeEmoji } from "@grammyjs/types";
import { Bot, InputFile, type ApiClientOptions } from "grammy";
import { type ApiClientOptions, Bot, InputFile } from "grammy";
import { loadConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { recordProviderActivity } from "../infra/provider-activity.js";
@@ -122,9 +122,7 @@ export async function sendMessageTelegram(
const client: ApiClientOptions | undefined = fetchImpl
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
: undefined;
const api =
opts.api ??
new Bot(token, client ? { client } : undefined).api;
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
const mediaUrl = opts.mediaUrl?.trim();
// Build optional params for forum topics and reply threading.
@@ -296,9 +294,7 @@ export async function reactMessageTelegram(
const client: ApiClientOptions | undefined = fetchImpl
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
: undefined;
const api =
opts.api ??
new Bot(token, client ? { client } : undefined).api;
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
const request = createTelegramRetryRunner({
retry: opts.retry,
configRetry: account.config.retry,

View File

@@ -1,4 +1,4 @@
import { Bot, type ApiClientOptions } from "grammy";
import { type ApiClientOptions, Bot } from "grammy";
import { resolveTelegramFetch } from "./fetch.js";
export async function setTelegramWebhook(opts: {
@@ -11,10 +11,7 @@ export async function setTelegramWebhook(opts: {
const client: ApiClientOptions | undefined = fetchImpl
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
: undefined;
const bot = new Bot(
opts.token,
client ? { client } : undefined,
);
const bot = new Bot(opts.token, client ? { client } : undefined);
await bot.api.setWebhook(opts.url, {
secret_token: opts.secret,
drop_pending_updates: opts.dropPendingUpdates ?? false,
@@ -26,9 +23,6 @@ export async function deleteTelegramWebhook(opts: { token: string }) {
const client: ApiClientOptions | undefined = fetchImpl
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
: undefined;
const bot = new Bot(
opts.token,
client ? { client } : undefined,
);
const bot = new Bot(opts.token, client ? { client } : undefined);
await bot.api.deleteWebhook();
}