@habeetat/cli 0.1.0-dev.20260324164706.1f6ff3c → 0.1.0-dev.20260324202418.70eccf7
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/bin.js +220 -25
- package/dist/bin.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -5,7 +5,7 @@ var commander = require('commander');
|
|
|
5
5
|
var fs2 = require('fs');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
var ora = require('ora');
|
|
8
|
-
var
|
|
8
|
+
var chalk3 = require('chalk');
|
|
9
9
|
var execa = require('execa');
|
|
10
10
|
var readline = require('readline');
|
|
11
11
|
|
|
@@ -14,28 +14,28 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
14
14
|
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
15
15
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
16
16
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
17
|
-
var
|
|
17
|
+
var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
|
|
18
18
|
|
|
19
|
-
var prefix =
|
|
19
|
+
var prefix = chalk3__default.default.cyan("[habeetat]");
|
|
20
20
|
var logger = {
|
|
21
21
|
info: (msg) => console.log(`${prefix} ${msg}`),
|
|
22
|
-
success: (msg) => console.log(`${prefix} ${
|
|
23
|
-
warn: (msg) => console.log(`${prefix} ${
|
|
24
|
-
error: (msg) => console.error(`${prefix} ${
|
|
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}`),
|
|
25
25
|
phase: (msg) => {
|
|
26
26
|
console.log("");
|
|
27
|
-
console.log(
|
|
28
|
-
console.log(
|
|
29
|
-
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)));
|
|
30
30
|
console.log("");
|
|
31
31
|
},
|
|
32
32
|
banner: () => {
|
|
33
33
|
console.log("");
|
|
34
|
-
console.log(
|
|
35
|
-
console.log(
|
|
36
|
-
console.log(
|
|
37
|
-
console.log(
|
|
38
|
-
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"));
|
|
39
39
|
console.log("");
|
|
40
40
|
}
|
|
41
41
|
};
|
|
@@ -746,13 +746,13 @@ async function doctorCommand() {
|
|
|
746
746
|
logger.phase("Habeetat Doctor \u2014 Diagnostics");
|
|
747
747
|
let issues = 0;
|
|
748
748
|
const dockerOk = await checkDocker();
|
|
749
|
-
console.log(` Docker: ${dockerOk ?
|
|
749
|
+
console.log(` Docker: ${dockerOk ? chalk3__default.default.green("\u2713 Installed") : chalk3__default.default.red("\u2717 Not found")}`);
|
|
750
750
|
if (!dockerOk) issues++;
|
|
751
751
|
const composeOk = await checkDockerCompose();
|
|
752
|
-
console.log(` Docker Compose: ${composeOk ?
|
|
752
|
+
console.log(` Docker Compose: ${composeOk ? chalk3__default.default.green("\u2713 Available") : chalk3__default.default.red("\u2717 Not found")}`);
|
|
753
753
|
if (!composeOk) issues++;
|
|
754
754
|
const projectDir = findProjectRoot();
|
|
755
|
-
console.log(` Project dir: ${projectDir ?
|
|
755
|
+
console.log(` Project dir: ${projectDir ? chalk3__default.default.green(`\u2713 ${projectDir}`) : chalk3__default.default.yellow("\u26A0 Not in a Habeetat project")}`);
|
|
756
756
|
if (!projectDir) {
|
|
757
757
|
issues++;
|
|
758
758
|
console.log("");
|
|
@@ -760,25 +760,25 @@ async function doctorCommand() {
|
|
|
760
760
|
return;
|
|
761
761
|
}
|
|
762
762
|
const configExists = fs2__default.default.existsSync(path__default.default.join(projectDir, CONFIG_FILE));
|
|
763
|
-
console.log(` ${CONFIG_FILE}: ${configExists ?
|
|
763
|
+
console.log(` ${CONFIG_FILE}: ${configExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
|
|
764
764
|
if (!configExists) issues++;
|
|
765
765
|
const composeExists = fs2__default.default.existsSync(path__default.default.join(projectDir, COMPOSE_FILE));
|
|
766
|
-
console.log(` ${COMPOSE_FILE}: ${composeExists ?
|
|
766
|
+
console.log(` ${COMPOSE_FILE}: ${composeExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
|
|
767
767
|
if (!composeExists) issues++;
|
|
768
768
|
const envExists = fs2__default.default.existsSync(path__default.default.join(projectDir, ENV_FILE));
|
|
769
|
-
console.log(` ${ENV_FILE}: ${envExists ?
|
|
769
|
+
console.log(` ${ENV_FILE}: ${envExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
|
|
770
770
|
if (!envExists) issues++;
|
|
771
771
|
const nginxExists = fs2__default.default.existsSync(path__default.default.join(projectDir, "nginx", "platform.conf"));
|
|
772
|
-
console.log(` nginx/platform.conf: ${nginxExists ?
|
|
772
|
+
console.log(` nginx/platform.conf: ${nginxExists ? chalk3__default.default.green("\u2713 Found") : chalk3__default.default.red("\u2717 Missing")}`);
|
|
773
773
|
if (!nginxExists) issues++;
|
|
774
774
|
if (configExists) {
|
|
775
775
|
try {
|
|
776
776
|
const config = loadConfig(projectDir);
|
|
777
|
-
console.log(` Version: ${
|
|
778
|
-
console.log(` Domain: ${
|
|
779
|
-
console.log(` Protocol: ${
|
|
777
|
+
console.log(` Version: ${chalk3__default.default.cyan(config.version)}`);
|
|
778
|
+
console.log(` Domain: ${chalk3__default.default.cyan(config.platform.domain)}`);
|
|
779
|
+
console.log(` Protocol: ${chalk3__default.default.cyan(config.platform.protocol)}`);
|
|
780
780
|
} catch (err) {
|
|
781
|
-
console.log(` Config: ${
|
|
781
|
+
console.log(` Config: ${chalk3__default.default.red("\u2717 Invalid JSON")}`);
|
|
782
782
|
issues++;
|
|
783
783
|
}
|
|
784
784
|
}
|
|
@@ -826,6 +826,200 @@ async function destroyCommand(options) {
|
|
|
826
826
|
process.exit(1);
|
|
827
827
|
}
|
|
828
828
|
}
|
|
829
|
+
function loadEnv2(projectDir) {
|
|
830
|
+
const envPath = path__default.default.join(projectDir, ".env");
|
|
831
|
+
if (!fs2__default.default.existsSync(envPath)) return {};
|
|
832
|
+
const lines = fs2__default.default.readFileSync(envPath, "utf-8").split("\n");
|
|
833
|
+
const env = {};
|
|
834
|
+
for (const line of lines) {
|
|
835
|
+
const trimmed = line.trim();
|
|
836
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
837
|
+
const eqIdx = trimmed.indexOf("=");
|
|
838
|
+
if (eqIdx === -1) continue;
|
|
839
|
+
env[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
|
|
840
|
+
}
|
|
841
|
+
return env;
|
|
842
|
+
}
|
|
843
|
+
async function runSeedScript(projectDir, command, args) {
|
|
844
|
+
const { stdout, stderr } = await execa.execa(
|
|
845
|
+
"docker",
|
|
846
|
+
[
|
|
847
|
+
"compose",
|
|
848
|
+
"-f",
|
|
849
|
+
COMPOSE_FILE,
|
|
850
|
+
"exec",
|
|
851
|
+
"-T",
|
|
852
|
+
"backend",
|
|
853
|
+
"npx",
|
|
854
|
+
"tsx",
|
|
855
|
+
"scripts/nhp-seed-dev.ts",
|
|
856
|
+
command,
|
|
857
|
+
...args
|
|
858
|
+
],
|
|
859
|
+
{ cwd: projectDir, stdio: "pipe" }
|
|
860
|
+
);
|
|
861
|
+
if (stderr) process.stderr.write(stderr + "\n");
|
|
862
|
+
const jsonLine = stdout.trim().split("\n").pop() ?? "";
|
|
863
|
+
try {
|
|
864
|
+
return JSON.parse(jsonLine);
|
|
865
|
+
} catch {
|
|
866
|
+
throw new Error(`Script produced unexpected output: ${stdout}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function printSdkConfig(config, env, parts) {
|
|
870
|
+
const protocol = config.protocol || "https";
|
|
871
|
+
const domain = config.domain;
|
|
872
|
+
const iamUrl = env.LOGTO_EXTERNAL_ENDPOINT || `${protocol}://iam.${domain}`;
|
|
873
|
+
const apiUrl = env.IAM_AUDIENCE ? env.IAM_AUDIENCE.replace(/\/api$/, "") : `${protocol}://api.${domain}`;
|
|
874
|
+
console.log("");
|
|
875
|
+
console.log(chalk3__default.default.cyan("\u2550".repeat(56)));
|
|
876
|
+
console.log(chalk3__default.default.cyan(" SDK Configuration"));
|
|
877
|
+
console.log(chalk3__default.default.cyan("\u2550".repeat(56)));
|
|
878
|
+
console.log("");
|
|
879
|
+
if (parts.logtoAppId) {
|
|
880
|
+
console.log(chalk3__default.default.bold(" Frontend SDK (@logto/react + @habeetat/sdk-frontend)"));
|
|
881
|
+
console.log(chalk3__default.default.dim(" " + "\u2500".repeat(50)));
|
|
882
|
+
console.log(` VITE_LOGTO_ENDPOINT=${chalk3__default.default.green(iamUrl)}`);
|
|
883
|
+
console.log(` VITE_LOGTO_APP_ID=${chalk3__default.default.green(parts.logtoAppId)}`);
|
|
884
|
+
console.log(` VITE_LOGTO_API_RESOURCE=${chalk3__default.default.green(env.IAM_AUDIENCE || `${protocol}://api.${domain}/api`)}`);
|
|
885
|
+
if (parts.tenant) {
|
|
886
|
+
console.log(` VITE_APP_TENANT=${chalk3__default.default.green(parts.tenant)}`);
|
|
887
|
+
}
|
|
888
|
+
console.log("");
|
|
889
|
+
}
|
|
890
|
+
if (parts.tenant) {
|
|
891
|
+
console.log(chalk3__default.default.bold(" Backend SDK (@habeetat/sdk-backend)"));
|
|
892
|
+
console.log(chalk3__default.default.dim(" " + "\u2500".repeat(50)));
|
|
893
|
+
console.log(` HABEETAT_ENDPOINT=${chalk3__default.default.green(apiUrl)}`);
|
|
894
|
+
console.log(` HABEETAT_TENANT_SLUG=${chalk3__default.default.green(parts.tenant)}`);
|
|
895
|
+
console.log("");
|
|
896
|
+
}
|
|
897
|
+
if (parts.email && parts.password) {
|
|
898
|
+
console.log(chalk3__default.default.bold(" Test credentials"));
|
|
899
|
+
console.log(chalk3__default.default.dim(" " + "\u2500".repeat(50)));
|
|
900
|
+
console.log(` Email: ${chalk3__default.default.green(parts.email)}`);
|
|
901
|
+
console.log(` Password: ${chalk3__default.default.green(parts.password)}`);
|
|
902
|
+
console.log(` IAM: ${chalk3__default.default.green(iamUrl)}`);
|
|
903
|
+
console.log("");
|
|
904
|
+
}
|
|
905
|
+
console.log(chalk3__default.default.cyan("\u2550".repeat(56)));
|
|
906
|
+
console.log("");
|
|
907
|
+
}
|
|
908
|
+
async function seedTenantCommand(options) {
|
|
909
|
+
const projectDir = getProjectDir();
|
|
910
|
+
const config = loadConfig(projectDir);
|
|
911
|
+
const env = loadEnv2(projectDir);
|
|
912
|
+
const args = [
|
|
913
|
+
"--name",
|
|
914
|
+
options.name,
|
|
915
|
+
"--slug",
|
|
916
|
+
options.slug,
|
|
917
|
+
...options.description ? ["--description", options.description] : []
|
|
918
|
+
];
|
|
919
|
+
const spinner = ora__default.default(`Creating tenant "${options.slug}"...`).start();
|
|
920
|
+
let result;
|
|
921
|
+
try {
|
|
922
|
+
result = await runSeedScript(projectDir, "create-tenant", args);
|
|
923
|
+
spinner.succeed(`Tenant created: ${chalk3__default.default.bold(result.slug)} (${result.displayName})`);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
spinner.fail("Failed to create tenant");
|
|
926
|
+
logger.error(String(err));
|
|
927
|
+
process.exit(1);
|
|
928
|
+
}
|
|
929
|
+
logger.info(` ID: ${result.id}`);
|
|
930
|
+
logger.info(` Logto Org ID: ${result.logtoOrganizationId}`);
|
|
931
|
+
printSdkConfig(config.platform, env, { tenant: result.slug });
|
|
932
|
+
}
|
|
933
|
+
async function seedAppCommand(options) {
|
|
934
|
+
const projectDir = getProjectDir();
|
|
935
|
+
const config = loadConfig(projectDir);
|
|
936
|
+
const env = loadEnv2(projectDir);
|
|
937
|
+
const args = [
|
|
938
|
+
"--name",
|
|
939
|
+
options.name,
|
|
940
|
+
"--slug",
|
|
941
|
+
options.slug,
|
|
942
|
+
...options.description ? ["--description", options.description] : [],
|
|
943
|
+
...options.tenant ? ["--tenant", options.tenant] : [],
|
|
944
|
+
...options.redirectUri?.flatMap((u) => ["--redirect-uri", u]) ?? [],
|
|
945
|
+
...options.logoutUri?.flatMap((u) => ["--logout-uri", u]) ?? [],
|
|
946
|
+
...options.corsOrigin?.flatMap((o) => ["--cors-origin", o]) ?? []
|
|
947
|
+
];
|
|
948
|
+
const spinner = ora__default.default(`Creating app "${options.slug}"...`).start();
|
|
949
|
+
let result;
|
|
950
|
+
try {
|
|
951
|
+
result = await runSeedScript(projectDir, "create-app", args);
|
|
952
|
+
spinner.succeed(`App created: ${chalk3__default.default.bold(result.slug)} (Logto app: ${result.logtoAppId})`);
|
|
953
|
+
} catch (err) {
|
|
954
|
+
spinner.fail("Failed to create app");
|
|
955
|
+
logger.error(String(err));
|
|
956
|
+
process.exit(1);
|
|
957
|
+
}
|
|
958
|
+
logger.info(` ID: ${result.id}`);
|
|
959
|
+
logger.info(` Logto App ID: ${result.logtoAppId}`);
|
|
960
|
+
if (result.redirectUris && result.redirectUris.length > 0) {
|
|
961
|
+
logger.info(` Redirect URIs: ${result.redirectUris.join(", ")}`);
|
|
962
|
+
}
|
|
963
|
+
if (result.tenantSlug) {
|
|
964
|
+
logger.info(` Installed on: ${result.tenantSlug}`);
|
|
965
|
+
}
|
|
966
|
+
printSdkConfig(config.platform, env, {
|
|
967
|
+
tenant: result.tenantSlug || options.tenant,
|
|
968
|
+
logtoAppId: result.logtoAppId
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
async function seedUserCommand(options) {
|
|
972
|
+
const projectDir = getProjectDir();
|
|
973
|
+
const config = loadConfig(projectDir);
|
|
974
|
+
const env = loadEnv2(projectDir);
|
|
975
|
+
const args = [
|
|
976
|
+
"--email",
|
|
977
|
+
options.email,
|
|
978
|
+
"--password",
|
|
979
|
+
options.password,
|
|
980
|
+
"--tenant",
|
|
981
|
+
options.tenant,
|
|
982
|
+
...options.username ? ["--username", options.username] : [],
|
|
983
|
+
...options.displayName ? ["--display-name", options.displayName] : [],
|
|
984
|
+
...options.role ? ["--role", options.role] : []
|
|
985
|
+
];
|
|
986
|
+
const spinner = ora__default.default(`Creating user "${options.email}" in tenant "${options.tenant}"...`).start();
|
|
987
|
+
let result;
|
|
988
|
+
try {
|
|
989
|
+
result = await runSeedScript(projectDir, "create-user", args);
|
|
990
|
+
spinner.succeed(`User created: ${chalk3__default.default.bold(result.email)} (Logto ID: ${result.logtoUserId})`);
|
|
991
|
+
} catch (err) {
|
|
992
|
+
spinner.fail("Failed to create user");
|
|
993
|
+
logger.error(String(err));
|
|
994
|
+
process.exit(1);
|
|
995
|
+
}
|
|
996
|
+
logger.info(` Logto User ID: ${result.logtoUserId}`);
|
|
997
|
+
logger.info(` Username: ${result.username}`);
|
|
998
|
+
logger.info(` Tenant: ${result.tenantSlug}`);
|
|
999
|
+
logger.info(` Role: ${result.role}${result.roleAssigned ? "" : " (not assigned \u2014 role not found in Logto)"}`);
|
|
1000
|
+
if (result.note) logger.info(` Note: ${result.note}`);
|
|
1001
|
+
printSdkConfig(config.platform, env, {
|
|
1002
|
+
tenant: result.tenantSlug,
|
|
1003
|
+
email: result.email,
|
|
1004
|
+
password: result.password
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
function registerSeedCommand(program2) {
|
|
1008
|
+
const seed = program2.command("seed").description("Seed dev data \u2014 create tenants, apps and users for local development");
|
|
1009
|
+
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) => {
|
|
1010
|
+
return seedTenantCommand(opts);
|
|
1011
|
+
});
|
|
1012
|
+
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").action(
|
|
1013
|
+
(opts) => {
|
|
1014
|
+
return seedAppCommand(opts);
|
|
1015
|
+
}
|
|
1016
|
+
);
|
|
1017
|
+
seed.command("user").description("Create a new user in Logto and add to a tenant organization").requiredOption("--email <email>", "User email address").requiredOption("--password <password>", "User password").requiredOption("--tenant <slug>", "Tenant slug to add the user to").option("--username <username>", "Logto username (defaults to email prefix)").option("--display-name <name>", "Display name (defaults to username)").option("--role <role>", "Organization role to assign (default: TENANT_ADMIN)", "TENANT_ADMIN").action(
|
|
1018
|
+
(opts) => {
|
|
1019
|
+
return seedUserCommand(opts);
|
|
1020
|
+
}
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
829
1023
|
|
|
830
1024
|
// src/bin.ts
|
|
831
1025
|
var pkg = JSON.parse(
|
|
@@ -847,6 +1041,7 @@ program.command("destroy").description("Destroy the platform \u2014 remove all c
|
|
|
847
1041
|
program.command("init").description("Initialize platform (seed Logto apps, create admin user, setup database)").action(() => {
|
|
848
1042
|
return initPlatform(getProjectDir());
|
|
849
1043
|
});
|
|
1044
|
+
registerSeedCommand(program);
|
|
850
1045
|
program.parse();
|
|
851
1046
|
//# sourceMappingURL=bin.js.map
|
|
852
1047
|
//# sourceMappingURL=bin.js.map
|