@hasna/connectors 1.2.0 → 1.3.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/bin/mcp.js CHANGED
@@ -6500,6 +6500,187 @@ var require_dist = __commonJS((exports, module) => {
6500
6500
  exports.default = formatsPlugin;
6501
6501
  });
6502
6502
 
6503
+ // src/lib/installer.ts
6504
+ var exports_installer = {};
6505
+ __export(exports_installer, {
6506
+ removeConnector: () => removeConnector,
6507
+ installConnectors: () => installConnectors,
6508
+ installConnector: () => installConnector,
6509
+ getInstalledConnectors: () => getInstalledConnectors,
6510
+ getConnectorPath: () => getConnectorPath,
6511
+ getConnectorDocs: () => getConnectorDocs,
6512
+ connectorExists: () => connectorExists
6513
+ });
6514
+ import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync, rmSync } from "fs";
6515
+ import { homedir } from "os";
6516
+ import { join as join2, dirname as dirname2 } from "path";
6517
+ import { fileURLToPath as fileURLToPath2 } from "url";
6518
+ function resolveConnectorsDir() {
6519
+ const fromBin = join2(__dirname2, "..", "connectors");
6520
+ if (existsSync2(fromBin))
6521
+ return fromBin;
6522
+ const fromSrc = join2(__dirname2, "..", "..", "connectors");
6523
+ if (existsSync2(fromSrc))
6524
+ return fromSrc;
6525
+ return fromBin;
6526
+ }
6527
+ function getConnectorPath(name) {
6528
+ const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6529
+ return join2(CONNECTORS_DIR, connectorName);
6530
+ }
6531
+ function connectorExists(name) {
6532
+ return existsSync2(getConnectorPath(name));
6533
+ }
6534
+ function installConnector(name, options = {}) {
6535
+ const { targetDir = process.cwd(), overwrite = false } = options;
6536
+ if (!/^[a-z0-9-]+$/.test(name)) {
6537
+ return {
6538
+ connector: name,
6539
+ success: false,
6540
+ error: `Invalid connector name '${name}'`
6541
+ };
6542
+ }
6543
+ const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6544
+ const sourcePath = getConnectorPath(name);
6545
+ const destDir = join2(targetDir, ".connectors");
6546
+ const destPath = join2(destDir, connectorName);
6547
+ if (!existsSync2(sourcePath)) {
6548
+ return {
6549
+ connector: name,
6550
+ success: false,
6551
+ error: `Connector '${name}' not found`
6552
+ };
6553
+ }
6554
+ if (existsSync2(destPath) && !overwrite) {
6555
+ return {
6556
+ connector: name,
6557
+ success: false,
6558
+ error: `Already installed. Use --overwrite to replace.`,
6559
+ path: destPath
6560
+ };
6561
+ }
6562
+ try {
6563
+ if (!existsSync2(destDir)) {
6564
+ mkdirSync(destDir, { recursive: true });
6565
+ }
6566
+ cpSync(sourcePath, destPath, { recursive: true });
6567
+ const homeCredDir = join2(homedir(), ".connectors", connectorName);
6568
+ if (existsSync2(homeCredDir)) {
6569
+ const filesToCopy = ["credentials.json", "current_profile"];
6570
+ for (const file of filesToCopy) {
6571
+ const src = join2(homeCredDir, file);
6572
+ if (existsSync2(src)) {
6573
+ cpSync(src, join2(destPath, file));
6574
+ }
6575
+ }
6576
+ const profilesDir = join2(homeCredDir, "profiles");
6577
+ if (existsSync2(profilesDir)) {
6578
+ cpSync(profilesDir, join2(destPath, "profiles"), { recursive: true });
6579
+ }
6580
+ }
6581
+ updateConnectorsIndex(destDir);
6582
+ return {
6583
+ connector: name,
6584
+ success: true,
6585
+ path: destPath
6586
+ };
6587
+ } catch (error2) {
6588
+ return {
6589
+ connector: name,
6590
+ success: false,
6591
+ error: error2 instanceof Error ? error2.message : "Unknown error"
6592
+ };
6593
+ }
6594
+ }
6595
+ function installConnectors(names, options = {}) {
6596
+ return names.map((name) => installConnector(name, options));
6597
+ }
6598
+ function updateConnectorsIndex(connectorsDir) {
6599
+ const indexPath = join2(connectorsDir, "index.ts");
6600
+ const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
6601
+ const exports = connectors.map((c) => {
6602
+ const name = c.replace("connect-", "");
6603
+ return `export * as ${name} from './${c}/src/index.js';`;
6604
+ }).join(`
6605
+ `);
6606
+ const content = `/**
6607
+ * Auto-generated index of installed connectors
6608
+ * Do not edit manually - run 'connectors install' to update
6609
+ */
6610
+
6611
+ ${exports}
6612
+ `;
6613
+ writeFileSync(indexPath, content);
6614
+ }
6615
+ function getInstalledConnectors(targetDir = process.cwd()) {
6616
+ const connectorsDir = join2(targetDir, ".connectors");
6617
+ if (!existsSync2(connectorsDir)) {
6618
+ return [];
6619
+ }
6620
+ return readdirSync(connectorsDir).filter((f) => {
6621
+ const fullPath = join2(connectorsDir, f);
6622
+ return f.startsWith("connect-") && statSync(fullPath).isDirectory();
6623
+ }).map((f) => f.replace("connect-", ""));
6624
+ }
6625
+ function getConnectorDocs(name) {
6626
+ const connectorPath = getConnectorPath(name);
6627
+ const claudeMdPath = join2(connectorPath, "CLAUDE.md");
6628
+ if (!existsSync2(claudeMdPath))
6629
+ return null;
6630
+ const raw = readFileSync2(claudeMdPath, "utf-8");
6631
+ return {
6632
+ overview: extractSection(raw, "Project Overview"),
6633
+ auth: extractSection(raw, "Authentication"),
6634
+ envVars: parseEnvVarsTable(extractSection(raw, "Environment Variables")),
6635
+ cliCommands: extractSection(raw, "CLI Commands"),
6636
+ dataStorage: extractSection(raw, "Data Storage"),
6637
+ raw
6638
+ };
6639
+ }
6640
+ function extractSection(markdown, heading) {
6641
+ const regex = new RegExp(`^##\\s+${escapeRegex2(heading)}\\s*$`, "m");
6642
+ const match = regex.exec(markdown);
6643
+ if (!match)
6644
+ return "";
6645
+ const start = match.index + match[0].length;
6646
+ const nextHeading = markdown.slice(start).search(/^##\s/m);
6647
+ const content = nextHeading === -1 ? markdown.slice(start) : markdown.slice(start, start + nextHeading);
6648
+ return content.trim();
6649
+ }
6650
+ function escapeRegex2(str) {
6651
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6652
+ }
6653
+ function parseEnvVarsTable(section) {
6654
+ if (!section)
6655
+ return [];
6656
+ const vars = [];
6657
+ const lines = section.split(`
6658
+ `);
6659
+ for (const line of lines) {
6660
+ const match = line.match(/\|\s*`([^`]+)`\s*\|\s*(.+?)\s*\|/);
6661
+ if (match && match[1] !== "Variable") {
6662
+ vars.push({ variable: match[1], description: match[2].trim() });
6663
+ }
6664
+ }
6665
+ return vars;
6666
+ }
6667
+ function removeConnector(name, targetDir = process.cwd()) {
6668
+ const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6669
+ const connectorsDir = join2(targetDir, ".connectors");
6670
+ const connectorPath = join2(connectorsDir, connectorName);
6671
+ if (!existsSync2(connectorPath)) {
6672
+ return false;
6673
+ }
6674
+ rmSync(connectorPath, { recursive: true });
6675
+ updateConnectorsIndex(connectorsDir);
6676
+ return true;
6677
+ }
6678
+ var __dirname2, CONNECTORS_DIR;
6679
+ var init_installer = __esm(() => {
6680
+ __dirname2 = dirname2(fileURLToPath2(import.meta.url));
6681
+ CONNECTORS_DIR = resolveConnectorsDir();
6682
+ });
6683
+
6503
6684
  // src/db/database.ts
6504
6685
  var exports_database = {};
6505
6686
  __export(exports_database, {
@@ -6607,6 +6788,22 @@ function migrate(db) {
6607
6788
  created_at TEXT NOT NULL
6608
6789
  )
6609
6790
  `);
6791
+ db.run(`
6792
+ CREATE TABLE IF NOT EXISTS connector_usage (
6793
+ id TEXT PRIMARY KEY,
6794
+ connector TEXT NOT NULL,
6795
+ action TEXT NOT NULL,
6796
+ agent_id TEXT,
6797
+ timestamp TEXT NOT NULL
6798
+ )
6799
+ `);
6800
+ db.run(`CREATE INDEX IF NOT EXISTS idx_usage_connector ON connector_usage(connector, timestamp DESC)`);
6801
+ db.run(`
6802
+ CREATE TABLE IF NOT EXISTS connector_promotions (
6803
+ connector TEXT UNIQUE NOT NULL,
6804
+ promoted_at TEXT NOT NULL
6805
+ )
6806
+ `);
6610
6807
  }
