@nordsym/apiclaw 2.1.0 → 2.2.1

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 (185) hide show
  1. package/README.md +15 -2
  2. package/dist/bin-http.js +0 -0
  3. package/dist/bin.bundled.js +79288 -0
  4. package/dist/funnel-client.d.ts +24 -0
  5. package/dist/funnel-client.d.ts.map +1 -0
  6. package/dist/funnel-client.js +131 -0
  7. package/dist/funnel-client.js.map +1 -0
  8. package/dist/funnel.test.d.ts +2 -0
  9. package/dist/funnel.test.d.ts.map +1 -0
  10. package/dist/funnel.test.js +145 -0
  11. package/dist/funnel.test.js.map +1 -0
  12. package/dist/gateway-client.d.ts.map +1 -1
  13. package/dist/gateway-client.js +24 -2
  14. package/dist/gateway-client.js.map +1 -1
  15. package/dist/index.bundled.js +61263 -0
  16. package/dist/index.js +161 -74
  17. package/dist/index.js.map +1 -1
  18. package/dist/postinstall.d.ts +0 -5
  19. package/dist/postinstall.d.ts.map +1 -1
  20. package/dist/postinstall.js +24 -3
  21. package/dist/postinstall.js.map +1 -1
  22. package/dist/registration-guard.d.ts +29 -0
  23. package/dist/registration-guard.d.ts.map +1 -0
  24. package/dist/registration-guard.js +87 -0
  25. package/dist/registration-guard.js.map +1 -0
  26. package/package.json +7 -2
  27. package/.claude/settings.local.json +0 -9
  28. package/.env.prod +0 -1
  29. package/apiclaw-README.md +0 -494
  30. package/convex/_generated/api.d.ts +0 -137
  31. package/convex/_generated/api.js +0 -23
  32. package/convex/_generated/dataModel.d.ts +0 -60
  33. package/convex/_generated/server.d.ts +0 -143
  34. package/convex/_generated/server.js +0 -93
  35. package/convex/adminActivate.ts +0 -53
  36. package/convex/adminStats.ts +0 -306
  37. package/convex/agents.ts +0 -939
  38. package/convex/analytics.ts +0 -187
  39. package/convex/apiKeys.ts +0 -220
  40. package/convex/backfillAnalytics.ts +0 -272
  41. package/convex/backfillSearchLogs.ts +0 -35
  42. package/convex/billing.ts +0 -834
  43. package/convex/capabilities.ts +0 -157
  44. package/convex/chains.ts +0 -1318
  45. package/convex/credits.ts +0 -211
  46. package/convex/crons.ts +0 -50
  47. package/convex/debugFilestackLogs.ts +0 -16
  48. package/convex/debugGetToken.ts +0 -18
  49. package/convex/directCall.ts +0 -713
  50. package/convex/earnProgress.ts +0 -753
  51. package/convex/email.ts +0 -329
  52. package/convex/feedback.ts +0 -265
  53. package/convex/http.ts +0 -3430
  54. package/convex/inbound.ts +0 -32
  55. package/convex/logs.ts +0 -701
  56. package/convex/migrateFilestack.ts +0 -81
  57. package/convex/migratePartnersProd.ts +0 -174
  58. package/convex/migratePratham.ts +0 -126
  59. package/convex/migrateProviderWorkspaces.ts +0 -175
  60. package/convex/mou.ts +0 -91
  61. package/convex/providerKeys.ts +0 -289
  62. package/convex/providers.ts +0 -1135
  63. package/convex/purchases.ts +0 -183
  64. package/convex/ratelimit.ts +0 -104
  65. package/convex/schema.ts +0 -869
  66. package/convex/searchLogs.ts +0 -265
  67. package/convex/seedAPILayerAPIs.ts +0 -191
  68. package/convex/seedDirectCallConfigs.ts +0 -336
  69. package/convex/seedPratham.ts +0 -149
  70. package/convex/spendAlerts.ts +0 -442
  71. package/convex/stripeActions.ts +0 -607
  72. package/convex/teams.ts +0 -243
  73. package/convex/telemetry.ts +0 -81
  74. package/convex/tsconfig.json +0 -25
  75. package/convex/updateAPIStatus.ts +0 -44
  76. package/convex/usage.ts +0 -260
  77. package/convex/usageReports.ts +0 -357
  78. package/convex/waitlist.ts +0 -55
  79. package/convex/webhooks.ts +0 -494
  80. package/convex/workspaceSettings.ts +0 -143
  81. package/convex/workspaces.ts +0 -1331
  82. package/convex.json +0 -3
  83. package/direct-test.mjs +0 -51
  84. package/email-templates/filestack-provider-outreach.html +0 -162
  85. package/email-templates/partnership-template.html +0 -116
  86. package/email-templates/pratham-draft-preview.txt +0 -57
  87. package/email-templates/pratham-partnership-draft.html +0 -141
  88. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  89. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  90. package/reports/pipeline/acquire_apisguru.json +0 -17
  91. package/reports/pipeline/capabilities.json +0 -38
  92. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  93. package/reports/pipeline/discover_github.json +0 -25
  94. package/reports/pipeline/discover_github_repos.json +0 -49
  95. package/reports/pipeline/discover_swaggerhub.json +0 -24
  96. package/reports/pipeline/discover_well_known.json +0 -23
  97. package/reports/pipeline/fetch_specs.json +0 -19
  98. package/reports/pipeline/generate_providers.json +0 -14
  99. package/reports/pipeline/match_registry.json +0 -11
  100. package/reports/pipeline/parse_specs.json +0 -17
  101. package/reports/pipeline/promote_candidates.json +0 -34
  102. package/reports/pipeline/validate.json +0 -30
  103. package/reports/pipeline/validate_smoke_details.json +0 -3835
  104. package/reports/session-report-2026-04-05.html +0 -433
  105. package/seed-apis-direct.mjs +0 -106
  106. package/src/access-control.ts +0 -174
  107. package/src/adapters/base.ts +0 -364
  108. package/src/adapters/claude-desktop.ts +0 -41
  109. package/src/adapters/cline.ts +0 -88
  110. package/src/adapters/continue.ts +0 -91
  111. package/src/adapters/cursor.ts +0 -43
  112. package/src/adapters/custom.ts +0 -188
  113. package/src/adapters/detect.ts +0 -202
  114. package/src/adapters/index.ts +0 -47
  115. package/src/adapters/windsurf.ts +0 -44
  116. package/src/bin-http.ts +0 -45
  117. package/src/bin.ts +0 -34
  118. package/src/capability-router.ts +0 -331
  119. package/src/chainExecutor.ts +0 -730
  120. package/src/chainResolver.test.ts +0 -246
  121. package/src/chainResolver.ts +0 -658
  122. package/src/cli/commands/demo.ts +0 -109
  123. package/src/cli/commands/doctor.ts +0 -435
  124. package/src/cli/commands/index.ts +0 -9
  125. package/src/cli/commands/login.ts +0 -203
  126. package/src/cli/commands/mcp-install.ts +0 -373
  127. package/src/cli/commands/restore.ts +0 -333
  128. package/src/cli/commands/setup.ts +0 -297
  129. package/src/cli/commands/uninstall.ts +0 -240
  130. package/src/cli/index.ts +0 -148
  131. package/src/cli.ts +0 -370
  132. package/src/confirmation.ts +0 -296
  133. package/src/credentials.ts +0 -455
  134. package/src/credits.ts +0 -329
  135. package/src/crypto.ts +0 -75
  136. package/src/discovery.ts +0 -568
  137. package/src/enterprise/env.ts +0 -156
  138. package/src/enterprise/index.ts +0 -7
  139. package/src/enterprise/script-generator.ts +0 -481
  140. package/src/execute-dynamic.ts +0 -617
  141. package/src/execute.ts +0 -2386
  142. package/src/gateway-client.ts +0 -192
  143. package/src/hivr-whitelist.ts +0 -110
  144. package/src/http-api.ts +0 -286
  145. package/src/http-server-minimal.ts +0 -154
  146. package/src/index.ts +0 -2611
  147. package/src/intelligent-gateway.ts +0 -339
  148. package/src/mcp-analytics.ts +0 -156
  149. package/src/metered.ts +0 -149
  150. package/src/open-apis-generated.ts +0 -157
  151. package/src/open-apis.ts +0 -558
  152. package/src/postinstall.ts +0 -18
  153. package/src/product-whitelist.ts +0 -246
  154. package/src/proxy.ts +0 -36
  155. package/src/session.ts +0 -129
  156. package/src/stripe.ts +0 -497
  157. package/src/telemetry.ts +0 -71
  158. package/src/test.ts +0 -135
  159. package/src/types/convex-api.d.ts +0 -20
  160. package/src/types/convex-api.ts +0 -21
  161. package/src/types.ts +0 -109
  162. package/src/ui/colors.ts +0 -219
  163. package/src/ui/errors.ts +0 -394
  164. package/src/ui/index.ts +0 -17
  165. package/src/ui/prompts.ts +0 -390
  166. package/src/ui/spinner.ts +0 -325
  167. package/src/utils/backup.ts +0 -224
  168. package/src/utils/config.ts +0 -318
  169. package/src/utils/os.ts +0 -124
  170. package/src/utils/paths.ts +0 -203
  171. package/src/webhook.ts +0 -107
  172. package/test-10-working.cjs +0 -97
  173. package/test-14-final.cjs +0 -96
  174. package/test-actual-handlers.ts +0 -92
  175. package/test-apilayer-all-14.ts +0 -249
  176. package/test-apilayer-fixed.ts +0 -248
  177. package/test-direct-endpoints.ts +0 -174
  178. package/test-exact-endpoints.ts +0 -144
  179. package/test-final.ts +0 -83
  180. package/test-full-routing.ts +0 -100
  181. package/test-handlers-correct.ts +0 -217
  182. package/test-numverify-key.ts +0 -41
  183. package/test-via-handlers.ts +0 -92
  184. package/test-worldnews.mjs +0 -26
  185. package/tsconfig.json +0 -20
