@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/index.js +432 -10
- package/bin/mcp.js +548 -167
- package/bin/serve.js +119 -0
- package/dist/db/promotions.d.ts +5 -0
- package/dist/db/promotions.test.d.ts +1 -0
- package/dist/db/usage.d.ts +17 -0
- package/dist/db/usage.test.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +234 -3
- package/dist/lib/fuzzy.d.ts +16 -0
- package/dist/lib/fuzzy.test.d.ts +1 -0
- package/dist/lib/registry.d.ts +22 -1
- package/dist/lib/synonyms.d.ts +12 -0
- package/dist/lib/synonyms.test.d.ts +1 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __create = Object.create;
|
|
|
4
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
9
|
var __toESM = (mod, isNodeMode, target) => {
|
|
9
10
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
@@ -16,6 +17,20 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
16
17
|
});
|
|
17
18
|
return to;
|
|
18
19
|
};
|
|
20
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
|
+
var __toCommonJS = (from) => {
|
|
22
|
+
var entry = __moduleCache.get(from), desc;
|
|
23
|
+
if (entry)
|
|
24
|
+
return entry;
|
|
25
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
28
|
+
get: () => from[key],
|
|
29
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
+
}));
|
|
31
|
+
__moduleCache.set(from, entry);
|
|
32
|
+
return entry;
|
|
33
|
+
};
|
|
19
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
35
|
var __export = (target, all) => {
|
|
21
36
|
for (var name in all)
|
|
@@ -2099,6 +2114,22 @@ function migrate(db) {
|
|
|
2099
2114
|
created_at TEXT NOT NULL
|
|
2100
2115
|
)
|
|
2101
2116
|
`);
|
|
2117
|
+
db.run(`
|
|
2118
|
+
CREATE TABLE IF NOT EXISTS connector_usage (
|
|
2119
|
+
id TEXT PRIMARY KEY,
|
|
2120
|
+
connector TEXT NOT NULL,
|
|
2121
|
+
action TEXT NOT NULL,
|
|
2122
|
+
agent_id TEXT,
|
|
2123
|
+
timestamp TEXT NOT NULL
|
|
2124
|
+
)
|
|
2125
|
+
`);
|
|
2126
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_usage_connector ON connector_usage(connector, timestamp DESC)`);
|
|
2127
|
+
db.run(`
|
|
2128
|
+
CREATE TABLE IF NOT EXISTS connector_promotions (
|
|
2129
|
+
connector TEXT UNIQUE NOT NULL,
|
|
2130
|
+
promoted_at TEXT NOT NULL
|
|
2131
|
+
)
|
|
2132
|
+
`);
|
|
2102
2133
|
}
|
|
2103
2134
|
var DB_DIR, DB_PATH, _db = null;
|
|
2104
2135
|
var init_database = __esm(() => {
|
|
@@ -2424,6 +2455,95 @@ var init_workflow_runner = __esm(() => {
|
|
|
2424
2455
|
init_strip();
|
|
2425
2456
|
});
|
|
2426
2457
|
|
|
2458
|
+
// src/lib/fuzzy.ts
|
|
2459
|
+
function levenshtein(a, b) {
|
|
2460
|
+
const m = a.length;
|
|
2461
|
+
const n = b.length;
|
|
2462
|
+
if (m === 0)
|
|
2463
|
+
return n;
|
|
2464
|
+
if (n === 0)
|
|
2465
|
+
return m;
|
|
2466
|
+
let prev = new Array(n + 1);
|
|
2467
|
+
let curr = new Array(n + 1);
|
|
2468
|
+
for (let j = 0;j <= n; j++)
|
|
2469
|
+
prev[j] = j;
|
|
2470
|
+
for (let i = 1;i <= m; i++) {
|
|
2471
|
+
curr[0] = i;
|
|
2472
|
+
for (let j = 1;j <= n; j++) {
|
|
2473
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
2474
|
+
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
2475
|
+
}
|
|
2476
|
+
[prev, curr] = [curr, prev];
|
|
2477
|
+
}
|
|
2478
|
+
return prev[n];
|
|
2479
|
+
}
|
|
2480
|
+
function bestFuzzyScore(token, candidates, maxDistance = 2) {
|
|
2481
|
+
if (token.length < 3)
|
|
2482
|
+
return 0;
|
|
2483
|
+
let bestDist = maxDistance + 1;
|
|
2484
|
+
for (const c of candidates) {
|
|
2485
|
+
if (Math.abs(token.length - c.length) > maxDistance)
|
|
2486
|
+
continue;
|
|
2487
|
+
const d = levenshtein(token, c);
|
|
2488
|
+
if (d < bestDist)
|
|
2489
|
+
bestDist = d;
|
|
2490
|
+
if (d === 0)
|
|
2491
|
+
return maxDistance + 1;
|
|
2492
|
+
}
|
|
2493
|
+
if (bestDist > maxDistance)
|
|
2494
|
+
return 0;
|
|
2495
|
+
return maxDistance - bestDist + 1;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// src/lib/synonyms.ts
|
|
2499
|
+
function expandQuery(tokens) {
|
|
2500
|
+
const synonyms = new Set;
|
|
2501
|
+
for (const token of tokens) {
|
|
2502
|
+
const matches = SYNONYM_MAP[token];
|
|
2503
|
+
if (matches) {
|
|
2504
|
+
for (const syn of matches) {
|
|
2505
|
+
if (!tokens.includes(syn))
|
|
2506
|
+
synonyms.add(syn);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
for (const [key, values] of Object.entries(SYNONYM_MAP)) {
|
|
2510
|
+
if (values.includes(token) && !tokens.includes(key)) {
|
|
2511
|
+
synonyms.add(key);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
return { original: tokens, expanded: [...synonyms] };
|
|
2516
|
+
}
|
|
2517
|
+
var SYNONYM_MAP;
|
|
2518
|
+
var init_synonyms = __esm(() => {
|
|
2519
|
+
SYNONYM_MAP = {
|
|
2520
|
+
email: ["smtp", "mail", "inbox", "resend", "ses"],
|
|
2521
|
+
chat: ["messaging", "im", "slack", "discord", "teams"],
|
|
2522
|
+
sms: ["text", "twilio", "messaging"],
|
|
2523
|
+
payment: ["billing", "invoicing", "commerce", "checkout", "stripe"],
|
|
2524
|
+
payments: ["billing", "invoicing", "commerce", "checkout", "stripe"],
|
|
2525
|
+
ecommerce: ["shop", "store", "commerce", "shopify"],
|
|
2526
|
+
finance: ["banking", "accounting", "invoicing"],
|
|
2527
|
+
crypto: ["blockchain", "web3", "wallet"],
|
|
2528
|
+
ai: ["llm", "ml", "model", "gpt", "claude", "anthropic", "openai"],
|
|
2529
|
+
llm: ["ai", "model", "gpt", "claude"],
|
|
2530
|
+
auth: ["oauth", "sso", "login", "identity", "authentication"],
|
|
2531
|
+
database: ["db", "sql", "nosql", "postgres", "mongo", "supabase"],
|
|
2532
|
+
deploy: ["hosting", "infrastructure", "ci", "cd", "vercel"],
|
|
2533
|
+
storage: ["files", "drive", "s3", "bucket", "upload"],
|
|
2534
|
+
cloud: ["aws", "gcp", "azure", "infrastructure"],
|
|
2535
|
+
api: ["rest", "graphql", "endpoint", "webhook"],
|
|
2536
|
+
monitoring: ["logs", "observability", "alerting", "datadog", "sentry"],
|
|
2537
|
+
ci: ["cd", "deploy", "pipeline", "github", "actions"],
|
|
2538
|
+
crm: ["sales", "leads", "contacts", "hubspot", "salesforce"],
|
|
2539
|
+
analytics: ["data", "metrics", "tracking", "mixpanel", "amplitude"],
|
|
2540
|
+
project: ["task", "issue", "board", "jira", "linear", "asana"],
|
|
2541
|
+
docs: ["documentation", "wiki", "notion", "confluence"],
|
|
2542
|
+
design: ["figma", "sketch", "ui", "ux"],
|
|
2543
|
+
security: ["auth", "encryption", "compliance", "vault"]
|
|
2544
|
+
};
|
|
2545
|
+
});
|
|
2546
|
+
|
|
2427
2547
|
// src/lib/registry.ts
|
|
2428
2548
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
2429
2549
|
import { join as join3, dirname } from "path";
|
|
@@ -2431,9 +2551,152 @@ import { fileURLToPath } from "url";
|
|
|
2431
2551
|
function getConnectorsByCategory(category) {
|
|
2432
2552
|
return CONNECTORS.filter((c) => c.category === category);
|
|
2433
2553
|
}
|
|
2434
|
-
function searchConnectors(query) {
|
|
2435
|
-
const
|
|
2436
|
-
|
|
2554
|
+
function searchConnectors(query, context) {
|
|
2555
|
+
const tokens = query.toLowerCase().trim().split(/\s+/).filter(Boolean);
|
|
2556
|
+
if (tokens.length === 0)
|
|
2557
|
+
return [];
|
|
2558
|
+
const limit = context?.limit ?? 20;
|
|
2559
|
+
const installed = new Set(context?.installed ?? []);
|
|
2560
|
+
const promoted = new Set(context?.promoted ?? []);
|
|
2561
|
+
const usage = context?.usage ?? new Map;
|
|
2562
|
+
const results = [];
|
|
2563
|
+
for (const c of CONNECTORS) {
|
|
2564
|
+
const nameLow = c.name.toLowerCase();
|
|
2565
|
+
const displayLow = c.displayName.toLowerCase();
|
|
2566
|
+
const descLow = c.description.toLowerCase();
|
|
2567
|
+
const tagsLow = c.tags.map((t) => t.toLowerCase());
|
|
2568
|
+
let score = 0;
|
|
2569
|
+
const matchReasons = [];
|
|
2570
|
+
let allTokensMatch = true;
|
|
2571
|
+
for (const token of tokens) {
|
|
2572
|
+
let tokenMatched = false;
|
|
2573
|
+
if (nameLow === token) {
|
|
2574
|
+
score += 100;
|
|
2575
|
+
matchReasons.push(`name="${token}"`);
|
|
2576
|
+
tokenMatched = true;
|
|
2577
|
+
} else if (nameLow.includes(token)) {
|
|
2578
|
+
score += 10;
|
|
2579
|
+
matchReasons.push(`name~${token}`);
|
|
2580
|
+
tokenMatched = true;
|
|
2581
|
+
}
|
|
2582
|
+
if (tagsLow.includes(token)) {
|
|
2583
|
+
score += 8;
|
|
2584
|
+
if (!tokenMatched)
|
|
2585
|
+
matchReasons.push(`tag="${token}"`);
|
|
2586
|
+
tokenMatched = true;
|
|
2587
|
+
} else if (tagsLow.some((t) => t.includes(token))) {
|
|
2588
|
+
score += 5;
|
|
2589
|
+
if (!tokenMatched)
|
|
2590
|
+
matchReasons.push(`tag~${token}`);
|
|
2591
|
+
tokenMatched = true;
|
|
2592
|
+
}
|
|
2593
|
+
if (displayLow.includes(token)) {
|
|
2594
|
+
score += 3;
|
|
2595
|
+
if (!tokenMatched)
|
|
2596
|
+
matchReasons.push(`display~${token}`);
|
|
2597
|
+
tokenMatched = true;
|
|
2598
|
+
}
|
|
2599
|
+
if (descLow.includes(token)) {
|
|
2600
|
+
score += 1;
|
|
2601
|
+
if (!tokenMatched)
|
|
2602
|
+
matchReasons.push(`desc~${token}`);
|
|
2603
|
+
tokenMatched = true;
|
|
2604
|
+
}
|
|
2605
|
+
if (!tokenMatched && token.length >= 3) {
|
|
2606
|
+
const nameFuzzy = bestFuzzyScore(token, [nameLow], 1);
|
|
2607
|
+
if (nameFuzzy > 0) {
|
|
2608
|
+
score += nameFuzzy * 6;
|
|
2609
|
+
matchReasons.push(`fuzzy:name\u2248${token}`);
|
|
2610
|
+
tokenMatched = true;
|
|
2611
|
+
}
|
|
2612
|
+
if (!tokenMatched) {
|
|
2613
|
+
const tagFuzzy = bestFuzzyScore(token, tagsLow, 2);
|
|
2614
|
+
if (tagFuzzy > 0) {
|
|
2615
|
+
score += tagFuzzy * 3;
|
|
2616
|
+
matchReasons.push(`fuzzy:tag\u2248${token}`);
|
|
2617
|
+
tokenMatched = true;
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
if (!tokenMatched) {
|
|
2621
|
+
const displayFuzzy = bestFuzzyScore(token, [displayLow], 2);
|
|
2622
|
+
if (displayFuzzy > 0) {
|
|
2623
|
+
score += displayFuzzy * 2;
|
|
2624
|
+
matchReasons.push(`fuzzy:display\u2248${token}`);
|
|
2625
|
+
tokenMatched = true;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
if (!tokenMatched) {
|
|
2630
|
+
allTokensMatch = false;
|
|
2631
|
+
break;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
if (!allTokensMatch)
|
|
2635
|
+
continue;
|
|
2636
|
+
const badges = [];
|
|
2637
|
+
if (installed.has(c.name)) {
|
|
2638
|
+
score += 50;
|
|
2639
|
+
badges.push("installed");
|
|
2640
|
+
}
|
|
2641
|
+
if (promoted.has(c.name)) {
|
|
2642
|
+
score += 30;
|
|
2643
|
+
badges.push("promoted");
|
|
2644
|
+
}
|
|
2645
|
+
const usageCount = usage.get(c.name) ?? 0;
|
|
2646
|
+
if (usageCount > 0) {
|
|
2647
|
+
score += Math.min(usageCount * 2, 40);
|
|
2648
|
+
if (usageCount >= 5)
|
|
2649
|
+
badges.push("hot");
|
|
2650
|
+
}
|
|
2651
|
+
results.push({ ...c, score, matchReasons, badges });
|
|
2652
|
+
}
|
|
2653
|
+
const matchedNames = new Set(results.map((r) => r.name));
|
|
2654
|
+
if (results.length < limit) {
|
|
2655
|
+
const { expanded } = expandQuery(tokens);
|
|
2656
|
+
if (expanded.length > 0) {
|
|
2657
|
+
for (const c of CONNECTORS) {
|
|
2658
|
+
if (matchedNames.has(c.name))
|
|
2659
|
+
continue;
|
|
2660
|
+
const nameLow2 = c.name.toLowerCase();
|
|
2661
|
+
const tagsLow2 = c.tags.map((t) => t.toLowerCase());
|
|
2662
|
+
const descLow2 = c.description.toLowerCase();
|
|
2663
|
+
let synScore = 0;
|
|
2664
|
+
const synReasons = [];
|
|
2665
|
+
for (const syn of expanded) {
|
|
2666
|
+
if (nameLow2.includes(syn)) {
|
|
2667
|
+
synScore += 2;
|
|
2668
|
+
synReasons.push(`syn:name~${syn}`);
|
|
2669
|
+
} else if (tagsLow2.some((t) => t.includes(syn))) {
|
|
2670
|
+
synScore += 1;
|
|
2671
|
+
synReasons.push(`syn:tag~${syn}`);
|
|
2672
|
+
} else if (descLow2.includes(syn)) {
|
|
2673
|
+
synScore += 1;
|
|
2674
|
+
synReasons.push(`syn:desc~${syn}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
if (synScore > 0) {
|
|
2678
|
+
const badges = [];
|
|
2679
|
+
if (installed.has(c.name)) {
|
|
2680
|
+
synScore += 50;
|
|
2681
|
+
badges.push("installed");
|
|
2682
|
+
}
|
|
2683
|
+
if (promoted.has(c.name)) {
|
|
2684
|
+
synScore += 30;
|
|
2685
|
+
badges.push("promoted");
|
|
2686
|
+
}
|
|
2687
|
+
const usageCount = usage.get(c.name) ?? 0;
|
|
2688
|
+
if (usageCount > 0) {
|
|
2689
|
+
synScore += Math.min(usageCount * 2, 40);
|
|
2690
|
+
if (usageCount >= 5)
|
|
2691
|
+
badges.push("hot");
|
|
2692
|
+
}
|
|
2693
|
+
results.push({ ...c, score: synScore, matchReasons: synReasons, badges });
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
results.sort((a, b) => b.score - a.score);
|
|
2699
|
+
return results.slice(0, limit);
|
|
2437
2700
|
}
|
|
2438
2701
|
function getConnector(name) {
|
|
2439
2702
|
return CONNECTORS.find((c) => c.name === name);
|
|
@@ -2462,6 +2725,7 @@ function loadConnectorVersions() {
|
|
|
2462
2725
|
}
|
|
2463
2726
|
var CATEGORIES, CONNECTORS, versionsLoaded = false;
|
|
2464
2727
|
var init_registry = __esm(() => {
|
|
2728
|
+
init_synonyms();
|
|
2465
2729
|
CATEGORIES = [
|
|
2466
2730
|
"AI & ML",
|
|
2467
2731
|
"Developer Tools",
|
|
@@ -10318,6 +10582,16 @@ function loadTokens(name) {
|
|
|
10318
10582
|
return null;
|
|
10319
10583
|
}
|
|
10320
10584
|
}
|
|
10585
|
+
const profileConfig = loadProfileConfig(name);
|
|
10586
|
+
if (profileConfig.refreshToken || profileConfig.accessToken) {
|
|
10587
|
+
return {
|
|
10588
|
+
accessToken: profileConfig.accessToken,
|
|
10589
|
+
refreshToken: profileConfig.refreshToken,
|
|
10590
|
+
expiresAt: profileConfig.expiresAt,
|
|
10591
|
+
tokenType: profileConfig.tokenType,
|
|
10592
|
+
scope: profileConfig.scope
|
|
10593
|
+
};
|
|
10594
|
+
}
|
|
10321
10595
|
return null;
|
|
10322
10596
|
}
|
|
10323
10597
|
function getAuthStatus(name) {
|
|
@@ -10643,6 +10917,75 @@ var init_auth = __esm(() => {
|
|
|
10643
10917
|
};
|
|
10644
10918
|
});
|
|
10645
10919
|
|
|
10920
|
+
// src/db/promotions.ts
|
|
10921
|
+
var exports_promotions = {};
|
|
10922
|
+
__export(exports_promotions, {
|
|
10923
|
+
promoteConnector: () => promoteConnector,
|
|
10924
|
+
isPromoted: () => isPromoted,
|
|
10925
|
+
getPromotedConnectors: () => getPromotedConnectors,
|
|
10926
|
+
demoteConnector: () => demoteConnector
|
|
10927
|
+
});
|
|
10928
|
+
function promoteConnector(name, db) {
|
|
10929
|
+
const d = db ?? getDatabase();
|
|
10930
|
+
d.run("INSERT OR REPLACE INTO connector_promotions (connector, promoted_at) VALUES (?, ?)", [name, now()]);
|
|
10931
|
+
}
|
|
10932
|
+
function demoteConnector(name, db) {
|
|
10933
|
+
const d = db ?? getDatabase();
|
|
10934
|
+
return d.run("DELETE FROM connector_promotions WHERE connector = ?", [name]).changes > 0;
|
|
10935
|
+
}
|
|
10936
|
+
function getPromotedConnectors(db) {
|
|
10937
|
+
const d = db ?? getDatabase();
|
|
10938
|
+
return d.query("SELECT connector FROM connector_promotions ORDER BY promoted_at DESC").all().map((r) => r.connector);
|
|
10939
|
+
}
|
|
10940
|
+
function isPromoted(name, db) {
|
|
10941
|
+
const d = db ?? getDatabase();
|
|
10942
|
+
const row = d.query("SELECT 1 FROM connector_promotions WHERE connector = ?").get(name);
|
|
10943
|
+
return !!row;
|
|
10944
|
+
}
|
|
10945
|
+
var init_promotions = __esm(() => {
|
|
10946
|
+
init_database();
|
|
10947
|
+
});
|
|
10948
|
+
|
|
10949
|
+
// src/db/usage.ts
|
|
10950
|
+
var exports_usage = {};
|
|
10951
|
+
__export(exports_usage, {
|
|
10952
|
+
logUsage: () => logUsage,
|
|
10953
|
+
getUsageStats: () => getUsageStats,
|
|
10954
|
+
getUsageMap: () => getUsageMap,
|
|
10955
|
+
getTopConnectors: () => getTopConnectors,
|
|
10956
|
+
cleanOldUsage: () => cleanOldUsage
|
|
10957
|
+
});
|
|
10958
|
+
function logUsage(connector, action, agentId, db) {
|
|
10959
|
+
const d = db ?? getDatabase();
|
|
10960
|
+
d.run("INSERT INTO connector_usage (id, connector, action, agent_id, timestamp) VALUES (?, ?, ?, ?, ?)", [shortUuid(), connector, action, agentId ?? null, now()]);
|
|
10961
|
+
}
|
|
10962
|
+
function getUsageStats(connector, db) {
|
|
10963
|
+
const d = db ?? getDatabase();
|
|
10964
|
+
const total = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ?").get(connector).c;
|
|
10965
|
+
const d7 = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
10966
|
+
const last7d = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ? AND timestamp > ?").get(connector, d7).c;
|
|
10967
|
+
const d1 = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
10968
|
+
const last24h = d.query("SELECT COUNT(*) as c FROM connector_usage WHERE connector = ? AND timestamp > ?").get(connector, d1).c;
|
|
10969
|
+
return { connector, total, last7d, last24h };
|
|
10970
|
+
}
|
|
10971
|
+
function getTopConnectors(limit = 10, days = 7, db) {
|
|
10972
|
+
const d = db ?? getDatabase();
|
|
10973
|
+
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
10974
|
+
return d.query("SELECT connector, COUNT(*) as count FROM connector_usage WHERE timestamp > ? GROUP BY connector ORDER BY count DESC LIMIT ?").all(since, limit);
|
|
10975
|
+
}
|
|
10976
|
+
function getUsageMap(days = 7, db) {
|
|
10977
|
+
const top = getTopConnectors(100, days, db);
|
|
10978
|
+
return new Map(top.map((t) => [t.connector, t.count]));
|
|
10979
|
+
}
|
|
10980
|
+
function cleanOldUsage(days = 30, db) {
|
|
10981
|
+
const d = db ?? getDatabase();
|
|
10982
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
10983
|
+
return d.run("DELETE FROM connector_usage WHERE timestamp < ?", [cutoff]).changes;
|
|
10984
|
+
}
|
|
10985
|
+
var init_usage = __esm(() => {
|
|
10986
|
+
init_database();
|
|
10987
|
+
});
|
|
10988
|
+
|
|
10646
10989
|
// src/db/agents.ts
|
|
10647
10990
|
function shortUuid2() {
|
|
10648
10991
|
return crypto.randomUUID().slice(0, 8);
|
|
@@ -11071,6 +11414,30 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
11071
11414
|
if (path === "/api/activity" && method === "GET") {
|
|
11072
11415
|
return json(activityLog, 200, port);
|
|
11073
11416
|
}
|
|
11417
|
+
if (path === "/api/hot" && method === "GET") {
|
|
11418
|
+
const { getTopConnectors: getTopConnectors2 } = await Promise.resolve().then(() => (init_usage(), exports_usage));
|
|
11419
|
+
const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
11420
|
+
const limit = parseInt(url2.searchParams.get("limit") || "10", 10);
|
|
11421
|
+
const days = parseInt(url2.searchParams.get("days") || "7", 10);
|
|
11422
|
+
const db = getDatabase2();
|
|
11423
|
+
const top = getTopConnectors2(limit, days, db);
|
|
11424
|
+
const promoted = new Set(getPromotedConnectors2(db));
|
|
11425
|
+
return json(top.map((t) => ({ ...t, promoted: promoted.has(t.connector) })), 200, port);
|
|
11426
|
+
}
|
|
11427
|
+
const promoteMatch = path.match(/^\/api\/connectors\/([^/]+)\/promote$/);
|
|
11428
|
+
if (promoteMatch && method === "POST") {
|
|
11429
|
+
const name = promoteMatch[1];
|
|
11430
|
+
if (!getConnector(name))
|
|
11431
|
+
return json({ error: "Connector not found" }, 404, port);
|
|
11432
|
+
const { promoteConnector: promoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
11433
|
+
promoteConnector2(name, getDatabase2());
|
|
11434
|
+
return json({ success: true, connector: name }, 200, port);
|
|
11435
|
+
}
|
|
11436
|
+
if (promoteMatch && method === "DELETE") {
|
|
11437
|
+
const { demoteConnector: demoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
11438
|
+
const removed = demoteConnector2(promoteMatch[1], getDatabase2());
|
|
11439
|
+
return json({ success: removed, connector: promoteMatch[1] }, 200, port);
|
|
11440
|
+
}
|
|
11074
11441
|
if (path === "/api/llm" && method === "GET") {
|
|
11075
11442
|
const config = getLlmConfig();
|
|
11076
11443
|
if (!config)
|
|
@@ -11484,7 +11851,7 @@ import chalk2 from "chalk";
|
|
|
11484
11851
|
// package.json
|
|
11485
11852
|
var package_default = {
|
|
11486
11853
|
name: "@hasna/connectors",
|
|
11487
|
-
version: "1.1
|
|
11854
|
+
version: "1.2.1",
|
|
11488
11855
|
description: "Open source connector library - Install API connectors with a single command",
|
|
11489
11856
|
type: "module",
|
|
11490
11857
|
bin: {
|
|
@@ -13738,10 +14105,18 @@ Available connectors (${CONNECTORS.length}):
|
|
|
13738
14105
|
console.log();
|
|
13739
14106
|
}
|
|
13740
14107
|
});
|
|
13741
|
-
program2.command("search").argument("<query>", "Search term").option("--json", "Output as JSON", false).description("Search for connectors").action((query, options) => {
|
|
13742
|
-
const
|
|
14108
|
+
program2.command("search").argument("<query>", "Search term").option("--json", "Output as JSON", false).option("--limit <n>", "Max results", "20").description("Search for connectors (ranked with fuzzy matching)").action((query, options) => {
|
|
14109
|
+
const installed = getInstalledConnectors();
|
|
14110
|
+
const { getPromotedConnectors: getPromotedConnectors2 } = (init_promotions(), __toCommonJS(exports_promotions));
|
|
14111
|
+
const { getUsageMap: getUsageMap2 } = (init_usage(), __toCommonJS(exports_usage));
|
|
14112
|
+
const results = searchConnectors(query, {
|
|
14113
|
+
installed,
|
|
14114
|
+
promoted: getPromotedConnectors2(),
|
|
14115
|
+
usage: getUsageMap2(),
|
|
14116
|
+
limit: parseInt(options.limit)
|
|
14117
|
+
});
|
|
13743
14118
|
if (options.json) {
|
|
13744
|
-
console.log(JSON.stringify(results));
|
|
14119
|
+
console.log(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, matchReasons: c.matchReasons }))));
|
|
13745
14120
|
return;
|
|
13746
14121
|
}
|
|
13747
14122
|
if (results.length === 0) {
|
|
@@ -13751,10 +14126,12 @@ program2.command("search").argument("<query>", "Search term").option("--json", "
|
|
|
13751
14126
|
console.log(chalk2.bold(`
|
|
13752
14127
|
Found ${results.length} connector(s):
|
|
13753
14128
|
`));
|
|
13754
|
-
console.log(` ${chalk2.dim("Name".padEnd(
|
|
13755
|
-
console.log(chalk2.dim(` ${"\u2500".repeat(
|
|
14129
|
+
console.log(` ${chalk2.dim("Name".padEnd(22))}${chalk2.dim("Score".padEnd(7))}${chalk2.dim("Category".padEnd(20))}${chalk2.dim("Description")}`);
|
|
14130
|
+
console.log(chalk2.dim(` ${"\u2500".repeat(75)}`));
|
|
13756
14131
|
for (const c of results) {
|
|
13757
|
-
|
|
14132
|
+
const badges = c.badges.map((b) => b === "installed" ? chalk2.green("[INS]") : b === "hot" ? chalk2.red("[HOT]") : b === "promoted" ? chalk2.yellow("[PRO]") : "").join(" ");
|
|
14133
|
+
const badgeStr = badges ? " " + badges : "";
|
|
14134
|
+
console.log(` ${chalk2.cyan(c.name.padEnd(22))}${String(c.score).padEnd(7)}${chalk2.dim(c.category.padEnd(20))}${c.description}${badgeStr}`);
|
|
13758
14135
|
}
|
|
13759
14136
|
});
|
|
13760
14137
|
program2.command("info").argument("<connector>", "Connector name").option("--json", "Output as JSON", false).description("Show detailed info about a connector").action((connector, options) => {
|
|
@@ -15464,6 +15841,51 @@ Setting up ${meta.displayName}...
|
|
|
15464
15841
|
}
|
|
15465
15842
|
process.exit(0);
|
|
15466
15843
|
});
|
|
15844
|
+
program2.command("hot").description("Show top connectors by usage").option("--limit <n>", "Max results", "10").option("--days <n>", "Time window in days", "7").option("--json", "Output as JSON").action((options) => {
|
|
15845
|
+
const { getTopConnectors: getTopConnectors2 } = (init_usage(), __toCommonJS(exports_usage));
|
|
15846
|
+
const { getPromotedConnectors: getPromotedConnectors2 } = (init_promotions(), __toCommonJS(exports_promotions));
|
|
15847
|
+
const top = getTopConnectors2(parseInt(options.limit), parseInt(options.days), getDatabase());
|
|
15848
|
+
const promoted = new Set(getPromotedConnectors2(getDatabase()));
|
|
15849
|
+
if (options.json) {
|
|
15850
|
+
console.log(JSON.stringify(top.map((t) => ({ ...t, promoted: promoted.has(t.connector) }))));
|
|
15851
|
+
return;
|
|
15852
|
+
}
|
|
15853
|
+
if (top.length === 0) {
|
|
15854
|
+
console.log(chalk2.dim("No usage data yet. Use connectors to build up stats."));
|
|
15855
|
+
return;
|
|
15856
|
+
}
|
|
15857
|
+
console.log(chalk2.bold(`
|
|
15858
|
+
Top connectors (last ${options.days} days):
|
|
15859
|
+
`));
|
|
15860
|
+
console.log(` ${chalk2.dim("#".padEnd(4))}${chalk2.dim("Connector".padEnd(22))}${chalk2.dim("Usage".padEnd(8))}${chalk2.dim("Badges")}`);
|
|
15861
|
+
console.log(chalk2.dim(` ${"\u2500".repeat(45)}`));
|
|
15862
|
+
for (let i = 0;i < top.length; i++) {
|
|
15863
|
+
const t = top[i];
|
|
15864
|
+
const badges = [
|
|
15865
|
+
t.count >= 5 ? chalk2.red("[HOT]") : "",
|
|
15866
|
+
promoted.has(t.connector) ? chalk2.yellow("[PRO]") : ""
|
|
15867
|
+
].filter(Boolean).join(" ");
|
|
15868
|
+
console.log(` ${String(i + 1).padEnd(4)}${chalk2.cyan(t.connector.padEnd(22))}${String(t.count).padEnd(8)}${badges}`);
|
|
15869
|
+
}
|
|
15870
|
+
});
|
|
15871
|
+
program2.command("promote").argument("<connector>", "Connector to promote").description("Mark a connector as promoted (boosted in search)").action((connector) => {
|
|
15872
|
+
const meta = getConnector(connector);
|
|
15873
|
+
if (!meta) {
|
|
15874
|
+
console.error(chalk2.red(`Connector '${connector}' not found`));
|
|
15875
|
+
process.exit(1);
|
|
15876
|
+
}
|
|
15877
|
+
const { promoteConnector: promoteConnector2 } = (init_promotions(), __toCommonJS(exports_promotions));
|
|
15878
|
+
promoteConnector2(connector, getDatabase());
|
|
15879
|
+
console.log(chalk2.green("\u2713") + ` ${meta.displayName} promoted \u2014 will rank higher in search`);
|
|
15880
|
+
});
|
|
15881
|
+
program2.command("demote").argument("<connector>", "Connector to demote").description("Remove promotion from a connector").action((connector) => {
|
|
15882
|
+
const { demoteConnector: demoteConnector2 } = (init_promotions(), __toCommonJS(exports_promotions));
|
|
15883
|
+
const removed = demoteConnector2(connector, getDatabase());
|
|
15884
|
+
if (removed)
|
|
15885
|
+
console.log(chalk2.green("\u2713") + ` ${connector} demoted`);
|
|
15886
|
+
else
|
|
15887
|
+
console.log(chalk2.dim(`${connector} was not promoted`));
|
|
15888
|
+
});
|
|
15467
15889
|
var jobsCmd = program2.command("jobs").description("Manage scheduled connector jobs");
|
|
15468
15890
|
jobsCmd.command("add").description("Add a scheduled job").requiredOption("--name <name>", "Job name").requiredOption("--connector <connector>", "Connector name").requiredOption("--command <command>", "Command to run").requiredOption("--cron <cron>", "Cron expression (5-field)").option("--args <args>", "Command args (space-separated)").option("--strip", "Apply LLM stripping to output").option("--json", "Output as JSON").action((options) => {
|
|
15469
15891
|
const db = getDatabase();
|