@hasna/connectors 0.1.0 → 0.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/LICENSE +191 -0
- package/README.md +19 -13
- package/bin/index.js +208 -73
- package/bin/mcp.js +24 -18
- package/bin/serve.js +94 -32
- package/connectors/connect-anthropic/package.json +2 -2
- package/connectors/connect-aws/package.json +2 -2
- package/connectors/connect-brandsight/package.json +2 -2
- package/connectors/connect-cloudflare/.env.example +0 -5
- package/connectors/connect-cloudflare/package.json +2 -2
- package/connectors/connect-discord/package.json +2 -2
- package/connectors/connect-docker/package.json +2 -2
- package/connectors/connect-e2b/package.json +2 -2
- package/connectors/connect-elevenlabs/package.json +2 -2
- package/connectors/connect-exa/package.json +2 -2
- package/connectors/connect-figma/package.json +2 -2
- package/connectors/connect-firecrawl/package.json +2 -2
- package/connectors/connect-github/package.json +2 -2
- package/connectors/connect-gmail/package.json +2 -2
- package/connectors/connect-google/package.json +2 -2
- package/connectors/connect-googlecalendar/package.json +2 -2
- package/connectors/connect-googlecloud/package.json +2 -2
- package/connectors/connect-googlecontacts/package.json +2 -2
- package/connectors/connect-googledocs/package.json +2 -2
- package/connectors/connect-googledrive/package.json +2 -2
- package/connectors/connect-googlegemini/package.json +2 -2
- package/connectors/connect-googlesheets/package.json +2 -2
- package/connectors/connect-googletasks/package.json +2 -2
- package/connectors/connect-hedra/package.json +2 -2
- package/connectors/connect-heygen/package.json +2 -2
- package/connectors/connect-huggingface/package.json +2 -2
- package/connectors/connect-icons8/package.json +2 -2
- package/connectors/connect-maropost/package.json +2 -2
- package/connectors/connect-mercury/package.json +2 -2
- package/connectors/connect-meta/package.json +2 -2
- package/connectors/connect-midjourney/package.json +2 -2
- package/connectors/connect-mistral/package.json +2 -2
- package/connectors/connect-mixpanel/package.json +2 -2
- package/connectors/connect-notion/.env.example +0 -5
- package/connectors/connect-notion/package.json +2 -2
- package/connectors/connect-openai/package.json +2 -2
- package/connectors/connect-openweathermap/package.json +2 -2
- package/connectors/connect-pandadoc/package.json +2 -2
- package/connectors/connect-quo/package.json +2 -2
- package/connectors/connect-reddit/package.json +2 -2
- package/connectors/connect-reducto/package.json +1 -1
- package/connectors/connect-resend/package.json +2 -2
- package/connectors/connect-revolut/package.json +2 -2
- package/connectors/connect-sedo/package.json +2 -2
- package/connectors/connect-sentry/package.json +2 -2
- package/connectors/connect-shadcn/package.json +2 -2
- package/connectors/connect-snap/package.json +2 -2
- package/connectors/connect-stabilityai/package.json +2 -2
- package/connectors/connect-stripe/package.json +2 -2
- package/connectors/connect-stripeatlas/package.json +2 -2
- package/connectors/connect-substack/package.json +2 -2
- package/connectors/connect-tiktok/package.json +2 -2
- package/connectors/connect-tinker/package.json +2 -2
- package/connectors/connect-twilio/package.json +4 -4
- package/connectors/connect-uspto/package.json +2 -2
- package/connectors/connect-x/package.json +2 -2
- package/connectors/connect-xads/package.json +2 -2
- package/connectors/connect-xai/package.json +2 -2
- package/connectors/connect-youtube/package.json +2 -2
- package/connectors/connect-zoom/package.json +2 -2
- package/dist/cli/cli.test.d.ts +1 -0
- package/dist/cli/components/App.d.ts +6 -0
- package/dist/cli/components/CategorySelect.d.ts +6 -0
- package/dist/cli/components/ConnectorSelect.d.ts +10 -0
- package/dist/cli/components/Header.d.ts +6 -0
- package/dist/cli/components/InstallProgress.d.ts +8 -0
- package/dist/cli/components/SearchView.d.ts +8 -0
- package/dist/cli/components/components.test.d.ts +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -16
- package/dist/lib/installer.d.ts +55 -0
- package/dist/lib/installer.test.d.ts +1 -0
- package/dist/lib/registry.d.ts +18 -0
- package/dist/lib/registry.test.d.ts +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/mcp.test.d.ts +1 -0
- package/dist/server/auth.d.ts +70 -0
- package/dist/server/dashboard.d.ts +5 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/serve.d.ts +12 -0
- package/dist/server/server.test.d.ts +1 -0
- package/package.json +5 -4
- package/connectors/connect-browseruse/.env.example +0 -7
- package/connectors/connect-browseruse/.npmrc.example +0 -2
- package/connectors/connect-browseruse/AGENTS.md +0 -172
- package/connectors/connect-browseruse/CLAUDE.md +0 -172
- package/connectors/connect-browseruse/GEMINI.md +0 -172
- package/connectors/connect-browseruse/README.md +0 -153
- package/connectors/connect-browseruse/package.json +0 -52
- package/connectors/connect-browseruse/src/api/billing.ts +0 -32
- package/connectors/connect-browseruse/src/api/browsers.ts +0 -50
- package/connectors/connect-browseruse/src/api/client.ts +0 -168
- package/connectors/connect-browseruse/src/api/files.ts +0 -70
- package/connectors/connect-browseruse/src/api/index.ts +0 -95
- package/connectors/connect-browseruse/src/api/profiles.ts +0 -59
- package/connectors/connect-browseruse/src/api/sessions.ts +0 -88
- package/connectors/connect-browseruse/src/api/skills.ts +0 -194
- package/connectors/connect-browseruse/src/api/tasks.ts +0 -127
- package/connectors/connect-browseruse/src/cli/index.ts +0 -888
- package/connectors/connect-browseruse/src/index.ts +0 -35
- package/connectors/connect-browseruse/src/types/index.ts +0 -312
- package/connectors/connect-browseruse/src/utils/config.ts +0 -212
- package/connectors/connect-browseruse/src/utils/output.ts +0 -119
- package/connectors/connect-browseruse/tsconfig.json +0 -16
package/bin/index.js
CHANGED
|
@@ -2060,13 +2060,6 @@ var init_registry = __esm(() => {
|
|
|
2060
2060
|
category: "Developer Tools",
|
|
2061
2061
|
tags: ["scraping", "web"]
|
|
2062
2062
|
},
|
|
2063
|
-
{
|
|
2064
|
-
name: "browseruse",
|
|
2065
|
-
displayName: "Browser Use",
|
|
2066
|
-
description: "Browser automation for AI",
|
|
2067
|
-
category: "Developer Tools",
|
|
2068
|
-
tags: ["browser", "automation"]
|
|
2069
|
-
},
|
|
2070
2063
|
{
|
|
2071
2064
|
name: "shadcn",
|
|
2072
2065
|
displayName: "shadcn/ui",
|
|
@@ -2336,9 +2329,9 @@ var init_registry = __esm(() => {
|
|
|
2336
2329
|
{
|
|
2337
2330
|
name: "tinker",
|
|
2338
2331
|
displayName: "Tinker",
|
|
2339
|
-
description: "
|
|
2340
|
-
category: "
|
|
2341
|
-
tags: ["
|
|
2332
|
+
description: "LLM fine-tuning and training API",
|
|
2333
|
+
category: "AI & ML",
|
|
2334
|
+
tags: ["ai", "llm", "fine-tuning"]
|
|
2342
2335
|
},
|
|
2343
2336
|
{
|
|
2344
2337
|
name: "sedo",
|
|
@@ -4005,7 +3998,7 @@ var require_cli_spinners = __commonJS((exports, module) => {
|
|
|
4005
3998
|
});
|
|
4006
3999
|
|
|
4007
4000
|
// src/lib/installer.ts
|
|
4008
|
-
import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
4001
|
+
import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync, rmSync } from "fs";
|
|
4009
4002
|
import { join as join2, dirname as dirname2 } from "path";
|
|
4010
4003
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4011
4004
|
function resolveConnectorsDir() {
|
|
@@ -4023,6 +4016,13 @@ function getConnectorPath(name) {
|
|
|
4023
4016
|
}
|
|
4024
4017
|
function installConnector(name, options = {}) {
|
|
4025
4018
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
4019
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
4020
|
+
return {
|
|
4021
|
+
connector: name,
|
|
4022
|
+
success: false,
|
|
4023
|
+
error: `Invalid connector name '${name}'`
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
4026
|
const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
|
|
4027
4027
|
const sourcePath = getConnectorPath(name);
|
|
4028
4028
|
const destDir = join2(targetDir, ".connectors");
|
|
@@ -4063,7 +4063,6 @@ function installConnector(name, options = {}) {
|
|
|
4063
4063
|
}
|
|
4064
4064
|
function updateConnectorsIndex(connectorsDir) {
|
|
4065
4065
|
const indexPath = join2(connectorsDir, "index.ts");
|
|
4066
|
-
const { readdirSync } = __require("fs");
|
|
4067
4066
|
const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
|
|
4068
4067
|
const exports = connectors.map((c) => {
|
|
4069
4068
|
const name = c.replace("connect-", "");
|
|
@@ -4084,7 +4083,6 @@ function getInstalledConnectors(targetDir = process.cwd()) {
|
|
|
4084
4083
|
if (!existsSync2(connectorsDir)) {
|
|
4085
4084
|
return [];
|
|
4086
4085
|
}
|
|
4087
|
-
const { readdirSync, statSync } = __require("fs");
|
|
4088
4086
|
return readdirSync(connectorsDir).filter((f) => {
|
|
4089
4087
|
const fullPath = join2(connectorsDir, f);
|
|
4090
4088
|
return f.startsWith("connect-") && statSync(fullPath).isDirectory();
|
|
@@ -4133,7 +4131,6 @@ function parseEnvVarsTable(section) {
|
|
|
4133
4131
|
return vars;
|
|
4134
4132
|
}
|
|
4135
4133
|
function removeConnector(name, targetDir = process.cwd()) {
|
|
4136
|
-
const { rmSync } = __require("fs");
|
|
4137
4134
|
const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
|
|
4138
4135
|
const connectorsDir = join2(targetDir, ".connectors");
|
|
4139
4136
|
const connectorPath = join2(connectorsDir, connectorName);
|
|
@@ -4152,6 +4149,7 @@ var init_installer = __esm(() => {
|
|
|
4152
4149
|
|
|
4153
4150
|
// src/server/auth.ts
|
|
4154
4151
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
4152
|
+
import { randomBytes } from "crypto";
|
|
4155
4153
|
import { homedir } from "os";
|
|
4156
4154
|
import { join as join3 } from "path";
|
|
4157
4155
|
function getAuthType(name) {
|
|
@@ -4249,14 +4247,22 @@ function saveApiKey(name, key, field) {
|
|
|
4249
4247
|
const profileFile = join3(configDir, "profiles", `${profile}.json`);
|
|
4250
4248
|
const profileDir = join3(configDir, "profiles", profile);
|
|
4251
4249
|
if (existsSync3(profileFile)) {
|
|
4252
|
-
|
|
4250
|
+
let config = {};
|
|
4251
|
+
try {
|
|
4252
|
+
config = JSON.parse(readFileSync3(profileFile, "utf-8"));
|
|
4253
|
+
} catch {}
|
|
4253
4254
|
config[keyField] = key;
|
|
4254
4255
|
writeFileSync2(profileFile, JSON.stringify(config, null, 2));
|
|
4255
4256
|
return;
|
|
4256
4257
|
}
|
|
4257
4258
|
if (existsSync3(profileDir)) {
|
|
4258
4259
|
const configFile = join3(profileDir, "config.json");
|
|
4259
|
-
|
|
4260
|
+
let config = {};
|
|
4261
|
+
if (existsSync3(configFile)) {
|
|
4262
|
+
try {
|
|
4263
|
+
config = JSON.parse(readFileSync3(configFile, "utf-8"));
|
|
4264
|
+
} catch {}
|
|
4265
|
+
}
|
|
4260
4266
|
config[keyField] = key;
|
|
4261
4267
|
writeFileSync2(configFile, JSON.stringify(config, null, 2));
|
|
4262
4268
|
return;
|
|
@@ -4300,16 +4306,33 @@ function getOAuthStartUrl(name, redirectUri) {
|
|
|
4300
4306
|
const scopes = GOOGLE_SCOPES[name];
|
|
4301
4307
|
if (!scopes)
|
|
4302
4308
|
return null;
|
|
4309
|
+
const state = randomBytes(32).toString("hex");
|
|
4310
|
+
oauthStateStore.set(state, { connector: name, createdAt: Date.now() });
|
|
4311
|
+
const tenMinutesAgo = Date.now() - 10 * 60 * 1000;
|
|
4312
|
+
for (const [key, val] of oauthStateStore) {
|
|
4313
|
+
if (val.createdAt < tenMinutesAgo)
|
|
4314
|
+
oauthStateStore.delete(key);
|
|
4315
|
+
}
|
|
4303
4316
|
const params = new URLSearchParams({
|
|
4304
4317
|
client_id: oauthConfig.clientId,
|
|
4305
4318
|
redirect_uri: redirectUri,
|
|
4306
4319
|
response_type: "code",
|
|
4307
4320
|
scope: scopes,
|
|
4308
4321
|
access_type: "offline",
|
|
4309
|
-
prompt: "consent"
|
|
4322
|
+
prompt: "consent",
|
|
4323
|
+
state
|
|
4310
4324
|
});
|
|
4311
4325
|
return `${GOOGLE_AUTH_URL}?${params.toString()}`;
|
|
4312
4326
|
}
|
|
4327
|
+
function validateOAuthState(state, expectedConnector) {
|
|
4328
|
+
if (!state)
|
|
4329
|
+
return false;
|
|
4330
|
+
const entry = oauthStateStore.get(state);
|
|
4331
|
+
if (!entry || entry.connector !== expectedConnector)
|
|
4332
|
+
return false;
|
|
4333
|
+
oauthStateStore.delete(state);
|
|
4334
|
+
return Date.now() - entry.createdAt < 10 * 60 * 1000;
|
|
4335
|
+
}
|
|
4313
4336
|
async function exchangeOAuthCode(name, code, redirectUri) {
|
|
4314
4337
|
const oauthConfig = getOAuthConfig(name);
|
|
4315
4338
|
if (!oauthConfig.clientId || !oauthConfig.clientSecret) {
|
|
@@ -4324,7 +4347,8 @@ async function exchangeOAuthCode(name, code, redirectUri) {
|
|
|
4324
4347
|
client_secret: oauthConfig.clientSecret,
|
|
4325
4348
|
redirect_uri: redirectUri,
|
|
4326
4349
|
grant_type: "authorization_code"
|
|
4327
|
-
})
|
|
4350
|
+
}),
|
|
4351
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT)
|
|
4328
4352
|
});
|
|
4329
4353
|
if (!response.ok) {
|
|
4330
4354
|
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
@@ -4366,7 +4390,8 @@ async function refreshOAuthToken(name) {
|
|
|
4366
4390
|
client_secret: oauthConfig.clientSecret,
|
|
4367
4391
|
refresh_token: currentTokens.refreshToken,
|
|
4368
4392
|
grant_type: "refresh_token"
|
|
4369
|
-
})
|
|
4393
|
+
}),
|
|
4394
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT)
|
|
4370
4395
|
});
|
|
4371
4396
|
if (!response.ok) {
|
|
4372
4397
|
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
@@ -4383,9 +4408,10 @@ async function refreshOAuthToken(name) {
|
|
|
4383
4408
|
saveOAuthTokens(name, tokens);
|
|
4384
4409
|
return tokens;
|
|
4385
4410
|
}
|
|
4386
|
-
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth", GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", GOOGLE_SCOPES;
|
|
4411
|
+
var FETCH_TIMEOUT = 1e4, oauthStateStore, GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth", GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", GOOGLE_SCOPES;
|
|
4387
4412
|
var init_auth = __esm(() => {
|
|
4388
4413
|
init_installer();
|
|
4414
|
+
oauthStateStore = new Map;
|
|
4389
4415
|
GOOGLE_SCOPES = {
|
|
4390
4416
|
gmail: [
|
|
4391
4417
|
"https://www.googleapis.com/auth/gmail.readonly",
|
|
@@ -4453,18 +4479,25 @@ function resolveDashboardDir() {
|
|
|
4453
4479
|
}
|
|
4454
4480
|
return join4(process.cwd(), "dashboard", "dist");
|
|
4455
4481
|
}
|
|
4456
|
-
function json(data, status = 200) {
|
|
4482
|
+
function json(data, status = 200, port) {
|
|
4457
4483
|
return new Response(JSON.stringify(data), {
|
|
4458
4484
|
status,
|
|
4459
|
-
headers: {
|
|
4485
|
+
headers: {
|
|
4486
|
+
"Content-Type": "application/json",
|
|
4487
|
+
"Access-Control-Allow-Origin": port ? `http://localhost:${port}` : "*",
|
|
4488
|
+
...SECURITY_HEADERS
|
|
4489
|
+
}
|
|
4460
4490
|
});
|
|
4461
4491
|
}
|
|
4462
4492
|
function htmlResponse(content, status = 200) {
|
|
4463
4493
|
return new Response(content, {
|
|
4464
4494
|
status,
|
|
4465
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
4495
|
+
headers: { "Content-Type": "text/html; charset=utf-8", ...SECURITY_HEADERS }
|
|
4466
4496
|
});
|
|
4467
4497
|
}
|
|
4498
|
+
function isValidConnectorName(name) {
|
|
4499
|
+
return /^[a-z0-9-]+$/.test(name);
|
|
4500
|
+
}
|
|
4468
4501
|
function getAllConnectorsWithAuth() {
|
|
4469
4502
|
const installed = new Set(getInstalledConnectors());
|
|
4470
4503
|
return CONNECTORS.map((meta) => {
|
|
@@ -4506,7 +4539,16 @@ async function startServer(port, options) {
|
|
|
4506
4539
|
const dashboardDir = resolveDashboardDir();
|
|
4507
4540
|
const dashboardExists = existsSync4(dashboardDir);
|
|
4508
4541
|
if (!dashboardExists) {
|
|
4509
|
-
console.error(`
|
|
4542
|
+
console.error(`
|
|
4543
|
+
Dashboard not found at: ${dashboardDir}`);
|
|
4544
|
+
console.error(`Run this to build it:
|
|
4545
|
+
`);
|
|
4546
|
+
console.error(` cd dashboard && bun install && bun run build
|
|
4547
|
+
`);
|
|
4548
|
+
console.error(`Or from the project root:
|
|
4549
|
+
`);
|
|
4550
|
+
console.error(` bun run build:dashboard
|
|
4551
|
+
`);
|
|
4510
4552
|
}
|
|
4511
4553
|
const server = Bun.serve({
|
|
4512
4554
|
port,
|
|
@@ -4515,14 +4557,16 @@ async function startServer(port, options) {
|
|
|
4515
4557
|
const path = url2.pathname;
|
|
4516
4558
|
const method = req.method;
|
|
4517
4559
|
if (path === "/api/connectors" && method === "GET") {
|
|
4518
|
-
return json(getAllConnectorsWithAuth());
|
|
4560
|
+
return json(getAllConnectorsWithAuth(), 200, port);
|
|
4519
4561
|
}
|
|
4520
4562
|
const singleMatch = path.match(/^\/api\/connectors\/([^/]+)$/);
|
|
4521
4563
|
if (singleMatch && method === "GET") {
|
|
4522
4564
|
const name = singleMatch[1];
|
|
4565
|
+
if (!isValidConnectorName(name))
|
|
4566
|
+
return json({ error: "Invalid connector name" }, 400, port);
|
|
4523
4567
|
const meta = getConnector(name);
|
|
4524
4568
|
if (!meta)
|
|
4525
|
-
return json({ error: `Connector '${name}' not found` }, 404);
|
|
4569
|
+
return json({ error: `Connector '${name}' not found` }, 404, port);
|
|
4526
4570
|
const auth = getAuthStatus(name);
|
|
4527
4571
|
const docs = getConnectorDocs(name);
|
|
4528
4572
|
return json({
|
|
@@ -4533,29 +4577,36 @@ async function startServer(port, options) {
|
|
|
4533
4577
|
version: meta.version,
|
|
4534
4578
|
auth,
|
|
4535
4579
|
overview: docs?.overview || null
|
|
4536
|
-
});
|
|
4580
|
+
}, 200, port);
|
|
4537
4581
|
}
|
|
4538
4582
|
const keyMatch = path.match(/^\/api\/connectors\/([^/]+)\/key$/);
|
|
4539
4583
|
if (keyMatch && method === "POST") {
|
|
4540
4584
|
const name = keyMatch[1];
|
|
4585
|
+
if (!isValidConnectorName(name))
|
|
4586
|
+
return json({ error: "Invalid connector name" }, 400, port);
|
|
4541
4587
|
try {
|
|
4588
|
+
const contentLength = parseInt(req.headers.get("content-length") || "0", 10);
|
|
4589
|
+
if (contentLength > MAX_BODY_SIZE)
|
|
4590
|
+
return json({ error: "Request body too large" }, 413, port);
|
|
4542
4591
|
const body = await req.json();
|
|
4543
4592
|
if (!body.key)
|
|
4544
|
-
return json({ error: "Missing 'key' in request body" }, 400);
|
|
4593
|
+
return json({ error: "Missing 'key' in request body" }, 400, port);
|
|
4545
4594
|
saveApiKey(name, body.key, body.field);
|
|
4546
|
-
return json({ success: true });
|
|
4595
|
+
return json({ success: true }, 200, port);
|
|
4547
4596
|
} catch (e) {
|
|
4548
|
-
return json({ error: e instanceof Error ? e.message : "Failed to save key" }, 500);
|
|
4597
|
+
return json({ error: e instanceof Error ? e.message : "Failed to save key" }, 500, port);
|
|
4549
4598
|
}
|
|
4550
4599
|
}
|
|
4551
4600
|
const refreshMatch = path.match(/^\/api\/connectors\/([^/]+)\/refresh$/);
|
|
4552
4601
|
if (refreshMatch && method === "POST") {
|
|
4553
4602
|
const name = refreshMatch[1];
|
|
4603
|
+
if (!isValidConnectorName(name))
|
|
4604
|
+
return json({ error: "Invalid connector name" }, 400, port);
|
|
4554
4605
|
try {
|
|
4555
4606
|
const tokens = await refreshOAuthToken(name);
|
|
4556
|
-
return json({ success: true, expiresAt: tokens.expiresAt });
|
|
4607
|
+
return json({ success: true, expiresAt: tokens.expiresAt }, 200, port);
|
|
4557
4608
|
} catch (e) {
|
|
4558
|
-
return json({ success: false, error: e instanceof Error ? e.message : "Failed to refresh" }, 500);
|
|
4609
|
+
return json({ success: false, error: e instanceof Error ? e.message : "Failed to refresh" }, 500, port);
|
|
4559
4610
|
}
|
|
4560
4611
|
}
|
|
4561
4612
|
const oauthStartMatch = path.match(/^\/oauth\/([^/]+)\/start$/);
|
|
@@ -4573,9 +4624,13 @@ async function startServer(port, options) {
|
|
|
4573
4624
|
const name = oauthCallbackMatch[1];
|
|
4574
4625
|
const code = url2.searchParams.get("code");
|
|
4575
4626
|
const error = url2.searchParams.get("error");
|
|
4627
|
+
const state = url2.searchParams.get("state");
|
|
4576
4628
|
if (error) {
|
|
4577
4629
|
return htmlResponse(errorPage("Authentication Failed", error, "You can close this window."));
|
|
4578
4630
|
}
|
|
4631
|
+
if (!validateOAuthState(state, name)) {
|
|
4632
|
+
return htmlResponse(errorPage("Invalid State", "CSRF validation failed. The OAuth state parameter is missing or invalid.", "Please try again from the dashboard."));
|
|
4633
|
+
}
|
|
4579
4634
|
if (!code) {
|
|
4580
4635
|
return htmlResponse(errorPage("Missing Authorization Code", "No code received from the OAuth provider.", "You can close this window and try again."));
|
|
4581
4636
|
}
|
|
@@ -4589,7 +4644,7 @@ async function startServer(port, options) {
|
|
|
4589
4644
|
<p style="color:#666;font-size:14px;">You can close this window and return to the dashboard.</p>
|
|
4590
4645
|
<script>
|
|
4591
4646
|
if (window.opener) {
|
|
4592
|
-
window.opener.postMessage({ type: 'oauth-complete', connector: '${name}' }, '
|
|
4647
|
+
window.opener.postMessage({ type: 'oauth-complete', connector: '${name}' }, 'http://localhost:${port}');
|
|
4593
4648
|
}
|
|
4594
4649
|
</script>
|
|
4595
4650
|
</div>
|
|
@@ -4601,7 +4656,7 @@ async function startServer(port, options) {
|
|
|
4601
4656
|
if (method === "OPTIONS") {
|
|
4602
4657
|
return new Response(null, {
|
|
4603
4658
|
headers: {
|
|
4604
|
-
"Access-Control-Allow-Origin":
|
|
4659
|
+
"Access-Control-Allow-Origin": `http://localhost:${port}`,
|
|
4605
4660
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
4606
4661
|
"Access-Control-Allow-Headers": "Content-Type"
|
|
4607
4662
|
}
|
|
@@ -4619,9 +4674,15 @@ async function startServer(port, options) {
|
|
|
4619
4674
|
if (res)
|
|
4620
4675
|
return res;
|
|
4621
4676
|
}
|
|
4622
|
-
return json({ error: "Not found" }, 404);
|
|
4677
|
+
return json({ error: "Not found" }, 404, port);
|
|
4623
4678
|
}
|
|
4624
4679
|
});
|
|
4680
|
+
const shutdown = () => {
|
|
4681
|
+
server.stop();
|
|
4682
|
+
process.exit(0);
|
|
4683
|
+
};
|
|
4684
|
+
process.on("SIGINT", shutdown);
|
|
4685
|
+
process.on("SIGTERM", shutdown);
|
|
4625
4686
|
const url = `http://localhost:${port}`;
|
|
4626
4687
|
console.log(`Connectors Dashboard running at ${url}`);
|
|
4627
4688
|
if (shouldOpen) {
|
|
@@ -4632,7 +4693,7 @@ async function startServer(port, options) {
|
|
|
4632
4693
|
} catch {}
|
|
4633
4694
|
}
|
|
4634
4695
|
}
|
|
4635
|
-
var MIME_TYPES;
|
|
4696
|
+
var MIME_TYPES, SECURITY_HEADERS, MAX_BODY_SIZE;
|
|
4636
4697
|
var init_serve = __esm(() => {
|
|
4637
4698
|
init_registry();
|
|
4638
4699
|
init_installer();
|
|
@@ -4649,6 +4710,11 @@ var init_serve = __esm(() => {
|
|
|
4649
4710
|
".woff": "font/woff",
|
|
4650
4711
|
".woff2": "font/woff2"
|
|
4651
4712
|
};
|
|
4713
|
+
SECURITY_HEADERS = {
|
|
4714
|
+
"X-Content-Type-Options": "nosniff",
|
|
4715
|
+
"X-Frame-Options": "DENY"
|
|
4716
|
+
};
|
|
4717
|
+
MAX_BODY_SIZE = 1024 * 1024;
|
|
4652
4718
|
});
|
|
4653
4719
|
|
|
4654
4720
|
// src/cli/index.tsx
|
|
@@ -5123,10 +5189,12 @@ function CategorySelect({ onSelect, onBack }) {
|
|
|
5123
5189
|
return /* @__PURE__ */ jsxDEV2(Box4, {
|
|
5124
5190
|
flexDirection: "column",
|
|
5125
5191
|
children: [
|
|
5126
|
-
/* @__PURE__ */ jsxDEV2(
|
|
5127
|
-
bold: true,
|
|
5192
|
+
/* @__PURE__ */ jsxDEV2(Box4, {
|
|
5128
5193
|
marginBottom: 1,
|
|
5129
|
-
children:
|
|
5194
|
+
children: /* @__PURE__ */ jsxDEV2(Text4, {
|
|
5195
|
+
bold: true,
|
|
5196
|
+
children: "Select a category:"
|
|
5197
|
+
}, undefined, false, undefined, this)
|
|
5130
5198
|
}, undefined, false, undefined, this),
|
|
5131
5199
|
/* @__PURE__ */ jsxDEV2(SelectInput_default, {
|
|
5132
5200
|
items,
|
|
@@ -5164,7 +5232,9 @@ function ConnectorSelect({
|
|
|
5164
5232
|
return cursor - half;
|
|
5165
5233
|
}, [cursor, totalItems]);
|
|
5166
5234
|
useInput2((input, key) => {
|
|
5167
|
-
if (key.
|
|
5235
|
+
if (key.escape) {
|
|
5236
|
+
onBack();
|
|
5237
|
+
} else if (key.upArrow) {
|
|
5168
5238
|
setCursor((c) => c > 0 ? c - 1 : totalItems - 1);
|
|
5169
5239
|
} else if (key.downArrow) {
|
|
5170
5240
|
setCursor((c) => c < totalItems - 1 ? c + 1 : 0);
|
|
@@ -5179,6 +5249,19 @@ function ConnectorSelect({
|
|
|
5179
5249
|
}
|
|
5180
5250
|
} else if (input === " " && cursor > 0 && cursor < totalItems - 1) {
|
|
5181
5251
|
onToggle(connectors[cursor - 1].name);
|
|
5252
|
+
} else if (input === "i" && selected.size > 0) {
|
|
5253
|
+
onConfirm();
|
|
5254
|
+
} else if (input === "a") {
|
|
5255
|
+
const allSelected = connectors.every((c) => selected.has(c.name));
|
|
5256
|
+
for (const c of connectors) {
|
|
5257
|
+
if (allSelected) {
|
|
5258
|
+
if (selected.has(c.name))
|
|
5259
|
+
onToggle(c.name);
|
|
5260
|
+
} else {
|
|
5261
|
+
if (!selected.has(c.name))
|
|
5262
|
+
onToggle(c.name);
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5182
5265
|
}
|
|
5183
5266
|
});
|
|
5184
5267
|
const visibleStart = scrollOffset;
|
|
@@ -5186,10 +5269,12 @@ function ConnectorSelect({
|
|
|
5186
5269
|
return /* @__PURE__ */ jsxDEV3(Box5, {
|
|
5187
5270
|
flexDirection: "column",
|
|
5188
5271
|
children: [
|
|
5189
|
-
/* @__PURE__ */ jsxDEV3(
|
|
5190
|
-
bold: true,
|
|
5272
|
+
/* @__PURE__ */ jsxDEV3(Box5, {
|
|
5191
5273
|
marginBottom: 1,
|
|
5192
|
-
children:
|
|
5274
|
+
children: /* @__PURE__ */ jsxDEV3(Text5, {
|
|
5275
|
+
bold: true,
|
|
5276
|
+
children: "Select connectors to install:"
|
|
5277
|
+
}, undefined, false, undefined, this)
|
|
5193
5278
|
}, undefined, false, undefined, this),
|
|
5194
5279
|
/* @__PURE__ */ jsxDEV3(Box5, {
|
|
5195
5280
|
children: [
|
|
@@ -5333,7 +5418,7 @@ function ConnectorSelect({
|
|
|
5333
5418
|
marginTop: 1,
|
|
5334
5419
|
children: /* @__PURE__ */ jsxDEV3(Text5, {
|
|
5335
5420
|
dimColor: true,
|
|
5336
|
-
children: "\u2191\u2193 navigate space toggle
|
|
5421
|
+
children: "\u2191\u2193 navigate space/enter toggle a select all i install esc back"
|
|
5337
5422
|
}, undefined, false, undefined, this)
|
|
5338
5423
|
}, undefined, false, undefined, this)
|
|
5339
5424
|
]
|
|
@@ -5513,6 +5598,19 @@ function SearchView({
|
|
|
5513
5598
|
if (connectorIdx < results.length) {
|
|
5514
5599
|
onToggle(results[connectorIdx].name);
|
|
5515
5600
|
}
|
|
5601
|
+
} else if (input === "i" && selected.size > 0) {
|
|
5602
|
+
onConfirm();
|
|
5603
|
+
} else if (input === "a" && mode === "select") {
|
|
5604
|
+
const allSelected = results.every((c) => selected.has(c.name));
|
|
5605
|
+
for (const c of results) {
|
|
5606
|
+
if (allSelected) {
|
|
5607
|
+
if (selected.has(c.name))
|
|
5608
|
+
onToggle(c.name);
|
|
5609
|
+
} else {
|
|
5610
|
+
if (!selected.has(c.name))
|
|
5611
|
+
onToggle(c.name);
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5516
5614
|
}
|
|
5517
5615
|
});
|
|
5518
5616
|
const visibleStart = scrollOffset;
|
|
@@ -5550,17 +5648,19 @@ function SearchView({
|
|
|
5550
5648
|
results.length > 0 && /* @__PURE__ */ jsxDEV4(Box6, {
|
|
5551
5649
|
flexDirection: "column",
|
|
5552
5650
|
children: [
|
|
5553
|
-
/* @__PURE__ */ jsxDEV4(
|
|
5554
|
-
dimColor: true,
|
|
5651
|
+
/* @__PURE__ */ jsxDEV4(Box6, {
|
|
5555
5652
|
marginBottom: 1,
|
|
5556
|
-
children:
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5653
|
+
children: /* @__PURE__ */ jsxDEV4(Text7, {
|
|
5654
|
+
dimColor: true,
|
|
5655
|
+
children: [
|
|
5656
|
+
"Found ",
|
|
5657
|
+
results.length,
|
|
5658
|
+
" connector(s)",
|
|
5659
|
+
mode === "search" ? " \u2014 press \u2193 to select" : "",
|
|
5660
|
+
":"
|
|
5661
|
+
]
|
|
5662
|
+
}, undefined, true, undefined, this)
|
|
5663
|
+
}, undefined, false, undefined, this),
|
|
5564
5664
|
/* @__PURE__ */ jsxDEV4(Box6, {
|
|
5565
5665
|
children: [
|
|
5566
5666
|
/* @__PURE__ */ jsxDEV4(Box6, {
|
|
@@ -5706,7 +5806,7 @@ function SearchView({
|
|
|
5706
5806
|
marginTop: 1,
|
|
5707
5807
|
children: /* @__PURE__ */ jsxDEV4(Text7, {
|
|
5708
5808
|
dimColor: true,
|
|
5709
|
-
children: mode === "search" ? "type to search \u2193 select results esc back" : "\u2191\u2193 navigate space toggle
|
|
5809
|
+
children: mode === "search" ? "type to search \u2193 select results esc back" : "\u2191\u2193 navigate space/enter toggle a select all i install esc search"
|
|
5710
5810
|
}, undefined, false, undefined, this)
|
|
5711
5811
|
}, undefined, false, undefined, this)
|
|
5712
5812
|
]
|
|
@@ -5862,9 +5962,14 @@ function App({ initialConnectors, overwrite = false }) {
|
|
|
5862
5962
|
if (key.escape) {
|
|
5863
5963
|
if (view === "main") {
|
|
5864
5964
|
exit();
|
|
5965
|
+
} else if (view === "browse" || view === "search") {
|
|
5966
|
+
setView("main");
|
|
5967
|
+
} else if (view === "connectors") {
|
|
5968
|
+
setCategory(null);
|
|
5969
|
+
setView("browse");
|
|
5865
5970
|
}
|
|
5866
5971
|
}
|
|
5867
|
-
if (input === "q") {
|
|
5972
|
+
if (input === "q" && view !== "search") {
|
|
5868
5973
|
exit();
|
|
5869
5974
|
}
|
|
5870
5975
|
});
|
|
@@ -5909,9 +6014,11 @@ function App({ initialConnectors, overwrite = false }) {
|
|
|
5909
6014
|
view === "main" && /* @__PURE__ */ jsxDEV6(Box8, {
|
|
5910
6015
|
flexDirection: "column",
|
|
5911
6016
|
children: [
|
|
5912
|
-
/* @__PURE__ */ jsxDEV6(
|
|
6017
|
+
/* @__PURE__ */ jsxDEV6(Box8, {
|
|
5913
6018
|
marginBottom: 1,
|
|
5914
|
-
children:
|
|
6019
|
+
children: /* @__PURE__ */ jsxDEV6(Text10, {
|
|
6020
|
+
children: "What would you like to do?"
|
|
6021
|
+
}, undefined, false, undefined, this)
|
|
5915
6022
|
}, undefined, false, undefined, this),
|
|
5916
6023
|
/* @__PURE__ */ jsxDEV6(SelectInput_default, {
|
|
5917
6024
|
items: mainMenuItems,
|
|
@@ -5957,11 +6064,13 @@ function App({ initialConnectors, overwrite = false }) {
|
|
|
5957
6064
|
view === "done" && /* @__PURE__ */ jsxDEV6(Box8, {
|
|
5958
6065
|
flexDirection: "column",
|
|
5959
6066
|
children: [
|
|
5960
|
-
/* @__PURE__ */ jsxDEV6(
|
|
5961
|
-
bold: true,
|
|
5962
|
-
color: "green",
|
|
6067
|
+
/* @__PURE__ */ jsxDEV6(Box8, {
|
|
5963
6068
|
marginBottom: 1,
|
|
5964
|
-
children:
|
|
6069
|
+
children: /* @__PURE__ */ jsxDEV6(Text10, {
|
|
6070
|
+
bold: true,
|
|
6071
|
+
color: "green",
|
|
6072
|
+
children: "Installation complete!"
|
|
6073
|
+
}, undefined, false, undefined, this)
|
|
5965
6074
|
}, undefined, false, undefined, this),
|
|
5966
6075
|
results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxDEV6(Box8, {
|
|
5967
6076
|
flexDirection: "column",
|
|
@@ -6049,7 +6158,7 @@ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
|
6049
6158
|
loadConnectorVersions();
|
|
6050
6159
|
var isTTY = process.stdout.isTTY ?? false;
|
|
6051
6160
|
var program2 = new Command;
|
|
6052
|
-
program2.name("connectors").description("Install API connectors for your project").version("0.1
|
|
6161
|
+
program2.name("connectors").description("Install API connectors for your project").version("0.2.1");
|
|
6053
6162
|
program2.command("interactive", { isDefault: true }).alias("i").description("Interactive connector browser").action(() => {
|
|
6054
6163
|
if (!isTTY) {
|
|
6055
6164
|
console.log(`Non-interactive environment detected. Use a subcommand:
|
|
@@ -6085,15 +6194,23 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
|
|
|
6085
6194
|
console.log(chalk2.bold(`
|
|
6086
6195
|
Installing connectors...
|
|
6087
6196
|
`));
|
|
6197
|
+
const succeeded = [];
|
|
6088
6198
|
for (const result of results) {
|
|
6089
6199
|
if (result.success) {
|
|
6090
6200
|
console.log(chalk2.green(`\u2713 ${result.connector}`));
|
|
6201
|
+
succeeded.push(result.connector);
|
|
6091
6202
|
} else {
|
|
6092
6203
|
console.log(chalk2.red(`\u2717 ${result.connector}: ${result.error}`));
|
|
6093
6204
|
}
|
|
6094
6205
|
}
|
|
6095
|
-
|
|
6096
|
-
|
|
6206
|
+
if (succeeded.length > 0) {
|
|
6207
|
+
console.log(chalk2.bold(`
|
|
6208
|
+
Next steps:`));
|
|
6209
|
+
const importNames = succeeded.join(", ");
|
|
6210
|
+
console.log(chalk2.dim(` 1. Import: `) + `import { ${importNames} } from './.connectors'`);
|
|
6211
|
+
console.log(chalk2.dim(` 2. Set key: `) + `connectors docs ${succeeded[0]}` + chalk2.dim(` (see env vars)`));
|
|
6212
|
+
console.log(chalk2.dim(` 3. Explore: `) + `connectors serve` + chalk2.dim(` (dashboard for auth management)`));
|
|
6213
|
+
}
|
|
6097
6214
|
process.exit(results.every((r) => r.success) ? 0 : 1);
|
|
6098
6215
|
});
|
|
6099
6216
|
program2.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-a, --all", "Show all available connectors", false).option("-i, --installed", "Show only installed connectors", false).option("--json", "Output as JSON", false).description("List available or installed connectors").action((options) => {
|
|
@@ -6319,7 +6436,7 @@ Categories:
|
|
|
6319
6436
|
console.log(` ${category} (${count})`);
|
|
6320
6437
|
}
|
|
6321
6438
|
});
|
|
6322
|
-
program2.command("serve").alias("dashboard").option("-p, --port <port>", "Port to run the dashboard on", "19426").option("--open", "Open dashboard in browser (default)", true).option("--no-open", "Don't open browser automatically").description("Start local dashboard for connector auth management").action(async (options) => {
|
|
6439
|
+
program2.command("serve").alias("dashboard").alias("open").option("-p, --port <port>", "Port to run the dashboard on", "19426").option("--open", "Open dashboard in browser (default)", true).option("--no-open", "Don't open browser automatically").description("Start local dashboard for connector auth management").action(async (options) => {
|
|
6323
6440
|
const port = parseInt(options.port, 10);
|
|
6324
6441
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
6325
6442
|
console.log(chalk2.red("Invalid port number"));
|
|
@@ -6332,14 +6449,32 @@ Starting Connectors Dashboard...
|
|
|
6332
6449
|
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
|
|
6333
6450
|
await startServer2(port, { open: options.open });
|
|
6334
6451
|
});
|
|
6335
|
-
program2.command("
|
|
6336
|
-
const
|
|
6337
|
-
if (
|
|
6338
|
-
|
|
6339
|
-
|
|
6452
|
+
program2.command("update").description("Update all installed connectors to the latest version from the package").option("--json", "Output as JSON", false).action((options) => {
|
|
6453
|
+
const installed = getInstalledConnectors();
|
|
6454
|
+
if (installed.length === 0) {
|
|
6455
|
+
if (options.json) {
|
|
6456
|
+
console.log(JSON.stringify({ updated: [] }));
|
|
6457
|
+
} else {
|
|
6458
|
+
console.log(chalk2.dim("No connectors installed. Run: connectors install <name>"));
|
|
6459
|
+
}
|
|
6340
6460
|
return;
|
|
6341
6461
|
}
|
|
6342
|
-
const
|
|
6343
|
-
|
|
6462
|
+
const results = installed.map((name) => installConnector(name, { overwrite: true }));
|
|
6463
|
+
if (options.json) {
|
|
6464
|
+
console.log(JSON.stringify(results, null, 2));
|
|
6465
|
+
process.exit(results.every((r) => r.success) ? 0 : 1);
|
|
6466
|
+
return;
|
|
6467
|
+
}
|
|
6468
|
+
console.log(chalk2.bold(`
|
|
6469
|
+
Updating ${installed.length} connector(s)...
|
|
6470
|
+
`));
|
|
6471
|
+
for (const result of results) {
|
|
6472
|
+
if (result.success) {
|
|
6473
|
+
console.log(chalk2.green(`\u2713 ${result.connector}`));
|
|
6474
|
+
} else {
|
|
6475
|
+
console.log(chalk2.red(`\u2717 ${result.connector}: ${result.error}`));
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
process.exit(results.every((r) => r.success) ? 0 : 1);
|
|
6344
6479
|
});
|
|
6345
6480
|
program2.parse();
|