@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 +249 -62
- package/bin/mcp.js +214 -71
- package/bin/serve.js +18 -17
- package/connectors/connect-aws/package.json +4 -1
- package/connectors/connect-aws/src/api/awsses.ts +311 -0
- package/connectors/connect-aws/src/api/client.ts +1 -0
- package/connectors/connect-aws/src/api/index.ts +5 -1
- package/connectors/connect-aws/src/index.ts +1 -1
- package/connectors/connect-aws/src/types/index.ts +91 -0
- package/dashboard/dist/assets/index-CSlS3oNV.css +1 -0
- package/dashboard/dist/assets/index-sSIkMXYs.js +284 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +121 -0
- package/dist/lib/runner.d.ts +44 -0
- package/dist/lib/runner.test.d.ts +1 -0
- package/package.json +3 -1
- package/dashboard/dist/assets/index-DmR_QNtT.css +0 -1
- package/dashboard/dist/assets/index-Dp-apHbC.js +0 -284
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
|
|
4523
|
-
import { join as
|
|
4524
|
-
import { fileURLToPath as
|
|
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 =
|
|
4536
|
-
candidates.push(
|
|
4537
|
-
candidates.push(
|
|
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 =
|
|
4541
|
-
candidates.push(
|
|
4542
|
-
candidates.push(
|
|
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(
|
|
4544
|
+
candidates.push(join5(process.cwd(), "dashboard", "dist"));
|
|
4545
4545
|
for (const candidate of candidates) {
|
|
4546
|
-
if (
|
|
4546
|
+
if (existsSync5(candidate))
|
|
4547
4547
|
return candidate;
|
|
4548
4548
|
}
|
|
4549
|
-
return
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
4761
|
-
const currentProfileFile =
|
|
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 (
|
|
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 =
|
|
4829
|
+
const connectDir = join5(homedir2(), ".connectors");
|
|
4813
4830
|
const result = {};
|
|
4814
|
-
if (
|
|
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 =
|
|
4821
|
-
if (!
|
|
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(
|
|
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 =
|
|
4835
|
-
if (
|
|
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 =
|
|
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 =
|
|
4879
|
-
const profilesDir =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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.
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
7765
|
+
const connectDir = join6(homedir3(), ".connectors");
|
|
7648
7766
|
const result = {};
|
|
7649
|
-
if (
|
|
7767
|
+
if (existsSync6(connectDir)) {
|
|
7650
7768
|
for (const entry of readdirSync4(connectDir)) {
|
|
7651
|
-
const entryPath =
|
|
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 =
|
|
7656
|
-
if (!
|
|
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 =
|
|
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 =
|
|
7667
|
-
if (
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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(
|
|
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.
|
|
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 =
|
|
8081
|
+
const configDir = join6(homedir3(), ".connectors");
|
|
7964
8082
|
const installed = getInstalledConnectors();
|
|
7965
|
-
const version = "0.3.
|
|
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 =
|
|
7976
|
-
const currentProfileFile =
|
|
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 (
|
|
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:
|
|
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}${
|
|
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 =
|
|
8074
|
-
const profileFile =
|
|
8075
|
-
if (
|
|
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 =
|
|
8083
|
-
if (
|
|
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();
|