@hasna/connectors 0.0.3 → 0.0.5

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 (3) hide show
  1. package/bin/index.js +225 -11
  2. package/dist/index.js +44 -1
  3. package/package.json +2 -1
package/bin/index.js CHANGED
@@ -4418,6 +4418,9 @@ function searchConnectors(query) {
4418
4418
  const q = query.toLowerCase();
4419
4419
  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)));
4420
4420
  }
4421
+ function getConnector(name) {
4422
+ return CONNECTORS.find((c) => c.name === name);
4423
+ }
4421
4424
  var versionsLoaded = false;
4422
4425
  function loadConnectorVersions() {
4423
4426
  if (versionsLoaded)
@@ -5078,7 +5081,7 @@ function Spinner({ type = "dots" }) {
5078
5081
  var build_default2 = Spinner;
5079
5082
 
5080
5083
  // src/lib/installer.ts
5081
- import { existsSync as existsSync2, cpSync, mkdirSync, writeFileSync } from "fs";
5084
+ import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
5082
5085
  import { join as join2, dirname as dirname2 } from "path";
5083
5086
  import { fileURLToPath as fileURLToPath2 } from "url";
5084
5087
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
@@ -5165,6 +5168,48 @@ function getInstalledConnectors(targetDir = process.cwd()) {
5165
5168
  return f.startsWith("connect-") && statSync(fullPath).isDirectory();
5166
5169
  }).map((f) => f.replace("connect-", ""));
5167
5170
  }
