@hasna/cloud 0.1.24 → 0.1.25

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/cli/index.js +92 -6
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -12810,8 +12810,21 @@ function logSync(direction, service, rows, errors2) {
12810
12810
  } catch {}
12811
12811
  }
12812
12812
  program2.name("cloud").description("Shared cloud infrastructure \u2014 database adapter, sync engine, feedback, dotfile migration").version("0.1.8");
12813
- 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) => {
12813
+ program2.command("setup").description("Configure cloud settings \u2014 interactive wizard or flags").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").option("--schedule <interval>", "Sync schedule (e.g. 30m, 1h)").option("--migrate", "Run PG migrations after setup").option("--pull", "Pull data from cloud after setup").action(async (opts) => {
12814
12814
  const config = getCloudConfig();
12815
+ const isAutoDetect = !opts.host && !opts.username;
12816
+ if (isAutoDetect) {
12817
+ const envHost = process.env.HASNA_RDS_HOST;
12818
+ const envUser = process.env.HASNA_RDS_USERNAME;
12819
+ if (envHost && !config.rds.host) {
12820
+ config.rds.host = envHost;
12821
+ console.log(`Auto-detected RDS host: ${envHost}`);
12822
+ }
12823
+ if (envUser && !config.rds.username) {
12824
+ config.rds.username = envUser;
12825
+ console.log(`Auto-detected RDS username: ${envUser}`);
12826
+ }
12827
+ }
12815
12828
  if (opts.host)
12816
12829
  config.rds.host = opts.host;
12817
12830
  if (opts.port)
@@ -12821,13 +12834,86 @@ program2.command("setup").description("Configure cloud settings").option("--host
12821
12834
  if (opts.passwordEnv)
12822
12835
  config.rds.password_env = opts.passwordEnv;
12823
12836
  config.rds.ssl = opts.ssl;
12824
- if (opts.mode)
12837
+ if (opts.mode) {
12825
12838
  config.mode = opts.mode;
12826
- if (opts.syncInterval)
12827
- config.auto_sync_interval_minutes = parseInt(opts.syncInterval, 10);
12839
+ } else if (config.mode === "local" && config.rds.host) {
12840
+ config.mode = "hybrid";
12841
+ console.log("Mode set to: hybrid (auto-upgraded from local)");
12842
+ }
12828
12843
  saveCloudConfig(config);
12829
- console.log("Cloud configuration saved.");
12830
- console.log(JSON.stringify(config, null, 2));
12844
+ console.log(`
12845
+ \u2713 Configuration saved
12846
+ `);
12847
+ const password = process.env[config.rds.password_env];
12848
+ if (!password) {
12849
+ console.error(`\u2717 ${config.rds.password_env} not set in environment`);
12850
+ console.error(` Add it to ~/.secrets/hasna/rds/live.env and source it`);
12851
+ return;
12852
+ }
12853
+ if (config.rds.host) {
12854
+ process.stdout.write("Testing PG connection... ");
12855
+ try {
12856
+ const connStr = getConnectionString("postgres");
12857
+ const pg2 = new PgAdapterAsync2(connStr);
12858
+ await pg2.all("SELECT 1");
12859
+ await pg2.close();
12860
+ console.log(`\u2713 Connected
12861
+ `);
12862
+ } catch (err) {
12863
+ console.log(`\u2717 Failed: ${err?.message ?? String(err)}`);
12864
+ return;
12865
+ }
12866
+ if (opts.migrate !== false) {
12867
+ console.log("Creating databases & running migrations...");
12868
+ const dbResults = await ensureAllPgDatabases();
12869
+ const created = dbResults.filter((r) => r.created);
12870
+ if (created.length > 0) {
12871
+ console.log(` Created ${created.length} database(s): ${created.map((r) => r.service).join(", ")}`);
12872
+ }
12873
+ const migResults = await migrateAllServices();
12874
+ const applied = migResults.filter((r) => r.applied.length > 0);
12875
+ const totalApplied = migResults.reduce((s, r) => s + r.applied.length, 0);
12876
+ if (totalApplied > 0) {
12877
+ console.log(` Applied ${totalApplied} migration(s) across ${applied.length} service(s)`);
12878
+ } else {
12879
+ console.log(" All migrations up to date");
12880
+ }
12881
+ console.log("");
12882
+ }
12883
+ if (opts.schedule) {
12884
+ try {
12885
+ const minutes = parseInterval(opts.schedule);
12886
+ await registerSyncSchedule(minutes);
12887
+ console.log(`\u2713 Sync scheduled every ${minutes}m
12888
+ `);
12889
+ } catch (err) {
12890
+ console.error(`\u2717 Schedule failed: ${err?.message}`);
12891
+ }
12892
+ }
12893
+ if (opts.pull) {
12894
+ console.log("Pulling data from cloud...");
12895
+ const services = discoverServices();
12896
+ for (const service of services) {
12897
+ try {
12898
+ const dbPath = getDbPath2(service);
12899
+ const local = new SqliteAdapter2(dbPath);
12900
+ const connStr = getConnectionString(service);
12901
+ const cloud = new PgAdapterAsync2(connStr);
12902
+ const tables = (await listPgTables(cloud)).filter((t) => !isSyncExcludedTable(t));
12903
+ if (tables.length > 0) {
12904
+ const results = await syncPull(cloud, local, { tables });
12905
+ const written = results.reduce((s, r) => s + r.rowsWritten, 0);
12906
+ if (written > 0)
12907
+ console.log(` ${service}: ${written} rows`);
12908
+ }
12909
+ local.close();
12910
+ await cloud.close();
12911
+ } catch {}
12912
+ }
12913
+ console.log("");
12914
+ }
12915
+ }
12916
+ console.log("Setup complete. Run `cloud doctor` to verify everything.");
12831
12917
  });
12832
12918
  program2.command("status").description("Show current cloud configuration and connection health").action(async () => {
12833
12919
  const config = getCloudConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/cloud",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Shared cloud infrastructure — database adapter (SQLite + PostgreSQL), sync engine, feedback system, unified dotfile config",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",