@markus-global/cli 0.4.11 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/commands/auth.d.ts +3 -0
  2. package/dist/commands/auth.d.ts.map +1 -0
  3. package/dist/commands/auth.js +420 -0
  4. package/dist/commands/auth.js.map +1 -0
  5. package/dist/commands/channel.d.ts +3 -0
  6. package/dist/commands/channel.d.ts.map +1 -0
  7. package/dist/commands/channel.js +259 -0
  8. package/dist/commands/channel.js.map +1 -0
  9. package/dist/commands/doctor.d.ts +9 -0
  10. package/dist/commands/doctor.d.ts.map +1 -0
  11. package/dist/commands/doctor.js +335 -0
  12. package/dist/commands/doctor.js.map +1 -0
  13. package/dist/commands/gateway.d.ts +1 -1
  14. package/dist/commands/gateway.d.ts.map +1 -1
  15. package/dist/commands/gateway.js +75 -139
  16. package/dist/commands/gateway.js.map +1 -1
  17. package/dist/commands/init.d.ts.map +1 -1
  18. package/dist/commands/init.js +6 -2
  19. package/dist/commands/init.js.map +1 -1
  20. package/dist/commands/model.d.ts +11 -0
  21. package/dist/commands/model.d.ts.map +1 -0
  22. package/dist/commands/model.js +485 -0
  23. package/dist/commands/model.js.map +1 -0
  24. package/dist/commands/models.d.ts +3 -0
  25. package/dist/commands/models.d.ts.map +1 -0
  26. package/dist/commands/models.js +43 -0
  27. package/dist/commands/models.js.map +1 -0
  28. package/dist/commands/start.d.ts.map +1 -1
  29. package/dist/commands/start.js +232 -30
  30. package/dist/commands/start.js.map +1 -1
  31. package/dist/index.js +27 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/markus.mjs +5927 -2040
  34. package/dist/utils/browser.d.ts +6 -0
  35. package/dist/utils/browser.d.ts.map +1 -0
  36. package/dist/utils/browser.js +18 -0
  37. package/dist/utils/browser.js.map +1 -0
  38. package/dist/utils/logger.d.ts +41 -0
  39. package/dist/utils/logger.d.ts.map +1 -0
  40. package/dist/utils/logger.js +103 -0
  41. package/dist/utils/logger.js.map +1 -0
  42. package/dist/utils/startupProgress.d.ts +61 -0
  43. package/dist/utils/startupProgress.d.ts.map +1 -0
  44. package/dist/utils/startupProgress.js +320 -0
  45. package/dist/utils/startupProgress.js.map +1 -0
  46. package/dist/web-ui/assets/index-OE34_qh_.css +1 -0
  47. package/dist/web-ui/assets/index-nuQoO_K8.js +63 -0
  48. package/dist/web-ui/index.html +2 -2
  49. package/package.json +1 -1
  50. package/templates/skills/chrome-devtools/SKILL.md +23 -4
  51. package/dist/web-ui/assets/index-Cc0Wqkrw.js +0 -63
  52. package/dist/web-ui/assets/index-d4taGsNz.css +0 -1
