Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 118 additions & 2 deletions cmd/onboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ var providerMap = map[string]providerInfo{
"minimax": {"minimax", "GOCLAW_MINIMAX_API_KEY", "MiniMax-M2.5"},
"cohere": {"cohere", "GOCLAW_COHERE_API_KEY", "command-a"},
"perplexity": {"perplexity", "GOCLAW_PERPLEXITY_API_KEY", "sonar-pro"},
"dashscope": {"dashscope", "GOCLAW_DASHSCOPE_API_KEY", "qwen3-max"},
"bailian": {"bailian", "GOCLAW_BAILIAN_API_KEY", "qwen3.5-plus"},
"claude_cli": {"claude-cli", "", "sonnet"},
"custom": {"custom", "", ""},
}
Expand Down Expand Up @@ -111,6 +113,10 @@ func runOnboard() {
feishuSecret string
feishuDomain = "lark"
feishuConnMode = "websocket"
discordToken string
slackBotToken string
slackAppToken string
whatsappBridge string

selectedFeatures []string
embProvider string
Expand Down Expand Up @@ -149,6 +155,22 @@ func runOnboard() {
feishuDomain = cfg.Channels.Feishu.Domain
feishuConnMode = cfg.Channels.Feishu.ConnectionMode
}
if cfg.Channels.Discord.Enabled {
selectedChannels = append(selectedChannels, "discord")
discordToken = cfg.Channels.Discord.Token
}
if cfg.Channels.Slack.Enabled {
selectedChannels = append(selectedChannels, "slack")
slackBotToken = cfg.Channels.Slack.BotToken
slackAppToken = cfg.Channels.Slack.AppToken
}
if cfg.Channels.WhatsApp.Enabled {
selectedChannels = append(selectedChannels, "whatsapp")
whatsappBridge = cfg.Channels.WhatsApp.BridgeURL
}
if cfg.Channels.ZaloPersonal.Enabled {
selectedChannels = append(selectedChannels, "zalo_personal")
}
if cfg.Agents.Defaults.Memory != nil && (cfg.Agents.Defaults.Memory.Enabled == nil || *cfg.Agents.Defaults.Memory.Enabled) {
selectedFeatures = append(selectedFeatures, "memory")
embProvider = cfg.Agents.Defaults.Memory.EmbeddingProvider
Expand Down Expand Up @@ -189,6 +211,8 @@ func runOnboard() {
{"MiniMax (MiniMax models)", "minimax"},
{"Cohere (Command models)", "cohere"},
{"Perplexity (Sonar search models)", "perplexity"},
{"DashScope (Alibaba Cloud Model Studio — Qwen API)", "dashscope"},
{"Bailian (Alibaba Cloud Model Studio — Coding Plan)", "bailian"},
{"Claude CLI (use local claude CLI — no API key needed)", "claude_cli"},
{"Custom (any OpenAI-compatible endpoint)", "custom"},
}
Expand Down Expand Up @@ -327,8 +351,12 @@ func runOnboard() {
// ── Step 2: Channels ──
selectedChannels, err = promptMultiSelect("Step 2 · Channels (select at least 1)", "Enter numbers to toggle channels", []SelectOption[string]{
{"Telegram", "telegram"},
{"Discord", "discord"},
{"Slack", "slack"},
{"WhatsApp (via bridge)", "whatsapp"},
{"Zalo OA", "zalo"},
{"Feishu / Lark", "feishu"},
{"Zalo Personal", "zalo_personal"},
{"Larksuite", "feishu"},
}, selectedChannels)
if err != nil {
fmt.Println("Cancelled.")
Expand Down Expand Up @@ -379,6 +407,44 @@ func runOnboard() {
}
}

if hasChannel("discord") {
discordToken, err = promptPassword("Discord Bot Token", "Get from https://discord.com/developers/applications")
if err != nil {
fmt.Println("Cancelled.")
return
}
if discordToken == "" {
discordToken = cfg.Channels.Discord.Token
}
}

if hasChannel("slack") {
slackBotToken, err = promptPassword("Slack Bot Token", "xoxb-... (Bot User OAuth Token)")
if err != nil {
fmt.Println("Cancelled.")
return
}
if slackBotToken == "" {
slackBotToken = cfg.Channels.Slack.BotToken
}
slackAppToken, err = promptPassword("Slack App Token", "xapp-... (App-Level Token for Socket Mode)")
if err != nil {
fmt.Println("Cancelled.")
return
}
if slackAppToken == "" {
slackAppToken = cfg.Channels.Slack.AppToken
}
}

if hasChannel("whatsapp") {
whatsappBridge, err = promptString("WhatsApp Bridge URL", "URL of your WhatsApp bridge (e.g. http://localhost:8080)", whatsappBridge)
if err != nil {
fmt.Println("Cancelled.")
return
}
}

// ── Features ──
selectedFeatures, err = promptMultiSelect("Features (recommended: keep both)", "Enter numbers to toggle features", []SelectOption[string]{
{"Memory (vector search over agent notes)", "memory"},
Expand Down Expand Up @@ -458,6 +524,15 @@ func runOnboard() {
if hasChannel("feishu") && (feishuAppID == "" || feishuSecret == "") {
errors = append(errors, "Feishu App ID and App Secret are required")
}
if hasChannel("discord") && discordToken == "" {
errors = append(errors, "Discord bot token is required")
}
if hasChannel("slack") && (slackBotToken == "" || slackAppToken == "") {
errors = append(errors, "Slack Bot Token and App Token are required")
}
if hasChannel("whatsapp") && whatsappBridge == "" {
errors = append(errors, "WhatsApp bridge URL is required")
}

if postgresDSN == "" {
errors = append(errors, "Postgres DSN is required (set GOCLAW_POSTGRES_DSN or enter above)")
Expand Down Expand Up @@ -544,6 +619,26 @@ func runOnboard() {
cfg.Channels.Feishu.ConnectionMode = feishuConnMode
}

cfg.Channels.Discord.Enabled = hasChannel("discord")
if cfg.Channels.Discord.Enabled {
cfg.Channels.Discord.Token = discordToken
cfg.Channels.Discord.DMPolicy = "pairing"
}

cfg.Channels.Slack.Enabled = hasChannel("slack")
if cfg.Channels.Slack.Enabled {
cfg.Channels.Slack.BotToken = slackBotToken
cfg.Channels.Slack.AppToken = slackAppToken
cfg.Channels.Slack.DMPolicy = "pairing"
}

cfg.Channels.WhatsApp.Enabled = hasChannel("whatsapp")
if cfg.Channels.WhatsApp.Enabled {
cfg.Channels.WhatsApp.BridgeURL = whatsappBridge
}

cfg.Channels.ZaloPersonal.Enabled = hasChannel("zalo_personal")

// Features
if hasFeature("memory") {
enabled := true
Expand Down Expand Up @@ -640,6 +735,9 @@ func runOnboard() {
savedTgToken := cfg.Channels.Telegram.Token
savedZaloToken := cfg.Channels.Zalo.Token
savedFeishuAppSecret := cfg.Channels.Feishu.AppSecret
savedDiscordToken := cfg.Channels.Discord.Token
savedSlackBotToken := cfg.Channels.Slack.BotToken
savedSlackAppToken := cfg.Channels.Slack.AppToken
savedTtsOpenAIKey := cfg.Tts.OpenAI.APIKey
savedTtsElevenLabsKey := cfg.Tts.ElevenLabs.APIKey
savedTtsMiniMaxKey := cfg.Tts.MiniMax.APIKey
Expand All @@ -652,6 +750,9 @@ func runOnboard() {
cfg.Channels.Telegram.Token = ""
cfg.Channels.Zalo.Token = ""
cfg.Channels.Feishu.AppSecret = ""
cfg.Channels.Discord.Token = ""
cfg.Channels.Slack.BotToken = ""
cfg.Channels.Slack.AppToken = ""
cfg.Tts.OpenAI.APIKey = ""
cfg.Tts.ElevenLabs.APIKey = ""
cfg.Tts.MiniMax.APIKey = ""
Expand All @@ -663,6 +764,9 @@ func runOnboard() {
cfg.Channels.Telegram.Token = savedTgToken
cfg.Channels.Zalo.Token = savedZaloToken
cfg.Channels.Feishu.AppSecret = savedFeishuAppSecret
cfg.Channels.Discord.Token = savedDiscordToken
cfg.Channels.Slack.BotToken = savedSlackBotToken
cfg.Channels.Slack.AppToken = savedSlackAppToken
cfg.Tts.OpenAI.APIKey = savedTtsOpenAIKey
cfg.Tts.ElevenLabs.APIKey = savedTtsElevenLabsKey
cfg.Tts.MiniMax.APIKey = savedTtsMiniMaxKey
Expand Down Expand Up @@ -697,7 +801,19 @@ func runOnboard() {
fmt.Println(" Zalo: enabled")
}
if cfg.Channels.Feishu.Enabled {
fmt.Printf(" Feishu: enabled (%s, %s)\n", cfg.Channels.Feishu.Domain, cfg.Channels.Feishu.ConnectionMode)
fmt.Printf(" Lark: enabled (%s, %s)\n", cfg.Channels.Feishu.Domain, cfg.Channels.Feishu.ConnectionMode)
}
if cfg.Channels.Discord.Enabled {
fmt.Println(" Discord: enabled")
}
if cfg.Channels.Slack.Enabled {
fmt.Println(" Slack: enabled")
}
if cfg.Channels.WhatsApp.Enabled {
fmt.Println(" WhatsApp: enabled")
}
if cfg.Channels.ZaloPersonal.Enabled {
fmt.Println(" Zalo Personal: enabled")
}
if cfg.Agents.Defaults.Memory != nil && (cfg.Agents.Defaults.Memory.Enabled == nil || *cfg.Agents.Defaults.Memory.Enabled) {
embProv := cfg.Agents.Defaults.Memory.EmbeddingProvider
Expand Down
13 changes: 13 additions & 0 deletions cmd/onboard_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func applyProviderAPIKey(cfg *config.Config, provider, key string) {
cfg.Providers.Cohere.APIKey = key
case "perplexity":
cfg.Providers.Perplexity.APIKey = key
case "dashscope":
cfg.Providers.DashScope.APIKey = key
case "bailian":
cfg.Providers.Bailian.APIKey = key
}
}

Expand Down Expand Up @@ -93,6 +97,8 @@ func onboardWriteEnvFile(path string, cfg *config.Config, primaryKey, primaryEnv
addIfSet("GOCLAW_MINIMAX_API_KEY", cfg.Providers.MiniMax.APIKey)
addIfSet("GOCLAW_COHERE_API_KEY", cfg.Providers.Cohere.APIKey)
addIfSet("GOCLAW_PERPLEXITY_API_KEY", cfg.Providers.Perplexity.APIKey)
addIfSet("GOCLAW_DASHSCOPE_API_KEY", cfg.Providers.DashScope.APIKey)
addIfSet("GOCLAW_BAILIAN_API_KEY", cfg.Providers.Bailian.APIKey)

if cfg.Gateway.Token != "" {
lines = append(lines, fmt.Sprintf("export GOCLAW_GATEWAY_TOKEN=%s", cfg.Gateway.Token))
Expand All @@ -108,6 +114,13 @@ func onboardWriteEnvFile(path string, cfg *config.Config, primaryKey, primaryEnv
lines = append(lines, fmt.Sprintf("export GOCLAW_FEISHU_APP_ID=%s", cfg.Channels.Feishu.AppID))
lines = append(lines, fmt.Sprintf("export GOCLAW_FEISHU_APP_SECRET=%s", cfg.Channels.Feishu.AppSecret))
}
if cfg.Channels.Discord.Enabled && cfg.Channels.Discord.Token != "" {
lines = append(lines, fmt.Sprintf("export GOCLAW_DISCORD_TOKEN=%s", cfg.Channels.Discord.Token))
}
if cfg.Channels.Slack.Enabled && cfg.Channels.Slack.BotToken != "" {
lines = append(lines, fmt.Sprintf("export GOCLAW_SLACK_BOT_TOKEN=%s", cfg.Channels.Slack.BotToken))
lines = append(lines, fmt.Sprintf("export GOCLAW_SLACK_APP_TOKEN=%s", cfg.Channels.Slack.AppToken))
}

// Database
if cfg.Database.PostgresDSN != "" {
Expand Down
8 changes: 8 additions & 0 deletions cmd/onboard_resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func resolveProviderAPIKey(cfg *config.Config, providerName string) string {
return cfg.Providers.Cohere.APIKey
case "perplexity":
return cfg.Providers.Perplexity.APIKey
case "dashscope":
return cfg.Providers.DashScope.APIKey
case "bailian":
return cfg.Providers.Bailian.APIKey
default:
return ""
}
Expand Down Expand Up @@ -90,6 +94,10 @@ func resolveProviderAPIBase(providerName string) string {
return "https://api.cohere.com/v2"
case "perplexity":
return "https://api.perplexity.ai"
case "dashscope":
return "https://dashscope.aliyuncs.com/compatible-mode/v1"
case "bailian":
return "https://coding-intl.dashscope.aliyuncs.com/v1"
case "yescale":
return "https://api.yescale.one/v1"
default:
Expand Down
2 changes: 1 addition & 1 deletion cmd/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func promptPassword(title, description string) (string, error) {
}

// filterThreshold: enable type-to-filter only when there are more than this many options.
const filterThreshold = 5
const filterThreshold = 10

const scrollableThreshold = 15 // enable scrollbars when there are more than this many options

Expand Down