@hasna/configs 0.2.36 → 0.2.38
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/README.md +2 -2
- package/dist/cli/index.js +166 -54
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -131
- package/dist/mcp/index.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/status.d.ts +56 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.test.d.ts +2 -0
- package/dist/status.test.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,8 +58,8 @@ Data is stored in `~/.hasna/configs/`.
|
|
|
58
58
|
|
|
59
59
|
`configs init` now seeds two platform profiles:
|
|
60
60
|
|
|
61
|
-
- `linux-arm64` for `
|
|
62
|
-
- `macos-arm64` for `
|
|
61
|
+
- `linux-arm64` for `linux-node-a` / `linux-node-b`
|
|
62
|
+
- `macos-arm64` for `macos-node-a` / `macos-node-b`
|
|
63
63
|
|
|
64
64
|
These profiles resolve machine variables like `{{WORKSPACE_ROOT}}`, `{{BUN_BIN_DIR}}`, `{{BUN_PATH}}`, and `{{PATH_PREFIX}}`, so synced configs can be portable across Linux and macOS arm64 machines.
|
|
65
65
|
|
package/dist/cli/index.js
CHANGED
|
@@ -12694,7 +12694,6 @@ var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
|
12694
12694
|
function getEventsDataDir(override) {
|
|
12695
12695
|
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join(homedir(), ".hasna", "events");
|
|
12696
12696
|
}
|
|
12697
|
-
|
|
12698
12697
|
class JsonEventsStore {
|
|
12699
12698
|
dataDir;
|
|
12700
12699
|
channelsPath;
|
|
@@ -13342,7 +13341,7 @@ var {
|
|
|
13342
13341
|
// src/cli/index.tsx
|
|
13343
13342
|
init_configs();
|
|
13344
13343
|
import chalk from "chalk";
|
|
13345
|
-
import { existsSync as
|
|
13344
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
13346
13345
|
import { homedir as homedir11 } from "os";
|
|
13347
13346
|
import { join as join13, resolve as resolve4 } from "path";
|
|
13348
13347
|
|
|
@@ -13600,8 +13599,8 @@ init_configs();
|
|
|
13600
13599
|
var PLATFORM_PROFILE_PRESETS = [
|
|
13601
13600
|
{
|
|
13602
13601
|
name: "linux-arm64",
|
|
13603
|
-
description: "Default Linux arm64 profile for
|
|
13604
|
-
selectors: { os: ["linux"], arch: ["arm64"], hostnames: ["
|
|
13602
|
+
description: "Default Linux arm64 profile for linux-node-a/linux-node-b-style machines",
|
|
13603
|
+
selectors: { os: ["linux"], arch: ["arm64"], hostnames: ["linux-node-a", "linux-node-b"] },
|
|
13605
13604
|
variables: {
|
|
13606
13605
|
WORKSPACE_ROOT: "{{HOME_DIR}}/workspace",
|
|
13607
13606
|
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
@@ -13611,8 +13610,8 @@ var PLATFORM_PROFILE_PRESETS = [
|
|
|
13611
13610
|
},
|
|
13612
13611
|
{
|
|
13613
13612
|
name: "macos-arm64",
|
|
13614
|
-
description: "Default macOS arm64 profile for
|
|
13615
|
-
selectors: { os: ["macos"], arch: ["arm64"], hostnames: ["
|
|
13613
|
+
description: "Default macOS arm64 profile for macos-node-a/macos-node-b-style machines",
|
|
13614
|
+
selectors: { os: ["macos"], arch: ["arm64"], hostnames: ["macos-node-a", "macos-node-b"] },
|
|
13616
13615
|
variables: {
|
|
13617
13616
|
WORKSPACE_ROOT: "{{HOME_DIR}}/Workspace",
|
|
13618
13617
|
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
@@ -13646,6 +13645,140 @@ function ensurePlatformProfiles(db) {
|
|
|
13646
13645
|
return ensured;
|
|
13647
13646
|
}
|
|
13648
13647
|
|
|
13648
|
+
// src/status.ts
|
|
13649
|
+
init_database();
|
|
13650
|
+
init_configs();
|
|
13651
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
|
|
13652
|
+
|
|
13653
|
+
// src/db/machines.ts
|
|
13654
|
+
init_database();
|
|
13655
|
+
function listMachines(db) {
|
|
13656
|
+
const d = db || getDatabase();
|
|
13657
|
+
return d.query("SELECT * FROM machines ORDER BY last_applied_at DESC NULLS LAST").all();
|
|
13658
|
+
}
|
|
13659
|
+
|
|
13660
|
+
// src/status.ts
|
|
13661
|
+
init_apply();
|
|
13662
|
+
init_redact();
|
|
13663
|
+
var PACKAGE_NAME = "@hasna/configs";
|
|
13664
|
+
var PACKAGE_VERSION = "0.2.38";
|
|
13665
|
+
function activeDatabaseEnv() {
|
|
13666
|
+
if (process.env["HASNA_CONFIGS_DB_PATH"])
|
|
13667
|
+
return "HASNA_CONFIGS_DB_PATH";
|
|
13668
|
+
if (process.env["CONFIGS_DB_PATH"])
|
|
13669
|
+
return "CONFIGS_DB_PATH";
|
|
13670
|
+
return null;
|
|
13671
|
+
}
|
|
13672
|
+
function configuredDatabaseKind() {
|
|
13673
|
+
const value = process.env["HASNA_CONFIGS_DB_PATH"] ?? process.env["CONFIGS_DB_PATH"] ?? "";
|
|
13674
|
+
return value === ":memory:" || value.startsWith("file::memory:") ? "memory" : "file";
|
|
13675
|
+
}
|
|
13676
|
+
function countBy(items, getValue) {
|
|
13677
|
+
const counts = {};
|
|
13678
|
+
for (const item of items) {
|
|
13679
|
+
const value = getValue(item);
|
|
13680
|
+
if (!value)
|
|
13681
|
+
continue;
|
|
13682
|
+
counts[value] = (counts[value] ?? 0) + 1;
|
|
13683
|
+
}
|
|
13684
|
+
return counts;
|
|
13685
|
+
}
|
|
13686
|
+
function tableCount(db, table) {
|
|
13687
|
+
try {
|
|
13688
|
+
const row = db.query(`SELECT COUNT(*) AS count FROM ${table}`).get();
|
|
13689
|
+
return Number(row?.count ?? 0);
|
|
13690
|
+
} catch {
|
|
13691
|
+
return 0;
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13694
|
+
function getConfigsStatus(db = getDatabase()) {
|
|
13695
|
+
let databaseReachable = true;
|
|
13696
|
+
let configs = [];
|
|
13697
|
+
let categoryStats = { total: 0 };
|
|
13698
|
+
try {
|
|
13699
|
+
configs = listConfigs(undefined, db);
|
|
13700
|
+
categoryStats = getConfigStats(db);
|
|
13701
|
+
} catch {
|
|
13702
|
+
databaseReachable = false;
|
|
13703
|
+
}
|
|
13704
|
+
const fileConfigs = configs.filter((config) => config.kind === "file");
|
|
13705
|
+
let driftedTargets = 0;
|
|
13706
|
+
let missingTargets = 0;
|
|
13707
|
+
let unredactedSecretFindings = 0;
|
|
13708
|
+
let knownTargets = 0;
|
|
13709
|
+
for (const config of fileConfigs) {
|
|
13710
|
+
unredactedSecretFindings += scanSecrets(config.content, config.format).length;
|
|
13711
|
+
if (!config.target_path)
|
|
13712
|
+
continue;
|
|
13713
|
+
knownTargets += 1;
|
|
13714
|
+
const targetPath = expandPath(config.target_path);
|
|
13715
|
+
if (!existsSync12(targetPath)) {
|
|
13716
|
+
missingTargets += 1;
|
|
13717
|
+
continue;
|
|
13718
|
+
}
|
|
13719
|
+
const disk = readFileSync6(targetPath, "utf-8");
|
|
13720
|
+
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
13721
|
+
if (redactedDisk !== config.content) {
|
|
13722
|
+
driftedTargets += 1;
|
|
13723
|
+
}
|
|
13724
|
+
}
|
|
13725
|
+
const profiles = databaseReachable ? listProfiles(db).length : 0;
|
|
13726
|
+
const machines = databaseReachable ? listMachines(db).length : 0;
|
|
13727
|
+
const profileLinks = databaseReachable ? tableCount(db, "profile_configs") : 0;
|
|
13728
|
+
const snapshots = databaseReachable ? tableCount(db, "config_snapshots") : 0;
|
|
13729
|
+
const byCategory = Object.fromEntries(Object.entries(categoryStats).filter(([key]) => key !== "total"));
|
|
13730
|
+
const status = databaseReachable && driftedTargets === 0 && missingTargets === 0 && unredactedSecretFindings === 0 ? "ok" : "warn";
|
|
13731
|
+
return {
|
|
13732
|
+
service: "configs",
|
|
13733
|
+
schemaVersion: "1.0",
|
|
13734
|
+
package: {
|
|
13735
|
+
name: PACKAGE_NAME,
|
|
13736
|
+
version: PACKAGE_VERSION
|
|
13737
|
+
},
|
|
13738
|
+
env: {
|
|
13739
|
+
database: {
|
|
13740
|
+
primary: "HASNA_CONFIGS_DB_PATH",
|
|
13741
|
+
fallback: "CONFIGS_DB_PATH",
|
|
13742
|
+
active: activeDatabaseEnv(),
|
|
13743
|
+
kind: configuredDatabaseKind()
|
|
13744
|
+
}
|
|
13745
|
+
},
|
|
13746
|
+
counts: {
|
|
13747
|
+
configs: {
|
|
13748
|
+
total: configs.length,
|
|
13749
|
+
file: fileConfigs.length,
|
|
13750
|
+
reference: configs.filter((config) => config.kind === "reference").length,
|
|
13751
|
+
templates: configs.filter((config) => config.is_template).length
|
|
13752
|
+
},
|
|
13753
|
+
byCategory,
|
|
13754
|
+
byAgent: countBy(configs, (config) => config.agent),
|
|
13755
|
+
byFormat: countBy(configs, (config) => config.format),
|
|
13756
|
+
profiles,
|
|
13757
|
+
profileLinks,
|
|
13758
|
+
machines,
|
|
13759
|
+
snapshots,
|
|
13760
|
+
knownTargets
|
|
13761
|
+
},
|
|
13762
|
+
health: {
|
|
13763
|
+
status,
|
|
13764
|
+
databaseReachable,
|
|
13765
|
+
driftedTargets,
|
|
13766
|
+
missingTargets,
|
|
13767
|
+
unredactedSecretFindings,
|
|
13768
|
+
hasDrift: driftedTargets > 0,
|
|
13769
|
+
hasMissingTargets: missingTargets > 0,
|
|
13770
|
+
hasUnredactedSecrets: unredactedSecretFindings > 0
|
|
13771
|
+
},
|
|
13772
|
+
safety: {
|
|
13773
|
+
includesConfigValues: false,
|
|
13774
|
+
includesPrivatePaths: false,
|
|
13775
|
+
includesHostnames: false,
|
|
13776
|
+
includesSecretValues: false,
|
|
13777
|
+
statusOutputIsMetadataOnly: true
|
|
13778
|
+
}
|
|
13779
|
+
};
|
|
13780
|
+
}
|
|
13781
|
+
|
|
13649
13782
|
// src/cli/index.tsx
|
|
13650
13783
|
import { createRequire as createRequire2 } from "module";
|
|
13651
13784
|
var pkg = createRequire2(import.meta.url)("../../package.json");
|
|
@@ -13759,11 +13892,11 @@ program.command("show <id>").description("Show a config's content and metadata")
|
|
|
13759
13892
|
});
|
|
13760
13893
|
program.command("add <path>").description("Ingest a file into the config DB").option("-n, --name <name>", "config name (defaults to filename)").option("-c, --category <cat>", "category override").option("-a, --agent <agent>", "agent override").option("-k, --kind <kind>", "kind: file|reference", "file").option("--template", "mark as template (has {{VAR}} placeholders)").action(async (filePath, opts) => {
|
|
13761
13894
|
const abs = resolve4(filePath);
|
|
13762
|
-
if (!
|
|
13895
|
+
if (!existsSync13(abs)) {
|
|
13763
13896
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
13764
13897
|
process.exit(1);
|
|
13765
13898
|
}
|
|
13766
|
-
const rawContent =
|
|
13899
|
+
const rawContent = readFileSync7(abs, "utf-8");
|
|
13767
13900
|
const fmt = detectFormat(abs);
|
|
13768
13901
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
13769
13902
|
const targetPath = abs.startsWith(homedir11()) ? abs.replace(homedir11(), "~") : abs;
|
|
@@ -13850,7 +13983,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
13850
13983
|
if (!entry.isDirectory())
|
|
13851
13984
|
continue;
|
|
13852
13985
|
const projDir = join13(absDir, entry.name);
|
|
13853
|
-
const hasClaude =
|
|
13986
|
+
const hasClaude = existsSync13(join13(projDir, "CLAUDE.md")) || existsSync13(join13(projDir, ".mcp.json")) || existsSync13(join13(projDir, ".claude"));
|
|
13854
13987
|
if (!hasClaude)
|
|
13855
13988
|
continue;
|
|
13856
13989
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun });
|
|
@@ -14234,7 +14367,7 @@ command = "${mcpBinary}"
|
|
|
14234
14367
|
args = []
|
|
14235
14368
|
`;
|
|
14236
14369
|
if (ex(configPath)) {
|
|
14237
|
-
const content =
|
|
14370
|
+
const content = readFileSync7(configPath, "utf-8");
|
|
14238
14371
|
if (content.includes("[mcp_servers.configs]")) {
|
|
14239
14372
|
console.log(chalk.dim("= Already installed in Codex"));
|
|
14240
14373
|
continue;
|
|
@@ -14273,7 +14406,7 @@ mcpCmd.command("uninstall").alias("remove").description("Remove configs MCP serv
|
|
|
14273
14406
|
});
|
|
14274
14407
|
program.command("init").description("First-time setup: sync all known configs, create default profile").option("--force", "delete existing DB and start fresh").action(async (opts) => {
|
|
14275
14408
|
const dbPath = join13(homedir11(), ".hasna", "configs", "configs.db");
|
|
14276
|
-
if (opts.force &&
|
|
14409
|
+
if (opts.force && existsSync13(dbPath)) {
|
|
14277
14410
|
const { rmSync: rmSync3 } = await import("fs");
|
|
14278
14411
|
rmSync3(dbPath);
|
|
14279
14412
|
console.log(chalk.dim("Deleted existing DB."));
|
|
@@ -14325,41 +14458,20 @@ DB stats:`));
|
|
|
14325
14458
|
console.log(chalk.dim(`
|
|
14326
14459
|
DB: ${dbPath}`));
|
|
14327
14460
|
});
|
|
14328
|
-
program.command("status").description("Health check: total configs, drift from disk, unredacted secrets").action(async () => {
|
|
14329
|
-
const
|
|
14330
|
-
|
|
14331
|
-
|
|
14332
|
-
|
|
14461
|
+
program.command("status").description("Health check: total configs, drift from disk, unredacted secrets").option("--json", "output metadata-only JSON").action(async (opts) => {
|
|
14462
|
+
const status = getConfigsStatus();
|
|
14463
|
+
if (opts.json) {
|
|
14464
|
+
console.log(JSON.stringify(status, null, 2));
|
|
14465
|
+
return;
|
|
14466
|
+
}
|
|
14333
14467
|
console.log(chalk.bold("@hasna/configs") + chalk.dim(` v${pkg.version}`));
|
|
14334
|
-
console.log(chalk.cyan("
|
|
14335
|
-
console.log(chalk.cyan("Total:") + ` ${
|
|
14468
|
+
console.log(chalk.cyan("Database:") + ` ${status.env.database.kind} (${status.env.database.active ?? "default"})`);
|
|
14469
|
+
console.log(chalk.cyan("Total:") + ` ${status.counts.configs.total} configs
|
|
14336
14470
|
`);
|
|
14337
|
-
|
|
14338
|
-
|
|
14339
|
-
|
|
14340
|
-
|
|
14341
|
-
let templates = 0;
|
|
14342
|
-
for (const c of allKnown) {
|
|
14343
|
-
if (!c.target_path)
|
|
14344
|
-
continue;
|
|
14345
|
-
const path = expandPath(c.target_path);
|
|
14346
|
-
if (!existsSync12(path)) {
|
|
14347
|
-
missing++;
|
|
14348
|
-
continue;
|
|
14349
|
-
}
|
|
14350
|
-
const disk = readFileSync6(path, "utf-8");
|
|
14351
|
-
const { content: redactedDisk } = redactContent(disk, c.format);
|
|
14352
|
-
if (redactedDisk !== c.content)
|
|
14353
|
-
drifted++;
|
|
14354
|
-
if (c.is_template)
|
|
14355
|
-
templates++;
|
|
14356
|
-
const found = scanSecrets(c.content, c.format);
|
|
14357
|
-
secrets += found.length;
|
|
14358
|
-
}
|
|
14359
|
-
console.log(chalk.cyan("Drifted:") + ` ${drifted === 0 ? chalk.green("0") : chalk.yellow(String(drifted))} (stored \u2260 disk)`);
|
|
14360
|
-
console.log(chalk.cyan("Missing:") + ` ${missing === 0 ? chalk.green("0") : chalk.yellow(String(missing))} (file not on disk)`);
|
|
14361
|
-
console.log(chalk.cyan("Secrets:") + ` ${secrets === 0 ? chalk.green("0 \u2713") : chalk.red(String(secrets) + " \u26A0")} unredacted`);
|
|
14362
|
-
console.log(chalk.cyan("Templates:") + ` ${templates} (with {{VAR}} placeholders)`);
|
|
14471
|
+
console.log(chalk.cyan("Drifted:") + ` ${status.health.driftedTargets === 0 ? chalk.green("0") : chalk.yellow(String(status.health.driftedTargets))} (stored differs from disk)`);
|
|
14472
|
+
console.log(chalk.cyan("Missing:") + ` ${status.health.missingTargets === 0 ? chalk.green("0") : chalk.yellow(String(status.health.missingTargets))} (file not on disk)`);
|
|
14473
|
+
console.log(chalk.cyan("Secrets:") + ` ${status.health.unredactedSecretFindings === 0 ? chalk.green("0 \u2713") : chalk.red(String(status.health.unredactedSecretFindings) + " \u26A0")} unredacted`);
|
|
14474
|
+
console.log(chalk.cyan("Templates:") + ` ${status.counts.configs.templates} (with {{VAR}} placeholders)`);
|
|
14363
14475
|
});
|
|
14364
14476
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
14365
14477
|
const { mkdirSync: mk } = await import("fs");
|
|
@@ -14393,9 +14505,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
14393
14505
|
console.log(chalk.cyan("Known files on disk:"));
|
|
14394
14506
|
for (const k of KNOWN_CONFIGS) {
|
|
14395
14507
|
if (k.rulesDir) {
|
|
14396
|
-
|
|
14508
|
+
existsSync13(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail(`${k.rulesDir}/ not found`);
|
|
14397
14509
|
} else {
|
|
14398
|
-
|
|
14510
|
+
existsSync13(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail(`${k.path} not found`);
|
|
14399
14511
|
}
|
|
14400
14512
|
}
|
|
14401
14513
|
const allConfigs = listConfigs();
|
|
@@ -14517,7 +14629,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
14517
14629
|
for (const k of KNOWN_CONFIGS) {
|
|
14518
14630
|
if (k.rulesDir) {
|
|
14519
14631
|
const absDir = expandPath2(k.rulesDir);
|
|
14520
|
-
if (!
|
|
14632
|
+
if (!existsSync13(absDir))
|
|
14521
14633
|
continue;
|
|
14522
14634
|
const { readdirSync: readdirSync5 } = await import("fs");
|
|
14523
14635
|
for (const f of readdirSync5(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
@@ -14526,7 +14638,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
14526
14638
|
}
|
|
14527
14639
|
} else {
|
|
14528
14640
|
const abs = expandPath2(k.path);
|
|
14529
|
-
if (
|
|
14641
|
+
if (existsSync13(abs))
|
|
14530
14642
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
14531
14643
|
}
|
|
14532
14644
|
}
|
|
@@ -14534,7 +14646,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
14534
14646
|
const tick = async () => {
|
|
14535
14647
|
let changed = 0;
|
|
14536
14648
|
for (const [abs, oldMtime] of mtimes) {
|
|
14537
|
-
if (!
|
|
14649
|
+
if (!existsSync13(abs))
|
|
14538
14650
|
continue;
|
|
14539
14651
|
const newMtime = st(abs).mtimeMs;
|
|
14540
14652
|
if (newMtime !== oldMtime) {
|
|
@@ -14546,7 +14658,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
14546
14658
|
for (const k of KNOWN_CONFIGS) {
|
|
14547
14659
|
if (k.rulesDir) {
|
|
14548
14660
|
const absDir = expandPath2(k.rulesDir);
|
|
14549
|
-
if (!
|
|
14661
|
+
if (!existsSync13(absDir))
|
|
14550
14662
|
continue;
|
|
14551
14663
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
14552
14664
|
const abs = join13(absDir, f);
|
|
@@ -14557,7 +14669,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
14557
14669
|
}
|
|
14558
14670
|
} else {
|
|
14559
14671
|
const abs = expandPath2(k.path);
|
|
14560
|
-
if (
|
|
14672
|
+
if (existsSync13(abs) && !mtimes.has(abs)) {
|
|
14561
14673
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
14562
14674
|
changed++;
|
|
14563
14675
|
}
|
|
@@ -14584,11 +14696,11 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
14584
14696
|
if (!c.target_path)
|
|
14585
14697
|
continue;
|
|
14586
14698
|
const abs = expandPath(c.target_path);
|
|
14587
|
-
if (!
|
|
14699
|
+
if (!existsSync13(abs)) {
|
|
14588
14700
|
missing++;
|
|
14589
14701
|
continue;
|
|
14590
14702
|
}
|
|
14591
|
-
const disk =
|
|
14703
|
+
const disk = readFileSync7(abs, "utf-8");
|
|
14592
14704
|
const { content: redactedDisk } = redactContent(disk, c.format);
|
|
14593
14705
|
if (redactedDisk !== c.content)
|
|
14594
14706
|
drifted++;
|
|
@@ -14626,7 +14738,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
14626
14738
|
if (!c.target_path)
|
|
14627
14739
|
continue;
|
|
14628
14740
|
const abs = expandPath(c.target_path);
|
|
14629
|
-
if (!
|
|
14741
|
+
if (!existsSync13(abs)) {
|
|
14630
14742
|
if (opts.dryRun) {
|
|
14631
14743
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${c.target_path})`)}`);
|
|
14632
14744
|
} else {
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export { createSnapshot, listSnapshots, getSnapshot, getSnapshotByVersion, prune
|
|
|
4
4
|
export { createProfile, getProfile, listProfiles, updateProfile, deleteProfile, addConfigToProfile, removeConfigFromProfile, getProfileConfigs, profileHasSelectors, profileMatchesMachine, resolveProfileForMachine } from "./db/profiles.js";
|
|
5
5
|
export { registerMachine, updateMachineApplied, listMachines, currentHostname, currentOs, currentArch } from "./db/machines.js";
|
|
6
6
|
export { getDatabase, resetDatabase, uuid, now, slugify } from "./db/database.js";
|
|
7
|
+
export { getConfigsStatus } from "./status.js";
|
|
8
|
+
export type { ConfigsStatusContract } from "./status.js";
|
|
7
9
|
export { PG_MIGRATIONS } from "./db/pg-migrations.js";
|
|
8
10
|
export { applyConfig, applyConfigs, expandPath } from "./lib/apply.js";
|
|
9
11
|
export type { ApplyOptions } from "./lib/apply.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGlI,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGrH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG/O,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhI,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGlF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACrL,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAG9F,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC/J,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1G,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACvG,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGlI,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGrH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG/O,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhI,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGlF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACrL,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAG9F,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC/J,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1G,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACvG,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10236,64 +10236,9 @@ function listMachines(db) {
|
|
|
10236
10236
|
const d = db || getDatabase();
|
|
10237
10237
|
return d.query("SELECT * FROM machines ORDER BY last_applied_at DESC NULLS LAST").all();
|
|
10238
10238
|
}
|
|
10239
|
-
// src/
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
id TEXT PRIMARY KEY,
|
|
10243
|
-
name TEXT NOT NULL,
|
|
10244
|
-
slug TEXT NOT NULL UNIQUE,
|
|
10245
|
-
kind TEXT NOT NULL DEFAULT 'file',
|
|
10246
|
-
category TEXT NOT NULL,
|
|
10247
|
-
agent TEXT NOT NULL DEFAULT 'global',
|
|
10248
|
-
target_path TEXT,
|
|
10249
|
-
format TEXT NOT NULL DEFAULT 'text',
|
|
10250
|
-
content TEXT NOT NULL DEFAULT '',
|
|
10251
|
-
description TEXT,
|
|
10252
|
-
tags TEXT NOT NULL DEFAULT '[]',
|
|
10253
|
-
is_template BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10254
|
-
version INTEGER NOT NULL DEFAULT 1,
|
|
10255
|
-
created_at TEXT NOT NULL,
|
|
10256
|
-
updated_at TEXT NOT NULL,
|
|
10257
|
-
synced_at TEXT
|
|
10258
|
-
)`,
|
|
10259
|
-
`CREATE TABLE IF NOT EXISTS config_snapshots (
|
|
10260
|
-
id TEXT PRIMARY KEY,
|
|
10261
|
-
config_id TEXT NOT NULL REFERENCES configs(id) ON DELETE CASCADE,
|
|
10262
|
-
content TEXT NOT NULL,
|
|
10263
|
-
version INTEGER NOT NULL,
|
|
10264
|
-
created_at TEXT NOT NULL
|
|
10265
|
-
)`,
|
|
10266
|
-
`CREATE TABLE IF NOT EXISTS profiles (
|
|
10267
|
-
id TEXT PRIMARY KEY,
|
|
10268
|
-
name TEXT NOT NULL,
|
|
10269
|
-
slug TEXT NOT NULL UNIQUE,
|
|
10270
|
-
description TEXT,
|
|
10271
|
-
created_at TEXT NOT NULL,
|
|
10272
|
-
updated_at TEXT NOT NULL
|
|
10273
|
-
)`,
|
|
10274
|
-
`CREATE TABLE IF NOT EXISTS profile_configs (
|
|
10275
|
-
profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
|
10276
|
-
config_id TEXT NOT NULL REFERENCES configs(id) ON DELETE CASCADE,
|
|
10277
|
-
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
10278
|
-
PRIMARY KEY (profile_id, config_id)
|
|
10279
|
-
)`,
|
|
10280
|
-
`CREATE TABLE IF NOT EXISTS machines (
|
|
10281
|
-
id TEXT PRIMARY KEY,
|
|
10282
|
-
hostname TEXT NOT NULL UNIQUE,
|
|
10283
|
-
os TEXT,
|
|
10284
|
-
last_applied_at TEXT,
|
|
10285
|
-
created_at TEXT NOT NULL
|
|
10286
|
-
)`,
|
|
10287
|
-
`CREATE TABLE IF NOT EXISTS feedback (
|
|
10288
|
-
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
10289
|
-
message TEXT NOT NULL,
|
|
10290
|
-
email TEXT,
|
|
10291
|
-
category TEXT DEFAULT 'general',
|
|
10292
|
-
version TEXT,
|
|
10293
|
-
machine_id TEXT,
|
|
10294
|
-
created_at TEXT NOT NULL DEFAULT NOW()::text
|
|
10295
|
-
)`
|
|
10296
|
-
];
|
|
10239
|
+
// src/status.ts
|
|
10240
|
+
import { existsSync as existsSync9, readFileSync as readFileSync3 } from "fs";
|
|
10241
|
+
|
|
10297
10242
|
// src/lib/apply.ts
|
|
10298
10243
|
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10299
10244
|
import { dirname as dirname2, resolve } from "path";
|
|
@@ -10344,59 +10289,6 @@ async function applyConfigs(configs, opts = {}) {
|
|
|
10344
10289
|
}
|
|
10345
10290
|
return results;
|
|
10346
10291
|
}
|
|
10347
|
-
// src/lib/platform-profiles.ts
|
|
10348
|
-
var PLATFORM_PROFILE_PRESETS = [
|
|
10349
|
-
{
|
|
10350
|
-
name: "linux-arm64",
|
|
10351
|
-
description: "Default Linux arm64 profile for spark01/spark02-style machines",
|
|
10352
|
-
selectors: { os: ["linux"], arch: ["arm64"], hostnames: ["spark01", "spark02"] },
|
|
10353
|
-
variables: {
|
|
10354
|
-
WORKSPACE_ROOT: "{{HOME_DIR}}/workspace",
|
|
10355
|
-
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
10356
|
-
BUN_PATH: "{{BUN_BIN_DIR}}/bun",
|
|
10357
|
-
PATH_PREFIX: "{{BUN_BIN_DIR}}"
|
|
10358
|
-
}
|
|
10359
|
-
},
|
|
10360
|
-
{
|
|
10361
|
-
name: "macos-arm64",
|
|
10362
|
-
description: "Default macOS arm64 profile for apple01/apple03-style machines",
|
|
10363
|
-
selectors: { os: ["macos"], arch: ["arm64"], hostnames: ["apple01", "apple03"] },
|
|
10364
|
-
variables: {
|
|
10365
|
-
WORKSPACE_ROOT: "{{HOME_DIR}}/Workspace",
|
|
10366
|
-
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
10367
|
-
BUN_PATH: "/opt/homebrew/bin/bun",
|
|
10368
|
-
PATH_PREFIX: "/opt/homebrew/bin:{{BUN_BIN_DIR}}"
|
|
10369
|
-
}
|
|
10370
|
-
}
|
|
10371
|
-
];
|
|
10372
|
-
function ensurePlatformProfiles(db) {
|
|
10373
|
-
const configs = listConfigs(undefined, db);
|
|
10374
|
-
const ensured = [];
|
|
10375
|
-
for (const preset of PLATFORM_PROFILE_PRESETS) {
|
|
10376
|
-
let profile;
|
|
10377
|
-
try {
|
|
10378
|
-
profile = getProfile(preset.name, db);
|
|
10379
|
-
if (!profileHasSelectors(profile) || Object.keys(profile.variables).length === 0) {
|
|
10380
|
-
profile = updateProfile(profile.id, {
|
|
10381
|
-
description: profile.description ?? preset.description,
|
|
10382
|
-
selectors: profileHasSelectors(profile) ? profile.selectors : preset.selectors,
|
|
10383
|
-
variables: Object.keys(profile.variables).length > 0 ? profile.variables : preset.variables
|
|
10384
|
-
}, db);
|
|
10385
|
-
}
|
|
10386
|
-
} catch {
|
|
10387
|
-
profile = createProfile(preset, db);
|
|
10388
|
-
}
|
|
10389
|
-
for (const config of configs) {
|
|
10390
|
-
addConfigToProfile(profile.id, config.id, db);
|
|
10391
|
-
}
|
|
10392
|
-
ensured.push(profile);
|
|
10393
|
-
}
|
|
10394
|
-
return ensured;
|
|
10395
|
-
}
|
|
10396
|
-
// src/lib/sync.ts
|
|
10397
|
-
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync4 } from "fs";
|
|
10398
|
-
import { extname, join as join10 } from "path";
|
|
10399
|
-
import { homedir as homedir10 } from "os";
|
|
10400
10292
|
|
|
10401
10293
|
// src/lib/redact.ts
|
|
10402
10294
|
var SECRET_KEY_PATTERN = /^(.*_?API_?KEY|.*_?TOKEN|.*_?SECRET|.*_?PASSWORD|.*_?PASSWD|.*_?CREDENTIAL|.*_?AUTH(?:_TOKEN|_KEY|ORIZATION)?|.*_?PRIVATE_?KEY|.*_?ACCESS_?KEY|.*_?CLIENT_?SECRET|.*_?SIGNING_?KEY|.*_?ENCRYPTION_?KEY|.*_AUTH_TOKEN)$/i;
|
|
@@ -10578,8 +10470,239 @@ function hasSecrets(content, format) {
|
|
|
10578
10470
|
return scanSecrets(content, format).length > 0;
|
|
10579
10471
|
}
|
|
10580
10472
|
|
|
10473
|
+
// src/status.ts
|
|
10474
|
+
var PACKAGE_NAME = "@hasna/configs";
|
|
10475
|
+
var PACKAGE_VERSION = "0.2.38";
|
|
10476
|
+
function activeDatabaseEnv() {
|
|
10477
|
+
if (process.env["HASNA_CONFIGS_DB_PATH"])
|
|
10478
|
+
return "HASNA_CONFIGS_DB_PATH";
|
|
10479
|
+
if (process.env["CONFIGS_DB_PATH"])
|
|
10480
|
+
return "CONFIGS_DB_PATH";
|
|
10481
|
+
return null;
|
|
10482
|
+
}
|
|
10483
|
+
function configuredDatabaseKind() {
|
|
10484
|
+
const value = process.env["HASNA_CONFIGS_DB_PATH"] ?? process.env["CONFIGS_DB_PATH"] ?? "";
|
|
10485
|
+
return value === ":memory:" || value.startsWith("file::memory:") ? "memory" : "file";
|
|
10486
|
+
}
|
|
10487
|
+
function countBy(items, getValue) {
|
|
10488
|
+
const counts = {};
|
|
10489
|
+
for (const item of items) {
|
|
10490
|
+
const value = getValue(item);
|
|
10491
|
+
if (!value)
|
|
10492
|
+
continue;
|
|
10493
|
+
counts[value] = (counts[value] ?? 0) + 1;
|
|
10494
|
+
}
|
|
10495
|
+
return counts;
|
|
10496
|
+
}
|
|
10497
|
+
function tableCount(db, table) {
|
|
10498
|
+
try {
|
|
10499
|
+
const row = db.query(`SELECT COUNT(*) AS count FROM ${table}`).get();
|
|
10500
|
+
return Number(row?.count ?? 0);
|
|
10501
|
+
} catch {
|
|
10502
|
+
return 0;
|
|
10503
|
+
}
|
|
10504
|
+
}
|
|
10505
|
+
function getConfigsStatus(db = getDatabase()) {
|
|
10506
|
+
let databaseReachable = true;
|
|
10507
|
+
let configs = [];
|
|
10508
|
+
let categoryStats = { total: 0 };
|
|
10509
|
+
try {
|
|
10510
|
+
configs = listConfigs(undefined, db);
|
|
10511
|
+
categoryStats = getConfigStats(db);
|
|
10512
|
+
} catch {
|
|
10513
|
+
databaseReachable = false;
|
|
10514
|
+
}
|
|
10515
|
+
const fileConfigs = configs.filter((config) => config.kind === "file");
|
|
10516
|
+
let driftedTargets = 0;
|
|
10517
|
+
let missingTargets = 0;
|
|
10518
|
+
let unredactedSecretFindings = 0;
|
|
10519
|
+
let knownTargets = 0;
|
|
10520
|
+
for (const config of fileConfigs) {
|
|
10521
|
+
unredactedSecretFindings += scanSecrets(config.content, config.format).length;
|
|
10522
|
+
if (!config.target_path)
|
|
10523
|
+
continue;
|
|
10524
|
+
knownTargets += 1;
|
|
10525
|
+
const targetPath = expandPath(config.target_path);
|
|
10526
|
+
if (!existsSync9(targetPath)) {
|
|
10527
|
+
missingTargets += 1;
|
|
10528
|
+
continue;
|
|
10529
|
+
}
|
|
10530
|
+
const disk = readFileSync3(targetPath, "utf-8");
|
|
10531
|
+
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
10532
|
+
if (redactedDisk !== config.content) {
|
|
10533
|
+
driftedTargets += 1;
|
|
10534
|
+
}
|
|
10535
|
+
}
|
|
10536
|
+
const profiles = databaseReachable ? listProfiles(db).length : 0;
|
|
10537
|
+
const machines = databaseReachable ? listMachines(db).length : 0;
|
|
10538
|
+
const profileLinks = databaseReachable ? tableCount(db, "profile_configs") : 0;
|
|
10539
|
+
const snapshots = databaseReachable ? tableCount(db, "config_snapshots") : 0;
|
|
10540
|
+
const byCategory = Object.fromEntries(Object.entries(categoryStats).filter(([key]) => key !== "total"));
|
|
10541
|
+
const status = databaseReachable && driftedTargets === 0 && missingTargets === 0 && unredactedSecretFindings === 0 ? "ok" : "warn";
|
|
10542
|
+
return {
|
|
10543
|
+
service: "configs",
|
|
10544
|
+
schemaVersion: "1.0",
|
|
10545
|
+
package: {
|
|
10546
|
+
name: PACKAGE_NAME,
|
|
10547
|
+
version: PACKAGE_VERSION
|
|
10548
|
+
},
|
|
10549
|
+
env: {
|
|
10550
|
+
database: {
|
|
10551
|
+
primary: "HASNA_CONFIGS_DB_PATH",
|
|
10552
|
+
fallback: "CONFIGS_DB_PATH",
|
|
10553
|
+
active: activeDatabaseEnv(),
|
|
10554
|
+
kind: configuredDatabaseKind()
|
|
10555
|
+
}
|
|
10556
|
+
},
|
|
10557
|
+
counts: {
|
|
10558
|
+
configs: {
|
|
10559
|
+
total: configs.length,
|
|
10560
|
+
file: fileConfigs.length,
|
|
10561
|
+
reference: configs.filter((config) => config.kind === "reference").length,
|
|
10562
|
+
templates: configs.filter((config) => config.is_template).length
|
|
10563
|
+
},
|
|
10564
|
+
byCategory,
|
|
10565
|
+
byAgent: countBy(configs, (config) => config.agent),
|
|
10566
|
+
byFormat: countBy(configs, (config) => config.format),
|
|
10567
|
+
profiles,
|
|
10568
|
+
profileLinks,
|
|
10569
|
+
machines,
|
|
10570
|
+
snapshots,
|
|
10571
|
+
knownTargets
|
|
10572
|
+
},
|
|
10573
|
+
health: {
|
|
10574
|
+
status,
|
|
10575
|
+
databaseReachable,
|
|
10576
|
+
driftedTargets,
|
|
10577
|
+
missingTargets,
|
|
10578
|
+
unredactedSecretFindings,
|
|
10579
|
+
hasDrift: driftedTargets > 0,
|
|
10580
|
+
hasMissingTargets: missingTargets > 0,
|
|
10581
|
+
hasUnredactedSecrets: unredactedSecretFindings > 0
|
|
10582
|
+
},
|
|
10583
|
+
safety: {
|
|
10584
|
+
includesConfigValues: false,
|
|
10585
|
+
includesPrivatePaths: false,
|
|
10586
|
+
includesHostnames: false,
|
|
10587
|
+
includesSecretValues: false,
|
|
10588
|
+
statusOutputIsMetadataOnly: true
|
|
10589
|
+
}
|
|
10590
|
+
};
|
|
10591
|
+
}
|
|
10592
|
+
// src/db/pg-migrations.ts
|
|
10593
|
+
var PG_MIGRATIONS = [
|
|
10594
|
+
`CREATE TABLE IF NOT EXISTS configs (
|
|
10595
|
+
id TEXT PRIMARY KEY,
|
|
10596
|
+
name TEXT NOT NULL,
|
|
10597
|
+
slug TEXT NOT NULL UNIQUE,
|
|
10598
|
+
kind TEXT NOT NULL DEFAULT 'file',
|
|
10599
|
+
category TEXT NOT NULL,
|
|
10600
|
+
agent TEXT NOT NULL DEFAULT 'global',
|
|
10601
|
+
target_path TEXT,
|
|
10602
|
+
format TEXT NOT NULL DEFAULT 'text',
|
|
10603
|
+
content TEXT NOT NULL DEFAULT '',
|
|
10604
|
+
description TEXT,
|
|
10605
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
10606
|
+
is_template BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10607
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
10608
|
+
created_at TEXT NOT NULL,
|
|
10609
|
+
updated_at TEXT NOT NULL,
|
|
10610
|
+
synced_at TEXT
|
|
10611
|
+
)`,
|
|
10612
|
+
`CREATE TABLE IF NOT EXISTS config_snapshots (
|
|
10613
|
+
id TEXT PRIMARY KEY,
|
|
10614
|
+
config_id TEXT NOT NULL REFERENCES configs(id) ON DELETE CASCADE,
|
|
10615
|
+
content TEXT NOT NULL,
|
|
10616
|
+
version INTEGER NOT NULL,
|
|
10617
|
+
created_at TEXT NOT NULL
|
|
10618
|
+
)`,
|
|
10619
|
+
`CREATE TABLE IF NOT EXISTS profiles (
|
|
10620
|
+
id TEXT PRIMARY KEY,
|
|
10621
|
+
name TEXT NOT NULL,
|
|
10622
|
+
slug TEXT NOT NULL UNIQUE,
|
|
10623
|
+
description TEXT,
|
|
10624
|
+
created_at TEXT NOT NULL,
|
|
10625
|
+
updated_at TEXT NOT NULL
|
|
10626
|
+
)`,
|
|
10627
|
+
`CREATE TABLE IF NOT EXISTS profile_configs (
|
|
10628
|
+
profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
|
10629
|
+
config_id TEXT NOT NULL REFERENCES configs(id) ON DELETE CASCADE,
|
|
10630
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
10631
|
+
PRIMARY KEY (profile_id, config_id)
|
|
10632
|
+
)`,
|
|
10633
|
+
`CREATE TABLE IF NOT EXISTS machines (
|
|
10634
|
+
id TEXT PRIMARY KEY,
|
|
10635
|
+
hostname TEXT NOT NULL UNIQUE,
|
|
10636
|
+
os TEXT,
|
|
10637
|
+
last_applied_at TEXT,
|
|
10638
|
+
created_at TEXT NOT NULL
|
|
10639
|
+
)`,
|
|
10640
|
+
`CREATE TABLE IF NOT EXISTS feedback (
|
|
10641
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
10642
|
+
message TEXT NOT NULL,
|
|
10643
|
+
email TEXT,
|
|
10644
|
+
category TEXT DEFAULT 'general',
|
|
10645
|
+
version TEXT,
|
|
10646
|
+
machine_id TEXT,
|
|
10647
|
+
created_at TEXT NOT NULL DEFAULT NOW()::text
|
|
10648
|
+
)`
|
|
10649
|
+
];
|
|
10650
|
+
// src/lib/platform-profiles.ts
|
|
10651
|
+
var PLATFORM_PROFILE_PRESETS = [
|
|
10652
|
+
{
|
|
10653
|
+
name: "linux-arm64",
|
|
10654
|
+
description: "Default Linux arm64 profile for linux-node-a/linux-node-b-style machines",
|
|
10655
|
+
selectors: { os: ["linux"], arch: ["arm64"], hostnames: ["linux-node-a", "linux-node-b"] },
|
|
10656
|
+
variables: {
|
|
10657
|
+
WORKSPACE_ROOT: "{{HOME_DIR}}/workspace",
|
|
10658
|
+
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
10659
|
+
BUN_PATH: "{{BUN_BIN_DIR}}/bun",
|
|
10660
|
+
PATH_PREFIX: "{{BUN_BIN_DIR}}"
|
|
10661
|
+
}
|
|
10662
|
+
},
|
|
10663
|
+
{
|
|
10664
|
+
name: "macos-arm64",
|
|
10665
|
+
description: "Default macOS arm64 profile for macos-node-a/macos-node-b-style machines",
|
|
10666
|
+
selectors: { os: ["macos"], arch: ["arm64"], hostnames: ["macos-node-a", "macos-node-b"] },
|
|
10667
|
+
variables: {
|
|
10668
|
+
WORKSPACE_ROOT: "{{HOME_DIR}}/Workspace",
|
|
10669
|
+
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
10670
|
+
BUN_PATH: "/opt/homebrew/bin/bun",
|
|
10671
|
+
PATH_PREFIX: "/opt/homebrew/bin:{{BUN_BIN_DIR}}"
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
];
|
|
10675
|
+
function ensurePlatformProfiles(db) {
|
|
10676
|
+
const configs = listConfigs(undefined, db);
|
|
10677
|
+
const ensured = [];
|
|
10678
|
+
for (const preset of PLATFORM_PROFILE_PRESETS) {
|
|
10679
|
+
let profile;
|
|
10680
|
+
try {
|
|
10681
|
+
profile = getProfile(preset.name, db);
|
|
10682
|
+
if (!profileHasSelectors(profile) || Object.keys(profile.variables).length === 0) {
|
|
10683
|
+
profile = updateProfile(profile.id, {
|
|
10684
|
+
description: profile.description ?? preset.description,
|
|
10685
|
+
selectors: profileHasSelectors(profile) ? profile.selectors : preset.selectors,
|
|
10686
|
+
variables: Object.keys(profile.variables).length > 0 ? profile.variables : preset.variables
|
|
10687
|
+
}, db);
|
|
10688
|
+
}
|
|
10689
|
+
} catch {
|
|
10690
|
+
profile = createProfile(preset, db);
|
|
10691
|
+
}
|
|
10692
|
+
for (const config of configs) {
|
|
10693
|
+
addConfigToProfile(profile.id, config.id, db);
|
|
10694
|
+
}
|
|
10695
|
+
ensured.push(profile);
|
|
10696
|
+
}
|
|
10697
|
+
return ensured;
|
|
10698
|
+
}
|
|
10699
|
+
// src/lib/sync.ts
|
|
10700
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
10701
|
+
import { extname, join as join10 } from "path";
|
|
10702
|
+
import { homedir as homedir10 } from "os";
|
|
10703
|
+
|
|
10581
10704
|
// src/lib/sync-dir.ts
|
|
10582
|
-
import { existsSync as
|
|
10705
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "fs";
|
|
10583
10706
|
import { join as join9, relative as relative2 } from "path";
|
|
10584
10707
|
import { homedir as homedir9 } from "os";
|
|
10585
10708
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
@@ -10589,7 +10712,7 @@ function shouldSkip(p) {
|
|
|
10589
10712
|
async function syncFromDir(dir, opts = {}) {
|
|
10590
10713
|
const d = opts.db || getDatabase();
|
|
10591
10714
|
const absDir = expandPath(dir);
|
|
10592
|
-
if (!
|
|
10715
|
+
if (!existsSync10(absDir))
|
|
10593
10716
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
10594
10717
|
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join9(absDir, f)).filter((f) => statSync(f).isFile());
|
|
10595
10718
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
@@ -10601,7 +10724,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
10601
10724
|
continue;
|
|
10602
10725
|
}
|
|
10603
10726
|
try {
|
|
10604
|
-
const content =
|
|
10727
|
+
const content = readFileSync4(file, "utf-8");
|
|
10605
10728
|
if (content.length > 500000) {
|
|
10606
10729
|
result.skipped.push(file + " (too large)");
|
|
10607
10730
|
continue;
|
|
@@ -10696,10 +10819,10 @@ async function syncProject(opts) {
|
|
|
10696
10819
|
const machine = detectMachineContext();
|
|
10697
10820
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
10698
10821
|
const abs = join10(absDir, pf.file);
|
|
10699
|
-
if (!
|
|
10822
|
+
if (!existsSync11(abs))
|
|
10700
10823
|
continue;
|
|
10701
10824
|
try {
|
|
10702
|
-
const rawContent =
|
|
10825
|
+
const rawContent = readFileSync5(abs, "utf-8");
|
|
10703
10826
|
if (rawContent.length > 500000) {
|
|
10704
10827
|
result.skipped.push(pf.file);
|
|
10705
10828
|
continue;
|
|
@@ -10728,11 +10851,11 @@ async function syncProject(opts) {
|
|
|
10728
10851
|
}
|
|
10729
10852
|
}
|
|
10730
10853
|
const rulesDir = join10(absDir, ".claude", "rules");
|
|
10731
|
-
if (
|
|
10854
|
+
if (existsSync11(rulesDir)) {
|
|
10732
10855
|
const mdFiles = readdirSync4(rulesDir).filter((f) => f.endsWith(".md"));
|
|
10733
10856
|
for (const f of mdFiles) {
|
|
10734
10857
|
const abs = join10(rulesDir, f);
|
|
10735
|
-
const raw =
|
|
10858
|
+
const raw = readFileSync5(abs, "utf-8");
|
|
10736
10859
|
const redacted = redactContent(raw, "markdown");
|
|
10737
10860
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
10738
10861
|
const content = machineAware.content;
|
|
@@ -10770,7 +10893,7 @@ async function syncKnown(opts = {}) {
|
|
|
10770
10893
|
for (const known of targets) {
|
|
10771
10894
|
if (known.rulesDir) {
|
|
10772
10895
|
const absDir = expandPath(known.rulesDir);
|
|
10773
|
-
if (!
|
|
10896
|
+
if (!existsSync11(absDir)) {
|
|
10774
10897
|
result.skipped.push(known.rulesDir);
|
|
10775
10898
|
continue;
|
|
10776
10899
|
}
|
|
@@ -10778,7 +10901,7 @@ async function syncKnown(opts = {}) {
|
|
|
10778
10901
|
for (const f of mdFiles) {
|
|
10779
10902
|
const abs2 = join10(absDir, f);
|
|
10780
10903
|
const targetPath = abs2.replace(home, "~");
|
|
10781
|
-
const raw =
|
|
10904
|
+
const raw = readFileSync5(abs2, "utf-8");
|
|
10782
10905
|
const redacted = redactContent(raw, "markdown");
|
|
10783
10906
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
10784
10907
|
const content = machineAware.content;
|
|
@@ -10801,12 +10924,12 @@ async function syncKnown(opts = {}) {
|
|
|
10801
10924
|
continue;
|
|
10802
10925
|
}
|
|
10803
10926
|
const abs = expandPath(known.path);
|
|
10804
|
-
if (!
|
|
10927
|
+
if (!existsSync11(abs)) {
|
|
10805
10928
|
result.skipped.push(known.path);
|
|
10806
10929
|
continue;
|
|
10807
10930
|
}
|
|
10808
10931
|
try {
|
|
10809
|
-
const rawContent =
|
|
10932
|
+
const rawContent = readFileSync5(abs, "utf-8");
|
|
10810
10933
|
if (rawContent.length > 500000) {
|
|
10811
10934
|
result.skipped.push(known.path + " (too large)");
|
|
10812
10935
|
continue;
|
|
@@ -10866,9 +10989,9 @@ function diffConfig(config) {
|
|
|
10866
10989
|
if (!config.target_path)
|
|
10867
10990
|
return "(reference \u2014 no target path)";
|
|
10868
10991
|
const path = expandPath(config.target_path);
|
|
10869
|
-
if (!
|
|
10992
|
+
if (!existsSync11(path))
|
|
10870
10993
|
return `(file not found on disk: ${path})`;
|
|
10871
|
-
const diskContent =
|
|
10994
|
+
const diskContent = readFileSync5(path, "utf-8");
|
|
10872
10995
|
if (diskContent === config.content)
|
|
10873
10996
|
return "(no diff \u2014 identical)";
|
|
10874
10997
|
const stored = config.content.split(`
|
|
@@ -10942,7 +11065,7 @@ function detectFormat(filePath) {
|
|
|
10942
11065
|
return "text";
|
|
10943
11066
|
}
|
|
10944
11067
|
// src/lib/export.ts
|
|
10945
|
-
import { existsSync as
|
|
11068
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
10946
11069
|
import { join as join11, resolve as resolve2 } from "path";
|
|
10947
11070
|
import { tmpdir } from "os";
|
|
10948
11071
|
async function exportConfigs(outputPath, opts = {}) {
|
|
@@ -10974,13 +11097,13 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
10974
11097
|
}
|
|
10975
11098
|
return { path: absOutput, count: configs.length };
|
|
10976
11099
|
} finally {
|
|
10977
|
-
if (
|
|
11100
|
+
if (existsSync12(tmpDir)) {
|
|
10978
11101
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
10979
11102
|
}
|
|
10980
11103
|
}
|
|
10981
11104
|
}
|
|
10982
11105
|
// src/lib/import.ts
|
|
10983
|
-
import { existsSync as
|
|
11106
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync6, rmSync as rmSync2 } from "fs";
|
|
10984
11107
|
import { join as join12, resolve as resolve3 } from "path";
|
|
10985
11108
|
import { tmpdir as tmpdir2 } from "os";
|
|
10986
11109
|
async function importConfigs(bundlePath, opts = {}) {
|
|
@@ -11001,14 +11124,14 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
11001
11124
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
11002
11125
|
}
|
|
11003
11126
|
const manifestPath = join12(tmpDir, "manifest.json");
|
|
11004
|
-
if (!
|
|
11127
|
+
if (!existsSync13(manifestPath))
|
|
11005
11128
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
11006
|
-
const manifest = JSON.parse(
|
|
11129
|
+
const manifest = JSON.parse(readFileSync6(manifestPath, "utf-8"));
|
|
11007
11130
|
for (const meta of manifest.configs) {
|
|
11008
11131
|
try {
|
|
11009
11132
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
11010
11133
|
const contentFile = join12(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
11011
|
-
const content =
|
|
11134
|
+
const content = existsSync13(contentFile) ? readFileSync6(contentFile, "utf-8") : "";
|
|
11012
11135
|
let existing = null;
|
|
11013
11136
|
try {
|
|
11014
11137
|
existing = getConfig(meta.slug, d);
|
|
@@ -11041,7 +11164,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
11041
11164
|
}
|
|
11042
11165
|
return result;
|
|
11043
11166
|
} finally {
|
|
11044
|
-
if (
|
|
11167
|
+
if (existsSync13(tmpDir)) {
|
|
11045
11168
|
rmSync2(tmpDir, { recursive: true, force: true });
|
|
11046
11169
|
}
|
|
11047
11170
|
}
|
|
@@ -11086,6 +11209,7 @@ export {
|
|
|
11086
11209
|
getProfileConfigs,
|
|
11087
11210
|
getProfile,
|
|
11088
11211
|
getDatabase,
|
|
11212
|
+
getConfigsStatus,
|
|
11089
11213
|
getConfigStats,
|
|
11090
11214
|
getConfigById,
|
|
11091
11215
|
getConfig,
|
package/dist/mcp/index.js
CHANGED
|
@@ -11241,7 +11241,7 @@ var init_sync_dir = __esm(() => {
|
|
|
11241
11241
|
var require_package = __commonJS((exports, module) => {
|
|
11242
11242
|
module.exports = {
|
|
11243
11243
|
name: "@hasna/configs",
|
|
11244
|
-
version: "0.2.
|
|
11244
|
+
version: "0.2.38",
|
|
11245
11245
|
description: "AI coding agent configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.",
|
|
11246
11246
|
type: "module",
|
|
11247
11247
|
main: "dist/index.js",
|
package/dist/server/index.js
CHANGED
|
@@ -17829,7 +17829,7 @@ var require_dist2 = __commonJS((exports, module) => {
|
|
|
17829
17829
|
var require_package = __commonJS((exports, module) => {
|
|
17830
17830
|
module.exports = {
|
|
17831
17831
|
name: "@hasna/configs",
|
|
17832
|
-
version: "0.2.
|
|
17832
|
+
version: "0.2.38",
|
|
17833
17833
|
description: "AI coding agent configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.",
|
|
17834
17834
|
type: "module",
|
|
17835
17835
|
main: "dist/index.js",
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
type ActiveDbEnv = "HASNA_CONFIGS_DB_PATH" | "CONFIGS_DB_PATH" | null;
|
|
3
|
+
type DatabaseKind = "memory" | "file";
|
|
4
|
+
type ContractStatus = "ok" | "warn";
|
|
5
|
+
export interface ConfigsStatusContract {
|
|
6
|
+
service: "configs";
|
|
7
|
+
schemaVersion: "1.0";
|
|
8
|
+
package: {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
};
|
|
12
|
+
env: {
|
|
13
|
+
database: {
|
|
14
|
+
primary: "HASNA_CONFIGS_DB_PATH";
|
|
15
|
+
fallback: "CONFIGS_DB_PATH";
|
|
16
|
+
active: ActiveDbEnv;
|
|
17
|
+
kind: DatabaseKind;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
counts: {
|
|
21
|
+
configs: {
|
|
22
|
+
total: number;
|
|
23
|
+
file: number;
|
|
24
|
+
reference: number;
|
|
25
|
+
templates: number;
|
|
26
|
+
};
|
|
27
|
+
byCategory: Record<string, number>;
|
|
28
|
+
byAgent: Record<string, number>;
|
|
29
|
+
byFormat: Record<string, number>;
|
|
30
|
+
profiles: number;
|
|
31
|
+
profileLinks: number;
|
|
32
|
+
machines: number;
|
|
33
|
+
snapshots: number;
|
|
34
|
+
knownTargets: number;
|
|
35
|
+
};
|
|
36
|
+
health: {
|
|
37
|
+
status: ContractStatus;
|
|
38
|
+
databaseReachable: boolean;
|
|
39
|
+
driftedTargets: number;
|
|
40
|
+
missingTargets: number;
|
|
41
|
+
unredactedSecretFindings: number;
|
|
42
|
+
hasDrift: boolean;
|
|
43
|
+
hasMissingTargets: boolean;
|
|
44
|
+
hasUnredactedSecrets: boolean;
|
|
45
|
+
};
|
|
46
|
+
safety: {
|
|
47
|
+
includesConfigValues: false;
|
|
48
|
+
includesPrivatePaths: false;
|
|
49
|
+
includesHostnames: false;
|
|
50
|
+
includesSecretValues: false;
|
|
51
|
+
statusOutputIsMetadataOnly: true;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export declare function getConfigsStatus(db?: Database): ConfigsStatusContract;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAW3C,KAAK,WAAW,GAAG,uBAAuB,GAAG,iBAAiB,GAAG,IAAI,CAAC;AACtE,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AACtC,KAAK,cAAc,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,SAAS,CAAC;IACnB,aAAa,EAAE,KAAK,CAAC;IACrB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,GAAG,EAAE;QACH,QAAQ,EAAE;YACR,OAAO,EAAE,uBAAuB,CAAC;YACjC,QAAQ,EAAE,iBAAiB,CAAC;YAC5B,MAAM,EAAE,WAAW,CAAC;YACpB,IAAI,EAAE,YAAY,CAAC;SACpB,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,cAAc,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,wBAAwB,EAAE,MAAM,CAAC;QACjC,QAAQ,EAAE,OAAO,CAAC;QAClB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,oBAAoB,EAAE,OAAO,CAAC;KAC/B,CAAC;IACF,MAAM,EAAE;QACN,oBAAoB,EAAE,KAAK,CAAC;QAC5B,oBAAoB,EAAE,KAAK,CAAC;QAC5B,iBAAiB,EAAE,KAAK,CAAC;QACzB,oBAAoB,EAAE,KAAK,CAAC;QAC5B,0BAA0B,EAAE,IAAI,CAAC;KAClC,CAAC;CACH;AAgCD,wBAAgB,gBAAgB,CAAC,EAAE,GAAE,QAAwB,GAAG,qBAAqB,CAmGpF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.test.d.ts","sourceRoot":"","sources":["../src/status.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/configs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.38",
|
|
4
4
|
"description": "AI coding agent configuration manager — store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|