@@ -0,0 +1,259 @@
1
+ import * as readline from 'node:readline';
2
+ import { loadConfig, saveConfig } from '@markus/shared';
3
+ const C = { GREEN: '\x1b[32m', RED: '\x1b[31m', YELLOW: '\x1b[33m', CYAN: '\x1b[36m', DIM: '\x1b[2m', RESET: '\x1b[0m', BOLD: '\x1b[1m' };
4
+ const ok = (t, d = '') => console.log(` ${C.GREEN}✓${C.RESET} ${t}${d ? ` ${C.DIM}${d}${C.RESET}` : ''}`);
5
+ const fail = (t, d = '') => console.log(` ${C.RED}✗${C.RESET} ${t}${d ? ` ${C.DIM}${d}${C.RESET}` : ''}`);
6
+ const warn = (t, d = '') => console.log(` ${C.YELLOW}⚠${C.RESET} ${t}${d ? ` ${C.DIM}${d}${C.RESET}` : ''}`);
7
+ const info = (t) => console.log(` ${C.CYAN}→${C.RESET} ${t}`);
8
+ const section = (t) => console.log(`\n${C.BOLD}◆ ${t}${C.RESET}`);
9
+ function createAsk() {
10
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11
+ const ask = (q, def) => new Promise(r => rl.question(`${q}${def ? ` [${def}]` : ''}: `, ans => r(ans.trim() || def || '')));
12
+ return { rl, ask };
13
+ }
14
+ const CHANNELS = [
15
+ {
16
+ id: 'feishu',
17
+ label: '飞书 (Feishu)',
18
+ description: 'Lark/Feishu open platform integration',
19
+ webhookPort: 9000,
20
+ fields: [
21
+ { key: 'appId', label: 'App ID', envKey: 'FEISHU_APP_ID', placeholder: 'cli_xxxxxxxxxxxx' },
22
+ { key: 'appSecret', label: 'App Secret', envKey: 'FEISHU_APP_SECRET', secret: true, placeholder: 'xxxxxxxxxxxxxxxx' },
23
+ { key: 'encryptKey', label: 'Encrypt Key', envKey: 'FEISHU_ENCRYPT_KEY', secret: true, placeholder: 'Optional — for encrypted mode' },
24
+ { key: 'verificationToken', label: 'Verification Token', envKey: 'FEISHU_VERIFICATION_TOKEN', secret: true },
25
+ ],
26
+ isConfigured: cfg => !!(cfg?.appId && cfg?.appSecret),
27
+ },
28
+ {
29
+ id: 'telegram',
30
+ label: 'Telegram',
31
+ description: 'Telegram Bot API integration',
32
+ webhookPort: 8443,
33
+ fields: [
34
+ { key: 'botToken', label: 'Bot Token', envKey: 'TELEGRAM_BOT_TOKEN', secret: true, placeholder: '123456:ABC-DefGhIJKlmno...' },
35
+ ],
36
+ isConfigured: cfg => !!(cfg?.botToken),
37
+ },
38
+ {
39
+ id: 'whatsapp',
40
+ label: 'WhatsApp',
41
+ description: 'WhatsApp Business API (Meta Graph API)',
42
+ webhookPort: 3001,
43
+ fields: [
44
+ { key: 'phoneNumberId', label: 'Phone Number ID', envKey: 'WHATSAPP_PHONE_NUMBER_ID', placeholder: '1234567890' },
45
+ { key: 'accessToken', label: 'Access Token', envKey: 'WHATSAPP_ACCESS_TOKEN', secret: true, placeholder: 'EAAAG...xxxx' },
46
+ { key: 'businessAccountId', label: 'Business Account ID', envKey: 'WHATSAPP_BUSINESS_ACCOUNT_ID', placeholder: '123456789' },
47
+ { key: 'appSecret', label: 'App Secret', envKey: 'WHATSAPP_APP_SECRET', secret: true, placeholder: 'xxxxxxxxxxxxxxxx' },
48
+ ],
49
+ isConfigured: cfg => !!(cfg?.phoneNumberId && cfg?.accessToken),
50
+ },
51
+ {
52
+ id: 'slack',
53
+ label: 'Slack',
54
+ description: 'Slack Bot with Events API webhook',
55
+ webhookPort: 3000,
56
+ fields: [
57
+ { key: 'botToken', label: 'Bot Token (xoxb-)', envKey: 'SLACK_BOT_TOKEN', secret: true, placeholder: 'xoxb-...' },
58
+ { key: 'signingSecret', label: 'Signing Secret', envKey: 'SLACK_SIGNING_SECRET', secret: true, placeholder: 'xxxxxxxxxxxxxxxx' },
59
+ ],
60
+ isConfigured: cfg => !!(cfg?.botToken && cfg?.signingSecret),
61
+ },
62
+ {
63
+ id: 'dingtalk',
64
+ label: '钉钉 (DingTalk)',
65
+ description: 'DingTalk enterprise messaging',
66
+ fields: [
67
+ { key: 'clientId', label: 'Client ID', envKey: 'DINGTALK_CLIENT_ID' },
68
+ { key: 'clientSecret', label: 'Client Secret', envKey: 'DINGTALK_CLIENT_SECRET', secret: true },
69
+ ],
70
+ isConfigured: cfg => !!(cfg?.clientId && cfg?.clientSecret),
71
+ },
72
+ {
73
+ id: 'wecom',
74
+ label: '企业微信 (WeCom)',
75
+ description: 'WeCom (WeChat Work) enterprise messaging',
76
+ fields: [
77
+ { key: 'corpId', label: 'Corp ID', envKey: 'WECOM_CORP_ID' },
78
+ { key: 'agentId', label: 'Agent ID', envKey: 'WECOM_AGENT_ID' },
79
+ { key: 'corpSecret', label: 'Corp Secret', envKey: 'WECOM_CORP_SECRET', secret: true },
80
+ ],
81
+ isConfigured: cfg => !!(cfg?.corpId && cfg?.agentId && cfg?.corpSecret),
82
+ },
83
+ ];
84
+ // ─── Interactive channel setup ────────────────────────────────────────────────
85
+ async function interactiveSetup(channelId) {
86
+ const { rl, ask } = createAsk();
87
+ const config = loadConfig(undefined);
88
+ const integrations = config.integrations ?? {};
89
+ if (channelId) {
90
+ const ch = CHANNELS.find(c => c.id === channelId);
91
+ if (!ch) {
92
+ console.log(`Unknown channel: ${channelId}`);
93
+ console.log(`Available: ${CHANNELS.map(c => c.id).join(', ')}`);
94
+ rl.close();
95
+ return;
96
+ }
97
+ await setupChannel(ch, ask, config, integrations);
98
+ rl.close();
99
+ return;
100
+ }
101
+ // Show channel status
102
+ console.log(`\n${C.BOLD}╔══════════════════════════════════════════════════════════════╗
103
+ ║ ${C.CYAN}Markus Channel Setup${C.RESET}${C.BOLD} ║
104
+ ╚══════════════════════════════════════════════════════════════╝${C.RESET}\n`);
105
+ section('Communication Channels');
106
+ for (const ch of CHANNELS) {
107
+ const chConfig = integrations[ch.id] ?? {};
108
+ const configured = ch.isConfigured(chConfig);
109
+ const port = ch.webhookPort ? ` port ${ch.webhookPort}` : '';
110
+ if (configured) {
111
+ ok(ch.label, `${C.DIM}${ch.description}${C.RESET}`);
112
+ if (ch.webhookPort)
113
+ info(`Webhook: http://localhost:${ch.webhookPort}/webhook/${ch.id}`);
114
+ }
115
+ else {
116
+ fail(ch.label, `${C.DIM}${ch.description}${C.RESET}`);
117
+ }
118
+ }
119
+ console.log(`\n ${C.BOLD}[1-${CHANNELS.length}]${C.RESET} Configure a channel`);
120
+ console.log(` ${C.BOLD}[A]${C.RESET} Configure all channels`);
121
+ console.log(` ${C.BOLD}[Q]${C.RESET} Quit\n`);
122
+ const choice = await ask('Select option', '1');
123
+ if (choice.toLowerCase() === 'q') {
124
+ rl.close();
125
+ return;
126
+ }
127
+ if (choice.toLowerCase() === 'a') {
128
+ for (const ch of CHANNELS) {
129
+ await setupChannel(ch, ask, config, integrations);
130
+ }
131
+ }
132
+ else {
133
+ const idx = parseInt(choice, 10) - 1;
134
+ if (!isNaN(idx) && idx >= 0 && idx < CHANNELS.length) {
135
+ await setupChannel(CHANNELS[idx], ask, config, integrations);
136
+ }
137
+ }
138
+ rl.close();
139
+ }
140
+ async function setupChannel(ch, ask, config, integrations) {
141
+ console.log(`\n${C.BOLD}── ${ch.label} Setup${C.RESET}\n`);
142
+ if (ch.webhookPort) {
143
+ console.log(` ${C.DIM}Webhook URL: http://localhost:${ch.webhookPort}/webhook/${ch.id}${C.RESET}`);
144
+ console.log(` ${C.DIM}Configure this URL in your ${ch.label} developer portal → Webhooks${C.RESET}\n`);
145
+ }
146
+ const chConfig = integrations[ch.id] ?? {};
147
+ const fieldValues = { ...chConfig };
148
+ for (const field of ch.fields) {
149
+ // Check env first
150
+ if (field.envKey && process.env[field.envKey] && !chConfig[field.key]) {
151
+ info(`${field.label}: found in ${field.envKey} — using environment`);
152
+ fieldValues[field.key] = process.env[field.envKey];
153
+ continue;
154
+ }
155
+ const current = chConfig[field.key] ?? '';
156
+ const envNote = field.envKey ? ` (env: ${field.envKey})` : '';
157
+ const input = await ask(`${field.label}${envNote}`, current || field.placeholder);
158
+ if (input)
159
+ fieldValues[field.key] = input;
160
+ }
161
+ const newIntegrations = {
162
+ ...integrations,
163
+ [ch.id]: fieldValues,
164
+ };
165
+ saveConfig({ ...config, integrations: newIntegrations });
166
+ console.log(`\n ${C.GREEN}✓${C.RESET} ${ch.label} saved!`);
167
+ if (ch.webhookPort) {
168
+ console.log(` ${C.DIM}Restart markus server for changes to take effect${C.RESET}\n`);
169
+ }
170
+ }
171
+ // ─── List status ───────────────────────────────────────────────────────────────
172
+ async function listStatus() {
173
+ const config = loadConfig(undefined);
174
+ const integrations = config.integrations ?? {};
175
+ console.log(`\n${C.BOLD}╔══════════════════════════════════════════════════════════════╗
176
+ ║ ${C.CYAN}Markus Channel Status${C.RESET}${C.BOLD} ║
177
+ ╚══════════════════════════════════════════════════════════════╝${C.RESET}\n`);
178
+ for (const ch of CHANNELS) {
179
+ const chConfig = integrations[ch.id] ?? {};
180
+ const configured = ch.isConfigured(chConfig);
181
+ const port = ch.webhookPort ? ` port ${ch.webhookPort}` : '';
182
+ const webhooks = ch.webhookPort ? ` → http://localhost:${ch.webhookPort}/webhook/${ch.id}` : '';
183
+ if (configured) {
184
+ ok(`${ch.label}${C.DIM}${port}${webhooks}${C.RESET}`);
185
+ // Show which fields are set
186
+ const fieldLabels = ch.fields.filter(f => chConfig[f.key]).map(f => f.label.toLowerCase());
187
+ info(`${fieldLabels.join(', ')} configured`);
188
+ }
189
+ else {
190
+ fail(`${ch.label}`, `${C.DIM}not configured — run: markus channel setup ${ch.id}${C.RESET}`);
191
+ }
192
+ }
193
+ console.log('');
194
+ }
195
+ // ─── Test webhook ─────────────────────────────────────────────────────────────
196
+ async function testWebhook(channelId) {
197
+ const config = loadConfig(undefined);
198
+ const chConfig = config.integrations?.[channelId];
199
+ if (!chConfig) {
200
+ fail(`Channel ${channelId} not configured`);
201
+ return;
202
+ }
203
+ const ch = CHANNELS.find(c => c.id === channelId);
204
+ if (!ch?.webhookPort) {
205
+ warn(`No webhook for ${ch?.label ?? channelId}`);
206
+ return;
207
+ }
208
+ const port = ch.webhookPort;
209
+ const path = `/webhook/${channelId}`;
210
+ try {
211
+ const res = await fetch(`http://localhost:${port}${path}`, {
212
+ method: 'GET',
213
+ signal: AbortSignal.timeout(3000),
214
+ });
215
+ ok(`${ch.label} webhook reachable`, `HTTP ${res.status}`);
216
+ }
217
+ catch (e) {
218
+ fail(`${ch.label} webhook not reachable`, `http://localhost:${port}${path}`);
219
+ info('Start server with markus start or check if channel is enabled');
220
+ }
221
+ }
222
+ // ─── CLI registration ─────────────────────────────────────────────────────────
223
+ export function registerChannelCommand(program) {
224
+ const channel = program
225
+ .command('channel')
226
+ .description('Configure and manage communication channels (飞书/Telegram/WhatsApp/Slack/钉钉/企业微信)');
227
+ channel
228
+ .command('setup [channel]')
229
+ .description('Interactive channel setup (or specify channel id)')
230
+ .option('--non-interactive', 'Use environment variables only')
231
+ .action(async (channelId, opts) => {
232
+ if (opts.nonInteractive) {
233
+ const config = loadConfig(undefined);
234
+ console.log(JSON.stringify(config.integrations ?? {}, null, 2));
235
+ return;
236
+ }
237
+ await interactiveSetup(channelId);
238
+ });
239
+ channel
240
+ .command('status')
241
+ .description('Show channel configuration status')
242
+ .action(async () => { await listStatus(); });
243
+ channel
244
+ .command('test <channel>')
245
+ .description('Test webhook connectivity for a channel')
246
+ .action(async (channelId) => { await testWebhook(channelId); });
247
+ channel
248
+ .command('list')
249
+ .description('List all supported channels')
250
+ .action(() => {
251
+ console.log(`\n${C.BOLD}Supported Channels:${C.RESET}\n`);
252
+ for (const ch of CHANNELS) {
253
+ const webhooks = ch.webhookPort ? ` ${C.CYAN}webhook:${ch.webhookPort}${C.RESET}` : '';
254
+ console.log(` ${C.BOLD}${ch.label.padEnd(14)}${C.RESET} ${ch.id.padEnd(12)} ${C.DIM}${ch.description}${webhooks}${C.RESET}`);
255
+ }
256
+ console.log(`\n ${C.DIM}Run: markus channel setup [channel-id]${C.RESET}\n`);
257
+ });
258
+ }
259
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/commands/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Command } from 'commander';
2
+ interface DoctorOptions {
3
+ fix?: boolean;
4
+ verbose?: boolean;
5
+ }
6
+ export declare function runDoctor(options?: DoctorOptions): Promise<void>;
7
+ export declare function registerDoctorCommand(program: Command): void;
8
+ export {};
9
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmHzC,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,SAAS,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6N1E;AAUD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QASrD"}
@@ -0,0 +1,335 @@
1
+ import { loadConfig } from '@markus/shared';
2
+ // ─── Color constants ──────────────────────────────────────────────────────────
3
+ const C = {
4
+ GREEN: '\x1b[32m',
5
+ RED: '\x1b[31m',
6
+ YELLOW: '\x1b[33m',
7
+ CYAN: '\x1b[36m',
8
+ DIM: '\x1b[2m',
9
+ RESET: '\x1b[0m',
10
+ BOLD: '\x1b[1m',
11
+ };
12
+ function checkOk(text, detail = '') {
13
+ console.log(` ${C.GREEN}✓${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
14
+ }
15
+ function checkFail(text, detail = '') {
16
+ console.log(` ${C.RED}✗${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
17
+ }
18
+ function checkWarn(text, detail = '') {
19
+ console.log(` ${C.YELLOW}⚠${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
20
+ }
21
+ function checkInfo(text) {
22
+ console.log(` ${C.CYAN}→${C.RESET} ${text}`);
23
+ }
24
+ function section(label) {
25
+ console.log(`\n${C.BOLD}◆ ${label}${C.RESET}`);
26
+ }
27
+ // ─── API validation helper ─────────────────────────────────────────────────────
28
+ const PLACEHOLDER_PATTERNS = ['***', 'your-', 'dummy', 'fake', 'test-key', 'replace-me'];
29
+ function isPlaceholder(key) {
30
+ return PLACEHOLDER_PATTERNS.some(p => key.toLowerCase().includes(p)) || key.length < 8;
31
+ }
32
+ const PROVIDERS = [
33
+ { id: 'anthropic', label: 'Anthropic', envKey: 'ANTHROPIC_API_KEY', baseUrl: 'https://api.anthropic.com/v1', defaultModel: 'claude-opus-4-6' },
34
+ { id: 'openai', label: 'OpenAI', envKey: 'OPENAI_API_KEY', baseUrl: 'https://api.openai.com/v1', defaultModel: 'gpt-5.4' },
35
+ { id: 'google', label: 'Google Gemini', envKey: 'GOOGLE_API_KEY', baseUrl: 'https://generativelanguage.googleapis.com/v1beta', defaultModel: 'gemini-3-1-pro' },
36
+ { id: 'minimax', label: 'MiniMax', envKey: 'MINIMAX_API_KEY', baseUrl: 'https://api.minimax.io/v1', defaultModel: 'MiniMax-M2.7' },
37
+ { id: 'siliconflow', label: 'SiliconFlow', envKey: 'SILICONFLOW_API_KEY', baseUrl: 'https://api.siliconflow.cn/v1', defaultModel: 'Qwen/Qwen3.5-35B-A3B' },
38
+ { id: 'openrouter', label: 'OpenRouter', envKey: 'OPENROUTER_API_KEY', baseUrl: 'https://openrouter.ai/api/v1', defaultModel: 'xiaomi/mimo-v2-pro:free' },
39
+ { id: 'zai', label: 'ZAI', envKey: 'ZAI_API_KEY', baseUrl: 'https://api.z.ai/api/paas/v4', defaultModel: 'glm-5.1' },
40
+ { id: 'deepseek', label: 'DeepSeek', envKey: 'DEEPSEEK_API_KEY', baseUrl: 'https://api.deepseek.com/v1', defaultModel: 'deepseek-chat' },
41
+ ];
42
+ async function validateProviderKey(id, apiKey, model, baseUrl) {
43
+ const pdef = PROVIDERS.find(p => p.id === id);
44
+ if (!pdef)
45
+ return { ok: false, error: 'Unknown provider' };
46
+ const url = baseUrl ?? pdef.baseUrl ?? '';
47
+ if (id === 'anthropic') {
48
+ const msgUrl = `${url}/v1/messages`.replace('/v1/v1/', '/v1/');
49
+ try {
50
+ const res = await fetch(msgUrl, {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json',
54
+ 'x-api-key': apiKey,
55
+ 'anthropic-version': '2023-06-01',
56
+ 'anthropic-dangerous-direct-browser-access': 'true',
57
+ },
58
+ body: JSON.stringify({
59
+ model: model || pdef.defaultModel,
60
+ max_tokens: 5,
61
+ messages: [{ role: 'user', content: 'hi' }],
62
+ }),
63
+ });
64
+ if (res.ok)
65
+ return { ok: true };
66
+ if (res.status === 401 || res.status === 403)
67
+ return { ok: false, error: 'Invalid API key' };
68
+ if (res.status === 429)
69
+ return { ok: false, error: 'Rate limited' };
70
+ return { ok: false, error: `HTTP ${res.status}` };
71
+ }
72
+ catch (e) {
73
+ return { ok: false, error: `Network error: ${e instanceof Error ? e.message : String(e)}` };
74
+ }
75
+ }
76
+ const chatUrl = `${url}/chat/completions`.replace('/v1/v1/', '/v1/');
77
+ try {
78
+ const res = await fetch(chatUrl, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
81
+ body: JSON.stringify({
82
+ model: model || pdef.defaultModel,
83
+ messages: [{ role: 'user', content: 'hi' }],
84
+ max_tokens: 5,
85
+ }),
86
+ });
87
+ if (res.ok)
88
+ return { ok: true };
89
+ if (res.status === 401 || res.status === 403)
90
+ return { ok: false, error: 'Invalid API key' };
91
+ if (res.status === 429)
92
+ return { ok: false, error: 'Rate limited / quota exceeded' };
93
+ return { ok: false, error: `HTTP ${res.status}` };
94
+ }
95
+ catch (e) {
96
+ return { ok: false, error: `Network error: ${e instanceof Error ? e.message : String(e)}` };
97
+ }
98
+ }
99
+ export async function runDoctor(options = {}) {
100
+ const { fix = false } = options;
101
+ const issues = [];
102
+ const manualIssues = [];
103
+ let fixedCount = 0;
104
+ console.log(`\n${C.BOLD}${C.CYAN}╔══════════════════════════════════════════════════════╗
105
+ ║ 🩺 Markus Doctor ║
106
+ ╚══════════════════════════════════════════════════════╝${C.RESET}\n`);
107
+ // ── 1. Node.js version ──────────────────────────────────────────────────
108
+ section('Runtime');
109
+ const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
110
+ if (nodeMajor >= 20) {
111
+ checkOk(`Node.js ${process.versions.node}`);
112
+ }
113
+ else if (nodeMajor >= 18) {
114
+ checkOk(`Node.js ${process.versions.node}`);
115
+ checkWarn('Node.js 20+ recommended for better performance');
116
+ }
117
+ else {
118
+ checkFail(`Node.js ${process.versions.node}`, '(18+ required, 20+ recommended)');
119
+ manualIssues.push('Upgrade Node.js to 18 or higher');
120
+ }
121
+ // ── 2. Config file ──────────────────────────────────────────────────────
122
+ section('Configuration');
123
+ const configPath = await getDefaultConfigPath();
124
+ const fs = await import('node:fs');
125
+ if (fs.existsSync(configPath)) {
126
+ checkOk(`Config file: ${configPath}`);
127
+ }
128
+ else {
129
+ checkFail(`Config file not found: ${configPath}`);
130
+ issues.push('Run markus model or markus init to create config');
131
+ }
132
+ // ── 3. LLM Provider keys ────────────────────────────────────────────────
133
+ section('LLM Providers');
134
+ const config = loadConfig(undefined);
135
+ const providers = config.llm?.providers ?? {};
136
+ const envProviders = [];
137
+ for (const p of PROVIDERS) {
138
+ const envKey = process.env[p.envKey];
139
+ if (envKey)
140
+ envProviders.push({ id: p.id, label: p.label, envKey: p.envKey, key: envKey });
141
+ }
142
+ const configuredProviders = Object.entries(providers).filter(([, cfg]) => {
143
+ const key = cfg?.apiKey ?? '';
144
+ return key && !isPlaceholder(key);
145
+ });
146
+ if (envProviders.length === 0 && configuredProviders.length === 0) {
147
+ checkFail('No LLM API keys configured');
148
+ manualIssues.push('Run: markus model');
149
+ }
150
+ else {
151
+ // Env-based keys
152
+ for (const ep of envProviders) {
153
+ const pdef = PROVIDERS.find(p => p.id === ep.id);
154
+ if (isPlaceholder(ep.key)) {
155
+ checkFail(`${ep.label} (${ep.envKey})`, 'placeholder — not a real key');
156
+ }
157
+ else {
158
+ checkOk(`${ep.label} (${ep.envKey})`, 'found in environment');
159
+ }
160
+ }
161
+ // Config-based keys
162
+ for (const [id, cfg] of configuredProviders) {
163
+ const key = cfg.apiKey ?? '';
164
+ const pdef = PROVIDERS.find(p => p.id === id);
165
+ if (pdef && !isPlaceholder(key)) {
166
+ checkOk(`${pdef.label}`, `configured${cfg.model ? ` — ${cfg.model}` : ''}`);
167
+ }
168
+ }
169
+ }
170
+ // ── 4. API key validation ───────────────────────────────────────────────
171
+ section('LLM API Validation');
172
+ const keysToValidate = [];
173
+ for (const ep of envProviders) {
174
+ if (!isPlaceholder(ep.key)) {
175
+ const pdef = PROVIDERS.find(p => p.id === ep.id);
176
+ keysToValidate.push({ id: ep.id, key: ep.key, model: pdef?.defaultModel ?? '', baseUrl: pdef?.baseUrl });
177
+ }
178
+ }
179
+ for (const [id, cfg] of configuredProviders) {
180
+ const key = cfg.apiKey ?? '';
181
+ if (!isPlaceholder(key)) {
182
+ const pdef = PROVIDERS.find(p => p.id === id);
183
+ keysToValidate.push({
184
+ id,
185
+ key,
186
+ model: cfg.model ?? pdef?.defaultModel ?? '',
187
+ baseUrl: cfg.baseUrl ?? pdef?.baseUrl,
188
+ });
189
+ }
190
+ }
191
+ if (keysToValidate.length === 0) {
192
+ checkWarn('No valid API keys to validate');
193
+ manualIssues.push('Configure API keys with: markus model');
194
+ }
195
+ else {
196
+ let allOk = true;
197
+ await Promise.all(keysToValidate.map(async ({ id, key, model, baseUrl }) => {
198
+ const result = await validateProviderKey(id, key, model, baseUrl);
199
+ const pdef = PROVIDERS.find(p => p.id === id);
200
+ const label = pdef?.label ?? id;
201
+ if (result.ok) {
202
+ checkOk(`${label} API key`, '✓ API call succeeded');
203
+ }
204
+ else {
205
+ checkFail(`${label} API key`, `✗ ${result.error}`);
206
+ issues.push(`Fix ${label} API key: ${result.error}`);
207
+ allOk = false;
208
+ }
209
+ }));
210
+ if (allOk) {
211
+ checkOk('All providers validated');
212
+ }
213
+ }
214
+ // ── 5. Storage ────────────────────────────────────────────────────────────
215
+ section('Storage');
216
+ const { homedir } = await import('node:os');
217
+ const storageDir = `${homedir()}/.markus`;
218
+ const dataFile = `${storageDir}/data.db`;
219
+ try {
220
+ if (!fs.existsSync(storageDir)) {
221
+ if (fix) {
222
+ fs.mkdirSync(storageDir, { recursive: true });
223
+ checkOk(`Created storage directory: ${storageDir}`);
224
+ fixedCount++;
225
+ }
226
+ else {
227
+ checkWarn(`Storage directory not found: ${storageDir}`);
228
+ issues.push(`Create ${storageDir}`);
229
+ }
230
+ }
231
+ else if (!fs.existsSync(dataFile)) {
232
+ checkWarn('Storage directory exists but database not initialized');
233
+ checkInfo('Database will be created on first run');
234
+ }
235
+ else {
236
+ checkOk(`Storage: ${storageDir}`);
237
+ const stat = fs.statSync(dataFile);
238
+ checkOk(`Database: ${dataFile}`, `${(stat.size / 1024).toFixed(1)} KB`);
239
+ }
240
+ }
241
+ catch (e) {
242
+ checkFail(`Storage check failed: ${e}`);
243
+ }
244
+ // ── 6. Skills directory ─────────────────────────────────────────────────
245
+ section('Skills');
246
+ const skillsDir = `${homedir()}/.markus/skills`;
247
+ if (fs.existsSync(skillsDir)) {
248
+ try {
249
+ const entries = fs.readdirSync(skillsDir);
250
+ const skills = entries.filter(e => !e.startsWith('.'));
251
+ checkOk(`Skills directory: ${skillsDir}`, `${skills.length} skill(s) installed`);
252
+ }
253
+ catch {
254
+ checkFail('Skills directory not readable');
255
+ }
256
+ }
257
+ else {
258
+ checkWarn('Skills directory not found', `${skillsDir} — run: markus skill install <name>`);
259
+ }
260
+ // ── 7. Network connectivity ─────────────────────────────────────────────
261
+ section('Network');
262
+ try {
263
+ const res = await fetch('https://api.anthropic.com/.well-known/ready', {
264
+ method: 'HEAD',
265
+ signal: AbortSignal.timeout(5000),
266
+ });
267
+ checkOk('Network connectivity', 'outbound HTTPS works');
268
+ }
269
+ catch {
270
+ checkWarn('Network connectivity', 'could not reach external APIs — check firewall/proxy');
271
+ issues.push('Verify network connectivity');
272
+ }
273
+ // ── 8. Config schema validation ─────────────────────────────────────────
274
+ section('Config Schema');
275
+ let schemaIssues = 0;
276
+ if (config.llm?.providers) {
277
+ for (const [id, cfg] of Object.entries(config.llm.providers)) {
278
+ if (cfg.apiKey && isPlaceholder(cfg.apiKey)) {
279
+ checkWarn(`${id} API key is a placeholder`, '"***" will not work — replace with real key');
280
+ schemaIssues++;
281
+ }
282
+ if (cfg.apiKey2 && isPlaceholder(cfg.apiKey2)) {
283
+ checkWarn(`${id} pool key (apiKey2) is a placeholder`);
284
+ schemaIssues++;
285
+ }
286
+ }
287
+ }
288
+ if (schemaIssues === 0) {
289
+ checkOk('Config schema valid');
290
+ }
291
+ // ── Summary ──────────────────────────────────────────────────────────────
292
+ console.log(`\n${C.BOLD}◆ Summary${C.RESET}\n`);
293
+ const totalIssues = issues.length + manualIssues.length;
294
+ if (totalIssues === 0) {
295
+ console.log(` ${C.GREEN}✓${C.RESET} ${C.BOLD}All checks passed!${C.RESET} Markus is ready to run.\n`);
296
+ console.log(` ${C.DIM}Start with:${C.RESET} ${C.CYAN}markus start${C.RESET}\n`);
297
+ }
298
+ else {
299
+ if (issues.length > 0) {
300
+ console.log(` ${C.RED}Issues (auto-fixable with --fix):${C.RESET}`);
301
+ for (const issue of issues) {
302
+ checkInfo(issue);
303
+ }
304
+ console.log('');
305
+ }
306
+ if (manualIssues.length > 0) {
307
+ console.log(` ${C.YELLOW}Manual action required:${C.RESET}`);
308
+ for (const issue of manualIssues) {
309
+ checkInfo(issue);
310
+ }
311
+ console.log('');
312
+ }
313
+ console.log(` ${C.DIM}${totalIssues} issue(s) found. Run ${C.CYAN}markus doctor --fix${C.DIM} to auto-fix what can be fixed.${C.RESET}\n`);
314
+ }
315
+ if (fix && fixedCount > 0) {
316
+ console.log(` ${C.GREEN}✓${C.RESET} Fixed ${fixedCount} issue(s).\n`);
317
+ }
318
+ }
319
+ async function getDefaultConfigPath() {
320
+ const { homedir } = await import('node:os');
321
+ const { join } = await import('node:path');
322
+ return join(homedir(), '.markus', 'markus.json');
323
+ }
324
+ // ─── CLI registration ────────────────────────────────────────────────────────
325
+ export function registerDoctorCommand(program) {
326
+ program
327
+ .command('doctor')
328
+ .description('Diagnose Markus configuration issues and environment health')
329
+ .option('--fix', 'Attempt to automatically fix issues')
330
+ .option('--verbose', 'Show detailed output')
331
+ .action(async (opts) => {
332
+ await runDoctor({ fix: opts.fix, verbose: opts.verbose });
333
+ });
334
+ }
335
+ //# sourceMappingURL=doctor.js.map