clementine-agent 1.0.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.
- package/.env.example +44 -0
- package/LICENSE +21 -0
- package/README.md +795 -0
- package/dist/agent/agent-manager.d.ts +69 -0
- package/dist/agent/agent-manager.js +441 -0
- package/dist/agent/assistant.d.ts +225 -0
- package/dist/agent/assistant.js +3888 -0
- package/dist/agent/auto-update.d.ts +32 -0
- package/dist/agent/auto-update.js +186 -0
- package/dist/agent/daily-planner.d.ts +24 -0
- package/dist/agent/daily-planner.js +379 -0
- package/dist/agent/execution-advisor.d.ts +10 -0
- package/dist/agent/execution-advisor.js +272 -0
- package/dist/agent/hooks.d.ts +45 -0
- package/dist/agent/hooks.js +564 -0
- package/dist/agent/insight-engine.d.ts +66 -0
- package/dist/agent/insight-engine.js +225 -0
- package/dist/agent/intent-classifier.d.ts +48 -0
- package/dist/agent/intent-classifier.js +214 -0
- package/dist/agent/link-extractor.d.ts +19 -0
- package/dist/agent/link-extractor.js +90 -0
- package/dist/agent/mcp-bridge.d.ts +62 -0
- package/dist/agent/mcp-bridge.js +435 -0
- package/dist/agent/metacognition.d.ts +66 -0
- package/dist/agent/metacognition.js +221 -0
- package/dist/agent/orchestrator.d.ts +81 -0
- package/dist/agent/orchestrator.js +790 -0
- package/dist/agent/profiles.d.ts +22 -0
- package/dist/agent/profiles.js +91 -0
- package/dist/agent/prompt-cache.d.ts +24 -0
- package/dist/agent/prompt-cache.js +68 -0
- package/dist/agent/prompt-evolver.d.ts +28 -0
- package/dist/agent/prompt-evolver.js +279 -0
- package/dist/agent/role-scaffolds.d.ts +28 -0
- package/dist/agent/role-scaffolds.js +433 -0
- package/dist/agent/safe-restart.d.ts +41 -0
- package/dist/agent/safe-restart.js +150 -0
- package/dist/agent/self-improve.d.ts +66 -0
- package/dist/agent/self-improve.js +1706 -0
- package/dist/agent/session-event-log.d.ts +114 -0
- package/dist/agent/session-event-log.js +233 -0
- package/dist/agent/skill-extractor.d.ts +72 -0
- package/dist/agent/skill-extractor.js +435 -0
- package/dist/agent/source-mods.d.ts +61 -0
- package/dist/agent/source-mods.js +230 -0
- package/dist/agent/source-preflight.d.ts +25 -0
- package/dist/agent/source-preflight.js +100 -0
- package/dist/agent/stall-guard.d.ts +62 -0
- package/dist/agent/stall-guard.js +109 -0
- package/dist/agent/strategic-planner.d.ts +60 -0
- package/dist/agent/strategic-planner.js +352 -0
- package/dist/agent/team-bus.d.ts +89 -0
- package/dist/agent/team-bus.js +556 -0
- package/dist/agent/team-router.d.ts +26 -0
- package/dist/agent/team-router.js +37 -0
- package/dist/agent/tool-loop-detector.d.ts +59 -0
- package/dist/agent/tool-loop-detector.js +242 -0
- package/dist/agent/workflow-runner.d.ts +36 -0
- package/dist/agent/workflow-runner.js +317 -0
- package/dist/agent/workflow-variables.d.ts +16 -0
- package/dist/agent/workflow-variables.js +62 -0
- package/dist/channels/discord-agent-bot.d.ts +101 -0
- package/dist/channels/discord-agent-bot.js +881 -0
- package/dist/channels/discord-bot-manager.d.ts +80 -0
- package/dist/channels/discord-bot-manager.js +262 -0
- package/dist/channels/discord-utils.d.ts +51 -0
- package/dist/channels/discord-utils.js +293 -0
- package/dist/channels/discord.d.ts +12 -0
- package/dist/channels/discord.js +1832 -0
- package/dist/channels/slack-agent-bot.d.ts +73 -0
- package/dist/channels/slack-agent-bot.js +320 -0
- package/dist/channels/slack-bot-manager.d.ts +66 -0
- package/dist/channels/slack-bot-manager.js +236 -0
- package/dist/channels/slack-utils.d.ts +39 -0
- package/dist/channels/slack-utils.js +189 -0
- package/dist/channels/slack.d.ts +11 -0
- package/dist/channels/slack.js +196 -0
- package/dist/channels/telegram.d.ts +10 -0
- package/dist/channels/telegram.js +235 -0
- package/dist/channels/webhook.d.ts +9 -0
- package/dist/channels/webhook.js +78 -0
- package/dist/channels/whatsapp.d.ts +11 -0
- package/dist/channels/whatsapp.js +181 -0
- package/dist/cli/chat.d.ts +14 -0
- package/dist/cli/chat.js +220 -0
- package/dist/cli/cron.d.ts +17 -0
- package/dist/cli/cron.js +552 -0
- package/dist/cli/dashboard.d.ts +15 -0
- package/dist/cli/dashboard.js +17677 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +2474 -0
- package/dist/cli/routes/delegations.d.ts +19 -0
- package/dist/cli/routes/delegations.js +154 -0
- package/dist/cli/routes/digest.d.ts +17 -0
- package/dist/cli/routes/digest.js +375 -0
- package/dist/cli/routes/goals.d.ts +14 -0
- package/dist/cli/routes/goals.js +258 -0
- package/dist/cli/routes/workflows.d.ts +18 -0
- package/dist/cli/routes/workflows.js +97 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +619 -0
- package/dist/cli/tunnel.d.ts +35 -0
- package/dist/cli/tunnel.js +141 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.js +278 -0
- package/dist/events/bus.d.ts +43 -0
- package/dist/events/bus.js +136 -0
- package/dist/gateway/cron-scheduler.d.ts +166 -0
- package/dist/gateway/cron-scheduler.js +1767 -0
- package/dist/gateway/delivery-queue.d.ts +30 -0
- package/dist/gateway/delivery-queue.js +110 -0
- package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
- package/dist/gateway/heartbeat-scheduler.js +1298 -0
- package/dist/gateway/heartbeat.d.ts +3 -0
- package/dist/gateway/heartbeat.js +3 -0
- package/dist/gateway/lanes.d.ts +24 -0
- package/dist/gateway/lanes.js +76 -0
- package/dist/gateway/notifications.d.ts +29 -0
- package/dist/gateway/notifications.js +75 -0
- package/dist/gateway/router.d.ts +210 -0
- package/dist/gateway/router.js +1330 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1015 -0
- package/dist/memory/chunker.d.ts +28 -0
- package/dist/memory/chunker.js +226 -0
- package/dist/memory/consolidation.d.ts +44 -0
- package/dist/memory/consolidation.js +171 -0
- package/dist/memory/context-assembler.d.ts +50 -0
- package/dist/memory/context-assembler.js +149 -0
- package/dist/memory/embeddings.d.ts +38 -0
- package/dist/memory/embeddings.js +180 -0
- package/dist/memory/graph-store.d.ts +66 -0
- package/dist/memory/graph-store.js +613 -0
- package/dist/memory/mmr.d.ts +21 -0
- package/dist/memory/mmr.js +75 -0
- package/dist/memory/search.d.ts +26 -0
- package/dist/memory/search.js +67 -0
- package/dist/memory/store.d.ts +530 -0
- package/dist/memory/store.js +2022 -0
- package/dist/security/integrity.d.ts +24 -0
- package/dist/security/integrity.js +58 -0
- package/dist/security/patterns.d.ts +34 -0
- package/dist/security/patterns.js +110 -0
- package/dist/security/scanner.d.ts +32 -0
- package/dist/security/scanner.js +263 -0
- package/dist/tools/admin-tools.d.ts +12 -0
- package/dist/tools/admin-tools.js +1278 -0
- package/dist/tools/external-tools.d.ts +11 -0
- package/dist/tools/external-tools.js +1327 -0
- package/dist/tools/goal-tools.d.ts +9 -0
- package/dist/tools/goal-tools.js +159 -0
- package/dist/tools/mcp-server.d.ts +13 -0
- package/dist/tools/mcp-server.js +141 -0
- package/dist/tools/memory-tools.d.ts +10 -0
- package/dist/tools/memory-tools.js +568 -0
- package/dist/tools/session-tools.d.ts +6 -0
- package/dist/tools/session-tools.js +146 -0
- package/dist/tools/shared.d.ts +216 -0
- package/dist/tools/shared.js +340 -0
- package/dist/tools/team-tools.d.ts +6 -0
- package/dist/tools/team-tools.js +447 -0
- package/dist/tools/tool-meta.d.ts +34 -0
- package/dist/tools/tool-meta.js +133 -0
- package/dist/tools/vault-tools.d.ts +8 -0
- package/dist/tools/vault-tools.js +457 -0
- package/dist/types.d.ts +716 -0
- package/dist/types.js +16 -0
- package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
- package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
- package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
- package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
- package/dist/vault-migrations/helpers.d.ts +14 -0
- package/dist/vault-migrations/helpers.js +44 -0
- package/dist/vault-migrations/runner.d.ts +14 -0
- package/dist/vault-migrations/runner.js +139 -0
- package/dist/vault-migrations/types.d.ts +42 -0
- package/dist/vault-migrations/types.js +9 -0
- package/install.sh +320 -0
- package/package.json +84 -0
- package/scripts/postinstall.js +125 -0
- package/vault/00-System/AGENTS.md +66 -0
- package/vault/00-System/CRON.md +71 -0
- package/vault/00-System/HEARTBEAT.md +58 -0
- package/vault/00-System/MEMORY.md +16 -0
- package/vault/00-System/SOUL.md +96 -0
- package/vault/05-Tasks/TASKS.md +19 -0
- package/vault/06-Templates/_Daily-Template.md +28 -0
- package/vault/06-Templates/_People-Template.md +22 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive configuration wizard.
|
|
3
|
+
*
|
|
4
|
+
* Uses @inquirer/prompts for arrow-key selection, password masking,
|
|
5
|
+
* and checkbox multi-select. Replaces the old readline-based flow.
|
|
6
|
+
*/
|
|
7
|
+
import { input, select, checkbox, password, confirm } from '@inquirer/prompts';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { BASE_DIR, SYSTEM_DIR, PROJECTS_DIR, SOUL_FILE, MEMORY_FILE } from '../config.js';
|
|
12
|
+
// ── ANSI helpers ─────────────────────────────────────────────────────
|
|
13
|
+
const DIM = '\x1b[0;90m';
|
|
14
|
+
const BOLD = '\x1b[1m';
|
|
15
|
+
const ORANGE = '\x1b[38;5;208m';
|
|
16
|
+
const GREEN = '\x1b[32m';
|
|
17
|
+
const RED = '\x1b[31m';
|
|
18
|
+
const CYAN = '\x1b[0;36m';
|
|
19
|
+
const RESET = '\x1b[0m';
|
|
20
|
+
const BANNER = `
|
|
21
|
+
${ORANGE} ██████╗██╗ ███████╗███╗ ███╗███████╗███╗ ██╗████████╗██╗███╗ ██╗███████╗
|
|
22
|
+
██╔════╝██║ ██╔════╝████╗ ████║██╔════╝████╗ ██║╚══██╔══╝██║████╗ ██║██╔════╝
|
|
23
|
+
██║ ██║ █████╗ ██╔████╔██║█████╗ ██╔██╗ ██║ ██║ ██║██╔██╗ ██║█████╗
|
|
24
|
+
██║ ██║ ██╔══╝ ██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║ ██║██║╚██╗██║██╔══╝
|
|
25
|
+
╚██████╗███████╗███████╗██║ ╚═╝ ██║███████╗██║ ╚████║ ██║ ██║██║ ╚████║███████╗
|
|
26
|
+
╚═════╝╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝${RESET}
|
|
27
|
+
`;
|
|
28
|
+
function sectionHeader(title) {
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(` ${ORANGE}${BOLD}── ${title} ${'─'.repeat(Math.max(0, 50 - title.length))}${RESET}`);
|
|
31
|
+
console.log();
|
|
32
|
+
}
|
|
33
|
+
const CHANNELS = [
|
|
34
|
+
{
|
|
35
|
+
value: 'discord',
|
|
36
|
+
name: 'Discord',
|
|
37
|
+
credentials: [
|
|
38
|
+
{
|
|
39
|
+
key: 'DISCORD_TOKEN',
|
|
40
|
+
label: 'Discord bot token',
|
|
41
|
+
help: `Get your bot token at ${CYAN}https://discord.com/developers/applications${DIM}\n Create an app > Bot > Reset Token > copy it`,
|
|
42
|
+
masked: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'DISCORD_OWNER_ID',
|
|
46
|
+
label: 'Discord owner user ID',
|
|
47
|
+
help: `Right-click your name in Discord > Copy User ID (enable Developer Mode in settings)`,
|
|
48
|
+
validate: (v) => /^\d{17,20}$/.test(v) || 'Must be a numeric Discord user ID (17-20 digits)',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'DISCORD_WATCHED_CHANNELS',
|
|
52
|
+
label: 'Watched channel IDs (optional, comma-separated)',
|
|
53
|
+
help: `Right-click a text channel > Copy Channel ID. Bot will listen for messages in these channels.`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
value: 'slack',
|
|
59
|
+
name: 'Slack',
|
|
60
|
+
credentials: [
|
|
61
|
+
{
|
|
62
|
+
key: 'SLACK_BOT_TOKEN',
|
|
63
|
+
label: 'Slack bot token (xoxb-...)',
|
|
64
|
+
help: `Create a Slack app at ${CYAN}https://api.slack.com/apps${DIM}\n OAuth & Permissions > Bot User OAuth Token`,
|
|
65
|
+
masked: true,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'SLACK_APP_TOKEN',
|
|
69
|
+
label: 'Slack app token (xapp-...)',
|
|
70
|
+
help: `Basic Information > App-Level Tokens > Generate`,
|
|
71
|
+
masked: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
key: 'SLACK_OWNER_USER_ID',
|
|
75
|
+
label: 'Slack owner user ID',
|
|
76
|
+
validate: (v) => /^[UW][A-Z0-9]+$/.test(v) || 'Must be a Slack user ID (starts with U or W)',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
value: 'telegram',
|
|
82
|
+
name: 'Telegram',
|
|
83
|
+
credentials: [
|
|
84
|
+
{
|
|
85
|
+
key: 'TELEGRAM_BOT_TOKEN',
|
|
86
|
+
label: 'Telegram bot token',
|
|
87
|
+
help: `Message ${CYAN}@BotFather${DIM} on Telegram > /newbot > follow prompts > copy token`,
|
|
88
|
+
masked: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'TELEGRAM_OWNER_ID',
|
|
92
|
+
label: 'Telegram owner user ID',
|
|
93
|
+
help: `Send /chatid to your bot after first launch to get your ID`,
|
|
94
|
+
validate: (v) => /^\d+$/.test(v) || 'Must be a numeric Telegram user ID',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
value: 'whatsapp',
|
|
100
|
+
name: 'WhatsApp (Twilio)',
|
|
101
|
+
credentials: [
|
|
102
|
+
{
|
|
103
|
+
key: 'TWILIO_ACCOUNT_SID',
|
|
104
|
+
label: 'Twilio Account SID',
|
|
105
|
+
help: `Get credentials at ${CYAN}https://console.twilio.com${DIM}`,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: 'TWILIO_AUTH_TOKEN',
|
|
109
|
+
label: 'Twilio Auth Token',
|
|
110
|
+
masked: true,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
key: 'WHATSAPP_OWNER_PHONE',
|
|
114
|
+
label: 'Owner phone (+1...)',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: 'WHATSAPP_FROM_PHONE',
|
|
118
|
+
label: 'WhatsApp from phone',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: 'WHATSAPP_WEBHOOK_PORT',
|
|
122
|
+
label: 'Webhook port',
|
|
123
|
+
defaultValue: '8421',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
value: 'webhook',
|
|
129
|
+
name: 'Webhook API',
|
|
130
|
+
credentials: [
|
|
131
|
+
{
|
|
132
|
+
key: 'WEBHOOK_PORT',
|
|
133
|
+
label: 'Webhook port',
|
|
134
|
+
defaultValue: '8420',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: 'WEBHOOK_SECRET',
|
|
138
|
+
label: 'Webhook secret',
|
|
139
|
+
masked: true,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
const FEATURES = [
|
|
145
|
+
{
|
|
146
|
+
value: 'voice',
|
|
147
|
+
name: 'Voice (STT via Groq + TTS via ElevenLabs)',
|
|
148
|
+
credentials: [
|
|
149
|
+
{
|
|
150
|
+
key: 'GROQ_API_KEY',
|
|
151
|
+
label: 'Groq API key (for Whisper STT)',
|
|
152
|
+
help: `Free tier: ${CYAN}https://console.groq.com${DIM}`,
|
|
153
|
+
masked: true,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
key: 'ELEVENLABS_API_KEY',
|
|
157
|
+
label: 'ElevenLabs API key (for TTS)',
|
|
158
|
+
help: `Free tier: ${CYAN}https://elevenlabs.io${DIM}`,
|
|
159
|
+
masked: true,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
key: 'ELEVENLABS_VOICE_ID',
|
|
163
|
+
label: 'ElevenLabs voice ID',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
value: 'video',
|
|
169
|
+
name: 'Video analysis (Google Gemini)',
|
|
170
|
+
credentials: [
|
|
171
|
+
{
|
|
172
|
+
key: 'GOOGLE_API_KEY',
|
|
173
|
+
label: 'Google API key',
|
|
174
|
+
help: `Get a free key at ${CYAN}https://aistudio.google.com${DIM}`,
|
|
175
|
+
masked: true,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
value: 'outlook',
|
|
181
|
+
name: 'Outlook (Microsoft Graph — email + calendar)',
|
|
182
|
+
credentials: [
|
|
183
|
+
{
|
|
184
|
+
key: 'MS_TENANT_ID',
|
|
185
|
+
label: 'Azure AD tenant ID',
|
|
186
|
+
help: `Azure Portal > App registrations > your app > Directory (tenant) ID`,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: 'MS_CLIENT_ID',
|
|
190
|
+
label: 'Azure AD client (app) ID',
|
|
191
|
+
help: `Azure Portal > App registrations > your app > Application (client) ID`,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: 'MS_CLIENT_SECRET',
|
|
195
|
+
label: 'Azure AD client secret',
|
|
196
|
+
help: `Azure Portal > App registrations > Certificates & secrets > New client secret`,
|
|
197
|
+
masked: true,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: 'MS_USER_EMAIL',
|
|
201
|
+
label: 'Mailbox email address',
|
|
202
|
+
help: `The email address Clementine should access (e.g. nathan@example.com)`,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
// ── Credential collection helper ─────────────────────────────────────
|
|
208
|
+
async function collectCredentials(creds, entries) {
|
|
209
|
+
for (const cred of creds) {
|
|
210
|
+
if (cred.help) {
|
|
211
|
+
console.log(` ${DIM}${cred.help}${RESET}`);
|
|
212
|
+
}
|
|
213
|
+
const existing = entries[cred.key] || cred.defaultValue || '';
|
|
214
|
+
if (cred.masked) {
|
|
215
|
+
const hint = existing ? ` ${DIM}(leave blank to keep current)${RESET}` : '';
|
|
216
|
+
const val = await password({
|
|
217
|
+
message: `${cred.label}${hint}`,
|
|
218
|
+
mask: '*',
|
|
219
|
+
});
|
|
220
|
+
entries[cred.key] = val || existing;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
entries[cred.key] = await input({
|
|
224
|
+
message: cred.label,
|
|
225
|
+
default: existing,
|
|
226
|
+
validate: cred.validate,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ── Slug helper ──────────────────────────────────────────────────────
|
|
232
|
+
function slugify(text) {
|
|
233
|
+
return text
|
|
234
|
+
.toLowerCase()
|
|
235
|
+
.trim()
|
|
236
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
237
|
+
.replace(/^-+|-+$/g, '');
|
|
238
|
+
}
|
|
239
|
+
// ── Style / proactivity / tone description maps ─────────────────────
|
|
240
|
+
const STYLE_DESCRIPTIONS = {
|
|
241
|
+
concise: 'Keep responses brief and to the point',
|
|
242
|
+
balanced: 'Standard responses with appropriate detail',
|
|
243
|
+
detailed: 'Provide thorough explanations and context',
|
|
244
|
+
};
|
|
245
|
+
const PROACTIVITY_DESCRIPTIONS = {
|
|
246
|
+
reactive: 'Only act when asked, don\'t volunteer suggestions',
|
|
247
|
+
balanced: 'Offer relevant suggestions when appropriate',
|
|
248
|
+
proactive: 'Actively suggest improvements and flag issues',
|
|
249
|
+
};
|
|
250
|
+
const TONE_DESCRIPTIONS = {
|
|
251
|
+
professional: 'Maintain a formal, business-appropriate tone',
|
|
252
|
+
casual: 'Use a friendly, conversational tone',
|
|
253
|
+
minimal: 'Be terse and efficient, skip pleasantries',
|
|
254
|
+
};
|
|
255
|
+
async function aboutYouInterview(ownerName) {
|
|
256
|
+
sectionHeader('Step 7: About You (optional)');
|
|
257
|
+
console.log(` ${DIM}These questions help me learn about you. Press Enter to skip any.${RESET}`);
|
|
258
|
+
console.log();
|
|
259
|
+
let answers;
|
|
260
|
+
try {
|
|
261
|
+
const role = await input({ message: 'What\'s your job title or role?' });
|
|
262
|
+
const interests = await input({ message: 'What are your main interests or hobbies?' });
|
|
263
|
+
const projects = await input({ message: 'What projects are you currently working on? (comma-separated)' });
|
|
264
|
+
const style = await select({
|
|
265
|
+
message: 'How should I communicate with you?',
|
|
266
|
+
default: 'balanced',
|
|
267
|
+
choices: [
|
|
268
|
+
{ value: 'concise', name: 'concise — Brief and to the point' },
|
|
269
|
+
{ value: 'balanced', name: 'balanced — Standard with appropriate detail' },
|
|
270
|
+
{ value: 'detailed', name: 'detailed — Thorough explanations and context' },
|
|
271
|
+
],
|
|
272
|
+
});
|
|
273
|
+
const proactivity = await select({
|
|
274
|
+
message: 'How proactive should I be?',
|
|
275
|
+
default: 'balanced',
|
|
276
|
+
choices: [
|
|
277
|
+
{ value: 'reactive', name: 'reactive — Only act when asked' },
|
|
278
|
+
{ value: 'balanced', name: 'balanced — Offer relevant suggestions' },
|
|
279
|
+
{ value: 'proactive', name: 'proactive — Actively suggest improvements' },
|
|
280
|
+
],
|
|
281
|
+
});
|
|
282
|
+
const tone = await select({
|
|
283
|
+
message: 'What tone should I use?',
|
|
284
|
+
default: 'casual',
|
|
285
|
+
choices: [
|
|
286
|
+
{ value: 'professional', name: 'professional — Formal and business-appropriate' },
|
|
287
|
+
{ value: 'casual', name: 'casual — Friendly and conversational' },
|
|
288
|
+
{ value: 'minimal', name: 'minimal — Terse and efficient' },
|
|
289
|
+
],
|
|
290
|
+
});
|
|
291
|
+
answers = { role, interests, projects, style, proactivity, tone };
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// User pressed Ctrl+C or prompt was cancelled — skip silently
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const hasAbout = answers.role || answers.interests || answers.projects;
|
|
298
|
+
const hasPrefs = answers.style || answers.proactivity || answers.tone;
|
|
299
|
+
if (!hasAbout && !hasPrefs)
|
|
300
|
+
return;
|
|
301
|
+
// Ensure vault directories exist
|
|
302
|
+
mkdirSync(SYSTEM_DIR, { recursive: true });
|
|
303
|
+
mkdirSync(PROJECTS_DIR, { recursive: true });
|
|
304
|
+
// ── Generate MEMORY.md ──────────────────────────────────────────
|
|
305
|
+
const memoryLines = [
|
|
306
|
+
'---',
|
|
307
|
+
'type: system-memory',
|
|
308
|
+
'tags:',
|
|
309
|
+
' - system',
|
|
310
|
+
' - memory',
|
|
311
|
+
'---',
|
|
312
|
+
'',
|
|
313
|
+
'# Memory',
|
|
314
|
+
'',
|
|
315
|
+
];
|
|
316
|
+
if (hasAbout) {
|
|
317
|
+
const displayName = ownerName || 'the Owner';
|
|
318
|
+
memoryLines.push(`## About ${displayName}`, '');
|
|
319
|
+
if (answers.role)
|
|
320
|
+
memoryLines.push(`- Role: ${answers.role}`);
|
|
321
|
+
if (answers.interests)
|
|
322
|
+
memoryLines.push(`- Interests: ${answers.interests}`);
|
|
323
|
+
if (answers.projects)
|
|
324
|
+
memoryLines.push(`- Current projects: ${answers.projects}`);
|
|
325
|
+
memoryLines.push('');
|
|
326
|
+
}
|
|
327
|
+
if (hasPrefs) {
|
|
328
|
+
memoryLines.push('## Owner Preferences', '');
|
|
329
|
+
if (answers.style)
|
|
330
|
+
memoryLines.push(`- Communication style: ${answers.style}`);
|
|
331
|
+
if (answers.proactivity)
|
|
332
|
+
memoryLines.push(`- Proactivity: ${answers.proactivity}`);
|
|
333
|
+
if (answers.tone)
|
|
334
|
+
memoryLines.push(`- Tone: ${answers.tone}`);
|
|
335
|
+
memoryLines.push('');
|
|
336
|
+
}
|
|
337
|
+
writeFileSync(MEMORY_FILE, memoryLines.join('\n'));
|
|
338
|
+
console.log(` ${GREEN}✔ Updated MEMORY.md with your preferences${RESET}`);
|
|
339
|
+
// ── Update SOUL.md ──────────────────────────────────────────────
|
|
340
|
+
if (hasPrefs && existsSync(SOUL_FILE)) {
|
|
341
|
+
let soul = readFileSync(SOUL_FILE, 'utf-8');
|
|
342
|
+
if (!soul.includes('## Owner Preferences')) {
|
|
343
|
+
const prefLines = ['', '## Owner Preferences', ''];
|
|
344
|
+
if (answers.style) {
|
|
345
|
+
prefLines.push(`- Communication style: ${answers.style} — ${STYLE_DESCRIPTIONS[answers.style]}`);
|
|
346
|
+
}
|
|
347
|
+
if (answers.proactivity) {
|
|
348
|
+
prefLines.push(`- Proactivity level: ${answers.proactivity} — ${PROACTIVITY_DESCRIPTIONS[answers.proactivity]}`);
|
|
349
|
+
}
|
|
350
|
+
if (answers.tone) {
|
|
351
|
+
prefLines.push(`- Tone: ${answers.tone} — ${TONE_DESCRIPTIONS[answers.tone]}`);
|
|
352
|
+
}
|
|
353
|
+
prefLines.push('');
|
|
354
|
+
soul = soul.trimEnd() + '\n' + prefLines.join('\n');
|
|
355
|
+
writeFileSync(SOUL_FILE, soul);
|
|
356
|
+
console.log(` ${GREEN}✔ Updated SOUL.md with personality settings${RESET}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// ── Create project notes ────────────────────────────────────────
|
|
360
|
+
if (answers.projects) {
|
|
361
|
+
const projectNames = answers.projects
|
|
362
|
+
.split(',')
|
|
363
|
+
.map((p) => p.trim())
|
|
364
|
+
.filter(Boolean);
|
|
365
|
+
let created = 0;
|
|
366
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
367
|
+
for (const name of projectNames) {
|
|
368
|
+
const slug = slugify(name);
|
|
369
|
+
if (!slug)
|
|
370
|
+
continue;
|
|
371
|
+
const filePath = path.join(PROJECTS_DIR, `${slug}.md`);
|
|
372
|
+
if (existsSync(filePath))
|
|
373
|
+
continue;
|
|
374
|
+
const content = [
|
|
375
|
+
'---',
|
|
376
|
+
`created: ${today}`,
|
|
377
|
+
'tags: [project]',
|
|
378
|
+
'---',
|
|
379
|
+
'',
|
|
380
|
+
`# ${name}`,
|
|
381
|
+
'',
|
|
382
|
+
'(Add details about this project)',
|
|
383
|
+
'',
|
|
384
|
+
].join('\n');
|
|
385
|
+
writeFileSync(filePath, content);
|
|
386
|
+
created++;
|
|
387
|
+
}
|
|
388
|
+
if (created > 0) {
|
|
389
|
+
console.log(` ${GREEN}✔ Created ${created} project note${created === 1 ? '' : 's'}${RESET}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ── Main ─────────────────────────────────────────────────────────────
|
|
394
|
+
export async function runSetup() {
|
|
395
|
+
const envPath = path.join(BASE_DIR, '.env');
|
|
396
|
+
const entries = {};
|
|
397
|
+
// Load existing values if .env exists
|
|
398
|
+
if (existsSync(envPath)) {
|
|
399
|
+
const existing = readFileSync(envPath, 'utf-8');
|
|
400
|
+
for (const line of existing.split('\n')) {
|
|
401
|
+
const match = line.match(/^([A-Z_]+)=(.*)$/);
|
|
402
|
+
if (match) {
|
|
403
|
+
entries[match[1]] = match[2];
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// ── Banner ───────────────────────────────────────────────────────
|
|
408
|
+
console.log(BANNER);
|
|
409
|
+
console.log(` ${BOLD}Setup Wizard${RESET}`);
|
|
410
|
+
console.log(` ${DIM}Use arrow keys to navigate, space to toggle, enter to confirm.${RESET}`);
|
|
411
|
+
console.log(` ${DIM}Existing values are preserved as defaults.${RESET}`);
|
|
412
|
+
// ── Prerequisites check ─────────────────────────────────────────
|
|
413
|
+
console.log();
|
|
414
|
+
console.log(` ${DIM}Checking prerequisites...${RESET}`);
|
|
415
|
+
// Node version
|
|
416
|
+
const major = parseInt(process.versions.node.split('.')[0], 10);
|
|
417
|
+
if (major < 20 || major > 24) {
|
|
418
|
+
console.error(`\n ${RED}✗${RESET} Node.js v${process.versions.node} detected — requires 20-24 LTS`);
|
|
419
|
+
console.error(` Fix: nvm install 22\n`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
console.log(` ${GREEN}✓${RESET} Node.js v${process.versions.node}`);
|
|
423
|
+
// Claude Code CLI
|
|
424
|
+
let hasClaude = false;
|
|
425
|
+
try {
|
|
426
|
+
execSync('which claude', { stdio: 'pipe' });
|
|
427
|
+
hasClaude = true;
|
|
428
|
+
}
|
|
429
|
+
catch { /* not found */ }
|
|
430
|
+
if (!hasClaude) {
|
|
431
|
+
console.log(` ${ORANGE}!${RESET} Claude Code CLI not found`);
|
|
432
|
+
const installClaude = await confirm({
|
|
433
|
+
message: 'Claude Code CLI is required. Install it now? (npm install -g @anthropic-ai/claude-code)',
|
|
434
|
+
default: true,
|
|
435
|
+
});
|
|
436
|
+
if (installClaude) {
|
|
437
|
+
console.log(` ${DIM}Installing Claude Code CLI...${RESET}`);
|
|
438
|
+
try {
|
|
439
|
+
execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' });
|
|
440
|
+
console.log(` ${GREEN}✓${RESET} Claude Code CLI installed`);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
console.error(`\n ${RED}✗${RESET} Installation failed. Install manually: npm install -g @anthropic-ai/claude-code\n`);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
console.error(`\n ${RED}✗${RESET} Claude Code CLI is required. Install it: npm install -g @anthropic-ai/claude-code\n`);
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
console.log(` ${GREEN}✓${RESET} Claude Code CLI`);
|
|
454
|
+
}
|
|
455
|
+
// better-sqlite3
|
|
456
|
+
try {
|
|
457
|
+
execSync('node -e "require(\'better-sqlite3\')"', { stdio: 'pipe', timeout: 5000 });
|
|
458
|
+
console.log(` ${GREEN}✓${RESET} better-sqlite3`);
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
console.log(` ${ORANGE}!${RESET} better-sqlite3 native module needs rebuild`);
|
|
462
|
+
try {
|
|
463
|
+
execSync('npm rebuild better-sqlite3', { stdio: 'pipe' });
|
|
464
|
+
console.log(` ${GREEN}✓${RESET} better-sqlite3 rebuilt`);
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
console.log(` ${DIM} (non-fatal — memory search may not work until fixed)${RESET}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
console.log();
|
|
471
|
+
// ── Step 1: Identity ─────────────────────────────────────────────
|
|
472
|
+
sectionHeader('Step 1: Identity');
|
|
473
|
+
entries['ASSISTANT_NAME'] = await input({
|
|
474
|
+
message: 'Assistant name',
|
|
475
|
+
default: entries['ASSISTANT_NAME'] || 'Clementine',
|
|
476
|
+
});
|
|
477
|
+
entries['ASSISTANT_NICKNAME'] = await input({
|
|
478
|
+
message: 'Nickname',
|
|
479
|
+
default: entries['ASSISTANT_NICKNAME'] || 'Clemmy',
|
|
480
|
+
});
|
|
481
|
+
entries['OWNER_NAME'] = await input({
|
|
482
|
+
message: 'Your name',
|
|
483
|
+
default: entries['OWNER_NAME'] || undefined,
|
|
484
|
+
});
|
|
485
|
+
// ── Step 2: Model ────────────────────────────────────────────────
|
|
486
|
+
sectionHeader('Step 2: Model');
|
|
487
|
+
entries['DEFAULT_MODEL_TIER'] = await select({
|
|
488
|
+
message: 'Default model tier',
|
|
489
|
+
default: entries['DEFAULT_MODEL_TIER'] || 'sonnet',
|
|
490
|
+
choices: [
|
|
491
|
+
{ value: 'sonnet', name: 'sonnet — Balanced (recommended)' },
|
|
492
|
+
{ value: 'haiku', name: 'haiku — Fast and affordable' },
|
|
493
|
+
{ value: 'opus', name: 'opus — Most capable' },
|
|
494
|
+
],
|
|
495
|
+
});
|
|
496
|
+
// ── Step 3: Channels ─────────────────────────────────────────────
|
|
497
|
+
sectionHeader('Step 3: Channels');
|
|
498
|
+
const selectedChannels = await checkbox({
|
|
499
|
+
message: 'Which channels do you want to connect?',
|
|
500
|
+
choices: CHANNELS.map((ch) => ({
|
|
501
|
+
value: ch.value,
|
|
502
|
+
name: ch.name,
|
|
503
|
+
checked: ch.credentials.some((c) => !!entries[c.key]),
|
|
504
|
+
})),
|
|
505
|
+
});
|
|
506
|
+
for (const channelValue of selectedChannels) {
|
|
507
|
+
const channel = CHANNELS.find((c) => c.value === channelValue);
|
|
508
|
+
if (!channel)
|
|
509
|
+
continue;
|
|
510
|
+
console.log();
|
|
511
|
+
console.log(` ${BOLD}${channel.name}${RESET}`);
|
|
512
|
+
await collectCredentials(channel.credentials, entries);
|
|
513
|
+
// Set webhook enabled flag
|
|
514
|
+
if (channelValue === 'webhook') {
|
|
515
|
+
entries['WEBHOOK_ENABLED'] = 'true';
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// ── Step 4: Optional features ────────────────────────────────────
|
|
519
|
+
sectionHeader('Step 4: Optional Features');
|
|
520
|
+
const selectedFeatures = await checkbox({
|
|
521
|
+
message: 'Optional features to enable',
|
|
522
|
+
choices: FEATURES.map((f) => ({
|
|
523
|
+
value: f.value,
|
|
524
|
+
name: f.name,
|
|
525
|
+
checked: f.credentials.some((c) => !!entries[c.key]),
|
|
526
|
+
})),
|
|
527
|
+
});
|
|
528
|
+
for (const featureValue of selectedFeatures) {
|
|
529
|
+
const feature = FEATURES.find((f) => f.value === featureValue);
|
|
530
|
+
if (!feature)
|
|
531
|
+
continue;
|
|
532
|
+
console.log();
|
|
533
|
+
console.log(` ${BOLD}${feature.name}${RESET}`);
|
|
534
|
+
await collectCredentials(feature.credentials, entries);
|
|
535
|
+
}
|
|
536
|
+
// ── Step 5: Workspace ──────────────────────────────────────────────
|
|
537
|
+
sectionHeader('Step 5: Workspace');
|
|
538
|
+
console.log(` ${DIM}Point Clementine at parent directories containing your projects.${RESET}`);
|
|
539
|
+
console.log(` ${DIM}She'll auto-discover project roots (git repos, npm packages, etc.)${RESET}`);
|
|
540
|
+
console.log();
|
|
541
|
+
entries['WORKSPACE_DIRS'] = await input({
|
|
542
|
+
message: 'Workspace directories (comma-separated, optional)',
|
|
543
|
+
default: entries['WORKSPACE_DIRS'] || '',
|
|
544
|
+
});
|
|
545
|
+
// ── Step 6: Security ─────────────────────────────────────────────
|
|
546
|
+
sectionHeader('Step 6: Security');
|
|
547
|
+
const allowAll = await confirm({
|
|
548
|
+
message: 'Allow all users (no owner check)?',
|
|
549
|
+
default: entries['ALLOW_ALL_USERS'] === 'true',
|
|
550
|
+
});
|
|
551
|
+
entries['ALLOW_ALL_USERS'] = allowAll ? 'true' : 'false';
|
|
552
|
+
// ── Step 7: About You ─────────────────────────────────────────
|
|
553
|
+
await aboutYouInterview(entries['OWNER_NAME'] || '');
|
|
554
|
+
// ── Write .env ─────────────────────────────────────────────────
|
|
555
|
+
const sections = [
|
|
556
|
+
{ header: 'Assistant Identity', keys: ['ASSISTANT_NAME', 'ASSISTANT_NICKNAME', 'OWNER_NAME'] },
|
|
557
|
+
{ header: 'Model', keys: ['DEFAULT_MODEL_TIER'] },
|
|
558
|
+
{ header: 'Discord', keys: ['DISCORD_TOKEN', 'DISCORD_OWNER_ID', 'DISCORD_WATCHED_CHANNELS'] },
|
|
559
|
+
{ header: 'Slack', keys: ['SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN', 'SLACK_OWNER_USER_ID'] },
|
|
560
|
+
{ header: 'Telegram', keys: ['TELEGRAM_BOT_TOKEN', 'TELEGRAM_OWNER_ID'] },
|
|
561
|
+
{ header: 'WhatsApp (Twilio)', keys: ['TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN', 'WHATSAPP_OWNER_PHONE', 'WHATSAPP_FROM_PHONE', 'WHATSAPP_WEBHOOK_PORT'] },
|
|
562
|
+
{ header: 'Webhook API', keys: ['WEBHOOK_ENABLED', 'WEBHOOK_PORT', 'WEBHOOK_SECRET'] },
|
|
563
|
+
{ header: 'Voice', keys: ['GROQ_API_KEY', 'ELEVENLABS_API_KEY', 'ELEVENLABS_VOICE_ID'] },
|
|
564
|
+
{ header: 'Video', keys: ['GOOGLE_API_KEY'] },
|
|
565
|
+
{ header: 'Outlook (Microsoft Graph)', keys: ['MS_TENANT_ID', 'MS_CLIENT_ID', 'MS_CLIENT_SECRET', 'MS_USER_EMAIL'] },
|
|
566
|
+
{ header: 'Workspace', keys: ['WORKSPACE_DIRS'] },
|
|
567
|
+
{ header: 'Security', keys: ['ALLOW_ALL_USERS'] },
|
|
568
|
+
];
|
|
569
|
+
const lines = [];
|
|
570
|
+
for (const section of sections) {
|
|
571
|
+
const hasValues = section.keys.some((k) => entries[k]);
|
|
572
|
+
if (!hasValues)
|
|
573
|
+
continue;
|
|
574
|
+
lines.push(`# ${section.header}`);
|
|
575
|
+
for (const key of section.keys) {
|
|
576
|
+
if (entries[key] !== undefined) {
|
|
577
|
+
lines.push(`${key}=${entries[key]}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
lines.push('');
|
|
581
|
+
}
|
|
582
|
+
writeFileSync(envPath, lines.join('\n'));
|
|
583
|
+
// ── Summary ────────────────────────────────────────────────────
|
|
584
|
+
console.log();
|
|
585
|
+
console.log(` ${GREEN}${BOLD}✔ Configuration written to ${envPath}${RESET}`);
|
|
586
|
+
console.log();
|
|
587
|
+
console.log(` ${BOLD}Summary${RESET}`);
|
|
588
|
+
console.log(` ${DIM}${'─'.repeat(50)}${RESET}`);
|
|
589
|
+
console.log(` Assistant: ${entries['ASSISTANT_NAME']} (${entries['ASSISTANT_NICKNAME']})`);
|
|
590
|
+
console.log(` Owner: ${entries['OWNER_NAME'] || '(not set)'}`);
|
|
591
|
+
console.log(` Model: ${entries['DEFAULT_MODEL_TIER']}`);
|
|
592
|
+
console.log(` Channels: ${selectedChannels.length > 0 ? selectedChannels.join(', ') : 'none'}`);
|
|
593
|
+
console.log(` Features: ${selectedFeatures.length > 0 ? selectedFeatures.join(', ') : 'none'}`);
|
|
594
|
+
console.log(` All users: ${allowAll ? 'yes' : 'no (owner only)'}`);
|
|
595
|
+
console.log();
|
|
596
|
+
// ── Step 8: Auto-start on login ───────────────────────────────────
|
|
597
|
+
if (process.platform === 'darwin') {
|
|
598
|
+
sectionHeader('Step 8: Auto-Start');
|
|
599
|
+
console.log(` ${DIM}Install a login service so ${entries['ASSISTANT_NAME'] || 'Clementine'} starts${RESET}`);
|
|
600
|
+
console.log(` ${DIM}automatically when you turn on your computer.${RESET}`);
|
|
601
|
+
console.log();
|
|
602
|
+
const installService = await confirm({
|
|
603
|
+
message: 'Start automatically on login? (recommended)',
|
|
604
|
+
default: true,
|
|
605
|
+
});
|
|
606
|
+
if (installService) {
|
|
607
|
+
// Signal to the caller that LaunchAgent should be installed
|
|
608
|
+
writeFileSync(path.join(BASE_DIR, '.install-launchagent'), '');
|
|
609
|
+
console.log(` ${GREEN}✔ Will install login service after first launch${RESET}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
console.log();
|
|
613
|
+
console.log(` ${BOLD}Next steps${RESET}`);
|
|
614
|
+
console.log(` ${DIM}${'─'.repeat(50)}${RESET}`);
|
|
615
|
+
console.log(` ${BOLD}clementine launch${RESET} Start the assistant`);
|
|
616
|
+
console.log(` ${BOLD}clementine doctor${RESET} Verify everything is configured`);
|
|
617
|
+
console.log();
|
|
618
|
+
}
|
|
619
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Cloudflare Tunnel Manager.
|
|
3
|
+
*
|
|
4
|
+
* Manages a cloudflared quick tunnel to expose the dashboard remotely.
|
|
5
|
+
* No Cloudflare account required — uses free quick tunnels that generate
|
|
6
|
+
* a random *.trycloudflare.com URL.
|
|
7
|
+
*
|
|
8
|
+
* Security: The tunnel only points at localhost. Combined with the
|
|
9
|
+
* dashboard's session-cookie auth, remote users must know the access
|
|
10
|
+
* token before they can see anything.
|
|
11
|
+
*/
|
|
12
|
+
import { EventEmitter } from 'node:events';
|
|
13
|
+
export declare class TunnelManager extends EventEmitter {
|
|
14
|
+
private process;
|
|
15
|
+
private _url;
|
|
16
|
+
private port;
|
|
17
|
+
private restartCount;
|
|
18
|
+
private maxRestarts;
|
|
19
|
+
private stopping;
|
|
20
|
+
constructor(port: number);
|
|
21
|
+
/** Check if cloudflared is installed. */
|
|
22
|
+
static isInstalled(): boolean;
|
|
23
|
+
/** Get install instructions for the current platform. */
|
|
24
|
+
static getInstallInstructions(): string;
|
|
25
|
+
/** Start the tunnel. Resolves with the public URL. */
|
|
26
|
+
start(): Promise<string>;
|
|
27
|
+
private _spawn;
|
|
28
|
+
/** Stop the tunnel gracefully. */
|
|
29
|
+
stop(): void;
|
|
30
|
+
/** Get the current public URL (null if not running). */
|
|
31
|
+
getUrl(): string | null;
|
|
32
|
+
/** Whether the tunnel process is alive. */
|
|
33
|
+
isRunning(): boolean;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=tunnel.d.ts.map
|