@@ -1,203 +0,0 @@
1
- /**
2
- * APIClaw CLI Login
3
- * In-terminal email signup/login via magic link.
4
- * Zero browser required.
5
- */
6
-
7
- import { input } from '@inquirer/prompts';
8
- import ora from 'ora';
9
- import chalk from 'chalk';
10
- import { writeSession, readSession, getMachineFingerprint } from '../../session.js';
11
-
12
- const CONVEX_URL = 'https://adventurous-avocet-799.convex.cloud';
13
- const APICLAW_URL = 'https://apiclaw.cloud';
14
- const POLL_INTERVAL_MS = 2000;
15
- const MAX_WAIT_MS = 15 * 60 * 1000; // 15 min (magic link TTL)
16
-
17
- export interface LoginOptions {
18
- email?: string;
19
- force?: boolean;
20
- }
21
-
22
- /**
23
- * Request a magic link via the CLI-specific APIClaw endpoint.
24
- * Returns the token directly so we can poll for verification.
25
- */
26
- async function requestMagicLink(email: string): Promise<string> {
27
- const fingerprint = getMachineFingerprint();
28
-
29
- const res = await fetch(`${APICLAW_URL}/api/workspace-auth/cli-link`, {
30
- method: 'POST',
31
- headers: { 'Content-Type': 'application/json' },
32
- body: JSON.stringify({ email: email.toLowerCase().trim(), fingerprint }),
33
- });
34
-
35
- if (!res.ok) {
36
- const err = await res.json().catch(() => ({}));
37
- throw new Error((err as any).error || `Failed to send magic link (${res.status})`);
38
- }
39
-
40
- const data = await res.json().catch(() => ({} as Record<string, unknown>));
41
- const token = (data as Record<string, unknown>)?.token as string | undefined;
42
-
43
- if (!token) {
44
- throw new Error('No token returned. Try again.');
45
- }
46
-
47
- return token;
48
- }
49
-
50
- /**
51
- * Poll Convex until the magic link is clicked or expired
52
- */
53
- async function pollForVerification(token: string): Promise<{
54
- sessionToken: string;
55
- workspaceId: string;
56
- email: string;
57
- tier: string;
58
- isNew: boolean;
59
- }> {
60
- const started = Date.now();
61
-
62
- while (Date.now() - started < MAX_WAIT_MS) {
63
- await sleep(POLL_INTERVAL_MS);
64
-
65
- const res = await fetch(`${CONVEX_URL}/api/query`, {
66
- method: 'POST',
67
- headers: { 'Content-Type': 'application/json' },
68
- body: JSON.stringify({
69
- path: 'workspaces:pollMagicLink',
70
- args: { token },
71
- }),
72
- });
73
-
74
- const data = await res.json().catch(() => ({}));
75
- interface PollResult {
76
- status: string;
77
- sessionToken?: string;
78
- workspace?: { id: string; email: string; tier: string; usageCount: number };
79
- expiresAt?: number;
80
- }
81
- const raw = (data as Record<string, unknown>)?.value || data;
82
- const result = raw as PollResult;
83
-
84
- if (result?.status === 'verified' && result.sessionToken && result.workspace) {
85
- return {
86
- sessionToken: result.sessionToken,
87
- workspaceId: result.workspace.id,
88
- email: result.workspace.email,
89
- tier: result.workspace.tier,
90
- isNew: result.workspace.usageCount === 0,
91
- };
92
- }
93
-
94
- if (result?.status === 'expired') {
95
- throw new Error('Magic link expired. Run login again to get a fresh link.');
96
- }
97
- }
98
-
99
- throw new Error('Timed out waiting for email verification.');
100
- }
101
-
102
- function sleep(ms: number): Promise<void> {
103
- return new Promise((resolve) => setTimeout(resolve, ms));
104
- }
105
-
106
- /**
107
- * Main login command
108
- */
109
- export async function loginCommand(options: LoginOptions = {}): Promise<{
110
- sessionToken: string;
111
- workspaceId: string;
112
- email: string;
113
- tier: string;
114
- isNew: boolean;
115
- } | null> {
116
- // Check existing session
117
- if (!options.force) {
118
- const existing = readSession();
119
- if (existing) {
120
- console.log(chalk.green(`\nāœ“ Already signed in as ${chalk.bold(existing.email)}\n`));
121
- console.log(
122
- chalk.dim(` Use ${chalk.white('npx @nordsym/apiclaw login --force')} to switch accounts.\n`)
123
- );
124
- return {
125
- sessionToken: existing.sessionToken,
126
- workspaceId: existing.workspaceId,
127
- email: existing.email,
128
- tier: 'free',
129
- isNew: false,
130
- };
131
- }
132
- }
133
-
134
- console.log('');
135
- console.log(chalk.bold(' šŸ¦ž APIClaw Workspace'));
136
- console.log(chalk.dim(' Sign in or create a free account\n'));
137
-
138
- // Get email
139
- let email = options.email;
140
- if (!email) {
141
- email = await input({
142
- message: 'Your email:',
143
- validate: (val) => {
144
- if (!val || !val.includes('@')) return 'Enter a valid email address';
145
- return true;
146
- },
147
- });
148
- }
149
-
150
- email = email.trim().toLowerCase();
151
-
152
- // Send magic link
153
- const sendSpinner = ora('Sending magic link...').start();
154
- let token: string;
155
-
156
- try {
157
- token = await requestMagicLink(email);
158
- sendSpinner.succeed(`Magic link sent to ${chalk.bold(email)}`);
159
- } catch (err: any) {
160
- sendSpinner.fail(`Failed: ${err.message}`);
161
- return null;
162
- }
163
-
164
- // Tell user to check email
165
- console.log('');
166
- console.log(chalk.cyan(' → Check your inbox and click the link to continue.'));
167
- console.log(chalk.dim(' The link expires in 15 minutes.\n'));
168
-
169
- // Poll for verification
170
- const pollSpinner = ora('Waiting for email verification...').start();
171
-
172
- try {
173
- const result = await pollForVerification(token);
174
-
175
- // Save session locally
176
- writeSession(result.sessionToken, result.workspaceId, result.email);
177
-
178
- pollSpinner.succeed(
179
- result.isNew
180
- ? chalk.green(`Workspace created for ${chalk.bold(result.email)}`)
181
- : chalk.green(`Signed in as ${chalk.bold(result.email)}`)
182
- );
183
-
184
- console.log('');
185
-
186
- if (result.isNew) {
187
- console.log(chalk.bold(' āœ… You\'re in. Free tier: 50 API calls/month.'));
188
- console.log(
189
- chalk.dim(
190
- ` Dashboard: ${chalk.white(`${APICLAW_URL}/workspace`)}\n`
191
- )
192
- );
193
- } else {
194
- console.log(chalk.bold(` āœ… Welcome back. Tier: ${result.tier}.`));
195
- console.log('');
196
- }
197
-
198
- return result;
199
- } catch (err: any) {
200
- pollSpinner.fail(err.message);
201
- return null;
202
- }
203
- }
@@ -1,373 +0,0 @@
1
- /**
2
- * MCP Install Command
3
- * Simple, focused command to install APIClaw into MCP config files
4
- * Supports Claude Desktop and Claude Code
5
- */
6
-
7
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
8
- import { dirname, join } from 'path';
9
- import { platform, homedir } from 'os';
10
- import { execSync } from 'child_process';
11
- import chalk from 'chalk';
12
-
13
- export interface MCPInstallOptions {
14
- client?: string;
15
- dryRun?: boolean;
16
- }
17
-
18
- type Platform = 'mac' | 'win' | 'linux';
19
-
20
- interface ClientConfig {
21
- name: string;
22
- displayName: string;
23
- getConfigPath: () => string;
24
- configKey: string; // Key path in config (e.g., "mcpServers" or root level)
25
- }
26
-
27
- /**
28
- * Detect operating system
29
- */
30
- function detectOS(): Platform {
31
- const os = platform();
32
- switch (os) {
33
- case 'darwin': return 'mac';
34
- case 'win32': return 'win';
35
- default: return 'linux';
36
- }
37
- }
38
-
39
- /**
40
- * Get home directory
41
- */
42
- function getHome(): string {
43
- return homedir();
44
- }
45
-
46
- /**
47
- * Get config paths for supported clients
48
- */
49
- function getClientConfigs(): ClientConfig[] {
50
- const os = detectOS();
51
- const home = getHome();
52
-
53
- const clients: ClientConfig[] = [
54
- {
55
- name: 'claude-desktop',
56
- displayName: 'Claude Desktop',
57
- configKey: 'mcpServers',
58
- getConfigPath: () => {
59
- switch (os) {
60
- case 'mac':
61
- return join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
62
- case 'win':
63
- return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
64
- case 'linux':
65
- return join(home, '.config', 'Claude', 'claude_desktop_config.json');
66
- }
67
- },
68
- },
69
- {
70
- name: 'claude-code',
71
- displayName: 'Claude Code',
72
- configKey: 'mcpServers',
73
- getConfigPath: () => {
74
- // Claude Code uses ~/.claude.json on all platforms
75
- return join(home, '.claude.json');
76
- },
77
- },
78
- {
79
- name: 'codex',
80
- displayName: 'Codex (OpenAI)',
81
- configKey: 'mcp',
82
- getConfigPath: () => {
83
- // Codex uses ~/.codex/config.toml
84
- return join(home, '.codex', 'config.toml');
85
- },
86
- },
87
- ];
88
-
89
- return clients;
90
- }
91
-
92
- /**
93
- * APIClaw MCP server configuration
94
- */
95
- const APICLAW_CONFIG = {
96
- command: 'npx',
97
- args: ['-y', '@nordsym/apiclaw', 'serve'],
98
- };
99
-
100
- /**
101
- * Read JSON config file
102
- */
103
- function readConfig(path: string): { success: boolean; config: any; error?: string; isNew?: boolean } {
104
- try {
105
- if (!existsSync(path)) {
106
- return { success: true, config: {}, isNew: true };
107
- }
108
-
109
- const content = readFileSync(path, 'utf-8');
110
- if (!content.trim()) {
111
- return { success: true, config: {}, isNew: true };
112
- }
113
-
114
- return { success: true, config: JSON.parse(content), isNew: false };
115
- } catch (error) {
116
- return {
117
- success: false,
118
- config: null,
119
- error: error instanceof Error ? error.message : 'Unknown error'
120
- };
121
- }
122
- }
123
-
124
- /**
125
- * Write JSON config file with backup
126
- */
127
- function writeConfig(path: string, config: any, createBackup = true): { success: boolean; error?: string } {
128
- try {
129
- const dir = dirname(path);
130
- if (!existsSync(dir)) {
131
- mkdirSync(dir, { recursive: true });
132
- }
133
-
134
- // Create backup if file exists
135
- if (createBackup && existsSync(path)) {
136
- const backupPath = `${path}.backup.${Date.now()}.json`;
137
- const existing = readFileSync(path, 'utf-8');
138
- writeFileSync(backupPath, existing, 'utf-8');
139
- }
140
-
141
- writeFileSync(path, JSON.stringify(config, null, 2), 'utf-8');
142
- return { success: true };
143
- } catch (error) {
144
- return {
145
- success: false,
146
- error: error instanceof Error ? error.message : 'Unknown error'
147
- };
148
- }
149
- }
150
-
151
- /**
152
- * Check if Codex CLI is available
153
- */
154
- function isCodexAvailable(): boolean {
155
- try {
156
- execSync('codex --version', { stdio: 'pipe' });
157
- return true;
158
- } catch {
159
- return false;
160
- }
161
- }
162
-
163
- /**
164
- * Install APIClaw to Codex using CLI
165
- */
166
- function installToCodex(dryRun: boolean): { success: boolean; message: string; skipped?: boolean } {
167
- if (!isCodexAvailable()) {
168
- return { success: false, message: 'Codex CLI not found' };
169
- }
170
-
171
- try {
172
- // Check if already installed
173
- try {
174
- const output = execSync('codex mcp get apiclaw', { encoding: 'utf-8', stdio: 'pipe' });
175
- if (output.includes('apiclaw')) {
176
- return { success: true, message: 'Already installed', skipped: true };
177
- }
178
- } catch {
179
- // Not installed, continue
180
- }
181
-
182
- if (dryRun) {
183
- console.log(chalk.cyan('\n Would run: codex mcp add apiclaw -- npx -y @nordsym/apiclaw'));
184
- return { success: true, message: 'Dry run - no changes made', skipped: true };
185
- }
186
-
187
- // Install
188
- execSync('codex mcp add apiclaw -- npx -y @nordsym/apiclaw', { stdio: 'pipe' });
189
- return { success: true, message: 'Installed via CLI' };
190
- } catch (error) {
191
- return {
192
- success: false,
193
- message: error instanceof Error ? error.message : 'Unknown error'
194
- };
195
- }
196
- }
197
-
198
- /**
199
- * Install APIClaw into a client config
200
- */
201
- function installToClient(client: ClientConfig, dryRun: boolean): { success: boolean; message: string; skipped?: boolean } {
202
- // Special handling for Codex
203
- if (client.name === 'codex') {
204
- return installToCodex(dryRun);
205
- }
206
-
207
- const configPath = client.getConfigPath();
208
-
209
- // Read existing config
210
- const readResult = readConfig(configPath);
211
- if (!readResult.success) {
212
- return { success: false, message: `Failed to read config: ${readResult.error}` };
213
- }
214
-
215
- const config = readResult.config;
216
-
217
- // Initialize mcpServers if not present
218
- if (!config.mcpServers) {
219
- config.mcpServers = {};
220
- }
221
-
222
- // Check if already installed
223
- if (config.mcpServers.apiclaw) {
224
- return { success: true, message: 'Already installed', skipped: true };
225
- }
226
-
227
- // Add APIClaw config
228
- config.mcpServers.apiclaw = APICLAW_CONFIG;
229
-
230
- if (dryRun) {
231
- console.log(chalk.cyan(`\n Would add to ${configPath}:`));
232
- console.log(chalk.gray(JSON.stringify({ apiclaw: APICLAW_CONFIG }, null, 4)));
233
- return { success: true, message: 'Dry run - no changes made', skipped: true };
234
- }
235
-
236
- // Write config
237
- const writeResult = writeConfig(configPath, config);
238
- if (!writeResult.success) {
239
- return { success: false, message: `Failed to write config: ${writeResult.error}` };
240
- }
241
-
242
- return {
243
- success: true,
244
- message: readResult.isNew ? 'Created new config' : 'Updated config'
245
- };
246
- }
247
-
248
- /**
249
- * Main mcp-install command handler
250
- */
251
- export async function mcpInstallCommand(options: MCPInstallOptions): Promise<void> {
252
- const os = detectOS();
253
- const osName = os === 'mac' ? 'macOS' : os === 'win' ? 'Windows' : 'Linux';
254
-
255
- console.log(chalk.bold('\nšŸ¦ž APIClaw MCP Install\n'));
256
- console.log(`Platform: ${osName}\n`);
257
-
258
- const clients = getClientConfigs();
259
- let targetClients = clients;
260
-
261
- // Filter to specific client if requested
262
- if (options.client) {
263
- const normalizedClient = options.client.toLowerCase().replace(/[_\s]/g, '-');
264
- const aliases: Record<string, string> = {
265
- 'claude': 'claude-desktop',
266
- 'claude-desktop': 'claude-desktop',
267
- 'claudedesktop': 'claude-desktop',
268
- 'desktop': 'claude-desktop',
269
- 'code': 'claude-code',
270
- 'claude-code': 'claude-code',
271
- 'claudecode': 'claude-code',
272
- 'codex': 'codex',
273
- 'openai': 'codex',
274
- };
275
-
276
- const targetName = aliases[normalizedClient];
277
- if (!targetName) {
278
- console.log(chalk.red(`āŒ Unknown client: ${options.client}`));
279
- console.log(' Supported: claude-desktop, claude-code, codex');
280
- process.exit(1);
281
- }
282
-
283
- targetClients = clients.filter(c => c.name === targetName);
284
- }
285
-
286
- // Detect which clients exist
287
- console.log('šŸ” Detecting MCP clients...\n');
288
-
289
- const detectedClients: ClientConfig[] = [];
290
- for (const client of targetClients) {
291
- let exists = false;
292
-
293
- // Special detection for Codex (check CLI availability)
294
- if (client.name === 'codex') {
295
- exists = isCodexAvailable();
296
- } else {
297
- // For JSON-based configs, check file/dir existence
298
- const configPath = client.getConfigPath();
299
- const configDir = dirname(configPath);
300
- exists = existsSync(configPath) || existsSync(configDir);
301
- }
302
-
303
- const icon = exists ? chalk.green('āœ“') : chalk.gray('āœ—');
304
- const status = exists ? 'found' : 'not found';
305
- console.log(` ${icon} ${client.displayName} ${status}`);
306
-
307
- if (exists) {
308
- detectedClients.push(client);
309
- }
310
- }
311
-
312
- console.log('');
313
-
314
- if (detectedClients.length === 0) {
315
- console.log(chalk.yellow('āš ļø No MCP clients detected.'));
316
- console.log(' Install Claude Desktop or Claude Code first.\n');
317
- process.exit(0);
318
- }
319
-
320
- // Install to each detected client
321
- let successCount = 0;
322
- let skipCount = 0;
323
- let failCount = 0;
324
-
325
- for (const client of detectedClients) {
326
- const result = installToClient(client, options.dryRun || false);
327
-
328
- if (result.success) {
329
- if (result.skipped) {
330
- skipCount++;
331
- console.log(chalk.yellow(`ā­ļø ${client.displayName}: ${result.message}`));
332
- } else {
333
- successCount++;
334
- console.log(chalk.green(`āœ“ ${client.displayName}: ${result.message}`));
335
- }
336
- } else {
337
- failCount++;
338
- console.log(chalk.red(`āœ— ${client.displayName}: ${result.message}`));
339
- }
340
- }
341
-
342
- // Summary
343
- console.log('\n' + '═'.repeat(50));
344
-
345
- if (failCount === 0) {
346
- if (options.dryRun) {
347
- console.log(chalk.cyan('\nāœ… Dry run complete! Run without --dry-run to apply changes.\n'));
348
- } else if (successCount > 0) {
349
- console.log(chalk.green('\nāœ… APIClaw installed successfully!\n'));
350
- console.log(chalk.bold('What you get:\n'));
351
- console.log(chalk.cyan(' šŸ” Search') + ' 22,000+ APIs to discover');
352
- console.log(chalk.cyan(' 🌐 Open APIs') + ' 1,600 free APIs');
353
- console.log(chalk.cyan(' šŸ”‘ Direct Call') + ' 1,500+ premium (APIClaw manages keys)');
354
- console.log('');
355
- console.log('Next:');
356
- console.log(' 1. Restart your MCP client');
357
- console.log(' 2. Try: "Find weather APIs"');
358
- console.log('');
359
- console.log('Docs: https://apiclaw.com/docs\n');
360
- } else {
361
- console.log(chalk.yellow('\nāœ… APIClaw already installed in all clients.\n'));
362
- console.log(chalk.bold('What you have:\n'));
363
- console.log(chalk.cyan(' šŸ” Search') + ' 22,000+ APIs to discover');
364
- console.log(chalk.cyan(' 🌐 Open APIs') + ' 1,600 free APIs');
365
- console.log(chalk.cyan(' šŸ”‘ Direct Call') + ' 1,500+ premium (APIClaw manages keys)');
366
- console.log('');
367
- console.log('Run with --force to reinstall (coming soon).\n');
368
- }
369
- } else {
370
- console.log(chalk.red(`\nāš ļø Installation completed with ${failCount} error(s).\n`));
371
- process.exit(1);
372
- }
373
- }