@hasna/cloud 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -12794,6 +12794,9 @@ async function ensureAllPgDatabases() {
12794
12794
  }
12795
12795
 
12796
12796
  // src/cli/index.ts
12797
+ import { existsSync as existsSync9, statSync as statSync6 } from "fs";
12798
+ import { join as join9 } from "path";
12799
+ import { homedir as homedir8 } from "os";
12797
12800
  var program2 = new Command;
12798
12801
  program2.name("cloud").description("Shared cloud infrastructure \u2014 database adapter, sync engine, feedback, dotfile migration").version("0.1.8");
12799
12802
  program2.command("setup").description("Configure cloud settings").option("--host <host>", "RDS hostname").option("--port <port>", "RDS port", "5432").option("--username <user>", "RDS username").option("--password-env <env>", "Env var for RDS password", "HASNA_RDS_PASSWORD").option("--ssl", "Enable SSL", true).option("--no-ssl", "Disable SSL").option("--mode <mode>", "Mode: local, cloud, or hybrid", "local").option("--sync-interval <minutes>", "Auto-sync interval in minutes", "0").action((opts) => {
@@ -13068,7 +13071,6 @@ Done. ${results.length} services, ${totalApplied} migrations applied, ${totalErr
13068
13071
  syncCmd.command("status").description("Show sync status for all discovered services").option("--service <name>", "Show status for a single service").option("--json", "Output as JSON").action(async (opts) => {
13069
13072
  const services = opts.service ? [opts.service] : discoverServices();
13070
13073
  const statuses = [];
13071
- const { existsSync: existsSync9, statSync: statSync6 } = __require("fs");
13072
13074
  for (const service of services) {
13073
13075
  const dbPath = getDbPath2(service);
13074
13076
  const localExists = existsSync9(dbPath);
@@ -13218,4 +13220,71 @@ program2.command("migrate").description("Migrate legacy dotfiles to ~/.hasna/").
13218
13220
  }
13219
13221
  }
13220
13222
  });
13223
+ program2.command("doctor").description("Comprehensive health check for cloud sync setup").action(async () => {
13224
+ const checks = [];
13225
+ const configPath = join9(homedir8(), ".hasna", "cloud", "config.json");
13226
+ if (existsSync9(configPath)) {
13227
+ checks.push({ name: "Config file", status: "pass", detail: configPath });
13228
+ } else {
13229
+ checks.push({ name: "Config file", status: "fail", detail: "Missing. Run `cloud setup`." });
13230
+ }
13231
+ const config = getCloudConfig();
13232
+ if (config.mode === "hybrid" || config.mode === "cloud") {
13233
+ checks.push({ name: "Sync mode", status: "pass", detail: config.mode });
13234
+ } else {
13235
+ checks.push({ name: "Sync mode", status: "fail", detail: `"${config.mode}" \u2014 sync disabled. Run \`cloud setup --mode hybrid\`.` });
13236
+ }
13237
+ if (config.rds.host) {
13238
+ checks.push({ name: "RDS host", status: "pass", detail: config.rds.host });
13239
+ } else {
13240
+ checks.push({ name: "RDS host", status: "fail", detail: "Not configured. Run `cloud setup`." });
13241
+ }
13242
+ const password = process.env[config.rds.password_env];
13243
+ if (password) {
13244
+ checks.push({ name: "RDS password", status: "pass", detail: `${config.rds.password_env} is set` });
13245
+ } else {
13246
+ checks.push({ name: "RDS password", status: "fail", detail: `${config.rds.password_env} not in environment. Add to ~/.secrets/hasna/rds/live.env` });
13247
+ }
13248
+ if (config.rds.host && password) {
13249
+ try {
13250
+ const connStr = getConnectionString("postgres");
13251
+ const pg2 = new PgAdapterAsync2(connStr);
13252
+ await pg2.all("SELECT 1");
13253
+ await pg2.close();
13254
+ checks.push({ name: "PG connection", status: "pass", detail: "Connected" });
13255
+ } catch (err) {
13256
+ checks.push({ name: "PG connection", status: "fail", detail: err?.message ?? String(err) });
13257
+ }
13258
+ } else {
13259
+ checks.push({ name: "PG connection", status: "fail", detail: "Skipped \u2014 missing host or password" });
13260
+ }
13261
+ const caPath = process.env.NODE_EXTRA_CA_CERTS;
13262
+ if (caPath && existsSync9(caPath)) {
13263
+ checks.push({ name: "SSL CA cert", status: "pass", detail: caPath });
13264
+ } else if (caPath) {
13265
+ checks.push({ name: "SSL CA cert", status: "warn", detail: `NODE_EXTRA_CA_CERTS set but file missing: ${caPath}` });
13266
+ } else {
13267
+ checks.push({ name: "SSL CA cert", status: "warn", detail: "NODE_EXTRA_CA_CERTS not set. May cause SSL errors on some systems." });
13268
+ }
13269
+ const services = discoverServices();
13270
+ checks.push({ name: "Local services", status: services.length > 0 ? "pass" : "warn", detail: `${services.length} found in ~/.hasna/` });
13271
+ const schedule = getSyncScheduleStatus();
13272
+ if (schedule.registered) {
13273
+ checks.push({ name: "Sync schedule", status: "pass", detail: `Every ${schedule.schedule_minutes}m (${schedule.mechanism})` });
13274
+ } else {
13275
+ checks.push({ name: "Sync schedule", status: "warn", detail: "Not configured. Run `cloud sync schedule --every 30m`." });
13276
+ }
13277
+ console.log(`Cloud Doctor
13278
+ `);
13279
+ for (const c of checks) {
13280
+ const icon = c.status === "pass" ? "\u2713" : c.status === "fail" ? "\u2717" : "\u26A0";
13281
+ console.log(` ${icon} ${c.name.padEnd(20)} ${c.detail}`);
13282
+ }
13283
+ const fails = checks.filter((c) => c.status === "fail").length;
13284
+ const warns = checks.filter((c) => c.status === "warn").length;
13285
+ console.log(`
13286
+ ${checks.length} checks: ${checks.length - fails - warns} passed, ${warns} warnings, ${fails} failed`);
13287
+ if (fails > 0)
13288
+ process.exit(1);
13289
+ });
13221
13290
  program2.parse();