@helyx/cli 0.1.1 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3 - 2026-07-23
4
+
5
+ - Add safe first-run environment setup, local `.env` loading and clearer Node.js, Docker Compose and managed-host guidance.
6
+
7
+ ## 0.1.2 - 2026-07-23
8
+
9
+ - Identify the included Helyx Source Available Licence as `HSAL 1.0` and align public core dependencies.
10
+
3
11
  ## 0.1.1 - 2026-07-23
4
12
 
5
13
  - Run npm through its JavaScript entry point when available so starter generation works reliably on supported Windows and Node.js versions.
package/README.md CHANGED
@@ -3,13 +3,14 @@
3
3
  The supported Helyx command-line entry point for running and generating self-hosted deployments.
4
4
 
5
5
  ```text
6
+ helyx setup
6
7
  helyx start
7
8
  helyx migrate
8
9
  helyx doctor
9
10
  helyx self-host init --manifest ./helyx-build.json --output ./helyx
10
11
  ```
11
12
 
12
- `start` performs the production bot bootstrap, including database and installed-module migrations. `migrate` applies only core migrations and exits. `doctor` validates the complete bot environment, PostgreSQL connectivity, immutable core migration history and configured module package metadata without logging in to Discord or modifying the database.
13
+ `setup` safely creates `.env` from a generated starter's `.env.example`, never overwrites existing configuration and reports whether Docker Compose is available. Direct Node.js operation remains fully supported with a local or hosted PostgreSQL database. `start` loads local configuration without overriding hosting-platform variables and performs the production bot bootstrap, including database and installed-module migrations. `migrate` applies only core migrations and exits. `doctor` validates the complete bot environment, PostgreSQL connectivity, immutable core migration history and configured module package metadata without logging in to Discord or modifying the database.
13
14
 
14
15
  `self-host init` validates a build manifest against the reviewed catalogue shipped with this exact CLI version, creates a new starter-project directory, pins the runtime and selected official modules, generates a lockfile with lifecycle scripts disabled, and writes checksums. Add `--install` only when the dependencies should also be installed locally. Unknown, unpublished, duplicated or modified package selections fail closed.
15
16
 
package/dist/cli.js CHANGED
@@ -2,18 +2,26 @@
2
2
  import { loadConfiguredModulePackages, resolveModulePackageSpecifiers, startBot, } from "@helyx/bot";
3
3
  import { parseBotEnvironment, parseDatabaseEnvironment } from "@helyx/config";
4
4
  import { checkDatabaseConnection, createDatabasePool, inspectCoreMigrations, runCoreMigrations, } from "@helyx/database";
5
+ import { formatCommandError } from "./errors.js";
5
6
  import { runSelfHostCommand } from "./self-host/command.js";
7
+ import { loadLocalEnvironment, setupSelfHostEnvironment } from "./setup.js";
6
8
  const command = process.argv[2];
7
9
  try {
8
10
  if (command === "start") {
11
+ await loadLocalEnvironment();
9
12
  await startBot();
10
13
  }
11
14
  else if (command === "migrate") {
15
+ await loadLocalEnvironment();
12
16
  await migrate();
13
17
  }
14
18
  else if (command === "doctor") {
19
+ await loadLocalEnvironment();
15
20
  await doctor();
16
21
  }
22
+ else if (command === "setup") {
23
+ await setup();
24
+ }
17
25
  else if (command === "self-host") {
18
26
  await runSelfHostCommand(process.argv.slice(3));
19
27
  }
@@ -28,10 +36,23 @@ try {
28
36
  }
29
37
  }
30
38
  catch (error) {
31
- const value = error instanceof Error ? error : new Error(String(error));
32
- console.error(`Helyx ${command ?? "command"} failed: ${value.message}`);
39
+ console.error(`Helyx ${command ?? "command"} failed: ${formatCommandError(error)}`);
33
40
  process.exitCode = 1;
34
41
  }
