@hasna/connectors 1.3.7 → 1.3.8

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
@@ -1904,147 +1904,45 @@ var require_commander = __commonJS((exports) => {
1904
1904
  exports.InvalidOptionArgumentError = InvalidArgumentError;
1905
1905
  });
1906
1906
 
1907
- // src/lib/llm.ts
1908
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
1909
- import { join } from "path";
1910
- import { homedir } from "os";
1911
- function getLlmConfigPath() {
1912
- return join(homedir(), ".connectors", "llm.json");
1913
- }
1914
- function getLlmConfig() {
1915
- const path = getLlmConfigPath();
1916
- if (!existsSync(path))
1917
- return null;
1918
- try {
1919
- return JSON.parse(readFileSync(path, "utf-8"));
1920
- } catch {
1921
- return null;
1922
- }
1923
- }
1924
- function saveLlmConfig(config) {
1925
- const dir = join(homedir(), ".connectors");
1926
- mkdirSync(dir, { recursive: true });
1927
- writeFileSync(getLlmConfigPath(), JSON.stringify(config, null, 2));
1928
- }
1929
- function setLlmStrip(enabled) {
1930
- const config = getLlmConfig();
1931
- if (!config)
1932
- throw new Error("No LLM config found. Run: connectors llm set --provider <provider> --key <key>");
1933
- saveLlmConfig({ ...config, strip: enabled });
1934
- }
1935
- function maskKey(key) {
1936
- if (key.length <= 8)
1937
- return "***";
1938
- return key.slice(0, 8) + "***";
1939
- }
1940
-
1941
- class LLMClient {
1942
- config;
1943
- constructor(config) {
1944
- this.config = config;
1945
- }
1946
- static fromConfig() {
1947
- const config = getLlmConfig();
1948
- if (!config)
1949
- return null;
1950
- return new LLMClient(config);
1951
- }
1952
- async complete(prompt, content) {
1953
- const start = Date.now();
1954
- const { provider, model, api_key } = this.config;
1955
- if (provider === "anthropic") {
1956
- return this._anthropicComplete(prompt, content, start);
1957
- }
1958
- const baseUrl = PROVIDER_BASE_URLS[provider];
1959
- const response = await fetch(`${baseUrl}/chat/completions`, {
1960
- method: "POST",
1961
- headers: {
1962
- "Content-Type": "application/json",
1963
- Authorization: `Bearer ${api_key}`
1964
- },
1965
- body: JSON.stringify({
1966
- model,
1967
- messages: [
1968
- { role: "system", content: prompt },
1969
- { role: "user", content }
1970
- ],
1971
- temperature: 0,
1972
- max_tokens: 4096
1973
- })
1974
- });
1975
- if (!response.ok) {
1976
- const error = await response.text();
1977
- throw new Error(`LLM request failed (${provider} ${response.status}): ${error}`);
1978
- }
1979
- const data = await response.json();
1980
- return {
1981
- content: data.choices[0].message.content,
1982
- provider,
1983
- model,
1984
- latency_ms: Date.now() - start
1985
- };
1986
- }
1987
- async _anthropicComplete(prompt, content, start) {
1988
- const { model, api_key } = this.config;
1989
- const response = await fetch("https://api.anthropic.com/v1/messages", {
1990
- method: "POST",
1991
- headers: {
1992
- "Content-Type": "application/json",
1993
- "x-api-key": api_key,
1994
- "anthropic-version": "2023-06-01"
1995
- },
1996
- body: JSON.stringify({
1997
- model,
1998
- system: prompt,
1999
- messages: [{ role: "user", content }],
2000
- max_tokens: 4096
2001
- })
2002
- });
2003
- if (!response.ok) {
2004
- const error = await response.text();
2005
- throw new Error(`LLM request failed (anthropic ${response.status}): ${error}`);
2006
- }
2007
- const data = await response.json();
2008
- return {
2009
- content: data.content[0].text,
2010
- provider: "anthropic",
2011
- model,
2012
- latency_ms: Date.now() - start
2013
- };
2014
- }
2015
- }
2016
- var PROVIDER_BASE_URLS, PROVIDER_DEFAULTS;
2017
- var init_llm = __esm(() => {
2018
- PROVIDER_BASE_URLS = {
2019
- cerebras: "https://api.cerebras.ai/v1",
2020
- groq: "https://api.groq.com/openai/v1",
2021
- openai: "https://api.openai.com/v1"
2022
- };
2023
- PROVIDER_DEFAULTS = {
2024
- cerebras: { model: "qwen-3-32b" },
2025
- groq: { model: "llama-3.3-70b-versatile" },
2026
- openai: { model: "gpt-4o-mini" },
2027
- anthropic: { model: "claude-haiku-4-5-20251001" }
2028
- };
2029
- });
2030
-
2031
1907
  // src/db/database.ts
2032
1908
  var exports_database = {};
2033
1909
  __export(exports_database, {
2034
1910
  shortUuid: () => shortUuid,
2035
1911
  now: () => now,
2036
1912
  getDatabase: () => getDatabase,
1913
+ getConnectorsHome: () => getConnectorsHome,
2037
1914
  closeDatabase: () => closeDatabase
2038
1915
  });
2039
1916
  import { Database } from "bun:sqlite";
