@hasna/connectors 0.1.0 → 0.2.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.
Files changed (110) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +19 -13
  3. package/bin/index.js +171 -69
  4. package/bin/mcp.js +24 -18
  5. package/bin/serve.js +94 -32
  6. package/connectors/connect-anthropic/package.json +2 -2
  7. package/connectors/connect-aws/package.json +2 -2
  8. package/connectors/connect-brandsight/package.json +2 -2
  9. package/connectors/connect-cloudflare/.env.example +0 -5
  10. package/connectors/connect-cloudflare/package.json +2 -2
  11. package/connectors/connect-discord/package.json +2 -2
  12. package/connectors/connect-docker/package.json +2 -2
  13. package/connectors/connect-e2b/package.json +2 -2
  14. package/connectors/connect-elevenlabs/package.json +2 -2
  15. package/connectors/connect-exa/package.json +2 -2
  16. package/connectors/connect-figma/package.json +2 -2
  17. package/connectors/connect-firecrawl/package.json +2 -2
  18. package/connectors/connect-github/package.json +2 -2
  19. package/connectors/connect-gmail/package.json +2 -2
  20. package/connectors/connect-google/package.json +2 -2
  21. package/connectors/connect-googlecalendar/package.json +2 -2
  22. package/connectors/connect-googlecloud/package.json +2 -2
  23. package/connectors/connect-googlecontacts/package.json +2 -2
  24. package/connectors/connect-googledocs/package.json +2 -2
  25. package/connectors/connect-googledrive/package.json +2 -2
  26. package/connectors/connect-googlegemini/package.json +2 -2
  27. package/connectors/connect-googlesheets/package.json +2 -2
  28. package/connectors/connect-googletasks/package.json +2 -2
  29. package/connectors/connect-hedra/package.json +2 -2
  30. package/connectors/connect-heygen/package.json +2 -2
  31. package/connectors/connect-huggingface/package.json +2 -2
  32. package/connectors/connect-icons8/package.json +2 -2
  33. package/connectors/connect-maropost/package.json +2 -2
  34. package/connectors/connect-mercury/package.json +2 -2
  35. package/connectors/connect-meta/package.json +2 -2
  36. package/connectors/connect-midjourney/package.json +2 -2
  37. package/connectors/connect-mistral/package.json +2 -2
  38. package/connectors/connect-mixpanel/package.json +2 -2
  39. package/connectors/connect-notion/.env.example +0 -5
  40. package/connectors/connect-notion/package.json +2 -2
  41. package/connectors/connect-openai/package.json +2 -2
  42. package/connectors/connect-openweathermap/package.json +2 -2
  43. package/connectors/connect-pandadoc/package.json +2 -2
  44. package/connectors/connect-quo/package.json +2 -2
  45. package/connectors/connect-reddit/package.json +2 -2
  46. package/connectors/connect-reducto/package.json +1 -1
  47. package/connectors/connect-resend/package.json +2 -2
  48. package/connectors/connect-revolut/package.json +2 -2
  49. package/connectors/connect-sedo/package.json +2 -2
  50. package/connectors/connect-sentry/package.json +2 -2
  51. package/connectors/connect-shadcn/package.json +2 -2
  52. package/connectors/connect-snap/package.json +2 -2
  53. package/connectors/connect-stabilityai/package.json +2 -2
  54. package/connectors/connect-stripe/package.json +2 -2
  55. package/connectors/connect-stripeatlas/package.json +2 -2
  56. package/connectors/connect-substack/package.json +2 -2
  57. package/connectors/connect-tiktok/package.json +2 -2
  58. package/connectors/connect-tinker/package.json +2 -2
  59. package/connectors/connect-twilio/package.json +4 -4
  60. package/connectors/connect-uspto/package.json +2 -2
  61. package/connectors/connect-x/package.json +2 -2
  62. package/connectors/connect-xads/package.json +2 -2
  63. package/connectors/connect-xai/package.json +2 -2
  64. package/connectors/connect-youtube/package.json +2 -2
  65. package/connectors/connect-zoom/package.json +2 -2
  66. package/dist/cli/cli.test.d.ts +1 -0
  67. package/dist/cli/components/App.d.ts +6 -0
  68. package/dist/cli/components/CategorySelect.d.ts +6 -0
  69. package/dist/cli/components/ConnectorSelect.d.ts +10 -0
  70. package/dist/cli/components/Header.d.ts +6 -0
  71. package/dist/cli/components/InstallProgress.d.ts +8 -0
  72. package/dist/cli/components/SearchView.d.ts +8 -0
  73. package/dist/cli/components/components.test.d.ts +1 -0
  74. package/dist/cli/index.d.ts +2 -0
  75. package/dist/index.d.ts +11 -0
  76. package/dist/index.js +11 -16
  77. package/dist/lib/installer.d.ts +55 -0
  78. package/dist/lib/installer.test.d.ts +1 -0
  79. package/dist/lib/registry.d.ts +18 -0
  80. package/dist/lib/registry.test.d.ts +1 -0
  81. package/dist/mcp/index.d.ts +2 -0
  82. package/dist/mcp/mcp.test.d.ts +1 -0
  83. package/dist/server/auth.d.ts +70 -0
  84. package/dist/server/dashboard.d.ts +5 -0
  85. package/dist/server/index.d.ts +6 -0
  86. package/dist/server/serve.d.ts +12 -0
  87. package/dist/server/server.test.d.ts +1 -0
  88. package/package.json +5 -4
  89. package/connectors/connect-browseruse/.env.example +0 -7
  90. package/connectors/connect-browseruse/.npmrc.example +0 -2
  91. package/connectors/connect-browseruse/AGENTS.md +0 -172
  92. package/connectors/connect-browseruse/CLAUDE.md +0 -172
  93. package/connectors/connect-browseruse/GEMINI.md +0 -172
  94. package/connectors/connect-browseruse/README.md +0 -153
  95. package/connectors/connect-browseruse/package.json +0 -52
  96. package/connectors/connect-browseruse/src/api/billing.ts +0 -32
  97. package/connectors/connect-browseruse/src/api/browsers.ts +0 -50
  98. package/connectors/connect-browseruse/src/api/client.ts +0 -168
  99. package/connectors/connect-browseruse/src/api/files.ts +0 -70
  100. package/connectors/connect-browseruse/src/api/index.ts +0 -95
  101. package/connectors/connect-browseruse/src/api/profiles.ts +0 -59
  102. package/connectors/connect-browseruse/src/api/sessions.ts +0 -88
  103. package/connectors/connect-browseruse/src/api/skills.ts +0 -194
  104. package/connectors/connect-browseruse/src/api/tasks.ts +0 -127
  105. package/connectors/connect-browseruse/src/cli/index.ts +0 -888
  106. package/connectors/connect-browseruse/src/index.ts +0 -35
  107. package/connectors/connect-browseruse/src/types/index.ts +0 -312
  108. package/connectors/connect-browseruse/src/utils/config.ts +0 -212
  109. package/connectors/connect-browseruse/src/utils/output.ts +0 -119
  110. 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: "Internal tooling",
