@nordsym/apiclaw 2.2.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.
- package/README.md +15 -2
- package/dist/bin-http.js +0 -0
- package/dist/bin.bundled.js +79288 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +24 -2
- package/dist/gateway-client.js.map +1 -1
- package/dist/index.bundled.js +61263 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/.claude/settings.local.json +0 -13
- package/.env.prod +0 -1
- package/apiclaw-README.md +0 -494
- package/convex/_generated/api.d.ts +0 -145
- package/convex/_generated/api.js +0 -23
- package/convex/_generated/dataModel.d.ts +0 -60
- package/convex/_generated/server.d.ts +0 -143
- package/convex/_generated/server.js +0 -93
- package/convex/_listWorkspaces.ts +0 -13
- package/convex/adminActivate.ts +0 -53
- package/convex/adminStats.ts +0 -306
- package/convex/agents.ts +0 -939
- package/convex/analytics.ts +0 -187
- package/convex/apiKeys.ts +0 -220
- package/convex/backfillAnalytics.ts +0 -272
- package/convex/backfillSearchLogs.ts +0 -35
- package/convex/billing.ts +0 -834
- package/convex/capabilities.ts +0 -157
- package/convex/chains.ts +0 -1318
- package/convex/credits.ts +0 -211
- package/convex/crons.ts +0 -65
- package/convex/debugFilestackLogs.ts +0 -16
- package/convex/debugGetToken.ts +0 -18
- package/convex/directCall.ts +0 -713
- package/convex/earnProgress.ts +0 -753
- package/convex/email.ts +0 -329
- package/convex/feedback.ts +0 -265
- package/convex/funnel.ts +0 -431
- package/convex/guards.ts +0 -174
- package/convex/http.ts +0 -3756
- package/convex/inbound.ts +0 -32
- package/convex/logs.ts +0 -701
- package/convex/migrateFilestack.ts +0 -81
- package/convex/migratePartnersProd.ts +0 -174
- package/convex/migratePratham.ts +0 -126
- package/convex/migrateProviderWorkspaces.ts +0 -175
- package/convex/mou.ts +0 -91
- package/convex/nurture.ts +0 -355
- package/convex/providerKeys.ts +0 -289
- package/convex/providers.ts +0 -1135
- package/convex/purchases.ts +0 -183
- package/convex/ratelimit.ts +0 -104
- package/convex/schema.ts +0 -926
- package/convex/searchLogs.ts +0 -265
- package/convex/seedAPILayerAPIs.ts +0 -191
- package/convex/seedDirectCallConfigs.ts +0 -336
- package/convex/seedPratham.ts +0 -149
- package/convex/spendAlerts.ts +0 -442
- package/convex/stripeActions.ts +0 -607
- package/convex/teams.ts +0 -243
- package/convex/telemetry.ts +0 -81
- package/convex/tsconfig.json +0 -25
- package/convex/updateAPIStatus.ts +0 -44
- package/convex/usage.ts +0 -260
- package/convex/usageReports.ts +0 -357
- package/convex/waitlist.ts +0 -55
- package/convex/webhooks.ts +0 -494
- package/convex/workspaceSettings.ts +0 -143
- package/convex/workspaces.ts +0 -1331
- package/convex.json +0 -3
- package/direct-test.mjs +0 -51
- package/email-templates/filestack-provider-outreach.html +0 -162
- package/email-templates/partnership-template.html +0 -116
- package/email-templates/pratham-draft-preview.txt +0 -57
- package/email-templates/pratham-partnership-draft.html +0 -141
- package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
- package/reports/pipeline/PIPELINE-REPORT.json +0 -153
- package/reports/pipeline/acquire_apisguru.json +0 -17
- package/reports/pipeline/capabilities.json +0 -38
- package/reports/pipeline/discover_azure_recursive.json +0 -1551
- package/reports/pipeline/discover_github.json +0 -25
- package/reports/pipeline/discover_github_repos.json +0 -49
- package/reports/pipeline/discover_swaggerhub.json +0 -24
- package/reports/pipeline/discover_well_known.json +0 -23
- package/reports/pipeline/fetch_specs.json +0 -19
- package/reports/pipeline/generate_providers.json +0 -14
- package/reports/pipeline/match_registry.json +0 -11
- package/reports/pipeline/parse_specs.json +0 -17
- package/reports/pipeline/promote_candidates.json +0 -34
- package/reports/pipeline/validate.json +0 -30
- package/reports/pipeline/validate_smoke_details.json +0 -3835
- package/reports/session-report-2026-04-05.html +0 -433
- package/seed-apis-direct.mjs +0 -106
- package/src/access-control.ts +0 -174
- package/src/adapters/base.ts +0 -364
- package/src/adapters/claude-desktop.ts +0 -41
- package/src/adapters/cline.ts +0 -88
- package/src/adapters/continue.ts +0 -91
- package/src/adapters/cursor.ts +0 -43
- package/src/adapters/custom.ts +0 -188
- package/src/adapters/detect.ts +0 -202
- package/src/adapters/index.ts +0 -47
- package/src/adapters/windsurf.ts +0 -44
- package/src/bin-http.ts +0 -45
- package/src/bin.ts +0 -34
- package/src/capability-router.ts +0 -331
- package/src/chainExecutor.ts +0 -730
- package/src/chainResolver.test.ts +0 -246
- package/src/chainResolver.ts +0 -658
- package/src/cli/commands/demo.ts +0 -109
- package/src/cli/commands/doctor.ts +0 -435
- package/src/cli/commands/index.ts +0 -9
- package/src/cli/commands/login.ts +0 -203
- package/src/cli/commands/mcp-install.ts +0 -373
- package/src/cli/commands/restore.ts +0 -333
- package/src/cli/commands/setup.ts +0 -297
- package/src/cli/commands/uninstall.ts +0 -240
- package/src/cli/index.ts +0 -148
- package/src/cli.ts +0 -370
- package/src/confirmation.ts +0 -296
- package/src/credentials.ts +0 -455
- package/src/credits.ts +0 -329
- package/src/crypto.ts +0 -75
- package/src/discovery.ts +0 -568
- package/src/enterprise/env.ts +0 -156
- package/src/enterprise/index.ts +0 -7
- package/src/enterprise/script-generator.ts +0 -481
- package/src/execute-dynamic.ts +0 -617
- package/src/execute.ts +0 -2386
- package/src/funnel-client.ts +0 -168
- package/src/funnel.test.ts +0 -187
- package/src/gateway-client.ts +0 -192
- package/src/hivr-whitelist.ts +0 -110
- package/src/http-api.ts +0 -286
- package/src/http-server-minimal.ts +0 -154
- package/src/index.ts +0 -2702
- package/src/intelligent-gateway.ts +0 -339
- package/src/mcp-analytics.ts +0 -156
- package/src/metered.ts +0 -149
- package/src/open-apis-generated.ts +0 -157
- package/src/open-apis.ts +0 -558
- package/src/postinstall.ts +0 -40
- package/src/product-whitelist.ts +0 -246
- package/src/proxy.ts +0 -36
- package/src/registration-guard.ts +0 -117
- package/src/session.ts +0 -129
- package/src/stripe.ts +0 -497
- package/src/telemetry.ts +0 -71
- package/src/test.ts +0 -135
- package/src/types/convex-api.d.ts +0 -20
- package/src/types/convex-api.ts +0 -21
- package/src/types.ts +0 -109
- package/src/ui/colors.ts +0 -219
- package/src/ui/errors.ts +0 -394
- package/src/ui/index.ts +0 -17
- package/src/ui/prompts.ts +0 -390
- package/src/ui/spinner.ts +0 -325
- package/src/utils/backup.ts +0 -224
- package/src/utils/config.ts +0 -318
- package/src/utils/os.ts +0 -124
- package/src/utils/paths.ts +0 -203
- package/src/webhook.ts +0 -107
- package/test-10-working.cjs +0 -97
- package/test-14-final.cjs +0 -96
- package/test-actual-handlers.ts +0 -92
- package/test-apilayer-all-14.ts +0 -249
- package/test-apilayer-fixed.ts +0 -248
- package/test-direct-endpoints.ts +0 -174
- package/test-exact-endpoints.ts +0 -144
- package/test-final.ts +0 -83
- package/test-full-routing.ts +0 -100
- package/test-handlers-correct.ts +0 -217
- package/test-numverify-key.ts +0 -41
- package/test-via-handlers.ts +0 -92
- package/test-worldnews.mjs +0 -26
- package/tsconfig.json +0 -20
package/src/cli/commands/demo.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* APIClaw CLI Demo
|
|
3
|
-
* Fires a real API call right in the terminal after login.
|
|
4
|
-
* Shows the value in 5 seconds flat.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import ora from 'ora';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import { readSession } from '../../session.js';
|
|
10
|
-
import { loginCommand } from './login.js';
|
|
11
|
-
|
|
12
|
-
const CONVEX_SITE = 'https://adventurous-avocet-799.convex.site';
|
|
13
|
-
|
|
14
|
-
async function convertCurrency(): Promise<void> {
|
|
15
|
-
const res = await fetch(`${CONVEX_SITE}/proxy/apilayer`, {
|
|
16
|
-
method: 'POST',
|
|
17
|
-
headers: { 'Content-Type': 'application/json' },
|
|
18
|
-
body: JSON.stringify({
|
|
19
|
-
service: 'exchangerates_data',
|
|
20
|
-
endpoint: '/latest',
|
|
21
|
-
params: { base: 'USD', symbols: 'SEK,EUR,GBP,JPY,NOK,DKK' },
|
|
22
|
-
}),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
26
|
-
const data = await res.json() as any;
|
|
27
|
-
|
|
28
|
-
if (!data.success) throw new Error(data.message || 'API error');
|
|
29
|
-
|
|
30
|
-
const rates = data.rates as Record<string, number>;
|
|
31
|
-
const base = data.base;
|
|
32
|
-
const date = data.date;
|
|
33
|
-
|
|
34
|
-
console.log('');
|
|
35
|
-
console.log(chalk.bold(` 💱 Live exchange rates · ${date}`));
|
|
36
|
-
console.log(chalk.dim(` Base: 1 ${base}\n`));
|
|
37
|
-
|
|
38
|
-
const pairs = [
|
|
39
|
-
{ code: 'SEK', flag: '🇸🇪', name: 'Swedish Krona' },
|
|
40
|
-
{ code: 'EUR', flag: '🇪🇺', name: 'Euro' },
|
|
41
|
-
{ code: 'GBP', flag: '🇬🇧', name: 'British Pound' },
|
|
42
|
-
{ code: 'JPY', flag: '🇯🇵', name: 'Japanese Yen' },
|
|
43
|
-
{ code: 'NOK', flag: '🇳🇴', name: 'Norwegian Krone' },
|
|
44
|
-
{ code: 'DKK', flag: '🇩🇰', name: 'Danish Krone' },
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
for (const { code, flag, name } of pairs) {
|
|
48
|
-
if (rates[code]) {
|
|
49
|
-
const val = rates[code].toFixed(2);
|
|
50
|
-
console.log(
|
|
51
|
-
` ${flag} ${chalk.bold((val + ' ' + code).padEnd(14))}${chalk.dim(name)}`
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
console.log('');
|
|
57
|
-
console.log(
|
|
58
|
-
chalk.dim(' Powered by APIClaw Direct Call · exchangerates_data via APILayer')
|
|
59
|
-
);
|
|
60
|
-
console.log(
|
|
61
|
-
chalk.dim(` No API key needed. Your agent can call this too.\n`)
|
|
62
|
-
);
|
|
63
|
-
console.log(
|
|
64
|
-
chalk.cyan(` Dashboard: https://apiclaw.cloud/workspace`) + '\n'
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export async function demoCommand(): Promise<void> {
|
|
69
|
-
console.log('');
|
|
70
|
-
|
|
71
|
-
// Ensure logged in
|
|
72
|
-
let session = readSession();
|
|
73
|
-
if (!session) {
|
|
74
|
-
console.log(chalk.yellow(' Sign in first to run the demo:\n'));
|
|
75
|
-
const result = await loginCommand({});
|
|
76
|
-
if (!result) {
|
|
77
|
-
console.error(chalk.red('\n Login failed. Run: npx @nordsym/apiclaw login\n'));
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
session = readSession();
|
|
81
|
-
console.log('');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
console.log(
|
|
85
|
-
chalk.bold(' 🦞 APIClaw Demo') +
|
|
86
|
-
chalk.dim(' — a real API call, right now, no config needed\n')
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const spinner = ora('Calling exchangerates_data via APIClaw...').start();
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await convertCurrency();
|
|
93
|
-
spinner.stop();
|
|
94
|
-
|
|
95
|
-
console.log(chalk.green(' ✓ That was a live Direct Call through APIClaw.'));
|
|
96
|
-
console.log(chalk.dim(' 20 providers, hundreds of APIs. Zero API keys needed.\n'));
|
|
97
|
-
console.log(
|
|
98
|
-
chalk.bold(' Next step:') +
|
|
99
|
-
chalk.dim(' add APIClaw to your AI agent:\n')
|
|
100
|
-
);
|
|
101
|
-
console.log(
|
|
102
|
-
' ' + chalk.cyan('npx @nordsym/apiclaw setup') +
|
|
103
|
-
chalk.dim(' auto-detects Claude, Cursor, Windsurf\n')
|
|
104
|
-
);
|
|
105
|
-
} catch (err: any) {
|
|
106
|
-
spinner.fail('Demo failed: ' + err.message);
|
|
107
|
-
console.log(chalk.dim('\n Try again or check https://apiclaw.cloud\n'));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor Command
|
|
3
|
-
* Health check for APIClaw installation and MCP client configurations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
import { execSync } from 'child_process';
|
|
8
|
-
import { getAllClients, getClientConfig, MCPClient } from '../../utils/paths.js';
|
|
9
|
-
import { readConfig, hasApiclawConfig } from '../../utils/config.js';
|
|
10
|
-
import { detectOS, getOSDisplayName } from '../../utils/os.js';
|
|
11
|
-
import { getApiUrl, readEnvConfig, ENV_VARS } from '../../enterprise/env.js';
|
|
12
|
-
|
|
13
|
-
export interface DoctorResult {
|
|
14
|
-
healthy: boolean;
|
|
15
|
-
checks: CheckResult[];
|
|
16
|
-
summary: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CheckResult {
|
|
20
|
-
category: string;
|
|
21
|
-
name: string;
|
|
22
|
-
status: 'pass' | 'fail' | 'warn' | 'skip';
|
|
23
|
-
message: string;
|
|
24
|
-
details?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Check Node.js availability and version
|
|
29
|
-
*/
|
|
30
|
-
function checkNode(): CheckResult {
|
|
31
|
-
try {
|
|
32
|
-
const version = execSync('node --version', { encoding: 'utf-8' }).trim();
|
|
33
|
-
const major = parseInt(version.replace('v', '').split('.')[0], 10);
|
|
34
|
-
|
|
35
|
-
if (major < 18) {
|
|
36
|
-
return {
|
|
37
|
-
category: 'System',
|
|
38
|
-
name: 'Node.js',
|
|
39
|
-
status: 'warn',
|
|
40
|
-
message: `${version} (recommend v18+)`,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
category: 'System',
|
|
46
|
-
name: 'Node.js',
|
|
47
|
-
status: 'pass',
|
|
48
|
-
message: version,
|
|
49
|
-
};
|
|
50
|
-
} catch {
|
|
51
|
-
return {
|
|
52
|
-
category: 'System',
|
|
53
|
-
name: 'Node.js',
|
|
54
|
-
status: 'fail',
|
|
55
|
-
message: 'Not found',
|
|
56
|
-
details: 'Node.js is required. Install from https://nodejs.org',
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Check npm availability
|
|
63
|
-
*/
|
|
64
|
-
function checkNpm(): CheckResult {
|
|
65
|
-
try {
|
|
66
|
-
const version = execSync('npm --version', { encoding: 'utf-8' }).trim();
|
|
67
|
-
return {
|
|
68
|
-
category: 'System',
|
|
69
|
-
name: 'npm',
|
|
70
|
-
status: 'pass',
|
|
71
|
-
message: `v${version}`,
|
|
72
|
-
};
|
|
73
|
-
} catch {
|
|
74
|
-
return {
|
|
75
|
-
category: 'System',
|
|
76
|
-
name: 'npm',
|
|
77
|
-
status: 'fail',
|
|
78
|
-
message: 'Not found',
|
|
79
|
-
details: 'npm is required for npx to work',
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Check npx availability
|
|
86
|
-
*/
|
|
87
|
-
function checkNpx(): CheckResult {
|
|
88
|
-
try {
|
|
89
|
-
execSync('npx --version', { encoding: 'utf-8', stdio: 'pipe' });
|
|
90
|
-
return {
|
|
91
|
-
category: 'System',
|
|
92
|
-
name: 'npx',
|
|
93
|
-
status: 'pass',
|
|
94
|
-
message: 'Available',
|
|
95
|
-
};
|
|
96
|
-
} catch {
|
|
97
|
-
return {
|
|
98
|
-
category: 'System',
|
|
99
|
-
name: 'npx',
|
|
100
|
-
status: 'fail',
|
|
101
|
-
message: 'Not found',
|
|
102
|
-
details: 'npx is required for MCP server execution',
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check Codex CLI installation and surface its binary path
|
|
109
|
-
*/
|
|
110
|
-
function checkCodex(): CheckResult {
|
|
111
|
-
try {
|
|
112
|
-
const binPath = execSync('which codex', { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
113
|
-
let version = '';
|
|
114
|
-
try {
|
|
115
|
-
version = execSync('codex --version', { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
116
|
-
} catch {
|
|
117
|
-
// version flag may vary — path is enough
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
category: 'Tooling',
|
|
121
|
-
name: 'Codex CLI',
|
|
122
|
-
status: 'pass',
|
|
123
|
-
message: version ? `${version} — ${binPath}` : binPath,
|
|
124
|
-
};
|
|
125
|
-
} catch {
|
|
126
|
-
return {
|
|
127
|
-
category: 'Tooling',
|
|
128
|
-
name: 'Codex CLI',
|
|
129
|
-
status: 'skip',
|
|
130
|
-
message: 'Not installed',
|
|
131
|
-
details: 'Install: npm install -g @openai/codex | Then: npx @nordsym/apiclaw setup --client codex',
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check APIClaw MCP server binary path
|
|
138
|
-
*/
|
|
139
|
-
function checkApiclawBin(): CheckResult {
|
|
140
|
-
try {
|
|
141
|
-
const binPath = execSync('which apiclaw', { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
142
|
-
return {
|
|
143
|
-
category: 'Tooling',
|
|
144
|
-
name: 'APIClaw binary',
|
|
145
|
-
status: 'pass',
|
|
146
|
-
message: binPath,
|
|
147
|
-
};
|
|
148
|
-
} catch {
|
|
149
|
-
return {
|
|
150
|
-
category: 'Tooling',
|
|
151
|
-
name: 'APIClaw binary',
|
|
152
|
-
status: 'skip',
|
|
153
|
-
message: 'Not in PATH (using npx)',
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Check MCP client configuration
|
|
160
|
-
*/
|
|
161
|
-
function checkClient(client: MCPClient, serverName = 'apiclaw'): CheckResult {
|
|
162
|
-
const config = getClientConfig(client);
|
|
163
|
-
const configPath = config.configPath;
|
|
164
|
-
|
|
165
|
-
// Check if config file exists
|
|
166
|
-
if (!existsSync(configPath)) {
|
|
167
|
-
// Check if config directory exists (client might be installed but not configured)
|
|
168
|
-
const dirExists = existsSync(config.configDir);
|
|
169
|
-
|
|
170
|
-
if (dirExists) {
|
|
171
|
-
return {
|
|
172
|
-
category: 'MCP Clients',
|
|
173
|
-
name: config.displayName,
|
|
174
|
-
status: 'warn',
|
|
175
|
-
message: 'Installed but not configured',
|
|
176
|
-
details: `Config file: ${configPath}`,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
category: 'MCP Clients',
|
|
182
|
-
name: config.displayName,
|
|
183
|
-
status: 'skip',
|
|
184
|
-
message: 'Not installed',
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Read and check config
|
|
189
|
-
const readResult = readConfig(configPath);
|
|
190
|
-
|
|
191
|
-
if (!readResult.success) {
|
|
192
|
-
return {
|
|
193
|
-
category: 'MCP Clients',
|
|
194
|
-
name: config.displayName,
|
|
195
|
-
status: 'fail',
|
|
196
|
-
message: 'Invalid config',
|
|
197
|
-
details: readResult.error,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Check if APIClaw is configured
|
|
202
|
-
if (readResult.config && hasApiclawConfig(readResult.config, serverName)) {
|
|
203
|
-
return {
|
|
204
|
-
category: 'MCP Clients',
|
|
205
|
-
name: config.displayName,
|
|
206
|
-
status: 'pass',
|
|
207
|
-
message: 'Configured',
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
category: 'MCP Clients',
|
|
213
|
-
name: config.displayName,
|
|
214
|
-
status: 'warn',
|
|
215
|
-
message: 'APIClaw not configured',
|
|
216
|
-
details: `Run: npx @nordsym/apiclaw setup --client ${client}`,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Check API connectivity
|
|
222
|
-
*/
|
|
223
|
-
async function checkConnectivity(): Promise<CheckResult> {
|
|
224
|
-
const apiUrl = getApiUrl();
|
|
225
|
-
const convexUrl = process.env.CONVEX_URL || 'https://brilliant-puffin-712.eu-west-1.convex.cloud';
|
|
226
|
-
const candidates = [
|
|
227
|
-
`${apiUrl}/health`,
|
|
228
|
-
'https://apiclaw.cloud',
|
|
229
|
-
`${convexUrl.replace('.cloud', '.site')}/workspace/poll`,
|
|
230
|
-
];
|
|
231
|
-
const failures: string[] = [];
|
|
232
|
-
|
|
233
|
-
for (const testUrl of candidates) {
|
|
234
|
-
try {
|
|
235
|
-
const controller = new AbortController();
|
|
236
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
237
|
-
const response = await fetch(testUrl, {
|
|
238
|
-
method: 'GET',
|
|
239
|
-
signal: controller.signal,
|
|
240
|
-
});
|
|
241
|
-
clearTimeout(timeout);
|
|
242
|
-
|
|
243
|
-
// Any HTTP response proves network + host reachability.
|
|
244
|
-
if (response.ok) {
|
|
245
|
-
return {
|
|
246
|
-
category: 'Connectivity',
|
|
247
|
-
name: 'API Server',
|
|
248
|
-
status: 'pass',
|
|
249
|
-
message: `${testUrl} reachable`,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (testUrl.includes('/workspace/poll') && response.status === 400) {
|
|
254
|
-
return {
|
|
255
|
-
category: 'Connectivity',
|
|
256
|
-
name: 'API Server',
|
|
257
|
-
status: 'pass',
|
|
258
|
-
message: `${testUrl} reachable (auth endpoint responding)`,
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
failures.push(`${testUrl} -> HTTP ${response.status}`);
|
|
263
|
-
} catch (error) {
|
|
264
|
-
const reason = error instanceof Error ? error.message : 'Unknown error';
|
|
265
|
-
failures.push(`${testUrl} -> ${reason}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
category: 'Connectivity',
|
|
271
|
-
name: 'API Server',
|
|
272
|
-
status: 'skip',
|
|
273
|
-
message: 'Could not reach API (offline or DNS/TLS issue?)',
|
|
274
|
-
details: failures.join(' | '),
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Check environment variables
|
|
280
|
-
*/
|
|
281
|
-
function checkEnvVars(): CheckResult[] {
|
|
282
|
-
const envConfig = readEnvConfig();
|
|
283
|
-
const results: CheckResult[] = [];
|
|
284
|
-
|
|
285
|
-
if (envConfig.workspace) {
|
|
286
|
-
results.push({
|
|
287
|
-
category: 'Environment',
|
|
288
|
-
name: ENV_VARS.WORKSPACE,
|
|
289
|
-
status: 'pass',
|
|
290
|
-
message: envConfig.workspace,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (envConfig.apiUrl) {
|
|
295
|
-
results.push({
|
|
296
|
-
category: 'Environment',
|
|
297
|
-
name: ENV_VARS.API_URL,
|
|
298
|
-
status: 'pass',
|
|
299
|
-
message: envConfig.apiUrl,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (envConfig.disableTelemetry) {
|
|
304
|
-
results.push({
|
|
305
|
-
category: 'Environment',
|
|
306
|
-
name: ENV_VARS.DISABLE_TELEMETRY,
|
|
307
|
-
status: 'pass',
|
|
308
|
-
message: 'true',
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return results;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Run all health checks
|
|
317
|
-
*/
|
|
318
|
-
export async function runDoctor(options: { serverName?: string } = {}): Promise<DoctorResult> {
|
|
319
|
-
const checks: CheckResult[] = [];
|
|
320
|
-
const serverName = options.serverName || 'apiclaw';
|
|
321
|
-
|
|
322
|
-
// System checks
|
|
323
|
-
checks.push(checkNode());
|
|
324
|
-
checks.push(checkNpm());
|
|
325
|
-
checks.push(checkNpx());
|
|
326
|
-
|
|
327
|
-
// Tooling checks
|
|
328
|
-
checks.push(checkCodex());
|
|
329
|
-
checks.push(checkApiclawBin());
|
|
330
|
-
|
|
331
|
-
// Client checks
|
|
332
|
-
for (const client of getAllClients()) {
|
|
333
|
-
checks.push(checkClient(client, serverName));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Connectivity check
|
|
337
|
-
checks.push(await checkConnectivity());
|
|
338
|
-
|
|
339
|
-
// Environment checks
|
|
340
|
-
checks.push(...checkEnvVars());
|
|
341
|
-
|
|
342
|
-
// Calculate health status
|
|
343
|
-
const failures = checks.filter(c => c.status === 'fail');
|
|
344
|
-
const warnings = checks.filter(c => c.status === 'warn');
|
|
345
|
-
const passes = checks.filter(c => c.status === 'pass');
|
|
346
|
-
|
|
347
|
-
let healthy = failures.length === 0;
|
|
348
|
-
let summary: string;
|
|
349
|
-
|
|
350
|
-
if (failures.length > 0) {
|
|
351
|
-
summary = `${failures.length} issue(s) found`;
|
|
352
|
-
} else if (warnings.length > 0) {
|
|
353
|
-
summary = `All systems operational (${warnings.length} warning(s))`;
|
|
354
|
-
} else {
|
|
355
|
-
summary = 'All systems operational ✓';
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return { healthy, checks, summary };
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Format doctor results for display
|
|
363
|
-
*/
|
|
364
|
-
export function formatDoctorOutput(result: DoctorResult): string {
|
|
365
|
-
const lines: string[] = [];
|
|
366
|
-
|
|
367
|
-
lines.push('');
|
|
368
|
-
lines.push('🔍 APIClaw Health Check');
|
|
369
|
-
lines.push('========================');
|
|
370
|
-
lines.push('');
|
|
371
|
-
|
|
372
|
-
// Group checks by category
|
|
373
|
-
const categories = new Map<string, CheckResult[]>();
|
|
374
|
-
|
|
375
|
-
for (const check of result.checks) {
|
|
376
|
-
const existing = categories.get(check.category) || [];
|
|
377
|
-
existing.push(check);
|
|
378
|
-
categories.set(check.category, existing);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Format each category
|
|
382
|
-
for (const [category, checks] of categories) {
|
|
383
|
-
// Skip empty categories
|
|
384
|
-
if (checks.length === 0) continue;
|
|
385
|
-
|
|
386
|
-
lines.push(`${category}:`);
|
|
387
|
-
|
|
388
|
-
for (const check of checks) {
|
|
389
|
-
const icon = getStatusIcon(check.status);
|
|
390
|
-
lines.push(` ${icon} ${check.name} - ${check.message}`);
|
|
391
|
-
|
|
392
|
-
if (check.details && (check.status === 'fail' || check.status === 'warn')) {
|
|
393
|
-
lines.push(` ${check.details}`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
lines.push('');
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Summary
|
|
401
|
-
lines.push(`Status: ${result.summary}`);
|
|
402
|
-
lines.push('');
|
|
403
|
-
|
|
404
|
-
return lines.join('\n');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Get icon for status
|
|
409
|
-
*/
|
|
410
|
-
function getStatusIcon(status: CheckResult['status']): string {
|
|
411
|
-
switch (status) {
|
|
412
|
-
case 'pass': return '✓';
|
|
413
|
-
case 'fail': return '✗';
|
|
414
|
-
case 'warn': return '⚠';
|
|
415
|
-
case 'skip': return '○';
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Doctor command handler
|
|
421
|
-
*/
|
|
422
|
-
export async function doctorCommand(options: { serverName?: string; json?: boolean } = {}): Promise<void> {
|
|
423
|
-
const result = await runDoctor(options);
|
|
424
|
-
|
|
425
|
-
if (options.json) {
|
|
426
|
-
console.log(JSON.stringify(result, null, 2));
|
|
427
|
-
} else {
|
|
428
|
-
console.log(formatDoctorOutput(result));
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Exit with error code if unhealthy
|
|
432
|
-
if (!result.healthy) {
|
|
433
|
-
process.exit(1);
|
|
434
|
-
}
|
|
435
|
-
}
|