@nordsym/apiclaw 1.5.18 → 1.6.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/CODEBASE.md +42 -0
- package/CONCEPT.md +9 -0
- package/DASHBOARD_FIX.md +9 -0
- package/EARN-CREDITS-SPEC.md +9 -0
- package/HIVR-INTEGRATION.md +9 -0
- package/HTTP-API.md +9 -0
- package/PRD-ANALYTICS-AGENTS-TEAMS.md +9 -0
- package/PRD-API-CHAINING.md +9 -0
- package/PRD-HARDEN-SHELL.md +9 -0
- package/PRD-LOGS-SUBAGENTS-V2.md +9 -0
- package/VISION.md +9 -0
- package/{AGENTS.md → apiclaw-AGENTS.md} +9 -0
- package/{CHANGELOG.md → apiclaw-CHANGELOG.md} +31 -0
- package/{CONTRIBUTING.md → apiclaw-CONTRIBUTING.md} +9 -0
- package/{HEARTBEAT.md → apiclaw-HEARTBEAT.md} +9 -0
- package/{IDENTITY.md → apiclaw-IDENTITY.md} +9 -0
- package/{README.md → apiclaw-README.md} +9 -0
- package/{SOUL.md → apiclaw-SOUL.md} +9 -0
- package/{TOOLS.md → apiclaw-TOOLS.md} +9 -0
- package/{USER.md → apiclaw-USER.md} +9 -0
- package/convex/_generated/api.d.ts +2 -0
- package/convex/{README.md → apiclaw-convex-README.md} +9 -0
- package/convex/http.ts +315 -0
- package/convex/seedPratham.ts +161 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +136 -2
- package/dist/credentials.js.map +1 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +289 -1
- package/dist/execute.js.map +1 -1
- package/docs/PRD-BILLING.md +9 -0
- package/docs/PRD-EARN-SYSTEM.md +9 -0
- package/docs/PRD-MCP-AUTO-SETUP.md +9 -0
- package/docs/PRD-ORGANIC-DISTRIBUTION.md +9 -0
- package/docs/PRD-agent-first-billing.md +9 -0
- package/docs/PRD-customer-key-passthrough.md +9 -0
- package/docs/PRD-final-polish.md +9 -0
- package/docs/PRD-mobile-responsive.md +9 -0
- package/docs/PRD-navigation-expansion.md +9 -0
- package/docs/PRD-stripe-billing.md +9 -0
- package/docs/PRD-workspace-cleanup.md +9 -0
- package/docs/PRD-workspace-fixes.md +9 -0
- package/docs/SUBAGENT-NAMING.md +9 -0
- package/docs/enterprise-deployment.md +6 -0
- package/email-templates/{README.md → email-templates-README.md} +9 -0
- package/landing/DESIGN.md +9 -0
- package/package.json +2 -2
- package/scripts/SYMBOT-FIX.md +9 -0
- package/src/credentials.ts +150 -2
- package/src/execute.ts +306 -1
- package/test-legacy-apis.sh +51 -0
- package/convex/adminActivate.d.ts +0 -3
- package/convex/adminActivate.js +0 -47
- package/convex/adminActivate.js.map +0 -1
- package/convex/adminStats.d.ts +0 -3
- package/convex/adminStats.js +0 -42
- package/convex/adminStats.js.map +0 -1
- package/convex/agents.d.ts +0 -54
- package/convex/agents.js +0 -499
- package/convex/agents.js.map +0 -1
- package/convex/analytics.d.ts +0 -5
- package/convex/analytics.js +0 -166
- package/convex/analytics.js.map +0 -1
- package/convex/billing.d.ts +0 -88
- package/convex/billing.js +0 -655
- package/convex/billing.js.map +0 -1
- package/convex/capabilities.d.ts +0 -9
- package/convex/capabilities.js +0 -145
- package/convex/capabilities.js.map +0 -1
- package/convex/chains.d.ts +0 -67
- package/convex/chains.js +0 -1042
- package/convex/chains.js.map +0 -1
- package/convex/credits.d.ts +0 -25
- package/convex/credits.js +0 -186
- package/convex/credits.js.map +0 -1
- package/convex/crons.d.ts +0 -3
- package/convex/crons.js +0 -17
- package/convex/crons.js.map +0 -1
- package/convex/directCall.d.ts +0 -72
- package/convex/directCall.js +0 -627
- package/convex/directCall.js.map +0 -1
- package/convex/earnProgress.d.ts +0 -58
- package/convex/earnProgress.js +0 -649
- package/convex/earnProgress.js.map +0 -1
- package/convex/email.d.ts +0 -14
- package/convex/email.js +0 -300
- package/convex/email.js.map +0 -1
- package/convex/feedback.d.ts +0 -7
- package/convex/feedback.js +0 -227
- package/convex/feedback.js.map +0 -1
- package/convex/http.d.ts +0 -3
- package/convex/http.js +0 -1106
- package/convex/http.js.map +0 -1
- package/convex/http.ts.bak +0 -934
- package/convex/logs.d.ts +0 -38
- package/convex/logs.js +0 -487
- package/convex/logs.js.map +0 -1
- package/convex/mou.d.ts +0 -6
- package/convex/mou.js +0 -82
- package/convex/mou.js.map +0 -1
- package/convex/providerKeys.d.ts +0 -31
- package/convex/providerKeys.js +0 -257
- package/convex/providerKeys.js.map +0 -1
- package/convex/providers.d.ts +0 -29
- package/convex/providers.js +0 -756
- package/convex/providers.js.map +0 -1
- package/convex/purchases.d.ts +0 -7
- package/convex/purchases.js +0 -157
- package/convex/purchases.js.map +0 -1
- package/convex/ratelimit.d.ts +0 -4
- package/convex/ratelimit.js +0 -91
- package/convex/ratelimit.js.map +0 -1
- package/convex/searchLogs.d.ts +0 -4
- package/convex/searchLogs.js +0 -129
- package/convex/searchLogs.js.map +0 -1
- package/convex/spendAlerts.d.ts +0 -36
- package/convex/spendAlerts.js +0 -380
- package/convex/spendAlerts.js.map +0 -1
- package/convex/stripeActions.d.ts +0 -19
- package/convex/stripeActions.js +0 -411
- package/convex/stripeActions.js.map +0 -1
- package/convex/teams.d.ts +0 -21
- package/convex/teams.js +0 -215
- package/convex/teams.js.map +0 -1
- package/convex/telemetry.d.ts +0 -4
- package/convex/telemetry.js +0 -74
- package/convex/telemetry.js.map +0 -1
- package/convex/usage.d.ts +0 -27
- package/convex/usage.js +0 -229
- package/convex/usage.js.map +0 -1
- package/convex/waitlist.d.ts +0 -4
- package/convex/waitlist.js +0 -49
- package/convex/waitlist.js.map +0 -1
- package/convex/webhooks.d.ts +0 -12
- package/convex/webhooks.js +0 -410
- package/convex/webhooks.js.map +0 -1
- package/convex/workspaces.d.ts +0 -29
- package/convex/workspaces.js +0 -880
- package/convex/workspaces.js.map +0 -1
- package/dist/registry/apis.json.bak +0 -248811
- package/src/registry/apis.json.bak +0 -248811
package/convex/http.ts
CHANGED
|
@@ -998,6 +998,321 @@ http.route({
|
|
|
998
998
|
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
999
999
|
});
|
|
1000
1000
|
|
|
1001
|
+
// ==============================================
|
|
1002
|
+
// REPLICATE (ML Models) PROXY
|
|
1003
|
+
// ==============================================
|
|
1004
|
+
http.route({
|
|
1005
|
+
path: "/proxy/replicate",
|
|
1006
|
+
method: "POST",
|
|
1007
|
+
handler: httpAction(async (ctx, request) => {
|
|
1008
|
+
await validateAndLogProxyCall(ctx, request, "replicate", "prediction");
|
|
1009
|
+
const REPLICATE_KEY = process.env.REPLICATE_API_TOKEN;
|
|
1010
|
+
if (!REPLICATE_KEY) {
|
|
1011
|
+
return jsonResponse({ error: "Replicate not configured" }, 500);
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
const body = await request.json();
|
|
1015
|
+
const { model, input, version } = body;
|
|
1016
|
+
if (!model && !version) {
|
|
1017
|
+
return jsonResponse({ error: "model or version required" }, 400);
|
|
1018
|
+
}
|
|
1019
|
+
const endpoint = version
|
|
1020
|
+
? "https://api.replicate.com/v1/predictions"
|
|
1021
|
+
: `https://api.replicate.com/v1/models/${model}/predictions`;
|
|
1022
|
+
const payload = version ? { version, input } : { input };
|
|
1023
|
+
const response = await fetch(endpoint, {
|
|
1024
|
+
method: "POST",
|
|
1025
|
+
headers: {
|
|
1026
|
+
Authorization: `Bearer ${REPLICATE_KEY}`,
|
|
1027
|
+
"Content-Type": "application/json",
|
|
1028
|
+
Prefer: "wait",
|
|
1029
|
+
},
|
|
1030
|
+
body: JSON.stringify(payload),
|
|
1031
|
+
});
|
|
1032
|
+
const data = await response.json();
|
|
1033
|
+
return jsonResponse(data, response.status);
|
|
1034
|
+
} catch (e: any) {
|
|
1035
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1036
|
+
}
|
|
1037
|
+
}),
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
http.route({
|
|
1041
|
+
path: "/proxy/replicate",
|
|
1042
|
+
method: "OPTIONS",
|
|
1043
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// ==============================================
|
|
1047
|
+
// DEEPGRAM (Speech-to-Text) PROXY
|
|
1048
|
+
// ==============================================
|
|
1049
|
+
http.route({
|
|
1050
|
+
path: "/proxy/deepgram",
|
|
1051
|
+
method: "POST",
|
|
1052
|
+
handler: httpAction(async (ctx, request) => {
|
|
1053
|
+
await validateAndLogProxyCall(ctx, request, "deepgram", "transcribe");
|
|
1054
|
+
const DEEPGRAM_KEY = process.env.DEEPGRAM_API_KEY;
|
|
1055
|
+
if (!DEEPGRAM_KEY) {
|
|
1056
|
+
return jsonResponse({ error: "Deepgram not configured" }, 500);
|
|
1057
|
+
}
|
|
1058
|
+
try {
|
|
1059
|
+
const body = await request.json();
|
|
1060
|
+
const { url, model = "nova-3", language = "en", smart_format = true } = body;
|
|
1061
|
+
if (!url) {
|
|
1062
|
+
return jsonResponse({ error: "url required (audio file URL)" }, 400);
|
|
1063
|
+
}
|
|
1064
|
+
const params = new URLSearchParams({
|
|
1065
|
+
model,
|
|
1066
|
+
language,
|
|
1067
|
+
smart_format: String(smart_format),
|
|
1068
|
+
});
|
|
1069
|
+
const response = await fetch(
|
|
1070
|
+
`https://api.deepgram.com/v1/listen?${params}`,
|
|
1071
|
+
{
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
headers: {
|
|
1074
|
+
Authorization: `Token ${DEEPGRAM_KEY}`,
|
|
1075
|
+
"Content-Type": "application/json",
|
|
1076
|
+
},
|
|
1077
|
+
body: JSON.stringify({ url }),
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
const data = await response.json();
|
|
1081
|
+
return jsonResponse(data, response.status);
|
|
1082
|
+
} catch (e: any) {
|
|
1083
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1084
|
+
}
|
|
1085
|
+
}),
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
http.route({
|
|
1089
|
+
path: "/proxy/deepgram",
|
|
1090
|
+
method: "OPTIONS",
|
|
1091
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
// ==============================================
|
|
1095
|
+
// E2B (Code Sandbox) PROXY
|
|
1096
|
+
// ==============================================
|
|
1097
|
+
http.route({
|
|
1098
|
+
path: "/proxy/e2b",
|
|
1099
|
+
method: "POST",
|
|
1100
|
+
handler: httpAction(async (ctx, request) => {
|
|
1101
|
+
await validateAndLogProxyCall(ctx, request, "e2b", "execute");
|
|
1102
|
+
const E2B_KEY = process.env.E2B_API_KEY;
|
|
1103
|
+
if (!E2B_KEY) {
|
|
1104
|
+
return jsonResponse({ error: "E2B not configured" }, 500);
|
|
1105
|
+
}
|
|
1106
|
+
try {
|
|
1107
|
+
const body = await request.json();
|
|
1108
|
+
const { code, language = "python", template = "base" } = body;
|
|
1109
|
+
if (!code) {
|
|
1110
|
+
return jsonResponse({ error: "code required" }, 400);
|
|
1111
|
+
}
|
|
1112
|
+
const response = await fetch("https://api.e2b.dev/sandboxes", {
|
|
1113
|
+
method: "POST",
|
|
1114
|
+
headers: {
|
|
1115
|
+
"X-API-Key": E2B_KEY,
|
|
1116
|
+
"Content-Type": "application/json",
|
|
1117
|
+
},
|
|
1118
|
+
body: JSON.stringify({ templateID: template, metadata: { language } }),
|
|
1119
|
+
});
|
|
1120
|
+
const sandbox = await response.json();
|
|
1121
|
+
if (!response.ok) {
|
|
1122
|
+
return jsonResponse(sandbox, response.status);
|
|
1123
|
+
}
|
|
1124
|
+
const execResponse = await fetch(
|
|
1125
|
+
`https://api.e2b.dev/sandboxes/${sandbox.sandboxID}/code/execution`,
|
|
1126
|
+
{
|
|
1127
|
+
method: "POST",
|
|
1128
|
+
headers: {
|
|
1129
|
+
"X-API-Key": E2B_KEY,
|
|
1130
|
+
"Content-Type": "application/json",
|
|
1131
|
+
},
|
|
1132
|
+
body: JSON.stringify({ code, language }),
|
|
1133
|
+
}
|
|
1134
|
+
);
|
|
1135
|
+
const result = await execResponse.json();
|
|
1136
|
+
return jsonResponse(result, execResponse.status);
|
|
1137
|
+
} catch (e: any) {
|
|
1138
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1139
|
+
}
|
|
1140
|
+
}),
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
http.route({
|
|
1144
|
+
path: "/proxy/e2b",
|
|
1145
|
+
method: "OPTIONS",
|
|
1146
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// ==============================================
|
|
1150
|
+
// TOGETHER AI (Open-source LLM Inference) PROXY
|
|
1151
|
+
// ==============================================
|
|
1152
|
+
http.route({
|
|
1153
|
+
path: "/proxy/together",
|
|
1154
|
+
method: "POST",
|
|
1155
|
+
handler: httpAction(async (ctx, request) => {
|
|
1156
|
+
await validateAndLogProxyCall(ctx, request, "together", "chat");
|
|
1157
|
+
const TOGETHER_KEY = process.env.TOGETHER_API_KEY;
|
|
1158
|
+
if (!TOGETHER_KEY) {
|
|
1159
|
+
return jsonResponse({ error: "Together AI not configured" }, 500);
|
|
1160
|
+
}
|
|
1161
|
+
try {
|
|
1162
|
+
const body = await request.json();
|
|
1163
|
+
const { model = "meta-llama/Llama-3.3-70B-Instruct-Turbo", messages, temperature = 0.7, max_tokens = 1024 } = body;
|
|
1164
|
+
if (!messages || !Array.isArray(messages)) {
|
|
1165
|
+
return jsonResponse({ error: "messages array required" }, 400);
|
|
1166
|
+
}
|
|
1167
|
+
const response = await fetch("https://api.together.xyz/v1/chat/completions", {
|
|
1168
|
+
method: "POST",
|
|
1169
|
+
headers: {
|
|
1170
|
+
Authorization: `Bearer ${TOGETHER_KEY}`,
|
|
1171
|
+
"Content-Type": "application/json",
|
|
1172
|
+
},
|
|
1173
|
+
body: JSON.stringify({ model, messages, temperature, max_tokens }),
|
|
1174
|
+
});
|
|
1175
|
+
const data = await response.json();
|
|
1176
|
+
return jsonResponse(data, response.status);
|
|
1177
|
+
} catch (e: any) {
|
|
1178
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1179
|
+
}
|
|
1180
|
+
}),
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
http.route({
|
|
1184
|
+
path: "/proxy/together",
|
|
1185
|
+
method: "OPTIONS",
|
|
1186
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
// ==============================================
|
|
1190
|
+
// STABILITY AI (Image Generation) PROXY
|
|
1191
|
+
// ==============================================
|
|
1192
|
+
http.route({
|
|
1193
|
+
path: "/proxy/stability",
|
|
1194
|
+
method: "POST",
|
|
1195
|
+
handler: httpAction(async (ctx, request) => {
|
|
1196
|
+
await validateAndLogProxyCall(ctx, request, "stability", "generate");
|
|
1197
|
+
const STABILITY_KEY = process.env.STABILITY_API_KEY;
|
|
1198
|
+
if (!STABILITY_KEY) {
|
|
1199
|
+
return jsonResponse({ error: "Stability AI not configured" }, 500);
|
|
1200
|
+
}
|
|
1201
|
+
try {
|
|
1202
|
+
const body = await request.json();
|
|
1203
|
+
const { prompt, model = "sd3.5-large", output_format = "png", aspect_ratio = "1:1" } = body;
|
|
1204
|
+
if (!prompt) {
|
|
1205
|
+
return jsonResponse({ error: "prompt required" }, 400);
|
|
1206
|
+
}
|
|
1207
|
+
const formData = new FormData();
|
|
1208
|
+
formData.append("prompt", prompt);
|
|
1209
|
+
formData.append("output_format", output_format);
|
|
1210
|
+
formData.append("aspect_ratio", aspect_ratio);
|
|
1211
|
+
const response = await fetch(
|
|
1212
|
+
`https://api.stability.ai/v2beta/stable-image/generate/${model}`,
|
|
1213
|
+
{
|
|
1214
|
+
method: "POST",
|
|
1215
|
+
headers: {
|
|
1216
|
+
Authorization: `Bearer ${STABILITY_KEY}`,
|
|
1217
|
+
Accept: "application/json",
|
|
1218
|
+
},
|
|
1219
|
+
body: formData,
|
|
1220
|
+
}
|
|
1221
|
+
);
|
|
1222
|
+
const data = await response.json();
|
|
1223
|
+
return jsonResponse(data, response.status);
|
|
1224
|
+
} catch (e: any) {
|
|
1225
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1226
|
+
}
|
|
1227
|
+
}),
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
http.route({
|
|
1231
|
+
path: "/proxy/stability",
|
|
1232
|
+
method: "OPTIONS",
|
|
1233
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
// ==============================================
|
|
1237
|
+
// ASSEMBLYAI (Audio Intelligence) PROXY
|
|
1238
|
+
// ==============================================
|
|
1239
|
+
http.route({
|
|
1240
|
+
path: "/proxy/assemblyai",
|
|
1241
|
+
method: "POST",
|
|
1242
|
+
handler: httpAction(async (ctx, request) => {
|
|
1243
|
+
await validateAndLogProxyCall(ctx, request, "assemblyai", "transcribe");
|
|
1244
|
+
const ASSEMBLYAI_KEY = process.env.ASSEMBLYAI_API_KEY;
|
|
1245
|
+
if (!ASSEMBLYAI_KEY) {
|
|
1246
|
+
return jsonResponse({ error: "AssemblyAI not configured" }, 500);
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
const body = await request.json();
|
|
1250
|
+
const { audio_url, language_detection = true, speaker_labels = true } = body;
|
|
1251
|
+
if (!audio_url) {
|
|
1252
|
+
return jsonResponse({ error: "audio_url required" }, 400);
|
|
1253
|
+
}
|
|
1254
|
+
const response = await fetch("https://api.assemblyai.com/v2/transcript", {
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
headers: {
|
|
1257
|
+
Authorization: ASSEMBLYAI_KEY,
|
|
1258
|
+
"Content-Type": "application/json",
|
|
1259
|
+
},
|
|
1260
|
+
body: JSON.stringify({ audio_url, language_detection, speaker_labels }),
|
|
1261
|
+
});
|
|
1262
|
+
const data = await response.json();
|
|
1263
|
+
return jsonResponse(data, response.status);
|
|
1264
|
+
} catch (e: any) {
|
|
1265
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1266
|
+
}
|
|
1267
|
+
}),
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
http.route({
|
|
1271
|
+
path: "/proxy/assemblyai",
|
|
1272
|
+
method: "OPTIONS",
|
|
1273
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
// ==============================================
|
|
1277
|
+
// APILAYER (Multi-API: Exchange, Stocks, Aviation, etc.) PROXY
|
|
1278
|
+
// ==============================================
|
|
1279
|
+
http.route({
|
|
1280
|
+
path: "/proxy/apilayer",
|
|
1281
|
+
method: "POST",
|
|
1282
|
+
handler: httpAction(async (ctx, request) => {
|
|
1283
|
+
await validateAndLogProxyCall(ctx, request, "apilayer", "call");
|
|
1284
|
+
const APILAYER_KEY = process.env.APILAYER_API_KEY;
|
|
1285
|
+
if (!APILAYER_KEY) {
|
|
1286
|
+
return jsonResponse({ error: "APILayer not configured" }, 500);
|
|
1287
|
+
}
|
|
1288
|
+
try {
|
|
1289
|
+
const body = await request.json();
|
|
1290
|
+
const { service, endpoint, params = {} } = body;
|
|
1291
|
+
if (!service || !endpoint) {
|
|
1292
|
+
return jsonResponse({ error: "service and endpoint required (e.g. service:'exchangerates', endpoint:'/latest')" }, 400);
|
|
1293
|
+
}
|
|
1294
|
+
const queryString = new URLSearchParams(params).toString();
|
|
1295
|
+
const url = `https://api.apilayer.com/${service}${endpoint}${queryString ? '?' + queryString : ''}`;
|
|
1296
|
+
const response = await fetch(url, {
|
|
1297
|
+
method: "GET",
|
|
1298
|
+
headers: {
|
|
1299
|
+
apikey: APILAYER_KEY,
|
|
1300
|
+
},
|
|
1301
|
+
});
|
|
1302
|
+
const data = await response.json();
|
|
1303
|
+
return jsonResponse(data, response.status);
|
|
1304
|
+
} catch (e: any) {
|
|
1305
|
+
return jsonResponse({ error: e.message }, 500);
|
|
1306
|
+
}
|
|
1307
|
+
}),
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
http.route({
|
|
1311
|
+
path: "/proxy/apilayer",
|
|
1312
|
+
method: "OPTIONS",
|
|
1313
|
+
handler: httpAction(async () => new Response(null, { headers: corsHeaders })),
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1001
1316
|
// ==============================================
|
|
1002
1317
|
// WORKSPACE / MAGIC LINK ENDPOINTS
|
|
1003
1318
|
// ==============================================
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { mutation } from "./_generated/server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Seed Pratham's workspace with all 27 APILayer APIs
|
|
5
|
+
* Run with: npx convex run seedPratham:seedPrathamWorkspace
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const seedPrathamWorkspace = mutation({
|
|
9
|
+
args: {},
|
|
10
|
+
handler: async (ctx) => {
|
|
11
|
+
const email = "gustav_hemmingsson@hotmail.com"; // Gustav's personal email for testing
|
|
12
|
+
|
|
13
|
+
// 1. Create or get workspace
|
|
14
|
+
let workspace = await ctx.db
|
|
15
|
+
.query("workspaces")
|
|
16
|
+
.withIndex("by_email", (q) => q.eq("email", email))
|
|
17
|
+
.first();
|
|
18
|
+
|
|
19
|
+
if (!workspace) {
|
|
20
|
+
const workspaceId = await ctx.db.insert("workspaces", {
|
|
21
|
+
email,
|
|
22
|
+
status: "active",
|
|
23
|
+
tier: "backer", // Give him backer tier (unlimited)
|
|
24
|
+
usageCount: 0,
|
|
25
|
+
usageLimit: -1, // unlimited
|
|
26
|
+
weeklyUsageCount: 0,
|
|
27
|
+
weeklyUsageLimit: -1, // unlimited
|
|
28
|
+
hourlyUsageCount: 0,
|
|
29
|
+
backerUntil: new Date("2027-12-31").getTime(), // Backer until end of 2027
|
|
30
|
+
mainAgentName: "APILayer Dashboard",
|
|
31
|
+
createdAt: Date.now(),
|
|
32
|
+
updatedAt: Date.now(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
workspace = await ctx.db.get(workspaceId);
|
|
36
|
+
console.log(`✓ Created workspace for ${email}`);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(`✓ Workspace exists for ${email}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!workspace) throw new Error("Failed to create workspace");
|
|
42
|
+
|
|
43
|
+
// 2. Create provider profile
|
|
44
|
+
let provider = await ctx.db
|
|
45
|
+
.query("providers")
|
|
46
|
+
.withIndex("by_email", (q) => q.eq("email", email))
|
|
47
|
+
.first();
|
|
48
|
+
|
|
49
|
+
if (!provider) {
|
|
50
|
+
const providerId = await ctx.db.insert("providers", {
|
|
51
|
+
email,
|
|
52
|
+
name: "Pratham (APILayer)",
|
|
53
|
+
company: "APILayer",
|
|
54
|
+
website: "https://apilayer.com",
|
|
55
|
+
status: "approved",
|
|
56
|
+
stripeOnboardingComplete: false,
|
|
57
|
+
createdAt: Date.now(),
|
|
58
|
+
updatedAt: Date.now(),
|
|
59
|
+
approvedAt: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
provider = await ctx.db.get(providerId);
|
|
63
|
+
console.log(`✓ Created provider profile`);
|
|
64
|
+
} else {
|
|
65
|
+
console.log(`✓ Provider profile exists`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!provider) throw new Error("Failed to create provider");
|
|
69
|
+
|
|
70
|
+
// 3. Create all 27 APILayer APIs
|
|
71
|
+
const apis = [
|
|
72
|
+
// Unified APIs (14)
|
|
73
|
+
{ name: "ExchangeRate API", description: "Real-time exchange rates", category: "finance", action: "exchange_rates" },
|
|
74
|
+
{ name: "Marketstack", description: "Real-time market data", category: "finance", action: "market_data" },
|
|
75
|
+
{ name: "AviationStack", description: "Flight data API", category: "geolocation", action: "aviation" },
|
|
76
|
+
{ name: "PDF Layer", description: "HTML to PDF conversion", category: "business", action: "pdf_generate" },
|
|
77
|
+
{ name: "Screenshot Layer", description: "Website screenshots", category: "marketing", action: "screenshot" },
|
|
78
|
+
{ name: "Email Verification API", description: "Verify email addresses", category: "devtools", action: "verify_email" },
|
|
79
|
+
{ name: "Number Verification API", description: "Validate phone numbers", category: "devtools", action: "verify_number" },
|
|
80
|
+
{ name: "VAT Layer", description: "VAT number validation", category: "finance", action: "vat_check" },
|
|
81
|
+
{ name: "World News API", description: "Extract news from URLs", category: "news", action: "world_news" },
|
|
82
|
+
{ name: "Finance News API", description: "Financial news feed", category: "news", action: "finance_news" },
|
|
83
|
+
{ name: "Advanced Scraper API", description: "Web scraping", category: "scraping", action: "scrape" },
|
|
84
|
+
{ name: "Image Crop API", description: "Smart image cropping", category: "marketing", action: "image_crop" },
|
|
85
|
+
{ name: "Skills API", description: "Skill database search", category: "devtools", action: "skills" },
|
|
86
|
+
{ name: "Form API", description: "Form submission handling", category: "devtools", action: "form_submit" },
|
|
87
|
+
|
|
88
|
+
// Legacy APIs (13)
|
|
89
|
+
{ name: "Fixer API", description: "Currency conversion (legacy)", category: "finance", action: "fixer_convert" },
|
|
90
|
+
{ name: "Fixer Latest Rates", description: "Latest exchange rates", category: "finance", action: "fixer_latest" },
|
|
91
|
+
{ name: "Currencylayer Live", description: "Live currency rates", category: "finance", action: "currencylayer_live" },
|
|
92
|
+
{ name: "Currencylayer Convert", description: "Currency conversion", category: "finance", action: "currencylayer_convert" },
|
|
93
|
+
{ name: "Coinlayer", description: "Crypto exchange rates", category: "finance", action: "coinlayer_live" },
|
|
94
|
+
{ name: "Exchangerate.host", description: "Exchange rates API", category: "finance", action: "exchangeratehost_latest" },
|
|
95
|
+
{ name: "Weatherstack Current", description: "Current weather data", category: "geolocation", action: "weatherstack_current" },
|
|
96
|
+
{ name: "Weatherstack Forecast", description: "Weather forecasts", category: "geolocation", action: "weatherstack_forecast" },
|
|
97
|
+
{ name: "IPstack", description: "IP geolocation lookup", category: "geolocation", action: "ipstack_lookup" },
|
|
98
|
+
{ name: "IPapi", description: "IP address lookup", category: "geolocation", action: "ipapi_lookup" },
|
|
99
|
+
{ name: "Positionstack Forward", description: "Geocoding (address → coords)", category: "geolocation", action: "positionstack_forward" },
|
|
100
|
+
{ name: "Positionstack Reverse", description: "Reverse geocoding (coords → address)", category: "geolocation", action: "positionstack_reverse" },
|
|
101
|
+
{ name: "Languagelayer", description: "Language detection", category: "devtools", action: "languagelayer_detect" },
|
|
102
|
+
{ name: "Scrapestack", description: "Web scraping service", category: "scraping", action: "scrapestack_scrape" },
|
|
103
|
+
{ name: "Serpstack", description: "Google search results", category: "scraping", action: "serpstack_search" },
|
|
104
|
+
{ name: "Mediastack", description: "News and blog articles", category: "news", action: "mediastack_news" },
|
|
105
|
+
{ name: "Userstack", description: "User agent detection", category: "devtools", action: "userstack_detect" },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
let createdCount = 0;
|
|
109
|
+
let existingCount = 0;
|
|
110
|
+
|
|
111
|
+
for (const api of apis) {
|
|
112
|
+
// Check if already exists
|
|
113
|
+
const existing = await ctx.db
|
|
114
|
+
.query("providerAPIs")
|
|
115
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider!._id))
|
|
116
|
+
.filter((q) => q.eq(q.field("name"), api.name))
|
|
117
|
+
.first();
|
|
118
|
+
|
|
119
|
+
if (!existing) {
|
|
120
|
+
await ctx.db.insert("providerAPIs", {
|
|
121
|
+
providerId: provider._id,
|
|
122
|
+
name: api.name,
|
|
123
|
+
description: api.description,
|
|
124
|
+
category: api.category,
|
|
125
|
+
pricingModel: "freemium",
|
|
126
|
+
pricingNotes: "Free tier available, paid tiers for higher limits",
|
|
127
|
+
status: "approved",
|
|
128
|
+
createdAt: Date.now(),
|
|
129
|
+
approvedAt: Date.now(),
|
|
130
|
+
discoveryCount: 0,
|
|
131
|
+
});
|
|
132
|
+
createdCount++;
|
|
133
|
+
} else {
|
|
134
|
+
existingCount++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(`✓ APIs: ${createdCount} created, ${existingCount} already existed`);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
workspace: {
|
|
143
|
+
email: workspace.email,
|
|
144
|
+
tier: workspace.tier,
|
|
145
|
+
id: workspace._id,
|
|
146
|
+
},
|
|
147
|
+
provider: {
|
|
148
|
+
name: provider.name,
|
|
149
|
+
company: provider.company,
|
|
150
|
+
id: provider._id,
|
|
151
|
+
},
|
|
152
|
+
apis: {
|
|
153
|
+
total: apis.length,
|
|
154
|
+
created: createdCount,
|
|
155
|
+
existing: existingCount,
|
|
156
|
+
},
|
|
157
|
+
dashboardUrl: `https://apiclaw.nordsym.com/workspace`,
|
|
158
|
+
loginInstructions: "Test account using Gustav's personal email. Magic link will be sent to gustav_hemmingsson@hotmail.com",
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAmS5C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAIxE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CA4E9D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEnE"}
|
package/dist/credentials.js
CHANGED
|
@@ -150,18 +150,116 @@ const providers = {
|
|
|
150
150
|
type: 'api_key',
|
|
151
151
|
get() {
|
|
152
152
|
const env = loadEnvFile('apilayer.env');
|
|
153
|
-
|
|
153
|
+
const legacy = loadEnvFile('apilayer-legacy.env');
|
|
154
|
+
// Merge unified + legacy keys
|
|
154
155
|
const keys = {};
|
|
156
|
+
// Unified APILayer keys (APILAYER_*)
|
|
155
157
|
for (const [k, v] of Object.entries(env)) {
|
|
156
158
|
if (k.startsWith('APILAYER_') && v) {
|
|
157
159
|
keys[k] = v;
|
|
158
160
|
}
|
|
159
161
|
}
|
|
162
|
+
// Legacy keys (separate API domains)
|
|
163
|
+
for (const [k, v] of Object.entries(legacy)) {
|
|
164
|
+
if (v && k.endsWith('_API_KEY')) {
|
|
165
|
+
keys[k] = v;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
160
168
|
if (Object.keys(keys).length === 0)
|
|
161
169
|
return null;
|
|
162
170
|
return { type: 'api_key', api_key: keys.APILAYER_EXCHANGERATE_KEY || '', ...keys };
|
|
163
171
|
},
|
|
164
172
|
},
|
|
173
|
+
groq: {
|
|
174
|
+
type: 'bearer',
|
|
175
|
+
get() {
|
|
176
|
+
const env = loadEnvFile('groq.env');
|
|
177
|
+
const key = env.GROQ_API_KEY || process.env.GROQ_API_KEY;
|
|
178
|
+
if (key) {
|
|
179
|
+
return { type: 'bearer', api_key: key };
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
mistral: {
|
|
185
|
+
type: 'bearer',
|
|
186
|
+
get() {
|
|
187
|
+
const env = loadEnvFile('mistral.env');
|
|
188
|
+
const key = env.MISTRAL_API_KEY || process.env.MISTRAL_API_KEY;
|
|
189
|
+
if (key) {
|
|
190
|
+
return { type: 'bearer', api_key: key };
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
cohere: {
|
|
196
|
+
type: 'bearer',
|
|
197
|
+
get() {
|
|
198
|
+
const env = loadEnvFile('cohere.env');
|
|
199
|
+
const key = env.COHERE_API_KEY || process.env.COHERE_API_KEY;
|
|
200
|
+
if (key) {
|
|
201
|
+
return { type: 'bearer', api_key: key };
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
together: {
|
|
207
|
+
type: 'bearer',
|
|
208
|
+
get() {
|
|
209
|
+
const env = loadEnvFile('together.env');
|
|
210
|
+
const key = env.TOGETHER_API_KEY || process.env.TOGETHER_API_KEY;
|
|
211
|
+
if (key) {
|
|
212
|
+
return { type: 'bearer', api_key: key };
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
stability: {
|
|
218
|
+
type: 'api_key',
|
|
219
|
+
get() {
|
|
220
|
+
const env = loadEnvFile('stability.env');
|
|
221
|
+
const key = env.STABILITY_API_KEY || process.env.STABILITY_API_KEY;
|
|
222
|
+
if (key) {
|
|
223
|
+
return { type: 'api_key', api_key: key };
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
deepgram: {
|
|
229
|
+
type: 'api_key',
|
|
230
|
+
get() {
|
|
231
|
+
const env = loadEnvFile('deepgram.env');
|
|
232
|
+
const key = env.DEEPGRAM_API_KEY || process.env.DEEPGRAM_API_KEY;
|
|
233
|
+
if (key) {
|
|
234
|
+
return { type: 'api_key', api_key: key };
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
assemblyai: {
|
|
240
|
+
type: 'api_key',
|
|
241
|
+
get() {
|
|
242
|
+
const env = loadEnvFile('assemblyai.env');
|
|
243
|
+
const key = env.ASSEMBLYAI_API_KEY || process.env.ASSEMBLYAI_API_KEY;
|
|
244
|
+
if (key) {
|
|
245
|
+
return { type: 'api_key', api_key: key };
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
serper: {
|
|
251
|
+
type: 'api_key',
|
|
252
|
+
get() {
|
|
253
|
+
// Note: local file is serpapi.env for historical reasons
|
|
254
|
+
const env = loadEnvFile('serper.env');
|
|
255
|
+
const envAlt = loadEnvFile('serpapi.env');
|
|
256
|
+
const key = env.SERPER_API_KEY || envAlt.SERPAPI_API_KEY || envAlt.SERPER_API_KEY || process.env.SERPER_API_KEY;
|
|
257
|
+
if (key) {
|
|
258
|
+
return { type: 'api_key', api_key: key };
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
},
|
|
262
|
+
},
|
|
165
263
|
};
|
|
166
264
|
/**
|
|
167
265
|
* Get real credentials for a provider
|
|
@@ -211,7 +309,43 @@ export function hasRealCredentials(providerId) {
|
|
|
211
309
|
}
|
|
212
310
|
if (providerId === 'apilayer') {
|
|
213
311
|
const env = loadEnvFile('apilayer.env');
|
|
214
|
-
|
|
312
|
+
const legacy = loadEnvFile('apilayer-legacy.env');
|
|
313
|
+
return !!(env.APILAYER_EXCHANGERATE_KEY ||
|
|
314
|
+
process.env.APILAYER_EXCHANGERATE_KEY ||
|
|
315
|
+
Object.keys(legacy).length > 0);
|
|
316
|
+
}
|
|
317
|
+
if (providerId === 'groq') {
|
|
318
|
+
const env = loadEnvFile('groq.env');
|
|
319
|
+
return !!(env.GROQ_API_KEY || process.env.GROQ_API_KEY);
|
|
320
|
+
}
|
|
321
|
+
if (providerId === 'mistral') {
|
|
322
|
+
const env = loadEnvFile('mistral.env');
|
|
323
|
+
return !!(env.MISTRAL_API_KEY || process.env.MISTRAL_API_KEY);
|
|
324
|
+
}
|
|
325
|
+
if (providerId === 'cohere') {
|
|
326
|
+
const env = loadEnvFile('cohere.env');
|
|
327
|
+
return !!(env.COHERE_API_KEY || process.env.COHERE_API_KEY);
|
|
328
|
+
}
|
|
329
|
+
if (providerId === 'together') {
|
|
330
|
+
const env = loadEnvFile('together.env');
|
|
331
|
+
return !!(env.TOGETHER_API_KEY || process.env.TOGETHER_API_KEY);
|
|
332
|
+
}
|
|
333
|
+
if (providerId === 'stability') {
|
|
334
|
+
const env = loadEnvFile('stability.env');
|
|
335
|
+
return !!(env.STABILITY_API_KEY || process.env.STABILITY_API_KEY);
|
|
336
|
+
}
|
|
337
|
+
if (providerId === 'deepgram') {
|
|
338
|
+
const env = loadEnvFile('deepgram.env');
|
|
339
|
+
return !!(env.DEEPGRAM_API_KEY || process.env.DEEPGRAM_API_KEY);
|
|
340
|
+
}
|
|
341
|
+
if (providerId === 'assemblyai') {
|
|
342
|
+
const env = loadEnvFile('assemblyai.env');
|
|
343
|
+
return !!(env.ASSEMBLYAI_API_KEY || process.env.ASSEMBLYAI_API_KEY);
|
|
344
|
+
}
|
|
345
|
+
if (providerId === 'serper') {
|
|
346
|
+
const env = loadEnvFile('serper.env');
|
|
347
|
+
const envAlt = loadEnvFile('serpapi.env');
|
|
348
|
+
return !!(env.SERPER_API_KEY || envAlt.SERPAPI_API_KEY || envAlt.SERPER_API_KEY || process.env.SERPER_API_KEY);
|
|
215
349
|
}
|
|
216
350
|
return false;
|
|
217
351
|
}
|