@habeetat/cli 0.1.0-dev.20260422103110.f3c4547 → 0.1.0-dev.20260424063302.01f2802

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.
@@ -1,41 +1,33 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
2
+ import { Command } from 'commander';
3
+ import fs2, { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import ora from 'ora';
7
+ import chalk3 from 'chalk';
8
+ import { execa, execaCommand } from 'execa';
9
+ import { createInterface } from 'readline';
3
10
 
4
- var commander = require('commander');
5
- var fs2 = require('fs');
6
- var path = require('path');
7
- var ora = require('ora');
8
- var chalk3 = require('chalk');
9
- var execa = require('execa');
10
- var readline = require('readline');
11
-
12
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
-
14
- var fs2__default = /*#__PURE__*/_interopDefault(fs2);
15
- var path__default = /*#__PURE__*/_interopDefault(path);
16
- var ora__default = /*#__PURE__*/_interopDefault(ora);
17
- var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
18
-
19
- var prefix = chalk3__default.default.cyan("[habeetat]");
11
+ var prefix = chalk3.cyan("[habeetat]");
20
12
  var logger = {
21
13
  info: (msg) => console.log(`${prefix} ${msg}`),
22
- success: (msg) => console.log(`${prefix} ${chalk3__default.default.green("\u2713")} ${msg}`),
23
- warn: (msg) => console.log(`${prefix} ${chalk3__default.default.yellow("\u26A0")} ${msg}`),
24
- error: (msg) => console.error(`${prefix} ${chalk3__default.default.red("\u2717")} ${msg}`),
14
+ success: (msg) => console.log(`${prefix} ${chalk3.green("\u2713")} ${msg}`),
15
+ warn: (msg) => console.log(`${prefix} ${chalk3.yellow("\u26A0")} ${msg}`),
16
+ error: (msg) => console.error(`${prefix} ${chalk3.red("\u2717")} ${msg}`),
25
17
  phase: (msg) => {
26
18
  console.log("");
27
- console.log(chalk3__default.default.cyan("\u2550".repeat(56)));
28
- console.log(chalk3__default.default.cyan(` ${msg}`));
29
- console.log(chalk3__default.default.cyan("\u2550".repeat(56)));
19
+ console.log(chalk3.cyan("\u2550".repeat(56)));
20
+ console.log(chalk3.cyan(` ${msg}`));
21
+ console.log(chalk3.cyan("\u2550".repeat(56)));
30
22
  console.log("");
31
23
  },
32
24
  banner: () => {
33
25
  console.log("");
34
- console.log(chalk3__default.default.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
35
- console.log(chalk3__default.default.cyan("\u2551 \u2551"));
36
- console.log(chalk3__default.default.cyan("\u2551 Habeetat Platform CLI \u2551"));
37
- console.log(chalk3__default.default.cyan("\u2551 \u2551"));
38
- console.log(chalk3__default.default.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
26
+ console.log(chalk3.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
27
+ console.log(chalk3.cyan("\u2551 \u2551"));
28
+ console.log(chalk3.cyan("\u2551 Habeetat Platform CLI \u2551"));
29
+ console.log(chalk3.cyan("\u2551 \u2551"));
30
+ console.log(chalk3.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
39
31
  console.log("");
40
32
  }
41
33
  };
@@ -66,11 +58,11 @@ var SERVICES = [
66
58
  // src/utils/config.ts
67
59
  function findProjectRoot(startDir = process.cwd()) {
68
60
  let dir = startDir;
69
- while (dir !== path__default.default.dirname(dir)) {
70
- if (fs2__default.default.existsSync(path__default.default.join(dir, CONFIG_FILE))) {
61
+ while (dir !== path.dirname(dir)) {
62
+ if (fs2.existsSync(path.join(dir, CONFIG_FILE))) {
71
63
  return dir;
72
64
  }
73
- dir = path__default.default.dirname(dir);
65
+ dir = path.dirname(dir);
74
66
  }
75
67
  return null;
76
68
  }
@@ -81,8 +73,8 @@ function loadConfig(projectDir) {
81
73
  `Could not find ${CONFIG_FILE}. Are you inside a Habeetat project directory?`
82
74
  );
83
75
  }
84
- const configPath = path__default.default.join(dir, CONFIG_FILE);
85
- const raw = fs2__default.default.readFileSync(configPath, "utf-8");
76
+ const configPath = path.join(dir, CONFIG_FILE);
77
+ const raw = fs2.readFileSync(configPath, "utf-8");
86
78
  return JSON.parse(raw);
87
79
  }
88
80
  function getProjectDir() {
@@ -96,7 +88,7 @@ function getProjectDir() {
96
88
  }
97
89
  async function checkDocker() {
98
90
  try {
99
- const { stdout } = await execa.execaCommand("docker --version");
91
+ const { stdout } = await execaCommand("docker --version");
100
92
  const match = stdout.match(/Docker version (\d+)/);
101
93
  if (match && parseInt(match[1], 10) >= 20) {
102
94
  return true;
@@ -111,7 +103,7 @@ async function checkDocker() {
111
103
  }
112
104
  async function checkDockerCompose() {
113
105
  try {
114
- await execa.execaCommand("docker compose version");
106
+ await execaCommand("docker compose version");
115
107
  return true;
116
108
  } catch {
117
109
  logger.error("Docker Compose V2 is not available");
@@ -126,28 +118,28 @@ async function checkPrerequisites() {
126
118
  }
127
119
  async function composeUp(opts) {
128
120
  const file = opts.composeFile || "docker-compose.yml";
129
- await execa.execaCommand(`docker compose -f ${file} up -d`, {
121
+ await execaCommand(`docker compose -f ${file} up -d`, {
130
122
  cwd: opts.cwd,
131
123
  stdio: opts.silent ? "pipe" : "inherit"
132
124
  });
133
125
  }
134
126
  async function composeDown(opts) {
135
127
  const file = opts.composeFile || "docker-compose.yml";
136
- await execa.execaCommand(`docker compose -f ${file} down`, {
128
+ await execaCommand(`docker compose -f ${file} down`, {
137
129
  cwd: opts.cwd,
138
130
  stdio: opts.silent ? "pipe" : "inherit"
139
131
  });
140
132
  }
141
133
  async function composeDestroy(opts) {
142
134
  const file = opts.composeFile || "docker-compose.yml";
143
- await execa.execaCommand(`docker compose -f ${file} down -v --remove-orphans`, {
135
+ await execaCommand(`docker compose -f ${file} down -v --remove-orphans`, {
144
136
  cwd: opts.cwd,
145
137
  stdio: opts.silent ? "pipe" : "inherit"
146
138
  });
147
139
  }
148
140
  async function composePs(opts) {
149
141
  const file = opts.composeFile || "docker-compose.yml";
150
- const { stdout } = await execa.execaCommand(`docker compose -f ${file} ps`, {
142
+ const { stdout } = await execaCommand(`docker compose -f ${file} ps`, {
151
143
  cwd: opts.cwd
152
144
  });
153
145
  return stdout;
@@ -158,7 +150,7 @@ async function composeLogs(opts) {
158
150
  if (opts.follow) parts.push("-f");
159
151
  if (opts.tail) parts.push("--tail", String(opts.tail));
160
152
  if (opts.service) parts.push(opts.service);
161
- await execa.execaCommand(parts.join(" "), {
153
+ await execaCommand(parts.join(" "), {
162
154
  cwd: opts.cwd,
163
155
  stdio: "inherit"
164
156
  });
@@ -166,25 +158,25 @@ async function composeLogs(opts) {
166
158
  async function composeRestart(opts) {
167
159
  const file = opts.composeFile || "docker-compose.yml";
168
160
  const cmd = opts.service ? `docker compose -f ${file} restart ${opts.service}` : `docker compose -f ${file} restart`;
169
- await execa.execaCommand(cmd, {
161
+ await execaCommand(cmd, {
170
162
  cwd: opts.cwd,
171
163
  stdio: opts.silent ? "pipe" : "inherit"
172
164
  });
173
165
  }
174
166
  var INITIALIZED_FILE = ".initialized";
175
167
  function isInitialized(projectDir) {
176
- return fs2__default.default.existsSync(path__default.default.join(projectDir, INITIALIZED_FILE));
168
+ return fs2.existsSync(path.join(projectDir, INITIALIZED_FILE));
177
169
  }
178
170
  function markInitialized(projectDir) {
179
- fs2__default.default.writeFileSync(
180
- path__default.default.join(projectDir, INITIALIZED_FILE),
171
+ fs2.writeFileSync(
172
+ path.join(projectDir, INITIALIZED_FILE),
181
173
  JSON.stringify({ initializedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
182
174
  );
183
175
  }
184
176
  function loadEnv(projectDir) {
185
- const envPath = path__default.default.join(projectDir, ".env");
186
- if (!fs2__default.default.existsSync(envPath)) return {};
187
- const lines = fs2__default.default.readFileSync(envPath, "utf-8").split("\n");
177
+ const envPath = path.join(projectDir, ".env");
178
+ if (!fs2.existsSync(envPath)) return {};
179
+ const lines = fs2.readFileSync(envPath, "utf-8").split("\n");
188
180
  const env = {};
189
181
  for (const line of lines) {
190
182
  const trimmed = line.trim();
@@ -196,7 +188,7 @@ function loadEnv(projectDir) {
196
188
  return env;
197
189
  }
198
190
  async function execCompose(projectDir, args) {
199
- const { stdout } = await execa.execaCommand(
191
+ const { stdout } = await execaCommand(
200
192
  `docker compose -f ${COMPOSE_FILE} ${args}`,
201
193
  { cwd: projectDir }
202
194
  );
@@ -216,7 +208,7 @@ async function waitForService(projectDir, service, checkCmd, timeoutSec) {
216
208
  }
217
209
  async function getContainerState(projectDir, service) {
218
210
  try {
219
- const { stdout } = await execa.execaCommand(
211
+ const { stdout } = await execaCommand(
220
212
  `docker compose -f ${COMPOSE_FILE} ps ${service} --format json`,
221
213
  { cwd: projectDir }
222
214
  );
@@ -599,7 +591,7 @@ async function initPlatform(projectDir) {
599
591
  const config = loadConfig(projectDir);
600
592
  const env = loadEnv(projectDir);
601
593
  const domain = config.platform.domain;
602
- const dbSpinner = ora__default.default("Waiting for databases...").start();
594
+ const dbSpinner = ora("Waiting for databases...").start();
603
595
  const logtoDbReady = await waitForService(
604
596
  projectDir,
605
597
  "logto-db",
@@ -621,7 +613,7 @@ async function initPlatform(projectDir) {
621
613
  throw new Error("platform-db timeout");
622
614
  }
623
615
  dbSpinner.succeed("Databases ready");
624
- const clickhouseSpinner = ora__default.default("Initializing ClickHouse analytics schema...").start();
616
+ const clickhouseSpinner = ora("Initializing ClickHouse analytics schema...").start();
625
617
  try {
626
618
  const clickhouseSql = [
627
619
  "CREATE DATABASE IF NOT EXISTS nhp_analytics",
@@ -630,7 +622,7 @@ async function initPlatform(projectDir) {
630
622
  "ALTER TABLE nhp_analytics.events ADD INDEX IF NOT EXISTS idx_session_id session_id TYPE bloom_filter(0.01) GRANULARITY 4"
631
623
  ];
632
624
  for (const sql of clickhouseSql) {
633
- await execa.execa("docker", [
625
+ await execa("docker", [
634
626
  "compose",
635
627
  "-f",
636
628
  COMPOSE_FILE,
@@ -648,7 +640,7 @@ async function initPlatform(projectDir) {
648
640
  logger.error(String(err));
649
641
  logger.info('Run manually: docker compose exec clickhouse clickhouse-client --query "CREATE DATABASE IF NOT EXISTS nhp_analytics"');
650
642
  }
651
- const logtoSpinner = ora__default.default("Waiting for Logto to be healthy (this may take up to 2 min)...").start();
643
+ const logtoSpinner = ora("Waiting for Logto to be healthy (this may take up to 2 min)...").start();
652
644
  const logtoReady = await waitForService(
653
645
  projectDir,
654
646
  "logto-core",
@@ -660,10 +652,10 @@ async function initPlatform(projectDir) {
660
652
  throw new Error("logto-core timeout");
661
653
  }
662
654
  logtoSpinner.succeed("Logto is healthy");
663
- const logtoSeedSpinner = ora__default.default("Seeding Logto applications...").start();
655
+ const logtoSeedSpinner = ora("Seeding Logto applications...").start();
664
656
  try {
665
657
  const sql = buildSeedLogtoAppsSql(env);
666
- await execa.execaCommand(
658
+ await execaCommand(
667
659
  `docker compose -f ${COMPOSE_FILE} exec -T logto-db psql -U logto -d logto`,
668
660
  { cwd: projectDir, input: sql }
669
661
  );
@@ -673,7 +665,7 @@ async function initPlatform(projectDir) {
673
665
  logger.error(String(err));
674
666
  throw err;
675
667
  }
676
- const bootstrapSpinner = ora__default.default("Bootstrapping Logto users and organizations...").start();
668
+ const bootstrapSpinner = ora("Bootstrapping Logto users and organizations...").start();
677
669
  try {
678
670
  const orgName = env.ORGANIZATION_NAME || config.platform.organization || "Default";
679
671
  const orgSlug = orgName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
@@ -681,10 +673,10 @@ async function initPlatform(projectDir) {
681
673
  const adminPassword = env.PLATFORM_ADMIN_PASSWORD || "Habeetat_01";
682
674
  const adminUsername = adminEmail.split("@")[0];
683
675
  const logtoDbUrl = `postgres://${env.LOGTO_DB_USER || "logto"}:${env.LOGTO_DB_PASSWORD || "logto"}@logto-db:5432/${env.LOGTO_DB_NAME || "logto"}`;
684
- const bootstrapScriptPath = path__default.default.join(projectDir, "scripts", "nhp-seed-logto-bootstrap.mjs");
685
- const hasLocalScript = fs2__default.default.existsSync(bootstrapScriptPath);
676
+ const bootstrapScriptPath = path.join(projectDir, "scripts", "nhp-seed-logto-bootstrap.mjs");
677
+ const hasLocalScript = fs2.existsSync(bootstrapScriptPath);
686
678
  if (hasLocalScript) {
687
- await execa.execa(
679
+ await execa(
688
680
  "docker",
689
681
  [
690
682
  "run",
@@ -726,14 +718,14 @@ async function initPlatform(projectDir) {
726
718
  logger.error(String(err));
727
719
  logger.info("You may need to create the admin user manually via the Logto admin console");
728
720
  }
729
- const backendRunSpinner = ora__default.default("Waiting for backend container to start...").start();
721
+ const backendRunSpinner = ora("Waiting for backend container to start...").start();
730
722
  const backendRunning = await waitForDockerRunning(projectDir, "backend", 90);
731
723
  if (!backendRunning) {
732
724
  backendRunSpinner.fail("Backend container did not start");
733
725
  throw new Error("backend container timeout");
734
726
  }
735
727
  backendRunSpinner.succeed("Backend container is running");
736
- const migrateSpinner = ora__default.default("Running database migrations...").start();
728
+ const migrateSpinner = ora("Running database migrations...").start();
737
729
  try {
738
730
  await execCompose(projectDir, "exec -T backend npx prisma migrate deploy");
739
731
  migrateSpinner.succeed("Database migrations completed");
@@ -742,12 +734,12 @@ async function initPlatform(projectDir) {
742
734
  logger.error(String(err));
743
735
  logger.info("Run manually: docker compose exec backend npx prisma migrate deploy");
744
736
  }
745
- const seedSpinner = ora__default.default("Seeding platform database...").start();
737
+ const seedSpinner = ora("Seeding platform database...").start();
746
738
  try {
747
739
  const orgName = env.ORGANIZATION_NAME || config.platform.organization || "Default";
748
740
  const orgSlug = orgName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
749
741
  const adminEmail = env.PLATFORM_ADMIN_EMAIL || config.platform.adminEmail;
750
- await execa.execa("docker", [
742
+ await execa("docker", [
751
743
  "compose",
752
744
  "-f",
753
745
  COMPOSE_FILE,
@@ -772,7 +764,7 @@ async function initPlatform(projectDir) {
772
764
  } catch {
773
765
  seedSpinner.warn("Prisma seed skipped (may not be available in Docker image)");
774
766
  }
775
- const backendSpinner = ora__default.default("Waiting for backend to be healthy...").start();
767
+ const backendSpinner = ora("Waiting for backend to be healthy...").start();
776
768
  const backendReady = await waitForDockerHealthy(
777
769
  projectDir,
778
770
  "backend",
@@ -784,10 +776,10 @@ async function initPlatform(projectDir) {
784
776
  } else {
785
777
  backendSpinner.succeed("Backend is healthy");
786
778
  }
787
- const platformSpinner = ora__default.default("Initializing platform data...").start();
779
+ const platformSpinner = ora("Initializing platform data...").start();
788
780
  try {
789
781
  const sql = buildInitPlatformSql(env);
790
- await execa.execaCommand(
782
+ await execaCommand(
791
783
  `docker compose -f ${COMPOSE_FILE} exec -T platform-db psql -U ${env.PLATFORM_DB_USER || "habeetat"} -d ${env.PLATFORM_DB_NAME || "habeetat_platform"}`,
792
784
  { cwd: projectDir, input: sql }
793
785
  );
@@ -797,13 +789,13 @@ async function initPlatform(projectDir) {
797
789
  logger.error(String(err));
798
790
  logger.info("You may need to initialize the platform manually after first login");
799
791
  }
800
- const analyticsSpinner = ora__default.default("Seeding analytics demo data...").start();
792
+ const analyticsSpinner = ora("Seeding analytics demo data...").start();
801
793
  try {
802
794
  const orgName = env.ORGANIZATION_NAME || config.platform.organization || "Default";
803
795
  const orgSlug = orgName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
804
796
  const adminUserId = "usr_plat_admin01";
805
797
  const chSql = buildClickhouseAnalyticsSeedSql(orgSlug, adminUserId);
806
- await execa.execa("docker", [
798
+ await execa("docker", [
807
799
  "compose",
808
800
  "-f",
809
801
  COMPOSE_FILE,
@@ -813,7 +805,7 @@ async function initPlatform(projectDir) {
813
805
  "clickhouse-client"
814
806
  ], { cwd: projectDir, input: chSql, stdio: ["pipe", "pipe", "pipe"] });
815
807
  const pgSql = buildAnalyticsObjectsSql(orgSlug, adminUserId);
816
- await execa.execaCommand(
808
+ await execaCommand(
817
809
  `docker compose -f ${COMPOSE_FILE} exec -T platform-db psql -U ${env.PLATFORM_DB_USER || "habeetat"} -d ${env.PLATFORM_DB_NAME || "habeetat_platform"}`,
818
810
  { cwd: projectDir, input: pgSql }
819
811
  );
@@ -823,7 +815,7 @@ async function initPlatform(projectDir) {
823
815
  logger.error(String(err));
824
816
  }
825
817
  if (config.services.signoz) {
826
- const signozUserSpinner = ora__default.default("Creating SigNoz admin user...").start();
818
+ const signozUserSpinner = ora("Creating SigNoz admin user...").start();
827
819
  try {
828
820
  const signozReady = await waitForDockerHealthy(projectDir, "signoz-query-service", 120);
829
821
  if (!signozReady) {
@@ -839,7 +831,7 @@ async function initPlatform(projectDir) {
839
831
  organizationName: orgName
840
832
  });
841
833
  try {
842
- await execa.execa("docker", [
834
+ await execa("docker", [
843
835
  "compose",
844
836
  "-f",
845
837
  COMPOSE_FILE,
@@ -879,7 +871,7 @@ async function upCommand() {
879
871
  process.exit(1);
880
872
  }
881
873
  const freshInstall = !isInitialized(projectDir);
882
- const spinner = ora__default.default("Starting services...").start();
874
+ const spinner = ora("Starting services...").start();
883
875
  try {
884
876
  await composeUp({ cwd: projectDir });
885
877
  spinner.succeed("All services started");
@@ -922,7 +914,7 @@ async function upCommand() {
922
914
  }
923
915
  async function downCommand() {
924
916
  const projectDir = getProjectDir();
925
- const spinner = ora__default.default("Stopping services...").start();
917
+ const spinner = ora("Stopping services...").start();
926
918
  try {
927
919
  await composeDown({ cwd: projectDir });
928
920
  spinner.succeed("All services stopped");
@@ -977,7 +969,7 @@ async function restartCommand(service) {
977
969
  process.exit(1);
978
970
  }
979
971
  const label = service || "all services";
980
- const spinner = ora__default.default(`Restarting ${label}...`).start();
972
+ const spinner = ora(`Restarting ${label}...`).start();
981
973
  try {
982
974
  await composeRestart({ cwd: projectDir, service });
983
975
  spinner.succeed(`Restarted ${label}`);
@@ -989,7 +981,7 @@ async function restartCommand(service) {
989
981
  }
990
982
  async function composePull(opts) {
991
983
  const cmd = opts.service ? `docker compose -f ${COMPOSE_FILE} pull ${opts.service}` : `docker compose -f ${COMPOSE_FILE} pull`;
992
- await execa.execaCommand(cmd, {
984
+ await execaCommand(cmd, {
993
985
  cwd: opts.cwd,
994
986
  stdio: "inherit"
995
987
  });
@@ -1002,7 +994,7 @@ async function updateCommand(service) {
1002
994
  process.exit(1);
1003
995
  }
1004
996
  const label = service ? `service '${service}'` : "all services";
1005
- const pullSpinner = ora__default.default(`Pulling latest images for ${label}...`).start();
997
+ const pullSpinner = ora(`Pulling latest images for ${label}...`).start();
1006
998
  try {
1007
999
  await composePull({ cwd: projectDir, service });
1008
1000
  pullSpinner.succeed("Images pulled");
@@ -1011,7 +1003,7 @@ async function updateCommand(service) {
1011
1003
  logger.error(String(err));
1012
1004
  process.exit(1);
1013
1005
  }
1014
- const upSpinner = ora__default.default("Restarting updated containers...").start();
1006
+ const upSpinner = ora("Restarting updated containers...").start();
1015
1007
  try {
1016
1008
  await composeUp({ cwd: projectDir });
1017
1009
  upSpinner.succeed("Platform updated and running");
@@ -1027,39 +1019,39 @@ async function doctorCommand() {
1027
1019
  logger.phase("Habeetat Doctor \u2014 Diagnostics");
1028
1020
  let issues = 0;
1029
1021
  const dockerOk = await checkDocker();
1030
- console.log(` Docker: ${dockerOk ? chalk3__default.default.green("\u2713 Installed") : chalk3__default.default.red("\u2717 Not found")}`);
1022
+ console.log(` Docker: ${dockerOk ? chalk3.green("\u2713 Installed") : chalk3.red("\u2717 Not found")}`);
1031
1023
  if (!dockerOk) issues++;
1032
1024
  const composeOk = await checkDockerCompose();
1033
- console.log(` Docker Compose: ${composeOk ? chalk3__default.default.green("\u2713 Available") : chalk3__default.default.red("\u2717 Not found")}`);
1025
+ console.log(` Docker Compose: ${composeOk ? chalk3.green("\u2713 Available") : chalk3.red("\u2717 Not found")}`);
1034
1026
  if (!composeOk) issues++;
1035
1027
  const projectDir = findProjectRoot();
1036
- console.log(` Project dir: ${projectDir ? chalk3__default.default.green(`\u2713 ${projectDir}`) : chalk3__default.default.yellow("\u26A0 Not in a Habeetat project")}`);
1028
+ console.log(` Project dir: ${projectDir ? chalk3.green(`\u2713 ${projectDir}`) : chalk3.yellow("\u26A0 Not in a Habeetat project")}`);
1037
1029
  if (!projectDir) {
1038
1030
  issues++;
1039
1031
  console.log("");
1040
1032
  logger.info(`Run \`npm create habeetat my-platform\` to create a new project`);
1041
1033
  return;
1042
1034
  }
1043
- const configExists = fs2__default.default.existsSync(path__default.default.join(projectDir, CONFIG_FILE));
1044
- console.log(` ${CONFIG_FILE}: ${configExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
1035
+ const configExists = fs2.existsSync(path.join(projectDir, CONFIG_FILE));
1036
+ console.log(` ${CONFIG_FILE}: ${configExists ? chalk3.green("\u2713 Found") : chalk3.red("\u2717 Missing")}`);
1045
1037
  if (!configExists) issues++;
1046
- const composeExists = fs2__default.default.existsSync(path__default.default.join(projectDir, COMPOSE_FILE));
1047
- console.log(` ${COMPOSE_FILE}: ${composeExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
1038
+ const composeExists = fs2.existsSync(path.join(projectDir, COMPOSE_FILE));
1039
+ console.log(` ${COMPOSE_FILE}: ${composeExists ? chalk3.green("\u2713 Found") : chalk3.red("\u2717 Missing")}`);
1048
1040
  if (!composeExists) issues++;
1049
- const envExists = fs2__default.default.existsSync(path__default.default.join(projectDir, ENV_FILE));
1050
- console.log(` ${ENV_FILE}: ${envExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
1041
+ const envExists = fs2.existsSync(path.join(projectDir, ENV_FILE));
1042
+ console.log(` ${ENV_FILE}: ${envExists ? chalk3.green("\u2713 Found") : chalk3.red("\u2717 Missing")}`);
1051
1043
  if (!envExists) issues++;
1052
- const nginxExists = fs2__default.default.existsSync(path__default.default.join(projectDir, "nginx", "platform.conf"));
1053
- console.log(` nginx/platform.conf: ${nginxExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
1044
+ const nginxExists = fs2.existsSync(path.join(projectDir, "nginx", "platform.conf"));
1045
+ console.log(` nginx/platform.conf: ${nginxExists ? chalk3.green("\u2713 Found") : chalk3.red("\u2717 Missing")}`);
1054
1046
  if (!nginxExists) issues++;
1055
1047
  if (configExists) {
1056
1048
  try {
1057
1049
  const config = loadConfig(projectDir);
1058
- console.log(` Version: ${chalk3__default.default.cyan(config.version)}`);
1059
- console.log(` Domain: ${chalk3__default.default.cyan(config.platform.domain)}`);
1060
- console.log(` Protocol: ${chalk3__default.default.cyan(config.platform.protocol)}`);
1050
+ console.log(` Version: ${chalk3.cyan(config.version)}`);
1051
+ console.log(` Domain: ${chalk3.cyan(config.platform.domain)}`);
1052
+ console.log(` Protocol: ${chalk3.cyan(config.platform.protocol)}`);
1061
1053
  } catch (err) {
1062
- console.log(` Config: ${chalk3__default.default.red("\u2717 Invalid JSON")}`);
1054
+ console.log(` Config: ${chalk3.red("\u2717 Invalid JSON")}`);
1063
1055
  issues++;
1064
1056
  }
1065
1057
  }
@@ -1071,7 +1063,7 @@ async function doctorCommand() {
1071
1063
  }
1072
1064
  }
1073
1065
  async function confirm(message) {
1074
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1066
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1075
1067
  return new Promise((resolve) => {
1076
1068
  rl.question(`${message} (y/N) `, (answer) => {
1077
1069
  rl.close();
@@ -1095,7 +1087,7 @@ async function destroyCommand(options) {
1095
1087
  return;
1096
1088
  }
1097
1089
  }
1098
- const spinner = ora__default.default("Destroying platform...").start();
1090
+ const spinner = ora("Destroying platform...").start();
1099
1091
  try {
1100
1092
  await composeDestroy({ cwd: projectDir });
1101
1093
  spinner.succeed("Platform destroyed \u2014 all containers and volumes removed");
@@ -1107,12 +1099,13 @@ async function destroyCommand(options) {
1107
1099
  process.exit(1);
1108
1100
  }
1109
1101
  }
1110
- var SEED_SCRIPT_PATH = path__default.default.resolve(__dirname, "scripts/nhp-seed-dev.ts");
1102
+ var __cli_dirname = path.dirname(fileURLToPath(import.meta.url));
1103
+ var SEED_SCRIPT_PATH = path.resolve(__cli_dirname, "scripts/nhp-seed-dev.ts");
1111
1104
  var SEED_SCRIPT_REMOTE = "/app/apps/habeetat-backend/nhp-seed-dev-tmp.ts";
1112
1105
  function loadEnv2(projectDir) {
1113
- const envPath = path__default.default.join(projectDir, ".env");
1114
- if (!fs2__default.default.existsSync(envPath)) return {};
1115
- const lines = fs2__default.default.readFileSync(envPath, "utf-8").split("\n");
1106
+ const envPath = path.join(projectDir, ".env");
1107
+ if (!fs2.existsSync(envPath)) return {};
1108
+ const lines = fs2.readFileSync(envPath, "utf-8").split("\n");
1116
1109
  const env = {};
1117
1110
  for (const line of lines) {
1118
1111
  const trimmed = line.trim();
@@ -1124,13 +1117,13 @@ function loadEnv2(projectDir) {
1124
1117
  return env;
1125
1118
  }
1126
1119
  async function runSeedScript(projectDir, command, args) {
1127
- if (!fs2__default.default.existsSync(SEED_SCRIPT_PATH)) {
1120
+ if (!fs2.existsSync(SEED_SCRIPT_PATH)) {
1128
1121
  throw new Error(
1129
1122
  `Seed script not found at ${SEED_SCRIPT_PATH}. This is a bug \u2014 please reinstall @habeetat/cli.`
1130
1123
  );
1131
1124
  }
1132
- const scriptContent = fs2__default.default.readFileSync(SEED_SCRIPT_PATH, "utf-8");
1133
- await execa.execa(
1125
+ const scriptContent = fs2.readFileSync(SEED_SCRIPT_PATH, "utf-8");
1126
+ await execa(
1134
1127
  "docker",
1135
1128
  [
1136
1129
  "compose",
@@ -1149,7 +1142,7 @@ async function runSeedScript(projectDir, command, args) {
1149
1142
  let stderr = "";
1150
1143
  try {
1151
1144
  const scriptFilename = SEED_SCRIPT_REMOTE.split("/").pop();
1152
- ({ stdout, stderr } = await execa.execa(
1145
+ ({ stdout, stderr } = await execa(
1153
1146
  "docker",
1154
1147
  [
1155
1148
  "compose",
@@ -1167,7 +1160,7 @@ async function runSeedScript(projectDir, command, args) {
1167
1160
  { cwd: projectDir, stdio: "pipe" }
1168
1161
  ));
1169
1162
  } finally {
1170
- await execa.execa(
1163
+ await execa(
1171
1164
  "docker",
1172
1165
  [
1173
1166
  "compose",
@@ -1199,49 +1192,49 @@ function printSdkConfig(config, env, parts) {
1199
1192
  const iamAudience = env.IAM_AUDIENCE || `${protocol}://api.${domain}/api`;
1200
1193
  const apiUrl = iamAudience.replace(/\/api$/, "");
1201
1194
  console.log("");
1202
- console.log(chalk3__default.default.cyan("\u2550".repeat(60)));
1203
- console.log(chalk3__default.default.cyan(" SDK Configuration"));
1204
- console.log(chalk3__default.default.cyan("\u2550".repeat(60)));
1195
+ console.log(chalk3.cyan("\u2550".repeat(60)));
1196
+ console.log(chalk3.cyan(" SDK Configuration"));
1197
+ console.log(chalk3.cyan("\u2550".repeat(60)));
1205
1198
  console.log("");
1206
1199
  if (parts.logtoAppId) {
1207
- console.log(chalk3__default.default.bold(" Frontend SDK (@habeetat/sdk-react)"));
1208
- console.log(chalk3__default.default.dim(" " + "\u2500".repeat(54)));
1209
- console.log(` VITE_LOGTO_ENDPOINT=${chalk3__default.default.green(iamUrl)}`);
1210
- console.log(` VITE_LOGTO_APP_ID=${chalk3__default.default.green(parts.logtoAppId)}`);
1211
- console.log(` VITE_LOGTO_API_RESOURCE=${chalk3__default.default.green(iamAudience)}`);
1200
+ console.log(chalk3.bold(" Frontend SDK (@habeetat/sdk-react)"));
1201
+ console.log(chalk3.dim(" " + "\u2500".repeat(54)));
1202
+ console.log(` VITE_LOGTO_ENDPOINT=${chalk3.green(iamUrl)}`);
1203
+ console.log(` VITE_LOGTO_APP_ID=${chalk3.green(parts.logtoAppId)}`);
1204
+ console.log(` VITE_LOGTO_API_RESOURCE=${chalk3.green(iamAudience)}`);
1212
1205
  if (parts.tenant) {
1213
- console.log(` VITE_APP_TENANT=${chalk3__default.default.green(parts.tenant)}`);
1206
+ console.log(` VITE_APP_TENANT=${chalk3.green(parts.tenant)}`);
1214
1207
  }
1215
1208
  console.log("");
1216
1209
  }
1217
1210
  if (parts.m2mClientId) {
1218
- console.log(chalk3__default.default.bold(" Backend SDK (@habeetat/sdk-node)"));
1219
- console.log(chalk3__default.default.dim(" " + "\u2500".repeat(54)));
1220
- console.log(` HABEETAT_ENDPOINT=${chalk3__default.default.green(apiUrl)}`);
1221
- console.log(` HABEETAT_LOGTO_ENDPOINT=${chalk3__default.default.green(iamUrl)}`);
1222
- console.log(` HABEETAT_IAM_AUDIENCE=${chalk3__default.default.green(iamAudience)}`);
1211
+ console.log(chalk3.bold(" Backend SDK (@habeetat/sdk-node)"));
1212
+ console.log(chalk3.dim(" " + "\u2500".repeat(54)));
1213
+ console.log(` HABEETAT_ENDPOINT=${chalk3.green(apiUrl)}`);
1214
+ console.log(` HABEETAT_LOGTO_ENDPOINT=${chalk3.green(iamUrl)}`);
1215
+ console.log(` HABEETAT_IAM_AUDIENCE=${chalk3.green(iamAudience)}`);
1223
1216
  if (parts.tenant) {
1224
- console.log(` HABEETAT_TENANT_SLUG=${chalk3__default.default.green(parts.tenant)}`);
1217
+ console.log(` HABEETAT_TENANT_SLUG=${chalk3.green(parts.tenant)}`);
1225
1218
  }
1226
- console.log(` HABEETAT_M2M_CLIENT_ID=${chalk3__default.default.green(parts.m2mClientId)}`);
1227
- console.log(` HABEETAT_M2M_CLIENT_SECRET=${chalk3__default.default.green(parts.m2mClientSecret ?? "")}`);
1219
+ console.log(` HABEETAT_M2M_CLIENT_ID=${chalk3.green(parts.m2mClientId)}`);
1220
+ console.log(` HABEETAT_M2M_CLIENT_SECRET=${chalk3.green(parts.m2mClientSecret ?? "")}`);
1228
1221
  console.log("");
1229
1222
  } else if (parts.tenant && !parts.logtoAppId) {
1230
- console.log(chalk3__default.default.bold(" Backend SDK (@habeetat/sdk-node)"));
1231
- console.log(chalk3__default.default.dim(" " + "\u2500".repeat(54)));
1232
- console.log(` HABEETAT_ENDPOINT=${chalk3__default.default.green(apiUrl)}`);
1233
- console.log(` HABEETAT_TENANT_SLUG=${chalk3__default.default.green(parts.tenant)}`);
1223
+ console.log(chalk3.bold(" Backend SDK (@habeetat/sdk-node)"));
1224
+ console.log(chalk3.dim(" " + "\u2500".repeat(54)));
1225
+ console.log(` HABEETAT_ENDPOINT=${chalk3.green(apiUrl)}`);
1226
+ console.log(` HABEETAT_TENANT_SLUG=${chalk3.green(parts.tenant)}`);
1234
1227
  console.log("");
1235
1228
  }
1236
1229
  if (parts.email && parts.password) {
1237
- console.log(chalk3__default.default.bold(" Test credentials"));
1238
- console.log(chalk3__default.default.dim(" " + "\u2500".repeat(54)));
1239
- console.log(` Email: ${chalk3__default.default.green(parts.email)}`);
1240
- console.log(` Password: ${chalk3__default.default.green(parts.password)}`);
1241
- console.log(` IAM: ${chalk3__default.default.green(iamUrl)}`);
1230
+ console.log(chalk3.bold(" Test credentials"));
1231
+ console.log(chalk3.dim(" " + "\u2500".repeat(54)));
1232
+ console.log(` Email: ${chalk3.green(parts.email)}`);
1233
+ console.log(` Password: ${chalk3.green(parts.password)}`);
1234
+ console.log(` IAM: ${chalk3.green(iamUrl)}`);
1242
1235
  console.log("");
1243
1236
  }
1244
- console.log(chalk3__default.default.cyan("\u2550".repeat(60)));
1237
+ console.log(chalk3.cyan("\u2550".repeat(60)));
1245
1238
  console.log("");
1246
1239
  }
1247
1240
  function validateSlug(slug, entity) {
@@ -1264,11 +1257,11 @@ async function seedTenantCommand(options) {
1264
1257
  options.slug,
1265
1258
  ...options.description ? ["--description", options.description] : []
1266
1259
  ];
1267
- const spinner = ora__default.default(`Creating tenant "${options.slug}"...`).start();
1260
+ const spinner = ora(`Creating tenant "${options.slug}"...`).start();
1268
1261
  let result;
1269
1262
  try {
1270
1263
  result = await runSeedScript(projectDir, "create-tenant", args);
1271
- spinner.succeed(`Tenant created: ${chalk3__default.default.bold(result.slug)} (${result.displayName})`);
1264
+ spinner.succeed(`Tenant created: ${chalk3.bold(result.slug)} (${result.displayName})`);
1272
1265
  } catch (err) {
1273
1266
  spinner.fail("Failed to create tenant");
1274
1267
  logger.error(String(err));
@@ -1293,13 +1286,14 @@ async function seedAppCommand(options) {
1293
1286
  ...options.redirectUri?.flatMap((u) => ["--redirect-uri", u]) ?? [],
1294
1287
  ...options.logoutUri?.flatMap((u) => ["--logout-uri", u]) ?? [],
1295
1288
  ...options.corsOrigin?.flatMap((o) => ["--cors-origin", o]) ?? [],
1296
- ...options.internal ? ["--internal"] : []
1289
+ ...options.internal ? ["--internal"] : [],
1290
+ ...options.noM2m ? ["--no-m2m"] : []
1297
1291
  ];
1298
- const spinner = ora__default.default(`Creating app "${options.slug}"...`).start();
1292
+ const spinner = ora(`Creating app "${options.slug}"...`).start();
1299
1293
  let result;
1300
1294
  try {
1301
1295
  result = await runSeedScript(projectDir, "create-app", args);
1302
- spinner.succeed(`App created: ${chalk3__default.default.bold(result.slug)} (Logto app: ${result.logtoAppId})`);
1296
+ spinner.succeed(`App created: ${chalk3.bold(result.slug)} (Logto app: ${result.logtoAppId})`);
1303
1297
  } catch (err) {
1304
1298
  spinner.fail("Failed to create app");
1305
1299
  logger.error(String(err));
@@ -1333,11 +1327,11 @@ async function seedUpdateAppCommand(options) {
1333
1327
  ...options.logoutUri?.flatMap((u) => ["--logout-uri", u]) ?? [],
1334
1328
  ...options.corsOrigin?.flatMap((o) => ["--cors-origin", o]) ?? []
1335
1329
  ];
1336
- const spinner = ora__default.default(`Updating app "${options.slug}"...`).start();
1330
+ const spinner = ora(`Updating app "${options.slug}"...`).start();
1337
1331
  let result;
1338
1332
  try {
1339
1333
  result = await runSeedScript(projectDir, "update-app", args);
1340
- spinner.succeed(`App updated: ${chalk3__default.default.bold(result.slug)}`);
1334
+ spinner.succeed(`App updated: ${chalk3.bold(result.slug)}`);
1341
1335
  } catch (err) {
1342
1336
  spinner.fail("Failed to update app");
1343
1337
  logger.error(String(err));
@@ -1368,11 +1362,11 @@ async function seedUserCommand(options) {
1368
1362
  ...options.displayName ? ["--display-name", options.displayName] : [],
1369
1363
  ...options.role ? ["--role", options.role] : []
1370
1364
  ];
1371
- const spinner = ora__default.default(`Creating user "${options.email}" in tenant "${options.tenant}"...`).start();
1365
+ const spinner = ora(`Creating user "${options.email}" in tenant "${options.tenant}"...`).start();
1372
1366
  let result;
1373
1367
  try {
1374
1368
  result = await runSeedScript(projectDir, "create-user", args);
1375
- spinner.succeed(`User created: ${chalk3__default.default.bold(result.email)} (Logto ID: ${result.logtoUserId})`);
1369
+ spinner.succeed(`User created: ${chalk3.bold(result.email)} (Logto ID: ${result.logtoUserId})`);
1376
1370
  } catch (err) {
1377
1371
  spinner.fail("Failed to create user");
1378
1372
  logger.error(String(err));
@@ -1394,9 +1388,9 @@ function registerSeedCommand(program2) {
1394
1388
  seed.command("tenant").description("Create a new tenant (Logto org + platform DB)").requiredOption("--name <name>", 'Display name (e.g. "Acme Corp")').requiredOption("--slug <slug>", "Unique slug, lowercase alphanumeric + hyphens (e.g. acme)").option("--description <desc>", "Optional description").action((opts) => {
1395
1389
  return seedTenantCommand(opts);
1396
1390
  });
1397
- seed.command("app").description("Create a new OIDC application (Logto SPA app + platform DB)").requiredOption("--name <name>", 'App display name (e.g. "My CRM")').requiredOption("--slug <slug>", "Unique slug, lowercase alphanumeric + hyphens (e.g. my-crm)").option("--redirect-uri <uri...>", "OIDC redirect URI(s) (e.g. http://localhost:3000/callback)").option("--logout-uri <uri...>", "Post-logout redirect URI(s)").option("--cors-origin <origin...>", "CORS allowed origin(s)").option("--description <desc>", "Optional description").option("--tenant <slug>", "Install app on this tenant after creation").option("--internal", "Mark app as internal/system app (default: false)").action(
1391
+ seed.command("app").description("Create a new OIDC application (Logto SPA app + platform DB)").requiredOption("--name <name>", 'App display name (e.g. "My CRM")').requiredOption("--slug <slug>", "Unique slug, lowercase alphanumeric + hyphens (e.g. my-crm)").option("--redirect-uri <uri...>", "OIDC redirect URI(s) (e.g. http://localhost:3000/callback)").option("--logout-uri <uri...>", "Post-logout redirect URI(s)").option("--cors-origin <origin...>", "CORS allowed origin(s)").option("--description <desc>", "Optional description").option("--tenant <slug>", "Install app on this tenant after creation").option("--internal", "Mark app as internal/system app (default: false)").option("--no-m2m", "Skip M2M Logto application creation (frontend-only apps)").action(
1398
1392
  (opts) => {
1399
- return seedAppCommand(opts);
1393
+ return seedAppCommand({ ...opts, noM2m: opts.m2m === false });
1400
1394
  }
1401
1395
  );
1402
1396
  seed.command("update-app").description("Update an existing app's OIDC URIs and CORS origins").requiredOption("--slug <slug>", "App slug to update").option("--redirect-uri <uri...>", "Replace OIDC redirect URI(s)").option("--logout-uri <uri...>", "Replace post-logout redirect URI(s)").option("--cors-origin <origin...>", "Replace CORS allowed origin(s)").action(
@@ -1412,10 +1406,11 @@ function registerSeedCommand(program2) {
1412
1406
  }
1413
1407
 
1414
1408
  // src/bin.ts
1409
+ var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
1415
1410
  var pkg = JSON.parse(
1416
- fs2.readFileSync(path__default.default.resolve(__dirname, "..", "package.json"), "utf-8")
1411
+ readFileSync(path.resolve(__dirname$1, "..", "package.json"), "utf-8")
1417
1412
  );
1418
- var program = new commander.Command();
1413
+ var program = new Command();
1419
1414
  program.name("habeetat").description("Habeetat Platform CLI \u2014 manage your platform instances").version(pkg.version);
1420
1415
  program.command("up").description("Start the platform").action(upCommand);
1421
1416
  program.command("down").description("Stop the platform").action(downCommand);
@@ -1434,5 +1429,5 @@ program.command("init").description("Initialize platform (seed Logto apps, creat
1434
1429
  });
1435
1430
  registerSeedCommand(program);
1436
1431
  program.parse();
1437
- //# sourceMappingURL=bin.js.map
1438
- //# sourceMappingURL=bin.js.map
1432
+ //# sourceMappingURL=bin.mjs.map
1433
+ //# sourceMappingURL=bin.mjs.map