@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/cli/index.js +781 -98
- package/dist/index.js +60 -62
- package/dist/mcp/index.js +58 -66
- package/dist/server/index.js +225 -132
- package/package.json +3 -2
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
|
|
36
|
-
import { join as
|
|
37
|
-
import { homedir as
|
|
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
|
|
40
|
-
import { join as
|
|
41
|
-
import { join as
|
|
42
|
-
import { homedir as
|
|
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 =
|
|
997
|
-
if (!
|
|
996
|
+
const dataDir = join6(homedir5(), ".hasna");
|
|
997
|
+
if (!existsSync6(dataDir))
|
|
998
998
|
return [];
|
|
999
999
|
try {
|
|
1000
|
-
const entries =
|
|
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
|
|
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 =
|
|
1019
|
-
if (!
|
|
1018
|
+
const dataDir = join6(homedir5(), ".hasna", service);
|
|
1019
|
+
if (!existsSync6(dataDir))
|
|
1020
1020
|
return null;
|
|
1021
1021
|
const candidates = [
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1022
|
+
join6(dataDir, `${service}.db`),
|
|
1023
|
+
join6(dataDir, "data.db"),
|
|
1024
|
+
join6(dataDir, "database.db")
|
|
1025
1025
|
];
|
|
1026
1026
|
try {
|
|
1027
|
-
const files =
|
|
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(
|
|
1030
|
+
candidates.push(join6(dataDir, f));
|
|
1031
1031
|
}
|
|
1032
1032
|
}
|
|
1033
1033
|
} catch {}
|
|
1034
1034
|
for (const p of candidates) {
|
|
1035
|
-
if (
|
|
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
|
|
1342
|
+
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
|
|
1343
1343
|
} else {
|
|
1344
|
-
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch
|
|
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
|
|
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}
|
|
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
|
|
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}
|
|
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
|
|
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(
|
|
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(
|
|
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: () =>
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
10124
|
+
const dir = join4(home, ".hasna", "configs");
|
|
10134
10125
|
mkdirSync3(dir, { recursive: true });
|
|
10135
|
-
return
|
|
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
|
|
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" &&
|
|
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
|
|
10868
|
-
import { extname, join as
|
|
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 =
|
|
10879
|
-
if (!
|
|
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 =
|
|
10911
|
-
if (
|
|
10912
|
-
const mdFiles =
|
|
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 =
|
|
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 (!
|
|
10944
|
+
if (!existsSync9(absDir)) {
|
|
10954
10945
|
result.skipped.push(known.rulesDir);
|
|
10955
10946
|
continue;
|
|
10956
10947
|
}
|
|
10957
|
-
const mdFiles =
|
|
10948
|
+
const mdFiles = readdirSync2(absDir).filter((f) => f.endsWith(".md"));
|
|
10958
10949
|
for (const f of mdFiles) {
|
|
10959
|
-
const abs2 =
|
|
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 (!
|
|
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 (!
|
|
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
|
|
11165
|
-
import { join as
|
|
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 (!
|
|
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) :
|
|
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
|
|
11230
|
-
const full =
|
|
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.
|
|
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": "
|
|
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",
|