@hasna/connectors 1.1.18 → 1.2.0

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/serve.js CHANGED
@@ -16,19 +16,30 @@ var __toESM = (mod, isNodeMode, target) => {
16
16
  });
17
17
  return to;
18
18
  };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
29
  var __require = import.meta.require;
20
30
 
21
- // src/server/serve.ts
22
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "fs";
23
-
24
31
  // src/db/database.ts
32
+ var exports_database = {};
33
+ __export(exports_database, {
34
+ shortUuid: () => shortUuid,
35
+ now: () => now,
36
+ getDatabase: () => getDatabase,
37
+ closeDatabase: () => closeDatabase
38
+ });
25
39
  import { Database } from "bun:sqlite";
26
40
  import { join } from "path";
27
41
  import { homedir } from "os";
28
42
  import { mkdirSync } from "fs";
29
- var DB_DIR = join(homedir(), ".connectors");
30
- var DB_PATH = join(DB_DIR, "connectors.db");
31
- var _db = null;
32
43
  function getDatabase(path) {
33
44
  if (_db)
34
45
  return _db;
@@ -39,9 +50,16 @@ function getDatabase(path) {
39
50
  migrate(_db);
40
51
  return _db;
41
52
  }
53
+ function closeDatabase() {
54
+ _db?.close();
55
+ _db = null;
56
+ }
42
57
  function now() {
43
58
  return new Date().toISOString();
44
59
  }
60
+ function shortUuid() {
61
+ return crypto.randomUUID().slice(0, 8);
62
+ }
45
63
  function migrate(db) {
46
64
  db.run(`
47
65
  CREATE TABLE IF NOT EXISTS agents (
@@ -71,11 +89,407 @@ function migrate(db) {
71
89
  `);
72
90
  db.run(`CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id)`);
73
91
  db.run(`CREATE INDEX IF NOT EXISTS idx_resource_locks_expires ON resource_locks(expires_at)`);
92
+ db.run(`
93
+ CREATE TABLE IF NOT EXISTS connector_rate_usage (
94
+ agent_id TEXT NOT NULL,
95
+ connector TEXT NOT NULL,
96
+ window_start TEXT NOT NULL,
97
+ call_count INTEGER NOT NULL DEFAULT 0,
98
+ PRIMARY KEY (agent_id, connector, window_start)
99
+ )
100
+ `);
101
+ db.run(`CREATE INDEX IF NOT EXISTS idx_rate_usage_window ON connector_rate_usage(connector, window_start)`);
102
+ db.run(`
103
+ CREATE TABLE IF NOT EXISTS connector_jobs (
104
+ id TEXT PRIMARY KEY,
105
+ name TEXT UNIQUE NOT NULL,
106
+ connector TEXT NOT NULL,
107
+ command TEXT NOT NULL,
108
+ args TEXT NOT NULL DEFAULT '[]',
109
+ cron TEXT NOT NULL,
110
+ enabled INTEGER NOT NULL DEFAULT 1,
111
+ strip INTEGER NOT NULL DEFAULT 0,
112
+ created_at TEXT NOT NULL,
113
+ last_run_at TEXT
114
+ )
115
+ `);
116
+ db.run(`CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON connector_jobs(enabled)`);
117
+ db.run(`
118
+ CREATE TABLE IF NOT EXISTS connector_job_runs (
119
+ id TEXT PRIMARY KEY,
120
+ job_id TEXT NOT NULL REFERENCES connector_jobs(id) ON DELETE CASCADE,
121
+ started_at TEXT NOT NULL,
122
+ finished_at TEXT,
123
+ exit_code INTEGER,
124
+ raw_output TEXT,
125
+ stripped_output TEXT
126
+ )
127
+ `);
128
+ db.run(`CREATE INDEX IF NOT EXISTS idx_job_runs_job ON connector_job_runs(job_id, started_at DESC)`);
129
+ db.run(`
130
+ CREATE TABLE IF NOT EXISTS connector_workflows (
131
+ id TEXT PRIMARY KEY,
132
+ name TEXT UNIQUE NOT NULL,
133
+ steps TEXT NOT NULL DEFAULT '[]',
134
+ enabled INTEGER NOT NULL DEFAULT 1,
135
+ created_at TEXT NOT NULL
136
+ )
137
+ `);
138
+ }
139
+ var DB_DIR, DB_PATH, _db = null;
140
+ var init_database = __esm(() => {
141
+ DB_DIR = join(homedir(), ".connectors");
142
+ DB_PATH = join(DB_DIR, "connectors.db");
143
+ });
144
+
145
+ // src/lib/llm.ts
146
+ import { existsSync, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
147
+ import { join as join2 } from "path";
148
+ import { homedir as homedir2 } from "os";
149
+ function getLlmConfigPath() {
150
+ return join2(homedir2(), ".connectors", "llm.json");
151
+ }
152
+ function getLlmConfig() {
153
+ const path = getLlmConfigPath();
154
+ if (!existsSync(path))
155
+ return null;
156
+ try {
157
+ return JSON.parse(readFileSync(path, "utf-8"));
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+ function saveLlmConfig(config) {
163
+ const dir = join2(homedir2(), ".connectors");
164
+ mkdirSync2(dir, { recursive: true });
165
+ writeFileSync(getLlmConfigPath(), JSON.stringify(config, null, 2));
166
+ }
167
+ function maskKey(key) {
168
+ if (key.length <= 8)
169
+ return "***";
170
+ return key.slice(0, 8) + "***";
171
+ }
172
+
173
+ class LLMClient {
174
+ config;
175
+ constructor(config) {
176
+ this.config = config;
177
+ }
178
+ static fromConfig() {
179
+ const config = getLlmConfig();
180
+ if (!config)
181
+ return null;
182
+ return new LLMClient(config);
183
+ }
184
+ async complete(prompt, content) {
185
+ const start = Date.now();
186
+ const { provider, model, api_key } = this.config;
187
+ if (provider === "anthropic") {
188
+ return this._anthropicComplete(prompt, content, start);
189
+ }
190
+ const baseUrl = PROVIDER_BASE_URLS[provider];
191
+ const response = await fetch(`${baseUrl}/chat/completions`, {
192
+ method: "POST",
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ Authorization: `Bearer ${api_key}`
196
+ },
197
+ body: JSON.stringify({
198
+ model,
199
+ messages: [
200
+ { role: "system", content: prompt },
201
+ { role: "user", content }
202
+ ],
203
+ temperature: 0,
204
+ max_tokens: 4096
205
+ })
206
+ });
207
+ if (!response.ok) {
208
+ const error = await response.text();
209
+ throw new Error(`LLM request failed (${provider} ${response.status}): ${error}`);
210
+ }
211
+ const data = await response.json();
212
+ return {
213
+ content: data.choices[0].message.content,
214
+ provider,
215
+ model,
216
+ latency_ms: Date.now() - start
217
+ };
218
+ }
219
+ async _anthropicComplete(prompt, content, start) {
220
+ const { model, api_key } = this.config;
221
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
222
+ method: "POST",
223
+ headers: {
224
+ "Content-Type": "application/json",
225
+ "x-api-key": api_key,
226
+ "anthropic-version": "2023-06-01"
227
+ },
228
+ body: JSON.stringify({
229
+ model,
230
+ system: prompt,
231
+ messages: [{ role: "user", content }],
232
+ max_tokens: 4096
233
+ })
234
+ });
235
+ if (!response.ok) {
236
+ const error = await response.text();
237
+ throw new Error(`LLM request failed (anthropic ${response.status}): ${error}`);
238
+ }
239
+ const data = await response.json();
240
+ return {
241
+ content: data.content[0].text,
242
+ provider: "anthropic",
243
+ model,
244
+ latency_ms: Date.now() - start
245
+ };
246
+ }
74
247
  }
248
+ var PROVIDER_BASE_URLS;
249
+ var init_llm = __esm(() => {
250
+ PROVIDER_BASE_URLS = {
251
+ cerebras: "https://api.cerebras.ai/v1",
252
+ groq: "https://api.groq.com/openai/v1",
253
+ openai: "https://api.openai.com/v1"
254
+ };
255
+ });
256
+
257
+ // src/lib/strip.ts
258
+ async function maybeStrip(output, _type = "json") {
259
+ const config = getLlmConfig();
260
+ if (!config?.strip)
261
+ return output;
262
+ if (!output || output.trim().length === 0)
263
+ return output;
264
+ const client = LLMClient.fromConfig();
265
+ if (!client)
266
+ return output;
267
+ try {
268
+ const result = await client.complete(STRIP_PROMPT, output);
269
+ return result.content.trim();
270
+ } catch {
271
+ return output;
272
+ }
273
+ }
274
+ var STRIP_PROMPT = `You are a data extraction assistant. Your job is to take raw API output and return ONLY the essential, structured data.
275
+
276
+ Rules:
277
+ - Return valid JSON only (no markdown, no explanation)
278
+ - Remove pagination metadata, rate limit headers, empty fields, null values
279
+ - Keep all meaningful data fields
280
+ - If the input is already minimal, return it unchanged
281
+ - If input is not JSON, extract key facts as a JSON object
282
+ - Never truncate actual data values`;
283
+ var init_strip = __esm(() => {
284
+ init_llm();
285
+ });
286
+
287
+ // src/db/jobs.ts
288
+ function rowToJob(row) {
289
+ return {
290
+ ...row,
291
+ args: JSON.parse(row.args || "[]"),
292
+ enabled: row.enabled === 1,
293
+ strip: row.strip === 1
294
+ };
295
+ }
296
+ function createJob(input, db) {
297
+ const d = db ?? getDatabase();
298
+ const id = shortUuid();
299
+ const ts = now();
300
+ d.run("INSERT INTO connector_jobs (id, name, connector, command, args, cron, enabled, strip, created_at) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)", [id, input.name, input.connector, input.command, JSON.stringify(input.args ?? []), input.cron, input.strip ? 1 : 0, ts]);
301
+ return getJob(id, d);
302
+ }
303
+ function getJob(id, db) {
304
+ const d = db ?? getDatabase();
305
+ const row = d.query("SELECT * FROM connector_jobs WHERE id = ?").get(id);
306
+ return row ? rowToJob(row) : null;
307
+ }
308
+ function getJobByName(name, db) {
309
+ const d = db ?? getDatabase();
310
+ const row = d.query("SELECT * FROM connector_jobs WHERE name = ?").get(name);
311
+ return row ? rowToJob(row) : null;
312
+ }
313
+ function listJobs(db) {
314
+ const d = db ?? getDatabase();
315
+ return d.query("SELECT * FROM connector_jobs ORDER BY name").all().map(rowToJob);
316
+ }
317
+ function listEnabledJobs(db) {
318
+ const d = db ?? getDatabase();
319
+ return d.query("SELECT * FROM connector_jobs WHERE enabled = 1").all().map(rowToJob);
320
+ }
321
+ function updateJob(id, input, db) {
322
+ const d = db ?? getDatabase();
323
+ const sets = [];
324
+ const params = [];
325
+ if (input.name !== undefined) {
326
+ sets.push("name = ?");
327
+ params.push(input.name);
328
+ }
329
+ if (input.connector !== undefined) {
330
+ sets.push("connector = ?");
331
+ params.push(input.connector);
332
+ }
333
+ if (input.command !== undefined) {
334
+ sets.push("command = ?");
335
+ params.push(input.command);
336
+ }
337
+ if (input.args !== undefined) {
338
+ sets.push("args = ?");
339
+ params.push(JSON.stringify(input.args));
340
+ }
341
+ if (input.cron !== undefined) {
342
+ sets.push("cron = ?");
343
+ params.push(input.cron);
344
+ }
345
+ if (input.enabled !== undefined) {
346
+ sets.push("enabled = ?");
347
+ params.push(input.enabled ? 1 : 0);
348
+ }
349
+ if (input.strip !== undefined) {
350
+ sets.push("strip = ?");
351
+ params.push(input.strip ? 1 : 0);
352
+ }
353
+ if (sets.length === 0)
354
+ return getJob(id, d);
355
+ params.push(id);
356
+ d.run(`UPDATE connector_jobs SET ${sets.join(", ")} WHERE id = ?`, params);
357
+ return getJob(id, d);
358
+ }
359
+ function deleteJob(id, db) {
360
+ const d = db ?? getDatabase();
361
+ return d.run("DELETE FROM connector_jobs WHERE id = ?", [id]).changes > 0;
362
+ }
363
+ function touchJobLastRun(id, db) {
364
+ const d = db ?? getDatabase();
365
+ d.run("UPDATE connector_jobs SET last_run_at = ? WHERE id = ?", [now(), id]);
366
+ }
367
+ function createJobRun(jobId, db) {
368
+ const d = db ?? getDatabase();
369
+ const id = shortUuid();
370
+ const ts = now();
371
+ d.run("INSERT INTO connector_job_runs (id, job_id, started_at) VALUES (?, ?, ?)", [id, jobId, ts]);
372
+ return { id, job_id: jobId, started_at: ts, finished_at: null, exit_code: null, raw_output: null, stripped_output: null };
373
+ }
374
+ function finishJobRun(id, result, db) {
375
+ const d = db ?? getDatabase();
376
+ d.run("UPDATE connector_job_runs SET finished_at = ?, exit_code = ?, raw_output = ?, stripped_output = ? WHERE id = ?", [now(), result.exit_code, result.raw_output, result.stripped_output ?? null, id]);
377
+ }
378
+ function listJobRuns(jobId, limit = 20, db) {
379
+ const d = db ?? getDatabase();
380
+ return d.query("SELECT * FROM connector_job_runs WHERE job_id = ? ORDER BY started_at DESC LIMIT ?").all(jobId, limit);
381
+ }
382
+ var init_jobs = __esm(() => {
383
+ init_database();
384
+ });
385
+
386
+ // src/lib/scheduler.ts
387
+ var exports_scheduler = {};
388
+ __export(exports_scheduler, {
389
+ triggerJob: () => triggerJob,
390
+ stopScheduler: () => stopScheduler,
391
+ startScheduler: () => startScheduler
392
+ });
393
+ import { spawn } from "child_process";
394
+ function cronMatches(cron, d) {
395
+ const parts = cron.trim().split(/\s+/);
396
+ if (parts.length !== 5)
397
+ return false;
398
+ const [min, hour, dom, mon, dow] = parts;
399
+ function matches(field, value, min_v, max_v) {
400
+ if (field === "*")
401
+ return true;
402
+ if (field.startsWith("*/")) {
403
+ const step = parseInt(field.slice(2));
404
+ return value % step === 0;
405
+ }
406
+ if (field.includes("-")) {
407
+ const [a, b] = field.split("-").map(Number);
408
+ return value >= a && value <= b;
409
+ }
410
+ if (field.includes(",")) {
411
+ return field.split(",").map(Number).includes(value);
412
+ }
413
+ return parseInt(field) === value;
414
+ }
415
+ return matches(min, d.getMinutes(), 0, 59) && matches(hour, d.getHours(), 0, 23) && matches(dom, d.getDate(), 1, 31) && matches(mon, d.getMonth() + 1, 1, 12) && matches(dow, d.getDay(), 0, 6);
416
+ }
417
+ async function runConnectorCommand(connector, command, args) {
418
+ return new Promise((resolve) => {
419
+ const cmdArgs = [connector, command, ...args, "--format", "json"];
420
+ const proc = spawn("connectors", ["run", ...cmdArgs], { shell: false });
421
+ let output = "";
422
+ proc.stdout.on("data", (d) => {
423
+ output += d.toString();
424
+ });
425
+ proc.stderr.on("data", (d) => {
426
+ output += d.toString();
427
+ });
428
+ proc.on("close", (code) => resolve({ exitCode: code ?? 1, output }));
429
+ proc.on("error", () => resolve({ exitCode: 1, output: `Failed to spawn connectors run` }));
430
+ setTimeout(() => {
431
+ proc.kill();
432
+ resolve({ exitCode: 124, output: output + `
433
+ [timeout]` });
434
+ }, 60000);
435
+ });
436
+ }
437
+ async function executeJob(job, db) {
438
+ const run = createJobRun(job.id, db);
439
+ try {
440
+ const { exitCode, output } = await runConnectorCommand(job.connector, job.command, job.args);
441
+ const stripped = job.strip ? await maybeStrip(output) : undefined;
442
+ finishJobRun(run.id, { exit_code: exitCode, raw_output: output, stripped_output: stripped }, db);
443
+ touchJobLastRun(job.id, db);
444
+ } catch (e) {
445
+ finishJobRun(run.id, { exit_code: 1, raw_output: String(e) }, db);
446
+ }
447
+ }
448
+ function startScheduler(db) {
449
+ if (_interval)
450
+ return;
451
+ _interval = setInterval(async () => {
452
+ const now3 = new Date;
453
+ const currentMinute = now3.getMinutes() + now3.getHours() * 60;
454
+ if (currentMinute === _lastCheckedMinute)
455
+ return;
456
+ _lastCheckedMinute = currentMinute;
457
+ const jobs = listEnabledJobs(db);
458
+ for (const job of jobs) {
459
+ if (cronMatches(job.cron, now3)) {
460
+ executeJob(job, db).catch(() => {});
461
+ }
462
+ }
463
+ }, 30000);
464
+ }
465
+ function stopScheduler() {
466
+ if (_interval) {
467
+ clearInterval(_interval);
468
+ _interval = null;
469
+ _lastCheckedMinute = -1;
470
+ }
471
+ }
472
+ async function triggerJob(job, db) {
473
+ const run = createJobRun(job.id, db);
474
+ const { exitCode, output } = await runConnectorCommand(job.connector, job.command, job.args);
475
+ const stripped = job.strip ? await maybeStrip(output) : undefined;
476
+ finishJobRun(run.id, { exit_code: exitCode, raw_output: output, stripped_output: stripped }, db);
477
+ touchJobLastRun(job.id, db);
478
+ return { run_id: run.id, exit_code: exitCode, output: stripped ?? output };
479
+ }
480
+ var _interval = null, _lastCheckedMinute = -1;
481
+ var init_scheduler = __esm(() => {
482
+ init_jobs();
483
+ init_strip();
484
+ });
485
+
486
+ // src/server/serve.ts
487
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
75
488
 
76
489
  // src/db/agents.ts
490
+ init_database();
77
491
  var AGENT_ACTIVE_WINDOW_MS = 30 * 60 * 1000;
78
- function shortUuid() {
492
+ function shortUuid2() {
79
493
  return crypto.randomUUID().slice(0, 8);
80
494
  }
81
495
  function isAgentConflict(result) {
@@ -112,7 +526,7 @@ function registerAgent(input, db) {
112
526
  d.run(`UPDATE agents SET ${updates.join(", ")} WHERE id = ?`, params);
113
527
  return getAgent(existing.id, d);
114
528
  }
115
- const id = shortUuid();
529
+ const id = shortUuid2();
116
530
  const ts = now();
117
531
  d.run(`INSERT INTO agents (id, name, session_id, role, last_seen_at, created_at)
118
532
  VALUES (?, ?, ?, ?, ?, ?)`, [id, normalizedName, input.session_id ?? null, input.role ?? "agent", ts, ts]);
@@ -136,6 +550,7 @@ function deleteAgent(id, db) {
136
550
  }
137
551
 
138
552
  // src/db/rate.ts
553
+ init_database();
139
554
  var AGENT_ACTIVE_WINDOW_MS2 = 30 * 60 * 1000;
140
555
  var WINDOW_SECONDS = 60;
141
556
  function ensureRateTable(db) {
@@ -205,13 +620,106 @@ function getRateBudget(agentId, connector, connectorLimit, db) {
205
620
  }
206
621
 
207
622
  // src/server/serve.ts
208
- import { join as join6, dirname as dirname3, extname, basename } from "path";
623
+ init_strip();
624
+ init_llm();
625
+ init_jobs();
626
+
627
+ // src/db/workflows.ts
628
+ init_database();
629
+ function rowToWorkflow(row) {
630
+ return {
631
+ ...row,
632
+ steps: JSON.parse(row.steps || "[]"),
633
+ enabled: row.enabled === 1
634
+ };
635
+ }
636
+ function createWorkflow(input, db) {
637
+ const d = db ?? getDatabase();
638
+ const id = shortUuid();
639
+ d.run("INSERT INTO connector_workflows (id, name, steps, enabled, created_at) VALUES (?, ?, ?, 1, ?)", [id, input.name, JSON.stringify(input.steps), now()]);
640
+ return getWorkflow(id, d);
641
+ }
642
+ function getWorkflow(id, db) {
643
+ const d = db ?? getDatabase();
644
+ const row = d.query("SELECT * FROM connector_workflows WHERE id = ?").get(id);
645
+ return row ? rowToWorkflow(row) : null;
646
+ }
647
+ function getWorkflowByName(name, db) {
648
+ const d = db ?? getDatabase();
649
+ const row = d.query("SELECT * FROM connector_workflows WHERE name = ?").get(name);
650
+ return row ? rowToWorkflow(row) : null;
651
+ }
652
+ function listWorkflows(db) {
653
+ const d = db ?? getDatabase();
654
+ return d.query("SELECT * FROM connector_workflows ORDER BY name").all().map(rowToWorkflow);
655
+ }
656
+ function deleteWorkflow(id, db) {
657
+ const d = db ?? getDatabase();
658
+ return d.run("DELETE FROM connector_workflows WHERE id = ?", [id]).changes > 0;
659
+ }
660
+
661
+ // src/server/serve.ts
662
+ init_scheduler();
663
+
664
+ // src/lib/workflow-runner.ts
665
+ init_strip();
666
+ import { spawn as spawn2 } from "child_process";
667
+ async function runStep(step, previousOutput) {
668
+ return new Promise((resolve) => {
669
+ const args = [...step.args ?? []];
670
+ if (previousOutput && previousOutput.trim()) {
671
+ args.push("--input", previousOutput.trim().slice(0, 4096));
672
+ }
673
+ const cmdArgs = ["run", step.connector, step.command, ...args, "--format", "json"];
674
+ const proc = spawn2("connectors", cmdArgs, { shell: false });
675
+ let output = "";
676
+ proc.stdout.on("data", (d) => {
677
+ output += d.toString();
678
+ });
679
+ proc.stderr.on("data", (d) => {
680
+ output += d.toString();
681
+ });
682
+ proc.on("close", (code) => resolve({ exitCode: code ?? 1, output }));
683
+ proc.on("error", () => resolve({ exitCode: 1, output: "Failed to spawn connectors" }));
684
+ setTimeout(() => {
685
+ proc.kill();
686
+ resolve({ exitCode: 124, output: output + `
687
+ [timeout]` });
688
+ }, 60000);
689
+ });
690
+ }
691
+ async function runWorkflow(workflow) {
692
+ const results = [];
693
+ let previousOutput;
694
+ let success = true;
695
+ for (let i = 0;i < workflow.steps.length; i++) {
696
+ const step = workflow.steps[i];
697
+ const { exitCode, output } = await runStep(step, previousOutput);
698
+ const stripped = await maybeStrip(output);
699
+ results.push({ step: i + 1, connector: step.connector, command: step.command, exit_code: exitCode, output: stripped });
700
+ if (exitCode !== 0) {
701
+ success = false;
702
+ break;
703
+ }
704
+ previousOutput = stripped;
705
+ }
706
+ return {
707
+ workflow_id: workflow.id,
708
+ workflow_name: workflow.name,
709
+ steps: results,
710
+ success,
711
+ final_output: results[results.length - 1]?.output ?? ""
712
+ };
713
+ }
714
+
715
+ // src/server/serve.ts
716
+ import { join as join7, dirname as dirname3, extname, basename } from "path";
209
717
  import { fileURLToPath as fileURLToPath3 } from "url";
210
- import { homedir as homedir5 } from "os";
718
+ import { homedir as homedir6 } from "os";
211
719
 
212
720
  // src/lib/registry.ts
213
- import { existsSync, readFileSync } from "fs";
214
- import { join as join2, dirname } from "path";
721
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
722
+ import { join as join3, dirname } from "path";
215
723
  import { fileURLToPath } from "url";
216
724
  var CONNECTORS = [
217
725
  {
@@ -6119,17 +6627,17 @@ function loadConnectorVersions() {
6119
6627
  versionsLoaded = true;
6120
6628
  const thisDir = dirname(fileURLToPath(import.meta.url));
6121
6629
  const candidates = [
6122
- join2(thisDir, "..", "connectors"),
6123
- join2(thisDir, "..", "..", "connectors")
6630
+ join3(thisDir, "..", "connectors"),
6631
+ join3(thisDir, "..", "..", "connectors")
6124
6632
  ];
6125
- const connectorsDir = candidates.find((d) => existsSync(d));
6633
+ const connectorsDir = candidates.find((d) => existsSync2(d));
6126
6634
  if (!connectorsDir)
6127
6635
  return;
6128
6636
  for (const connector of CONNECTORS) {
6129
6637
  try {
6130
- const pkgPath = join2(connectorsDir, `connect-${connector.name}`, "package.json");
6131
- if (existsSync(pkgPath)) {
6132
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
6638
+ const pkgPath = join3(connectorsDir, `connect-${connector.name}`, "package.json");
6639
+ if (existsSync2(pkgPath)) {
6640
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
6133
6641
  connector.version = pkg.version || "0.0.0";
6134
6642
  }
6135
6643
  } catch {}
@@ -6137,24 +6645,24 @@ function loadConnectorVersions() {
6137
6645
  }
6138
6646
 
6139
6647
  // src/lib/installer.ts
6140
- import { existsSync as existsSync2, cpSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync, rmSync } from "fs";
6141
- import { homedir as homedir2 } from "os";
6142
- import { join as join3, dirname as dirname2 } from "path";
6648
+ import { existsSync as existsSync3, cpSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync, statSync, rmSync } from "fs";
6649
+ import { homedir as homedir3 } from "os";
6650
+ import { join as join4, dirname as dirname2 } from "path";
6143
6651
  import { fileURLToPath as fileURLToPath2 } from "url";
6144
6652
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
6145
6653
  function resolveConnectorsDir() {
6146
- const fromBin = join3(__dirname2, "..", "connectors");
6147
- if (existsSync2(fromBin))
6654
+ const fromBin = join4(__dirname2, "..", "connectors");
6655
+ if (existsSync3(fromBin))
6148
6656
  return fromBin;
6149
- const fromSrc = join3(__dirname2, "..", "..", "connectors");
6150
- if (existsSync2(fromSrc))
6657
+ const fromSrc = join4(__dirname2, "..", "..", "connectors");
6658
+ if (existsSync3(fromSrc))
6151
6659
  return fromSrc;
6152
6660
  return fromBin;
6153
6661
  }
6154
6662
  var CONNECTORS_DIR = resolveConnectorsDir();
6155
6663
  function getConnectorPath(name) {
6156
6664
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6157
- return join3(CONNECTORS_DIR, connectorName);
6665
+ return join4(CONNECTORS_DIR, connectorName);
6158
6666
  }
6159
6667
  function installConnector(name, options = {}) {
6160
6668
  const { targetDir = process.cwd(), overwrite = false } = options;
@@ -6167,16 +6675,16 @@ function installConnector(name, options = {}) {
6167
6675
  }
6168
6676
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6169
6677
  const sourcePath = getConnectorPath(name);
6170
- const destDir = join3(targetDir, ".connectors");
6171
- const destPath = join3(destDir, connectorName);
6172
- if (!existsSync2(sourcePath)) {
6678
+ const destDir = join4(targetDir, ".connectors");
6679
+ const destPath = join4(destDir, connectorName);
6680
+ if (!existsSync3(sourcePath)) {
6173
6681
  return {
6174
6682
  connector: name,
6175
6683
  success: false,
6176
6684
  error: `Connector '${name}' not found`
6177
6685
  };
6178
6686
  }
6179
- if (existsSync2(destPath) && !overwrite) {
6687
+ if (existsSync3(destPath) && !overwrite) {
6180
6688
  return {
6181
6689
  connector: name,
6182
6690
  success: false,
@@ -6185,22 +6693,22 @@ function installConnector(name, options = {}) {
6185
6693
  };
6186
6694
  }
6187
6695
  try {
6188
- if (!existsSync2(destDir)) {
6189
- mkdirSync2(destDir, { recursive: true });
6696
+ if (!existsSync3(destDir)) {
6697
+ mkdirSync3(destDir, { recursive: true });
6190
6698
  }
6191
6699
  cpSync(sourcePath, destPath, { recursive: true });
6192
- const homeCredDir = join3(homedir2(), ".connectors", connectorName);
6193
- if (existsSync2(homeCredDir)) {
6700
+ const homeCredDir = join4(homedir3(), ".connectors", connectorName);
6701
+ if (existsSync3(homeCredDir)) {
6194
6702
  const filesToCopy = ["credentials.json", "current_profile"];
6195
6703
  for (const file of filesToCopy) {
6196
- const src = join3(homeCredDir, file);
6197
- if (existsSync2(src)) {
6198
- cpSync(src, join3(destPath, file));
6704
+ const src = join4(homeCredDir, file);
6705
+ if (existsSync3(src)) {
6706
+ cpSync(src, join4(destPath, file));
6199
6707
  }
6200
6708
  }
6201
- const profilesDir = join3(homeCredDir, "profiles");
6202
- if (existsSync2(profilesDir)) {
6203
- cpSync(profilesDir, join3(destPath, "profiles"), { recursive: true });
6709
+ const profilesDir = join4(homeCredDir, "profiles");
6710
+ if (existsSync3(profilesDir)) {
6711
+ cpSync(profilesDir, join4(destPath, "profiles"), { recursive: true });
6204
6712
  }
6205
6713
  }
6206
6714
  updateConnectorsIndex(destDir);
@@ -6218,7 +6726,7 @@ function installConnector(name, options = {}) {
6218
6726
  }
6219
6727
  }
6220
6728
  function updateConnectorsIndex(connectorsDir) {
6221
- const indexPath = join3(connectorsDir, "index.ts");
6729
+ const indexPath = join4(connectorsDir, "index.ts");
6222
6730
  const connectors = readdirSync(connectorsDir).filter((f) => f.startsWith("connect-") && !f.includes("."));
6223
6731
  const exports = connectors.map((c) => {
6224
6732
  const name = c.replace("connect-", "");
@@ -6232,24 +6740,24 @@ function updateConnectorsIndex(connectorsDir) {
6232
6740
 
6233
6741
  ${exports}
6234
6742
  `;
6235
- writeFileSync(indexPath, content);
6743
+ writeFileSync2(indexPath, content);
6236
6744
  }
6237
6745
  function getInstalledConnectors(targetDir = process.cwd()) {
6238
- const connectorsDir = join3(targetDir, ".connectors");
6239
- if (!existsSync2(connectorsDir)) {
6746
+ const connectorsDir = join4(targetDir, ".connectors");
6747
+ if (!existsSync3(connectorsDir)) {
6240
6748
  return [];
6241
6749
  }
6242
6750
  return readdirSync(connectorsDir).filter((f) => {
6243
- const fullPath = join3(connectorsDir, f);
6751
+ const fullPath = join4(connectorsDir, f);
6244
6752
  return f.startsWith("connect-") && statSync(fullPath).isDirectory();
6245
6753
  }).map((f) => f.replace("connect-", ""));
6246
6754
  }
6247
6755
  function getConnectorDocs(name) {
6248
6756
  const connectorPath = getConnectorPath(name);
6249
- const claudeMdPath = join3(connectorPath, "CLAUDE.md");
6250
- if (!existsSync2(claudeMdPath))
6757
+ const claudeMdPath = join4(connectorPath, "CLAUDE.md");
6758
+ if (!existsSync3(claudeMdPath))
6251
6759
  return null;
6252
- const raw = readFileSync2(claudeMdPath, "utf-8");
6760
+ const raw = readFileSync3(claudeMdPath, "utf-8");
6253
6761
  return {
6254
6762
  overview: extractSection(raw, "Project Overview"),
6255
6763
  auth: extractSection(raw, "Authentication"),
@@ -6288,9 +6796,9 @@ function parseEnvVarsTable(section) {
6288
6796
  }
6289
6797
  function removeConnector(name, targetDir = process.cwd()) {
6290
6798
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6291
- const connectorsDir = join3(targetDir, ".connectors");
6292
- const connectorPath = join3(connectorsDir, connectorName);
6293
- if (!existsSync2(connectorPath)) {
6799
+ const connectorsDir = join4(targetDir, ".connectors");
6800
+ const connectorPath = join4(connectorsDir, connectorName);
6801
+ if (!existsSync3(connectorPath)) {
6294
6802
  return false;
6295
6803
  }
6296
6804
  rmSync(connectorPath, { recursive: true });
@@ -6299,16 +6807,16 @@ function removeConnector(name, targetDir = process.cwd()) {
6299
6807
  }
6300
6808
 
6301
6809
  // src/server/auth.ts
6302
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
6810
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
6303
6811
  import { randomBytes } from "crypto";
6304
- import { homedir as homedir4 } from "os";
6305
- import { join as join5 } from "path";
6812
+ import { homedir as homedir5 } from "os";
6813
+ import { join as join6 } from "path";
6306
6814
 
6307
6815
  // src/lib/lock.ts
6308
- import { openSync, closeSync, unlinkSync, existsSync as existsSync3, statSync as statSync2 } from "fs";
6309
- import { join as join4 } from "path";
6310
- import { homedir as homedir3 } from "os";
6311
- import { mkdirSync as mkdirSync3 } from "fs";
6816
+ import { openSync, closeSync, unlinkSync, existsSync as existsSync4, statSync as statSync2 } from "fs";
6817
+ import { join as join5 } from "path";
6818
+ import { homedir as homedir4 } from "os";
6819
+ import { mkdirSync as mkdirSync4 } from "fs";
6312
6820
  var LOCK_TIMEOUT_MS = 5000;
6313
6821
  var LOCK_RETRY_MS = 100;
6314
6822
  var STALE_LOCK_MS = 30000;
@@ -6322,9 +6830,9 @@ class LockTimeoutError extends Error {
6322
6830
  }
6323
6831
  }
6324
6832
  function lockPath(connector) {
6325
- const dir = join4(homedir3(), ".connectors", `connect-${connector}`);
6326
- mkdirSync3(dir, { recursive: true });
6327
- return join4(dir, ".write.lock");
6833
+ const dir = join5(homedir4(), ".connectors", `connect-${connector}`);
6834
+ mkdirSync4(dir, { recursive: true });
6835
+ return join5(dir, ".write.lock");
6328
6836
  }
6329
6837
  function isStale(path) {
6330
6838
  try {
@@ -6335,7 +6843,7 @@ function isStale(path) {
6335
6843
  }
6336
6844
  }
6337
6845
  function tryAcquire(path) {
6338
- if (existsSync3(path) && isStale(path)) {
6846
+ if (existsSync4(path) && isStale(path)) {
6339
6847
  try {
6340
6848
  unlinkSync(path);
6341
6849
  } catch {}
@@ -6427,14 +6935,14 @@ function getAuthType(name) {
6427
6935
  }
6428
6936
  function getConnectorConfigDir(name) {
6429
6937
  const connectorName = name.startsWith("connect-") ? name : `connect-${name}`;
6430
- return join5(homedir4(), ".connectors", connectorName);
6938
+ return join6(homedir5(), ".connectors", connectorName);
6431
6939
  }
6432
6940
  function getCurrentProfile(name) {
6433
6941
  const configDir = getConnectorConfigDir(name);
6434
- const currentProfileFile = join5(configDir, "current_profile");
6435
- if (existsSync4(currentProfileFile)) {
6942
+ const currentProfileFile = join6(configDir, "current_profile");
6943
+ if (existsSync5(currentProfileFile)) {
6436
6944
  try {
6437
- return readFileSync3(currentProfileFile, "utf-8").trim() || "default";
6945
+ return readFileSync4(currentProfileFile, "utf-8").trim() || "default";
6438
6946
  } catch {
6439
6947
  return "default";
6440
6948
  }
@@ -6446,16 +6954,16 @@ function loadProfileConfig(name) {
6446
6954
  const profile = getCurrentProfile(name);
6447
6955
  let flatConfig = {};
6448
6956
  let dirConfig = {};
6449
- const profileFile = join5(configDir, "profiles", `${profile}.json`);
6450
- if (existsSync4(profileFile)) {
6957
+ const profileFile = join6(configDir, "profiles", `${profile}.json`);
6958
+ if (existsSync5(profileFile)) {
6451
6959
  try {
6452
- flatConfig = JSON.parse(readFileSync3(profileFile, "utf-8"));
6960
+ flatConfig = JSON.parse(readFileSync4(profileFile, "utf-8"));
6453
6961
  } catch {}
6454
6962
  }
6455
- const profileDirConfig = join5(configDir, "profiles", profile, "config.json");
6456
- if (existsSync4(profileDirConfig)) {
6963
+ const profileDirConfig = join6(configDir, "profiles", profile, "config.json");
6964
+ if (existsSync5(profileDirConfig)) {
6457
6965
  try {
6458
- dirConfig = JSON.parse(readFileSync3(profileDirConfig, "utf-8"));
6966
+ dirConfig = JSON.parse(readFileSync4(profileDirConfig, "utf-8"));
6459
6967
  } catch {}
6460
6968
  }
6461
6969
  if (Object.keys(flatConfig).length === 0 && Object.keys(dirConfig).length === 0) {
@@ -6466,10 +6974,10 @@ function loadProfileConfig(name) {
6466
6974
  function loadTokens(name) {
6467
6975
  const configDir = getConnectorConfigDir(name);
6468
6976
  const profile = getCurrentProfile(name);
6469
- const tokensFile = join5(configDir, "profiles", profile, "tokens.json");
6470
- if (existsSync4(tokensFile)) {
6977
+ const tokensFile = join6(configDir, "profiles", profile, "tokens.json");
6978
+ if (existsSync5(tokensFile)) {
6471
6979
  try {
6472
- return JSON.parse(readFileSync3(tokensFile, "utf-8"));
6980
+ return JSON.parse(readFileSync4(tokensFile, "utf-8"));
6473
6981
  } catch {
6474
6982
  return null;
6475
6983
  }
@@ -6522,43 +7030,43 @@ function _saveApiKey(name, key, field) {
6522
7030
  const profile = getCurrentProfile(name);
6523
7031
  const keyField = field || guessKeyField(name);
6524
7032
  if (keyField === "clientId" || keyField === "clientSecret") {
6525
- const credentialsFile = join5(configDir, "credentials.json");
6526
- mkdirSync4(configDir, { recursive: true });
7033
+ const credentialsFile = join6(configDir, "credentials.json");
7034
+ mkdirSync5(configDir, { recursive: true });
6527
7035
  let creds = {};
6528
- if (existsSync4(credentialsFile)) {
7036
+ if (existsSync5(credentialsFile)) {
6529
7037
  try {
6530
- creds = JSON.parse(readFileSync3(credentialsFile, "utf-8"));
7038
+ creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
6531
7039
  } catch {}
6532
7040
  }
6533
7041
  creds[keyField] = key;
6534
- writeFileSync2(credentialsFile, JSON.stringify(creds, null, 2));
7042
+ writeFileSync3(credentialsFile, JSON.stringify(creds, null, 2));
6535
7043
  return;
6536
7044
  }
6537
- const profileFile = join5(configDir, "profiles", `${profile}.json`);
6538
- const profileDir = join5(configDir, "profiles", profile);
6539
- if (existsSync4(profileFile)) {
7045
+ const profileFile = join6(configDir, "profiles", `${profile}.json`);
7046
+ const profileDir = join6(configDir, "profiles", profile);
7047
+ if (existsSync5(profileFile)) {
6540
7048
  let config = {};
6541
7049
  try {
6542
- config = JSON.parse(readFileSync3(profileFile, "utf-8"));
7050
+ config = JSON.parse(readFileSync4(profileFile, "utf-8"));
6543
7051
  } catch {}
6544
7052
  config[keyField] = key;
6545
- writeFileSync2(profileFile, JSON.stringify(config, null, 2));
7053
+ writeFileSync3(profileFile, JSON.stringify(config, null, 2));
6546
7054
  return;
6547
7055
  }
6548
- if (existsSync4(profileDir)) {
6549
- const configFile = join5(profileDir, "config.json");
7056
+ if (existsSync5(profileDir)) {
7057
+ const configFile = join6(profileDir, "config.json");
6550
7058
  let config = {};
6551
- if (existsSync4(configFile)) {
7059
+ if (existsSync5(configFile)) {
6552
7060
  try {
6553
- config = JSON.parse(readFileSync3(configFile, "utf-8"));
7061
+ config = JSON.parse(readFileSync4(configFile, "utf-8"));
6554
7062
  } catch {}
6555
7063
  }
6556
7064
  config[keyField] = key;
6557
- writeFileSync2(configFile, JSON.stringify(config, null, 2));
7065
+ writeFileSync3(configFile, JSON.stringify(config, null, 2));
6558
7066
  return;
6559
7067
  }
6560
- mkdirSync4(profileDir, { recursive: true });
6561
- writeFileSync2(join5(profileDir, "config.json"), JSON.stringify({ [keyField]: key }, null, 2));
7068
+ mkdirSync5(profileDir, { recursive: true });
7069
+ writeFileSync3(join6(profileDir, "config.json"), JSON.stringify({ [keyField]: key }, null, 2));
6562
7070
  }
6563
7071
  function guessKeyField(name) {
6564
7072
  const docs = getConnectorDocs(name);
@@ -6576,10 +7084,10 @@ function guessKeyField(name) {
6576
7084
  }
6577
7085
  function getOAuthConfig(name) {
6578
7086
  const configDir = getConnectorConfigDir(name);
6579
- const credentialsFile = join5(configDir, "credentials.json");
6580
- if (existsSync4(credentialsFile)) {
7087
+ const credentialsFile = join6(configDir, "credentials.json");
7088
+ if (existsSync5(credentialsFile)) {
6581
7089
  try {
6582
- const creds = JSON.parse(readFileSync3(credentialsFile, "utf-8"));
7090
+ const creds = JSON.parse(readFileSync4(credentialsFile, "utf-8"));
6583
7091
  return { clientId: creds.clientId, clientSecret: creds.clientSecret };
6584
7092
  } catch {}
6585
7093
  }
@@ -6658,10 +7166,10 @@ async function exchangeOAuthCode(name, code, redirectUri) {
6658
7166
  function saveOAuthTokens(name, tokens) {
6659
7167
  const configDir = getConnectorConfigDir(name);
6660
7168
  const profile = getCurrentProfile(name);
6661
- const profileDir = join5(configDir, "profiles", profile);
6662
- mkdirSync4(profileDir, { recursive: true });
6663
- const tokensFile = join5(profileDir, "tokens.json");
6664
- writeFileSync2(tokensFile, JSON.stringify(tokens, null, 2), { mode: 384 });
7169
+ const profileDir = join6(configDir, "profiles", profile);
7170
+ mkdirSync5(profileDir, { recursive: true });
7171
+ const tokensFile = join6(profileDir, "tokens.json");
7172
+ writeFileSync3(tokensFile, JSON.stringify(tokens, null, 2), { mode: 384 });
6665
7173
  }
6666
7174
  async function refreshOAuthToken(name) {
6667
7175
  return withWriteLock(name, () => _refreshOAuthToken(name));
@@ -6703,14 +7211,14 @@ async function _refreshOAuthToken(name) {
6703
7211
  }
6704
7212
  function listProfiles(name) {
6705
7213
  const configDir = getConnectorConfigDir(name);
6706
- const profilesDir = join5(configDir, "profiles");
6707
- if (!existsSync4(profilesDir))
7214
+ const profilesDir = join6(configDir, "profiles");
7215
+ if (!existsSync5(profilesDir))
6708
7216
  return ["default"];
6709
7217
  const seen = new Set;
6710
7218
  try {
6711
7219
  const entries = readdirSync2(profilesDir);
6712
7220
  for (const entry of entries) {
6713
- const fullPath = join5(profilesDir, entry);
7221
+ const fullPath = join6(profilesDir, entry);
6714
7222
  const stat = statSync3(fullPath);
6715
7223
  if (stat.isDirectory()) {
6716
7224
  seen.add(entry);
@@ -6724,24 +7232,24 @@ function listProfiles(name) {
6724
7232
  }
6725
7233
  function switchProfile(name, profile) {
6726
7234
  const configDir = getConnectorConfigDir(name);
6727
- mkdirSync4(configDir, { recursive: true });
6728
- writeFileSync2(join5(configDir, "current_profile"), profile);
7235
+ mkdirSync5(configDir, { recursive: true });
7236
+ writeFileSync3(join6(configDir, "current_profile"), profile);
6729
7237
  }
6730
7238
  function deleteProfile(name, profile) {
6731
7239
  if (profile === "default")
6732
7240
  return false;
6733
7241
  const configDir = getConnectorConfigDir(name);
6734
- const profilesDir = join5(configDir, "profiles");
6735
- const profileFile = join5(profilesDir, `${profile}.json`);
6736
- if (existsSync4(profileFile)) {
7242
+ const profilesDir = join6(configDir, "profiles");
7243
+ const profileFile = join6(profilesDir, `${profile}.json`);
7244
+ if (existsSync5(profileFile)) {
6737
7245
  rmSync2(profileFile);
6738
7246
  if (getCurrentProfile(name) === profile) {
6739
7247
  switchProfile(name, "default");
6740
7248
  }
6741
7249
  return true;
6742
7250
  }
6743
- const profileDir = join5(profilesDir, profile);
6744
- if (existsSync4(profileDir)) {
7251
+ const profileDir = join6(profilesDir, profile);
7252
+ if (existsSync5(profileDir)) {
6745
7253
  rmSync2(profileDir, { recursive: true });
6746
7254
  if (getCurrentProfile(name) === profile) {
6747
7255
  switchProfile(name, "default");
@@ -6764,20 +7272,20 @@ function resolveDashboardDir() {
6764
7272
  const candidates = [];
6765
7273
  try {
6766
7274
  const scriptDir = dirname3(fileURLToPath3(import.meta.url));
6767
- candidates.push(join6(scriptDir, "..", "dashboard", "dist"));
6768
- candidates.push(join6(scriptDir, "..", "..", "dashboard", "dist"));
7275
+ candidates.push(join7(scriptDir, "..", "dashboard", "dist"));
7276
+ candidates.push(join7(scriptDir, "..", "..", "dashboard", "dist"));
6769
7277
  } catch {}
6770
7278
  if (process.argv[1]) {
6771
7279
  const mainDir = dirname3(process.argv[1]);
6772
- candidates.push(join6(mainDir, "..", "dashboard", "dist"));
6773
- candidates.push(join6(mainDir, "..", "..", "dashboard", "dist"));
7280
+ candidates.push(join7(mainDir, "..", "dashboard", "dist"));
7281
+ candidates.push(join7(mainDir, "..", "..", "dashboard", "dist"));
6774
7282
  }
6775
- candidates.push(join6(process.cwd(), "dashboard", "dist"));
7283
+ candidates.push(join7(process.cwd(), "dashboard", "dist"));
6776
7284
  for (const candidate of candidates) {
6777
- if (existsSync5(candidate))
7285
+ if (existsSync6(candidate))
6778
7286
  return candidate;
6779
7287
  }
6780
- return join6(process.cwd(), "dashboard", "dist");
7288
+ return join7(process.cwd(), "dashboard", "dist");
6781
7289
  }
6782
7290
  var MIME_TYPES = {
6783
7291
  ".html": "text/html; charset=utf-8",
@@ -6805,6 +7313,18 @@ function json(data, status = 200, port) {
6805
7313
  }
6806
7314
  });
6807
7315
  }
7316
+ async function jsonStripped(data, status = 200, port) {
7317
+ const raw = JSON.stringify(data);
7318
+ const body = await maybeStrip(raw);
7319
+ return new Response(body, {
7320
+ status,
7321
+ headers: {
7322
+ "Content-Type": "application/json",
7323
+ "Access-Control-Allow-Origin": port ? `http://localhost:${port}` : "*",
7324
+ ...SECURITY_HEADERS
7325
+ }
7326
+ });
7327
+ }
6808
7328
  function htmlResponse(content, status = 200) {
6809
7329
  return new Response(content, {
6810
7330
  status,
@@ -6867,7 +7387,7 @@ function oauthPage(type, title, message, hint, extra) {
6867
7387
  </body></html>`;
6868
7388
  }
6869
7389
  function serveStaticFile(filePath) {
6870
- if (!existsSync5(filePath))
7390
+ if (!existsSync6(filePath))
6871
7391
  return null;
6872
7392
  const ext = extname(filePath);
6873
7393
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -6891,7 +7411,7 @@ async function startServer(requestedPort, options) {
6891
7411
  const shouldOpen = options?.open ?? true;
6892
7412
  loadConnectorVersions();
6893
7413
  const dashboardDir = resolveDashboardDir();
6894
- const dashboardExists = existsSync5(dashboardDir);
7414
+ const dashboardExists = existsSync6(dashboardDir);
6895
7415
  if (!dashboardExists) {
6896
7416
  console.error(`
6897
7417
  Dashboard not found at: ${dashboardDir}`);
@@ -6920,10 +7440,10 @@ Dashboard not found at: ${dashboardDir}`);
6920
7440
  const fields = fieldsParam ? new Set(fieldsParam.split(",").map((f) => f.trim())) : null;
6921
7441
  const data = getAllConnectorsWithAuth();
6922
7442
  if (compact) {
6923
- return json(data.map((c) => ({ name: c.name, category: c.category, installed: c.installed })), 200, port);
7443
+ return jsonStripped(data.map((c) => ({ name: c.name, category: c.category, installed: c.installed })), 200, port);
6924
7444
  }
6925
7445
  if (fields) {
6926
- return json(data.map((c) => {
7446
+ return jsonStripped(data.map((c) => {
6927
7447
  const out = {};
6928
7448
  for (const f of fields) {
6929
7449
  if (f in c)
@@ -6932,7 +7452,7 @@ Dashboard not found at: ${dashboardDir}`);
6932
7452
  return out;
6933
7453
  }), 200, port);
6934
7454
  }
6935
- return json(data, 200, port);
7455
+ return jsonStripped(data, 200, port);
6936
7456
  }
6937
7457
  const singleMatch = path.match(/^\/api\/connectors\/([^/]+)$/);
6938
7458
  if (singleMatch && method === "GET") {
@@ -7040,6 +7560,110 @@ Dashboard not found at: ${dashboardDir}`);
7040
7560
  if (path === "/api/activity" && method === "GET") {
7041
7561
  return json(activityLog, 200, port);
7042
7562
  }
7563
+ if (path === "/api/llm" && method === "GET") {
7564
+ const config = getLlmConfig();
7565
+ if (!config)
7566
+ return json({ configured: false }, 200, port);
7567
+ return json({ configured: true, provider: config.provider, model: config.model, key: maskKey(config.api_key), strip: config.strip }, 200, port);
7568
+ }
7569
+ if (path === "/api/llm" && method === "POST") {
7570
+ const body = await req.json().catch(() => ({}));
7571
+ const validProviders = ["cerebras", "groq", "openai", "anthropic"];
7572
+ const provider = body.provider;
7573
+ if (!provider || !validProviders.includes(provider))
7574
+ return json({ error: "provider must be one of: " + validProviders.join(", ") }, 400, port);
7575
+ const api_key = body.api_key;
7576
+ if (!api_key)
7577
+ return json({ error: "api_key is required" }, 400, port);
7578
+ const model = body.model || getLlmConfig()?.model || "qwen-3-32b";
7579
+ const strip = typeof body.strip === "boolean" ? body.strip : getLlmConfig()?.strip ?? false;
7580
+ saveLlmConfig({ provider, model, api_key, strip });
7581
+ return json({ success: true, provider, model, strip }, 200, port);
7582
+ }
7583
+ if (path === "/api/llm/test" && method === "POST") {
7584
+ const config = getLlmConfig();
7585
+ if (!config)
7586
+ return json({ error: "No LLM configured" }, 400, port);
7587
+ try {
7588
+ const client = new LLMClient(config);
7589
+ const result = await client.complete('Respond with exactly: {"status":"ok"}', "ping");
7590
+ return json({ success: true, provider: result.provider, model: result.model, latency_ms: result.latency_ms, response: result.content }, 200, port);
7591
+ } catch (e) {
7592
+ return json({ success: false, error: e instanceof Error ? e.message : String(e) }, 500, port);
7593
+ }
7594
+ }
7595
+ if (path === "/api/jobs" && method === "GET") {
7596
+ return json(listJobs(getDatabase2()), 200, port);
7597
+ }
7598
+ if (path === "/api/jobs" && method === "POST") {
7599
+ const body = await req.json().catch(() => ({}));
7600
+ if (!body.name || !body.connector || !body.command || !body.cron)
7601
+ return json({ error: "name, connector, command, cron required" }, 400, port);
7602
+ const job = createJob({ name: body.name, connector: body.connector, command: body.command, args: body.args ?? [], cron: body.cron, strip: !!body.strip }, getDatabase2());
7603
+ return json(job, 201, port);
7604
+ }
7605
+ const jobMatch = path.match(/^\/api\/jobs\/([^/]+)$/);
7606
+ if (jobMatch) {
7607
+ const db = getDatabase2();
7608
+ const job = getJobByName(jobMatch[1]) ?? getDatabase2().query("SELECT * FROM connector_jobs WHERE id = ?").get(jobMatch[1]);
7609
+ if (!job && method !== "DELETE")
7610
+ return json({ error: "Job not found" }, 404, port);
7611
+ if (method === "GET")
7612
+ return json(listJobRuns(job.id, 20, db), 200, port);
7613
+ if (method === "DELETE") {
7614
+ const j = getJobByName(jobMatch[1], db);
7615
+ if (!j)
7616
+ return json({ error: "Job not found" }, 404, port);
7617
+ deleteJob(j.id, db);
7618
+ return json({ success: true }, 200, port);
7619
+ }
7620
+ if (method === "PATCH") {
7621
+ const body = await req.json().catch(() => ({}));
7622
+ const j = getJobByName(jobMatch[1], db);
7623
+ const updated = updateJob(j.id, { enabled: typeof body.enabled === "boolean" ? body.enabled : undefined, strip: typeof body.strip === "boolean" ? body.strip : undefined }, db);
7624
+ return json(updated, 200, port);
7625
+ }
7626
+ }
7627
+ const jobRunMatch = path.match(/^\/api\/jobs\/([^/]+)\/run$/);
7628
+ if (jobRunMatch && method === "POST") {
7629
+ const db = getDatabase2();
7630
+ const job = getJobByName(jobRunMatch[1], db);
7631
+ if (!job)
7632
+ return json({ error: "Job not found" }, 404, port);
7633
+ const result = await triggerJob(job, db);
7634
+ return json(result, 200, port);
7635
+ }
7636
+ if (path === "/api/workflows" && method === "GET") {
7637
+ return json(listWorkflows(getDatabase2()), 200, port);
7638
+ }
7639
+ if (path === "/api/workflows" && method === "POST") {
7640
+ const body = await req.json().catch(() => ({}));
7641
+ if (!body.name || !body.steps)
7642
+ return json({ error: "name and steps required" }, 400, port);
7643
+ const wf = createWorkflow({ name: body.name, steps: body.steps }, getDatabase2());
7644
+ return json(wf, 201, port);
7645
+ }
7646
+ const wfMatch = path.match(/^\/api\/workflows\/([^/]+)$/);
7647
+ if (wfMatch) {
7648
+ const db = getDatabase2();
7649
+ const wf = getWorkflowByName(wfMatch[1], db);
7650
+ if (!wf)
7651
+ return json({ error: "Workflow not found" }, 404, port);
7652
+ if (method === "GET")
7653
+ return json(wf, 200, port);
7654
+ if (method === "DELETE") {
7655
+ deleteWorkflow(wf.id, db);
7656
+ return json({ success: true }, 200, port);
7657
+ }
7658
+ }
7659
+ const wfRunMatch = path.match(/^\/api\/workflows\/([^/]+)\/run$/);
7660
+ if (wfRunMatch && method === "POST") {
7661
+ const wf = getWorkflowByName(wfRunMatch[1], getDatabase2());
7662
+ if (!wf)
7663
+ return json({ error: "Workflow not found" }, 404, port);
7664
+ const result = await runWorkflow(wf);
7665
+ return json(result, 200, port);
7666
+ }
7043
7667
  if (path === "/api/agents" && method === "GET") {
7044
7668
  return json(listAgents(), 200, port);
7045
7669
  }
@@ -7080,12 +7704,12 @@ Dashboard not found at: ${dashboardDir}`);
7080
7704
  return json({ error: "Invalid connector name" }, 400, port);
7081
7705
  try {
7082
7706
  const profiles = listProfiles(name);
7083
- const configDir = join6(homedir5(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
7084
- const currentProfileFile = join6(configDir, "current_profile");
7707
+ const configDir = join7(homedir6(), ".connectors", name.startsWith("connect-") ? name : `connect-${name}`);
7708
+ const currentProfileFile = join7(configDir, "current_profile");
7085
7709
  let current = "default";
7086
- if (existsSync5(currentProfileFile)) {
7710
+ if (existsSync6(currentProfileFile)) {
7087
7711
  try {
7088
- current = readFileSync4(currentProfileFile, "utf-8").trim() || "default";
7712
+ current = readFileSync5(currentProfileFile, "utf-8").trim() || "default";
7089
7713
  } catch {}
7090
7714
  }
7091
7715
  return json({ current, profiles }, 200, port);
@@ -7132,16 +7756,16 @@ Dashboard not found at: ${dashboardDir}`);
7132
7756
  }
7133
7757
  if (path === "/api/export" && method === "GET") {
7134
7758
  try {
7135
- const connectDir = join6(homedir5(), ".connectors");
7759
+ const connectDir = join7(homedir6(), ".connectors");
7136
7760
  const result = {};
7137
- if (existsSync5(connectDir)) {
7761
+ if (existsSync6(connectDir)) {
7138
7762
  const entries = readdirSync3(connectDir, { withFileTypes: true });
7139
7763
  for (const entry of entries) {
7140
7764
  if (!entry.isDirectory() || !entry.name.startsWith("connect-"))
7141
7765
  continue;
7142
7766
  const connectorName = entry.name.replace(/^connect-/, "");
7143
- const profilesDir = join6(connectDir, entry.name, "profiles");
7144
- if (!existsSync5(profilesDir))
7767
+ const profilesDir = join7(connectDir, entry.name, "profiles");
7768
+ if (!existsSync6(profilesDir))
7145
7769
  continue;
7146
7770
  const profiles = {};
7147
7771
  const profileEntries = readdirSync3(profilesDir, { withFileTypes: true });
@@ -7149,15 +7773,15 @@ Dashboard not found at: ${dashboardDir}`);
7149
7773
  if (pEntry.isFile() && pEntry.name.endsWith(".json")) {
7150
7774
  const profileName = basename(pEntry.name, ".json");
7151
7775
  try {
7152
- const config = JSON.parse(readFileSync4(join6(profilesDir, pEntry.name), "utf-8"));
7776
+ const config = JSON.parse(readFileSync5(join7(profilesDir, pEntry.name), "utf-8"));
7153
7777
  profiles[profileName] = config;
7154
7778
  } catch {}
7155
7779
  }
7156
7780
  if (pEntry.isDirectory()) {
7157
- const configPath = join6(profilesDir, pEntry.name, "config.json");
7158
- if (existsSync5(configPath)) {
7781
+ const configPath = join7(profilesDir, pEntry.name, "config.json");
7782
+ if (existsSync6(configPath)) {
7159
7783
  try {
7160
- const config = JSON.parse(readFileSync4(configPath, "utf-8"));
7784
+ const config = JSON.parse(readFileSync5(configPath, "utf-8"));
7161
7785
  profiles[pEntry.name] = config;
7162
7786
  } catch {}
7163
7787
  }
@@ -7192,20 +7816,20 @@ Dashboard not found at: ${dashboardDir}`);
7192
7816
  return json({ error: "Invalid import format: missing 'connectors' object" }, 400, port);
7193
7817
  }
7194
7818
  let imported = 0;
7195
- const connectDir = join6(homedir5(), ".connectors");
7819
+ const connectDir = join7(homedir6(), ".connectors");
7196
7820
  for (const [connectorName, data] of Object.entries(body.connectors)) {
7197
7821
  if (!isValidConnectorName(connectorName))
7198
7822
  continue;
7199
7823
  if (!data.profiles || typeof data.profiles !== "object")
7200
7824
  continue;
7201
- const connectorDir = join6(connectDir, `connect-${connectorName}`);
7202
- const profilesDir = join6(connectorDir, "profiles");
7825
+ const connectorDir = join7(connectDir, `connect-${connectorName}`);
7826
+ const profilesDir = join7(connectorDir, "profiles");
7203
7827
  for (const [profileName, config] of Object.entries(data.profiles)) {
7204
7828
  if (!config || typeof config !== "object")
7205
7829
  continue;
7206
- mkdirSync5(profilesDir, { recursive: true });
7207
- const profileFile = join6(profilesDir, `${profileName}.json`);
7208
- writeFileSync3(profileFile, JSON.stringify(config, null, 2));
7830
+ mkdirSync6(profilesDir, { recursive: true });
7831
+ const profileFile = join7(profilesDir, `${profileName}.json`);
7832
+ writeFileSync4(profileFile, JSON.stringify(config, null, 2));
7209
7833
  imported++;
7210
7834
  }
7211
7835
  }
@@ -7260,12 +7884,12 @@ Dashboard not found at: ${dashboardDir}`);
7260
7884
  }
7261
7885
  if (dashboardExists && (method === "GET" || method === "HEAD")) {
7262
7886
  if (path !== "/") {
7263
- const filePath = join6(dashboardDir, path);
7887
+ const filePath = join7(dashboardDir, path);
7264
7888
  const res2 = serveStaticFile(filePath);
7265
7889
  if (res2)
7266
7890
  return res2;
7267
7891
  }
7268
- const indexPath = join6(dashboardDir, "index.html");
7892
+ const indexPath = join7(dashboardDir, "index.html");
7269
7893
  const res = serveStaticFile(indexPath);
7270
7894
  if (res)
7271
7895
  return res;
@@ -7279,6 +7903,9 @@ Dashboard not found at: ${dashboardDir}`);
7279
7903
  };
7280
7904
  process.on("SIGINT", shutdown);
7281
7905
  process.on("SIGTERM", shutdown);
7906
+ const { startScheduler: startScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), exports_scheduler));
7907
+ const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_database(), exports_database));
7908
+ startScheduler2(getDatabase2());
7282
7909
  const url = `http://localhost:${port}`;
7283
7910
  console.log(`Connectors Dashboard running at ${url}`);
7284
7911
  if (shouldOpen) {