2340
- category: "Business Tools",
2341
- tags: ["internal", "tools"]
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
- const config = JSON.parse(readFileSync3(profileFile, "utf-8"));
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
- const config = existsSync3(configFile) ? JSON.parse(readFileSync3(configFile, "utf-8")) : {};
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: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
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(`Dashboard not built at ${dashboardDir}. Run: cd dashboard && bun run build`);
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(Text4, {
5127
- bold: true,
5192
+ /* @__PURE__ */ jsxDEV2(Box4, {
5128
5193
  marginBottom: 1,
5129
- children: "Select a category:"
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,
@@ -5186,10 +5254,12 @@ function ConnectorSelect({
5186
5254
  return /* @__PURE__ */ jsxDEV3(Box5, {
5187
5255
  flexDirection: "column",
5188
5256
  children: [
5189
- /* @__PURE__ */ jsxDEV3(Text5, {
5190
- bold: true,
5257
+ /* @__PURE__ */ jsxDEV3(Box5, {
5191
5258
  marginBottom: 1,
5192
- children: "Select connectors to install:"
5259
+ children: /* @__PURE__ */ jsxDEV3(Text5, {
5260
+ bold: true,
5261
+ children: "Select connectors to install:"
5262
+ }, undefined, false, undefined, this)
5193
5263
  }, undefined, false, undefined, this),
5194
5264
  /* @__PURE__ */ jsxDEV3(Box5, {
5195
5265
  children: [
@@ -5550,17 +5620,19 @@ function SearchView({
5550
5620
  results.length > 0 && /* @__PURE__ */ jsxDEV4(Box6, {
5551
5621
  flexDirection: "column",
5552
5622
  children: [
5553
- /* @__PURE__ */ jsxDEV4(Text7, {
5554
- dimColor: true,
5623
+ /* @__PURE__ */ jsxDEV4(Box6, {
5555
5624
  marginBottom: 1,
5556
- children: [
5557
- "Found ",
5558
- results.length,
5559
- " connector(s)",
5560
- mode === "search" ? " \u2014 press \u2193 to select" : "",
5561
- ":"
5562
- ]
5563
- }, undefined, true, undefined, this),
5625
+ children: /* @__PURE__ */ jsxDEV4(Text7, {
5626
+ dimColor: true,
5627
+ children: [
5628
+ "Found ",
5629
+ results.length,
5630
+ " connector(s)",
5631
+ mode === "search" ? " \u2014 press \u2193 to select" : "",
5632
+ ":"
5633
+ ]
5634
+ }, undefined, true, undefined, this)
5635
+ }, undefined, false, undefined, this),
5564
5636
  /* @__PURE__ */ jsxDEV4(Box6, {
5565
5637
  children: [
5566
5638
  /* @__PURE__ */ jsxDEV4(Box6, {
@@ -5909,9 +5981,11 @@ function App({ initialConnectors, overwrite = false }) {
5909
5981
  view === "main" && /* @__PURE__ */ jsxDEV6(Box8, {
5910
5982
  flexDirection: "column",
5911
5983
  children: [
5912
- /* @__PURE__ */ jsxDEV6(Text10, {
5984
+ /* @__PURE__ */ jsxDEV6(Box8, {
5913
5985
  marginBottom: 1,
5914
- children: "What would you like to do?"
5986
+ children: /* @__PURE__ */ jsxDEV6(Text10, {
5987
+ children: "What would you like to do?"
5988
+ }, undefined, false, undefined, this)
5915
5989
  }, undefined, false, undefined, this),
5916
5990
  /* @__PURE__ */ jsxDEV6(SelectInput_default, {
5917
5991
  items: mainMenuItems,
@@ -5957,11 +6031,13 @@ function App({ initialConnectors, overwrite = false }) {
5957
6031
  view === "done" && /* @__PURE__ */ jsxDEV6(Box8, {
5958
6032
  flexDirection: "column",
5959
6033
  children: [
5960
- /* @__PURE__ */ jsxDEV6(Text10, {
5961
- bold: true,
5962
- color: "green",
6034
+ /* @__PURE__ */ jsxDEV6(Box8, {
5963
6035
  marginBottom: 1,
5964
- children: "Installation complete!"
6036
+ children: /* @__PURE__ */ jsxDEV6(Text10, {
6037
+ bold: true,
6038
+ color: "green",
6039
+ children: "Installation complete!"
6040
+ }, undefined, false, undefined, this)
5965
6041
  }, undefined, false, undefined, this),
5966
6042
  results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxDEV6(Box8, {
5967
6043
  flexDirection: "column",
@@ -6049,7 +6125,7 @@ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
6049
6125
  loadConnectorVersions();
6050
6126
  var isTTY = process.stdout.isTTY ?? false;
6051
6127
  var program2 = new Command;
6052
- program2.name("connectors").description("Install API connectors for your project").version("0.1.0");
6128
+ program2.name("connectors").description("Install API connectors for your project").version("0.2.0");
6053
6129
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive connector browser").action(() => {
6054
6130
  if (!isTTY) {
6055
6131
  console.log(`Non-interactive environment detected. Use a subcommand:
@@ -6085,15 +6161,23 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
6085
6161
  console.log(chalk2.bold(`
6086
6162
  Installing connectors...
6087
6163
  `));
6164
+ const succeeded = [];
6088
6165
  for (const result of results) {
6089
6166
  if (result.success) {
6090
6167
  console.log(chalk2.green(`\u2713 ${result.connector}`));
6168
+ succeeded.push(result.connector);
6091
6169
  } else {
6092
6170
  console.log(chalk2.red(`\u2717 ${result.connector}: ${result.error}`));
6093
6171
  }
6094
6172
  }
6095
- console.log(chalk2.dim(`
6096
- Connectors installed to .connectors/`));
6173
+ if (succeeded.length > 0) {
6174
+ console.log(chalk2.bold(`
6175
+ Next steps:`));
6176
+ const importNames = succeeded.join(", ");
6177
+ console.log(chalk2.dim(` 1. Import: `) + `import { ${importNames} } from './.connectors'`);
6178
+ console.log(chalk2.dim(` 2. Set key: `) + `connectors docs ${succeeded[0]}` + chalk2.dim(` (see env vars)`));
6179
+ console.log(chalk2.dim(` 3. Explore: `) + `connectors serve` + chalk2.dim(` (dashboard for auth management)`));
6180
+ }
6097
6181
  process.exit(results.every((r) => r.success) ? 0 : 1);
6098
6182
  });
6099
6183
  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 +6403,7 @@ Categories:
6319
6403
  console.log(` ${category} (${count})`);
6320
6404
  }
6321
6405
  });
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) => {
6406
+ 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
6407
  const port = parseInt(options.port, 10);
6324
6408
  if (isNaN(port) || port < 1 || port > 65535) {
6325
6409
  console.log(chalk2.red("Invalid port number"));
@@ -6332,14 +6416,32 @@ Starting Connectors Dashboard...
6332
6416
  const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
6333
6417
  await startServer2(port, { open: options.open });
6334
6418
  });
6335
- program2.command("open").option("-p, --port <port>", "Port to run the dashboard on", "19426").description("Open the connectors dashboard in your browser").action(async (options) => {
6336
- const port = parseInt(options.port, 10);
6337
- if (isNaN(port) || port < 1 || port > 65535) {
6338
- console.log(chalk2.red("Invalid port number"));
6339
- process.exit(1);
6419
+ program2.command("update").description("Update all installed connectors to the latest version from the package").option("--json", "Output as JSON", false).action((options) => {
6420
+ const installed = getInstalledConnectors();
6421
+ if (installed.length === 0) {
6422
+ if (options.json) {
6423
+ console.log(JSON.stringify({ updated: [] }));
6424
+ } else {
6425
+ console.log(chalk2.dim("No connectors installed. Run: connectors install <name>"));
6426
+ }
6340
6427
  return;
6341
6428
  }
6342
- const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
6343
- await startServer2(port, { open: true });
6429
+ const results = installed.map((name) => installConnector(name, { overwrite: true }));
6430
+ if (options.json) {
6431
+ console.log(JSON.stringify(results, null, 2));
6432
+ process.exit(results.every((r) => r.success) ? 0 : 1);
6433
+ return;
6434
+ }
6435
+ console.log(chalk2.bold(`
6436
+ Updating ${installed.length} connector(s)...
6437
+ `));
6438
+ for (const result of results) {
6439
+ if (result.success) {
6440
+ console.log(chalk2.green(`\u2713 ${result.connector}`));
6441
+ } else {
6442
+ console.log(chalk2.red(`\u2717 ${result.connector}: ${result.error}`));
6443
+ }
6444
+ }
6445
+ process.exit(results.every((r) => r.success) ? 0 : 1);
6344
6446
  });
6345
6447
  program2.parse();
package/bin/mcp.js CHANGED
@@ -26,7 +26,6 @@ var __export = (target, all) => {
26
26
  set: (newValue) => all[name] = () => newValue
27
27
  });
28
28
  };
29
- var __require = import.meta.require;
30
29
 
31
30
  // node_modules/ajv/dist/compile/codegen/code.js
32
31
  var require_code = __commonJS((exports) => {
@@ -4570,6 +4569,7 @@ var require_limitLength = __commonJS((exports) => {
4570
4569
  var require_pattern = __commonJS((exports) => {
4571
4570
  Object.defineProperty(exports, "__esModule", { value: true });
4572
4571
  var code_1 = require_code2();
4572
+ var util_1 = require_util();
4573
4573
  var codegen_1 = require_codegen();
4574
4574
  var error2 = {
4575
4575
  message: ({ schemaCode }) => (0, codegen_1.str)`must match pattern "${schemaCode}"`,
@@ -4582,10 +4582,18 @@ var require_pattern = __commonJS((exports) => {
4582
4582
  $data: true,
4583
4583
  error: error2,
4584
4584
  code(cxt) {
4585
- const { data, $data, schema, schemaCode, it } = cxt;
4585
+ const { gen, data, $data, schema, schemaCode, it } = cxt;
4586
4586
  const u = it.opts.unicodeRegExp ? "u" : "";
4587
- const regExp = $data ? (0, codegen_1._)`(new RegExp(${schemaCode}, ${u}))` : (0, code_1.usePattern)(cxt, schema);
4588
- cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
4587
+ if ($data) {
4588
+ const { regExp } = it.opts.code;
4589
+ const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._)`new RegExp` : (0, util_1.useFunc)(gen, regExp);
4590
+ const valid = gen.let("valid");
4591
+ gen.try(() => gen.assign(valid, (0, codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false));
4592
+ cxt.fail$data((0, codegen_1._)`!${valid}`);
4593
+ } else {
4594
+ const regExp = (0, code_1.usePattern)(cxt, schema);
4595
+ cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
4596
+ }
4589
4597
  }
4590
4598
  };
4591
4599
  exports.default = def;
@@ -19549,13 +19557,6 @@ var CONNECTORS = [
19549
19557
  category: "Developer Tools",
19550
19558
  tags: ["scraping", "web"]
19551
19559
  },
19552
- {
19553
- name: "browseruse",
19554
- displayName: "Browser Use",
19555
- description: "Browser automation for AI",
19556
- category: "Developer Tools",
19557
- tags: ["browser", "automation"]
19558
- },
19559
19560
  {
19560
19561
  name: "shadcn",
19561
19562
  displayName: "shadcn/ui",
@@ -19825,9 +19826,9 @@ var CONNECTORS = [
19825
19826
  {
19826
19827
  name: "tinker",
19827
19828
  displayName: "Tinker",
19828
- description: "Internal tooling",
19829
- category: "Business Tools",
19830
- tags: ["internal", "tools"]
19829
+ description: "LLM fine-tuning and training API",
19830
+ category: "AI & ML",
19831
+ tags: ["ai", "llm", "fine-tuning"]
19831
19832
  },
19832
19833
  {
19833
19834
  name: "sedo",
@@ -19886,7 +19887,7 @@ function loadConnectorVersions() {
19886
19887
  }
19887
19888
 
19888
19889
  // src/lib/installer.ts
19889
- import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
19890
+ import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync, rmSync } from "fs";
19890
19891
  import { join as join2, dirname as dirname2 } from "path";
19891
19892
  import { fileURLToPath as fileURLToPath2 } from "url";
19892
19893
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
@@ -19906,6 +19907,13 @@ function getConnectorPath(name) {
19906
19907
  }
19907
19908
  function installConnector(name, options = {}) {
19908
19909
  const { targetDir = process.cwd(), overwrite = false } = options;
19910
+ if (!/^[a-z0-9-]+$/.test(name)) {
19911
+ return {
19912
+ connector: name,
19913
+ success: false,
19914
+ error: `Invalid connector name '${name}'`
19915
+ };
19916
+ }
19909
19917
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
19910
19918
  const sourcePath = getConnectorPath(name);
19911
19919
  const destDir = join2(targetDir, ".connectors");
@@ -19946,7 +19954,6 @@ function installConnector(name, options = {}) {
19946
19954
  }
19947
19955
  function updateConnectorsIndex(connectorsDir) {
19948
19956
  const indexPath = join2(connectorsDir, "index.ts");
19949
- const { readdirSync } = __require("fs");
19950
19957
  const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
19951
19958
  const exports = connectors.map((c) => {
19952
19959
  const name = c.replace("connect-", "");
@@ -19967,7 +19974,6 @@ function getInstalledConnectors(targetDir = process.cwd()) {
19967
19974
  if (!existsSync2(connectorsDir)) {
19968
19975
  return [];
19969
19976
  }
19970
- const { readdirSync, statSync } = __require("fs");
19971
19977
  return readdirSync(connectorsDir).filter((f) => {
19972
19978
  const fullPath = join2(connectorsDir, f);
19973
19979
  return f.startsWith("connect-") && statSync(fullPath).isDirectory();
@@ -20016,7 +20022,6 @@ function parseEnvVarsTable(section) {
20016
20022
  return vars;
20017
20023
  }
20018
20024
  function removeConnector(name, targetDir = process.cwd()) {
20019
- const { rmSync } = __require("fs");
20020
20025
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
20021
20026
  const connectorsDir = join2(targetDir, ".connectors");
20022
20027
  const connectorPath = join2(connectorsDir, connectorName);
@@ -20032,6 +20037,7 @@ function removeConnector(name, targetDir = process.cwd()) {
20032
20037
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
20033
20038
  import { homedir } from "os";
20034
20039
  import { join as join3 } from "path";
20040
+ var oauthStateStore = new Map;
20035
20041
  var GOOGLE_SCOPES = {
20036
20042
  gmail: [
20037
20043
  "https://www.googleapis.com/auth/gmail.readonly",