@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 +283 -253
- package/bin/mcp.js +241 -238
- package/bin/serve.js +106 -77
- package/dist/db/database.d.ts +6 -0
- package/dist/index.js +82 -46
- package/dist/lib/llm.d.ts +1 -1
- package/dist/lib/lock.d.ts +1 -1
- package/package.json +1 -1
package/bin/serve.js
CHANGED
|
@@ -23,12 +23,34 @@ __export(exports_database, {
|
|
|
23
23
|
shortUuid: () => shortUuid,
|
|
24
24
|
now: () => now,
|
|
25
25
|
getDatabase: () => getDatabase,
|
|
26
|
+
getConnectorsHome: () => getConnectorsHome,
|
|
26
27
|
closeDatabase: () => closeDatabase
|
|
27
28
|
});
|
|
28
29
|
import { Database } from "bun:sqlite";
|
|
29
30
|
import { join } from "path";
|
|
30
31
|
import { homedir } from "os";
|
|
31
|
-
import { mkdirSync } from "fs";
|
|
32
|
+
import { mkdirSync, existsSync, readdirSync, copyFileSync, statSync } from "fs";
|
|
33
|
+
function getConnectorsHome() {
|
|
34
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
35
|
+
const newDir = join(home, ".hasna", "connectors");
|
|
36
|
+
const oldDir = join(home, ".connectors");
|
|
37
|
+
if (existsSync(oldDir) && !existsSync(newDir)) {
|
|
38
|
+
mkdirSync(newDir, { recursive: true });
|
|
39
|
+
try {
|
|
40
|
+
for (const file of readdirSync(oldDir)) {
|
|
41
|
+
const oldPath = join(oldDir, file);
|
|
42
|
+
const newPath = join(newDir, file);
|
|
43
|
+
try {
|
|
44
|
+
if (statSync(oldPath).isFile()) {
|
|
45
|
+
copyFileSync(oldPath, newPath);
|
|
46
|
+
}
|
|
47
|
+
} catch {}
|
|
48
|
+
}
|
|
49
|
+
} catch {}
|
|
50
|
+
}
|
|
51
|
+
mkdirSync(newDir, { recursive: true });
|
|
52
|
+
return newDir;
|
|
53
|
+
}
|
|
32
54
|
function getDatabase(path) {
|
|
33
55
|
if (_db)
|
|
34
56
|
return _db;
|
|
@@ -143,20 +165,19 @@ function migrate(db) {
|
|
|
143
165
|
}
|
|
144
166
|
var DB_DIR, DB_PATH, _db = null;
|
|
145
167
|
var init_database = __esm(() => {
|
|
146
|
-
DB_DIR =
|
|
168
|
+
DB_DIR = getConnectorsHome();
|
|
147
169
|
DB_PATH = join(DB_DIR, "connectors.db");
|
|
148
170
|
});
|
|
149
171
|
|
|
150
172
|
// src/lib/llm.ts
|
|
151
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
173
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
152
174
|
import { join as join2 } from "path";
|
|
153
|
-
import { homedir as homedir2 } from "os";
|
|
154
175
|
function getLlmConfigPath() {
|
|
155
|
-
return join2(
|
|
176
|
+
return join2(getConnectorsHome(), "llm.json");
|
|
156
177
|
}
|
|
157
178
|
function getLlmConfig() {
|
|
158
179
|
const path = getLlmConfigPath();
|
|
159
|
-
if (!
|
|
180
|
+
if (!existsSync2(path))
|
|
160
181
|
return null;
|
|
161
182
|
try {
|
|
162
183
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
@@ -165,7 +186,7 @@ function getLlmConfig() {
|
|
|
165
186
|
}
|
|
166
187
|
}
|
|
167
188
|
function saveLlmConfig(config) {
|
|
168
|
-
const dir =
|
|
189
|
+
const dir = getConnectorsHome();
|
|
169
190
|
mkdirSync2(dir, { recursive: true });
|
|
170
191
|
writeFileSync(getLlmConfigPath(), JSON.stringify(config, null, 2));
|
|
171
192
|
}
|
|
@@ -252,6 +273,7 @@ class LLMClient {
|
|
|
252
273
|
}
|
|
253
274
|
var PROVIDER_BASE_URLS;
|
|
254
275
|
var init_llm = __esm(() => {
|
|
276
|
+
init_database();
|
|
255
277
|
PROVIDER_BASE_URLS = {
|
|
256
278
|
cerebras: "https://api.cerebras.ai/v1",
|
|
257
279
|
groq: "https://api.groq.com/openai/v1",
|
|
@@ -558,7 +580,7 @@ var init_promotions = __esm(() => {
|
|
|
558
580
|
});
|
|
559
581
|
|
|
560
582
|
// src/server/serve.ts
|
|
561
|
-
import { existsSync as
|
|
583
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
562
584
|
|
|
563
585
|
// src/db/agents.ts
|
|
564
586
|
init_database();
|
|
@@ -787,12 +809,12 @@ async function runWorkflow(workflow) {
|
|
|
787
809
|
}
|
|
788
810
|
|
|
789
811
|
// src/server/serve.ts
|
|
812
|
+
init_database();
|
|
790
813
|
import { join as join7, dirname as dirname3, extname, basename } from "path";
|
|
791
814
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
792
|
-
import { homedir as homedir6 } from "os";
|
|
793
815
|
|
|
794
816
|
// src/lib/registry.ts
|
|
795
|
-
import { existsSync as
|
|
817
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
796
818
|
import { join as join3, dirname } from "path";
|
|
797
819
|
import { fileURLToPath } from "url";
|
|
798
820
|
var CONNECTORS = [
|
|
@@ -1181,6 +1203,13 @@ var CONNECTORS = [
|
|
|
1181
1203
|
category: "Data & Analytics",
|
|
1182
1204
|
tags: ["weather", "data"]
|
|
1183
1205
|
},
|
|
1206
|
+
{
|
|
1207
|
+
name: "arxiv",
|
|
1208
|
+
displayName: "arXiv",
|
|
1209
|
+
description: "Research paper search and retrieval",
|
|
1210
|
+
category: "Data & Analytics",
|
|
1211
|
+
tags: ["research", "papers", "academic"]
|
|
1212
|
+
},
|
|
1184
1213
|
{
|
|
1185
1214
|
name: "brandsight",
|
|
1186
1215
|
displayName: "Brandsight",
|
|
@@ -6704,13 +6733,13 @@ function loadConnectorVersions() {
|
|
|
6704
6733
|
join3(thisDir, "..", "connectors"),
|
|
6705
6734
|
join3(thisDir, "..", "..", "connectors")
|
|
6706
6735
|
];
|
|
6707
|
-
const connectorsDir = candidates.find((d) =>
|
|
6736
|
+
const connectorsDir = candidates.find((d) => existsSync3(d));
|
|
6708
6737
|
if (!connectorsDir)
|
|
6709
6738
|
return;
|
|
6710
6739
|
for (const connector of CONNECTORS) {
|
|
6711
6740
|
try {
|
|
6712
6741
|
const pkgPath = join3(connectorsDir, `connect-${connector.name}`, "package.json");
|
|
6713
|
-
if (
|
|
6742
|
+
if (existsSync3(pkgPath)) {
|
|
6714
6743
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
6715
6744
|
connector.version = pkg.version || "0.0.0";
|
|
6716
6745
|
}
|
|
@@ -6719,17 +6748,17 @@ function loadConnectorVersions() {
|
|
|
6719
6748
|
}
|
|
6720
6749
|
|
|
6721
6750
|
// src/lib/installer.ts
|
|
6722
|
-
|
|
6723
|
-
import {
|
|
6751
|
+
init_database();
|
|
6752
|
+
import { existsSync as existsSync4, cpSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync2, statSync as statSync2, rmSync } from "fs";
|
|
6724
6753
|
import { join as join4, dirname as dirname2 } from "path";
|
|
6725
6754
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6726
6755
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
6727
6756
|
function resolveConnectorsDir() {
|
|
6728
6757
|
const fromBin = join4(__dirname2, "..", "connectors");
|
|
6729
|
-
if (
|
|
6758
|
+
if (existsSync4(fromBin))
|
|
6730
6759
|
return fromBin;
|
|
6731
6760
|
const fromSrc = join4(__dirname2, "..", "..", "connectors");
|
|
6732
|
-
if (
|
|
6761
|
+
if (existsSync4(fromSrc))
|
|
6733
6762
|
return fromSrc;
|
|
6734
6763
|
return fromBin;
|
|
6735
6764
|
}
|
|
@@ -6751,14 +6780,14 @@ function installConnector(name, options = {}) {
|
|
|
6751
6780
|
const sourcePath = getConnectorPath(name);
|
|
6752
6781
|
const destDir = join4(targetDir, ".connectors");
|
|
6753
6782
|
const destPath = join4(destDir, connectorName);
|
|
6754
|
-
if (!
|
|
6783
|
+
if (!existsSync4(sourcePath)) {
|
|
6755
6784
|
return {
|
|
6756
6785
|
connector: name,
|
|
6757
6786
|
success: false,
|
|
6758
6787
|
error: `Connector '${name}' not found`
|
|
6759
6788
|
};
|
|
6760
6789
|
}
|
|
6761
|
-
if (
|
|
6790
|
+
if (existsSync4(destPath) && !overwrite) {
|
|
6762
6791
|
return {
|
|
6763
6792
|
connector: name,
|
|
6764
6793
|
success: false,
|
|
@@ -6767,21 +6796,21 @@ function installConnector(name, options = {}) {
|
|
|
6767
6796
|
};
|
|
6768
6797
|
}
|
|
6769
6798
|
try {
|
|
6770
|
-
if (!
|
|
6799
|
+
if (!existsSync4(destDir)) {
|
|
6771
6800
|
mkdirSync3(destDir, { recursive: true });
|
|
6772
6801
|
}
|
|
6773
6802
|
cpSync(sourcePath, destPath, { recursive: true });
|
|
6774
|
-
const homeCredDir = join4(
|
|
6775
|
-
if (
|
|
6803
|
+
const homeCredDir = join4(getConnectorsHome(), connectorName);
|
|
6804
|
+
if (existsSync4(homeCredDir)) {
|
|
6776
6805
|
const filesToCopy = ["credentials.json", "current_profile"];
|
|
6777
6806
|
for (const file of filesToCopy) {
|
|
6778
6807
|
const src = join4(homeCredDir, file);
|
|
6779
|
-
if (
|
|
6808
|
+
if (existsSync4(src)) {
|
|
6780
6809
|
cpSync(src, join4(destPath, file));
|
|
6781
6810
|
}
|
|
6782
6811
|
}
|
|
6783
6812
|
const profilesDir = join4(homeCredDir, "profiles");
|
|
6784
|
-
if (
|
|
6813
|
+
if (existsSync4(profilesDir)) {
|
|
6785
6814
|
cpSync(profilesDir, join4(destPath, "profiles"), { recursive: true });
|
|
6786
6815
|
}
|
|
6787
6816
|
}
|
|
@@ -6801,7 +6830,7 @@ function installConnector(name, options = {}) {
|
|
|
6801
6830
|
}
|
|
6802
6831
|
function updateConnectorsIndex(connectorsDir) {
|
|
6803
6832
|
const indexPath = join4(connectorsDir, "index.ts");
|
|
6804
|
-
const connectors =
|
|
6833
|
+
const connectors = readdirSync2(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
|
|
6805
6834
|
const exports = connectors.map((c) => {
|
|
6806
6835
|
const name = c.replace("connect-", "");
|
|
6807
6836
|
return `export * as ${name} from './${c}/src/index.js';`;
|
|
@@ -6818,18 +6847,18 @@ ${exports}
|
|
|
6818
6847
|
}
|
|
6819
6848
|
function getInstalledConnectors(targetDir = process.cwd()) {
|
|
6820
6849
|
const connectorsDir = join4(targetDir, ".connectors");
|
|
6821
|
-
if (!
|
|
6850
|
+
if (!existsSync4(connectorsDir)) {
|
|
6822
6851
|
return [];
|
|
6823
6852
|
}
|
|
6824
|
-
return
|
|
6853
|
+
return readdirSync2(connectorsDir).filter((f) => {
|
|
6825
6854
|
const fullPath = join4(connectorsDir, f);
|
|
6826
|
-
return f.startsWith("connect-") &&
|
|
6855
|
+
return f.startsWith("connect-") && statSync2(fullPath).isDirectory();
|
|
6827
6856
|
}).map((f) => f.replace("connect-", ""));
|
|
6828
6857
|
}
|
|
6829
6858
|
function getConnectorDocs(name) {
|
|
6830
6859
|
const connectorPath = getConnectorPath(name);
|
|
6831
6860
|
const claudeMdPath = join4(connectorPath, "CLAUDE.md");
|
|
6832
|
-
if (!
|
|
6861
|
+
if (!existsSync4(claudeMdPath))
|
|
6833
6862
|
return null;
|
|
6834
6863
|
const raw = readFileSync3(claudeMdPath, "utf-8");
|
|
6835
6864
|
return {
|
|
@@ -6872,7 +6901,7 @@ function removeConnector(name, targetDir = process.cwd()) {
|
|
|
6872
6901
|
const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
|
|
6873
6902
|
const connectorsDir = join4(targetDir, ".connectors");
|
|
6874
6903
|
const connectorPath = join4(connectorsDir, connectorName);
|
|
6875
|
-
if (!
|
|
6904
|
+
if (!existsSync4(connectorPath)) {
|
|
6876
6905
|
return false;
|
|
6877
6906
|
}
|
|
6878
6907
|
rmSync(connectorPath, { recursive: true });
|
|
@@ -6881,15 +6910,14 @@ function removeConnector(name, targetDir = process.cwd()) {
|
|
|
6881
6910
|
}
|
|
6882
6911
|
|
|
6883
6912
|
// src/server/auth.ts
|
|
6884
|
-
import { existsSync as
|
|
6913
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync3, rmSync as rmSync2, statSync as statSync4 } from "fs";
|
|
6885
6914
|
import { randomBytes } from "crypto";
|
|
6886
|
-
import { homedir as homedir5 } from "os";
|
|
6887
6915
|
import { join as join6 } from "path";
|
|
6888
6916
|
|
|
6889
6917
|
// src/lib/lock.ts
|
|
6890
|
-
|
|
6918
|
+
init_database();
|
|
6919
|
+
import { openSync, closeSync, unlinkSync, existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
6891
6920
|
import { join as join5 } from "path";
|
|
6892
|
-
import { homedir as homedir4 } from "os";
|
|
6893
6921
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
6894
6922
|
var LOCK_TIMEOUT_MS = 5000;
|
|
6895
6923
|
var LOCK_RETRY_MS = 100;
|
|
@@ -6904,20 +6932,20 @@ class LockTimeoutError extends Error {
|
|
|
6904
6932
|
}
|
|
6905
6933
|
}
|
|
6906
6934
|
function lockPath(connector) {
|
|
6907
|
-
const dir = join5(
|
|
6935
|
+
const dir = join5(getConnectorsHome(), `connect-${connector}`);
|
|
6908
6936
|
mkdirSync4(dir, { recursive: true });
|
|
6909
6937
|
return join5(dir, ".write.lock");
|
|
6910
6938
|
}
|
|
6911
6939
|
function isStale(path) {
|
|
6912
6940
|
try {
|
|
6913
|
-
const stat =
|
|
6941
|
+
const stat = statSync3(path);
|
|
6914
6942
|
return Date.now() - stat.mtimeMs > STALE_LOCK_MS;
|
|
6915
6943
|
} catch {
|
|
6916
6944
|
return false;
|
|
6917
6945
|
}
|
|
6918
6946
|
}
|
|
6919
6947
|
function tryAcquire(path) {
|
|
6920
|
-
if (
|
|
6948
|
+
if (existsSync5(path) && isStale(path)) {
|
|
6921
6949
|
try {
|
|
6922
6950
|
unlinkSync(path);
|
|
6923
6951
|
} catch {}
|
|
@@ -6954,6 +6982,7 @@ async function withWriteLock(connector, fn) {
|
|
|
6954
6982
|
}
|
|
6955
6983
|
|
|
6956
6984
|
// src/server/auth.ts
|
|
6985
|
+
init_database();
|
|
6957
6986
|
var FETCH_TIMEOUT = 1e4;
|
|
6958
6987
|
var oauthStateStore = new Map;
|
|
6959
6988
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
@@ -7009,12 +7038,12 @@ function getAuthType(name) {
|
|
|
7009
7038
|
}
|
|
7010
7039
|
function getConnectorConfigDir(name) {
|
|
7011
7040
|
const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
|
|
7012
|
-
return join6(
|
|
7041
|
+
return join6(getConnectorsHome(), connectorName);
|
|
7013
7042
|
}
|
|
7014
7043
|
function getCurrentProfile(name) {
|
|
7015
7044
|
const configDir = getConnectorConfigDir(name);
|
|
7016
7045
|
const currentProfileFile = join6(configDir, "current_profile");
|
|
7017
|
-
if (
|
|
7046
|
+
if (existsSync6(currentProfileFile)) {
|
|
7018
7047
|
try {
|
|
7019
7048
|
return readFileSync4(currentProfileFile, "utf-8").trim() || "default";
|
|
7020
7049
|
} catch {
|
|
@@ -7029,13 +7058,13 @@ function loadProfileConfig(name) {
|
|
|
7029
7058
|
let flatConfig = {};
|
|
7030
7059
|
let dirConfig = {};
|
|
7031
7060
|
const profileFile = join6(configDir, "profiles", `${profile}.json`);
|
|
7032
|
-
if (
|
|
7061
|
+
if (existsSync6(profileFile)) {
|
|
7033
7062
|
try {
|
|
7034
7063
|
flatConfig = JSON.parse(readFileSync4(profileFile, "utf-8"));
|
|
7035
7064
|
} catch {}
|
|
7036
7065
|
}
|
|
7037
7066
|
const profileDirConfig = join6(configDir, "profiles", profile, "config.json");
|
|
7038
|
-
if (
|
|
7067
|
+
if (existsSync6(profileDirConfig)) {
|
|
7039
7068
|
try {
|
|
7040
7069
|
dirConfig = JSON.parse(readFileSync4(profileDirConfig, "utf-8"));
|
|
7041
7070
|
} catch {}
|
|
@@ -7049,7 +7078,7 @@ function loadTokens(name) {
|
|
|
7049
7078
|
const configDir = getConnectorConfigDir(name);
|
|
7050
7079
|
const profile = getCurrentProfile(name);
|
|
7051
7080
|
const tokensFile = join6(configDir, "profiles", profile, "tokens.json");
|
|
7052
|
-
if (
|
|
7081
|
+
if (existsSync6(tokensFile)) {
|
|
7053
7082
|
try {
|
|
7054
7083
|
return JSON.parse(readFileSync4(tokensFile, "utf-8"));
|
|
7055
7084
|
} catch {
|
|
@@ -7117,7 +7146,7 @@ function _saveApiKey(name, key, field) {
|
|
|
7117
7146
|
const credentialsFile = join6(configDir, "credentials.json");
|
|
7118
7147
|
mkdirSync5(configDir, { recursive: true });
|
|
7119
7148
|
let creds = {};
|
|
7120
|
-
if (
|
|
7149
|
+
if (existsSync6(credentialsFile)) {
|
|
7121
7150
|
try {
|
|
7122
7151
|
creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
|
|
7123
7152
|
} catch {}
|
|
@@ -7128,7 +7157,7 @@ function _saveApiKey(name, key, field) {
|
|
|
7128
7157
|
}
|
|
7129
7158
|
const profileFile = join6(configDir, "profiles", `${profile}.json`);
|
|
7130
7159
|
const profileDir = join6(configDir, "profiles", profile);
|
|
7131
|
-
if (
|
|
7160
|
+
if (existsSync6(profileFile)) {
|
|
7132
7161
|
let config = {};
|
|
7133
7162
|
try {
|
|
7134
7163
|
config = JSON.parse(readFileSync4(profileFile, "utf-8"));
|
|
@@ -7137,10 +7166,10 @@ function _saveApiKey(name, key, field) {
|
|
|
7137
7166
|
writeFileSync3(profileFile, JSON.stringify(config, null, 2));
|
|
7138
7167
|
return;
|
|
7139
7168
|
}
|
|
7140
|
-
if (
|
|
7169
|
+
if (existsSync6(profileDir)) {
|
|
7141
7170
|
const configFile = join6(profileDir, "config.json");
|
|
7142
7171
|
let config = {};
|
|
7143
|
-
if (
|
|
7172
|
+
if (existsSync6(configFile)) {
|
|
7144
7173
|
try {
|
|
7145
7174
|
config = JSON.parse(readFileSync4(configFile, "utf-8"));
|
|
7146
7175
|
} catch {}
|
|
@@ -7169,7 +7198,7 @@ function guessKeyField(name) {
|
|
|
7169
7198
|
function getOAuthConfig(name) {
|
|
7170
7199
|
const configDir = getConnectorConfigDir(name);
|
|
7171
7200
|
const credentialsFile = join6(configDir, "credentials.json");
|
|
7172
|
-
if (
|
|
7201
|
+
if (existsSync6(credentialsFile)) {
|
|
7173
7202
|
try {
|
|
7174
7203
|
const creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
|
|
7175
7204
|
return { clientId: creds.clientId, clientSecret: creds.clientSecret };
|
|
@@ -7296,14 +7325,14 @@ async function _refreshOAuthToken(name) {
|
|
|
7296
7325
|
function listProfiles(name) {
|
|
7297
7326
|
const configDir = getConnectorConfigDir(name);
|
|
7298
7327
|
const profilesDir = join6(configDir, "profiles");
|
|
7299
|
-
if (!
|
|
7328
|
+
if (!existsSync6(profilesDir))
|
|
7300
7329
|
return ["default"];
|
|
7301
7330
|
const seen = new Set;
|
|
7302
7331
|
try {
|
|
7303
|
-
const entries =
|
|
7332
|
+
const entries = readdirSync3(profilesDir);
|
|
7304
7333
|
for (const entry of entries) {
|
|
7305
7334
|
const fullPath = join6(profilesDir, entry);
|
|
7306
|
-
const stat =
|
|
7335
|
+
const stat = statSync4(fullPath);
|
|
7307
7336
|
if (stat.isDirectory()) {
|
|
7308
7337
|
seen.add(entry);
|
|
7309
7338
|
} else if (entry.endsWith(".json")) {
|
|
@@ -7325,7 +7354,7 @@ function deleteProfile(name, profile) {
|
|
|
7325
7354
|
const configDir = getConnectorConfigDir(name);
|
|
7326
7355
|
const profilesDir = join6(configDir, "profiles");
|
|
7327
7356
|
const profileFile = join6(profilesDir, `${profile}.json`);
|
|
7328
|
-
if (
|
|
7357
|
+
if (existsSync6(profileFile)) {
|
|
7329
7358
|
rmSync2(profileFile);
|
|
7330
7359
|
if (getCurrentProfile(name) === profile) {
|
|
7331
7360
|
switchProfile(name, "default");
|
|
@@ -7333,7 +7362,7 @@ function deleteProfile(name, profile) {
|
|
|
7333
7362
|
return true;
|
|
7334
7363
|
}
|
|
7335
7364
|
const profileDir = join6(profilesDir, profile);
|
|
7336
|
-
if (
|
|
7365
|
+
if (existsSync6(profileDir)) {
|
|
7337
7366
|
rmSync2(profileDir, { recursive: true });
|
|
7338
7367
|
if (getCurrentProfile(name) === profile) {
|
|
7339
7368
|
switchProfile(name, "default");
|
|
@@ -7366,7 +7395,7 @@ function resolveDashboardDir() {
|
|
|
7366
7395
|
}
|
|
7367
7396
|
candidates.push(join7(process.cwd(), "dashboard", "dist"));
|
|
7368
7397
|
for (const candidate of candidates) {
|
|
7369
|
-
if (
|
|
7398
|
+
if (existsSync7(candidate))
|
|
7370
7399
|
return candidate;
|
|
7371
7400
|
}
|
|
7372
7401
|
return join7(process.cwd(), "dashboard", "dist");
|
|
@@ -7471,7 +7500,7 @@ function oauthPage(type, title, message, hint, extra) {
|
|
|
7471
7500
|
</body></html>`;
|
|
7472
7501
|
}
|
|
7473
7502
|
function serveStaticFile(filePath) {
|
|
7474
|
-
if (!
|
|
7503
|
+
if (!existsSync7(filePath))
|
|
7475
7504
|
return null;
|
|
7476
7505
|
const ext = extname(filePath);
|
|
7477
7506
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -7495,7 +7524,7 @@ async function startServer(requestedPort, options) {
|
|
|
7495
7524
|
const shouldOpen = options?.open ?? true;
|
|
7496
7525
|
loadConnectorVersions();
|
|
7497
7526
|
const dashboardDir = resolveDashboardDir();
|
|
7498
|
-
const dashboardExists =
|
|
7527
|
+
const dashboardExists = existsSync7(dashboardDir);
|
|
7499
7528
|
if (!dashboardExists) {
|
|
7500
7529
|
console.error(`
|
|
7501
7530
|
Dashboard not found at: ${dashboardDir}`);
|
|
@@ -7649,7 +7678,7 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7649
7678
|
const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
7650
7679
|
const limit = parseInt(url2.searchParams.get("limit") || "10", 10);
|
|
7651
7680
|
const days = parseInt(url2.searchParams.get("days") || "7", 10);
|
|
7652
|
-
const db =
|
|
7681
|
+
const db = getDatabase3();
|
|
7653
7682
|
const top = getTopConnectors2(limit, days, db);
|
|
7654
7683
|
const promoted = new Set(getPromotedConnectors2(db));
|
|
7655
7684
|
return json(top.map((t) => ({ ...t, promoted: promoted.has(t.connector) })), 200, port);
|
|
@@ -7660,12 +7689,12 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7660
7689
|
if (!getConnector(name))
|
|
7661
7690
|
return json({ error: "Connector not found" }, 404, port);
|
|
7662
7691
|
const { promoteConnector: promoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
7663
|
-
promoteConnector2(name,
|
|
7692
|
+
promoteConnector2(name, getDatabase3());
|
|
7664
7693
|
return json({ success: true, connector: name }, 200, port);
|
|
7665
7694
|
}
|
|
7666
7695
|
if (promoteMatch && method === "DELETE") {
|
|
7667
7696
|
const { demoteConnector: demoteConnector2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
|
|
7668
|
-
const removed = demoteConnector2(promoteMatch[1],
|
|
7697
|
+
const removed = demoteConnector2(promoteMatch[1], getDatabase3());
|
|
7669
7698
|
return json({ success: removed, connector: promoteMatch[1] }, 200, port);
|
|
7670
7699
|
}
|
|
7671
7700
|
if (path === "/api/llm" && method === "GET") {
|
|
@@ -7701,19 +7730,19 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7701
7730
|
}
|
|
7702
7731
|
}
|
|
7703
7732
|
if (path === "/api/jobs" && method === "GET") {
|
|
7704
|
-
return json(listJobs(
|
|
7733
|
+
return json(listJobs(getDatabase3()), 200, port);
|
|
7705
7734
|
}
|
|
7706
7735
|
if (path === "/api/jobs" && method === "POST") {
|
|
7707
7736
|
const body = await req.json().catch(() => ({}));
|
|
7708
7737
|
if (!body.name || !body.connector || !body.command || !body.cron)
|
|
7709
7738
|
return json({ error: "name, connector, command, cron required" }, 400, port);
|
|
7710
|
-
const job = createJob({ name: body.name, connector: body.connector, command: body.command, args: body.args ?? [], cron: body.cron, strip: !!body.strip },
|
|
7739
|
+
const job = createJob({ name: body.name, connector: body.connector, command: body.command, args: body.args ?? [], cron: body.cron, strip: !!body.strip }, getDatabase3());
|
|
7711
7740
|
return json(job, 201, port);
|
|
7712
7741
|
}
|
|
7713
7742
|
const jobMatch = path.match(/^\/api\/jobs\/([^/]+)$/);
|
|
7714
7743
|
if (jobMatch) {
|
|
7715
|
-
const db =
|
|
7716
|
-
const job = getJobByName(jobMatch[1]) ??
|
|
7744
|
+
const db = getDatabase3();
|
|
7745
|
+
const job = getJobByName(jobMatch[1]) ?? getDatabase3().query("SELECT * FROM connector_jobs WHERE id = ?").get(jobMatch[1]);
|
|
7717
7746
|
if (!job && method !== "DELETE")
|
|
7718
7747
|
return json({ error: "Job not found" }, 404, port);
|
|
7719
7748
|
if (method === "GET")
|
|
@@ -7734,7 +7763,7 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7734
7763
|
}
|
|
7735
7764
|
const jobRunMatch = path.match(/^\/api\/jobs\/([^/]+)\/run$/);
|
|
7736
7765
|
if (jobRunMatch && method === "POST") {
|
|
7737
|
-
const db =
|
|
7766
|
+
const db = getDatabase3();
|
|
7738
7767
|
const job = getJobByName(jobRunMatch[1], db);
|
|
7739
7768
|
if (!job)
|
|
7740
7769
|
return json({ error: "Job not found" }, 404, port);
|
|
@@ -7742,18 +7771,18 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7742
7771
|
return json(result, 200, port);
|
|
7743
7772
|
}
|
|
7744
7773
|
if (path === "/api/workflows" && method === "GET") {
|
|
7745
|
-
return json(listWorkflows(
|
|
7774
|
+
return json(listWorkflows(getDatabase3()), 200, port);
|
|
7746
7775
|
}
|
|
7747
7776
|
if (path === "/api/workflows" && method === "POST") {
|
|
7748
7777
|
const body = await req.json().catch(() => ({}));
|
|
7749
7778
|
if (!body.name || !body.steps)
|
|
7750
7779
|
return json({ error: "name and steps required" }, 400, port);
|
|
7751
|
-
const wf = createWorkflow({ name: body.name, steps: body.steps },
|
|
7780
|
+
const wf = createWorkflow({ name: body.name, steps: body.steps }, getDatabase3());
|
|
7752
7781
|
return json(wf, 201, port);
|
|
7753
7782
|
}
|
|
7754
7783
|
const wfMatch = path.match(/^\/api\/workflows\/([^/]+)$/);
|
|
7755
7784
|
if (wfMatch) {
|
|
7756
|
-
const db =
|
|
7785
|
+
const db = getDatabase3();
|
|
7757
7786
|
const wf = getWorkflowByName(wfMatch[1], db);
|
|
7758
7787
|
if (!wf)
|
|
7759
7788
|
return json({ error: "Workflow not found" }, 404, port);
|
|
@@ -7766,7 +7795,7 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7766
7795
|
}
|
|
7767
7796
|
const wfRunMatch = path.match(/^\/api\/workflows\/([^/]+)\/run$/);
|
|
7768
7797
|
if (wfRunMatch && method === "POST") {
|
|
7769
|
-
const wf = getWorkflowByName(wfRunMatch[1],
|
|
7798
|
+
const wf = getWorkflowByName(wfRunMatch[1], getDatabase3());
|
|
7770
7799
|
if (!wf)
|
|
7771
7800
|
return json({ error: "Workflow not found" }, 404, port);
|
|
7772
7801
|
const result = await runWorkflow(wf);
|
|
@@ -7812,10 +7841,10 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7812
7841
|
return json({ error: "Invalid connector name" }, 400, port);
|
|
7813
7842
|
try {
|
|
7814
7843
|
const profiles = listProfiles(name);
|
|
7815
|
-
const configDir = join7(
|
|
7844
|
+
const configDir = join7(getConnectorsHome(), name.startsWith("connect-") ? name : `connect-${name}`);
|
|
7816
7845
|
const currentProfileFile = join7(configDir, "current_profile");
|
|
7817
7846
|
let current = "default";
|
|
7818
|
-
if (
|
|
7847
|
+
if (existsSync7(currentProfileFile)) {
|
|
7819
7848
|
try {
|
|
7820
7849
|
current = readFileSync5(currentProfileFile, "utf-8").trim() || "default";
|
|
7821
7850
|
} catch {}
|
|
@@ -7864,19 +7893,19 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7864
7893
|
}
|
|
7865
7894
|
if (path === "/api/export" && method === "GET") {
|
|
7866
7895
|
try {
|
|
7867
|
-
const connectDir =
|
|
7896
|
+
const connectDir = getConnectorsHome();
|
|
7868
7897
|
const result = {};
|
|
7869
|
-
if (
|
|
7870
|
-
const entries =
|
|
7898
|
+
if (existsSync7(connectDir)) {
|
|
7899
|
+
const entries = readdirSync4(connectDir, { withFileTypes: true });
|
|
7871
7900
|
for (const entry of entries) {
|
|
7872
7901
|
if (!entry.isDirectory() || !entry.name.startsWith("connect-"))
|
|
7873
7902
|
continue;
|
|
7874
7903
|
const connectorName = entry.name.replace(/^connect-/, "");
|
|
7875
7904
|
const profilesDir = join7(connectDir, entry.name, "profiles");
|
|
7876
|
-
if (!
|
|
7905
|
+
if (!existsSync7(profilesDir))
|
|
7877
7906
|
continue;
|
|
7878
7907
|
const profiles = {};
|
|
7879
|
-
const profileEntries =
|
|
7908
|
+
const profileEntries = readdirSync4(profilesDir, { withFileTypes: true });
|
|
7880
7909
|
for (const pEntry of profileEntries) {
|
|
7881
7910
|
if (pEntry.isFile() && pEntry.name.endsWith(".json")) {
|
|
7882
7911
|
const profileName = basename(pEntry.name, ".json");
|
|
@@ -7887,7 +7916,7 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7887
7916
|
}
|
|
7888
7917
|
if (pEntry.isDirectory()) {
|
|
7889
7918
|
const configPath = join7(profilesDir, pEntry.name, "config.json");
|
|
7890
|
-
if (
|
|
7919
|
+
if (existsSync7(configPath)) {
|
|
7891
7920
|
try {
|
|
7892
7921
|
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7893
7922
|
profiles[pEntry.name] = config;
|
|
@@ -7924,7 +7953,7 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7924
7953
|
return json({ error: "Invalid import format: missing 'connectors' object" }, 400, port);
|
|
7925
7954
|
}
|
|
7926
7955
|
let imported = 0;
|
|
7927
|
-
const connectDir =
|
|
7956
|
+
const connectDir = getConnectorsHome();
|
|
7928
7957
|
for (const [connectorName, data] of Object.entries(body.connectors)) {
|
|
7929
7958
|
if (!isValidConnectorName(connectorName))
|
|
7930
7959
|
continue;
|
|
@@ -8012,8 +8041,8 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
8012
8041
|
process.on("SIGINT", shutdown);
|
|
8013
8042
|
process.on("SIGTERM", shutdown);
|
|
8014
8043
|
const { startScheduler: startScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), exports_scheduler));
|
|
8015
|
-
const { getDatabase:
|
|
8016
|
-
startScheduler2(
|
|
8044
|
+
const { getDatabase: getDatabase3 } = await Promise.resolve().then(() => (init_database(), exports_database));
|
|
8045
|
+
startScheduler2(getDatabase3());
|
|
8017
8046
|
const url = `http://localhost:${port}`;
|
|
8018
8047
|
console.log(`Connectors Dashboard running at ${url}`);
|
|
8019
8048
|
if (shouldOpen) {
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
+
/**
|
|
3
|
+
* Get the connectors home directory.
|
|
4
|
+
* New default: ~/.hasna/connectors/
|
|
5
|
+
* Auto-migrates from ~/.connectors/ if the new dir doesn't exist yet.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getConnectorsHome(): string;
|
|
2
8
|
export declare function getDatabase(path?: string): Database;
|
|
3
9
|
export declare function closeDatabase(): void;
|
|
4
10
|
/** ISO timestamp string */
|