42
+ async function setup() {
43
+ const result = await setupSelfHostEnvironment();
44
+ console.log(result.created
45
+ ? "Created .env from .env.example. Existing files were not overwritten."
46
+ : "The existing .env file was left unchanged.");
47
+ if (result.dockerComposeAvailable) {
48
+ console.log("Docker Compose is available. You may use the included bot and PostgreSQL services.");
49
+ }
50
+ else {
51
+ console.log("Docker Compose was not detected. Direct Node.js operation is supported, but you must provide a local or hosted PostgreSQL database.");
52
+ }
53
+ console.log("Next: edit .env, replace every placeholder, then run npm run doctor.");
54
+ console.log("Managed hosts may set the same variables in their control panel instead of using .env.");
55
+ }
35
56
  async function migrate() {
36
57
  const environment = parseDatabaseEnvironment(process.env);
37
58
  const database = createDatabasePool({
@@ -60,12 +81,12 @@ async function doctor() {
60
81
  try {
61
82
  await checkDatabaseConnection(database);
62
83
  const migrations = await inspectCoreMigrations(database);
63
- if (migrations.pending.length > 0) {
64
- throw new Error(`${migrations.pending.length} core migration(s) are pending; run helyx migrate`);
65
- }
66
84
  const moduleSpecifiers = await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH);
67
85
  const modules = await loadConfiguredModulePackages(moduleSpecifiers);
68
86
  console.log(`Helyx doctor passed: PostgreSQL connected, ${migrations.verified.length} core migrations verified, ${modules.length} module packages validated.`);
87
+ if (migrations.pending.length > 0) {
88
+ console.log(`${migrations.pending.length} core migration(s) are pending and will be applied automatically by npm start.`);
89
+ }
69
90
  }
70
91
  finally {
71
92
  await database.end();
@@ -75,6 +96,7 @@ function printHelp() {
75
96
  console.log(`Usage: helyx <command>
76
97
 
77
98
  Commands:
99
+ setup Create .env safely and explain the available hosting paths
78
100
  start Start the Helyx Discord bot
79
101
  migrate Apply core PostgreSQL migrations and exit
80
102
  doctor Validate environment, database schema and module packages
@@ -0,0 +1,2 @@
1
+ export declare function formatCommandError(error: unknown): string;
2
+ //# sourceMappingURL=errors.d.ts.map
package/dist/errors.js ADDED
@@ -0,0 +1,14 @@
1
+ import { ZodError } from "zod";
2
+ export function formatCommandError(error) {
3
+ if (error instanceof ZodError) {
4
+ const fields = [
5
+ ...new Set(error.issues
6
+ .map((issue) => issue.path.join("."))
7
+ .filter((field) => field.length > 0)),
8
+ ];
9
+ const fieldList = fields.length > 0 ? ` (${fields.join(", ")})` : "";
10
+ return `configuration is incomplete or invalid${fieldList}. Run "npm run setup" in a downloaded starter, edit .env, then retry`;
11
+ }
12
+ return error instanceof Error ? error.message : String(error);
13
+ }
14
+ //# sourceMappingURL=errors.js.map
package/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export * from "./self-host/catalogue.js";
2
2
  export * from "./self-host/command.js";
3
3
  export * from "./self-host/manifest.js";
4
4
  export * from "./self-host/project-generator.js";
5
+ export * from "./setup.js";
5
6
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@ export * from "./self-host/catalogue.js";
2
2
  export * from "./self-host/command.js";
3
3
  export * from "./self-host/manifest.js";
4
4
  export * from "./self-host/project-generator.js";
5
+ export * from "./setup.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -5,6 +5,7 @@ import { dirname, parse, resolve } from "node:path";
5
5
  import { parseSelfHostBuildManifest, } from "./manifest.js";
6
6
  const generatedFileNames = [
7
7
  ".env.example",
8
+ ".gitignore",
8
9
  ".npmrc",
9
10
  "Dockerfile",
10
11
  "compose.yaml",
@@ -65,6 +66,7 @@ async function writeGeneratedFiles(targetDirectory, manifest, catalogue) {
65
66
  packageManager: "npm@11.18.0",
66
67
  engines: { node: ">=24 <27", npm: ">=11" },
67
68
  scripts: {
69
+ setup: "helyx setup",
68
70
  start: "helyx start",
69
71
  doctor: "helyx doctor",
70
72
  migrate: "helyx migrate",
@@ -100,7 +102,11 @@ function renderReadme(template, manifest, catalogue) {
100
102
  return normalizeCrLf(template.replace("{{SELECTED_MODULES}}", selected));
101
103
  }
102
104
  function templateUrl(fileName) {
103
- const templateName = fileName === ".npmrc" ? "npmrc.template" : fileName;
105
+ const templateName = fileName === ".npmrc"
106
+ ? "npmrc.template"
107
+ : fileName === ".gitignore"
108
+ ? "gitignore.template"
109
+ : fileName;
104
110
  return new URL(`../../templates/self-host/${templateName}`, import.meta.url);
105
111
  }
106
112
  function formatJson(value) {
@@ -0,0 +1,12 @@
1
+ export interface SetupSelfHostEnvironmentOptions {
2
+ cwd?: string;
3
+ detectDockerCompose?: () => Promise<boolean>;
4
+ }
5
+ export interface SetupSelfHostEnvironmentResult {
6
+ created: boolean;
7
+ dockerComposeAvailable: boolean;
8
+ environmentPath: string;
9
+ }
10
+ export declare function setupSelfHostEnvironment(options?: SetupSelfHostEnvironmentOptions): Promise<SetupSelfHostEnvironmentResult>;
11
+ export declare function loadLocalEnvironment(cwd?: string, environment?: NodeJS.ProcessEnv): Promise<boolean>;
12
+ //# sourceMappingURL=setup.d.ts.map
package/dist/setup.js ADDED
@@ -0,0 +1,101 @@
1
+ import { constants } from "node:fs";
2
+ import { chmod, copyFile, readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import { parseEnv } from "node:util";
6
+ const starterPackageName = "helyx-self-hosted";
7
+ export async function setupSelfHostEnvironment(options = {}) {
8
+ const cwd = resolve(options.cwd ?? process.cwd());
9
+ await assertGeneratedStarter(cwd);
10
+ const examplePath = resolve(cwd, ".env.example");
11
+ const environmentPath = resolve(cwd, ".env");
12
+ let created = false;
13
+ try {
14
+ await copyFile(examplePath, environmentPath, constants.COPYFILE_EXCL);
15
+ await chmod(environmentPath, 0o600);
16
+ created = true;
17
+ }
18
+ catch (error) {
19
+ if (!isFileSystemError(error, "EEXIST"))
20
+ throw error;
21
+ }
22
+ return {
23
+ created,
24
+ dockerComposeAvailable: await (options.detectDockerCompose ?? detectDockerCompose)(),
25
+ environmentPath,
26
+ };
27
+ }
28
+ export async function loadLocalEnvironment(cwd = process.cwd(), environment = process.env) {
29
+ let content;
30
+ try {
31
+ content = await readFile(resolve(cwd, ".env"), "utf8");
32
+ }
33
+ catch (error) {
34
+ if (isFileSystemError(error, "ENOENT"))
35
+ return false;
36
+ throw error;
37
+ }
38
+ for (const [key, value] of Object.entries(parseEnv(content))) {
39
+ if (key !== "NODE_OPTIONS" && environment[key] === undefined) {
40
+ environment[key] = value;
41
+ }
42
+ }
43
+ return true;
44
+ }
45
+ async function assertGeneratedStarter(cwd) {
46
+ let packageJson;
47
+ try {
48
+ packageJson = JSON.parse(await readFile(resolve(cwd, "package.json"), "utf8"));
49
+ }
50
+ catch (error) {
51
+ if (isFileSystemError(error, "ENOENT")) {
52
+ throw new Error("Run this command from the downloaded Helyx starter directory", { cause: error });
53
+ }
54
+ throw error;
55
+ }
56
+ if (typeof packageJson !== "object" ||
57
+ packageJson === null ||
58
+ !("name" in packageJson) ||
59
+ packageJson.name !== starterPackageName) {
60
+ throw new Error("Run this command from the downloaded Helyx starter directory");
61
+ }
62
+ try {
63
+ await readFile(resolve(cwd, ".env.example"), "utf8");
64
+ }
65
+ catch (error) {
66
+ if (isFileSystemError(error, "ENOENT")) {
67
+ throw new Error("The starter is missing its .env.example file", {
68
+ cause: error,
69
+ });
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+ async function detectDockerCompose() {
75
+ return new Promise((resolvePromise) => {
76
+ const child = spawn("docker", ["compose", "version"], {
77
+ shell: false,
78
+ stdio: "ignore",
79
+ windowsHide: true,
80
+ });
81
+ const timeout = setTimeout(() => {
82
+ child.kill("SIGTERM");
83
+ resolvePromise(false);
84
+ }, 5_000);
85
+ child.once("error", () => {
86
+ clearTimeout(timeout);
87
+ resolvePromise(false);
88
+ });
89
+ child.once("close", (code) => {
90
+ clearTimeout(timeout);
91
+ resolvePromise(code === 0);
92
+ });
93
+ });
94
+ }
95
+ function isFileSystemError(error, code) {
96
+ return (error instanceof Error &&
97
+ "code" in error &&
98
+ typeof error.code === "string" &&
99
+ error.code === code);
100
+ }
101
+ //# sourceMappingURL=setup.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helyx/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Command-line tools for running and generating self-hosted Helyx deployments.",
5
5
  "keywords": [
6
6
  "helyx",
@@ -9,7 +9,7 @@
9
9
  "self-hosted"
10
10
  ],
11
11
  "private": false,
12
- "license": "SEE LICENSE IN LICENSE",
12
+ "license": "HSAL 1.0",
13
13
  "homepage": "https://helyx.gg/",
14
14
  "bugs": {
15
15
  "url": "https://github.com/ZyC0R3/Helyx/issues"
@@ -52,9 +52,9 @@
52
52
  "build": "tsc -b"
53
53
  },
54
54
  "dependencies": {
55
- "@helyx/bot": "0.1.1",
56
- "@helyx/config": "0.1.1",
57
- "@helyx/database": "0.1.0",
55
+ "@helyx/bot": "0.1.2",
56
+ "@helyx/config": "0.1.2",
57
+ "@helyx/database": "0.1.1",
58
58
  "semver": "7.8.5",
59
59
  "zod": "4.4.3"
60
60
  },
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "catalogueVersion": 5,
3
+ "catalogueVersion": 7,
4
4
  "core": {
5
5
  "package": "@helyx/bot",
6
- "version": "0.1.1",
6
+ "version": "0.1.2",
7
7
  "availability": "published"
8
8
  },
9
9
  "generator": {
10
10
  "package": "@helyx/cli",
11
- "version": "0.1.1",
11
+ "version": "0.1.3",
12
12
  "command": "helyx",
13
13
  "availability": "published"
14
14
  },
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "id": "helyx.feedback",
18
18
  "package": "@helyx/module-feedback",
19
- "version": "1.0.1",
19
+ "version": "1.0.2",
20
20
  "name": "Feedback",
21
21
  "description": "Collect structured member feedback in a channel you choose.",
22
22
  "category": "Community",
@@ -25,7 +25,7 @@
25
25
  {
26
26
  "id": "helyx.welcome",
27
27
  "package": "@helyx/module-welcome",
28
- "version": "1.0.1",
28
+ "version": "1.0.2",
29
29
  "name": "Welcome",
30
30
  "description": "Greet new members and optionally give them a starting role.",
31
31
  "category": "Community",
@@ -34,7 +34,7 @@
34
34
  {
35
35
  "id": "helyx.suggestions",
36
36
  "package": "@helyx/module-suggestions",
37
- "version": "1.0.1",
37
+ "version": "1.0.2",
38
38
  "name": "Suggestions",
39
39
  "description": "Collect community ideas with reaction or button voting.",
40
40
  "category": "Community",
@@ -214,7 +214,7 @@
214
214
  {
215
215
  "id": "helyx.logging",
216
216
  "package": "@helyx/module-logging",
217
- "version": "1.0.1",
217
+ "version": "1.0.2",
218
218
  "name": "Logging",
219
219
  "description": "Post selected Helyx module activity to one server channel.",
220
220
  "category": "Insights",
@@ -1,10 +1,14 @@
1
- # Required Discord and database configuration.
1
+ # Required Discord configuration.
2
+ DISCORD_TOKEN=replace-me
3
+ DISCORD_CLIENT_ID=12345678901234567
4
+
5
+ # Database defaults for the included Docker Compose service.
6
+ # For direct Node.js or managed hosting, replace DATABASE_URL with the
7
+ # connection string for your local or hosted PostgreSQL database.
2
8
  DATABASE_URL=postgresql://database:5432/helyx
3
9
  DATABASE_SSL=disable
4
10
  PGUSER=helyx
5
11
  PGPASSWORD=replace-with-a-strong-password
6
- DISCORD_TOKEN=replace-me
7
- DISCORD_CLIENT_ID=12345678901234567
8
12
 
9
13
  # Generated module selection. Keep this file under version control.
10
14
  HELYX_MODULE_CATALOGUE_PATH=./helyx.modules.json
@@ -20,7 +24,7 @@ HELYX_MODULE_CATALOGUE_PATH=./helyx.modules.json
20
24
  LOG_LEVEL=info
21
25
  NODE_ENV=production
22
26
 
23
- # Used only by the included Docker Compose database.
27
+ # Used only by the included Docker Compose PostgreSQL service.
24
28
  POSTGRES_DB=helyx
25
29
  POSTGRES_USER=helyx
26
30
  POSTGRES_PASSWORD=replace-with-a-strong-password
@@ -10,33 +10,52 @@ Package installation and module activation are separate. Installed feature modul
10
10
 
11
11
  ## Requirements
12
12
 
13
- - Node.js `>=24 <27` and npm `>=11`, or Docker with Compose support
14
13
  - A Discord application and bot token
15
- - PostgreSQL 17 or another currently supported PostgreSQL release
14
+ - One of the supported runtime paths below
15
+ - PostgreSQL 17 or another currently supported PostgreSQL release, supplied by your chosen path
16
16
  - A safe backup location for the database and this project's lockfile
17
17
 
18
- ## Local setup
18
+ ## Choose a runtime path
19
+
20
+ ### Node.js and PowerShell or another shell
21
+
22
+ Use this path on a local machine or VPS without Docker. It requires Node.js `>=24 <27`, npm `>=11`, and a local or hosted PostgreSQL database. Helyx does not require Docker, but PostgreSQL remains mandatory.
23
+
24
+ 1. Run `npm ci`.
25
+ 2. Run `npm run setup`. Helyx creates `.env` from the supplied example without overwriting an existing file.
26
+ 3. Edit `.env`, replace every placeholder and set `DATABASE_URL` for your PostgreSQL server.
27
+ 4. Set `DATABASE_SSL=disable` for a trusted local connection, or `require`/`verify-full` according to your database provider.
28
+ 5. Run `npm run doctor`.
29
+ 6. Run `npm start`.
30
+
31
+ ### Docker Compose
32
+
33
+ Use this path when Docker with Compose support is available. The included configuration runs both Helyx and PostgreSQL 17.
34
+
35
+ 1. Run `npm ci`, then `npm run setup`. This creates the local configuration safely; the bot itself will still run in Docker.
36
+ 2. Edit `.env` and replace all placeholder credentials and passwords.
37
+ 3. Keep the supplied internal `DATABASE_URL` and `DATABASE_SSL=disable`.
38
+ 4. Run `docker compose up --build -d`.
39
+ 5. Review startup with `docker compose logs -f bot`.
40
+ 6. Stop services with `docker compose down`. Do not add `--volumes` unless you deliberately intend to delete the database volume.
41
+
42
+ ### Managed bot hosting
43
+
44
+ Use this path when a provider installs npm packages and runs the package start script for you. Configure `npm start` as the start command and supply every required variable through the provider's environment or variables panel. Use a PostgreSQL database reachable from that provider. A local `.env` file is not required when the host supplies the variables.
45
+
46
+ If a provider cannot run `npm ci`, execute `npm start`, or connect to PostgreSQL, it cannot run this Helyx starter. A future hosting-specific download may package additional provider files, but it cannot remove the PostgreSQL requirement.
47
+
48
+ ## Discord setup
19
49
 
20
50
  1. In the Discord Developer Portal, create or select an application and add its bot user.
21
- 2. Copy the application ID into `DISCORD_CLIENT_ID`. Generate a bot token and copy it into `DISCORD_TOKEN`; never place that token in Discord, Git, screenshots or support messages.
51
+ 2. Copy the application ID into `DISCORD_CLIENT_ID`. Generate a bot token and place it directly in `.env` or the host's variables panel; never place that token in Discord, Git, screenshots or support messages.
22
52
  3. Install the bot using both the `bot` and `applications.commands` scopes. Review the permissions required by your selected modules and grant only what you intend; administrator access is convenient but not required by Helyx core.
23
- 4. Copy `.env.example` to `.env` and replace every placeholder credential. Do not commit `.env`.
24
- 5. For a non-Compose PostgreSQL server, replace `DATABASE_URL` and set `DATABASE_SSL=require` or `verify-full` as required by the provider. `PGUSER`, `PGPASSWORD` and `POSTGRES_*` are for the included Compose database only.
25
- 6. Run `npm ci`.
26
- 7. Run `npm run doctor` to validate the environment, database and selected packages without signing in to Discord or changing the database.
27
- 8. Run `npm start`.
28
53
 
29
54
  `npm start` validates the environment, connects to PostgreSQL, applies core and selected-module migrations safely, validates installed modules, synchronises Discord commands and then reports readiness. Do not run a separate migration command before every start. `npm run migrate` is available when you deliberately need to apply core migrations without starting Discord.
30
55
 
31
- Command registration is global or guild-scoped according to each reviewed command contribution. Discord may take time to refresh global commands. Installing a package does not enable its feature for a server; run `/helyx` in the server after startup to enable modules and configure their settings. Use `/permissions` when module or command access policies need to be changed.
32
-
33
- ## Docker Compose
56
+ `npm run doctor` validates the environment, PostgreSQL connection, existing migration history and selected packages without signing in to Discord or modifying the database. On a new database it reports migrations that `npm start` will apply automatically.
34
57
 
35
- 1. Copy `.env.example` to `.env` and replace all placeholder credentials.
36
- 2. Keep the included internal `DATABASE_URL` and `DATABASE_SSL=disable` only when using the Compose database service on its private network.
37
- 3. Run `docker compose up --build -d`.
38
- 4. Review startup with `docker compose logs -f bot`.
39
- 5. Stop services with `docker compose down`. Do not add `--volumes` unless you deliberately intend to delete the database volume.
58
+ Command registration is global or guild-scoped according to each reviewed command contribution. Discord may take time to refresh global commands. Installing a package does not enable its feature for a server; run `/helyx` in the server after startup to enable modules and configure their settings. Use `/permissions` when module or command access policies need to be changed.
40
59
 
41
60
  ## Backups and upgrades
42
61
 
@@ -0,0 +1,3 @@
1
+ .env
2
+ node_modules/
3
+ npm-debug.log*