@heysummon/app 0.1.0

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/README.md ADDED
@@ -0,0 +1,226 @@
1
+ <div align="center">
2
+
3
+ # ๐Ÿฆž HeySummon CLI
4
+
5
+ **The fastest way to self-host HeySummon โ€” no Docker, no Git, one command.**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/heysummon)](https://www.npmjs.com/package/heysummon)
8
+ [![License: SUL](https://img.shields.io/badge/license-Sustainable%20Use-blue)](https://github.com/thomasansems/heysummon/blob/main/LICENSE.md)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
10
+
11
+ [Documentation](https://docs.heysummon.ai) ยท [Cloud](https://cloud.heysummon.ai) ยท [GitHub](https://github.com/thomasansems/heysummon)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## What is HeySummon?
18
+
19
+ HeySummon is an open-source **Human-in-the-Loop platform for AI agents**. When an AI agent gets stuck, needs approval, or requires human context, it sends an encrypted help request to a human expert โ€” in real time, end-to-end encrypted, via a clean dashboard.
20
+
21
+ Think of it as **a pager for your AI agents**: they summon a human when they hit a wall, get a response, and continue their workflow โ€” all without breaking the loop.
22
+
23
+ - **E2E encrypted** โ€” RSA-OAEP + AES-256-GCM. The server never reads your messages.
24
+ - **Real-time** โ€” SSE-powered push updates, no polling needed.
25
+ - **Self-hostable** โ€” full control, runs on a single machine with SQLite.
26
+ - **Or use the cloud** โ€” [cloud.heysummon.ai](https://cloud.heysummon.ai) if you'd rather not host anything.
27
+
28
+ ---
29
+
30
+ ## Why a CLI?
31
+
32
+ The HeySummon platform is a full Next.js application. The CLI exists so you can install and manage it without touching Git, Docker, or config files manually. It handles:
33
+
34
+ - Downloading the latest release from GitHub
35
+ - Generating cryptographic secrets
36
+ - Configuring your environment interactively
37
+ - Setting up the SQLite database and running migrations
38
+ - Building the app
39
+ - Starting, stopping, and updating the server
40
+
41
+ One command gets you from zero to a running server.
42
+
43
+ ---
44
+
45
+ ## Quick Start
46
+
47
+ ```bash
48
+ npx heysummon
49
+ ```
50
+
51
+ The interactive installer walks you through everything. Takes ~2 minutes. No Docker or Git required.
52
+
53
+ Once installed, open the URL shown in the terminal to create your account.
54
+
55
+ ---
56
+
57
+ ## Commands
58
+
59
+ ```
60
+ heysummon [command]
61
+ ```
62
+
63
+ | Command | Description |
64
+ |---|---|
65
+ | `heysummon` / `heysummon init` | First-time setup โ€” download, configure, build, and start |
66
+ | `heysummon start` | Start the server |
67
+ | `heysummon start -d` | Start the server in the background (daemon) |
68
+ | `heysummon stop` | Stop the server |
69
+ | `heysummon status` | Check if the server is running |
70
+ | `heysummon update` | Update to the latest release |
71
+ | `heysummon uninstall` | Safely remove all data and stop the server |
72
+
73
+ ### Options
74
+
75
+ ```
76
+ --help, -h Show help
77
+ --version, -v Show version
78
+ ```
79
+
80
+ ---
81
+
82
+ ## What gets installed
83
+
84
+ Everything lives in `~/.heysummon/`:
85
+
86
+ ```
87
+ ~/.heysummon/
88
+ โ”œโ”€โ”€ app/ โ† Next.js server (downloaded from GitHub)
89
+ โ”‚ โ”œโ”€โ”€ prisma/
90
+ โ”‚ โ”‚ โ””โ”€โ”€ heysummon.db โ† SQLite database (your data)
91
+ โ”‚ โ””โ”€โ”€ .next/ โ† build output
92
+ โ”œโ”€โ”€ .env โ† config & secrets
93
+ โ””โ”€โ”€ heysummon.pid โ† running server PID
94
+ ```
95
+
96
+ The CLI binary itself is installed separately via npm and is not part of `~/.heysummon/`.
97
+
98
+ ---
99
+
100
+ ## Updating
101
+
102
+ ```bash
103
+ heysummon update
104
+ ```
105
+
106
+ Downloads the latest release, runs migrations, rebuilds, and restarts โ€” your data is preserved.
107
+
108
+ ---
109
+
110
+ ## Uninstalling
111
+
112
+ ```bash
113
+ heysummon uninstall
114
+ ```
115
+
116
+ Stops the server, offers a database backup, asks for explicit confirmation, and removes `~/.heysummon/`. Then remove the CLI binary:
117
+
118
+ ```bash
119
+ npm uninstall -g heysummon
120
+ ```
121
+
122
+ > **Note:** `npm uninstall` alone only removes the CLI binary โ€” it does **not** touch `~/.heysummon/`. Always run `heysummon uninstall` first for a clean removal.
123
+
124
+ ---
125
+
126
+ ## Requirements
127
+
128
+ | | Minimum |
129
+ |---|---|
130
+ | Node.js | 18+ |
131
+ | OS | Linux, macOS, WSL2 |
132
+ | Disk | ~500 MB (app + build) |
133
+
134
+ ---
135
+
136
+ ## Alternative installation methods
137
+
138
+ The CLI is the easiest path but not the only one. Choose based on your use case:
139
+
140
+ ### Docker (recommended for production)
141
+
142
+ Full stack with PostgreSQL, Guard reverse proxy, and optional tunnel:
143
+
144
+ ```bash
145
+ git clone https://github.com/thomasansems/heysummon.git
146
+ cd heysummon
147
+ cp .env.example .env # edit secrets
148
+ docker compose up -d
149
+ ```
150
+
151
+ #### Make it publicly accessible
152
+
153
+ ```bash
154
+ # Cloudflare Tunnel (recommended for production)
155
+ docker compose --profile cloudflare up -d
156
+
157
+ # Tailscale Funnel (great for teams, zero config firewall)
158
+ docker compose --profile tailscale up -d
159
+
160
+ # Ngrok (quick testing)
161
+ docker compose --profile ngrok up -d
162
+ ```
163
+
164
+ See the [Self-Hosting Guide](https://docs.heysummon.ai/self-hosting) for per-provider setup.
165
+
166
+ ### Managed Cloud
167
+
168
+ No self-hosting at all โ€” [cloud.heysummon.ai](https://cloud.heysummon.ai) is the hosted version with a free tier.
169
+
170
+ ### Comparison
171
+
172
+ | Method | Database | Tunnel | Best for |
173
+ |---|---|---|---|
174
+ | `npx heysummon` (this CLI) | SQLite | Manual / reverse proxy | Quick start, single machine |
175
+ | Docker Compose | PostgreSQL | Cloudflare / Tailscale / Ngrok | Production, teams |
176
+ | Cloud | Managed | Built-in | Zero ops |
177
+
178
+ ---
179
+
180
+ ## Using HeySummon from an AI agent
181
+
182
+ Once your server is running, install the SDK in your agent's project:
183
+
184
+ ```bash
185
+ npm install @heysummon/sdk
186
+ ```
187
+
188
+ ```ts
189
+ import { HeySummon } from "@heysummon/sdk";
190
+
191
+ const hs = new HeySummon({ apiKey: "hs_cli_..." });
192
+
193
+ const response = await hs.ask("Should I proceed with deleting the old records?");
194
+ console.log(response); // human's answer, E2E decrypted
195
+ ```
196
+
197
+ Get your API key from the dashboard under **Settings โ†’ API Keys**.
198
+
199
+ ---
200
+
201
+ ## Documentation
202
+
203
+ - **[Getting started](https://docs.heysummon.ai/getting-started/quickstart)**
204
+ - **[SDK reference](https://docs.heysummon.ai/sdk)**
205
+ - **[Self-hosting guide](https://docs.heysummon.ai/self-hosting)**
206
+ - **[API reference](https://docs.heysummon.ai/api)**
207
+ - **[GitHub repository](https://github.com/thomasansems/heysummon)**
208
+
209
+ ---
210
+
211
+ ## License
212
+
213
+ HeySummon uses a sustainable use model:
214
+
215
+ - **Core platform** โ€” [Sustainable Use License](https://github.com/thomasansems/heysummon/blob/main/LICENSE.md). Free for personal and internal business use.
216
+ - **Cloud features** โ€” separate [HeySummon Cloud License](https://github.com/thomasansems/heysummon/blob/main/LICENSE_CLOUD.md).
217
+
218
+ ---
219
+
220
+ <div align="center">
221
+
222
+ **[docs.heysummon.ai](https://docs.heysummon.ai)** ยท **[cloud.heysummon.ai](https://cloud.heysummon.ai)** ยท **[GitHub](https://github.com/thomasansems/heysummon)**
223
+
224
+ Made with ๐Ÿฆž by the HeySummon team
225
+
226
+ </div>
package/bin/CLAUDE.md ADDED
@@ -0,0 +1,11 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 23, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #3341 | 2:18 PM | ๐Ÿ”ต | HeySummon CLI Wizard Flow Lacks Platform Selection | ~699 |
11
+ </claude-mem-context>
package/bin/cli.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-undef */
3
+ "use strict";
4
+
5
+ require("../dist/index.js");
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const child_process_1 = require("child_process");
39
+ const path = __importStar(require("path"));
40
+ const CLI_PATH = path.join(__dirname, "..", "..", "bin", "cli.js");
41
+ (0, node_test_1.describe)("CLI", () => {
42
+ (0, node_test_1.it)("prints version with --version flag", () => {
43
+ const output = (0, child_process_1.execSync)(`node "${CLI_PATH}" --version`, {
44
+ encoding: "utf-8",
45
+ }).trim();
46
+ assert.match(output, /^\d+\.\d+\.\d+$/);
47
+ });
48
+ (0, node_test_1.it)("prints version with -v flag", () => {
49
+ const output = (0, child_process_1.execSync)(`node "${CLI_PATH}" -v`, {
50
+ encoding: "utf-8",
51
+ }).trim();
52
+ assert.match(output, /^\d+\.\d+\.\d+$/);
53
+ });
54
+ (0, node_test_1.it)("prints help with --help flag", () => {
55
+ const output = (0, child_process_1.execSync)(`node "${CLI_PATH}" --help`, {
56
+ encoding: "utf-8",
57
+ });
58
+ assert.ok(output.includes("HeySummon CLI"));
59
+ assert.ok(output.includes("Usage:"));
60
+ assert.ok(output.includes("Commands:"));
61
+ assert.ok(output.includes("init"));
62
+ assert.ok(output.includes("start"));
63
+ assert.ok(output.includes("stop"));
64
+ assert.ok(output.includes("status"));
65
+ assert.ok(output.includes("update"));
66
+ });
67
+ (0, node_test_1.it)("prints help with -h flag", () => {
68
+ const output = (0, child_process_1.execSync)(`node "${CLI_PATH}" -h`, {
69
+ encoding: "utf-8",
70
+ });
71
+ assert.ok(output.includes("HeySummon CLI"));
72
+ });
73
+ (0, node_test_1.it)("exits with error for unknown commands", () => {
74
+ assert.throws(() => {
75
+ (0, child_process_1.execSync)(`node "${CLI_PATH}" foobar`, {
76
+ encoding: "utf-8",
77
+ stdio: "pipe",
78
+ });
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const config_1 = require("../lib/config");
39
+ (0, node_test_1.describe)("config", () => {
40
+ (0, node_test_1.describe)("generateEnv", () => {
41
+ const baseConfig = {
42
+ port: 3435,
43
+ publicUrl: "http://localhost:3435",
44
+ enableFormLogin: true,
45
+ enableGithubOauth: false,
46
+ enableGoogleOauth: false,
47
+ };
48
+ const secrets = {
49
+ nextauthSecret: "abc123",
50
+ };
51
+ (0, node_test_1.it)("generates env with all required variables", () => {
52
+ const env = (0, config_1.generateEnv)(baseConfig, secrets);
53
+ assert.ok(env.includes("DATABASE_URL="));
54
+ assert.ok(env.includes("PORT=3435"));
55
+ assert.ok(env.includes('NEXTAUTH_URL="http://localhost:3435"'));
56
+ assert.ok(env.includes('NEXTAUTH_SECRET="abc123"'));
57
+ assert.ok(env.includes('ENABLE_FORM_LOGIN="true"'));
58
+ });
59
+ (0, node_test_1.it)("includes GitHub OAuth when enabled", () => {
60
+ const config = {
61
+ ...baseConfig,
62
+ enableGithubOauth: true,
63
+ githubId: "gh-id",
64
+ githubSecret: "gh-secret",
65
+ };
66
+ const env = (0, config_1.generateEnv)(config, secrets);
67
+ assert.ok(env.includes('GITHUB_ID="gh-id"'));
68
+ assert.ok(env.includes('GITHUB_SECRET="gh-secret"'));
69
+ });
70
+ (0, node_test_1.it)("includes Google OAuth when enabled", () => {
71
+ const config = {
72
+ ...baseConfig,
73
+ enableGoogleOauth: true,
74
+ googleId: "g-id",
75
+ googleSecret: "g-secret",
76
+ };
77
+ const env = (0, config_1.generateEnv)(config, secrets);
78
+ assert.ok(env.includes('GOOGLE_ID="g-id"'));
79
+ assert.ok(env.includes('GOOGLE_SECRET="g-secret"'));
80
+ });
81
+ (0, node_test_1.it)("excludes OAuth vars when disabled", () => {
82
+ const env = (0, config_1.generateEnv)(baseConfig, secrets);
83
+ assert.ok(!env.includes("GITHUB_ID"));
84
+ assert.ok(!env.includes("GITHUB_SECRET"));
85
+ assert.ok(!env.includes("GOOGLE_ID"));
86
+ assert.ok(!env.includes("GOOGLE_SECRET"));
87
+ });
88
+ (0, node_test_1.it)("uses custom port in output", () => {
89
+ const config = {
90
+ ...baseConfig,
91
+ port: 8080,
92
+ publicUrl: "https://my.domain.com",
93
+ };
94
+ const env = (0, config_1.generateEnv)(config, secrets);
95
+ assert.ok(env.includes("PORT=8080"));
96
+ assert.ok(env.includes('NEXTAUTH_URL="https://my.domain.com"'));
97
+ });
98
+ });
99
+ });
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const secrets_1 = require("../lib/secrets");
39
+ (0, node_test_1.describe)("secrets", () => {
40
+ (0, node_test_1.it)("generates a hex string of correct length", () => {
41
+ const secret = (0, secrets_1.generateSecret)(32);
42
+ assert.strictEqual(secret.length, 64); // 32 bytes = 64 hex chars
43
+ assert.match(secret, /^[0-9a-f]{64}$/);
44
+ });
45
+ (0, node_test_1.it)("generates unique secrets each time", () => {
46
+ const a = (0, secrets_1.generateSecret)();
47
+ const b = (0, secrets_1.generateSecret)();
48
+ assert.notStrictEqual(a, b);
49
+ });
50
+ (0, node_test_1.it)("generates the required secret", () => {
51
+ const secrets = (0, secrets_1.generateSecrets)();
52
+ assert.ok(secrets.nextauthSecret);
53
+ assert.match(secrets.nextauthSecret, /^[0-9a-f]{64}$/);
54
+ });
55
+ (0, node_test_1.it)("respects custom byte length", () => {
56
+ const short = (0, secrets_1.generateSecret)(16);
57
+ assert.strictEqual(short.length, 32); // 16 bytes = 32 hex chars
58
+ assert.match(short, /^[0-9a-f]{32}$/);
59
+ });
60
+ });
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.init = init;
37
+ const fs = __importStar(require("fs"));
38
+ const p = __importStar(require("@clack/prompts"));
39
+ const config_1 = require("../lib/config");
40
+ const secrets_1 = require("../lib/secrets");
41
+ const download_1 = require("../lib/download");
42
+ const database_1 = require("../lib/database");
43
+ const prompts_1 = require("../lib/prompts");
44
+ const start_1 = require("./start");
45
+ const ui_1 = require("../lib/ui");
46
+ async function init(opts) {
47
+ const quickstart = opts?.yes ?? false;
48
+ await (0, ui_1.printAnimatedBanner)();
49
+ p.intro("heysummon init");
50
+ const heysummonDir = (0, config_1.getHeysummonDir)();
51
+ p.log.info(`Home: ${ui_1.color.cyan(heysummonDir)}`);
52
+ // Check existing installation
53
+ if ((0, config_1.isInitialized)()) {
54
+ if (quickstart) {
55
+ p.log.warn("Existing installation found โ€” reinstalling.");
56
+ }
57
+ else {
58
+ const reinit = await (0, prompts_1.askYesNo)("HeySummon is already installed. Reinstall from scratch?", false);
59
+ if (!reinit) {
60
+ p.outro(`Tip: use ${ui_1.color.cyan("heysummon start")} to run the server.`);
61
+ return;
62
+ }
63
+ }
64
+ const appDir = (0, config_1.getAppDir)();
65
+ if (fs.existsSync(appDir)) {
66
+ fs.rmSync(appDir, { recursive: true });
67
+ }
68
+ }
69
+ // Create directory structure
70
+ (0, config_1.ensureDir)((0, config_1.getHeysummonDir)());
71
+ (0, config_1.ensureDir)((0, config_1.getAppDir)());
72
+ // โ”€โ”€ Download โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
73
+ const dlSpinner = p.spinner();
74
+ dlSpinner.start("Downloading latest release from GitHub...");
75
+ const version = await (0, download_1.downloadAndExtract)();
76
+ dlSpinner.stop(`Downloaded HeySummon ${version}`);
77
+ // โ”€โ”€ Configuration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
78
+ let port = 3435;
79
+ let publicUrl = `http://localhost:${port}`;
80
+ if (!quickstart) {
81
+ const portInput = await p.text({
82
+ message: "Guard port (app entry point)",
83
+ defaultValue: "3435",
84
+ placeholder: "3435",
85
+ });
86
+ if (p.isCancel(portInput)) {
87
+ p.cancel("Setup cancelled.");
88
+ process.exit(0);
89
+ }
90
+ port = parseInt(String(portInput), 10) || 3435;
91
+ p.log.info(`HeySummon will be available at: ${ui_1.color.cyan(`http://localhost:${port}`)}`);
92
+ const urlInput = await p.text({
93
+ message: "Public URL (for internet access, or keep default)",
94
+ defaultValue: `http://localhost:${port}`,
95
+ placeholder: `http://localhost:${port}`,
96
+ });
97
+ if (p.isCancel(urlInput)) {
98
+ p.cancel("Setup cancelled.");
99
+ process.exit(0);
100
+ }
101
+ publicUrl = String(urlInput);
102
+ }
103
+ else {
104
+ p.log.info(`Using defaults: port ${ui_1.color.cyan("3435")}, URL ${ui_1.color.cyan(publicUrl)}`);
105
+ }
106
+ // โ”€โ”€ Secrets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
107
+ const secretsSpinner = p.spinner();
108
+ secretsSpinner.start("Generating secure random secrets...");
109
+ const secrets = (0, secrets_1.generateSecrets)();
110
+ const config = {
111
+ port,
112
+ publicUrl,
113
+ enableFormLogin: true,
114
+ enableGithubOauth: false,
115
+ enableGoogleOauth: false,
116
+ };
117
+ const envContent = (0, config_1.generateEnv)(config, secrets);
118
+ (0, config_1.writeEnv)(envContent);
119
+ secretsSpinner.stop("Secrets generated and saved");
120
+ // โ”€โ”€ Database โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
121
+ const dbSpinner = p.spinner();
122
+ dbSpinner.start("Setting up SQLite database and running migrations...");
123
+ (0, database_1.installDependencies)();
124
+ (0, database_1.runMigrations)();
125
+ dbSpinner.stop("Database ready");
126
+ // โ”€โ”€ Build โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
127
+ const buildSpinner = p.spinner();
128
+ buildSpinner.start("Building application (this takes ~30 seconds)...");
129
+ (0, database_1.buildApp)();
130
+ buildSpinner.stop("Build complete");
131
+ // โ”€โ”€ Configuration summary โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
132
+ p.note([
133
+ `Port: ${port}`,
134
+ `URL: ${publicUrl}`,
135
+ `Database: SQLite`,
136
+ `Auth: Form login`,
137
+ `Home: ${heysummonDir}`,
138
+ ].join("\n"), "Configuration");
139
+ // โ”€โ”€ Start daemon โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
140
+ const startSpinner = p.spinner();
141
+ startSpinner.start("Starting HeySummon in the background...");
142
+ try {
143
+ await (0, start_1.startDaemon)(port);
144
+ startSpinner.stop("HeySummon is running!");
145
+ p.log.info(`Dashboard: ${ui_1.color.cyan(publicUrl)}`);
146
+ p.log.info(`Docs: ${ui_1.color.cyan("https://docs.heysummon.ai/getting-started/quickstart")}`);
147
+ p.log.info(`Status: ${ui_1.color.cyan("heysummon status")}`);
148
+ p.log.info(`Stop: ${ui_1.color.cyan("heysummon stop")}`);
149
+ p.outro(`Open ${ui_1.color.cyan(publicUrl)} to create your account.`);
150
+ }
151
+ catch {
152
+ startSpinner.stop("Could not start automatically.");
153
+ p.outro(`Run ${ui_1.color.cyan("heysummon start -d")} to start manually.`);
154
+ }
155
+ }