cognova 0.2.14 → 0.2.16
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/bf0f9039-944a-4581-ac5f-cf72fe2c7dae.json +1 -0
- package/.output/server/chunks/build/styles.mjs +2 -2
- package/.output/server/chunks/nitro/nitro.mjs +221 -221
- package/.output/server/package.json +1 -1
- package/dist/cli/index.js +665 -215
- package/package.json +1 -1
- package/.output/public/_nuxt/builds/meta/d7188f50-b42c-4d2c-afab-066d318f0af8.json +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -156,7 +156,7 @@ var require_src = __commonJS({
|
|
|
156
156
|
|
|
157
157
|
// src/commands/init.ts
|
|
158
158
|
import { execSync as execSync5 } from "child_process";
|
|
159
|
-
import { join as
|
|
159
|
+
import { join as join9 } from "path";
|
|
160
160
|
import crypto3 from "crypto";
|
|
161
161
|
|
|
162
162
|
// ../node_modules/.pnpm/@clack+core@1.0.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -1189,7 +1189,7 @@ ${l}
|
|
|
1189
1189
|
} }).prompt();
|
|
1190
1190
|
|
|
1191
1191
|
// src/commands/init.ts
|
|
1192
|
-
var
|
|
1192
|
+
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
1193
1193
|
|
|
1194
1194
|
// src/lib/prerequisites.ts
|
|
1195
1195
|
import { execSync } from "child_process";
|
|
@@ -1201,6 +1201,9 @@ function checkCommand(cmd) {
|
|
|
1201
1201
|
return null;
|
|
1202
1202
|
}
|
|
1203
1203
|
}
|
|
1204
|
+
function sleep(ms) {
|
|
1205
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1206
|
+
}
|
|
1204
1207
|
async function checkPrerequisites() {
|
|
1205
1208
|
R2.step("Checking prerequisites...");
|
|
1206
1209
|
const nodeOut = checkCommand("node --version");
|
|
@@ -1250,9 +1253,48 @@ async function checkPrerequisites() {
|
|
|
1250
1253
|
} else {
|
|
1251
1254
|
R2.success(`Claude Code ${claudeOut}`);
|
|
1252
1255
|
}
|
|
1253
|
-
let
|
|
1256
|
+
let dockerInstalled = !!dockerOut;
|
|
1257
|
+
let dockerReady = false;
|
|
1254
1258
|
if (dockerOut) {
|
|
1255
|
-
|
|
1259
|
+
const daemonRunning = checkCommand("docker info");
|
|
1260
|
+
if (daemonRunning) {
|
|
1261
|
+
dockerReady = true;
|
|
1262
|
+
R2.success(`Docker available ${import_picocolors3.default.dim("(for local PostgreSQL)")}`);
|
|
1263
|
+
} else {
|
|
1264
|
+
R2.warn("Docker is installed but the daemon is not running");
|
|
1265
|
+
const startDocker = await Re({
|
|
1266
|
+
message: "Start Docker now?",
|
|
1267
|
+
initialValue: true
|
|
1268
|
+
});
|
|
1269
|
+
if (!Ct(startDocker) && startDocker) {
|
|
1270
|
+
const s = bt2();
|
|
1271
|
+
s.start("Starting Docker daemon");
|
|
1272
|
+
try {
|
|
1273
|
+
if (process.platform === "darwin") {
|
|
1274
|
+
execSync("open -a Docker", { stdio: "pipe" });
|
|
1275
|
+
} else if (process.platform === "linux") {
|
|
1276
|
+
execSync("sudo systemctl start docker", { stdio: "inherit" });
|
|
1277
|
+
}
|
|
1278
|
+
for (let i = 0; i < 30; i++) {
|
|
1279
|
+
await sleep(1e3);
|
|
1280
|
+
if (checkCommand("docker info")) {
|
|
1281
|
+
dockerReady = true;
|
|
1282
|
+
s.stop("Docker daemon is running");
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (!dockerReady) {
|
|
1287
|
+
s.stop("Docker daemon did not start in time");
|
|
1288
|
+
R2.warn("You can start it manually and continue with local PostgreSQL, or use remote PostgreSQL");
|
|
1289
|
+
}
|
|
1290
|
+
} catch {
|
|
1291
|
+
s.stop("Failed to start Docker daemon");
|
|
1292
|
+
R2.warn("Start Docker manually: https://docs.docker.com/get-docker/");
|
|
1293
|
+
}
|
|
1294
|
+
} else {
|
|
1295
|
+
R2.info("You can use a remote PostgreSQL instead");
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1256
1298
|
} else {
|
|
1257
1299
|
const installDocker = await Re({
|
|
1258
1300
|
message: "Docker not found. Install it? (needed only for local PostgreSQL)",
|
|
@@ -1265,23 +1307,26 @@ async function checkPrerequisites() {
|
|
|
1265
1307
|
if (process.platform === "darwin") {
|
|
1266
1308
|
execSync("brew install --cask docker", { stdio: "inherit" });
|
|
1267
1309
|
s.stop("Docker Desktop installed \u2014 open it from Applications to finish setup");
|
|
1310
|
+
R2.info("Open Docker Desktop from Applications, then re-run this installer");
|
|
1268
1311
|
} else {
|
|
1269
1312
|
const cmd = process.platform === "linux" ? "sudo apt-get update -qq && sudo apt-get install -y -qq docker.io docker-compose-plugin" : "apt-get update -qq && apt-get install -y -qq docker.io docker-compose-plugin";
|
|
1270
1313
|
execSync(cmd, { stdio: "inherit" });
|
|
1271
1314
|
s.stop("Docker installed");
|
|
1272
1315
|
}
|
|
1273
|
-
|
|
1316
|
+
dockerInstalled = !!checkCommand("docker --version");
|
|
1317
|
+
dockerReady = !!checkCommand("docker info");
|
|
1274
1318
|
} catch {
|
|
1275
1319
|
s.stop("Docker installation failed");
|
|
1276
1320
|
R2.warn("Install Docker manually: https://docs.docker.com/get-docker/");
|
|
1277
1321
|
}
|
|
1278
1322
|
} else {
|
|
1279
|
-
R2.info(
|
|
1323
|
+
R2.info("Skipped \u2014 you can use a remote PostgreSQL instead");
|
|
1280
1324
|
}
|
|
1281
1325
|
}
|
|
1282
1326
|
return {
|
|
1283
1327
|
ok: true,
|
|
1284
|
-
|
|
1328
|
+
dockerInstalled,
|
|
1329
|
+
dockerReady,
|
|
1285
1330
|
nodeVersion: nodeOut || "",
|
|
1286
1331
|
pythonVersion: pythonOut || "",
|
|
1287
1332
|
pnpmVersion: pnpmOut || "",
|
|
@@ -1471,13 +1516,15 @@ async function setupInstallDir(installDir) {
|
|
|
1471
1516
|
copyAppSource(packageDir, installDir);
|
|
1472
1517
|
s.stop("Application files installed");
|
|
1473
1518
|
}
|
|
1474
|
-
function writeMetadata(installDir, vaultPath, version) {
|
|
1519
|
+
function writeMetadata(installDir, vaultPath, version, dbPassword, dbPort) {
|
|
1475
1520
|
const metadata = {
|
|
1476
1521
|
version,
|
|
1477
1522
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1478
1523
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1479
1524
|
installDir,
|
|
1480
|
-
vaultPath
|
|
1525
|
+
vaultPath,
|
|
1526
|
+
dbPassword,
|
|
1527
|
+
dbPort
|
|
1481
1528
|
};
|
|
1482
1529
|
writeFileSync(join2(installDir, ".cognova"), JSON.stringify(metadata, null, 2));
|
|
1483
1530
|
const homeMeta = join2(process.env.HOME || "~", ".cognova");
|
|
@@ -1517,9 +1564,37 @@ async function setupVault() {
|
|
|
1517
1564
|
|
|
1518
1565
|
// src/lib/database.ts
|
|
1519
1566
|
import { execSync as execSync2 } from "child_process";
|
|
1567
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
1568
|
+
import { join as join4 } from "path";
|
|
1520
1569
|
import crypto from "crypto";
|
|
1521
1570
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
1522
|
-
|
|
1571
|
+
function checkPortInUse(port) {
|
|
1572
|
+
try {
|
|
1573
|
+
const containers = execSync2(`docker ps --filter "publish=${port}" --format "{{.Names}}"`, {
|
|
1574
|
+
encoding: "utf-8"
|
|
1575
|
+
}).trim();
|
|
1576
|
+
return containers.length > 0;
|
|
1577
|
+
} catch {
|
|
1578
|
+
try {
|
|
1579
|
+
execSync2(`lsof -i :${port}`, { stdio: "pipe" });
|
|
1580
|
+
return true;
|
|
1581
|
+
} catch {
|
|
1582
|
+
return false;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
function readMetadata2() {
|
|
1587
|
+
try {
|
|
1588
|
+
const metaPath = join4(process.env.HOME || "~", ".cognova");
|
|
1589
|
+
if (!existsSync4(metaPath))
|
|
1590
|
+
return null;
|
|
1591
|
+
const content = readFileSync2(metaPath, "utf-8");
|
|
1592
|
+
return JSON.parse(content);
|
|
1593
|
+
} catch {
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
async function setupDatabase(dockerReady) {
|
|
1523
1598
|
const dbType = await Je({
|
|
1524
1599
|
message: "Database setup",
|
|
1525
1600
|
options: [
|
|
@@ -1529,14 +1604,15 @@ async function setupDatabase(hasDocker) {
|
|
|
1529
1604
|
});
|
|
1530
1605
|
if (Ct(dbType)) process.exit(0);
|
|
1531
1606
|
if (dbType === "local") {
|
|
1532
|
-
if (!
|
|
1533
|
-
R2.error("Docker is
|
|
1534
|
-
R2.info("
|
|
1607
|
+
if (!dockerReady) {
|
|
1608
|
+
R2.error("Docker daemon is not running.");
|
|
1609
|
+
R2.info("Start Docker Desktop (macOS) or run: sudo systemctl start docker (Linux)");
|
|
1535
1610
|
R2.info('Or choose "Remote PostgreSQL" and use Neon: https://neon.tech');
|
|
1536
1611
|
process.exit(1);
|
|
1537
1612
|
}
|
|
1538
1613
|
const password = crypto.randomBytes(16).toString("hex");
|
|
1539
1614
|
const containerName = "cognova-db";
|
|
1615
|
+
let port = 5432;
|
|
1540
1616
|
try {
|
|
1541
1617
|
const existing = execSync2(`docker ps -a --filter name=${containerName} --format "{{.Status}}"`, {
|
|
1542
1618
|
encoding: "utf-8"
|
|
@@ -1551,6 +1627,17 @@ async function setupDatabase(hasDocker) {
|
|
|
1551
1627
|
if (reuse) {
|
|
1552
1628
|
if (!existing.startsWith("Up"))
|
|
1553
1629
|
execSync2(`docker start ${containerName}`, { stdio: "pipe" });
|
|
1630
|
+
const metadata = readMetadata2();
|
|
1631
|
+
if (metadata?.dbPassword && metadata?.dbPort) {
|
|
1632
|
+
const connectionString3 = `postgres://postgres:${metadata.dbPassword}@localhost:${metadata.dbPort}/cognova`;
|
|
1633
|
+
R2.info(`Using stored credentials (port ${metadata.dbPort})`);
|
|
1634
|
+
return {
|
|
1635
|
+
type: "local",
|
|
1636
|
+
connectionString: connectionString3,
|
|
1637
|
+
password: metadata.dbPassword,
|
|
1638
|
+
port: metadata.dbPort
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1554
1641
|
R2.info("Using existing container. Ensure DATABASE_URL in .env matches its credentials.");
|
|
1555
1642
|
const connStr = await Ze({
|
|
1556
1643
|
message: "Connection string for existing container",
|
|
@@ -1564,6 +1651,23 @@ async function setupDatabase(hasDocker) {
|
|
|
1564
1651
|
}
|
|
1565
1652
|
} catch {
|
|
1566
1653
|
}
|
|
1654
|
+
if (checkPortInUse(port)) {
|
|
1655
|
+
R2.warn(`Port ${port} is already in use`);
|
|
1656
|
+
const altPort = await Ze({
|
|
1657
|
+
message: "Choose an alternative port",
|
|
1658
|
+
placeholder: "5433",
|
|
1659
|
+
defaultValue: "5433",
|
|
1660
|
+
validate: (v) => {
|
|
1661
|
+
const num = parseInt(v);
|
|
1662
|
+
if (isNaN(num) || num < 1024 || num > 65535)
|
|
1663
|
+
return "Port must be between 1024 and 65535";
|
|
1664
|
+
if (checkPortInUse(num))
|
|
1665
|
+
return `Port ${num} is also in use`;
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
if (Ct(altPort)) process.exit(0);
|
|
1669
|
+
port = parseInt(altPort);
|
|
1670
|
+
}
|
|
1567
1671
|
const s = bt2();
|
|
1568
1672
|
s.start("Starting PostgreSQL container");
|
|
1569
1673
|
try {
|
|
@@ -1573,7 +1677,7 @@ async function setupDatabase(hasDocker) {
|
|
|
1573
1677
|
`-e POSTGRES_USER=postgres`,
|
|
1574
1678
|
`-e POSTGRES_PASSWORD=${password}`,
|
|
1575
1679
|
`-e POSTGRES_DB=cognova`,
|
|
1576
|
-
`-p
|
|
1680
|
+
`-p ${port}:5432`,
|
|
1577
1681
|
`--restart unless-stopped`,
|
|
1578
1682
|
`postgres:16-alpine`
|
|
1579
1683
|
].join(" "), { stdio: "pipe" });
|
|
@@ -1584,7 +1688,7 @@ async function setupDatabase(hasDocker) {
|
|
|
1584
1688
|
ready = true;
|
|
1585
1689
|
break;
|
|
1586
1690
|
} catch {
|
|
1587
|
-
await
|
|
1691
|
+
await sleep2(1e3);
|
|
1588
1692
|
}
|
|
1589
1693
|
}
|
|
1590
1694
|
if (!ready) {
|
|
@@ -1598,9 +1702,14 @@ async function setupDatabase(hasDocker) {
|
|
|
1598
1702
|
R2.error(`Docker error: ${err}`);
|
|
1599
1703
|
process.exit(1);
|
|
1600
1704
|
}
|
|
1601
|
-
const connectionString2 = `postgres://postgres:${password}@localhost
|
|
1705
|
+
const connectionString2 = `postgres://postgres:${password}@localhost:${port}/cognova`;
|
|
1602
1706
|
R2.info(`Connection: ${import_picocolors4.default.dim(connectionString2)}`);
|
|
1603
|
-
return {
|
|
1707
|
+
return {
|
|
1708
|
+
type: "local",
|
|
1709
|
+
connectionString: connectionString2,
|
|
1710
|
+
password,
|
|
1711
|
+
port
|
|
1712
|
+
};
|
|
1604
1713
|
}
|
|
1605
1714
|
const connectionString = await Ze({
|
|
1606
1715
|
message: "PostgreSQL connection string",
|
|
@@ -1613,13 +1722,13 @@ async function setupDatabase(hasDocker) {
|
|
|
1613
1722
|
if (Ct(connectionString)) process.exit(0);
|
|
1614
1723
|
return { type: "remote", connectionString };
|
|
1615
1724
|
}
|
|
1616
|
-
function
|
|
1725
|
+
function sleep2(ms) {
|
|
1617
1726
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1618
1727
|
}
|
|
1619
1728
|
|
|
1620
1729
|
// src/lib/config.ts
|
|
1621
|
-
import { readFileSync as
|
|
1622
|
-
import { join as
|
|
1730
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1731
|
+
import { join as join5 } from "path";
|
|
1623
1732
|
|
|
1624
1733
|
// src/templates/env.ts
|
|
1625
1734
|
import crypto2 from "crypto";
|
|
@@ -1658,11 +1767,11 @@ function generateEnvFile(config) {
|
|
|
1658
1767
|
// src/lib/config.ts
|
|
1659
1768
|
function writeEnvFile(config) {
|
|
1660
1769
|
const content = generateEnvFile(config);
|
|
1661
|
-
writeFileSync2(
|
|
1770
|
+
writeFileSync2(join5(config.installDir, ".env"), content);
|
|
1662
1771
|
}
|
|
1663
1772
|
function loadEnvFile(installDir) {
|
|
1664
1773
|
try {
|
|
1665
|
-
const content =
|
|
1774
|
+
const content = readFileSync3(join5(installDir, ".env"), "utf-8");
|
|
1666
1775
|
const env = {};
|
|
1667
1776
|
for (const line of content.split("\n")) {
|
|
1668
1777
|
const trimmed = line.trim();
|
|
@@ -1678,8 +1787,8 @@ function loadEnvFile(installDir) {
|
|
|
1678
1787
|
}
|
|
1679
1788
|
|
|
1680
1789
|
// src/lib/claude-config.ts
|
|
1681
|
-
import { cpSync as cpSync2, mkdirSync as mkdirSync3, existsSync as
|
|
1682
|
-
import { join as
|
|
1790
|
+
import { cpSync as cpSync2, mkdirSync as mkdirSync3, existsSync as existsSync5, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
|
|
1791
|
+
import { join as join6 } from "path";
|
|
1683
1792
|
|
|
1684
1793
|
// src/templates/claude-md.ts
|
|
1685
1794
|
function generateClaudeMd(config) {
|
|
@@ -1923,27 +2032,27 @@ function generateSettingsJson(config) {
|
|
|
1923
2032
|
// src/lib/claude-config.ts
|
|
1924
2033
|
async function installClaudeConfig(config, options) {
|
|
1925
2034
|
const claudeDir = getClaudeDir();
|
|
1926
|
-
const sourceDir =
|
|
2035
|
+
const sourceDir = join6(getPackageDir(), "Claude");
|
|
1927
2036
|
const installAll = !options;
|
|
1928
2037
|
mkdirSync3(claudeDir, { recursive: true });
|
|
1929
2038
|
if (installAll || options?.skills) {
|
|
1930
|
-
const skillsSrc =
|
|
1931
|
-
if (
|
|
1932
|
-
cpSync2(skillsSrc,
|
|
2039
|
+
const skillsSrc = join6(sourceDir, "skills");
|
|
2040
|
+
if (existsSync5(skillsSrc))
|
|
2041
|
+
cpSync2(skillsSrc, join6(claudeDir, "skills"), { recursive: true, force: true });
|
|
1933
2042
|
}
|
|
1934
2043
|
if (installAll || options?.hooks) {
|
|
1935
|
-
const hooksSrc =
|
|
1936
|
-
if (
|
|
1937
|
-
cpSync2(hooksSrc,
|
|
2044
|
+
const hooksSrc = join6(sourceDir, "hooks");
|
|
2045
|
+
if (existsSync5(hooksSrc))
|
|
2046
|
+
cpSync2(hooksSrc, join6(claudeDir, "hooks"), { recursive: true, force: true });
|
|
1938
2047
|
}
|
|
1939
2048
|
if (installAll || options?.rules) {
|
|
1940
|
-
const rulesSrc =
|
|
1941
|
-
if (
|
|
1942
|
-
cpSync2(rulesSrc,
|
|
2049
|
+
const rulesSrc = join6(sourceDir, "rules");
|
|
2050
|
+
if (existsSync5(rulesSrc))
|
|
2051
|
+
cpSync2(rulesSrc, join6(claudeDir, "rules"), { recursive: true, force: true });
|
|
1943
2052
|
}
|
|
1944
2053
|
if (installAll || options?.claudeMd) {
|
|
1945
|
-
const claudeMdPath =
|
|
1946
|
-
if (
|
|
2054
|
+
const claudeMdPath = join6(claudeDir, "CLAUDE.md");
|
|
2055
|
+
if (existsSync5(claudeMdPath) && installAll) {
|
|
1947
2056
|
const overwrite = await Re({
|
|
1948
2057
|
message: "~/.claude/CLAUDE.md already exists. Overwrite?",
|
|
1949
2058
|
initialValue: false
|
|
@@ -1959,10 +2068,10 @@ async function installClaudeConfig(config, options) {
|
|
|
1959
2068
|
}
|
|
1960
2069
|
}
|
|
1961
2070
|
if (installAll || options?.settings) {
|
|
1962
|
-
const settingsPath =
|
|
1963
|
-
if (
|
|
2071
|
+
const settingsPath = join6(claudeDir, "settings.json");
|
|
2072
|
+
if (existsSync5(settingsPath) && installAll) {
|
|
1964
2073
|
try {
|
|
1965
|
-
const existing = JSON.parse(
|
|
2074
|
+
const existing = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
1966
2075
|
const generated = JSON.parse(generateSettingsJson(config));
|
|
1967
2076
|
const merged = mergeSettings(existing, generated);
|
|
1968
2077
|
writeFileSync3(settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
@@ -1977,23 +2086,23 @@ async function installClaudeConfig(config, options) {
|
|
|
1977
2086
|
}
|
|
1978
2087
|
function syncClaudeConfig(sourceDir) {
|
|
1979
2088
|
const claudeDir = getClaudeDir();
|
|
1980
|
-
const claudeSrc =
|
|
1981
|
-
if (!
|
|
2089
|
+
const claudeSrc = join6(sourceDir, "Claude");
|
|
2090
|
+
if (!existsSync5(claudeSrc)) return;
|
|
1982
2091
|
mkdirSync3(claudeDir, { recursive: true });
|
|
1983
|
-
const skillsSrc =
|
|
1984
|
-
if (
|
|
1985
|
-
mkdirSync3(
|
|
1986
|
-
cpSync2(skillsSrc,
|
|
2092
|
+
const skillsSrc = join6(claudeSrc, "skills");
|
|
2093
|
+
if (existsSync5(skillsSrc)) {
|
|
2094
|
+
mkdirSync3(join6(claudeDir, "skills"), { recursive: true });
|
|
2095
|
+
cpSync2(skillsSrc, join6(claudeDir, "skills"), { recursive: true, force: true });
|
|
1987
2096
|
}
|
|
1988
|
-
const hooksSrc =
|
|
1989
|
-
if (
|
|
1990
|
-
mkdirSync3(
|
|
1991
|
-
cpSync2(hooksSrc,
|
|
2097
|
+
const hooksSrc = join6(claudeSrc, "hooks");
|
|
2098
|
+
if (existsSync5(hooksSrc)) {
|
|
2099
|
+
mkdirSync3(join6(claudeDir, "hooks"), { recursive: true });
|
|
2100
|
+
cpSync2(hooksSrc, join6(claudeDir, "hooks"), { recursive: true, force: true });
|
|
1992
2101
|
}
|
|
1993
|
-
const rulesSrc =
|
|
1994
|
-
if (
|
|
1995
|
-
mkdirSync3(
|
|
1996
|
-
cpSync2(rulesSrc,
|
|
2102
|
+
const rulesSrc = join6(claudeSrc, "rules");
|
|
2103
|
+
if (existsSync5(rulesSrc)) {
|
|
2104
|
+
mkdirSync3(join6(claudeDir, "rules"), { recursive: true });
|
|
2105
|
+
cpSync2(rulesSrc, join6(claudeDir, "rules"), { recursive: true, force: true });
|
|
1997
2106
|
}
|
|
1998
2107
|
}
|
|
1999
2108
|
function mergeSettings(existing, generated) {
|
|
@@ -2009,8 +2118,8 @@ function mergeSettings(existing, generated) {
|
|
|
2009
2118
|
|
|
2010
2119
|
// src/lib/process-manager.ts
|
|
2011
2120
|
import { execSync as execSync3 } from "child_process";
|
|
2012
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as
|
|
2013
|
-
import { join as
|
|
2121
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
2122
|
+
import { join as join7 } from "path";
|
|
2014
2123
|
|
|
2015
2124
|
// src/templates/pm2-ecosystem.ts
|
|
2016
2125
|
function generatePm2Ecosystem(config) {
|
|
@@ -2040,6 +2149,8 @@ module.exports = {
|
|
|
2040
2149
|
name: 'cognova',
|
|
2041
2150
|
script: '.output/server/index.mjs',
|
|
2042
2151
|
cwd: '${config.installDir}',
|
|
2152
|
+
// Use 'node' interpreter when NVM is detected to avoid path parsing issues
|
|
2153
|
+
interpreter: 'node',
|
|
2043
2154
|
node_args: '--max-old-space-size=4096',
|
|
2044
2155
|
env: {
|
|
2045
2156
|
...dotenv,
|
|
@@ -2082,11 +2193,11 @@ async function setupAndStart(config) {
|
|
|
2082
2193
|
return;
|
|
2083
2194
|
}
|
|
2084
2195
|
}
|
|
2085
|
-
const logsDir =
|
|
2086
|
-
if (!
|
|
2196
|
+
const logsDir = join7(config.installDir, "logs");
|
|
2197
|
+
if (!existsSync6(logsDir))
|
|
2087
2198
|
mkdirSync4(logsDir, { recursive: true });
|
|
2088
2199
|
const ecosystem = generatePm2Ecosystem(config);
|
|
2089
|
-
writeFileSync4(
|
|
2200
|
+
writeFileSync4(join7(config.installDir, "ecosystem.config.cjs"), ecosystem);
|
|
2090
2201
|
const s = bt2();
|
|
2091
2202
|
s.start("Starting Cognova with PM2");
|
|
2092
2203
|
try {
|
|
@@ -2097,7 +2208,15 @@ async function setupAndStart(config) {
|
|
|
2097
2208
|
s.stop("Cognova is running");
|
|
2098
2209
|
} catch (err) {
|
|
2099
2210
|
s.stop("Failed to start");
|
|
2100
|
-
|
|
2211
|
+
const errMsg = String(err);
|
|
2212
|
+
if (errMsg.includes("nvm") && errMsg.includes("not found")) {
|
|
2213
|
+
R2.error("PM2 + NVM compatibility issue detected");
|
|
2214
|
+
R2.info("Try: pm2 delete cognova && pm2 start ecosystem.config.cjs");
|
|
2215
|
+
R2.info(`Working directory: ${config.installDir}`);
|
|
2216
|
+
} else {
|
|
2217
|
+
R2.error(`PM2 error: ${err}`);
|
|
2218
|
+
}
|
|
2219
|
+
throw err;
|
|
2101
2220
|
}
|
|
2102
2221
|
}
|
|
2103
2222
|
|
|
@@ -2123,103 +2242,278 @@ async function waitForHealth(url, maxWaitSeconds = 30) {
|
|
|
2123
2242
|
return false;
|
|
2124
2243
|
}
|
|
2125
2244
|
|
|
2245
|
+
// src/lib/progress.ts
|
|
2246
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync } from "fs";
|
|
2247
|
+
import { join as join8 } from "path";
|
|
2248
|
+
var PROGRESS_FILE = join8(process.env.HOME || "~", ".cognova-setup.json");
|
|
2249
|
+
function loadProgress() {
|
|
2250
|
+
try {
|
|
2251
|
+
if (!existsSync7(PROGRESS_FILE))
|
|
2252
|
+
return null;
|
|
2253
|
+
const content = readFileSync5(PROGRESS_FILE, "utf-8");
|
|
2254
|
+
return JSON.parse(content);
|
|
2255
|
+
} catch {
|
|
2256
|
+
return null;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
function saveProgress(progress) {
|
|
2260
|
+
try {
|
|
2261
|
+
const content = JSON.stringify(progress, null, 2);
|
|
2262
|
+
writeFileSync5(PROGRESS_FILE, content, "utf-8");
|
|
2263
|
+
} catch (err) {
|
|
2264
|
+
console.warn(`Failed to save progress: ${err}`);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
function clearProgress() {
|
|
2268
|
+
try {
|
|
2269
|
+
if (existsSync7(PROGRESS_FILE))
|
|
2270
|
+
unlinkSync(PROGRESS_FILE);
|
|
2271
|
+
} catch {
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
// src/lib/retry.ts
|
|
2276
|
+
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
2277
|
+
var SkipError = class extends Error {
|
|
2278
|
+
constructor() {
|
|
2279
|
+
super("Step skipped by user");
|
|
2280
|
+
this.name = "SkipError";
|
|
2281
|
+
}
|
|
2282
|
+
};
|
|
2283
|
+
async function withRetry(label, fn, options = {}) {
|
|
2284
|
+
const { maxRetries = 3, canSkip = true } = options;
|
|
2285
|
+
let attempt = 0;
|
|
2286
|
+
while (true) {
|
|
2287
|
+
try {
|
|
2288
|
+
return await fn();
|
|
2289
|
+
} catch (err) {
|
|
2290
|
+
attempt++;
|
|
2291
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2292
|
+
R2.error(`${label} failed: ${errorMsg}`);
|
|
2293
|
+
if (attempt >= maxRetries) {
|
|
2294
|
+
R2.error(`Maximum retry attempts (${maxRetries}) reached`);
|
|
2295
|
+
if (!canSkip) {
|
|
2296
|
+
R2.error("This step cannot be skipped. Setup aborted.");
|
|
2297
|
+
process.exit(1);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
const choices = [
|
|
2301
|
+
{ value: "retry", label: "Retry", hint: "Try again" }
|
|
2302
|
+
];
|
|
2303
|
+
if (canSkip) {
|
|
2304
|
+
choices.push({ value: "skip", label: "Skip", hint: "Continue without this step" });
|
|
2305
|
+
}
|
|
2306
|
+
choices.push({ value: "abort", label: "Abort", hint: "Exit setup" });
|
|
2307
|
+
const action = await Je({
|
|
2308
|
+
message: `${label} failed. What would you like to do?`,
|
|
2309
|
+
options: choices
|
|
2310
|
+
});
|
|
2311
|
+
if (Ct(action) || action === "abort") {
|
|
2312
|
+
R2.warn("Setup aborted by user");
|
|
2313
|
+
process.exit(1);
|
|
2314
|
+
}
|
|
2315
|
+
if (action === "skip") {
|
|
2316
|
+
throw new SkipError();
|
|
2317
|
+
}
|
|
2318
|
+
R2.info(import_picocolors5.default.dim(`Retrying ${label}...`));
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2126
2323
|
// src/commands/init.ts
|
|
2127
2324
|
async function init() {
|
|
2128
|
-
We(
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
const resolvedInstallDir = installDir.replace("~", process.env.HOME || "");
|
|
2141
|
-
await setupInstallDir(resolvedInstallDir);
|
|
2142
|
-
R2.step(import_picocolors5.default.bold("Vault"));
|
|
2143
|
-
const vault = await setupVault();
|
|
2144
|
-
R2.step(import_picocolors5.default.bold("Database"));
|
|
2145
|
-
const database = await setupDatabase(prereqs.hasDocker);
|
|
2146
|
-
R2.step(import_picocolors5.default.bold("Network Access"));
|
|
2147
|
-
const accessMode = await Je({
|
|
2148
|
-
message: "How will you access Cognova?",
|
|
2149
|
-
options: [
|
|
2150
|
-
{ value: "localhost", label: "Local only", hint: "http://localhost:3000" },
|
|
2151
|
-
{ value: "specific", label: "Specific IP or domain", hint: "LAN IP, hostname, or domain" },
|
|
2152
|
-
{ value: "any", label: "Any connection", hint: "Accepts requests from any origin (0.0.0.0)" }
|
|
2153
|
-
]
|
|
2154
|
-
});
|
|
2155
|
-
if (Ct(accessMode)) process.exit(0);
|
|
2156
|
-
let appUrl = "http://localhost:3000";
|
|
2157
|
-
if (accessMode === "specific") {
|
|
2158
|
-
const host = await Ze({
|
|
2159
|
-
message: "IP address or domain (include port if not 80/443)",
|
|
2160
|
-
placeholder: "192.168.1.100:3000"
|
|
2325
|
+
We(import_picocolors6.default.bgCyan(import_picocolors6.default.black(" Cognova Setup ")));
|
|
2326
|
+
const existingProgress = loadProgress();
|
|
2327
|
+
let progress = existingProgress || {
|
|
2328
|
+
completedSteps: [],
|
|
2329
|
+
partialConfig: {},
|
|
2330
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2331
|
+
};
|
|
2332
|
+
if (existingProgress) {
|
|
2333
|
+
R2.warn("Previous setup was interrupted");
|
|
2334
|
+
const resume = await Re({
|
|
2335
|
+
message: "Resume from where you left off?",
|
|
2336
|
+
initialValue: true
|
|
2161
2337
|
});
|
|
2162
|
-
if (Ct(
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2338
|
+
if (Ct(resume)) process.exit(0);
|
|
2339
|
+
if (!resume) {
|
|
2340
|
+
clearProgress();
|
|
2341
|
+
progress = {
|
|
2342
|
+
completedSteps: [],
|
|
2343
|
+
partialConfig: {},
|
|
2344
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
const prereqs = progress.completedSteps.includes("prerequisites") ? { ok: true, dockerInstalled: true, dockerReady: true, nodeVersion: "", pythonVersion: "", pnpmVersion: "", claudeInstalled: true } : await checkPrerequisites();
|
|
2349
|
+
if (!progress.completedSteps.includes("prerequisites")) {
|
|
2350
|
+
progress.completedSteps.push("prerequisites");
|
|
2351
|
+
saveProgress(progress);
|
|
2352
|
+
}
|
|
2353
|
+
R2.step(import_picocolors6.default.bold("Agent Personality"));
|
|
2354
|
+
const personality = progress.partialConfig.personality || await promptPersonality();
|
|
2355
|
+
if (!progress.completedSteps.includes("personality")) {
|
|
2356
|
+
progress.partialConfig.personality = personality;
|
|
2357
|
+
progress.completedSteps.push("personality");
|
|
2358
|
+
saveProgress(progress);
|
|
2359
|
+
}
|
|
2360
|
+
R2.step(import_picocolors6.default.bold("Installation"));
|
|
2361
|
+
const defaultDir = join9(process.env.HOME || "~", "cognova");
|
|
2362
|
+
let resolvedInstallDir;
|
|
2363
|
+
if (progress.partialConfig.installDir) {
|
|
2364
|
+
resolvedInstallDir = progress.partialConfig.installDir;
|
|
2365
|
+
R2.info(`Using install directory: ${resolvedInstallDir}`);
|
|
2366
|
+
} else {
|
|
2367
|
+
const installDir = await Ze({
|
|
2368
|
+
message: "Where should Cognova be installed?",
|
|
2369
|
+
placeholder: defaultDir,
|
|
2370
|
+
defaultValue: defaultDir
|
|
2371
|
+
});
|
|
2372
|
+
if (Ct(installDir)) process.exit(0);
|
|
2373
|
+
resolvedInstallDir = installDir.replace("~", process.env.HOME || "");
|
|
2374
|
+
await setupInstallDir(resolvedInstallDir);
|
|
2375
|
+
progress.partialConfig.installDir = resolvedInstallDir;
|
|
2376
|
+
progress.completedSteps.push("installDir");
|
|
2377
|
+
saveProgress(progress);
|
|
2378
|
+
}
|
|
2379
|
+
R2.step(import_picocolors6.default.bold("Vault"));
|
|
2380
|
+
const vault = progress.partialConfig.vault || await setupVault();
|
|
2381
|
+
if (!progress.completedSteps.includes("vault")) {
|
|
2382
|
+
progress.partialConfig.vault = vault;
|
|
2383
|
+
progress.completedSteps.push("vault");
|
|
2384
|
+
saveProgress(progress);
|
|
2385
|
+
}
|
|
2386
|
+
R2.step(import_picocolors6.default.bold("Database"));
|
|
2387
|
+
let database = progress.partialConfig.database;
|
|
2388
|
+
if (!database) {
|
|
2389
|
+
try {
|
|
2390
|
+
database = await withRetry(
|
|
2391
|
+
"Database setup",
|
|
2392
|
+
() => setupDatabase(prereqs.dockerReady),
|
|
2393
|
+
{ canSkip: false }
|
|
2394
|
+
);
|
|
2395
|
+
progress.partialConfig.database = database;
|
|
2396
|
+
progress.completedSteps.push("database");
|
|
2397
|
+
saveProgress(progress);
|
|
2398
|
+
} catch (err) {
|
|
2399
|
+
if (err instanceof SkipError) {
|
|
2400
|
+
R2.error("Database setup is required and cannot be skipped");
|
|
2199
2401
|
}
|
|
2402
|
+
process.exit(1);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
R2.step(import_picocolors6.default.bold("Network Access"));
|
|
2406
|
+
let accessMode;
|
|
2407
|
+
let appUrl;
|
|
2408
|
+
if (progress.partialConfig.accessMode && progress.partialConfig.appUrl) {
|
|
2409
|
+
accessMode = progress.partialConfig.accessMode;
|
|
2410
|
+
appUrl = progress.partialConfig.appUrl;
|
|
2411
|
+
R2.info(`Using access mode: ${accessMode} (${appUrl})`);
|
|
2412
|
+
} else {
|
|
2413
|
+
accessMode = await Je({
|
|
2414
|
+
message: "How will you access Cognova?",
|
|
2415
|
+
options: [
|
|
2416
|
+
{ value: "localhost", label: "Local only", hint: "http://localhost:3000" },
|
|
2417
|
+
{ value: "specific", label: "Specific IP or domain", hint: "LAN IP, hostname, or domain" },
|
|
2418
|
+
{ value: "any", label: "Any connection", hint: "Accepts requests from any origin (0.0.0.0)" }
|
|
2419
|
+
]
|
|
2420
|
+
});
|
|
2421
|
+
if (Ct(accessMode)) process.exit(0);
|
|
2422
|
+
appUrl = "http://localhost:3000";
|
|
2423
|
+
if (accessMode === "specific") {
|
|
2424
|
+
const host = await Ze({
|
|
2425
|
+
message: "IP address or domain (include port if not 80/443)",
|
|
2426
|
+
placeholder: "192.168.1.100:3000"
|
|
2427
|
+
});
|
|
2428
|
+
if (Ct(host)) process.exit(0);
|
|
2429
|
+
appUrl = host.startsWith("http") ? host : `http://${host}`;
|
|
2430
|
+
}
|
|
2431
|
+
progress.partialConfig.accessMode = accessMode;
|
|
2432
|
+
progress.partialConfig.appUrl = appUrl;
|
|
2433
|
+
progress.completedSteps.push("network");
|
|
2434
|
+
saveProgress(progress);
|
|
2435
|
+
}
|
|
2436
|
+
if (!progress.completedSteps.includes("security")) {
|
|
2437
|
+
R2.warn(import_picocolors6.default.yellow(import_picocolors6.default.bold("Security Notice")));
|
|
2438
|
+
R2.warn([
|
|
2439
|
+
"Cognova gives an AI agent unrestricted access to this machine.",
|
|
2440
|
+
"It can read, write, and execute anything via the embedded terminal",
|
|
2441
|
+
"and Claude Code CLI.",
|
|
2442
|
+
"",
|
|
2443
|
+
` ${import_picocolors6.default.dim("\u2022")} Do not run on a personal machine or alongside sensitive data`,
|
|
2444
|
+
` ${import_picocolors6.default.dim("\u2022")} Use a dedicated VM, container, or isolated environment`,
|
|
2445
|
+
` ${import_picocolors6.default.dim("\u2022")} Put a reverse proxy with TLS in front for remote access`,
|
|
2446
|
+
` ${import_picocolors6.default.dim("\u2022")} Do not store SSH keys, cloud creds, or production secrets here`
|
|
2447
|
+
].join("\n"));
|
|
2448
|
+
if (accessMode === "any") {
|
|
2449
|
+
R2.warn(import_picocolors6.default.red(
|
|
2450
|
+
"Binding to 0.0.0.0 exposes this to your entire network."
|
|
2451
|
+
));
|
|
2452
|
+
}
|
|
2453
|
+
const proceed = await Re({
|
|
2454
|
+
message: "I understand the risks. Continue?",
|
|
2455
|
+
initialValue: false
|
|
2200
2456
|
});
|
|
2201
|
-
if (Ct(
|
|
2202
|
-
|
|
2203
|
-
|
|
2457
|
+
if (Ct(proceed) || !proceed) process.exit(0);
|
|
2458
|
+
progress.completedSteps.push("security");
|
|
2459
|
+
saveProgress(progress);
|
|
2460
|
+
}
|
|
2461
|
+
R2.step(import_picocolors6.default.bold("Authentication"));
|
|
2462
|
+
let adminEmail;
|
|
2463
|
+
let adminPassword;
|
|
2464
|
+
let adminName;
|
|
2465
|
+
if (progress.partialConfig.auth) {
|
|
2466
|
+
adminEmail = progress.partialConfig.auth.adminEmail;
|
|
2467
|
+
adminPassword = progress.partialConfig.auth.adminPassword;
|
|
2468
|
+
adminName = progress.partialConfig.auth.adminName;
|
|
2469
|
+
R2.info(`Using admin: ${adminEmail}`);
|
|
2470
|
+
} else {
|
|
2471
|
+
const emailInput = await Ze({
|
|
2472
|
+
message: "Admin email",
|
|
2473
|
+
placeholder: "admin@example.com",
|
|
2474
|
+
defaultValue: "admin@example.com"
|
|
2204
2475
|
});
|
|
2205
|
-
if (Ct(
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2476
|
+
if (Ct(emailInput)) process.exit(0);
|
|
2477
|
+
adminEmail = emailInput;
|
|
2478
|
+
while (true) {
|
|
2479
|
+
const pw = await He({
|
|
2480
|
+
message: "Admin password",
|
|
2481
|
+
validate: (v) => {
|
|
2482
|
+
if (!v || v.length < 8) return "Minimum 8 characters";
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
2485
|
+
if (Ct(pw)) process.exit(0);
|
|
2486
|
+
const confirm = await He({
|
|
2487
|
+
message: "Confirm password"
|
|
2488
|
+
});
|
|
2489
|
+
if (Ct(confirm)) process.exit(0);
|
|
2490
|
+
if (pw === confirm) {
|
|
2491
|
+
adminPassword = pw;
|
|
2492
|
+
break;
|
|
2493
|
+
}
|
|
2494
|
+
R2.warn("Passwords do not match. Try again.");
|
|
2209
2495
|
}
|
|
2210
|
-
|
|
2496
|
+
const nameInput = await Ze({
|
|
2497
|
+
message: "Admin display name",
|
|
2498
|
+
placeholder: personality.userName,
|
|
2499
|
+
defaultValue: personality.userName
|
|
2500
|
+
});
|
|
2501
|
+
if (Ct(nameInput)) process.exit(0);
|
|
2502
|
+
adminName = nameInput;
|
|
2503
|
+
progress.partialConfig.auth = {
|
|
2504
|
+
adminEmail,
|
|
2505
|
+
adminPassword,
|
|
2506
|
+
adminName,
|
|
2507
|
+
authSecret: crypto3.randomBytes(32).toString("base64")
|
|
2508
|
+
};
|
|
2509
|
+
progress.completedSteps.push("auth");
|
|
2510
|
+
saveProgress(progress);
|
|
2211
2511
|
}
|
|
2212
|
-
const adminName = await Ze({
|
|
2213
|
-
message: "Admin display name",
|
|
2214
|
-
placeholder: personality.userName,
|
|
2215
|
-
defaultValue: personality.userName
|
|
2216
|
-
});
|
|
2217
|
-
if (Ct(adminName)) process.exit(0);
|
|
2218
2512
|
const config = {
|
|
2219
2513
|
personality,
|
|
2220
2514
|
vault,
|
|
2221
2515
|
database,
|
|
2222
|
-
auth: {
|
|
2516
|
+
auth: progress.partialConfig.auth || {
|
|
2223
2517
|
adminEmail,
|
|
2224
2518
|
adminPassword,
|
|
2225
2519
|
adminName,
|
|
@@ -2229,7 +2523,7 @@ async function init() {
|
|
|
2229
2523
|
accessMode,
|
|
2230
2524
|
installDir: resolvedInstallDir
|
|
2231
2525
|
};
|
|
2232
|
-
R2.step(
|
|
2526
|
+
R2.step(import_picocolors6.default.bold("Configuration"));
|
|
2233
2527
|
const s = bt2();
|
|
2234
2528
|
s.start("Writing .env");
|
|
2235
2529
|
writeEnvFile(config);
|
|
@@ -2237,24 +2531,86 @@ async function init() {
|
|
|
2237
2531
|
s.start("Installing Claude Code configuration");
|
|
2238
2532
|
await installClaudeConfig(config);
|
|
2239
2533
|
s.stop("Claude config installed to ~/.claude/");
|
|
2240
|
-
writeMetadata(
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2534
|
+
writeMetadata(
|
|
2535
|
+
resolvedInstallDir,
|
|
2536
|
+
vault.path,
|
|
2537
|
+
getPackageVersion(),
|
|
2538
|
+
database.password,
|
|
2539
|
+
database.port
|
|
2540
|
+
);
|
|
2541
|
+
R2.step(import_picocolors6.default.bold("Setup"));
|
|
2542
|
+
if (!progress.completedSteps.includes("install")) {
|
|
2543
|
+
try {
|
|
2544
|
+
await withRetry(
|
|
2545
|
+
"Dependency installation",
|
|
2546
|
+
async () => {
|
|
2547
|
+
s.start("Installing dependencies");
|
|
2548
|
+
execSync5("pnpm install", { cwd: resolvedInstallDir, stdio: "pipe" });
|
|
2549
|
+
s.stop("Dependencies installed");
|
|
2550
|
+
},
|
|
2551
|
+
{ canSkip: false }
|
|
2552
|
+
);
|
|
2553
|
+
progress.completedSteps.push("install");
|
|
2554
|
+
saveProgress(progress);
|
|
2555
|
+
} catch (err) {
|
|
2556
|
+
s.stop("Dependency installation failed");
|
|
2557
|
+
if (err instanceof SkipError) {
|
|
2558
|
+
R2.error("Dependency installation is required and cannot be skipped");
|
|
2559
|
+
}
|
|
2560
|
+
process.exit(1);
|
|
2561
|
+
}
|
|
2562
|
+
} else {
|
|
2563
|
+
R2.info("Dependencies already installed");
|
|
2564
|
+
}
|
|
2565
|
+
R2.step(import_picocolors6.default.bold("Launch"));
|
|
2566
|
+
if (!progress.completedSteps.includes("pm2")) {
|
|
2567
|
+
try {
|
|
2568
|
+
await withRetry(
|
|
2569
|
+
"PM2 setup and start",
|
|
2570
|
+
() => setupAndStart(config),
|
|
2571
|
+
{ canSkip: false }
|
|
2572
|
+
);
|
|
2573
|
+
progress.completedSteps.push("pm2");
|
|
2574
|
+
saveProgress(progress);
|
|
2575
|
+
} catch (err) {
|
|
2576
|
+
if (err instanceof SkipError) {
|
|
2577
|
+
R2.error("PM2 setup is required and cannot be skipped");
|
|
2578
|
+
}
|
|
2579
|
+
process.exit(1);
|
|
2580
|
+
}
|
|
2581
|
+
} else {
|
|
2582
|
+
R2.info("PM2 already configured");
|
|
2583
|
+
}
|
|
2584
|
+
if (!progress.completedSteps.includes("health")) {
|
|
2585
|
+
try {
|
|
2586
|
+
await withRetry(
|
|
2587
|
+
"Health check",
|
|
2588
|
+
() => waitForHealth("http://localhost:3000"),
|
|
2589
|
+
{ canSkip: true }
|
|
2590
|
+
);
|
|
2591
|
+
progress.completedSteps.push("health");
|
|
2592
|
+
saveProgress(progress);
|
|
2593
|
+
} catch (err) {
|
|
2594
|
+
if (err instanceof SkipError) {
|
|
2595
|
+
R2.warn("Health check skipped - app may not be fully started");
|
|
2596
|
+
R2.info("Check status with: pm2 status");
|
|
2597
|
+
} else {
|
|
2598
|
+
process.exit(1);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
} else {
|
|
2602
|
+
R2.info("App already healthy");
|
|
2603
|
+
}
|
|
2604
|
+
R2.step(import_picocolors6.default.bold("Summary"));
|
|
2249
2605
|
R2.info([
|
|
2250
2606
|
"",
|
|
2251
|
-
` ${
|
|
2252
|
-
` ${
|
|
2253
|
-
` ${
|
|
2254
|
-
` ${
|
|
2255
|
-
` ${
|
|
2607
|
+
` ${import_picocolors6.default.cyan("App URL:")} ${config.appUrl}`,
|
|
2608
|
+
` ${import_picocolors6.default.cyan("Admin Login:")} ${config.auth.adminEmail}`,
|
|
2609
|
+
` ${import_picocolors6.default.cyan("Vault:")} ${vault.path}`,
|
|
2610
|
+
` ${import_picocolors6.default.cyan("Install Dir:")} ${resolvedInstallDir}`,
|
|
2611
|
+
` ${import_picocolors6.default.cyan("Agent:")} ${personality.agentName}`,
|
|
2256
2612
|
"",
|
|
2257
|
-
` ${
|
|
2613
|
+
` ${import_picocolors6.default.dim("Manage:")}`,
|
|
2258
2614
|
` cognova start Start the app`,
|
|
2259
2615
|
` cognova stop Stop the app`,
|
|
2260
2616
|
` cognova restart Restart the app`,
|
|
@@ -2266,17 +2622,18 @@ async function init() {
|
|
|
2266
2622
|
` pm2 monit Monitor resources`,
|
|
2267
2623
|
""
|
|
2268
2624
|
].join("\n"));
|
|
2269
|
-
Le(`${personality.agentName} is ready. Open ${
|
|
2625
|
+
Le(`${personality.agentName} is ready. Open ${import_picocolors6.default.underline(config.appUrl)} to get started.`);
|
|
2626
|
+
clearProgress();
|
|
2270
2627
|
}
|
|
2271
2628
|
|
|
2272
2629
|
// src/commands/start.ts
|
|
2273
2630
|
import { execSync as execSync6 } from "child_process";
|
|
2274
|
-
import { existsSync as
|
|
2275
|
-
import { join as
|
|
2631
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2632
|
+
import { join as join10 } from "path";
|
|
2276
2633
|
async function start() {
|
|
2277
2634
|
const installDir = findInstallDir();
|
|
2278
|
-
const ecosystem =
|
|
2279
|
-
if (!
|
|
2635
|
+
const ecosystem = join10(installDir, "ecosystem.config.cjs");
|
|
2636
|
+
if (!existsSync8(ecosystem)) {
|
|
2280
2637
|
console.error("No ecosystem.config.cjs found. Run `cognova init` first.");
|
|
2281
2638
|
process.exit(1);
|
|
2282
2639
|
}
|
|
@@ -2302,9 +2659,9 @@ async function restart() {
|
|
|
2302
2659
|
|
|
2303
2660
|
// src/commands/update.ts
|
|
2304
2661
|
import { execSync as execSync7 } from "child_process";
|
|
2305
|
-
import { existsSync as
|
|
2306
|
-
import { join as
|
|
2307
|
-
var
|
|
2662
|
+
import { existsSync as existsSync9, cpSync as cpSync3, rmSync } from "fs";
|
|
2663
|
+
import { join as join11 } from "path";
|
|
2664
|
+
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
2308
2665
|
var BACKUP_ITEMS = [
|
|
2309
2666
|
"app",
|
|
2310
2667
|
"server",
|
|
@@ -2318,33 +2675,33 @@ var BACKUP_ITEMS = [
|
|
|
2318
2675
|
"pnpm-lock.yaml"
|
|
2319
2676
|
];
|
|
2320
2677
|
function createBackup(installDir) {
|
|
2321
|
-
const backupDir =
|
|
2322
|
-
if (
|
|
2678
|
+
const backupDir = join11(installDir, ".update-backup");
|
|
2679
|
+
if (existsSync9(backupDir))
|
|
2323
2680
|
rmSync(backupDir, { recursive: true });
|
|
2324
2681
|
for (const item of BACKUP_ITEMS) {
|
|
2325
|
-
const src =
|
|
2326
|
-
if (!
|
|
2327
|
-
const dest =
|
|
2682
|
+
const src = join11(installDir, item);
|
|
2683
|
+
if (!existsSync9(src)) continue;
|
|
2684
|
+
const dest = join11(backupDir, item);
|
|
2328
2685
|
cpSync3(src, dest, { recursive: true });
|
|
2329
2686
|
}
|
|
2330
2687
|
return backupDir;
|
|
2331
2688
|
}
|
|
2332
2689
|
function restoreBackup(installDir, backupDir) {
|
|
2333
2690
|
for (const item of BACKUP_ITEMS) {
|
|
2334
|
-
const src =
|
|
2335
|
-
if (!
|
|
2336
|
-
const dest =
|
|
2337
|
-
if (
|
|
2691
|
+
const src = join11(backupDir, item);
|
|
2692
|
+
if (!existsSync9(src)) continue;
|
|
2693
|
+
const dest = join11(installDir, item);
|
|
2694
|
+
if (existsSync9(dest))
|
|
2338
2695
|
rmSync(dest, { recursive: true });
|
|
2339
2696
|
cpSync3(src, dest, { recursive: true });
|
|
2340
2697
|
}
|
|
2341
2698
|
}
|
|
2342
2699
|
function cleanupBackup(backupDir) {
|
|
2343
|
-
if (
|
|
2700
|
+
if (existsSync9(backupDir))
|
|
2344
2701
|
rmSync(backupDir, { recursive: true });
|
|
2345
2702
|
}
|
|
2346
2703
|
async function update() {
|
|
2347
|
-
We(
|
|
2704
|
+
We(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" Cognova Update ")));
|
|
2348
2705
|
const installDir = findInstallDir();
|
|
2349
2706
|
const metadata = readMetadata(installDir);
|
|
2350
2707
|
if (!metadata) {
|
|
@@ -2366,7 +2723,7 @@ async function update() {
|
|
|
2366
2723
|
Le("Nothing to update.");
|
|
2367
2724
|
return;
|
|
2368
2725
|
}
|
|
2369
|
-
s.stop(`Update available: ${metadata.version} \u2192 ${
|
|
2726
|
+
s.stop(`Update available: ${metadata.version} \u2192 ${import_picocolors7.default.green(latestVersion)}`);
|
|
2370
2727
|
s.start("Creating backup");
|
|
2371
2728
|
const backupDir = createBackup(installDir);
|
|
2372
2729
|
s.stop("Backup created");
|
|
@@ -2395,14 +2752,14 @@ async function update() {
|
|
|
2395
2752
|
s.stop("Migrations complete");
|
|
2396
2753
|
} catch (err) {
|
|
2397
2754
|
updateFailed = true;
|
|
2398
|
-
s.stop(
|
|
2755
|
+
s.stop(import_picocolors7.default.red("Update failed"));
|
|
2399
2756
|
const execErr = err;
|
|
2400
2757
|
R2.error(`Error: ${execErr.message || err}`);
|
|
2401
2758
|
if (execErr.stdout)
|
|
2402
|
-
R2.error(
|
|
2759
|
+
R2.error(import_picocolors7.default.dim(String(execErr.stdout).trim()));
|
|
2403
2760
|
if (execErr.stderr)
|
|
2404
|
-
R2.error(
|
|
2405
|
-
R2.step(
|
|
2761
|
+
R2.error(import_picocolors7.default.dim(String(execErr.stderr).trim()));
|
|
2762
|
+
R2.step(import_picocolors7.default.bold("Rolling back"));
|
|
2406
2763
|
const rollbackSpinner = bt2();
|
|
2407
2764
|
rollbackSpinner.start("Restoring previous version");
|
|
2408
2765
|
try {
|
|
@@ -2411,7 +2768,7 @@ async function update() {
|
|
|
2411
2768
|
rollbackSpinner.stop("Previous version restored");
|
|
2412
2769
|
R2.info("Your previous installation has been restored.");
|
|
2413
2770
|
} catch (rollbackErr) {
|
|
2414
|
-
rollbackSpinner.stop(
|
|
2771
|
+
rollbackSpinner.stop(import_picocolors7.default.red("Rollback failed"));
|
|
2415
2772
|
R2.error(`Rollback failed: ${rollbackErr instanceof Error ? rollbackErr.message : rollbackErr}`);
|
|
2416
2773
|
R2.error(`Manual recovery: backup is at ${backupDir}`);
|
|
2417
2774
|
Le("Update and rollback both failed. See errors above.");
|
|
@@ -2433,17 +2790,17 @@ async function update() {
|
|
|
2433
2790
|
} catch {
|
|
2434
2791
|
s.stop("PM2 restart failed \u2014 start manually with `cognova start`");
|
|
2435
2792
|
}
|
|
2436
|
-
R2.info(`Run ${
|
|
2793
|
+
R2.info(`Run ${import_picocolors7.default.cyan("cognova reset")} to regenerate CLAUDE.md or settings.json.`);
|
|
2437
2794
|
Le(`Updated to v${latestVersion}`);
|
|
2438
2795
|
}
|
|
2439
2796
|
|
|
2440
2797
|
// src/commands/doctor.ts
|
|
2441
2798
|
import { execSync as execSync8 } from "child_process";
|
|
2442
|
-
import { existsSync as
|
|
2443
|
-
import { join as
|
|
2444
|
-
var
|
|
2799
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2800
|
+
import { join as join12 } from "path";
|
|
2801
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
2445
2802
|
async function doctor() {
|
|
2446
|
-
We(
|
|
2803
|
+
We(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" Cognova Doctor ")));
|
|
2447
2804
|
const installDir = findInstallDir();
|
|
2448
2805
|
const claudeDir = getClaudeDir();
|
|
2449
2806
|
const metadata = readMetadata(installDir);
|
|
@@ -2453,35 +2810,55 @@ async function doctor() {
|
|
|
2453
2810
|
const checks = [
|
|
2454
2811
|
{
|
|
2455
2812
|
name: ".env file",
|
|
2456
|
-
check: () =>
|
|
2813
|
+
check: () => existsSync10(join12(installDir, ".env")),
|
|
2814
|
+
fixDescription: 'Run "cognova reset" to regenerate'
|
|
2457
2815
|
},
|
|
2458
2816
|
{
|
|
2459
2817
|
name: "node_modules",
|
|
2460
|
-
check: () =>
|
|
2818
|
+
check: () => existsSync10(join12(installDir, "node_modules")),
|
|
2819
|
+
fix: async () => {
|
|
2820
|
+
const s = bt2();
|
|
2821
|
+
s.start("Installing dependencies");
|
|
2822
|
+
execSync8("pnpm install", { cwd: installDir, stdio: "inherit" });
|
|
2823
|
+
s.stop("Dependencies installed");
|
|
2824
|
+
},
|
|
2825
|
+
fixDescription: "Install dependencies"
|
|
2461
2826
|
},
|
|
2462
2827
|
{
|
|
2463
2828
|
name: "Build output",
|
|
2464
|
-
check: () =>
|
|
2829
|
+
check: () => existsSync10(join12(installDir, ".output", "server", "index.mjs")),
|
|
2830
|
+
fix: async () => {
|
|
2831
|
+
const s = bt2();
|
|
2832
|
+
s.start("Building application");
|
|
2833
|
+
execSync8("pnpm build", { cwd: installDir, stdio: "inherit" });
|
|
2834
|
+
s.stop("Build complete");
|
|
2835
|
+
},
|
|
2836
|
+
fixDescription: "Build the application"
|
|
2465
2837
|
},
|
|
2466
2838
|
{
|
|
2467
2839
|
name: "~/.claude/CLAUDE.md",
|
|
2468
|
-
check: () =>
|
|
2840
|
+
check: () => existsSync10(join12(claudeDir, "CLAUDE.md")),
|
|
2841
|
+
fixDescription: 'Run "cognova reset --claude" to reinstall'
|
|
2469
2842
|
},
|
|
2470
2843
|
{
|
|
2471
2844
|
name: "~/.claude/skills/",
|
|
2472
|
-
check: () =>
|
|
2845
|
+
check: () => existsSync10(join12(claudeDir, "skills")),
|
|
2846
|
+
fixDescription: 'Run "cognova reset --claude" to reinstall'
|
|
2473
2847
|
},
|
|
2474
2848
|
{
|
|
2475
2849
|
name: "~/.claude/hooks/",
|
|
2476
|
-
check: () =>
|
|
2850
|
+
check: () => existsSync10(join12(claudeDir, "hooks")),
|
|
2851
|
+
fixDescription: 'Run "cognova reset --claude" to reinstall'
|
|
2477
2852
|
},
|
|
2478
2853
|
{
|
|
2479
2854
|
name: "~/.claude/rules/",
|
|
2480
|
-
check: () =>
|
|
2855
|
+
check: () => existsSync10(join12(claudeDir, "rules")),
|
|
2856
|
+
fixDescription: 'Run "cognova reset --claude" to reinstall'
|
|
2481
2857
|
},
|
|
2482
2858
|
{
|
|
2483
2859
|
name: "~/.claude/settings.json",
|
|
2484
|
-
check: () =>
|
|
2860
|
+
check: () => existsSync10(join12(claudeDir, "settings.json")),
|
|
2861
|
+
fixDescription: 'Run "cognova reset --claude" to reinstall'
|
|
2485
2862
|
},
|
|
2486
2863
|
{
|
|
2487
2864
|
name: "Claude Code CLI",
|
|
@@ -2492,7 +2869,8 @@ async function doctor() {
|
|
|
2492
2869
|
} catch {
|
|
2493
2870
|
return false;
|
|
2494
2871
|
}
|
|
2495
|
-
}
|
|
2872
|
+
},
|
|
2873
|
+
fixDescription: "Install with: npm install -g @anthropic-ai/claude-code"
|
|
2496
2874
|
},
|
|
2497
2875
|
{
|
|
2498
2876
|
name: "Python 3",
|
|
@@ -2503,7 +2881,36 @@ async function doctor() {
|
|
|
2503
2881
|
} catch {
|
|
2504
2882
|
return false;
|
|
2505
2883
|
}
|
|
2506
|
-
}
|
|
2884
|
+
},
|
|
2885
|
+
fixDescription: "Install from https://python.org/"
|
|
2886
|
+
},
|
|
2887
|
+
{
|
|
2888
|
+
name: "Docker daemon",
|
|
2889
|
+
check: () => {
|
|
2890
|
+
try {
|
|
2891
|
+
execSync8("docker info", { stdio: "pipe" });
|
|
2892
|
+
return true;
|
|
2893
|
+
} catch {
|
|
2894
|
+
return false;
|
|
2895
|
+
}
|
|
2896
|
+
},
|
|
2897
|
+
fix: async () => {
|
|
2898
|
+
const s = bt2();
|
|
2899
|
+
s.start("Starting Docker daemon");
|
|
2900
|
+
try {
|
|
2901
|
+
if (process.platform === "darwin") {
|
|
2902
|
+
execSync8("open -a Docker", { stdio: "pipe" });
|
|
2903
|
+
R2.info("Docker Desktop is starting - this may take a moment");
|
|
2904
|
+
} else if (process.platform === "linux") {
|
|
2905
|
+
execSync8("sudo systemctl start docker", { stdio: "inherit" });
|
|
2906
|
+
}
|
|
2907
|
+
s.stop("Docker daemon start command issued");
|
|
2908
|
+
} catch (err) {
|
|
2909
|
+
s.stop("Failed to start Docker daemon");
|
|
2910
|
+
throw err;
|
|
2911
|
+
}
|
|
2912
|
+
},
|
|
2913
|
+
fixDescription: "Start Docker daemon"
|
|
2507
2914
|
},
|
|
2508
2915
|
{
|
|
2509
2916
|
name: "PM2 process",
|
|
@@ -2517,7 +2924,19 @@ async function doctor() {
|
|
|
2517
2924
|
} catch {
|
|
2518
2925
|
return false;
|
|
2519
2926
|
}
|
|
2520
|
-
}
|
|
2927
|
+
},
|
|
2928
|
+
fix: async () => {
|
|
2929
|
+
const s = bt2();
|
|
2930
|
+
s.start("Starting PM2 process");
|
|
2931
|
+
try {
|
|
2932
|
+
execSync8("pm2 start ecosystem.config.cjs", { cwd: installDir, stdio: "inherit" });
|
|
2933
|
+
s.stop("PM2 process started");
|
|
2934
|
+
} catch (err) {
|
|
2935
|
+
s.stop("Failed to start PM2");
|
|
2936
|
+
throw err;
|
|
2937
|
+
}
|
|
2938
|
+
},
|
|
2939
|
+
fixDescription: "Start the PM2 process"
|
|
2521
2940
|
},
|
|
2522
2941
|
{
|
|
2523
2942
|
name: "App health endpoint",
|
|
@@ -2547,7 +2966,7 @@ async function doctor() {
|
|
|
2547
2966
|
name: "Vault directory",
|
|
2548
2967
|
check: () => {
|
|
2549
2968
|
if (!metadata) return false;
|
|
2550
|
-
return
|
|
2969
|
+
return existsSync10(metadata.vaultPath);
|
|
2551
2970
|
}
|
|
2552
2971
|
},
|
|
2553
2972
|
{
|
|
@@ -2563,32 +2982,63 @@ async function doctor() {
|
|
|
2563
2982
|
}
|
|
2564
2983
|
}
|
|
2565
2984
|
];
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
for (const { name, check } of checks) {
|
|
2985
|
+
const results = [];
|
|
2986
|
+
for (const check of checks) {
|
|
2569
2987
|
try {
|
|
2570
|
-
const result = check();
|
|
2988
|
+
const result = check.check();
|
|
2571
2989
|
if (result === true) {
|
|
2572
|
-
R2.success(`${
|
|
2573
|
-
|
|
2990
|
+
R2.success(`${import_picocolors8.default.green("PASS")} ${check.name}`);
|
|
2991
|
+
results.push({ check, status: "pass" });
|
|
2574
2992
|
} else if (result === false) {
|
|
2575
|
-
R2.error(`${
|
|
2576
|
-
|
|
2993
|
+
R2.error(`${import_picocolors8.default.red("FAIL")} ${check.name}`);
|
|
2994
|
+
if (check.fixDescription)
|
|
2995
|
+
R2.info(` \u2192 ${import_picocolors8.default.dim(check.fixDescription)}`);
|
|
2996
|
+
results.push({ check, status: "fail" });
|
|
2577
2997
|
} else {
|
|
2578
|
-
R2.warn(`${
|
|
2998
|
+
R2.warn(`${import_picocolors8.default.yellow("WARN")} ${check.name}: ${result}`);
|
|
2999
|
+
results.push({ check, status: "warn", message: result });
|
|
2579
3000
|
}
|
|
2580
3001
|
} catch {
|
|
2581
|
-
R2.error(`${
|
|
2582
|
-
|
|
3002
|
+
R2.error(`${import_picocolors8.default.red("FAIL")} ${check.name}`);
|
|
3003
|
+
if (check.fixDescription)
|
|
3004
|
+
R2.info(` \u2192 ${import_picocolors8.default.dim(check.fixDescription)}`);
|
|
3005
|
+
results.push({ check, status: "fail" });
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
const passed = results.filter((r) => r.status === "pass").length;
|
|
3009
|
+
const failed = results.filter((r) => r.status === "fail").length;
|
|
3010
|
+
const fixableFailures = results.filter((r) => r.status === "fail" && r.check.fix);
|
|
3011
|
+
if (fixableFailures.length > 0) {
|
|
3012
|
+
R2.info("");
|
|
3013
|
+
const runFixes = await Re({
|
|
3014
|
+
message: `${fixableFailures.length} issue(s) can be auto-fixed. Run fixes now?`,
|
|
3015
|
+
initialValue: true
|
|
3016
|
+
});
|
|
3017
|
+
if (!Ct(runFixes) && runFixes) {
|
|
3018
|
+
for (const { check } of fixableFailures) {
|
|
3019
|
+
if (!check.fix) continue;
|
|
3020
|
+
R2.step(import_picocolors8.default.bold(`Fixing: ${check.name}`));
|
|
3021
|
+
try {
|
|
3022
|
+
await check.fix();
|
|
3023
|
+
const result = check.check();
|
|
3024
|
+
if (result === true) {
|
|
3025
|
+
R2.success(`${import_picocolors8.default.green("\u2713")} ${check.name} is now fixed`);
|
|
3026
|
+
} else {
|
|
3027
|
+
R2.warn(`${import_picocolors8.default.yellow("!")} ${check.name} still failing after fix attempt`);
|
|
3028
|
+
}
|
|
3029
|
+
} catch (err) {
|
|
3030
|
+
R2.error(`${import_picocolors8.default.red("\u2717")} Failed to fix ${check.name}: ${err}`);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
2583
3033
|
}
|
|
2584
3034
|
}
|
|
2585
3035
|
Le(`${passed} passed, ${failed} failed`);
|
|
2586
3036
|
}
|
|
2587
3037
|
|
|
2588
3038
|
// src/commands/reset.ts
|
|
2589
|
-
var
|
|
3039
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
2590
3040
|
async function reset() {
|
|
2591
|
-
We(
|
|
3041
|
+
We(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" Cognova Reset ")));
|
|
2592
3042
|
const installDir = findInstallDir();
|
|
2593
3043
|
const metadata = readMetadata(installDir);
|
|
2594
3044
|
if (!metadata) {
|