@hasna/connectors 0.3.0 → 0.3.2

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 CHANGED
@@ -4519,9 +4519,9 @@ var exports_serve = {};
4519
4519
  __export(exports_serve, {
4520
4520
  startServer: () => startServer
4521
4521
  });
4522
- import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
4523
- import { join as join4, dirname as dirname3, extname, basename } from "path";
4524
- import { fileURLToPath as fileURLToPath3 } from "url";
4522
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
4523
+ import { join as join5, dirname as dirname4, extname, basename } from "path";
4524
+ import { fileURLToPath as fileURLToPath4 } from "url";
4525
4525
  import { homedir as homedir2 } from "os";
4526
4526
  function logActivity(action, connector, detail) {
4527
4527
  activityLog.unshift({ action, connector, timestamp: Date.now(), detail });
@@ -4532,21 +4532,21 @@ function logActivity(action, connector, detail) {
4532
4532
  function resolveDashboardDir() {
4533
4533
  const candidates = [];
4534
4534
  try {
4535
- const scriptDir = dirname3(fileURLToPath3(import.meta.url));
4536
- candidates.push(join4(scriptDir, "..", "dashboard", "dist"));
4537
- candidates.push(join4(scriptDir, "..", "..", "dashboard", "dist"));
4535
+ const scriptDir = dirname4(fileURLToPath4(import.meta.url));
4536
+ candidates.push(join5(scriptDir, "..", "dashboard", "dist"));
4537
+ candidates.push(join5(scriptDir, "..", "..", "dashboard", "dist"));
4538
4538
  } catch {}
4539
4539
  if (process.argv[1]) {
4540
- const mainDir = dirname3(process.argv[1]);
4541
- candidates.push(join4(mainDir, "..", "dashboard", "dist"));
4542
- candidates.push(join4(mainDir, "..", "..", "dashboard", "dist"));
4540
+ const mainDir = dirname4(process.argv[1]);
4541
+ candidates.push(join5(mainDir, "..", "dashboard", "dist"));
4542
+ candidates.push(join5(mainDir, "..", "..", "dashboard", "dist"));
4543
4543
  }
4544
- candidates.push(join4(process.cwd(), "dashboard", "dist"));
4544
+ candidates.push(join5(process.cwd(), "dashboard", "dist"));
4545
4545
  for (const candidate of candidates) {
4546
- if (existsSync4(candidate))
4546
+ if (existsSync5(candidate))
4547
4547
  return candidate;
4548
4548
  }
4549
- return join4(process.cwd(), "dashboard", "dist");
4549
+ return join5(process.cwd(), "dashboard", "dist");
4550
4550
  }
4551
4551
  function json(data, status = 200, port) {
4552
4552
  return new Response(JSON.stringify(data), {
@@ -4594,7 +4594,7 @@ function errorPage(title, message, hint) {
4594
4594
  </body></html>`;
4595
4595
  }
4596
4596
  function serveStaticFile(filePath) {
4597
- if (!existsSync4(filePath))
4597
+ if (!existsSync5(filePath))
4598
4598
  return null;
4599
4599
  const ext = extname(filePath);
4600
4600
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -4618,7 +4618,7 @@ async function startServer(requestedPort, options) {
4618
4618
  const shouldOpen = options?.open ?? true;
4619
4619
  loadConnectorVersions();
4620
4620
  const dashboardDir = resolveDashboardDir();
4621
- const dashboardExists = existsSync4(dashboardDir);
4621
+ const dashboardExists = existsSync5(dashboardDir);
4622
4622
  if (!dashboardExists) {
4623
4623
  console.error(`
4624
4624
  Dashboard not found at: ${dashboardDir}`);
@@ -4642,7 +4642,24 @@ Dashboard not found at: ${dashboardDir}`);
4642
4642
  const path = url2.pathname;
4643
4643
  const method = req.method;
4644
4644
  if (path === "/api/connectors" && method === "GET") {
4645
- return json(getAllConnectorsWithAuth(), 200, port);
4645
+ const compact = url2.searchParams.get("compact") === "true";
4646
+ const fieldsParam = url2.searchParams.get("fields");
4647
+ const fields = fieldsParam ? new Set(fieldsParam.split(",").map((f) => f.trim())) : null;
4648
+ const data = getAllConnectorsWithAuth();
4649
+ if (compact) {
4650
+ return json(data.map((c) => ({ name: c.name, category: c.category, installed: c.installed })), 200, port);
4651
+ }
4652
+ if (fields) {
4653
+ return json(data.map((c) => {
4654
+ const out = {};
4655
+ for (const f of fields) {
4656
+ if (f in c)
4657
+ out[f] = c[f];
4658
+ }
4659
+ return out;
4660
+ }), 200, port);
4661
+ }
4662
+ return json(data, 200, port);
4646
4663
  }
4647
4664
  const singleMatch = path.match(/^\/api\/connectors\/([^/]+)$/);
4648
4665
  if (singleMatch && method === "GET") {
@@ -4757,10 +4774,10 @@ Dashboard not found at: ${dashboardDir}`);
4757
4774
  return json({ error: "Invalid connector name" }, 400, port);
4758
4775
  try {
4759
4776
  const profiles = listProfiles(name);
4760
- const configDir = join4(homedir2(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
4761
- const currentProfileFile = join4(configDir, "current_profile");
4777
+ const configDir = join5(homedir2(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
4778
+ const currentProfileFile = join5(configDir, "current_profile");
4762
4779
  let current = "default";
4763
- if (existsSync4(currentProfileFile)) {
4780
+ if (existsSync5(currentProfileFile)) {
4764
4781
  try {
4765
4782
  current = readFileSync4(currentProfileFile, "utf-8").trim() || "default";
4766
4783
  } catch {}
@@ -4809,16 +4826,16 @@ Dashboard not found at: ${dashboardDir}`);
4809
4826
  }
4810
4827
  if (path === "/api/export" && method === "GET") {
4811
4828
  try {
4812
- const connectDir = join4(homedir2(), ".connectors");
4829
+ const connectDir = join5(homedir2(), ".connectors");
4813
4830
  const result = {};
4814
- if (existsSync4(connectDir)) {
4831
+ if (existsSync5(connectDir)) {
4815
4832
  const entries = readdirSync3(connectDir, { withFileTypes: true });
4816
4833
  for (const entry of entries) {
4817
4834
  if (!entry.isDirectory() || !entry.name.startsWith("connect-"))
4818
4835
  continue;
4819
4836
  const connectorName = entry.name.replace(/^connect-/, "");
4820
- const profilesDir = join4(connectDir, entry.name, "profiles");
4821
- if (!existsSync4(profilesDir))
4837
+ const profilesDir = join5(connectDir, entry.name, "profiles");
4838
+ if (!existsSync5(profilesDir))
4822
4839
  continue;
4823
4840
  const profiles = {};
4824
4841
  const profileEntries = readdirSync3(profilesDir, { withFileTypes: true });
@@ -4826,13 +4843,13 @@ Dashboard not found at: ${dashboardDir}`);
4826
4843
  if (pEntry.isFile() && pEntry.name.endsWith(".json")) {
4827
4844
  const profileName = basename(pEntry.name, ".json");
4828
4845
  try {
4829
- const config = JSON.parse(readFileSync4(join4(profilesDir, pEntry.name), "utf-8"));
4846
+ const config = JSON.parse(readFileSync4(join5(profilesDir, pEntry.name), "utf-8"));
4830
4847
  profiles[profileName] = config;
4831
4848
  } catch {}
4832
4849
  }
4833
4850
  if (pEntry.isDirectory()) {
4834
- const configPath = join4(profilesDir, pEntry.name, "config.json");
4835
- if (existsSync4(configPath)) {
4851
+ const configPath = join5(profilesDir, pEntry.name, "config.json");
4852
+ if (existsSync5(configPath)) {
4836
4853
  try {
4837
4854
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
4838
4855
  profiles[pEntry.name] = config;
@@ -4869,19 +4886,19 @@ Dashboard not found at: ${dashboardDir}`);
4869
4886
  return json({ error: "Invalid import format: missing 'connectors' object" }, 400, port);
4870
4887
  }
4871
4888
  let imported = 0;
4872
- const connectDir = join4(homedir2(), ".connectors");
4889
+ const connectDir = join5(homedir2(), ".connectors");
4873
4890
  for (const [connectorName, data] of Object.entries(body.connectors)) {
4874
4891
  if (!isValidConnectorName(connectorName))
4875
4892
  continue;
4876
4893
  if (!data.profiles || typeof data.profiles !== "object")
4877
4894
  continue;
4878
- const connectorDir = join4(connectDir, `connect-${connectorName}`);
4879
- const profilesDir = join4(connectorDir, "profiles");
4895
+ const connectorDir = join5(connectDir, `connect-${connectorName}`);
4896
+ const profilesDir = join5(connectorDir, "profiles");
4880
4897
  for (const [profileName, config] of Object.entries(data.profiles)) {
4881
4898
  if (!config || typeof config !== "object")
4882
4899
  continue;
4883
4900
  mkdirSync3(profilesDir, { recursive: true });
4884
- const profileFile = join4(profilesDir, `${profileName}.json`);
4901
+ const profileFile = join5(profilesDir, `${profileName}.json`);
4885
4902
  writeFileSync3(profileFile, JSON.stringify(config, null, 2));
4886
4903
  imported++;
4887
4904
  }
@@ -4948,12 +4965,12 @@ Dashboard not found at: ${dashboardDir}`);
4948
4965
  }
4949
4966
  if (dashboardExists && (method === "GET" || method === "HEAD")) {
4950
4967
  if (path !== "/") {
4951
- const filePath = join4(dashboardDir, path);
4968
+ const filePath = join5(dashboardDir, path);
4952
4969
  const res2 = serveStaticFile(filePath);
4953
4970
  if (res2)
4954
4971
  return res2;
4955
4972
  }
4956
- const indexPath = join4(dashboardDir, "index.html");
4973
+ const indexPath = join5(dashboardDir, "index.html");
4957
4974
  const res = serveStaticFile(indexPath);
4958
4975
  if (res)
4959
4976
  return res;
@@ -6496,9 +6513,9 @@ function App({ initialConnectors, overwrite = false }) {
6496
6513
  init_registry();
6497
6514
  init_installer();
6498
6515
  init_auth();
6499
- import { readdirSync as readdirSync4, existsSync as existsSync5, statSync as statSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
6516
+ import { readdirSync as readdirSync4, existsSync as existsSync6, statSync as statSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
6500
6517
  import { homedir as homedir3 } from "os";
6501
- import { join as join5, relative } from "path";
6518
+ import { join as join6, relative } from "path";
6502
6519
 
6503
6520
  // src/lib/test-endpoints.ts
6504
6521
  var TEST_ENDPOINTS = {
@@ -6571,6 +6588,107 @@ var TEST_ENDPOINTS = {
6571
6588
 
6572
6589
  // src/cli/index.tsx
6573
6590
  import { createInterface } from "readline";
6591
+
6592
+ // src/lib/runner.ts
6593
+ import { existsSync as existsSync4 } from "fs";
6594
+ import { join as join4, dirname as dirname3 } from "path";
6595
+ import { fileURLToPath as fileURLToPath3 } from "url";
6596
+ import { spawn } from "child_process";
6597
+ var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
6598
+ function resolveConnectorsDir2() {
6599
+ const fromBin = join4(__dirname3, "..", "connectors");
6600
+ if (existsSync4(fromBin))
6601
+ return fromBin;
6602
+ const fromSrc = join4(__dirname3, "..", "..", "connectors");
6603
+ if (existsSync4(fromSrc))
6604
+ return fromSrc;
6605
+ return fromBin;
6606
+ }
6607
+ var CONNECTORS_DIR2 = resolveConnectorsDir2();
6608
+ function getConnectorCliPath(name) {
6609
+ const safeName = name.replace(/[^a-z0-9-]/g, "");
6610
+ const connectorDir = join4(CONNECTORS_DIR2, `connect-${safeName}`);
6611
+ const cliPath = join4(connectorDir, "src", "cli", "index.ts");
6612
+ if (existsSync4(cliPath))
6613
+ return cliPath;
6614
+ return null;
6615
+ }
6616
+ function runConnectorCommand(name, args, timeoutMs = 30000) {
6617
+ const cliPath = getConnectorCliPath(name);
6618
+ if (!cliPath) {
6619
+ return Promise.resolve({
6620
+ stdout: "",
6621
+ stderr: `Connector '${name}' not found or has no CLI.`,
6622
+ exitCode: 1,
6623
+ success: false
6624
+ });
6625
+ }
6626
+ return new Promise((resolve) => {
6627
+ const proc = spawn("bun", ["run", cliPath, ...args], {
6628
+ timeout: timeoutMs,
6629
+ env: { ...process.env },
6630
+ stdio: ["pipe", "pipe", "pipe"]
6631
+ });
6632
+ let stdout = "";
6633
+ let stderr = "";
6634
+ proc.stdout.on("data", (data) => {
6635
+ stdout += data.toString();
6636
+ });
6637
+ proc.stderr.on("data", (data) => {
6638
+ stderr += data.toString();
6639
+ });
6640
+ proc.on("close", (code) => {
6641
+ resolve({
6642
+ stdout: stdout.trim(),
6643
+ stderr: stderr.trim(),
6644
+ exitCode: code ?? 1,
6645
+ success: code === 0
6646
+ });
6647
+ });
6648
+ proc.on("error", (err) => {
6649
+ resolve({
6650
+ stdout: "",
6651
+ stderr: err.message,
6652
+ exitCode: 1,
6653
+ success: false
6654
+ });
6655
+ });
6656
+ });
6657
+ }
6658
+ async function getConnectorOperations(name) {
6659
+ const cliPath = getConnectorCliPath(name);
6660
+ if (!cliPath) {
6661
+ return { commands: [], helpText: "", hasCli: false };
6662
+ }
6663
+ const result = await runConnectorCommand(name, ["--help"]);
6664
+ const helpText = result.stdout || result.stderr;
6665
+ const commands = [];
6666
+ const lines = helpText.split(`
6667
+ `);
6668
+ let inCommands = false;
6669
+ for (const line of lines) {
6670
+ if (line.trim().startsWith("Commands:")) {
6671
+ inCommands = true;
6672
+ continue;
6673
+ }
6674
+ if (inCommands) {
6675
+ const match = line.match(/^\s{2,}(\S+)/);
6676
+ if (match && match[1] !== "help") {
6677
+ commands.push(match[1]);
6678
+ }
6679
+ if (line.trim() === "" && commands.length > 0) {
6680
+ inCommands = false;
6681
+ }
6682
+ }
6683
+ }
6684
+ return { commands, helpText, hasCli: true };
6685
+ }
6686
+ async function getConnectorCommandHelp(name, command) {
6687
+ const result = await runConnectorCommand(name, [command, "--help"]);
6688
+ return result.stdout || result.stderr;
6689
+ }
6690
+
6691
+ // src/cli/index.tsx
6574
6692
  import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
6575
6693
  loadConnectorVersions();
6576
6694
  var isTTY = process.stdout.isTTY ?? false;
@@ -6583,7 +6701,7 @@ var PRESETS = {
6583
6701
  commerce: { description: "Commerce and finance", connectors: ["stripe", "shopify", "revolut", "mercury", "pandadoc"] }
6584
6702
  };
6585
6703
  var program2 = new Command;
6586
- program2.name("connectors").description("Install API connectors for your project").version("0.3.0");
6704
+ program2.name("connectors").description("Install API connectors for your project").version("0.3.2");
6587
6705
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive connector browser").action(() => {
6588
6706
  if (!isTTY) {
6589
6707
  console.log(`Non-interactive environment detected. Use a subcommand:
@@ -6604,7 +6722,7 @@ Run 'connectors --help' for full usage.`);
6604
6722
  function listFilesRecursive(dir, base = dir) {
6605
6723
  const files = [];
6606
6724
  for (const entry of readdirSync4(dir)) {
6607
- const fullPath = join5(dir, entry);
6725
+ const fullPath = join6(dir, entry);
6608
6726
  if (statSync3(fullPath).isDirectory()) {
6609
6727
  files.push(...listFilesRecursive(fullPath, base));
6610
6728
  } else {
@@ -6653,7 +6771,7 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
6653
6771
  }
6654
6772
  if (options.dryRun) {
6655
6773
  const installed = getInstalledConnectors();
6656
- const destDir = join5(process.cwd(), ".connectors");
6774
+ const destDir = join6(process.cwd(), ".connectors");
6657
6775
  const actions = [];
6658
6776
  for (const name of connectors) {
6659
6777
  if (!/^[a-z0-9-]+$/.test(name)) {
@@ -6671,7 +6789,7 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
6671
6789
  }
6672
6790
  const connectorDirName = name.startsWith("connect-") ? name : `connect-${name}`;
6673
6791
  const sourcePath = getConnectorPath(name);
6674
- const destPath = join5(destDir, connectorDirName);
6792
+ const destPath = join6(destDir, connectorDirName);
6675
6793
  const alreadyInstalled = installed.includes(name);
6676
6794
  const files = listFilesRecursive(sourcePath);
6677
6795
  const importLine = `export * as ${name} from './${connectorDirName}/src/index.js';`;
@@ -7644,27 +7762,27 @@ Next steps:
7644
7762
  process.exit(results.every((r) => r.success) ? 0 : 1);
7645
7763
  });
7646
7764
  program2.command("export").option("-o, --output <file>", "Write to file instead of stdout").description("Export all connector credentials as JSON backup").action((options) => {
7647
- const connectDir = join5(homedir3(), ".connectors");
7765
+ const connectDir = join6(homedir3(), ".connectors");
7648
7766
  const result = {};
7649
- if (existsSync5(connectDir)) {
7767
+ if (existsSync6(connectDir)) {
7650
7768
  for (const entry of readdirSync4(connectDir)) {
7651
- const entryPath = join5(connectDir, entry);
7769
+ const entryPath = join6(connectDir, entry);
7652
7770
  if (!statSync3(entryPath).isDirectory() || !entry.startsWith("connect-"))
7653
7771
  continue;
7654
7772
  const connectorName = entry.replace(/^connect-/, "");
7655
- const profilesDir = join5(entryPath, "profiles");
7656
- if (!existsSync5(profilesDir))
7773
+ const profilesDir = join6(entryPath, "profiles");
7774
+ if (!existsSync6(profilesDir))
7657
7775
  continue;
7658
7776
  const profiles = {};
7659
7777
  for (const pEntry of readdirSync4(profilesDir)) {
7660
- const pPath = join5(profilesDir, pEntry);
7778
+ const pPath = join6(profilesDir, pEntry);
7661
7779
  if (statSync3(pPath).isFile() && pEntry.endsWith(".json")) {
7662
7780
  try {
7663
7781
  profiles[pEntry.replace(/\.json$/, "")] = JSON.parse(readFileSync5(pPath, "utf-8"));
7664
7782
  } catch {}
7665
7783
  } else if (statSync3(pPath).isDirectory()) {
7666
- const configPath = join5(pPath, "config.json");
7667
- if (existsSync5(configPath)) {
7784
+ const configPath = join6(pPath, "config.json");
7785
+ if (existsSync6(configPath)) {
7668
7786
  try {
7669
7787
  profiles[pEntry] = JSON.parse(readFileSync5(configPath, "utf-8"));
7670
7788
  } catch {}
@@ -7691,7 +7809,7 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
7691
7809
  chunks.push(chunk.toString());
7692
7810
  raw = chunks.join("");
7693
7811
  } else {
7694
- if (!existsSync5(file)) {
7812
+ if (!existsSync6(file)) {
7695
7813
  if (options.json) {
7696
7814
  console.log(JSON.stringify({ error: `File not found: ${file}` }));
7697
7815
  } else {
@@ -7723,19 +7841,19 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
7723
7841
  process.exit(1);
7724
7842
  return;
7725
7843
  }
7726
- const connectDir = join5(homedir3(), ".connectors");
7844
+ const connectDir = join6(homedir3(), ".connectors");
7727
7845
  let imported = 0;
7728
7846
  for (const [connectorName, connData] of Object.entries(data.connectors)) {
7729
7847
  if (!/^[a-z0-9-]+$/.test(connectorName))
7730
7848
  continue;
7731
7849
  if (!connData.profiles || typeof connData.profiles !== "object")
7732
7850
  continue;
7733
- const profilesDir = join5(connectDir, `connect-${connectorName}`, "profiles");
7851
+ const profilesDir = join6(connectDir, `connect-${connectorName}`, "profiles");
7734
7852
  for (const [profileName, config] of Object.entries(connData.profiles)) {
7735
7853
  if (!config || typeof config !== "object")
7736
7854
  continue;
7737
7855
  mkdirSync4(profilesDir, { recursive: true });
7738
- writeFileSync4(join5(profilesDir, `${profileName}.json`), JSON.stringify(config, null, 2));
7856
+ writeFileSync4(join6(profilesDir, `${profileName}.json`), JSON.stringify(config, null, 2));
7739
7857
  imported++;
7740
7858
  }
7741
7859
  }
@@ -7746,7 +7864,7 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
7746
7864
  }
7747
7865
  });
7748
7866
  program2.command("upgrade").alias("self-update").option("--check", "Only check for updates, don't install", false).option("--json", "Output as JSON", false).description("Check for updates and upgrade to the latest version").action(async (options) => {
7749
- const currentVersion = "0.3.0";
7867
+ const currentVersion = "0.3.1";
7750
7868
  try {
7751
7869
  const res = await fetch("https://registry.npmjs.org/@hasna/connectors/latest");
7752
7870
  if (!res.ok)
@@ -7960,9 +8078,9 @@ Available presets:
7960
8078
  `));
7961
8079
  });
7962
8080
  program2.command("whoami").option("--json", "Output as JSON", false).description("Show current setup: config dir, installed connectors, auth status").action((options) => {
7963
- const configDir = join5(homedir3(), ".connectors");
8081
+ const configDir = join6(homedir3(), ".connectors");
7964
8082
  const installed = getInstalledConnectors();
7965
- const version = "0.3.0";
8083
+ const version = "0.3.1";
7966
8084
  let configured = 0;
7967
8085
  let unconfigured = 0;
7968
8086
  const connectorDetails = [];
@@ -7972,10 +8090,10 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
7972
8090
  configured++;
7973
8091
  else
7974
8092
  unconfigured++;
7975
- const connectorConfigDir = join5(configDir, name.startsWith("connect-") ? name : `connect-${name}`);
7976
- const currentProfileFile = join5(connectorConfigDir, "current_profile");
8093
+ const connectorConfigDir = join6(configDir, name.startsWith("connect-") ? name : `connect-${name}`);
8094
+ const currentProfileFile = join6(connectorConfigDir, "current_profile");
7977
8095
  let profile = "default";
7978
- if (existsSync5(currentProfileFile)) {
8096
+ if (existsSync6(currentProfileFile)) {
7979
8097
  try {
7980
8098
  profile = readFileSync5(currentProfileFile, "utf-8").trim() || "default";
7981
8099
  } catch {}
@@ -7986,7 +8104,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
7986
8104
  console.log(JSON.stringify({
7987
8105
  version,
7988
8106
  configDir,
7989
- configDirExists: existsSync5(configDir),
8107
+ configDirExists: existsSync6(configDir),
7990
8108
  installed: installed.length,
7991
8109
  configured,
7992
8110
  unconfigured,
@@ -7998,7 +8116,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
7998
8116
  Connectors Setup
7999
8117
  `));
8000
8118
  console.log(` Version: ${chalk2.cyan(version)}`);
8001
- console.log(` Config: ${configDir}${existsSync5(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
8119
+ console.log(` Config: ${configDir}${existsSync6(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
8002
8120
  console.log(` Installed: ${installed.length} connector${installed.length !== 1 ? "s" : ""}`);
8003
8121
  console.log(` Configured: ${chalk2.green(String(configured))} ready, ${unconfigured > 0 ? chalk2.red(String(unconfigured)) : chalk2.dim("0")} need auth`);
8004
8122
  if (connectorDetails.length > 0) {
@@ -8070,17 +8188,17 @@ Testing connector credentials...
8070
8188
  }
8071
8189
  }
8072
8190
  if (!apiKey) {
8073
- const connectorConfigDir = join5(homedir3(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
8074
- const profileFile = join5(connectorConfigDir, "profiles", "default.json");
8075
- if (existsSync5(profileFile)) {
8191
+ const connectorConfigDir = join6(homedir3(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
8192
+ const profileFile = join6(connectorConfigDir, "profiles", "default.json");
8193
+ if (existsSync6(profileFile)) {
8076
8194
  try {
8077
8195
  const config = JSON.parse(readFileSync5(profileFile, "utf-8"));
8078
8196
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);
8079
8197
  } catch {}
8080
8198
  }
8081
8199
  if (!apiKey) {
8082
- const profileDirConfig = join5(connectorConfigDir, "profiles", "default", "config.json");
8083
- if (existsSync5(profileDirConfig)) {
8200
+ const profileDirConfig = join6(connectorConfigDir, "profiles", "default", "config.json");
8201
+ if (existsSync6(profileDirConfig)) {
8084
8202
  try {
8085
8203
  const config = JSON.parse(readFileSync5(profileDirConfig, "utf-8"));
8086
8204
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);
@@ -8142,4 +8260,73 @@ Testing connector credentials...
8142
8260
  }
8143
8261
  process.exit(results.some((r) => r.status === "fail") ? 1 : 0);
8144
8262
  });
8263
+ program2.command("ops").description("List available API operations for a connector").argument("<name>", "Connector name (e.g. stripe, gmail)").argument("[command]", "Get detailed help for a specific subcommand").option("--json", "Output as JSON").action(async (name, command, options) => {
8264
+ const meta = getConnector(name);
8265
+ if (!meta) {
8266
+ console.error(chalk2.red(`Connector '${name}' not found.`));
8267
+ process.exit(1);
8268
+ }
8269
+ if (!getConnectorCliPath(name)) {
8270
+ console.error(chalk2.red(`Connector '${name}' does not have a CLI.`));
8271
+ process.exit(1);
8272
+ }
8273
+ if (command) {
8274
+ const help = await getConnectorCommandHelp(name, command);
8275
+ if (options.json) {
8276
+ console.log(JSON.stringify({ connector: name, command, help }, null, 2));
8277
+ } else {
8278
+ console.log(chalk2.bold(`
8279
+ ${meta.displayName} \u2192 ${command}
8280
+ `));
8281
+ console.log(help);
8282
+ }
8283
+ return;
8284
+ }
8285
+ const ops = await getConnectorOperations(name);
8286
+ if (options.json) {
8287
+ console.log(JSON.stringify({
8288
+ connector: name,
8289
+ displayName: meta.displayName,
8290
+ commands: ops.commands
8291
+ }, null, 2));
8292
+ } else {
8293
+ console.log(chalk2.bold(`
8294
+ ${meta.displayName} operations:
8295
+ `));
8296
+ if (ops.commands.length > 0) {
8297
+ for (const cmd of ops.commands) {
8298
+ console.log(` ${chalk2.cyan(cmd)}`);
8299
+ }
8300
+ console.log(chalk2.dim(`
8301
+ Run ${chalk2.white(`connectors ops ${name} <command>`)} for details`));
8302
+ console.log(chalk2.dim(` Run ${chalk2.white(`connectors run ${name} <command> [args...]`)} to execute
8303
+ `));
8304
+ } else {
8305
+ console.log(ops.helpText);
8306
+ }
8307
+ }
8308
+ });
8309
+ program2.command("run").description("Execute an API operation on a connector").argument("<name>", "Connector name (e.g. stripe, gmail)").argument("[args...]", "Command arguments (e.g. products list --limit 5)").option("--timeout <ms>", "Timeout in milliseconds", "30000").allowUnknownOption(true).action(async (name, args, options) => {
8310
+ const meta = getConnector(name);
8311
+ if (!meta) {
8312
+ console.error(chalk2.red(`Connector '${name}' not found.`));
8313
+ process.exit(1);
8314
+ }
8315
+ if (!getConnectorCliPath(name)) {
8316
+ console.error(chalk2.red(`Connector '${name}' does not have a CLI.`));
8317
+ process.exit(1);
8318
+ }
8319
+ if (args.length === 0) {
8320
+ console.error(chalk2.yellow(`No command specified. Run ${chalk2.white(`connectors ops ${name}`)} to see available operations.`));
8321
+ process.exit(1);
8322
+ }
8323
+ const result = await runConnectorCommand(name, args, parseInt(options.timeout));
8324
+ if (result.stdout) {
8325
+ console.log(result.stdout);
8326
+ }
8327
+ if (result.stderr) {
8328
+ console.error(result.stderr);
8329
+ }
8330
+ process.exit(result.exitCode);
8331
+ });
8145
8332
  program2.parse();