daemora 1.0.1 → 1.0.3
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.
- package/README.md +106 -76
- package/SOUL.md +100 -28
- package/config/mcp.json +9 -9
- package/package.json +15 -8
- package/skills/apple-notes.md +0 -52
- package/skills/apple-reminders.md +1 -87
- package/skills/camsnap.md +20 -144
- package/skills/coding.md +7 -7
- package/skills/documents.md +6 -6
- package/skills/email.md +6 -6
- package/skills/gif-search.md +28 -171
- package/skills/healthcheck.md +21 -203
- package/skills/image-gen.md +24 -123
- package/skills/model-usage.md +18 -165
- package/skills/obsidian.md +28 -174
- package/skills/pdf.md +30 -181
- package/skills/research.md +6 -6
- package/skills/skill-creator.md +35 -111
- package/skills/spotify.md +2 -17
- package/skills/summarize.md +36 -193
- package/skills/things.md +23 -175
- package/skills/tmux.md +1 -91
- package/skills/trello.md +32 -157
- package/skills/video-frames.md +26 -166
- package/skills/weather.md +6 -6
- package/src/a2a/A2AClient.js +2 -2
- package/src/a2a/A2AServer.js +6 -6
- package/src/a2a/AgentCard.js +2 -2
- package/src/agents/SubAgentManager.js +61 -19
- package/src/agents/Supervisor.js +4 -4
- package/src/channels/BaseChannel.js +6 -6
- package/src/channels/BlueBubblesChannel.js +112 -0
- package/src/channels/DiscordChannel.js +8 -8
- package/src/channels/EmailChannel.js +54 -26
- package/src/channels/FeishuChannel.js +140 -0
- package/src/channels/GoogleChatChannel.js +8 -8
- package/src/channels/HttpChannel.js +2 -2
- package/src/channels/IRCChannel.js +144 -0
- package/src/channels/LineChannel.js +13 -13
- package/src/channels/MatrixChannel.js +97 -0
- package/src/channels/MattermostChannel.js +119 -0
- package/src/channels/NextcloudChannel.js +133 -0
- package/src/channels/NostrChannel.js +175 -0
- package/src/channels/SignalChannel.js +9 -9
- package/src/channels/SlackChannel.js +10 -10
- package/src/channels/TeamsChannel.js +10 -10
- package/src/channels/TelegramChannel.js +8 -8
- package/src/channels/TwitchChannel.js +128 -0
- package/src/channels/WhatsAppChannel.js +10 -10
- package/src/channels/ZaloChannel.js +119 -0
- package/src/channels/iMessageChannel.js +150 -0
- package/src/channels/index.js +241 -11
- package/src/cli.js +835 -38
- package/src/config/agentProfiles.js +19 -19
- package/src/config/channels.js +1 -1
- package/src/config/default.js +12 -7
- package/src/config/models.js +3 -3
- package/src/config/permissions.js +2 -2
- package/src/core/AgentLoop.js +13 -13
- package/src/core/Compaction.js +3 -3
- package/src/core/CostTracker.js +2 -2
- package/src/core/EventBus.js +15 -15
- package/src/core/TaskQueue.js +24 -7
- package/src/core/TaskRunner.js +19 -6
- package/src/daemon/DaemonManager.js +4 -4
- package/src/hooks/HookRunner.js +4 -4
- package/src/index.js +6 -2
- package/src/mcp/MCPAgentRunner.js +3 -3
- package/src/mcp/MCPClient.js +9 -9
- package/src/mcp/MCPManager.js +14 -14
- package/src/models/ModelRouter.js +2 -2
- package/src/safety/AuditLog.js +3 -3
- package/src/safety/CircuitBreaker.js +2 -2
- package/src/safety/CommandGuard.js +132 -0
- package/src/safety/FilesystemGuard.js +23 -3
- package/src/safety/GitRollback.js +5 -5
- package/src/safety/HumanApproval.js +9 -9
- package/src/safety/InputSanitizer.js +81 -8
- package/src/safety/PermissionGuard.js +2 -2
- package/src/safety/Sandbox.js +1 -1
- package/src/safety/SecretScanner.js +90 -28
- package/src/safety/SecretVault.js +2 -2
- package/src/scheduler/Heartbeat.js +3 -3
- package/src/scheduler/Scheduler.js +6 -6
- package/src/setup/theme.js +171 -66
- package/src/setup/wizard.js +432 -57
- package/src/skills/SkillLoader.js +145 -8
- package/src/storage/TaskStore.js +39 -15
- package/src/systemPrompt.js +45 -43
- package/src/tenants/TenantManager.js +79 -22
- package/src/tools/ToolRegistry.js +3 -3
- package/src/tools/applyPatch.js +2 -2
- package/src/tools/browserAutomation.js +4 -4
- package/src/tools/calendar.js +155 -0
- package/src/tools/clipboard.js +71 -0
- package/src/tools/contacts.js +138 -0
- package/src/tools/createDocument.js +2 -2
- package/src/tools/cronTool.js +14 -14
- package/src/tools/database.js +165 -0
- package/src/tools/editFile.js +10 -10
- package/src/tools/executeCommand.js +11 -3
- package/src/tools/generateImage.js +79 -0
- package/src/tools/gitTool.js +141 -0
- package/src/tools/glob.js +1 -1
- package/src/tools/googlePlaces.js +136 -0
- package/src/tools/grep.js +2 -2
- package/src/tools/iMessageTool.js +86 -0
- package/src/tools/imageAnalysis.js +3 -3
- package/src/tools/index.js +56 -2
- package/src/tools/makeVoiceCall.js +283 -0
- package/src/tools/manageAgents.js +2 -2
- package/src/tools/manageMCP.js +38 -20
- package/src/tools/memory.js +25 -32
- package/src/tools/messageChannel.js +1 -1
- package/src/tools/notification.js +90 -0
- package/src/tools/philipsHue.js +147 -0
- package/src/tools/projectTracker.js +8 -8
- package/src/tools/readFile.js +1 -1
- package/src/tools/readPDF.js +73 -0
- package/src/tools/screenCapture.js +6 -6
- package/src/tools/searchContent.js +2 -2
- package/src/tools/searchFiles.js +1 -1
- package/src/tools/sendEmail.js +79 -24
- package/src/tools/sendFile.js +4 -4
- package/src/tools/sonos.js +137 -0
- package/src/tools/sshTool.js +130 -0
- package/src/tools/textToSpeech.js +5 -5
- package/src/tools/transcribeAudio.js +4 -4
- package/src/tools/useMCP.js +4 -4
- package/src/tools/webFetch.js +2 -2
- package/src/tools/webSearch.js +1 -1
- package/src/utils/Embeddings.js +79 -0
- package/src/voice/VoiceSessionManager.js +170 -0
- package/src/voice/VoiceWebhook.js +188 -0
package/src/setup/wizard.js
CHANGED
|
@@ -31,42 +31,113 @@ export async function runSetupWizard() {
|
|
|
31
31
|
const provider = guard(await p.select({
|
|
32
32
|
message: "Which AI provider?",
|
|
33
33
|
options: [
|
|
34
|
-
{ value: "openai", label: "OpenAI", hint: "GPT-4.1 \u2014 best all-rounder" },
|
|
35
|
-
{ value: "anthropic", label: "Anthropic", hint: "Claude \u2014 great for coding & reasoning" },
|
|
36
|
-
{ value: "google", label: "Google AI", hint: "Gemini \u2014 fast & capable" },
|
|
37
|
-
{ value: "
|
|
34
|
+
{ value: "openai", label: "OpenAI", hint: "GPT-5 / GPT-4.1 \u2014 best all-rounder" },
|
|
35
|
+
{ value: "anthropic", label: "Anthropic", hint: "Claude 4 \u2014 great for coding & reasoning" },
|
|
36
|
+
{ value: "google", label: "Google AI", hint: "Gemini 3.1 / 2.5 \u2014 fast & capable" },
|
|
37
|
+
{ value: "xai", label: "xAI", hint: "Grok 4 \u2014 conversational & capable" },
|
|
38
|
+
{ value: "deepseek", label: "DeepSeek", hint: "DeepSeek V3 / R1 \u2014 excellent coder, cheap" },
|
|
39
|
+
{ value: "mistral", label: "Mistral", hint: "Mistral Large \u2014 European AI, GDPR-friendly" },
|
|
40
|
+
{ value: "ollama", label: "Ollama", hint: "Local models \u2014 free, private, offline" },
|
|
38
41
|
],
|
|
39
42
|
}));
|
|
40
43
|
|
|
41
44
|
if (provider === "openai") {
|
|
42
|
-
const key = guard(await p.password({ message: "OpenAI API key", validate: (v) => !v ? "Required" : undefined }));
|
|
45
|
+
const key = guard(await p.password({ message: "OpenAI API key (sk-...)", validate: (v) => !v ? "Required" : undefined }));
|
|
43
46
|
envConfig.OPENAI_API_KEY = key;
|
|
44
47
|
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
45
48
|
message: "OpenAI model",
|
|
46
49
|
options: [
|
|
47
|
-
{ value: "openai:gpt-4.1-mini",
|
|
48
|
-
{ value: "openai:gpt-
|
|
49
|
-
{ value: "openai:gpt-
|
|
50
|
+
{ value: "openai:gpt-4.1-mini", label: "gpt-4.1-mini", hint: "1M ctx \u2014 fast & affordable (recommended)" },
|
|
51
|
+
{ value: "openai:gpt-5.2-pro", label: "gpt-5.2-pro", hint: "GPT-5.2 Pro \u2014 highest capability [NEW]" },
|
|
52
|
+
{ value: "openai:gpt-5.2", label: "gpt-5.2", hint: "GPT-5.2 flagship (Dec 2025) [NEW]" },
|
|
53
|
+
{ value: "openai:gpt-5", label: "gpt-5", hint: "GPT-5 flagship (Aug 2025)" },
|
|
54
|
+
{ value: "openai:gpt-5-mini", label: "gpt-5-mini", hint: "GPT-5 Mini \u2014 fast & cheap" },
|
|
55
|
+
{ value: "openai:gpt-4.1", label: "gpt-4.1", hint: "1M ctx, best instruction following" },
|
|
56
|
+
{ value: "openai:gpt-4.1-nano", label: "gpt-4.1-nano", hint: "1M ctx, cheapest" },
|
|
57
|
+
{ value: "openai:o3-pro", label: "o3-pro", hint: "Best reasoning \u2014 most thorough" },
|
|
58
|
+
{ value: "openai:o4-mini", label: "o4-mini", hint: "Fast reasoning (Apr 2025)" },
|
|
59
|
+
{ value: "openai:gpt-4o", label: "gpt-4o", hint: "Vision + text (128K ctx)" },
|
|
60
|
+
{ value: "openai:gpt-4o-mini", label: "gpt-4o-mini", hint: "GPT-4o Mini \u2014 balanced" },
|
|
50
61
|
],
|
|
51
62
|
}));
|
|
52
63
|
} else if (provider === "anthropic") {
|
|
53
|
-
const key = guard(await p.password({ message: "Anthropic API key", validate: (v) => !v ? "Required" : undefined }));
|
|
64
|
+
const key = guard(await p.password({ message: "Anthropic API key (sk-ant-...)", validate: (v) => !v ? "Required" : undefined }));
|
|
54
65
|
envConfig.ANTHROPIC_API_KEY = key;
|
|
55
66
|
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
56
67
|
message: "Claude model",
|
|
57
68
|
options: [
|
|
58
|
-
{ value: "anthropic:claude-sonnet-4-6",
|
|
59
|
-
{ value: "anthropic:claude-opus-4-6",
|
|
60
|
-
{ value: "anthropic:claude-haiku-4-5
|
|
69
|
+
{ value: "anthropic:claude-sonnet-4-6", label: "claude-sonnet-4-6", hint: "Best speed/intelligence \u2014 coding & agents [NEW]" },
|
|
70
|
+
{ value: "anthropic:claude-opus-4-6", label: "claude-opus-4-6", hint: "Most intelligent \u2014 extended thinking [NEW]" },
|
|
71
|
+
{ value: "anthropic:claude-haiku-4-5", label: "claude-haiku-4-5", hint: "Fastest \u2014 high-volume tasks" },
|
|
72
|
+
{ value: "anthropic:claude-sonnet-4-5-20250929", label: "claude-sonnet-4-5-20250929", hint: "Sonnet 4.5 \u2014 coding & agentic (200K ctx)" },
|
|
73
|
+
{ value: "anthropic:claude-3-5-sonnet-latest", label: "claude-3-5-sonnet-latest", hint: "3.5 Sonnet \u2014 widely used previous gen" },
|
|
61
74
|
],
|
|
62
75
|
}));
|
|
63
76
|
} else if (provider === "google") {
|
|
64
77
|
const key = guard(await p.password({ message: "Google AI API key", validate: (v) => !v ? "Required" : undefined }));
|
|
65
78
|
envConfig.GOOGLE_AI_API_KEY = key;
|
|
66
|
-
envConfig.DEFAULT_MODEL =
|
|
79
|
+
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
80
|
+
message: "Gemini model",
|
|
81
|
+
options: [
|
|
82
|
+
{ value: "google:gemini-2.5-flash", label: "gemini-2.5-flash", hint: "Fast & cost-effective \u2014 recommended" },
|
|
83
|
+
{ value: "google:gemini-3.1-pro-preview", label: "gemini-3.1-pro-preview", hint: "Latest \u2014 complex tasks [NEW]" },
|
|
84
|
+
{ value: "google:gemini-3.1-flash-lite-preview", label: "gemini-3.1-flash-lite-preview", hint: "Latest lite \u2014 cost-efficient [NEW]" },
|
|
85
|
+
{ value: "google:gemini-2.5-pro", label: "gemini-2.5-pro", hint: "Complex reasoning & coding (1M ctx)" },
|
|
86
|
+
{ value: "google:gemini-2.5-flash-lite", label: "gemini-2.5-flash-lite", hint: "Speed-optimised high-throughput" },
|
|
87
|
+
{ value: "google:gemini-2.0-flash", label: "gemini-2.0-flash", hint: "Previous gen flash" },
|
|
88
|
+
],
|
|
89
|
+
}));
|
|
90
|
+
} else if (provider === "xai") {
|
|
91
|
+
const key = guard(await p.password({ message: "xAI API key", validate: (v) => !v ? "Required" : undefined }));
|
|
92
|
+
envConfig.XAI_API_KEY = key;
|
|
93
|
+
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
94
|
+
message: "Grok model",
|
|
95
|
+
options: [
|
|
96
|
+
{ value: "xai:grok-4", label: "grok-4", hint: "Latest & most capable (Jul 2025) [NEW]" },
|
|
97
|
+
{ value: "xai:grok-3-beta", label: "grok-3-beta", hint: "Grok 3 Beta \u2014 131K ctx" },
|
|
98
|
+
{ value: "xai:grok-3-mini-beta", label: "grok-3-mini-beta", hint: "Grok 3 Mini \u2014 fast, 131K ctx" },
|
|
99
|
+
],
|
|
100
|
+
}));
|
|
101
|
+
} else if (provider === "deepseek") {
|
|
102
|
+
const key = guard(await p.password({ message: "DeepSeek API key (sk-...)", validate: (v) => !v ? "Required" : undefined }));
|
|
103
|
+
envConfig.DEEPSEEK_API_KEY = key;
|
|
104
|
+
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
105
|
+
message: "DeepSeek model",
|
|
106
|
+
options: [
|
|
107
|
+
{ value: "deepseek:deepseek-chat", label: "deepseek-chat", hint: "V3 \u2014 excellent coder (128K ctx, recommended)" },
|
|
108
|
+
{ value: "deepseek:deepseek-reasoner", label: "deepseek-reasoner", hint: "R1 \u2014 chain-of-thought reasoning" },
|
|
109
|
+
],
|
|
110
|
+
}));
|
|
111
|
+
} else if (provider === "mistral") {
|
|
112
|
+
const key = guard(await p.password({ message: "Mistral API key", validate: (v) => !v ? "Required" : undefined }));
|
|
113
|
+
envConfig.MISTRAL_API_KEY = key;
|
|
114
|
+
envConfig.DEFAULT_MODEL = guard(await p.select({
|
|
115
|
+
message: "Mistral model",
|
|
116
|
+
options: [
|
|
117
|
+
{ value: "mistral:mistral-large-2512", label: "mistral-large-2512", hint: "Flagship \u2014 best quality (Dec 2025) [NEW]" },
|
|
118
|
+
{ value: "mistral:mistral-medium-3", label: "mistral-medium-3", hint: "Balanced capability & speed" },
|
|
119
|
+
{ value: "mistral:codestral-2508", label: "codestral-2508", hint: "Code specialist (Aug 2025)" },
|
|
120
|
+
{ value: "mistral:mistral-small-3.2-24b", label: "mistral-small-3.2-24b", hint: "Lightweight, runs locally (24B)" },
|
|
121
|
+
],
|
|
122
|
+
}));
|
|
67
123
|
} else if (provider === "ollama") {
|
|
68
|
-
p.note(
|
|
69
|
-
|
|
124
|
+
p.note(
|
|
125
|
+
[
|
|
126
|
+
"Make sure Ollama is running: ollama serve",
|
|
127
|
+
"Pull a model first: ollama pull llama4-maverick",
|
|
128
|
+
"Recommended models:",
|
|
129
|
+
" llama4-maverick \u2014 Llama 4, 17B MoE, multimodal, 1M ctx",
|
|
130
|
+
" llama4-scout \u2014 Llama 4, 17B MoE, 10M ctx",
|
|
131
|
+
" llama3.3 \u2014 best 70B open model",
|
|
132
|
+
" qwen2.5 \u2014 strong coder",
|
|
133
|
+
].join("\n"),
|
|
134
|
+
"Ollama (local models)",
|
|
135
|
+
);
|
|
136
|
+
const model = guard(await p.text({
|
|
137
|
+
message: "Ollama model name",
|
|
138
|
+
initialValue: "llama4-maverick",
|
|
139
|
+
placeholder: "e.g. llama4-maverick, llama3.3, qwen2.5",
|
|
140
|
+
}));
|
|
70
141
|
envConfig.DEFAULT_MODEL = `ollama:${model}`;
|
|
71
142
|
}
|
|
72
143
|
|
|
@@ -109,11 +180,11 @@ export async function runSetupWizard() {
|
|
|
109
180
|
p.note(
|
|
110
181
|
[
|
|
111
182
|
"Control which directories the agent can read/write.",
|
|
112
|
-
"Works like Docker volume mounts
|
|
183
|
+
"Works like Docker volume mounts - the agent cannot escape",
|
|
113
184
|
"the directories you allow.",
|
|
114
185
|
"",
|
|
115
|
-
` ${S.arrow} ${t.accent("Global")}
|
|
116
|
-
` ${S.arrow} ${t.accent("Scoped")}
|
|
186
|
+
` ${S.arrow} ${t.accent("Global")} - agent accesses any file the OS allows (default)`,
|
|
187
|
+
` ${S.arrow} ${t.accent("Scoped")} - agent locked to specific directories only`,
|
|
117
188
|
"",
|
|
118
189
|
"Sensitive system files (.ssh, .env, /etc/shadow, etc.)",
|
|
119
190
|
"are always blocked regardless of this setting.",
|
|
@@ -182,24 +253,59 @@ export async function runSetupWizard() {
|
|
|
182
253
|
p.log.info(`HTTP API is always enabled on port ${t.bold(port)}`);
|
|
183
254
|
p.log.info(`Press ${t.bold("space")} to select, ${t.bold("enter")} to confirm`);
|
|
184
255
|
|
|
256
|
+
p.note(
|
|
257
|
+
[
|
|
258
|
+
"Select every channel you want to activate.",
|
|
259
|
+
"Each selected channel will ask for its credentials.",
|
|
260
|
+
"You can add more channels later by editing your .env file.",
|
|
261
|
+
"",
|
|
262
|
+
"Tip: each channel supports an optional allowlist (restrict",
|
|
263
|
+
" who can message the agent) and a model override.",
|
|
264
|
+
" Configure those later with: daemora tenant set",
|
|
265
|
+
].join("\n"),
|
|
266
|
+
"Channels"
|
|
267
|
+
);
|
|
268
|
+
|
|
185
269
|
const channels = guard(await p.multiselect({
|
|
186
|
-
message: "Enable
|
|
270
|
+
message: "Enable channels (space = toggle, enter = confirm)",
|
|
187
271
|
options: [
|
|
188
|
-
{ value: "telegram",
|
|
189
|
-
{ value: "whatsapp",
|
|
190
|
-
{ value: "
|
|
272
|
+
{ value: "telegram", label: "Telegram", hint: "Bot via @BotFather — easiest to set up" },
|
|
273
|
+
{ value: "whatsapp", label: "WhatsApp", hint: "Via Twilio sandbox" },
|
|
274
|
+
{ value: "discord", label: "Discord", hint: "Bot via Discord Developer Portal" },
|
|
275
|
+
{ value: "slack", label: "Slack", hint: "Socket Mode bot" },
|
|
276
|
+
{ value: "email", label: "Email", hint: "Gmail IMAP/SMTP or any provider" },
|
|
277
|
+
{ value: "line", label: "LINE", hint: "LINE Messaging API" },
|
|
278
|
+
{ value: "signal", label: "Signal", hint: "Requires signal-cli daemon" },
|
|
279
|
+
{ value: "teams", label: "Microsoft Teams", hint: "Azure Bot Framework" },
|
|
280
|
+
{ value: "googlechat", label: "Google Chat", hint: "Service account" },
|
|
281
|
+
{ value: "matrix", label: "Matrix", hint: "Element / matrix.org" },
|
|
282
|
+
{ value: "mattermost", label: "Mattermost", hint: "Self-hosted or cloud" },
|
|
283
|
+
{ value: "twitch", label: "Twitch", hint: "Chat commands with !ask prefix" },
|
|
284
|
+
{ value: "irc", label: "IRC", hint: "Any IRC network (Libera, Freenode, ...)" },
|
|
285
|
+
{ value: "imessage", label: "iMessage", hint: "macOS only — AppleScript polling" },
|
|
286
|
+
{ value: "feishu", label: "Feishu / Lark", hint: "Bytedance enterprise messaging" },
|
|
287
|
+
{ value: "zalo", label: "Zalo", hint: "Vietnam — 75M+ users" },
|
|
288
|
+
{ value: "nextcloud", label: "Nextcloud Talk", hint: "Self-hosted collaboration" },
|
|
289
|
+
{ value: "bluebubbles", label: "BlueBubbles", hint: "iMessage relay server on a Mac" },
|
|
290
|
+
{ value: "nostr", label: "Nostr", hint: "Decentralized protocol — NIP-04 DMs" },
|
|
191
291
|
],
|
|
192
292
|
required: false,
|
|
193
293
|
}));
|
|
194
294
|
|
|
295
|
+
// ── Per-channel credential collection ─────────────────────────────────────
|
|
296
|
+
|
|
195
297
|
if (channels.includes("telegram")) {
|
|
196
298
|
p.note(
|
|
197
299
|
[
|
|
198
|
-
"1. Open Telegram
|
|
199
|
-
"2. Send /newbot
|
|
200
|
-
"3. Copy the bot token
|
|
300
|
+
"1. Open Telegram → search @BotFather",
|
|
301
|
+
"2. Send /newbot, follow the prompts",
|
|
302
|
+
"3. Copy the bot token (format: 123456789:ABCdef...)",
|
|
303
|
+
"",
|
|
304
|
+
"Optional later — add to .env:",
|
|
305
|
+
" TELEGRAM_ALLOWLIST=123456,987654 (comma-separated chat IDs)",
|
|
306
|
+
" TELEGRAM_MODEL=anthropic:claude-sonnet-4-6",
|
|
201
307
|
].join("\n"),
|
|
202
|
-
"
|
|
308
|
+
"Telegram Setup"
|
|
203
309
|
);
|
|
204
310
|
const token = guard(await p.password({ message: "Telegram bot token" }));
|
|
205
311
|
if (token) envConfig.TELEGRAM_BOT_TOKEN = token;
|
|
@@ -208,39 +314,307 @@ export async function runSetupWizard() {
|
|
|
208
314
|
if (channels.includes("whatsapp")) {
|
|
209
315
|
p.note(
|
|
210
316
|
[
|
|
211
|
-
"1.
|
|
212
|
-
"2. Copy Account SID
|
|
213
|
-
"3.
|
|
214
|
-
"4.
|
|
317
|
+
"1. Sign up at https://console.twilio.com",
|
|
318
|
+
"2. Copy Account SID + Auth Token from the dashboard",
|
|
319
|
+
"3. Messaging › Try it out › WhatsApp → join sandbox",
|
|
320
|
+
"4. Sandbox number is pre-filled below (change if you have a dedicated number)",
|
|
321
|
+
"",
|
|
322
|
+
"Optional: WHATSAPP_ALLOWLIST=+1234567890,+9876543210",
|
|
215
323
|
].join("\n"),
|
|
216
|
-
"
|
|
324
|
+
"WhatsApp / Twilio Setup"
|
|
217
325
|
);
|
|
218
|
-
envConfig.TWILIO_ACCOUNT_SID
|
|
219
|
-
envConfig.TWILIO_AUTH_TOKEN
|
|
220
|
-
envConfig.TWILIO_WHATSAPP_FROM
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
326
|
+
envConfig.TWILIO_ACCOUNT_SID = guard(await p.password({ message: "Twilio Account SID" }));
|
|
327
|
+
envConfig.TWILIO_AUTH_TOKEN = guard(await p.password({ message: "Twilio Auth Token" }));
|
|
328
|
+
envConfig.TWILIO_WHATSAPP_FROM = guard(await p.text({ message: "WhatsApp From number", initialValue: "whatsapp:+14155238886" }));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (channels.includes("discord")) {
|
|
332
|
+
p.note(
|
|
333
|
+
[
|
|
334
|
+
"1. Go to https://discord.com/developers/applications",
|
|
335
|
+
"2. New Application → Bot → Add Bot → Reset Token → copy token",
|
|
336
|
+
"3. Enable 'Message Content Intent' under Privileged Gateway Intents",
|
|
337
|
+
"4. OAuth2 › URL Generator → bot scope → Send Messages, Read Message History",
|
|
338
|
+
"5. Invite bot to your server with the generated URL",
|
|
339
|
+
"",
|
|
340
|
+
"Optional: DISCORD_ALLOWLIST=123456789,987654321 (Discord user snowflake IDs)",
|
|
341
|
+
].join("\n"),
|
|
342
|
+
"Discord Bot Setup"
|
|
343
|
+
);
|
|
344
|
+
const token = guard(await p.password({ message: "Discord bot token" }));
|
|
345
|
+
if (token) envConfig.DISCORD_BOT_TOKEN = token;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (channels.includes("slack")) {
|
|
349
|
+
p.note(
|
|
350
|
+
[
|
|
351
|
+
"1. Go to https://api.slack.com/apps → Create New App → From scratch",
|
|
352
|
+
"2. Socket Mode → Enable → create App-Level Token (xapp-...) → copy as App Token",
|
|
353
|
+
"3. OAuth & Permissions → Bot Token Scopes: chat:write, im:history, app_mentions:read",
|
|
354
|
+
"4. Install to workspace → copy Bot Token (xoxb-...)",
|
|
355
|
+
"",
|
|
356
|
+
"Optional: SLACK_ALLOWLIST=U01234567,U09876543 (Slack user IDs)",
|
|
357
|
+
].join("\n"),
|
|
358
|
+
"Slack Setup"
|
|
359
|
+
);
|
|
360
|
+
envConfig.SLACK_BOT_TOKEN = guard(await p.password({ message: "Slack Bot Token (xoxb-...)" }));
|
|
361
|
+
envConfig.SLACK_APP_TOKEN = guard(await p.password({ message: "Slack App Token (xapp-...)" }));
|
|
224
362
|
}
|
|
225
363
|
|
|
226
364
|
if (channels.includes("email")) {
|
|
227
365
|
p.note(
|
|
228
366
|
[
|
|
229
|
-
"
|
|
230
|
-
"1.
|
|
231
|
-
"2.
|
|
232
|
-
"3.
|
|
233
|
-
"
|
|
367
|
+
"Gmail setup:",
|
|
368
|
+
"1. Google Account › Security › 2-Step Verification → enable",
|
|
369
|
+
"2. Google Account › Security › App Passwords → Mail → create",
|
|
370
|
+
"3. Use the 16-char app password below (NOT your Gmail password)",
|
|
371
|
+
"",
|
|
372
|
+
"For other providers: change IMAP/SMTP hosts below.",
|
|
373
|
+
"Optional: EMAIL_ALLOWLIST=alice@example.com,bob@example.com",
|
|
234
374
|
].join("\n"),
|
|
235
375
|
"Email Setup"
|
|
236
376
|
);
|
|
237
|
-
envConfig.EMAIL_USER
|
|
238
|
-
envConfig.EMAIL_PASSWORD
|
|
377
|
+
envConfig.EMAIL_USER = guard(await p.text({ message: "Email address" }));
|
|
378
|
+
envConfig.EMAIL_PASSWORD = guard(await p.password({ message: "App password" }));
|
|
239
379
|
envConfig.EMAIL_IMAP_HOST = guard(await p.text({ message: "IMAP host", initialValue: "imap.gmail.com" }));
|
|
240
380
|
envConfig.EMAIL_SMTP_HOST = guard(await p.text({ message: "SMTP host", initialValue: "smtp.gmail.com" }));
|
|
241
381
|
}
|
|
242
382
|
|
|
243
|
-
|
|
383
|
+
if (channels.includes("line")) {
|
|
384
|
+
p.note(
|
|
385
|
+
[
|
|
386
|
+
"1. Go to https://developers.line.biz → Create a Provider",
|
|
387
|
+
"2. Create a Messaging API channel",
|
|
388
|
+
"3. Basic settings → Channel Secret",
|
|
389
|
+
"4. Messaging API → Channel Access Token (long-lived) → Issue",
|
|
390
|
+
"5. Set webhook URL to: https://your-server/webhooks/line",
|
|
391
|
+
].join("\n"),
|
|
392
|
+
"LINE Setup"
|
|
393
|
+
);
|
|
394
|
+
envConfig.LINE_CHANNEL_ACCESS_TOKEN = guard(await p.password({ message: "LINE Channel Access Token" }));
|
|
395
|
+
envConfig.LINE_CHANNEL_SECRET = guard(await p.password({ message: "LINE Channel Secret" }));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (channels.includes("signal")) {
|
|
399
|
+
p.note(
|
|
400
|
+
[
|
|
401
|
+
"Requires signal-cli running as a REST daemon:",
|
|
402
|
+
" npm install -g signal-cli (or download from GitHub)",
|
|
403
|
+
" signal-cli -u +1234567890 register",
|
|
404
|
+
" signal-cli -u +1234567890 verify <code>",
|
|
405
|
+
" signal-cli -u +1234567890 daemon --http 127.0.0.1:8080",
|
|
406
|
+
"",
|
|
407
|
+
"Optional: SIGNAL_ALLOWLIST=+1234567890,+0987654321",
|
|
408
|
+
].join("\n"),
|
|
409
|
+
"Signal Setup"
|
|
410
|
+
);
|
|
411
|
+
envConfig.SIGNAL_CLI_URL = guard(await p.text({ message: "signal-cli REST URL", initialValue: "http://127.0.0.1:8080" }));
|
|
412
|
+
envConfig.SIGNAL_PHONE_NUMBER = guard(await p.text({ message: "Your Signal phone number (+1234567890)" }));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (channels.includes("teams")) {
|
|
416
|
+
p.note(
|
|
417
|
+
[
|
|
418
|
+
"1. Go to https://portal.azure.com → Create an Azure Bot",
|
|
419
|
+
"2. Configuration → set messaging endpoint: https://your-server/webhooks/teams",
|
|
420
|
+
"3. Copy App ID from Configuration",
|
|
421
|
+
"4. Manage Password → New client secret → copy value",
|
|
422
|
+
"5. Channels → Add Microsoft Teams",
|
|
423
|
+
].join("\n"),
|
|
424
|
+
"Microsoft Teams Setup"
|
|
425
|
+
);
|
|
426
|
+
envConfig.TEAMS_APP_ID = guard(await p.text({ message: "Teams App ID (UUID)" }));
|
|
427
|
+
envConfig.TEAMS_APP_PASSWORD = guard(await p.password({ message: "Teams App Password (client secret)" }));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (channels.includes("googlechat")) {
|
|
431
|
+
p.note(
|
|
432
|
+
[
|
|
433
|
+
"1. Google Cloud Console → Enable 'Google Chat API'",
|
|
434
|
+
"2. IAM → Service Accounts → Create → download JSON key",
|
|
435
|
+
"3. Chat API → Configuration → Bot URL: https://your-server/webhooks/googlechat",
|
|
436
|
+
"4. Paste the ENTIRE JSON key file contents as one line below",
|
|
437
|
+
].join("\n"),
|
|
438
|
+
"Google Chat Setup"
|
|
439
|
+
);
|
|
440
|
+
envConfig.GOOGLE_CHAT_SERVICE_ACCOUNT = guard(await p.text({ message: "Service account JSON (one line)" }));
|
|
441
|
+
envConfig.GOOGLE_CHAT_PROJECT_NUMBER = guard(await p.text({ message: "Google Cloud project number" }));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (channels.includes("matrix")) {
|
|
445
|
+
p.note(
|
|
446
|
+
[
|
|
447
|
+
"1. Create a bot account on matrix.org or your homeserver",
|
|
448
|
+
"2. Get access token: POST /_matrix/client/v3/login",
|
|
449
|
+
" Body: {\"type\":\"m.login.password\",\"user\":\"@bot:matrix.org\",\"password\":\"...\"}",
|
|
450
|
+
"3. Copy 'access_token' from the response",
|
|
451
|
+
].join("\n"),
|
|
452
|
+
"Matrix Setup"
|
|
453
|
+
);
|
|
454
|
+
envConfig.MATRIX_HOMESERVER_URL = guard(await p.text({ message: "Homeserver URL", initialValue: "https://matrix.org" }));
|
|
455
|
+
envConfig.MATRIX_ACCESS_TOKEN = guard(await p.password({ message: "Bot access token" }));
|
|
456
|
+
envConfig.MATRIX_BOT_USER_ID = guard(await p.text({ message: "Bot user ID (e.g. @daemora:matrix.org)" }));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (channels.includes("mattermost")) {
|
|
460
|
+
p.note(
|
|
461
|
+
[
|
|
462
|
+
"1. System Console → Integrations → Bot Accounts → Enable",
|
|
463
|
+
"2. Integrations → Bot Accounts → Add Bot Account",
|
|
464
|
+
"3. Copy the bot token shown after creation",
|
|
465
|
+
"4. Find bot user ID: GET /api/v4/users/me (Authorization: Bearer <token>)",
|
|
466
|
+
].join("\n"),
|
|
467
|
+
"Mattermost Setup"
|
|
468
|
+
);
|
|
469
|
+
envConfig.MATTERMOST_URL = guard(await p.text({ message: "Mattermost URL", placeholder: "https://your-mattermost.example.com" }));
|
|
470
|
+
envConfig.MATTERMOST_TOKEN = guard(await p.password({ message: "Bot token" }));
|
|
471
|
+
envConfig.MATTERMOST_BOT_USER_ID = guard(await p.text({ message: "Bot user ID (optional)", initialValue: "" }));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (channels.includes("twitch")) {
|
|
475
|
+
p.note(
|
|
476
|
+
[
|
|
477
|
+
"1. Create a Twitch account for your bot",
|
|
478
|
+
"2. Get OAuth token: https://twitchapps.com/tmi/",
|
|
479
|
+
" (authorize with your BOT account, not your main account)",
|
|
480
|
+
"3. Copy the oauth:... token",
|
|
481
|
+
"",
|
|
482
|
+
"Users trigger the bot with: !ask <message>",
|
|
483
|
+
"Change prefix with: TWITCH_COMMAND_PREFIX=!ai",
|
|
484
|
+
].join("\n"),
|
|
485
|
+
"Twitch Setup"
|
|
486
|
+
);
|
|
487
|
+
envConfig.TWITCH_BOT_USERNAME = guard(await p.text({ message: "Bot Twitch username" }));
|
|
488
|
+
envConfig.TWITCH_OAUTH_TOKEN = guard(await p.password({ message: "OAuth token (oauth:...)" }));
|
|
489
|
+
envConfig.TWITCH_CHANNEL = guard(await p.text({ message: "Channel to join (without #)" }));
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (channels.includes("irc")) {
|
|
493
|
+
p.note(
|
|
494
|
+
[
|
|
495
|
+
"Connects to any IRC network. No external packages — uses raw TCP.",
|
|
496
|
+
"Popular networks: irc.libera.chat irc.freenode.net irc.oftc.net",
|
|
497
|
+
"",
|
|
498
|
+
"Optional: IRC_PASSWORD=<nickserv-password>",
|
|
499
|
+
].join("\n"),
|
|
500
|
+
"IRC Setup"
|
|
501
|
+
);
|
|
502
|
+
envConfig.IRC_SERVER = guard(await p.text({ message: "IRC server", initialValue: "irc.libera.chat" }));
|
|
503
|
+
envConfig.IRC_PORT = guard(await p.text({ message: "IRC port", initialValue: "6667" }));
|
|
504
|
+
envConfig.IRC_NICK = guard(await p.text({ message: "Bot nick", initialValue: "daemora-bot" }));
|
|
505
|
+
envConfig.IRC_CHANNEL = guard(await p.text({ message: "Channel to join (e.g. #mychannel)" }));
|
|
506
|
+
const ircPass = guard(await p.password({ message: "NickServ password (optional, leave blank)" }));
|
|
507
|
+
if (ircPass) envConfig.IRC_PASSWORD = ircPass;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (channels.includes("imessage")) {
|
|
511
|
+
if (process.platform !== "darwin") {
|
|
512
|
+
p.log.warn("iMessage requires macOS. Skipping.");
|
|
513
|
+
} else {
|
|
514
|
+
p.note(
|
|
515
|
+
[
|
|
516
|
+
"Polls iMessages via AppleScript — macOS only.",
|
|
517
|
+
"Requires: Messages app open + Accessibility permissions granted to Terminal",
|
|
518
|
+
"",
|
|
519
|
+
"System Preferences › Privacy & Security › Accessibility → allow Terminal",
|
|
520
|
+
"",
|
|
521
|
+
"Optional: IMESSAGE_ALLOWLIST=+1234567890,user@icloud.com",
|
|
522
|
+
].join("\n"),
|
|
523
|
+
"iMessage Setup"
|
|
524
|
+
);
|
|
525
|
+
envConfig.IMESSAGE_ENABLED = "true";
|
|
526
|
+
envConfig.IMESSAGE_POLL_INTERVAL_MS = "5000";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (channels.includes("feishu")) {
|
|
531
|
+
p.note(
|
|
532
|
+
[
|
|
533
|
+
"1. Go to https://open.feishu.cn/app → Create App",
|
|
534
|
+
"2. Credentials & Basic Info → copy App ID and App Secret",
|
|
535
|
+
"3. Add capability: Bot",
|
|
536
|
+
"4. Event Subscriptions → set webhook: https://your-server/channels/feishu",
|
|
537
|
+
"5. Add events: im.message.receive_v1",
|
|
538
|
+
].join("\n"),
|
|
539
|
+
"Feishu / Lark Setup"
|
|
540
|
+
);
|
|
541
|
+
envConfig.FEISHU_APP_ID = guard(await p.text({ message: "Feishu App ID" }));
|
|
542
|
+
envConfig.FEISHU_APP_SECRET = guard(await p.password({ message: "Feishu App Secret" }));
|
|
543
|
+
envConfig.FEISHU_VERIFICATION_TOKEN = guard(await p.password({ message: "Verification token (optional)", }));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (channels.includes("zalo")) {
|
|
547
|
+
p.note(
|
|
548
|
+
[
|
|
549
|
+
"1. Register Official Account at https://oa.zalo.me",
|
|
550
|
+
"2. Create app at https://developers.zalo.me → API Tools",
|
|
551
|
+
"3. Copy App ID and App Secret",
|
|
552
|
+
"4. Get access token via OAuth",
|
|
553
|
+
"5. Set webhook: https://your-server/channels/zalo",
|
|
554
|
+
].join("\n"),
|
|
555
|
+
"Zalo Setup"
|
|
556
|
+
);
|
|
557
|
+
envConfig.ZALO_APP_ID = guard(await p.text({ message: "Zalo App ID" }));
|
|
558
|
+
envConfig.ZALO_APP_SECRET = guard(await p.password({ message: "Zalo App Secret" }));
|
|
559
|
+
envConfig.ZALO_ACCESS_TOKEN = guard(await p.password({ message: "Zalo Access Token" }));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (channels.includes("nextcloud")) {
|
|
563
|
+
p.note(
|
|
564
|
+
[
|
|
565
|
+
"1. Log into Nextcloud → Profile icon → Settings → Security",
|
|
566
|
+
"2. Devices & Sessions → create App Password for the bot account",
|
|
567
|
+
"3. Find the room token in Talk URL: /call/<room-token>",
|
|
568
|
+
].join("\n"),
|
|
569
|
+
"Nextcloud Talk Setup"
|
|
570
|
+
);
|
|
571
|
+
envConfig.NEXTCLOUD_URL = guard(await p.text({ message: "Nextcloud URL", placeholder: "https://cloud.example.com" }));
|
|
572
|
+
envConfig.NEXTCLOUD_USER = guard(await p.text({ message: "Bot username", initialValue: "daemora-bot" }));
|
|
573
|
+
envConfig.NEXTCLOUD_PASSWORD = guard(await p.password({ message: "App password" }));
|
|
574
|
+
envConfig.NEXTCLOUD_ROOM_TOKEN = guard(await p.text({ message: "Room token (from Talk URL)" }));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (channels.includes("bluebubbles")) {
|
|
578
|
+
p.note(
|
|
579
|
+
[
|
|
580
|
+
"BlueBubbles is an iMessage relay server that runs on a Mac.",
|
|
581
|
+
"Download: https://bluebubbles.app",
|
|
582
|
+
"",
|
|
583
|
+
"1. Install BlueBubbles on a Mac that is signed into iMessage",
|
|
584
|
+
"2. Settings → Server → copy Server URL and Password",
|
|
585
|
+
"3. The URL is usually http://192.168.x.x:1234 on your LAN",
|
|
586
|
+
].join("\n"),
|
|
587
|
+
"BlueBubbles Setup"
|
|
588
|
+
);
|
|
589
|
+
envConfig.BLUEBUBBLES_URL = guard(await p.text({ message: "BlueBubbles server URL", placeholder: "http://192.168.1.100:1234" }));
|
|
590
|
+
envConfig.BLUEBUBBLES_PASSWORD = guard(await p.password({ message: "BlueBubbles server password" }));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (channels.includes("nostr")) {
|
|
594
|
+
p.note(
|
|
595
|
+
[
|
|
596
|
+
"Nostr is a decentralized protocol. The bot receives NIP-04 encrypted DMs.",
|
|
597
|
+
"",
|
|
598
|
+
"Generate a private key:",
|
|
599
|
+
" openssl rand -hex 32",
|
|
600
|
+
"",
|
|
601
|
+
"Default relays are pre-filled. Add/remove as needed.",
|
|
602
|
+
"Share your bot's npub (public key) so users can DM it.",
|
|
603
|
+
].join("\n"),
|
|
604
|
+
"Nostr Setup"
|
|
605
|
+
);
|
|
606
|
+
envConfig.NOSTR_PRIVATE_KEY = guard(await p.password({ message: "Nostr private key (hex, 64 chars)" }));
|
|
607
|
+
const relaysRaw = guard(await p.text({
|
|
608
|
+
message: "Relay URLs (comma-separated)",
|
|
609
|
+
initialValue: "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band",
|
|
610
|
+
}));
|
|
611
|
+
envConfig.NOSTR_RELAYS = relaysRaw;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const activeChannels = ["HTTP", ...channels.map((c) => {
|
|
615
|
+
const labels = { googlechat: "GoogleChat", imessage: "iMessage", bluebubbles: "BlueBubbles" };
|
|
616
|
+
return labels[c] || c.charAt(0).toUpperCase() + c.slice(1);
|
|
617
|
+
})];
|
|
244
618
|
p.log.success(`Channels: ${t.bold(activeChannels.join(", "))}`);
|
|
245
619
|
|
|
246
620
|
// ━━━ Step 6: Daemon ━━━
|
|
@@ -389,17 +763,17 @@ export async function runSetupWizard() {
|
|
|
389
763
|
}));
|
|
390
764
|
|
|
391
765
|
while (addMore) {
|
|
392
|
-
p.log.info(`${t.bold("Custom MCP Server")}
|
|
393
|
-
p.log.info(` ${S.arrow} ${t.accent("stdio")}
|
|
394
|
-
p.log.info(` ${S.arrow} ${t.accent("http")}
|
|
395
|
-
p.log.info(` ${S.arrow} ${t.accent("sse")}
|
|
766
|
+
p.log.info(`${t.bold("Custom MCP Server")} - 3 transport types supported:`);
|
|
767
|
+
p.log.info(` ${S.arrow} ${t.accent("stdio")} - local subprocess (npx, node, python, go, etc.)`);
|
|
768
|
+
p.log.info(` ${S.arrow} ${t.accent("http")} - remote HTTP server (streamable MCP)`);
|
|
769
|
+
p.log.info(` ${S.arrow} ${t.accent("sse")} - remote SSE server (Server-Sent Events)`);
|
|
396
770
|
|
|
397
771
|
const customName = guard(await p.text({
|
|
398
772
|
message: "Server name (no spaces, e.g. mytools, notion, postgres)",
|
|
399
773
|
validate: (v) => {
|
|
400
774
|
if (!v) return "Name is required";
|
|
401
775
|
if (/\s/.test(v)) return "Name cannot contain spaces";
|
|
402
|
-
if (mcpConfig.mcpServers[v]) return `"${v}" already exists
|
|
776
|
+
if (mcpConfig.mcpServers[v]) return `"${v}" already exists - choose a different name`;
|
|
403
777
|
},
|
|
404
778
|
}));
|
|
405
779
|
|
|
@@ -412,7 +786,7 @@ export async function runSetupWizard() {
|
|
|
412
786
|
const transport = guard(await p.select({
|
|
413
787
|
message: "Transport type",
|
|
414
788
|
options: [
|
|
415
|
-
{ value: "stdio", label: "stdio", hint: "Local subprocess
|
|
789
|
+
{ value: "stdio", label: "stdio", hint: "Local subprocess - npx, node, python, go binary, etc." },
|
|
416
790
|
{ value: "http", label: "http", hint: "Remote HTTP endpoint (streamable MCP protocol)" },
|
|
417
791
|
{ value: "sse", label: "sse", hint: "Remote SSE endpoint (Server-Sent Events)" },
|
|
418
792
|
],
|
|
@@ -486,7 +860,7 @@ export async function runSetupWizard() {
|
|
|
486
860
|
if (transport === "sse") serverCfg.transport = "sse";
|
|
487
861
|
|
|
488
862
|
// HTTP/SSE auth: credentials go as HTTP request HEADERS (Authorization, X-API-Key, etc.)
|
|
489
|
-
// They are NOT env vars
|
|
863
|
+
// They are NOT env vars - they are sent with every HTTP request to the server.
|
|
490
864
|
p.note(
|
|
491
865
|
[
|
|
492
866
|
"HTTP/SSE servers authenticate via request headers, not env vars.",
|
|
@@ -505,7 +879,7 @@ export async function runSetupWizard() {
|
|
|
505
879
|
const authType = guard(await p.select({
|
|
506
880
|
message: "Authentication type",
|
|
507
881
|
options: [
|
|
508
|
-
{ value: "none", label: "None", hint: "No auth
|
|
882
|
+
{ value: "none", label: "None", hint: "No auth - public or local server" },
|
|
509
883
|
{ value: "bearer", label: "Bearer token", hint: "Authorization: Bearer <token>" },
|
|
510
884
|
{ value: "apikey", label: "API key header", hint: "X-API-Key or custom header name" },
|
|
511
885
|
{ value: "custom", label: "Custom headers", hint: "Add any headers manually" },
|
|
@@ -520,7 +894,7 @@ export async function runSetupWizard() {
|
|
|
520
894
|
message: "Bearer token (or ${MY_ENV_VAR} to reference an env var)",
|
|
521
895
|
validate: (v) => !v ? "Required" : undefined,
|
|
522
896
|
}));
|
|
523
|
-
// Store the raw value
|
|
897
|
+
// Store the raw value - ${VAR} gets expanded at connect time by MCPClient
|
|
524
898
|
serverCfg.headers["Authorization"] = `Bearer ${token}`;
|
|
525
899
|
|
|
526
900
|
} else if (authType === "apikey") {
|
|
@@ -536,7 +910,7 @@ export async function runSetupWizard() {
|
|
|
536
910
|
serverCfg.headers[headerName] = apiKey;
|
|
537
911
|
|
|
538
912
|
} else {
|
|
539
|
-
// custom
|
|
913
|
+
// custom - loop
|
|
540
914
|
let addHeader = true;
|
|
541
915
|
while (addHeader) {
|
|
542
916
|
const headerName = guard(await p.text({
|
|
@@ -650,6 +1024,7 @@ export async function runSetupWizard() {
|
|
|
650
1024
|
|
|
651
1025
|
const secretKeys = [
|
|
652
1026
|
"OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GOOGLE_AI_API_KEY",
|
|
1027
|
+
"XAI_API_KEY", "DEEPSEEK_API_KEY", "MISTRAL_API_KEY",
|
|
653
1028
|
"TELEGRAM_BOT_TOKEN", "TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN",
|
|
654
1029
|
"EMAIL_PASSWORD",
|
|
655
1030
|
];
|
|
@@ -684,7 +1059,7 @@ export async function runSetupWizard() {
|
|
|
684
1059
|
];
|
|
685
1060
|
|
|
686
1061
|
const categories = {
|
|
687
|
-
"AI Model": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GOOGLE_AI_API_KEY", "DEFAULT_MODEL"],
|
|
1062
|
+
"AI Model": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GOOGLE_AI_API_KEY", "XAI_API_KEY", "DEEPSEEK_API_KEY", "MISTRAL_API_KEY", "DEFAULT_MODEL"],
|
|
688
1063
|
"Server": ["PORT"],
|
|
689
1064
|
"Safety": ["PERMISSION_TIER", "MAX_COST_PER_TASK", "MAX_DAILY_COST"],
|
|
690
1065
|
"Filesystem": ["ALLOWED_PATHS", "BLOCKED_PATHS", "RESTRICT_COMMANDS"],
|
|
@@ -722,7 +1097,7 @@ export async function runSetupWizard() {
|
|
|
722
1097
|
const dm = new DaemonManager();
|
|
723
1098
|
dm.install();
|
|
724
1099
|
} catch {
|
|
725
|
-
// Non-fatal
|
|
1100
|
+
// Non-fatal - user can install later
|
|
726
1101
|
}
|
|
727
1102
|
}
|
|
728
1103
|
|