contribute-now 0.7.0 → 0.7.1-dev.42d9d46

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.
Files changed (2) hide show
  1. package/dist/index.js +128 -1649
  2. package/package.json +1 -3
package/dist/index.js CHANGED
@@ -1298,579 +1298,6 @@ var init_formatter = __esm(() => {
1298
1298
  init_message_formatter();
1299
1299
  });
1300
1300
 
1301
- // node_modules/@wgtechlabs/secrets-engine/dist/index.js
1302
- var exports_dist = {};
1303
- __export(exports_dist, {
1304
- SecurityError: () => SecurityError,
1305
- SecretsEngineError: () => SecretsEngineError,
1306
- SecretsEngine: () => SecretsEngine,
1307
- KeyNotFoundError: () => KeyNotFoundError,
1308
- IntegrityError: () => IntegrityError,
1309
- InitializationError: () => InitializationError,
1310
- DecryptionError: () => DecryptionError
1311
- });
1312
- import { readdir, rm, unlink } from "fs/promises";
1313
- import { join as join3 } from "path";
1314
- import {
1315
- createCipheriv,
1316
- createDecipheriv,
1317
- createHash,
1318
- createHmac,
1319
- randomBytes,
1320
- scryptSync
1321
- } from "crypto";
1322
- import { readFile as readFile2 } from "fs/promises";
1323
- import { chmod, mkdir, readFile, stat, writeFile } from "fs/promises";
1324
- import { homedir, hostname, networkInterfaces, userInfo } from "os";
1325
- import { join as join4 } from "path";
1326
- import { Database } from "bun:sqlite";
1327
- import { readFile as readFile3 } from "fs/promises";
1328
- import { join as join22 } from "path";
1329
- function deriveMasterKey(machineId, keyfile, salt) {
1330
- const password = Buffer.concat([Buffer.from(machineId, "utf-8"), keyfile]);
1331
- return scryptSync(password, salt, CONSTANTS.KEY_LENGTH, {
1332
- N: CONSTANTS.SCRYPT_N,
1333
- r: CONSTANTS.SCRYPT_R,
1334
- p: CONSTANTS.SCRYPT_P,
1335
- maxmem: 256 * CONSTANTS.SCRYPT_N * CONSTANTS.SCRYPT_R
1336
- });
1337
- }
1338
- function encrypt(masterKey, plaintext) {
1339
- const iv = randomBytes(CONSTANTS.IV_LENGTH);
1340
- const cipher = createCipheriv("aes-256-gcm", masterKey, iv);
1341
- const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
1342
- const authTag = cipher.getAuthTag();
1343
- return {
1344
- iv,
1345
- ciphertext: Buffer.concat([encrypted, authTag])
1346
- };
1347
- }
1348
- function decrypt(masterKey, iv, ciphertext, keyHash) {
1349
- if (ciphertext.length < CONSTANTS.AUTH_TAG_LENGTH) {
1350
- throw new DecryptionError("Ciphertext too short to contain auth tag", keyHash);
1351
- }
1352
- const authTag = ciphertext.subarray(ciphertext.length - CONSTANTS.AUTH_TAG_LENGTH);
1353
- const encrypted = ciphertext.subarray(0, ciphertext.length - CONSTANTS.AUTH_TAG_LENGTH);
1354
- try {
1355
- const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
1356
- decipher.setAuthTag(authTag);
1357
- return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf-8");
1358
- } catch (error) {
1359
- throw new DecryptionError(error instanceof Error ? error.message : "Unknown decryption error", keyHash);
1360
- }
1361
- }
1362
- function hmac(masterKey, data) {
1363
- return createHmac("sha256", masterKey).update(data).digest("hex");
1364
- }
1365
- function sha256(data) {
1366
- return createHash("sha256").update(data).digest();
1367
- }
1368
- function generateSalt() {
1369
- return randomBytes(CONSTANTS.SALT_LENGTH);
1370
- }
1371
- function matchGlob(pattern, key) {
1372
- const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^.]*");
1373
- const regex2 = new RegExp(`^${regexStr}$`);
1374
- return regex2.test(key);
1375
- }
1376
- function filterKeys(keys, pattern) {
1377
- return keys.filter((key) => matchGlob(pattern, key));
1378
- }
1379
- function resolveStoragePath(options) {
1380
- if (options?.path) {
1381
- return options.path;
1382
- }
1383
- if (options?.location === "xdg") {
1384
- return resolveXdgPath();
1385
- }
1386
- if (process.platform !== "win32" && process.env.XDG_CONFIG_HOME) {
1387
- return join4(process.env.XDG_CONFIG_HOME, "secrets-engine");
1388
- }
1389
- return join4(homedir(), CONSTANTS.DIR_NAME);
1390
- }
1391
- function resolveXdgPath() {
1392
- if (process.platform === "win32") {
1393
- const appData = process.env.APPDATA;
1394
- if (!appData) {
1395
- throw new InitializationError("APPDATA environment variable is not set");
1396
- }
1397
- return join4(appData, "secrets-engine");
1398
- }
1399
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join4(homedir(), ".config");
1400
- return join4(xdgConfig, "secrets-engine");
1401
- }
1402
- async function ensureDirectory(dirPath) {
1403
- try {
1404
- await mkdir(dirPath, { recursive: true, mode: EXPECTED_MODES.directory });
1405
- } catch (error) {
1406
- throw new InitializationError(`Cannot create storage directory at "${dirPath}"`, error);
1407
- }
1408
- if (process.platform !== "win32") {
1409
- await verifyPermission(dirPath, EXPECTED_MODES.directory, "directory");
1410
- }
1411
- }
1412
- async function verifyPermission(filePath, expectedMode, label) {
1413
- if (process.platform === "win32") {
1414
- return;
1415
- }
1416
- const fileStat = await stat(filePath);
1417
- const actualMode = fileStat.mode & 511;
1418
- if (actualMode !== expectedMode) {
1419
- throw new SecurityError(`Insecure ${label} permissions`, formatOctal(expectedMode), formatOctal(actualMode), filePath);
1420
- }
1421
- }
1422
- function formatOctal(mode) {
1423
- return `0o${mode.toString(8).padStart(3, "0")}`;
1424
- }
1425
- function getMachineIdentity() {
1426
- const host = hostname();
1427
- const mac = getPrimaryMac();
1428
- const user = userInfo().username;
1429
- return `${host}:${mac}:${user}`;
1430
- }
1431
- function getPrimaryMac() {
1432
- const interfaces = networkInterfaces();
1433
- for (const entries of Object.values(interfaces)) {
1434
- if (!entries)
1435
- continue;
1436
- for (const entry of entries) {
1437
- if (!entry.internal && entry.mac && entry.mac !== "00:00:00:00:00:00") {
1438
- return entry.mac;
1439
- }
1440
- }
1441
- }
1442
- return "no-mac-available";
1443
- }
1444
- async function ensureKeyfile(dirPath) {
1445
- const keyfilePath = join4(dirPath, CONSTANTS.KEYFILE_NAME);
1446
- try {
1447
- if (process.platform !== "win32") {
1448
- await verifyPermission(keyfilePath, EXPECTED_MODES.keyfile, "keyfile");
1449
- }
1450
- return await readFile(keyfilePath);
1451
- } catch (error) {
1452
- if (isFileNotFoundError(error)) {
1453
- return await createKeyfile(keyfilePath);
1454
- }
1455
- throw error;
1456
- }
1457
- }
1458
- async function createKeyfile(keyfilePath) {
1459
- const { randomBytes: randomBytes2 } = await import("crypto");
1460
- const keyfileData = randomBytes2(CONSTANTS.KEYFILE_LENGTH);
1461
- await writeFile(keyfilePath, keyfileData, { mode: EXPECTED_MODES.keyfile });
1462
- if (process.platform !== "win32") {
1463
- await chmod(keyfilePath, EXPECTED_MODES.keyfile);
1464
- }
1465
- return keyfileData;
1466
- }
1467
- async function readMetaFile(dirPath) {
1468
- const metaPath = join4(dirPath, CONSTANTS.META_NAME);
1469
- try {
1470
- return await readFile(metaPath, "utf-8");
1471
- } catch (error) {
1472
- if (isFileNotFoundError(error)) {
1473
- return null;
1474
- }
1475
- throw error;
1476
- }
1477
- }
1478
- async function writeMetaFile(dirPath, content) {
1479
- const metaPath = join4(dirPath, CONSTANTS.META_NAME);
1480
- await writeFile(metaPath, content, { mode: EXPECTED_MODES.meta });
1481
- if (process.platform !== "win32") {
1482
- await chmod(metaPath, EXPECTED_MODES.meta);
1483
- }
1484
- }
1485
- function isFileNotFoundError(error) {
1486
- return error instanceof Error && "code" in error && error.code === "ENOENT";
1487
- }
1488
- async function computeIntegrityHmac(masterKey, dbFilePath, checkpointFn) {
1489
- if (checkpointFn) {
1490
- try {
1491
- checkpointFn();
1492
- } catch (err) {
1493
- const originalMessage = err instanceof Error ? err.message : String(err);
1494
- throw new IntegrityError(`Integrity checkpoint failed: ${originalMessage}`);
1495
- }
1496
- }
1497
- const dbBytes = Buffer.from(await readFile2(dbFilePath));
1498
- const dbHash = sha256(dbBytes);
1499
- return hmac(masterKey, dbHash);
1500
- }
1501
- async function verifyIntegrity(masterKey, dbFilePath, dirPath, checkpointFn) {
1502
- const metaRaw = await readMetaFile(dirPath);
1503
- if (!metaRaw) {
1504
- throw new IntegrityError("Metadata file (meta.json) is missing");
1505
- }
1506
- let meta;
1507
- try {
1508
- meta = JSON.parse(metaRaw);
1509
- } catch {
1510
- throw new IntegrityError("Metadata file (meta.json) is corrupted");
1511
- }
1512
- if (meta.version !== CONSTANTS.STORE_VERSION) {
1513
- throw new IntegrityError(`Unsupported store version: expected "${CONSTANTS.STORE_VERSION}", got "${meta.version}"`);
1514
- }
1515
- const computedHmac = await computeIntegrityHmac(masterKey, dbFilePath, checkpointFn);
1516
- if (computedHmac !== meta.integrity) {
1517
- throw new IntegrityError;
1518
- }
1519
- return meta;
1520
- }
1521
- async function updateIntegrity(masterKey, dbFilePath, dirPath, salt, checkpointFn) {
1522
- const integrity = await computeIntegrityHmac(masterKey, dbFilePath, checkpointFn);
1523
- const meta = {
1524
- version: CONSTANTS.STORE_VERSION,
1525
- salt,
1526
- integrity
1527
- };
1528
- await writeMetaFile(dirPath, JSON.stringify(meta, null, 2));
1529
- }
1530
-
1531
- class SecretStore {
1532
- db;
1533
- constructor(db) {
1534
- this.db = db;
1535
- }
1536
- static open(dirPath) {
1537
- const dbPath = join22(dirPath, CONSTANTS.DB_NAME);
1538
- try {
1539
- const db = new Database(dbPath, { create: true });
1540
- db.exec("PRAGMA journal_mode = WAL;");
1541
- db.exec("PRAGMA foreign_keys = ON;");
1542
- db.exec("PRAGMA busy_timeout = 5000;");
1543
- db.exec(CREATE_SECRETS_TABLE);
1544
- db.exec(CREATE_META_TABLE);
1545
- return new SecretStore(db);
1546
- } catch (error) {
1547
- throw new InitializationError(`Cannot open database at "${dbPath}"`, error);
1548
- }
1549
- }
1550
- upsert(entry) {
1551
- const now = Math.floor(Date.now() / 1000);
1552
- const stmt = this.db.prepare(`
1553
- INSERT INTO secrets (key_hash, key_enc, iv, cipher, created, updated)
1554
- VALUES ($key_hash, $key_enc, $iv, $cipher, $created, $updated)
1555
- ON CONFLICT(key_hash) DO UPDATE SET
1556
- key_enc = excluded.key_enc,
1557
- iv = excluded.iv,
1558
- cipher = excluded.cipher,
1559
- updated = excluded.updated
1560
- `);
1561
- stmt.run({
1562
- $key_hash: entry.key_hash,
1563
- $key_enc: entry.key_enc,
1564
- $iv: entry.iv,
1565
- $cipher: entry.cipher,
1566
- $created: now,
1567
- $updated: now
1568
- });
1569
- }
1570
- findByHash(keyHash) {
1571
- const stmt = this.db.prepare("SELECT key_hash, key_enc, iv, cipher, created, updated FROM secrets WHERE key_hash = ?");
1572
- const row = stmt.get(keyHash);
1573
- return row ?? null;
1574
- }
1575
- findAll() {
1576
- const stmt = this.db.prepare("SELECT key_hash, key_enc, iv, cipher, created, updated FROM secrets");
1577
- return stmt.all();
1578
- }
1579
- deleteByHash(keyHash) {
1580
- const stmt = this.db.prepare("DELETE FROM secrets WHERE key_hash = ?");
1581
- const result = stmt.run(keyHash);
1582
- return result.changes > 0;
1583
- }
1584
- deleteAll() {
1585
- this.db.exec("DELETE FROM secrets");
1586
- }
1587
- count() {
1588
- const stmt = this.db.prepare("SELECT COUNT(*) as count FROM secrets");
1589
- const row = stmt.get();
1590
- return row.count;
1591
- }
1592
- async readRawBytes() {
1593
- return Buffer.from(await readFile3(this.db.filename));
1594
- }
1595
- get filePath() {
1596
- return this.db.filename;
1597
- }
1598
- checkpoint() {
1599
- this.db.exec("PRAGMA wal_checkpoint(TRUNCATE);");
1600
- }
1601
- close() {
1602
- this.db.close();
1603
- }
1604
- }
1605
-
1606
- class SecretsEngine {
1607
- masterKey;
1608
- store;
1609
- dirPath;
1610
- salt;
1611
- keyIndex = new Map;
1612
- closed = false;
1613
- constructor(masterKey, store, dirPath, salt) {
1614
- this.masterKey = masterKey;
1615
- this.store = store;
1616
- this.dirPath = dirPath;
1617
- this.salt = salt;
1618
- }
1619
- static async open(options) {
1620
- const dirPath = resolveStoragePath(options);
1621
- await ensureDirectory(dirPath);
1622
- const keyfile = await ensureKeyfile(dirPath);
1623
- const { salt, isNewStore } = await resolveSalt(dirPath);
1624
- const machineId = getMachineIdentity();
1625
- const masterKey = deriveMasterKey(machineId, keyfile, Buffer.from(salt, "hex"));
1626
- const store = SecretStore.open(dirPath);
1627
- try {
1628
- if (!isNewStore) {
1629
- await verifyIntegrity(masterKey, store.filePath, dirPath, () => store.checkpoint());
1630
- }
1631
- const engine = new SecretsEngine(masterKey, store, dirPath, salt);
1632
- engine.buildKeyIndex();
1633
- if (isNewStore) {
1634
- await updateIntegrity(masterKey, store.filePath, dirPath, salt, () => store.checkpoint());
1635
- }
1636
- return engine;
1637
- } catch (error) {
1638
- try {
1639
- store.close();
1640
- } catch {}
1641
- throw error;
1642
- }
1643
- }
1644
- async get(key) {
1645
- this.ensureOpen();
1646
- const keyHash = this.hashKey(key);
1647
- const entry = this.store.findByHash(keyHash);
1648
- if (!entry) {
1649
- return null;
1650
- }
1651
- return decrypt(this.masterKey, Buffer.from(entry.iv), Buffer.from(entry.cipher), keyHash);
1652
- }
1653
- async getOrThrow(key) {
1654
- const value = await this.get(key);
1655
- if (value === null) {
1656
- throw new KeyNotFoundError(key);
1657
- }
1658
- return value;
1659
- }
1660
- async set(key, value) {
1661
- this.ensureOpen();
1662
- const keyHash = this.hashKey(key);
1663
- const encryptedKey = encrypt(this.masterKey, key);
1664
- const encryptedValue = encrypt(this.masterKey, value);
1665
- const keyEncPacked = Buffer.concat([encryptedKey.iv, encryptedKey.ciphertext]);
1666
- this.store.upsert({
1667
- key_hash: keyHash,
1668
- key_enc: keyEncPacked,
1669
- iv: encryptedValue.iv,
1670
- cipher: encryptedValue.ciphertext
1671
- });
1672
- this.keyIndex.set(keyHash, key);
1673
- await updateIntegrity(this.masterKey, this.store.filePath, this.dirPath, this.salt, () => this.store.checkpoint());
1674
- }
1675
- async has(key) {
1676
- this.ensureOpen();
1677
- return this.keyIndex.has(this.hashKey(key));
1678
- }
1679
- async delete(key) {
1680
- this.ensureOpen();
1681
- const keyHash = this.hashKey(key);
1682
- const deleted = this.store.deleteByHash(keyHash);
1683
- if (deleted) {
1684
- this.keyIndex.delete(keyHash);
1685
- await updateIntegrity(this.masterKey, this.store.filePath, this.dirPath, this.salt, () => this.store.checkpoint());
1686
- }
1687
- return deleted;
1688
- }
1689
- async keys(pattern) {
1690
- this.ensureOpen();
1691
- const allKeys = Array.from(this.keyIndex.values());
1692
- if (!pattern) {
1693
- return allKeys.sort();
1694
- }
1695
- return filterKeys(allKeys, pattern).sort();
1696
- }
1697
- async destroy() {
1698
- this.ensureOpen();
1699
- this.store.checkpoint();
1700
- this.store.close();
1701
- this.keyIndex.clear();
1702
- this.closed = true;
1703
- await new Promise((resolve3) => setTimeout(resolve3, 150));
1704
- await removeDirectoryContents(this.dirPath);
1705
- }
1706
- async close() {
1707
- if (!this.closed) {
1708
- try {
1709
- this.store.checkpoint();
1710
- await updateIntegrity(this.masterKey, this.store.filePath, this.dirPath, this.salt);
1711
- } finally {
1712
- this.store.close();
1713
- this.keyIndex.clear();
1714
- this.closed = true;
1715
- }
1716
- }
1717
- }
1718
- get size() {
1719
- this.ensureOpen();
1720
- return this.keyIndex.size;
1721
- }
1722
- get storagePath() {
1723
- return this.dirPath;
1724
- }
1725
- buildKeyIndex() {
1726
- const entries = this.store.findAll();
1727
- for (const entry of entries) {
1728
- try {
1729
- const keyEncBuf = Buffer.from(entry.key_enc);
1730
- const keyIv = keyEncBuf.subarray(0, 12);
1731
- const keyCipher = keyEncBuf.subarray(12);
1732
- const keyName = decrypt(this.masterKey, keyIv, keyCipher, entry.key_hash);
1733
- this.keyIndex.set(entry.key_hash, keyName);
1734
- } catch (error) {
1735
- if (error instanceof DecryptionError) {
1736
- console.warn(`[secrets-engine] Skipping corrupted entry: ${entry.key_hash.slice(0, 16)}…`);
1737
- continue;
1738
- }
1739
- throw error;
1740
- }
1741
- }
1742
- }
1743
- hashKey(key) {
1744
- return hmac(this.masterKey, key);
1745
- }
1746
- ensureOpen() {
1747
- if (this.closed) {
1748
- throw new Error("SecretsEngine instance is closed");
1749
- }
1750
- }
1751
- }
1752
- async function resolveSalt(dirPath) {
1753
- const metaRaw = await readMetaFile(dirPath);
1754
- if (metaRaw) {
1755
- try {
1756
- const meta = JSON.parse(metaRaw);
1757
- if (meta.salt) {
1758
- return { salt: meta.salt, isNewStore: false };
1759
- }
1760
- } catch {}
1761
- }
1762
- const salt = generateSalt().toString("hex");
1763
- return { salt, isNewStore: true };
1764
- }
1765
- async function removeDirectoryContents(dirPath) {
1766
- const maxRetries = 5;
1767
- const retryDelay = 200;
1768
- for (let attempt = 0;attempt < maxRetries; attempt++) {
1769
- try {
1770
- const entries = await readdir(dirPath, { withFileTypes: true });
1771
- for (const entry of entries) {
1772
- const fullPath = join3(dirPath, entry.name);
1773
- if (entry.isDirectory()) {
1774
- await rm(fullPath, { recursive: true, force: true });
1775
- } else {
1776
- await unlink(fullPath);
1777
- }
1778
- }
1779
- await rm(dirPath, { force: true, recursive: true });
1780
- return;
1781
- } catch (error) {
1782
- const isRetryable = error instanceof Error && "code" in error && (error.code === "EBUSY" || error.code === "EPERM");
1783
- if (!isRetryable || attempt === maxRetries - 1) {
1784
- throw error;
1785
- }
1786
- await new Promise((resolve3) => setTimeout(resolve3, retryDelay * (attempt + 1)));
1787
- }
1788
- }
1789
- }
1790
- var SecretsEngineError, SecurityError, IntegrityError, KeyNotFoundError, DecryptionError, InitializationError, CONSTANTS, EXPECTED_MODES, CREATE_SECRETS_TABLE = `
1791
- CREATE TABLE IF NOT EXISTS secrets (
1792
- key_hash TEXT PRIMARY KEY,
1793
- key_enc BLOB NOT NULL,
1794
- iv BLOB NOT NULL,
1795
- cipher BLOB NOT NULL,
1796
- created INTEGER NOT NULL,
1797
- updated INTEGER NOT NULL
1798
- ) STRICT;
1799
- `, CREATE_META_TABLE = `
1800
- CREATE TABLE IF NOT EXISTS meta (
1801
- key TEXT PRIMARY KEY,
1802
- value TEXT NOT NULL
1803
- ) STRICT;
1804
- `;
1805
- var init_dist = __esm(() => {
1806
- SecretsEngineError = class SecretsEngineError extends Error {
1807
- constructor(message, options) {
1808
- super(message, options);
1809
- this.name = this.constructor.name;
1810
- }
1811
- };
1812
- SecurityError = class SecurityError extends SecretsEngineError {
1813
- expectedPermission;
1814
- actualPermission;
1815
- path;
1816
- code = "SECURITY_ERROR";
1817
- constructor(message, expectedPermission, actualPermission, path2) {
1818
- super(`${message} — expected ${expectedPermission}, got ${actualPermission} on "${path2}"`);
1819
- this.expectedPermission = expectedPermission;
1820
- this.actualPermission = actualPermission;
1821
- this.path = path2;
1822
- }
1823
- };
1824
- IntegrityError = class IntegrityError extends SecretsEngineError {
1825
- code = "INTEGRITY_ERROR";
1826
- constructor(message = "Database integrity check failed — possible tampering detected") {
1827
- super(message);
1828
- }
1829
- };
1830
- KeyNotFoundError = class KeyNotFoundError extends SecretsEngineError {
1831
- code = "KEY_NOT_FOUND";
1832
- constructor(key) {
1833
- super(`Key not found: "${key}"`);
1834
- }
1835
- };
1836
- DecryptionError = class DecryptionError extends SecretsEngineError {
1837
- keyHash;
1838
- code = "DECRYPTION_ERROR";
1839
- constructor(message, keyHash) {
1840
- const detail = keyHash ? ` (entry: ${keyHash.slice(0, 16)}…)` : "";
1841
- super(`Decryption failed${detail}: ${message}`);
1842
- this.keyHash = keyHash;
1843
- }
1844
- };
1845
- InitializationError = class InitializationError extends SecretsEngineError {
1846
- code = "INITIALIZATION_ERROR";
1847
- constructor(message, cause) {
1848
- super(`Initialization failed: ${message}`, { cause });
1849
- }
1850
- };
1851
- CONSTANTS = {
1852
- IV_LENGTH: 12,
1853
- AUTH_TAG_LENGTH: 16,
1854
- KEY_LENGTH: 32,
1855
- SCRYPT_N: 131072,
1856
- SCRYPT_R: 8,
1857
- SCRYPT_P: 1,
1858
- KEYFILE_LENGTH: 32,
1859
- SALT_LENGTH: 32,
1860
- STORE_VERSION: "1",
1861
- DIR_NAME: ".secrets-engine",
1862
- KEYFILE_NAME: ".keyfile",
1863
- DB_NAME: "store.db",
1864
- META_NAME: "meta.json"
1865
- };
1866
- EXPECTED_MODES = {
1867
- directory: 448,
1868
- database: 384,
1869
- keyfile: 256,
1870
- meta: 384
1871
- };
1872
- });
1873
-
1874
1301
  // node_modules/sisteransi/src/index.js