2040
- import { join as join2 } from "path";
2041
- import { homedir as homedir2 } from "os";
2042
- import { mkdirSync as mkdirSync2 } from "fs";
1917
+ import { join } from "path";
1918
+ import { homedir } from "os";
1919
+ import { mkdirSync, existsSync, readdirSync, copyFileSync, statSync } from "fs";
1920
+ function getConnectorsHome() {
1921
+ const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
1922
+ const newDir = join(home, ".hasna", "connectors");
1923
+ const oldDir = join(home, ".connectors");
1924
+ if (existsSync(oldDir) && !existsSync(newDir)) {
1925
+ mkdirSync(newDir, { recursive: true });
1926
+ try {
1927
+ for (const file of readdirSync(oldDir)) {
1928
+ const oldPath = join(oldDir, file);
1929
+ const newPath = join(newDir, file);
1930
+ try {
1931
+ if (statSync(oldPath).isFile()) {
1932
+ copyFileSync(oldPath, newPath);
1933
+ }
1934
+ } catch {}
1935
+ }
1936
+ } catch {}
1937
+ }
1938
+ mkdirSync(newDir, { recursive: true });
1939
+ return newDir;
1940
+ }
2043
1941
  function getDatabase(path) {
2044
1942
  if (_db)
2045
1943
  return _db;
2046
1944
  const dbPath = path ?? DB_PATH;
2047
- mkdirSync2(join2(dbPath, ".."), { recursive: true });
1945
+ mkdirSync(join(dbPath, ".."), { recursive: true });
2048
1946
  _db = new Database(dbPath);
2049
1947
  _db.run("PRAGMA journal_mode = WAL");
2050
1948
  migrate(_db);
@@ -2154,8 +2052,132 @@ function migrate(db) {
2154
2052
  }
2155
2053
  var DB_DIR, DB_PATH, _db = null;
2156
2054
  var init_database = __esm(() => {
2157
- DB_DIR = join2(homedir2(), ".connectors");
2158
- DB_PATH = join2(DB_DIR, "connectors.db");
2055
+ DB_DIR = getConnectorsHome();
2056
+ DB_PATH = join(DB_DIR, "connectors.db");
2057
+ });
2058
+
2059
+ // src/lib/llm.ts
2060
+ import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
2061
+ import { join as join2 } from "path";
2062
+ function getLlmConfigPath() {
2063
+ return join2(getConnectorsHome(), "llm.json");
2064
+ }
2065
+ function getLlmConfig() {
2066
+ const path = getLlmConfigPath();
2067
+ if (!existsSync2(path))
2068
+ return null;
2069
+ try {
2070
+ return JSON.parse(readFileSync(path, "utf-8"));
2071
+ } catch {
2072
+ return null;
2073
+ }
2074
+ }
2075
+ function saveLlmConfig(config) {
2076
+ const dir = getConnectorsHome();
2077
+ mkdirSync2(dir, { recursive: true });
2078
+ writeFileSync(getLlmConfigPath(), JSON.stringify(config, null, 2));
2079
+ }
2080
+ function setLlmStrip(enabled) {
2081
+ const config = getLlmConfig();
2082
+ if (!config)
2083
+ throw new Error("No LLM config found. Run: connectors llm set --provider <provider> --key <key>");
2084
+ saveLlmConfig({ ...config, strip: enabled });
2085
+ }
2086
+ function maskKey(key) {
2087
+ if (key.length <= 8)
2088
+ return "***";
2089
+ return key.slice(0, 8) + "***";
2090
+ }
2091
+
2092
+ class LLMClient {
2093
+ config;
2094
+ constructor(config) {
2095
+ this.config = config;
2096
+ }
2097
+ static fromConfig() {
2098
+ const config = getLlmConfig();
2099
+ if (!config)
2100
+ return null;
2101
+ return new LLMClient(config);
2102
+ }
2103
+ async complete(prompt, content) {
2104
+ const start = Date.now();
2105
+ const { provider, model, api_key } = this.config;
2106
+ if (provider === "anthropic") {
2107
+ return this._anthropicComplete(prompt, content, start);
2108
+ }
2109
+ const baseUrl = PROVIDER_BASE_URLS[provider];
2110
+ const response = await fetch(`${baseUrl}/chat/completions`, {
2111
+ method: "POST",
2112
+ headers: {
2113
+ "Content-Type": "application/json",
2114
+ Authorization: `Bearer ${api_key}`
2115
+ },
2116
+ body: JSON.stringify({
2117
+ model,
2118
+ messages: [
2119
+ { role: "system", content: prompt },
2120
+ { role: "user", content }
2121
+ ],
2122
+ temperature: 0,
2123
+ max_tokens: 4096
2124
+ })
2125
+ });
2126
+ if (!response.ok) {
2127
+ const error = await response.text();
2128
+ throw new Error(`LLM request failed (${provider} ${response.status}): ${error}`);
2129
+ }
2130
+ const data = await response.json();
2131
+ return {
2132
+ content: data.choices[0].message.content,
2133
+ provider,
2134
+ model,
2135
+ latency_ms: Date.now() - start
2136
+ };
2137
+ }
2138
+ async _anthropicComplete(prompt, content, start) {
2139
+ const { model, api_key } = this.config;
2140
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
2141
+ method: "POST",
2142
+ headers: {
2143
+ "Content-Type": "application/json",
2144
+ "x-api-key": api_key,
2145
+ "anthropic-version": "2023-06-01"
2146
+ },
2147
+ body: JSON.stringify({
2148
+ model,
2149
+ system: prompt,
2150
+ messages: [{ role: "user", content }],
2151
+ max_tokens: 4096
2152
+ })
2153
+ });
2154
+ if (!response.ok) {
2155
+ const error = await response.text();
2156
+ throw new Error(`LLM request failed (anthropic ${response.status}): ${error}`);
2157
+ }
2158
+ const data = await response.json();
2159
+ return {
2160
+ content: data.content[0].text,
2161
+ provider: "anthropic",
2162
+ model,
2163
+ latency_ms: Date.now() - start
2164
+ };
2165
+ }
2166
+ }
2167
+ var PROVIDER_BASE_URLS, PROVIDER_DEFAULTS;
2168
+ var init_llm = __esm(() => {
2169
+ init_database();
2170
+ PROVIDER_BASE_URLS = {
2171
+ cerebras: "https://api.cerebras.ai/v1",
2172
+ groq: "https://api.groq.com/openai/v1",
2173
+ openai: "https://api.openai.com/v1"
2174
+ };
2175
+ PROVIDER_DEFAULTS = {
2176
+ cerebras: { model: "qwen-3-32b" },
2177
+ groq: { model: "llama-3.3-70b-versatile" },
2178
+ openai: { model: "gpt-4o-mini" },
2179
+ anthropic: { model: "claude-haiku-4-5-20251001" }
2180
+ };
2159
2181
  });
2160
2182
 
2161
2183
  // src/db/jobs.ts
@@ -2566,7 +2588,7 @@ var init_synonyms = __esm(() => {
2566
2588
  });
2567
2589
 
2568
2590
  // src/lib/registry.ts
2569
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2591
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2570
2592
  import { join as join3, dirname } from "path";
2571
2593
  import { fileURLToPath } from "url";
