@hasna/connectors 0.3.1 → 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}`);
@@ -4774,10 +4774,10 @@ Dashboard not found at: ${dashboardDir}`);
4774
4774
  return json({ error: "Invalid connector name" }, 400, port);
4775
4775
  try {
4776
4776
  const profiles = listProfiles(name);
4777
- const configDir = join4(homedir2(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
4778
- 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");
4779
4779
  let current = "default";
4780
- if (existsSync4(currentProfileFile)) {
4780
+ if (existsSync5(currentProfileFile)) {
4781
4781
  try {
4782
4782
  current = readFileSync4(currentProfileFile, "utf-8").trim() || "default";
4783
4783
  } catch {}
@@ -4826,16 +4826,16 @@ Dashboard not found at: ${dashboardDir}`);
4826
4826
  }
4827
4827
  if (path === "/api/export" && method === "GET") {
4828
4828
  try {
4829
- const connectDir = join4(homedir2(), ".connectors");
4829
+ const connectDir = join5(homedir2(), ".connectors");
4830
4830
  const result = {};
4831
- if (existsSync4(connectDir)) {
4831
+ if (existsSync5(connectDir)) {
4832
4832
  const entries = readdirSync3(connectDir, { withFileTypes: true });
4833
4833
  for (const entry of entries) {
4834
4834
  if (!entry.isDirectory() || !entry.name.startsWith("connect-"))
4835
4835
  continue;
4836
4836
  const connectorName = entry.name.replace(/^connect-/, "");
4837
- const profilesDir = join4(connectDir, entry.name, "profiles");
4838
- if (!existsSync4(profilesDir))
4837
+ const profilesDir = join5(connectDir, entry.name, "profiles");
4838
+ if (!existsSync5(profilesDir))
4839
4839
  continue;
4840
4840
  const profiles = {};
4841
4841
  const profileEntries = readdirSync3(profilesDir, { withFileTypes: true });
@@ -4843,13 +4843,13 @@ Dashboard not found at: ${dashboardDir}`);
4843
4843
  if (pEntry.isFile() && pEntry.name.endsWith(".json")) {
4844
4844
  const profileName = basename(pEntry.name, ".json");
4845
4845
  try {
4846
- const config = JSON.parse(readFileSync4(join4(profilesDir, pEntry.name), "utf-8"));
4846
+ const config = JSON.parse(readFileSync4(join5(profilesDir, pEntry.name), "utf-8"));
4847
4847
  profiles[profileName] = config;
4848
4848
  } catch {}
4849
4849
  }
4850
4850
  if (pEntry.isDirectory()) {
4851
- const configPath = join4(profilesDir, pEntry.name, "config.json");
4852
- if (existsSync4(configPath)) {
4851
+ const configPath = join5(profilesDir, pEntry.name, "config.json");
4852
+ if (existsSync5(configPath)) {
4853
4853
  try {
4854
4854
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
4855
4855
  profiles[pEntry.name] = config;
@@ -4886,19 +4886,19 @@ Dashboard not found at: ${dashboardDir}`);
4886
4886
  return json({ error: "Invalid import format: missing 'connectors' object" }, 400, port);
4887
4887
  }
4888
4888
  let imported = 0;
4889
- const connectDir = join4(homedir2(), ".connectors");
4889
+ const connectDir = join5(homedir2(), ".connectors");
4890
4890
  for (const [connectorName, data] of Object.entries(body.connectors)) {
4891
4891
  if (!isValidConnectorName(connectorName))
4892
4892
  continue;
4893
4893
  if (!data.profiles || typeof data.profiles !== "object")
4894
4894
  continue;
4895
- const connectorDir = join4(connectDir, `connect-${connectorName}`);
4896
- const profilesDir = join4(connectorDir, "profiles");
4895
+ const connectorDir = join5(connectDir, `connect-${connectorName}`);
4896
+ const profilesDir = join5(connectorDir, "profiles");
4897
4897
  for (const [profileName, config] of Object.entries(data.profiles)) {
4898
4898
  if (!config || typeof config !== "object")
4899
4899
  continue;
4900
4900
  mkdirSync3(profilesDir, { recursive: true });
4901
- const profileFile = join4(profilesDir, `${profileName}.json`);
4901
+ const profileFile = join5(profilesDir, `${profileName}.json`);
4902
4902
  writeFileSync3(profileFile, JSON.stringify(config, null, 2));
4903
4903
  imported++;
4904
4904
  }
@@ -4965,12 +4965,12 @@ Dashboard not found at: ${dashboardDir}`);
4965
4965
  }
4966
4966
  if (dashboardExists && (method === "GET" || method === "HEAD")) {
4967
4967
  if (path !== "/") {
4968
- const filePath = join4(dashboardDir, path);
4968
+ const filePath = join5(dashboardDir, path);
4969
4969
  const res2 = serveStaticFile(filePath);
4970
4970
  if (res2)
4971
4971
  return res2;
4972
4972
  }
4973
- const indexPath = join4(dashboardDir, "index.html");
4973
+ const indexPath = join5(dashboardDir, "index.html");
4974
4974
  const res = serveStaticFile(indexPath);
4975
4975
  if (res)
4976
4976
  return res;
@@ -6513,9 +6513,9 @@ function App({ initialConnectors, overwrite = false }) {
6513
6513
  init_registry();
6514
6514
  init_installer();
6515
6515
  init_auth();
6516
- 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";
6517
6517
  import { homedir as homedir3 } from "os";
6518
- import { join as join5, relative } from "path";
6518
+ import { join as join6, relative } from "path";
6519
6519
 
6520
6520
  // src/lib/test-endpoints.ts
6521
6521
  var TEST_ENDPOINTS = {
@@ -6588,6 +6588,107 @@ var TEST_ENDPOINTS = {
6588
6588
 
6589
6589
  // src/cli/index.tsx
6590
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
6591
6692
  import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
6592
6693
  loadConnectorVersions();
6593
6694
  var isTTY = process.stdout.isTTY ?? false;
@@ -6600,7 +6701,7 @@ var PRESETS = {
6600
6701
  commerce: { description: "Commerce and finance", connectors: ["stripe", "shopify", "revolut", "mercury", "pandadoc"] }
6601
6702
  };
6602
6703
  var program2 = new Command;
6603
- program2.name("connectors").description("Install API connectors for your project").version("0.3.1");
6704
+ program2.name("connectors").description("Install API connectors for your project").version("0.3.2");
6604
6705
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive connector browser").action(() => {
6605
6706
  if (!isTTY) {
6606
6707
  console.log(`Non-interactive environment detected. Use a subcommand:
@@ -6621,7 +6722,7 @@ Run 'connectors --help' for full usage.`);
6621
6722
  function listFilesRecursive(dir, base = dir) {
6622
6723
  const files = [];
6623
6724
  for (const entry of readdirSync4(dir)) {
6624
- const fullPath = join5(dir, entry);
6725
+ const fullPath = join6(dir, entry);
6625
6726
  if (statSync3(fullPath).isDirectory()) {
6626
6727
  files.push(...listFilesRecursive(fullPath, base));
6627
6728
  } else {
@@ -6670,7 +6771,7 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
6670
6771
  }
6671
6772
  if (options.dryRun) {
6672
6773
  const installed = getInstalledConnectors();
6673
- const destDir = join5(process.cwd(), ".connectors");
6774
+ const destDir = join6(process.cwd(), ".connectors");
6674
6775
  const actions = [];
6675
6776
  for (const name of connectors) {
6676
6777
  if (!/^[a-z0-9-]+$/.test(name)) {
@@ -6688,7 +6789,7 @@ program2.command("install").alias("add").argument("[connectors...]", "Connectors
6688
6789
  }
6689
6790
  const connectorDirName = name.startsWith("connect-") ? name : `connect-${name}`;
6690
6791
  const sourcePath = getConnectorPath(name);
6691
- const destPath = join5(destDir, connectorDirName);
6792
+ const destPath = join6(destDir, connectorDirName);
6692
6793
  const alreadyInstalled = installed.includes(name);
6693
6794
  const files = listFilesRecursive(sourcePath);
6694
6795
  const importLine = `export * as ${name} from './${connectorDirName}/src/index.js';`;
@@ -7661,27 +7762,27 @@ Next steps:
7661
7762
  process.exit(results.every((r) => r.success) ? 0 : 1);
7662
7763
  });
7663
7764
  program2.command("export").option("-o, --output <file>", "Write to file instead of stdout").description("Export all connector credentials as JSON backup").action((options) => {
7664
- const connectDir = join5(homedir3(), ".connectors");
7765
+ const connectDir = join6(homedir3(), ".connectors");
7665
7766
  const result = {};
7666
- if (existsSync5(connectDir)) {
7767
+ if (existsSync6(connectDir)) {
7667
7768
  for (const entry of readdirSync4(connectDir)) {
7668
- const entryPath = join5(connectDir, entry);
7769
+ const entryPath = join6(connectDir, entry);
7669
7770
  if (!statSync3(entryPath).isDirectory() || !entry.startsWith("connect-"))
7670
7771
  continue;
7671
7772
  const connectorName = entry.replace(/^connect-/, "");
7672
- const profilesDir = join5(entryPath, "profiles");
7673
- if (!existsSync5(profilesDir))
7773
+ const profilesDir = join6(entryPath, "profiles");
7774
+ if (!existsSync6(profilesDir))
7674
7775
  continue;
7675
7776
  const profiles = {};
7676
7777
  for (const pEntry of readdirSync4(profilesDir)) {
7677
- const pPath = join5(profilesDir, pEntry);
7778
+ const pPath = join6(profilesDir, pEntry);
7678
7779
  if (statSync3(pPath).isFile() && pEntry.endsWith(".json")) {
7679
7780
  try {
7680
7781
  profiles[pEntry.replace(/\.json$/, "")] = JSON.parse(readFileSync5(pPath, "utf-8"));
7681
7782
  } catch {}
7682
7783
  } else if (statSync3(pPath).isDirectory()) {
7683
- const configPath = join5(pPath, "config.json");
7684
- if (existsSync5(configPath)) {
7784
+ const configPath = join6(pPath, "config.json");
7785
+ if (existsSync6(configPath)) {
7685
7786
  try {
7686
7787
  profiles[pEntry] = JSON.parse(readFileSync5(configPath, "utf-8"));
7687
7788
  } catch {}
@@ -7708,7 +7809,7 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
7708
7809
  chunks.push(chunk.toString());
7709
7810
  raw = chunks.join("");
7710
7811
  } else {
7711
- if (!existsSync5(file)) {
7812
+ if (!existsSync6(file)) {
7712
7813
  if (options.json) {
7713
7814
  console.log(JSON.stringify({ error: `File not found: ${file}` }));
7714
7815
  } else {
@@ -7740,19 +7841,19 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
7740
7841
  process.exit(1);
7741
7842
  return;
7742
7843
  }
7743
- const connectDir = join5(homedir3(), ".connectors");
7844
+ const connectDir = join6(homedir3(), ".connectors");
7744
7845
  let imported = 0;
7745
7846
  for (const [connectorName, connData] of Object.entries(data.connectors)) {
7746
7847
  if (!/^[a-z0-9-]+$/.test(connectorName))
7747
7848
  continue;
7748
7849
  if (!connData.profiles || typeof connData.profiles !== "object")
7749
7850
  continue;
7750
- const profilesDir = join5(connectDir, `connect-${connectorName}`, "profiles");
7851
+ const profilesDir = join6(connectDir, `connect-${connectorName}`, "profiles");
7751
7852
  for (const [profileName, config] of Object.entries(connData.profiles)) {
7752
7853
  if (!config || typeof config !== "object")
7753
7854
  continue;
7754
7855
  mkdirSync4(profilesDir, { recursive: true });
7755
- writeFileSync4(join5(profilesDir, `${profileName}.json`), JSON.stringify(config, null, 2));
7856
+ writeFileSync4(join6(profilesDir, `${profileName}.json`), JSON.stringify(config, null, 2));
7756
7857
  imported++;
7757
7858
  }
7758
7859
  }
@@ -7977,7 +8078,7 @@ Available presets:
7977
8078
  `));
7978
8079
  });
7979
8080
  program2.command("whoami").option("--json", "Output as JSON", false).description("Show current setup: config dir, installed connectors, auth status").action((options) => {
7980
- const configDir = join5(homedir3(), ".connectors");
8081
+ const configDir = join6(homedir3(), ".connectors");
7981
8082
  const installed = getInstalledConnectors();
7982
8083
  const version = "0.3.1";
7983
8084
  let configured = 0;
@@ -7989,10 +8090,10 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
7989
8090
  configured++;
7990
8091
  else
7991
8092
  unconfigured++;
7992
- const connectorConfigDir = join5(configDir, name.startsWith("connect-") ? name : `connect-${name}`);
7993
- const currentProfileFile = join5(connectorConfigDir, "current_profile");
8093
+ const connectorConfigDir = join6(configDir, name.startsWith("connect-") ? name : `connect-${name}`);
8094
+ const currentProfileFile = join6(connectorConfigDir, "current_profile");
7994
8095
  let profile = "default";
7995
- if (existsSync5(currentProfileFile)) {
8096
+ if (existsSync6(currentProfileFile)) {
7996
8097
  try {
7997
8098
  profile = readFileSync5(currentProfileFile, "utf-8").trim() || "default";
7998
8099
  } catch {}
@@ -8003,7 +8104,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
8003
8104
  console.log(JSON.stringify({
8004
8105
  version,
8005
8106
  configDir,
8006
- configDirExists: existsSync5(configDir),
8107
+ configDirExists: existsSync6(configDir),
8007
8108
  installed: installed.length,
8008
8109
  configured,
8009
8110
  unconfigured,
@@ -8015,7 +8116,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
8015
8116
  Connectors Setup
8016
8117
  `));
8017
8118
  console.log(` Version: ${chalk2.cyan(version)}`);
8018
- console.log(` Config: ${configDir}${existsSync5(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
8119
+ console.log(` Config: ${configDir}${existsSync6(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
8019
8120
  console.log(` Installed: ${installed.length} connector${installed.length !== 1 ? "s" : ""}`);
8020
8121
  console.log(` Configured: ${chalk2.green(String(configured))} ready, ${unconfigured > 0 ? chalk2.red(String(unconfigured)) : chalk2.dim("0")} need auth`);
8021
8122
  if (connectorDetails.length > 0) {
@@ -8087,17 +8188,17 @@ Testing connector credentials...
8087
8188
  }
8088
8189
  }
8089
8190
  if (!apiKey) {
8090
- const connectorConfigDir = join5(homedir3(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
8091
- const profileFile = join5(connectorConfigDir, "profiles", "default.json");
8092
- 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)) {
8093
8194
  try {
8094
8195
  const config = JSON.parse(readFileSync5(profileFile, "utf-8"));
8095
8196
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);
8096
8197
  } catch {}
8097
8198
  }
8098
8199
  if (!apiKey) {
8099
- const profileDirConfig = join5(connectorConfigDir, "profiles", "default", "config.json");
8100
- if (existsSync5(profileDirConfig)) {
8200
+ const profileDirConfig = join6(connectorConfigDir, "profiles", "default", "config.json");
8201
+ if (existsSync6(profileDirConfig)) {
8101
8202
  try {
8102
8203
  const config = JSON.parse(readFileSync5(profileDirConfig, "utf-8"));
8103
8204
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);
@@ -8159,4 +8260,73 @@ Testing connector credentials...
8159
8260
  }
8160
8261
  process.exit(results.some((r) => r.status === "fail") ? 1 : 0);
8161
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
+ });
8162
8332
  program2.parse();