1875
1302
  var require_src = __commonJS((exports, module) => {
1876
1303
  var ESC = "\x1B";
@@ -5430,7 +4857,7 @@ var init_session = __esm(() => {
5430
4857
  import { spawn } from "node:child_process";
5431
4858
  import { existsSync as existsSync4 } from "node:fs";
5432
4859
  import { Socket } from "node:net";
5433
- import { dirname as dirname5, join as join8 } from "node:path";
4860
+ import { dirname as dirname4, join as join4 } from "node:path";
5434
4861
  import { fileURLToPath } from "node:url";
5435
4862
  function isZodSchema(value) {
5436
4863
  return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
@@ -5452,7 +4879,7 @@ function getNodeExecPath() {
5452
4879
  function getBundledCliPath() {
5453
4880
  const sdkUrl = import.meta.resolve("@github/copilot/sdk");
5454
4881
  const sdkPath = fileURLToPath(sdkUrl);
5455
- return join8(dirname5(dirname5(sdkPath)), "index.js");
4882
+ return join4(dirname4(dirname4(sdkPath)), "index.js");
5456
4883
  }
5457
4884
 
5458
4885
  class CopilotClient {
@@ -6204,14 +5631,14 @@ var approveAll = () => ({ kind: "approved" });
6204
5631
  var init_types2 = () => {};
6205
5632
 
6206
5633
  // node_modules/@github/copilot-sdk/dist/index.js
6207
- var exports_dist3 = {};
6208
- __export(exports_dist3, {
5634
+ var exports_dist2 = {};
5635
+ __export(exports_dist2, {
6209
5636
  defineTool: () => defineTool,
6210
5637
  approveAll: () => approveAll,
6211
5638
  CopilotSession: () => CopilotSession,
6212
5639
  CopilotClient: () => CopilotClient
6213
5640
  });
6214
- var init_dist2 = __esm(() => {
5641
+ var init_dist = __esm(() => {
6215
5642
  init_client();
6216
5643
  init_session();
6217
5644
  init_types2();
@@ -9399,991 +8826,21 @@ var LogEngine = {
9399
8826
  var import_picocolors = __toESM(require_picocolors(), 1);
9400
8827
 
9401
8828
  // src/utils/state.ts
9402
- import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
9403
- import { dirname as dirname4, join as join7, resolve as resolve3 } from "node:path";
9404
-
9405
- // node_modules/@wgtechlabs/config-engine/dist/chunk-gs6j6hrj.js
9406
- import { createRequire as createRequire2 } from "node:module";
9407
- var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
9408
- function createZodValidator(schema) {
9409
- return {
9410
- validate(data) {
9411
- const result = schema.safeParse(data);
9412
- if (result.success) {
9413
- return { success: true, data: result.data };
9414
- }
9415
- return {
9416
- success: false,
9417
- errors: formatZodErrors(result.error)
9418
- };
9419
- }
9420
- };
9421
- }
9422
- function formatZodErrors(error) {
9423
- return error.issues.map((issue) => {
9424
- const path2 = issue.path.length > 0 ? `\`${issue.path.join(".")}\` ` : "";
9425
- return `${path2}${issue.message}`;
9426
- });
9427
- }
9428
- function resolveValidator(options) {
9429
- if (options.validator)
9430
- return options.validator;
9431
- if (options.schema)
9432
- return createZodValidator(options.schema);
9433
- return;
9434
- }
9435
-
9436
- // node_modules/@wgtechlabs/config-engine/dist/index.js
9437
- import { mkdirSync as mkdirSync3 } from "node:fs";
9438
- import { dirname as dirname3 } from "node:path";
9439
- import { watch } from "node:fs";
9440
- import { homedir as homedir2, platform as platform2 } from "node:os";
9441
- import { join as join6 } from "node:path";
9442
-
9443
- class ConfigCache {
9444
- #store;
9445
- #strategy;
9446
- #data = new Map;
9447
- #dirty = new Set;
9448
- #deleted = new Set;
9449
- #flushScheduled = false;
9450
- #flushPromise = null;
9451
- constructor(store, strategy = "batched") {
9452
- this.#store = store;
9453
- this.#strategy = strategy;
9454
- }
9455
- load(initial) {
9456
- this.#data.clear();
9457
- this.#dirty.clear();
9458
- this.#deleted.clear();
9459
- const stored = initial ?? this.#store.getAll();
9460
- for (const [key, value] of Object.entries(stored)) {
9461
- this.#data.set(key, value);
9462
- }
9463
- }
9464
- get(key) {
9465
- return this.#data.get(key);
9466
- }
9467
- has(key) {
9468
- return this.#data.has(key);
9469
- }
9470
- get size() {
9471
- return this.#data.size;
9472
- }
9473
- getAll() {
9474
- const obj = Object.create(null);
9475
- for (const [key, value] of this.#data) {
9476
- obj[key] = value;
9477
- }
9478
- return obj;
9479
- }
9480
- entries() {
9481
- return this.#data.entries();
9482
- }
9483
- set(key, value) {
9484
- this.#data.set(key, value);
9485
- this.#dirty.add(key);
9486
- this.#deleted.delete(key);
9487
- this.#scheduleFlush();
9488
- }
9489
- setMany(entries) {
9490
- for (const [key, value] of entries) {
9491
- this.#data.set(key, value);
9492
- this.#dirty.add(key);
9493
- this.#deleted.delete(key);
9494
- }
9495
- this.#scheduleFlush();
9496
- }
9497
- delete(key) {
9498
- const existed = this.#data.delete(key);
9499
- if (existed) {
9500
- this.#dirty.delete(key);
9501
- this.#deleted.add(key);
9502
- this.#scheduleFlush();
9503
- }
9504
- return existed;
9505
- }
9506
- clear() {
9507
- for (const key of this.#data.keys()) {
9508
- this.#deleted.add(key);
9509
- }
9510
- this.#data.clear();
9511
- this.#dirty.clear();
9512
- this.#scheduleFlush();
9513
- }
9514
- replaceAll(data) {
9515
- for (const key of this.#data.keys()) {
9516
- if (!(key in data)) {
9517
- this.#deleted.add(key);
9518
- }
9519
- }
9520
- this.#data.clear();
9521
- this.#dirty.clear();
9522
- for (const [key, value] of Object.entries(data)) {
9523
- this.#data.set(key, value);
9524
- this.#dirty.add(key);
9525
- this.#deleted.delete(key);
9526
- }
9527
- this.#scheduleFlush();
9528
- }
9529
- #scheduleFlush() {
9530
- if (this.#strategy === "manual")
9531
- return;
9532
- if (this.#strategy === "immediate") {
9533
- this.flushSync();
9534
- return;
9535
- }
9536
- if (!this.#flushScheduled) {
9537
- this.#flushScheduled = true;
9538
- this.#flushPromise = Promise.resolve().then(() => {
9539
- this.flushSync();
9540
- this.#flushScheduled = false;
9541
- this.#flushPromise = null;
9542
- });
9543
- }
9544
- }
9545
- flushSync() {
9546
- if (this.#dirty.size === 0 && this.#deleted.size === 0)
9547
- return;
9548
- this.#store.transaction(() => {
9549
- if (this.#dirty.size > 0) {
9550
- const entries = [];
9551
- for (const key of this.#dirty) {
9552
- entries.push([key, this.#data.get(key)]);
9553
- }
9554
- this.#store.setMany(entries);
9555
- }
9556
- for (const key of this.#deleted) {
9557
- this.#store.deleteOne(key);
9558
- }
9559
- this.#store.touchLastWrite();
9560
- });
9561
- this.#dirty.clear();
9562
- this.#deleted.clear();
9563
- }
9564
- async flush() {
9565
- if (this.#flushPromise) {
9566
- await this.#flushPromise;
9567
- } else if (this.#dirty.size > 0 || this.#deleted.size > 0) {
9568
- this.flushSync();
9569
- }
9570
- }
9571
- reload() {
9572
- this.#dirty.clear();
9573
- this.#deleted.clear();
9574
- this.load();
9575
- }
9576
- }
9577
- function getByPath(obj, path2) {
9578
- if (typeof obj !== "object" || obj === null)
9579
- return;
9580
- const keys = path2.split(".");
9581
- let current = obj;
9582
- for (const key of keys) {
9583
- if (typeof current !== "object" || current === null)
9584
- return;
9585
- current = current[key];
9586
- }
9587
- return current;
9588
- }
9589
- function setByPath(obj, path2, value) {
9590
- const keys = path2.split(".");
9591
- if (keys.length === 0)
9592
- return obj;
9593
- const result = { ...obj };
9594
- let current = result;
9595
- for (let i2 = 0;i2 < keys.length - 1; i2++) {
9596
- const key = keys[i2];
9597
- if (typeof current[key] !== "object" || current[key] === null) {
9598
- current[key] = {};
9599
- } else {
9600
- current[key] = { ...current[key] };
9601
- }
9602
- current = current[key];
9603
- }
9604
- const lastKey = keys[keys.length - 1];
9605
- current[lastKey] = value;
9606
- return result;
9607
- }
9608
- function hasByPath(obj, path2) {
9609
- if (typeof obj !== "object" || obj === null)
9610
- return false;
9611
- const keys = path2.split(".");
9612
- let current = obj;
9613
- for (let i2 = 0;i2 < keys.length; i2++) {
9614
- if (typeof current !== "object" || current === null)
9615
- return false;
9616
- const key = keys[i2];
9617
- if (!Object.prototype.hasOwnProperty.call(current, key))
9618
- return false;
9619
- current = current[key];
9620
- }
9621
- return true;
9622
- }
9623
- function deleteByPath(obj, path2) {
9624
- const keys = path2.split(".");
9625
- if (keys.length === 0)
9626
- return obj;
9627
- const result = { ...obj };
9628
- let current = result;
9629
- for (let i2 = 0;i2 < keys.length - 1; i2++) {
9630
- const key = keys[i2];
9631
- if (typeof current[key] !== "object" || current[key] === null) {
9632
- return obj;
9633
- }
9634
- current[key] = { ...current[key] };
9635
- current = current[key];
9636
- }
9637
- const lastKey = keys[keys.length - 1];
9638
- delete current[lastKey];
9639
- return result;
9640
- }
9641
-
9642
- class SecretsEngineEncryptor {
9643
- #keyName;
9644
- #derivedKey = null;
9645
- #secrets = null;
9646
- constructor(keyName) {
9647
- this.#keyName = keyName;
9648
- }
9649
- async init() {
9650
- let SecretsEngine2;
9651
- try {
9652
- const mod = await Promise.resolve().then(() => (init_dist(), exports_dist));
9653
- SecretsEngine2 = mod.SecretsEngine ?? mod.default?.SecretsEngine ?? mod;
9654
- } catch {
9655
- throw new Error('Encryption requires "@wgtechlabs/secrets-engine" as a peer dependency. Install it with: bun add @wgtechlabs/secrets-engine');
9656
- }
9657
- this.#secrets = await SecretsEngine2.open();
9658
- const hasKey = await this.#secrets.has(this.#keyName);
9659
- if (!hasKey) {
9660
- const keyBytes = crypto.getRandomValues(new Uint8Array(32));
9661
- const keyHex2 = Array.from(keyBytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
9662
- await this.#secrets.set(this.#keyName, keyHex2);
9663
- }
9664
- const keyHex = await this.#secrets.get(this.#keyName);
9665
- const keyBuffer = hexToUint8Array(keyHex);
9666
- this.#derivedKey = await crypto.subtle.importKey("raw", keyBuffer.buffer, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
9667
- }
9668
- async encrypt(plaintext) {
9669
- if (!this.#derivedKey)
9670
- throw new Error("Encryptor not initialized. Call init() first.");
9671
- const iv = crypto.getRandomValues(new Uint8Array(12));
9672
- const encoded = new TextEncoder().encode(plaintext);
9673
- const cipherBuffer = await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv.buffer }, this.#derivedKey, encoded);
9674
- const ivB64 = uint8ArrayToBase64(iv);
9675
- const cipherB64 = uint8ArrayToBase64(new Uint8Array(cipherBuffer));
9676
- return `${ivB64}:${cipherB64}`;
9677
- }
9678
- async decrypt(ciphertext) {
9679
- if (!this.#derivedKey)
9680
- throw new Error("Encryptor not initialized. Call init() first.");
9681
- const [ivB64, cipherB64] = ciphertext.split(":");
9682
- if (!ivB64 || !cipherB64)
9683
- throw new Error("Invalid encrypted data format.");
9684
- const iv = base64ToUint8Array(ivB64);
9685
- const cipherBuffer = base64ToUint8Array(cipherB64);
9686
- const plainBuffer = await crypto.subtle.decrypt({ name: "AES-GCM", iv: iv.buffer }, this.#derivedKey, cipherBuffer.buffer);
9687
- return new TextDecoder().decode(plainBuffer);
9688
- }
9689
- async close() {
9690
- if (this.#secrets) {
9691
- this.#secrets.close();
9692
- this.#secrets = null;
9693
- }
9694
- }
9695
- }
9696
- function hexToUint8Array(hex) {
9697
- const bytes = new Uint8Array(hex.length / 2);
9698
- for (let i2 = 0;i2 < hex.length; i2 += 2) {
9699
- bytes[i2 / 2] = Number.parseInt(hex.substring(i2, i2 + 2), 16);
9700
- }
9701
- return bytes;
9702
- }
9703
- function uint8ArrayToBase64(bytes) {
9704
- if (typeof Buffer !== "undefined") {
9705
- return Buffer.from(bytes).toString("base64");
9706
- }
9707
- let binary = "";
9708
- for (const byte of bytes) {
9709
- binary += String.fromCharCode(byte);
9710
- }
9711
- return btoa(binary);
9712
- }
9713
- function base64ToUint8Array(b64) {
9714
- if (typeof Buffer !== "undefined") {
9715
- return new Uint8Array(Buffer.from(b64, "base64"));
9716
- }
9717
- const binary = atob(b64);
9718
- const bytes = new Uint8Array(binary.length);
9719
- for (let i2 = 0;i2 < binary.length; i2++) {
9720
- bytes[i2] = binary.charCodeAt(i2);
9721
- }
9722
- return bytes;
9723
- }
9724
- async function resolveEncryptor(encryptionKey) {
9725
- if (!encryptionKey)
9726
- return;
9727
- if (typeof encryptionKey === "string") {
9728
- const enc = new SecretsEngineEncryptor(encryptionKey);
9729
- await enc.init();
9730
- return enc;
9731
- }
9732
- return encryptionKey;
9733
- }
9734
- var META_KEY = "schema_version";
9735
- var INITIAL_VERSION = "0.0.0";
9736
- async function runMigrations(options) {
9737
- const { store, migrations, projectVersion, beforeEachMigration, afterEachMigration } = options;
9738
- if (migrations.length === 0)
9739
- return;
9740
- const currentVersion = store.getMeta(META_KEY) ?? INITIAL_VERSION;
9741
- const versions = migrations.map((m2) => m2.version);
9742
- const finalVersion = projectVersion;
9743
- const pending = migrations.filter((m2) => compareVersions(m2.version, currentVersion) > 0);
9744
- if (pending.length === 0) {
9745
- store.setMeta(META_KEY, projectVersion);
9746
- return;
9747
- }
9748
- pending.sort((a2, b2) => compareVersions(a2.version, b2.version));
9749
- const ctx = createMigrationContext(store);
9750
- const snapshot = store.getAll();
9751
- const snapshotVersion = currentVersion;
9752
- try {
9753
- for (const migration of pending) {
9754
- const hookCtx = {
9755
- fromVersion: currentVersion,
9756
- toVersion: migration.version,
9757
- finalVersion,
9758
- versions
9759
- };
9760
- if (beforeEachMigration) {
9761
- await beforeEachMigration(hookCtx);
9762
- }
9763
- await migration.up(ctx);
9764
- if (afterEachMigration) {
9765
- await afterEachMigration(hookCtx);
9766
- }
9767
- }
9768
- store.setMeta(META_KEY, projectVersion);
9769
- store.touchLastWrite();
9770
- } catch (error) {
9771
- store.transaction(() => {
9772
- store.deleteAll();
9773
- const entries = Object.entries(snapshot);
9774
- if (entries.length > 0) {
9775
- store.setMany(entries);
9776
- }
9777
- store.setMeta(META_KEY, snapshotVersion);
9778
- });
9779
- throw new Error(`Migration to version "${pending.find(() => true)?.version}" failed. All changes have been rolled back. Original error: ${error instanceof Error ? error.message : String(error)}`);
9780
- }
9781
- }
9782
- function createMigrationContext(store) {
9783
- return {
9784
- get(key) {
9785
- return store.getOne(key);
9786
- },
9787
- set(key, value) {
9788
- store.setOne(key, value);
9789
- },
9790
- has(key) {
9791
- return store.has(key);
9792
- },
9793
- delete(key) {
9794
- return store.deleteOne(key);
9795
- }
9796
- };
9797
- }
9798
- function compareVersions(a2, b2) {
9799
- const partsA = a2.split(".").map(Number);
9800
- const partsB = b2.split(".").map(Number);
9801
- for (let i2 = 0;i2 < 3; i2++) {
9802
- const va = partsA[i2] ?? 0;
9803
- const vb = partsB[i2] ?? 0;
9804
- if (va !== vb)
9805
- return va - vb;
9806
- }
9807
- return 0;
9808
- }
9809
- function resolveConfigDir(projectName) {
9810
- const home = homedir2();
9811
- const os2 = platform2();
9812
- switch (os2) {
9813
- case "darwin":
9814
- return join6(home, "Library", "Preferences", projectName);
9815
- case "win32":
9816
- return join6(process.env.APPDATA ?? join6(home, "AppData", "Roaming"), projectName);
9817
- default:
9818
- return join6(process.env.XDG_CONFIG_HOME ?? join6(home, ".config"), projectName);
9819
- }
9820
- }
9821
- function resolveConfigPath(options) {
9822
- const dir = options.cwd ?? resolveConfigDir(options.projectName);
9823
- const name = options.configName ?? "config";
9824
- return join6(dir, `${name}.db`);
9825
- }
9826
- function isBun() {
9827
- return typeof globalThis.Bun !== "undefined";
9828
- }
9829
- function openDatabase(filepath) {
9830
- if (isBun()) {
9831
- return openBunDatabase(filepath);
9832
- }
9833
- return openNodeDatabase(filepath);
9834
- }
9835
- function openBunDatabase(filepath) {
9836
- const { Database: Database2 } = __require2("bun:sqlite");
9837
- const db = new Database2(filepath);
9838
- db.exec("PRAGMA journal_mode = WAL;");
9839
- db.exec("PRAGMA busy_timeout = 5000;");
9840
- return {
9841
- prepare(sql) {
9842
- const stmt = db.prepare(sql);
9843
- return {
9844
- run(...params) {
9845
- stmt.run(...params);
9846
- },
9847
- get(...params) {
9848
- return stmt.get(...params);
9849
- },
9850
- all(...params) {
9851
- return stmt.all(...params);
9852
- }
9853
- };
9854
- },
9855
- exec(sql) {
9856
- db.exec(sql);
9857
- },
9858
- close() {
9859
- db.close();
9860
- },
9861
- transaction(fn) {
9862
- return db.transaction(fn);
9863
- }
9864
- };
9865
- }
9866
- function openNodeDatabase(filepath) {
9867
- let BetterSqlite3;
9868
- try {
9869
- BetterSqlite3 = __require2("better-sqlite3");
9870
- } catch {
9871
- throw new Error('config-engine requires "better-sqlite3" as a peer dependency when running on Node.js. Install it with: npm install better-sqlite3');
9872
- }
9873
- const db = new BetterSqlite3(filepath);
9874
- db.pragma("journal_mode = WAL");
9875
- db.pragma("busy_timeout = 5000");
9876
- return {
9877
- prepare(sql) {
9878
- const stmt = db.prepare(sql);
9879
- return {
9880
- run(...params) {
9881
- stmt.run(...params);
9882
- },
9883
- get(...params) {
9884
- return stmt.get(...params);
9885
- },
9886
- all(...params) {
9887
- return stmt.all(...params);
9888
- }
9889
- };
9890
- },
9891
- exec(sql) {
9892
- db.exec(sql);
9893
- },
9894
- close() {
9895
- db.close();
9896
- },
9897
- transaction(fn) {
9898
- return db.transaction(fn);
9899
- }
9900
- };
9901
- }
9902
-
9903
- class ConfigStore {
9904
- #db;
9905
- constructor(db) {
9906
- this.#db = db;
9907
- this.#initialize();
9908
- }
9909
- #initialize() {
9910
- this.#db.exec(`
9911
- CREATE TABLE IF NOT EXISTS config (
9912
- key TEXT PRIMARY KEY,
9913
- value TEXT NOT NULL
9914
- );
9915
- `);
9916
- this.#db.exec(`
9917
- CREATE TABLE IF NOT EXISTS _meta (
9918
- key TEXT PRIMARY KEY,
9919
- value TEXT NOT NULL
9920
- );
9921
- `);
9922
- }
9923
- getAll() {
9924
- const rows = this.#db.prepare("SELECT key, value FROM config").all();
9925
- const result = Object.create(null);
9926
- for (const row of rows) {
9927
- result[row.key] = JSON.parse(row.value);
9928
- }
9929
- return result;
9930
- }
9931
- getOne(key) {
9932
- const row = this.#db.prepare("SELECT value FROM config WHERE key = ?").get(key);
9933
- return row ? JSON.parse(row.value) : undefined;
9934
- }
9935
- setOne(key, value) {
9936
- this.#db.prepare("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)").run(key, JSON.stringify(value));
9937
- }
9938
- setMany(entries) {
9939
- const stmt = this.#db.prepare("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)");
9940
- const runTransaction = this.#db.transaction(() => {
9941
- for (const [key, value] of entries) {
9942
- stmt.run(key, JSON.stringify(value));
9943
- }
9944
- });
9945
- runTransaction();
9946
- }
9947
- deleteOne(key) {
9948
- const before = this.count();
9949
- this.#db.prepare("DELETE FROM config WHERE key = ?").run(key);
9950
- return this.count() < before;
9951
- }
9952
- deleteAll() {
9953
- this.#db.exec("DELETE FROM config;");
9954
- }
9955
- has(key) {
9956
- const row = this.#db.prepare("SELECT 1 FROM config WHERE key = ? LIMIT 1").get(key);
9957
- return row !== undefined && row !== null;
9958
- }
9959
- count() {
9960
- const row = this.#db.prepare("SELECT COUNT(*) as cnt FROM config").get();
9961
- return row.cnt;
9962
- }
9963
- getMeta(key) {
9964
- const row = this.#db.prepare("SELECT value FROM _meta WHERE key = ?").get(key);
9965
- return row?.value;
9966
- }
9967
- setMeta(key, value) {
9968
- this.#db.prepare("INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)").run(key, value);
9969
- }
9970
- hasMeta(key) {
9971
- const row = this.#db.prepare("SELECT 1 FROM _meta WHERE key = ? LIMIT 1").get(key);
9972
- return row !== undefined && row !== null;
9973
- }
9974
- transaction(fn) {
9975
- const wrapped = this.#db.transaction(fn);
9976
- return wrapped();
9977
- }
9978
- touchLastWrite() {
9979
- this.setMeta("last_write", Date.now().toString());
9980
- }
9981
- getLastWrite() {
9982
- const val = this.getMeta("last_write");
9983
- return val ? Number(val) : 0;
9984
- }
9985
- close() {
9986
- this.#db.close();
9987
- }
9988
- }
9989
-
9990
- class ConfigEngineError extends Error {
9991
- name = "ConfigEngineError";
9992
- }
9993
-
9994
- class ValidationError extends ConfigEngineError {
9995
- name = "ValidationError";
9996
- errors;
9997
- constructor(errors) {
9998
- super(`Config validation failed:
9999
- - ${errors.join(`
10000
- - `)}`);
10001
- this.errors = errors;
10002
- }
10003
- }
10004
-
10005
- class ConfigEngine {
10006
- #store;
10007
- #cache;
10008
- #validator;
10009
- #encryptor;
10010
- #defaults;
10011
- #dotNotation;
10012
- #filePath;
10013
- #listeners = new Map;
10014
- #anyListeners = new Set;
10015
- #watcher = null;
10016
- #lastKnownWrite = 0;
10017
- #closed = false;
10018
- constructor(store, cache, options) {
10019
- this.#store = store;
10020
- this.#cache = cache;
10021
- this.#validator = options.validator;
10022
- this.#encryptor = options.encryptor;
10023
- this.#defaults = options.defaults;
10024
- this.#dotNotation = options.dotNotation;
10025
- this.#filePath = options.filePath;
10026
- this.#lastKnownWrite = store.getLastWrite();
10027
- if (options.watch) {
10028
- this.#startWatching();
10029
- }
10030
- }
10031
- static async open(options) {
10032
- const {
10033
- projectName,
10034
- projectVersion,
10035
- cwd,
10036
- configName,
10037
- defaults = {},
10038
- encryptionKey,
10039
- migrations,
10040
- beforeEachMigration,
10041
- afterEachMigration,
10042
- flushStrategy = "batched",
10043
- accessPropertiesByDotNotation = true,
10044
- watch: enableWatch = false,
10045
- clearInvalidConfig = false
10046
- } = options;
10047
- const filePath = resolveConfigPath({ projectName, cwd, configName });
10048
- mkdirSync3(dirname3(filePath), { recursive: true });
10049
- const db = openDatabase(filePath);
10050
- const store = new ConfigStore(db);
10051
- const encryptor = await resolveEncryptor(encryptionKey);
10052
- const validator = resolveValidator(options);
10053
- let storedData;
10054
- try {
10055
- storedData = store.getAll();
10056
- if (encryptor) {
10057
- storedData = await decryptStore(storedData, encryptor);
10058
- }
10059
- } catch (error) {
10060
- if (clearInvalidConfig) {
10061
- store.deleteAll();
10062
- storedData = {};
10063
- } else {
10064
- store.close();
10065
- throw new ConfigEngineError(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
10066
- }
10067
- }
10068
- const merged = { ...defaults, ...storedData };
10069
- if (validator) {
10070
- const result = validator.validate(merged);
10071
- if (!result.success) {
10072
- if (clearInvalidConfig) {
10073
- store.deleteAll();
10074
- const freshData = { ...defaults };
10075
- const freshResult = validator.validate(freshData);
10076
- if (!freshResult.success) {
10077
- store.close();
10078
- throw new ValidationError(freshResult.errors);
10079
- }
10080
- const entries = Object.entries(freshData);
10081
- if (encryptor) {
10082
- const encrypted = await encryptStore(freshData, encryptor);
10083
- store.setMany(Object.entries(encrypted));
10084
- } else {
10085
- store.setMany(entries);
10086
- }
10087
- store.touchLastWrite();
10088
- const cache2 = new ConfigCache(store, flushStrategy);
10089
- cache2.load(freshData);
10090
- return new ConfigEngine(store, cache2, {
10091
- validator,
10092
- encryptor,
10093
- defaults,
10094
- dotNotation: accessPropertiesByDotNotation,
10095
- filePath,
10096
- watch: enableWatch
10097
- });
10098
- }
10099
- store.close();
10100
- throw new ValidationError(result.errors);
10101
- }
10102
- }
10103
- const hasNewDefaults = Object.keys(defaults).some((key) => !(key in storedData));
10104
- if (hasNewDefaults) {
10105
- if (encryptor) {
10106
- const encrypted = await encryptStore(merged, encryptor);
10107
- store.setMany(Object.entries(encrypted));
10108
- } else {
10109
- store.setMany(Object.entries(merged));
10110
- }
10111
- store.touchLastWrite();
10112
- }
10113
- if (migrations && migrations.length > 0) {
10114
- if (!projectVersion) {
10115
- store.close();
10116
- throw new ConfigEngineError('"projectVersion" is required when "migrations" is provided.');
10117
- }
10118
- await runMigrations({
10119
- store,
10120
- migrations,
10121
- projectVersion,
10122
- beforeEachMigration,
10123
- afterEachMigration
10124
- });
10125
- }
10126
- const cache = new ConfigCache(store, flushStrategy);
10127
- let finalData;
10128
- if (encryptor) {
10129
- finalData = await decryptStore(store.getAll(), encryptor);
10130
- } else {
10131
- finalData = store.getAll();
10132
- }
10133
- const cacheData = { ...defaults, ...finalData };
10134
- cache.load(cacheData);
10135
- return new ConfigEngine(store, cache, {
10136
- validator,
10137
- encryptor,
10138
- defaults,
10139
- dotNotation: accessPropertiesByDotNotation,
10140
- filePath,
10141
- watch: enableWatch
10142
- });
10143
- }
10144
- get(key, defaultValue) {
10145
- this.#ensureOpen();
10146
- if (this.#dotNotation && key.includes(".")) {
10147
- const full = this.#cache.getAll();
10148
- const value2 = getByPath(full, key);
10149
- return value2 !== undefined ? value2 : defaultValue;
10150
- }
10151
- const value = this.#cache.get(key);
10152
- return value !== undefined ? value : defaultValue;
10153
- }
10154
- has(key) {
10155
- this.#ensureOpen();
10156
- if (this.#dotNotation && key.includes(".")) {
10157
- return hasByPath(this.#cache.getAll(), key);
10158
- }
10159
- return this.#cache.has(key);
10160
- }
10161
- get store() {
10162
- this.#ensureOpen();
10163
- return this.#cache.getAll();
10164
- }
10165
- set store(value) {
10166
- this.#ensureOpen();
10167
- this.#validateFull(value);
10168
- const oldStore = this.#cache.getAll();
10169
- this.#cache.replaceAll(value);
10170
- this.#notifyAnyChange(value, oldStore);
10171
- }
10172
- get size() {
10173
- this.#ensureOpen();
10174
- return this.#cache.size;
10175
- }
10176
- get path() {
10177
- return this.#filePath;
10178
- }
10179
- set(keyOrObject, value) {
10180
- this.#ensureOpen();
10181
- if (typeof keyOrObject === "string") {
10182
- this.#setKey(keyOrObject, value);
10183
- } else {
10184
- const entries = Object.entries(keyOrObject);
10185
- const oldStore = this.#cache.getAll();
10186
- for (const [key, val] of entries) {
10187
- if (this.#dotNotation && key.includes(".")) {
10188
- const full = this.#cache.getAll();
10189
- const updated = setByPath(full, key, val);
10190
- this.#validateFull(updated);
10191
- this.#cache.replaceAll(updated);
10192
- } else {
10193
- this.#cache.set(key, val);
10194
- }
10195
- }
10196
- this.#validateFull(this.#cache.getAll());
10197
- this.#notifyAnyChange(this.#cache.getAll(), oldStore);
10198
- }
10199
- }
10200
- #setKey(key, value) {
10201
- const oldStore = this.#cache.getAll();
10202
- const oldValue = this.get(key);
10203
- if (this.#dotNotation && key.includes(".")) {
10204
- const full = this.#cache.getAll();
10205
- const updated = setByPath(full, key, value);
10206
- this.#validateFull(updated);
10207
- this.#cache.replaceAll(updated);
10208
- } else {
10209
- const testStore = { ...this.#cache.getAll(), [key]: value };
10210
- this.#validateFull(testStore);
10211
- this.#cache.set(key, value);
10212
- }
10213
- this.#notifyKeyChange(key, value, oldValue);
10214
- this.#notifyAnyChange(this.#cache.getAll(), oldStore);
10215
- }
10216
- delete(key) {
10217
- this.#ensureOpen();
10218
- const oldStore = this.#cache.getAll();
10219
- const oldValue = this.get(key);
10220
- if (this.#dotNotation && key.includes(".")) {
10221
- const full = this.#cache.getAll();
10222
- const updated = deleteByPath(full, key);
10223
- this.#cache.replaceAll(updated);
10224
- } else {
10225
- this.#cache.delete(key);
10226
- }
10227
- this.#notifyKeyChange(key, undefined, oldValue);
10228
- this.#notifyAnyChange(this.#cache.getAll(), oldStore);
10229
- }
10230
- clear() {
10231
- this.#ensureOpen();
10232
- const oldStore = this.#cache.getAll();
10233
- this.#cache.clear();
10234
- if (this.#defaults && Object.keys(this.#defaults).length > 0) {
10235
- this.#cache.setMany(Object.entries(this.#defaults));
10236
- }
10237
- this.#notifyAnyChange(this.#cache.getAll(), oldStore);
10238
- }
10239
- reset(...keys) {
10240
- this.#ensureOpen();
10241
- const oldStore = this.#cache.getAll();
10242
- for (const key of keys) {
10243
- if (key in this.#defaults) {
10244
- this.#cache.set(key, this.#defaults[key]);
10245
- } else {
10246
- this.#cache.delete(key);
10247
- }
10248
- }
10249
- this.#notifyAnyChange(this.#cache.getAll(), oldStore);
10250
- }
10251
- onDidChange(key, callback) {
10252
- if (!this.#listeners.has(key)) {
10253
- this.#listeners.set(key, new Set);
10254
- }
10255
- const set = this.#listeners.get(key);
10256
- set.add(callback);
10257
- return () => {
10258
- set.delete(callback);
10259
- if (set.size === 0)
10260
- this.#listeners.delete(key);
10261
- };
10262
- }
10263
- onDidAnyChange(callback) {
10264
- this.#anyListeners.add(callback);
10265
- return () => {
10266
- this.#anyListeners.delete(callback);
10267
- };
10268
- }
10269
- async flush() {
10270
- this.#ensureOpen();
10271
- await this.#cache.flush();
10272
- }
10273
- flushSync() {
10274
- this.#ensureOpen();
10275
- this.#cache.flushSync();
10276
- }
10277
- close() {
10278
- if (this.#closed)
10279
- return;
10280
- this.#stopWatching();
10281
- this.#cache.flushSync();
10282
- this.#store.close();
10283
- this.#closed = true;
10284
- }
10285
- *[Symbol.iterator]() {
10286
- this.#ensureOpen();
10287
- yield* this.#cache.entries();
10288
- }
10289
- #ensureOpen() {
10290
- if (this.#closed) {
10291
- throw new ConfigEngineError("This ConfigEngine instance has been closed. Create a new one with ConfigEngine.open().");
10292
- }
10293
- }
10294
- #validateFull(data) {
10295
- if (!this.#validator)
10296
- return;
10297
- const result = this.#validator.validate(data);
10298
- if (!result.success) {
10299
- throw new ValidationError(result.errors);
10300
- }
10301
- }
10302
- #notifyKeyChange(key, newValue, oldValue) {
10303
- if (newValue === oldValue)
10304
- return;
10305
- if (typeof newValue === "object" && typeof oldValue === "object" && JSON.stringify(newValue) === JSON.stringify(oldValue)) {
10306
- return;
10307
- }
10308
- const listeners = this.#listeners.get(key);
10309
- if (listeners) {
10310
- for (const cb of listeners) {
10311
- cb(newValue, oldValue);
10312
- }
10313
- }
10314
- }
10315
- #notifyAnyChange(newStore, oldStore) {
10316
- if (JSON.stringify(newStore) === JSON.stringify(oldStore))
10317
- return;
10318
- for (const cb of this.#anyListeners) {
10319
- cb(newStore, oldStore);
10320
- }
10321
- }
10322
- #startWatching() {
10323
- try {
10324
- this.#watcher = watch(dirname3(this.#filePath), (_event, filename) => {
10325
- if (!filename)
10326
- return;
10327
- const expectedName = this.#filePath.split(/[\\/]/).pop();
10328
- if (filename !== expectedName)
10329
- return;
10330
- const currentWrite = this.#store.getLastWrite();
10331
- if (currentWrite > this.#lastKnownWrite) {
10332
- this.#lastKnownWrite = currentWrite;
10333
- const oldStore = this.#cache.getAll();
10334
- this.#cache.reload();
10335
- const newStore = this.#cache.getAll();
10336
- this.#notifyAnyChange(newStore, oldStore);
10337
- }
10338
- });
10339
- if (this.#watcher && "unref" in this.#watcher) {
10340
- this.#watcher.unref();
10341
- }
10342
- } catch {}
10343
- }
10344
- #stopWatching() {
10345
- if (this.#watcher) {
10346
- this.#watcher.close();
10347
- this.#watcher = null;
10348
- }
10349
- }
10350
- }
10351
- async function encryptStore(data, encryptor) {
10352
- const result = {};
10353
- for (const [key, value] of Object.entries(data)) {
10354
- result[key] = await encryptor.encrypt(JSON.stringify(value));
10355
- }
10356
- return result;
10357
- }
10358
- async function decryptStore(data, encryptor) {
10359
- const result = {};
10360
- for (const [key, value] of Object.entries(data)) {
10361
- if (typeof value === "string") {
10362
- try {
10363
- const decrypted = await encryptor.decrypt(value);
10364
- result[key] = JSON.parse(decrypted);
10365
- } catch {
10366
- result[key] = value;
10367
- }
10368
- } else {
10369
- result[key] = value;
10370
- }
10371
- }
10372
- return result;
10373
- }
10374
-
10375
- // src/utils/state.ts
8829
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
8830
+ import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
10376
8831
  var LOCAL_STATE_DIRNAME = "contribute-now";
10377
8832
  var LOCAL_STATE_CONFIG_NAME = "state";
10378
8833
  var LOCAL_STATE_LOCATION_LABEL = `.git/${LOCAL_STATE_DIRNAME}/${LOCAL_STATE_CONFIG_NAME}.db`;
10379
- var stateStoreCache = new Map;
8834
+ var DEFAULT_LOCAL_STATE = {
8835
+ guideRotation: {}
8836
+ };
10380
8837
  function findRepoRoot2(cwd = process.cwd()) {
10381
8838
  let current = resolve3(cwd);
10382
8839
  while (true) {
10383
- if (existsSync3(join7(current, ".git"))) {
8840
+ if (existsSync3(join3(current, ".git"))) {
10384
8841
  return current;
10385
8842
  }
10386
- const parent = dirname4(current);
8843
+ const parent = dirname3(current);
10387
8844
  if (parent === current) {
10388
8845
  return null;
10389
8846
  }
@@ -10395,13 +8852,13 @@ function resolveGitDir2(cwd = process.cwd()) {
10395
8852
  if (!repoRoot) {
10396
8853
  return null;
10397
8854
  }
10398
- const dotGitPath = join7(repoRoot, ".git");
8855
+ const dotGitPath = join3(repoRoot, ".git");
10399
8856
  try {
10400
- const stat2 = statSync3(dotGitPath);
10401
- if (stat2.isDirectory()) {
8857
+ const stat = statSync3(dotGitPath);
8858
+ if (stat.isDirectory()) {
10402
8859
  return dotGitPath;
10403
8860
  }
10404
- if (!stat2.isFile()) {
8861
+ if (!stat.isFile()) {
10405
8862
  return null;
10406
8863
  }
10407
8864
  const content = readFileSync3(dotGitPath, "utf-8").trim();
@@ -10419,40 +8876,41 @@ function getLocalStateDir(cwd = process.cwd()) {
10419
8876
  if (!gitDir) {
10420
8877
  return null;
10421
8878
  }
10422
- return join7(gitDir, LOCAL_STATE_DIRNAME);
8879
+ return join3(gitDir, LOCAL_STATE_DIRNAME);
10423
8880
  }
10424
- async function openLocalStateStore(cwd = process.cwd()) {
10425
- const stateDir = getLocalStateDir(cwd);
10426
- if (!stateDir) {
10427
- return null;
8881
+ function readLocalState(cwd = process.cwd()) {
8882
+ const statePath = getLocalStatePath(cwd);
8883
+ if (!statePath || !existsSync3(statePath)) {
8884
+ return DEFAULT_LOCAL_STATE;
8885
+ }
8886
+ try {
8887
+ const raw = JSON.parse(readFileSync3(statePath, "utf-8"));
8888
+ const guideRotation = raw.guideRotation && typeof raw.guideRotation === "object" ? raw.guideRotation : {};
8889
+ return {
8890
+ guideRotation: Object.fromEntries(Object.entries(guideRotation).filter((entry) => {
8891
+ const [key, value] = entry;
8892
+ return typeof key === "string" && typeof value === "number" && Number.isFinite(value);
8893
+ }))
8894
+ };
8895
+ } catch {
8896
+ return DEFAULT_LOCAL_STATE;
10428
8897
  }
10429
- return ConfigEngine.open({
10430
- projectName: "contribute-now",
10431
- cwd: stateDir,
10432
- configName: LOCAL_STATE_CONFIG_NAME,
10433
- defaults: {
10434
- guideRotation: {}
10435
- },
10436
- flushStrategy: "immediate"
10437
- });
10438
8898
  }
10439
- async function getLocalStateStore(cwd = process.cwd()) {
8899
+ function writeLocalState(state, cwd = process.cwd()) {
10440
8900
  const statePath = getLocalStatePath(cwd);
10441
- const cacheKey = statePath ?? resolve3(cwd);
10442
- const existing = stateStoreCache.get(cacheKey);
10443
- if (existing) {
10444
- return existing;
8901
+ if (!statePath) {
8902
+ return;
10445
8903
  }
10446
- const storePromise = openLocalStateStore(cwd).catch(() => null);
10447
- stateStoreCache.set(cacheKey, storePromise);
10448
- return storePromise;
8904
+ mkdirSync3(dirname3(statePath), { recursive: true });
8905
+ writeFileSync3(statePath, `${JSON.stringify(state, null, 2)}
8906
+ `, "utf-8");
10449
8907
  }
10450
8908
  function getLocalStatePath(cwd = process.cwd()) {
10451
8909
  const stateDir = getLocalStateDir(cwd);
10452
8910
  if (!stateDir) {
10453
8911
  return null;
10454
8912
  }
10455
- return join7(stateDir, `${LOCAL_STATE_CONFIG_NAME}.db`);
8913
+ return join3(stateDir, `${LOCAL_STATE_CONFIG_NAME}.db`);
10456
8914
  }
10457
8915
  function getLocalStateLocationLabel(cwd = process.cwd()) {
10458
8916
  return getLocalStatePath(cwd) ? LOCAL_STATE_LOCATION_LABEL : null;
@@ -10462,25 +8920,23 @@ function hasLocalStateStore(cwd = process.cwd()) {
10462
8920
  return !!statePath && existsSync3(statePath);
10463
8921
  }
10464
8922
  async function getGuideRotationIndex(commandKey, cwd = process.cwd()) {
10465
- const store = await getLocalStateStore(cwd);
10466
- if (!store) {
10467
- return 0;
10468
- }
10469
- const rotationIndex = store.get(`guideRotation.${commandKey}`, 0);
8923
+ const rotationIndex = readLocalState(cwd).guideRotation[commandKey];
10470
8924
  return typeof rotationIndex === "number" ? rotationIndex : 0;
10471
8925
  }
10472
8926
  async function advanceGuideRotation(commandKey, rotatableCount, cwd = process.cwd()) {
10473
8927
  if (rotatableCount <= 0) {
10474
8928
  return;
10475
8929
  }
10476
- const store = await getLocalStateStore(cwd);
10477
- if (!store) {
10478
- return;
10479
- }
10480
- const rotationIndex = store.get(`guideRotation.${commandKey}`, 0);
8930
+ const state = readLocalState(cwd);
8931
+ const rotationIndex = state.guideRotation[commandKey];
10481
8932
  const currentIndex = typeof rotationIndex === "number" ? rotationIndex : 0;
10482
8933
  const nextIndex = (currentIndex + 1) % rotatableCount;
10483
- store.set(`guideRotation.${commandKey}`, nextIndex);
8934
+ writeLocalState({
8935
+ guideRotation: {
8936
+ ...state.guideRotation,
8937
+ [commandKey]: nextIndex
8938
+ }
8939
+ }, cwd);
10484
8940
  }
10485
8941
 
10486
8942
  // src/utils/tips.ts
@@ -12156,54 +10612,79 @@ async function multiSelectPrompt(message, choices) {
12156
10612
  }
12157
10613
 
12158
10614
  // src/utils/copilot.ts
12159
- init_dist2();
10615
+ init_dist();
12160
10616
 
12161
10617
  // src/utils/secrets.ts
12162
- init_dist();
12163
- import { existsSync as existsSync5 } from "node:fs";
12164
- import { homedir as homedir3 } from "node:os";
12165
- import { resolve as resolve4 } from "node:path";
10618
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
10619
+ import { homedir } from "node:os";
10620
+ import { join as join5, resolve as resolve4 } from "node:path";
12166
10621
  var CONTRIBUTE_NOW_SECRETS_DIRNAME = ".contribute-now";
12167
10622
  var CONTRIBUTE_NOW_SECRETS_STORE_DIRNAME = "secrets";
12168
10623
  var OLLAMA_CLOUD_API_KEY = "ollama.cloud.apiKey";
12169
- var secretsStoreCache = new Map;
12170
- function getSecretsStorePath(baseDir = homedir3()) {
10624
+ function getSecretsStorePath(baseDir = homedir()) {
12171
10625
  return resolve4(baseDir, CONTRIBUTE_NOW_SECRETS_DIRNAME, CONTRIBUTE_NOW_SECRETS_STORE_DIRNAME);
12172
10626
  }
12173
- async function getSecretsStore(baseDir = homedir3(), createIfMissing = true) {
12174
- const storePath = getSecretsStorePath(baseDir);
12175
- const existing = secretsStoreCache.get(storePath);
12176
- if (existing) {
12177
- return existing;
10627
+ function getSecretsFilePath(baseDir = homedir()) {
10628
+ return join5(getSecretsStorePath(baseDir), "store.json");
10629
+ }
10630
+ function readSecretsStore(baseDir = homedir()) {
10631
+ const filePath = getSecretsFilePath(baseDir);
10632
+ if (!existsSync5(filePath)) {
10633
+ return null;
12178
10634
  }
12179
- if (!createIfMissing && !existsSync5(storePath)) {
10635
+ try {
10636
+ const parsed = JSON.parse(readFileSync4(filePath, "utf-8"));
10637
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
10638
+ } catch {
12180
10639
  return null;
12181
10640
  }
12182
- const storePromise = SecretsEngine.open({ path: storePath });
12183
- secretsStoreCache.set(storePath, storePromise);
12184
- return storePromise;
12185
10641
  }
12186
- function hasSecretsStore(baseDir = homedir3()) {
12187
- return existsSync5(getSecretsStorePath(baseDir));
10642
+ function writeSecretsStore(store, baseDir = homedir()) {
10643
+ const storePath = getSecretsStorePath(baseDir);
10644
+ const filePath = getSecretsFilePath(baseDir);
10645
+ mkdirSync4(storePath, { recursive: true, mode: 448 });
10646
+ writeFileSync4(filePath, `${JSON.stringify(store, null, 2)}
10647
+ `, {
10648
+ encoding: "utf-8",
10649
+ mode: 384
10650
+ });
10651
+ try {
10652
+ chmodSync(storePath, 448);
10653
+ chmodSync(filePath, 384);
10654
+ } catch {}
12188
10655
  }
12189
- async function hasOllamaCloudApiKey(baseDir = homedir3()) {
12190
- const store = await getSecretsStore(baseDir, false);
12191
- return store ? store.has(OLLAMA_CLOUD_API_KEY) : false;
10656
+ function hasSecretsStore(baseDir = homedir()) {
10657
+ return existsSync5(getSecretsFilePath(baseDir));
12192
10658
  }
12193
- async function getOllamaCloudApiKey(baseDir = homedir3()) {
12194
- const store = await getSecretsStore(baseDir, false);
12195
- return store ? store.get(OLLAMA_CLOUD_API_KEY) : null;
10659
+ async function hasOllamaCloudApiKey(baseDir = homedir()) {
10660
+ return typeof readSecretsStore(baseDir)?.[OLLAMA_CLOUD_API_KEY] === "string";
12196
10661
  }
12197
- async function setOllamaCloudApiKey(value, baseDir = homedir3()) {
12198
- const store = await getSecretsStore(baseDir);
12199
- if (!store) {
12200
- throw new Error("Secrets store could not be opened");
12201
- }
12202
- await store.set(OLLAMA_CLOUD_API_KEY, value);
10662
+ async function getOllamaCloudApiKey(baseDir = homedir()) {
10663
+ return readSecretsStore(baseDir)?.[OLLAMA_CLOUD_API_KEY] ?? null;
12203
10664
  }
12204
- async function deleteOllamaCloudApiKey(baseDir = homedir3()) {
12205
- const store = await getSecretsStore(baseDir, false);
12206
- return store ? store.delete(OLLAMA_CLOUD_API_KEY) : false;
10665
+ async function setOllamaCloudApiKey(value, baseDir = homedir()) {
10666
+ const existingStore = readSecretsStore(baseDir) ?? {};
10667
+ writeSecretsStore({
10668
+ ...existingStore,
10669
+ [OLLAMA_CLOUD_API_KEY]: value
10670
+ }, baseDir);
10671
+ }
10672
+ async function deleteOllamaCloudApiKey(baseDir = homedir()) {
10673
+ const existingStore = readSecretsStore(baseDir);
10674
+ if (!existingStore || !(OLLAMA_CLOUD_API_KEY in existingStore)) {
10675
+ return false;
10676
+ }
10677
+ const nextStore = { ...existingStore };
10678
+ delete nextStore[OLLAMA_CLOUD_API_KEY];
10679
+ if (Object.keys(nextStore).length === 0) {
10680
+ try {
10681
+ rmSync(getSecretsFilePath(baseDir), { force: true });
10682
+ rmSync(getSecretsStorePath(baseDir), { recursive: true, force: true });
10683
+ } catch {}
10684
+ return true;
10685
+ }
10686
+ writeSecretsStore(nextStore, baseDir);
10687
+ return true;
12207
10688
  }
12208
10689
 
12209
10690
  // src/utils/copilot.ts
@@ -14189,13 +12670,13 @@ async function promptForConfigEdits(current, hasExistingOllamaApiKey) {
14189
12670
  const modelLookupApiKey = ollamaApiKeyAction === "set" ? ollamaApiKey ?? null : ollamaApiKeyAction === "keep" ? await getOllamaCloudApiKey() : null;
14190
12671
  aiModel = await promptForOllamaCloudModelSelection(modelLookupApiKey, current.aiProvider === "ollama-cloud" ? current.aiModel ?? DEFAULT_OLLAMA_CLOUD_MODEL : DEFAULT_OLLAMA_CLOUD_MODEL);
14191
12672
  } else if (hasExistingOllamaApiKey) {
14192
- const shouldDeleteStoredKey = await confirmPrompt("Delete the stored Ollama Cloud API key from secrets-engine?");
12673
+ const shouldDeleteStoredKey = await confirmPrompt("Delete the stored Ollama Cloud API key from the local secrets store?");
14193
12674
  if (shouldDeleteStoredKey) {
14194
12675
  ollamaApiKeyAction = "delete";
14195
12676
  }
14196
12677
  }
14197
12678
  } else if (hasExistingOllamaApiKey) {
14198
- const shouldDeleteStoredKey = await confirmPrompt("AI is disabled. Delete the stored Ollama Cloud API key from secrets-engine?");
12679
+ const shouldDeleteStoredKey = await confirmPrompt("AI is disabled. Delete the stored Ollama Cloud API key from the local secrets store?");
14199
12680
  if (shouldDeleteStoredKey) {
14200
12681
  ollamaApiKeyAction = "delete";
14201
12682
  }
@@ -14222,7 +12703,7 @@ async function promptForConfigEdits(current, hasExistingOllamaApiKey) {
14222
12703
  async function applyOllamaApiKeyEdit(result) {
14223
12704
  if (result.ollamaApiKeyAction === "set" && result.ollamaApiKey) {
14224
12705
  await setOllamaCloudApiKey(result.ollamaApiKey);
14225
- success("Stored Ollama Cloud API key in secrets-engine.");
12706
+ success("Stored Ollama Cloud API key in the local secrets store.");
14226
12707
  info(`Secrets path: ${import_picocolors10.default.bold(getSecretsStorePath())}`);
14227
12708
  return;
14228
12709
  }
@@ -14345,7 +12826,7 @@ var import_picocolors11 = __toESM(require_picocolors(), 1);
14345
12826
  // package.json
14346
12827
  var package_default = {
14347
12828
  name: "contribute-now",
14348
- version: "0.7.0",
12829
+ version: "0.7.1-dev.42d9d46",
14349
12830
  description: "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
14350
12831
  type: "module",
14351
12832
  bin: {
@@ -14393,9 +12874,7 @@ var package_default = {
14393
12874
  dependencies: {
14394
12875
  "@clack/prompts": "^1.0.1",
14395
12876
  "@github/copilot-sdk": "^0.1.25",
14396
- "@wgtechlabs/config-engine": "^0.1.0",
14397
12877
  "@wgtechlabs/log-engine": "^2.3.1",
14398
- "@wgtechlabs/secrets-engine": "^2.0.0",
14399
12878
  citty: "^0.1.6",
14400
12879
  figlet: "^1.10.0",
14401
12880
  picocolors: "^1.1.1"
@@ -14512,7 +12991,7 @@ async function depsSection() {
14512
12991
  });
14513
12992
  }
14514
12993
  try {
14515
- await Promise.resolve().then(() => (init_dist2(), exports_dist3));
12994
+ await Promise.resolve().then(() => (init_dist(), exports_dist2));
14516
12995
  checks.push({ label: "Copilot SDK importable", ok: true });
14517
12996
  } catch {
14518
12997
  checks.push({
@@ -14600,7 +13079,7 @@ async function configSection() {
14600
13079
  label: hasApiKey ? "Ollama Cloud API key present" : "Ollama Cloud API key missing",
14601
13080
  ok: true,
14602
13081
  warning: !hasApiKey,
14603
- detail: hasSecretsStore() ? "stored in secrets-engine" : "run `contrib setup` to save it"
13082
+ detail: hasSecretsStore() ? "stored in the local secrets store" : "run `contrib setup` to save it"
14604
13083
  });
14605
13084
  }
14606
13085
  }
@@ -14794,15 +13273,15 @@ var doctor_default = defineCommand({
14794
13273
  });
14795
13274
 
14796
13275
  // src/commands/hook.ts
14797
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
14798
- import { join as join9 } from "node:path";
13276
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
13277
+ import { join as join6 } from "node:path";
14799
13278
  var import_picocolors12 = __toESM(require_picocolors(), 1);
14800
13279
  var HOOK_MARKER = "# managed by contribute-now";
14801
13280
  function getHooksDir(cwd = process.cwd()) {
14802
- return join9(cwd, ".git", "hooks");
13281
+ return join6(cwd, ".git", "hooks");
14803
13282
  }
14804
13283
  function getHookPath(cwd = process.cwd()) {
14805
- return join9(getHooksDir(cwd), "commit-msg");
13284
+ return join6(getHooksDir(cwd), "commit-msg");
14806
13285
  }
14807
13286
  function generateHookScript() {
14808
13287
  return `#!/bin/sh
@@ -14878,7 +13357,7 @@ async function installHook() {
14878
13357
  const hookPath = getHookPath();
14879
13358
  const hooksDir = getHooksDir();
14880
13359
  if (existsSync6(hookPath)) {
14881
- const existing = readFileSync4(hookPath, "utf-8");
13360
+ const existing = readFileSync5(hookPath, "utf-8");
14882
13361
  if (!existing.includes(HOOK_MARKER)) {
14883
13362
  error("A commit-msg hook already exists and was not installed by contribute-now.");
14884
13363
  warn(`Path: ${hookPath}`);
@@ -14888,9 +13367,9 @@ async function installHook() {
14888
13367
  info("Updating existing contribute-now hook...");
14889
13368
  }
14890
13369
  if (!existsSync6(hooksDir)) {
14891
- mkdirSync4(hooksDir, { recursive: true });
13370
+ mkdirSync5(hooksDir, { recursive: true });
14892
13371
  }
14893
- writeFileSync3(hookPath, generateHookScript(), { mode: 493 });
13372
+ writeFileSync5(hookPath, generateHookScript(), { mode: 493 });
14894
13373
  success(`commit-msg hook installed.`);
14895
13374
  info(`Convention: ${import_picocolors12.default.bold(CONVENTION_LABELS[config.commitConvention])}`, "");
14896
13375
  info(`Path: ${import_picocolors12.default.dim(hookPath)}`, "");
@@ -14903,12 +13382,12 @@ async function uninstallHook() {
14903
13382
  info("No commit-msg hook found. Nothing to uninstall.");
14904
13383
  return;
14905
13384
  }
14906
- const content = readFileSync4(hookPath, "utf-8");
13385
+ const content = readFileSync5(hookPath, "utf-8");
14907
13386
  if (!content.includes(HOOK_MARKER)) {
14908
13387
  error("The commit-msg hook was not installed by contribute-now. Leaving it untouched.");
14909
13388
  process.exit(1);
14910
13389
  }
14911
- rmSync(hookPath);
13390
+ rmSync2(hookPath);
14912
13391
  success("commit-msg hook removed.");
14913
13392
  }
14914
13393
 
@@ -15589,7 +14068,7 @@ var setup_default = defineCommand({
15589
14068
  if (enableAI) {
15590
14069
  const providerChoice = await selectPrompt("Which AI provider should this clone use?", [
15591
14070
  "GitHub Copilot — use your existing GitHub/Copilot auth",
15592
- "Ollama Cloud — use an API key stored with secrets-engine"
14071
+ "Ollama Cloud — use an API key stored in the local secrets store"
15593
14072
  ]);
15594
14073
  aiProvider = providerChoice.startsWith("Ollama Cloud") ? "ollama-cloud" : "copilot";
15595
14074
  if (aiProvider === "ollama-cloud") {
@@ -15601,7 +14080,7 @@ var setup_default = defineCommand({
15601
14080
  aiModel = await promptForOllamaCloudModel(apiKey);
15602
14081
  try {
15603
14082
  await setOllamaCloudApiKey(apiKey);
15604
- success("Stored Ollama Cloud API key in secrets-engine.");
14083
+ success("Stored Ollama Cloud API key in the local secrets store.");
15605
14084
  info(`Secrets path: ${import_picocolors15.default.bold(getSecretsStorePath())}`);
15606
14085
  } catch (err) {
15607
14086
  const message = err instanceof Error ? err.message : String(err);
@@ -16880,7 +15359,7 @@ var sync_default = defineCommand({
16880
15359
  });
16881
15360
 
16882
15361
  // src/commands/update.ts
16883
- import { readFileSync as readFileSync5 } from "node:fs";
15362
+ import { readFileSync as readFileSync6 } from "node:fs";
16884
15363
  var import_picocolors21 = __toESM(require_picocolors(), 1);
16885
15364
  function hasStaleBranchWorkToPreserve(uniqueCommitsAheadOfBase, hasUncommittedChanges2) {
16886
15365
  return hasUncommittedChanges2 || uniqueCommitsAheadOfBase > 0;
@@ -17084,7 +15563,7 @@ var update_default = defineCommand({
17084
15563
  let conflictDiff = "";
17085
15564
  for (const file of conflictFiles.slice(0, 3)) {
17086
15565
  try {
17087
- const content = readFileSync5(file, "utf-8");
15566
+ const content = readFileSync6(file, "utf-8");
17088
15567
  if (content.includes("<<<<<<<")) {
17089
15568
  conflictDiff += `
17090
15569
  --- ${file} ---
@@ -17125,7 +15604,7 @@ ${import_picocolors21.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance
17125
15604
  });
17126
15605
 
17127
15606
  // src/commands/validate.ts
17128
- import { readFileSync as readFileSync6 } from "node:fs";
15607
+ import { readFileSync as readFileSync7 } from "node:fs";
17129
15608
  var import_picocolors22 = __toESM(require_picocolors(), 1);
17130
15609
  var validate_default = defineCommand({
17131
15610
  meta: {
@@ -17155,7 +15634,7 @@ var validate_default = defineCommand({
17155
15634
  info('Commit convention is set to "none". All messages are accepted.');
17156
15635
  process.exit(0);
17157
15636
  }
17158
- const message = args.file ? readFileSync6(args.file, "utf-8").split(/\r?\n/, 1)[0] ?? "" : args.message;
15637
+ const message = args.file ? readFileSync7(args.file, "utf-8").split(/\r?\n/, 1)[0] ?? "" : args.message;
17159
15638
  if (!message) {
17160
15639
  error("No commit message provided. Pass a message or use --file <path>.");
17161
15640
  process.exit(1);