2572
2594
  function getConnectorsByCategory(category) {
@@ -2731,13 +2753,13 @@ function loadConnectorVersions() {
2731
2753
  join3(thisDir, "..", "connectors"),
2732
2754
  join3(thisDir, "..", "..", "connectors")
2733
2755
  ];
2734
- const connectorsDir = candidates.find((d) => existsSync2(d));
2756
+ const connectorsDir = candidates.find((d) => existsSync3(d));
2735
2757
  if (!connectorsDir)
2736
2758
  return;
2737
2759
  for (const connector of CONNECTORS) {
2738
2760
  try {
2739
2761
  const pkgPath = join3(connectorsDir, `connect-${connector.name}`, "package.json");
2740
- if (existsSync2(pkgPath)) {
2762
+ if (existsSync3(pkgPath)) {
2741
2763
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
2742
2764
  connector.version = pkg.version || "0.0.0";
2743
2765
  }
@@ -3152,6 +3174,13 @@ var init_registry = __esm(() => {
3152
3174
  category: "Data & Analytics",
3153
3175
  tags: ["weather", "data"]
3154
3176
  },
3177
+ {
3178
+ name: "arxiv",
3179
+ displayName: "arXiv",
3180
+ description: "Research paper search and retrieval",
3181
+ category: "Data & Analytics",
3182
+ tags: ["research", "papers", "academic"]
3183
+ },
3155
3184
  {
3156
3185
  name: "brandsight",
3157
3186
  displayName: "Brandsight",
@@ -10305,16 +10334,15 @@ var require_cli_spinners = __commonJS((exports, module) => {
10305
10334
  });
10306
10335
 
10307
10336
  // src/lib/installer.ts
10308
- import { existsSync as existsSync3, cpSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync, statSync, rmSync } from "fs";
10309
- import { homedir as homedir3 } from "os";
10337
+ import { existsSync as existsSync4, cpSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync2, statSync as statSync2, rmSync } from "fs";
10310
10338
  import { join as join4, dirname as dirname2 } from "path";
10311
10339
  import { fileURLToPath as fileURLToPath2 } from "url";
10312
10340
  function resolveConnectorsDir() {
10313
10341
  const fromBin = join4(__dirname2, "..", "connectors");
10314
- if (existsSync3(fromBin))
10342
+ if (existsSync4(fromBin))
10315
10343
  return fromBin;
10316
10344
  const fromSrc = join4(__dirname2, "..", "..", "connectors");
10317
- if (existsSync3(fromSrc))
10345
+ if (existsSync4(fromSrc))
10318
10346
  return fromSrc;
10319
10347
  return fromBin;
10320
10348
  }
@@ -10323,7 +10351,7 @@ function getConnectorPath(name) {
10323
10351
  return join4(CONNECTORS_DIR, connectorName);
10324
10352
  }
10325
10353
  function connectorExists(name) {
10326
- return existsSync3(getConnectorPath(name));
10354
+ return existsSync4(getConnectorPath(name));
10327
10355
  }
10328
10356
  function installConnector(name, options = {}) {
10329
10357
  const { targetDir = process.cwd(), overwrite = false } = options;
@@ -10338,14 +10366,14 @@ function installConnector(name, options = {}) {
10338
10366
  const sourcePath = getConnectorPath(name);
10339
10367
  const destDir = join4(targetDir, ".connectors");
10340
10368
  const destPath = join4(destDir, connectorName);
10341
- if (!existsSync3(sourcePath)) {
10369
+ if (!existsSync4(sourcePath)) {
10342
10370
  return {
10343
10371
  connector: name,
10344
10372
  success: false,
10345
10373
  error: `Connector '${name}' not found`
10346
10374
  };
10347
10375
  }
10348
- if (existsSync3(destPath) && !overwrite) {
10376
+ if (existsSync4(destPath) && !overwrite) {
10349
10377
  return {
10350
10378
  connector: name,
10351
10379
  success: false,
@@ -10354,21 +10382,21 @@ function installConnector(name, options = {}) {
10354
10382
  };
10355
10383
  }
10356
10384
  try {
10357
- if (!existsSync3(destDir)) {
10385
+ if (!existsSync4(destDir)) {
10358
10386
  mkdirSync3(destDir, { recursive: true });
10359
10387
  }
10360
10388
  cpSync(sourcePath, destPath, { recursive: true });
10361
- const homeCredDir = join4(homedir3(), ".connectors", connectorName);
10362
- if (existsSync3(homeCredDir)) {
10389
+ const homeCredDir = join4(getConnectorsHome(), connectorName);
10390
+ if (existsSync4(homeCredDir)) {
10363
10391
  const filesToCopy = ["credentials.json", "current_profile"];
10364
10392
  for (const file of filesToCopy) {
10365
10393
  const src = join4(homeCredDir, file);
10366
- if (existsSync3(src)) {
10394
+ if (existsSync4(src)) {
10367
10395
  cpSync(src, join4(destPath, file));
10368
10396
  }
10369
10397
  }
10370
10398
  const profilesDir = join4(homeCredDir, "profiles");
10371
- if (existsSync3(profilesDir)) {
10399
+ if (existsSync4(profilesDir)) {
10372
10400
  cpSync(profilesDir, join4(destPath, "profiles"), { recursive: true });
10373
10401
  }
10374
10402
  }
@@ -10388,7 +10416,7 @@ function installConnector(name, options = {}) {
10388
10416
  }
10389
10417
  function updateConnectorsIndex(connectorsDir) {
10390
10418
  const indexPath = join4(connectorsDir, "index.ts");
10391
- const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
10419
+ const connectors = readdirSync2(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
10392
10420
  const exports = connectors.map((c) => {
10393
10421
  const name = c.replace("connect-", "");
10394
10422
  return `export * as ${name} from './${c}/src/index.js';`;
@@ -10405,18 +10433,18 @@ ${exports}
10405
10433
  }
10406
10434
  function getInstalledConnectors(targetDir = process.cwd()) {
10407
10435
  const connectorsDir = join4(targetDir, ".connectors");
10408
- if (!existsSync3(connectorsDir)) {
10436
+ if (!existsSync4(connectorsDir)) {
10409
10437
  return [];
10410
10438
  }
10411
- return readdirSync(connectorsDir).filter((f) => {
10439
+ return readdirSync2(connectorsDir).filter((f) => {
10412
10440
  const fullPath = join4(connectorsDir, f);
10413
- return f.startsWith("connect-") && statSync(fullPath).isDirectory();
10441
+ return f.startsWith("connect-") && statSync2(fullPath).isDirectory();
10414
10442
  }).map((f) => f.replace("connect-", ""));
10415
10443
  }
10416
10444
  function getConnectorDocs(name) {
10417
10445
  const connectorPath = getConnectorPath(name);
10418
10446
  const claudeMdPath = join4(connectorPath, "CLAUDE.md");
10419
- if (!existsSync3(claudeMdPath))
10447
+ if (!existsSync4(claudeMdPath))
10420
10448
  return null;
10421
10449
  const raw = readFileSync3(claudeMdPath, "utf-8");
10422
10450
  return {
@@ -10459,7 +10487,7 @@ function removeConnector(name, targetDir = process.cwd()) {
10459
10487
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
10460
10488
  const connectorsDir = join4(targetDir, ".connectors");
10461
10489
  const connectorPath = join4(connectorsDir, connectorName);
10462
- if (!existsSync3(connectorPath)) {
10490
+ if (!existsSync4(connectorPath)) {
10463
10491
  return false;
10464
10492
  }
10465
10493
  rmSync(connectorPath, { recursive: true });
@@ -10468,30 +10496,30 @@ function removeConnector(name, targetDir = process.cwd()) {
10468
10496
  }
10469
10497
  var __dirname2, CONNECTORS_DIR;
10470
10498
  var init_installer = __esm(() => {
10499
+ init_database();
10471
10500
  __dirname2 = dirname2(fileURLToPath2(import.meta.url));
10472
10501
  CONNECTORS_DIR = resolveConnectorsDir();
10473
10502
  });
10474
10503
 
10475
10504
  // src/lib/lock.ts
10476
- import { openSync, closeSync, unlinkSync, existsSync as existsSync4, statSync as statSync2 } from "fs";
10505
+ import { openSync, closeSync, unlinkSync, existsSync as existsSync5, statSync as statSync3 } from "fs";
10477
10506
  import { join as join5 } from "path";
10478
- import { homedir as homedir4 } from "os";
10479
10507
  import { mkdirSync as mkdirSync4 } from "fs";
10480
10508
  function lockPath(connector) {
10481
- const dir = join5(homedir4(), ".connectors", `connect-${connector}`);
10509
+ const dir = join5(getConnectorsHome(), `connect-${connector}`);
10482
10510
  mkdirSync4(dir, { recursive: true });
10483
10511
  return join5(dir, ".write.lock");
10484
10512
  }
10485
10513
  function isStale(path) {
10486
10514
  try {
10487
- const stat = statSync2(path);
10515
+ const stat = statSync3(path);
10488
10516
  return Date.now() - stat.mtimeMs > STALE_LOCK_MS;
10489
10517
  } catch {
10490
10518
  return false;
10491
10519
  }
10492
10520
  }
10493
10521
  function tryAcquire(path) {
10494
- if (existsSync4(path) && isStale(path)) {
10522
+ if (existsSync5(path) && isStale(path)) {
10495
10523
  try {
10496
10524
  unlinkSync(path);
10497
10525
  } catch {}
@@ -10528,6 +10556,7 @@ async function withWriteLock(connector, fn) {
10528
10556
  }
10529
10557
  var LOCK_TIMEOUT_MS = 5000, LOCK_RETRY_MS = 100, STALE_LOCK_MS = 30000, LockTimeoutError;
10530
10558
  var init_lock = __esm(() => {
10559
+ init_database();
10531
10560
  LockTimeoutError = class LockTimeoutError extends Error {
10532
10561
  connector;
10533
10562
  constructor(connector) {
@@ -10539,9 +10568,8 @@ var init_lock = __esm(() => {
10539
10568
  });
10540
10569
 
10541
10570
  // src/server/auth.ts
10542
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
10571
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync3, rmSync as rmSync2, statSync as statSync4 } from "fs";
10543
10572
  import { randomBytes } from "crypto";
10544
- import { homedir as homedir5 } from "os";
10545
10573
  import { join as join6 } from "path";
10546
10574
  function getAuthType(name) {
10547
10575
  const docs = getConnectorDocs(name);
@@ -10556,12 +10584,12 @@ function getAuthType(name) {
10556
10584
  }
10557
10585
  function getConnectorConfigDir(name) {
10558
10586
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
10559
- return join6(homedir5(), ".connectors", connectorName);
10587
+ return join6(getConnectorsHome(), connectorName);
10560
10588
  }
10561
10589
  function getCurrentProfile(name) {
10562
10590
  const configDir = getConnectorConfigDir(name);
10563
10591
  const currentProfileFile = join6(configDir, "current_profile");
10564
- if (existsSync5(currentProfileFile)) {
10592
+ if (existsSync6(currentProfileFile)) {
10565
10593
  try {
10566
10594
  return readFileSync4(currentProfileFile, "utf-8").trim() || "default";
10567
10595
  } catch {
@@ -10576,13 +10604,13 @@ function loadProfileConfig(name) {
10576
10604
  let flatConfig = {};
10577
10605
  let dirConfig = {};
10578
10606
  const profileFile = join6(configDir, "profiles", `${profile}.json`);
10579
- if (existsSync5(profileFile)) {
10607
+ if (existsSync6(profileFile)) {
10580
10608
  try {
10581
10609
  flatConfig = JSON.parse(readFileSync4(profileFile, "utf-8"));
10582
10610
  } catch {}
10583
10611
  }
10584
10612
  const profileDirConfig = join6(configDir, "profiles", profile, "config.json");
10585
- if (existsSync5(profileDirConfig)) {
10613
+ if (existsSync6(profileDirConfig)) {
10586
10614
  try {
10587
10615
  dirConfig = JSON.parse(readFileSync4(profileDirConfig, "utf-8"));
10588
10616
  } catch {}
@@ -10596,7 +10624,7 @@ function loadTokens(name) {
10596
10624
  const configDir = getConnectorConfigDir(name);
10597
10625
  const profile = getCurrentProfile(name);
10598
10626
  const tokensFile = join6(configDir, "profiles", profile, "tokens.json");
10599
- if (existsSync5(tokensFile)) {
10627
+ if (existsSync6(tokensFile)) {
10600
10628
  try {
10601
10629
  return JSON.parse(readFileSync4(tokensFile, "utf-8"));
10602
10630
  } catch {
@@ -10668,7 +10696,7 @@ function _saveApiKey(name, key, field) {
10668
10696
  const credentialsFile = join6(configDir, "credentials.json");
10669
10697
  mkdirSync5(configDir, { recursive: true });
10670
10698
  let creds = {};
10671
- if (existsSync5(credentialsFile)) {
10699
+ if (existsSync6(credentialsFile)) {
10672
10700
  try {
10673
10701
  creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
10674
10702
  } catch {}
@@ -10679,7 +10707,7 @@ function _saveApiKey(name, key, field) {
10679
10707
  }
10680
10708
  const profileFile = join6(configDir, "profiles", `${profile}.json`);
10681
10709
  const profileDir = join6(configDir, "profiles", profile);
10682
- if (existsSync5(profileFile)) {
10710
+ if (existsSync6(profileFile)) {
10683
10711
  let config = {};
10684
10712
  try {
10685
10713
  config = JSON.parse(readFileSync4(profileFile, "utf-8"));
@@ -10688,10 +10716,10 @@ function _saveApiKey(name, key, field) {
10688
10716
  writeFileSync3(profileFile, JSON.stringify(config, null, 2));
10689
10717
  return;
10690
10718
  }
10691
- if (existsSync5(profileDir)) {
10719
+ if (existsSync6(profileDir)) {
10692
10720
  const configFile = join6(profileDir, "config.json");
10693
10721
  let config = {};
10694
- if (existsSync5(configFile)) {
10722
+ if (existsSync6(configFile)) {
10695
10723
  try {
10696
10724
  config = JSON.parse(readFileSync4(configFile, "utf-8"));
10697
10725
  } catch {}
@@ -10720,7 +10748,7 @@ function guessKeyField(name) {
10720
10748
  function getOAuthConfig(name) {
10721
10749
  const configDir = getConnectorConfigDir(name);
10722
10750
  const credentialsFile = join6(configDir, "credentials.json");
10723
- if (existsSync5(credentialsFile)) {
10751
+ if (existsSync6(credentialsFile)) {
10724
10752
  try {
10725
10753
  const creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
10726
10754
  return { clientId: creds.clientId, clientSecret: creds.clientSecret };
@@ -10847,14 +10875,14 @@ async function _refreshOAuthToken(name) {
10847
10875
  function listProfiles(name) {
10848
10876
  const configDir = getConnectorConfigDir(name);
10849
10877
  const profilesDir = join6(configDir, "profiles");
10850
- if (!existsSync5(profilesDir))
10878
+ if (!existsSync6(profilesDir))
10851
10879
  return ["default"];
10852
10880
  const seen = new Set;
10853
10881
  try {
10854
- const entries = readdirSync2(profilesDir);
10882
+ const entries = readdirSync3(profilesDir);
10855
10883
  for (const entry of entries) {
10856
10884
  const fullPath = join6(profilesDir, entry);
10857
- const stat = statSync3(fullPath);
10885
+ const stat = statSync4(fullPath);
10858
10886
  if (stat.isDirectory()) {
10859
10887
  seen.add(entry);
10860
10888
  } else if (entry.endsWith(".json")) {
@@ -10876,7 +10904,7 @@ function deleteProfile(name, profile) {
10876
10904
  const configDir = getConnectorConfigDir(name);
10877
10905
  const profilesDir = join6(configDir, "profiles");
10878
10906
  const profileFile = join6(profilesDir, `${profile}.json`);
10879
- if (existsSync5(profileFile)) {
10907
+ if (existsSync6(profileFile)) {
10880
10908
  rmSync2(profileFile);
10881
10909
  if (getCurrentProfile(name) === profile) {
10882
10910
  switchProfile(name, "default");
@@ -10884,7 +10912,7 @@ function deleteProfile(name, profile) {
10884
10912
  return true;
10885
10913
  }
10886
10914
  const profileDir = join6(profilesDir, profile);
10887
- if (existsSync5(profileDir)) {
10915
+ if (existsSync6(profileDir)) {
10888
10916
  rmSync2(profileDir, { recursive: true });
10889
10917
  if (getCurrentProfile(name) === profile) {
10890
10918
  switchProfile(name, "default");
@@ -10897,6 +10925,7 @@ var FETCH_TIMEOUT = 1e4, oauthStateStore, GOOGLE_AUTH_URL = "https://accounts.go
10897
10925
  var init_auth = __esm(() => {
10898
10926
  init_installer();
10899
10927
  init_lock();
10928
+ init_database();
10900
10929
  oauthStateStore = new Map;
10901
10930
  GOOGLE_SCOPES = {
10902
10931
  gmail: [
@@ -11150,10 +11179,9 @@ var exports_serve = {};
11150
11179
  __export(exports_serve, {
11151
11180
  startServer: () => startServer
11152
11181
  });
11153
- import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
11182
+ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
11154
11183
  import { join as join8, dirname as dirname4, extname, basename } from "path";
11155
11184
  import { fileURLToPath as fileURLToPath4 } from "url";
11156
- import { homedir as homedir6 } from "os";
11157
11185
  function logActivity(action, connector, detail) {
11158
11186
  activityLog.unshift({ action, connector, timestamp: Date.now(), detail });
11159
11187
  if (activityLog.length > MAX_ACTIVITY_LOG) {
@@ -11174,7 +11202,7 @@ function resolveDashboardDir() {
11174
11202
  }
11175
11203
  candidates.push(join8(process.cwd(), "dashboard", "dist"));
11176
11204
  for (const candidate of candidates) {
11177
- if (existsSync7(candidate))
11205
+ if (existsSync8(candidate))
11178
11206
  return candidate;
11179
11207
  }
11180
11208
  return join8(process.cwd(), "dashboard", "dist");
@@ -11262,7 +11290,7 @@ function oauthPage(type, title, message, hint, extra) {
11262
11290
  </body></html>`;
11263
11291
  }
11264
11292
  function serveStaticFile(filePath) {
11265
- if (!existsSync7(filePath))
11293
+ if (!existsSync8(filePath))
11266
11294
  return null;
11267
11295
  const ext = extname(filePath);
11268
11296
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -11286,7 +11314,7 @@ async function startServer(requestedPort, options) {
11286
11314
  const shouldOpen = options?.open ?? true;
11287
11315
  loadConnectorVersions();
11288
11316
  const dashboardDir = resolveDashboardDir();
11289
- const dashboardExists = existsSync7(dashboardDir);
11317
+ const dashboardExists = existsSync8(dashboardDir);
11290
11318
  if (!dashboardExists) {
11291
11319
  console.error(`
11292
11320
  Dashboard not found at: ${dashboardDir}`);
@@ -11440,7 +11468,7 @@ Dashboard not found at: ${dashboardDir}`);
11440
11468
  const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
11441
11469
  const limit = parseInt(url2.searchParams.get("limit") || "10", 10);
11442
11470
  const days = parseInt(url2.searchParams.get("days") || "7", 10);
11443
- const db = getDatabase2();
11471
+ const db = getDatabase3();
11444
11472
  const top = getTopConnectors2(limit, days, db);
11445
11473
  const promoted = new Set(getPromotedConnectors2(db));
11446
11474
  return json(top.map((t) => ({ ...t, promoted: promoted.has(t.connector) })), 200, port);
@@ -11451,12 +11479,12 @@ Dashboard not found at: ${dashboardDir}`);
11451
11479
  if (!getConnector(name))
11452
11480
  return json({ error: "Connector not found" }, 404, port);
11453
11481
  const { promoteConnector: promoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
11454
- promoteConnector2(name, getDatabase2());
11482
+ promoteConnector2(name, getDatabase3());
11455
11483
  return json({ success: true, connector: name }, 200, port);
11456
11484
  }
11457
11485
  if (promoteMatch && method === "DELETE") {
11458
11486
  const { demoteConnector: demoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
11459
- const removed = demoteConnector2(promoteMatch[1], getDatabase2());
11487
+ const removed = demoteConnector2(promoteMatch[1], getDatabase3());
11460
11488
  return json({ success: removed, connector: promoteMatch[1] }, 200, port);
11461
11489
  }
11462
11490
  if (path === "/api/llm" && method === "GET") {
@@ -11492,19 +11520,19 @@ Dashboard not found at: ${dashboardDir}`);
11492
11520
  }
11493
11521
  }
11494
11522
  if (path === "/api/jobs" && method === "GET") {
11495
- return json(listJobs(getDatabase2()), 200, port);
11523
+ return json(listJobs(getDatabase3()), 200, port);
11496
11524
  }
11497
11525
  if (path === "/api/jobs" && method === "POST") {
11498
11526
  const body = await req.json().catch(() => ({}));
11499
11527
  if (!body.name || !body.connector || !body.command || !body.cron)
11500
11528
  return json({ error: "name, connector, command, cron required" }, 400, port);
11501
- const job = createJob({ name: body.name, connector: body.connector, command: body.command, args: body.args ?? [], cron: body.cron, strip: !!body.strip }, getDatabase2());
11529
+ const job = createJob({ name: body.name, connector: body.connector, command: body.command, args: body.args ?? [], cron: body.cron, strip: !!body.strip }, getDatabase3());
11502
11530
  return json(job, 201, port);
11503
11531
  }
11504
11532
  const jobMatch = path.match(/^\/api\/jobs\/([^/]+)$/);
11505
11533
  if (jobMatch) {
11506
- const db = getDatabase2();
11507
- const job = getJobByName(jobMatch[1]) ?? getDatabase2().query("SELECT * FROM connector_jobs WHERE id = ?").get(jobMatch[1]);
11534
+ const db = getDatabase3();
11535
+ const job = getJobByName(jobMatch[1]) ?? getDatabase3().query("SELECT * FROM connector_jobs WHERE id = ?").get(jobMatch[1]);
11508
11536
  if (!job && method !== "DELETE")
11509
11537
  return json({ error: "Job not found" }, 404, port);
11510
11538
  if (method === "GET")
@@ -11525,7 +11553,7 @@ Dashboard not found at: ${dashboardDir}`);
11525
11553
  }
11526
11554
  const jobRunMatch = path.match(/^\/api\/jobs\/([^/]+)\/run$/);
11527
11555
  if (jobRunMatch && method === "POST") {
11528
- const db = getDatabase2();
11556
+ const db = getDatabase3();
11529
11557
  const job = getJobByName(jobRunMatch[1], db);
11530
11558
  if (!job)
11531
11559
  return json({ error: "Job not found" }, 404, port);
@@ -11533,18 +11561,18 @@ Dashboard not found at: ${dashboardDir}`);
11533
11561
  return json(result, 200, port);
11534
11562
  }
11535
11563
  if (path === "/api/workflows" && method === "GET") {
11536
- return json(listWorkflows(getDatabase2()), 200, port);
11564
+ return json(listWorkflows(getDatabase3()), 200, port);
11537
11565
  }
11538
11566
  if (path === "/api/workflows" && method === "POST") {
11539
11567
  const body = await req.json().catch(() => ({}));
11540
11568
  if (!body.name || !body.steps)
11541
11569
  return json({ error: "name and steps required" }, 400, port);
11542
- const wf = createWorkflow({ name: body.name, steps: body.steps }, getDatabase2());
11570
+ const wf = createWorkflow({ name: body.name, steps: body.steps }, getDatabase3());
11543
11571
  return json(wf, 201, port);
11544
11572
  }
11545
11573
  const wfMatch = path.match(/^\/api\/workflows\/([^/]+)$/);
11546
11574
  if (wfMatch) {
11547
- const db = getDatabase2();
11575
+ const db = getDatabase3();
11548
11576
  const wf = getWorkflowByName(wfMatch[1], db);
11549
11577
  if (!wf)
11550
11578
  return json({ error: "Workflow not found" }, 404, port);
@@ -11557,7 +11585,7 @@ Dashboard not found at: ${dashboardDir}`);
11557
11585
  }
11558
11586
  const wfRunMatch = path.match(/^\/api\/workflows\/([^/]+)\/run$/);
11559
11587
  if (wfRunMatch && method === "POST") {
11560
- const wf = getWorkflowByName(wfRunMatch[1], getDatabase2());
11588
+ const wf = getWorkflowByName(wfRunMatch[1], getDatabase3());
11561
11589
  if (!wf)
11562
11590
  return json({ error: "Workflow not found" }, 404, port);
11563
11591
  const result = await runWorkflow(wf);
@@ -11603,10 +11631,10 @@ Dashboard not found at: ${dashboardDir}`);
11603
11631
  return json({ error: "Invalid connector name" }, 400, port);
11604
11632
  try {
11605
11633
  const profiles = listProfiles(name);
11606
- const configDir = join8(homedir6(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
11634
+ const configDir = join8(getConnectorsHome(), name.startsWith("connect-") ? name : `connect-${name}`);
11607
11635
  const currentProfileFile = join8(configDir, "current_profile");
11608
11636
  let current = "default";
11609
- if (existsSync7(currentProfileFile)) {
11637
+ if (existsSync8(currentProfileFile)) {
11610
11638
  try {
11611
11639
  current = readFileSync5(currentProfileFile, "utf-8").trim() || "default";
11612
11640
  } catch {}
@@ -11655,19 +11683,19 @@ Dashboard not found at: ${dashboardDir}`);
11655
11683
  }
11656
11684
  if (path === "/api/export" && method === "GET") {
11657
11685
  try {
11658
- const connectDir = join8(homedir6(), ".connectors");
11686
+ const connectDir = getConnectorsHome();
11659
11687
  const result = {};
11660
- if (existsSync7(connectDir)) {
11661
- const entries = readdirSync3(connectDir, { withFileTypes: true });
11688
+ if (existsSync8(connectDir)) {
11689
+ const entries = readdirSync4(connectDir, { withFileTypes: true });
11662
11690
  for (const entry of entries) {
11663
11691
  if (!entry.isDirectory() || !entry.name.startsWith("connect-"))
11664
11692
  continue;
11665
11693
  const connectorName = entry.name.replace(/^connect-/, "");
11666
11694
  const profilesDir = join8(connectDir, entry.name, "profiles");
11667
- if (!existsSync7(profilesDir))
11695
+ if (!existsSync8(profilesDir))
11668
11696
  continue;
11669
11697
  const profiles = {};
11670
- const profileEntries = readdirSync3(profilesDir, { withFileTypes: true });
11698
+ const profileEntries = readdirSync4(profilesDir, { withFileTypes: true });
11671
11699
  for (const pEntry of profileEntries) {
11672
11700
  if (pEntry.isFile() && pEntry.name.endsWith(".json")) {
11673
11701
  const profileName = basename(pEntry.name, ".json");
@@ -11678,7 +11706,7 @@ Dashboard not found at: ${dashboardDir}`);
11678
11706
  }
11679
11707
  if (pEntry.isDirectory()) {
11680
11708
  const configPath = join8(profilesDir, pEntry.name, "config.json");
11681
- if (existsSync7(configPath)) {
11709
+ if (existsSync8(configPath)) {
11682
11710
  try {
11683
11711
  const config = JSON.parse(readFileSync5(configPath, "utf-8"));
11684
11712
  profiles[pEntry.name] = config;
@@ -11715,7 +11743,7 @@ Dashboard not found at: ${dashboardDir}`);
11715
11743
  return json({ error: "Invalid import format: missing 'connectors' object" }, 400, port);
11716
11744
  }
11717
11745
  let imported = 0;
11718
- const connectDir = join8(homedir6(), ".connectors");
11746
+ const connectDir = getConnectorsHome();
11719
11747
  for (const [connectorName, data] of Object.entries(body.connectors)) {
11720
11748
  if (!isValidConnectorName(connectorName))
11721
11749
  continue;
@@ -11803,8 +11831,8 @@ Dashboard not found at: ${dashboardDir}`);
11803
11831
  process.on("SIGINT", shutdown);
11804
11832
  process.on("SIGTERM", shutdown);
11805
11833
  const { startScheduler: startScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), exports_scheduler));
11806
- const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_database(), exports_database));
11807
- startScheduler2(getDatabase2());
11834
+ const { getDatabase: getDatabase3 } = await Promise.resolve().then(() => (init_database(), exports_database));
11835
+ startScheduler2(getDatabase3());
11808
11836
  const url = `http://localhost:${port}`;
11809
11837
  console.log(`Connectors Dashboard running at ${url}`);
11810
11838
  if (shouldOpen) {
@@ -11825,6 +11853,7 @@ var init_serve = __esm(() => {
11825
11853
  init_workflows();
11826
11854
  init_scheduler();
11827
11855
  init_workflow_runner();
11856
+ init_database();
11828
11857
  init_registry();
11829
11858
  init_installer();
11830
11859
  init_auth();
@@ -11872,7 +11901,7 @@ import chalk2 from "chalk";
11872
11901
  // package.json
11873
11902
  var package_default = {
11874
11903
  name: "@hasna/connectors",
11875
- version: "1.3.4",
11904
+ version: "1.3.8",
11876
11905
  description: "Open source connector library - Install API connectors with a single command",
11877
11906
  type: "module",
11878
11907
  bin: {
@@ -13437,8 +13466,9 @@ function App({ initialConnectors, overwrite = false }) {
13437
13466
  init_registry();
13438
13467
  init_installer();
13439
13468
  init_auth();
13440
- import { readdirSync as readdirSync4, existsSync as existsSync8, statSync as statSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync7 } from "fs";
13441
- import { homedir as homedir7 } from "os";
13469
+ init_database();
13470
+ import { readdirSync as readdirSync5, existsSync as existsSync9, statSync as statSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync7 } from "fs";
13471
+ import { homedir as homedir2 } from "os";
13442
13472
  import { join as join9, relative } from "path";
13443
13473
 
13444
13474
  // src/lib/test-endpoints.ts
@@ -13640,17 +13670,17 @@ var TEST_ENDPOINTS = {
13640
13670
  import { createInterface } from "readline";
13641
13671
 
13642
13672
  // src/lib/runner.ts
13643
- import { existsSync as existsSync6 } from "fs";
13673
+ import { existsSync as existsSync7 } from "fs";
13644
13674
  import { join as join7, dirname as dirname3 } from "path";
13645
13675
  import { fileURLToPath as fileURLToPath3 } from "url";
13646
13676
  import { spawn as spawn3 } from "child_process";
13647
13677
  var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
13648
13678
  function resolveConnectorsDir2() {
13649
13679
  const fromBin = join7(__dirname3, "..", "connectors");
13650
- if (existsSync6(fromBin))
13680
+ if (existsSync7(fromBin))
13651
13681
  return fromBin;
13652
13682
  const fromSrc = join7(__dirname3, "..", "..", "connectors");
13653
- if (existsSync6(fromSrc))
13683
+ if (existsSync7(fromSrc))
13654
13684
  return fromSrc;
13655
13685
  return fromBin;
13656
13686
  }
@@ -13692,7 +13722,7 @@ function getConnectorCliPath(name) {
13692
13722
  const safeName = name.replace(/[^a-z0-9-]/g, "");
13693
13723
  const connectorDir = join7(CONNECTORS_DIR2, `connect-${safeName}`);
13694
13724
  const cliPath = join7(connectorDir, "src", "cli", "index.ts");
13695
- if (existsSync6(cliPath))
13725
+ if (existsSync7(cliPath))
13696
13726
  return cliPath;
13697
13727
  return null;
13698
13728
  }
@@ -13804,9 +13834,9 @@ Run 'connectors --help' for full usage.`);
13804
13834
  });
13805
13835
  function listFilesRecursive(dir, base = dir) {
13806
13836
  const files = [];
13807
- for (const entry of readdirSync4(dir)) {
13837
+ for (const entry of readdirSync5(dir)) {
13808
13838
  const fullPath = join9(dir, entry);
13809
- if (statSync4(fullPath).isDirectory()) {
13839
+ if (statSync5(fullPath).isDirectory()) {
13810
13840
  files.push(...listFilesRecursive(fullPath, base));
13811
13841
  } else {
13812
13842
  files.push(relative(base, fullPath));
@@ -14386,7 +14416,7 @@ Updating ${toUpdate.length} connector(s)...
14386
14416
  });
14387
14417
  program2.command("status").option("--json", "Output as JSON", false).description("Show auth status of all configured connectors (project + global)").action((options) => {
14388
14418
  const installed = getInstalledConnectors();
14389
- const configDir = join9(homedir7(), ".connectors");
14419
+ const configDir = getConnectorsHome();
14390
14420
  const seen = new Set;
14391
14421
  const allStatuses = [];
14392
14422
  function buildStatusEntry(name, source) {
@@ -14394,7 +14424,7 @@ program2.command("status").option("--json", "Output as JSON", false).description
14394
14424
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
14395
14425
  const currentProfileFile = join9(configDir, connectorName, "current_profile");
14396
14426
  let profile = "default";
14397
- if (existsSync8(currentProfileFile)) {
14427
+ if (existsSync9(currentProfileFile)) {
14398
14428
  try {
14399
14429
  profile = readFileSync6(currentProfileFile, "utf-8").trim() || "default";
14400
14430
  } catch {}
@@ -14432,13 +14462,13 @@ program2.command("status").option("--json", "Output as JSON", false).description
14432
14462
  seen.add(name);
14433
14463
  allStatuses.push(buildStatusEntry(name, "project"));
14434
14464
  }
14435
- if (existsSync8(configDir)) {
14465
+ if (existsSync9(configDir)) {
14436
14466
  try {
14437
- const globalDirs = readdirSync4(configDir).filter((f) => {
14467
+ const globalDirs = readdirSync5(configDir).filter((f) => {
14438
14468
  if (!f.startsWith("connect-"))
14439
14469
  return false;
14440
14470
  try {
14441
- return statSync4(join9(configDir, f)).isDirectory();
14471
+ return statSync5(join9(configDir, f)).isDirectory();
14442
14472
  } catch {
14443
14473
  return false;
14444
14474
  }
@@ -14803,15 +14833,15 @@ program2.command("init").option("--json", "Output presets and suggestions as JSO
14803
14833
  { key: "commerce", emoji: "\uD83D\uDCB3", label: "Commerce", connectors: ["stripe", "shopify", "paypal", "revolut", "mercury"], description: "Commerce and finance" },
14804
14834
  { key: "google", emoji: "\uD83D\uDCC1", label: "Google Workspace", connectors: ["gmail", "googledrive", "googlecalendar", "googledocs", "googlesheets"], description: "Google Workspace suite" }
14805
14835
  ];
14806
- const connectorsHome = join9(homedir7(), ".connectors");
14836
+ const connectorsHome = getConnectorsHome();
14807
14837
  let configuredCount = 0;
14808
14838
  const configuredNames = [];
14809
14839
  try {
14810
- if (existsSync8(connectorsHome)) {
14811
- const entries = readdirSync4(connectorsHome).filter((e) => e.startsWith("connect-") && statSync4(join9(connectorsHome, e)).isDirectory());
14840
+ if (existsSync9(connectorsHome)) {
14841
+ const entries = readdirSync5(connectorsHome).filter((e) => e.startsWith("connect-") && statSync5(join9(connectorsHome, e)).isDirectory());
14812
14842
  for (const entry of entries) {
14813
14843
  const profilesDir = join9(connectorsHome, entry, "profiles");
14814
- if (existsSync8(profilesDir)) {
14844
+ if (existsSync9(profilesDir)) {
14815
14845
  configuredCount++;
14816
14846
  configuredNames.push(entry.replace(/^connect-/, ""));
14817
14847
  }
@@ -14909,42 +14939,42 @@ function redactSecrets(obj) {
14909
14939
  return obj;
14910
14940
  }
14911
14941
  program2.command("export").option("-o, --output <file>", "Write to file instead of stdout").option("--include-secrets", "Include secrets in plaintext (dangerous \u2014 use only for backup/restore)").description("Export all connector credentials as JSON backup").action((options) => {
14912
- const connectDir = join9(homedir7(), ".connectors");
14942
+ const connectDir = getConnectorsHome();
14913
14943
  const result = {};
14914
- if (existsSync8(connectDir)) {
14915
- for (const entry of readdirSync4(connectDir)) {
14944
+ if (existsSync9(connectDir)) {
14945
+ for (const entry of readdirSync5(connectDir)) {
14916
14946
  const entryPath = join9(connectDir, entry);
14917
- if (!statSync4(entryPath).isDirectory() || !entry.startsWith("connect-"))
14947
+ if (!statSync5(entryPath).isDirectory() || !entry.startsWith("connect-"))
14918
14948
  continue;
14919
14949
  const connectorName = entry.replace(/^connect-/, "");
14920
14950
  let credentials = undefined;
14921
14951
  const credentialsPath = join9(entryPath, "credentials.json");
14922
- if (existsSync8(credentialsPath)) {
14952
+ if (existsSync9(credentialsPath)) {
14923
14953
  try {
14924
14954
  credentials = JSON.parse(readFileSync6(credentialsPath, "utf-8"));
14925
14955
  } catch {}
14926
14956
  }
14927
14957
  const profilesDir = join9(entryPath, "profiles");
14928
- if (!existsSync8(profilesDir) && !credentials)
14958
+ if (!existsSync9(profilesDir) && !credentials)
14929
14959
  continue;
14930
14960
  const profiles = {};
14931
- if (existsSync8(profilesDir)) {
14932
- for (const pEntry of readdirSync4(profilesDir)) {
14961
+ if (existsSync9(profilesDir)) {
14962
+ for (const pEntry of readdirSync5(profilesDir)) {
14933
14963
  const pPath = join9(profilesDir, pEntry);
14934
- if (statSync4(pPath).isFile() && pEntry.endsWith(".json")) {
14964
+ if (statSync5(pPath).isFile() && pEntry.endsWith(".json")) {
14935
14965
  try {
14936
14966
  profiles[pEntry.replace(/\.json$/, "")] = JSON.parse(readFileSync6(pPath, "utf-8"));
14937
14967
  } catch {}
14938
- } else if (statSync4(pPath).isDirectory()) {
14968
+ } else if (statSync5(pPath).isDirectory()) {
14939
14969
  const configPath = join9(pPath, "config.json");
14940
14970
  const tokensPath = join9(pPath, "tokens.json");
14941
14971
  let merged = {};
14942
- if (existsSync8(configPath)) {
14972
+ if (existsSync9(configPath)) {
14943
14973
  try {
14944
14974
  merged = { ...merged, ...JSON.parse(readFileSync6(configPath, "utf-8")) };
14945
14975
  } catch {}
14946
14976
  }
14947
- if (existsSync8(tokensPath)) {
14977
+ if (existsSync9(tokensPath)) {
14948
14978
  try {
14949
14979
  merged = { ...merged, ...JSON.parse(readFileSync6(tokensPath, "utf-8")) };
14950
14980
  } catch {}
@@ -14981,7 +15011,7 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
14981
15011
  chunks.push(chunk.toString());
14982
15012
  raw = chunks.join("");
14983
15013
  } else {
14984
- if (!existsSync8(file)) {
15014
+ if (!existsSync9(file)) {
14985
15015
  if (options.json) {
14986
15016
  console.log(JSON.stringify({ error: `File not found: ${file}` }));
14987
15017
  } else {
@@ -15013,7 +15043,7 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
15013
15043
  process.exit(1);
15014
15044
  return;
15015
15045
  }
15016
- const connectDir = join9(homedir7(), ".connectors");
15046
+ const connectDir = getConnectorsHome();
15017
15047
  let imported = 0;
15018
15048
  for (const [connectorName, connData] of Object.entries(data.connectors)) {
15019
15049
  if (!/^[a-z0-9-]+$/.test(connectorName))
@@ -15041,10 +15071,10 @@ program2.command("import").argument("<file>", "JSON backup file to import (use -
15041
15071
  console.log(chalk2.green(`\u2713 Imported ${imported} profile(s)`));
15042
15072
  }
15043
15073
  });
15044
- program2.command("auth-import").option("--json", "Output as JSON", false).option("-d, --dry-run", "Preview what would be imported without copying", false).option("--force", "Overwrite existing files in ~/.connectors/", false).description("Migrate auth tokens from ~/.connect/ to ~/.connectors/").action((options) => {
15045
- const oldBase = join9(homedir7(), ".connect");
15046
- const newBase = join9(homedir7(), ".connectors");
15047
- if (!existsSync8(oldBase)) {
15074
+ program2.command("auth-import").option("--json", "Output as JSON", false).option("-d, --dry-run", "Preview what would be imported without copying", false).option("--force", "Overwrite existing files in ~/.hasna/connectors/", false).description("Migrate auth tokens from ~/.connect/ to ~/.hasna/connectors/").action((options) => {
15075
+ const oldBase = join9(homedir2(), ".connect");
15076
+ const newBase = getConnectorsHome();
15077
+ if (!existsSync9(oldBase)) {
15048
15078
  if (options.json) {
15049
15079
  console.log(JSON.stringify({ imported: [], skipped: [], error: null, message: "No ~/.connect/ directory found" }));
15050
15080
  } else {
@@ -15052,11 +15082,11 @@ program2.command("auth-import").option("--json", "Output as JSON", false).option
15052
15082
  }
15053
15083
  return;
15054
15084
  }
15055
- const entries = readdirSync4(oldBase).filter((name) => {
15085
+ const entries = readdirSync5(oldBase).filter((name) => {
15056
15086
  if (!name.startsWith("connect-"))
15057
15087
  return false;
15058
15088
  try {
15059
- return statSync4(join9(oldBase, name)).isDirectory();
15089
+ return statSync5(join9(oldBase, name)).isDirectory();
15060
15090
  } catch {
15061
15091
  return false;
15062
15092
  }
@@ -15086,7 +15116,7 @@ program2.command("auth-import").option("--json", "Output as JSON", false).option
15086
15116
  for (const relFile of authFiles) {
15087
15117
  const srcPath = join9(oldDir, relFile);
15088
15118
  const destPath = join9(newDir, relFile);
15089
- if (existsSync8(destPath) && !options.force) {
15119
+ if (existsSync9(destPath) && !options.force) {
15090
15120
  skippedFiles.push(relFile);
15091
15121
  continue;
15092
15122
  }
@@ -15367,7 +15397,7 @@ Available presets:
15367
15397
  `));
15368
15398
  });
15369
15399
  program2.command("whoami").option("--json", "Output as JSON", false).description("Show current setup: config dir, installed connectors, auth status").action((options) => {
15370
- const configDir = join9(homedir7(), ".connectors");
15400
+ const configDir = getConnectorsHome();
15371
15401
  const installed = getInstalledConnectors();
15372
15402
  const version = "0.3.1";
15373
15403
  let configured = 0;
@@ -15384,20 +15414,20 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
15384
15414
  const connectorConfigDir = join9(configDir, name.startsWith("connect-") ? name : `connect-${name}`);
15385
15415
  const currentProfileFile = join9(connectorConfigDir, "current_profile");
15386
15416
  let profile = "default";
15387
- if (existsSync8(currentProfileFile)) {
15417
+ if (existsSync9(currentProfileFile)) {
15388
15418
  try {
15389
15419
  profile = readFileSync6(currentProfileFile, "utf-8").trim() || "default";
15390
15420
  } catch {}
15391
15421
  }
15392
15422
  connectorDetails.push({ name, configured: auth.configured, authType: auth.type, profile, source: "project" });
15393
15423
  }
15394
- if (existsSync8(configDir)) {
15424
+ if (existsSync9(configDir)) {
15395
15425
  try {
15396
- const globalDirs = readdirSync4(configDir).filter((f) => {
15426
+ const globalDirs = readdirSync5(configDir).filter((f) => {
15397
15427
  if (!f.startsWith("connect-"))
15398
15428
  return false;
15399
15429
  try {
15400
- return statSync4(join9(configDir, f)).isDirectory();
15430
+ return statSync5(join9(configDir, f)).isDirectory();
15401
15431
  } catch {
15402
15432
  return false;
15403
15433
  }
@@ -15413,7 +15443,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
15413
15443
  configured++;
15414
15444
  const currentProfileFile = join9(configDir, dir, "current_profile");
15415
15445
  let profile = "default";
15416
- if (existsSync8(currentProfileFile)) {
15446
+ if (existsSync9(currentProfileFile)) {
15417
15447
  try {
15418
15448
  profile = readFileSync6(currentProfileFile, "utf-8").trim() || "default";
15419
15449
  } catch {}
@@ -15426,7 +15456,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
15426
15456
  console.log(JSON.stringify({
15427
15457
  version,
15428
15458
  configDir,
15429
- configDirExists: existsSync8(configDir),
15459
+ configDirExists: existsSync9(configDir),
15430
15460
  installed: installed.length,
15431
15461
  configured,
15432
15462
  unconfigured,
@@ -15438,7 +15468,7 @@ program2.command("whoami").option("--json", "Output as JSON", false).description
15438
15468
  Connectors Setup
15439
15469
  `));
15440
15470
  console.log(` Version: ${chalk2.cyan(version)}`);
15441
- console.log(` Config: ${configDir}${existsSync8(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
15471
+ console.log(` Config: ${configDir}${existsSync9(configDir) ? "" : chalk2.dim(" (not created yet)")}`);
15442
15472
  console.log(` Installed: ${installed.length} connector${installed.length !== 1 ? "s" : ""}`);
15443
15473
  console.log(` Configured: ${chalk2.green(String(configured))} ready, ${unconfigured > 0 ? chalk2.red(String(unconfigured)) : chalk2.dim("0")} need auth`);
15444
15474
  const projectConnectors = connectorDetails.filter((c) => c.source === "project");
@@ -15528,16 +15558,16 @@ Testing connector credentials...
15528
15558
  }
15529
15559
  }
15530
15560
  if (!apiKey) {
15531
- const connectorConfigDir = join9(homedir7(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
15561
+ const connectorConfigDir = join9(getConnectorsHome(), name.startsWith("connect-") ? name : `connect-${name}`);
15532
15562
  let currentProfile = "default";
15533
15563
  const currentProfileFile = join9(connectorConfigDir, "current_profile");
15534
- if (existsSync8(currentProfileFile)) {
15564
+ if (existsSync9(currentProfileFile)) {
15535
15565
  try {
15536
15566
  currentProfile = readFileSync6(currentProfileFile, "utf-8").trim() || "default";
15537
15567
  } catch {}
15538
15568
  }
15539
15569
  const tokensFile = join9(connectorConfigDir, "profiles", currentProfile, "tokens.json");
15540
- if (existsSync8(tokensFile)) {
15570
+ if (existsSync9(tokensFile)) {
15541
15571
  try {
15542
15572
  const tokens = JSON.parse(readFileSync6(tokensFile, "utf-8"));
15543
15573
  const isExpired = tokens.expiresAt && Date.now() >= tokens.expiresAt - 60000;
@@ -15558,7 +15588,7 @@ Testing connector credentials...
15558
15588
  }
15559
15589
  if (!apiKey) {
15560
15590
  const profileFile = join9(connectorConfigDir, "profiles", `${currentProfile}.json`);
15561
- if (existsSync8(profileFile)) {
15591
+ if (existsSync9(profileFile)) {
15562
15592
  try {
15563
15593
  const config = JSON.parse(readFileSync6(profileFile, "utf-8"));
15564
15594
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);
@@ -15567,7 +15597,7 @@ Testing connector credentials...
15567
15597
  }
15568
15598
  if (!apiKey) {
15569
15599
  const profileDirConfig = join9(connectorConfigDir, "profiles", currentProfile, "config.json");
15570
- if (existsSync8(profileDirConfig)) {
15600
+ if (existsSync9(profileDirConfig)) {
15571
15601
  try {
15572
15602
  const config = JSON.parse(readFileSync6(profileDirConfig, "utf-8"));
15573
15603
  apiKey = Object.values(config).find((v) => typeof v === "string" && v.length > 0);