5171
+ function getConnectorDocs(name) {
5172
+ const connectorPath = getConnectorPath(name);
5173
+ const claudeMdPath = join2(connectorPath, "CLAUDE.md");
5174
+ if (!existsSync2(claudeMdPath))
5175
+ return null;
5176
+ const raw = readFileSync2(claudeMdPath, "utf-8");
5177
+ return {
5178
+ overview: extractSection(raw, "Project Overview"),
5179
+ auth: extractSection(raw, "Authentication"),
5180
+ envVars: parseEnvVarsTable(extractSection(raw, "Environment Variables")),
5181
+ cliCommands: extractSection(raw, "CLI Commands"),
5182
+ dataStorage: extractSection(raw, "Data Storage"),
5183
+ raw
5184
+ };
5185
+ }
5186
+ function extractSection(markdown, heading) {
5187
+ const regex = new RegExp(`^##\\s+${escapeRegex(heading)}\\s*$`, "m");
5188
+ const match = regex.exec(markdown);
5189
+ if (!match)
5190
+ return "";
5191
+ const start = match.index + match[0].length;
5192
+ const nextHeading = markdown.slice(start).search(/^##\s/m);
5193
+ const content = nextHeading === -1 ? markdown.slice(start) : markdown.slice(start, start + nextHeading);
5194
+ return content.trim();
5195
+ }
5196
+ function escapeRegex(str) {
5197
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5198
+ }
5199
+ function parseEnvVarsTable(section) {
5200
+ if (!section)
5201
+ return [];
5202
+ const vars = [];
5203
+ const lines = section.split(`
5204
+ `);
5205
+ for (const line of lines) {
5206
+ const match = line.match(/\|\s*`([^`]+)`\s*\|\s*(.+?)\s*\|/);
5207
+ if (match && match[1] !== "Variable") {
5208
+ vars.push({ variable: match[1], description: match[2].trim() });
5209
+ }
5210
+ }
5211
+ return vars;
5212
+ }
5168
5213
  function removeConnector(name, targetDir = process.cwd()) {
5169
5214
  const { rmSync } = __require("fs");
5170
5215
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
@@ -5482,33 +5527,62 @@ function App({ initialConnectors, overwrite = false }) {
5482
5527
  // src/cli/index.tsx
5483
5528
  import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
5484
5529
  loadConnectorVersions();
5530
+ var isTTY = process.stdout.isTTY ?? false;
5485
5531
  var program2 = new Command;
5486
- program2.name("connectors").description("Install API connectors for your project").version("0.0.3");
5532
+ program2.name("connectors").description("Install API connectors for your project").version("0.0.5");
5487
5533
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive connector browser").action(() => {
5534
+ if (!isTTY) {
5535
+ console.log(`Non-interactive environment detected. Use a subcommand:
5536
+ `);
5537
+ console.log(" connectors list List all available connectors");
5538
+ console.log(" connectors list --json List as JSON (for AI agents)");
5539
+ console.log(" connectors search <query> Search connectors");
5540
+ console.log(" connectors install <names...> Install connectors");
5541
+ console.log(" connectors remove <name> Remove a connector");
5542
+ console.log(" connectors info <name> Show connector details");
5543
+ console.log(" connectors categories List categories");
5544
+ console.log(`
5545
+ Run 'connectors --help' for full usage.`);
5546
+ process.exit(0);
5547
+ }
5488
5548
  render(/* @__PURE__ */ jsxDEV7(App, {}, undefined, false, undefined, this));
5489
5549
  });
5490
- program2.command("install").alias("add").argument("[connectors...]", "Connectors to install").option("-o, --overwrite", "Overwrite existing connectors", false).description("Install one or more connectors").action((connectors, options) => {
5550
+ program2.command("install").alias("add").argument("[connectors...]", "Connectors to install").option("-o, --overwrite", "Overwrite existing connectors", false).option("--json", "Output results as JSON", false).description("Install one or more connectors").action((connectors, options) => {
5491
5551
  if (connectors.length === 0) {
5552
+ if (!isTTY) {
5553
+ console.error("Error: specify connectors to install. Example: connectors install figma stripe");
5554
+ process.exit(1);
5555
+ }
5492
5556
  render(/* @__PURE__ */ jsxDEV7(App, {}, undefined, false, undefined, this));
5493
5557
  return;
5494
5558
  }
5559
+ const results = connectors.map((name) => installConnector(name, { overwrite: options.overwrite }));
5560
+ if (options.json) {
5561
+ console.log(JSON.stringify(results, null, 2));
5562
+ process.exit(results.every((r) => r.success) ? 0 : 1);
5563
+ return;
5564
+ }
5495
5565
  console.log(chalk2.bold(`
5496
5566
  Installing connectors...
5497
5567
  `));
5498
- for (const name of connectors) {
5499
- const result = installConnector(name, { overwrite: options.overwrite });
5568
+ for (const result of results) {
5500
5569
  if (result.success) {
5501
- console.log(chalk2.green(`\u2713 ${name}`));
5570
+ console.log(chalk2.green(`\u2713 ${result.connector}`));
5502
5571
  } else {
5503
- console.log(chalk2.red(`\u2717 ${name}: ${result.error}`));
5572
+ console.log(chalk2.red(`\u2717 ${result.connector}: ${result.error}`));
5504
5573
  }
5505
5574
  }
5506
5575
  console.log(chalk2.dim(`
5507
5576
  Connectors installed to .connectors/`));
5577
+ process.exit(results.every((r) => r.success) ? 0 : 1);
5508
5578
  });
5509
- 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).description("List available or installed connectors").action((options) => {
5579
+ 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) => {
5510
5580
  if (options.installed) {
5511
5581
  const installed = getInstalledConnectors();
5582
+ if (options.json) {
5583
+ console.log(JSON.stringify(installed));
5584
+ return;
5585
+ }
5512
5586
  if (installed.length === 0) {
5513
5587
  console.log(chalk2.dim("No connectors installed"));
5514
5588
  return;
@@ -5524,11 +5598,19 @@ Installed connectors (${installed.length}):
5524
5598
  if (options.category) {
5525
5599
  const category = CATEGORIES.find((c) => c.toLowerCase() === options.category.toLowerCase());
5526
5600
  if (!category) {
5601
+ if (options.json) {
5602
+ console.log(JSON.stringify({ error: `Unknown category: ${options.category}` }));
5603
+ process.exit(1);
5604
+ }
5527
5605
  console.log(chalk2.red(`Unknown category: ${options.category}`));
5528
5606
  console.log(chalk2.dim(`Available: ${CATEGORIES.join(", ")}`));
5529
5607
  return;
5530
5608
  }
5531
5609
  const connectors = getConnectorsByCategory(category);
5610
+ if (options.json) {
5611
+ console.log(JSON.stringify(connectors));
5612
+ return;
5613
+ }
5532
5614
  console.log(chalk2.bold(`
5533
5615
  ${category} (${connectors.length}):
5534
5616
  `));
@@ -5539,6 +5621,10 @@ ${category} (${connectors.length}):
5539
5621
  }
5540
5622
  return;
5541
5623
  }
5624
+ if (options.json) {
5625
+ console.log(JSON.stringify(CONNECTORS));
5626
+ return;
5627
+ }
5542
5628
  console.log(chalk2.bold(`
5543
5629
  Available connectors (${CONNECTORS.length}):
5544
5630
  `));
@@ -5553,8 +5639,12 @@ Available connectors (${CONNECTORS.length}):
5553
5639
  console.log();
5554
5640
  }
5555
5641
  });
5556
- program2.command("search").argument("<query>", "Search term").description("Search for connectors").action((query) => {
5642
+ program2.command("search").argument("<query>", "Search term").option("--json", "Output as JSON", false).description("Search for connectors").action((query, options) => {
5557
5643
  const results = searchConnectors(query);
5644
+ if (options.json) {
5645
+ console.log(JSON.stringify(results));
5646
+ return;
5647
+ }
5558
5648
  if (results.length === 0) {
5559
5649
  console.log(chalk2.dim(`No connectors found for "${query}"`));
5560
5650
  return;
@@ -5568,15 +5658,139 @@ Found ${results.length} connector(s):
5568
5658
  console.log(` ${chalk2.cyan(c.name.padEnd(20))}${chalk2.dim((c.version || "-").padEnd(10))}${chalk2.dim(c.category.padEnd(20))}${c.description}`);
5569
5659
  }
5570
5660
  });
5571
- program2.command("remove").alias("rm").argument("<connector>", "Connector to remove").description("Remove an installed connector").action((connector) => {
5661
+ program2.command("info").argument("<connector>", "Connector name").option("--json", "Output as JSON", false).description("Show detailed info about a connector").action((connector, options) => {
5662
+ const meta = getConnector(connector);
5663
+ if (!meta) {
5664
+ if (options.json) {
5665
+ console.log(JSON.stringify({ error: `Connector '${connector}' not found` }));
5666
+ process.exit(1);
5667
+ }
5668
+ console.log(chalk2.red(`Connector '${connector}' not found`));
5669
+ process.exit(1);
5670
+ return;
5671
+ }
5672
+ const installed = getInstalledConnectors();
5673
+ const isInstalled = installed.includes(meta.name);
5674
+ if (options.json) {
5675
+ console.log(JSON.stringify({ ...meta, installed: isInstalled }));
5676
+ return;
5677
+ }
5678
+ console.log(chalk2.bold(`
5679
+ ${meta.displayName}`));
5680
+ console.log(chalk2.dim(`${"\u2500".repeat(40)}`));
5681
+ console.log(` Name: ${chalk2.cyan(meta.name)}`);
5682
+ console.log(` Version: ${meta.version || "-"}`);
5683
+ console.log(` Category: ${meta.category}`);
5684
+ console.log(` Description: ${meta.description}`);
5685
+ console.log(` Tags: ${meta.tags.join(", ")}`);
5686
+ console.log(` Installed: ${isInstalled ? chalk2.green("yes") : "no"}`);
5687
+ console.log(` Package: @hasna/connect-${meta.name}`);
5688
+ });
5689
+ program2.command("docs").argument("<connector>", "Connector name").option("--json", "Output as structured JSON", false).option("--raw", "Output raw markdown", false).description("Show connector documentation (auth, env vars, API, CLI commands)").action((connector, options) => {
5690
+ const meta = getConnector(connector);
5691
+ if (!meta) {
5692
+ if (options.json) {
5693
+ console.log(JSON.stringify({ error: `Connector '${connector}' not found` }));
5694
+ } else {
5695
+ console.log(chalk2.red(`Connector '${connector}' not found`));
5696
+ }
5697
+ process.exit(1);
5698
+ return;
5699
+ }
5700
+ const docs = getConnectorDocs(connector);
5701
+ if (!docs) {
5702
+ if (options.json) {
5703
+ console.log(JSON.stringify({ error: `No documentation found for '${connector}'` }));
5704
+ } else {
5705
+ console.log(chalk2.red(`No documentation found for '${connector}'`));
5706
+ }
5707
+ process.exit(1);
5708
+ return;
5709
+ }
5710
+ if (options.raw) {
5711
+ console.log(docs.raw);
5712
+ return;
5713
+ }
5714
+ if (options.json) {
5715
+ console.log(JSON.stringify({
5716
+ name: meta.name,
5717
+ displayName: meta.displayName,
5718
+ version: meta.version,
5719
+ category: meta.category,
5720
+ description: meta.description,
5721
+ overview: docs.overview,
5722
+ auth: docs.auth,
5723
+ envVars: docs.envVars,
5724
+ cliCommands: docs.cliCommands,
5725
+ dataStorage: docs.dataStorage
5726
+ }, null, 2));
5727
+ return;
5728
+ }
5729
+ console.log(chalk2.bold(`
5730
+ ${meta.displayName} \u2014 Documentation`));
5731
+ console.log(chalk2.dim("\u2500".repeat(50)));
5732
+ if (docs.overview) {
5733
+ console.log(chalk2.bold(`
5734
+ Overview`));
5735
+ console.log(` ${docs.overview.split(`
5736
+ `)[0]}`);
5737
+ }
5738
+ if (docs.auth) {
5739
+ console.log(chalk2.bold(`
5740
+ Authentication`));
5741
+ for (const line of docs.auth.split(`
5742
+ `).filter(Boolean)) {
5743
+ console.log(` ${line}`);
5744
+ }
5745
+ }
5746
+ if (docs.envVars.length > 0) {
5747
+ console.log(chalk2.bold(`
5748
+ Environment Variables`));
5749
+ for (const v of docs.envVars) {
5750
+ console.log(` ${chalk2.cyan(v.variable.padEnd(30))}${v.description}`);
5751
+ }
5752
+ }
5753
+ if (docs.cliCommands) {
5754
+ console.log(chalk2.bold(`
5755
+ CLI Commands`));
5756
+ for (const line of docs.cliCommands.split(`
5757
+ `)) {
5758
+ console.log(` ${line}`);
5759
+ }
5760
+ }
5761
+ if (docs.dataStorage) {
5762
+ console.log(chalk2.bold(`
5763
+ Data Storage`));
5764
+ for (const line of docs.dataStorage.split(`
5765
+ `).filter(Boolean)) {
5766
+ console.log(` ${line}`);
5767
+ }
5768
+ }
5769
+ console.log();
5770
+ });
5771
+ program2.command("remove").alias("rm").argument("<connector>", "Connector to remove").option("--json", "Output as JSON", false).description("Remove an installed connector").action((connector, options) => {
5572
5772
  const removed = removeConnector(connector);
5773
+ if (options.json) {
5774
+ console.log(JSON.stringify({ connector, removed }));
5775
+ process.exit(removed ? 0 : 1);
5776
+ return;
5777
+ }
5573
5778
  if (removed) {
5574
5779
  console.log(chalk2.green(`\u2713 Removed ${connector}`));
5575
5780
  } else {
5576
5781
  console.log(chalk2.red(`\u2717 ${connector} is not installed`));
5782
+ process.exit(1);
5577
5783
  }
5578
5784
  });
5579
- program2.command("categories").description("List all categories").action(() => {
5785
+ program2.command("categories").option("--json", "Output as JSON", false).description("List all categories").action((options) => {
5786
+ if (options.json) {
5787
+ const data = CATEGORIES.map((category) => ({
5788
+ name: category,
5789
+ count: getConnectorsByCategory(category).length
5790
+ }));
5791
+ console.log(JSON.stringify(data));
5792
+ return;
5793
+ }
5580
5794
  console.log(chalk2.bold(`
5581
5795
  Categories:
5582
5796
  `));
package/dist/index.js CHANGED
@@ -495,7 +495,7 @@ function loadConnectorVersions() {
495
495
  }
496
496
  }
497
497
  // src/lib/installer.ts
498
- import { existsSync as existsSync2, cpSync, mkdirSync, writeFileSync } from "fs";
498
+ import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
499
499
  import { join as join2, dirname as dirname2 } from "path";
500
500
  import { fileURLToPath as fileURLToPath2 } from "url";
501
501
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
@@ -588,6 +588,48 @@ function getInstalledConnectors(targetDir = process.cwd()) {
588
588
  return f.startsWith("connect-") && statSync(fullPath).isDirectory();
589
589
  }).map((f) => f.replace("connect-", ""));
590
590
  }
591
+ function getConnectorDocs(name) {
592
+ const connectorPath = getConnectorPath(name);
593
+ const claudeMdPath = join2(connectorPath, "CLAUDE.md");
594
+ if (!existsSync2(claudeMdPath))
595
+ return null;
596
+ const raw = readFileSync2(claudeMdPath, "utf-8");
597
+ return {
598
+ overview: extractSection(raw, "Project Overview"),
599
+ auth: extractSection(raw, "Authentication"),
600
+ envVars: parseEnvVarsTable(extractSection(raw, "Environment Variables")),
601
+ cliCommands: extractSection(raw, "CLI Commands"),
602
+ dataStorage: extractSection(raw, "Data Storage"),
603
+ raw
604
+ };
605
+ }
606
+ function extractSection(markdown, heading) {
607
+ const regex = new RegExp(`^##\\s+${escapeRegex(heading)}\\s*$`, "m");
608
+ const match = regex.exec(markdown);
609
+ if (!match)
610
+ return "";
611
+ const start = match.index + match[0].length;
612
+ const nextHeading = markdown.slice(start).search(/^##\s/m);
613
+ const content = nextHeading === -1 ? markdown.slice(start) : markdown.slice(start, start + nextHeading);
614
+ return content.trim();
615
+ }
616
+ function escapeRegex(str) {
617
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
618
+ }
619
+ function parseEnvVarsTable(section) {
620
+ if (!section)
621
+ return [];
622
+ const vars = [];
623
+ const lines = section.split(`
624
+ `);
625
+ for (const line of lines) {
626
+ const match = line.match(/\|\s*`([^`]+)`\s*\|\s*(.+?)\s*\|/);
627
+ if (match && match[1] !== "Variable") {
628
+ vars.push({ variable: match[1], description: match[2].trim() });
629
+ }
630
+ }
631
+ return vars;
632
+ }
591
633
  function removeConnector(name, targetDir = process.cwd()) {
592
634
  const { rmSync } = __require("fs");
593
635
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
@@ -609,6 +651,7 @@ export {
609
651
  getInstalledConnectors,
610
652
  getConnectorsByCategory,
611
653
  getConnectorPath,
654
+ getConnectorDocs,
612
655
  getConnector,
613
656
  connectorExists,
614
657
  CONNECTORS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/connectors",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Open source connector library - Install API connectors with a single command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,6 +24,7 @@
24
24
  "build": "bun build ./src/cli/index.tsx --outdir ./bin --target bun --external ink --external react --external chalk --external conf && bun build ./src/index.ts --outdir ./dist --target bun",
25
25
  "dev": "bun run ./src/cli/index.tsx",
26
26
  "typecheck": "tsc --noEmit",
27
+ "test": "bun test",
27
28
  "prepublishOnly": "bun run build"
28
29
  },
29
30
  "keywords": [