@hasna/configs 0.2.33 → 0.2.35

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/dist/mcp/index.js CHANGED
@@ -32,14 +32,14 @@ import { join, relative } from "path";
32
32
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
33
33
  import { homedir as homedir2 } from "os";
34
34
  import { join as join2 } from "path";
35
- import { readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
36
- import { join as join3 } from "path";
37
- import { homedir as homedir3 } from "os";
35
+ import { readdirSync as readdirSync3, existsSync as existsSync6 } from "fs";
36
+ import { join as join6 } from "path";
37
+ import { homedir as homedir5 } from "os";
38
38
  import { hostname } from "os";
39
- import { homedir as homedir4 } from "os";
40
- import { join as join4 } from "path";
41
- import { join as join6, dirname } from "path";
42
- import { homedir as homedir5, platform } from "os";
39
+ import { homedir as homedir3 } from "os";
40
+ import { join as join3 } from "path";
41
+ import { join as join5, dirname } from "path";
42
+ import { homedir as homedir4, platform } from "os";
43
43
  function __accessProp(key) {
44
44
  return this[key];
45
45
  }
@@ -993,11 +993,11 @@ function isSyncExcludedTable(table) {
993
993
  return SYNC_EXCLUDED_TABLE_PATTERNS.some((p) => p.test(table));
994
994
  }
995
995
  function discoverServices() {
996
- const dataDir = join3(homedir3(), ".hasna");
997
- if (!existsSync3(dataDir))
996
+ const dataDir = join6(homedir5(), ".hasna");
997
+ if (!existsSync6(dataDir))
998
998
  return [];
999
999
  try {
1000
- const entries = readdirSync2(dataDir, { withFileTypes: true });
1000
+ const entries = readdirSync3(dataDir, { withFileTypes: true });
1001
1001
  return entries.filter((e) => {
1002
1002
  if (!e.isDirectory())
1003
1003
  return false;
@@ -1009,30 +1009,30 @@ function discoverServices() {
1009
1009
  return [];
1010
1010
  }
1011
1011
  }
1012
- function discoverSyncableServices() {
1012
+ function discoverSyncableServices2() {
1013
1013
  const local = discoverServices();
1014
1014
  const pgSet = new Set(KNOWN_PG_SERVICES);
1015
1015
  return local.filter((s) => pgSet.has(s));
1016
1016
  }
1017
1017
  function getServiceDbPath(service) {
1018
- const dataDir = join3(homedir3(), ".hasna", service);
1019
- if (!existsSync3(dataDir))
1018
+ const dataDir = join6(homedir5(), ".hasna", service);
1019
+ if (!existsSync6(dataDir))
1020
1020
  return null;
1021
1021
  const candidates = [
1022
- join3(dataDir, `${service}.db`),
1023
- join3(dataDir, "data.db"),
1024
- join3(dataDir, "database.db")
1022
+ join6(dataDir, `${service}.db`),
1023
+ join6(dataDir, "data.db"),
1024
+ join6(dataDir, "database.db")
1025
1025
  ];
1026
1026
  try {
1027
- const files = readdirSync2(dataDir);
1027
+ const files = readdirSync3(dataDir);
1028
1028
  for (const f of files) {
1029
1029
  if (f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm")) {
1030
- candidates.push(join3(dataDir, f));
1030
+ candidates.push(join6(dataDir, f));
1031
1031
  }
1032
1032
  }
1033
1033
  } catch {}
1034
1034
  for (const p of candidates) {
1035
- if (existsSync3(p))
1035
+ if (existsSync6(p))
1036
1036
  return p;
1037
1037
  }
1038
1038
  return null;
@@ -1339,9 +1339,9 @@ async function syncTransfer(source, target, options, _direction) {
1339
1339
  const batch = rows.slice(offset, offset + batchSize);
1340
1340
  try {
1341
1341
  if (isAsyncAdapter(target)) {
1342
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
1342
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
1343
1343
  } else {
1344
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
1344
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
1345
1345
  }
1346
1346
  result.rowsWritten += batch.length;
1347
1347
  } catch (err) {
@@ -1388,7 +1388,7 @@ async function syncTransfer(source, target, options, _direction) {
1388
1388
  }
1389
1389
  return results;
1390
1390
  }
1391
- async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
1391
+ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch) {
1392
1392
  if (batch.length === 0)
1393
1393
  return;
1394
1394
  const colList = columns.map((c) => `"${c}"`).join(", ");
@@ -1398,22 +1398,20 @@ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, ba
1398
1398
  }).join(", ");
1399
1399
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
1400
1400
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
1401
- const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
1402
1401
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
1403
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
1402
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
1404
1403
  const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
1405
1404
  await target.run(sql, ...params);
1406
1405
  }
1407
- function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
1406
+ function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch) {
1408
1407
  if (batch.length === 0)
1409
1408
  return;
1410
1409
  const colList = columns.map((c) => `"${c}"`).join(", ");
1411
1410
  const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
1412
1411
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
1413
1412
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
1414
- const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
1415
1413
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
1416
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
1414
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
1417
1415
  const params = batch.flatMap((row) => columns.map((c) => coerceForSqlite(row[c])));
1418
1416
  target.run(sql, ...params);
1419
1417
  }
@@ -1638,7 +1636,7 @@ class SyncProgressTracker {
1638
1636
  }
1639
1637
  }
1640
1638
  }
1641
- function registerCloudTools(server, serviceName, opts = {}) {
1639
+ function registerCloudTools(server, serviceName) {
1642
1640
  server.tool(`${serviceName}_cloud_status`, "Show cloud configuration and connection health", {}, async () => {
1643
1641
  const config = getCloudConfig();
1644
1642
  const lines = [
@@ -1671,13 +1669,8 @@ function registerCloudTools(server, serviceName, opts = {}) {
1671
1669
  isError: true
1672
1670
  };
1673
1671
  }
1674
- const local = new SqliteAdapter(opts.dbPath ?? getDbPath(serviceName));
1672
+ const local = new SqliteAdapter(getDbPath(serviceName));
1675
1673
  const cloud = new PgAdapterAsync(getConnectionString(serviceName));
1676
- if (opts.migrations?.length) {
1677
- for (const sql of opts.migrations) {
1678
- await cloud.run(sql);
1679
- }
1680
- }
1681
1674
  const tableList = tablesStr ? tablesStr.split(",").map((t) => t.trim()) : listSqliteTables(local);
1682
1675
  const results = await syncPush(local, cloud, { tables: tableList });
1683
1676
  local.close();
@@ -1699,7 +1692,7 @@ function registerCloudTools(server, serviceName, opts = {}) {
1699
1692
  isError: true
1700
1693
  };
1701
1694
  }
1702
- const local = new SqliteAdapter(opts.dbPath ?? getDbPath(serviceName));
1695
+ const local = new SqliteAdapter(getDbPath(serviceName));
1703
1696
  const cloud = new PgAdapterAsync(getConnectionString(serviceName));
1704
1697
  let tableList;
1705
1698
  if (tablesStr) {
@@ -10004,7 +9997,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
10004
9997
  __export2(exports_discover, {
10005
9998
  isSyncExcludedTable: () => isSyncExcludedTable,
10006
9999
  getServiceDbPath: () => getServiceDbPath,
10007
- discoverSyncableServices: () => discoverSyncableServices,
10000
+ discoverSyncableServices: () => discoverSyncableServices2,
10008
10001
  discoverServices: () => discoverServices,
10009
10002
  SYNC_EXCLUDED_TABLE_PATTERNS: () => SYNC_EXCLUDED_TABLE_PATTERNS,
10010
10003
  KNOWN_PG_SERVICES: () => KNOWN_PG_SERVICES
@@ -10059,15 +10052,13 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
10059
10052
  init_config();
10060
10053
  init_config();
10061
10054
  init_dotfile();
10062
- init_adapter();
10063
10055
  init_config();
10064
- init_discover();
10065
- AUTO_SYNC_CONFIG_PATH = join4(homedir4(), ".hasna", "cloud", "config.json");
10056
+ AUTO_SYNC_CONFIG_PATH = join3(homedir3(), ".hasna", "cloud", "config.json");
10066
10057
  init_config();
10067
10058
  init_adapter();
10068
10059
  init_dotfile();
10069
10060
  init_config();
10070
- CONFIG_DIR2 = join6(homedir5(), ".hasna", "cloud");
10061
+ CONFIG_DIR2 = join5(homedir4(), ".hasna", "cloud");
10071
10062
  init_adapter();
10072
10063
  init_config();
10073
10064
  init_discover();
@@ -10119,7 +10110,7 @@ __export(exports_database, {
10119
10110
  getDatabase: () => getDatabase
10120
10111
  });
10121
10112
  import { mkdirSync as mkdirSync3 } from "fs";
10122
- import { join as join5 } from "path";
10113
+ import { join as join4 } from "path";
10123
10114
  import { randomUUID } from "crypto";
10124
10115
  function getDbPath2() {
10125
10116
  if (process.env["HASNA_CONFIGS_DB_PATH"]) {
@@ -10130,9 +10121,9 @@ function getDbPath2() {
10130
10121
  }
10131
10122
  migrateDotfile("configs");
10132
10123
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
10133
- const dir = join5(home, ".hasna", "configs");
10124
+ const dir = join4(home, ".hasna", "configs");
10134
10125
  mkdirSync3(dir, { recursive: true });
10135
- return join5(dir, "configs.db");
10126
+ return join4(dir, "configs.db");
10136
10127
  }
10137
10128
  function uuid() {
10138
10129
  return randomUUID();
@@ -10499,7 +10490,7 @@ var init_template = __esm(() => {
10499
10490
 
10500
10491
  // src/lib/machine.ts
10501
10492
  import { arch as currentArch, homedir as homedir6, hostname as currentHostname, type as currentOsType } from "os";
10502
- import { existsSync as existsSync6 } from "fs";
10493
+ import { existsSync as existsSync5 } from "fs";
10503
10494
  import { join as join7 } from "path";
10504
10495
  function normalizeOsFamily(os) {
10505
10496
  const value = (os ?? "").trim().toLowerCase();
@@ -10516,7 +10507,7 @@ function detectMachineContext(overrides = {}) {
10516
10507
  const os = overrides.os ?? currentOsType();
10517
10508
  const osFamily = normalizeOsFamily(os);
10518
10509
  const bunBinDir = overrides.bun_bin_dir ?? join7(homeDir, ".bun", "bin");
10519
- const defaultBunPath = osFamily === "macos" && existsSync6(BREW_BUN_PATH) ? BREW_BUN_PATH : join7(bunBinDir, "bun");
10510
+ const defaultBunPath = osFamily === "macos" && existsSync5(BREW_BUN_PATH) ? BREW_BUN_PATH : join7(bunBinDir, "bun");
10520
10511
  return {
10521
10512
  id: "current-machine",
10522
10513
  hostname: overrides.hostname ?? currentHostname(),
@@ -10864,8 +10855,8 @@ __export(exports_sync, {
10864
10855
  PROJECT_CONFIG_FILES: () => PROJECT_CONFIG_FILES,
10865
10856
  KNOWN_CONFIGS: () => KNOWN_CONFIGS
10866
10857
  });
10867
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync3 } from "fs";
10868
- import { extname, join as join8 } from "path";
10858
+ import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
10859
+ import { extname, join as join9 } from "path";
10869
10860
  import { homedir as homedir9 } from "os";
10870
10861
  async function syncProject(opts) {
10871
10862
  const d = opts.db || getDatabase();
@@ -10875,8 +10866,8 @@ async function syncProject(opts) {
10875
10866
  const allConfigs = listConfigs(undefined, d);
10876
10867
  const machine = detectMachineContext();
10877
10868
  for (const pf of PROJECT_CONFIG_FILES) {
10878
- const abs = join8(absDir, pf.file);
10879
- if (!existsSync8(abs))
10869
+ const abs = join9(absDir, pf.file);
10870
+ if (!existsSync9(abs))
10880
10871
  continue;
10881
10872
  try {
10882
10873
  const rawContent = readFileSync3(abs, "utf-8");
@@ -10907,11 +10898,11 @@ async function syncProject(opts) {
10907
10898
  result.skipped.push(pf.file);
10908
10899
  }
10909
10900
  }
10910
- const rulesDir = join8(absDir, ".claude", "rules");
10911
- if (existsSync8(rulesDir)) {
10912
- const mdFiles = readdirSync3(rulesDir).filter((f) => f.endsWith(".md"));
10901
+ const rulesDir = join9(absDir, ".claude", "rules");
10902
+ if (existsSync9(rulesDir)) {
10903
+ const mdFiles = readdirSync2(rulesDir).filter((f) => f.endsWith(".md"));
10913
10904
  for (const f of mdFiles) {
10914
- const abs = join8(rulesDir, f);
10905
+ const abs = join9(rulesDir, f);
10915
10906
  const raw = readFileSync3(abs, "utf-8");
10916
10907
  const redacted = redactContent(raw, "markdown");
10917
10908
  const machineAware = templateizeMachineContent(redacted.content, machine);
@@ -10950,13 +10941,13 @@ async function syncKnown(opts = {}) {
10950
10941
  for (const known of targets) {
10951
10942
  if (known.rulesDir) {
10952
10943
  const absDir = expandPath(known.rulesDir);
10953
- if (!existsSync8(absDir)) {
10944
+ if (!existsSync9(absDir)) {
10954
10945
  result.skipped.push(known.rulesDir);
10955
10946
  continue;
10956
10947
  }
10957
- const mdFiles = readdirSync3(absDir).filter((f) => f.endsWith(".md"));
10948
+ const mdFiles = readdirSync2(absDir).filter((f) => f.endsWith(".md"));
10958
10949
  for (const f of mdFiles) {
10959
- const abs2 = join8(absDir, f);
10950
+ const abs2 = join9(absDir, f);
10960
10951
  const targetPath = abs2.replace(home, "~");
10961
10952
  const raw = readFileSync3(abs2, "utf-8");
10962
10953
  const redacted = redactContent(raw, "markdown");
@@ -10981,7 +10972,7 @@ async function syncKnown(opts = {}) {
10981
10972
  continue;
10982
10973
  }
10983
10974
  const abs = expandPath(known.path);
10984
- if (!existsSync8(abs)) {
10975
+ if (!existsSync9(abs)) {
10985
10976
  result.skipped.push(known.path);
10986
10977
  continue;
10987
10978
  }
@@ -11046,7 +11037,7 @@ function diffConfig(config) {
11046
11037
  if (!config.target_path)
11047
11038
  return "(reference \u2014 no target path)";
11048
11039
  const path = expandPath(config.target_path);
11049
- if (!existsSync8(path))
11040
+ if (!existsSync9(path))
11050
11041
  return `(file not found on disk: ${path})`;
11051
11042
  const diskContent = readFileSync3(path, "utf-8");
11052
11043
  if (diskContent === config.content)
@@ -11161,8 +11152,8 @@ var init_sync = __esm(() => {
11161
11152
  });
11162
11153
 
11163
11154
  // src/lib/sync-dir.ts
11164
- import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync4, statSync } from "fs";
11165
- import { join as join9, relative as relative2 } from "path";
11155
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync } from "fs";
11156
+ import { join as join10, relative as relative2 } from "path";
11166
11157
  import { homedir as homedir10 } from "os";
11167
11158
  function shouldSkip(p) {
11168
11159
  return SKIP.some((s) => p.includes(s));
@@ -11170,9 +11161,9 @@ function shouldSkip(p) {
11170
11161
  async function syncFromDir(dir, opts = {}) {
11171
11162
  const d = opts.db || getDatabase();
11172
11163
  const absDir = expandPath(dir);
11173
- if (!existsSync9(absDir))
11164
+ if (!existsSync10(absDir))
11174
11165
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
11175
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync5(absDir).map((f) => join9(absDir, f)).filter((f) => statSync(f).isFile());
11166
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) => join10(absDir, f)).filter((f) => statSync(f).isFile());
11176
11167
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
11177
11168
  const home = homedir10();
11178
11169
  const allConfigs = listConfigs(undefined, d);
@@ -11226,8 +11217,8 @@ async function syncToDir(dir, opts = {}) {
11226
11217
  return result;
11227
11218
  }
11228
11219
  function walkDir(dir, files = []) {
11229
- for (const entry of readdirSync5(dir, { withFileTypes: true })) {
11230
- const full = join9(dir, entry.name);
11220
+ for (const entry of readdirSync4(dir, { withFileTypes: true })) {
11221
+ const full = join10(dir, entry.name);
11231
11222
  if (shouldSkip(full))
11232
11223
  continue;
11233
11224
  if (entry.isDirectory())
@@ -11250,7 +11241,7 @@ var init_sync_dir = __esm(() => {
11250
11241
  var require_package = __commonJS((exports, module) => {
11251
11242
  module.exports = {
11252
11243
  name: "@hasna/configs",
11253
- version: "0.2.33",
11244
+ version: "0.2.35",
11254
11245
  description: "AI coding agent configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.",
11255
11246
  type: "module",
11256
11247
  main: "dist/index.js",
@@ -11315,7 +11306,8 @@ var require_package = __commonJS((exports, module) => {
11315
11306
  author: "Andrei Hasna <andrei@hasna.com>",
11316
11307
  license: "Apache-2.0",
11317
11308
  dependencies: {
11318
- "@hasna/cloud": "^0.1.24",
11309
+ "@hasna/cloud": "0.1.24",
11310
+ "@hasna/events": "^0.1.6",
11319
11311
  "@modelcontextprotocol/sdk": "^1.12.1",
11320
11312
  chalk: "^5.4.1",
11321
11313
  commander: "^13.1.0",