6611
6808
  var DB_DIR, DB_PATH, _db = null;
6612
6809
  var init_database = __esm(() => {
@@ -6781,6 +6978,75 @@ var init_strip = __esm(() => {
6781
6978
  init_llm();
6782
6979
  });
6783
6980
 
6981
+ // src/db/promotions.ts
6982
+ var exports_promotions = {};
6983
+ __export(exports_promotions, {
6984
+ promoteConnector: () => promoteConnector,
6985
+ isPromoted: () => isPromoted,
6986
+ getPromotedConnectors: () => getPromotedConnectors,
6987
+ demoteConnector: () => demoteConnector
6988
+ });
6989
+ function promoteConnector(name, db) {
6990
+ const d = db ?? getDatabase();
6991
+ d.run("INSERT OR REPLACE INTO connector_promotions (connector, promoted_at) VALUES (?, ?)", [name, now()]);
6992
+ }
6993
+ function demoteConnector(name, db) {
6994
+ const d = db ?? getDatabase();
6995
+ return d.run("DELETE FROM connector_promotions WHERE connector = ?", [name]).changes > 0;
6996
+ }
6997
+ function getPromotedConnectors(db) {
6998
+ const d = db ?? getDatabase();
6999
+ return d.query("SELECT connector FROM connector_promotions ORDER BY promoted_at DESC").all().map((r) => r.connector);
7000
+ }
7001
+ function isPromoted(name, db) {
7002
+ const d = db ?? getDatabase();
7003
+ const row = d.query("SELECT 1 FROM connector_promotions WHERE connector = ?").get(name);
7004
+ return !!row;
7005
+ }
7006
+ var init_promotions = __esm(() => {
7007
+ init_database();
7008
+ });
7009
+
7010
+ // src/db/usage.ts
7011
+ var exports_usage = {};
7012
+ __export(exports_usage, {
7013
+ logUsage: () => logUsage,
7014
+ getUsageStats: () => getUsageStats,
7015
+ getUsageMap: () => getUsageMap,
7016
+ getTopConnectors: () => getTopConnectors,
7017
+ cleanOldUsage: () => cleanOldUsage
7018
+ });
7019
+ function logUsage(connector, action, agentId, db) {
7020
+ const d = db ?? getDatabase();
7021
+ d.run("INSERT INTO connector_usage (id, connector, action, agent_id, timestamp) VALUES (?, ?, ?, ?, ?)", [shortUuid(), connector, action, agentId ?? null, now()]);
7022
+ }
7023
+ function getUsageStats(connector, db) {
7024
+ const d = db ?? getDatabase();
7025
+ const total = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ?").get(connector).c;
7026
+ const d7 = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
7027
+ const last7d = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ? AND timestamp > ?").get(connector, d7).c;
7028
+ const d1 = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
7029
+ const last24h = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ? AND timestamp > ?").get(connector, d1).c;
7030
+ return { connector, total, last7d, last24h };
7031
+ }
7032
+ function getTopConnectors(limit = 10, days = 7, db) {
7033
+ const d = db ?? getDatabase();
7034
+ const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
7035
+ return d.query("SELECT connector, COUNT(*) as count FROM connector_usage WHERE timestamp > ? GROUP BY connector ORDER BY count DESC LIMIT ?").all(since, limit);
7036
+ }
7037
+ function getUsageMap(days = 7, db) {
7038
+ const top = getTopConnectors(100, days, db);
7039
+ return new Map(top.map((t) => [t.connector, t.count]));
7040
+ }
7041
+ function cleanOldUsage(days = 30, db) {
7042
+ const d = db ?? getDatabase();
7043
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
7044
+ return d.run("DELETE FROM connector_usage WHERE timestamp < ?", [cutoff]).changes;
7045
+ }
7046
+ var init_usage = __esm(() => {
7047
+ init_database();
7048
+ });
7049
+
6784
7050
  // src/db/jobs.ts
6785
7051
  var exports_jobs = {};
6786
7052
  __export(exports_jobs, {
@@ -20083,6 +20349,94 @@ class StdioServerTransport {
20083
20349
  import { existsSync, readFileSync } from "fs";
20084
20350
  import { join, dirname } from "path";
20085
20351
  import { fileURLToPath } from "url";
20352
+
20353
+ // src/lib/fuzzy.ts
20354
+ function levenshtein(a, b) {
20355
+ const m = a.length;
20356
+ const n = b.length;
20357
+ if (m === 0)
20358
+ return n;
20359
+ if (n === 0)
20360
+ return m;
20361
+ let prev = new Array(n + 1);
20362
+ let curr = new Array(n + 1);
20363
+ for (let j = 0;j <= n; j++)
20364
+ prev[j] = j;
20365
+ for (let i = 1;i <= m; i++) {
20366
+ curr[0] = i;
20367
+ for (let j = 1;j <= n; j++) {
20368
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
20369
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
20370
+ }
20371
+ [prev, curr] = [curr, prev];
20372
+ }
20373
+ return prev[n];
20374
+ }
20375
+ function bestFuzzyScore(token, candidates, maxDistance = 2) {
20376
+ if (token.length < 3)
20377
+ return 0;
20378
+ let bestDist = maxDistance + 1;
20379
+ for (const c of candidates) {
20380
+ if (Math.abs(token.length - c.length) > maxDistance)
20381
+ continue;
20382
+ const d = levenshtein(token, c);
20383
+ if (d < bestDist)
20384
+ bestDist = d;
20385
+ if (d === 0)
20386
+ return maxDistance + 1;
20387
+ }
20388
+ if (bestDist > maxDistance)
20389
+ return 0;
20390
+ return maxDistance - bestDist + 1;
20391
+ }
20392
+
20393
+ // src/lib/synonyms.ts
20394
+ var SYNONYM_MAP = {
20395
+ email: ["smtp", "mail", "inbox", "resend", "ses"],
20396
+ chat: ["messaging", "im", "slack", "discord", "teams"],
20397
+ sms: ["text", "twilio", "messaging"],
20398
+ payment: ["billing", "invoicing", "commerce", "checkout", "stripe"],
20399
+ payments: ["billing", "invoicing", "commerce", "checkout", "stripe"],
20400
+ ecommerce: ["shop", "store", "commerce", "shopify"],
20401
+ finance: ["banking", "accounting", "invoicing"],
20402
+ crypto: ["blockchain", "web3", "wallet"],
20403
+ ai: ["llm", "ml", "model", "gpt", "claude", "anthropic", "openai"],
20404
+ llm: ["ai", "model", "gpt", "claude"],
20405
+ auth: ["oauth", "sso", "login", "identity", "authentication"],
20406
+ database: ["db", "sql", "nosql", "postgres", "mongo", "supabase"],
20407
+ deploy: ["hosting", "infrastructure", "ci", "cd", "vercel"],
20408
+ storage: ["files", "drive", "s3", "bucket", "upload"],
20409
+ cloud: ["aws", "gcp", "azure", "infrastructure"],
20410
+ api: ["rest", "graphql", "endpoint", "webhook"],
20411
+ monitoring: ["logs", "observability", "alerting", "datadog", "sentry"],
20412
+ ci: ["cd", "deploy", "pipeline", "github", "actions"],
20413
+ crm: ["sales", "leads", "contacts", "hubspot", "salesforce"],
20414
+ analytics: ["data", "metrics", "tracking", "mixpanel", "amplitude"],
20415
+ project: ["task", "issue", "board", "jira", "linear", "asana"],
20416
+ docs: ["documentation", "wiki", "notion", "confluence"],
20417
+ design: ["figma", "sketch", "ui", "ux"],
20418
+ security: ["auth", "encryption", "compliance", "vault"]
20419
+ };
20420
+ function expandQuery(tokens) {
20421
+ const synonyms = new Set;
20422
+ for (const token of tokens) {
20423
+ const matches = SYNONYM_MAP[token];
20424
+ if (matches) {
20425
+ for (const syn of matches) {
20426
+ if (!tokens.includes(syn))
20427
+ synonyms.add(syn);
20428
+ }
20429
+ }
20430
+ for (const [key, values] of Object.entries(SYNONYM_MAP)) {
20431
+ if (values.includes(token) && !tokens.includes(key)) {
20432
+ synonyms.add(key);
20433
+ }
20434
+ }
20435
+ }
20436
+ return { original: tokens, expanded: [...synonyms] };
20437
+ }
20438
+
20439
+ // src/lib/registry.ts
20086
20440
  var CATEGORIES = [
20087
20441
  "AI & ML",
20088
20442
  "Developer Tools",
@@ -26001,9 +26355,152 @@ var CONNECTORS = [
26001
26355
  function getConnectorsByCategory(category) {
26002
26356
  return CONNECTORS.filter((c) => c.category === category);
26003
26357
  }
26004
- function searchConnectors(query) {
26005
- const q = query.toLowerCase();
26006
- return CONNECTORS.filter((c) => c.name.toLowerCase().includes(q) || c.displayName.toLowerCase().includes(q) || c.description.toLowerCase().includes(q) || c.tags.some((t) => t.includes(q)));
26358
+ function searchConnectors(query, context) {
26359
+ const tokens = query.toLowerCase().trim().split(/\s+/).filter(Boolean);
26360
+ if (tokens.length === 0)
26361
+ return [];
26362
+ const limit = context?.limit ?? 20;
26363
+ const installed = new Set(context?.installed ?? []);
26364
+ const promoted = new Set(context?.promoted ?? []);
26365
+ const usage = context?.usage ?? new Map;
26366
+ const results = [];
26367
+ for (const c of CONNECTORS) {
26368
+ const nameLow = c.name.toLowerCase();
26369
+ const displayLow = c.displayName.toLowerCase();
26370
+ const descLow = c.description.toLowerCase();
26371
+ const tagsLow = c.tags.map((t) => t.toLowerCase());
26372
+ let score = 0;
26373
+ const matchReasons = [];
26374
+ let allTokensMatch = true;
26375
+ for (const token of tokens) {
26376
+ let tokenMatched = false;
26377
+ if (nameLow === token) {
26378
+ score += 100;
26379
+ matchReasons.push(`name="${token}"`);
26380
+ tokenMatched = true;
26381
+ } else if (nameLow.includes(token)) {
26382
+ score += 10;
26383
+ matchReasons.push(`name~${token}`);
26384
+ tokenMatched = true;
26385
+ }
26386
+ if (tagsLow.includes(token)) {
26387
+ score += 8;
26388
+ if (!tokenMatched)
26389
+ matchReasons.push(`tag="${token}"`);
26390
+ tokenMatched = true;
26391
+ } else if (tagsLow.some((t) => t.includes(token))) {
26392
+ score += 5;
26393
+ if (!tokenMatched)
26394
+ matchReasons.push(`tag~${token}`);
26395
+ tokenMatched = true;
26396
+ }
26397
+ if (displayLow.includes(token)) {
26398
+ score += 3;
26399
+ if (!tokenMatched)
26400
+ matchReasons.push(`display~${token}`);
26401
+ tokenMatched = true;
26402
+ }
26403
+ if (descLow.includes(token)) {
26404
+ score += 1;
26405
+ if (!tokenMatched)
26406
+ matchReasons.push(`desc~${token}`);
26407
+ tokenMatched = true;
26408
+ }
26409
+ if (!tokenMatched && token.length >= 3) {
26410
+ const nameFuzzy = bestFuzzyScore(token, [nameLow], 1);
26411
+ if (nameFuzzy > 0) {
26412
+ score += nameFuzzy * 6;
26413
+ matchReasons.push(`fuzzy:name\u2248${token}`);
26414
+ tokenMatched = true;
26415
+ }
26416
+ if (!tokenMatched) {
26417
+ const tagFuzzy = bestFuzzyScore(token, tagsLow, 2);
26418
+ if (tagFuzzy > 0) {
26419
+ score += tagFuzzy * 3;
26420
+ matchReasons.push(`fuzzy:tag\u2248${token}`);
26421
+ tokenMatched = true;
26422
+ }
26423
+ }
26424
+ if (!tokenMatched) {
26425
+ const displayFuzzy = bestFuzzyScore(token, [displayLow], 2);
26426
+ if (displayFuzzy > 0) {
26427
+ score += displayFuzzy * 2;
26428
+ matchReasons.push(`fuzzy:display\u2248${token}`);
26429
+ tokenMatched = true;
26430
+ }
26431
+ }
26432
+ }
26433
+ if (!tokenMatched) {
26434
+ allTokensMatch = false;
26435
+ break;
26436
+ }
26437
+ }
26438
+ if (!allTokensMatch)
26439
+ continue;
26440
+ const badges = [];
26441
+ if (installed.has(c.name)) {
26442
+ score += 50;
26443
+ badges.push("installed");
26444
+ }
26445
+ if (promoted.has(c.name)) {
26446
+ score += 30;
26447
+ badges.push("promoted");
26448
+ }
26449
+ const usageCount = usage.get(c.name) ?? 0;
26450
+ if (usageCount > 0) {
26451
+ score += Math.min(usageCount * 2, 40);
26452
+ if (usageCount >= 5)
26453
+ badges.push("hot");
26454
+ }
26455
+ results.push({ ...c, score, matchReasons, badges });
26456
+ }
26457
+ const matchedNames = new Set(results.map((r) => r.name));
26458
+ if (results.length < limit) {
26459
+ const { expanded } = expandQuery(tokens);
26460
+ if (expanded.length > 0) {
26461
+ for (const c of CONNECTORS) {
26462
+ if (matchedNames.has(c.name))
26463
+ continue;
26464
+ const nameLow2 = c.name.toLowerCase();
26465
+ const tagsLow2 = c.tags.map((t) => t.toLowerCase());
26466
+ const descLow2 = c.description.toLowerCase();
26467
+ let synScore = 0;
26468
+ const synReasons = [];
26469
+ for (const syn of expanded) {
26470
+ if (nameLow2.includes(syn)) {
26471
+ synScore += 2;
26472
+ synReasons.push(`syn:name~${syn}`);
26473
+ } else if (tagsLow2.some((t) => t.includes(syn))) {
26474
+ synScore += 1;
26475
+ synReasons.push(`syn:tag~${syn}`);
26476
+ } else if (descLow2.includes(syn)) {
26477
+ synScore += 1;
26478
+ synReasons.push(`syn:desc~${syn}`);
26479
+ }
26480
+ }
26481
+ if (synScore > 0) {
26482
+ const badges = [];
26483
+ if (installed.has(c.name)) {
26484
+ synScore += 50;
26485
+ badges.push("installed");
26486
+ }
26487
+ if (promoted.has(c.name)) {
26488
+ synScore += 30;
26489
+ badges.push("promoted");
26490
+ }
26491
+ const usageCount = usage.get(c.name) ?? 0;
26492
+ if (usageCount > 0) {
26493
+ synScore += Math.min(usageCount * 2, 40);
26494
+ if (usageCount >= 5)
26495
+ badges.push("hot");
26496
+ }
26497
+ results.push({ ...c, score: synScore, matchReasons: synReasons, badges });
26498
+ }
26499
+ }
26500
+ }
26501
+ }
26502
+ results.sort((a, b) => b.score - a.score);
26503
+ return results.slice(0, limit);
26007
26504
  }
26008
26505
  function getConnector(name) {
26009
26506
  return CONNECTORS.find((c) => c.name === name);
@@ -26032,170 +26529,12 @@ function loadConnectorVersions() {
26032
26529
  }
26033
26530
  }
26034
26531
 
26035
- // src/lib/installer.ts
26036
- import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync, rmSync } from "fs";
26037
- import { homedir } from "os";
26038
- import { join as join2, dirname as dirname2 } from "path";
26039
- import { fileURLToPath as fileURLToPath2 } from "url";
26040
- var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
26041
- function resolveConnectorsDir() {
26042
- const fromBin = join2(__dirname2, "..", "connectors");
26043
- if (existsSync2(fromBin))
26044
- return fromBin;
26045
- const fromSrc = join2(__dirname2, "..", "..", "connectors");
26046
- if (existsSync2(fromSrc))
26047
- return fromSrc;
26048
- return fromBin;
26049
- }
26050
- var CONNECTORS_DIR = resolveConnectorsDir();
26051
- function getConnectorPath(name) {
26052
- const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
26053
- return join2(CONNECTORS_DIR, connectorName);
26054
- }
26055
- function installConnector(name, options = {}) {
26056
- const { targetDir = process.cwd(), overwrite = false } = options;
26057
- if (!/^[a-z0-9-]+$/.test(name)) {
26058
- return {
26059
- connector: name,
26060
- success: false,
26061
- error: `Invalid connector name '${name}'`
26062
- };
26063
- }
26064
- const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
26065
- const sourcePath = getConnectorPath(name);
26066
- const destDir = join2(targetDir, ".connectors");
26067
- const destPath = join2(destDir, connectorName);
26068
- if (!existsSync2(sourcePath)) {
26069
- return {
26070
- connector: name,
26071
- success: false,
26072
- error: `Connector '${name}' not found`
26073
- };
26074
- }
26075
- if (existsSync2(destPath) && !overwrite) {
26076
- return {
26077
- connector: name,
26078
- success: false,
26079
- error: `Already installed. Use --overwrite to replace.`,
26080
- path: destPath
26081
- };
26082
- }
26083
- try {
26084
- if (!existsSync2(destDir)) {
26085
- mkdirSync(destDir, { recursive: true });
26086
- }
26087
- cpSync(sourcePath, destPath, { recursive: true });
26088
- const homeCredDir = join2(homedir(), ".connectors", connectorName);
26089
- if (existsSync2(homeCredDir)) {
26090
- const filesToCopy = ["credentials.json", "current_profile"];
26091
- for (const file of filesToCopy) {
26092
- const src = join2(homeCredDir, file);
26093
- if (existsSync2(src)) {
26094
- cpSync(src, join2(destPath, file));
26095
- }
26096
- }
26097
- const profilesDir = join2(homeCredDir, "profiles");
26098
- if (existsSync2(profilesDir)) {
26099
- cpSync(profilesDir, join2(destPath, "profiles"), { recursive: true });
26100
- }
26101
- }
26102
- updateConnectorsIndex(destDir);
26103
- return {
26104
- connector: name,
26105
- success: true,
26106
- path: destPath
26107
- };
26108
- } catch (error2) {
26109
- return {
26110
- connector: name,
26111
- success: false,
26112
- error: error2 instanceof Error ? error2.message : "Unknown error"
26113
- };
26114
- }
26115
- }
26116
- function updateConnectorsIndex(connectorsDir) {
26117
- const indexPath = join2(connectorsDir, "index.ts");
26118
- const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
26119
- const exports = connectors.map((c) => {
26120
- const name = c.replace("connect-", "");
26121
- return `export * as ${name} from './${c}/src/index.js';`;
26122
- }).join(`
26123
- `);
26124
- const content = `/**
26125
- * Auto-generated index of installed connectors
26126
- * Do not edit manually - run 'connectors install' to update
26127
- */
26128
-
26129
- ${exports}
26130
- `;
26131
- writeFileSync(indexPath, content);
26132
- }
26133
- function getInstalledConnectors(targetDir = process.cwd()) {
26134
- const connectorsDir = join2(targetDir, ".connectors");
26135
- if (!existsSync2(connectorsDir)) {
26136
- return [];
26137
- }
26138
- return readdirSync(connectorsDir).filter((f) => {
26139
- const fullPath = join2(connectorsDir, f);
26140
- return f.startsWith("connect-") && statSync(fullPath).isDirectory();
26141
- }).map((f) => f.replace("connect-", ""));
26142
- }
26143
- function getConnectorDocs(name) {
26144
- const connectorPath = getConnectorPath(name);
26145
- const claudeMdPath = join2(connectorPath, "CLAUDE.md");
26146
- if (!existsSync2(claudeMdPath))
26147
- return null;
26148
- const raw = readFileSync2(claudeMdPath, "utf-8");
26149
- return {
26150
- overview: extractSection(raw, "Project Overview"),
26151
- auth: extractSection(raw, "Authentication"),
26152
- envVars: parseEnvVarsTable(extractSection(raw, "Environment Variables")),
26153
- cliCommands: extractSection(raw, "CLI Commands"),
26154
- dataStorage: extractSection(raw, "Data Storage"),
26155
- raw
26156
- };
26157
- }
26158
- function extractSection(markdown, heading) {
26159
- const regex = new RegExp(`^##\\s+${escapeRegex2(heading)}\\s*$`, "m");
26160
- const match = regex.exec(markdown);
26161
- if (!match)
26162
- return "";
26163
- const start = match.index + match[0].length;
26164
- const nextHeading = markdown.slice(start).search(/^##\s/m);
26165
- const content = nextHeading === -1 ? markdown.slice(start) : markdown.slice(start, start + nextHeading);
26166
- return content.trim();
26167
- }
26168
- function escapeRegex2(str) {
26169
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26170
- }
26171
- function parseEnvVarsTable(section) {
26172
- if (!section)
26173
- return [];
26174
- const vars = [];
26175
- const lines = section.split(`
26176
- `);
26177
- for (const line of lines) {
26178
- const match = line.match(/\|\s*`([^`]+)`\s*\|\s*(.+?)\s*\|/);
26179
- if (match && match[1] !== "Variable") {
26180
- vars.push({ variable: match[1], description: match[2].trim() });
26181
- }
26182
- }
26183
- return vars;
26184
- }
26185
- function removeConnector(name, targetDir = process.cwd()) {
26186
- const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
26187
- const connectorsDir = join2(targetDir, ".connectors");
26188
- const connectorPath = join2(connectorsDir, connectorName);
26189
- if (!existsSync2(connectorPath)) {
26190
- return false;
26191
- }
26192
- rmSync(connectorPath, { recursive: true });
26193
- updateConnectorsIndex(connectorsDir);
26194
- return true;
26195
- }
26532
+ // src/mcp/index.ts
26533
+ init_installer();
26196
26534
 
26197
26535
  // src/server/auth.ts
26198
26536
  import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
26537
+ init_installer();
26199
26538
  import { homedir as homedir3 } from "os";
26200
26539
  import { join as join4 } from "path";
26201
26540
 
@@ -26366,6 +26705,16 @@ function loadTokens(name) {
26366
26705
  return null;
26367
26706
  }
26368
26707
  }
26708
+ const profileConfig = loadProfileConfig(name);
26709
+ if (profileConfig.refreshToken || profileConfig.accessToken) {
26710
+ return {
26711
+ accessToken: profileConfig.accessToken,
26712
+ refreshToken: profileConfig.refreshToken,
26713
+ expiresAt: profileConfig.expiresAt,
26714
+ tokenType: profileConfig.tokenType,
26715
+ scope: profileConfig.scope
26716
+ };
26717
+ }
26369
26718
  return null;
26370
26719
  }
26371
26720
  function getAuthStatus(name) {
@@ -26733,7 +27082,7 @@ init_strip();
26733
27082
  // package.json
26734
27083
  var package_default = {
26735
27084
  name: "@hasna/connectors",
26736
- version: "1.1.18",
27085
+ version: "1.2.1",
26737
27086
  description: "Open source connector library - Install API connectors with a single command",
26738
27087
  type: "module",
26739
27088
  bin: {
@@ -26832,8 +27181,15 @@ server.registerTool("search_connectors", {
26832
27181
  description: "Search connectors by name or keyword.",
26833
27182
  inputSchema: { query: exports_external.string() }
26834
27183
  }, async ({ query }) => {
26835
- const results = searchConnectors(query);
26836
- return stripped(JSON.stringify(results.map((c) => ({ name: c.name, displayName: c.displayName, version: c.version, category: c.category, description: c.description })), null, 2));
27184
+ const { getInstalledConnectors: getInstalledConnectors2 } = await Promise.resolve().then(() => (init_installer(), exports_installer));
27185
+ const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
27186
+ const { getUsageMap: getUsageMap2 } = await Promise.resolve().then(() => (init_usage(), exports_usage));
27187
+ const results = searchConnectors(query, {
27188
+ installed: getInstalledConnectors2(),
27189
+ promoted: getPromotedConnectors2(),
27190
+ usage: getUsageMap2()
27191
+ });
27192
+ return stripped(JSON.stringify(results.map((c) => ({ name: c.name, displayName: c.displayName, version: c.version, category: c.category, description: c.description, score: c.score, badges: c.badges })), null, 2));
26837
27193
  });
26838
27194
  server.registerTool("list_connectors", {
26839
27195
  title: "List Connectors",
@@ -27127,6 +27483,10 @@ server.registerTool("run_connector_operation", {
27127
27483
  isError: true
27128
27484
  };
27129
27485
  }
27486
+ try {
27487
+ const { logUsage: logUsage2 } = await Promise.resolve().then(() => (init_usage(), exports_usage));
27488
+ logUsage2(name, "run");
27489
+ } catch {}
27130
27490
  const finalArgs = [...args];
27131
27491
  if (format) {
27132
27492
  finalArgs.push("--format", format);
@@ -27295,6 +27655,27 @@ server.registerTool("describe_tools", {
27295
27655
  `);
27296
27656
  return { content: [{ type: "text", text: result }] };
27297
27657
  });
27658
+ server.registerTool("get_hot_connectors", { title: "Get Hot Connectors", description: "Top connectors by usage.", inputSchema: { limit: exports_external.number().optional(), days: exports_external.number().optional() } }, async ({ limit, days }) => {
27659
+ const { getTopConnectors: getTopConnectors2 } = await Promise.resolve().then(() => (init_usage(), exports_usage));
27660
+ const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
27661
+ const top = getTopConnectors2(limit ?? 10, days ?? 7);
27662
+ const promoted = new Set(getPromotedConnectors2());
27663
+ const result = top.map((t) => ({ ...t, promoted: promoted.has(t.connector) }));
27664
+ return stripped(JSON.stringify(result, null, 2));
27665
+ });
27666
+ server.registerTool("promote_connector", { title: "Promote Connector", description: "Boost a connector in search rankings.", inputSchema: { name: exports_external.string() } }, async ({ name }) => {
27667
+ const meta = getConnector(name);
27668
+ if (!meta)
27669
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Connector not found" }) }], isError: true };
27670
+ const { promoteConnector: promoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
27671
+ promoteConnector2(name);
27672
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, connector: name }) }] };
27673
+ });
27674
+ server.registerTool("demote_connector", { title: "Demote Connector", description: "Remove search ranking boost.", inputSchema: { name: exports_external.string() } }, async ({ name }) => {
27675
+ const { demoteConnector: demoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
27676
+ const removed = demoteConnector2(name);
27677
+ return { content: [{ type: "text", text: JSON.stringify({ success: removed, connector: name }) }] };
27678
+ });
27298
27679
  server.registerTool("list_jobs", { title: "List Jobs", description: "List scheduled connector jobs.", inputSchema: {} }, async () => {
27299
27680
  const { listJobs: listJobs2 } = await Promise.resolve().then(() => (init_jobs(), exports_jobs));
27300
27681
  const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_database(), exports_database));